@pipelab/core-node 1.0.0-beta.17 → 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/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, { createWriteStream, existsSync, readFileSync, readdirSync, statSync } from "node: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, configRegistry, isRequired, isSupabaseAvailable, isWebSocketRequestMessage, normalizePipelineConfig, processGraph, savedFileMigrator, supabase, transformUrl, useLogger, usePlugins } from "@pipelab/shared";
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";
@@ -80,6 +77,11 @@ function findProjectRoot(startDir) {
80
77
  return null;
81
78
  }
82
79
  const projectRoot = findProjectRoot(_dirname);
80
+ const CacheFolder = {
81
+ Actions: "actions",
82
+ Pipelines: "pipelines",
83
+ Pacote: "pacote"
84
+ };
83
85
  var PipelabContext = class {
84
86
  userDataPath;
85
87
  releaseTag;
@@ -96,20 +98,50 @@ var PipelabContext = class {
96
98
  getConfigPath(...subpaths) {
97
99
  return join(this.userDataPath, "config", ...subpaths);
98
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
+ }
99
126
  getTempPath(...subpaths) {
100
- return join(this.userDataPath, "temp", ...subpaths);
127
+ return join(this.getSettings()?.tempFolder || join(this.userDataPath, "temp"), ...subpaths);
101
128
  }
102
129
  async createTempFolder(prefix = "pipelab-") {
103
130
  const baseDir = this.getTempPath();
104
131
  await mkdir(baseDir, { recursive: true });
105
132
  return await mkdtemp(join(await realpath(baseDir), prefix));
106
133
  }
107
- getCachePath(...subpaths) {
108
- return join(this.userDataPath, "cache", ...subpaths);
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);
109
138
  }
110
139
  getPnpmPath(...subpaths) {
111
140
  return join(this.userDataPath, "pnpm", ...subpaths);
112
141
  }
142
+ getBuildHistoryPath(...subpaths) {
143
+ return join(this.userDataPath, "build-history", ...subpaths);
144
+ }
113
145
  getNodePath(version = DEFAULT_NODE_VERSION) {
114
146
  const isWindows = process.platform === "win32";
115
147
  return this.getThirdPartyPath("node", version, isWindows ? "node.exe" : "bin/node");
@@ -502,287 +534,207 @@ const registerFsHandlers = (_context) => {
502
534
  });
503
535
  };
504
536
  //#endregion
505
- //#region src/utils/fs-extras.ts
506
- /**
507
- * Ensures a directory exists and a file is created with default content if missing.
508
- */
509
- const ensure = async (filesPath, defaultContent = "{}") => {
510
- await mkdir(dirname(filesPath), { recursive: true });
511
- try {
512
- if ((await stat(filesPath)).size === 0) await writeFile(filesPath, defaultContent);
513
- } catch {
514
- await writeFile(filesPath, defaultContent);
515
- }
516
- };
517
- /**
518
- * Extracts a .tar.gz archive.
519
- */
520
- async function extractTarGz(archivePath, destinationDir) {
521
- await mkdir(destinationDir, { recursive: true });
522
- await tar.x({
523
- file: archivePath,
524
- cwd: destinationDir
525
- });
526
- }
527
- /**
528
- * Extracts a .zip archive.
529
- */
530
- async function extractZip(archivePath, destinationDir) {
531
- await mkdir(destinationDir, { recursive: true });
532
- return new Promise((resolve, reject) => {
533
- yauzl.open(archivePath, { lazyEntries: true }, (err, zipfile) => {
534
- if (err || !zipfile) return reject(err || /* @__PURE__ */ new Error("Could not open zip file"));
535
- zipfile.on("error", reject);
536
- zipfile.readEntry();
537
- zipfile.on("entry", (entry) => {
538
- const entryPath = join(destinationDir, entry.fileName);
539
- if (/\/$/.test(entry.fileName)) mkdir(entryPath, { recursive: true }).then(() => zipfile.readEntry()).catch(reject);
540
- else mkdir(dirname(entryPath), { recursive: true }).then(() => {
541
- zipfile.openReadStream(entry, (err, readStream) => {
542
- if (err || !readStream) return reject(err || /* @__PURE__ */ new Error("Could not open read stream"));
543
- readStream.on("error", reject);
544
- const writeStream = createWriteStream(entryPath);
545
- writeStream.on("error", reject);
546
- writeStream.on("close", () => zipfile.readEntry());
547
- readStream.pipe(writeStream);
548
- });
549
- }).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
+ }
550
550
  });
551
- zipfile.on("end", () => resolve());
552
- });
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
+ }
553
561
  });
554
- }
555
- /**
556
- * Zips a folder.
557
- */
558
- const zipFolder = async (from, to, log = console.log) => {
559
- const output = createWriteStream(to);
560
- const archive = archiver("zip", { zlib: { level: 9 } });
561
- return new Promise((resolve, reject) => {
562
- output.on("close", () => {
563
- log(archive.pointer() + " total bytes");
564
- resolve(to);
565
- });
566
- archive.on("error", reject);
567
- archive.pipe(output);
568
- archive.directory(from, false);
569
- archive.finalize();
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
+ }
570
585
  });
