@sentry/junior-plugin-api 0.117.0 → 0.119.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/README.md +19 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +63 -0
- package/dist/registration.d.ts +2 -0
- package/dist/user-pages.d.ts +56 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/registration.ts +44 -0
- package/src/user-pages.ts +81 -0
package/README.md
CHANGED
|
@@ -8,6 +8,11 @@ exported TypeScript types and runtime validators are authoritative.
|
|
|
8
8
|
Use `defineJuniorPlugin({ manifest, hooks, tasks, cli, model })`. A plugin name
|
|
9
9
|
is a lowercase identifier and is unique within the enabled app plugin set.
|
|
10
10
|
|
|
11
|
+
Packages with JavaScript registration export one callable factory named
|
|
12
|
+
`<domain>Plugin`, such as `githubPlugin(options?)`. Do not use a public
|
|
13
|
+
`create<Domain>Plugin` alias or export a prebuilt registration. Call the factory
|
|
14
|
+
even when it accepts no options.
|
|
15
|
+
|
|
11
16
|
A plugin may instead be a declarative `plugin.yaml` package when it has no
|
|
12
17
|
host-executed hooks. Do not combine an inline manifest with a second YAML
|
|
13
18
|
definition for the same plugin.
|
|
@@ -21,7 +26,8 @@ The manifest declares runtime metadata such as:
|
|
|
21
26
|
- provider domains, grants, OAuth, API-header transformations, and safe command
|
|
22
27
|
environment placeholders;
|
|
23
28
|
- runtime dependencies and snapshot installation steps;
|
|
24
|
-
- configuration fields
|
|
29
|
+
- configuration fields;
|
|
30
|
+
- signed-in user pages backed by core-owned discovery and read routes.
|
|
25
31
|
|
|
26
32
|
Manifest values are validated before runtime activation. Secret deployment
|
|
27
33
|
values remain host-only; sandbox-exposed command environment must be explicitly
|
|
@@ -39,6 +45,18 @@ reports, and other typed hook surfaces exported by this package.
|
|
|
39
45
|
- Tool hooks return model-visible schemas aligned with their executor inputs.
|
|
40
46
|
- Host-owned structured model and embedding calls do not expose provider
|
|
41
47
|
credentials to plugins.
|
|
48
|
+
- User page readers receive the signed-in email plus runtime-owned actors linked
|
|
49
|
+
to that user. Plugins return bounded data and do not mount their own page
|
|
50
|
+
routes or browser code.
|
|
51
|
+
|
|
52
|
+
## User Pages
|
|
53
|
+
|
|
54
|
+
Register signed-in pages through `userPages` beside `hooks`, `tasks`, and `cli`.
|
|
55
|
+
Each definition owns its navigation metadata and `read(ctx)` function, so a
|
|
56
|
+
page cannot be advertised without an implementation. Core owns discovery,
|
|
57
|
+
authentication, actor resolution, routing, response validation, and rendering.
|
|
58
|
+
The initial response type is the bounded `list` page; add another core-rendered
|
|
59
|
+
type only when a concrete product need proves it.
|
|
42
60
|
|
|
43
61
|
## Durable Work
|
|
44
62
|
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -462,6 +462,7 @@ var EgressPolicyDenied = class extends Error {
|
|
|
462
462
|
|
|
463
463
|
// src/registration.ts
|
|
464
464
|
var PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
465
|
+
var USER_PAGE_ID_RE = /^[a-z][a-z0-9-]*$/;
|
|
465
466
|
function defineJuniorPlugin(plugin) {
|
|
466
467
|
if ("pluginConfig" in plugin) {
|
|
467
468
|
throw new Error(
|
|
@@ -496,10 +497,69 @@ function defineJuniorPlugin(plugin) {
|
|
|
496
497
|
`Junior plugin "${name}" manifest.description is required.`
|
|
497
498
|
);
|
|
498
499
|
}
|
|
500
|
+
if (plugin.userPages !== void 0 && !Array.isArray(plugin.userPages)) {
|
|
501
|
+
throw new Error(`Junior plugin "${name}" userPages must be an array.`);
|
|
502
|
+
}
|
|
503
|
+
const userPageIds = /* @__PURE__ */ new Set();
|
|
504
|
+
for (const page of plugin.userPages ?? []) {
|
|
505
|
+
if (!page || typeof page !== "object") {
|
|
506
|
+
throw new Error(`Junior plugin "${name}" user pages must be objects.`);
|
|
507
|
+
}
|
|
508
|
+
if (typeof page.id !== "string" || page.id.length > 64 || !USER_PAGE_ID_RE.test(page.id)) {
|
|
509
|
+
throw new Error(
|
|
510
|
+
`Junior plugin "${name}" user page id "${page.id}" must be a lowercase identifier.`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
if (userPageIds.has(page.id)) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
`Junior plugin "${name}" has duplicate user page id "${page.id}".`
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
if (typeof page.label !== "string" || !page.label.trim() || page.label.length > 80 || typeof page.description !== "string" || !page.description.trim() || page.description.length > 500) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
`Junior plugin "${name}" user page "${page.id}" requires label and description.`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (typeof page.read !== "function") {
|
|
524
|
+
throw new Error(
|
|
525
|
+
`Junior plugin "${name}" user page "${page.id}" requires a read function.`
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
userPageIds.add(page.id);
|
|
529
|
+
}
|
|
499
530
|
return {
|
|
500
531
|
...plugin
|
|
501
532
|
};
|
|
502
533
|
}
|
|
534
|
+
|
|
535
|
+
// src/user-pages.ts
|
|
536
|
+
import { z as z9 } from "zod";
|
|
537
|
+
var userPageIdSchema = nonBlankStringSchema.max(64).regex(/^[a-z][a-z0-9-]*$/);
|
|
538
|
+
var userPageLabelSchema = nonBlankStringSchema.max(80);
|
|
539
|
+
var userPageDescriptionSchema = nonBlankStringSchema.max(500);
|
|
540
|
+
var pluginUserPageMetadataSchema = z9.object({
|
|
541
|
+
label: nonBlankStringSchema.max(80),
|
|
542
|
+
value: nonBlankStringSchema.max(500)
|
|
543
|
+
}).strict();
|
|
544
|
+
var pluginUserPageRecordSchema = z9.object({
|
|
545
|
+
description: nonBlankStringSchema.max(1e3).optional(),
|
|
546
|
+
id: nonBlankStringSchema.max(128),
|
|
547
|
+
metadata: z9.array(pluginUserPageMetadataSchema).max(8).optional(),
|
|
548
|
+
title: nonBlankStringSchema.max(4e3)
|
|
549
|
+
}).strict();
|
|
550
|
+
var pluginUserPageContentSchema = z9.object({
|
|
551
|
+
emptyText: nonBlankStringSchema.max(500).optional(),
|
|
552
|
+
records: z9.array(pluginUserPageRecordSchema).max(100),
|
|
553
|
+
type: z9.literal("list")
|
|
554
|
+
}).strict();
|
|
555
|
+
var pluginUserPageLinkSchema = z9.object({
|
|
556
|
+
description: userPageDescriptionSchema,
|
|
557
|
+
id: userPageIdSchema,
|
|
558
|
+
label: userPageLabelSchema,
|
|
559
|
+
pluginDisplayName: nonBlankStringSchema.max(200),
|
|
560
|
+
pluginName: nonBlankStringSchema.max(100)
|
|
561
|
+
}).strict();
|
|
562
|
+
var pluginUserPageLinksSchema = z9.array(pluginUserPageLinkSchema);
|
|
503
563
|
export {
|
|
504
564
|
EgressAuthRequired,
|
|
505
565
|
EgressPolicyDenied,
|
|
@@ -537,6 +597,9 @@ export {
|
|
|
537
597
|
pluginToolContinuationSchema,
|
|
538
598
|
pluginToolErrorSchema,
|
|
539
599
|
pluginToolResultSchema,
|
|
600
|
+
pluginUserPageContentSchema,
|
|
601
|
+
pluginUserPageLinkSchema,
|
|
602
|
+
pluginUserPageLinksSchema,
|
|
540
603
|
promptContextSchema,
|
|
541
604
|
promptMessageSchema,
|
|
542
605
|
resourceEventSchema,
|
package/dist/registration.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { PluginCliDefinition } from "./cli";
|
|
|
2
2
|
import type { PluginHooks } from "./hooks";
|
|
3
3
|
import type { PluginManifest } from "./manifest";
|
|
4
4
|
import type { PluginTasks } from "./tasks";
|
|
5
|
+
import type { PluginUserPageDefinition } from "./user-pages";
|
|
5
6
|
export interface PluginModelConfig {
|
|
6
7
|
/** Host model family used when no explicit structured model id is configured. */
|
|
7
8
|
structuredModel?: "default" | "fast";
|
|
@@ -15,6 +16,7 @@ export type PluginRegistrationInput = {
|
|
|
15
16
|
model?: PluginModelConfig;
|
|
16
17
|
packageName?: string;
|
|
17
18
|
tasks?: PluginTasks;
|
|
19
|
+
userPages?: PluginUserPageDefinition[];
|
|
18
20
|
};
|
|
19
21
|
export interface PluginRegistration extends PluginRegistrationInput {
|
|
20
22
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core-rendered plugin user pages.
|
|
3
|
+
*
|
|
4
|
+
* Plugins own bounded data projection; Junior owns viewer authorization,
|
|
5
|
+
* routing, response validation, and browser rendering.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { LocalActor, PluginContext, SlackActor } from "./context";
|
|
9
|
+
/** Bounded list content returned by a plugin-owned user page. */
|
|
10
|
+
export declare const pluginUserPageContentSchema: z.ZodObject<{
|
|
11
|
+
emptyText: z.ZodOptional<z.ZodString>;
|
|
12
|
+
records: z.ZodArray<z.ZodObject<{
|
|
13
|
+
description: z.ZodOptional<z.ZodString>;
|
|
14
|
+
id: z.ZodString;
|
|
15
|
+
metadata: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
16
|
+
label: z.ZodString;
|
|
17
|
+
value: z.ZodString;
|
|
18
|
+
}, z.core.$strict>>>;
|
|
19
|
+
title: z.ZodString;
|
|
20
|
+
}, z.core.$strict>>;
|
|
21
|
+
type: z.ZodLiteral<"list">;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
/** Validated content for the current core-rendered list page type. */
|
|
24
|
+
export type PluginUserPageContent = z.output<typeof pluginUserPageContentSchema>;
|
|
25
|
+
export declare const pluginUserPageLinkSchema: z.ZodObject<{
|
|
26
|
+
description: z.ZodString;
|
|
27
|
+
id: z.ZodString;
|
|
28
|
+
label: z.ZodString;
|
|
29
|
+
pluginDisplayName: z.ZodString;
|
|
30
|
+
pluginName: z.ZodString;
|
|
31
|
+
}, z.core.$strict>;
|
|
32
|
+
export declare const pluginUserPageLinksSchema: z.ZodArray<z.ZodObject<{
|
|
33
|
+
description: z.ZodString;
|
|
34
|
+
id: z.ZodString;
|
|
35
|
+
label: z.ZodString;
|
|
36
|
+
pluginDisplayName: z.ZodString;
|
|
37
|
+
pluginName: z.ZodString;
|
|
38
|
+
}, z.core.$strict>>;
|
|
39
|
+
/** Safe navigation metadata for one registered plugin user page. */
|
|
40
|
+
export type PluginUserPageLink = z.output<typeof pluginUserPageLinkSchema>;
|
|
41
|
+
/** Runtime-owned actor shapes that may belong to an authenticated viewer. */
|
|
42
|
+
export type PluginUserPageActor = SlackActor | LocalActor;
|
|
43
|
+
/** Trusted host context supplied while reading one plugin user page. */
|
|
44
|
+
export interface PluginUserPageContext extends PluginContext {
|
|
45
|
+
viewer: {
|
|
46
|
+
actors: PluginUserPageActor[];
|
|
47
|
+
email: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Navigation metadata and reader for one core-rendered plugin user page. */
|
|
51
|
+
export interface PluginUserPageDefinition {
|
|
52
|
+
description: string;
|
|
53
|
+
id: string;
|
|
54
|
+
label: string;
|
|
55
|
+
read(ctx: PluginUserPageContext): Promise<PluginUserPageContent> | PluginUserPageContent;
|
|
56
|
+
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/registration.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { PluginCliDefinition } from "./cli";
|
|
|
2
2
|
import type { PluginHooks } from "./hooks";
|
|
3
3
|
import type { PluginManifest } from "./manifest";
|
|
4
4
|
import type { PluginTasks } from "./tasks";
|
|
5
|
+
import type { PluginUserPageDefinition } from "./user-pages";
|
|
5
6
|
|
|
6
7
|
export interface PluginModelConfig {
|
|
7
8
|
/** Host model family used when no explicit structured model id is configured. */
|
|
@@ -17,11 +18,13 @@ export type PluginRegistrationInput = {
|
|
|
17
18
|
model?: PluginModelConfig;
|
|
18
19
|
packageName?: string;
|
|
19
20
|
tasks?: PluginTasks;
|
|
21
|
+
userPages?: PluginUserPageDefinition[];
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
export interface PluginRegistration extends PluginRegistrationInput {}
|
|
23
25
|
|
|
24
26
|
const PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
27
|
+
const USER_PAGE_ID_RE = /^[a-z][a-z0-9-]*$/;
|
|
25
28
|
|
|
26
29
|
/** Define one Junior plugin registration for app and build-time wiring. */
|
|
27
30
|
export function defineJuniorPlugin(
|
|
@@ -66,6 +69,47 @@ export function defineJuniorPlugin(
|
|
|
66
69
|
`Junior plugin "${name}" manifest.description is required.`,
|
|
67
70
|
);
|
|
68
71
|
}
|
|
72
|
+
if (plugin.userPages !== undefined && !Array.isArray(plugin.userPages)) {
|
|
73
|
+
throw new Error(`Junior plugin "${name}" userPages must be an array.`);
|
|
74
|
+
}
|
|
75
|
+
const userPageIds = new Set<string>();
|
|
76
|
+
for (const page of plugin.userPages ?? []) {
|
|
77
|
+
if (!page || typeof page !== "object") {
|
|
78
|
+
throw new Error(`Junior plugin "${name}" user pages must be objects.`);
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
typeof page.id !== "string" ||
|
|
82
|
+
page.id.length > 64 ||
|
|
83
|
+
!USER_PAGE_ID_RE.test(page.id)
|
|
84
|
+
) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Junior plugin "${name}" user page id "${page.id}" must be a lowercase identifier.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (userPageIds.has(page.id)) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Junior plugin "${name}" has duplicate user page id "${page.id}".`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
typeof page.label !== "string" ||
|
|
96
|
+
!page.label.trim() ||
|
|
97
|
+
page.label.length > 80 ||
|
|
98
|
+
typeof page.description !== "string" ||
|
|
99
|
+
!page.description.trim() ||
|
|
100
|
+
page.description.length > 500
|
|
101
|
+
) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Junior plugin "${name}" user page "${page.id}" requires label and description.`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (typeof page.read !== "function") {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Junior plugin "${name}" user page "${page.id}" requires a read function.`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
userPageIds.add(page.id);
|
|
112
|
+
}
|
|
69
113
|
return {
|
|
70
114
|
...plugin,
|
|
71
115
|
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core-rendered plugin user pages.
|
|
3
|
+
*
|
|
4
|
+
* Plugins own bounded data projection; Junior owns viewer authorization,
|
|
5
|
+
* routing, response validation, and browser rendering.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import type { LocalActor, PluginContext, SlackActor } from "./context";
|
|
9
|
+
import { nonBlankStringSchema } from "./schemas";
|
|
10
|
+
|
|
11
|
+
const userPageIdSchema = nonBlankStringSchema
|
|
12
|
+
.max(64)
|
|
13
|
+
.regex(/^[a-z][a-z0-9-]*$/);
|
|
14
|
+
const userPageLabelSchema = nonBlankStringSchema.max(80);
|
|
15
|
+
const userPageDescriptionSchema = nonBlankStringSchema.max(500);
|
|
16
|
+
|
|
17
|
+
const pluginUserPageMetadataSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
label: nonBlankStringSchema.max(80),
|
|
20
|
+
value: nonBlankStringSchema.max(500),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
const pluginUserPageRecordSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
description: nonBlankStringSchema.max(1_000).optional(),
|
|
27
|
+
id: nonBlankStringSchema.max(128),
|
|
28
|
+
metadata: z.array(pluginUserPageMetadataSchema).max(8).optional(),
|
|
29
|
+
title: nonBlankStringSchema.max(4_000),
|
|
30
|
+
})
|
|
31
|
+
.strict();
|
|
32
|
+
|
|
33
|
+
/** Bounded list content returned by a plugin-owned user page. */
|
|
34
|
+
export const pluginUserPageContentSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
emptyText: nonBlankStringSchema.max(500).optional(),
|
|
37
|
+
records: z.array(pluginUserPageRecordSchema).max(100),
|
|
38
|
+
type: z.literal("list"),
|
|
39
|
+
})
|
|
40
|
+
.strict();
|
|
41
|
+
|
|
42
|
+
/** Validated content for the current core-rendered list page type. */
|
|
43
|
+
export type PluginUserPageContent = z.output<
|
|
44
|
+
typeof pluginUserPageContentSchema
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
export const pluginUserPageLinkSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
description: userPageDescriptionSchema,
|
|
50
|
+
id: userPageIdSchema,
|
|
51
|
+
label: userPageLabelSchema,
|
|
52
|
+
pluginDisplayName: nonBlankStringSchema.max(200),
|
|
53
|
+
pluginName: nonBlankStringSchema.max(100),
|
|
54
|
+
})
|
|
55
|
+
.strict();
|
|
56
|
+
|
|
57
|
+
export const pluginUserPageLinksSchema = z.array(pluginUserPageLinkSchema);
|
|
58
|
+
|
|
59
|
+
/** Safe navigation metadata for one registered plugin user page. */
|
|
60
|
+
export type PluginUserPageLink = z.output<typeof pluginUserPageLinkSchema>;
|
|
61
|
+
|
|
62
|
+
/** Runtime-owned actor shapes that may belong to an authenticated viewer. */
|
|
63
|
+
export type PluginUserPageActor = SlackActor | LocalActor;
|
|
64
|
+
|
|
65
|
+
/** Trusted host context supplied while reading one plugin user page. */
|
|
66
|
+
export interface PluginUserPageContext extends PluginContext {
|
|
67
|
+
viewer: {
|
|
68
|
+
actors: PluginUserPageActor[];
|
|
69
|
+
email: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Navigation metadata and reader for one core-rendered plugin user page. */
|
|
74
|
+
export interface PluginUserPageDefinition {
|
|
75
|
+
description: string;
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
read(
|
|
79
|
+
ctx: PluginUserPageContext,
|
|
80
|
+
): Promise<PluginUserPageContent> | PluginUserPageContent;
|
|
81
|
+
}
|