@uipath/solution-sdk 1.202.0-preview.138 → 1.202.0-preview.140

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8544,7 +8544,7 @@ class TextApiResponse {
8544
8544
  var package_default = {
8545
8545
  name: "@uipath/solution-sdk",
8546
8546
  license: "MIT",
8547
- version: "1.202.0-preview.138",
8547
+ version: "1.202.0-preview.140",
8548
8548
  repository: {
8549
8549
  type: "git",
8550
8550
  url: "https://github.com/UiPath/cli.git",
@@ -17363,9 +17363,11 @@ async function listStudioWebSolutions(config, organizationName, options = {}) {
17363
17363
  // src/upload-service.ts
17364
17364
  class HttpError extends Error {
17365
17365
  status;
17366
- constructor(status, message) {
17366
+ authChallenge;
17367
+ constructor(status, message, authChallenge) {
17367
17368
  super(message);
17368
17369
  this.status = status;
17370
+ this.authChallenge = authChallenge;
17369
17371
  }
17370
17372
  }
17371
17373
 
@@ -17379,6 +17381,7 @@ class SolutionUploadHttpError extends Error {
17379
17381
  solutionId;
17380
17382
  parsedErrorCode;
17381
17383
  parsedErrorMessage;
17384
+ authChallenge;
17382
17385
  constructor(details) {
17383
17386
  super(details.message);
17384
17387
  this.name = "SolutionUploadHttpError";
@@ -17391,6 +17394,7 @@ class SolutionUploadHttpError extends Error {
17391
17394
  if (details.operation === "overwrite") {
17392
17395
  this.solutionId = details.solutionId;
17393
17396
  }
17397
+ this.authChallenge = details.authChallenge;
17394
17398
  const parsed = parseUploadErrorBody(details.body);
17395
17399
  this.parsedErrorCode = parsed.code;
17396
17400
  this.parsedErrorMessage = parsed.message;
@@ -17414,16 +17418,71 @@ function requestPath(url) {
17414
17418
  const [, parsed] = catchError(() => new URL(url));
17415
17419
  return parsed ? `${parsed.pathname}${parsed.search}` : url;
17416
17420
  }
17421
+ 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;
17422
+ function redactTokens(text) {
17423
+ return text.replace(TOKEN_SHAPED, "[REDACTED]");
17424
+ }
17425
+ var MAX_CHALLENGE_DESCRIPTION = 200;
17426
+ function parseAuthChallenge(header) {
17427
+ if (!header)
17428
+ return {};
17429
+ const code = /\berror="([^"]*)"/.exec(header)?.[1]?.trim();
17430
+ const raw = /\berror_description="([^"]*)"/.exec(header)?.[1]?.trim();
17431
+ const description = raw ? redactTokens(raw).slice(0, MAX_CHALLENGE_DESCRIPTION) : undefined;
17432
+ return {
17433
+ error: code || undefined,
17434
+ description: description || undefined
17435
+ };
17436
+ }
17437
+ function summarizeAuthRefusalBody(body) {
17438
+ return summarizePlatformBody(redactTokens(body));
17439
+ }
17440
+ function isAuthRefusal(status) {
17441
+ return status === 401 || status === 403;
17442
+ }
17443
+ function buildAuthRefusalInstructions({
17444
+ err: err2,
17445
+ subject,
17446
+ target,
17447
+ bodySuffix,
17448
+ identityType,
17449
+ clientId
17450
+ }) {
17451
+ const refusal = err2.status === 401 ? "rejected the call as unauthorized (401)" : "forbade the call (403)";
17452
+ const what = err2.operation === "overwrite" ? `replacing the contents of solution ${target}` : "importing the solution package";
17453
+ 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. `;
17454
+ const tenant = err2.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. `;
17455
+ const challenge = parseAuthChallenge(err2.authChallenge);
17456
+ const quoted = challenge.description ? ` ("${challenge.description}")` : "";
17457
+ if (challenge.error === "invalid_token") {
17458
+ 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}`;
17459
+ }
17460
+ 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. `;
17461
+ 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;
17462
+ 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.`;
17463
+ return `Studio Web ${refusal} while ${what}. ${diagnosis}${actor}${tenant}` + `Nothing was uploaded and nothing ran, so the ${subject} is not the ` + `cause. ${closing}`;
17464
+ }
17417
17465
  function buildSolutionUploadFailureOutput(err2, options = {}) {
17418
17466
  const stage = err2.operation === "overwrite" ? "overwrite-solution" : "import-solution";
17419
- const detail = err2.parsedErrorMessage ? ` — ${err2.parsedErrorMessage}` : "";
17467
+ const authRefusal = isAuthRefusal(err2.status);
17468
+ const parsedMessage = authRefusal && err2.parsedErrorMessage ? redactTokens(err2.parsedErrorMessage) : err2.parsedErrorMessage;
17469
+ const detail = parsedMessage ? ` — ${parsedMessage}` : "";
17420
17470
  const Message = `Failed during ${stage}: HTTP ${err2.status} on ${err2.method} ` + `${err2.endpoint}${detail}`;
17421
- const body = err2.parsedErrorMessage ? "" : summarizePlatformBody(err2.body);
17471
+ const quote = authRefusal ? summarizeAuthRefusalBody : summarizePlatformBody;
17472
+ const body = err2.parsedErrorMessage ? "" : quote(err2.body);
17422
17473
  const bodySuffix = body ? ` Platform response: "${body}".` : "";
17423
17474
  const importHint = options.importCheckHint ? ` ${options.importCheckHint}` : "";
17424
17475
  const subject = options.subject ?? "project";
17425
17476
  const target = err2.solutionId ?? `the solution at ${err2.endpoint}`;
17426
- const Instructions = err2.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}`;
17477
+ const authInstructions = isAuthRefusal(err2.status) ? buildAuthRefusalInstructions({
17478
+ err: err2,
17479
+ subject,
17480
+ target,
17481
+ bodySuffix,
17482
+ identityType: options.identityType,
17483
+ clientId: options.clientId
17484
+ }) : undefined;
17485
+ const Instructions = authInstructions ?? (err2.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}`);
17427
17486
  const Context = {
17428
17487
  httpStatus: err2.status,
17429
17488
  stage,
@@ -17439,6 +17498,9 @@ function buildSolutionUploadFailureOutput(err2, options = {}) {
17439
17498
  Instructions,
17440
17499
  Context
17441
17500
  };
17501
+ if (err2.status === 401 && options.identityType === "Application") {
17502
+ output.ErrorCode = "permission_denied";
17503
+ }
17442
17504
  if (err2.operation === "overwrite" && err2.status >= 500 && err2.status < 600) {
17443
17505
  output.Retry = "RetryAfter30Seconds";
17444
17506
  }
@@ -17489,8 +17551,10 @@ async function solutionExistsOnStudioWeb(config, organizationName, solutionId) {
17489
17551
  if (error.response.status === 404) {
17490
17552
  return false;
17491
17553
  }
17554
+ const status = error.response.status;
17492
17555
  const text = await error.response.text().catch(() => "");
17493
- throw new HttpError(error.response.status, `Studio Web solution existence probe failed (${error.response.status}): ${text}`);
17556
+ const body = isAuthRefusal(status) ? summarizeAuthRefusalBody(text) : text;
17557
+ throw new HttpError(status, `Studio Web solution existence probe failed (${status}): ${body}`, parseAuthChallenge(error.response.headers?.get("www-authenticate") ?? undefined));
17494
17558
  }
17495
17559
  throw error;
17496
17560
  }
@@ -17519,6 +17583,7 @@ async function importSolution(config, organizationName, fileBuffer, fileName) {
17519
17583
  endpoint: requestPath(importUrl),
17520
17584
  url: importUrl,
17521
17585
  body: text,
17586
+ authChallenge: response.headers?.get("www-authenticate") ?? undefined,
17522
17587
  message: `Import failed (${response.status}): ${text}`
17523
17588
  });
17524
17589
  }
@@ -17550,6 +17615,7 @@ async function overwriteSolution(config, organizationName, fileBuffer, fileName,
17550
17615
  endpoint: requestPath(overwriteUrl),
17551
17616
  url: overwriteUrl,
17552
17617
  body: text,
17618
+ authChallenge: response.headers?.get("www-authenticate") ?? undefined,
17553
17619
  message: `Overwrite failed (${response.status}): ${text}`,
17554
17620
  solutionId
17555
17621
  });
@@ -17901,4 +17967,4 @@ export {
17901
17967
  uploadOrOverwriteSolution
17902
17968
  };
17903
17969
 
17904
- //# debugId=CE11D385CFED5B9064756E2164756E21
17970
+ //# debugId=3C3C2A67310527AD64756E2164756E21
@@ -30515,6 +30515,59 @@ class FileSystemFileStorageProviderFactory {
30515
30515
  }
30516
30516
  }
30517
30517
 
30518
+ // src/resources/providers/resource-builder-logger.ts
30519
+ var PLACEHOLDER = /\{(\w+)\}|%[sdifoOj]/g;
30520
+ function render(value) {
30521
+ if (typeof value === "string")
30522
+ return value;
30523
+ if (value === null || value === undefined)
30524
+ return String(value);
30525
+ if (typeof value !== "object")
30526
+ return String(value);
30527
+ try {
30528
+ return JSON.stringify(value) ?? String(value);
30529
+ } catch {
30530
+ return String(value);
30531
+ }
30532
+ }
30533
+ function interpolate(message, args) {
30534
+ const fields = args[0] !== null && typeof args[0] === "object" && !Array.isArray(args[0]) ? args[0] : undefined;
30535
+ const consumed = new Set;
30536
+ let positional = 0;
30537
+ const filled = message.replace(PLACEHOLDER, (match, name) => {
30538
+ if (name !== undefined) {
30539
+ if (fields && name in fields) {
30540
+ consumed.add(0);
30541
+ return render(fields[name]);
30542
+ }
30543
+ return match;
30544
+ }
30545
+ const index = positional;
30546
+ positional += 1;
30547
+ if (index >= args.length)
30548
+ return match;
30549
+ consumed.add(index);
30550
+ return render(args[index]);
30551
+ });
30552
+ const extra = args.filter((_, index) => !consumed.has(index)).map(render).filter((text) => text.length > 0);
30553
+ return extra.length > 0 ? `${filled} ${extra.join(" ")}` : filled;
30554
+ }
30555
+
30556
+ class CliResourceBuilderLoggerFactory {
30557
+ create(target) {
30558
+ const name = typeof target === "string" ? target : target.name;
30559
+ const line = (message, args) => `[${name}] ${interpolate(message, args)}`;
30560
+ return {
30561
+ trace: (message, ...args) => logger.debug(line(message, args)),
30562
+ debug: (message, ...args) => logger.debug(line(message, args)),
30563
+ info: (message, ...args) => logger.info(line(message, args)),
30564
+ warn: (message, ...args) => logger.warn(line(message, args)),
30565
+ error: (message, error, ...args) => logger.error(error ? `${line(message, args)}: ${error.message}` : line(message, args)),
30566
+ fatal: (message, ...args) => logger.error(line(message, args))
30567
+ };
30568
+ }
30569
+ }
30570
+
30518
30571
  // ../orchestrator-sdk/generated/src/runtime.ts
30519
30572
  var BASE_PATH = "https://alpha.uipath.com/uipattycyrhx/abizon_1/orchestrator_".replace(/\/+$/, "");
30520
30573
 
@@ -32388,7 +32441,7 @@ class SolutionAccessProvider {
32388
32441
 
32389
32442
  // src/resources/providers/resource-builder-init.ts
32390
32443
  async function createResourceBuilderServices() {
32391
- return await createResourceBuilderConfiguration().addAccessProvider(new SolutionAccessProvider).addFileStorageProviderFactory(new FileSystemFileStorageProviderFactory).addArtifactResourceSpecEnhancer(new EntryPointSpecEnhancer).build();
32444
+ return await createResourceBuilderConfiguration().addAccessProvider(new SolutionAccessProvider).addFileStorageProviderFactory(new FileSystemFileStorageProviderFactory).addArtifactResourceSpecEnhancer(new EntryPointSpecEnhancer).addLoggerFactory(new CliResourceBuilderLoggerFactory).build();
32392
32445
  }
32393
32446
 
32394
32447
  // src/resources/commands/project-utils.ts
@@ -33339,4 +33392,4 @@ export {
33339
33392
  writeSolutionFile
33340
33393
  };
33341
33394
 
33342
- //# debugId=BE6597D080D5AC2664756E2164756E21
33395
+ //# debugId=E988507D1077967864756E2164756E21
@@ -8,5 +8,9 @@ import { type IResourceBuilderServices } from "@uipath/resource-builder-sdk";
8
8
  * `EntryPointSpecEnhancer` is registered so artefact resources (resources
9
9
  * with a `projectKey`) reflect the latest `entry-points.json` from disk at
10
10
  * read time, instead of the snapshot captured at `solution projects add`.
11
+ *
12
+ * `CliResourceBuilderLoggerFactory` replaces the SDK's default logger, which
13
+ * prints its message templates unfilled (`{solutionKey}`, `%s`) and bypasses
14
+ * `--log-level` / `--log-file` — see that file for why both matter.
11
15
  */
12
16
  export declare function createResourceBuilderServices(): Promise<IResourceBuilderServices>;
@@ -0,0 +1,25 @@
1
+ import type { ILogger, ILoggerFactory } from "@uipath/resource-builder-sdk";
2
+ /**
3
+ * Fill a message template from the arguments the SDK passed alongside it.
4
+ *
5
+ * `{name}` is looked up on the first argument when that is a plain object —
6
+ * the shape the SDK uses. `%s` and friends consume arguments in order. A
7
+ * placeholder with nothing to fill it is left as written rather than replaced
8
+ * with "undefined", so a template the SDK logs with no arguments at all still
9
+ * reads as a template.
10
+ *
11
+ * Arguments no placeholder consumed are appended, so nothing the SDK meant to
12
+ * report is dropped.
13
+ */
14
+ export declare function interpolate(message: string, args: unknown[]): string;
15
+ /**
16
+ * The CLI's `ILoggerFactory` for the resource engine.
17
+ *
18
+ * The SDK's own factory prefixes each line with a timestamp and its component
19
+ * name; `logger` already stamps its own lines, so only the component name is
20
+ * carried over — it is what tells a `solution resource refresh` line apart
21
+ * from a `solution upload` one in a `--log-file`.
22
+ */
23
+ export declare class CliResourceBuilderLoggerFactory implements ILoggerFactory {
24
+ create(target: (new (...args: never[]) => unknown) | string): ILogger;
25
+ }
@@ -17,6 +17,9 @@ type SolutionUploadFailureDetails = {
17
17
  /** Full request URL, for diagnostics. */
18
18
  url: string;
19
19
  body: string;
20
+ /** Raw `WWW-Authenticate` when the response carried one. Parsed, never
21
+ * echoed whole — see {@link parseAuthChallenge}. */
22
+ authChallenge?: string;
20
23
  message: string;
21
24
  } & ({
22
25
  operation: "import";
@@ -47,11 +50,44 @@ export declare class SolutionUploadHttpError extends Error {
47
50
  /** From a `{"code":…,"message":…}` body; undefined for any other shape. */
48
51
  readonly parsedErrorCode?: string;
49
52
  readonly parsedErrorMessage?: string;
53
+ /** Raw `WWW-Authenticate`, when the response had one. On a real 401 from
54
+ * Studio Web this is often the *only* diagnostic: the body is empty
55
+ * (`content-length: 0`) and the reason is in this header. */
56
+ readonly authChallenge?: string;
50
57
  constructor(details: SolutionUploadFailureDetails);
51
58
  }
52
59
  /** Matched by shape, not `instanceof`: tools bundle their own copy of this
53
60
  * package, so a cross-bundle `instanceof` is not reliable. */
54
61
  export declare function isSolutionUploadHttpError(error: unknown): error is SolutionUploadHttpError;
62
+ /** Replace anything token-shaped in `text`. Exported for the auth-refusal
63
+ * paths and their tests; not re-exported from the package barrel. */
64
+ export declare function redactTokens(text: string): string;
65
+ /**
66
+ * Pull the reason out of a `WWW-Authenticate` challenge.
67
+ *
68
+ * Measured against alpha: a real Studio Web 401 comes back with
69
+ * `content-length: 0` and puts the whole diagnostic in this header —
70
+ * `Bearer realm="…", error="invalid_token", error_description="The audience
71
+ * 'empty' is invalid"`. Without this the CLI has nothing to tell the user, and
72
+ * the envelope ends up asking them to report a platform response that does not
73
+ * exist.
74
+ *
75
+ * Only the two RFC 6750 fields are taken. `realm` is deliberately dropped: it
76
+ * is an internal endpoint URL, it says nothing about why the call failed, and
77
+ * echoing routing metadata into user-facing errors is the pattern the security
78
+ * checklist warns about. `error` is a small fixed vocabulary; `error_description`
79
+ * is free text, so it goes through {@link redactTokens} and a length cap in
80
+ * case Identity ever quotes the presented credential back.
81
+ */
82
+ export declare function parseAuthChallenge(header?: string): {
83
+ error?: string;
84
+ description?: string;
85
+ };
86
+ /** Quote a platform body for an auth refusal, minus anything token-shaped.
87
+ * Redaction runs before `summarizePlatformBody`'s length cap, so a token
88
+ * cannot survive by being truncated mid-string into something the pattern no
89
+ * longer matches. */
90
+ export declare function summarizeAuthRefusalBody(body: string): string;
55
91
  /**
56
92
  * Build the CLI failure envelope for a Studio Web upload refusal.
57
93
  *
@@ -66,11 +102,20 @@ export declare function isSolutionUploadHttpError(error: unknown): error is Solu
66
102
  * the caller has a command for checking it; an overwrite refusal is about the
67
103
  * target solution, not the package.
68
104
  *
69
- * `Context.httpStatus` is what gives the envelope its `ErrorCode`.
105
+ * `identityType` (with `clientId` when it is known) only changes the wording
106
+ * of a 401/403: who the session acts as decides whether "check your Studio Web
107
+ * access" or "check the external application's scopes" is the useful sentence.
108
+ * It is read from unverified token claims, so it must never be used for
109
+ * anything but text — see `SessionIdentity` in `@uipath/auth`.
110
+ *
111
+ * `Context.httpStatus` is what gives the envelope its `ErrorCode`, except on an
112
+ * application 401 — see {@link buildAuthRefusalInstructions}.
70
113
  */
71
114
  export declare function buildSolutionUploadFailureOutput(err: SolutionUploadHttpError, options?: {
72
115
  subject?: string;
73
116
  importCheckHint?: string;
117
+ identityType?: "User" | "Application";
118
+ clientId?: string;
74
119
  }): FailureOutput;
75
120
  /**
76
121
  * Escape hatch for very large solutions or unusually slow links:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@uipath/solution-sdk",
3
3
  "license": "MIT",
4
- "version": "1.202.0-preview.138",
4
+ "version": "1.202.0-preview.140",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/UiPath/cli.git",
@@ -39,5 +39,5 @@
39
39
  "dist"
40
40
  ],
41
41
  "private": false,
42
- "gitHead": "46372d5c4c4ae9352e9457a99e9d5efc1890cb48"
42
+ "gitHead": "1861498d8a158a710a718c04614523ba59c93781"
43
43
  }