571
- };
572
- const downloadFile = async (url, localPath, hooks, abortSignal) => {
573
- const response = await fetch(url, { signal: abortSignal });
574
- if (!response.ok) throw new Error(`Failed to fetch file: ${response.statusText}`);
575
- const contentLength = response.headers.get("content-length");
576
- if (!contentLength) throw new Error("Content-Length header is missing");
577
- const totalSize = parseInt(contentLength, 10);
578
- let downloadedSize = 0;
579
- const fileStream = createWriteStream(localPath);
580
- const progressStream = new TransformStream({ transform(chunk, controller) {
581
- downloadedSize += chunk.length;
582
- const progress = downloadedSize / totalSize * 100;
583
- hooks?.onProgress?.({
584
- progress,
585
- downloadedSize
586
- });
587
- controller.enqueue(chunk);
588
- } });
589
- const readable = response.body?.pipeThrough(progressStream);
590
- if (!readable) throw new Error("Failed to create a readable stream");
591
- await pipeline(readable, fileStream);
592
- };
593
- const runWithLiveLogs = async (command, args, execaOptions, log, hooks, abortSignal) => {
594
- const subprocess = execa(command, args, {
595
- ...execaOptions,
596
- stdout: "pipe",
597
- stderr: "pipe",
598
- stdin: "pipe",
599
- env: {
600
- ...process.env,
601
- ...execaOptions.env,
602
- TERM: "xterm-256color",
603
- FORCE_STDERR_LOGGING: "1"
604
- },
605
- 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
+ }
606
613
  });
607
- hooks?.onCreated?.(subprocess);
608
- subprocess.stdout?.on("data", (data) => {
609
- hooks?.onStdout?.(data.toString(), subprocess);
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
+ }
610
634
  });
611
- subprocess.stderr?.on("data", (data) => {
612
- hooks?.onStderr?.(data.toString(), subprocess);
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
+ }
613
658
  });
