@uipath/agent-tool 1.202.0 → 1.203.0-preview.160
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/THIRD-PARTY-NOTICES.md +41 -28
- package/dist/{agent-tool-OK5DK57D-zkbmhnmd.js → agent-tool-OK5DK57D-rjan4r3q.js} +2 -2
- package/dist/{de-5KVP66U6-1bnf4zt5.js → de-MUJPA4EB-209jpeqe.js} +14 -2
- package/dist/{index-4vpac2gy.js → embedded-file-reader-0sjt4nkj.js} +96 -17497
- package/dist/{en-X3S6IZ5J-nhkg6pwn.js → en-GN2ZGWUE-03z2df4k.js} +26 -2
- package/dist/{es-VFPNVMP5-bx2c337j.js → es-7SE7HX2X-qda7whn6.js} +14 -2
- package/dist/{es-MX-XAVLOQW3-q00km2y7.js → es-MX-2MYFZU7X-6v0q5rbn.js} +14 -2
- package/dist/executor-tool.js +2 -2
- package/dist/first-party-service-srxaftkn.js +27 -0
- package/dist/{fr-E5MDO4EG-c3mkty2t.js → fr-MDFKNZ4C-qmypp92m.js} +14 -2
- package/dist/index-kmp9fmhb.js +25 -0
- package/dist/index-s1mvhfbj.js +17598 -0
- package/dist/index.js +5 -5
- package/dist/{ja-DPO5PPLO-mysfmznx.js → ja-UTQEAP34-hvyf683h.js} +14 -2
- package/dist/{ko-ITOPNIYI-hmxd2asd.js → ko-AXCHW2OK-pkwdcbmx.js} +14 -2
- package/dist/{packager-tool-a42jnpy5.js → packager-tool-4mna26cc.js} +27 -3
- package/dist/packager-tool-53w8skv5.js +262 -0
- package/dist/packager-tool-abj3ftf8.js +1751 -0
- package/dist/{packager-tool-pst3whq5.js → packager-tool-byvnxap3.js} +1 -1
- package/dist/{packager-tool-wmd0pbnz.js → packager-tool-g5g8vyx2.js} +466 -2217
- package/dist/packager-tool-gdtpsdn7.js +342 -0
- package/dist/{packager-tool-0srzfhdr.js → packager-tool-mc1499dc.js} +2 -2
- package/dist/{packager-tool-p1yrrbpt.js → packager-tool-tnz82jk0.js} +301 -35
- package/dist/packager-tool.js +3 -3
- package/dist/{pt-YI4VE4YQ-wdkn2dcf.js → pt-3DIO5QWG-hb763vct.js} +14 -2
- package/dist/{pt-BR-BXURWX4W-bwfcwhk1.js → pt-BR-Y6K6XHXK-37a932vp.js} +14 -2
- package/dist/{ro-RKXZUG7Z-nndrrsq7.js → ro-BWR3ONRR-jmap5ajd.js} +14 -2
- package/dist/tool.js +5 -5
- package/dist/{tr-AA6O2WSR-86ba4bdf.js → tr-5IIHSFW5-stsb3dar.js} +14 -2
- package/dist/{zh-CN-R2KLHXQV-agqr45ds.js → zh-CN-R2KU4U5L-q4yhc33t.js} +14 -2
- package/dist/{zh-TW-RJGCQANM-w0x3qc1p.js → zh-TW-GNKMELBF-r4ay2475.js} +14 -2
- package/package.json +3 -2
- package/dist/packager-tool-kpf6eryr.js +0 -4241
|
@@ -0,0 +1,1751 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OutputFormatter,
|
|
3
|
+
PersonalWorkspacesApi,
|
|
4
|
+
RESULTS,
|
|
5
|
+
addSdkUserAgentHeader,
|
|
6
|
+
catchError,
|
|
7
|
+
createApiClient,
|
|
8
|
+
findSolutionFile,
|
|
9
|
+
getFileSystem,
|
|
10
|
+
getLoginStatusAsync,
|
|
11
|
+
getSdkUserAgentToken,
|
|
12
|
+
installSdkUserAgentHeader,
|
|
13
|
+
logger,
|
|
14
|
+
readUipxFile,
|
|
15
|
+
summarizePlatformBody,
|
|
16
|
+
updateSolutionFile,
|
|
17
|
+
updateUipxSolutionId
|
|
18
|
+
} from "./packager-tool-tnz82jk0.js";
|
|
19
|
+
import {
|
|
20
|
+
zipSync
|
|
21
|
+
} from "./packager-tool-b098y4a2.js";
|
|
22
|
+
|
|
23
|
+
// ../solution-sdk/src/studio-web-compat.ts
|
|
24
|
+
var API_PROJECT_TYPES = new Set(["api", "apiautomation"]);
|
|
25
|
+
|
|
26
|
+
class StudioWebIncompatibleProjectError extends Error {
|
|
27
|
+
offendingProjects;
|
|
28
|
+
constructor(offendingProjects) {
|
|
29
|
+
super(`Studio Web cannot import API Workflow project(s) manifested by project.json: ${offendingProjects.join(", ")}. Studio Web's solution import accepts only project.uiproj-manifested projects.`);
|
|
30
|
+
this.offendingProjects = offendingProjects;
|
|
31
|
+
this.name = "StudioWebIncompatibleProjectError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function assertStudioWebCompatibleProjects(projects) {
|
|
35
|
+
const incompatible = projects.filter((p) => typeof p.Type === "string" && API_PROJECT_TYPES.has(p.Type.toLowerCase()) && p.ProjectRelativePath.replace(/\\/g, "/").split("/").pop() === "project.json");
|
|
36
|
+
if (incompatible.length > 0) {
|
|
37
|
+
throw new StudioWebIncompatibleProjectError(incompatible.map((p) => p.ProjectRelativePath));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ../studioweb-sdk/generated/src/runtime.ts
|
|
42
|
+
var BASE_PATH = "https://alpha.uipath.com/studio_/backend".replace(/\/+$/, "");
|
|
43
|
+
|
|
44
|
+
class Configuration {
|
|
45
|
+
configuration;
|
|
46
|
+
constructor(configuration = {}) {
|
|
47
|
+
this.configuration = configuration;
|
|
48
|
+
}
|
|
49
|
+
set config(configuration) {
|
|
50
|
+
this.configuration = configuration;
|
|
51
|
+
}
|
|
52
|
+
get basePath() {
|
|
53
|
+
return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH;
|
|
54
|
+
}
|
|
55
|
+
get fetchApi() {
|
|
56
|
+
return this.configuration.fetchApi;
|
|
57
|
+
}
|
|
58
|
+
get middleware() {
|
|
59
|
+
return this.configuration.middleware || [];
|
|
60
|
+
}
|
|
61
|
+
get queryParamsStringify() {
|
|
62
|
+
return this.configuration.queryParamsStringify || querystring;
|
|
63
|
+
}
|
|
64
|
+
get username() {
|
|
65
|
+
return this.configuration.username;
|
|
66
|
+
}
|
|
67
|
+
get password() {
|
|
68
|
+
return this.configuration.password;
|
|
69
|
+
}
|
|
70
|
+
get apiKey() {
|
|
71
|
+
const apiKey = this.configuration.apiKey;
|
|
72
|
+
if (apiKey) {
|
|
73
|
+
return typeof apiKey === "function" ? apiKey : () => apiKey;
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
get accessToken() {
|
|
78
|
+
const accessToken = this.configuration.accessToken;
|
|
79
|
+
if (accessToken) {
|
|
80
|
+
return typeof accessToken === "function" ? accessToken : async () => accessToken;
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
get headers() {
|
|
85
|
+
return this.configuration.headers;
|
|
86
|
+
}
|
|
87
|
+
get credentials() {
|
|
88
|
+
return this.configuration.credentials;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
var DefaultConfig = new Configuration;
|
|
92
|
+
|
|
93
|
+
class BaseAPI {
|
|
94
|
+
configuration;
|
|
95
|
+
static jsonRegex = new RegExp("^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$", "i");
|
|
96
|
+
middleware;
|
|
97
|
+
constructor(configuration = DefaultConfig) {
|
|
98
|
+
this.configuration = configuration;
|
|
99
|
+
this.middleware = configuration.middleware;
|
|
100
|
+
}
|
|
101
|
+
withMiddleware(...middlewares) {
|
|
102
|
+
const next = this.clone();
|
|
103
|
+
next.middleware = next.middleware.concat(...middlewares);
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
106
|
+
withPreMiddleware(...preMiddlewares) {
|
|
107
|
+
const middlewares = preMiddlewares.map((pre) => ({ pre }));
|
|
108
|
+
return this.withMiddleware(...middlewares);
|
|
109
|
+
}
|
|
110
|
+
withPostMiddleware(...postMiddlewares) {
|
|
111
|
+
const middlewares = postMiddlewares.map((post) => ({ post }));
|
|
112
|
+
return this.withMiddleware(...middlewares);
|
|
113
|
+
}
|
|
114
|
+
isJsonMime(mime) {
|
|
115
|
+
if (!mime) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return BaseAPI.jsonRegex.test(mime);
|
|
119
|
+
}
|
|
120
|
+
async request(context, initOverrides) {
|
|
121
|
+
const { url, init } = await this.createFetchParams(context, initOverrides);
|
|
122
|
+
const response = await this.fetchApi(url, init);
|
|
123
|
+
if (response && (response.status >= 200 && response.status < 300)) {
|
|
124
|
+
return response;
|
|
125
|
+
}
|
|
126
|
+
throw new ResponseError(response, "Response returned an error code");
|
|
127
|
+
}
|
|
128
|
+
async createFetchParams(context, initOverrides) {
|
|
129
|
+
let url = this.configuration.basePath + context.path;
|
|
130
|
+
if (context.query !== undefined && Object.keys(context.query).length !== 0) {
|
|
131
|
+
url += "?" + this.configuration.queryParamsStringify(context.query);
|
|
132
|
+
}
|
|
133
|
+
const headers = Object.assign({}, this.configuration.headers, context.headers);
|
|
134
|
+
Object.keys(headers).forEach((key) => headers[key] === undefined ? delete headers[key] : {});
|
|
135
|
+
const initOverrideFn = typeof initOverrides === "function" ? initOverrides : async () => initOverrides;
|
|
136
|
+
const initParams = {
|
|
137
|
+
method: context.method,
|
|
138
|
+
headers,
|
|
139
|
+
body: context.body,
|
|
140
|
+
credentials: this.configuration.credentials
|
|
141
|
+
};
|
|
142
|
+
const overriddenInit = {
|
|
143
|
+
...initParams,
|
|
144
|
+
...await initOverrideFn({
|
|
145
|
+
init: initParams,
|
|
146
|
+
context
|
|
147
|
+
})
|
|
148
|
+
};
|
|
149
|
+
let body;
|
|
150
|
+
if (isFormData(overriddenInit.body) || overriddenInit.body instanceof URLSearchParams || isBlob(overriddenInit.body)) {
|
|
151
|
+
body = overriddenInit.body;
|
|
152
|
+
} else if (this.isJsonMime(headers["Content-Type"])) {
|
|
153
|
+
body = JSON.stringify(overriddenInit.body);
|
|
154
|
+
} else {
|
|
155
|
+
body = overriddenInit.body;
|
|
156
|
+
}
|
|
157
|
+
const init = {
|
|
158
|
+
...overriddenInit,
|
|
159
|
+
body
|
|
160
|
+
};
|
|
161
|
+
return { url, init };
|
|
162
|
+
}
|
|
163
|
+
fetchApi = async (url, init) => {
|
|
164
|
+
let fetchParams = { url, init };
|
|
165
|
+
for (const middleware of this.middleware) {
|
|
166
|
+
if (middleware.pre) {
|
|
167
|
+
fetchParams = await middleware.pre({
|
|
168
|
+
fetch: this.fetchApi,
|
|
169
|
+
...fetchParams
|
|
170
|
+
}) || fetchParams;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
let response = undefined;
|
|
174
|
+
try {
|
|
175
|
+
response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
for (const middleware of this.middleware) {
|
|
178
|
+
if (middleware.onError) {
|
|
179
|
+
response = await middleware.onError({
|
|
180
|
+
fetch: this.fetchApi,
|
|
181
|
+
url: fetchParams.url,
|
|
182
|
+
init: fetchParams.init,
|
|
183
|
+
error: e,
|
|
184
|
+
response: response ? response.clone() : undefined
|
|
185
|
+
}) || response;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (response === undefined) {
|
|
189
|
+
if (e instanceof Error) {
|
|
190
|
+
throw new FetchError(e, "The request failed and the interceptors did not return an alternative response");
|
|
191
|
+
} else {
|
|
192
|
+
throw e;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
for (const middleware of this.middleware) {
|
|
197
|
+
if (middleware.post) {
|
|
198
|
+
response = await middleware.post({
|
|
199
|
+
fetch: this.fetchApi,
|
|
200
|
+
url: fetchParams.url,
|
|
201
|
+
init: fetchParams.init,
|
|
202
|
+
response: response.clone()
|
|
203
|
+
}) || response;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return response;
|
|
207
|
+
};
|
|
208
|
+
clone() {
|
|
209
|
+
const constructor = this.constructor;
|
|
210
|
+
const next = new constructor(this.configuration);
|
|
211
|
+
next.middleware = this.middleware.slice();
|
|
212
|
+
return next;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function isBlob(value) {
|
|
216
|
+
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
217
|
+
}
|
|
218
|
+
function isFormData(value) {
|
|
219
|
+
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
class ResponseError extends Error {
|
|
223
|
+
response;
|
|
224
|
+
name = "ResponseError";
|
|
225
|
+
constructor(response, msg) {
|
|
226
|
+
super(msg);
|
|
227
|
+
this.response = response;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
class FetchError extends Error {
|
|
232
|
+
cause;
|
|
233
|
+
name = "FetchError";
|
|
234
|
+
constructor(cause, msg) {
|
|
235
|
+
super(msg);
|
|
236
|
+
this.cause = cause;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function querystring(params, prefix = "") {
|
|
240
|
+
return Object.keys(params).map((key) => querystringSingleKey(key, params[key], prefix)).filter((part) => part.length > 0).join("&");
|
|
241
|
+
}
|
|
242
|
+
function querystringSingleKey(key, value, keyPrefix = "") {
|
|
243
|
+
const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key);
|
|
244
|
+
if (value instanceof Array) {
|
|
245
|
+
const multiValue = value.map((singleValue) => encodeURIComponent(String(singleValue))).join(`&${encodeURIComponent(fullKey)}=`);
|
|
246
|
+
return `${encodeURIComponent(fullKey)}=${multiValue}`;
|
|
247
|
+
}
|
|
248
|
+
if (value instanceof Set) {
|
|
249
|
+
const valueAsArray = Array.from(value);
|
|
250
|
+
return querystringSingleKey(key, valueAsArray, keyPrefix);
|
|
251
|
+
}
|
|
252
|
+
if (value instanceof Date) {
|
|
253
|
+
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
|
|
254
|
+
}
|
|
255
|
+
if (value instanceof Object) {
|
|
256
|
+
return querystring(value, fullKey);
|
|
257
|
+
}
|
|
258
|
+
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`;
|
|
259
|
+
}
|
|
260
|
+
// ../solution-sdk/generated/src/runtime.ts
|
|
261
|
+
var BASE_PATH2 = "https://alpha.uipath.com/uipattycyrhx/abizon_1/automationsolutions_".replace(/\/+$/, "");
|
|
262
|
+
|
|
263
|
+
class Configuration2 {
|
|
264
|
+
configuration;
|
|
265
|
+
constructor(configuration = {}) {
|
|
266
|
+
this.configuration = configuration;
|
|
267
|
+
}
|
|
268
|
+
set config(configuration) {
|
|
269
|
+
this.configuration = configuration;
|
|
270
|
+
}
|
|
271
|
+
get basePath() {
|
|
272
|
+
return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH2;
|
|
273
|
+
}
|
|
274
|
+
get fetchApi() {
|
|
275
|
+
return this.configuration.fetchApi;
|
|
276
|
+
}
|
|
277
|
+
get middleware() {
|
|
278
|
+
return this.configuration.middleware || [];
|
|
279
|
+
}
|
|
280
|
+
get queryParamsStringify() {
|
|
281
|
+
return this.configuration.queryParamsStringify || querystring2;
|
|
282
|
+
}
|
|
283
|
+
get username() {
|
|
284
|
+
return this.configuration.username;
|
|
285
|
+
}
|
|
286
|
+
get password() {
|
|
287
|
+
return this.configuration.password;
|
|
288
|
+
}
|
|
289
|
+
get apiKey() {
|
|
290
|
+
const apiKey = this.configuration.apiKey;
|
|
291
|
+
if (apiKey) {
|
|
292
|
+
return typeof apiKey === "function" ? apiKey : () => apiKey;
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
get accessToken() {
|
|
297
|
+
const accessToken = this.configuration.accessToken;
|
|
298
|
+
if (accessToken) {
|
|
299
|
+
return typeof accessToken === "function" ? accessToken : async () => accessToken;
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
get headers() {
|
|
304
|
+
return this.configuration.headers;
|
|
305
|
+
}
|
|
306
|
+
get credentials() {
|
|
307
|
+
return this.configuration.credentials;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
var DefaultConfig2 = new Configuration2;
|
|
311
|
+
|
|
312
|
+
class BaseAPI2 {
|
|
313
|
+
configuration;
|
|
314
|
+
static jsonRegex = new RegExp("^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$", "i");
|
|
315
|
+
middleware;
|
|
316
|
+
constructor(configuration = DefaultConfig2) {
|
|
317
|
+
this.configuration = configuration;
|
|
318
|
+
this.middleware = configuration.middleware;
|
|
319
|
+
}
|
|
320
|
+
withMiddleware(...middlewares) {
|
|
321
|
+
const next = this.clone();
|
|
322
|
+
next.middleware = next.middleware.concat(...middlewares);
|
|
323
|
+
return next;
|
|
324
|
+
}
|
|
325
|
+
withPreMiddleware(...preMiddlewares) {
|
|
326
|
+
const middlewares = preMiddlewares.map((pre) => ({ pre }));
|
|
327
|
+
return this.withMiddleware(...middlewares);
|
|
328
|
+
}
|
|
329
|
+
withPostMiddleware(...postMiddlewares) {
|
|
330
|
+
const middlewares = postMiddlewares.map((post) => ({ post }));
|
|
331
|
+
return this.withMiddleware(...middlewares);
|
|
332
|
+
}
|
|
333
|
+
isJsonMime(mime) {
|
|
334
|
+
if (!mime) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return BaseAPI2.jsonRegex.test(mime);
|
|
338
|
+
}
|
|
339
|
+
async request(context, initOverrides) {
|
|
340
|
+
const { url, init } = await this.createFetchParams(context, initOverrides);
|
|
341
|
+
const response = await this.fetchApi(url, init);
|
|
342
|
+
if (response && (response.status >= 200 && response.status < 300)) {
|
|
343
|
+
return response;
|
|
344
|
+
}
|
|
345
|
+
throw new ResponseError2(response, "Response returned an error code");
|
|
346
|
+
}
|
|
347
|
+
async createFetchParams(context, initOverrides) {
|
|
348
|
+
let url = this.configuration.basePath + context.path;
|
|
349
|
+
if (context.query !== undefined && Object.keys(context.query).length !== 0) {
|
|
350
|
+
url += "?" + this.configuration.queryParamsStringify(context.query);
|
|
351
|
+
}
|
|
352
|
+
const headers = Object.assign({}, this.configuration.headers, context.headers);
|
|
353
|
+
Object.keys(headers).forEach((key) => headers[key] === undefined ? delete headers[key] : {});
|
|
354
|
+
const initOverrideFn = typeof initOverrides === "function" ? initOverrides : async () => initOverrides;
|
|
355
|
+
const initParams = {
|
|
356
|
+
method: context.method,
|
|
357
|
+
headers,
|
|
358
|
+
body: context.body,
|
|
359
|
+
credentials: this.configuration.credentials
|
|
360
|
+
};
|
|
361
|
+
const overriddenInit = {
|
|
362
|
+
...initParams,
|
|
363
|
+
...await initOverrideFn({
|
|
364
|
+
init: initParams,
|
|
365
|
+
context
|
|
366
|
+
})
|
|
367
|
+
};
|
|
368
|
+
let body;
|
|
369
|
+
if (isFormData2(overriddenInit.body) || overriddenInit.body instanceof URLSearchParams || isBlob2(overriddenInit.body)) {
|
|
370
|
+
body = overriddenInit.body;
|
|
371
|
+
} else if (this.isJsonMime(headers["Content-Type"])) {
|
|
372
|
+
body = JSON.stringify(overriddenInit.body);
|
|
373
|
+
} else {
|
|
374
|
+
body = overriddenInit.body;
|
|
375
|
+
}
|
|
376
|
+
const init = {
|
|
377
|
+
...overriddenInit,
|
|
378
|
+
body
|
|
379
|
+
};
|
|
380
|
+
return { url, init };
|
|
381
|
+
}
|
|
382
|
+
fetchApi = async (url, init) => {
|
|
383
|
+
let fetchParams = { url, init };
|
|
384
|
+
for (const middleware of this.middleware) {
|
|
385
|
+
if (middleware.pre) {
|
|
386
|
+
fetchParams = await middleware.pre({
|
|
387
|
+
fetch: this.fetchApi,
|
|
388
|
+
...fetchParams
|
|
389
|
+
}) || fetchParams;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
let response = undefined;
|
|
393
|
+
try {
|
|
394
|
+
response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init);
|
|
395
|
+
} catch (e) {
|
|
396
|
+
for (const middleware of this.middleware) {
|
|
397
|
+
if (middleware.onError) {
|
|
398
|
+
response = await middleware.onError({
|
|
399
|
+
fetch: this.fetchApi,
|
|
400
|
+
url: fetchParams.url,
|
|
401
|
+
init: fetchParams.init,
|
|
402
|
+
error: e,
|
|
403
|
+
response: response ? response.clone() : undefined
|
|
404
|
+
}) || response;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (response === undefined) {
|
|
408
|
+
if (e instanceof Error) {
|
|
409
|
+
throw new FetchError2(e, "The request failed and the interceptors did not return an alternative response");
|
|
410
|
+
} else {
|
|
411
|
+
throw e;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const middleware of this.middleware) {
|
|
416
|
+
if (middleware.post) {
|
|
417
|
+
response = await middleware.post({
|
|
418
|
+
fetch: this.fetchApi,
|
|
419
|
+
url: fetchParams.url,
|
|
420
|
+
init: fetchParams.init,
|
|
421
|
+
response: response.clone()
|
|
422
|
+
}) || response;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return response;
|
|
426
|
+
};
|
|
427
|
+
clone() {
|
|
428
|
+
const constructor = this.constructor;
|
|
429
|
+
const next = new constructor(this.configuration);
|
|
430
|
+
next.middleware = this.middleware.slice();
|
|
431
|
+
return next;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function isBlob2(value) {
|
|
435
|
+
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
436
|
+
}
|
|
437
|
+
function isFormData2(value) {
|
|
438
|
+
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
class ResponseError2 extends Error {
|
|
442
|
+
response;
|
|
443
|
+
name = "ResponseError";
|
|
444
|
+
constructor(response, msg) {
|
|
445
|
+
super(msg);
|
|
446
|
+
this.response = response;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
class FetchError2 extends Error {
|
|
451
|
+
cause;
|
|
452
|
+
name = "FetchError";
|
|
453
|
+
constructor(cause, msg) {
|
|
454
|
+
super(msg);
|
|
455
|
+
this.cause = cause;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function querystring2(params, prefix = "") {
|
|
459
|
+
return Object.keys(params).map((key) => querystringSingleKey2(key, params[key], prefix)).filter((part) => part.length > 0).join("&");
|
|
460
|
+
}
|
|
461
|
+
function querystringSingleKey2(key, value, keyPrefix = "") {
|
|
462
|
+
const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key);
|
|
463
|
+
if (value instanceof Array) {
|
|
464
|
+
const multiValue = value.map((singleValue) => encodeURIComponent(String(singleValue))).join(`&${encodeURIComponent(fullKey)}=`);
|
|
465
|
+
return `${encodeURIComponent(fullKey)}=${multiValue}`;
|
|
466
|
+
}
|
|
467
|
+
if (value instanceof Set) {
|
|
468
|
+
const valueAsArray = Array.from(value);
|
|
469
|
+
return querystringSingleKey2(key, valueAsArray, keyPrefix);
|
|
470
|
+
}
|
|
471
|
+
if (value instanceof Date) {
|
|
472
|
+
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
|
|
473
|
+
}
|
|
474
|
+
if (value instanceof Object) {
|
|
475
|
+
return querystring2(value, fullKey);
|
|
476
|
+
}
|
|
477
|
+
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`;
|
|
478
|
+
}
|
|
479
|
+
// ../solution-sdk/package.json
|
|
480
|
+
var package_default = {
|
|
481
|
+
name: "@uipath/solution-sdk",
|
|
482
|
+
author: "UiPath",
|
|
483
|
+
license: "SEE LICENSE IN LICENSE.txt",
|
|
484
|
+
version: "1.203.0-preview.160",
|
|
485
|
+
repository: {
|
|
486
|
+
type: "git",
|
|
487
|
+
url: "https://github.com/UiPath/cli.git",
|
|
488
|
+
directory: "packages/solution-sdk"
|
|
489
|
+
},
|
|
490
|
+
publishConfig: {
|
|
491
|
+
registry: "https://npm.pkg.github.com/@uipath"
|
|
492
|
+
},
|
|
493
|
+
keywords: [
|
|
494
|
+
"uipath",
|
|
495
|
+
"solution",
|
|
496
|
+
"sdk"
|
|
497
|
+
],
|
|
498
|
+
type: "module",
|
|
499
|
+
main: "./dist/index.js",
|
|
500
|
+
types: "./dist/src/index.d.ts",
|
|
501
|
+
exports: {
|
|
502
|
+
".": {
|
|
503
|
+
types: "./dist/src/index.d.ts",
|
|
504
|
+
default: "./dist/index.js"
|
|
505
|
+
},
|
|
506
|
+
"./resources": {
|
|
507
|
+
types: "./dist/src/resources/index.d.ts",
|
|
508
|
+
default: "./dist/resources/index.js"
|
|
509
|
+
},
|
|
510
|
+
"./auth": {
|
|
511
|
+
types: "./dist/src/solution-auth.d.ts",
|
|
512
|
+
default: "./dist/solution-auth.js"
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
bin: {
|
|
516
|
+
"generate-sdk": "./dist/scripts/generate-sdk.js"
|
|
517
|
+
},
|
|
518
|
+
files: [
|
|
519
|
+
"dist"
|
|
520
|
+
],
|
|
521
|
+
private: false,
|
|
522
|
+
scripts: {
|
|
523
|
+
build: "bun build ./src/index.ts ./src/resources/index.ts ./src/solution-auth.ts --outdir dist --format esm --target node --splitting --sourcemap=linked && bun build ./src/scripts/generate-sdk.ts --outdir dist/scripts --format esm --target node --sourcemap=linked && tsc -p tsconfig.build.json --noCheck",
|
|
524
|
+
generate: "bun run src/scripts/generate-sdk.ts",
|
|
525
|
+
lint: "biome check .",
|
|
526
|
+
test: "vitest run",
|
|
527
|
+
"test:coverage": "vitest run --coverage"
|
|
528
|
+
},
|
|
529
|
+
devDependencies: {
|
|
530
|
+
"@openapitools/openapi-generator-cli": "^2.31.1",
|
|
531
|
+
"@uipath/auth": "workspace:*",
|
|
532
|
+
"@uipath/common": "workspace:*",
|
|
533
|
+
"@uipath/filesystem": "workspace:*",
|
|
534
|
+
"@uipath/orchestrator-sdk": "workspace:*",
|
|
535
|
+
"@uipath/resource-builder-sdk": "2025.11.0-alpha5891-4020",
|
|
536
|
+
"@uipath/solutionpackager-tool-core": "workspace:*",
|
|
537
|
+
"@uipath/studioweb-sdk": "workspace:*",
|
|
538
|
+
"@types/node": "^25.5.2",
|
|
539
|
+
fflate: "^0.8.2",
|
|
540
|
+
typescript: "^7.0.2"
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// ../solution-sdk/src/user-agent.ts
|
|
545
|
+
var SDK_USER_AGENT = getSdkUserAgentToken(package_default);
|
|
546
|
+
installSdkUserAgentHeader(BaseAPI2, SDK_USER_AGENT);
|
|
547
|
+
|
|
548
|
+
// ../solution-sdk/src/upload-service.ts
|
|
549
|
+
class SolutionUploadHttpError extends Error {
|
|
550
|
+
status;
|
|
551
|
+
operation;
|
|
552
|
+
method;
|
|
553
|
+
endpoint;
|
|
554
|
+
url;
|
|
555
|
+
body;
|
|
556
|
+
solutionId;
|
|
557
|
+
parsedErrorCode;
|
|
558
|
+
parsedErrorMessage;
|
|
559
|
+
authChallenge;
|
|
560
|
+
constructor(details) {
|
|
561
|
+
super(details.message);
|
|
562
|
+
this.name = "SolutionUploadHttpError";
|
|
563
|
+
this.status = details.status;
|
|
564
|
+
this.operation = details.operation;
|
|
565
|
+
this.method = details.method;
|
|
566
|
+
this.endpoint = details.endpoint;
|
|
567
|
+
this.url = details.url;
|
|
568
|
+
this.body = details.body;
|
|
569
|
+
if (details.operation === "overwrite") {
|
|
570
|
+
this.solutionId = details.solutionId;
|
|
571
|
+
}
|
|
572
|
+
this.authChallenge = details.authChallenge;
|
|
573
|
+
const parsed = parseUploadErrorBody(details.body);
|
|
574
|
+
this.parsedErrorCode = parsed.code;
|
|
575
|
+
this.parsedErrorMessage = parsed.message;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function parseUploadErrorBody(body) {
|
|
579
|
+
const [, parsed] = catchError(() => JSON.parse(body));
|
|
580
|
+
if (!parsed || typeof parsed !== "object")
|
|
581
|
+
return {};
|
|
582
|
+
const rawCode = parsed.code ?? parsed.Code;
|
|
583
|
+
const rawMessage = parsed.message ?? parsed.Message;
|
|
584
|
+
return {
|
|
585
|
+
code: typeof rawCode === "string" || typeof rawCode === "number" ? String(rawCode) : undefined,
|
|
586
|
+
message: typeof rawMessage === "string" && rawMessage.trim() ? rawMessage : undefined
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
function isSolutionUploadHttpError(error) {
|
|
590
|
+
return error instanceof Error && error.name === "SolutionUploadHttpError" && "operation" in error && (error.operation === "import" || error.operation === "overwrite") && "status" in error && typeof error.status === "number" && "body" in error && typeof error.body === "string";
|
|
591
|
+
}
|
|
592
|
+
function requestPath(url) {
|
|
593
|
+
const [, parsed] = catchError(() => new URL(url));
|
|
594
|
+
return parsed ? `${parsed.pathname}${parsed.search}` : url;
|
|
595
|
+
}
|
|
596
|
+
var TOKEN_SHAPED = /\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]*)?|\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/gi;
|
|
597
|
+
function redactTokens(text) {
|
|
598
|
+
return text.replace(TOKEN_SHAPED, "[REDACTED]");
|
|
599
|
+
}
|
|
600
|
+
var MAX_CHALLENGE_DESCRIPTION = 200;
|
|
601
|
+
function parseAuthChallenge(header) {
|
|
602
|
+
if (!header)
|
|
603
|
+
return {};
|
|
604
|
+
const code = /\berror="([^"]*)"/.exec(header)?.[1]?.trim();
|
|
605
|
+
const raw = /\berror_description="([^"]*)"/.exec(header)?.[1]?.trim();
|
|
606
|
+
const description = raw ? redactTokens(raw).slice(0, MAX_CHALLENGE_DESCRIPTION) : undefined;
|
|
607
|
+
return {
|
|
608
|
+
error: code || undefined,
|
|
609
|
+
description: description || undefined
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function summarizeAuthRefusalBody(body) {
|
|
613
|
+
return summarizePlatformBody(redactTokens(body));
|
|
614
|
+
}
|
|
615
|
+
function isAuthRefusal(status) {
|
|
616
|
+
return status === 401 || status === 403;
|
|
617
|
+
}
|
|
618
|
+
function buildAuthRefusalInstructions({
|
|
619
|
+
err,
|
|
620
|
+
subject,
|
|
621
|
+
target,
|
|
622
|
+
bodySuffix,
|
|
623
|
+
identityType,
|
|
624
|
+
clientId
|
|
625
|
+
}) {
|
|
626
|
+
const refusal = err.status === 401 ? "rejected the call as unauthorized (401)" : "forbade the call (403)";
|
|
627
|
+
const what = err.operation === "overwrite" ? `replacing the contents of solution ${target}` : "importing the solution package";
|
|
628
|
+
const actor = identityType === "Application" ? `This session acts as external application ${clientId ?? "(client id unknown)"}, so ` + `check what that application is allowed to do: it needs the Studio ` + `Web scopes that cover writing a solution (the StudioWebS2S grants ` + `on the external application), not just the Orchestrator or ` + `Solutions ones. ` : identityType === "User" ? `This session acts as a user, so check that your account has ` + `Studio Web access in this tenant. ` : `Check what this session is allowed to do in Studio Web — an ` + `external application needs the StudioWebS2S grants that cover ` + `writing a solution, a user needs Studio Web access in the tenant. `;
|
|
629
|
+
const tenant = err.operation === "overwrite" ? `Also confirm that ${target} is the solution this project should ` + `publish to, and that it lives in the organization and tenant ` + `this session logged into — the id comes from the SolutionId ` + `recorded in the enclosing solution file. An id this session ` + `cannot see at all is reported separately, as nothing to ` + `overwrite, so a refusal here points at the grants first. ` : `Also confirm you are logged into the organization and tenant the ` + `solution should land in. `;
|
|
630
|
+
const challenge = parseAuthChallenge(err.authChallenge);
|
|
631
|
+
const quoted = challenge.description ? ` ("${challenge.description}")` : "";
|
|
632
|
+
if (challenge.error === "invalid_token") {
|
|
633
|
+
return `Studio Web ${refusal} while ${what}, and rejected the credential ` + `itself with the reason \`invalid_token\`${quoted}. That is the ` + `platform refusing this token rather than the permissions behind ` + `it, so unlike a grants refusal this one is worth a fresh ` + `\`uip login\` — or, in a pipeline, a newly minted ` + `client-credentials token. Check too that the token was issued for ` + `the organization being called: one minted against a different ` + `environment or audience is refused this way. Nothing was uploaded ` + `and nothing ran, so the ${subject} is not the cause.${bodySuffix}`;
|
|
634
|
+
}
|
|
635
|
+
const authenticated = `The session is authenticated — the token was issued and had not ` + `expired when the call went out — so re-running \`uip login\` mints ` + `the same token and fails the same way. `;
|
|
636
|
+
const diagnosis = challenge.error === "insufficient_scope" ? `Studio Web reported \`insufficient_scope\`${quoted}, so the ` + `token is valid and simply not allowed to do this: logging in ` + `again will not change it. ` : challenge.error ? `${authenticated}Studio Web gave the reason ` + `\`${challenge.error}\`${quoted}. ` : authenticated;
|
|
637
|
+
const closing = bodySuffix ? `If the grants and the tenant are already right, report the platform ` + `response, including its traceId.${bodySuffix}` : challenge.error ? `If the grants and the tenant are already right, report that reason ` + `together with the request path and status above.` : `If the grants and the tenant are already right, report the request ` + `path and status above — Studio Web returned no response body and ` + `no authentication challenge, so there is nothing further to quote.`;
|
|
638
|
+
return `Studio Web ${refusal} while ${what}. ${diagnosis}${actor}${tenant}` + `Nothing was uploaded and nothing ran, so the ${subject} is not the ` + `cause. ${closing}`;
|
|
639
|
+
}
|
|
640
|
+
function buildSolutionUploadFailureOutput(err, options = {}) {
|
|
641
|
+
const stage = err.operation === "overwrite" ? "overwrite-solution" : "import-solution";
|
|
642
|
+
const authRefusal = isAuthRefusal(err.status);
|
|
643
|
+
const parsedMessage = authRefusal && err.parsedErrorMessage ? redactTokens(err.parsedErrorMessage) : err.parsedErrorMessage;
|
|
644
|
+
const detail = parsedMessage ? ` — ${parsedMessage}` : "";
|
|
645
|
+
const Message = `Failed during ${stage}: HTTP ${err.status} on ${err.method} ` + `${err.endpoint}${detail}`;
|
|
646
|
+
const quote = authRefusal ? summarizeAuthRefusalBody : summarizePlatformBody;
|
|
647
|
+
const body = err.parsedErrorMessage ? "" : quote(err.body);
|
|
648
|
+
const bodySuffix = body ? ` Platform response: "${body}".` : "";
|
|
649
|
+
const importHint = options.importCheckHint ? ` ${options.importCheckHint}` : "";
|
|
650
|
+
const subject = options.subject ?? "project";
|
|
651
|
+
const target = err.solutionId ?? `the solution at ${err.endpoint}`;
|
|
652
|
+
const authInstructions = isAuthRefusal(err.status) ? buildAuthRefusalInstructions({
|
|
653
|
+
err,
|
|
654
|
+
subject,
|
|
655
|
+
target,
|
|
656
|
+
bodySuffix,
|
|
657
|
+
identityType: options.identityType,
|
|
658
|
+
clientId: options.clientId
|
|
659
|
+
}) : undefined;
|
|
660
|
+
const Instructions = authInstructions ?? (err.operation === "overwrite" ? `Studio Web refused to replace the contents of solution ` + `${target}. Nothing was uploaded and nothing ran, so the ` + `${subject} is not the cause: do not re-author it, and do not ` + `copy the project into another solution. Confirm that ${target} ` + `is a solution you can still write to, and that it is the one ` + `this project should publish to — it comes from the SolutionId ` + `recorded in the enclosing solution file. If it is right, report ` + `the platform response.${bodySuffix}` : `Studio Web refused to import the solution package. Nothing was ` + `uploaded and nothing ran, so no part of the ${subject} executed ` + `— but the package itself may be what was rejected.${importHint} ` + `Report the platform response if the ${subject} is ` + `well-formed.${bodySuffix}`);
|
|
661
|
+
const Context = {
|
|
662
|
+
httpStatus: err.status,
|
|
663
|
+
stage,
|
|
664
|
+
endpoint: err.endpoint,
|
|
665
|
+
method: err.method
|
|
666
|
+
};
|
|
667
|
+
if (err.parsedErrorCode !== undefined) {
|
|
668
|
+
Context.errorCode = err.parsedErrorCode;
|
|
669
|
+
}
|
|
670
|
+
const output = {
|
|
671
|
+
Result: RESULTS.Failure,
|
|
672
|
+
Message,
|
|
673
|
+
Instructions,
|
|
674
|
+
Context
|
|
675
|
+
};
|
|
676
|
+
if (err.status === 401 && options.identityType === "Application") {
|
|
677
|
+
output.ErrorCode = "permission_denied";
|
|
678
|
+
}
|
|
679
|
+
if (err.operation === "overwrite" && err.status >= 500 && err.status < 600) {
|
|
680
|
+
output.Retry = "RetryAfter30Seconds";
|
|
681
|
+
}
|
|
682
|
+
return output;
|
|
683
|
+
}
|
|
684
|
+
var DEFAULT_UPLOAD_TIMEOUT_MS = 120000;
|
|
685
|
+
function resolveUploadTimeoutMs() {
|
|
686
|
+
const raw = Number(process.env?.UIPATH_SOLUTION_UPLOAD_TIMEOUT_MS);
|
|
687
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_UPLOAD_TIMEOUT_MS;
|
|
688
|
+
}
|
|
689
|
+
function formatTimeout(timeoutMs) {
|
|
690
|
+
return timeoutMs < 1000 ? `${Math.round(timeoutMs)}ms` : `${Math.round(timeoutMs / 1000)}s`;
|
|
691
|
+
}
|
|
692
|
+
function timeoutMessage(what, timeoutMs) {
|
|
693
|
+
return `${what} timed out after ${formatTimeout(timeoutMs)} — the connection ` + `stalled with no response from Studio Web. Retry the command; if it keeps ` + `timing out, check connectivity (VPN/proxy) to the tenant, or raise the ` + `ceiling with UIPATH_SOLUTION_UPLOAD_TIMEOUT_MS.`;
|
|
694
|
+
}
|
|
695
|
+
function isTimeoutError(error) {
|
|
696
|
+
let current = error;
|
|
697
|
+
for (let depth = 0;depth < 5 && current instanceof Error; depth += 1) {
|
|
698
|
+
if (current.name === "TimeoutError")
|
|
699
|
+
return true;
|
|
700
|
+
current = current.cause;
|
|
701
|
+
}
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
async function fetchWithUploadTimeout(url, init, what) {
|
|
705
|
+
const timeoutMs = resolveUploadTimeoutMs();
|
|
706
|
+
const [error, response] = await catchError(fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) }));
|
|
707
|
+
if (error) {
|
|
708
|
+
if (isTimeoutError(error)) {
|
|
709
|
+
throw new Error(timeoutMessage(what, timeoutMs));
|
|
710
|
+
}
|
|
711
|
+
throw error;
|
|
712
|
+
}
|
|
713
|
+
return response;
|
|
714
|
+
}
|
|
715
|
+
async function importSolution(config, organizationName, fileBuffer, fileName) {
|
|
716
|
+
const importUrl = `${config.baseUrl}/${encodeURIComponent(organizationName)}/studio_/backend/api/Solution/Import?desktopJit=true&useFileRest=true&enableUnifiedBuild=true`;
|
|
717
|
+
const formData = new FormData;
|
|
718
|
+
const fileBlob = new Blob([fileBuffer], {
|
|
719
|
+
type: "application/octet-stream"
|
|
720
|
+
});
|
|
721
|
+
formData.append("uploadFile", fileBlob, fileName);
|
|
722
|
+
formData.append("telemetryData", JSON.stringify({ FirstRun: false }));
|
|
723
|
+
const headers = addSdkUserAgentHeader({
|
|
724
|
+
Authorization: `Bearer ${config.authToken}`
|
|
725
|
+
}, SDK_USER_AGENT);
|
|
726
|
+
if (config.tenantId) {
|
|
727
|
+
headers["x-uipath-tenantid"] = config.tenantId;
|
|
728
|
+
}
|
|
729
|
+
const response = await fetchWithUploadTimeout(importUrl, { method: "POST", headers, body: formData }, "Studio Web solution import");
|
|
730
|
+
if (!response.ok) {
|
|
731
|
+
const text = await response.text().catch(() => "");
|
|
732
|
+
throw new SolutionUploadHttpError({
|
|
733
|
+
operation: "import",
|
|
734
|
+
status: response.status,
|
|
735
|
+
method: "POST",
|
|
736
|
+
endpoint: requestPath(importUrl),
|
|
737
|
+
url: importUrl,
|
|
738
|
+
body: text,
|
|
739
|
+
authChallenge: response.headers?.get("www-authenticate") ?? undefined,
|
|
740
|
+
message: `Import failed (${response.status}): ${text}`
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
return await response.json();
|
|
744
|
+
}
|
|
745
|
+
async function overwriteSolution(config, organizationName, fileBuffer, fileName, solutionId, createSnapshot = false) {
|
|
746
|
+
const overwriteUrl = `${config.baseUrl}/${encodeURIComponent(organizationName)}/studio_/backend/api/Solution/${encodeURIComponent(solutionId)}/Overwrite`;
|
|
747
|
+
const formData = new FormData;
|
|
748
|
+
const fileBlob = new Blob([fileBuffer], {
|
|
749
|
+
type: "application/octet-stream"
|
|
750
|
+
});
|
|
751
|
+
formData.append("UploadFile", fileBlob, fileName);
|
|
752
|
+
if (createSnapshot) {
|
|
753
|
+
formData.append("createSnapshot", "true");
|
|
754
|
+
}
|
|
755
|
+
const headers = addSdkUserAgentHeader({
|
|
756
|
+
Authorization: `Bearer ${config.authToken}`
|
|
757
|
+
}, SDK_USER_AGENT);
|
|
758
|
+
if (config.tenantId) {
|
|
759
|
+
headers["x-uipath-tenantid"] = config.tenantId;
|
|
760
|
+
}
|
|
761
|
+
const response = await fetchWithUploadTimeout(overwriteUrl, { method: "POST", headers, body: formData }, "Studio Web solution overwrite");
|
|
762
|
+
if (!response.ok) {
|
|
763
|
+
const text = await response.text().catch(() => "");
|
|
764
|
+
throw new SolutionUploadHttpError({
|
|
765
|
+
operation: "overwrite",
|
|
766
|
+
status: response.status,
|
|
767
|
+
method: "POST",
|
|
768
|
+
endpoint: requestPath(overwriteUrl),
|
|
769
|
+
url: overwriteUrl,
|
|
770
|
+
body: text,
|
|
771
|
+
authChallenge: response.headers?.get("www-authenticate") ?? undefined,
|
|
772
|
+
message: `Overwrite failed (${response.status}): ${text}`,
|
|
773
|
+
solutionId
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
return await response.json();
|
|
777
|
+
}
|
|
778
|
+
async function uploadOrOverwriteSolution(config, organizationName, fileBufferOrPath, solutionId, fs) {
|
|
779
|
+
let fileBuffer;
|
|
780
|
+
let fileName;
|
|
781
|
+
if (typeof fileBufferOrPath === "string") {
|
|
782
|
+
if (!fs) {
|
|
783
|
+
throw new Error("IFileSystem is required when passing a file path to uploadOrOverwriteSolution");
|
|
784
|
+
}
|
|
785
|
+
const content = await fs.readFile(fileBufferOrPath);
|
|
786
|
+
if (content === null) {
|
|
787
|
+
throw new Error(`Failed to read file: ${fileBufferOrPath}`);
|
|
788
|
+
}
|
|
789
|
+
fileBuffer = new Uint8Array(content);
|
|
790
|
+
fileName = fs.path.basename(fileBufferOrPath);
|
|
791
|
+
} else {
|
|
792
|
+
fileBuffer = fileBufferOrPath;
|
|
793
|
+
fileName = "solution.uis";
|
|
794
|
+
}
|
|
795
|
+
if (solutionId) {
|
|
796
|
+
logger.info(`Attempting to overwrite existing solution ${solutionId} on Studio Web...`);
|
|
797
|
+
const [overwriteError, overwriteResult] = await catchError(overwriteSolution(config, organizationName, fileBuffer, fileName, solutionId));
|
|
798
|
+
if (!overwriteError) {
|
|
799
|
+
logger.info("Solution overwritten successfully.");
|
|
800
|
+
return overwriteResult;
|
|
801
|
+
}
|
|
802
|
+
if (isSolutionUploadHttpError(overwriteError) && overwriteError.status === 404) {
|
|
803
|
+
logger.info(`Solution ${solutionId} not found on Studio Web. Importing as new solution...`);
|
|
804
|
+
} else {
|
|
805
|
+
throw overwriteError;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
logger.info("Importing new solution to Studio Web...");
|
|
809
|
+
return importSolution(config, organizationName, fileBuffer, fileName);
|
|
810
|
+
}
|
|
811
|
+
// ../solution-sdk/src/zip-utils.ts
|
|
812
|
+
var DEFAULT_EXCLUDED_DIR_NAMES = new Set([
|
|
813
|
+
".venv",
|
|
814
|
+
"node_modules",
|
|
815
|
+
"__pycache__",
|
|
816
|
+
".git",
|
|
817
|
+
".local"
|
|
818
|
+
]);
|
|
819
|
+
var DEFAULT_EXCLUDED_FILE_PATTERNS = [
|
|
820
|
+
/^\.env($|\..+)/
|
|
821
|
+
];
|
|
822
|
+
function isDefaultExcludedFileName(name) {
|
|
823
|
+
for (const pattern of DEFAULT_EXCLUDED_FILE_PATTERNS) {
|
|
824
|
+
if (pattern.test(name)) {
|
|
825
|
+
return true;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
var UIPIGNORE_FILENAME = ".uipignore";
|
|
831
|
+
function parseUipignore(body) {
|
|
832
|
+
const names = new Set;
|
|
833
|
+
for (const rawLine of body.split(/\r?\n/)) {
|
|
834
|
+
const line = rawLine.split("#")[0]?.trim();
|
|
835
|
+
if (!line) {
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
if (line.includes("/") || line.includes("\\")) {
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
names.add(line);
|
|
842
|
+
}
|
|
843
|
+
return names;
|
|
844
|
+
}
|
|
845
|
+
async function collectTree(fs, dir, options = {}) {
|
|
846
|
+
const files = [];
|
|
847
|
+
const emptyDirs = [];
|
|
848
|
+
const excluded = new Set(DEFAULT_EXCLUDED_DIR_NAMES);
|
|
849
|
+
if (options.additionalExcludeDirs) {
|
|
850
|
+
for (const name of options.additionalExcludeDirs) {
|
|
851
|
+
excluded.add(name);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
async function walk(current, isRoot) {
|
|
855
|
+
const entries = await fs.readdir(current);
|
|
856
|
+
if (entries.length === 0) {
|
|
857
|
+
if (!isRoot) {
|
|
858
|
+
emptyDirs.push(current);
|
|
859
|
+
}
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
for (const entryName of entries) {
|
|
863
|
+
const fullPath = fs.path.join(current, entryName);
|
|
864
|
+
const entryStat = await fs.stat(fullPath);
|
|
865
|
+
if (entryStat?.isDirectory()) {
|
|
866
|
+
if (excluded.has(entryName)) {
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
await walk(fullPath, false);
|
|
870
|
+
} else if (entryStat?.isFile()) {
|
|
871
|
+
if (isDefaultExcludedFileName(entryName)) {
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
files.push(fullPath);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
await walk(dir, true);
|
|
879
|
+
return { files, emptyDirs };
|
|
880
|
+
}
|
|
881
|
+
async function createZipFromDir(fs, dir, options = {}) {
|
|
882
|
+
const { files, emptyDirs } = await collectTree(fs, dir, options);
|
|
883
|
+
const zippable = {};
|
|
884
|
+
for (const filePath of files) {
|
|
885
|
+
const relPath = fs.path.relative(dir, filePath).replace(/\\/g, "/");
|
|
886
|
+
const data = await fs.readFile(filePath);
|
|
887
|
+
if (data) {
|
|
888
|
+
zippable[relPath] = new Uint8Array(data);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
for (const emptyDir of emptyDirs) {
|
|
892
|
+
const relPath = fs.path.relative(dir, emptyDir).replace(/\\/g, "/");
|
|
893
|
+
if (!relPath) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
zippable[`${relPath}/`] = new Uint8Array(0);
|
|
897
|
+
}
|
|
898
|
+
return finalizeZip64(zipSync(zippable, { level: 6 }));
|
|
899
|
+
}
|
|
900
|
+
var EOCD_SIZE = 22;
|
|
901
|
+
var EOCD_SIGNATURE = 101010256;
|
|
902
|
+
var CD_HEADER_SIGNATURE = 33639248;
|
|
903
|
+
var ZIP64_EOCD_SIGNATURE = 101075792;
|
|
904
|
+
var ZIP64_EOCD_SIZE = 56;
|
|
905
|
+
var ZIP64_LOCATOR_SIGNATURE = 117853008;
|
|
906
|
+
var ZIP64_LOCATOR_SIZE = 20;
|
|
907
|
+
var MAX_ZIP32_ENTRIES = 65535;
|
|
908
|
+
var MAX_ZIP32_OFFSET = 4294967295;
|
|
909
|
+
function finalizeZip64(zip) {
|
|
910
|
+
const view = new DataView(zip.buffer, zip.byteOffset, zip.byteLength);
|
|
911
|
+
const eocdStart = zip.byteLength - EOCD_SIZE;
|
|
912
|
+
if (eocdStart < 0 || view.getUint32(eocdStart, true) !== EOCD_SIGNATURE) {
|
|
913
|
+
throw new Error("Cannot finalize the solution archive: it does not end with an end-of-central-directory record.");
|
|
914
|
+
}
|
|
915
|
+
const cdSize = view.getUint32(eocdStart + 12, true);
|
|
916
|
+
const cdOffset = view.getUint32(eocdStart + 16, true);
|
|
917
|
+
const entryCount = countCentralDirectoryEntries(view, cdOffset, cdSize);
|
|
918
|
+
if (entryCount <= MAX_ZIP32_ENTRIES) {
|
|
919
|
+
return zip;
|
|
920
|
+
}
|
|
921
|
+
if (cdOffset >= MAX_ZIP32_OFFSET || zip.byteLength >= MAX_ZIP32_OFFSET) {
|
|
922
|
+
throw new Error(`The solution archive is ${zip.byteLength} bytes, past the 4 GB limit this packer supports. Exclude large directories from the solution with a .uipignore file.`);
|
|
923
|
+
}
|
|
924
|
+
const tail = new Uint8Array(ZIP64_EOCD_SIZE + ZIP64_LOCATOR_SIZE + EOCD_SIZE);
|
|
925
|
+
const tailView = new DataView(tail.buffer);
|
|
926
|
+
const count = BigInt(entryCount);
|
|
927
|
+
tailView.setUint32(0, ZIP64_EOCD_SIGNATURE, true);
|
|
928
|
+
tailView.setBigUint64(4, BigInt(ZIP64_EOCD_SIZE - 12), true);
|
|
929
|
+
tailView.setUint16(12, 45, true);
|
|
930
|
+
tailView.setUint16(14, 45, true);
|
|
931
|
+
tailView.setUint32(16, 0, true);
|
|
932
|
+
tailView.setUint32(20, 0, true);
|
|
933
|
+
tailView.setBigUint64(24, count, true);
|
|
934
|
+
tailView.setBigUint64(32, count, true);
|
|
935
|
+
tailView.setBigUint64(40, BigInt(cdSize), true);
|
|
936
|
+
tailView.setBigUint64(48, BigInt(cdOffset), true);
|
|
937
|
+
const locator = ZIP64_EOCD_SIZE;
|
|
938
|
+
tailView.setUint32(locator, ZIP64_LOCATOR_SIGNATURE, true);
|
|
939
|
+
tailView.setUint32(locator + 4, 0, true);
|
|
940
|
+
tailView.setBigUint64(locator + 8, BigInt(eocdStart), true);
|
|
941
|
+
tailView.setUint32(locator + 16, 1, true);
|
|
942
|
+
const eocd = ZIP64_EOCD_SIZE + ZIP64_LOCATOR_SIZE;
|
|
943
|
+
tailView.setUint32(eocd, EOCD_SIGNATURE, true);
|
|
944
|
+
tailView.setUint16(eocd + 4, 0, true);
|
|
945
|
+
tailView.setUint16(eocd + 6, 0, true);
|
|
946
|
+
tailView.setUint16(eocd + 8, MAX_ZIP32_ENTRIES, true);
|
|
947
|
+
tailView.setUint16(eocd + 10, MAX_ZIP32_ENTRIES, true);
|
|
948
|
+
tailView.setUint32(eocd + 12, cdSize, true);
|
|
949
|
+
tailView.setUint32(eocd + 16, cdOffset, true);
|
|
950
|
+
tailView.setUint16(eocd + 20, 0, true);
|
|
951
|
+
const out = new Uint8Array(eocdStart + tail.byteLength);
|
|
952
|
+
out.set(zip.subarray(0, eocdStart), 0);
|
|
953
|
+
out.set(tail, eocdStart);
|
|
954
|
+
return out;
|
|
955
|
+
}
|
|
956
|
+
function countCentralDirectoryEntries(view, cdOffset, cdSize) {
|
|
957
|
+
const end = cdOffset + cdSize;
|
|
958
|
+
let position = cdOffset;
|
|
959
|
+
let count = 0;
|
|
960
|
+
while (position + 46 <= end && view.getUint32(position, true) === CD_HEADER_SIGNATURE) {
|
|
961
|
+
const nameLength = view.getUint16(position + 28, true);
|
|
962
|
+
const extraLength = view.getUint16(position + 30, true);
|
|
963
|
+
const commentLength = view.getUint16(position + 32, true);
|
|
964
|
+
position += 46 + nameLength + extraLength + commentLength;
|
|
965
|
+
count++;
|
|
966
|
+
}
|
|
967
|
+
return count;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// ../solution-sdk/src/bundle-service.ts
|
|
971
|
+
async function readUipignore(fs, dir) {
|
|
972
|
+
const ignorePath = fs.path.join(dir, UIPIGNORE_FILENAME);
|
|
973
|
+
if (!await fs.exists(ignorePath)) {
|
|
974
|
+
return new Set;
|
|
975
|
+
}
|
|
976
|
+
try {
|
|
977
|
+
const body = await fs.readFile(ignorePath, "utf-8");
|
|
978
|
+
if (!body) {
|
|
979
|
+
return new Set;
|
|
980
|
+
}
|
|
981
|
+
return parseUipignore(body);
|
|
982
|
+
} catch (err) {
|
|
983
|
+
logger.warn(`Could not read ${UIPIGNORE_FILENAME}; ignoring it: ${String(err)}`);
|
|
984
|
+
return new Set;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
async function mergeProjectUipignore(fs, projectPath, projectLabel, solutionExcludeDirs) {
|
|
988
|
+
const projectExtra = await readUipignore(fs, projectPath);
|
|
989
|
+
if (projectExtra.size === 0) {
|
|
990
|
+
return solutionExcludeDirs;
|
|
991
|
+
}
|
|
992
|
+
const merged = new Set(solutionExcludeDirs);
|
|
993
|
+
for (const name of projectExtra) {
|
|
994
|
+
merged.add(name);
|
|
995
|
+
}
|
|
996
|
+
logger.info(`[${projectLabel}] Applying ${projectExtra.size} extra exclude(s) from project ${UIPIGNORE_FILENAME}: ${[...projectExtra].join(", ")}`);
|
|
997
|
+
return merged;
|
|
998
|
+
}
|
|
999
|
+
var toForwardSlash = (p) => p.replace(/\\/g, "/");
|
|
1000
|
+
async function mirrorTree(fs, srcDir, relativeBase, destBaseDir, additionalExcludeDirs) {
|
|
1001
|
+
const { files, emptyDirs } = await collectTree(fs, srcDir, {
|
|
1002
|
+
additionalExcludeDirs
|
|
1003
|
+
});
|
|
1004
|
+
if (files.length === 0 && emptyDirs.length === 0) {
|
|
1005
|
+
const rootRel = fs.path.relative(relativeBase, srcDir);
|
|
1006
|
+
if (rootRel) {
|
|
1007
|
+
await fs.mkdir(fs.path.join(destBaseDir, rootRel));
|
|
1008
|
+
}
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
for (const f of files) {
|
|
1012
|
+
const relPath = fs.path.relative(relativeBase, f);
|
|
1013
|
+
const destPath = fs.path.join(destBaseDir, relPath);
|
|
1014
|
+
const data = await fs.readFile(f);
|
|
1015
|
+
if (data) {
|
|
1016
|
+
await fs.writeFile(destPath, data);
|
|
1017
|
+
} else {
|
|
1018
|
+
logger.warn(`Skipping empty or unreadable file: ${f}`);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
for (const dir of emptyDirs) {
|
|
1022
|
+
const relPath = fs.path.relative(relativeBase, dir);
|
|
1023
|
+
if (!relPath) {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
await fs.mkdir(fs.path.join(destBaseDir, relPath));
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
async function flattenProjectDir(fs, srcProjectDir, destProjectDir, additionalExcludeDirs) {
|
|
1030
|
+
await fs.mkdir(destProjectDir);
|
|
1031
|
+
const topLevelExclude = new Set(DEFAULT_EXCLUDED_DIR_NAMES);
|
|
1032
|
+
if (additionalExcludeDirs) {
|
|
1033
|
+
for (const name of additionalExcludeDirs) {
|
|
1034
|
+
topLevelExclude.add(name);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
const entries = await fs.readdir(srcProjectDir);
|
|
1038
|
+
for (const entryName of entries) {
|
|
1039
|
+
const srcPath = fs.path.join(srcProjectDir, entryName);
|
|
1040
|
+
const entryStat = await fs.stat(srcPath);
|
|
1041
|
+
if (!entryStat) {
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
if (entryStat.isFile()) {
|
|
1045
|
+
if (isDefaultExcludedFileName(entryName)) {
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
const data = await fs.readFile(srcPath);
|
|
1049
|
+
if (data) {
|
|
1050
|
+
await fs.writeFile(fs.path.join(destProjectDir, entryName), data);
|
|
1051
|
+
}
|
|
1052
|
+
} else if (entryStat.isDirectory()) {
|
|
1053
|
+
if (topLevelExclude.has(entryName)) {
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
if (entryName === "content" || entryName === "flow_files") {
|
|
1057
|
+
await mirrorTree(fs, srcPath, srcPath, destProjectDir, additionalExcludeDirs);
|
|
1058
|
+
} else {
|
|
1059
|
+
await mirrorTree(fs, srcPath, srcProjectDir, destProjectDir, additionalExcludeDirs);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
async function bundleSolution(fs, solutionDir, outputDir, outputName) {
|
|
1065
|
+
const resolvedPath = fs.path.resolve(solutionDir);
|
|
1066
|
+
const solStat = await fs.stat(resolvedPath);
|
|
1067
|
+
if (!solStat) {
|
|
1068
|
+
throw new Error(`Path not found: ${resolvedPath}`);
|
|
1069
|
+
}
|
|
1070
|
+
if (!solStat.isDirectory()) {
|
|
1071
|
+
throw new Error(`Not a directory: ${resolvedPath}. Provide the solution directory containing a .uipx file.`);
|
|
1072
|
+
}
|
|
1073
|
+
const { uipx, uipxFileName } = await readUipxFile(fs, resolvedPath);
|
|
1074
|
+
const additionalExcludeDirs = await readUipignore(fs, resolvedPath);
|
|
1075
|
+
if (additionalExcludeDirs.size > 0) {
|
|
1076
|
+
logger.info(`Applying ${additionalExcludeDirs.size} extra exclude(s) from .uipignore: ${[...additionalExcludeDirs].join(", ")}`);
|
|
1077
|
+
}
|
|
1078
|
+
const effectiveExclude = new Set(DEFAULT_EXCLUDED_DIR_NAMES);
|
|
1079
|
+
for (const name of additionalExcludeDirs) {
|
|
1080
|
+
effectiveExclude.add(name);
|
|
1081
|
+
}
|
|
1082
|
+
for (const project of uipx.Projects) {
|
|
1083
|
+
project.ProjectRelativePath = toForwardSlash(project.ProjectRelativePath);
|
|
1084
|
+
}
|
|
1085
|
+
assertStudioWebCompatibleProjects(uipx.Projects);
|
|
1086
|
+
const solutionStorage = {
|
|
1087
|
+
SolutionId: uipx.SolutionId,
|
|
1088
|
+
Projects: uipx.Projects.map((p) => ({
|
|
1089
|
+
ProjectId: p.Id,
|
|
1090
|
+
ProjectRelativePath: p.ProjectRelativePath
|
|
1091
|
+
}))
|
|
1092
|
+
};
|
|
1093
|
+
const solutionName = outputName || fs.path.basename(uipxFileName, ".uipx");
|
|
1094
|
+
const stagingDir = fs.path.join(resolvedPath, `.uis-staging-${Date.now()}`);
|
|
1095
|
+
await fs.mkdir(stagingDir);
|
|
1096
|
+
try {
|
|
1097
|
+
await fs.writeFile(fs.path.join(stagingDir, "SolutionStorage.json"), JSON.stringify(solutionStorage));
|
|
1098
|
+
await fs.writeFile(fs.path.join(stagingDir, uipxFileName), JSON.stringify(uipx));
|
|
1099
|
+
for (const project of uipx.Projects) {
|
|
1100
|
+
const projectDir = fs.path.dirname(project.ProjectRelativePath);
|
|
1101
|
+
const srcProjectPath = fs.path.join(resolvedPath, projectDir);
|
|
1102
|
+
const projectStat = await fs.stat(srcProjectPath);
|
|
1103
|
+
if (!projectStat) {
|
|
1104
|
+
throw new Error(`Project directory not found: ${srcProjectPath}`);
|
|
1105
|
+
}
|
|
1106
|
+
const projectExcludeDirs = await mergeProjectUipignore(fs, srcProjectPath, projectDir, additionalExcludeDirs);
|
|
1107
|
+
await flattenProjectDir(fs, srcProjectPath, fs.path.join(stagingDir, projectDir), projectExcludeDirs);
|
|
1108
|
+
}
|
|
1109
|
+
const resourcesPath = fs.path.join(resolvedPath, "resources");
|
|
1110
|
+
if (!effectiveExclude.has("resources") && await fs.exists(resourcesPath)) {
|
|
1111
|
+
await mirrorTree(fs, resourcesPath, resolvedPath, stagingDir, additionalExcludeDirs);
|
|
1112
|
+
}
|
|
1113
|
+
const userProfilePath = fs.path.join(resolvedPath, "userProfile");
|
|
1114
|
+
if (!effectiveExclude.has("userProfile") && await fs.exists(userProfilePath)) {
|
|
1115
|
+
await mirrorTree(fs, userProfilePath, resolvedPath, stagingDir, additionalExcludeDirs);
|
|
1116
|
+
}
|
|
1117
|
+
const resolvedOutputDir = fs.path.resolve(outputDir);
|
|
1118
|
+
if (!await fs.exists(resolvedOutputDir)) {
|
|
1119
|
+
await fs.mkdir(resolvedOutputDir);
|
|
1120
|
+
}
|
|
1121
|
+
const uisPath = fs.path.join(resolvedOutputDir, `${solutionName}.uis`);
|
|
1122
|
+
const zipData = await createZipFromDir(fs, stagingDir);
|
|
1123
|
+
await fs.writeFile(uisPath, zipData);
|
|
1124
|
+
const uipxPath = fs.path.join(resolvedPath, uipxFileName);
|
|
1125
|
+
return {
|
|
1126
|
+
uisPath,
|
|
1127
|
+
solutionName,
|
|
1128
|
+
solutionId: uipx.SolutionId,
|
|
1129
|
+
uipxPath,
|
|
1130
|
+
projects: uipx.Projects.map((p) => ({
|
|
1131
|
+
Name: fs.path.dirname(p.ProjectRelativePath),
|
|
1132
|
+
Type: p.Type
|
|
1133
|
+
}))
|
|
1134
|
+
};
|
|
1135
|
+
} finally {
|
|
1136
|
+
await fs.rm(stagingDir);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
// ../solution-sdk/src/project-debug-status.ts
|
|
1140
|
+
var SUCCEEDED = "creationsucceeded";
|
|
1141
|
+
var TERMINAL = new Set([SUCCEEDED, "creationfailed", "validationfailed"]);
|
|
1142
|
+
// src/services/agent-debug-service.ts
|
|
1143
|
+
import { randomUUID } from "node:crypto";
|
|
1144
|
+
|
|
1145
|
+
// src/services/agent-builder-migrations.ts
|
|
1146
|
+
var warnedLegacyToolGuardrailPolicies = false;
|
|
1147
|
+
function migrateLegacyToolGuardrailPolicies(builder) {
|
|
1148
|
+
let migrated = false;
|
|
1149
|
+
for (const resource of builder.resources ?? []) {
|
|
1150
|
+
if (resource.$resourceType !== "tool" || !Array.isArray(resource.guardrailPolicies)) {
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
if (!resource.guardrail?.policies?.length) {
|
|
1154
|
+
resource.guardrail = { policies: resource.guardrailPolicies };
|
|
1155
|
+
migrated = true;
|
|
1156
|
+
}
|
|
1157
|
+
delete resource.guardrailPolicies;
|
|
1158
|
+
}
|
|
1159
|
+
if (migrated && !warnedLegacyToolGuardrailPolicies) {
|
|
1160
|
+
OutputFormatter.log({
|
|
1161
|
+
Warning: "Migrated legacy guardrailPolicies to guardrail.policies."
|
|
1162
|
+
});
|
|
1163
|
+
warnedLegacyToolGuardrailPolicies = true;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/services/agent-project-service.ts
|
|
1168
|
+
class AgentProjectService {
|
|
1169
|
+
fs = getFileSystem();
|
|
1170
|
+
resolvedAgentDirs = new Map;
|
|
1171
|
+
getAgentDir(projectPath) {
|
|
1172
|
+
const cached = this.resolvedAgentDirs.get(projectPath);
|
|
1173
|
+
if (cached) {
|
|
1174
|
+
return cached;
|
|
1175
|
+
}
|
|
1176
|
+
return projectPath;
|
|
1177
|
+
}
|
|
1178
|
+
async resolveAgentDir(projectPath) {
|
|
1179
|
+
const cached = this.resolvedAgentDirs.get(projectPath);
|
|
1180
|
+
if (cached) {
|
|
1181
|
+
return cached;
|
|
1182
|
+
}
|
|
1183
|
+
const flatAgent = this.fs.path.join(projectPath, "agent.json");
|
|
1184
|
+
if (await this.fs.stat(flatAgent)) {
|
|
1185
|
+
this.resolvedAgentDirs.set(projectPath, projectPath);
|
|
1186
|
+
return projectPath;
|
|
1187
|
+
}
|
|
1188
|
+
const entries = await this.fs.readdir(projectPath);
|
|
1189
|
+
for (const entry of entries) {
|
|
1190
|
+
if (entry.startsWith(".") || entry === "evals" || entry === "features" || entry === "resources" || entry === "userProfile") {
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
const candidate = this.fs.path.join(projectPath, entry);
|
|
1194
|
+
const candidateStat = await this.fs.stat(candidate);
|
|
1195
|
+
if (!candidateStat?.isDirectory())
|
|
1196
|
+
continue;
|
|
1197
|
+
const agentStat = await this.fs.stat(this.fs.path.join(candidate, "agent.json"));
|
|
1198
|
+
if (agentStat) {
|
|
1199
|
+
this.resolvedAgentDirs.set(projectPath, candidate);
|
|
1200
|
+
return candidate;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
this.resolvedAgentDirs.set(projectPath, projectPath);
|
|
1204
|
+
return projectPath;
|
|
1205
|
+
}
|
|
1206
|
+
async readAgentJson(projectPath) {
|
|
1207
|
+
const agentDir = this.getAgentDir(projectPath);
|
|
1208
|
+
const raw = await this.fs.readFile(this.fs.path.join(agentDir, "agent.json"), "utf-8");
|
|
1209
|
+
return JSON.parse(raw);
|
|
1210
|
+
}
|
|
1211
|
+
async writeAgentJson(projectPath, agent) {
|
|
1212
|
+
const agentDir = this.getAgentDir(projectPath);
|
|
1213
|
+
await this.fs.writeFile(this.fs.path.join(agentDir, "agent.json"), `${JSON.stringify(agent, null, 2)}
|
|
1214
|
+
`);
|
|
1215
|
+
const builderDir = this.fs.path.join(agentDir, ".agent-builder");
|
|
1216
|
+
await this.safeMkdir(builderDir);
|
|
1217
|
+
let existingBuilder;
|
|
1218
|
+
const [builderReadError, builderRaw] = await catchError(this.fs.readFile(this.fs.path.join(builderDir, "agent.json"), "utf-8"));
|
|
1219
|
+
if (!builderReadError && builderRaw) {
|
|
1220
|
+
try {
|
|
1221
|
+
existingBuilder = JSON.parse(builderRaw);
|
|
1222
|
+
migrateLegacyToolGuardrailPolicies(existingBuilder);
|
|
1223
|
+
} catch {}
|
|
1224
|
+
}
|
|
1225
|
+
const builderAgent = {
|
|
1226
|
+
id: agent.projectId,
|
|
1227
|
+
name: existingBuilder?.name ?? "Agent",
|
|
1228
|
+
...agent,
|
|
1229
|
+
resources: existingBuilder?.resources ?? agent.resources ?? [],
|
|
1230
|
+
features: existingBuilder?.features ?? agent.features ?? []
|
|
1231
|
+
};
|
|
1232
|
+
await this.fs.writeFile(this.fs.path.join(builderDir, "agent.json"), `${JSON.stringify(builderAgent, null, 2)}
|
|
1233
|
+
`);
|
|
1234
|
+
}
|
|
1235
|
+
async readSolutionStorage(projectPath) {
|
|
1236
|
+
const raw = await this.fs.readFile(this.fs.path.join(projectPath, "SolutionStorage.json"), "utf-8");
|
|
1237
|
+
return JSON.parse(raw);
|
|
1238
|
+
}
|
|
1239
|
+
async writeSolutionStorage(projectPath, storage) {
|
|
1240
|
+
await this.fs.writeFile(this.fs.path.join(projectPath, "SolutionStorage.json"), `${JSON.stringify(storage, null, 2)}
|
|
1241
|
+
`);
|
|
1242
|
+
}
|
|
1243
|
+
async readEvaluator(projectPath, fileName) {
|
|
1244
|
+
const filePath = this.fs.path.join(this.getAgentDir(projectPath), "evals", "evaluators", fileName);
|
|
1245
|
+
const raw = await this.fs.readFile(filePath, "utf-8");
|
|
1246
|
+
return JSON.parse(raw);
|
|
1247
|
+
}
|
|
1248
|
+
async writeEvaluator(projectPath, evaluator) {
|
|
1249
|
+
const dir = this.fs.path.join(this.getAgentDir(projectPath), "evals", "evaluators");
|
|
1250
|
+
await this.safeMkdir(dir);
|
|
1251
|
+
await this.fs.writeFile(this.fs.path.join(dir, evaluator.fileName), `${JSON.stringify(evaluator, null, 2)}
|
|
1252
|
+
`);
|
|
1253
|
+
}
|
|
1254
|
+
async listEvaluators(projectPath) {
|
|
1255
|
+
const dir = this.fs.path.join(this.getAgentDir(projectPath), "evals", "evaluators");
|
|
1256
|
+
const [error, files] = await catchError(this.fs.readdir(dir));
|
|
1257
|
+
if (error) {
|
|
1258
|
+
return [];
|
|
1259
|
+
}
|
|
1260
|
+
const evaluators = [];
|
|
1261
|
+
for (const file of files) {
|
|
1262
|
+
if (file.endsWith(".json")) {
|
|
1263
|
+
const raw = await this.fs.readFile(this.fs.path.join(dir, file), "utf-8");
|
|
1264
|
+
evaluators.push(JSON.parse(raw));
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return evaluators;
|
|
1268
|
+
}
|
|
1269
|
+
async deleteEvaluator(projectPath, fileName) {
|
|
1270
|
+
const filePath = this.fs.path.join(this.getAgentDir(projectPath), "evals", "evaluators", fileName);
|
|
1271
|
+
await this.fs.rm(filePath);
|
|
1272
|
+
}
|
|
1273
|
+
async readEvalSet(projectPath, fileName) {
|
|
1274
|
+
const filePath = this.fs.path.join(this.getAgentDir(projectPath), "evals", "eval-sets", fileName);
|
|
1275
|
+
const raw = await this.fs.readFile(filePath, "utf-8");
|
|
1276
|
+
return JSON.parse(raw);
|
|
1277
|
+
}
|
|
1278
|
+
async writeEvalSet(projectPath, evalSet, fileName) {
|
|
1279
|
+
const dir = this.fs.path.join(this.getAgentDir(projectPath), "evals", "eval-sets");
|
|
1280
|
+
await this.safeMkdir(dir);
|
|
1281
|
+
await this.fs.writeFile(this.fs.path.join(dir, fileName), `${JSON.stringify(evalSet, null, 2)}
|
|
1282
|
+
`);
|
|
1283
|
+
}
|
|
1284
|
+
async listEvalSets(projectPath) {
|
|
1285
|
+
const dir = this.fs.path.join(this.getAgentDir(projectPath), "evals", "eval-sets");
|
|
1286
|
+
const [error, files] = await catchError(this.fs.readdir(dir));
|
|
1287
|
+
if (error) {
|
|
1288
|
+
return [];
|
|
1289
|
+
}
|
|
1290
|
+
const sets = [];
|
|
1291
|
+
for (const file of files) {
|
|
1292
|
+
if (file.endsWith(".json")) {
|
|
1293
|
+
const raw = await this.fs.readFile(this.fs.path.join(dir, file), "utf-8");
|
|
1294
|
+
sets.push(JSON.parse(raw));
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
return sets;
|
|
1298
|
+
}
|
|
1299
|
+
async deleteEvalSet(projectPath, fileName) {
|
|
1300
|
+
const filePath = this.fs.path.join(this.getAgentDir(projectPath), "evals", "eval-sets", fileName);
|
|
1301
|
+
await this.fs.rm(filePath);
|
|
1302
|
+
}
|
|
1303
|
+
async writeFeature(projectPath, featureName, feature) {
|
|
1304
|
+
this.validateFeatureName(featureName);
|
|
1305
|
+
const dir = this.fs.path.join(this.getAgentDir(projectPath), "features", featureName);
|
|
1306
|
+
await this.safeMkdir(dir);
|
|
1307
|
+
await this.fs.writeFile(this.fs.path.join(dir, "feature.json"), `${JSON.stringify(feature, null, 2)}
|
|
1308
|
+
`);
|
|
1309
|
+
}
|
|
1310
|
+
async readFeature(projectPath, featureName) {
|
|
1311
|
+
this.validateFeatureName(featureName);
|
|
1312
|
+
const filePath = this.fs.path.join(this.getAgentDir(projectPath), "features", featureName, "feature.json");
|
|
1313
|
+
const raw = await this.fs.readFile(filePath, "utf-8");
|
|
1314
|
+
if (!raw) {
|
|
1315
|
+
throw new Error(`Feature file not found: ${filePath}`);
|
|
1316
|
+
}
|
|
1317
|
+
const parsed = JSON.parse(raw);
|
|
1318
|
+
if (!parsed) {
|
|
1319
|
+
throw new Error(`Feature file is empty: ${filePath}`);
|
|
1320
|
+
}
|
|
1321
|
+
return parsed;
|
|
1322
|
+
}
|
|
1323
|
+
async listFeatureDirs(projectPath) {
|
|
1324
|
+
const dir = this.fs.path.join(this.getAgentDir(projectPath), "features");
|
|
1325
|
+
const [error, entries] = await catchError(this.fs.readdir(dir));
|
|
1326
|
+
if (error) {
|
|
1327
|
+
return [];
|
|
1328
|
+
}
|
|
1329
|
+
return entries;
|
|
1330
|
+
}
|
|
1331
|
+
async deleteFeature(projectPath, featureName) {
|
|
1332
|
+
this.validateFeatureName(featureName);
|
|
1333
|
+
const dir = this.fs.path.join(this.getAgentDir(projectPath), "features", featureName);
|
|
1334
|
+
await this.fs.rm(dir);
|
|
1335
|
+
}
|
|
1336
|
+
async safeMkdir(dir) {
|
|
1337
|
+
await this.fs.mkdir(dir);
|
|
1338
|
+
}
|
|
1339
|
+
async writeJsonFile(filePath, data) {
|
|
1340
|
+
await this.fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}
|
|
1341
|
+
`);
|
|
1342
|
+
}
|
|
1343
|
+
validateFeatureName(featureName) {
|
|
1344
|
+
if (featureName.trim().length === 0 || featureName === "." || featureName === ".." || featureName.includes("/") || featureName.includes("\\") || featureName.includes("\x00") || featureName !== this.fs.path.basename(featureName)) {
|
|
1345
|
+
throw new Error(`Invalid feature name: "${featureName}"`);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// src/services/pack-helpers.ts
|
|
1351
|
+
async function collectProjectFiles(rootDir) {
|
|
1352
|
+
const files = [];
|
|
1353
|
+
await collectRecursive(rootDir, rootDir, files);
|
|
1354
|
+
return files;
|
|
1355
|
+
}
|
|
1356
|
+
async function collectRecursive(rootDir, currentDir, files) {
|
|
1357
|
+
const fs = getFileSystem();
|
|
1358
|
+
const entries = await fs.readdir(currentDir);
|
|
1359
|
+
for (const entry of entries) {
|
|
1360
|
+
const fullPath = fs.path.join(currentDir, entry);
|
|
1361
|
+
const stats = await fs.stat(fullPath);
|
|
1362
|
+
if (stats?.isDirectory()) {
|
|
1363
|
+
await collectRecursive(rootDir, fullPath, files);
|
|
1364
|
+
} else if (stats) {
|
|
1365
|
+
files.push(fs.path.relative(rootDir, fullPath).replace(/\\/g, "/"));
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
function matchesExclude(relPath, exclude) {
|
|
1370
|
+
for (const pattern of exclude) {
|
|
1371
|
+
const cleaned = pattern.replace(/^\.\//, "").replace(/\/\*$/, "");
|
|
1372
|
+
if (cleaned.startsWith("*")) {
|
|
1373
|
+
const suffix = cleaned.slice(1).replace(/^\//, "");
|
|
1374
|
+
if (relPath.includes(`/${suffix}/`) || relPath.includes(`/${suffix}`)) {
|
|
1375
|
+
return true;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
if (relPath === cleaned || relPath.startsWith(`${cleaned}/`)) {
|
|
1379
|
+
return true;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
return false;
|
|
1383
|
+
}
|
|
1384
|
+
async function createArchive(sourceDir, outputPath, exclude) {
|
|
1385
|
+
const fs = getFileSystem();
|
|
1386
|
+
const files = await collectProjectFiles(sourceDir);
|
|
1387
|
+
const zippable = {};
|
|
1388
|
+
for (const relPath of files) {
|
|
1389
|
+
if (exclude && matchesExclude(relPath, exclude)) {
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
const data = await fs.readFile(fs.path.join(sourceDir, relPath));
|
|
1393
|
+
if (data !== null) {
|
|
1394
|
+
zippable[relPath] = new Uint8Array(data);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
await fs.writeFile(outputPath, finalizeZip64(zipSync(zippable, { level: 6 })));
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// src/services/agent-local-workspace-eval-preparer.ts
|
|
1401
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1402
|
+
function isUUID(value) {
|
|
1403
|
+
return UUID_PATTERN.test(value);
|
|
1404
|
+
}
|
|
1405
|
+
function isRecord(value) {
|
|
1406
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1407
|
+
}
|
|
1408
|
+
function toForwardSlash2(path) {
|
|
1409
|
+
return path.replace(/\\/g, "/");
|
|
1410
|
+
}
|
|
1411
|
+
async function resolveStudioWebConfig() {
|
|
1412
|
+
const [loginError, login] = await catchError(getLoginStatusAsync());
|
|
1413
|
+
if (loginError) {
|
|
1414
|
+
throw new Error(loginError.message);
|
|
1415
|
+
}
|
|
1416
|
+
if (login.loginStatus !== "Logged in" || !login.baseUrl) {
|
|
1417
|
+
throw new Error("Not logged in. Run 'uip login' first.");
|
|
1418
|
+
}
|
|
1419
|
+
if (!login.accessToken) {
|
|
1420
|
+
throw new Error("Access token not available from login.");
|
|
1421
|
+
}
|
|
1422
|
+
if (!login.organizationName) {
|
|
1423
|
+
throw new Error("Organization name not available from login. Required for Studio Web evaluation.");
|
|
1424
|
+
}
|
|
1425
|
+
return {
|
|
1426
|
+
baseUrl: login.baseUrl,
|
|
1427
|
+
tenantId: login.tenantId,
|
|
1428
|
+
authToken: login.accessToken,
|
|
1429
|
+
organizationName: login.organizationName
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
class AgentLocalWorkspaceEvalPreparer {
|
|
1434
|
+
fs;
|
|
1435
|
+
constructor(fs = getFileSystem()) {
|
|
1436
|
+
this.fs = fs;
|
|
1437
|
+
}
|
|
1438
|
+
async isLocalWorkspace(projectPath) {
|
|
1439
|
+
return await this.readLocalWorkspaceMarker(projectPath) !== undefined;
|
|
1440
|
+
}
|
|
1441
|
+
async prepare(projectPath, config, existingSolutionIdOverride) {
|
|
1442
|
+
const absoluteProjectPath = this.fs.path.resolve(projectPath);
|
|
1443
|
+
const marker = await this.readLocalWorkspaceMarker(absoluteProjectPath);
|
|
1444
|
+
const packageRoot = marker?.rootPath ?? absoluteProjectPath;
|
|
1445
|
+
const projectService = new AgentProjectService;
|
|
1446
|
+
const agentDir = await projectService.resolveAgentDir(absoluteProjectPath);
|
|
1447
|
+
const agent = await projectService.readAgentJson(absoluteProjectPath);
|
|
1448
|
+
const projectFilePath = this.fs.path.join(agentDir, "project.uiproj");
|
|
1449
|
+
if (!await this.fs.exists(projectFilePath)) {
|
|
1450
|
+
throw new Error(`Not a valid agent project directory: missing project.uiproj in ${agentDir}`);
|
|
1451
|
+
}
|
|
1452
|
+
const projectFileContent = await this.fs.readFile(projectFilePath, "utf-8");
|
|
1453
|
+
const [projectFileError, parsedProjectFile] = catchError(() => JSON.parse(String(projectFileContent)));
|
|
1454
|
+
if (projectFileError || !isRecord(parsedProjectFile)) {
|
|
1455
|
+
throw new Error(`Invalid project.uiproj in ${agentDir}: ${projectFileError?.message ?? "expected a JSON object"}`);
|
|
1456
|
+
}
|
|
1457
|
+
const projectFile = {
|
|
1458
|
+
Name: typeof parsedProjectFile.Name === "string" ? parsedProjectFile.Name : undefined
|
|
1459
|
+
};
|
|
1460
|
+
const solutionInfo = await findSolutionFile(this.fs, agentDir);
|
|
1461
|
+
const solutionId = existingSolutionIdOverride ?? marker?.solutionId ?? solutionInfo?.solutionId;
|
|
1462
|
+
const solutionName = projectFile.Name ?? agent.metadata.name ?? this.fs.path.basename(agentDir);
|
|
1463
|
+
const safeSolutionName = solutionName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1464
|
+
const stagingDir = this.fs.path.join(this.fs.env.tmpdir(), `.agent-eval-staging-${safeSolutionName}-${Date.now()}`);
|
|
1465
|
+
await this.fs.mkdir(stagingDir);
|
|
1466
|
+
const uisPath = this.fs.path.join(stagingDir, `${safeSolutionName}.uis`);
|
|
1467
|
+
try {
|
|
1468
|
+
logger.info("Building agent package for local workspace eval...");
|
|
1469
|
+
await createArchive(packageRoot, uisPath, [
|
|
1470
|
+
"./.agent-builder/*",
|
|
1471
|
+
"*/.agent-builder/*",
|
|
1472
|
+
".sw-path-marker",
|
|
1473
|
+
"*/.sw-path-marker"
|
|
1474
|
+
]);
|
|
1475
|
+
logger.info("Uploading local workspace agent for eval...");
|
|
1476
|
+
const importResult = await uploadOrOverwriteSolution({
|
|
1477
|
+
baseUrl: config.baseUrl,
|
|
1478
|
+
tenantId: config.tenantId,
|
|
1479
|
+
authToken: config.authToken
|
|
1480
|
+
}, config.organizationName, uisPath, solutionId, this.fs);
|
|
1481
|
+
const matchedProject = this.resolveImportedProject(importResult, solutionName);
|
|
1482
|
+
await this.persistSolutionStorage(projectService, absoluteProjectPath, packageRoot, projectFilePath, importResult.id, agent.projectId, matchedProject.id);
|
|
1483
|
+
if (packageRoot !== absoluteProjectPath) {
|
|
1484
|
+
await this.persistSolutionStorage(projectService, packageRoot, packageRoot, projectFilePath, importResult.id, agent.projectId, matchedProject.id);
|
|
1485
|
+
}
|
|
1486
|
+
await updateSolutionFile(this.fs, agentDir, importResult.id);
|
|
1487
|
+
if (marker) {
|
|
1488
|
+
await this.updateLocalWorkspaceMarker(marker.rootPath, importResult.id);
|
|
1489
|
+
}
|
|
1490
|
+
return {
|
|
1491
|
+
solutionId: importResult.id,
|
|
1492
|
+
agentProjectId: matchedProject.id
|
|
1493
|
+
};
|
|
1494
|
+
} finally {
|
|
1495
|
+
const [cleanupError] = await catchError(this.fs.rm(stagingDir, { recursive: true }));
|
|
1496
|
+
if (cleanupError) {
|
|
1497
|
+
logger.warn(`Cleanup failed: ${cleanupError.message}`);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
async readLocalWorkspaceMarker(projectPath) {
|
|
1502
|
+
const absoluteProjectPath = this.fs.path.resolve(projectPath);
|
|
1503
|
+
const candidates = [
|
|
1504
|
+
absoluteProjectPath,
|
|
1505
|
+
this.fs.path.dirname(absoluteProjectPath)
|
|
1506
|
+
];
|
|
1507
|
+
for (const candidate of candidates) {
|
|
1508
|
+
const markerPath = this.fs.path.join(candidate, ".sw-path-marker");
|
|
1509
|
+
if (!await this.fs.exists(markerPath)) {
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
const content = String(await this.fs.readFile(markerPath, "utf-8"));
|
|
1513
|
+
const solutionId = content.trim();
|
|
1514
|
+
return {
|
|
1515
|
+
rootPath: candidate,
|
|
1516
|
+
...isUUID(solutionId) ? { solutionId } : {}
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
resolveImportedProject(importResult, agentName) {
|
|
1522
|
+
const exactProject = importResult.projects.find((project) => project.name === agentName);
|
|
1523
|
+
if (exactProject?.id) {
|
|
1524
|
+
return exactProject;
|
|
1525
|
+
}
|
|
1526
|
+
const [onlyProject] = importResult.projects;
|
|
1527
|
+
if (importResult.projects.length === 1 && onlyProject?.id) {
|
|
1528
|
+
return onlyProject;
|
|
1529
|
+
}
|
|
1530
|
+
throw new Error(`Upload response had ${importResult.projects.length} projects and none matched "${agentName}"; refusing to guess.`);
|
|
1531
|
+
}
|
|
1532
|
+
async persistSolutionStorage(projectService, storagePath, packageRoot, projectFilePath, solutionId, localProjectId, cloudProjectId) {
|
|
1533
|
+
const projectRelativePath = toForwardSlash2(this.fs.path.relative(packageRoot, projectFilePath));
|
|
1534
|
+
const projectEntry = {
|
|
1535
|
+
ProjectId: localProjectId,
|
|
1536
|
+
ProjectRelativePath: projectRelativePath || "project.uiproj",
|
|
1537
|
+
CloudProjectId: cloudProjectId
|
|
1538
|
+
};
|
|
1539
|
+
const [, existingStorage] = await catchError(projectService.readSolutionStorage(storagePath));
|
|
1540
|
+
const existingProjects = existingStorage?.Projects ?? [];
|
|
1541
|
+
const projectIndex = existingProjects.findIndex((project) => project.ProjectId === localProjectId);
|
|
1542
|
+
const projects = projectIndex === -1 ? [...existingProjects, projectEntry] : existingProjects.map((project, index) => index === projectIndex ? { ...project, ...projectEntry } : project);
|
|
1543
|
+
await projectService.writeSolutionStorage(storagePath, {
|
|
1544
|
+
SolutionId: solutionId,
|
|
1545
|
+
Projects: projects
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
async updateLocalWorkspaceMarker(workspaceRoot, solutionId) {
|
|
1549
|
+
await this.fs.writeFile(this.fs.path.join(workspaceRoot, ".sw-path-marker"), solutionId);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// src/services/agent-debug-service.ts
|
|
1554
|
+
class AgentDebugCancelledError extends Error {
|
|
1555
|
+
jobKey;
|
|
1556
|
+
constructor(jobKey) {
|
|
1557
|
+
super(jobKey ? `Debug run cancelled. Job ${jobKey} may still be running on the serverless runtime.` : "Debug run cancelled before the job started.");
|
|
1558
|
+
this.name = "AgentDebugCancelledError";
|
|
1559
|
+
this.jobKey = jobKey;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
var TERMINAL_STATES = new Set([
|
|
1563
|
+
"Successful",
|
|
1564
|
+
"Faulted",
|
|
1565
|
+
"Stopped",
|
|
1566
|
+
"Suspended"
|
|
1567
|
+
]);
|
|
1568
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1569
|
+
|
|
1570
|
+
class AgentDebugService {
|
|
1571
|
+
fs = getFileSystem();
|
|
1572
|
+
projectService = new AgentProjectService;
|
|
1573
|
+
async debug(options, auth) {
|
|
1574
|
+
throwIfAborted(options.signal);
|
|
1575
|
+
const projectId = await this.resolveProjectId(options.projectPath);
|
|
1576
|
+
throwIfAborted(options.signal);
|
|
1577
|
+
const folderKey = await this.resolvePersonalWorkspaceKey();
|
|
1578
|
+
throwIfAborted(options.signal);
|
|
1579
|
+
const { jobKey, traceId } = await asCancellation(() => this.startDebugJob(auth, folderKey, projectId, options.inputs, options.signal));
|
|
1580
|
+
const job = await asCancellation(() => this.pollJob(auth, folderKey, jobKey, options), jobKey);
|
|
1581
|
+
return {
|
|
1582
|
+
jobKey,
|
|
1583
|
+
traceId,
|
|
1584
|
+
finalState: job.state,
|
|
1585
|
+
success: job.state === "Successful",
|
|
1586
|
+
output: job.output,
|
|
1587
|
+
info: job.info
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
async resolveProjectId(projectPath) {
|
|
1591
|
+
const preparer = new AgentLocalWorkspaceEvalPreparer;
|
|
1592
|
+
if (await preparer.isLocalWorkspace(projectPath)) {
|
|
1593
|
+
const [, storage] = await catchError(this.projectService.readSolutionStorage(projectPath));
|
|
1594
|
+
const config = await resolveStudioWebConfig();
|
|
1595
|
+
const prepared = await preparer.prepare(projectPath, config, storage?.SolutionId);
|
|
1596
|
+
return prepared.agentProjectId;
|
|
1597
|
+
}
|
|
1598
|
+
return this.uploadSolutionAndResolveProjectId(projectPath);
|
|
1599
|
+
}
|
|
1600
|
+
async uploadSolutionAndResolveProjectId(projectPath) {
|
|
1601
|
+
const solution = await findSolutionFile(this.fs, projectPath);
|
|
1602
|
+
if (!solution) {
|
|
1603
|
+
throw new Error(`No enclosing .uipx solution found for '${projectPath}'. Create one with 'uip solution init' and add the agent to it.`);
|
|
1604
|
+
}
|
|
1605
|
+
const config = await resolveStudioWebConfig();
|
|
1606
|
+
const solutionDir = this.fs.path.resolve(projectPath, "..");
|
|
1607
|
+
const tempDir = await this.fs.getTempDir();
|
|
1608
|
+
try {
|
|
1609
|
+
const bundle = await bundleSolution(this.fs, solutionDir, tempDir);
|
|
1610
|
+
const importResult = await uploadOrOverwriteSolution(config, config.organizationName, bundle.uisPath, bundle.solutionId, this.fs);
|
|
1611
|
+
if (importResult.id && bundle.uipxPath) {
|
|
1612
|
+
await updateUipxSolutionId(this.fs, bundle.uipxPath, importResult.id);
|
|
1613
|
+
}
|
|
1614
|
+
const projectName = await this.readProjectName(projectPath);
|
|
1615
|
+
const byName = importResult.projects.find((p) => p.name === projectName);
|
|
1616
|
+
if (byName?.id) {
|
|
1617
|
+
return byName.id;
|
|
1618
|
+
}
|
|
1619
|
+
const [only] = importResult.projects;
|
|
1620
|
+
if (importResult.projects.length === 1 && only?.id) {
|
|
1621
|
+
return only.id;
|
|
1622
|
+
}
|
|
1623
|
+
throw new Error(`Could not resolve the Studio Web project for "${projectName}": the upload returned ${importResult.projects.length} projects and none is named "${projectName}". Ensure the Name in project.uiproj matches the agent project in the solution.`);
|
|
1624
|
+
} finally {
|
|
1625
|
+
const [cleanupError] = await catchError(this.fs.rm(tempDir));
|
|
1626
|
+
if (cleanupError) {
|
|
1627
|
+
logger.debug(`Failed to clean up temp dir: ${cleanupError.message}`);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
async readProjectName(projectPath) {
|
|
1632
|
+
const [, content] = await catchError(this.fs.readFile(this.fs.path.join(projectPath, "project.uiproj"), "utf-8"));
|
|
1633
|
+
const [, parsed] = catchError(() => JSON.parse(String(content)));
|
|
1634
|
+
return parsed?.Name ?? this.fs.path.basename(projectPath);
|
|
1635
|
+
}
|
|
1636
|
+
async resolvePersonalWorkspaceKey() {
|
|
1637
|
+
const wsApi = await createApiClient(PersonalWorkspacesApi, {});
|
|
1638
|
+
const workspace = await wsApi.personalWorkspacesGetPersonalWorkspace();
|
|
1639
|
+
if (!workspace?.key) {
|
|
1640
|
+
throw new Error("Could not resolve your personal workspace. Ensure it is enabled for your account.");
|
|
1641
|
+
}
|
|
1642
|
+
return workspace.key;
|
|
1643
|
+
}
|
|
1644
|
+
orchestratorBaseUrl(auth) {
|
|
1645
|
+
return `${auth.baseUrl}/${auth.organizationId}/${auth.tenantName}/orchestrator_`;
|
|
1646
|
+
}
|
|
1647
|
+
async startDebugJob(auth, folderKey, projectId, inputs, signal) {
|
|
1648
|
+
const traceId = randomUUID();
|
|
1649
|
+
const inputArguments = JSON.stringify({
|
|
1650
|
+
Command: "debug",
|
|
1651
|
+
EntryPoint: "agent.json",
|
|
1652
|
+
Input: JSON.stringify(inputs),
|
|
1653
|
+
args: "--attach=none"
|
|
1654
|
+
});
|
|
1655
|
+
const response = await fetch(`${this.orchestratorBaseUrl(auth)}/api/serverless/jobs`, {
|
|
1656
|
+
method: "POST",
|
|
1657
|
+
headers: {
|
|
1658
|
+
Authorization: `Bearer ${auth.accessToken}`,
|
|
1659
|
+
"Content-Type": "application/json",
|
|
1660
|
+
"X-UIPATH-FolderKey": folderKey
|
|
1661
|
+
},
|
|
1662
|
+
body: JSON.stringify({
|
|
1663
|
+
JobType: "PythonAgent",
|
|
1664
|
+
InputArguments: inputArguments,
|
|
1665
|
+
Source: "StudioWeb",
|
|
1666
|
+
RunAsMe: true,
|
|
1667
|
+
EnvironmentVariables: `UIPATH_PROJECT_ID=${projectId}`,
|
|
1668
|
+
TraceId: traceId,
|
|
1669
|
+
Reference: randomUUID()
|
|
1670
|
+
}),
|
|
1671
|
+
signal
|
|
1672
|
+
});
|
|
1673
|
+
if (!response.ok) {
|
|
1674
|
+
throw new Error(`Failed to start debug job: ${response.status} ${await response.text()}`);
|
|
1675
|
+
}
|
|
1676
|
+
const data = await response.json();
|
|
1677
|
+
if (!data.key) {
|
|
1678
|
+
throw new Error("Debug job started but no job key was returned.");
|
|
1679
|
+
}
|
|
1680
|
+
return { jobKey: data.key, traceId };
|
|
1681
|
+
}
|
|
1682
|
+
async pollJob(auth, folderKey, jobKey, options) {
|
|
1683
|
+
const url = `${this.orchestratorBaseUrl(auth)}/odata/Jobs/UiPath.Server.Configuration.OData.GetByKey(identifier=${jobKey})`;
|
|
1684
|
+
const headers = {
|
|
1685
|
+
Authorization: `Bearer ${auth.accessToken}`,
|
|
1686
|
+
"X-UIPATH-FolderKey": folderKey
|
|
1687
|
+
};
|
|
1688
|
+
let lastState = "";
|
|
1689
|
+
let consecutiveErrors = 0;
|
|
1690
|
+
const maxConsecutiveErrors = 3;
|
|
1691
|
+
for (let poll = 0;poll < options.maxPolls; poll++) {
|
|
1692
|
+
await delay(options.pollIntervalMs);
|
|
1693
|
+
throwIfAborted(options.signal, jobKey);
|
|
1694
|
+
const response = await fetch(url, {
|
|
1695
|
+
headers,
|
|
1696
|
+
signal: options.signal
|
|
1697
|
+
});
|
|
1698
|
+
if (!response.ok) {
|
|
1699
|
+
consecutiveErrors++;
|
|
1700
|
+
if (response.status < 500 || consecutiveErrors > maxConsecutiveErrors) {
|
|
1701
|
+
const body = await response.text();
|
|
1702
|
+
throw new Error(`Failed to poll debug job (HTTP ${response.status}): ${body.slice(0, 300)}`);
|
|
1703
|
+
}
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
consecutiveErrors = 0;
|
|
1707
|
+
const job = await response.json();
|
|
1708
|
+
if (job.State !== lastState) {
|
|
1709
|
+
lastState = job.State;
|
|
1710
|
+
options.onState?.(job.State);
|
|
1711
|
+
}
|
|
1712
|
+
if (TERMINAL_STATES.has(job.State)) {
|
|
1713
|
+
return {
|
|
1714
|
+
state: job.State,
|
|
1715
|
+
output: parseOutput(job.OutputArguments),
|
|
1716
|
+
info: job.Info?.trim() || undefined
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
throw new Error(`Debug run did not reach a terminal state within the timeout (last state: ${lastState || "unknown"}). Increase --timeout and retry.`);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
function throwIfAborted(signal, jobKey) {
|
|
1724
|
+
if (signal?.aborted) {
|
|
1725
|
+
throw new AgentDebugCancelledError(jobKey);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
async function asCancellation(step, jobKey) {
|
|
1729
|
+
try {
|
|
1730
|
+
return await step();
|
|
1731
|
+
} catch (error) {
|
|
1732
|
+
if (isAbortError(error)) {
|
|
1733
|
+
throw new AgentDebugCancelledError(jobKey);
|
|
1734
|
+
}
|
|
1735
|
+
throw error;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
function isAbortError(error) {
|
|
1739
|
+
return error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
1740
|
+
}
|
|
1741
|
+
function parseOutput(outputArguments) {
|
|
1742
|
+
if (!outputArguments) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
const [error, parsed] = catchError(() => JSON.parse(outputArguments));
|
|
1746
|
+
return error ? outputArguments : parsed;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
export { StudioWebIncompatibleProjectError, isSolutionUploadHttpError, buildSolutionUploadFailureOutput, migrateLegacyToolGuardrailPolicies, AgentProjectService, resolveStudioWebConfig, AgentLocalWorkspaceEvalPreparer, AgentDebugCancelledError, AgentDebugService };
|
|
1750
|
+
|
|
1751
|
+
//# debugId=936AF142F4482B6164756E2164756E21
|