@uipath/packager-tool-workflowcompiler-browser 0.0.29
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/i18n/index.d.ts +7 -0
- package/dist/i18n/locales/en.d.ts +40 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1104 -0
- package/dist/pack-result-processor.d.ts +17 -0
- package/dist/packager-hub-connection.d.ts +28 -0
- package/dist/project-bundle-executor.d.ts +26 -0
- package/dist/workflow-compiler-tool-factory.d.ts +12 -0
- package/dist/workflow-compiler-tool.d.ts +16 -0
- package/package.json +60 -0
- package/src/i18n/index.ts +45 -0
- package/src/i18n/locales/de.json +34 -0
- package/src/i18n/locales/en.ts +57 -0
- package/src/i18n/locales/es-MX.json +34 -0
- package/src/i18n/locales/es.json +34 -0
- package/src/i18n/locales/fr.json +34 -0
- package/src/i18n/locales/ja.json +34 -0
- package/src/i18n/locales/ko.json +34 -0
- package/src/i18n/locales/pt-BR.json +34 -0
- package/src/i18n/locales/pt.json +34 -0
- package/src/i18n/locales/ro.json +34 -0
- package/src/i18n/locales/ru.json +34 -0
- package/src/i18n/locales/tr.json +34 -0
- package/src/i18n/locales/zh-CN.json +34 -0
- package/src/i18n/locales/zh-TW.json +34 -0
- package/src/i18n/locales/zu.json +34 -0
- package/src/index.ts +7 -0
- package/src/pack-result-processor.ts +309 -0
- package/src/packager-hub-connection.ts +257 -0
- package/src/project-bundle-executor.ts +414 -0
- package/src/workflow-compiler-tool-factory.ts +47 -0
- package/src/workflow-compiler-tool.ts +72 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpTransportType,
|
|
3
|
+
type HubConnection,
|
|
4
|
+
HubConnectionBuilder,
|
|
5
|
+
HubConnectionState,
|
|
6
|
+
} from "@microsoft/signalr";
|
|
7
|
+
import {
|
|
8
|
+
type IToolLogger,
|
|
9
|
+
LogLevel,
|
|
10
|
+
LogMessage,
|
|
11
|
+
translate,
|
|
12
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
13
|
+
|
|
14
|
+
/** @public */
|
|
15
|
+
export enum CompletedStatus {
|
|
16
|
+
Succeeded = 0,
|
|
17
|
+
Stopped = 1,
|
|
18
|
+
Failed = 2,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface PackAgentResult {
|
|
22
|
+
errorCode: number;
|
|
23
|
+
status: CompletedStatus;
|
|
24
|
+
message?: string;
|
|
25
|
+
bucketId?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ExecutorCommand<T> {
|
|
29
|
+
Id: number;
|
|
30
|
+
Argument1: T;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PackJobResult {
|
|
34
|
+
ErrorCode: number;
|
|
35
|
+
Status: number;
|
|
36
|
+
Message?: string;
|
|
37
|
+
BucketId?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface PackAgentLogMessage {
|
|
41
|
+
Message: string;
|
|
42
|
+
LogLevel: string;
|
|
43
|
+
Source?: string;
|
|
44
|
+
SourceTarget?: string;
|
|
45
|
+
Progress?: number;
|
|
46
|
+
Code?: string;
|
|
47
|
+
FilePath?: string;
|
|
48
|
+
Line?: string;
|
|
49
|
+
Id?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function toLogLevel(level: string): LogLevel {
|
|
53
|
+
switch (level) {
|
|
54
|
+
case "Debug":
|
|
55
|
+
return LogLevel.Debug;
|
|
56
|
+
case "Information":
|
|
57
|
+
return LogLevel.Info;
|
|
58
|
+
case "Warning":
|
|
59
|
+
return LogLevel.Warn;
|
|
60
|
+
case "Error":
|
|
61
|
+
return LogLevel.Error;
|
|
62
|
+
default:
|
|
63
|
+
return LogLevel.Info;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
enum PackagerCommandType {
|
|
68
|
+
PeerDisconnected = "PeerDisconnected",
|
|
69
|
+
SetLogMessage = "SetLogMessage",
|
|
70
|
+
SetExecutionStarted = "SetExecutionStarted",
|
|
71
|
+
SetExecutionCompleted = "SetExecutionCompleted",
|
|
72
|
+
Stop = "Stop",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class PackagerHubConnection {
|
|
76
|
+
private hubConnection: HubConnection | null = null;
|
|
77
|
+
private completionResolve: ((result: PackAgentResult) => void) | null =
|
|
78
|
+
null;
|
|
79
|
+
private completionReject: ((error: Error) => void) | null = null;
|
|
80
|
+
|
|
81
|
+
constructor(private readonly logger?: IToolLogger) {}
|
|
82
|
+
|
|
83
|
+
async connectAsync(hubUrl: string, accessToken: string): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
this.hubConnection = this.buildConnection(hubUrl, accessToken);
|
|
86
|
+
this.registerHandlers();
|
|
87
|
+
this.registerOnClose();
|
|
88
|
+
await this.hubConnection.start();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Retry with SSE/LongPolling if WebSocket fails (matches StudioWeb pattern)
|
|
91
|
+
if (
|
|
92
|
+
error instanceof Error &&
|
|
93
|
+
error.message.toLowerCase().includes("websocket")
|
|
94
|
+
) {
|
|
95
|
+
this.hubConnection = this.buildConnection(
|
|
96
|
+
hubUrl,
|
|
97
|
+
accessToken,
|
|
98
|
+
true,
|
|
99
|
+
);
|
|
100
|
+
this.registerHandlers();
|
|
101
|
+
this.registerOnClose();
|
|
102
|
+
await this.hubConnection.start();
|
|
103
|
+
} else {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private buildConnection(
|
|
110
|
+
hubUrl: string,
|
|
111
|
+
accessToken: string,
|
|
112
|
+
disableWs = false,
|
|
113
|
+
): HubConnection {
|
|
114
|
+
return new HubConnectionBuilder()
|
|
115
|
+
.withUrl(hubUrl, {
|
|
116
|
+
accessTokenFactory: () => accessToken,
|
|
117
|
+
transport: disableWs
|
|
118
|
+
? HttpTransportType.ServerSentEvents |
|
|
119
|
+
HttpTransportType.LongPolling
|
|
120
|
+
: undefined,
|
|
121
|
+
})
|
|
122
|
+
.withAutomaticReconnect()
|
|
123
|
+
.build();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private registerOnClose(): void {
|
|
127
|
+
this.hubConnection?.onclose((error) => {
|
|
128
|
+
this.logger?.info(
|
|
129
|
+
`[PackagerHub] onclose fired. error=${error?.message ?? "none"}, hasReject=${!!this.completionReject}, hasResolve=${!!this.completionResolve}`,
|
|
130
|
+
);
|
|
131
|
+
if (this.completionReject) {
|
|
132
|
+
this.completionReject(
|
|
133
|
+
error ??
|
|
134
|
+
new Error(
|
|
135
|
+
translate.t(
|
|
136
|
+
"toolWorkflowCompilerBrowser.errors.hubConnectionClosed",
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
this.completionResolve = null;
|
|
141
|
+
this.completionReject = null;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
waitForCompletionAsync(): Promise<PackAgentResult> {
|
|
147
|
+
return new Promise<PackAgentResult>((resolve, reject) => {
|
|
148
|
+
this.completionResolve = resolve;
|
|
149
|
+
this.completionReject = reject;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async disconnectAsync(): Promise<void> {
|
|
154
|
+
if (
|
|
155
|
+
this.hubConnection &&
|
|
156
|
+
this.hubConnection.state !== HubConnectionState.Disconnected
|
|
157
|
+
) {
|
|
158
|
+
await this.hubConnection.stop();
|
|
159
|
+
}
|
|
160
|
+
this.hubConnection = null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private registerHandlers(): void {
|
|
164
|
+
if (!this.hubConnection) return;
|
|
165
|
+
|
|
166
|
+
this.hubConnection.on(
|
|
167
|
+
PackagerCommandType.SetExecutionCompleted,
|
|
168
|
+
(jsonCommand: string) => {
|
|
169
|
+
this.onExecutionCompleted(jsonCommand);
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
this.hubConnection.on(
|
|
174
|
+
PackagerCommandType.SetLogMessage,
|
|
175
|
+
(jsonCommand: string) => {
|
|
176
|
+
this.onLogMessage(jsonCommand);
|
|
177
|
+
},
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
this.hubConnection.on(
|
|
181
|
+
PackagerCommandType.SetExecutionStarted,
|
|
182
|
+
(_jsonCommand: string) => {
|
|
183
|
+
this.logger?.info(
|
|
184
|
+
translate.t(
|
|
185
|
+
"toolWorkflowCompilerBrowser.info.remoteAgentStartedExecution",
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
this.hubConnection.on(PackagerCommandType.PeerDisconnected, () => {
|
|
192
|
+
this.logger?.info(
|
|
193
|
+
`[PackagerHub] PeerDisconnected received. hasReject=${!!this.completionReject}`,
|
|
194
|
+
);
|
|
195
|
+
if (this.completionReject) {
|
|
196
|
+
this.completionReject(
|
|
197
|
+
new Error(
|
|
198
|
+
translate.t(
|
|
199
|
+
"toolWorkflowCompilerBrowser.errors.packAgentDisconnected",
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
);
|
|
203
|
+
this.completionResolve = null;
|
|
204
|
+
this.completionReject = null;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
private onLogMessage(jsonCommand: string): void {
|
|
210
|
+
if (!this.logger) return;
|
|
211
|
+
|
|
212
|
+
const command: ExecutorCommand<PackAgentLogMessage> =
|
|
213
|
+
JSON.parse(jsonCommand);
|
|
214
|
+
const agentLog = command.Argument1;
|
|
215
|
+
|
|
216
|
+
const logLevel = toLogLevel(agentLog.LogLevel);
|
|
217
|
+
const logMessage = new LogMessage(agentLog.Message, logLevel, {
|
|
218
|
+
source: agentLog.Source,
|
|
219
|
+
sourceTarget: agentLog.SourceTarget,
|
|
220
|
+
progress: agentLog.Progress,
|
|
221
|
+
code: agentLog.Code,
|
|
222
|
+
filePath: agentLog.FilePath,
|
|
223
|
+
line: agentLog.Line,
|
|
224
|
+
id: agentLog.Id,
|
|
225
|
+
});
|
|
226
|
+
this.logger.logMessage(logMessage);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private onExecutionCompleted(jsonCommand: string): void {
|
|
230
|
+
this.logger?.info(
|
|
231
|
+
`[PackagerHub] onExecutionCompleted called. hasResolve=${!!this.completionResolve}, raw=${jsonCommand}`,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (!this.completionResolve) {
|
|
235
|
+
this.logger?.warn(
|
|
236
|
+
`[PackagerHub] onExecutionCompleted: completionResolve is null, ignoring result`,
|
|
237
|
+
);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const command: ExecutorCommand<PackJobResult> = JSON.parse(jsonCommand);
|
|
242
|
+
const jobResult = command.Argument1;
|
|
243
|
+
|
|
244
|
+
this.logger?.info(
|
|
245
|
+
`[PackagerHub] onExecutionCompleted parsed: errorCode=${jobResult.ErrorCode}, status=${jobResult.Status}, message=${jobResult.Message}, bucketId=${jobResult.BucketId}`,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
this.completionResolve({
|
|
249
|
+
errorCode: jobResult.ErrorCode,
|
|
250
|
+
status: jobResult.Status,
|
|
251
|
+
message: jobResult.Message,
|
|
252
|
+
bucketId: jobResult.BucketId,
|
|
253
|
+
});
|
|
254
|
+
this.completionResolve = null;
|
|
255
|
+
this.completionReject = null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ConnectionInfo,
|
|
3
|
+
type IFileSystem,
|
|
4
|
+
type IProjectPackOptions,
|
|
5
|
+
type IProjectToolFactory,
|
|
6
|
+
type IToolLogger,
|
|
7
|
+
type ProjectOperationContext,
|
|
8
|
+
ToolResult,
|
|
9
|
+
toRelativeUrl,
|
|
10
|
+
translate,
|
|
11
|
+
} from "@uipath/solutionpackager-tool-core";
|
|
12
|
+
import { PackResultProcessor } from "./pack-result-processor.js";
|
|
13
|
+
import {
|
|
14
|
+
CompletedStatus,
|
|
15
|
+
type PackAgentResult,
|
|
16
|
+
PackagerHubConnection,
|
|
17
|
+
} from "./packager-hub-connection.js";
|
|
18
|
+
|
|
19
|
+
type ResolveCallback = (result: ToolResult) => void;
|
|
20
|
+
|
|
21
|
+
interface ProjectEntry {
|
|
22
|
+
options: IProjectPackOptions;
|
|
23
|
+
resolve: ResolveCallback;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface BundleSession {
|
|
27
|
+
expectedCount: number;
|
|
28
|
+
projects: Map<string, ProjectEntry>;
|
|
29
|
+
timeout?: ReturnType<typeof setTimeout>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IProjectBundleExecutor {
|
|
33
|
+
packAsync(
|
|
34
|
+
options: IProjectPackOptions,
|
|
35
|
+
context: ProjectOperationContext,
|
|
36
|
+
): Promise<ToolResult>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Coordinates packing of multiple projects within a single operation context.
|
|
41
|
+
* Waits until all compatible projects have submitted their pack options,
|
|
42
|
+
* then executes a single bundled operation and resolves all pending promises.
|
|
43
|
+
*/
|
|
44
|
+
export class ProjectBundleExecutor implements IProjectBundleExecutor {
|
|
45
|
+
static readonly SESSION_TIMEOUT_MS = 1_800_000;
|
|
46
|
+
|
|
47
|
+
private readonly sessions = new Map<string, BundleSession>();
|
|
48
|
+
private readonly pendingSessionCreations = new Map<
|
|
49
|
+
string,
|
|
50
|
+
Promise<BundleSession>
|
|
51
|
+
>();
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
private readonly toolFactory: IProjectToolFactory,
|
|
55
|
+
private readonly fileSystem: IFileSystem,
|
|
56
|
+
) {}
|
|
57
|
+
|
|
58
|
+
async packAsync(
|
|
59
|
+
options: IProjectPackOptions,
|
|
60
|
+
context: ProjectOperationContext,
|
|
61
|
+
): Promise<ToolResult> {
|
|
62
|
+
const projectPath = options.projectPath;
|
|
63
|
+
context.logger?.info(
|
|
64
|
+
`[BundleExecutor] packAsync called for project: ${projectPath}`,
|
|
65
|
+
);
|
|
66
|
+
const session = await this.getSessionAsync(context);
|
|
67
|
+
|
|
68
|
+
let resolve!: (result: ToolResult) => void;
|
|
69
|
+
const promise = new Promise<ToolResult>((r) => {
|
|
70
|
+
resolve = r;
|
|
71
|
+
});
|
|
72
|
+
session.projects.set(projectPath, { options, resolve });
|
|
73
|
+
const allReceived = session.projects.size === session.expectedCount;
|
|
74
|
+
context.logger?.info(
|
|
75
|
+
`[BundleExecutor] session: ${session.projects.size}/${session.expectedCount} projects received. allReceived=${allReceived}`,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (allReceived) {
|
|
79
|
+
clearTimeout(session.timeout);
|
|
80
|
+
await this.packOnAgentAsync(session, context);
|
|
81
|
+
this.sessions.delete(context.id);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return promise;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private getSessionAsync(
|
|
88
|
+
context: ProjectOperationContext,
|
|
89
|
+
): Promise<BundleSession> {
|
|
90
|
+
const existing = this.sessions.get(context.id);
|
|
91
|
+
if (existing) {
|
|
92
|
+
return Promise.resolve(existing);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let pending = this.pendingSessionCreations.get(context.id);
|
|
96
|
+
if (!pending) {
|
|
97
|
+
pending = this.createSessionAsync(context);
|
|
98
|
+
this.pendingSessionCreations.set(context.id, pending);
|
|
99
|
+
}
|
|
100
|
+
return pending;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private async createSessionAsync(
|
|
104
|
+
context: ProjectOperationContext,
|
|
105
|
+
): Promise<BundleSession> {
|
|
106
|
+
const expectedCount = this.countCompatibleProjects(context);
|
|
107
|
+
const session: BundleSession = {
|
|
108
|
+
expectedCount,
|
|
109
|
+
projects: new Map(),
|
|
110
|
+
};
|
|
111
|
+
session.timeout = setTimeout(() => {
|
|
112
|
+
if (session.projects.size < session.expectedCount) {
|
|
113
|
+
context.logger?.error(
|
|
114
|
+
`[BundleExecutor] Session timed out after ${ProjectBundleExecutor.SESSION_TIMEOUT_MS}ms: ` +
|
|
115
|
+
`received ${session.projects.size}/${session.expectedCount} projects`,
|
|
116
|
+
);
|
|
117
|
+
this.resolveAll(
|
|
118
|
+
session,
|
|
119
|
+
ToolResult.error(
|
|
120
|
+
"SessionTimeoutError",
|
|
121
|
+
translate.t(
|
|
122
|
+
"toolWorkflowCompilerBrowser.errors.sessionTimeout",
|
|
123
|
+
{
|
|
124
|
+
received: session.projects.size,
|
|
125
|
+
expected: session.expectedCount,
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
),
|
|
129
|
+
);
|
|
130
|
+
this.sessions.delete(context.id);
|
|
131
|
+
}
|
|
132
|
+
}, ProjectBundleExecutor.SESSION_TIMEOUT_MS);
|
|
133
|
+
this.sessions.set(context.id, session);
|
|
134
|
+
this.pendingSessionCreations.delete(context.id);
|
|
135
|
+
return session;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private async packOnAgentAsync(
|
|
139
|
+
session: BundleSession,
|
|
140
|
+
context: ProjectOperationContext,
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
const projectPaths = [...session.projects.keys()];
|
|
143
|
+
context.logger?.info(
|
|
144
|
+
translate.t(
|
|
145
|
+
"toolWorkflowCompilerBrowser.info.startingRemotePackAgent",
|
|
146
|
+
{
|
|
147
|
+
count: session.expectedCount,
|
|
148
|
+
paths: projectPaths.join(", "),
|
|
149
|
+
},
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const connection = context.connection;
|
|
154
|
+
context.logger?.info(
|
|
155
|
+
`[BundleExecutor] connection info: cloudUrl=${connection?.cloudUrl ?? "MISSING"}, hasToken=${!!connection?.accessToken}, folderId=${connection?.folderId ?? "MISSING"}`,
|
|
156
|
+
);
|
|
157
|
+
if (
|
|
158
|
+
!connection?.cloudUrl ||
|
|
159
|
+
!connection.accessToken ||
|
|
160
|
+
!connection.folderId
|
|
161
|
+
) {
|
|
162
|
+
this.resolveAll(
|
|
163
|
+
session,
|
|
164
|
+
ToolResult.error(
|
|
165
|
+
"ConnectionError",
|
|
166
|
+
translate.t(
|
|
167
|
+
"toolWorkflowCompilerBrowser.errors.missingConnectionInfo",
|
|
168
|
+
),
|
|
169
|
+
),
|
|
170
|
+
);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const sessionId = crypto.randomUUID();
|
|
175
|
+
const hubConnection = await this.connectToHubAsync(
|
|
176
|
+
connection,
|
|
177
|
+
sessionId,
|
|
178
|
+
context.logger,
|
|
179
|
+
);
|
|
180
|
+
if (!hubConnection) {
|
|
181
|
+
this.resolveAll(
|
|
182
|
+
session,
|
|
183
|
+
ToolResult.error(
|
|
184
|
+
"SignalRConnectionError",
|
|
185
|
+
translate.t(
|
|
186
|
+
"toolWorkflowCompilerBrowser.errors.signalRConnectionFailed",
|
|
187
|
+
),
|
|
188
|
+
),
|
|
189
|
+
);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
context.logger?.info(`[BundleExecutor] Sending BeginSession...`);
|
|
195
|
+
await this.sendBeginSessionAsync(
|
|
196
|
+
session,
|
|
197
|
+
context,
|
|
198
|
+
connection,
|
|
199
|
+
sessionId,
|
|
200
|
+
);
|
|
201
|
+
context.logger?.info(
|
|
202
|
+
`[BundleExecutor] BeginSession succeeded. Waiting for completion...`,
|
|
203
|
+
);
|
|
204
|
+
const result = await hubConnection.waitForCompletionAsync();
|
|
205
|
+
context.logger?.info(
|
|
206
|
+
`[BundleExecutor] Got result: errorCode=${result.errorCode}, status=${result.status}, message=${result.message}, bucketId=${result.bucketId}`,
|
|
207
|
+
);
|
|
208
|
+
await this.handlePackResultAsync(result, session, context);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
const message =
|
|
211
|
+
error instanceof Error ? error.message : String(error);
|
|
212
|
+
context.logger?.error(
|
|
213
|
+
`[BundleExecutor] packOnAgentAsync caught error: ${message}`,
|
|
214
|
+
);
|
|
215
|
+
this.resolveAll(
|
|
216
|
+
session,
|
|
217
|
+
ToolResult.error("PackAgentError", message),
|
|
218
|
+
);
|
|
219
|
+
} finally {
|
|
220
|
+
await hubConnection.disconnectAsync();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private async connectToHubAsync(
|
|
225
|
+
connection: ConnectionInfo,
|
|
226
|
+
sessionId: string,
|
|
227
|
+
logger?: IToolLogger,
|
|
228
|
+
): Promise<PackagerHubConnection | null> {
|
|
229
|
+
const relativeBase = toRelativeUrl(connection.cloudUrl!);
|
|
230
|
+
const hubUrl = `${relativeBase}/orchestrator_/signalr/robotdebug?sessionId=${sessionId}`;
|
|
231
|
+
logger?.info(
|
|
232
|
+
translate.t("toolWorkflowCompilerBrowser.info.connectingToHub", {
|
|
233
|
+
url: hubUrl,
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const hubConnection = new PackagerHubConnection(logger);
|
|
238
|
+
try {
|
|
239
|
+
await hubConnection.connectAsync(hubUrl, connection.accessToken!);
|
|
240
|
+
return hubConnection;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
const message =
|
|
243
|
+
error instanceof Error ? error.message : String(error);
|
|
244
|
+
logger?.error(
|
|
245
|
+
translate.t(
|
|
246
|
+
"toolWorkflowCompilerBrowser.errors.hubConnectionFailed",
|
|
247
|
+
{ url: hubUrl, message },
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private async handlePackResultAsync(
|
|
255
|
+
result: PackAgentResult,
|
|
256
|
+
session: BundleSession,
|
|
257
|
+
context: ProjectOperationContext,
|
|
258
|
+
): Promise<void> {
|
|
259
|
+
if (
|
|
260
|
+
result.errorCode !== 0 ||
|
|
261
|
+
result.status !== CompletedStatus.Succeeded
|
|
262
|
+
) {
|
|
263
|
+
this.resolveAll(
|
|
264
|
+
session,
|
|
265
|
+
ToolResult.error(
|
|
266
|
+
"PackAgentError",
|
|
267
|
+
result.message ??
|
|
268
|
+
translate.t(
|
|
269
|
+
"toolWorkflowCompilerBrowser.errors.packAgentFailed",
|
|
270
|
+
{
|
|
271
|
+
status: result.status,
|
|
272
|
+
errorCode: result.errorCode,
|
|
273
|
+
},
|
|
274
|
+
),
|
|
275
|
+
),
|
|
276
|
+
);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (result.bucketId == null) {
|
|
281
|
+
this.resolveAll(
|
|
282
|
+
session,
|
|
283
|
+
ToolResult.error(
|
|
284
|
+
"PackAgentError",
|
|
285
|
+
translate.t(
|
|
286
|
+
"toolWorkflowCompilerBrowser.errors.packSucceededNoBucket",
|
|
287
|
+
),
|
|
288
|
+
),
|
|
289
|
+
);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const resultProcessor = new PackResultProcessor(
|
|
294
|
+
this.fileSystem,
|
|
295
|
+
context.logger,
|
|
296
|
+
);
|
|
297
|
+
const perProjectResults = await resultProcessor.processAsync(
|
|
298
|
+
result.bucketId,
|
|
299
|
+
session.projects,
|
|
300
|
+
context,
|
|
301
|
+
);
|
|
302
|
+
for (const [projectPath, entry] of session.projects) {
|
|
303
|
+
const projectResult = perProjectResults.get(projectPath);
|
|
304
|
+
entry.resolve(
|
|
305
|
+
projectResult ??
|
|
306
|
+
ToolResult.error(
|
|
307
|
+
"PackResultError",
|
|
308
|
+
translate.t(
|
|
309
|
+
"toolWorkflowCompilerBrowser.errors.noResultForProject",
|
|
310
|
+
),
|
|
311
|
+
),
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private async sendBeginSessionAsync(
|
|
317
|
+
session: BundleSession,
|
|
318
|
+
context: ProjectOperationContext,
|
|
319
|
+
connection: ConnectionInfo,
|
|
320
|
+
sessionId: string,
|
|
321
|
+
): Promise<void> {
|
|
322
|
+
const relativeBase = toRelativeUrl(connection.cloudUrl!);
|
|
323
|
+
const url = `${relativeBase}/orchestrator_/api/serverless/jobs`;
|
|
324
|
+
context.logger?.info(`[BundleExecutor] BeginSession URL: ${url}`);
|
|
325
|
+
|
|
326
|
+
// Use the first entry for shared fields (package info, options)
|
|
327
|
+
const firstEntry = session.projects.values().next().value!;
|
|
328
|
+
const pkg = firstEntry.options.package;
|
|
329
|
+
const opts = firstEntry.options;
|
|
330
|
+
|
|
331
|
+
// Build RpaProjects from all projects in the session
|
|
332
|
+
const rpaProjects = [...session.projects.entries()].map(
|
|
333
|
+
([projectPath, entry]) => {
|
|
334
|
+
const project = context.solution.Projects.find(
|
|
335
|
+
(p) => p.ProjectPath === projectPath,
|
|
336
|
+
);
|
|
337
|
+
return {
|
|
338
|
+
RelativePath: project?.ProjectRelativePath ?? "",
|
|
339
|
+
DesignId: project?.Id ?? "",
|
|
340
|
+
PackageId: entry.options.package.id,
|
|
341
|
+
};
|
|
342
|
+
},
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
const executorInfo = JSON.stringify({
|
|
346
|
+
Command: "Pack",
|
|
347
|
+
SupportedProtocolVersions: ["2.0"],
|
|
348
|
+
ProjectId: "",
|
|
349
|
+
DownloadUrl: context.downloadUrl ?? "",
|
|
350
|
+
SessionId: sessionId,
|
|
351
|
+
ProfileKey: "StudioWeb",
|
|
352
|
+
LicenseType: "Development",
|
|
353
|
+
LogLevel: opts.logLevel ?? "Warning",
|
|
354
|
+
Name: pkg.id,
|
|
355
|
+
Configuration: context.configuration,
|
|
356
|
+
Culture: "en-US",
|
|
357
|
+
Version: pkg.version,
|
|
358
|
+
Author: pkg.author ?? "",
|
|
359
|
+
Description: pkg.description ?? "",
|
|
360
|
+
SkipAnalyze: String(opts.skipAnalyze),
|
|
361
|
+
SkipValidate: String(opts.skipValidate),
|
|
362
|
+
TargetFramework: context.targetFramework,
|
|
363
|
+
RpaProjects: rpaProjects,
|
|
364
|
+
WorkflowCompilerVersion: context.workflowCompilerVersion ?? "",
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const body = {
|
|
368
|
+
source: "StudioWeb",
|
|
369
|
+
targetFramework: context.targetFramework,
|
|
370
|
+
jobType: "PublishStudioProject",
|
|
371
|
+
inputArguments: executorInfo,
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const headers: Record<string, string> = {
|
|
375
|
+
"Content-Type": "application/json",
|
|
376
|
+
Authorization: `Bearer ${connection.accessToken}`,
|
|
377
|
+
"X-UIPATH-OrganizationUnitId": connection.folderId ?? "",
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
if (connection.tenantId) {
|
|
381
|
+
headers["x-uipath-tenantid"] = connection.tenantId;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const response = await fetch(url, {
|
|
385
|
+
method: "POST",
|
|
386
|
+
headers,
|
|
387
|
+
body: JSON.stringify(body),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
if (!response.ok) {
|
|
391
|
+
const errorText = await response.text();
|
|
392
|
+
throw new Error(
|
|
393
|
+
translate.t(
|
|
394
|
+
"toolWorkflowCompilerBrowser.errors.beginSessionFailed",
|
|
395
|
+
{ status: response.status, errorText },
|
|
396
|
+
),
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private resolveAll(session: BundleSession, result: ToolResult): void {
|
|
402
|
+
for (const [, entry] of session.projects) {
|
|
403
|
+
entry.resolve(result);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
private countCompatibleProjects(context: ProjectOperationContext): number {
|
|
408
|
+
return context.solution.Projects.filter(
|
|
409
|
+
(p) =>
|
|
410
|
+
p.ProjectPath != null &&
|
|
411
|
+
this.toolFactory.supportedTypes.includes(p.Type),
|
|
412
|
+
).length;
|
|
413
|
+
}
|
|
414
|
+
}
|