@uipath/packager-tool-flow 0.0.17
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/README.md +51 -0
- package/dist/agents.d.ts +2 -0
- package/dist/bindings.d.ts +17 -0
- package/dist/flow-tool-factory.d.ts +8 -0
- package/dist/flow-tool.d.ts +42 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +263 -0
- package/package.json +60 -0
- package/src/agents.ts +50 -0
- package/src/bindings.ts +64 -0
- package/src/flow-tool-factory.ts +23 -0
- package/src/flow-tool.ts +359 -0
- package/src/index.ts +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @uipath/tool-flow
|
|
2
|
+
|
|
3
|
+
A **Project Tool** for the UiPath Solution Packager, designed for handling **Flow** projects.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package implements a packaging tool for Flow projects using `@uipath/solutionpackager-tool-core`. It implements the lifecycle methods for building and packing Flow projects.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Build Operations**:
|
|
12
|
+
- Copies project files to a clean output directory
|
|
13
|
+
- Generates `operate.json` configuration
|
|
14
|
+
- Generates `package-descriptor.json`
|
|
15
|
+
- **Pack Operations**:
|
|
16
|
+
- Creates `.nupkg` packages (NuGet)
|
|
17
|
+
- Handles versioning and metadata
|
|
18
|
+
|
|
19
|
+
## Project Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/
|
|
23
|
+
├── flow-tool.ts # Main tool logic implementation
|
|
24
|
+
├── flow-tool-factory.ts # Factory for creating tool instances
|
|
25
|
+
└── index.ts # Export entry point
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
This tool is designed to be loaded dynamically by `solutionpackager`.
|
|
31
|
+
|
|
32
|
+
### Development
|
|
33
|
+
|
|
34
|
+
**Install dependencies:**
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Build:**
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm run build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Run Tests:**
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm test
|
|
50
|
+
npm run test:browser
|
|
51
|
+
```
|
package/dist/agents.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IFileSystem } from "@uipath/solutionpackager-tool-core";
|
|
2
|
+
import type { AgentBuildResult } from "@uipath/tool-agent/build";
|
|
3
|
+
export interface BindingsResource {
|
|
4
|
+
resource: string;
|
|
5
|
+
key: string;
|
|
6
|
+
[other: string]: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface Bindings {
|
|
9
|
+
version: string;
|
|
10
|
+
resources: BindingsResource[];
|
|
11
|
+
}
|
|
12
|
+
export declare const toBindingsResources: (result: AgentBuildResult) => BindingsResource[];
|
|
13
|
+
/**
|
|
14
|
+
* Merge agent binding resources into the flow's bindings_v2.json.
|
|
15
|
+
* Deduplicates by (resource, key) tuple, keeping the first occurrence.
|
|
16
|
+
*/
|
|
17
|
+
export declare const mergeAgentBindings: (fileSystem: IFileSystem, contentFolder: string, agentBindingResources: BindingsResource[]) => Promise<void>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type IFileSystem, type IProjectToolFactory, type IToolLogger, type ProjectTool, type ProjectType } from "@uipath/solutionpackager-tool-core";
|
|
2
|
+
/**
|
|
3
|
+
* Factory for creating Flow project tools
|
|
4
|
+
*/
|
|
5
|
+
export declare class FlowToolFactory implements IProjectToolFactory {
|
|
6
|
+
readonly supportedTypes: readonly ProjectType[];
|
|
7
|
+
createAsync(logger: IToolLogger, fileSystem: IFileSystem): Promise<ProjectTool>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { IProjectBuildOptions, IProjectPackOptions, IProjectRestoreOptions, IProjectValidateOptions } from "@uipath/solutionpackager-tool-core";
|
|
2
|
+
import { type IFileSystem, type IToolLogger, ProjectTool, ToolResult } from "@uipath/solutionpackager-tool-core";
|
|
3
|
+
/**
|
|
4
|
+
* Flow project tool implementation
|
|
5
|
+
*/
|
|
6
|
+
export declare class FlowTool extends ProjectTool {
|
|
7
|
+
private readonly _temporaryStorage;
|
|
8
|
+
constructor(fileSystem: IFileSystem, logger: IToolLogger);
|
|
9
|
+
restoreAsync(_options: IProjectRestoreOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
10
|
+
validateAsync(_options: IProjectValidateOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
11
|
+
buildAsync(options: IProjectBuildOptions, _cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
12
|
+
packAsync(options: IProjectPackOptions, cancellationToken?: AbortSignal): Promise<ToolResult>;
|
|
13
|
+
dispose(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Copy project files into the correct nupkg structure.
|
|
16
|
+
*
|
|
17
|
+
* Flow projects have two layouts:
|
|
18
|
+
* 1. Structured: content/ subdirectory
|
|
19
|
+
* 2. Flat: .bpmn, .flow, and config files at the project root
|
|
20
|
+
*
|
|
21
|
+
* In the nupkg, all files go under content/. This avoids the double-nesting
|
|
22
|
+
* bug where content/content/ would be created.
|
|
23
|
+
*/
|
|
24
|
+
private copyProjectFiles;
|
|
25
|
+
/**
|
|
26
|
+
* Recursively copy all files and subdirectories from source to destination.
|
|
27
|
+
*/
|
|
28
|
+
private copyDirectoryContents;
|
|
29
|
+
/**
|
|
30
|
+
* Create operate.json file if it doesn't already exist
|
|
31
|
+
*/
|
|
32
|
+
private createOperateFile;
|
|
33
|
+
/**
|
|
34
|
+
* Create the operate.json file with project configuration
|
|
35
|
+
*/
|
|
36
|
+
private createOperateJsonFile;
|
|
37
|
+
/**
|
|
38
|
+
* Create package-descriptor.json file.
|
|
39
|
+
* Lists standard packaging files plus .bpmn and .flow files from content/.
|
|
40
|
+
*/
|
|
41
|
+
private createPackageDescriptor;
|
|
42
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { toolsFactoryRepository } from "@uipath/solutionpackager-tool-core";
|
|
3
|
+
|
|
4
|
+
// src/flow-tool-factory.ts
|
|
5
|
+
import {
|
|
6
|
+
ProjectTypes as ProjectTypes2
|
|
7
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
8
|
+
|
|
9
|
+
// src/flow-tool.ts
|
|
10
|
+
import {
|
|
11
|
+
NugetConstants,
|
|
12
|
+
NugetPackager,
|
|
13
|
+
Path as Path3,
|
|
14
|
+
ProjectTool,
|
|
15
|
+
ProjectTypes,
|
|
16
|
+
TemporaryStorageService,
|
|
17
|
+
ToolErrorCodes,
|
|
18
|
+
ToolResult
|
|
19
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
20
|
+
|
|
21
|
+
// src/agents.ts
|
|
22
|
+
import { Path as Path2 } from "@uipath/solutionpackager-tool-core";
|
|
23
|
+
import { buildAgent } from "@uipath/tool-agent/build";
|
|
24
|
+
|
|
25
|
+
// src/bindings.ts
|
|
26
|
+
import { Path } from "@uipath/solutionpackager-tool-core";
|
|
27
|
+
var BINDINGS_V2_FILE_NAME = "bindings_v2.json";
|
|
28
|
+
var toBindingsResources = (result) => result.bindings.resources.map((r) => ({ ...r }));
|
|
29
|
+
var mergeAgentBindings = async (fileSystem, contentFolder, agentBindingResources) => {
|
|
30
|
+
const bindingsPath = Path.join(contentFolder, BINDINGS_V2_FILE_NAME);
|
|
31
|
+
let existing = { version: "2.0", resources: [] };
|
|
32
|
+
const bindingsExists = await fileSystem.exists(bindingsPath);
|
|
33
|
+
if (bindingsExists) {
|
|
34
|
+
const content = await fileSystem.readFile(bindingsPath);
|
|
35
|
+
if (content) {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(new TextDecoder().decode(content));
|
|
38
|
+
if (Array.isArray(parsed?.resources)) {
|
|
39
|
+
existing = parsed;
|
|
40
|
+
}
|
|
41
|
+
} catch {}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const dedupKey = (b) => JSON.stringify([b.resource, b.key]);
|
|
45
|
+
const seen = new Set(existing.resources.map(dedupKey));
|
|
46
|
+
for (const binding of agentBindingResources) {
|
|
47
|
+
const key = dedupKey(binding);
|
|
48
|
+
if (!seen.has(key)) {
|
|
49
|
+
seen.add(key);
|
|
50
|
+
existing.resources.push(binding);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
await fileSystem.writeFile(bindingsPath, JSON.stringify(existing, null, 2));
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/agents.ts
|
|
57
|
+
var processAgents = async (fileSystem, logger, projectPath, contentFolder) => {
|
|
58
|
+
const agentResults = [];
|
|
59
|
+
const entries = await fileSystem.readdir(projectPath);
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
const sourceDir = Path2.join(projectPath, entry);
|
|
62
|
+
const stat = await fileSystem.stat(sourceDir);
|
|
63
|
+
if (!stat?.isDirectory()) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const agentJsonPath = Path2.join(sourceDir, "agent.json");
|
|
67
|
+
if (!await fileSystem.exists(agentJsonPath)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const outputDir = Path2.join(contentFolder, entry);
|
|
71
|
+
logger.progress(`Processing agent: ${entry}...`);
|
|
72
|
+
const result = await buildAgent({
|
|
73
|
+
fileSystem,
|
|
74
|
+
projectPath: sourceDir,
|
|
75
|
+
outputPath: outputDir,
|
|
76
|
+
logger
|
|
77
|
+
});
|
|
78
|
+
agentResults.push(result);
|
|
79
|
+
}
|
|
80
|
+
const agentBindingResources = agentResults.flatMap(toBindingsResources);
|
|
81
|
+
if (agentBindingResources.length > 0) {
|
|
82
|
+
await mergeAgentBindings(fileSystem, contentFolder, agentBindingResources);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/flow-tool.ts
|
|
87
|
+
var FlowConstants = {
|
|
88
|
+
EntryPointsFileName: "entry-points.json",
|
|
89
|
+
BindingsV2FileName: "bindings_v2.json"
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
class FlowTool extends ProjectTool {
|
|
93
|
+
_temporaryStorage;
|
|
94
|
+
constructor(fileSystem, logger) {
|
|
95
|
+
super(fileSystem, logger);
|
|
96
|
+
this._temporaryStorage = new TemporaryStorageService(fileSystem);
|
|
97
|
+
}
|
|
98
|
+
async restoreAsync(_options, _cancellationToken) {
|
|
99
|
+
this.logger.info("Restore operation is not required for Flow projects");
|
|
100
|
+
return ToolResult.success();
|
|
101
|
+
}
|
|
102
|
+
async validateAsync(_options, _cancellationToken) {
|
|
103
|
+
this.logger.info("Validate operation is not required for Flow projects");
|
|
104
|
+
return ToolResult.success();
|
|
105
|
+
}
|
|
106
|
+
async buildAsync(options, _cancellationToken) {
|
|
107
|
+
const tempFolder = await this._temporaryStorage.getTempFolderPath();
|
|
108
|
+
const localBuildFolder = Path3.join(tempFolder, NugetConstants.OutputFolderName);
|
|
109
|
+
const contentFolder = Path3.join(localBuildFolder, NugetConstants.ContentFolderName);
|
|
110
|
+
try {
|
|
111
|
+
this.logger.progress("Copying files...");
|
|
112
|
+
await this.copyProjectFiles(options.projectPath, contentFolder);
|
|
113
|
+
this.logger.progress("Processing agents...");
|
|
114
|
+
await processAgents(this.fileSystem, this.logger, options.projectPath, contentFolder);
|
|
115
|
+
this.logger.progress("Creating operate.json file...");
|
|
116
|
+
await this.createOperateFile(options, contentFolder);
|
|
117
|
+
this.logger.progress("Creating package-descriptor.json file...");
|
|
118
|
+
await this.createPackageDescriptor(localBuildFolder, contentFolder);
|
|
119
|
+
return new ToolResult(ToolErrorCodes.Success, "done", [
|
|
120
|
+
localBuildFolder
|
|
121
|
+
]);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
const errorMessage = error instanceof Error ? error.toString() : String(error);
|
|
124
|
+
this.logger.error(errorMessage);
|
|
125
|
+
return ToolResult.error(ToolErrorCodes.InternalError, "An error occurred while building Flow project files");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async packAsync(options, cancellationToken) {
|
|
129
|
+
const buildResult = await this.buildAsync(options, cancellationToken);
|
|
130
|
+
if (!buildResult.isSuccess) {
|
|
131
|
+
return buildResult;
|
|
132
|
+
}
|
|
133
|
+
const localBuildFolder = buildResult.packages[0];
|
|
134
|
+
try {
|
|
135
|
+
this.logger.progress("Creating NuGet package...");
|
|
136
|
+
const nupkgFileName = `${options.package.id}.${options.package.version}.nupkg`;
|
|
137
|
+
const nupkgPath = Path3.join(options.outputPath, nupkgFileName);
|
|
138
|
+
const packager = new NugetPackager(this.fileSystem);
|
|
139
|
+
const result = await packager.packAsync(localBuildFolder, options.package, nupkgPath);
|
|
140
|
+
this.logger.progress("Package created successfully");
|
|
141
|
+
return new ToolResult(ToolErrorCodes.Success, "done", [
|
|
142
|
+
result.outputPath
|
|
143
|
+
]);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const errorMessage = error instanceof Error ? error.toString() : String(error);
|
|
146
|
+
this.logger.error(errorMessage);
|
|
147
|
+
return ToolResult.error(ToolErrorCodes.InternalError, "An error occurred while packing Flow project");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async dispose() {
|
|
151
|
+
this.logger.info("Disposing Flow Tool");
|
|
152
|
+
try {
|
|
153
|
+
await this._temporaryStorage.cleanup();
|
|
154
|
+
} catch {}
|
|
155
|
+
}
|
|
156
|
+
async copyProjectFiles(projectPath, contentFolder) {
|
|
157
|
+
await this.fileSystem.mkdir(contentFolder);
|
|
158
|
+
const entries = await this.fileSystem.readdir(projectPath);
|
|
159
|
+
for (const entry of entries) {
|
|
160
|
+
const sourceEntry = Path3.join(projectPath, entry);
|
|
161
|
+
const stat = await this.fileSystem.stat(sourceEntry);
|
|
162
|
+
if (stat?.isDirectory()) {
|
|
163
|
+
if (entry === NugetConstants.ContentFolderName) {
|
|
164
|
+
await this.copyDirectoryContents(sourceEntry, contentFolder);
|
|
165
|
+
}
|
|
166
|
+
} else if (stat?.isFile()) {
|
|
167
|
+
if (entry === "project.uiproj") {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const content = await this.fileSystem.readFile(sourceEntry);
|
|
171
|
+
if (content) {
|
|
172
|
+
await this.fileSystem.writeFile(Path3.join(contentFolder, entry), content);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async copyDirectoryContents(sourcePath, destinationPath) {
|
|
178
|
+
await this.fileSystem.mkdir(destinationPath);
|
|
179
|
+
const entries = await this.fileSystem.readdir(sourcePath);
|
|
180
|
+
for (const entry of entries) {
|
|
181
|
+
const sourceEntry = Path3.join(sourcePath, entry);
|
|
182
|
+
const destinationEntry = Path3.join(destinationPath, entry);
|
|
183
|
+
const stat = await this.fileSystem.stat(sourceEntry);
|
|
184
|
+
if (stat?.isDirectory()) {
|
|
185
|
+
await this.copyDirectoryContents(sourceEntry, destinationEntry);
|
|
186
|
+
} else if (stat?.isFile()) {
|
|
187
|
+
const content = await this.fileSystem.readFile(sourceEntry);
|
|
188
|
+
if (content) {
|
|
189
|
+
await this.fileSystem.writeFile(destinationEntry, content);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async createOperateFile(options, contentFolder) {
|
|
195
|
+
const operateJsonFilePath = Path3.join(contentFolder, NugetConstants.OperateFileName);
|
|
196
|
+
const exists = await this.fileSystem.exists(operateJsonFilePath);
|
|
197
|
+
if (!exists) {
|
|
198
|
+
await this.createOperateJsonFile(contentFolder, options.projectStorageId ?? "", operateJsonFilePath);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async createOperateJsonFile(projectPath, projectId, filePath) {
|
|
202
|
+
const entryPointsFilePath = Path3.join(projectPath, FlowConstants.EntryPointsFileName);
|
|
203
|
+
let mainPath = "";
|
|
204
|
+
const entryPointsExists = await this.fileSystem.exists(entryPointsFilePath);
|
|
205
|
+
if (entryPointsExists) {
|
|
206
|
+
const entryPointsContent = await this.fileSystem.readFile(entryPointsFilePath);
|
|
207
|
+
if (entryPointsContent) {
|
|
208
|
+
try {
|
|
209
|
+
const entryPointsText = new TextDecoder().decode(entryPointsContent);
|
|
210
|
+
const entryPoints = JSON.parse(entryPointsText);
|
|
211
|
+
mainPath = entryPoints.entryPoints?.[0]?.filePath ?? "";
|
|
212
|
+
} catch {}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const operateFileModel = {
|
|
216
|
+
$schema: "https://cloud.uipath.com/draft/2024-12/operate",
|
|
217
|
+
contentType: ProjectTypes.Flow,
|
|
218
|
+
projectId,
|
|
219
|
+
main: mainPath,
|
|
220
|
+
targetFramework: "Portable",
|
|
221
|
+
runtimeOptions: {
|
|
222
|
+
isAttended: false,
|
|
223
|
+
requiresUserInteraction: false
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const operateJsonString = JSON.stringify(operateFileModel, null, 2);
|
|
227
|
+
await this.fileSystem.writeFile(filePath, operateJsonString);
|
|
228
|
+
}
|
|
229
|
+
async createPackageDescriptor(localBuildFolder, contentFolder) {
|
|
230
|
+
const descriptorFiles = {};
|
|
231
|
+
descriptorFiles[NugetConstants.OperateFileName] = Path3.join(NugetConstants.ContentFolderName, NugetConstants.OperateFileName);
|
|
232
|
+
descriptorFiles[FlowConstants.EntryPointsFileName] = Path3.join(NugetConstants.ContentFolderName, FlowConstants.EntryPointsFileName);
|
|
233
|
+
descriptorFiles[NugetConstants.BindingsFileId] = Path3.join(NugetConstants.ContentFolderName, FlowConstants.BindingsV2FileName);
|
|
234
|
+
const contentEntries = await Path3.walkDirectory(this.fileSystem, contentFolder);
|
|
235
|
+
for (const entry of contentEntries) {
|
|
236
|
+
const ext = Path3.extname(entry.relativePath);
|
|
237
|
+
if (ext === ".bpmn" || ext === ".flow") {
|
|
238
|
+
descriptorFiles[entry.relativePath] = Path3.join(NugetConstants.ContentFolderName, entry.relativePath);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const packageDescriptorPath = Path3.join(localBuildFolder, NugetConstants.ContentFolderName, NugetConstants.PackageDescriptorFileName);
|
|
242
|
+
const packageDescriptorJson = JSON.stringify({
|
|
243
|
+
$schema: "https://cloud.uipath.com/draft/2024-12/package-descriptor",
|
|
244
|
+
files: descriptorFiles
|
|
245
|
+
}, null, 2);
|
|
246
|
+
await this.fileSystem.writeFile(packageDescriptorPath, packageDescriptorJson);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/flow-tool-factory.ts
|
|
251
|
+
class FlowToolFactory {
|
|
252
|
+
supportedTypes = [ProjectTypes2.Flow];
|
|
253
|
+
async createAsync(logger, fileSystem) {
|
|
254
|
+
return new FlowTool(fileSystem, logger);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/index.ts
|
|
259
|
+
toolsFactoryRepository.registerProjectToolFactory(new FlowToolFactory);
|
|
260
|
+
export {
|
|
261
|
+
FlowToolFactory,
|
|
262
|
+
FlowTool
|
|
263
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uipath/packager-tool-flow",
|
|
3
|
+
"version": "0.0.17",
|
|
4
|
+
"description": "UiPath Flow tool implementation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"source": "./src/index.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/UiPath/cli.git",
|
|
15
|
+
"directory": "packages/packager/packager-tool-flow"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"registry": "https://registry.npmjs.org/"
|
|
19
|
+
},
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "bun build ./src/index.ts --outdir dist --format esm --target browser --external @uipath/solutionpackager-tool-core --external @uipath/tool-agent && tsc --emitDeclarationOnly --outDir dist",
|
|
23
|
+
"dev": "bun build ./src/index.ts --outdir dist --format esm --target browser --external @uipath/solutionpackager-tool-core --external @uipath/tool-agent --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:browser": "vitest run --config=vitest.browser.config.ts",
|
|
26
|
+
"test:coverage": "vitest run --coverage",
|
|
27
|
+
"test:browser:coverage": "vitest run --config=vitest.browser.config.ts --coverage",
|
|
28
|
+
"test:all": "bun run test && bun run test:browser",
|
|
29
|
+
"test:all:coverage": "bun run test:coverage && bun run test:browser:coverage",
|
|
30
|
+
"prepack": "bun run build",
|
|
31
|
+
"publish:dry": "bun publish --dry-run",
|
|
32
|
+
"publish:gh": "bun publish",
|
|
33
|
+
"version:patch": "bun version patch --no-git-tag-version",
|
|
34
|
+
"version:minor": "bun version minor --no-git-tag-version",
|
|
35
|
+
"version:major": "bun version major --no-git-tag-version",
|
|
36
|
+
"lint": "biome check ."
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"!dist/**/*.map",
|
|
41
|
+
"src"
|
|
42
|
+
],
|
|
43
|
+
"author": "",
|
|
44
|
+
"license": "ISC",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@uipath/solutionpackager-tool-core": "0.0.31",
|
|
47
|
+
"@uipath/tool-agent": "^1.0.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^25.5.0",
|
|
51
|
+
"@uipath/solutionpackager-tool-core": "0.0.31",
|
|
52
|
+
"@uipath/tool-agent": "^1.0.1",
|
|
53
|
+
"@vitest/browser": "^4.0.14",
|
|
54
|
+
"@vitest/browser-playwright": "^4.0.14",
|
|
55
|
+
"@vitest/coverage-v8": "^4.0.14",
|
|
56
|
+
"playwright": "^1.57.0",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitest": "^4.0.14"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/agents.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IFileSystem,
|
|
3
|
+
IToolLogger,
|
|
4
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
5
|
+
import { Path } from "@uipath/solutionpackager-tool-core";
|
|
6
|
+
import { type AgentBuildResult, buildAgent } from "@uipath/tool-agent/build";
|
|
7
|
+
import { mergeAgentBindings, toBindingsResources } from "./bindings.js";
|
|
8
|
+
|
|
9
|
+
export const processAgents = async (
|
|
10
|
+
fileSystem: IFileSystem,
|
|
11
|
+
logger: IToolLogger,
|
|
12
|
+
projectPath: string,
|
|
13
|
+
contentFolder: string,
|
|
14
|
+
): Promise<void> => {
|
|
15
|
+
const agentResults: AgentBuildResult[] = [];
|
|
16
|
+
|
|
17
|
+
const entries = await fileSystem.readdir(projectPath);
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const sourceDir = Path.join(projectPath, entry);
|
|
20
|
+
const stat = await fileSystem.stat(sourceDir);
|
|
21
|
+
if (!stat?.isDirectory()) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const agentJsonPath = Path.join(sourceDir, "agent.json");
|
|
26
|
+
if (!(await fileSystem.exists(agentJsonPath))) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const outputDir = Path.join(contentFolder, entry);
|
|
31
|
+
logger.progress(`Processing agent: ${entry}...`);
|
|
32
|
+
|
|
33
|
+
const result = await buildAgent({
|
|
34
|
+
fileSystem,
|
|
35
|
+
projectPath: sourceDir,
|
|
36
|
+
outputPath: outputDir,
|
|
37
|
+
logger,
|
|
38
|
+
});
|
|
39
|
+
agentResults.push(result);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const agentBindingResources = agentResults.flatMap(toBindingsResources);
|
|
43
|
+
if (agentBindingResources.length > 0) {
|
|
44
|
+
await mergeAgentBindings(
|
|
45
|
+
fileSystem,
|
|
46
|
+
contentFolder,
|
|
47
|
+
agentBindingResources,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
};
|
package/src/bindings.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { IFileSystem } from "@uipath/solutionpackager-tool-core";
|
|
2
|
+
import { Path } from "@uipath/solutionpackager-tool-core";
|
|
3
|
+
import type { AgentBuildResult } from "@uipath/tool-agent/build";
|
|
4
|
+
|
|
5
|
+
export interface BindingsResource {
|
|
6
|
+
resource: string;
|
|
7
|
+
key: string;
|
|
8
|
+
[other: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Bindings {
|
|
12
|
+
version: string;
|
|
13
|
+
resources: BindingsResource[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const BINDINGS_V2_FILE_NAME = "bindings_v2.json";
|
|
17
|
+
|
|
18
|
+
export const toBindingsResources = (
|
|
19
|
+
result: AgentBuildResult,
|
|
20
|
+
): BindingsResource[] => result.bindings.resources.map((r) => ({ ...r }));
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Merge agent binding resources into the flow's bindings_v2.json.
|
|
24
|
+
* Deduplicates by (resource, key) tuple, keeping the first occurrence.
|
|
25
|
+
*/
|
|
26
|
+
export const mergeAgentBindings = async (
|
|
27
|
+
fileSystem: IFileSystem,
|
|
28
|
+
contentFolder: string,
|
|
29
|
+
agentBindingResources: BindingsResource[],
|
|
30
|
+
): Promise<void> => {
|
|
31
|
+
const bindingsPath = Path.join(contentFolder, BINDINGS_V2_FILE_NAME);
|
|
32
|
+
|
|
33
|
+
let existing: Bindings = { version: "2.0", resources: [] };
|
|
34
|
+
|
|
35
|
+
const bindingsExists = await fileSystem.exists(bindingsPath);
|
|
36
|
+
if (bindingsExists) {
|
|
37
|
+
const content = await fileSystem.readFile(bindingsPath);
|
|
38
|
+
if (content) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(new TextDecoder().decode(content));
|
|
41
|
+
if (Array.isArray(parsed?.resources)) {
|
|
42
|
+
existing = parsed;
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
// Invalid JSON, start fresh
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const dedupKey = (b: BindingsResource) =>
|
|
51
|
+
JSON.stringify([b.resource, b.key]);
|
|
52
|
+
|
|
53
|
+
const seen = new Set(existing.resources.map(dedupKey));
|
|
54
|
+
|
|
55
|
+
for (const binding of agentBindingResources) {
|
|
56
|
+
const key = dedupKey(binding);
|
|
57
|
+
if (!seen.has(key)) {
|
|
58
|
+
seen.add(key);
|
|
59
|
+
existing.resources.push(binding);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await fileSystem.writeFile(bindingsPath, JSON.stringify(existing, null, 2));
|
|
64
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type IFileSystem,
|
|
3
|
+
type IProjectToolFactory,
|
|
4
|
+
type IToolLogger,
|
|
5
|
+
type ProjectTool,
|
|
6
|
+
type ProjectType,
|
|
7
|
+
ProjectTypes,
|
|
8
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
9
|
+
import { FlowTool } from "./flow-tool.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Factory for creating Flow project tools
|
|
13
|
+
*/
|
|
14
|
+
export class FlowToolFactory implements IProjectToolFactory {
|
|
15
|
+
readonly supportedTypes: readonly ProjectType[] = [ProjectTypes.Flow];
|
|
16
|
+
|
|
17
|
+
async createAsync(
|
|
18
|
+
logger: IToolLogger,
|
|
19
|
+
fileSystem: IFileSystem,
|
|
20
|
+
): Promise<ProjectTool> {
|
|
21
|
+
return new FlowTool(fileSystem, logger);
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/flow-tool.ts
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EntryPointsFileModel,
|
|
3
|
+
IProjectBuildOptions,
|
|
4
|
+
IProjectPackOptions,
|
|
5
|
+
IProjectRestoreOptions,
|
|
6
|
+
IProjectValidateOptions,
|
|
7
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
8
|
+
import {
|
|
9
|
+
type IFileSystem,
|
|
10
|
+
type IToolLogger,
|
|
11
|
+
NugetConstants,
|
|
12
|
+
NugetPackager,
|
|
13
|
+
Path,
|
|
14
|
+
ProjectTool,
|
|
15
|
+
ProjectTypes,
|
|
16
|
+
TemporaryStorageService,
|
|
17
|
+
ToolErrorCodes,
|
|
18
|
+
ToolResult,
|
|
19
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
20
|
+
import { processAgents } from "./agents.js";
|
|
21
|
+
|
|
22
|
+
const FlowConstants = {
|
|
23
|
+
EntryPointsFileName: "entry-points.json",
|
|
24
|
+
BindingsV2FileName: "bindings_v2.json",
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Flow project tool implementation
|
|
29
|
+
*/
|
|
30
|
+
export class FlowTool extends ProjectTool {
|
|
31
|
+
private readonly _temporaryStorage: TemporaryStorageService;
|
|
32
|
+
|
|
33
|
+
constructor(fileSystem: IFileSystem, logger: IToolLogger) {
|
|
34
|
+
super(fileSystem, logger);
|
|
35
|
+
this._temporaryStorage = new TemporaryStorageService(fileSystem);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
override async restoreAsync(
|
|
39
|
+
_options: IProjectRestoreOptions,
|
|
40
|
+
_cancellationToken?: AbortSignal,
|
|
41
|
+
): Promise<ToolResult> {
|
|
42
|
+
this.logger.info("Restore operation is not required for Flow projects");
|
|
43
|
+
return ToolResult.success();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override async validateAsync(
|
|
47
|
+
_options: IProjectValidateOptions,
|
|
48
|
+
_cancellationToken?: AbortSignal,
|
|
49
|
+
): Promise<ToolResult> {
|
|
50
|
+
this.logger.info(
|
|
51
|
+
"Validate operation is not required for Flow projects",
|
|
52
|
+
);
|
|
53
|
+
return ToolResult.success();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override async buildAsync(
|
|
57
|
+
options: IProjectBuildOptions,
|
|
58
|
+
_cancellationToken?: AbortSignal,
|
|
59
|
+
): Promise<ToolResult> {
|
|
60
|
+
const tempFolder = await this._temporaryStorage.getTempFolderPath();
|
|
61
|
+
const localBuildFolder = Path.join(
|
|
62
|
+
tempFolder,
|
|
63
|
+
NugetConstants.OutputFolderName,
|
|
64
|
+
);
|
|
65
|
+
const contentFolder = Path.join(
|
|
66
|
+
localBuildFolder,
|
|
67
|
+
NugetConstants.ContentFolderName,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
this.logger.progress("Copying files...");
|
|
72
|
+
await this.copyProjectFiles(options.projectPath, contentFolder);
|
|
73
|
+
|
|
74
|
+
this.logger.progress("Processing agents...");
|
|
75
|
+
await processAgents(
|
|
76
|
+
this.fileSystem,
|
|
77
|
+
this.logger,
|
|
78
|
+
options.projectPath,
|
|
79
|
+
contentFolder,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
this.logger.progress("Creating operate.json file...");
|
|
83
|
+
await this.createOperateFile(options, contentFolder);
|
|
84
|
+
|
|
85
|
+
this.logger.progress("Creating package-descriptor.json file...");
|
|
86
|
+
await this.createPackageDescriptor(localBuildFolder, contentFolder);
|
|
87
|
+
|
|
88
|
+
return new ToolResult(ToolErrorCodes.Success, "done", [
|
|
89
|
+
localBuildFolder,
|
|
90
|
+
]);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
const errorMessage =
|
|
93
|
+
error instanceof Error ? error.toString() : String(error);
|
|
94
|
+
this.logger.error(errorMessage);
|
|
95
|
+
return ToolResult.error(
|
|
96
|
+
ToolErrorCodes.InternalError,
|
|
97
|
+
"An error occurred while building Flow project files",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
override async packAsync(
|
|
103
|
+
options: IProjectPackOptions,
|
|
104
|
+
cancellationToken?: AbortSignal,
|
|
105
|
+
): Promise<ToolResult> {
|
|
106
|
+
// First, run the build step to prepare the bundle content
|
|
107
|
+
const buildResult = await this.buildAsync(options, cancellationToken);
|
|
108
|
+
if (!buildResult.isSuccess) {
|
|
109
|
+
return buildResult;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const localBuildFolder = buildResult.packages[0];
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
// Create the NuGet package
|
|
116
|
+
this.logger.progress("Creating NuGet package...");
|
|
117
|
+
|
|
118
|
+
const nupkgFileName = `${options.package.id}.${options.package.version}.nupkg`;
|
|
119
|
+
const nupkgPath = Path.join(options.outputPath, nupkgFileName);
|
|
120
|
+
|
|
121
|
+
const packager = new NugetPackager(this.fileSystem);
|
|
122
|
+
const result = await packager.packAsync(
|
|
123
|
+
localBuildFolder,
|
|
124
|
+
options.package,
|
|
125
|
+
nupkgPath,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
this.logger.progress("Package created successfully");
|
|
129
|
+
return new ToolResult(ToolErrorCodes.Success, "done", [
|
|
130
|
+
result.outputPath,
|
|
131
|
+
]);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
const errorMessage =
|
|
134
|
+
error instanceof Error ? error.toString() : String(error);
|
|
135
|
+
this.logger.error(errorMessage);
|
|
136
|
+
return ToolResult.error(
|
|
137
|
+
ToolErrorCodes.InternalError,
|
|
138
|
+
"An error occurred while packing Flow project",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
override async dispose(): Promise<void> {
|
|
144
|
+
this.logger.info("Disposing Flow Tool");
|
|
145
|
+
try {
|
|
146
|
+
await this._temporaryStorage.cleanup();
|
|
147
|
+
} catch {
|
|
148
|
+
// Ignore errors during cleanup
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Copy project files into the correct nupkg structure.
|
|
154
|
+
*
|
|
155
|
+
* Flow projects have two layouts:
|
|
156
|
+
* 1. Structured: content/ subdirectory
|
|
157
|
+
* 2. Flat: .bpmn, .flow, and config files at the project root
|
|
158
|
+
*
|
|
159
|
+
* In the nupkg, all files go under content/. This avoids the double-nesting
|
|
160
|
+
* bug where content/content/ would be created.
|
|
161
|
+
*/
|
|
162
|
+
private async copyProjectFiles(
|
|
163
|
+
projectPath: string,
|
|
164
|
+
contentFolder: string,
|
|
165
|
+
): Promise<void> {
|
|
166
|
+
await this.fileSystem.mkdir(contentFolder);
|
|
167
|
+
|
|
168
|
+
const entries = await this.fileSystem.readdir(projectPath);
|
|
169
|
+
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
const sourceEntry = Path.join(projectPath, entry);
|
|
172
|
+
const stat = await this.fileSystem.stat(sourceEntry);
|
|
173
|
+
|
|
174
|
+
if (stat?.isDirectory()) {
|
|
175
|
+
if (entry === NugetConstants.ContentFolderName) {
|
|
176
|
+
// Copy content/ subdir contents directly into contentFolder.
|
|
177
|
+
await this.copyDirectoryContents(
|
|
178
|
+
sourceEntry,
|
|
179
|
+
contentFolder,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
// Other directories (agent projects, .local, .git, etc.)
|
|
183
|
+
// are not copied. Agent dirs are handled by processAgents
|
|
184
|
+
// which writes only the processed agent.json via buildAgent.
|
|
185
|
+
} else if (stat?.isFile()) {
|
|
186
|
+
// Skip the project.uiproj project metadata file
|
|
187
|
+
if (entry === "project.uiproj") {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const content = await this.fileSystem.readFile(sourceEntry);
|
|
192
|
+
if (content) {
|
|
193
|
+
await this.fileSystem.writeFile(
|
|
194
|
+
Path.join(contentFolder, entry),
|
|
195
|
+
content,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Recursively copy all files and subdirectories from source to destination.
|
|
204
|
+
*/
|
|
205
|
+
private async copyDirectoryContents(
|
|
206
|
+
sourcePath: string,
|
|
207
|
+
destinationPath: string,
|
|
208
|
+
): Promise<void> {
|
|
209
|
+
await this.fileSystem.mkdir(destinationPath);
|
|
210
|
+
|
|
211
|
+
const entries = await this.fileSystem.readdir(sourcePath);
|
|
212
|
+
|
|
213
|
+
for (const entry of entries) {
|
|
214
|
+
const sourceEntry = Path.join(sourcePath, entry);
|
|
215
|
+
const destinationEntry = Path.join(destinationPath, entry);
|
|
216
|
+
|
|
217
|
+
const stat = await this.fileSystem.stat(sourceEntry);
|
|
218
|
+
if (stat?.isDirectory()) {
|
|
219
|
+
await this.copyDirectoryContents(sourceEntry, destinationEntry);
|
|
220
|
+
} else if (stat?.isFile()) {
|
|
221
|
+
const content = await this.fileSystem.readFile(sourceEntry);
|
|
222
|
+
if (content) {
|
|
223
|
+
await this.fileSystem.writeFile(destinationEntry, content);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Create operate.json file if it doesn't already exist
|
|
231
|
+
*/
|
|
232
|
+
private async createOperateFile(
|
|
233
|
+
options: IProjectBuildOptions,
|
|
234
|
+
contentFolder: string,
|
|
235
|
+
): Promise<void> {
|
|
236
|
+
const operateJsonFilePath = Path.join(
|
|
237
|
+
contentFolder,
|
|
238
|
+
NugetConstants.OperateFileName,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
const exists = await this.fileSystem.exists(operateJsonFilePath);
|
|
242
|
+
if (!exists) {
|
|
243
|
+
await this.createOperateJsonFile(
|
|
244
|
+
contentFolder,
|
|
245
|
+
options.projectStorageId ?? "",
|
|
246
|
+
operateJsonFilePath,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Create the operate.json file with project configuration
|
|
253
|
+
*/
|
|
254
|
+
private async createOperateJsonFile(
|
|
255
|
+
projectPath: string,
|
|
256
|
+
projectId: string,
|
|
257
|
+
filePath: string,
|
|
258
|
+
): Promise<void> {
|
|
259
|
+
// Flow uses 'entry-points.json' (with dash), not 'entrypoints.json'
|
|
260
|
+
const entryPointsFilePath = Path.join(
|
|
261
|
+
projectPath,
|
|
262
|
+
FlowConstants.EntryPointsFileName,
|
|
263
|
+
);
|
|
264
|
+
let mainPath = "";
|
|
265
|
+
|
|
266
|
+
const entryPointsExists =
|
|
267
|
+
await this.fileSystem.exists(entryPointsFilePath);
|
|
268
|
+
if (entryPointsExists) {
|
|
269
|
+
const entryPointsContent =
|
|
270
|
+
await this.fileSystem.readFile(entryPointsFilePath);
|
|
271
|
+
if (entryPointsContent) {
|
|
272
|
+
try {
|
|
273
|
+
const entryPointsText = new TextDecoder().decode(
|
|
274
|
+
entryPointsContent,
|
|
275
|
+
);
|
|
276
|
+
const entryPoints: EntryPointsFileModel =
|
|
277
|
+
JSON.parse(entryPointsText);
|
|
278
|
+
mainPath = entryPoints.entryPoints?.[0]?.filePath ?? "";
|
|
279
|
+
} catch {
|
|
280
|
+
// Ignore parsing errors, use empty mainPath
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const operateFileModel = {
|
|
286
|
+
$schema: "https://cloud.uipath.com/draft/2024-12/operate",
|
|
287
|
+
contentType: ProjectTypes.Flow,
|
|
288
|
+
projectId: projectId,
|
|
289
|
+
main: mainPath,
|
|
290
|
+
targetFramework: "Portable",
|
|
291
|
+
runtimeOptions: {
|
|
292
|
+
isAttended: false,
|
|
293
|
+
requiresUserInteraction: false,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const operateJsonString = JSON.stringify(operateFileModel, null, 2);
|
|
298
|
+
await this.fileSystem.writeFile(filePath, operateJsonString);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Create package-descriptor.json file.
|
|
303
|
+
* Lists standard packaging files plus .bpmn and .flow files from content/.
|
|
304
|
+
*/
|
|
305
|
+
private async createPackageDescriptor(
|
|
306
|
+
localBuildFolder: string,
|
|
307
|
+
contentFolder: string,
|
|
308
|
+
): Promise<void> {
|
|
309
|
+
const descriptorFiles: Record<string, string> = {};
|
|
310
|
+
|
|
311
|
+
// Standard packaging files
|
|
312
|
+
descriptorFiles[NugetConstants.OperateFileName] = Path.join(
|
|
313
|
+
NugetConstants.ContentFolderName,
|
|
314
|
+
NugetConstants.OperateFileName,
|
|
315
|
+
);
|
|
316
|
+
descriptorFiles[FlowConstants.EntryPointsFileName] = Path.join(
|
|
317
|
+
NugetConstants.ContentFolderName,
|
|
318
|
+
FlowConstants.EntryPointsFileName,
|
|
319
|
+
);
|
|
320
|
+
descriptorFiles[NugetConstants.BindingsFileId] = Path.join(
|
|
321
|
+
NugetConstants.ContentFolderName,
|
|
322
|
+
FlowConstants.BindingsV2FileName,
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
// Discover .bpmn and .flow files from content/
|
|
326
|
+
const contentEntries = await Path.walkDirectory(
|
|
327
|
+
this.fileSystem,
|
|
328
|
+
contentFolder,
|
|
329
|
+
);
|
|
330
|
+
for (const entry of contentEntries) {
|
|
331
|
+
const ext = Path.extname(entry.relativePath);
|
|
332
|
+
if (ext === ".bpmn" || ext === ".flow") {
|
|
333
|
+
descriptorFiles[entry.relativePath] = Path.join(
|
|
334
|
+
NugetConstants.ContentFolderName,
|
|
335
|
+
entry.relativePath,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const packageDescriptorPath = Path.join(
|
|
341
|
+
localBuildFolder,
|
|
342
|
+
NugetConstants.ContentFolderName,
|
|
343
|
+
NugetConstants.PackageDescriptorFileName,
|
|
344
|
+
);
|
|
345
|
+
const packageDescriptorJson = JSON.stringify(
|
|
346
|
+
{
|
|
347
|
+
$schema:
|
|
348
|
+
"https://cloud.uipath.com/draft/2024-12/package-descriptor",
|
|
349
|
+
files: descriptorFiles,
|
|
350
|
+
},
|
|
351
|
+
null,
|
|
352
|
+
2,
|
|
353
|
+
);
|
|
354
|
+
await this.fileSystem.writeFile(
|
|
355
|
+
packageDescriptorPath,
|
|
356
|
+
packageDescriptorJson,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Self-register Flow tool factory with the singleton repository
|
|
2
|
+
import { toolsFactoryRepository } from "@uipath/solutionpackager-tool-core";
|
|
3
|
+
import { FlowToolFactory } from "./flow-tool-factory.js";
|
|
4
|
+
|
|
5
|
+
export { FlowTool } from "./flow-tool.js";
|
|
6
|
+
export { FlowToolFactory } from "./flow-tool-factory.js";
|
|
7
|
+
|
|
8
|
+
toolsFactoryRepository.registerProjectToolFactory(new FlowToolFactory());
|