@uipath/packager-tool-workflowcompiler 1.197.0 → 1.198.0-preview.81

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.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import "./i18n/index.js";
2
2
  export { WorkflowCompilerToolFactory } from "./workflow-compiler-tool-factory.js";
3
3
  export { setupWorkflowCompiler } from "./workflow-compiler-config.js";
4
+ export type { CleanedWorkflowDetail, WorkflowCleanupDetails, } from "./workflow-cleanup-details.js";
package/dist/index.js CHANGED
@@ -850,17 +850,8 @@ function feedsEndpoint(orchestratorUrl) {
850
850
  end--;
851
851
  }
852
852
  const trimmed = orchestratorUrl.slice(0, end);
853
- if (/\/orchestrator_$/i.test(trimmed)) {
854
- return `${trimmed}${FEEDS_PATH}`;
855
- }
856
- return hasPathSegments(trimmed) ? `${trimmed}/orchestrator_${FEEDS_PATH}` : `${trimmed}${FEEDS_PATH}`;
857
- }
858
- function hasPathSegments(url) {
859
- const [error, parsed] = catchError(() => new URL(url));
860
- if (error) {
861
- return true;
862
- }
863
- return parsed.pathname !== "/" && parsed.pathname !== "";
853
+ const base = /\/orchestrator_$/i.test(trimmed) ? trimmed : `${trimmed}/orchestrator_`;
854
+ return `${base}${FEEDS_PATH}`;
864
855
  }
