@nospt/plugin-dev-ai-hub-common 1.1.3 → 1.2.0-rc2
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/README.md +3 -1
- package/config.schema.json +34 -0
- package/dist/curation.cjs.js +16 -0
- package/dist/curation.cjs.js.map +1 -0
- package/dist/curation.esm.js +12 -0
- package/dist/curation.esm.js.map +1 -0
- package/dist/index.cjs.js +18 -0
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +279 -23
- package/dist/index.esm.js +4 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/permissions.cjs.js +21 -0
- package/dist/permissions.cjs.js.map +1 -0
- package/dist/permissions.esm.js +17 -0
- package/dist/permissions.esm.js.map +1 -0
- package/dist/resources.cjs.js +49 -2
- package/dist/resources.cjs.js.map +1 -1
- package/dist/resources.esm.js +48 -3
- package/dist/resources.esm.js.map +1 -1
- package/dist/sorting.cjs.js +71 -0
- package/dist/sorting.cjs.js.map +1 -0
- package/dist/sorting.esm.js +63 -0
- package/dist/sorting.esm.js.map +1 -0
- package/dist/telemetry.cjs.js.map +1 -1
- package/dist/telemetry.esm.js.map +1 -1
- package/dist/toResourceSummary.cjs.js +21 -5
- package/dist/toResourceSummary.cjs.js.map +1 -1
- package/dist/toResourceSummary.esm.js +22 -6
- package/dist/toResourceSummary.esm.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -35,16 +35,18 @@ interface ResourceSummary {
|
|
|
35
35
|
childCount?: number; // = children.length
|
|
36
36
|
helpText?: string;
|
|
37
37
|
annotations: Record<string, string>;
|
|
38
|
+
recommended?: boolean; // curation overlay, set by the backend router — never by toResourceSummary
|
|
38
39
|
}
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
`toResourceSummary(entity)` maps one `Entity`; `toResourceSummaries(entities)` maps a whole catalog read and resolves containment in both directions (ADR-0015). Both are exported here so the backend router and the test fixtures cannot disagree about them.
|
|
42
|
+
`toResourceSummary(entity)` maps one `Entity`; `toResourceSummaries(entities)` maps a whole catalog read and resolves containment in both directions (ADR-0015). Both are exported here so the backend router and the test fixtures cannot disagree about them. Neither ever sets `recommended` — that is plugin-owned curation state, merged onto the summary by the backend router after mapping (ADR-0017), not derived from the catalog entity.
|
|
42
43
|
|
|
43
44
|
## Also in here
|
|
44
45
|
|
|
45
46
|
- **The `ResourceType` vocabulary** — `skill`, `agent`, `hook`, `mcp-config`, `plugin`, `marketplace` — and the rules derived from it: which body shape a type renders as (`mcp-config` is JSON, the rest markdown), whether its body is a downloadable artifact or merely a pointer, and its per-framework install paths.
|
|
46
47
|
- **Framework tokens and normalisation**, so `claude-code` means the same thing on both sides.
|
|
47
48
|
- **Telemetry schemas** for the event contract.
|
|
49
|
+
- **The permission contract** — `devAiHubBackofficeReadPermission` (read the backoffice) and `devAiHubBackofficeCuratePermission` (write curation state), plus the `RecommendationInput` schema validated on the write path.
|
|
48
50
|
|
|
49
51
|
## Full documentation
|
|
50
52
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"devAiHub": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"frameworks": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"custom": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"additionalProperties": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"displayName": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Display label. Defaults to the key when omitted."
|
|
19
|
+
},
|
|
20
|
+
"icon": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Icon shown beside the label. Any browser-loadable source — an absolute URL, an app-served static path, or a data URI. Rendered as an `<img>`, so it is not theme-inverted the way the built-in monochrome brand marks are: supply an image that reads on both light and dark backgrounds. A neutral icon is used when omitted."
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"description": "Frameworks this deployment recognises beyond the ones built into the plugin — an internal assistant, a tool too new or too niche to ship in code. Keyed by the token authors write in an AiResource's `spec.agents` (or the `devaihub.io/compatible-frameworks` annotation); keys are normalised (trimmed, lowercased) before matching.\n\nRegistering a custom framework **activates the allow-list** (`devAiHub.frameworks.allowed`, declared by `@nospt/plugin-dev-ai-hub-backend`) even when `allowed` is omitted: the effective set becomes the plugin's built-in frameworks plus these keys, and any other token is dropped. With `allowed` also set, the effective set is `allowed` plus these keys.\n\nA key that collides with a built-in token or one of its aliases (`claude-code`, `claude`, …) is ignored with a logged warning — built-ins stay authoritative so their install paths cannot be lost.\n\nCustom frameworks are discovery-only: they appear as badges, as \"Works with\" entries and in the AI Tool filter, but never produce a generated install path or launcher, because the plugin knows no filesystem convention for them. The `all` wildcard on a resource never expands into them — a resource must name the token explicitly (ADR-0018).\n\nDeclared here rather than in the backend or frontend plugin's own `config.d.ts`: the backend needs the names to gate the served payload, the frontend needs `displayName`/`icon` to render, and `backstage-cli`'s config schema collector only walks a package's actual `dependencies`/`devDependencies` — since neither plugin package depends on the other, a declaration in just one of them would be invisible to the other. Both already depend on this common package, so declaring it here is what makes one declaration serve both (ADR-0018).",
|
|
27
|
+
"deepVisibility": "frontend"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
|
|
5
|
+
const RECOMMENDED_TAG = "recommended";
|
|
6
|
+
const RecommendationInputSchema = zod.z.object({
|
|
7
|
+
recommended: zod.z.boolean()
|
|
8
|
+
});
|
|
9
|
+
const ExclusionInputSchema = zod.z.object({
|
|
10
|
+
excluded: zod.z.boolean()
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
exports.ExclusionInputSchema = ExclusionInputSchema;
|
|
14
|
+
exports.RECOMMENDED_TAG = RECOMMENDED_TAG;
|
|
15
|
+
exports.RecommendationInputSchema = RecommendationInputSchema;
|
|
16
|
+
//# sourceMappingURL=curation.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curation.cjs.js","sources":["../src/curation.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * The catalog tag that recommends a resource from its own definition\n * (ADR-0020). A resource carrying it is recommended regardless of the\n * `curation` table, and the backoffice cannot un-recommend it — the author\n * removes the tag instead.\n */\nexport const RECOMMENDED_TAG = 'recommended';\n\n/**\n * The write body for `PUT /curation/:ref/recommendation` (ADR-0017).\n *\n * `recommended: false` clears the flag rather than deleting the row, so\n * `curated_by`/`curated_at` still say who last touched this resource even\n * after it is un-recommended — the same \"never forget who curated this\"\n * requirement #68 states for demote and hide.\n */\nexport const RecommendationInputSchema = z.object({\n recommended: z.boolean(),\n});\nexport type RecommendationInput = z.infer<typeof RecommendationInputSchema>;\n\n/**\n * The write body for `PUT /curation/:ref/exclusion` (#68, ADR-0019).\n *\n * `excluded: false` restores the resource to the hub but keeps the row, so\n * `excluded_by`/`excluded_at` still record the most recent exclusion — the\n * same \"never forget who curated this\" requirement that shapes\n * `RecommendationInputSchema`.\n */\nexport const ExclusionInputSchema = z.object({\n excluded: z.boolean(),\n});\nexport type ExclusionInput = z.infer<typeof ExclusionInputSchema>;\n\n/** One resource's curation record, as stored by `CurationStore`. */\nexport interface CurationRecord {\n entityRef: string;\n recommended: boolean;\n curatedBy?: string;\n curatedAt: string;\n /**\n * Excluded resources are dropped from `GET /resources` entirely rather\n * than flagged for the frontend to skip (ADR-0019), so this is only ever\n * observed by the backoffice, which asks for them explicitly.\n */\n excluded: boolean;\n /**\n * Who last changed this resource's exclusion state, and when. Tracked\n * separately from `curatedBy`/`curatedAt` so a later recommendation cannot\n * overwrite the exclusion's attribution. Both are `undefined` until the\n * first exclusion write; while `excluded` is `true` they name whoever\n * excluded it.\n */\n excludedBy?: string;\n excludedAt?: string;\n}\n"],"names":["z"],"mappings":";;;;AAQO,MAAM,eAAA,GAAkB;AAUxB,MAAM,yBAAA,GAA4BA,MAAE,MAAA,CAAO;AAAA,EAChD,WAAA,EAAaA,MAAE,OAAA;AACjB,CAAC;AAWM,MAAM,oBAAA,GAAuBA,MAAE,MAAA,CAAO;AAAA,EAC3C,QAAA,EAAUA,MAAE,OAAA;AACd,CAAC;;;;;;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
const RECOMMENDED_TAG = "recommended";
|
|
4
|
+
const RecommendationInputSchema = z.object({
|
|
5
|
+
recommended: z.boolean()
|
|
6
|
+
});
|
|
7
|
+
const ExclusionInputSchema = z.object({
|
|
8
|
+
excluded: z.boolean()
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export { ExclusionInputSchema, RECOMMENDED_TAG, RecommendationInputSchema };
|
|
12
|
+
//# sourceMappingURL=curation.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curation.esm.js","sources":["../src/curation.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * The catalog tag that recommends a resource from its own definition\n * (ADR-0020). A resource carrying it is recommended regardless of the\n * `curation` table, and the backoffice cannot un-recommend it — the author\n * removes the tag instead.\n */\nexport const RECOMMENDED_TAG = 'recommended';\n\n/**\n * The write body for `PUT /curation/:ref/recommendation` (ADR-0017).\n *\n * `recommended: false` clears the flag rather than deleting the row, so\n * `curated_by`/`curated_at` still say who last touched this resource even\n * after it is un-recommended — the same \"never forget who curated this\"\n * requirement #68 states for demote and hide.\n */\nexport const RecommendationInputSchema = z.object({\n recommended: z.boolean(),\n});\nexport type RecommendationInput = z.infer<typeof RecommendationInputSchema>;\n\n/**\n * The write body for `PUT /curation/:ref/exclusion` (#68, ADR-0019).\n *\n * `excluded: false` restores the resource to the hub but keeps the row, so\n * `excluded_by`/`excluded_at` still record the most recent exclusion — the\n * same \"never forget who curated this\" requirement that shapes\n * `RecommendationInputSchema`.\n */\nexport const ExclusionInputSchema = z.object({\n excluded: z.boolean(),\n});\nexport type ExclusionInput = z.infer<typeof ExclusionInputSchema>;\n\n/** One resource's curation record, as stored by `CurationStore`. */\nexport interface CurationRecord {\n entityRef: string;\n recommended: boolean;\n curatedBy?: string;\n curatedAt: string;\n /**\n * Excluded resources are dropped from `GET /resources` entirely rather\n * than flagged for the frontend to skip (ADR-0019), so this is only ever\n * observed by the backoffice, which asks for them explicitly.\n */\n excluded: boolean;\n /**\n * Who last changed this resource's exclusion state, and when. Tracked\n * separately from `curatedBy`/`curatedAt` so a later recommendation cannot\n * overwrite the exclusion's attribution. Both are `undefined` until the\n * first exclusion write; while `excluded` is `true` they name whoever\n * excluded it.\n */\n excludedBy?: string;\n excludedAt?: string;\n}\n"],"names":[],"mappings":";;AAQO,MAAM,eAAA,GAAkB;AAUxB,MAAM,yBAAA,GAA4B,EAAE,MAAA,CAAO;AAAA,EAChD,WAAA,EAAa,EAAE,OAAA;AACjB,CAAC;AAWM,MAAM,oBAAA,GAAuB,EAAE,MAAA,CAAO;AAAA,EAC3C,QAAA,EAAU,EAAE,OAAA;AACd,CAAC;;;;"}
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var curation = require('./curation.cjs.js');
|
|
4
|
+
var permissions = require('./permissions.cjs.js');
|
|
3
5
|
var resources = require('./resources.cjs.js');
|
|
6
|
+
var sorting = require('./sorting.cjs.js');
|
|
4
7
|
var telemetry = require('./telemetry.cjs.js');
|
|
5
8
|
var toResourceSummary = require('./toResourceSummary.cjs.js');
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
|
|
12
|
+
exports.ExclusionInputSchema = curation.ExclusionInputSchema;
|
|
13
|
+
exports.RECOMMENDED_TAG = curation.RECOMMENDED_TAG;
|
|
14
|
+
exports.RecommendationInputSchema = curation.RecommendationInputSchema;
|
|
15
|
+
exports.devAiHubBackofficeCuratePermission = permissions.devAiHubBackofficeCuratePermission;
|
|
16
|
+
exports.devAiHubBackofficeReadPermission = permissions.devAiHubBackofficeReadPermission;
|
|
17
|
+
exports.devAiHubPermissions = permissions.devAiHubPermissions;
|
|
9
18
|
exports.AGENT_LINK_CAPABLE_FRAMEWORKS = resources.AGENT_LINK_CAPABLE_FRAMEWORKS;
|
|
10
19
|
exports.ANNOTATION_COMPATIBLE_FRAMEWORKS = resources.ANNOTATION_COMPATIBLE_FRAMEWORKS;
|
|
11
20
|
exports.ANNOTATION_HELP = resources.ANNOTATION_HELP;
|
|
@@ -37,8 +46,17 @@ exports.hasDownloadableArtifact = resources.hasDownloadableArtifact;
|
|
|
37
46
|
exports.isRenderableContainment = resources.isRenderableContainment;
|
|
38
47
|
exports.isResourceType = resources.isResourceType;
|
|
39
48
|
exports.normalizeFramework = resources.normalizeFramework;
|
|
49
|
+
exports.parseCustomFrameworks = resources.parseCustomFrameworks;
|
|
50
|
+
exports.resolveAllowedFrameworks = resources.resolveAllowedFrameworks;
|
|
40
51
|
exports.resolveCapableFrameworks = resources.resolveCapableFrameworks;
|
|
41
52
|
exports.showsContentSection = resources.showsContentSection;
|
|
53
|
+
exports.DEFAULT_SORT_MODE = sorting.DEFAULT_SORT_MODE;
|
|
54
|
+
exports.NATURAL_SORT_DIRECTION = sorting.NATURAL_SORT_DIRECTION;
|
|
55
|
+
exports.SORT_MODES = sorting.SORT_MODES;
|
|
56
|
+
exports.SortModeEnum = sorting.SortModeEnum;
|
|
57
|
+
exports.isSortMode = sorting.isSortMode;
|
|
58
|
+
exports.sortResources = sorting.sortResources;
|
|
59
|
+
exports.usageScore = sorting.usageScore;
|
|
42
60
|
exports.TelemetryActionEnum = telemetry.TelemetryActionEnum;
|
|
43
61
|
exports.TelemetryEventInputSchema = telemetry.TelemetryEventInputSchema;
|
|
44
62
|
exports.isParseableEntityRef = toResourceSummary.isParseableEntityRef;
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,100 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import * as _backstage_plugin_permission_common from '@backstage/plugin-permission-common';
|
|
2
3
|
import { Entity } from '@backstage/catalog-model';
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The catalog tag that recommends a resource from its own definition
|
|
7
|
+
* (ADR-0020). A resource carrying it is recommended regardless of the
|
|
8
|
+
* `curation` table, and the backoffice cannot un-recommend it — the author
|
|
9
|
+
* removes the tag instead.
|
|
10
|
+
*/
|
|
11
|
+
declare const RECOMMENDED_TAG = "recommended";
|
|
12
|
+
/**
|
|
13
|
+
* The write body for `PUT /curation/:ref/recommendation` (ADR-0017).
|
|
14
|
+
*
|
|
15
|
+
* `recommended: false` clears the flag rather than deleting the row, so
|
|
16
|
+
* `curated_by`/`curated_at` still say who last touched this resource even
|
|
17
|
+
* after it is un-recommended — the same "never forget who curated this"
|
|
18
|
+
* requirement #68 states for demote and hide.
|
|
19
|
+
*/
|
|
20
|
+
declare const RecommendationInputSchema: z.ZodObject<{
|
|
21
|
+
recommended: z.ZodBoolean;
|
|
22
|
+
}, "strip", z.ZodTypeAny, {
|
|
23
|
+
recommended: boolean;
|
|
24
|
+
}, {
|
|
25
|
+
recommended: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
type RecommendationInput = z.infer<typeof RecommendationInputSchema>;
|
|
28
|
+
/**
|
|
29
|
+
* The write body for `PUT /curation/:ref/exclusion` (#68, ADR-0019).
|
|
30
|
+
*
|
|
31
|
+
* `excluded: false` restores the resource to the hub but keeps the row, so
|
|
32
|
+
* `excluded_by`/`excluded_at` still record the most recent exclusion — the
|
|
33
|
+
* same "never forget who curated this" requirement that shapes
|
|
34
|
+
* `RecommendationInputSchema`.
|
|
35
|
+
*/
|
|
36
|
+
declare const ExclusionInputSchema: z.ZodObject<{
|
|
37
|
+
excluded: z.ZodBoolean;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
excluded: boolean;
|
|
40
|
+
}, {
|
|
41
|
+
excluded: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
type ExclusionInput = z.infer<typeof ExclusionInputSchema>;
|
|
44
|
+
/** One resource's curation record, as stored by `CurationStore`. */
|
|
45
|
+
interface CurationRecord {
|
|
46
|
+
entityRef: string;
|
|
47
|
+
recommended: boolean;
|
|
48
|
+
curatedBy?: string;
|
|
49
|
+
curatedAt: string;
|
|
50
|
+
/**
|
|
51
|
+
* Excluded resources are dropped from `GET /resources` entirely rather
|
|
52
|
+
* than flagged for the frontend to skip (ADR-0019), so this is only ever
|
|
53
|
+
* observed by the backoffice, which asks for them explicitly.
|
|
54
|
+
*/
|
|
55
|
+
excluded: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Who last changed this resource's exclusion state, and when. Tracked
|
|
58
|
+
* separately from `curatedBy`/`curatedAt` so a later recommendation cannot
|
|
59
|
+
* overwrite the exclusion's attribution. Both are `undefined` until the
|
|
60
|
+
* first exclusion write; while `excluded` is `true` they name whoever
|
|
61
|
+
* excluded it.
|
|
62
|
+
*/
|
|
63
|
+
excludedBy?: string;
|
|
64
|
+
excludedAt?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Opening the Dev AI Hub backoffice (ADR-0016).
|
|
69
|
+
*
|
|
70
|
+
* Held in `-common` because both sides need the same identity: the backend
|
|
71
|
+
* registers it so the Backstage RBAC plugin can list and grant it, and the
|
|
72
|
+
* frontend checks it to decide whether the surface exists for this user at all.
|
|
73
|
+
*
|
|
74
|
+
* Read-only on purpose. Every governance capability that writes — curation
|
|
75
|
+
* (#68, ADR-0017), tool visibility (#85) — gets its own permission rather
|
|
76
|
+
* than riding on this one, so "can see the backoffice" never silently
|
|
77
|
+
* becomes "can change the catalog's presentation".
|
|
78
|
+
*/
|
|
79
|
+
declare const devAiHubBackofficeReadPermission: _backstage_plugin_permission_common.BasicPermission;
|
|
80
|
+
/**
|
|
81
|
+
* Changing a resource's curation state — currently just "recommended"
|
|
82
|
+
* (ADR-0017), later also demote/hide (#68) — from the backoffice.
|
|
83
|
+
*
|
|
84
|
+
* Scoped to the capability, not to one action: a future demote or hide
|
|
85
|
+
* reuses this permission rather than minting one each, the same way granting
|
|
86
|
+
* someone curation trust once covers whichever curation actions the backoffice
|
|
87
|
+
* exposes. Separate from `dev-ai-hub.backoffice.read` so seeing the backoffice
|
|
88
|
+
* never implies being able to write to it.
|
|
89
|
+
*/
|
|
90
|
+
declare const devAiHubBackofficeCuratePermission: _backstage_plugin_permission_common.BasicPermission;
|
|
91
|
+
/**
|
|
92
|
+
* Every permission this plugin owns, for bulk registration with the backend's
|
|
93
|
+
* permissions registry. New permissions must be added here or the RBAC plugin
|
|
94
|
+
* will not offer them.
|
|
95
|
+
*/
|
|
96
|
+
declare const devAiHubPermissions: _backstage_plugin_permission_common.BasicPermission[];
|
|
97
|
+
|
|
4
98
|
/**
|
|
5
99
|
* The v2 read contract: AiResource vocabulary, the flat ResourceSummary
|
|
6
100
|
* returned by the backend, and the shared framework resolver (ADR-0003).
|
|
@@ -53,16 +147,74 @@ type FrameworkToken = (typeof KNOWN_FRAMEWORKS)[number];
|
|
|
53
147
|
declare function normalizeFramework(token: string): string;
|
|
54
148
|
/**
|
|
55
149
|
* Narrow a resource's frameworks to a deployment-level allow-list
|
|
56
|
-
* (`devAiHub.frameworks.allowed
|
|
57
|
-
* `undefined`) permits every framework; an
|
|
58
|
-
* so a deployment can suppress framework
|
|
59
|
-
* normalised (aliases resolved) before
|
|
150
|
+
* (`devAiHub.frameworks.allowed`, unioned with the `custom` keys). Omitting
|
|
151
|
+
* the config key (`allowed` is `undefined`) permits every framework; an
|
|
152
|
+
* explicit empty list permits none, so a deployment can suppress framework
|
|
153
|
+
* exposure entirely. Both sides are normalised (aliases resolved) before
|
|
154
|
+
* comparison.
|
|
60
155
|
*
|
|
61
156
|
* The `all` wildcard is expanded to the allow-list itself before filtering, so
|
|
62
157
|
* a resource that declares `all` degrades to "every permitted host" rather
|
|
63
|
-
* than disappearing entirely
|
|
158
|
+
* than disappearing entirely — minus `custom`, which `all` never reaches
|
|
159
|
+
* (ADR-0018): the wildcard means "every host the plugin knows", and claiming
|
|
160
|
+
* an internal assistant on every universal resource would be an assertion the
|
|
161
|
+
* author never made.
|
|
64
162
|
*/
|
|
65
|
-
declare function filterAllowedFrameworks(frameworks: string[], allowed: string[] | undefined): string[];
|
|
163
|
+
declare function filterAllowedFrameworks(frameworks: string[], allowed: string[] | undefined, custom?: readonly string[]): string[];
|
|
164
|
+
/**
|
|
165
|
+
* A deployment-registered framework (`devAiHub.frameworks.custom`) — a tool
|
|
166
|
+
* the plugin does not ship knowledge of, named by the token its authors write
|
|
167
|
+
* in `spec.agents` (ADR-0018).
|
|
168
|
+
*/
|
|
169
|
+
interface CustomFramework {
|
|
170
|
+
/** Normalised token matched against a resource's declared frameworks. */
|
|
171
|
+
name: string;
|
|
172
|
+
/** Display label. Falls back to `name` when the config omits `displayName`. */
|
|
173
|
+
displayName: string;
|
|
174
|
+
/** Browser-loadable icon source; `undefined` selects the neutral fallback. */
|
|
175
|
+
icon?: string;
|
|
176
|
+
}
|
|
177
|
+
/** The raw per-token shape of `devAiHub.frameworks.custom`. */
|
|
178
|
+
interface CustomFrameworkConfig {
|
|
179
|
+
displayName?: string;
|
|
180
|
+
icon?: string;
|
|
181
|
+
}
|
|
182
|
+
interface ParsedCustomFrameworks {
|
|
183
|
+
frameworks: CustomFramework[];
|
|
184
|
+
/**
|
|
185
|
+
* Human-readable reasons entries were dropped. The backend logs these; the
|
|
186
|
+
* frontend ignores them, but both run the same parse so they can never
|
|
187
|
+
* disagree about which tokens exist.
|
|
188
|
+
*/
|
|
189
|
+
warnings: string[];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Parse `devAiHub.frameworks.custom` into the registry both sides read.
|
|
193
|
+
*
|
|
194
|
+
* Entries are dropped rather than rejected wholesale — one malformed entry
|
|
195
|
+
* must not take out a deployment's whole framework registry — and each drop
|
|
196
|
+
* carries a reason:
|
|
197
|
+
*
|
|
198
|
+
* - a key that normalises to empty has nothing to match against;
|
|
199
|
+
* - a key that collides with a built-in token or alias (`claude-code`,
|
|
200
|
+
* `claude`) is ignored so built-ins stay authoritative: custom frameworks
|
|
201
|
+
* are install-suppressed, and letting one shadow `claude-code` would
|
|
202
|
+
* silently strip Claude's install paths;
|
|
203
|
+
* - a duplicate after normalisation keeps the first entry, since the second
|
|
204
|
+
* would otherwise win non-deterministically.
|
|
205
|
+
*/
|
|
206
|
+
declare function parseCustomFrameworks(raw: unknown): ParsedCustomFrameworks;
|
|
207
|
+
/**
|
|
208
|
+
* The allow-list actually applied, once `custom` is taken into account
|
|
209
|
+
* (ADR-0018). Registering a custom framework activates the gate even when
|
|
210
|
+
* `allowed` is omitted — otherwise the deployment would have registered a
|
|
211
|
+
* token that nothing distinguishes from the unknown tokens beside it.
|
|
212
|
+
*
|
|
213
|
+
* - no custom frameworks → `allowed` unchanged (including `undefined`);
|
|
214
|
+
* - `allowed` set → `allowed` ∪ custom;
|
|
215
|
+
* - `allowed` omitted → every built-in ∪ custom.
|
|
216
|
+
*/
|
|
217
|
+
declare function resolveAllowedFrameworks(allowed: string[] | undefined, custom: readonly string[]): string[] | undefined;
|
|
66
218
|
/**
|
|
67
219
|
* The minimal structural shape of an AiResource entity that the read helpers
|
|
68
220
|
* need. A real `@backstage/catalog-model` `Entity` satisfies it, but keeping
|
|
@@ -133,22 +285,24 @@ interface ResourceSummary {
|
|
|
133
285
|
sourceLocation?: string;
|
|
134
286
|
frameworks: string[];
|
|
135
287
|
/**
|
|
136
|
-
* True when
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* "author declared nothing"
|
|
140
|
-
* defaults) apart from "
|
|
141
|
-
* install paths and launchers;
|
|
288
|
+
* True when this resource has no generated install path to offer — either
|
|
289
|
+
* an active deployment allow-list (`devAiHub.frameworks.allowed`) left it
|
|
290
|
+
* with no frameworks, or every framework it does have is a custom,
|
|
291
|
+
* discovery-only one (ADR-0018). Lets the UI tell "author declared nothing"
|
|
292
|
+
* (`frameworks: []`, show neutral install defaults) apart from "nothing
|
|
293
|
+
* installable remains" (hide generated install paths and launchers;
|
|
294
|
+
* Copy/Download still work).
|
|
142
295
|
*/
|
|
143
296
|
frameworksRestricted?: boolean;
|
|
144
297
|
/**
|
|
145
298
|
* The framework set that install-path and launcher generation must be
|
|
146
|
-
* scoped to, when it differs from `frameworks`.
|
|
147
|
-
* that declared no frameworks (which otherwise means "every host")
|
|
148
|
-
* active allow-list
|
|
149
|
-
* cannot produce launchers for tools
|
|
150
|
-
*
|
|
151
|
-
*
|
|
299
|
+
* scoped to, when it differs from `frameworks`. Two cases set it: a
|
|
300
|
+
* resource that declared no frameworks (which otherwise means "every host")
|
|
301
|
+
* under an active allow-list, where it holds the allow-list itself so a
|
|
302
|
+
* bare resource cannot produce launchers for disallowed tools; and a
|
|
303
|
+
* resource carrying custom frameworks, where it holds the remainder once
|
|
304
|
+
* those are removed (ADR-0018). Kept separate from `frameworks` so neither
|
|
305
|
+
* inferred set renders as a compatibility badge the author never claimed.
|
|
152
306
|
*/
|
|
153
307
|
installFrameworks?: string[];
|
|
154
308
|
version?: string;
|
|
@@ -170,6 +324,47 @@ interface ResourceSummary {
|
|
|
170
324
|
childCount?: number;
|
|
171
325
|
helpText?: string;
|
|
172
326
|
annotations: Record<string, string>;
|
|
327
|
+
/**
|
|
328
|
+
* True when the resource is recommended from either source: its catalog
|
|
329
|
+
* tags carry `RECOMMENDED_TAG` (set by `toResourceSummary`, ADR-0020), or
|
|
330
|
+
* the backend router overlaid it from the `curation` table (ADR-0017).
|
|
331
|
+
* `undefined` and `false` both mean "not recommended"; only `true` renders
|
|
332
|
+
* the badge.
|
|
333
|
+
*/
|
|
334
|
+
recommended?: boolean;
|
|
335
|
+
/**
|
|
336
|
+
* True when `recommended` comes from the entity's own `RECOMMENDED_TAG`
|
|
337
|
+
* (ADR-0020). Such a recommendation is owned by the resource's author, so
|
|
338
|
+
* the backoffice cannot remove it — only removing the tag can.
|
|
339
|
+
*/
|
|
340
|
+
recommendedByTag?: boolean;
|
|
341
|
+
/**
|
|
342
|
+
* True when a curator recommended this resource in the backoffice — a
|
|
343
|
+
* `curation` row (ADR-0017). Can be true alongside `recommendedByTag`, for
|
|
344
|
+
* a row recorded before the author added the tag; the backoffice then
|
|
345
|
+
* offers to clear it, so that removing the tag really un-recommends.
|
|
346
|
+
*/
|
|
347
|
+
recommendedByCuration?: boolean;
|
|
348
|
+
/**
|
|
349
|
+
* When the plugin first saw this resource, as an ISO timestamp — set by the
|
|
350
|
+
* backend router from its own `resource_first_seen` table (ADR-0021), since
|
|
351
|
+
* the catalog exposes no creation date. `undefined` for a resource that
|
|
352
|
+
* already existed when tracking began; the "Newest" sort ranks those as
|
|
353
|
+
* the oldest.
|
|
354
|
+
*/
|
|
355
|
+
firstSeenAt?: string;
|
|
356
|
+
/**
|
|
357
|
+
* Set by the backend router from the `curation` table (ADR-0019), and only
|
|
358
|
+
* ever present when the caller explicitly asked for excluded resources via
|
|
359
|
+
* `?includeExcluded=true`. The default `GET /resources` response omits
|
|
360
|
+
* excluded resources entirely rather than flagging them, so on the browse
|
|
361
|
+
* page this field is always `undefined`.
|
|
362
|
+
*/
|
|
363
|
+
excluded?: boolean;
|
|
364
|
+
/** Who last excluded this resource, for the backoffice's exclusion list. */
|
|
365
|
+
excludedBy?: string;
|
|
366
|
+
/** When it was last excluded, ISO-8601. */
|
|
367
|
+
excludedAt?: string;
|
|
173
368
|
}
|
|
174
369
|
interface ResourceListResponse {
|
|
175
370
|
items: ResourceSummary[];
|
|
@@ -413,6 +608,64 @@ declare const TelemetryEventInputSchema: z.ZodObject<{
|
|
|
413
608
|
type TelemetryEventInput = z.infer<typeof TelemetryEventInputSchema>;
|
|
414
609
|
/** Raw per-action counts for one resource, as returned by `GET /telemetry/:ref`. */
|
|
415
610
|
type TelemetryCounts = Record<TelemetryAction, number>;
|
|
611
|
+
/**
|
|
612
|
+
* Raw per-action counts for every resource that has recorded at least one
|
|
613
|
+
* telemetry event, keyed by `entityRef`, as returned by `GET /telemetry`
|
|
614
|
+
* (#83 backoffice overview). A resource with no events at all is simply
|
|
615
|
+
* absent from the map — the caller already has the full resource list and
|
|
616
|
+
* can treat a missing key as zero, so the response only carries entries
|
|
617
|
+
* that mean something.
|
|
618
|
+
*/
|
|
619
|
+
type TelemetrySummary = Record<string, TelemetryCounts>;
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* The named sort modes exposed on the browse page (issue #102). `usage` is
|
|
623
|
+
* the composite "most popular" score; `newest` orders by when the plugin
|
|
624
|
+
* first saw each resource (ADR-0021); `name` is the alphabetical fallback
|
|
625
|
+
* every list needs.
|
|
626
|
+
*
|
|
627
|
+
* The raw per-counter modes (installs, downloads, views) and "Recommended
|
|
628
|
+
* first" were dropped: `usage` already folds the counters into one ranking,
|
|
629
|
+
* and recommended resources are pinned in their own section above the grid,
|
|
630
|
+
* so sorting by the flag would only reorder a list it no longer contains.
|
|
631
|
+
*
|
|
632
|
+
* Deliberately no `framework` mode: `frameworks` is a multi-value array per
|
|
633
|
+
* resource, so a resource compatible with several tools has no single sort
|
|
634
|
+
* position — that dimension is a filter (the AI Tool dropdown), not a sort.
|
|
635
|
+
*/
|
|
636
|
+
declare const SortModeEnum: z.ZodEnum<["usage", "newest", "name"]>;
|
|
637
|
+
type SortMode = z.infer<typeof SortModeEnum>;
|
|
638
|
+
declare const SORT_MODES: readonly SortMode[];
|
|
639
|
+
declare function isSortMode(value: unknown): value is SortMode;
|
|
640
|
+
type SortDirection = 'asc' | 'desc';
|
|
641
|
+
/**
|
|
642
|
+
* The default sort mode when no configuration is set and the mode a deployer
|
|
643
|
+
* gets on an unrecognised `devAiHub.ui.defaultSort` value (soft fallback —
|
|
644
|
+
* a typo'd config value must never blank the page).
|
|
645
|
+
*/
|
|
646
|
+
declare const DEFAULT_SORT_MODE: SortMode;
|
|
647
|
+
/**
|
|
648
|
+
* Each mode's "obvious" direction — usage and newest rank highest (most
|
|
649
|
+
* popular, most recent) first, name reads A-Z. The dropdown and direction toggle reset to this whenever the user
|
|
650
|
+
* switches mode.
|
|
651
|
+
*/
|
|
652
|
+
declare const NATURAL_SORT_DIRECTION: Record<SortMode, SortDirection>;
|
|
653
|
+
declare function usageScore(counts: TelemetryCounts | undefined): number;
|
|
654
|
+
/**
|
|
655
|
+
* Sort resources by the given mode and direction. `counts` is the shared
|
|
656
|
+
* page-level telemetry summary map (entityRef -> raw counts) — a resource
|
|
657
|
+
* absent from the map is treated as all-zero, per `TelemetrySummary`'s
|
|
658
|
+
* contract.
|
|
659
|
+
*
|
|
660
|
+
* Every mode tie-breaks on Name (A-Z) so equal-scoring resources — the
|
|
661
|
+
* common case for a catalog with little or no telemetry yet — render in a
|
|
662
|
+
* predictable, stable order rather than arbitrary fetch order. The `name`
|
|
663
|
+
* mode itself has no primary key to break ties on, so it tie-breaks on
|
|
664
|
+
* `entityRef` instead (see `compareNames`).
|
|
665
|
+
*
|
|
666
|
+
* Pure and allocation-light: does not mutate `resources`.
|
|
667
|
+
*/
|
|
668
|
+
declare function sortResources(resources: readonly ResourceSummary[], mode: SortMode, direction: SortDirection, counts: Map<string, TelemetryCounts>): ResourceSummary[];
|
|
416
669
|
|
|
417
670
|
/**
|
|
418
671
|
* Whether a string is a parseable entity ref. The frontend links `owner` to
|
|
@@ -444,13 +697,16 @@ declare function toResourceSummary(entity: Entity): ResourceSummary | undefined;
|
|
|
444
697
|
*
|
|
445
698
|
* `opts.allowedFrameworks` (from `devAiHub.frameworks.allowed`) narrows every
|
|
446
699
|
* summary's `frameworks` to the permitted set — undefined/empty allows all.
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
700
|
+
* `opts.customFrameworks` (the keys of `devAiHub.frameworks.custom`) joins
|
|
701
|
+
* that set and, on its own, activates it (ADR-0018). Applied here so the
|
|
702
|
+
* served payload is the single choke point: badges, the "Works with" section,
|
|
703
|
+
* the framework filter and install-path generation all read the
|
|
704
|
+
* already-filtered list.
|
|
450
705
|
*/
|
|
451
706
|
declare function toResourceSummaries(entities: Entity[], opts?: {
|
|
452
707
|
allowedFrameworks?: string[];
|
|
708
|
+
customFrameworks?: string[];
|
|
453
709
|
}): ResourceSummary[];
|
|
454
710
|
|
|
455
|
-
export { AGENT_LINK_CAPABLE_FRAMEWORKS, ANNOTATION_COMPATIBLE_FRAMEWORKS, ANNOTATION_HELP, DEVAIHUB_ANNOTATION_PREFIX, INSTALLABLE_FRAMEWORKS, KNOWN_FRAMEWORKS, MARKETPLACE_CAPABLE_FRAMEWORKS, MARKETPLACE_MEMBER_TYPES, MCP_LINK_CAPABLE_FRAMEWORKS, PLUGIN_MEMBER_TYPES, PROMPT_LINK_CAPABLE_FRAMEWORKS, RESOURCE_TYPES, RESOURCE_TYPE_REGISTRY, TelemetryActionEnum, TelemetryEventInputSchema, expandInstallFrameworks, filterAllowedFrameworks, getAgentInstallLinks, getBodyShape, getFrameworks, getInstallSteps, getMarketplaceAddCommands, getMarketplaceInstallTemplate, getMarketplaceRepoSlug, getMarketplaceTeamSnippet, getMcpInstallLinks, getPromptInstallLinks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, isParseableEntityRef, isRenderableContainment, isResourceType, normalizeFramework, resolveCapableFrameworks, showsContentSection, toResourceSummaries, toResourceSummary };
|
|
456
|
-
export type { AiResourceEntityLike, BodyShape, FrameworkToken, InstallMode, MarketplaceAddCommand, ResourceDeepLink, ResourceInstallStep, ResourceInstallTarget, ResourceListResponse, ResourceSummary, ResourceType, ResourceTypeIcon, ResourceTypeInfo, TelemetryAction, TelemetryCounts, TelemetryEventInput };
|
|
711
|
+
export { AGENT_LINK_CAPABLE_FRAMEWORKS, ANNOTATION_COMPATIBLE_FRAMEWORKS, ANNOTATION_HELP, DEFAULT_SORT_MODE, DEVAIHUB_ANNOTATION_PREFIX, ExclusionInputSchema, INSTALLABLE_FRAMEWORKS, KNOWN_FRAMEWORKS, MARKETPLACE_CAPABLE_FRAMEWORKS, MARKETPLACE_MEMBER_TYPES, MCP_LINK_CAPABLE_FRAMEWORKS, NATURAL_SORT_DIRECTION, PLUGIN_MEMBER_TYPES, PROMPT_LINK_CAPABLE_FRAMEWORKS, RECOMMENDED_TAG, RESOURCE_TYPES, RESOURCE_TYPE_REGISTRY, RecommendationInputSchema, SORT_MODES, SortModeEnum, TelemetryActionEnum, TelemetryEventInputSchema, devAiHubBackofficeCuratePermission, devAiHubBackofficeReadPermission, devAiHubPermissions, expandInstallFrameworks, filterAllowedFrameworks, getAgentInstallLinks, getBodyShape, getFrameworks, getInstallSteps, getMarketplaceAddCommands, getMarketplaceInstallTemplate, getMarketplaceRepoSlug, getMarketplaceTeamSnippet, getMcpInstallLinks, getPromptInstallLinks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, isParseableEntityRef, isRenderableContainment, isResourceType, isSortMode, normalizeFramework, parseCustomFrameworks, resolveAllowedFrameworks, resolveCapableFrameworks, showsContentSection, sortResources, toResourceSummaries, toResourceSummary, usageScore };
|
|
712
|
+
export type { AiResourceEntityLike, BodyShape, CurationRecord, CustomFramework, CustomFrameworkConfig, ExclusionInput, FrameworkToken, InstallMode, MarketplaceAddCommand, ParsedCustomFrameworks, RecommendationInput, ResourceDeepLink, ResourceInstallStep, ResourceInstallTarget, ResourceListResponse, ResourceSummary, ResourceType, ResourceTypeIcon, ResourceTypeInfo, SortDirection, SortMode, TelemetryAction, TelemetryCounts, TelemetryEventInput, TelemetrySummary };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { ExclusionInputSchema, RECOMMENDED_TAG, RecommendationInputSchema } from './curation.esm.js';
|
|
2
|
+
export { devAiHubBackofficeCuratePermission, devAiHubBackofficeReadPermission, devAiHubPermissions } from './permissions.esm.js';
|
|
3
|
+
export { AGENT_LINK_CAPABLE_FRAMEWORKS, ANNOTATION_COMPATIBLE_FRAMEWORKS, ANNOTATION_HELP, DEVAIHUB_ANNOTATION_PREFIX, INSTALLABLE_FRAMEWORKS, KNOWN_FRAMEWORKS, MARKETPLACE_CAPABLE_FRAMEWORKS, MARKETPLACE_MEMBER_TYPES, MCP_LINK_CAPABLE_FRAMEWORKS, PLUGIN_MEMBER_TYPES, PROMPT_LINK_CAPABLE_FRAMEWORKS, RESOURCE_TYPES, RESOURCE_TYPE_REGISTRY, expandInstallFrameworks, filterAllowedFrameworks, getAgentInstallLinks, getBodyShape, getFrameworks, getInstallSteps, getMarketplaceAddCommands, getMarketplaceInstallTemplate, getMarketplaceRepoSlug, getMarketplaceTeamSnippet, getMcpInstallLinks, getPromptInstallLinks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, isRenderableContainment, isResourceType, normalizeFramework, parseCustomFrameworks, resolveAllowedFrameworks, resolveCapableFrameworks, showsContentSection } from './resources.esm.js';
|
|
4
|
+
export { DEFAULT_SORT_MODE, NATURAL_SORT_DIRECTION, SORT_MODES, SortModeEnum, isSortMode, sortResources, usageScore } from './sorting.esm.js';
|
|
2
5
|
export { TelemetryActionEnum, TelemetryEventInputSchema } from './telemetry.esm.js';
|
|
3
6
|
export { isParseableEntityRef, toResourceSummaries, toResourceSummary } from './toResourceSummary.esm.js';
|
|
4
7
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
4
|
+
|
|
5
|
+
const devAiHubBackofficeReadPermission = pluginPermissionCommon.createPermission({
|
|
6
|
+
name: "dev-ai-hub.backoffice.read",
|
|
7
|
+
attributes: { action: "read" }
|
|
8
|
+
});
|
|
9
|
+
const devAiHubBackofficeCuratePermission = pluginPermissionCommon.createPermission({
|
|
10
|
+
name: "dev-ai-hub.backoffice.curate",
|
|
11
|
+
attributes: { action: "update" }
|
|
12
|
+
});
|
|
13
|
+
const devAiHubPermissions = [
|
|
14
|
+
devAiHubBackofficeReadPermission,
|
|
15
|
+
devAiHubBackofficeCuratePermission
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
exports.devAiHubBackofficeCuratePermission = devAiHubBackofficeCuratePermission;
|
|
19
|
+
exports.devAiHubBackofficeReadPermission = devAiHubBackofficeReadPermission;
|
|
20
|
+
exports.devAiHubPermissions = devAiHubPermissions;
|
|
21
|
+
//# sourceMappingURL=permissions.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.cjs.js","sources":["../src/permissions.ts"],"sourcesContent":["import { createPermission } from '@backstage/plugin-permission-common';\n\n/**\n * Opening the Dev AI Hub backoffice (ADR-0016).\n *\n * Held in `-common` because both sides need the same identity: the backend\n * registers it so the Backstage RBAC plugin can list and grant it, and the\n * frontend checks it to decide whether the surface exists for this user at all.\n *\n * Read-only on purpose. Every governance capability that writes — curation\n * (#68, ADR-0017), tool visibility (#85) — gets its own permission rather\n * than riding on this one, so \"can see the backoffice\" never silently\n * becomes \"can change the catalog's presentation\".\n */\nexport const devAiHubBackofficeReadPermission = createPermission({\n name: 'dev-ai-hub.backoffice.read',\n attributes: { action: 'read' },\n});\n\n/**\n * Changing a resource's curation state — currently just \"recommended\"\n * (ADR-0017), later also demote/hide (#68) — from the backoffice.\n *\n * Scoped to the capability, not to one action: a future demote or hide\n * reuses this permission rather than minting one each, the same way granting\n * someone curation trust once covers whichever curation actions the backoffice\n * exposes. Separate from `dev-ai-hub.backoffice.read` so seeing the backoffice\n * never implies being able to write to it.\n */\nexport const devAiHubBackofficeCuratePermission = createPermission({\n name: 'dev-ai-hub.backoffice.curate',\n attributes: { action: 'update' },\n});\n\n/**\n * Every permission this plugin owns, for bulk registration with the backend's\n * permissions registry. New permissions must be added here or the RBAC plugin\n * will not offer them.\n */\nexport const devAiHubPermissions = [\n devAiHubBackofficeReadPermission,\n devAiHubBackofficeCuratePermission,\n];\n"],"names":["createPermission"],"mappings":";;;;AAcO,MAAM,mCAAmCA,uCAAA,CAAiB;AAAA,EAC/D,IAAA,EAAM,4BAAA;AAAA,EACN,UAAA,EAAY,EAAE,MAAA,EAAQ,MAAA;AACxB,CAAC;AAYM,MAAM,qCAAqCA,uCAAA,CAAiB;AAAA,EACjE,IAAA,EAAM,8BAAA;AAAA,EACN,UAAA,EAAY,EAAE,MAAA,EAAQ,QAAA;AACxB,CAAC;AAOM,MAAM,mBAAA,GAAsB;AAAA,EACjC,gCAAA;AAAA,EACA;AACF;;;;;;"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createPermission } from '@backstage/plugin-permission-common';
|
|
2
|
+
|
|
3
|
+
const devAiHubBackofficeReadPermission = createPermission({
|
|
4
|
+
name: "dev-ai-hub.backoffice.read",
|
|
5
|
+
attributes: { action: "read" }
|
|
6
|
+
});
|
|
7
|
+
const devAiHubBackofficeCuratePermission = createPermission({
|
|
8
|
+
name: "dev-ai-hub.backoffice.curate",
|
|
9
|
+
attributes: { action: "update" }
|
|
10
|
+
});
|
|
11
|
+
const devAiHubPermissions = [
|
|
12
|
+
devAiHubBackofficeReadPermission,
|
|
13
|
+
devAiHubBackofficeCuratePermission
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export { devAiHubBackofficeCuratePermission, devAiHubBackofficeReadPermission, devAiHubPermissions };
|
|
17
|
+
//# sourceMappingURL=permissions.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.esm.js","sources":["../src/permissions.ts"],"sourcesContent":["import { createPermission } from '@backstage/plugin-permission-common';\n\n/**\n * Opening the Dev AI Hub backoffice (ADR-0016).\n *\n * Held in `-common` because both sides need the same identity: the backend\n * registers it so the Backstage RBAC plugin can list and grant it, and the\n * frontend checks it to decide whether the surface exists for this user at all.\n *\n * Read-only on purpose. Every governance capability that writes — curation\n * (#68, ADR-0017), tool visibility (#85) — gets its own permission rather\n * than riding on this one, so \"can see the backoffice\" never silently\n * becomes \"can change the catalog's presentation\".\n */\nexport const devAiHubBackofficeReadPermission = createPermission({\n name: 'dev-ai-hub.backoffice.read',\n attributes: { action: 'read' },\n});\n\n/**\n * Changing a resource's curation state — currently just \"recommended\"\n * (ADR-0017), later also demote/hide (#68) — from the backoffice.\n *\n * Scoped to the capability, not to one action: a future demote or hide\n * reuses this permission rather than minting one each, the same way granting\n * someone curation trust once covers whichever curation actions the backoffice\n * exposes. Separate from `dev-ai-hub.backoffice.read` so seeing the backoffice\n * never implies being able to write to it.\n */\nexport const devAiHubBackofficeCuratePermission = createPermission({\n name: 'dev-ai-hub.backoffice.curate',\n attributes: { action: 'update' },\n});\n\n/**\n * Every permission this plugin owns, for bulk registration with the backend's\n * permissions registry. New permissions must be added here or the RBAC plugin\n * will not offer them.\n */\nexport const devAiHubPermissions = [\n devAiHubBackofficeReadPermission,\n devAiHubBackofficeCuratePermission,\n];\n"],"names":[],"mappings":";;AAcO,MAAM,mCAAmC,gBAAA,CAAiB;AAAA,EAC/D,IAAA,EAAM,4BAAA;AAAA,EACN,UAAA,EAAY,EAAE,MAAA,EAAQ,MAAA;AACxB,CAAC;AAYM,MAAM,qCAAqC,gBAAA,CAAiB;AAAA,EACjE,IAAA,EAAM,8BAAA;AAAA,EACN,UAAA,EAAY,EAAE,MAAA,EAAQ,QAAA;AACxB,CAAC;AAOM,MAAM,mBAAA,GAAsB;AAAA,EACjC,gCAAA;AAAA,EACA;AACF;;;;"}
|