@pipelab/core-node 1.0.0-beta.21 → 1.0.0-beta.25
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 +36 -0
- package/dist/config-CFgGRD9U.mjs.map +1 -1
- package/dist/index.d.mts +24 -17
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +144 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/config.test.ts +1 -4
- package/src/config.ts +11 -3
- package/src/context.ts +31 -16
- package/src/fs-utils.test.ts +125 -0
- package/src/fs-utils.ts +142 -0
- package/src/handler-func.ts +15 -2
- package/src/handlers/config.ts +60 -15
- package/src/handlers/fs.ts +25 -0
- package/src/handlers/migration.ts +31 -6
- package/src/index.ts +1 -0
- package/src/server.ts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipelab/core-node",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.25",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Pipelab automation engine for Node.js",
|
|
6
6
|
"license": "FSL-1.1-MIT",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"type-fest": "4.39.0",
|
|
41
41
|
"ws": "8.18.3",
|
|
42
42
|
"yauzl": "2.10.0",
|
|
43
|
-
"@pipelab/constants": "1.0.0-beta.
|
|
44
|
-
"@pipelab/shared": "1.0.0-beta.
|
|
43
|
+
"@pipelab/constants": "1.0.0-beta.23",
|
|
44
|
+
"@pipelab/shared": "1.0.0-beta.21"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/adm-zip": "0.5.7",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"tsdown": "0.21.2",
|
|
57
57
|
"typescript": "^5.0.0",
|
|
58
58
|
"vitest": "3.1.4",
|
|
59
|
-
"@pipelab/tsconfig": "1.0.0-beta.
|
|
59
|
+
"@pipelab/tsconfig": "1.0.0-beta.21"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsdown",
|
package/src/config.test.ts
CHANGED
|
@@ -180,9 +180,7 @@ describe("setupConfigFile & Backup Creation", () => {
|
|
|
180
180
|
const success = await configInstance.setConfig(newConfig);
|
|
181
181
|
expect(success).toBe(true);
|
|
182
182
|
|
|
183
|
-
const savedContent = JSON.parse(
|
|
184
|
-
await fs.readFile(context.getProjectsPath(), "utf8"),
|
|
185
|
-
);
|
|
183
|
+
const savedContent = JSON.parse(await fs.readFile(context.getProjectsPath(), "utf8"));
|
|
186
184
|
expect(savedContent.pipelines).toHaveLength(1);
|
|
187
185
|
expect(savedContent.pipelines[0].id).toBe("pipeline-new");
|
|
188
186
|
});
|
|
@@ -229,4 +227,3 @@ describe("deletePipelineConfigFile", () => {
|
|
|
229
227
|
await expect(deletePipelineConfigFileByName("does-not-exist", context)).resolves.not.toThrow();
|
|
230
228
|
});
|
|
231
229
|
});
|
|
232
|
-
|
package/src/config.ts
CHANGED
|
@@ -59,10 +59,15 @@ export const setupConfigFile = async <T>(
|
|
|
59
59
|
json = await migrator.migrate(originalJson, {
|
|
60
60
|
debug: false,
|
|
61
61
|
onStep: async (state: any, version: string) => {
|
|
62
|
-
const versionedPath = path.join(
|
|
62
|
+
const versionedPath = path.join(
|
|
63
|
+
parsedPath.dir,
|
|
64
|
+
`${parsedPath.name}.v${version}.json`,
|
|
65
|
+
);
|
|
63
66
|
try {
|
|
64
67
|
await fs.writeFile(versionedPath, JSON.stringify(state));
|
|
65
|
-
logger().info(
|
|
68
|
+
logger().info(
|
|
69
|
+
`Intermediate backup created for ${parsedPath.name} at ${versionedPath}`,
|
|
70
|
+
);
|
|
66
71
|
} catch (e) {
|
|
67
72
|
logger().error(
|
|
68
73
|
`Failed to create intermediate backup for ${parsedPath.name} at v${version}:`,
|
|
@@ -161,6 +166,9 @@ export const deletePipelineConfigFileByName = async (name: string, context: Pipe
|
|
|
161
166
|
await deleteConfigFile(filesPath);
|
|
162
167
|
};
|
|
163
168
|
|
|
164
|
-
export const deletePipelineConfigFileByPath = async (
|
|
169
|
+
export const deletePipelineConfigFileByPath = async (
|
|
170
|
+
absolutePath: string,
|
|
171
|
+
context: PipelabContext,
|
|
172
|
+
) => {
|
|
165
173
|
await deleteConfigFile(absolutePath);
|
|
166
174
|
};
|
package/src/context.ts
CHANGED
|
@@ -66,6 +66,12 @@ export interface PipelabContextOptions {
|
|
|
66
66
|
releaseTag?: string;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
type Join<T extends string[], D extends string> =
|
|
70
|
+
T extends [] ? "" :
|
|
71
|
+
T extends [infer F extends string] ? F :
|
|
72
|
+
T extends [infer F extends string, ...infer R extends string[]] ? `${F}${D}${Join<R, D>}` :
|
|
73
|
+
string;
|
|
74
|
+
|
|
69
75
|
export class PipelabContext {
|
|
70
76
|
public readonly userDataPath: string;
|
|
71
77
|
public readonly releaseTag: string;
|
|
@@ -75,27 +81,30 @@ export class PipelabContext {
|
|
|
75
81
|
this.releaseTag = options.releaseTag || "latest";
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
getPackagesPath(...subpaths:
|
|
84
|
+
getPackagesPath<S extends string[]>(...subpaths: S): `PACKAGES/${Join<S, "/">}`;
|
|
85
|
+
getPackagesPath(...subpaths: string[]): string {
|
|
79
86
|
return join(this.userDataPath, "packages", ...subpaths);
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
getThirdPartyPath(...subpaths:
|
|
89
|
+
getThirdPartyPath<S extends string[]>(...subpaths: S): `THIRDPARTY/${Join<S, "/">}`;
|
|
90
|
+
getThirdPartyPath(...subpaths: string[]): string {
|
|
83
91
|
return join(this.userDataPath, "thirdparty", ...subpaths);
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
getConfigPath(...subpaths:
|
|
94
|
+
getConfigPath<S extends string[]>(...subpaths: S): `CONFIG/${Join<S, "/">}`;
|
|
95
|
+
getConfigPath(...subpaths: string[]): string {
|
|
87
96
|
return join(this.userDataPath, "config", ...subpaths);
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
getSettingsPath() {
|
|
99
|
+
getSettingsPath(): `CONFIG/settings.json` {
|
|
91
100
|
return this.getConfigPath("settings.json");
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
getConnectionsPath() {
|
|
103
|
+
getConnectionsPath(): `CONFIG/connections.json` {
|
|
95
104
|
return this.getConfigPath("connections.json");
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
getProjectsPath() {
|
|
107
|
+
getProjectsPath(): `CONFIG/projects.json` {
|
|
99
108
|
return this.getConfigPath("projects.json");
|
|
100
109
|
}
|
|
101
110
|
|
|
@@ -121,22 +130,24 @@ export class PipelabContext {
|
|
|
121
130
|
}
|
|
122
131
|
}
|
|
123
132
|
|
|
124
|
-
getTempPath(...subpaths:
|
|
133
|
+
getTempPath<S extends string[]>(...subpaths: S): `TEMP/${Join<S, "/">}`;
|
|
134
|
+
getTempPath(...subpaths: string[]): string {
|
|
125
135
|
const settings = this.getSettings();
|
|
126
136
|
const base = settings?.tempFolder || join(this.userDataPath, "temp");
|
|
127
137
|
return join(base, ...subpaths);
|
|
128
138
|
}
|
|
129
139
|
|
|
130
|
-
|
|
140
|
+
createTempFolder<T extends string = "pipelab-">(prefix?: T): Promise<`TEMP/${T}${string}`>;
|
|
141
|
+
async createTempFolder(prefix = "pipelab-"): Promise<string> {
|
|
131
142
|
const baseDir = this.getTempPath();
|
|
132
143
|
await mkdir(baseDir, { recursive: true });
|
|
133
144
|
const realBaseDir = await realpath(baseDir);
|
|
134
|
-
return
|
|
145
|
+
return mkdtemp(join(realBaseDir, prefix));
|
|
135
146
|
}
|
|
136
147
|
|
|
137
|
-
getCachePath():
|
|
138
|
-
getCachePath(folder:
|
|
139
|
-
getCachePath(folder?: CacheFolderType, ...subpaths: string[]) {
|
|
148
|
+
getCachePath(): `CACHE/`;
|
|
149
|
+
getCachePath<F extends CacheFolderType, S extends string[]>(folder: F, ...subpaths: S): `CACHE/${F}/${Join<S, "/">}`;
|
|
150
|
+
getCachePath(folder?: CacheFolderType, ...subpaths: string[]): string {
|
|
140
151
|
const settings = this.getSettings();
|
|
141
152
|
const base = settings?.cacheFolder || join(this.userDataPath, "cache");
|
|
142
153
|
if (!folder) {
|
|
@@ -145,20 +156,24 @@ export class PipelabContext {
|
|
|
145
156
|
return join(base, folder, ...subpaths);
|
|
146
157
|
}
|
|
147
158
|
|
|
148
|
-
getPnpmPath(...subpaths:
|
|
159
|
+
getPnpmPath<S extends string[]>(...subpaths: S): `PNPM/${Join<S, "/">}`;
|
|
160
|
+
getPnpmPath(...subpaths: string[]): string {
|
|
149
161
|
return join(this.userDataPath, "pnpm", ...subpaths);
|
|
150
162
|
}
|
|
151
163
|
|
|
152
|
-
getBuildHistoryPath(...subpaths:
|
|
164
|
+
getBuildHistoryPath<S extends string[]>(...subpaths: S): `BUILD_HISTORY/${Join<S, "/">}`;
|
|
165
|
+
getBuildHistoryPath(...subpaths: string[]): string {
|
|
153
166
|
return join(this.userDataPath, "build-history", ...subpaths);
|
|
154
167
|
}
|
|
155
168
|
|
|
156
|
-
getNodePath
|
|
169
|
+
getNodePath<V extends string = typeof DEFAULT_NODE_VERSION>(version?: V): `THIRDPARTY/node/${V}/${string}`;
|
|
170
|
+
getNodePath(version = DEFAULT_NODE_VERSION): string {
|
|
157
171
|
const isWindows = process.platform === "win32";
|
|
158
172
|
return this.getThirdPartyPath("node", version, isWindows ? "node.exe" : "bin/node");
|
|
159
173
|
}
|
|
160
174
|
|
|
161
|
-
getPnpmBinPath
|
|
175
|
+
getPnpmBinPath<V extends string = typeof DEFAULT_PNPM_VERSION>(version?: V): `PACKAGES/pnpm/${V}/bin/pnpm.cjs`;
|
|
176
|
+
getPnpmBinPath(version = DEFAULT_PNPM_VERSION): string {
|
|
162
177
|
return this.getPackagesPath("pnpm", version, "bin", "pnpm.cjs");
|
|
163
178
|
}
|
|
164
179
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { resolve, join } from "node:path";
|
|
4
|
+
import slash from "slash";
|
|
5
|
+
import { isPathBlacklisted } from "./fs-utils";
|
|
6
|
+
import { getDefaultUserDataPath, projectRoot } from "./context";
|
|
7
|
+
|
|
8
|
+
const isWindows = process.platform === "win32";
|
|
9
|
+
|
|
10
|
+
describe("isPathBlacklisted", () => {
|
|
11
|
+
test("should return false for empty or falsy paths", () => {
|
|
12
|
+
expect(isPathBlacklisted("")).toBe(false);
|
|
13
|
+
expect(isPathBlacklisted(undefined as any)).toBe(false);
|
|
14
|
+
expect(isPathBlacklisted(null as any)).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("should return false for safe user workspace paths", () => {
|
|
18
|
+
const safePath = join(homedir(), "my-custom-projects", "project-1");
|
|
19
|
+
expect(isPathBlacklisted(safePath)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("should blacklist user home directory", () => {
|
|
23
|
+
expect(isPathBlacklisted(homedir())).toBe(true);
|
|
24
|
+
expect(isPathBlacklisted(homedir() + "/")).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test.skipIf(isWindows)("should blacklist common system paths on Unix-like systems", () => {
|
|
28
|
+
// These Unix paths are checked as absolute paths starting with /
|
|
29
|
+
expect(isPathBlacklisted("/usr")).toBe(true);
|
|
30
|
+
expect(isPathBlacklisted("/var")).toBe(true);
|
|
31
|
+
expect(isPathBlacklisted("/etc")).toBe(true);
|
|
32
|
+
expect(isPathBlacklisted("/bin")).toBe(true);
|
|
33
|
+
expect(isPathBlacklisted("/sbin")).toBe(true);
|
|
34
|
+
expect(isPathBlacklisted("/lib")).toBe(true);
|
|
35
|
+
expect(isPathBlacklisted("/lib64")).toBe(true);
|
|
36
|
+
expect(isPathBlacklisted("/boot")).toBe(true);
|
|
37
|
+
expect(isPathBlacklisted("/sys")).toBe(true);
|
|
38
|
+
expect(isPathBlacklisted("/proc")).toBe(true);
|
|
39
|
+
expect(isPathBlacklisted("/dev")).toBe(true);
|
|
40
|
+
expect(isPathBlacklisted("/run")).toBe(true);
|
|
41
|
+
expect(isPathBlacklisted("/home")).toBe(true);
|
|
42
|
+
expect(isPathBlacklisted("/root")).toBe(true);
|
|
43
|
+
expect(isPathBlacklisted("/mnt")).toBe(true);
|
|
44
|
+
expect(isPathBlacklisted("/media")).toBe(true);
|
|
45
|
+
expect(isPathBlacklisted("/srv")).toBe(true);
|
|
46
|
+
expect(isPathBlacklisted("/applications")).toBe(true);
|
|
47
|
+
expect(isPathBlacklisted("/library")).toBe(true);
|
|
48
|
+
expect(isPathBlacklisted("/system")).toBe(true);
|
|
49
|
+
expect(isPathBlacklisted("/volumes")).toBe(true);
|
|
50
|
+
expect(isPathBlacklisted("/snap")).toBe(true);
|
|
51
|
+
expect(isPathBlacklisted("/")).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test.runIf(isWindows)(
|
|
55
|
+
"should blacklist common Windows directories dynamically on any drive (Windows-only)",
|
|
56
|
+
() => {
|
|
57
|
+
// Windows drive roots
|
|
58
|
+
expect(isPathBlacklisted("c:")).toBe(true);
|
|
59
|
+
expect(isPathBlacklisted("d:")).toBe(true);
|
|
60
|
+
expect(isPathBlacklisted("Z:")).toBe(true);
|
|
61
|
+
expect(isPathBlacklisted("C:\\")).toBe(true);
|
|
62
|
+
expect(isPathBlacklisted("d:/")).toBe(true);
|
|
63
|
+
|
|
64
|
+
// Windows system directories on any drive letter
|
|
65
|
+
expect(isPathBlacklisted("c:/windows")).toBe(true);
|
|
66
|
+
expect(isPathBlacklisted("D:\\Windows\\")).toBe(true);
|
|
67
|
+
expect(isPathBlacklisted("e:/program files")).toBe(true);
|
|
68
|
+
expect(isPathBlacklisted("f:/program files (x86)")).toBe(true);
|
|
69
|
+
expect(isPathBlacklisted("g:/users")).toBe(true);
|
|
70
|
+
expect(isPathBlacklisted("h:/programdata")).toBe(true);
|
|
71
|
+
expect(isPathBlacklisted("i:/perflogs")).toBe(true);
|
|
72
|
+
|
|
73
|
+
// Should NOT blacklist normal subfolders on other drives
|
|
74
|
+
expect(isPathBlacklisted("d:/my-folder")).toBe(false);
|
|
75
|
+
expect(isPathBlacklisted("c:/windows-backup")).toBe(false);
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
test("should blacklist standard user profile directories", () => {
|
|
80
|
+
expect(isPathBlacklisted(join(homedir(), "Downloads"))).toBe(true);
|
|
81
|
+
expect(isPathBlacklisted(join(homedir(), "Documents"))).toBe(true);
|
|
82
|
+
expect(isPathBlacklisted(join(homedir(), "Desktop"))).toBe(true);
|
|
83
|
+
expect(isPathBlacklisted(join(homedir(), "Pictures"))).toBe(true);
|
|
84
|
+
expect(isPathBlacklisted(join(homedir(), "Music"))).toBe(true);
|
|
85
|
+
expect(isPathBlacklisted(join(homedir(), "Videos"))).toBe(true);
|
|
86
|
+
expect(isPathBlacklisted(join(homedir(), "Saved Games"))).toBe(true);
|
|
87
|
+
expect(isPathBlacklisted(join(homedir(), "Contacts"))).toBe(true);
|
|
88
|
+
expect(isPathBlacklisted(join(homedir(), "Searches"))).toBe(true);
|
|
89
|
+
expect(isPathBlacklisted(join(homedir(), "Links"))).toBe(true);
|
|
90
|
+
expect(isPathBlacklisted(join(homedir(), "3D Objects"))).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should blacklist developer settings and credentials", () => {
|
|
94
|
+
expect(isPathBlacklisted(join(homedir(), ".ssh"))).toBe(true);
|
|
95
|
+
expect(isPathBlacklisted(join(homedir(), ".gnupg"))).toBe(true);
|
|
96
|
+
expect(isPathBlacklisted(join(homedir(), ".aws"))).toBe(true);
|
|
97
|
+
expect(isPathBlacklisted(join(homedir(), ".docker"))).toBe(true);
|
|
98
|
+
expect(isPathBlacklisted(join(homedir(), ".kube"))).toBe(true);
|
|
99
|
+
expect(isPathBlacklisted(join(homedir(), ".vscode"))).toBe(true);
|
|
100
|
+
expect(isPathBlacklisted(join(homedir(), ".cursor"))).toBe(true);
|
|
101
|
+
expect(isPathBlacklisted(join(homedir(), ".npm"))).toBe(true);
|
|
102
|
+
expect(isPathBlacklisted(join(homedir(), ".pnpm-state"))).toBe(true);
|
|
103
|
+
expect(isPathBlacklisted(join(homedir(), ".yarn"))).toBe(true);
|
|
104
|
+
expect(isPathBlacklisted(join(homedir(), ".cargo"))).toBe(true);
|
|
105
|
+
expect(isPathBlacklisted(join(homedir(), ".rustup"))).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("should blacklist Pipelab-specific directories", () => {
|
|
109
|
+
expect(isPathBlacklisted(getDefaultUserDataPath("prod"))).toBe(true);
|
|
110
|
+
expect(isPathBlacklisted(getDefaultUserDataPath("dev"))).toBe(true);
|
|
111
|
+
expect(isPathBlacklisted(getDefaultUserDataPath("beta"))).toBe(true);
|
|
112
|
+
if (projectRoot) {
|
|
113
|
+
expect(isPathBlacklisted(projectRoot)).toBe(true);
|
|
114
|
+
}
|
|
115
|
+
expect(isPathBlacklisted(process.cwd())).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("should handle case-insensitive paths and slash normalization", () => {
|
|
119
|
+
const mixedPath = join(homedir(), "DoWnLoAdS");
|
|
120
|
+
expect(isPathBlacklisted(mixedPath)).toBe(true);
|
|
121
|
+
|
|
122
|
+
const trailingSlashPath = homedir() + "/";
|
|
123
|
+
expect(isPathBlacklisted(trailingSlashPath)).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
});
|
package/src/fs-utils.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { resolve, join } from "node:path";
|
|
3
|
+
import slash from "slash";
|
|
4
|
+
|
|
5
|
+
function removeTrailingSlash(path: string): string {
|
|
6
|
+
return path.replace(/[\\/]+$/, "");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isWindowsDriveRoot(path: string): boolean {
|
|
10
|
+
return /^[a-z]:$/.test(path);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
import { getDefaultUserDataPath, projectRoot } from "./context";
|
|
14
|
+
|
|
15
|
+
const windowsSystemFolders = [
|
|
16
|
+
"windows",
|
|
17
|
+
"program files",
|
|
18
|
+
"program files (x86)",
|
|
19
|
+
"users",
|
|
20
|
+
"programdata",
|
|
21
|
+
"perflogs",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function isWindowsSystemDirectory(path: string): boolean {
|
|
25
|
+
const driveRoot = path.substring(0, 2);
|
|
26
|
+
if (!isWindowsDriveRoot(driveRoot) || path.charAt(2) !== "/") return false;
|
|
27
|
+
const folder = path.substring(3);
|
|
28
|
+
return windowsSystemFolders.includes(folder);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Static Unix/Linux/macOS system directories (normalized)
|
|
32
|
+
const unixSystemDirectories = new Set([
|
|
33
|
+
"", // represents "/" after removeTrailingSlash
|
|
34
|
+
"/usr",
|
|
35
|
+
"/var",
|
|
36
|
+
"/opt",
|
|
37
|
+
"/etc",
|
|
38
|
+
"/bin",
|
|
39
|
+
"/sbin",
|
|
40
|
+
"/lib",
|
|
41
|
+
"/lib64",
|
|
42
|
+
"/boot",
|
|
43
|
+
"/sys",
|
|
44
|
+
"/proc",
|
|
45
|
+
"/dev",
|
|
46
|
+
"/run",
|
|
47
|
+
"/home",
|
|
48
|
+
"/root",
|
|
49
|
+
"/mnt",
|
|
50
|
+
"/media",
|
|
51
|
+
"/srv",
|
|
52
|
+
"/applications",
|
|
53
|
+
"/library",
|
|
54
|
+
"/system",
|
|
55
|
+
"/volumes",
|
|
56
|
+
"/snap",
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
// Statically resolved home directory, user directories, temp directories, and Pipelab's AppData
|
|
60
|
+
const homeDirectory = removeTrailingSlash(slash(resolve(homedir()))).toLowerCase();
|
|
61
|
+
|
|
62
|
+
const additionalProtectedPaths = [
|
|
63
|
+
// Pipelab user data directories (dev, beta, prod)
|
|
64
|
+
getDefaultUserDataPath("dev"),
|
|
65
|
+
getDefaultUserDataPath("beta"),
|
|
66
|
+
getDefaultUserDataPath("prod"),
|
|
67
|
+
// Pipelab source directory if available
|
|
68
|
+
projectRoot,
|
|
69
|
+
// Current working directory of the process
|
|
70
|
+
process.cwd(),
|
|
71
|
+
// System temp directories
|
|
72
|
+
"/tmp",
|
|
73
|
+
"/var/tmp",
|
|
74
|
+
process.env.TEMP,
|
|
75
|
+
process.env.TMP,
|
|
76
|
+
// Windows AppData roots
|
|
77
|
+
join(homedir(), "AppData"),
|
|
78
|
+
join(homedir(), "AppData", "Local"),
|
|
79
|
+
join(homedir(), "AppData", "Roaming"),
|
|
80
|
+
// macOS / Unix config roots
|
|
81
|
+
join(homedir(), "Library"),
|
|
82
|
+
join(homedir(), "Library", "Application Support"),
|
|
83
|
+
join(homedir(), ".config"),
|
|
84
|
+
join(homedir(), ".local"),
|
|
85
|
+
join(homedir(), ".local", "share"),
|
|
86
|
+
join(homedir(), ".cache"),
|
|
87
|
+
// Cloud directories
|
|
88
|
+
join(homedir(), "OneDrive"),
|
|
89
|
+
join(homedir(), "Dropbox"),
|
|
90
|
+
// Critical SSH/GPG credentials folders
|
|
91
|
+
join(homedir(), ".ssh"),
|
|
92
|
+
join(homedir(), ".gnupg"),
|
|
93
|
+
// Developer CLI configurations
|
|
94
|
+
join(homedir(), ".aws"),
|
|
95
|
+
join(homedir(), ".docker"),
|
|
96
|
+
join(homedir(), ".kube"),
|
|
97
|
+
// IDE environments
|
|
98
|
+
join(homedir(), ".vscode"),
|
|
99
|
+
join(homedir(), ".vscode-insiders"),
|
|
100
|
+
join(homedir(), ".cursor"),
|
|
101
|
+
// Package manager / toolchain directories
|
|
102
|
+
join(homedir(), ".npm"),
|
|
103
|
+
join(homedir(), ".pnpm-state"),
|
|
104
|
+
join(homedir(), ".yarn"),
|
|
105
|
+
join(homedir(), ".cargo"),
|
|
106
|
+
join(homedir(), ".rustup"),
|
|
107
|
+
join(homedir(), ".m2"),
|
|
108
|
+
join(homedir(), ".gradle"),
|
|
109
|
+
].filter((p): p is string => typeof p === "string" && p.length > 0);
|
|
110
|
+
|
|
111
|
+
const userSubdirectories = new Set(
|
|
112
|
+
[
|
|
113
|
+
join(homedir(), "Downloads"),
|
|
114
|
+
join(homedir(), "Documents"),
|
|
115
|
+
join(homedir(), "Desktop"),
|
|
116
|
+
join(homedir(), "Pictures"),
|
|
117
|
+
join(homedir(), "Music"),
|
|
118
|
+
join(homedir(), "Videos"),
|
|
119
|
+
join(homedir(), "Saved Games"),
|
|
120
|
+
join(homedir(), "Contacts"),
|
|
121
|
+
join(homedir(), "Searches"),
|
|
122
|
+
join(homedir(), "Links"),
|
|
123
|
+
join(homedir(), "3D Objects"),
|
|
124
|
+
...additionalProtectedPaths,
|
|
125
|
+
].map((p) => removeTrailingSlash(slash(resolve(p))).toLowerCase()),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Checks if a path matches a critical user or system directory.
|
|
130
|
+
*/
|
|
131
|
+
export function isPathBlacklisted(pathToCheck: string): boolean {
|
|
132
|
+
if (!pathToCheck) return false;
|
|
133
|
+
const normalized = removeTrailingSlash(slash(resolve(pathToCheck))).toLowerCase();
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
normalized === homeDirectory ||
|
|
137
|
+
unixSystemDirectories.has(normalized) ||
|
|
138
|
+
userSubdirectories.has(normalized) ||
|
|
139
|
+
isWindowsDriveRoot(normalized) ||
|
|
140
|
+
isWindowsSystemDirectory(normalized)
|
|
141
|
+
);
|
|
142
|
+
}
|
package/src/handler-func.ts
CHANGED
|
@@ -100,14 +100,27 @@ export const handleActionExecute = async (
|
|
|
100
100
|
inputs: resolvedInputs,
|
|
101
101
|
log: (...args) => {
|
|
102
102
|
const decorator = `[${node.node.name}]`;
|
|
103
|
-
const
|
|
103
|
+
const formattedArgs = args.map((arg) => {
|
|
104
|
+
if (arg instanceof Error) {
|
|
105
|
+
return arg.stack || arg.message || String(arg);
|
|
106
|
+
}
|
|
107
|
+
if (typeof arg === "object" && arg !== null) {
|
|
108
|
+
try {
|
|
109
|
+
return JSON.stringify(arg, null, 2);
|
|
110
|
+
} catch {
|
|
111
|
+
return String(arg);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return arg;
|
|
115
|
+
});
|
|
116
|
+
const logArgs = [decorator, ...formattedArgs];
|
|
104
117
|
logger().info(...logArgs);
|
|
105
118
|
send({
|
|
106
119
|
type: "log",
|
|
107
120
|
data: {
|
|
108
121
|
decorator,
|
|
109
122
|
time: Date.now(),
|
|
110
|
-
message:
|
|
123
|
+
message: formattedArgs,
|
|
111
124
|
},
|
|
112
125
|
});
|
|
113
126
|
},
|
package/src/handlers/config.ts
CHANGED
|
@@ -34,7 +34,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
34
34
|
logger().error("settings:load error:", e);
|
|
35
35
|
send({
|
|
36
36
|
type: "end",
|
|
37
|
-
data: {
|
|
37
|
+
data: {
|
|
38
|
+
type: "error",
|
|
39
|
+
ipcError: e instanceof Error ? e.message : "Unable to load settings",
|
|
40
|
+
},
|
|
38
41
|
});
|
|
39
42
|
}
|
|
40
43
|
});
|
|
@@ -53,7 +56,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
53
56
|
logger().error("settings:save error:", e);
|
|
54
57
|
send({
|
|
55
58
|
type: "end",
|
|
56
|
-
data: {
|
|
59
|
+
data: {
|
|
60
|
+
type: "error",
|
|
61
|
+
ipcError: e instanceof Error ? e.message : "Unable to save settings",
|
|
62
|
+
},
|
|
57
63
|
});
|
|
58
64
|
}
|
|
59
65
|
});
|
|
@@ -76,7 +82,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
76
82
|
logger().error("settings:reset error:", e);
|
|
77
83
|
send({
|
|
78
84
|
type: "end",
|
|
79
|
-
data: {
|
|
85
|
+
data: {
|
|
86
|
+
type: "error",
|
|
87
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset settings",
|
|
88
|
+
},
|
|
80
89
|
});
|
|
81
90
|
}
|
|
82
91
|
});
|
|
@@ -95,7 +104,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
95
104
|
logger().error("connections:load error:", e);
|
|
96
105
|
send({
|
|
97
106
|
type: "end",
|
|
98
|
-
data: {
|
|
107
|
+
data: {
|
|
108
|
+
type: "error",
|
|
109
|
+
ipcError: e instanceof Error ? e.message : "Unable to load connections",
|
|
110
|
+
},
|
|
99
111
|
});
|
|
100
112
|
}
|
|
101
113
|
});
|
|
@@ -114,7 +126,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
114
126
|
logger().error("connections:save error:", e);
|
|
115
127
|
send({
|
|
116
128
|
type: "end",
|
|
117
|
-
data: {
|
|
129
|
+
data: {
|
|
130
|
+
type: "error",
|
|
131
|
+
ipcError: e instanceof Error ? e.message : "Unable to save connections",
|
|
132
|
+
},
|
|
118
133
|
});
|
|
119
134
|
}
|
|
120
135
|
});
|
|
@@ -137,7 +152,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
137
152
|
logger().error("connections:reset error:", e);
|
|
138
153
|
send({
|
|
139
154
|
type: "end",
|
|
140
|
-
data: {
|
|
155
|
+
data: {
|
|
156
|
+
type: "error",
|
|
157
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset connections",
|
|
158
|
+
},
|
|
141
159
|
});
|
|
142
160
|
}
|
|
143
161
|
});
|
|
@@ -156,7 +174,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
156
174
|
logger().error("projects:load error:", e);
|
|
157
175
|
send({
|
|
158
176
|
type: "end",
|
|
159
|
-
data: {
|
|
177
|
+
data: {
|
|
178
|
+
type: "error",
|
|
179
|
+
ipcError: e instanceof Error ? e.message : "Unable to load projects",
|
|
180
|
+
},
|
|
160
181
|
});
|
|
161
182
|
}
|
|
162
183
|
});
|
|
@@ -175,7 +196,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
175
196
|
logger().error("projects:save error:", e);
|
|
176
197
|
send({
|
|
177
198
|
type: "end",
|
|
178
|
-
data: {
|
|
199
|
+
data: {
|
|
200
|
+
type: "error",
|
|
201
|
+
ipcError: e instanceof Error ? e.message : "Unable to save projects",
|
|
202
|
+
},
|
|
179
203
|
});
|
|
180
204
|
}
|
|
181
205
|
});
|
|
@@ -198,7 +222,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
198
222
|
logger().error("projects:reset error:", e);
|
|
199
223
|
send({
|
|
200
224
|
type: "end",
|
|
201
|
-
data: {
|
|
225
|
+
data: {
|
|
226
|
+
type: "error",
|
|
227
|
+
ipcError: e instanceof Error ? e.message : "Unable to reset projects",
|
|
228
|
+
},
|
|
202
229
|
});
|
|
203
230
|
}
|
|
204
231
|
});
|
|
@@ -218,7 +245,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
218
245
|
logger().error(`pipeline:load-by-name error for ${name}:`, e);
|
|
219
246
|
send({
|
|
220
247
|
type: "end",
|
|
221
|
-
data: {
|
|
248
|
+
data: {
|
|
249
|
+
type: "error",
|
|
250
|
+
ipcError: e instanceof Error ? e.message : `Unable to load pipeline ${name}`,
|
|
251
|
+
},
|
|
222
252
|
});
|
|
223
253
|
}
|
|
224
254
|
});
|
|
@@ -238,7 +268,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
238
268
|
logger().error(`pipeline:load-by-path error for ${absolutePath}:`, e);
|
|
239
269
|
send({
|
|
240
270
|
type: "end",
|
|
241
|
-
data: {
|
|
271
|
+
data: {
|
|
272
|
+
type: "error",
|
|
273
|
+
ipcError: e instanceof Error ? e.message : `Unable to load pipeline ${absolutePath}`,
|
|
274
|
+
},
|
|
242
275
|
});
|
|
243
276
|
}
|
|
244
277
|
});
|
|
@@ -257,7 +290,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
257
290
|
logger().error(`pipeline:save-by-name error for ${name}:`, e);
|
|
258
291
|
send({
|
|
259
292
|
type: "end",
|
|
260
|
-
data: {
|
|
293
|
+
data: {
|
|
294
|
+
type: "error",
|
|
295
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${name}`,
|
|
296
|
+
},
|
|
261
297
|
});
|
|
262
298
|
}
|
|
263
299
|
});
|
|
@@ -276,7 +312,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
276
312
|
logger().error(`pipeline:save-by-path error for ${absolutePath}:`, e);
|
|
277
313
|
send({
|
|
278
314
|
type: "end",
|
|
279
|
-
data: {
|
|
315
|
+
data: {
|
|
316
|
+
type: "error",
|
|
317
|
+
ipcError: e instanceof Error ? e.message : `Unable to save pipeline ${absolutePath}`,
|
|
318
|
+
},
|
|
280
319
|
});
|
|
281
320
|
}
|
|
282
321
|
});
|
|
@@ -294,7 +333,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
294
333
|
logger().error(`pipeline:delete-by-name error for ${name}:`, e);
|
|
295
334
|
send({
|
|
296
335
|
type: "end",
|
|
297
|
-
data: {
|
|
336
|
+
data: {
|
|
337
|
+
type: "error",
|
|
338
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${name}`,
|
|
339
|
+
},
|
|
298
340
|
});
|
|
299
341
|
}
|
|
300
342
|
});
|
|
@@ -312,7 +354,10 @@ export const registerConfigHandlers = (context: PipelabContext) => {
|
|
|
312
354
|
logger().error(`pipeline:delete-by-path error for ${absolutePath}:`, e);
|
|
313
355
|
send({
|
|
314
356
|
type: "end",
|
|
315
|
-
data: {
|
|
357
|
+
data: {
|
|
358
|
+
type: "error",
|
|
359
|
+
ipcError: e instanceof Error ? e.message : `Unable to delete pipeline ${absolutePath}`,
|
|
360
|
+
},
|
|
316
361
|
});
|
|
317
362
|
}
|
|
318
363
|
});
|