@sentry/junior-sentry 0.134.0 → 0.136.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 +23 -12
- package/dist/chunk-5IAUCX6U.js +183 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +81 -0
- package/dist/resource-events/issue.d.ts +13 -0
- package/dist/testing.d.ts +4 -0
- package/dist/testing.js +16 -0
- package/dist/webhooks/handler.d.ts +7 -0
- package/dist/webhooks/resource-events.d.ts +8 -0
- package/dist/webhooks/secret.d.ts +4 -0
- package/package.json +30 -3
- package/skills/sentry/SOURCES.md +18 -18
- package/skills/sentry/SPEC.md +1 -1
- package/plugin.yaml +0 -27
package/README.md
CHANGED
|
@@ -1,34 +1,45 @@
|
|
|
1
1
|
# @sentry/junior-sentry
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Install it alongside `@sentry/junior`:
|
|
3
|
+
Sentry investigations (per-user OAuth) and signed issue webhooks (internal integration) for Junior.
|
|
6
4
|
|
|
7
5
|
```bash
|
|
8
6
|
pnpm add @sentry/junior @sentry/junior-sentry
|
|
9
7
|
```
|
|
10
8
|
|
|
11
|
-
Add the package name to the plugin set exported from `plugins.ts`:
|
|
12
|
-
|
|
13
9
|
```ts
|
|
14
10
|
import { defineJuniorPlugins } from "@sentry/junior";
|
|
11
|
+
import { sentryPlugin } from "@sentry/junior-sentry";
|
|
15
12
|
|
|
16
|
-
export const plugins = defineJuniorPlugins([
|
|
13
|
+
export const plugins = defineJuniorPlugins([sentryPlugin()]);
|
|
17
14
|
```
|
|
18
15
|
|
|
19
|
-
##
|
|
16
|
+
## User OAuth
|
|
17
|
+
|
|
18
|
+
Set `SENTRY_CLIENT_ID` and `SENTRY_CLIENT_SECRET` from a Sentry OAuth app whose redirect is:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
<base-url>/api/oauth/callback/sentry
|
|
22
|
+
```
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
Junior injects the user's token as `SENTRY_AUTH_TOKEN` for skill/CLI commands. Reconnect after scope changes.
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
Verified CLI surface (check live `sentry --help` before blocking):
|
|
24
27
|
|
|
25
28
|
- `sentry issue list|events|explain|plan|view`
|
|
26
29
|
- `sentry org list|view`
|
|
27
30
|
- `sentry log list|view`
|
|
28
31
|
- `sentry trace list|view|logs`
|
|
29
32
|
- `sentry alert metrics list|view|create|edit|delete`
|
|
30
|
-
- `sentry api <endpoint>`
|
|
33
|
+
- `sentry api <endpoint>` fallback
|
|
34
|
+
|
|
35
|
+
## Issue webhooks
|
|
36
|
+
|
|
37
|
+
Use a **Sentry internal integration** (not a public app install flow):
|
|
38
|
+
|
|
39
|
+
1. Subscribe to the **issue** webhook resource.
|
|
40
|
+
2. Point it at `https://<junior-host>/api/webhooks/sentry`.
|
|
41
|
+
3. Set the org slug as `SENTRY_WEBHOOK_ORG` and the integration client secret as `SENTRY_WEBHOOK_SECRET`, then redeploy.
|
|
31
42
|
|
|
32
|
-
|
|
43
|
+
Junior verifies `Sentry-Hook-Signature`, rejects payloads outside `SENTRY_WEBHOOK_ORG`, and publishes `issue.created` for `org/project#issueId` and `org/project`. Create watches/event tasks before the issue arrives; unmatched deliveries are not replayed.
|
|
33
44
|
|
|
34
|
-
Full
|
|
45
|
+
Full guide: https://junior.sentry.dev/extend/sentry-plugin/
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// src/resource-events/issue.ts
|
|
2
|
+
var SENTRY_ISSUE_EVENTS = ["issue.created"];
|
|
3
|
+
function sentryIssueResource(input) {
|
|
4
|
+
return {
|
|
5
|
+
identifier: `${input.org}/${input.project}#${input.issueId}`,
|
|
6
|
+
label: `Sentry issue ${input.org}/${input.project}#${input.issueId}`,
|
|
7
|
+
namespace: "sentry"
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function sentryProjectResource(input) {
|
|
11
|
+
return {
|
|
12
|
+
identifier: `${input.org}/${input.project}`,
|
|
13
|
+
label: `Sentry project ${input.org}/${input.project}`,
|
|
14
|
+
namespace: "sentry"
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/webhooks/resource-events.ts
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
var issueWebhookSchema = z.object({
|
|
21
|
+
action: z.string(),
|
|
22
|
+
data: z.object({
|
|
23
|
+
issue: z.object({
|
|
24
|
+
culprit: z.string().optional(),
|
|
25
|
+
firstSeen: z.string().optional(),
|
|
26
|
+
id: z.string().min(1),
|
|
27
|
+
issueCategory: z.string().optional(),
|
|
28
|
+
issueType: z.string().optional(),
|
|
29
|
+
level: z.string().optional(),
|
|
30
|
+
priority: z.string().optional(),
|
|
31
|
+
project: z.object({ slug: z.string().min(1) }).passthrough(),
|
|
32
|
+
status: z.string().optional(),
|
|
33
|
+
substatus: z.string().optional().nullable(),
|
|
34
|
+
title: z.string().optional(),
|
|
35
|
+
url: z.string().url(),
|
|
36
|
+
web_url: z.string().url().optional()
|
|
37
|
+
}).passthrough()
|
|
38
|
+
}),
|
|
39
|
+
installation: z.object({ uuid: z.string().min(1) }).passthrough()
|
|
40
|
+
}).passthrough();
|
|
41
|
+
function organizationSlug(issueApiUrl) {
|
|
42
|
+
try {
|
|
43
|
+
const segments = new URL(issueApiUrl).pathname.split("/").filter(Boolean);
|
|
44
|
+
const organizationsIndex = segments.indexOf("organizations");
|
|
45
|
+
return organizationsIndex >= 0 ? segments[organizationsIndex + 1] : void 0;
|
|
46
|
+
} catch {
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function providerTime(value) {
|
|
51
|
+
if (!value) return void 0;
|
|
52
|
+
if (/^\d+$/.test(value)) {
|
|
53
|
+
const seconds = Number(value);
|
|
54
|
+
return Number.isSafeInteger(seconds) ? seconds * 1e3 : void 0;
|
|
55
|
+
}
|
|
56
|
+
const parsed = Date.parse(value);
|
|
57
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
58
|
+
}
|
|
59
|
+
function issueText(issue) {
|
|
60
|
+
const parts = [
|
|
61
|
+
issue.title ? `Title: ${issue.title}` : void 0,
|
|
62
|
+
issue.culprit ? `Culprit: ${issue.culprit}` : void 0,
|
|
63
|
+
issue.level ? `Level: ${issue.level}` : void 0,
|
|
64
|
+
issue.priority ? `Priority: ${issue.priority}` : void 0,
|
|
65
|
+
issue.issueCategory ? `Category: ${issue.issueCategory}` : void 0,
|
|
66
|
+
issue.issueType ? `Type: ${issue.issueType}` : void 0,
|
|
67
|
+
issue.status ? `Status: ${issue.status}` : void 0,
|
|
68
|
+
issue.substatus ? `Substatus: ${issue.substatus}` : void 0,
|
|
69
|
+
issue.web_url ? `URL: ${issue.web_url}` : void 0
|
|
70
|
+
].filter((part) => part !== void 0);
|
|
71
|
+
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
72
|
+
}
|
|
73
|
+
function normalizeSentryResourceEvents(input) {
|
|
74
|
+
if (input.hookResource !== "issue") return [];
|
|
75
|
+
const parsed = issueWebhookSchema.safeParse(input.body);
|
|
76
|
+
if (!parsed.success || parsed.data.action !== "created") return [];
|
|
77
|
+
const issue = parsed.data.data.issue;
|
|
78
|
+
const org = organizationSlug(issue.url)?.toLowerCase();
|
|
79
|
+
if (!org || org !== input.webhookOrg.toLowerCase()) return [];
|
|
80
|
+
const issueResource = sentryIssueResource({
|
|
81
|
+
issueId: issue.id,
|
|
82
|
+
org,
|
|
83
|
+
project: issue.project.slug
|
|
84
|
+
});
|
|
85
|
+
const projectResource = sentryProjectResource({
|
|
86
|
+
org,
|
|
87
|
+
project: issue.project.slug
|
|
88
|
+
});
|
|
89
|
+
const eventType = "issue.created";
|
|
90
|
+
const occurredAtMs = providerTime(issue.firstSeen) ?? providerTime(input.hookTimestamp) ?? Date.now();
|
|
91
|
+
const untrustedText = issueText(issue);
|
|
92
|
+
const event = {
|
|
93
|
+
// Sentry assigns a new Request-ID to retries, so use resource identity for
|
|
94
|
+
// idempotency across repeated deliveries of the same lifecycle event.
|
|
95
|
+
eventKey: `sentry:${issueResource.identifier}:${eventType}`,
|
|
96
|
+
eventType,
|
|
97
|
+
occurredAtMs,
|
|
98
|
+
trustedSummary: `${issueResource.label} was created.`,
|
|
99
|
+
...untrustedText ? { untrustedText } : {}
|
|
100
|
+
};
|
|
101
|
+
return [
|
|
102
|
+
{ ...event, identifier: issueResource.identifier },
|
|
103
|
+
{ ...event, identifier: projectResource.identifier }
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/webhooks/handler.ts
|
|
108
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
109
|
+
function verifySentrySignature(body, signature, secret) {
|
|
110
|
+
if (!secret || !/^[0-9a-f]{64}$/i.test(signature)) return false;
|
|
111
|
+
const actual = Buffer.from(signature.toLowerCase());
|
|
112
|
+
const expected = Buffer.from(
|
|
113
|
+
createHmac("sha256", secret).update(body).digest("hex")
|
|
114
|
+
);
|
|
115
|
+
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
116
|
+
}
|
|
117
|
+
function parseJson(body) {
|
|
118
|
+
try {
|
|
119
|
+
return JSON.parse(body);
|
|
120
|
+
} catch {
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function createSentryWebhookRoute(args) {
|
|
125
|
+
return {
|
|
126
|
+
method: "POST",
|
|
127
|
+
path: "/api/webhooks/sentry",
|
|
128
|
+
async handler(request) {
|
|
129
|
+
const rawBody = await request.text();
|
|
130
|
+
const signature = request.headers.get("sentry-hook-signature") ?? "";
|
|
131
|
+
if (!verifySentrySignature(rawBody, signature, args.webhookSecret())) {
|
|
132
|
+
return new Response("Unauthorized", { status: 401 });
|
|
133
|
+
}
|
|
134
|
+
const body = parseJson(rawBody);
|
|
135
|
+
if (body === void 0) {
|
|
136
|
+
return new Response("Malformed Sentry webhook", { status: 400 });
|
|
137
|
+
}
|
|
138
|
+
const requestId = request.headers.get("request-id")?.trim();
|
|
139
|
+
const hookResource = request.headers.get("sentry-hook-resource")?.trim();
|
|
140
|
+
if (!requestId || !hookResource) {
|
|
141
|
+
return new Response("Malformed Sentry webhook headers", {
|
|
142
|
+
status: 400
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
const webhookOrg = args.webhookOrg();
|
|
146
|
+
if (!webhookOrg) {
|
|
147
|
+
return new Response("Sentry webhook organization is not configured", {
|
|
148
|
+
status: 503
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const events = normalizeSentryResourceEvents({
|
|
152
|
+
body,
|
|
153
|
+
hookResource,
|
|
154
|
+
hookTimestamp: request.headers.get("sentry-hook-timestamp")?.trim() || void 0,
|
|
155
|
+
webhookOrg
|
|
156
|
+
});
|
|
157
|
+
for (const event of events) {
|
|
158
|
+
await args.resourceEvents.publish(event);
|
|
159
|
+
}
|
|
160
|
+
return new Response(events.length ? "Accepted" : "Ignored", {
|
|
161
|
+
status: 202
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/webhooks/secret.ts
|
|
168
|
+
function sentryWebhookSecret() {
|
|
169
|
+
return process.env.SENTRY_WEBHOOK_SECRET?.trim() || void 0;
|
|
170
|
+
}
|
|
171
|
+
function sentryWebhookOrg() {
|
|
172
|
+
return process.env.SENTRY_WEBHOOK_ORG?.trim().toLowerCase() || void 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export {
|
|
176
|
+
SENTRY_ISSUE_EVENTS,
|
|
177
|
+
sentryIssueResource,
|
|
178
|
+
sentryProjectResource,
|
|
179
|
+
normalizeSentryResourceEvents,
|
|
180
|
+
createSentryWebhookRoute,
|
|
181
|
+
sentryWebhookSecret,
|
|
182
|
+
sentryWebhookOrg
|
|
183
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sentry plugin runtime boundary.
|
|
3
|
+
*
|
|
4
|
+
* This package owns per-user Sentry OAuth, CLI setup, internal-integration
|
|
5
|
+
* issue webhook normalization, and Sentry resource identities. Junior core owns
|
|
6
|
+
* watches and event tasks.
|
|
7
|
+
*/
|
|
8
|
+
import { type PluginRegistration } from "@sentry/junior-plugin-api";
|
|
9
|
+
/** Register Sentry runtime metadata and signed resource-event ingress. */
|
|
10
|
+
export declare function sentryPlugin(): PluginRegistration;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SENTRY_ISSUE_EVENTS,
|
|
3
|
+
createSentryWebhookRoute,
|
|
4
|
+
sentryWebhookOrg,
|
|
5
|
+
sentryWebhookSecret
|
|
6
|
+
} from "./chunk-5IAUCX6U.js";
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
import {
|
|
10
|
+
defineJuniorPlugin
|
|
11
|
+
} from "@sentry/junior-plugin-api";
|
|
12
|
+
function sentryPlugin() {
|
|
13
|
+
return defineJuniorPlugin({
|
|
14
|
+
packageName: "@sentry/junior-sentry",
|
|
15
|
+
resourceEvents: {
|
|
16
|
+
resourceTypes: [
|
|
17
|
+
{
|
|
18
|
+
type: "issue",
|
|
19
|
+
supportedEvents: [...SENTRY_ISSUE_EVENTS],
|
|
20
|
+
suggestedEvents: [...SENTRY_ISSUE_EVENTS]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: "project",
|
|
24
|
+
supportedEvents: [...SENTRY_ISSUE_EVENTS],
|
|
25
|
+
suggestedEvents: [...SENTRY_ISSUE_EVENTS]
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
isEnabled: () => Boolean(sentryWebhookSecret()) && Boolean(sentryWebhookOrg()),
|
|
29
|
+
normalizeIdentifier: (identifier) => identifier.toLowerCase()
|
|
30
|
+
},
|
|
31
|
+
manifest: {
|
|
32
|
+
commandEnv: {
|
|
33
|
+
SENTRY_AUTH_TOKEN: "host_managed_credential"
|
|
34
|
+
},
|
|
35
|
+
configKeys: ["org", "project"],
|
|
36
|
+
credentials: {
|
|
37
|
+
authTokenEnv: "SENTRY_AUTH_TOKEN",
|
|
38
|
+
authTokenPlaceholder: "host_managed_credential",
|
|
39
|
+
domains: ["sentry.io", "us.sentry.io", "de.sentry.io"],
|
|
40
|
+
type: "oauth-bearer"
|
|
41
|
+
},
|
|
42
|
+
description: "Investigate Sentry telemetry, manage alerting, and receive issue webhooks",
|
|
43
|
+
displayName: "Sentry",
|
|
44
|
+
envVars: {
|
|
45
|
+
SENTRY_CLIENT_ID: {},
|
|
46
|
+
SENTRY_CLIENT_SECRET: {},
|
|
47
|
+
SENTRY_WEBHOOK_ORG: {},
|
|
48
|
+
SENTRY_WEBHOOK_SECRET: {}
|
|
49
|
+
},
|
|
50
|
+
name: "sentry",
|
|
51
|
+
oauth: {
|
|
52
|
+
authorizeEndpoint: "https://sentry.io/oauth/authorize/",
|
|
53
|
+
clientIdEnv: "SENTRY_CLIENT_ID",
|
|
54
|
+
clientSecretEnv: "SENTRY_CLIENT_SECRET",
|
|
55
|
+
scope: "alerts:write event:write member:read org:read project:releases project:write team:write",
|
|
56
|
+
tokenEndpoint: "https://sentry.io/oauth/token/"
|
|
57
|
+
},
|
|
58
|
+
runtimeDependencies: [
|
|
59
|
+
{
|
|
60
|
+
package: "sentry",
|
|
61
|
+
type: "npm",
|
|
62
|
+
version: "latest"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
hooks: {
|
|
67
|
+
routes(ctx) {
|
|
68
|
+
return [
|
|
69
|
+
createSentryWebhookRoute({
|
|
70
|
+
resourceEvents: ctx.resourceEvents,
|
|
71
|
+
webhookOrg: sentryWebhookOrg,
|
|
72
|
+
webhookSecret: sentryWebhookSecret
|
|
73
|
+
})
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
sentryPlugin
|
|
81
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SubscribableResource } from "@sentry/junior-plugin-api";
|
|
2
|
+
export declare const SENTRY_ISSUE_EVENTS: readonly ["issue.created"];
|
|
3
|
+
/** Build the stable Sentry issue identity shared by tools and webhooks. */
|
|
4
|
+
export declare function sentryIssueResource(input: {
|
|
5
|
+
issueId: string;
|
|
6
|
+
org: string;
|
|
7
|
+
project: string;
|
|
8
|
+
}): Pick<SubscribableResource, "identifier" | "label" | "namespace">;
|
|
9
|
+
/** Build the stable Sentry project identity used for project-scoped events. */
|
|
10
|
+
export declare function sentryProjectResource(input: {
|
|
11
|
+
org: string;
|
|
12
|
+
project: string;
|
|
13
|
+
}): Pick<SubscribableResource, "identifier" | "label" | "namespace">;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { sentryIssueResource, sentryProjectResource, } from "./resource-events/issue.js";
|
|
2
|
+
export { createSentryWebhookRoute } from "./webhooks/handler.js";
|
|
3
|
+
export { normalizeSentryResourceEvents } from "./webhooks/resource-events.js";
|
|
4
|
+
export { sentryWebhookOrg, sentryWebhookSecret } from "./webhooks/secret.js";
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSentryWebhookRoute,
|
|
3
|
+
normalizeSentryResourceEvents,
|
|
4
|
+
sentryIssueResource,
|
|
5
|
+
sentryProjectResource,
|
|
6
|
+
sentryWebhookOrg,
|
|
7
|
+
sentryWebhookSecret
|
|
8
|
+
} from "./chunk-5IAUCX6U.js";
|
|
9
|
+
export {
|
|
10
|
+
createSentryWebhookRoute,
|
|
11
|
+
normalizeSentryResourceEvents,
|
|
12
|
+
sentryIssueResource,
|
|
13
|
+
sentryProjectResource,
|
|
14
|
+
sentryWebhookOrg,
|
|
15
|
+
sentryWebhookSecret
|
|
16
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PluginRoute, ResourceEventPublisher } from "@sentry/junior-plugin-api";
|
|
2
|
+
/** Create the public, signed Sentry resource-event webhook route. */
|
|
3
|
+
export declare function createSentryWebhookRoute(args: {
|
|
4
|
+
resourceEvents: ResourceEventPublisher;
|
|
5
|
+
webhookOrg(): string | undefined;
|
|
6
|
+
webhookSecret(): string | undefined;
|
|
7
|
+
}): PluginRoute;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ResourceEventInput } from "@sentry/junior-plugin-api";
|
|
2
|
+
/** Normalize one verified Sentry webhook into issue- and project-scoped events. */
|
|
3
|
+
export declare function normalizeSentryResourceEvents(input: {
|
|
4
|
+
body: unknown;
|
|
5
|
+
hookResource: string;
|
|
6
|
+
hookTimestamp?: string;
|
|
7
|
+
webhookOrg: string;
|
|
8
|
+
}): ResourceEventInput[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Read the internal-integration client secret used for issue webhook ingress. */
|
|
2
|
+
export declare function sentryWebhookSecret(): string | undefined;
|
|
3
|
+
/** Read the Sentry organization allowed to publish issue webhooks. */
|
|
4
|
+
export declare function sentryWebhookOrg(): string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-sentry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.136.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -11,8 +11,35 @@
|
|
|
11
11
|
"url": "git+https://github.com/getsentry/junior.git",
|
|
12
12
|
"directory": "packages/junior-sentry"
|
|
13
13
|
},
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./testing": {
|
|
20
|
+
"types": "./dist/testing.d.ts",
|
|
21
|
+
"default": "./dist/testing.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
14
24
|
"files": [
|
|
15
|
-
"
|
|
25
|
+
"dist",
|
|
16
26
|
"skills"
|
|
17
|
-
]
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"zod": "^4.4.3",
|
|
30
|
+
"@sentry/junior-plugin-api": "0.136.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^25.9.1",
|
|
34
|
+
"oxlint": "^1.66.0",
|
|
35
|
+
"tsup": "^8.5.1",
|
|
36
|
+
"typescript": "^6.0.3",
|
|
37
|
+
"vitest": "^4.1.7"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
41
|
+
"lint": "oxlint --config ../junior/.oxlintrc.json --deny-warnings src tests tsup.config.ts vitest.config.ts",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
}
|
|
18
45
|
}
|
package/skills/sentry/SOURCES.md
CHANGED
|
@@ -4,22 +4,22 @@ Last updated: 2026-07-13
|
|
|
4
4
|
|
|
5
5
|
## Source inventory
|
|
6
6
|
|
|
7
|
-
| Source
|
|
8
|
-
|
|
|
9
|
-
| `https://github.com/getsentry/junior/issues/271`
|
|
10
|
-
| `https://cli.sentry.dev/commands/issue/`
|
|
11
|
-
| `https://cli.sentry.dev/commands/org/`
|
|
12
|
-
| `https://cli.sentry.dev/commands/log/`
|
|
13
|
-
| `https://cli.sentry.dev/commands/trace/`
|
|
14
|
-
| `https://cli.sentry.dev/commands/api/`
|
|
15
|
-
| `https://cli.sentry.dev/configuration/`
|
|
16
|
-
| `pnpm view sentry version dist-tags description bin repository`
|
|
17
|
-
| `pnpm dlx sentry@latest --help` and subcommand help
|
|
18
|
-
| `packages/junior-sentry/
|
|
19
|
-
| `https://github.com/getsentry/junior/issues/615`
|
|
20
|
-
| `https://docs.sentry.io/api/monitors/create-a-monitor-for-a-project/`
|
|
21
|
-
| `https://docs.sentry.io/api/monitors/create-an-alert-for-an-organization/` | canonical
|
|
22
|
-
| `getsentry/sentry` workflow engine endpoint and frontend form sources
|
|
7
|
+
| Source | Trust tier | Confidence | Contribution | Usage constraints |
|
|
8
|
+
| -------------------------------------------------------------------------- | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | ------ | ---- | ---------------------------------------------------------------------------------- | ------------------------------------- |
|
|
9
|
+
| `https://github.com/getsentry/junior/issues/271` | canonical | high | Regression report: Junior tried stale `sentry organizations list` and should verify current CLI help before blocking. | Use as issue context, not as a full command reference. |
|
|
10
|
+
| `https://cli.sentry.dev/commands/issue/` | canonical | high | Current `sentry issue list`, target syntax, issue subcommands, and JSON support. | Verify live help when runtime CLI differs. |
|
|
11
|
+
| `https://cli.sentry.dev/commands/org/` | canonical | high | Current `sentry org list` and `sentry org view` commands. | Verify live help when runtime CLI differs. |
|
|
12
|
+
| `https://cli.sentry.dev/commands/log/` | canonical | high | Current `sentry log list` and `sentry log view` commands, trace filtering, and log query flags. | Verify live help when runtime CLI differs. |
|
|
13
|
+
| `https://cli.sentry.dev/commands/trace/` | canonical | high | Current `sentry trace list`, `view`, and `logs` commands. | Verify live help when runtime CLI differs. |
|
|
14
|
+
| `https://cli.sentry.dev/commands/api/` | canonical | high | Authenticated `sentry api <endpoint>` fallback and request flags. | Use read-only requests unless the user asks for mutation. |
|
|
15
|
+
| `https://cli.sentry.dev/configuration/` | canonical | high | `SENTRY_AUTH_TOKEN`, JSON/global flags, cache controls, and runtime configuration behavior. | Junior injects credentials; do not persist or print tokens. |
|
|
16
|
+
| `pnpm view sentry version dist-tags description bin repository` | canonical | high | Confirmed npm package `sentry` latest is `0.38.0` and exposes the `sentry` binary. | Package metadata only; command behavior still comes from help/docs. |
|
|
17
|
+
| `pnpm dlx sentry@latest --help` and subcommand help | canonical | high | Confirmed `alert metrics list | view | create | edit | delete`, including triggers and dry-run, plus the existing investigation commands. | Re-run when updating for a newer CLI. |
|
|
18
|
+
| `packages/junior-sentry/src/index.ts` | canonical | high | Confirms runtime dependency is the npm `sentry` package, auth token env is `SENTRY_AUTH_TOKEN`, and optional `SENTRY_WEBHOOK_SECRET` enables issue webhooks. | Local repo contract. |
|
|
19
|
+
| `https://github.com/getsentry/junior/issues/615` | canonical | high | Regression report: Sentry product feature usage routed to Hex, then an explicit "use Sentry telemetry" redirect was ignored after Hex auth paused. | Use as routing evidence, not as command reference. |
|
|
20
|
+
| `https://docs.sentry.io/api/monitors/create-a-monitor-for-a-project/` | canonical | high | Current public monitor creation endpoint, payload fields, and metric monitor examples. | Alerting API may evolve; verify live docs before writes. |
|
|
21
|
+
| `https://docs.sentry.io/api/monitors/create-an-alert-for-an-organization/` | canonical | high | Current public alert workflow endpoint, connection fields, conditions, and actions. | Resolve integration and target IDs; never guess action identifiers. |
|
|
22
|
+
| `getsentry/sentry` workflow engine endpoint and frontend form sources | canonical | high | Confirms `alerts:write`, `detectors`/`workflows` paths, dynamic anomaly payload shape, and legacy alert-rule deprecation. | Source-backed implementation detail; public API docs remain the user-facing contract. |
|
|
23
23
|
|
|
24
24
|
## Decisions
|
|
25
25
|
|
|
@@ -33,8 +33,8 @@ Last updated: 2026-07-13
|
|
|
33
33
|
| Preserve stale plural subcommands as recommended forms. | rejected | `organizations list` was the root failure; aliases should not be taught as canonical command shapes. |
|
|
34
34
|
| Create a broad new troubleshooting reference. | deferred | Current failure modes fit in the focused CLI reference without crowding `SKILL.md`. |
|
|
35
35
|
| Permit explicitly requested alert/monitor writes only. | adopted | `alerts:write` is intentionally narrow; unrelated Sentry mutations remain out of scope. |
|
|
36
|
-
| Prefer first-class `sentry alert metrics` commands.
|
|
37
|
-
| Keep API fallback for dynamic anomaly detection.
|
|
36
|
+
| Prefer first-class `sentry alert metrics` commands. | adopted | CLI `0.38.0` supports list, view, create, edit, delete, triggers, and dry-run. |
|
|
37
|
+
| Keep API fallback for dynamic anomaly detection. | adopted | The current CLI create flags expose threshold triggers but no dynamic/anomaly configuration. |
|
|
38
38
|
|
|
39
39
|
## Coverage matrix
|
|
40
40
|
|
package/skills/sentry/SPEC.md
CHANGED
|
@@ -50,7 +50,7 @@ Out of scope:
|
|
|
50
50
|
Authoritative sources:
|
|
51
51
|
|
|
52
52
|
- Current Sentry CLI docs and live `sentry --help` output.
|
|
53
|
-
- The Sentry plugin
|
|
53
|
+
- The Sentry plugin runtime registration (`packages/junior-sentry/src/index.ts`) and Junior runtime contracts.
|
|
54
54
|
- GitHub issues or PRs that document observed skill failures.
|
|
55
55
|
|
|
56
56
|
Useful improvement sources:
|
package/plugin.yaml
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
name: sentry
|
|
2
|
-
display-name: Sentry
|
|
3
|
-
description: Sentry issue tracking
|
|
4
|
-
|
|
5
|
-
config-keys:
|
|
6
|
-
- org
|
|
7
|
-
- project
|
|
8
|
-
|
|
9
|
-
credentials:
|
|
10
|
-
type: oauth-bearer
|
|
11
|
-
domains:
|
|
12
|
-
- sentry.io
|
|
13
|
-
- us.sentry.io
|
|
14
|
-
- de.sentry.io
|
|
15
|
-
auth-token-env: SENTRY_AUTH_TOKEN
|
|
16
|
-
auth-token-placeholder: host_managed_credential
|
|
17
|
-
|
|
18
|
-
oauth:
|
|
19
|
-
client-id-env: SENTRY_CLIENT_ID
|
|
20
|
-
client-secret-env: SENTRY_CLIENT_SECRET
|
|
21
|
-
authorize-endpoint: https://sentry.io/oauth/authorize/
|
|
22
|
-
token-endpoint: https://sentry.io/oauth/token/
|
|
23
|
-
scope: "alerts:write event:write member:read org:read project:releases project:write team:write"
|
|
24
|
-
|
|
25
|
-
runtime-dependencies:
|
|
26
|
-
- type: npm
|
|
27
|
-
package: "sentry"
|