@pipelab/core-node 1.0.0-beta.18 → 1.0.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/config-Bi0ORcTK.mjs +2 -0
- package/dist/config-CFgGRD9U.mjs +265 -0
- package/dist/config-CFgGRD9U.mjs.map +1 -0
- package/dist/index.d.mts +29 -18
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +784 -303
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/config.test.ts +59 -11
- package/src/config.ts +71 -32
- package/src/context.ts +42 -4
- package/src/handlers/config.ts +252 -72
- package/src/handlers/engine.ts +2 -2
- package/src/handlers/index.ts +2 -0
- package/src/handlers/migration.ts +598 -0
- package/src/index.ts +9 -2
- package/src/plugins-registry.ts +2 -2
- package/src/runner.ts +0 -3
- package/src/server.ts +0 -3
- package/src/utils.ts +2 -2
- package/dist/config-CEkOf95p.mjs +0 -2
- package/src/migrations.ts +0 -60
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
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
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";
|
|
6
7
|
import fs$1, { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
7
8
|
import { DEFAULT_NODE_VERSION, DEFAULT_PNPM_VERSION, SandboxFolder, getUiDevServerMissingWarning, uiDevPort, websocketPort } from "@pipelab/constants";
|
|
8
9
|
import { WebSocket, WebSocketServer as WebSocketServer$1 } from "ws";
|
|
9
10
|
import http from "node:http";
|
|
10
11
|
import { nanoid } from "nanoid";
|
|
11
|
-
import { SubscriptionRequiredError, WebSocketError,
|
|
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";
|
|
18
15
|
import dns from "node:dns/promises";
|
|
19
16
|
import pacote from "pacote";
|
|
@@ -101,8 +98,33 @@ var PipelabContext = class {
|
|
|
101
98
|
getConfigPath(...subpaths) {
|
|
102
99
|
return join(this.userDataPath, "config", ...subpaths);
|
|
103
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
|
+
}
|
|
104
126
|
getTempPath(...subpaths) {
|
|
105
|
-
return join(this.userDataPath, "temp", ...subpaths);
|
|
127
|
+
return join(this.getSettings()?.tempFolder || join(this.userDataPath, "temp"), ...subpaths);
|
|
106
128
|
}
|
|
107
129
|
async createTempFolder(prefix = "pipelab-") {
|
|
108
130
|
const baseDir = this.getTempPath();
|
|
@@ -110,8 +132,9 @@ var PipelabContext = class {
|
|
|
110
132
|
return await mkdtemp(join(await realpath(baseDir), prefix));
|
|
111
133
|
}
|
|
112
134
|
getCachePath(folder, ...subpaths) {
|
|
113
|
-
|
|
114
|
-
|
|
135
|
+
const base = this.getSettings()?.cacheFolder || join(this.userDataPath, "cache");
|
|
136
|
+
if (!folder) return base;
|
|
137
|
+
return join(base, folder, ...subpaths);
|
|
115
138
|
}
|
|
116
139
|
getPnpmPath(...subpaths) {
|
|
117
140
|
return join(this.userDataPath, "pnpm", ...subpaths);
|
|
@@ -511,289 +534,207 @@ const registerFsHandlers = (_context) => {
|
|
|
511
534
|
});
|
|
512
535
|
};
|
|
513
536
|
//#endregion
|
|
514
|
-
//#region src/
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
* Extracts a .tar.gz archive.
|
|
528
|
-
*/
|
|
529
|
-
async function extractTarGz(archivePath, destinationDir) {
|
|
530
|
-
await mkdir(destinationDir, { recursive: true });
|
|
531
|
-
await tar.x({
|
|
532
|
-
file: archivePath,
|
|
533
|
-
cwd: destinationDir
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Extracts a .zip archive.
|
|
538
|
-
*/
|
|
539
|
-
async function extractZip(archivePath, destinationDir) {
|
|
540
|
-
await mkdir(destinationDir, { recursive: true });
|
|
541
|
-
return new Promise((resolve, reject) => {
|
|
542
|
-
yauzl.open(archivePath, { lazyEntries: true }, (err, zipfile) => {
|
|
543
|
-
if (err || !zipfile) return reject(err || /* @__PURE__ */ new Error("Could not open zip file"));
|
|
544
|
-
zipfile.on("error", reject);
|
|
545
|
-
zipfile.readEntry();
|
|
546
|
-
zipfile.on("entry", (entry) => {
|
|
547
|
-
const entryPath = join(destinationDir, entry.fileName);
|
|
548
|
-
if (/\/$/.test(entry.fileName)) mkdir(entryPath, { recursive: true }).then(() => zipfile.readEntry()).catch(reject);
|
|
549
|
-
else mkdir(dirname(entryPath), { recursive: true }).then(() => {
|
|
550
|
-
zipfile.openReadStream(entry, (err, readStream) => {
|
|
551
|
-
if (err || !readStream) return reject(err || /* @__PURE__ */ new Error("Could not open read stream"));
|
|
552
|
-
readStream.on("error", reject);
|
|
553
|
-
const writeStream = createWriteStream(entryPath);
|
|
554
|
-
writeStream.on("error", reject);
|
|
555
|
-
writeStream.on("close", () => zipfile.readEntry());
|
|
556
|
-
readStream.pipe(writeStream);
|
|
557
|
-
});
|
|
558
|
-
}).catch(reject);
|
|
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
|
+
}
|
|
559
550
|
});
|
|
560
|
-
|
|
561
|
-
|
|
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
|
+
}
|
|
562
561
|
});
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
+
}
|
|
574
|
+
});
|
|
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
|
+
}
|
|
579
585
|
});
|
|
580
|
-
}
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
stdin: "pipe",
|
|
608
|
-
env: {
|
|
609
|
-
...process.env,
|
|
610
|
-
...execaOptions.env,
|
|
611
|
-
TERM: "xterm-256color",
|
|
612
|
-
FORCE_STDERR_LOGGING: "1"
|
|
613
|
-
},
|
|
614
|
-
cancelSignal: abortSignal
|
|
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
|
+
}
|
|
615
613
|
});
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
+
}
|
|
619
634
|
});
|
|
620
|
-
|
|
621
|
-
|
|
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
|
+
}
|
|
622
658
|
});
|
|
623
|
-
|
|
624
|
-
const {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
const path = join(dirPath, file.name);
|
|
639
|
-
if (file.isDirectory()) try {
|
|
640
|
-
return await getFolderSize(path);
|
|
641
|
-
} catch {
|
|
642
|
-
return 0;
|
|
643
|
-
}
|
|
644
|
-
try {
|
|
645
|
-
const { size } = await stat(path);
|
|
646
|
-
return size;
|
|
647
|
-
} catch {
|
|
648
|
-
return 0;
|
|
649
|
-
}
|
|
650
|
-
});
|
|
651
|
-
return (await Promise.all(ArrayOfPromises)).reduce((acc, size) => acc + size, 0);
|
|
652
|
-
} catch {
|
|
653
|
-
return 0;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
//#endregion
|
|
657
|
-
//#region src/config.ts
|
|
658
|
-
const getMigrator = (name) => {
|
|
659
|
-
return configRegistry[name] || configRegistry["pipeline"];
|
|
660
|
-
};
|
|
661
|
-
const setupConfigFile = async (name, options) => {
|
|
662
|
-
const ctx = options.context;
|
|
663
|
-
const migrator = options.migrator || getMigrator(name);
|
|
664
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All managed files must have a migration schema.`);
|
|
665
|
-
const filesPath = path.isAbsolute(name) ? name : ctx.getConfigPath(`${name}.json`);
|
|
666
|
-
await ensure(filesPath, JSON.stringify(migrator.defaultValue));
|
|
667
|
-
return {
|
|
668
|
-
setConfig: async (config) => {
|
|
669
|
-
const { logger } = useLogger();
|
|
670
|
-
try {
|
|
671
|
-
await fs$1.writeFile(filesPath, JSON.stringify(config));
|
|
672
|
-
return true;
|
|
673
|
-
} catch (e) {
|
|
674
|
-
logger().error(`Error saving config ${name}:`, e);
|
|
675
|
-
return false;
|
|
676
|
-
}
|
|
677
|
-
},
|
|
678
|
-
getConfig: async () => {
|
|
679
|
-
const { logger } = useLogger();
|
|
680
|
-
let content = void 0;
|
|
681
|
-
let originalJson = void 0;
|
|
682
|
-
let parseFailed = false;
|
|
683
|
-
try {
|
|
684
|
-
content = await fs$1.readFile(filesPath, "utf8");
|
|
685
|
-
if (content !== void 0) originalJson = JSON.parse(content);
|
|
686
|
-
} catch (e) {
|
|
687
|
-
logger().error(`Error reading or parsing config ${name}:`, e);
|
|
688
|
-
parseFailed = true;
|
|
689
|
-
}
|
|
690
|
-
let json = void 0;
|
|
691
|
-
let migrationFailed = false;
|
|
692
|
-
try {
|
|
693
|
-
if (!parseFailed) json = await migrator.migrate(originalJson, {
|
|
694
|
-
debug: false,
|
|
695
|
-
onStep: async (state, version) => {
|
|
696
|
-
const parsedPath = path.parse(filesPath);
|
|
697
|
-
const versionedPath = ctx.getConfigPath(`${parsedPath.name}.v${version}.json`);
|
|
698
|
-
try {
|
|
699
|
-
await fs$1.writeFile(versionedPath, JSON.stringify(state));
|
|
700
|
-
logger().info(`Intermediate backup created for ${name} at ${versionedPath}`);
|
|
701
|
-
} catch (e) {
|
|
702
|
-
logger().error(`Failed to create intermediate backup for ${name} at v${version}:`, e);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
});
|
|
706
|
-
else json = migrator.defaultValue;
|
|
707
|
-
} catch (e) {
|
|
708
|
-
logger().error(`Error migrating config ${name}:`, e);
|
|
709
|
-
migrationFailed = true;
|
|
710
|
-
json = migrator.defaultValue;
|
|
711
|
-
}
|
|
712
|
-
if (originalJson?.version !== json?.version || content === void 0 || parseFailed || migrationFailed) {
|
|
713
|
-
if (parseFailed || migrationFailed) try {
|
|
714
|
-
const parsedPath = path.parse(filesPath);
|
|
715
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
716
|
-
const corruptedPath = ctx.getConfigPath(`${parsedPath.name}.corrupted.${timestamp}.json`);
|
|
717
|
-
const backupContent = parseFailed ? content || "" : JSON.stringify(originalJson, null, 2);
|
|
718
|
-
await fs$1.writeFile(corruptedPath, backupContent);
|
|
719
|
-
logger().info(`Corrupted config file preserved at ${corruptedPath}`);
|
|
720
|
-
} catch (e) {
|
|
721
|
-
logger().error(`Failed to backup corrupted config ${name}:`, e);
|
|
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"
|
|
722
674
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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"
|
|
727
683
|
}
|
|
728
|
-
}
|
|
729
|
-
return json;
|
|
684
|
+
});
|
|
730
685
|
}
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
const filesPath = path.isAbsolute(nameOrPath) ? nameOrPath : context.getConfigPath(`${nameOrPath}.json`);
|
|
735
|
-
await fs$1.rm(filesPath, { force: true });
|
|
736
|
-
};
|
|
737
|
-
//#endregion
|
|
738
|
-
//#region src/handlers/config.ts
|
|
739
|
-
const registerConfigHandlers = (context) => {
|
|
740
|
-
const { handle } = useAPI();
|
|
741
|
-
const { logger } = useLogger();
|
|
742
|
-
handle("config:load", async (_, { send, value }) => {
|
|
743
|
-
const { config: name } = value;
|
|
744
|
-
logger().info("config:load", name);
|
|
686
|
+
});
|
|
687
|
+
handle("projects:load", async (_, { send }) => {
|
|
688
|
+
logger().info("projects:load");
|
|
745
689
|
try {
|
|
746
690
|
send({
|
|
747
691
|
type: "end",
|
|
748
692
|
data: {
|
|
749
693
|
type: "success",
|
|
750
|
-
result:
|
|
694
|
+
result: await (await setupProjectsConfigFile(context)).getConfig()
|
|
751
695
|
}
|
|
752
696
|
});
|
|
753
697
|
} catch (e) {
|
|
754
|
-
logger().error(
|
|
698
|
+
logger().error("projects:load error:", e);
|
|
755
699
|
send({
|
|
756
700
|
type: "end",
|
|
757
701
|
data: {
|
|
758
702
|
type: "error",
|
|
759
|
-
ipcError: e instanceof Error ? e.message :
|
|
703
|
+
ipcError: e instanceof Error ? e.message : "Unable to load projects"
|
|
760
704
|
}
|
|
761
705
|
});
|
|
762
706
|
}
|
|
763
707
|
});
|
|
764
|
-
handle("
|
|
765
|
-
const { data
|
|
708
|
+
handle("projects:save", async (_, { send, value }) => {
|
|
709
|
+
const { data } = value;
|
|
766
710
|
try {
|
|
767
|
-
const manager = await
|
|
711
|
+
const manager = await setupProjectsConfigFile(context);
|
|
768
712
|
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
769
713
|
await manager.setConfig(json);
|
|
770
714
|
send({
|
|
771
715
|
type: "end",
|
|
772
716
|
data: {
|
|
773
717
|
type: "success",
|
|
774
|
-
result:
|
|
718
|
+
result: "ok"
|
|
775
719
|
}
|
|
776
720
|
});
|
|
777
721
|
} catch (e) {
|
|
778
|
-
logger().error(
|
|
722
|
+
logger().error("projects:save error:", e);
|
|
779
723
|
send({
|
|
780
724
|
type: "end",
|
|
781
725
|
data: {
|
|
782
726
|
type: "error",
|
|
783
|
-
ipcError: e instanceof Error ? e.message :
|
|
727
|
+
ipcError: e instanceof Error ? e.message : "Unable to save projects"
|
|
784
728
|
}
|
|
785
729
|
});
|
|
786
730
|
}
|
|
787
731
|
});
|
|
788
|
-
handle("
|
|
789
|
-
const {
|
|
790
|
-
logger().info("config:reset", name, key);
|
|
732
|
+
handle("projects:reset", async (_, { send, value }) => {
|
|
733
|
+
const { key } = value;
|
|
791
734
|
try {
|
|
792
|
-
const manager = await
|
|
735
|
+
const manager = await setupProjectsConfigFile(context);
|
|
793
736
|
const currentConfig = await manager.getConfig();
|
|
794
|
-
const
|
|
795
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}`);
|
|
796
|
-
const defaultValue = migrator.defaultValue[key];
|
|
737
|
+
const defaultValue = fileRepoMigrations.defaultValue[key];
|
|
797
738
|
await manager.setConfig({
|
|
798
739
|
...currentConfig ? currentConfig : {},
|
|
799
740
|
[key]: defaultValue
|
|
@@ -802,39 +743,154 @@ const registerConfigHandlers = (context) => {
|
|
|
802
743
|
type: "end",
|
|
803
744
|
data: {
|
|
804
745
|
type: "success",
|
|
805
|
-
result:
|
|
746
|
+
result: "ok"
|
|
806
747
|
}
|
|
807
748
|
});
|
|
808
749
|
} catch (e) {
|
|
809
|
-
logger().error(
|
|
750
|
+
logger().error("projects:reset error:", e);
|
|
810
751
|
send({
|
|
811
752
|
type: "end",
|
|
812
753
|
data: {
|
|
813
754
|
type: "error",
|
|
814
|
-
ipcError: e instanceof Error ? e.message :
|
|
755
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset projects"
|
|
815
756
|
}
|
|
816
757
|
});
|
|
817
758
|
}
|
|
818
759
|
});
|
|
819
|
-
handle("
|
|
820
|
-
const {
|
|
821
|
-
logger().info("
|
|
760
|
+
handle("pipeline:load-by-name", async (_, { send, value }) => {
|
|
761
|
+
const { name } = value;
|
|
762
|
+
logger().info("pipeline:load-by-name", name);
|
|
822
763
|
try {
|
|
823
|
-
await deleteConfigFile(name, context);
|
|
824
764
|
send({
|
|
825
765
|
type: "end",
|
|
826
766
|
data: {
|
|
827
767
|
type: "success",
|
|
828
|
-
result:
|
|
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);
|
|
785
|
+
try {
|
|
786
|
+
send({
|
|
787
|
+
type: "end",
|
|
788
|
+
data: {
|
|
789
|
+
type: "success",
|
|
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"
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
} catch (e) {
|
|
818
|
+
logger().error(`pipeline:save-by-name error for ${name}:`, e);
|
|
819
|
+
send({
|
|
820
|
+
type: "end",
|
|
821
|
+
data: {
|
|
822
|
+
type: "error",
|
|
823
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${name}`
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
handle("pipeline:save-by-path", async (_, { send, value }) => {
|
|
829
|
+
const { data, path: absolutePath } = value;
|
|
830
|
+
try {
|
|
831
|
+
const manager = await setupPipelineConfigFileByPath(absolutePath, context);
|
|
832
|
+
const json = typeof data === "string" ? JSON.parse(data) : data;
|
|
833
|
+
await manager.setConfig(json);
|
|
834
|
+
send({
|
|
835
|
+
type: "end",
|
|
836
|
+
data: {
|
|
837
|
+
type: "success",
|
|
838
|
+
result: "ok"
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
} catch (e) {
|
|
842
|
+
logger().error(`pipeline:save-by-path error for ${absolutePath}:`, e);
|
|
843
|
+
send({
|
|
844
|
+
type: "end",
|
|
845
|
+
data: {
|
|
846
|
+
type: "error",
|
|
847
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${absolutePath}`
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
handle("pipeline:delete-by-name", async (_, { send, value }) => {
|
|
853
|
+
const { name } = value;
|
|
854
|
+
logger().info("pipeline:delete-by-name", name);
|
|
855
|
+
try {
|
|
856
|
+
await deletePipelineConfigFileByName(name, context);
|
|
857
|
+
send({
|
|
858
|
+
type: "end",
|
|
859
|
+
data: {
|
|
860
|
+
type: "success",
|
|
861
|
+
result: "ok"
|
|
862
|
+
}
|
|
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);
|
|
880
|
+
send({
|
|
881
|
+
type: "end",
|
|
882
|
+
data: {
|
|
883
|
+
type: "success",
|
|
884
|
+
result: "ok"
|
|
829
885
|
}
|
|
830
886
|
});
|
|
831
887
|
} catch (e) {
|
|
832
|
-
logger().error(`
|
|
888
|
+
logger().error(`pipeline:delete-by-path error for ${absolutePath}:`, e);
|
|
833
889
|
send({
|
|
834
890
|
type: "end",
|
|
835
891
|
data: {
|
|
836
892
|
type: "error",
|
|
837
|
-
ipcError: e instanceof Error ? e.message : `Unable to delete
|
|
893
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${absolutePath}`
|
|
838
894
|
}
|
|
839
895
|
});
|
|
840
896
|
}
|
|
@@ -8383,7 +8439,7 @@ var require_error = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
8383
8439
|
})();
|
|
8384
8440
|
}));
|
|
8385
8441
|
//#endregion
|
|
8386
|
-
//#region src/
|
|
8442
|
+
//#region src/server.ts
|
|
8387
8443
|
var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8388
8444
|
const { promisify } = __require("util");
|
|
8389
8445
|
const path$1 = __require("path");
|
|
@@ -8837,44 +8893,6 @@ var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((export
|
|
|
8837
8893
|
stream.pipe(response);
|
|
8838
8894
|
};
|
|
8839
8895
|
})))(), 1);
|
|
8840
|
-
/**
|
|
8841
|
-
* Registers migration handlers for the CLI/Standalone server.
|
|
8842
|
-
* This overrides the default handlers from @pipelab/core-node to include
|
|
8843
|
-
* pipeline and file repo migrations directly in the backend.
|
|
8844
|
-
*/
|
|
8845
|
-
function registerMigrationHandlers(context) {
|
|
8846
|
-
const { handle } = useAPI();
|
|
8847
|
-
const { logger } = useLogger();
|
|
8848
|
-
handle("config:load", async (_, { send, value }) => {
|
|
8849
|
-
const { config: name } = value;
|
|
8850
|
-
logger().info("[CLI Migration] config:load", name);
|
|
8851
|
-
try {
|
|
8852
|
-
const migrator = getMigrator(name);
|
|
8853
|
-
if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All files loaded via config:load MUST have a migration schema.`);
|
|
8854
|
-
send({
|
|
8855
|
-
type: "end",
|
|
8856
|
-
data: {
|
|
8857
|
-
type: "success",
|
|
8858
|
-
result: { result: await (await setupConfigFile(name, {
|
|
8859
|
-
context,
|
|
8860
|
-
migrator
|
|
8861
|
-
})).getConfig() }
|
|
8862
|
-
}
|
|
8863
|
-
});
|
|
8864
|
-
} catch (e) {
|
|
8865
|
-
logger().error(`[CLI Migration] config:load error for ${name}:`, e);
|
|
8866
|
-
send({
|
|
8867
|
-
type: "end",
|
|
8868
|
-
data: {
|
|
8869
|
-
type: "error",
|
|
8870
|
-
ipcError: e instanceof Error ? e.message : `Unable to load config ${name}`
|
|
8871
|
-
}
|
|
8872
|
-
});
|
|
8873
|
-
}
|
|
8874
|
-
});
|
|
8875
|
-
}
|
|
8876
|
-
//#endregion
|
|
8877
|
-
//#region src/server.ts
|
|
8878
8896
|
const sendStartupProgress = (message) => {
|
|
8879
8897
|
console.log(`[Startup Progress] ${message}`);
|
|
8880
8898
|
webSocketServer.broadcast("startup:progress", {
|
|
@@ -8961,7 +8979,6 @@ async function serveCommand(options, version, _dirname) {
|
|
|
8961
8979
|
version,
|
|
8962
8980
|
context
|
|
8963
8981
|
});
|
|
8964
|
-
registerMigrationHandlers(context);
|
|
8965
8982
|
sendStartupReady();
|
|
8966
8983
|
return server;
|
|
8967
8984
|
}
|
|
@@ -9693,8 +9710,8 @@ const builtInPlugins = async (options) => {
|
|
|
9693
9710
|
}
|
|
9694
9711
|
}
|
|
9695
9712
|
try {
|
|
9696
|
-
const {
|
|
9697
|
-
const settingsPlugins = (await (await
|
|
9713
|
+
const { setupSettingsConfigFile } = await import("./config-Bi0ORcTK.mjs");
|
|
9714
|
+
const settingsPlugins = (await (await setupSettingsConfigFile(options.context)).getConfig())?.plugins || [];
|
|
9698
9715
|
for (const plugin of settingsPlugins) if (plugin.name) if (plugin.enabled) {
|
|
9699
9716
|
if (!pluginsToLoad.has(plugin.name)) pluginsToLoad.set(plugin.name, "latest");
|
|
9700
9717
|
} else pluginsToLoad.delete(plugin.name);
|
|
@@ -9793,7 +9810,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
|
|
|
9793
9810
|
} else logger().error(`[Runner] Failed to load or register plugin "${pluginId}"`);
|
|
9794
9811
|
}
|
|
9795
9812
|
logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
|
|
9796
|
-
await (await
|
|
9813
|
+
await (await setupSettingsConfigFile(ctx)).getConfig();
|
|
9797
9814
|
try {
|
|
9798
9815
|
const result = await processGraph({
|
|
9799
9816
|
graph,
|
|
@@ -10301,7 +10318,7 @@ const registerEngineHandlers = (context) => {
|
|
|
10301
10318
|
});
|
|
10302
10319
|
handle("graph:execute", async (event, { send, value }) => {
|
|
10303
10320
|
const { graph, variables, projectName, projectPath, pipelineId } = value;
|
|
10304
|
-
await (await
|
|
10321
|
+
await (await setupSettingsConfigFile(context)).getConfig();
|
|
10305
10322
|
const effectiveProjectName = projectName || "Unnamed Project";
|
|
10306
10323
|
const effectiveProjectPath = projectPath || "";
|
|
10307
10324
|
const effectivePipelineId = pipelineId || "unknown";
|
|
@@ -10854,6 +10871,470 @@ const registerPluginsHandlers = (context) => {
|
|
|
10854
10871
|
});
|
|
10855
10872
|
};
|
|
10856
10873
|
//#endregion
|
|
10874
|
+
//#region src/handlers/migration.ts
|
|
10875
|
+
const registerMigrationHandlers = (context) => {
|
|
10876
|
+
const { handle } = useAPI();
|
|
10877
|
+
const { logger } = useLogger();
|
|
10878
|
+
const getFileMetadata = async (filePath) => {
|
|
10879
|
+
const exists = existsSync(filePath);
|
|
10880
|
+
let mtime = Date.now();
|
|
10881
|
+
let version = null;
|
|
10882
|
+
if (exists) {
|
|
10883
|
+
try {
|
|
10884
|
+
const stat = await fs$1.stat(filePath);
|
|
10885
|
+
mtime = stat.mtime ? stat.mtime.getTime() : Date.now();
|
|
10886
|
+
} catch (e) {
|
|
10887
|
+
mtime = Date.now();
|
|
10888
|
+
}
|
|
10889
|
+
try {
|
|
10890
|
+
const content = await fs$1.readFile(filePath, "utf8");
|
|
10891
|
+
const json = JSON.parse(content);
|
|
10892
|
+
version = json && typeof json.version === "string" ? json.version : null;
|
|
10893
|
+
} catch (e) {}
|
|
10894
|
+
} else mtime = Date.now();
|
|
10895
|
+
return {
|
|
10896
|
+
exists,
|
|
10897
|
+
mtime,
|
|
10898
|
+
version
|
|
10899
|
+
};
|
|
10900
|
+
};
|
|
10901
|
+
const isVersionImportable = (sourceVer, targetVer) => {
|
|
10902
|
+
if (!sourceVer) return false;
|
|
10903
|
+
if (!targetVer) return true;
|
|
10904
|
+
try {
|
|
10905
|
+
const coercedSource = semver.coerce(sourceVer);
|
|
10906
|
+
const coercedTarget = semver.coerce(targetVer);
|
|
10907
|
+
if (!coercedSource) return false;
|
|
10908
|
+
if (!coercedTarget) return true;
|
|
10909
|
+
return semver.lte(coercedSource, coercedTarget);
|
|
10910
|
+
} catch {
|
|
10911
|
+
return sourceVer <= targetVer;
|
|
10912
|
+
}
|
|
10913
|
+
};
|
|
10914
|
+
handle("migration:scan-stable", async (_, { send }) => {
|
|
10915
|
+
logger().info("[Migration] Scanning other channel database...");
|
|
10916
|
+
try {
|
|
10917
|
+
const isStable = context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta");
|
|
10918
|
+
const sourceEnv = isStable ? "beta" : "prod";
|
|
10919
|
+
const sourcePath = getDefaultUserDataPath(sourceEnv);
|
|
10920
|
+
if (sourcePath === context.userDataPath) {
|
|
10921
|
+
logger().info("[Migration] Running in same channel. Disabling migration scan.");
|
|
10922
|
+
return send({
|
|
10923
|
+
type: "end",
|
|
10924
|
+
data: {
|
|
10925
|
+
type: "success",
|
|
10926
|
+
result: {
|
|
10927
|
+
sourceChannel: isStable ? "Beta" : "Stable",
|
|
10928
|
+
targetChannel: isStable ? "Stable" : "Beta",
|
|
10929
|
+
settingsExists: false,
|
|
10930
|
+
settingsVersion: null,
|
|
10931
|
+
settingsVersionTarget: null,
|
|
10932
|
+
settingsMtimeSource: Date.now(),
|
|
10933
|
+
settingsMtimeTarget: Date.now(),
|
|
10934
|
+
settingsImportable: false,
|
|
10935
|
+
connectionsExists: false,
|
|
10936
|
+
connectionsCount: 0,
|
|
10937
|
+
connectionsVersion: null,
|
|
10938
|
+
connectionsVersionTarget: null,
|
|
10939
|
+
connectionsMtimeSource: Date.now(),
|
|
10940
|
+
connectionsMtimeTarget: Date.now(),
|
|
10941
|
+
connectionsImportable: false,
|
|
10942
|
+
projectsExists: false,
|
|
10943
|
+
projectsVersion: null,
|
|
10944
|
+
projectsVersionTarget: null,
|
|
10945
|
+
projectsMtimeSource: Date.now(),
|
|
10946
|
+
projectsMtimeTarget: Date.now(),
|
|
10947
|
+
projectsImportable: false,
|
|
10948
|
+
projects: []
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10951
|
+
});
|
|
10952
|
+
}
|
|
10953
|
+
const sourceContext = new PipelabContext({
|
|
10954
|
+
userDataPath: sourcePath,
|
|
10955
|
+
releaseTag: sourceEnv
|
|
10956
|
+
});
|
|
10957
|
+
const sourceSettingsPath = sourceContext.getSettingsPath();
|
|
10958
|
+
const targetSettingsPath = context.getSettingsPath();
|
|
10959
|
+
const sourceConnectionsPath = sourceContext.getConnectionsPath();
|
|
10960
|
+
const targetConnectionsPath = context.getConnectionsPath();
|
|
10961
|
+
const sourceProjectsPath = sourceContext.getProjectsPath();
|
|
10962
|
+
const targetProjectsPath = context.getProjectsPath();
|
|
10963
|
+
const sourceSettingsMeta = await getFileMetadata(sourceSettingsPath);
|
|
10964
|
+
const targetSettingsMeta = await getFileMetadata(targetSettingsPath);
|
|
10965
|
+
const sourceConnectionsMeta = await getFileMetadata(sourceConnectionsPath);
|
|
10966
|
+
const targetConnectionsMeta = await getFileMetadata(targetConnectionsPath);
|
|
10967
|
+
const sourceProjectsMeta = await getFileMetadata(sourceProjectsPath);
|
|
10968
|
+
const targetProjectsMeta = await getFileMetadata(targetProjectsPath);
|
|
10969
|
+
const settingsExists = sourceSettingsMeta.exists;
|
|
10970
|
+
const settingsVersion = sourceSettingsMeta.version;
|
|
10971
|
+
const settingsMtimeSource = sourceSettingsMeta.mtime;
|
|
10972
|
+
const settingsMtimeTarget = targetSettingsMeta.mtime;
|
|
10973
|
+
const settingsImportable = isVersionImportable(settingsVersion, targetSettingsMeta.version);
|
|
10974
|
+
const connectionsExists = sourceConnectionsMeta.exists;
|
|
10975
|
+
const connectionsVersion = sourceConnectionsMeta.version;
|
|
10976
|
+
const connectionsMtimeSource = sourceConnectionsMeta.mtime;
|
|
10977
|
+
const connectionsMtimeTarget = targetConnectionsMeta.mtime;
|
|
10978
|
+
const connectionsImportable = isVersionImportable(connectionsVersion, targetConnectionsMeta.version);
|
|
10979
|
+
const projectsExists = sourceProjectsMeta.exists;
|
|
10980
|
+
const projectsVersion = sourceProjectsMeta.version;
|
|
10981
|
+
const projectsMtimeSource = sourceProjectsMeta.mtime;
|
|
10982
|
+
const projectsMtimeTarget = targetProjectsMeta.mtime;
|
|
10983
|
+
const projectsImportable = isVersionImportable(projectsVersion, targetProjectsMeta.version);
|
|
10984
|
+
let connectionsCount = 0;
|
|
10985
|
+
try {
|
|
10986
|
+
if (sourceConnectionsMeta.exists) {
|
|
10987
|
+
const connContent = await fs$1.readFile(sourceConnectionsPath, "utf8");
|
|
10988
|
+
const connJson = JSON.parse(connContent);
|
|
10989
|
+
if (connJson && Array.isArray(connJson.connections)) connectionsCount = connJson.connections.length;
|
|
10990
|
+
}
|
|
10991
|
+
} catch (err) {
|
|
10992
|
+
logger().error("[Migration] Error parsing source connections.json:", err);
|
|
10993
|
+
}
|
|
10994
|
+
let sourceProjectsList = [];
|
|
10995
|
+
let sourcePipelinesList = [];
|
|
10996
|
+
try {
|
|
10997
|
+
if (sourceProjectsMeta.exists) {
|
|
10998
|
+
const projContent = await fs$1.readFile(sourceProjectsPath, "utf8");
|
|
10999
|
+
const projJson = JSON.parse(projContent);
|
|
11000
|
+
sourceProjectsList = projJson.projects || [];
|
|
11001
|
+
sourcePipelinesList = projJson.pipelines || [];
|
|
11002
|
+
}
|
|
11003
|
+
} catch (err) {
|
|
11004
|
+
logger().error("[Migration] Error parsing source projects.json:", err);
|
|
11005
|
+
}
|
|
11006
|
+
let targetPipelinesList = [];
|
|
11007
|
+
try {
|
|
11008
|
+
if (targetProjectsMeta.exists) {
|
|
11009
|
+
const targetProjContent = await fs$1.readFile(targetProjectsPath, "utf8");
|
|
11010
|
+
targetPipelinesList = JSON.parse(targetProjContent).pipelines || [];
|
|
11011
|
+
}
|
|
11012
|
+
} catch (err) {}
|
|
11013
|
+
const projectsReport = [];
|
|
11014
|
+
for (const proj of sourceProjectsList) {
|
|
11015
|
+
const projPipelines = sourcePipelinesList.filter((p) => p.project === proj.id);
|
|
11016
|
+
const pipelinesReport = [];
|
|
11017
|
+
for (const pipe of projPipelines) {
|
|
11018
|
+
const targetPipe = targetPipelinesList.find((bp) => bp.id === pipe.id);
|
|
11019
|
+
const existsInBeta = !!targetPipe;
|
|
11020
|
+
let pipeName = pipe.id;
|
|
11021
|
+
let pipeDesc = "";
|
|
11022
|
+
if (pipe.type === "internal") {
|
|
11023
|
+
const sourcePipeFile = sourceContext.getConfigPath(`${pipe.configName}.json`);
|
|
11024
|
+
try {
|
|
11025
|
+
if (existsSync(sourcePipeFile)) {
|
|
11026
|
+
const pipeContent = await fs$1.readFile(sourcePipeFile, "utf8");
|
|
11027
|
+
const pipeJson = JSON.parse(pipeContent);
|
|
11028
|
+
pipeName = pipeJson.name || pipeName;
|
|
11029
|
+
pipeDesc = pipeJson.description || pipeDesc;
|
|
11030
|
+
}
|
|
11031
|
+
} catch (err) {
|
|
11032
|
+
logger().error(`[Migration] Error reading source pipeline file ${sourcePipeFile}:`, err);
|
|
11033
|
+
}
|
|
11034
|
+
} else if (pipe.type === "external") {
|
|
11035
|
+
pipeName = pipe.summary?.name || pipeName;
|
|
11036
|
+
pipeDesc = pipe.summary?.description || pipeDesc;
|
|
11037
|
+
}
|
|
11038
|
+
pipelinesReport.push({
|
|
11039
|
+
id: pipe.id,
|
|
11040
|
+
name: pipeName,
|
|
11041
|
+
description: pipeDesc,
|
|
11042
|
+
type: pipe.type,
|
|
11043
|
+
lastModifiedStable: pipe.type !== "pipelab-cloud" ? pipe.lastModified : void 0,
|
|
11044
|
+
lastModifiedBeta: targetPipe && targetPipe.type !== "pipelab-cloud" ? targetPipe.lastModified : void 0,
|
|
11045
|
+
existsInBeta
|
|
11046
|
+
});
|
|
11047
|
+
}
|
|
11048
|
+
projectsReport.push({
|
|
11049
|
+
id: proj.id,
|
|
11050
|
+
name: proj.name,
|
|
11051
|
+
description: proj.description || "",
|
|
11052
|
+
pipelines: pipelinesReport
|
|
11053
|
+
});
|
|
11054
|
+
}
|
|
11055
|
+
send({
|
|
11056
|
+
type: "end",
|
|
11057
|
+
data: {
|
|
11058
|
+
type: "success",
|
|
11059
|
+
result: {
|
|
11060
|
+
sourceChannel: isStable ? "Beta" : "Stable",
|
|
11061
|
+
targetChannel: isStable ? "Stable" : "Beta",
|
|
11062
|
+
settingsExists,
|
|
11063
|
+
settingsVersion,
|
|
11064
|
+
settingsVersionTarget: targetSettingsMeta.version,
|
|
11065
|
+
settingsMtimeSource,
|
|
11066
|
+
settingsMtimeTarget,
|
|
11067
|
+
settingsImportable,
|
|
11068
|
+
connectionsExists,
|
|
11069
|
+
connectionsCount,
|
|
11070
|
+
connectionsVersion,
|
|
11071
|
+
connectionsVersionTarget: targetConnectionsMeta.version,
|
|
11072
|
+
connectionsMtimeSource,
|
|
11073
|
+
connectionsMtimeTarget,
|
|
11074
|
+
connectionsImportable,
|
|
11075
|
+
projectsExists,
|
|
11076
|
+
projectsVersion,
|
|
11077
|
+
projectsVersionTarget: targetProjectsMeta.version,
|
|
11078
|
+
projectsMtimeSource,
|
|
11079
|
+
projectsMtimeTarget,
|
|
11080
|
+
projectsImportable,
|
|
11081
|
+
projects: projectsReport
|
|
11082
|
+
}
|
|
11083
|
+
}
|
|
11084
|
+
});
|
|
11085
|
+
} catch (e) {
|
|
11086
|
+
logger().error("[Migration] Error scanning other channel folder:", e);
|
|
11087
|
+
send({
|
|
11088
|
+
type: "end",
|
|
11089
|
+
data: {
|
|
11090
|
+
type: "error",
|
|
11091
|
+
ipcError: e instanceof Error ? e.message : "Failed to scan other channel database"
|
|
11092
|
+
}
|
|
11093
|
+
});
|
|
11094
|
+
}
|
|
11095
|
+
});
|
|
11096
|
+
handle("migration:perform", async (_, { send, value }) => {
|
|
11097
|
+
logger().info("[Migration] Performing migration...");
|
|
11098
|
+
try {
|
|
11099
|
+
const { migrateSettings, migrateConnections, selectedProjects, selectedPipelines } = value;
|
|
11100
|
+
const sourceEnv = context.userDataPath.endsWith("app") || !context.userDataPath.includes("app-beta") ? "beta" : "prod";
|
|
11101
|
+
const sourcePath = getDefaultUserDataPath(sourceEnv);
|
|
11102
|
+
const sourceContext = new PipelabContext({
|
|
11103
|
+
userDataPath: sourcePath,
|
|
11104
|
+
releaseTag: sourceEnv
|
|
11105
|
+
});
|
|
11106
|
+
sourceContext.getConfigPath();
|
|
11107
|
+
const targetConfigDir = context.getConfigPath();
|
|
11108
|
+
if (sourcePath === context.userDataPath) throw new Error("Cannot migrate data: Source and target directories are identical.");
|
|
11109
|
+
if (existsSync(targetConfigDir)) {
|
|
11110
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
11111
|
+
const backupDir = context.getConfigPath("backups", `backup_${timestamp}`);
|
|
11112
|
+
await fs$1.mkdir(backupDir, { recursive: true });
|
|
11113
|
+
const entries = await fs$1.readdir(targetConfigDir, { withFileTypes: true });
|
|
11114
|
+
for (const entry of entries) {
|
|
11115
|
+
if (entry.name === "backups") continue;
|
|
11116
|
+
const srcPath = join(targetConfigDir, entry.name);
|
|
11117
|
+
const destPath = join(backupDir, entry.name);
|
|
11118
|
+
try {
|
|
11119
|
+
await fs$1.cp(srcPath, destPath, { recursive: true });
|
|
11120
|
+
} catch (copyErr) {
|
|
11121
|
+
logger().error(`[Migration] Failed to backup ${entry.name}:`, copyErr);
|
|
11122
|
+
}
|
|
11123
|
+
}
|
|
11124
|
+
logger().info(`[Migration] Config backup successfully created at ${backupDir}`);
|
|
11125
|
+
}
|
|
11126
|
+
if (migrateSettings) {
|
|
11127
|
+
const sourceSettingsFile = sourceContext.getSettingsPath();
|
|
11128
|
+
const targetSettingsFile = context.getSettingsPath();
|
|
11129
|
+
const sourceMeta = await getFileMetadata(sourceSettingsFile);
|
|
11130
|
+
const targetMeta = await getFileMetadata(targetSettingsFile);
|
|
11131
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate settings: Source version is newer than current version.");
|
|
11132
|
+
if (existsSync(sourceSettingsFile)) {
|
|
11133
|
+
let stableSettings = {
|
|
11134
|
+
version: "7.0.0",
|
|
11135
|
+
locale: "en-US",
|
|
11136
|
+
theme: "light",
|
|
11137
|
+
autosave: true,
|
|
11138
|
+
agents: [],
|
|
11139
|
+
plugins: [],
|
|
11140
|
+
tours: {
|
|
11141
|
+
dashboard: {
|
|
11142
|
+
step: 0,
|
|
11143
|
+
completed: false
|
|
11144
|
+
},
|
|
11145
|
+
editor: {
|
|
11146
|
+
step: 0,
|
|
11147
|
+
completed: false
|
|
11148
|
+
}
|
|
11149
|
+
}
|
|
11150
|
+
};
|
|
11151
|
+
try {
|
|
11152
|
+
const stableContent = await fs$1.readFile(sourceSettingsFile, "utf8");
|
|
11153
|
+
stableSettings = JSON.parse(stableContent);
|
|
11154
|
+
} catch (e) {
|
|
11155
|
+
logger().error("[Migration] Failed to read source settings.json:", e);
|
|
11156
|
+
}
|
|
11157
|
+
let betaSettings = {
|
|
11158
|
+
version: "7.0.0",
|
|
11159
|
+
locale: "en-US",
|
|
11160
|
+
theme: "light",
|
|
11161
|
+
autosave: true,
|
|
11162
|
+
agents: [],
|
|
11163
|
+
plugins: [],
|
|
11164
|
+
tours: {
|
|
11165
|
+
dashboard: {
|
|
11166
|
+
step: 0,
|
|
11167
|
+
completed: false
|
|
11168
|
+
},
|
|
11169
|
+
editor: {
|
|
11170
|
+
step: 0,
|
|
11171
|
+
completed: false
|
|
11172
|
+
}
|
|
11173
|
+
}
|
|
11174
|
+
};
|
|
11175
|
+
if (existsSync(targetSettingsFile)) try {
|
|
11176
|
+
const betaContent = await fs$1.readFile(targetSettingsFile, "utf8");
|
|
11177
|
+
betaSettings = JSON.parse(betaContent);
|
|
11178
|
+
} catch (e) {}
|
|
11179
|
+
betaSettings.theme = stableSettings.theme ?? betaSettings.theme;
|
|
11180
|
+
betaSettings.locale = stableSettings.locale ?? betaSettings.locale;
|
|
11181
|
+
betaSettings.autosave = stableSettings.autosave ?? betaSettings.autosave;
|
|
11182
|
+
if (stableSettings.tours) {
|
|
11183
|
+
betaSettings.tours = betaSettings.tours || {
|
|
11184
|
+
dashboard: {
|
|
11185
|
+
step: 0,
|
|
11186
|
+
completed: false
|
|
11187
|
+
},
|
|
11188
|
+
editor: {
|
|
11189
|
+
step: 0,
|
|
11190
|
+
completed: false
|
|
11191
|
+
}
|
|
11192
|
+
};
|
|
11193
|
+
if (stableSettings.tours.dashboard) betaSettings.tours.dashboard = stableSettings.tours.dashboard;
|
|
11194
|
+
if (stableSettings.tours.editor) betaSettings.tours.editor = stableSettings.tours.editor;
|
|
11195
|
+
}
|
|
11196
|
+
const betaAgents = betaSettings.agents || [];
|
|
11197
|
+
const stableAgents = stableSettings.agents || [];
|
|
11198
|
+
for (const sAgent of stableAgents) {
|
|
11199
|
+
const existingIdx = betaAgents.findIndex((a) => a.id === sAgent.id);
|
|
11200
|
+
if (existingIdx >= 0) betaAgents[existingIdx] = { ...sAgent };
|
|
11201
|
+
else betaAgents.push({ ...sAgent });
|
|
11202
|
+
}
|
|
11203
|
+
betaSettings.agents = betaAgents;
|
|
11204
|
+
const betaPlugins = betaSettings.plugins || [];
|
|
11205
|
+
const stablePlugins = stableSettings.plugins || [];
|
|
11206
|
+
for (const sPlugin of stablePlugins) {
|
|
11207
|
+
const existingIdx = betaPlugins.findIndex((p) => p.name === sPlugin.name);
|
|
11208
|
+
if (existingIdx >= 0) betaPlugins[existingIdx] = { ...sPlugin };
|
|
11209
|
+
else betaPlugins.push({ ...sPlugin });
|
|
11210
|
+
}
|
|
11211
|
+
betaSettings.plugins = betaPlugins;
|
|
11212
|
+
await fs$1.mkdir(dirname(targetSettingsFile), { recursive: true });
|
|
11213
|
+
await fs$1.writeFile(targetSettingsFile, JSON.stringify(betaSettings, null, 2));
|
|
11214
|
+
logger().info("[Migration] Settings merged successfully");
|
|
11215
|
+
}
|
|
11216
|
+
}
|
|
11217
|
+
if (migrateConnections) {
|
|
11218
|
+
const sourceConnectionsFile = sourceContext.getConnectionsPath();
|
|
11219
|
+
const targetConnectionsFile = context.getConnectionsPath();
|
|
11220
|
+
const sourceMeta = await getFileMetadata(sourceConnectionsFile);
|
|
11221
|
+
const targetMeta = await getFileMetadata(targetConnectionsFile);
|
|
11222
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate connections: Source version is newer than current version.");
|
|
11223
|
+
if (existsSync(sourceConnectionsFile)) {
|
|
11224
|
+
let stableConnections = {
|
|
11225
|
+
version: "1.0.0",
|
|
11226
|
+
connections: []
|
|
11227
|
+
};
|
|
11228
|
+
try {
|
|
11229
|
+
const stableContent = await fs$1.readFile(sourceConnectionsFile, "utf8");
|
|
11230
|
+
stableConnections = JSON.parse(stableContent);
|
|
11231
|
+
} catch (e) {
|
|
11232
|
+
logger().error("[Migration] Failed to read source connections.json:", e);
|
|
11233
|
+
}
|
|
11234
|
+
let betaConnections = {
|
|
11235
|
+
version: "1.0.0",
|
|
11236
|
+
connections: []
|
|
11237
|
+
};
|
|
11238
|
+
if (existsSync(targetConnectionsFile)) try {
|
|
11239
|
+
const betaContent = await fs$1.readFile(targetConnectionsFile, "utf8");
|
|
11240
|
+
betaConnections = JSON.parse(betaContent);
|
|
11241
|
+
} catch (e) {}
|
|
11242
|
+
betaConnections.connections = betaConnections.connections || [];
|
|
11243
|
+
stableConnections.connections = stableConnections.connections || [];
|
|
11244
|
+
for (const sConn of stableConnections.connections) {
|
|
11245
|
+
const existingIdx = betaConnections.connections.findIndex((c) => c.id === sConn.id);
|
|
11246
|
+
if (existingIdx >= 0) betaConnections.connections[existingIdx] = { ...sConn };
|
|
11247
|
+
else betaConnections.connections.push({ ...sConn });
|
|
11248
|
+
}
|
|
11249
|
+
await fs$1.mkdir(dirname(targetConnectionsFile), { recursive: true });
|
|
11250
|
+
await fs$1.writeFile(targetConnectionsFile, JSON.stringify(betaConnections, null, 2));
|
|
11251
|
+
logger().info("[Migration] Connections merged successfully");
|
|
11252
|
+
}
|
|
11253
|
+
}
|
|
11254
|
+
if (selectedPipelines.length > 0 || selectedProjects.length > 0) {
|
|
11255
|
+
const sourceProjectsFile = sourceContext.getProjectsPath();
|
|
11256
|
+
const targetProjectsFile = context.getProjectsPath();
|
|
11257
|
+
const sourceMeta = await getFileMetadata(sourceProjectsFile);
|
|
11258
|
+
const targetMeta = await getFileMetadata(targetProjectsFile);
|
|
11259
|
+
if (!isVersionImportable(sourceMeta.version, targetMeta.version)) throw new Error("Cannot migrate projects: Source version is newer than current version.");
|
|
11260
|
+
let stableFileRepo = {
|
|
11261
|
+
version: "3.0.0",
|
|
11262
|
+
projects: [],
|
|
11263
|
+
pipelines: []
|
|
11264
|
+
};
|
|
11265
|
+
if (existsSync(sourceProjectsFile)) {
|
|
11266
|
+
const stableContent = await fs$1.readFile(sourceProjectsFile, "utf8");
|
|
11267
|
+
stableFileRepo = JSON.parse(stableContent);
|
|
11268
|
+
}
|
|
11269
|
+
let betaFileRepo = {
|
|
11270
|
+
version: "3.0.0",
|
|
11271
|
+
projects: [],
|
|
11272
|
+
pipelines: []
|
|
11273
|
+
};
|
|
11274
|
+
if (existsSync(targetProjectsFile)) try {
|
|
11275
|
+
const betaContent = await fs$1.readFile(targetProjectsFile, "utf8");
|
|
11276
|
+
betaFileRepo = JSON.parse(betaContent);
|
|
11277
|
+
} catch (readErr) {
|
|
11278
|
+
logger().warn("[Migration] Failed to parse existing target projects.json, starting fresh:", readErr);
|
|
11279
|
+
}
|
|
11280
|
+
betaFileRepo.projects = betaFileRepo.projects || [];
|
|
11281
|
+
betaFileRepo.pipelines = betaFileRepo.pipelines || [];
|
|
11282
|
+
for (const projId of selectedProjects) {
|
|
11283
|
+
const stableProj = stableFileRepo.projects.find((p) => p.id === projId);
|
|
11284
|
+
if (stableProj) {
|
|
11285
|
+
const existingProjIdx = betaFileRepo.projects.findIndex((p) => p.id === projId);
|
|
11286
|
+
if (existingProjIdx >= 0) betaFileRepo.projects[existingProjIdx] = { ...stableProj };
|
|
11287
|
+
else betaFileRepo.projects.push({ ...stableProj });
|
|
11288
|
+
}
|
|
11289
|
+
}
|
|
11290
|
+
for (const pipeId of selectedPipelines) {
|
|
11291
|
+
const stablePipe = (stableFileRepo.pipelines || []).find((p) => p.id === pipeId);
|
|
11292
|
+
if (stablePipe) {
|
|
11293
|
+
const parentProjId = stablePipe.project;
|
|
11294
|
+
if (!betaFileRepo.projects.some((p) => p.id === parentProjId)) {
|
|
11295
|
+
const stableProj = stableFileRepo.projects.find((p) => p.id === parentProjId);
|
|
11296
|
+
if (stableProj) betaFileRepo.projects.push({ ...stableProj });
|
|
11297
|
+
}
|
|
11298
|
+
if (stablePipe.type === "internal") {
|
|
11299
|
+
const stablePipeFile = sourceContext.getConfigPath(`${stablePipe.configName}.json`);
|
|
11300
|
+
const betaPipeFile = context.getConfigPath(`${stablePipe.configName}.json`);
|
|
11301
|
+
if (existsSync(stablePipeFile)) {
|
|
11302
|
+
await fs$1.mkdir(dirname(betaPipeFile), { recursive: true });
|
|
11303
|
+
await fs$1.copyFile(stablePipeFile, betaPipeFile);
|
|
11304
|
+
logger().info(`[Migration] Copied pipeline file: ${stablePipe.configName}`);
|
|
11305
|
+
}
|
|
11306
|
+
}
|
|
11307
|
+
const updatedPipe = { ...stablePipe };
|
|
11308
|
+
if (updatedPipe.type !== "pipelab-cloud") updatedPipe.lastModified = (/* @__PURE__ */ new Date()).toISOString();
|
|
11309
|
+
const existingPipeIdx = betaFileRepo.pipelines.findIndex((p) => p.id === pipeId);
|
|
11310
|
+
if (existingPipeIdx >= 0) betaFileRepo.pipelines[existingPipeIdx] = updatedPipe;
|
|
11311
|
+
else betaFileRepo.pipelines.push(updatedPipe);
|
|
11312
|
+
}
|
|
11313
|
+
}
|
|
11314
|
+
await fs$1.mkdir(dirname(targetProjectsFile), { recursive: true });
|
|
11315
|
+
await fs$1.writeFile(targetProjectsFile, JSON.stringify(betaFileRepo, null, 2));
|
|
11316
|
+
logger().info("[Migration] Merged and saved projects.json successfully");
|
|
11317
|
+
}
|
|
11318
|
+
send({
|
|
11319
|
+
type: "end",
|
|
11320
|
+
data: {
|
|
11321
|
+
type: "success",
|
|
11322
|
+
result: { result: "ok" }
|
|
11323
|
+
}
|
|
11324
|
+
});
|
|
11325
|
+
} catch (e) {
|
|
11326
|
+
logger().error("[Migration] Error performing migration:", e);
|
|
11327
|
+
send({
|
|
11328
|
+
type: "end",
|
|
11329
|
+
data: {
|
|
11330
|
+
type: "error",
|
|
11331
|
+
ipcError: e instanceof Error ? e.message : "Failed to migrate selected data"
|
|
11332
|
+
}
|
|
11333
|
+
});
|
|
11334
|
+
}
|
|
11335
|
+
});
|
|
11336
|
+
};
|
|
11337
|
+
//#endregion
|
|
10857
11338
|
//#region src/handlers/index.ts
|
|
10858
11339
|
const registerAllHandlers = async (options) => {
|
|
10859
11340
|
const context = options.context;
|
|
@@ -10866,6 +11347,7 @@ const registerAllHandlers = async (options) => {
|
|
|
10866
11347
|
registerAuthHandlers(context);
|
|
10867
11348
|
registerSystemHandlers(options);
|
|
10868
11349
|
registerPluginsHandlers(context);
|
|
11350
|
+
registerMigrationHandlers(context);
|
|
10869
11351
|
const { registerPlugins } = usePlugins();
|
|
10870
11352
|
builtInPlugins({ context });
|
|
10871
11353
|
};
|
|
@@ -53138,7 +53620,6 @@ async function runPipelineCommand(file, options, version) {
|
|
|
53138
53620
|
version,
|
|
53139
53621
|
context
|
|
53140
53622
|
});
|
|
53141
|
-
registerMigrationHandlers(context);
|
|
53142
53623
|
const cachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
|
|
53143
53624
|
await mkdir(cachePath, { recursive: true });
|
|
53144
53625
|
const abortController = new AbortController();
|
|
@@ -53311,6 +53792,6 @@ async function fetchLatestDesktopRelease(options = {}) {
|
|
|
53311
53792
|
return latest;
|
|
53312
53793
|
}
|
|
53313
53794
|
//#endregion
|
|
53314
|
-
export { BuildHistoryStorage, CacheFolder, PipelabContext, WebSocketServer, builtInPlugins,
|
|
53795
|
+
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 };
|
|
53315
53796
|
|
|
53316
53797
|
//# sourceMappingURL=index.mjs.map
|