614
- try {
615
- const { exitCode } = await subprocess;
616
- hooks?.onExit?.(exitCode ?? 0);
617
- } catch (error) {
618
- const code = error.exitCode ?? 1;
619
- hooks?.onExit?.(code);
620
- throw new Error(`Command failed with exit code ${code}: ${error.message}`);
621
- }
622
- };
623
- /**
624
- * Calculates the total size of a directory recursively.
625
- */
626
- async function getFolderSize(dirPath) {
627
- try {
628
- const ArrayOfPromises = (await readdir(dirPath, { withFileTypes: true })).map(async (file) => {
629
- const path = join(dirPath, file.name);
630
- if (file.isDirectory()) try {
631
- return await getFolderSize(path);
632
- } catch {
633
- return 0;
634
- }
635
- try {
636
- const { size } = await stat(path);
637
- return size;
638
- } catch {
639
- return 0;
640
- }
641
- });
642
- return (await Promise.all(ArrayOfPromises)).reduce((acc, size) => acc + size, 0);
643
- } catch {
644
- return 0;
645
- }
646
- }
647
- //#endregion
648
- //#region src/config.ts
649
- const getMigrator = (name) => {
650
- return configRegistry[name] || configRegistry["pipeline"];
651
- };
652
- const setupConfigFile = async (name, options) => {
653
- const ctx = options.context;
654
- const migrator = options.migrator || getMigrator(name);
655
- if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All managed files must have a migration schema.`);
656
- const filesPath = path.isAbsolute(name) ? name : ctx.getConfigPath(`${name}.json`);
657
- await ensure(filesPath, JSON.stringify(migrator.defaultValue));
658
- return {
659
- setConfig: async (config) => {
660
- const { logger } = useLogger();
661
- try {
662
- await fs$1.writeFile(filesPath, JSON.stringify(config));
663
- return true;
664
- } catch (e) {
665
- logger().error(`Error saving config ${name}:`, e);
666
- return false;
667
- }
668
- },
669
- getConfig: async () => {
670
- const { logger } = useLogger();
671
- let content = void 0;
672
- let originalJson = void 0;
673
- let parseFailed = false;
674
- try {
675
- content = await fs$1.readFile(filesPath, "utf8");
676
- if (content !== void 0) originalJson = JSON.parse(content);
677
- } catch (e) {
678
- logger().error(`Error reading or parsing config ${name}:`, e);
679
- parseFailed = true;
680
- }
681
- let json = void 0;
682
- let migrationFailed = false;
683
- try {
684
- if (!parseFailed) json = await migrator.migrate(originalJson, {
685
- debug: false,
686
- onStep: async (state, version) => {
687
- const parsedPath = path.parse(filesPath);
688
- const versionedPath = path.join(parsedPath.dir, `${parsedPath.name}.v${version}.json`);
689
- try {
690
- await fs$1.writeFile(versionedPath, JSON.stringify(state));
691
- logger().info(`Intermediate backup created for ${name} at ${versionedPath}`);
692
- } catch (e) {
693
- logger().error(`Failed to create intermediate backup for ${name} at v${version}:`, e);
694
- }
695
- }
696
- });
697
- else json = migrator.defaultValue;
698
- } catch (e) {
699
- logger().error(`Error migrating config ${name}:`, e);
700
- migrationFailed = true;
701
- json = migrator.defaultValue;
702
- }
703
- let normalized = false;
704
- if (name.startsWith("pipeline-") || path.isAbsolute(name) || name.endsWith(".json") || name === "pipeline") normalized = normalizePipelineConfig(json);
705
- if (originalJson?.version !== json?.version || normalized || content === void 0 || parseFailed || migrationFailed) {
706
- if (parseFailed || migrationFailed) try {
707
- const parsedPath = path.parse(filesPath);
708
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
709
- const corruptedPath = path.join(parsedPath.dir, `${parsedPath.name}.corrupted.${timestamp}.json`);
710
- const backupContent = parseFailed ? content || "" : JSON.stringify(originalJson, null, 2);
711
- await fs$1.writeFile(corruptedPath, backupContent);
712
- logger().info(`Corrupted config file preserved at ${corruptedPath}`);
713
- } catch (e) {
714
- 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"
715
674
  }
716
- try {
717
- await fs$1.writeFile(filesPath, JSON.stringify(json));
718
- } catch (e) {
719
- logger().error(`Error saving migrated config ${name}:`, e);
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"
720
683
  }
721
- }
722
- return json;
684
+ });
723
685
  }
724
- };
725
- };
726
- //#endregion
727
- //#region src/handlers/config.ts
728
- const registerConfigHandlers = (context) => {
729
- const { handle } = useAPI();
730
- const { logger } = useLogger();
731
- handle("config:load", async (_, { send, value }) => {
732
- const { config: name } = value;
733
- logger().info("config:load", name);
686
+ });
687
+ handle("projects:load", async (_, { send }) => {
688
+ logger().info("projects:load");
734
689
  try {
735
690
  send({
736
691
  type: "end",
737
692
  data: {
738
693
  type: "success",
739
- result: { result: await (await setupConfigFile(name, { context })).getConfig() }
694
+ result: await (await setupProjectsConfigFile(context)).getConfig()
740
695
  }
741
696
  });
742
697
  } catch (e) {
743
- logger().error(`config:load error for ${name}:`, e);
698
+ logger().error("projects:load error:", e);
744
699
  send({
745
700
  type: "end",
746
701
  data: {
747
702
  type: "error",
748
- ipcError: e instanceof Error ? e.message : `Unable to load config ${name}`
703
+ ipcError: e instanceof Error ? e.message : "Unable to load projects"
749
704
  }
750
705
  });
751
706
  }
752
707
  });
753
- handle("config:save", async (_, { send, value }) => {
754
- const { data, config: name } = value;
708
+ handle("projects:save", async (_, { send, value }) => {
709
+ const { data } = value;
755
710
  try {
756
- const manager = await setupConfigFile(name, { context });
711
+ const manager = await setupProjectsConfigFile(context);
757
712
  const json = typeof data === "string" ? JSON.parse(data) : data;
758
713
  await manager.setConfig(json);
759
714
  send({
760
715
  type: "end",
761
716
  data: {
762
717
  type: "success",
763
- result: { result: "ok" }
718
+ result: "ok"
764
719
  }
765
720
  });
766
721
  } catch (e) {
767
- logger().error(`config:save error for ${name}:`, e);
722
+ logger().error("projects:save error:", e);
768
723
  send({
769
724
  type: "end",
770
725
  data: {
771
726
  type: "error",
772
- ipcError: e instanceof Error ? e.message : `Unable to save config ${name}`
727
+ ipcError: e instanceof Error ? e.message : "Unable to save projects"
773
728
  }
774
729
  });
775
730
  }
776
731
  });
777
- handle("config:reset", async (event, { value, send }) => {
778
- const { config: name, key } = value;
779
- logger().info("config:reset", name, key);
732
+ handle("projects:reset", async (_, { send, value }) => {
733
+ const { key } = value;
780
734
  try {
781
- const manager = await setupConfigFile(name, { context });
735
+ const manager = await setupProjectsConfigFile(context);
782
736
  const currentConfig = await manager.getConfig();
783
- const migrator = getMigrator(name);
784
- if (!migrator) throw new Error(`No migrator found for configuration: ${name}`);
785
- const defaultValue = migrator.defaultValue[key];
737
+ const defaultValue = fileRepoMigrations.defaultValue[key];
786
738
  await manager.setConfig({
787
739
  ...currentConfig ? currentConfig : {},
788
740
  [key]: defaultValue
@@ -791,50 +743,154 @@ const registerConfigHandlers = (context) => {
791
743
  type: "end",
792
744
  data: {
793
745
  type: "success",
794
- result: { result: "ok" }
746
+ result: "ok"
795
747
  }
796
748
  });
797
749
  } catch (e) {
798
- logger().error(`config:reset error for ${name}:`, e);
750
+ logger().error("projects:reset error:", e);
799
751
  send({
800
752
  type: "end",
801
753
  data: {
802
754
  type: "error",
803
- ipcError: e instanceof Error ? e.message : `Unable to reset config ${name}`
755
+ ipcError: e instanceof Error ? e.message : "Unable to reset projects"
804
756
  }
805
757
  });
806
758
  }
807
759
  });
808
- handle("config:delete", async (_, { send, value }) => {
809
- const { config: name } = value;
810
- logger().info("config:delete", name);
760
+ handle("pipeline:load-by-name", async (_, { send, value }) => {
761
+ const { name } = value;
762
+ logger().info("pipeline:load-by-name", name);
811
763
  try {
812
- const filesPath = path.isAbsolute(name) ? name : context.getConfigPath(`${name}.json`);
813
- await fs$1.rm(filesPath, { force: true });
814
- const parsedPath = path.parse(filesPath);
815
- const dirEntries = await fs$1.readdir(parsedPath.dir).catch(() => []);
816
- const prefix = `${parsedPath.name}.v`;
817
- const suffix = `.json`;
818
- for (const entry of dirEntries) if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
819
- const backupPath = path.join(parsedPath.dir, entry);
820
- await fs$1.rm(backupPath, { force: true }).catch((err) => {
821
- logger().error(`Failed to delete backup ${backupPath}:`, err);
822
- });
823
- }
824
764
  send({
825
765
  type: "end",
826
766
  data: {
827
767
  type: "success",
828
- result: { result: "ok" }
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(`config:delete error for ${name}:`, e);
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 config ${name}`
893
+ ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${absolutePath}`
838
894
  }
839
895
  });
840
896
  }
@@ -848,11 +904,10 @@ var BuildHistoryStorage = class {
848
904
  this.context = context;
849
905
  }
850
906
  getStoragePath() {
851
- return join(this.context.userDataPath, "build-history");
907
+ return this.context.getConfigPath("pipelines");
852
908
  }
853
909
  getPipelinePath(pipelineId) {
854
- const sanitizedId = pipelineId.replace(/[/\:*?"<>|]/g, "_").replace(/__/g, "_").replace(/^_+|_+$/g, "");
855
- return join(this.getStoragePath(), `pipeline-${sanitizedId}.json`);
910
+ return join(this.getStoragePath(), `${pipelineId}.history.json`);
856
911
  }
857
912
  async ensureStoragePath() {
858
913
  try {
@@ -879,35 +934,6 @@ var BuildHistoryStorage = class {
879
934
  throw new Error(`Failed to save pipeline history: ${error}`);
880
935
  }
881
936
  }
882
- async applyRetentionPolicy() {
883
- try {
884
- this.logger.logger().info("Applying build history retention policy...");
885
- const policy = (await (await setupConfigFile("settings", { context: this.context })).getConfig())?.buildHistory?.retentionPolicy;
886
- if (!policy || !policy.enabled) {
887
- this.logger.logger().info("Retention policy is disabled. Skipping.");
888
- return;
889
- }
890
- const { maxAge, maxEntries } = policy;
891
- const pipelineFiles = await this.getAllPipelineFiles();
892
- for (const file of pipelineFiles) {
893
- const pipelineId = file.replace("pipeline-", "").replace(".json", "");
894
- let entries = await this.loadPipelineHistory(pipelineId);
895
- const originalCount = entries.length;
896
- if (maxAge > 0) {
897
- const minDate = Date.now() - maxAge * 24 * 60 * 60 * 1e3;
898
- entries = entries.filter((entry) => entry.createdAt >= minDate);
899
- }
900
- if (maxEntries > 0 && entries.length > maxEntries) entries = entries.sort((a, b) => b.createdAt - a.createdAt).slice(0, maxEntries);
901
- if (entries.length < originalCount) {
902
- this.logger.logger().info(`[${pipelineId}] Pruned ${originalCount - entries.length} history entries.`);
903
- await this.savePipelineHistory(pipelineId, entries);
904
- }
905
- }
906
- this.logger.logger().info("Retention policy applied successfully.");
907
- } catch (error) {
908
- this.logger.logger().error("Failed to apply retention policy:", error);
909
- }
910
- }
911
937
  async save(entry) {
912
938
  try {
913
939
  const entries = await this.loadPipelineHistory(entry.pipelineId);
@@ -916,9 +942,6 @@ var BuildHistoryStorage = class {
916
942
  else entries.push(entry);
917
943
  await this.savePipelineHistory(entry.pipelineId, entries);
918
944
  this.logger.logger().info(`Saved build history entry: ${entry.id} for pipeline: ${entry.pipelineId}`);
919
- this.applyRetentionPolicy().catch((err) => {
920
- this.logger.logger().error("Failed to apply retention policy after save:", err);
921
- });
922
945
  } catch (error) {
923
946
  this.logger.logger().error("Failed to save build history entry:", error);
924
947
  throw new Error(`Failed to save build history entry: ${error}`);
@@ -929,7 +952,8 @@ var BuildHistoryStorage = class {
929
952
  if (pipelineId) return (await this.loadPipelineHistory(pipelineId)).find((e) => e.id === id);
930
953
  const files = await this.getAllPipelineFiles();
931
954
  for (const file of files) {
932
- const pId = file.replace("pipeline-", "").replace(".json", "");
955
+ const pId = this.parsePipelineIdFromFilename(file);
956
+ if (!pId) continue;
933
957
  const entry = (await this.loadPipelineHistory(pId)).find((e) => e.id === id);
934
958
  if (entry) return entry;
935
959
  }
@@ -944,7 +968,8 @@ var BuildHistoryStorage = class {
944
968
  const files = await this.getAllPipelineFiles();
945
969
  const allEntries = [];
946
970
  for (const file of files) {
947
- const pipelineId = file.replace("pipeline-", "").replace(".json", "");
971
+ const pipelineId = this.parsePipelineIdFromFilename(file);
972
+ if (!pipelineId) continue;
948
973
  const entries = await this.loadPipelineHistory(pipelineId);
949
974
  allEntries.push(...entries);
950
975
  }
@@ -979,7 +1004,8 @@ var BuildHistoryStorage = class {
979
1004
  } else {
980
1005
  const files = await this.getAllPipelineFiles();
981
1006
  for (const file of files) {
982
- const pId = file.replace("pipeline-", "").replace(".json", "");
1007
+ const pId = this.parsePipelineIdFromFilename(file);
1008
+ if (!pId) continue;
983
1009
  const entries = await this.loadPipelineHistory(pId);
984
1010
  const entryIndex = entries.findIndex((e) => e.id === id);
985
1011
  if (entryIndex >= 0) {
@@ -1013,7 +1039,8 @@ var BuildHistoryStorage = class {
1013
1039
  } else {
1014
1040
  const files = await this.getAllPipelineFiles();
1015
1041
  for (const file of files) {
1016
- const pId = file.replace("pipeline-", "").replace(".json", "");
1042
+ const pId = this.parsePipelineIdFromFilename(file);
1043
+ if (!pId) continue;
1017
1044
  const entries = await this.loadPipelineHistory(pId);
1018
1045
  const entryIndex = entries.findIndex((e) => e.id === id);
1019
1046
  if (entryIndex >= 0) {
@@ -1034,8 +1061,20 @@ var BuildHistoryStorage = class {
1034
1061
  try {
1035
1062
  await this.ensureStoragePath();
1036
1063
  const files = await this.getAllPipelineFiles();
1037
- await Promise.all(files.map((file) => unlink(join(this.getStoragePath(), file))));
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
+ }
1038
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(() => {});
1039
1078
  } catch (error) {
1040
1079
  this.logger.logger().error("Failed to clear build history:", error);
1041
1080
  throw new Error(`Failed to clear build history: ${error}`);
@@ -1043,8 +1082,16 @@ var BuildHistoryStorage = class {
1043
1082
  }
1044
1083
  async clearByPipeline(pipelineId) {
1045
1084
  try {
1046
- await unlink(this.getPipelinePath(pipelineId));
1085
+ const pipelinePath = this.getPipelinePath(pipelineId);
1086
+ const entries = await this.loadPipelineHistory(pipelineId);
1087
+ await unlink(pipelinePath);
1047
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(() => {});
1048
1095
  } catch (error) {
1049
1096
  if (error.code === "ENOENT") {
1050
1097
  this.logger.logger().warn(`No history file found for pipeline "${pipelineId}". Nothing to clear.`);
@@ -1069,21 +1116,11 @@ var BuildHistoryStorage = class {
1069
1116
  size
1070
1117
  });
1071
1118
  }
1072
- const policy = (await (await setupConfigFile("settings", { context: this.context })).getConfig())?.buildHistory?.retentionPolicy || {
1073
- enabled: false,
1074
- maxEntries: 50,
1075
- maxAge: 30
1076
- };
1077
1119
  if (allEntries.length === 0) return {
1078
1120
  totalEntries: 0,
1079
1121
  totalSize: 0,
1080
1122
  numberOfPipelines: files.length,
1081
1123
  userDataPath: this.context.userDataPath,
1082
- retentionPolicy: {
1083
- enabled: policy.enabled,
1084
- maxEntries: policy.maxEntries,
1085
- maxAge: policy.maxAge
1086
- },
1087
1124
  disk: {
1088
1125
  total: diskSpace.size,
1089
1126
  free: diskSpace.free,
@@ -1108,11 +1145,6 @@ var BuildHistoryStorage = class {
1108
1145
  newestEntry: sortedEntries[sortedEntries.length - 1]?.createdAt,
1109
1146
  numberOfPipelines: files.length,
1110
1147
  userDataPath: this.context.userDataPath,
1111
- retentionPolicy: {
1112
- enabled: policy.enabled,
1113
- maxEntries: policy.maxEntries,
1114
- maxAge: policy.maxAge
1115
- },
1116
1148
  disk: {
1117
1149
  total: diskSpace.size,
1118
1150
  free: diskSpace.free,
@@ -1128,11 +1160,15 @@ var BuildHistoryStorage = class {
1128
1160
  async getAllPipelineFiles() {
1129
1161
  try {
1130
1162
  await this.ensureStoragePath();
1131
- return (await readdir(this.getStoragePath())).filter((file) => file.startsWith("pipeline-") && file.endsWith(".json"));
1163
+ return (await readdir(this.getStoragePath())).filter((file) => file.endsWith(".history.json"));
1132
1164
  } catch (error) {
1133
1165
  return [];
1134
1166
  }
1135
1167
  }
1168
+ parsePipelineIdFromFilename(filename) {
1169
+ const match = filename.match(/^(.+)\.history\.json$/);
1170
+ return match ? match[1] : null;
1171
+ }
1136
1172
  };
1137
1173
  //#endregion
1138
1174
  //#region src/handlers/history.ts
@@ -1414,40 +1450,6 @@ const registerHistoryHandlers = (context) => {
1414
1450
  });
1415
1451
  }
1416
1452
  });
1417
- handle("build-history:configure", async (_, { send, value }) => {
1418
- try {
1419
- logger().info("Updating build history configuration:", value.config);
1420
- const settings = await setupConfigFile("settings", { context });
1421
- const currentConfig = await settings.getConfig();
1422
- const newConfig = {
1423
- ...currentConfig,
1424
- buildHistory: {
1425
- ...currentConfig?.buildHistory,
1426
- retentionPolicy: {
1427
- ...currentConfig?.buildHistory?.retentionPolicy,
1428
- ...value.config.retentionPolicy
1429
- }
1430
- }
1431
- };
1432
- await settings.setConfig(newConfig);
1433
- send({
1434
- type: "end",
1435
- data: {
1436
- type: "success",
1437
- result: { result: "ok" }
1438
- }
1439
- });
1440
- } catch (error) {
1441
- logger().error("Failed to configure build history:", error);
1442
- send({
1443
- type: "end",
1444
- data: {
1445
- type: "error",
1446
- ipcError: error instanceof Error ? error.message : "Failed to configure build history"
1447
- }
1448
- });
1449
- }
1450
- });
1451
1453
  };
1452
1454
  //#endregion
1453
1455
  //#region ../../node_modules/serve-handler/src/glob-slash.js
@@ -8437,7 +8439,7 @@ var require_error = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8437
8439
  })();
8438
8440
  }));
8439
8441
  //#endregion
8440
- //#region src/migrations.ts
8442
+ //#region src/server.ts
8441
8443
  var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
8442
8444
  const { promisify } = __require("util");
8443
8445
  const path$1 = __require("path");
@@ -8891,44 +8893,6 @@ var import_src = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((export
8891
8893
  stream.pipe(response);
8892
8894
  };
8893
8895
  })))(), 1);
8894
- /**
8895
- * Registers migration handlers for the CLI/Standalone server.
8896
- * This overrides the default handlers from @pipelab/core-node to include
8897
- * pipeline and file repo migrations directly in the backend.
8898
- */
8899
- function registerMigrationHandlers(context) {
8900
- const { handle } = useAPI();
8901
- const { logger } = useLogger();
8902
- handle("config:load", async (_, { send, value }) => {
8903
- const { config: name } = value;
8904
- logger().info("[CLI Migration] config:load", name);
8905
- try {
8906
- const migrator = getMigrator(name);
8907
- if (!migrator) throw new Error(`No migrator found for configuration: ${name}. All files loaded via config:load MUST have a migration schema.`);
8908
- send({
8909
- type: "end",
8910
- data: {
8911
- type: "success",
8912
- result: { result: await (await setupConfigFile(name, {
8913
- context,
8914
- migrator
8915
- })).getConfig() }
8916
- }
8917
- });
8918
- } catch (e) {
8919
- logger().error(`[CLI Migration] config:load error for ${name}:`, e);
8920
- send({
8921
- type: "end",
8922
- data: {
8923
- type: "error",
8924
- ipcError: e instanceof Error ? e.message : `Unable to load config ${name}`
8925
- }
8926
- });
8927
- }
8928
- });
8929
- }
8930
- //#endregion
8931
- //#region src/server.ts
8932
8896
  const sendStartupProgress = (message) => {
8933
8897
  console.log(`[Startup Progress] ${message}`);
8934
8898
  webSocketServer.broadcast("startup:progress", {
@@ -8997,7 +8961,13 @@ async function serveCommand(options, version, _dirname) {
8997
8961
  response.end(`Error: UI directory not found at ${rawAssetFolder}.\nPlease run 'pnpm build' in apps/ui to generate the distribution.`);
8998
8962
  return;
8999
8963
  }
9000
- return (0, import_src.default)(request, response, { public: rawAssetFolder });
8964
+ return (0, import_src.default)(request, response, {
8965
+ public: rawAssetFolder,
8966
+ rewrites: [{
8967
+ source: "/**",
8968
+ destination: "/index.html"
8969
+ }]
8970
+ });
9001
8971
  });
9002
8972
  console.log(`Starting Pipelab server on port ${options.port}...`);
9003
8973
  if (isDev) {
@@ -9009,7 +8979,6 @@ async function serveCommand(options, version, _dirname) {
9009
8979
  version,
9010
8980
  context
9011
8981
  });
9012
- registerMigrationHandlers(context);
9013
8982
  sendStartupReady();
9014
8983
  return server;
9015
8984
  }
@@ -9104,7 +9073,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
9104
9073
  console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
9105
9074
  } else throw new Error(`Offline and no local fallback version available for ${packageName}`);
9106
9075
  } else try {
9107
- const cachePath = join(ctx.userDataPath, "cache", "pacote");
9076
+ const cachePath = ctx.getCachePath(CacheFolder.Pacote);
9108
9077
  let packumentPromise = packumentRequests.get(packageName);
9109
9078
  if (!packumentPromise) {
9110
9079
  packumentPromise = pacote.packument(packageName, { cache: cachePath });
@@ -9135,7 +9104,7 @@ async function fetchPackage(packageName, versionOrRange, options) {
9135
9104
  console.log(`[Fetcher] ${packageName}: Resolved to local fallback ${resolvedVersion} (${Date.now() - fallbackStart}ms)`);
9136
9105
  } else throw error;
9137
9106
  }
9138
- const cachePath = join(ctx.userDataPath, "cache", "pacote");
9107
+ const cachePath = ctx.getCachePath(CacheFolder.Pacote);
9139
9108
  const packageDir = join(baseDir, resolvedVersion);
9140
9109
  const checkStart = Date.now();
9141
9110
  const isInstalled = options?.installDeps ? isPackageComplete(packageDir) && isDependenciesInstalledSync(packageDir) : isPackageComplete(packageDir);
@@ -9212,7 +9181,7 @@ async function runPnpm(cwd, options) {
9212
9181
  ...process.env,
9213
9182
  NODE_ENV: "production",
9214
9183
  PATH: nodePath ? `${dirname(nodePath)}${delimiter}${process.env.PATH}` : process.env.PATH,
9215
- PNPM_HOME: join(ctx.userDataPath, "pnpm"),
9184
+ PNPM_HOME: ctx.getPnpmPath(),
9216
9185
  PNPM_ONLY_ALLOW_TRUSTED_DEPENDENCIES: "false",
9217
9186
  ...extraEnv
9218
9187
  }
@@ -9741,8 +9710,8 @@ const builtInPlugins = async (options) => {
9741
9710
  }
9742
9711
  }
9743
9712
  try {
9744
- const { setupConfigFile } = await import("./config-CEkOf95p.mjs");
9745
- const settingsPlugins = (await (await setupConfigFile("settings", { context: options.context })).getConfig())?.plugins || [];
9713
+ const { setupSettingsConfigFile } = await import("./config-Bi0ORcTK.mjs");
9714
+ const settingsPlugins = (await (await setupSettingsConfigFile(options.context)).getConfig())?.plugins || [];
9746
9715
  for (const plugin of settingsPlugins) if (plugin.name) if (plugin.enabled) {
9747
9716
  if (!pluginsToLoad.has(plugin.name)) pluginsToLoad.set(plugin.name, "latest");
9748
9717
  } else pluginsToLoad.delete(plugin.name);
@@ -9812,6 +9781,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
9812
9781
  projectName,
9813
9782
  projectPath,
9814
9783
  pipelineId,
9784
+ cachePath,
9815
9785
  startTime,
9816
9786
  status: "running",
9817
9787
  logs: [],
@@ -9840,7 +9810,7 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
9840
9810
  } else logger().error(`[Runner] Failed to load or register plugin "${pluginId}"`);
9841
9811
  }
9842
9812
  logger().info(`[Sandbox] Execution sandbox created at: ${sandboxPath}`);
9843
- await (await setupConfigFile("settings", { context: ctx })).getConfig();
9813
+ await (await setupSettingsConfigFile(ctx)).getConfig();
9844
9814
  try {
9845
9815
  const result = await processGraph({
9846
9816
  graph,
@@ -9908,7 +9878,6 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
9908
9878
  }, pipelineId);
9909
9879
  throw error;
9910
9880
  } finally {
9911
- if (!shouldDisableHistory) buildHistoryStorage.applyRetentionPolicy();
9912
9881
  try {
9913
9882
  await rm(sandboxPath, {
9914
9883
  recursive: true,
@@ -9917,6 +9886,14 @@ const executeGraphWithHistory = async ({ graph, variables, projectName, projectP
9917
9886
  } catch (e) {
9918
9887
  console.warn(`Failed to cleanup sandbox at ${sandboxPath}:`, e);
9919
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
+ }
9920
9897
  }
9921
9898
  };
9922
9899
  //#endregion
@@ -10292,24 +10269,32 @@ const registerEngineHandlers = (context) => {
10292
10269
  };
10293
10270
  handle("action:execute", async (event, { send, value }) => {
10294
10271
  const { nodeId, params, pluginId } = value;
10295
- await (await setupConfigFile("settings", { context })).getConfig();
10296
- const cachePath = join(context.userDataPath, "cache", "actions", pluginId, nodeId);
10272
+ const cachePath = context.getCachePath(CacheFolder.Actions, pluginId, nodeId);
10297
10273
  const cwd = await context.createTempFolder("action-execute-");
10298
- const mainWindow = void 0;
10299
- abortControllerGraph = new AbortController();
10300
- const signalPromise = new Promise((resolve, reject) => {
10301
- abortControllerGraph.signal.addEventListener("abort", async () => {
10302
- await send({
10303
- type: "end",
10304
- data: {
10305
- ipcError: "Action aborted",
10306
- type: "error"
10307
- }
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"));
10308
10287
  });
10309
- return reject(/* @__PURE__ */ new Error("Action interrupted"));
10310
10288
  });
10311
- });
10312
- await Promise.race([signalPromise, effectiveActionExecute(nodeId, pluginId, params, mainWindow, send, cwd, cachePath)]);
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
+ }
10313
10298
  });
10314
10299
  handle("constants:get", async (_, { send }) => {
10315
10300
  const userData = context.userDataPath;
@@ -10333,11 +10318,11 @@ const registerEngineHandlers = (context) => {
10333
10318
  });
10334
10319
  handle("graph:execute", async (event, { send, value }) => {
10335
10320
  const { graph, variables, projectName, projectPath, pipelineId } = value;
10336
- await (await setupConfigFile("settings", { context })).getConfig();
10321
+ await (await setupSettingsConfigFile(context)).getConfig();
10337
10322
  const effectiveProjectName = projectName || "Unnamed Project";
10338
10323
  const effectiveProjectPath = projectPath || "";
10339
10324
  const effectivePipelineId = pipelineId || "unknown";
10340
- const effectiveCachePath = join(context.userDataPath, "cache", effectivePipelineId);
10325
+ const effectiveCachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
10341
10326
  const mainWindow = void 0;
10342
10327
  abortControllerGraph = new AbortController();
10343
10328
  try {
@@ -10445,7 +10430,7 @@ var JsonFileStorage = class {
10445
10430
  filePath;
10446
10431
  logger = useLogger().logger;
10447
10432
  constructor(fileName, context) {
10448
- this.filePath = path.join(context.userDataPath, fileName);
10433
+ this.filePath = context.getConfigPath(fileName);
10449
10434
  const dir = path.dirname(this.filePath);
10450
10435
  if (!fs.existsSync(dir)) try {
10451
10436
  fs.mkdirSync(dir, { recursive: true });
@@ -10886,6 +10871,470 @@ const registerPluginsHandlers = (context) => {
10886
10871
  });
10887
10872
  };
10888
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
10889
11338
  //#region src/handlers/index.ts
10890
11339
  const registerAllHandlers = async (options) => {
10891
11340
  const context = options.context;
@@ -10898,6 +11347,7 @@ const registerAllHandlers = async (options) => {
10898
11347
  registerAuthHandlers(context);
10899
11348
  registerSystemHandlers(options);
10900
11349
  registerPluginsHandlers(context);
11350
+ registerMigrationHandlers(context);
10901
11351
  const { registerPlugins } = usePlugins();
10902
11352
  builtInPlugins({ context });
10903
11353
  };
@@ -53170,9 +53620,7 @@ async function runPipelineCommand(file, options, version) {
53170
53620
  version,
53171
53621
  context
53172
53622
  });
53173
- registerMigrationHandlers(context);
53174
- await (await setupConfigFile("settings", { context })).getConfig();
53175
- const cachePath = join(context.userDataPath, "cache");
53623
+ const cachePath = context.getCachePath(CacheFolder.Pipelines, effectivePipelineId);
53176
53624
  await mkdir(cachePath, { recursive: true });
53177
53625
  const abortController = new AbortController();
53178
53626
  let supabase;
@@ -53344,6 +53792,6 @@ async function fetchLatestDesktopRelease(options = {}) {
53344
53792
  return latest;
53345
53793
  }
53346
53794
  //#endregion
53347
- export { BuildHistoryStorage, PipelabContext, WebSocketServer, builtInPlugins, downloadFile, ensure, ensureNodeJS, ensurePNPM, executeGraphWithHistory, extractTarGz, extractZip, fetchLatestDesktopRelease, fetchLatestPackageRelease, fetchPackage, fetchPackageReleases, fetchPipelabAsset, fetchPipelabCli, fetchPipelabPlugin, findInstalledPlugins, getDefaultUserDataPath, getFinalPlugins, getFolderSize, getMigrator, handleActionExecute, isDev, isOnline, loadCustomPlugin, loadPipelabPlugin, projectRoot, registerAgentsHandlers, registerAllHandlers, registerConfigHandlers, registerEngineHandlers, registerFsHandlers, registerHistoryHandlers, registerMigrationHandlers, registerShellHandlers, runPipelineCommand, runPnpm, runWithLiveLogs, sendStartupProgress, sendStartupReady, serveCommand, setupConfigFile, useAPI, usePluginAPI, webSocketServer, zipFolder };
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 };
53348
53796
 
53349
53797
  //# sourceMappingURL=index.mjs.map