feat: add AIResource catalog entity kind as opt-in module
Introduces @backstage/plugin-catalog-backend-module-ai-resource-entity-model, a new backend module that registers the AIResource kind with the catalog. Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-ai-resource-entity-model': minor
|
||||
---
|
||||
|
||||
Introduced the `AIResource` catalog entity kind as an opt-in backend module. Install this module to register support for `AIResource` entities in your catalog, used to represent contextual information consumed by AI coding tools such as skills and rules.
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-catalog-backend-module-ai-resource-entity-model
|
||||
title: '@backstage/plugin-catalog-backend-module-ai-resource-entity-model'
|
||||
description: Adds support for the AIResource entity kind to the catalog backend plugin.
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-backend-plugin-module
|
||||
owner: maintainers
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog-backend-module-ai-resource-entity-model",
|
||||
"version": "0.1.0",
|
||||
"description": "Adds support for the AIResource entity kind to the catalog backend plugin.",
|
||||
"backstage": {
|
||||
"role": "backend-plugin-module",
|
||||
"pluginId": "catalog",
|
||||
"pluginPackage": "@backstage/plugin-catalog-backend"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/catalog-backend-module-ai-resource-entity-model"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./alpha": "./src/alpha.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"alpha": [
|
||||
"src/alpha.ts"
|
||||
],
|
||||
"package.json": [
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"start": "backstage-cli package start",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/plugin-catalog-node": "workspace:^",
|
||||
"@backstage/types": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
## API Report File for "@backstage/plugin-catalog-backend-module-ai-resource-entity-model"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { CatalogModelLayer } from '@backstage/catalog-model/alpha';
|
||||
|
||||
// @alpha
|
||||
export const aiResourceEntityModel: CatalogModelLayer;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
## API Report File for "@backstage/plugin-catalog-backend-module-ai-resource-entity-model"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { KindValidator } from '@backstage/catalog-model';
|
||||
|
||||
// @public
|
||||
export interface AIResourceEntityV1alpha1 extends Entity {
|
||||
// (undocumented)
|
||||
apiVersion: 'backstage.io/v1alpha1';
|
||||
// (undocumented)
|
||||
kind: 'AIResource';
|
||||
// (undocumented)
|
||||
spec: {
|
||||
type: string;
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// @public
|
||||
export const aiResourceEntityV1alpha1Validator: KindValidator;
|
||||
|
||||
// @public
|
||||
const catalogModuleAIResourceEntityModel: BackendFeature;
|
||||
export default catalogModuleAIResourceEntityModel;
|
||||
|
||||
// @public
|
||||
export const isAIResourceEntity: (
|
||||
entity: Entity,
|
||||
) => entity is AIResourceEntityV1alpha1;
|
||||
```
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { Entity } from '@backstage/catalog-model';
|
||||
import {
|
||||
type AIResourceEntityV1alpha1,
|
||||
aiResourceEntityV1alpha1Validator as validator,
|
||||
isAIResourceEntity,
|
||||
} from './AIResourceEntityV1alpha1';
|
||||
|
||||
describe('AIResourceV1alpha1Validator', () => {
|
||||
let entity: AIResourceEntityV1alpha1;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'AIResource',
|
||||
metadata: {
|
||||
name: 'frontend-design',
|
||||
},
|
||||
spec: {
|
||||
type: 'skill',
|
||||
lifecycle: 'production',
|
||||
owner: 'ai-platform-team',
|
||||
system: 'ai-tooling',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('accepts valid data', async () => {
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('ignores unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('ignores unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('rejects missing type', async () => {
|
||||
delete (entity as any).spec.type;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects wrong type', async () => {
|
||||
(entity as any).spec.type = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects empty type', async () => {
|
||||
(entity as any).spec.type = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
});
|
||||
|
||||
it('rejects missing lifecycle', async () => {
|
||||
delete (entity as any).spec.lifecycle;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects wrong lifecycle', async () => {
|
||||
(entity as any).spec.lifecycle = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects empty lifecycle', async () => {
|
||||
(entity as any).spec.lifecycle = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/lifecycle/);
|
||||
});
|
||||
|
||||
it('rejects missing owner', async () => {
|
||||
delete (entity as any).spec.owner;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects wrong owner', async () => {
|
||||
(entity as any).spec.owner = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('rejects empty owner', async () => {
|
||||
(entity as any).spec.owner = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/owner/);
|
||||
});
|
||||
|
||||
it('accepts missing system', async () => {
|
||||
delete (entity as any).spec.system;
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong system', async () => {
|
||||
(entity as any).spec.system = 7;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
|
||||
it('rejects empty system', async () => {
|
||||
(entity as any).spec.system = '';
|
||||
await expect(validator.check(entity)).rejects.toThrow(/system/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAIResourceEntity', () => {
|
||||
it('returns true for a valid AIResource entity', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'AIResource',
|
||||
metadata: { name: 'test' },
|
||||
};
|
||||
expect(isAIResourceEntity(entity)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for a different kind', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: { name: 'test' },
|
||||
};
|
||||
expect(isAIResourceEntity(entity)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for a different apiVersion', () => {
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1beta1',
|
||||
kind: 'AIResource',
|
||||
metadata: { name: 'test' },
|
||||
};
|
||||
expect(isAIResourceEntity(entity)).toBe(false);
|
||||
});
|
||||
});
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
type Entity,
|
||||
entityKindSchemaValidator,
|
||||
type KindValidator,
|
||||
} from '@backstage/catalog-model';
|
||||
import { createCatalogModelLayer } from '@backstage/catalog-model/alpha';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import jsonSchema from './schema/AIResource.v1alpha1.schema.json';
|
||||
|
||||
/**
|
||||
* Backstage catalog AIResource kind Entity. Represents contextual information
|
||||
* consumed by AI coding tools, such as skills and rules.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AIResourceEntityV1alpha1 extends Entity {
|
||||
apiVersion: 'backstage.io/v1alpha1';
|
||||
kind: 'AIResource';
|
||||
spec: {
|
||||
type: string;
|
||||
lifecycle: string;
|
||||
owner: string;
|
||||
system?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const validator = entityKindSchemaValidator(jsonSchema);
|
||||
|
||||
/**
|
||||
* Entity data validator for {@link AIResourceEntityV1alpha1}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const aiResourceEntityV1alpha1Validator: KindValidator = {
|
||||
async check(data: Entity) {
|
||||
return validator(data) === data;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Type guard for {@link AIResourceEntityV1alpha1}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const isAIResourceEntity = (
|
||||
entity: Entity,
|
||||
): entity is AIResourceEntityV1alpha1 =>
|
||||
entity.apiVersion === 'backstage.io/v1alpha1' && entity.kind === 'AIResource';
|
||||
|
||||
/**
|
||||
* Extends the catalog model with the AIResource kind.
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export const aiResourceEntityModel = createCatalogModelLayer({
|
||||
layerId: 'catalog.backstage.io/kind-ai-resource',
|
||||
builder: model => {
|
||||
model.addKind({
|
||||
group: 'backstage.io',
|
||||
names: {
|
||||
kind: 'AIResource',
|
||||
singular: 'airesource',
|
||||
plural: 'airesources',
|
||||
},
|
||||
description:
|
||||
'An AI resource represents contextual information consumed by AI coding tools, such as skills and rules.',
|
||||
versions: [
|
||||
{
|
||||
name: 'v1alpha1',
|
||||
relationFields: [
|
||||
{
|
||||
selector: { path: 'spec.owner' },
|
||||
relation: 'ownedBy',
|
||||
defaultKind: 'Group',
|
||||
defaultNamespace: 'inherit',
|
||||
allowedKinds: ['Group', 'User'],
|
||||
},
|
||||
{
|
||||
selector: { path: 'spec.system' },
|
||||
relation: 'partOf',
|
||||
defaultKind: 'System',
|
||||
defaultNamespace: 'inherit',
|
||||
},
|
||||
],
|
||||
schema: {
|
||||
jsonSchema: jsonSchema as JsonObject,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { aiResourceEntityModel } from './AIResourceEntityV1alpha1';
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds support for the AIResource entity kind to the catalog backend plugin.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export type { AIResourceEntityV1alpha1 } from './AIResourceEntityV1alpha1';
|
||||
export {
|
||||
aiResourceEntityV1alpha1Validator,
|
||||
isAIResourceEntity,
|
||||
} from './AIResourceEntityV1alpha1';
|
||||
export { catalogModuleAIResourceEntityModel as default } from './module';
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { startTestBackend } from '@backstage/backend-test-utils';
|
||||
import { catalogModelExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
|
||||
import { catalogModuleAIResourceEntityModel } from './module';
|
||||
|
||||
describe('catalogModuleAIResourceEntityModel', () => {
|
||||
it('should register the model source', async () => {
|
||||
const extensionPoint = {
|
||||
setFieldValidators: jest.fn(),
|
||||
setEntityDataParser: jest.fn(),
|
||||
addModelSource: jest.fn(),
|
||||
};
|
||||
|
||||
await startTestBackend({
|
||||
extensionPoints: [[catalogModelExtensionPoint, extensionPoint]],
|
||||
features: [catalogModuleAIResourceEntityModel],
|
||||
});
|
||||
|
||||
expect(extensionPoint.addModelSource).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createBackendModule } from '@backstage/backend-plugin-api';
|
||||
import { CatalogModelSources } from '@backstage/catalog-model/alpha';
|
||||
import { catalogModelExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
|
||||
import { aiResourceEntityModel } from './AIResourceEntityV1alpha1';
|
||||
|
||||
/**
|
||||
* Registers support for the AIResource entity kind in the catalog.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const catalogModuleAIResourceEntityModel = createBackendModule({
|
||||
pluginId: 'catalog',
|
||||
moduleId: 'ai-resource-entity-model',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {
|
||||
model: catalogModelExtensionPoint,
|
||||
},
|
||||
async init({ model }) {
|
||||
model.addModelSource(
|
||||
CatalogModelSources.static([aiResourceEntityModel]),
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "AIResourceV1alpha1",
|
||||
"description": "An AI resource represents contextual information consumed by AI coding tools, such as skills and rules.",
|
||||
"examples": [
|
||||
{
|
||||
"apiVersion": "backstage.io/v1alpha1",
|
||||
"kind": "AIResource",
|
||||
"metadata": {
|
||||
"name": "frontend-design",
|
||||
"description": "Skill for creating production-grade frontend interfaces"
|
||||
},
|
||||
"spec": {
|
||||
"type": "skill",
|
||||
"lifecycle": "production",
|
||||
"owner": "ai-platform-team",
|
||||
"system": "ai-tooling"
|
||||
}
|
||||
}
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "Entity"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["spec"],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"enum": ["backstage.io/v1alpha1"]
|
||||
},
|
||||
"kind": {
|
||||
"enum": ["AIResource"]
|
||||
},
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"required": ["type", "lifecycle", "owner"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "The type of AI resource.",
|
||||
"examples": ["skill", "rule"],
|
||||
"minLength": 1
|
||||
},
|
||||
"lifecycle": {
|
||||
"type": "string",
|
||||
"description": "The lifecycle state of the AI resource.",
|
||||
"examples": ["experimental", "production", "deprecated"],
|
||||
"minLength": 1
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"description": "An entity reference to the owner of the AI resource.",
|
||||
"examples": ["ai-platform-team", "user:john.johnson"],
|
||||
"minLength": 1
|
||||
},
|
||||
"system": {
|
||||
"type": "string",
|
||||
"description": "An entity reference to the system that the AI resource belongs to.",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -4759,6 +4759,19 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-catalog-backend-module-ai-resource-entity-model@workspace:plugins/catalog-backend-module-ai-resource-entity-model":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-catalog-backend-module-ai-resource-entity-model@workspace:plugins/catalog-backend-module-ai-resource-entity-model"
|
||||
dependencies:
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/catalog-model": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/plugin-catalog-node": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-catalog-backend-module-aws@workspace:plugins/catalog-backend-module-aws":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-catalog-backend-module-aws@workspace:plugins/catalog-backend-module-aws"
|
||||
|
||||
Reference in New Issue
Block a user