@backstage/plugin-scaffolder-backend 1.6.0-next.1 → 1.6.0-next.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @backstage/plugin-scaffolder-backend
2
2
 
3
+ ## 1.6.0-next.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 50467bc15b: The number of task workers used to execute templates now default to 3, rather than 1.
8
+ - Updated dependencies
9
+ - @backstage/plugin-catalog-node@1.1.0-next.2
10
+ - @backstage/backend-plugin-api@0.1.2-next.2
11
+ - @backstage/catalog-client@1.1.0-next.2
12
+ - @backstage/catalog-model@1.1.1-next.0
13
+ - @backstage/config@1.0.2-next.0
14
+ - @backstage/errors@1.1.1-next.0
15
+ - @backstage/integration@1.3.1-next.2
16
+ - @backstage/plugin-catalog-backend@1.4.0-next.3
17
+ - @backstage/backend-common@0.15.1-next.3
18
+ - @backstage/plugin-scaffolder-common@1.2.0-next.1
19
+ - @backstage/plugin-auth-node@0.2.5-next.3
20
+
21
+ ## 1.6.0-next.2
22
+
23
+ ### Minor Changes
24
+
25
+ - d1f7ba58e3: Added `repositoryId` output when create a repository in Azure
26
+
27
+ ### Patch Changes
28
+
29
+ - eadf56bbbf: Bump `git-url-parse` version to `^13.0.0`
30
+ - 096631e571: Added support for handling broken symlinks within the scaffolder backend. This is intended for templates that may hold a symlink that is invalid at build time but valid within the destination repo.
31
+ - 667d917488: Updated dependency `msw` to `^0.47.0`.
32
+ - 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`.
33
+ - 6b9f6c0a4d: Added alpha `scaffolderPlugin` to be used with experimental backend system.
34
+ - 83c037cd46: Disable octokit throttling in publish:github:pull-request
35
+ - 2cbd533426: Uptake the `IdentityApi` change to use `getIdentity` instead of `authenticate` for retrieving the logged in users identity.
36
+ - Updated dependencies
37
+ - @backstage/backend-plugin-api@0.1.2-next.1
38
+ - @backstage/plugin-catalog-node@1.0.2-next.1
39
+ - @backstage/backend-common@0.15.1-next.2
40
+ - @backstage/integration@1.3.1-next.1
41
+ - @backstage/plugin-catalog-backend@1.4.0-next.2
42
+ - @backstage/plugin-auth-node@0.2.5-next.2
43
+ - @backstage/catalog-client@1.0.5-next.1
44
+
3
45
  ## 1.6.0-next.1
4
46
 
5
47
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-scaffolder-backend",
3
- "version": "1.6.0-next.1",
3
+ "version": "1.6.0-next.3",
4
4
  "main": "../dist/index.cjs.js",
5
5
  "types": "../dist/index.alpha.d.ts"
6
6
  }
@@ -15,6 +15,7 @@ import { createPullRequest } from 'octokit-plugin-create-pull-request';
15
15
  import { Entity } from '@backstage/catalog-model';
16
16
  import express from 'express';
17
17
  import { GithubCredentialsProvider } from '@backstage/integration';
18
+ import { IdentityApi } from '@backstage/plugin-auth-node';
18
19
  import { JsonObject } from '@backstage/types';
19
20
  import { JsonValue } from '@backstage/types';
20
21
  import { Knex } from 'knex';
@@ -692,6 +693,7 @@ export declare interface RouterOptions {
692
693
  taskWorkers?: number;
693
694
  taskBroker?: TaskBroker;
694
695
  additionalTemplateFilters?: Record<string, TemplateFilter>;
696
+ identity?: IdentityApi;
695
697
  }
696
698
 
697
699
  /** @public */
@@ -710,7 +712,7 @@ export declare type RunCommandOptions = {
710
712
  * @alpha
711
713
  * Registers the ScaffolderEntitiesProcessor with the catalog processing extension point.
712
714
  */
713
- export declare const scaffolderCatalogModule: (options?: unknown) => BackendFeature;
715
+ export declare const scaffolderCatalogModule: (options?: undefined) => BackendFeature;
714
716
 
715
717
  /** @public */
716
718
  export declare class ScaffolderEntitiesProcessor implements CatalogProcessor {
@@ -720,6 +722,23 @@ export declare class ScaffolderEntitiesProcessor implements CatalogProcessor {
720
722
  postProcessEntity(entity: Entity, _location: LocationSpec, emit: CatalogProcessorEmit): Promise<Entity>;
721
723
  }
722
724
 
725
+ /**
726
+ * Catalog plugin
727
+ * @alpha
728
+ */
729
+ export declare const scaffolderPlugin: (options: ScaffolderPluginOptions) => BackendFeature;
730
+
731
+ /**
732
+ * Catalog plugin options
733
+ * @alpha
734
+ */
735
+ export declare type ScaffolderPluginOptions = {
736
+ actions?: TemplateAction<any>[];
737
+ taskWorkers?: number;
738
+ taskBroker?: TaskBroker;
739
+ additionalTemplateFilters?: Record<string, TemplateFilter>;
740
+ };
741
+
723
742
  /**
724
743
  * SerializedTask
725
744
  *
@@ -15,6 +15,7 @@ import { createPullRequest } from 'octokit-plugin-create-pull-request';
15
15
  import { Entity } from '@backstage/catalog-model';
16
16
  import express from 'express';
17
17
  import { GithubCredentialsProvider } from '@backstage/integration';
18
+ import { IdentityApi } from '@backstage/plugin-auth-node';
18
19
  import { JsonObject } from '@backstage/types';
19
20
  import { JsonValue } from '@backstage/types';
20
21
  import { Knex } from 'knex';
@@ -692,6 +693,7 @@ export declare interface RouterOptions {
692
693
  taskWorkers?: number;
693
694
  taskBroker?: TaskBroker;
694
695
  additionalTemplateFilters?: Record<string, TemplateFilter>;
696
+ identity?: IdentityApi;
695
697
  }
696
698
 
697
699
  /** @public */
@@ -716,6 +718,10 @@ export declare class ScaffolderEntitiesProcessor implements CatalogProcessor {
716
718
  postProcessEntity(entity: Entity, _location: LocationSpec, emit: CatalogProcessorEmit): Promise<Entity>;
717
719
  }
718
720
 
721
+ /* Excluded from this release type: scaffolderPlugin */
722
+
723
+ /* Excluded from this release type: ScaffolderPluginOptions */
724
+
719
725
  /**
720
726
  * SerializedTask
721
727
  *
package/dist/index.cjs.js CHANGED
@@ -20,6 +20,7 @@ var azureDevopsNodeApi = require('azure-devops-node-api');
20
20
  var fetch = require('node-fetch');
21
21
  var crypto = require('crypto');
22
22
  var octokitPluginCreatePullRequest = require('octokit-plugin-create-pull-request');
23
+ var fs$1 = require('fs');
23
24
  var limiterFactory = require('p-limit');
24
25
  var node = require('@gitbeaker/node');
25
26
  var uuid = require('uuid');
@@ -1804,6 +1805,10 @@ function createPublishAzureAction(options) {
1804
1805
  repoContentsUrl: {
1805
1806
  title: "A URL to the root of the repository",
1806
1807
  type: "string"
1808
+ },
1809
+ repositoryId: {
1810
+ title: "The Id of the created repository",
1811
+ type: "string"
1807
1812
  }
1808
1813
  }
1809
1814
  }
@@ -1853,6 +1858,10 @@ function createPublishAzureAction(options) {
1853
1858
  "No remote URL returned from create repository for Azure"
1854
1859
  );
1855
1860
  }
1861
+ const repositoryId = returnedRepo.id;
1862
+ if (!repositoryId) {
1863
+ throw new errors.InputError("No Id returned from create repository for Azure");
1864
+ }
1856
1865
  const repoContentsUrl = remoteUrl;
1857
1866
  const gitAuthorInfo = {
1858
1867
  name: gitAuthorName ? gitAuthorName : config.getOptionalString("scaffolder.defaultAuthor.name"),
@@ -1872,6 +1881,7 @@ function createPublishAzureAction(options) {
1872
1881
  });
1873
1882
  ctx.output("remoteUrl", remoteUrl);
1874
1883
  ctx.output("repoContentsUrl", repoContentsUrl);
1884
+ ctx.output("repositoryId", repositoryId);
1875
1885
  }
1876
1886
  });
1877
1887
  }
@@ -2931,6 +2941,10 @@ const isExecutable = (fileMode) => {
2931
2941
  const res = fileMode & executeBitMask;
2932
2942
  return res > 0;
2933
2943
  };
2944
+ async function asyncFilter(array, callback) {
2945
+ const filterMap = await Promise.all(array.map(callback));
2946
+ return array.filter((_value, index) => filterMap[index]);
2947
+ }
2934
2948
  async function serializeDirectoryContents(sourcePath, options) {
2935
2949
  var _a;
2936
2950
  const paths = await globby__default["default"]((_a = options == null ? void 0 : options.globPatterns) != null ? _a : DEFAULT_GLOB_PATTERNS, {
@@ -2938,17 +2952,36 @@ async function serializeDirectoryContents(sourcePath, options) {
2938
2952
  dot: true,
2939
2953
  gitignore: options == null ? void 0 : options.gitignore,
2940
2954
  followSymbolicLinks: false,
2955
+ onlyFiles: false,
2941
2956
  objectMode: true,
2942
2957
  stats: true
2943
2958
  });
2944
2959
  const limiter = limiterFactory__default["default"](10);
2960
+ const valid = await asyncFilter(paths, async ({ dirent, path }) => {
2961
+ if (dirent.isDirectory())
2962
+ return false;
2963
+ if (!dirent.isSymbolicLink())
2964
+ return true;
2965
+ const safePath = backendCommon.resolveSafeChildPath(sourcePath, path);
2966
+ try {
2967
+ await fs$1.promises.stat(safePath);
2968
+ return false;
2969
+ } catch (e) {
2970
+ return errors.isError(e) && e.code === "ENOENT";
2971
+ }
2972
+ });
2945
2973
  return Promise.all(
2946
- paths.map(async ({ path: path$1, stats }) => ({
2947
- path: path$1,
2948
- content: await limiter(
2949
- async () => fs__default["default"].readFile(path.join(sourcePath, path$1))
2950
- ),
2951
- executable: isExecutable(stats == null ? void 0 : stats.mode)
2974
+ valid.map(async ({ dirent, path, stats }) => ({
2975
+ path,
2976
+ content: await limiter(async () => {
2977
+ const absFilePath = backendCommon.resolveSafeChildPath(sourcePath, path);
2978
+ if (dirent.isSymbolicLink()) {
2979
+ return fs$1.promises.readlink(absFilePath, "buffer");
2980
+ }
2981
+ return fs$1.promises.readFile(absFilePath);
2982
+ }),
2983
+ executable: isExecutable(stats == null ? void 0 : stats.mode),
2984
+ symlink: dirent.isSymbolicLink()
2952
2985
  }))
2953
2986
  );
2954
2987
  }
@@ -2981,7 +3014,10 @@ const defaultClientFactory = async ({
2981
3014
  token: providedToken
2982
3015
  });
2983
3016
  const OctokitPR = octokit.Octokit.plugin(octokitPluginCreatePullRequest.createPullRequest);
2984
- return new OctokitPR(octokitOptions);
3017
+ return new OctokitPR({
3018
+ ...octokitOptions,
3019
+ ...{ throttle: { enabled: false } }
3020
+ });
2985
3021
  };
2986
3022
  const createPublishGithubPullRequestAction = ({
2987
3023
  integrations,
@@ -3101,13 +3137,21 @@ const createPublishGithubPullRequestAction = ({
3101
3137
  const directoryContents = await serializeDirectoryContents(fileRoot, {
3102
3138
  gitignore: true
3103
3139
  });
3140
+ const determineFileMode = (file) => {
3141
+ if (file.symlink)
3142
+ return "120000";
3143
+ if (file.executable)
3144
+ return "100755";
3145
+ return "100644";
3146
+ };
3147
+ const determineFileEncoding = (file) => file.symlink ? "utf-8" : "base64";
3104
3148
  const files = Object.fromEntries(
3105
3149
  directoryContents.map((file) => [
3106
3150
  targetPath ? path__default["default"].posix.join(targetPath, file.path) : file.path,
3107
3151
  {
3108
- mode: file.executable ? "100755" : "100644",
3109
- encoding: "base64",
3110
- content: file.content.toString("base64")
3152
+ mode: determineFileMode(file),
3153
+ encoding: determineFileEncoding(file),
3154
+ content: file.content.toString(determineFileEncoding(file))
3111
3155
  }
3112
3156
  ])
3113
3157
  );
@@ -4450,6 +4494,48 @@ async function findTemplate(options) {
4450
4494
  function isSupportedTemplate(entity) {
4451
4495
  return entity.apiVersion === "scaffolder.backstage.io/v1beta3";
4452
4496
  }
4497
+ function buildDefaultIdentityClient({
4498
+ logger
4499
+ }) {
4500
+ return {
4501
+ getIdentity: async ({ request }) => {
4502
+ var _a;
4503
+ const header = request.headers.authorization;
4504
+ if (!header) {
4505
+ return void 0;
4506
+ }
4507
+ try {
4508
+ const token = (_a = header.match(/^Bearer\s(\S+\.\S+\.\S+)$/i)) == null ? void 0 : _a[1];
4509
+ if (!token) {
4510
+ throw new TypeError("Expected Bearer with JWT");
4511
+ }
4512
+ const [_header, rawPayload, _signature] = token.split(".");
4513
+ const payload = JSON.parse(
4514
+ Buffer.from(rawPayload, "base64").toString()
4515
+ );
4516
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
4517
+ throw new TypeError("Malformed JWT payload");
4518
+ }
4519
+ const sub = payload.sub;
4520
+ if (typeof sub !== "string") {
4521
+ throw new TypeError("Expected string sub claim");
4522
+ }
4523
+ catalogModel.parseEntityRef(sub);
4524
+ return {
4525
+ identity: {
4526
+ userEntityRef: sub,
4527
+ ownershipEntityRefs: [],
4528
+ type: "user"
4529
+ },
4530
+ token
4531
+ };
4532
+ } catch (e) {
4533
+ logger.error(`Invalid authorization header: ${errors.stringifyError(e)}`);
4534
+ return void 0;
4535
+ }
4536
+ }
4537
+ };
4538
+ }
4453
4539
  async function createRouter(options) {
4454
4540
  const router = Router__default["default"]();
4455
4541
  router.use(express__default["default"].json());
@@ -4464,6 +4550,7 @@ async function createRouter(options) {
4464
4550
  additionalTemplateFilters
4465
4551
  } = options;
4466
4552
  const logger = parentLogger.child({ plugin: "scaffolder" });
4553
+ const identity = options.identity || buildDefaultIdentityClient({ logger });
4467
4554
  const workingDirectory = await getWorkingDirectory(config, logger);
4468
4555
  const integrations = integration.ScmIntegrations.fromConfig(config);
4469
4556
  let taskBroker;
@@ -4475,7 +4562,7 @@ async function createRouter(options) {
4475
4562
  }
4476
4563
  const actionRegistry = new TemplateActionRegistry();
4477
4564
  const workers = [];
4478
- for (let i = 0; i < (taskWorkers || 1); i++) {
4565
+ for (let i = 0; i < (taskWorkers || 3); i++) {
4479
4566
  const worker = await TaskWorker.create({
4480
4567
  taskBroker,
4481
4568
  actionRegistry,
@@ -4507,10 +4594,10 @@ async function createRouter(options) {
4507
4594
  async (req, res) => {
4508
4595
  var _a, _b;
4509
4596
  const { namespace, kind, name } = req.params;
4510
- const { token } = parseBearerToken({
4511
- header: req.headers.authorization,
4512
- logger
4597
+ const userIdentity = await identity.getIdentity({
4598
+ request: req
4513
4599
  });
4600
+ const token = userIdentity == null ? void 0 : userIdentity.token;
4514
4601
  const template = await findTemplate({
4515
4602
  catalogApi: catalogClient,
4516
4603
  entityRef: { kind, namespace, name },
@@ -4551,10 +4638,11 @@ async function createRouter(options) {
4551
4638
  const { kind, namespace, name } = catalogModel.parseEntityRef(templateRef, {
4552
4639
  defaultKind: "template"
4553
4640
  });
4554
- const { token, entityRef: userEntityRef } = parseBearerToken({
4555
- header: req.headers.authorization,
4556
- logger
4641
+ const callerIdentity = await identity.getIdentity({
4642
+ request: req
4557
4643
  });
4644
+ const token = callerIdentity == null ? void 0 : callerIdentity.token;
4645
+ const userEntityRef = callerIdentity == null ? void 0 : callerIdentity.identity.userEntityRef;
4558
4646
  const userEntity = userEntityRef ? await catalogClient.getEntityByRef(userEntityRef, { token }) : void 0;
4559
4647
  let auditLog = `Scaffolding task for ${templateRef}`;
4560
4648
  if (userEntityRef) {
@@ -4703,7 +4791,7 @@ data: ${JSON.stringify(event)}
4703
4791
  clearTimeout(timeout);
4704
4792
  });
4705
4793
  }).post("/v2/dry-run", async (req, res) => {
4706
- var _a, _b, _c;
4794
+ var _a, _b, _c, _d;
4707
4795
  const bodySchema = zod.z.object({
4708
4796
  template: zod.z.unknown(),
4709
4797
  values: zod.z.record(zod.z.unknown()),
@@ -4719,11 +4807,10 @@ data: ${JSON.stringify(event)}
4719
4807
  if (!await pluginScaffolderCommon.templateEntityV1beta3Validator.check(template)) {
4720
4808
  throw new errors.InputError("Input template is not a template");
4721
4809
  }
4722
- const { token } = parseBearerToken({
4723
- header: req.headers.authorization,
4724
- logger
4725
- });
4726
- for (const parameters of [(_a = template.spec.parameters) != null ? _a : []].flat()) {
4810
+ const token = (_a = await identity.getIdentity({
4811
+ request: req
4812
+ })) == null ? void 0 : _a.token;
4813
+ for (const parameters of [(_b = template.spec.parameters) != null ? _b : []].flat()) {
4727
4814
  const result2 = jsonschema.validate(body.values, parameters);
4728
4815
  if (!result2.valid) {
4729
4816
  res.status(400).json({ errors: result2.errors });
@@ -4742,10 +4829,10 @@ data: ${JSON.stringify(event)}
4742
4829
  spec: {
4743
4830
  apiVersion: template.apiVersion,
4744
4831
  steps,
4745
- output: (_b = template.spec.output) != null ? _b : {},
4832
+ output: (_c = template.spec.output) != null ? _c : {},
4746
4833
  parameters: body.values
4747
4834
  },
4748
- directoryContents: ((_c = body.directoryContents) != null ? _c : []).map((file) => ({
4835
+ directoryContents: ((_d = body.directoryContents) != null ? _d : []).map((file) => ({
4749
4836
  path: file.path,
4750
4837
  content: Buffer.from(file.base64Content, "base64")
4751
4838
  })),
@@ -4769,37 +4856,6 @@ data: ${JSON.stringify(event)}
4769
4856
  app.use("/", router);
4770
4857
  return app;
4771
4858
  }
4772
- function parseBearerToken({
4773
- header,
4774
- logger
4775
- }) {
4776
- var _a;
4777
- if (!header) {
4778
- return {};
4779
- }
4780
- try {
4781
- const token = (_a = header.match(/^Bearer\s(\S+\.\S+\.\S+)$/i)) == null ? void 0 : _a[1];
4782
- if (!token) {
4783
- throw new TypeError("Expected Bearer with JWT");
4784
- }
4785
- const [_header, rawPayload, _signature] = token.split(".");
4786
- const payload = JSON.parse(
4787
- Buffer.from(rawPayload, "base64").toString()
4788
- );
4789
- if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
4790
- throw new TypeError("Malformed JWT payload");
4791
- }
4792
- const sub = payload.sub;
4793
- if (typeof sub !== "string") {
4794
- throw new TypeError("Expected string sub claim");
4795
- }
4796
- catalogModel.parseEntityRef(sub);
4797
- return { entityRef: sub, token };
4798
- } catch (e) {
4799
- logger.error(`Invalid authorization header: ${errors.stringifyError(e)}`);
4800
- return {};
4801
- }
4802
- }
4803
4859
 
4804
4860
  class ScaffolderEntitiesProcessor {
4805
4861
  constructor() {
@@ -4869,6 +4925,94 @@ const scaffolderCatalogModule = backendPluginApi.createBackendModule({
4869
4925
  }
4870
4926
  });
4871
4927
 
4928
+ var __accessCheck = (obj, member, msg) => {
4929
+ if (!member.has(obj))
4930
+ throw TypeError("Cannot " + msg);
4931
+ };
4932
+ var __privateGet = (obj, member, getter) => {
4933
+ __accessCheck(obj, member, "read from private field");
4934
+ return getter ? getter.call(obj) : member.get(obj);
4935
+ };
4936
+ var __privateAdd = (obj, member, value) => {
4937
+ if (member.has(obj))
4938
+ throw TypeError("Cannot add the same private member more than once");
4939
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
4940
+ };
4941
+ var _actions;
4942
+ class ScaffolderActionsExtensionPointImpl {
4943
+ constructor() {
4944
+ __privateAdd(this, _actions, new Array());
4945
+ }
4946
+ addActions(...actions) {
4947
+ __privateGet(this, _actions).push(...actions);
4948
+ }
4949
+ get actions() {
4950
+ return __privateGet(this, _actions);
4951
+ }
4952
+ }
4953
+ _actions = new WeakMap();
4954
+ const scaffolderActionsExtensionPoint = backendPluginApi.createExtensionPoint({
4955
+ id: "scaffolder.actions"
4956
+ });
4957
+ const scaffolderPlugin = backendPluginApi.createBackendPlugin({
4958
+ id: "scaffolder",
4959
+ register(env, options) {
4960
+ const actionsExtensions = new ScaffolderActionsExtensionPointImpl();
4961
+ env.registerExtensionPoint(
4962
+ scaffolderActionsExtensionPoint,
4963
+ actionsExtensions
4964
+ );
4965
+ env.registerInit({
4966
+ deps: {
4967
+ logger: backendPluginApi.loggerServiceRef,
4968
+ config: backendPluginApi.configServiceRef,
4969
+ reader: backendPluginApi.urlReaderServiceRef,
4970
+ permissions: backendPluginApi.permissionsServiceRef,
4971
+ database: backendPluginApi.databaseServiceRef,
4972
+ httpRouter: backendPluginApi.httpRouterServiceRef,
4973
+ catalogClient: pluginCatalogNode.catalogServiceRef
4974
+ },
4975
+ async init({
4976
+ logger,
4977
+ config,
4978
+ reader,
4979
+ database,
4980
+ httpRouter,
4981
+ catalogClient
4982
+ }) {
4983
+ const { additionalTemplateFilters, taskBroker, taskWorkers } = options;
4984
+ const log = backendPluginApi.loggerToWinstonLogger(logger);
4985
+ const actions = options.actions || [
4986
+ ...actionsExtensions.actions,
4987
+ ...createBuiltinActions({
4988
+ integrations: integration.ScmIntegrations.fromConfig(config),
4989
+ catalogClient,
4990
+ reader,
4991
+ config,
4992
+ additionalTemplateFilters
4993
+ })
4994
+ ];
4995
+ const actionIds = actions.map((action) => action.id).join(", ");
4996
+ log.info(
4997
+ `Starting scaffolder with the following actions enabled ${actionIds}`
4998
+ );
4999
+ const router = await createRouter({
5000
+ logger: log,
5001
+ config,
5002
+ database,
5003
+ catalogClient,
5004
+ reader,
5005
+ actions,
5006
+ taskBroker,
5007
+ taskWorkers,
5008
+ additionalTemplateFilters
5009
+ });
5010
+ httpRouter.use(router);
5011
+ }
5012
+ });
5013
+ }
5014
+ });
5015
+
4872
5016
  exports.DatabaseTaskStore = DatabaseTaskStore;
4873
5017
  exports.ScaffolderEntitiesProcessor = ScaffolderEntitiesProcessor;
4874
5018
  exports.TaskManager = TaskManager;
@@ -4902,4 +5046,5 @@ exports.createTemplateAction = createTemplateAction;
4902
5046
  exports.executeShellCommand = executeShellCommand;
4903
5047
  exports.fetchContents = fetchContents;
4904
5048
  exports.scaffolderCatalogModule = scaffolderCatalogModule;
5049
+ exports.scaffolderPlugin = scaffolderPlugin;
4905
5050
  //# sourceMappingURL=index.cjs.js.map