@openbrf/plugin-sdk 0.0.0-stage → 1.0.0
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/LICENSE +661 -0
- package/LICENSE-EXCEPTION.md +28 -0
- package/dist/action-schema.d.ts +42 -0
- package/dist/action-schema.d.ts.map +1 -0
- package/dist/action-schema.js +64 -0
- package/dist/action-schema.js.map +1 -0
- package/dist/actions.d.ts +199 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +68 -0
- package/dist/actions.js.map +1 -0
- package/dist/api-version.d.ts +23 -0
- package/dist/api-version.d.ts.map +1 -0
- package/dist/api-version.js +27 -0
- package/dist/api-version.js.map +1 -0
- package/dist/catalog.d.ts +315 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +203 -0
- package/dist/catalog.js.map +1 -0
- package/dist/define-plugin.d.ts +14 -0
- package/dist/define-plugin.d.ts.map +1 -0
- package/dist/define-plugin.js +15 -0
- package/dist/define-plugin.js.map +1 -0
- package/dist/findings.d.ts +67 -0
- package/dist/findings.d.ts.map +1 -0
- package/dist/findings.js +57 -0
- package/dist/findings.js.map +1 -0
- package/dist/host.d.ts +208 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +41 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/integrity.d.ts +38 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +105 -0
- package/dist/integrity.js.map +1 -0
- package/dist/manifest.d.ts +392 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +324 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +35 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +2 -0
- package/dist/module.js.map +1 -0
- package/dist/package-check.d.ts +66 -0
- package/dist/package-check.d.ts.map +1 -0
- package/dist/package-check.js +755 -0
- package/dist/package-check.js.map +1 -0
- package/dist/permissions.d.ts +65 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +69 -0
- package/dist/permissions.js.map +1 -0
- package/dist/settings-schema.d.ts +95 -0
- package/dist/settings-schema.d.ts.map +1 -0
- package/dist/settings-schema.js +219 -0
- package/dist/settings-schema.js.map +1 -0
- package/dist/standard-schema.d.ts +43 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/standard-schema.js +2 -0
- package/dist/standard-schema.js.map +1 -0
- package/package.json +55 -3
- package/src/action-schema.ts +86 -0
- package/src/actions.ts +247 -0
- package/src/api-version.ts +28 -0
- package/src/catalog.ts +236 -0
- package/src/define-plugin.ts +18 -0
- package/src/findings.ts +71 -0
- package/src/host.ts +240 -0
- package/src/index.ts +123 -0
- package/src/integrity.ts +125 -0
- package/src/manifest.ts +385 -0
- package/src/module.ts +38 -0
- package/src/package-check.ts +894 -0
- package/src/permissions.ts +75 -0
- package/src/settings-schema.ts +272 -0
- package/src/standard-schema.ts +39 -0
- package/README.md +0 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Open BRF Module Exception
|
|
2
|
+
|
|
3
|
+
Version 1.0
|
|
4
|
+
|
|
5
|
+
## The exception
|
|
6
|
+
|
|
7
|
+
As an additional permission under section 7 of the GNU Affero General Public License, version 3 ("AGPL-3.0"), the copyright holders of Open BRF give you permission to combine Open BRF with modules.
|
|
8
|
+
|
|
9
|
+
A "module" is a plugin, theme, or other extension package that interacts with the Open BRF core exclusively through the documented public extension interfaces: the plugin API, the theme/token contract, the public HTTP API, and the extension points declared in the module manifest format (together, the "Module API").
|
|
10
|
+
|
|
11
|
+
If you create and convey a module:
|
|
12
|
+
|
|
13
|
+
1. The module is **not** considered a work based on Open BRF for the purposes of AGPL-3.0, regardless of the linking or loading mechanism.
|
|
14
|
+
2. You may license the module under terms of your own choosing, including proprietary terms; nothing requires you to convey the module's source code.
|
|
15
|
+
3. Running a combined work consisting of an unmodified Open BRF core and one or more modules, including offering it to users over a network, does not extend the obligations of AGPL-3.0 (including section 13) to the modules.
|
|
16
|
+
|
|
17
|
+
This exception does **not** apply to:
|
|
18
|
+
|
|
19
|
+
- Modifications of the Open BRF core itself, which remain governed by AGPL-3.0 in full. A "module" that patches, replaces, or bundles modified core code is a modification of the core, not a module.
|
|
20
|
+
- The obligations of AGPL-3.0 regarding the core: anyone conveying or network-hosting a modified core must still offer its Corresponding Source.
|
|
21
|
+
|
|
22
|
+
As provided by section 7 of AGPL-3.0, you may remove this additional permission from your copy, but you may not add further restrictions.
|
|
23
|
+
|
|
24
|
+
## Why this exists
|
|
25
|
+
|
|
26
|
+
The legal status of in-process plugins to (A)GPL software is a decades-old gray zone. Rather than relying on it, Open BRF states the rule explicitly, in both directions: the core is and remains copyleft, and the module ecosystem - community and commercial alike - is legally safe to build in. Vendure pioneered this model for GPL; Open BRF applies it to AGPL.
|
|
27
|
+
|
|
28
|
+
This exception is granted by Apteo AB as copyright holder of Open BRF. Contributions are accepted under a [CLA](CLA.md) precisely so that this grant remains valid for the whole codebase.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { StandardSchemaV1 } from "./standard-schema.ts";
|
|
3
|
+
/**
|
|
4
|
+
* An action's schemas, as the JSON Schema a caller is published.
|
|
5
|
+
*
|
|
6
|
+
* The only call to `z.toJSONSchema` in the product. Every option here is set
|
|
7
|
+
* against a failure that would otherwise be silent, so they are worth reading
|
|
8
|
+
* one at a time.
|
|
9
|
+
*
|
|
10
|
+
* `io: "input"` because output mode makes a key with a default REQUIRED, and a
|
|
11
|
+
* model reading that will dutifully supply a value for every field the platform
|
|
12
|
+
* was perfectly happy to choose itself.
|
|
13
|
+
*
|
|
14
|
+
* `cycles: "throw"` because a self-referential schema has no JSON Schema
|
|
15
|
+
* without `$ref`, and a silently emitted `$ref` to a positional name is worse
|
|
16
|
+
* than a refusal at registration.
|
|
17
|
+
*
|
|
18
|
+
* `reused: "inline"` because `$defs` invents names from the position a schema
|
|
19
|
+
* happens to hold, and those names would reach a committed artefact and a
|
|
20
|
+
* third-party client. Inlining costs bytes and keeps the document stable.
|
|
21
|
+
*
|
|
22
|
+
* The function form of `unrepresentable` because the default error names
|
|
23
|
+
* neither the action nor the path, and a build failing with "unrepresentable
|
|
24
|
+
* type" and nothing else is a half hour of searching.
|
|
25
|
+
*
|
|
26
|
+
* One property is worth stating because it is NOT what one would assume: a bare
|
|
27
|
+
* `.transform()` does not throw under input mode - it is erased and the
|
|
28
|
+
* pre-transform type is emitted. The guarantee is placed where it is real, on
|
|
29
|
+
* the output helper, which does throw. So an action whose pair is converted at
|
|
30
|
+
* registration cannot carry a transform anywhere.
|
|
31
|
+
*
|
|
32
|
+
* `.refine()`, `.check()`, `.superRefine()` and `.brand()` are erased with no
|
|
33
|
+
* marker under both modes. That is why the rule for a published schema is
|
|
34
|
+
* structural constraints first: what the document says has to be at least as
|
|
35
|
+
* strict as what the runtime enforces, or a caller is told a value is
|
|
36
|
+
* acceptable that the service will refuse.
|
|
37
|
+
*/
|
|
38
|
+
/** Refuses a schema built with a second copy of zod, whose internals differ. */
|
|
39
|
+
export declare function isHostZodSchema(schema: unknown): schema is z.ZodType;
|
|
40
|
+
export declare function actionInputJsonSchema(schema: StandardSchemaV1<unknown, unknown>, actionName: string): Record<string, unknown>;
|
|
41
|
+
export declare function actionOutputJsonSchema(schema: StandardSchemaV1<unknown, unknown>, actionName: string): Record<string, unknown>;
|
|
42
|
+
//# sourceMappingURL=action-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-schema.d.ts","sourceRoot":"","sources":["../src/action-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,CAAC,CAAC,OAAO,CAOpE;AAyBD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,EAC1C,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,EAC1C,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* An action's schemas, as the JSON Schema a caller is published.
|
|
4
|
+
*
|
|
5
|
+
* The only call to `z.toJSONSchema` in the product. Every option here is set
|
|
6
|
+
* against a failure that would otherwise be silent, so they are worth reading
|
|
7
|
+
* one at a time.
|
|
8
|
+
*
|
|
9
|
+
* `io: "input"` because output mode makes a key with a default REQUIRED, and a
|
|
10
|
+
* model reading that will dutifully supply a value for every field the platform
|
|
11
|
+
* was perfectly happy to choose itself.
|
|
12
|
+
*
|
|
13
|
+
* `cycles: "throw"` because a self-referential schema has no JSON Schema
|
|
14
|
+
* without `$ref`, and a silently emitted `$ref` to a positional name is worse
|
|
15
|
+
* than a refusal at registration.
|
|
16
|
+
*
|
|
17
|
+
* `reused: "inline"` because `$defs` invents names from the position a schema
|
|
18
|
+
* happens to hold, and those names would reach a committed artefact and a
|
|
19
|
+
* third-party client. Inlining costs bytes and keeps the document stable.
|
|
20
|
+
*
|
|
21
|
+
* The function form of `unrepresentable` because the default error names
|
|
22
|
+
* neither the action nor the path, and a build failing with "unrepresentable
|
|
23
|
+
* type" and nothing else is a half hour of searching.
|
|
24
|
+
*
|
|
25
|
+
* One property is worth stating because it is NOT what one would assume: a bare
|
|
26
|
+
* `.transform()` does not throw under input mode - it is erased and the
|
|
27
|
+
* pre-transform type is emitted. The guarantee is placed where it is real, on
|
|
28
|
+
* the output helper, which does throw. So an action whose pair is converted at
|
|
29
|
+
* registration cannot carry a transform anywhere.
|
|
30
|
+
*
|
|
31
|
+
* `.refine()`, `.check()`, `.superRefine()` and `.brand()` are erased with no
|
|
32
|
+
* marker under both modes. That is why the rule for a published schema is
|
|
33
|
+
* structural constraints first: what the document says has to be at least as
|
|
34
|
+
* strict as what the runtime enforces, or a caller is told a value is
|
|
35
|
+
* acceptable that the service will refuse.
|
|
36
|
+
*/
|
|
37
|
+
/** Refuses a schema built with a second copy of zod, whose internals differ. */
|
|
38
|
+
export function isHostZodSchema(schema) {
|
|
39
|
+
return (typeof schema === "object" &&
|
|
40
|
+
schema !== null &&
|
|
41
|
+
"_zod" in schema &&
|
|
42
|
+
schema._zod !== undefined);
|
|
43
|
+
}
|
|
44
|
+
function convert(schema, actionName, io) {
|
|
45
|
+
if (!isHostZodSchema(schema)) {
|
|
46
|
+
throw new Error(`${actionName}: an action schema must be built with the host's zod.`);
|
|
47
|
+
}
|
|
48
|
+
return z.toJSONSchema(schema, {
|
|
49
|
+
target: "draft-2020-12",
|
|
50
|
+
io,
|
|
51
|
+
cycles: "throw",
|
|
52
|
+
reused: "inline",
|
|
53
|
+
unrepresentable: ({ path, message }) => {
|
|
54
|
+
throw new Error(`${actionName}: ${message} at ${[io, ...path.map(String)].join(".")}`);
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export function actionInputJsonSchema(schema, actionName) {
|
|
59
|
+
return convert(schema, actionName, "input");
|
|
60
|
+
}
|
|
61
|
+
export function actionOutputJsonSchema(schema, actionName) {
|
|
62
|
+
return convert(schema, actionName, "output");
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=action-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-schema.js","sourceRoot":"","sources":["../src/action-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,MAAe;IAC7C,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,MAAM,IAAI,MAAM;QACf,MAA6B,CAAC,IAAI,KAAK,SAAS,CAClD,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CACd,MAAe,EACf,UAAkB,EAClB,EAAsB;IAEtB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,GAAG,UAAU,uDAAuD,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE;QAC5B,MAAM,EAAE,eAAe;QACvB,EAAE;QACF,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,QAAQ;QAChB,eAAe,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;YACrC,MAAM,IAAI,KAAK,CACb,GAAG,UAAU,KAAK,OAAO,OAAO,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACtE,CAAC;QACJ,CAAC;KACF,CAA4B,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,MAA0C,EAC1C,UAAkB;IAElB,OAAO,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAA0C,EAC1C,UAAkB;IAElB,OAAO,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "./standard-schema.ts";
|
|
2
|
+
/**
|
|
3
|
+
* What the platform can be asked to do, declared once and dispatched through
|
|
4
|
+
* one place: an action (atgard).
|
|
5
|
+
*
|
|
6
|
+
* An action is a name, an input schema, the single capability it needs, what it
|
|
7
|
+
* does to the records, which categories of personal data it can touch, and which
|
|
8
|
+
* surfaces it may be offered on. The registry checks the calling person's
|
|
9
|
+
* capability on every call rather than trusting the caller, so registering an
|
|
10
|
+
* action is never a permission of its own - it is a way of reaching something
|
|
11
|
+
* the caller could already do.
|
|
12
|
+
*
|
|
13
|
+
* A core feature registers its own. A plugin registers the ones its manifest
|
|
14
|
+
* declared and the board consented to, and they disappear with the plugin.
|
|
15
|
+
*
|
|
16
|
+
* Swedish domain terms follow GLOSSARY.md.
|
|
17
|
+
*/
|
|
18
|
+
export declare const ACTION_EFFECTS: readonly ["read", "write", "delete"];
|
|
19
|
+
export type ActionEffect = (typeof ACTION_EFFECTS)[number];
|
|
20
|
+
/**
|
|
21
|
+
* Where an action may be offered.
|
|
22
|
+
*
|
|
23
|
+
* "ui" means in-process only: the platform's own screens and a plugin's own
|
|
24
|
+
* route. The other two are the ones a board switches on deliberately.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ACTION_SURFACES: readonly ["ui", "mcp", "ai"];
|
|
27
|
+
export type ActionSurface = (typeof ACTION_SURFACES)[number];
|
|
28
|
+
/**
|
|
29
|
+
* What an action can touch.
|
|
30
|
+
*
|
|
31
|
+
* The first thirteen are PERSONAL_DATA_CATEGORIES from @openbrf/shared, in
|
|
32
|
+
* order, which this package deliberately does not depend on; the API side
|
|
33
|
+
* asserts the prefix equality, because it is the only place that can import
|
|
34
|
+
* both. "protected" is this list's own and marks an action that can return a
|
|
35
|
+
* field of a person carrying protected personal data (skyddade
|
|
36
|
+
* personuppgifter): it is the value the registry refuses to expose to a
|
|
37
|
+
* connected app or to the AI package at all.
|
|
38
|
+
*/
|
|
39
|
+
export declare const ACTION_PERSONAL_DATA: readonly ["name", "apartment", "residency", "email", "phone", "postalAddress", "personalIdentityNumber", "account", "financial", "health", "photograph", "freeText", "auditTrail", "protected"];
|
|
40
|
+
export type ActionPersonalData = (typeof ACTION_PERSONAL_DATA)[number];
|
|
41
|
+
/**
|
|
42
|
+
* What a public action name may look like.
|
|
43
|
+
*
|
|
44
|
+
* The intersection of three separate limits: what an MCP tools/list entry
|
|
45
|
+
* allows, what a model API allows (no dots), and what stays readable in a
|
|
46
|
+
* directory review (64 characters).
|
|
47
|
+
*/
|
|
48
|
+
export declare const ACTION_NAME_PATTERN: RegExp;
|
|
49
|
+
/** What a refusal means for a caller that is a model rather than a person. */
|
|
50
|
+
export interface ActionErrorSpec {
|
|
51
|
+
/** The machine-readable reason the handler raises. */
|
|
52
|
+
readonly reason: string;
|
|
53
|
+
/** Whether trying again could ever work, and after what. */
|
|
54
|
+
readonly retry: "never" | "after-edit" | "after-backoff";
|
|
55
|
+
/** The sentence a person is shown, as an i18n key. */
|
|
56
|
+
readonly messageKey: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The request a plugin's own route received.
|
|
60
|
+
*
|
|
61
|
+
* Opaque on purpose: the host reads a symbol on it that only the host writes,
|
|
62
|
+
* so a plugin passes the request it was handed straight through and cannot
|
|
63
|
+
* construct one. That is what stops a plugin naming a person it was not given.
|
|
64
|
+
*/
|
|
65
|
+
export type ActionRequest = object;
|
|
66
|
+
/** The lowercase channel vocabulary. The registry maps it to the audit channel. */
|
|
67
|
+
export declare const ACTION_CHANNELS: readonly ["web", "mcp", "ai", "system", "plugin"];
|
|
68
|
+
export type ActionChannel = (typeof ACTION_CHANNELS)[number];
|
|
69
|
+
/**
|
|
70
|
+
* Who is acting.
|
|
71
|
+
*
|
|
72
|
+
* The person, and the capabilities the registry re-derived for this call. No
|
|
73
|
+
* email, no name, no roles: a handler that needs to decide what an audience may
|
|
74
|
+
* see reads the capability list, and a handler that does not need it is not
|
|
75
|
+
* given a person's details to leak.
|
|
76
|
+
*/
|
|
77
|
+
export interface ActionPrincipal {
|
|
78
|
+
readonly personId: string;
|
|
79
|
+
readonly capabilities: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
/** The connected app that acted, when one did. Both hosts, never the token. */
|
|
82
|
+
export interface ActionClient {
|
|
83
|
+
readonly clientId: string;
|
|
84
|
+
readonly clientHost: string | null;
|
|
85
|
+
}
|
|
86
|
+
/** What a handler is told about the call. Built by the host, never by a caller. */
|
|
87
|
+
export interface ActionContext {
|
|
88
|
+
readonly principal: ActionPrincipal;
|
|
89
|
+
readonly channel: ActionChannel;
|
|
90
|
+
readonly client?: ActionClient;
|
|
91
|
+
readonly requestId: string;
|
|
92
|
+
readonly now: Date;
|
|
93
|
+
}
|
|
94
|
+
export interface ActionDefinition<Input = unknown, Output = unknown> {
|
|
95
|
+
readonly name: string;
|
|
96
|
+
readonly titleKey: string;
|
|
97
|
+
readonly descriptionKey: string;
|
|
98
|
+
readonly group: string;
|
|
99
|
+
readonly groupTitleKey: string;
|
|
100
|
+
/** Exactly one core capability, by name. */
|
|
101
|
+
readonly capability: string;
|
|
102
|
+
readonly effect: ActionEffect;
|
|
103
|
+
readonly idempotent: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* True when the act adds and overwrites nothing a person wrote.
|
|
106
|
+
*
|
|
107
|
+
* It is what separates a create from an update, and the annotation mapping
|
|
108
|
+
* reads it: MCP's destructiveHint defaults to true, so a non-read action has
|
|
109
|
+
* to say for itself that it destroys nothing.
|
|
110
|
+
*/
|
|
111
|
+
readonly additive: boolean;
|
|
112
|
+
readonly needsConfirmation: boolean;
|
|
113
|
+
/** True when the handler reaches a system outside this instance. */
|
|
114
|
+
readonly openWorld: boolean;
|
|
115
|
+
/** Never empty by convention: an action that touches none says []. */
|
|
116
|
+
readonly personalData: readonly ActionPersonalData[];
|
|
117
|
+
/** Opt-in allowlist. An empty array means the action is offered nowhere. */
|
|
118
|
+
readonly surfaces: readonly ActionSurface[];
|
|
119
|
+
/** Every domain refusal the handler can raise, for the caller to act on. */
|
|
120
|
+
readonly errors: readonly ActionErrorSpec[];
|
|
121
|
+
readonly deprecatedAliases?: readonly string[];
|
|
122
|
+
/** Every object in it, at every depth, must refuse unknown keys. */
|
|
123
|
+
readonly input: StandardSchemaV1<unknown, Input>;
|
|
124
|
+
readonly output: StandardSchemaV1<unknown, Output>;
|
|
125
|
+
readonly handler: (input: Input, context: ActionContext) => Promise<Output>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* What a plugin hands the host: the id its manifest declared, and the rest.
|
|
129
|
+
*
|
|
130
|
+
* The capability, the effect, the personal-data categories and the surfaces are
|
|
131
|
+
* manifest data and are not repeated here. The loader's rule is that nothing
|
|
132
|
+
* which can refuse a plugin may run after the code that executes it, and the
|
|
133
|
+
* install consent screen renders from the catalog before anything is
|
|
134
|
+
* downloaded - so what the board consents to cannot come from the bundle.
|
|
135
|
+
*/
|
|
136
|
+
export interface PluginActionRegistration {
|
|
137
|
+
readonly id: string;
|
|
138
|
+
readonly definition: Omit<ActionDefinition, "capability" | "effect" | "personalData" | "surfaces">;
|
|
139
|
+
}
|
|
140
|
+
/** Narrows what a plugin's route offers the request it is serving. */
|
|
141
|
+
export interface PluginActionFilter {
|
|
142
|
+
readonly surface?: ActionSurface;
|
|
143
|
+
readonly group?: string;
|
|
144
|
+
}
|
|
145
|
+
/** One action as a caller sees it, with its text already in the right language. */
|
|
146
|
+
export interface ActionSummary {
|
|
147
|
+
readonly name: string;
|
|
148
|
+
readonly title: string;
|
|
149
|
+
readonly description: string;
|
|
150
|
+
readonly titleKey: string;
|
|
151
|
+
readonly descriptionKey: string;
|
|
152
|
+
readonly group: string;
|
|
153
|
+
readonly groupTitle: string;
|
|
154
|
+
/**
|
|
155
|
+
* The key behind `groupTitle`, alongside the two above.
|
|
156
|
+
*
|
|
157
|
+
* Both forms travel for the reason `titleKey` does: the resolved string is
|
|
158
|
+
* for a caller with no translations of its own - a connected app, a model -
|
|
159
|
+
* while a screen in this product translates the key itself. The interface's
|
|
160
|
+
* language and the language a request happens to declare are not the same
|
|
161
|
+
* thing, so a screen rendering the resolved string can show one heading in
|
|
162
|
+
* the browser's language above a page in the association's.
|
|
163
|
+
*/
|
|
164
|
+
readonly groupTitleKey: string;
|
|
165
|
+
readonly capability: string;
|
|
166
|
+
readonly effect: ActionEffect;
|
|
167
|
+
readonly idempotent: boolean;
|
|
168
|
+
readonly additive: boolean;
|
|
169
|
+
readonly needsConfirmation: boolean;
|
|
170
|
+
readonly openWorld: boolean;
|
|
171
|
+
readonly personalData: readonly ActionPersonalData[];
|
|
172
|
+
readonly surfaces: readonly ActionSurface[];
|
|
173
|
+
readonly errors: readonly ActionErrorSpec[];
|
|
174
|
+
}
|
|
175
|
+
export interface PluginActions {
|
|
176
|
+
/**
|
|
177
|
+
* Registers an action the manifest declared.
|
|
178
|
+
*
|
|
179
|
+
* Callable from the module factory onwards: a call made that early is
|
|
180
|
+
* buffered until the application is built, because the host object is not
|
|
181
|
+
* bound yet.
|
|
182
|
+
*
|
|
183
|
+
* It does not throw for a registration the host will refuse. An id the
|
|
184
|
+
* manifest never declared, or a schema that cannot be published, is found
|
|
185
|
+
* when the buffer is flushed - long after this call returned - and reaches
|
|
186
|
+
* the board as an `action-refused` finding rather than as an error the
|
|
187
|
+
* factory could have caught. A plugin that wants to know sooner validates
|
|
188
|
+
* its own manifest in its own build.
|
|
189
|
+
*/
|
|
190
|
+
register: (registration: PluginActionRegistration) => void;
|
|
191
|
+
/** What this plugin's route may offer the request it is serving. */
|
|
192
|
+
list: (request: ActionRequest, filter?: PluginActionFilter) => Promise<ActionSummary[]>;
|
|
193
|
+
/**
|
|
194
|
+
* Dispatch. The person, the channel and the connected app all come from the
|
|
195
|
+
* request the host authenticated, never from an argument.
|
|
196
|
+
*/
|
|
197
|
+
invoke: <Output>(request: ActionRequest, name: string, input: unknown) => Promise<Output>;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the platform can be asked to do, declared once and dispatched through
|
|
3
|
+
* one place: an action (atgard).
|
|
4
|
+
*
|
|
5
|
+
* An action is a name, an input schema, the single capability it needs, what it
|
|
6
|
+
* does to the records, which categories of personal data it can touch, and which
|
|
7
|
+
* surfaces it may be offered on. The registry checks the calling person's
|
|
8
|
+
* capability on every call rather than trusting the caller, so registering an
|
|
9
|
+
* action is never a permission of its own - it is a way of reaching something
|
|
10
|
+
* the caller could already do.
|
|
11
|
+
*
|
|
12
|
+
* A core feature registers its own. A plugin registers the ones its manifest
|
|
13
|
+
* declared and the board consented to, and they disappear with the plugin.
|
|
14
|
+
*
|
|
15
|
+
* Swedish domain terms follow GLOSSARY.md.
|
|
16
|
+
*/
|
|
17
|
+
export const ACTION_EFFECTS = ["read", "write", "delete"];
|
|
18
|
+
/**
|
|
19
|
+
* Where an action may be offered.
|
|
20
|
+
*
|
|
21
|
+
* "ui" means in-process only: the platform's own screens and a plugin's own
|
|
22
|
+
* route. The other two are the ones a board switches on deliberately.
|
|
23
|
+
*/
|
|
24
|
+
export const ACTION_SURFACES = ["ui", "mcp", "ai"];
|
|
25
|
+
/**
|
|
26
|
+
* What an action can touch.
|
|
27
|
+
*
|
|
28
|
+
* The first thirteen are PERSONAL_DATA_CATEGORIES from @openbrf/shared, in
|
|
29
|
+
* order, which this package deliberately does not depend on; the API side
|
|
30
|
+
* asserts the prefix equality, because it is the only place that can import
|
|
31
|
+
* both. "protected" is this list's own and marks an action that can return a
|
|
32
|
+
* field of a person carrying protected personal data (skyddade
|
|
33
|
+
* personuppgifter): it is the value the registry refuses to expose to a
|
|
34
|
+
* connected app or to the AI package at all.
|
|
35
|
+
*/
|
|
36
|
+
export const ACTION_PERSONAL_DATA = [
|
|
37
|
+
"name",
|
|
38
|
+
"apartment",
|
|
39
|
+
"residency",
|
|
40
|
+
"email",
|
|
41
|
+
"phone",
|
|
42
|
+
"postalAddress",
|
|
43
|
+
"personalIdentityNumber",
|
|
44
|
+
"account",
|
|
45
|
+
"financial",
|
|
46
|
+
"health",
|
|
47
|
+
"photograph",
|
|
48
|
+
"freeText",
|
|
49
|
+
"auditTrail",
|
|
50
|
+
"protected",
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* What a public action name may look like.
|
|
54
|
+
*
|
|
55
|
+
* The intersection of three separate limits: what an MCP tools/list entry
|
|
56
|
+
* allows, what a model API allows (no dots), and what stays readable in a
|
|
57
|
+
* directory review (64 characters).
|
|
58
|
+
*/
|
|
59
|
+
export const ACTION_NAME_PATTERN = /^[a-z][a-z0-9_]{2,63}$/;
|
|
60
|
+
/** The lowercase channel vocabulary. The registry maps it to the audit channel. */
|
|
61
|
+
export const ACTION_CHANNELS = [
|
|
62
|
+
"web",
|
|
63
|
+
"mcp",
|
|
64
|
+
"ai",
|
|
65
|
+
"system",
|
|
66
|
+
"plugin",
|
|
67
|
+
];
|
|
68
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAU,CAAC;AAGnE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAU,CAAC;AAG5D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,MAAM;IACN,WAAW;IACX,WAAW;IACX,OAAO;IACP,OAAO;IACP,eAAe;IACf,wBAAwB;IACxB,SAAS;IACT,WAAW;IACX,QAAQ;IACR,YAAY;IACZ,UAAU;IACV,YAAY;IACZ,WAAW;CACH,CAAC;AAGX;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAqB5D,mFAAmF;AACnF,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,KAAK;IACL,KAAK;IACL,IAAI;IACJ,QAAQ;IACR,QAAQ;CACA,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plugin API version.
|
|
3
|
+
*
|
|
4
|
+
* A single integer rather than a semver range, because the gate it guards is
|
|
5
|
+
* binary: a plugin either speaks the contract this host implements or it does
|
|
6
|
+
* not get loaded. A range would invite a plugin to claim ">=1" and then fail
|
|
7
|
+
* at the first call to a method version 2 removed, which is exactly the class
|
|
8
|
+
* of breakage the gate exists to prevent.
|
|
9
|
+
*
|
|
10
|
+
* Additive changes - a new optional manifest field, a new SDK method - keep
|
|
11
|
+
* the number. Anything a plugin built against the old contract could notice
|
|
12
|
+
* raises it, and the host may then support more than one version at a time by
|
|
13
|
+
* listing them in SUPPORTED_PLUGIN_API_VERSIONS.
|
|
14
|
+
*/
|
|
15
|
+
export declare const PLUGIN_API_VERSION = 1;
|
|
16
|
+
/**
|
|
17
|
+
* Every version this contract can still load. Kept as a list so a host can
|
|
18
|
+
* carry an old version through a deprecation window without a plugin having
|
|
19
|
+
* to be republished on the day the new one lands.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SUPPORTED_PLUGIN_API_VERSIONS: readonly number[];
|
|
22
|
+
export declare function isSupportedApiVersion(apiVersion: number): boolean;
|
|
23
|
+
//# sourceMappingURL=api-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-version.d.ts","sourceRoot":"","sources":["../src/api-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,MAAM,EAE1D,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEjE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plugin API version.
|
|
3
|
+
*
|
|
4
|
+
* A single integer rather than a semver range, because the gate it guards is
|
|
5
|
+
* binary: a plugin either speaks the contract this host implements or it does
|
|
6
|
+
* not get loaded. A range would invite a plugin to claim ">=1" and then fail
|
|
7
|
+
* at the first call to a method version 2 removed, which is exactly the class
|
|
8
|
+
* of breakage the gate exists to prevent.
|
|
9
|
+
*
|
|
10
|
+
* Additive changes - a new optional manifest field, a new SDK method - keep
|
|
11
|
+
* the number. Anything a plugin built against the old contract could notice
|
|
12
|
+
* raises it, and the host may then support more than one version at a time by
|
|
13
|
+
* listing them in SUPPORTED_PLUGIN_API_VERSIONS.
|
|
14
|
+
*/
|
|
15
|
+
export const PLUGIN_API_VERSION = 1;
|
|
16
|
+
/**
|
|
17
|
+
* Every version this contract can still load. Kept as a list so a host can
|
|
18
|
+
* carry an old version through a deprecation window without a plugin having
|
|
19
|
+
* to be republished on the day the new one lands.
|
|
20
|
+
*/
|
|
21
|
+
export const SUPPORTED_PLUGIN_API_VERSIONS = [
|
|
22
|
+
PLUGIN_API_VERSION,
|
|
23
|
+
];
|
|
24
|
+
export function isSupportedApiVersion(apiVersion) {
|
|
25
|
+
return SUPPORTED_PLUGIN_API_VERSIONS.includes(apiVersion);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=api-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-version.js","sourceRoot":"","sources":["../src/api-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAEpC;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAsB;IAC9D,kBAAkB;CACnB,CAAC;AAEF,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,OAAO,6BAA6B,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC5D,CAAC"}
|