@sentry/junior-plugin-api 0.74.1 → 0.75.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/dist/context.d.ts +59 -0
- package/dist/credentials.d.ts +158 -0
- package/dist/database.d.ts +13 -0
- package/dist/dispatch.d.ts +13 -0
- package/dist/hooks.d.ts +17 -0
- package/dist/index.d.ts +11 -616
- package/dist/index.js +79 -59
- package/dist/manifest.d.ts +74 -0
- package/dist/operations.d.ts +93 -0
- package/dist/prompt.d.ts +42 -0
- package/dist/registration.d.ts +13 -0
- package/dist/schemas.d.ts +108 -0
- package/dist/state.d.ts +20 -0
- package/dist/tools.d.ts +107 -0
- package/package.json +2 -1
- package/src/context.ts +72 -0
- package/src/credentials.ts +200 -0
- package/src/database.ts +22 -0
- package/src/dispatch.ts +22 -0
- package/src/hooks.ts +67 -0
- package/src/index.ts +11 -875
- package/src/manifest.ts +87 -0
- package/src/operations.ts +126 -0
- package/src/prompt.ts +59 -0
- package/src/registration.ts +62 -0
- package/src/schemas.ts +161 -0
- package/src/state.ts +26 -0
- package/src/tools.ts +130 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-plugin-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"src"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
+
"drizzle-orm": "^0.45.2",
|
|
25
26
|
"zod": "^4.4.3"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
destinationSchema,
|
|
4
|
+
localRequesterSchema,
|
|
5
|
+
requesterSchema,
|
|
6
|
+
slackRequesterSchema,
|
|
7
|
+
sourceSchema,
|
|
8
|
+
} from "./schemas";
|
|
9
|
+
import type { PluginDb } from "./database";
|
|
10
|
+
|
|
11
|
+
export type Requester = z.output<typeof requesterSchema>;
|
|
12
|
+
export type SlackRequester = z.output<typeof slackRequesterSchema>;
|
|
13
|
+
export type LocalRequester = z.output<typeof localRequesterSchema>;
|
|
14
|
+
export type Source = z.output<typeof sourceSchema>;
|
|
15
|
+
export type SlackSource = Extract<Source, { platform: "slack" }>;
|
|
16
|
+
export type LocalSource = Extract<Source, { platform: "local" }>;
|
|
17
|
+
|
|
18
|
+
export type Destination = z.output<typeof destinationSchema>;
|
|
19
|
+
|
|
20
|
+
export type SlackDestination = Extract<Destination, { platform: "slack" }>;
|
|
21
|
+
|
|
22
|
+
export type LocalDestination = Extract<Destination, { platform: "local" }>;
|
|
23
|
+
|
|
24
|
+
export interface PluginMetadata {
|
|
25
|
+
name: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PluginLogger {
|
|
29
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
30
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
31
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PluginContext {
|
|
35
|
+
/** Shared database connection for plugins that declare database access. */
|
|
36
|
+
db?: PluginDb;
|
|
37
|
+
log: PluginLogger;
|
|
38
|
+
plugin: PluginMetadata;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface BaseInvocationContext {
|
|
42
|
+
/**
|
|
43
|
+
* Opaque Junior conversation/session identity for this invocation.
|
|
44
|
+
* Interactive Slack turns use `slack:{channelId}:{threadTs}`.
|
|
45
|
+
*/
|
|
46
|
+
conversationId?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface SlackInvocationContext extends BaseInvocationContext {
|
|
50
|
+
/** Runtime-owned default outbound destination for this invocation, if any. */
|
|
51
|
+
destination?: SlackDestination;
|
|
52
|
+
requester?: SlackRequester;
|
|
53
|
+
/** Runtime-owned source where the invocation came from. */
|
|
54
|
+
source: SlackSource;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface LocalInvocationContext extends BaseInvocationContext {
|
|
58
|
+
/** Runtime-owned default outbound destination for this invocation, if any. */
|
|
59
|
+
destination?: LocalDestination;
|
|
60
|
+
requester?: LocalRequester;
|
|
61
|
+
/** Runtime-owned source where the invocation came from. */
|
|
62
|
+
source: LocalSource;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type InvocationContext = LocalInvocationContext | SlackInvocationContext;
|
|
66
|
+
|
|
67
|
+
/** Narrow a runtime destination to the Slack-specific address shape. */
|
|
68
|
+
export function isSlackDestination(
|
|
69
|
+
destination: Destination | undefined,
|
|
70
|
+
): destination is SlackDestination {
|
|
71
|
+
return destination?.platform === "slack";
|
|
72
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { PluginContext } from "./context";
|
|
3
|
+
import { nonBlankStringSchema, pluginCredentialSubjectSchema } from "./schemas";
|
|
4
|
+
|
|
5
|
+
const pluginProviderNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
6
|
+
const pluginGrantNameSchema = z.string().regex(/^[a-z][a-z0-9.-]*$/);
|
|
7
|
+
const pluginGrantAccessSchema = z.union([
|
|
8
|
+
z.literal("read"),
|
|
9
|
+
z.literal("write"),
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
/** Runtime schema for provider authorization a plugin may request. */
|
|
13
|
+
export const pluginAuthorizationSchema = z
|
|
14
|
+
.object({
|
|
15
|
+
provider: pluginProviderNameSchema,
|
|
16
|
+
scope: nonBlankStringSchema.optional(),
|
|
17
|
+
type: z.literal("oauth"),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
20
|
+
|
|
21
|
+
/** Runtime schema for a provider account attached to stored OAuth tokens. */
|
|
22
|
+
export const pluginProviderAccountSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
id: nonBlankStringSchema,
|
|
25
|
+
label: nonBlankStringSchema.optional(),
|
|
26
|
+
url: nonBlankStringSchema.optional(),
|
|
27
|
+
})
|
|
28
|
+
.strict();
|
|
29
|
+
|
|
30
|
+
/** Runtime schema for a plugin-defined outbound credential grant. */
|
|
31
|
+
export const pluginGrantSchema = z
|
|
32
|
+
.object({
|
|
33
|
+
access: pluginGrantAccessSchema,
|
|
34
|
+
name: pluginGrantNameSchema,
|
|
35
|
+
reason: nonBlankStringSchema.optional(),
|
|
36
|
+
requirements: z.array(nonBlankStringSchema).min(1).optional(),
|
|
37
|
+
})
|
|
38
|
+
.strict();
|
|
39
|
+
|
|
40
|
+
/** Runtime schema for plugin-issued header mutations. */
|
|
41
|
+
export const pluginCredentialHeaderTransformSchema = z
|
|
42
|
+
.object({
|
|
43
|
+
domain: z.string().min(1),
|
|
44
|
+
headers: z
|
|
45
|
+
.record(z.string(), z.string())
|
|
46
|
+
.refine((headers) => Object.keys(headers).length > 0),
|
|
47
|
+
})
|
|
48
|
+
.strict();
|
|
49
|
+
|
|
50
|
+
/** Runtime schema for a short-lived plugin-issued credential lease. */
|
|
51
|
+
export const pluginCredentialLeaseSchema = z
|
|
52
|
+
.object({
|
|
53
|
+
account: pluginProviderAccountSchema.optional(),
|
|
54
|
+
authorization: pluginAuthorizationSchema.optional(),
|
|
55
|
+
expiresAt: z.string().refine((value) => Number.isFinite(Date.parse(value))),
|
|
56
|
+
headerTransforms: z.array(pluginCredentialHeaderTransformSchema).min(1),
|
|
57
|
+
})
|
|
58
|
+
.strict();
|
|
59
|
+
|
|
60
|
+
/** Runtime schema for the result returned by a plugin credential hook. */
|
|
61
|
+
export const pluginCredentialResultSchema = z.discriminatedUnion("type", [
|
|
62
|
+
z
|
|
63
|
+
.object({
|
|
64
|
+
lease: pluginCredentialLeaseSchema,
|
|
65
|
+
type: z.literal("lease"),
|
|
66
|
+
})
|
|
67
|
+
.strict(),
|
|
68
|
+
z
|
|
69
|
+
.object({
|
|
70
|
+
authorization: pluginAuthorizationSchema.optional(),
|
|
71
|
+
message: nonBlankStringSchema,
|
|
72
|
+
type: z.literal("needed"),
|
|
73
|
+
})
|
|
74
|
+
.strict(),
|
|
75
|
+
z
|
|
76
|
+
.object({
|
|
77
|
+
message: nonBlankStringSchema,
|
|
78
|
+
type: z.literal("unavailable"),
|
|
79
|
+
})
|
|
80
|
+
.strict(),
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
export type PluginCredentialSubject = z.output<
|
|
84
|
+
typeof pluginCredentialSubjectSchema
|
|
85
|
+
>;
|
|
86
|
+
|
|
87
|
+
export type PluginGrantAccess = z.output<typeof pluginGrantAccessSchema>;
|
|
88
|
+
|
|
89
|
+
/** Provider authorization Junior can start when a plugin-owned grant is missing. */
|
|
90
|
+
export type PluginAuthorization = z.output<typeof pluginAuthorizationSchema>;
|
|
91
|
+
|
|
92
|
+
/** Interrupt sandbox egress so Junior can start provider authorization. */
|
|
93
|
+
export class EgressAuthRequired extends Error {
|
|
94
|
+
authorization?: PluginAuthorization;
|
|
95
|
+
|
|
96
|
+
constructor(
|
|
97
|
+
message: string,
|
|
98
|
+
options?: {
|
|
99
|
+
authorization?: PluginAuthorization;
|
|
100
|
+
cause?: unknown;
|
|
101
|
+
},
|
|
102
|
+
) {
|
|
103
|
+
super(message, { cause: options?.cause });
|
|
104
|
+
this.name = "EgressAuthRequired";
|
|
105
|
+
this.authorization = options?.authorization;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Provider account identity resolved by a plugin OAuth hook. */
|
|
110
|
+
export type PluginProviderAccount = z.output<
|
|
111
|
+
typeof pluginProviderAccountSchema
|
|
112
|
+
>;
|
|
113
|
+
|
|
114
|
+
/** Plugin-defined grant required before Junior can forward one outbound request. */
|
|
115
|
+
export type PluginGrant = z.output<typeof pluginGrantSchema>;
|
|
116
|
+
|
|
117
|
+
/** Request details available while selecting the grant for sandbox egress. */
|
|
118
|
+
export interface PluginEgressRequest {
|
|
119
|
+
/** Capped request body text when the host exposes it for provider-specific grant classification. */
|
|
120
|
+
bodyText?: string;
|
|
121
|
+
method: string;
|
|
122
|
+
url: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface EgressHookContext extends PluginContext {
|
|
126
|
+
request: PluginEgressRequest;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface PluginEgressResponse {
|
|
130
|
+
/** Snapshot of upstream response headers; mutations do not affect pass-through. */
|
|
131
|
+
headers: Headers;
|
|
132
|
+
readText(maxBytes: number): Promise<string | undefined>;
|
|
133
|
+
status: number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface EgressResponseHookContext extends PluginContext {
|
|
137
|
+
grant: PluginGrant;
|
|
138
|
+
permissionDenied(message: string): void;
|
|
139
|
+
request: Omit<PluginEgressRequest, "bodyText">;
|
|
140
|
+
response: PluginEgressResponse;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Header mutations a plugin-issued credential lease may apply to owned domains. */
|
|
144
|
+
export type PluginCredentialHeaderTransform = z.output<
|
|
145
|
+
typeof pluginCredentialHeaderTransformSchema
|
|
146
|
+
>;
|
|
147
|
+
|
|
148
|
+
/** Short-lived credential headers issued by a plugin for a selected grant. */
|
|
149
|
+
export type PluginCredentialLease = z.output<
|
|
150
|
+
typeof pluginCredentialLeaseSchema
|
|
151
|
+
>;
|
|
152
|
+
|
|
153
|
+
export type PluginCredentialResult = z.output<
|
|
154
|
+
typeof pluginCredentialResultSchema
|
|
155
|
+
>;
|
|
156
|
+
|
|
157
|
+
export type PluginCredentialActor =
|
|
158
|
+
| {
|
|
159
|
+
type: "system";
|
|
160
|
+
id: string;
|
|
161
|
+
}
|
|
162
|
+
| {
|
|
163
|
+
type: "user";
|
|
164
|
+
userId: string;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export interface PluginResolvedCredentialUser {
|
|
168
|
+
type: "user";
|
|
169
|
+
userId: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface PluginStoredTokens {
|
|
173
|
+
account?: PluginProviderAccount;
|
|
174
|
+
accessToken: string;
|
|
175
|
+
expiresAt?: number;
|
|
176
|
+
refreshToken: string;
|
|
177
|
+
scope?: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface PluginUserTokenSlot {
|
|
181
|
+
get(): Promise<PluginStoredTokens | undefined>;
|
|
182
|
+
set(tokens: PluginStoredTokens): Promise<void>;
|
|
183
|
+
userId: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface PluginTokenStore {
|
|
187
|
+
credentialSubject?: PluginUserTokenSlot;
|
|
188
|
+
currentUser?: PluginUserTokenSlot;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface ResolveOAuthAccountHookContext extends PluginContext {
|
|
192
|
+
tokens: PluginStoredTokens;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface IssueCredentialHookContext extends PluginContext {
|
|
196
|
+
actor: PluginCredentialActor;
|
|
197
|
+
credentialSubject?: PluginResolvedCredentialUser;
|
|
198
|
+
grant: PluginGrant;
|
|
199
|
+
tokens: PluginTokenStore;
|
|
200
|
+
}
|
package/src/database.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PgDatabase } from "drizzle-orm/pg-core";
|
|
2
|
+
import type { PgQueryResultHKT } from "drizzle-orm/pg-core/session";
|
|
3
|
+
|
|
4
|
+
export type PluginDrizzleDatabase = PgDatabase<
|
|
5
|
+
PgQueryResultHKT,
|
|
6
|
+
Record<string, never>
|
|
7
|
+
>;
|
|
8
|
+
|
|
9
|
+
export interface PluginDb {
|
|
10
|
+
delete: PluginDrizzleDatabase["delete"];
|
|
11
|
+
execute(statement: string, params?: readonly unknown[]): Promise<void>;
|
|
12
|
+
insert: PluginDrizzleDatabase["insert"];
|
|
13
|
+
query<T = unknown>(
|
|
14
|
+
statement: string,
|
|
15
|
+
params?: readonly unknown[],
|
|
16
|
+
): Promise<T[]>;
|
|
17
|
+
select: PluginDrizzleDatabase["select"];
|
|
18
|
+
transaction<T>(callback: (tx: PluginDb) => Promise<T>): Promise<T>;
|
|
19
|
+
update: PluginDrizzleDatabase["update"];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type PluginDatabaseConfig = Record<string, never>;
|
package/src/dispatch.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { dispatchOptionsSchema } from "./schemas";
|
|
3
|
+
|
|
4
|
+
export type DispatchOptions = z.output<typeof dispatchOptionsSchema>;
|
|
5
|
+
|
|
6
|
+
export interface DispatchResult {
|
|
7
|
+
id: string;
|
|
8
|
+
status: "created" | "already_exists";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Dispatch {
|
|
12
|
+
errorMessage?: string;
|
|
13
|
+
id: string;
|
|
14
|
+
resultMessageTs?: string;
|
|
15
|
+
status:
|
|
16
|
+
| "pending"
|
|
17
|
+
| "running"
|
|
18
|
+
| "awaiting_resume"
|
|
19
|
+
| "completed"
|
|
20
|
+
| "failed"
|
|
21
|
+
| "blocked";
|
|
22
|
+
}
|
package/src/hooks.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EgressHookContext,
|
|
3
|
+
EgressResponseHookContext,
|
|
4
|
+
IssueCredentialHookContext,
|
|
5
|
+
PluginCredentialResult,
|
|
6
|
+
PluginGrant,
|
|
7
|
+
PluginProviderAccount,
|
|
8
|
+
ResolveOAuthAccountHookContext,
|
|
9
|
+
} from "./credentials";
|
|
10
|
+
import type {
|
|
11
|
+
HeartbeatHookContext,
|
|
12
|
+
HeartbeatResult,
|
|
13
|
+
OperationalReportHookContext,
|
|
14
|
+
PluginOperationalReportContent,
|
|
15
|
+
PluginRoute,
|
|
16
|
+
RouteRegistrationHookContext,
|
|
17
|
+
SlackConversationLink,
|
|
18
|
+
SlackConversationLinkHookContext,
|
|
19
|
+
StorageMigrationContext,
|
|
20
|
+
StorageMigrationResult,
|
|
21
|
+
} from "./operations";
|
|
22
|
+
import type {
|
|
23
|
+
BeforeToolExecuteHookContext,
|
|
24
|
+
PluginToolDefinition,
|
|
25
|
+
SandboxPrepareHookContext,
|
|
26
|
+
ToolRegistrationHookContext,
|
|
27
|
+
} from "./tools";
|
|
28
|
+
|
|
29
|
+
export interface PluginHooks {
|
|
30
|
+
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
|
31
|
+
grantForEgress?(
|
|
32
|
+
ctx: EgressHookContext,
|
|
33
|
+
): Promise<PluginGrant | undefined> | PluginGrant | undefined;
|
|
34
|
+
heartbeat?(
|
|
35
|
+
ctx: HeartbeatHookContext,
|
|
36
|
+
): Promise<HeartbeatResult | void> | HeartbeatResult | void;
|
|
37
|
+
issueCredential?(
|
|
38
|
+
ctx: IssueCredentialHookContext,
|
|
39
|
+
): Promise<PluginCredentialResult> | PluginCredentialResult;
|
|
40
|
+
onEgressResponse?(ctx: EgressResponseHookContext): Promise<void> | void;
|
|
41
|
+
operationalReport?(
|
|
42
|
+
ctx: OperationalReportHookContext,
|
|
43
|
+
):
|
|
44
|
+
| Promise<PluginOperationalReportContent | undefined>
|
|
45
|
+
| PluginOperationalReportContent
|
|
46
|
+
| undefined;
|
|
47
|
+
resolveOAuthAccount?(
|
|
48
|
+
ctx: ResolveOAuthAccountHookContext,
|
|
49
|
+
):
|
|
50
|
+
| Promise<PluginProviderAccount | undefined>
|
|
51
|
+
| PluginProviderAccount
|
|
52
|
+
| undefined;
|
|
53
|
+
routes?(ctx: RouteRegistrationHookContext): PluginRoute[];
|
|
54
|
+
sandboxPrepare?(ctx: SandboxPrepareHookContext): Promise<void> | void;
|
|
55
|
+
slackConversationLink?(
|
|
56
|
+
ctx: SlackConversationLinkHookContext,
|
|
57
|
+
): SlackConversationLink | undefined;
|
|
58
|
+
tools?(
|
|
59
|
+
ctx: ToolRegistrationHookContext,
|
|
60
|
+
): Record<string, PluginToolDefinition>;
|
|
61
|
+
migrateStorage?(
|
|
62
|
+
ctx: StorageMigrationContext,
|
|
63
|
+
):
|
|
64
|
+
| Promise<StorageMigrationResult | undefined>
|
|
65
|
+
| StorageMigrationResult
|
|
66
|
+
| undefined;
|
|
67
|
+
}
|