865
856
  async function describeFailure(response) {
866
857
  const text = await response.text().catch(() => "");
@@ -957,7 +948,8 @@ function toTenantFeedSource(feed, accessToken) {
957
948
  // src/workflow-compiler-tool.ts
958
949
  import {
959
950
  LogLevel,
960
- ProjectTool
951
+ ProjectTool,
952
+ toProjectCleanupInfo
961
953
  } from "@uipath/solutionpackager-tool-core";
962
954
 
963
955
  // src/workflow-compiler-executor.ts
@@ -1381,20 +1373,24 @@ class WorkflowCompilerTool extends ProjectTool {
1381
1373
  this.context = context;
1382
1374
  this.executor = new WorkflowCompilerExecutor(logger, fileSystem);
1383
1375
  }
1384
- async withNuGetConfig(options, run) {
1376
+ async withFeedPaths(options, run) {
1377
+ const userNugetConfigPath = options.nuGetSourcesConfigPath;
1385
1378
  const connection = this.context?.connection;
1386
- if (options.nuGetSourcesConfigPath || !connection) {
1387
- return run(options.nuGetSourcesConfigPath);
1379
+ if (!connection) {
1380
+ return run({ userNugetConfigPath });
1388
1381
  }
1389
1382
  const composed = await new OrchestratorFeedComposer(this.fileSystem, this.logger).composeAsync({ connection });
1390
1383
  try {
1391
- return await run(composed.configPath);
1384
+ return await run({
1385
+ orchestratorFeedsJsonPath: composed.configPath,
1386
+ userNugetConfigPath
1387
+ });
1392
1388
  } finally {
1393
1389
  await composed.dispose();
1394
1390
  }
1395
1391
  }
1396
1392
  async restoreAsync(options, cancellationToken) {
1397
- return this.withNuGetConfig(options, (nuGetConfigPath) => {
1393
+ return this.withFeedPaths(options, (paths) => {
1398
1394
  const args = [
1399
1395
  `-p`,
1400
1396
  options.projectPath,
@@ -1407,14 +1403,17 @@ class WorkflowCompilerTool extends ProjectTool {
1407
1403
  `--exclude-configured-sources`,
1408
1404
  `${options.excludeConfiguredSources}`
1409
1405
  ];
1410
- if (nuGetConfigPath) {
1411
- args.push(`--nuget-config-file`, nuGetConfigPath);
1406
+ if (paths.orchestratorFeedsJsonPath) {
1407
+ args.push(`--nuget-config-file`, paths.orchestratorFeedsJsonPath);
1408
+ }
1409
+ if (paths.userNugetConfigPath) {
1410
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
1412
1411
  }
1413
1412
  return this.executor.executeAsync("restore", args, cancellationToken);
1414
1413
  });
1415
1414
  }
1416
1415
  async validateAsync(options, cancellationToken) {
1417
- return this.withNuGetConfig(options, (nuGetConfigPath) => {
1416
+ return this.withFeedPaths(options, (paths) => {
1418
1417
  const args = [
1419
1418
  `-p`,
1420
1419
  options.projectPath,
@@ -1441,14 +1440,17 @@ class WorkflowCompilerTool extends ProjectTool {
1441
1440
  if (options.policyFileType) {
1442
1441
  args.push(`--policy-file-type`, `${options.policyFileType}`);
1443
1442
  }
1444
- if (nuGetConfigPath) {
1445
- args.push(`--nuget-config-file`, nuGetConfigPath);
1443
+ if (paths.orchestratorFeedsJsonPath) {
1444
+ args.push(`--nuget-config-file`, paths.orchestratorFeedsJsonPath);
1445
+ }
1446
+ if (paths.userNugetConfigPath) {
1447
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
1446
1448
  }
1447
1449
  return this.executor.executeAsync("validate", args, cancellationToken);
1448
1450
  });
1449
1451
  }
1450
1452
  async buildAsync(options, cancellationToken) {
1451
- return this.withNuGetConfig(options, (nuGetConfigPath) => {
1453
+ return this.withFeedPaths(options, (paths) => {
1452
1454
  const args = [
1453
1455
  `-p`,
1454
1456
  options.projectPath,
@@ -1478,14 +1480,17 @@ class WorkflowCompilerTool extends ProjectTool {
1478
1480
  const outputType = this.mapOutputType(options.outputType);
1479
1481
  args.push(`--output-type`, outputType);
1480
1482
  }
1481
- if (nuGetConfigPath) {
1482
- args.push(`--nuget-config-file`, nuGetConfigPath);
1483
+ if (paths.orchestratorFeedsJsonPath) {
1484
+ args.push(`--nuget-config-file`, paths.orchestratorFeedsJsonPath);
1485
+ }
1486
+ if (paths.userNugetConfigPath) {
1487
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
1483
1488
  }
1484
1489
  return this.executor.executeAsync("build", args, cancellationToken);
1485
1490
  });
1486
1491
  }
1487
1492
  async packAsync(options, cancellationToken) {
1488
- return this.withNuGetConfig(options, async (nuGetConfigPath) => {
1493
+ return this.withFeedPaths(options, async (paths) => {
1489
1494
  const args = [
1490
1495
  `-p`,
1491
1496
  options.projectPath,
@@ -1528,15 +1533,92 @@ class WorkflowCompilerTool extends ProjectTool {
1528
1533
  const outputType = this.mapOutputType(options.outputType);
1529
1534
  args.push(`--output-type`, outputType);
1530
1535
  }
1531
- if (nuGetConfigPath) {
1532
- args.push(`--nuget-config-file`, nuGetConfigPath);
1536
+ if (paths.orchestratorFeedsJsonPath) {
1537
+ args.push(`--nuget-config-file`, paths.orchestratorFeedsJsonPath);
1533
1538
  }
1534
- return this.executor.executeAsync("build", args, cancellationToken);
1539
+ if (paths.userNugetConfigPath) {
1540
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
1541
+ }
1542
+ const result = await this.executor.executeAsync("build", args, cancellationToken);
1543
+ return this.recoverPackagePathsAsync(result, options.outputPath);
1535
1544
  });
1536
1545
  }
1546
+ async cleanupAsync(options, cancellationToken) {
1547
+ return this.withFeedPaths(options, async (paths) => {
1548
+ const args = [
1549
+ `-p`,
1550
+ options.projectPath,
1551
+ `--log-level`,
1552
+ String(options.logLevel ?? LogLevel.Info),
1553
+ `--format-logs`,
1554
+ `true`,
1555
+ `--exclude-configured-sources`,
1556
+ `${options.excludeConfiguredSources}`
1557
+ ];
1558
+ if (options.dryRun) {
1559
+ args.push(`--dry-run`);
1560
+ }
1561
+ if (options.skipImports) {
1562
+ args.push(`--skip-imports`);
1563
+ }
1564
+ if (paths.orchestratorFeedsJsonPath) {
1565
+ args.push(`--nuget-config-file`, paths.orchestratorFeedsJsonPath);
1566
+ }
1567
+ if (paths.userNugetConfigPath) {
1568
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
1569
+ }
1570
+ const result = await this.executor.executeAsync("remove-unused-dependencies", args, cancellationToken);
1571
+ return this.toProjectCleanupResult(result);
1572
+ });
1573
+ }
1574
+ toProjectCleanupResult(result) {
1575
+ const raw = result.details?.removeUnusedDependencies;
1576
+ if (!raw) {
1577
+ return result;
1578
+ }
1579
+ const details = { ...result.details };
1580
+ delete details.removeUnusedDependencies;
1581
+ details.projectCleanup = toProjectCleanupInfo(raw);
1582
+ result.details = details;
1583
+ return result;
1584
+ }
1537
1585
  mapOutputType(outputType) {
1538
1586
  return outputType === "WebApp" ? "Process" : outputType;
1539
1587
  }
1588
+ async recoverPackagePathsAsync(result, outputPath) {
1589
+ if (!result.isSuccess) {
1590
+ return result;
1591
+ }
1592
+ const alreadyHasNupkgPaths = result.packages.length > 0 && result.packages.every((p) => p.toLowerCase().endsWith(".nupkg"));
1593
+ if (alreadyHasNupkgPaths) {
1594
+ return result;
1595
+ }
1596
+ const recovered = await this.findNupkgFilesAsync(outputPath);
1597
+ if (recovered.length > 0) {
1598
+ this.logger.warn(`[WorkflowCompiler] pack result did not include .nupkg paths; recovered ${recovered.length} package(s) by scanning the output folder (UV-15007 workaround).`);
1599
+ result.packages = recovered;
1600
+ }
1601
+ return result;
1602
+ }
1603
+ async findNupkgFilesAsync(dir) {
1604
+ const results = [];
1605
+ let entries;
1606
+ try {
1607
+ entries = await this.fileSystem.readdir(dir);
1608
+ } catch {
1609
+ return results;
1610
+ }
1611
+ for (const entry of entries) {
1612
+ const fullPath = this.fileSystem.path.join(dir, entry);
1613
+ const stats = await this.fileSystem.stat(fullPath);
1614
+ if (stats?.isDirectory()) {
1615
+ results.push(...await this.findNupkgFilesAsync(fullPath));
1616
+ } else if (entry.toLowerCase().endsWith(".nupkg")) {
1617
+ results.push(fullPath);
1618
+ }
1619
+ }
1620
+ return results;
1621
+ }
1540
1622
  }
1541
1623
 
1542
1624
  // src/workflow-compiler-tool-factory.ts
@@ -1574,4 +1656,4 @@ export {
1574
1656
  WorkflowCompilerToolFactory
1575
1657
  };
1576
1658
 
1577
- //# debugId=3093E6E2D5D9AED664756E2164756E21
1659
+ //# debugId=34D4B1BF483A641664756E2164756E21
@@ -0,0 +1 @@
1
+ export type { CleanedWorkflowDetail, WorkflowCleanupDetails, } from "@uipath/solutionpackager-tool-core";
@@ -1,4 +1,4 @@
1
- import { type IFileSystem, type IProjectBuildOptions, type IProjectPackOptions, type IProjectRestoreOptions, type IProjectValidateOptions, type IToolLogger, type ProjectOperationContext, ProjectTool, type ToolResult } from "@uipath/solutionpackager-tool-core";
1
+ import { type IFileSystem, type IProjectBuildOptions, type IProjectCleanupOptions, type IProjectPackOptions, type IProjectRestoreOptions, type IProjectValidateOptions, type IToolLogger, type ProjectOperationContext, ProjectTool, type ToolResult } from "@uipath/solutionpackager-tool-core";
2
2
  /**
3
3
  * Workflow compiler tool implementation.
4
4
  * Builds commands and delegates execution to WorkflowCompilerExecutor.
@@ -7,13 +7,41 @@ export declare class WorkflowCompilerTool extends ProjectTool {
7
7
  private readonly context?;
8
8
  private readonly executor;
9
9
  constructor(fileSystem: IFileSystem, logger: IToolLogger, context?: ProjectOperationContext | undefined);
10
- private withNuGetConfig;
10
+ private withFeedPaths;
11
11
  restoreAsync(options: IProjectRestoreOptions, cancellationToken?: AbortSignal): Promise<ToolResult>;
12
12
  validateAsync(options: IProjectValidateOptions, cancellationToken?: AbortSignal): Promise<ToolResult>;
13
13
  buildAsync(options: IProjectBuildOptions, cancellationToken?: AbortSignal): Promise<ToolResult>;
14
14
  packAsync(options: IProjectPackOptions, cancellationToken?: AbortSignal): Promise<ToolResult>;
15
+ cleanupAsync(options: IProjectCleanupOptions, cancellationToken?: AbortSignal): Promise<ToolResult>;
16
+ /**
17
+ * Convert the WorkflowCompiler's raw `remove-unused-dependencies` payload
18
+ * into the generic {@link ProjectCleanupInfo} the cleanup pipeline expects,
19
+ * exposed under the generic `details.projectCleanup` key. The
20
+ * workflow-specific bits (per-file removed namespaces) are preserved under
21
+ * `toolDetails` with this tool's own type; callers that only want the general
22
+ * result ignore it. Leaves the result untouched when the tool reported no
23
+ * cleanup payload (e.g. a failure, or an older compiler build).
24
+ */
25
+ private toProjectCleanupResult;
15
26
  /**
16
27
  * Maps output type for workflow compiler.
17
28
  */
18
29
  private mapOutputType;
30
+ /**
31
+ * WORKAROUND (UV-15007): older WorkflowCompiler builds report the staging
32
+ * content folder in their pack result instead of the produced `.nupkg` path,
33
+ * so downstream consumers (package signing, solution compression) cannot rely
34
+ * on `packages`. When the result does not already carry `.nupkg` paths, recover
35
+ * them by scanning the output folder.
36
+ *
37
+ * Remove this fallback after UV-15007 lands and the bundled WorkflowCompiler
38
+ * surfaces the produced `.nupkg` path in its result
39
+ * (see scripts/Download-WorkflowCompiler.ps1).
40
+ */
41
+ private recoverPackagePathsAsync;
42
+ /**
43
+ * Recursively collect `.nupkg` files under `dir` (cross-platform). Returns an
44
+ * empty list if the folder cannot be read.
45
+ */
46
+ private findNupkgFilesAsync;
19
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/packager-tool-workflowcompiler",
3
- "version": "1.197.0",
3
+ "version": "1.198.0-preview.81",
4
4
  "description": "UiPath Workflow Compiler tool implementation",
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,8 +25,8 @@
25
25
  "author": "",
26
26
  "license": "ISC",
27
27
  "peerDependencies": {
28
- "@uipath/solutionpackager-tool-core": "1.197.0"
28
+ "@uipath/solutionpackager-tool-core": "1.198.0"
29
29
  },
30
30
  "dependencies": {},
31
- "gitHead": "56f68b263cec4ea1f2a39d738bd99ecf52f88fea"
31
+ "gitHead": "d6a326ee051d553694b61f4923bcf1719d2e5110"
32
32
  }
package/src/index.ts CHANGED
@@ -16,3 +16,13 @@ export { WorkflowCompilerToolFactory } from "./workflow-compiler-tool-factory.js
16
16
  // ============================================================================
17
17
 
18
18
  export { setupWorkflowCompiler } from "./workflow-compiler-config.js";
19
+
20
+ // ============================================================================
21
+ // Tool-specific cleanup detail. Surfaced via `ProjectCleanupInfo.toolDetails`;
22
+ // only consumers that want WorkflowCompiler specifics import these types.
23
+ // ============================================================================
24
+
25
+ export type {
26
+ CleanedWorkflowDetail,
27
+ WorkflowCleanupDetails,
28
+ } from "./workflow-cleanup-details.js";
@@ -0,0 +1,4 @@
1
+ export type {
2
+ CleanedWorkflowDetail,
3
+ WorkflowCleanupDetails,
4
+ } from "@uipath/solutionpackager-tool-core";
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { type ChildProcess, spawn } from "node:child_process";
2
2
  import {
3
3
  type IFileSystem,
4
4
  type IToolLogger,
@@ -17,6 +17,13 @@ import {
17
17
  WorkflowCompilerPathResolver,
18
18
  } from "./workflow-compiler-path-resolver.js";
19
19
 
20
+ /** Signals when each child-process stream has finished. */
21
+ interface StreamCompletion {
22
+ markStdoutEnded(): void;
23
+ markStderrEnded(): void;
24
+ markProcessExited(): void;
25
+ }
26
+
20
27
  function camelCaseKeys(obj: unknown): unknown {
21
28
  if (Array.isArray(obj)) {
22
29
  return obj.map(camelCaseKeys);
@@ -195,8 +202,8 @@ export class WorkflowCompilerExecutor {
195
202
  }
196
203
 
197
204
  private setupStdoutHandling(
198
- childProcess: any,
199
- streamCompletion: any,
205
+ childProcess: ChildProcess,
206
+ streamCompletion: StreamCompletion,
200
207
  onResult: (result: IOutputResult) => void,
201
208
  ): void {
202
209
  if (!childProcess.stdout) {
@@ -261,8 +268,8 @@ export class WorkflowCompilerExecutor {
261
268
  }
262
269
 
263
270
  private setupStderrHandling(
264
- childProcess: any,
265
- streamCompletion: any,
271
+ childProcess: ChildProcess,
272
+ streamCompletion: StreamCompletion,
266
273
  fallbackErrors: string[],
267
274
  ): void {
268
275
  if (!childProcess.stderr) {
@@ -301,8 +308,8 @@ export class WorkflowCompilerExecutor {
301
308
  }
302
309
 
303
310
  private setupProcessEventHandlers(
304
- childProcess: any,
305
- streamCompletion: any,
311
+ childProcess: ChildProcess,
312
+ streamCompletion: StreamCompletion,
306
313
  resolve: (value: ToolResult) => void,
307
314
  ): void {
308
315
  childProcess.on("exit", () => streamCompletion.markProcessExited());
@@ -326,7 +333,7 @@ export class WorkflowCompilerExecutor {
326
333
  }
327
334
 
328
335
  private setupCancellationHandler(
329
- childProcess: any,
336
+ childProcess: ChildProcess,
330
337
  cancellationToken: AbortSignal | undefined,
331
338
  resolve: (value: ToolResult) => void,
332
339
  ): void {
@@ -354,7 +361,7 @@ export class WorkflowCompilerExecutor {
354
361
  const parsed = JSON.parse(line);
355
362
 
356
363
  // Convert PascalCase to camelCase
357
- const camelCased = camelCaseKeys(parsed) as Record<string, any>;
364
+ const camelCased = camelCaseKeys(parsed) as Record<string, unknown>;
358
365
 
359
366
  // Convert numeric error codes to strings
360
367
  if (camelCased.errorCode !== undefined) {
@@ -367,12 +374,12 @@ export class WorkflowCompilerExecutor {
367
374
 
368
375
  // Extract paths from OutputPackages array of objects
369
376
  if (camelCased.outputPackages) {
370
- camelCased.outputPackages = camelCased.outputPackages.map(
371
- (pkg: any) => pkg.path || pkg,
372
- );
377
+ camelCased.outputPackages = (
378
+ camelCased.outputPackages as Array<{ path?: string }>
379
+ ).map((pkg) => pkg.path || pkg);
373
380
  }
374
381
 
375
- return camelCased as IOutputMessage;
382
+ return camelCased as unknown as IOutputMessage;
376
383
  } catch {
377
384
  // Not JSON, return null
378
385
  return null;
@@ -2,6 +2,7 @@ import { OrchestratorFeedComposer } from "@uipath/packager-orchestrator-client";
2
2
  import {
3
3
  type IFileSystem,
4
4
  type IProjectBuildOptions,
5
+ type IProjectCleanupOptions,
5
6
  type IProjectOptions,
6
7
  type IProjectPackOptions,
7
8
  type IProjectRestoreOptions,
@@ -11,9 +12,33 @@ import {
11
12
  type ProjectOperationContext,
12
13
  ProjectTool,
13
14
  type ToolResult,
15
+ toProjectCleanupInfo,
16
+ type WorkflowCompilerCleanupPayload,
14
17
  } from "@uipath/solutionpackager-tool-core";
15
18
  import { WorkflowCompilerExecutor } from "./workflow-compiler-executor.js";
16
19
 
20
+ /**
21
+ * Raw `remove-unused-dependencies` payload the WorkflowCompiler emits on
22
+ * `details.removeUnusedDependencies` (PascalCase on the wire, camelCased by the
23
+ * executor). Owned by this tool; mapped to the generic {@link ProjectCleanupInfo}
24
+ * before it leaves `cleanupAsync` so nothing downstream depends on this shape.
25
+ */
26
+ type RawRemoveUnusedDependenciesResult = WorkflowCompilerCleanupPayload;
27
+
28
+ /**
29
+ * The two NuGet-source channels the WorkflowCompiler accepts, kept apart on
30
+ * purpose:
31
+ * - `orchestratorFeedsJsonPath` — Orchestrator tenant feeds composed internally into a
32
+ * JSON file, passed on the hidden `--nuget-config-file` option. Never
33
+ * user-supplied.
34
+ * - `userNugetConfigPath` — the user's own NuGet.config (XML), passed verbatim on
35
+ * `--nuget-config`.
36
+ */
37
+ interface FeedPaths {
38
+ orchestratorFeedsJsonPath?: string;
39
+ userNugetConfigPath?: string;
40
+ }
41
+
17
42
  /**
18
43
  * Workflow compiler tool implementation.
19
44
  * Builds commands and delegates execution to WorkflowCompilerExecutor.
@@ -30,20 +55,28 @@ export class WorkflowCompilerTool extends ProjectTool {
30
55
  this.executor = new WorkflowCompilerExecutor(logger, fileSystem);
31
56
  }
32
57
 
33
- private async withNuGetConfig(
58
+ private async withFeedPaths(
34
59
  options: IProjectOptions,
35
- run: (nuGetConfigPath?: string) => Promise<ToolResult>,
60
+ run: (paths: FeedPaths) => Promise<ToolResult>,
36
61
  ): Promise<ToolResult> {
62
+ // The user's own NuGet.config (XML) always rides the --nuget-config
63
+ // channel; it is never mixed into the internal feeds file.
64
+ const userNugetConfigPath = options.nuGetSourcesConfigPath;
37
65
  const connection = this.context?.connection;
38
- if (options.nuGetSourcesConfigPath || !connection) {
39
- return run(options.nuGetSourcesConfigPath);
66
+ if (!connection) {
67
+ return run({ userNugetConfigPath });
40
68
  }
69
+ // Orchestrator tenant feeds are composed into a JSON file that only ever
70
+ // goes on the hidden --nuget-config-file option.
41
71
  const composed = await new OrchestratorFeedComposer(
42
72
  this.fileSystem,
43
73
  this.logger,
44
74
  ).composeAsync({ connection });
45
75
  try {
46
- return await run(composed.configPath);
76
+ return await run({
77
+ orchestratorFeedsJsonPath: composed.configPath,
78
+ userNugetConfigPath,
79
+ });
47
80
  } finally {
48
81
  await composed.dispose();
49
82
  }
@@ -53,7 +86,7 @@ export class WorkflowCompilerTool extends ProjectTool {
53
86
  options: IProjectRestoreOptions,
54
87
  cancellationToken?: AbortSignal,
55
88
  ): Promise<ToolResult> {
56
- return this.withNuGetConfig(options, (nuGetConfigPath) => {
89
+ return this.withFeedPaths(options, (paths) => {
57
90
  const args = [
58
91
  `-p`,
59
92
  options.projectPath,
@@ -67,8 +100,14 @@ export class WorkflowCompilerTool extends ProjectTool {
67
100
  `${options.excludeConfiguredSources}`,
68
101
  ];
69
102
 
70
- if (nuGetConfigPath) {
71
- args.push(`--nuget-config-file`, nuGetConfigPath);
103
+ if (paths.orchestratorFeedsJsonPath) {
104
+ args.push(
105
+ `--nuget-config-file`,
106
+ paths.orchestratorFeedsJsonPath,
107
+ );
108
+ }
109
+ if (paths.userNugetConfigPath) {
110
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
72
111
  }
73
112
 
74
113
  return this.executor.executeAsync(
@@ -83,7 +122,7 @@ export class WorkflowCompilerTool extends ProjectTool {
83
122
  options: IProjectValidateOptions,
84
123
  cancellationToken?: AbortSignal,
85
124
  ): Promise<ToolResult> {
86
- return this.withNuGetConfig(options, (nuGetConfigPath) => {
125
+ return this.withFeedPaths(options, (paths) => {
87
126
  const args = [
88
127
  `-p`,
89
128
  options.projectPath,
@@ -111,8 +150,14 @@ export class WorkflowCompilerTool extends ProjectTool {
111
150
  if (options.policyFileType) {
112
151
  args.push(`--policy-file-type`, `${options.policyFileType}`);
113
152
  }
114
- if (nuGetConfigPath) {
115
- args.push(`--nuget-config-file`, nuGetConfigPath);
153
+ if (paths.orchestratorFeedsJsonPath) {
154
+ args.push(
155
+ `--nuget-config-file`,
156
+ paths.orchestratorFeedsJsonPath,
157
+ );
158
+ }
159
+ if (paths.userNugetConfigPath) {
160
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
116
161
  }
117
162
 
118
163
  return this.executor.executeAsync(
@@ -127,7 +172,7 @@ export class WorkflowCompilerTool extends ProjectTool {
127
172
  options: IProjectBuildOptions,
128
173
  cancellationToken?: AbortSignal,
129
174
  ): Promise<ToolResult> {
130
- return this.withNuGetConfig(options, (nuGetConfigPath) => {
175
+ return this.withFeedPaths(options, (paths) => {
131
176
  const args = [
132
177
  `-p`,
133
178
  options.projectPath,
@@ -158,8 +203,14 @@ export class WorkflowCompilerTool extends ProjectTool {
158
203
  const outputType = this.mapOutputType(options.outputType);
159
204
  args.push(`--output-type`, outputType);
160
205
  }
161
- if (nuGetConfigPath) {
162
- args.push(`--nuget-config-file`, nuGetConfigPath);
206
+ if (paths.orchestratorFeedsJsonPath) {
207
+ args.push(
208
+ `--nuget-config-file`,
209
+ paths.orchestratorFeedsJsonPath,
210
+ );
211
+ }
212
+ if (paths.userNugetConfigPath) {
213
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
163
214
  }
164
215
 
165
216
  return this.executor.executeAsync("build", args, cancellationToken);
@@ -170,7 +221,7 @@ export class WorkflowCompilerTool extends ProjectTool {
170
221
  options: IProjectPackOptions,
171
222
  cancellationToken?: AbortSignal,
172
223
  ): Promise<ToolResult> {
173
- return this.withNuGetConfig(options, async (nuGetConfigPath) => {
224
+ return this.withFeedPaths(options, async (paths) => {
174
225
  const args: string[] = [
175
226
  `-p`,
176
227
  options.projectPath,
@@ -214,18 +265,161 @@ export class WorkflowCompilerTool extends ProjectTool {
214
265
  const outputType = this.mapOutputType(options.outputType);
215
266
  args.push(`--output-type`, outputType);
216
267
  }
217
- if (nuGetConfigPath) {
218
- args.push(`--nuget-config-file`, nuGetConfigPath);
268
+ if (paths.orchestratorFeedsJsonPath) {
269
+ args.push(
270
+ `--nuget-config-file`,
271
+ paths.orchestratorFeedsJsonPath,
272
+ );
273
+ }
274
+ if (paths.userNugetConfigPath) {
275
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
219
276
  }
220
277
 
221
- return this.executor.executeAsync("build", args, cancellationToken);
278
+ const result = await this.executor.executeAsync(
279
+ "build",
280
+ args,
281
+ cancellationToken,
282
+ );
283
+ return this.recoverPackagePathsAsync(result, options.outputPath);
284
+ });
285
+ }
286
+
287
+ async cleanupAsync(
288
+ options: IProjectCleanupOptions,
289
+ cancellationToken?: AbortSignal,
290
+ ): Promise<ToolResult> {
291
+ return this.withFeedPaths(options, async (paths) => {
292
+ const args = [
293
+ `-p`,
294
+ options.projectPath,
295
+ `--log-level`,
296
+ String(options.logLevel ?? LogLevel.Info),
297
+ `--format-logs`,
298
+ `true`,
299
+ `--exclude-configured-sources`,
300
+ `${options.excludeConfiguredSources}`,
301
+ ];
302
+
303
+ // --dry-run and --skip-imports are boolean switches on the compiler CLI:
304
+ // present means true, absent means false. They take no value — passing
305
+ // one (`--dry-run false`) leaves a stray token and still enables the flag.
306
+ if (options.dryRun) {
307
+ args.push(`--dry-run`);
308
+ }
309
+ if (options.skipImports) {
310
+ args.push(`--skip-imports`);
311
+ }
312
+
313
+ if (paths.orchestratorFeedsJsonPath) {
314
+ args.push(
315
+ `--nuget-config-file`,
316
+ paths.orchestratorFeedsJsonPath,
317
+ );
318
+ }
319
+ if (paths.userNugetConfigPath) {
320
+ args.push(`--nuget-config`, paths.userNugetConfigPath);
321
+ }
322
+
323
+ const result = await this.executor.executeAsync(
324
+ "remove-unused-dependencies",
325
+ args,
326
+ cancellationToken,
327
+ );
328
+ return this.toProjectCleanupResult(result);
222
329
  });
223
330
  }
224
331
 
332
+ /**
333
+ * Convert the WorkflowCompiler's raw `remove-unused-dependencies` payload
334
+ * into the generic {@link ProjectCleanupInfo} the cleanup pipeline expects,
335
+ * exposed under the generic `details.projectCleanup` key. The
336
+ * workflow-specific bits (per-file removed namespaces) are preserved under
337
+ * `toolDetails` with this tool's own type; callers that only want the general
338
+ * result ignore it. Leaves the result untouched when the tool reported no
339
+ * cleanup payload (e.g. a failure, or an older compiler build).
340
+ */
341
+ private toProjectCleanupResult(result: ToolResult): ToolResult {
342
+ const raw = result.details?.removeUnusedDependencies as
343
+ | RawRemoveUnusedDependenciesResult
344
+ | undefined;
345
+ if (!raw) {
346
+ return result;
347
+ }
348
+
349
+ // Re-key the wire-named payload under the generic `projectCleanup` so
350
+ // nothing downstream sees this tool's domain naming.
351
+ const details = { ...result.details };
352
+ delete details.removeUnusedDependencies;
353
+ details.projectCleanup = toProjectCleanupInfo(raw);
354
+ result.details = details;
355
+ return result;
356
+ }
357
+
225
358
  /**
226
359
  * Maps output type for workflow compiler.
227
360
  */
228
361
  private mapOutputType(outputType: string): string {
229
362
  return outputType === "WebApp" ? "Process" : outputType;
230
363
  }
364
+
365
+ /**
366
+ * WORKAROUND (UV-15007): older WorkflowCompiler builds report the staging
367
+ * content folder in their pack result instead of the produced `.nupkg` path,
368
+ * so downstream consumers (package signing, solution compression) cannot rely
369
+ * on `packages`. When the result does not already carry `.nupkg` paths, recover
370
+ * them by scanning the output folder.
371
+ *
372
+ * Remove this fallback after UV-15007 lands and the bundled WorkflowCompiler
373
+ * surfaces the produced `.nupkg` path in its result
374
+ * (see scripts/Download-WorkflowCompiler.ps1).
375
+ */
376
+ private async recoverPackagePathsAsync(
377
+ result: ToolResult,
378
+ outputPath: string,
379
+ ): Promise<ToolResult> {
380
+ if (!result.isSuccess) {
381
+ return result;
382
+ }
383
+
384
+ const alreadyHasNupkgPaths =
385
+ result.packages.length > 0 &&
386
+ result.packages.every((p) => p.toLowerCase().endsWith(".nupkg"));
387
+ if (alreadyHasNupkgPaths) {
388
+ return result;
389
+ }
390
+
391
+ const recovered = await this.findNupkgFilesAsync(outputPath);
392
+ if (recovered.length > 0) {
393
+ this.logger.warn(
394
+ `[WorkflowCompiler] pack result did not include .nupkg paths; recovered ${recovered.length} package(s) by scanning the output folder (UV-15007 workaround).`,
395
+ );
396
+ result.packages = recovered;
397
+ }
398
+ return result;
399
+ }
400
+
401
+ /**
402
+ * Recursively collect `.nupkg` files under `dir` (cross-platform). Returns an
403
+ * empty list if the folder cannot be read.
404
+ */
405
+ private async findNupkgFilesAsync(dir: string): Promise<string[]> {
406
+ const results: string[] = [];
407
+ let entries: string[];
408
+ try {
409
+ entries = await this.fileSystem.readdir(dir);
410
+ } catch {
411
+ return results;
412
+ }
413
+
414
+ for (const entry of entries) {
415
+ const fullPath = this.fileSystem.path.join(dir, entry);
416
+ const stats = await this.fileSystem.stat(fullPath);
417
+ if (stats?.isDirectory()) {
418
+ results.push(...(await this.findNupkgFilesAsync(fullPath)));
419
+ } else if (entry.toLowerCase().endsWith(".nupkg")) {
420
+ results.push(fullPath);
421
+ }
422
+ }
423
+ return results;
424
+ }
231
425
  }