From 0fdde187bad35001ffa9d4ecd544ab68d001c295 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:15:48 +0000 Subject: [PATCH 1/2] feat(pagerduty): Add read-only PagerDuty plugin Add @sentry/junior-pagerduty, a manifest-only plugin that injects a deployment-level PagerDuty REST API key on sandbox requests to api.pagerduty.com. The bundled skill covers on-call, handoff, schedule, escalation policy, service, team, and incident lookups with GET requests. Refs #2233 Co-Authored-By: David Cramer --- .craft.yml | 3 + .github/workflows/ci.yml | 2 + CONTRIBUTING.md | 1 + README.md | 1 + package.json | 2 +- packages/docs/astro.config.mjs | 5 ++ .../src/content/docs/contribute/releasing.md | 1 + .../docs/src/content/docs/extend/index.md | 1 + .../content/docs/extend/pagerduty-plugin.md | 85 +++++++++++++++++++ packages/junior-pagerduty/README.md | 37 ++++++++ packages/junior-pagerduty/package.json | 18 ++++ packages/junior-pagerduty/plugin.yaml | 14 +++ .../skills/pagerduty/SKILL.md | 62 ++++++++++++++ pnpm-lock.yaml | 2 + scripts/bump-release-versions.mjs | 1 + 15 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 packages/docs/src/content/docs/extend/pagerduty-plugin.md create mode 100644 packages/junior-pagerduty/README.md create mode 100644 packages/junior-pagerduty/package.json create mode 100644 packages/junior-pagerduty/plugin.yaml create mode 100644 packages/junior-pagerduty/skills/pagerduty/SKILL.md diff --git a/.craft.yml b/.craft.yml index 868d5085c8..93fd5ce543 100644 --- a/.craft.yml +++ b/.craft.yml @@ -55,6 +55,9 @@ targets: - name: npm id: "@sentry/junior-octolens" includeNames: /^sentry-junior-octolens-\d.*\.tgz$/ + - name: npm + id: "@sentry/junior-pagerduty" + includeNames: /^sentry-junior-pagerduty-\d.*\.tgz$/ - name: npm id: "@sentry/junior-sentry" includeNames: /^sentry-junior-sentry-\d.*\.tgz$/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6a39d14a2..0f26bdea81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,6 +83,7 @@ jobs: - packages/junior-memory/** - packages/junior-notion/** - packages/junior-octolens/** + - packages/junior-pagerduty/** - packages/junior-sentry/** - packages/junior-vercel/** repo: @@ -501,6 +502,7 @@ jobs: pnpm --filter @sentry/junior-memory pack --pack-destination artifacts pnpm --filter @sentry/junior-notion pack --pack-destination artifacts pnpm --filter @sentry/junior-octolens pack --pack-destination artifacts + pnpm --filter @sentry/junior-pagerduty pack --pack-destination artifacts pnpm --filter @sentry/junior-maintenance pack --pack-destination artifacts pnpm --filter @sentry/junior-sentry pack --pack-destination artifacts pnpm --filter @sentry/junior-vercel pack --pack-destination artifacts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b08b933e04..4bccef961b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -139,6 +139,7 @@ This repo uses Craft for manual lockstep npm releases of: - `@sentry/junior-memory` - `@sentry/junior-notion` - `@sentry/junior-octolens` +- `@sentry/junior-pagerduty` - `@sentry/junior-sentry` - `@sentry/junior-vercel` diff --git a/README.md b/README.md index af11a45ee3..76b6d49ddb 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Start here: | `@sentry/junior-memory` | Memory plugin package for long-term Junior memory storage | | `@sentry/junior-notion` | Notion plugin package for page search workflows | | `@sentry/junior-octolens` | Octolens plugin package for social-listening workflows | +| `@sentry/junior-pagerduty` | Read-only PagerDuty on-call, schedule, and incident lookups | | `@sentry/junior-maintenance` | Maintenance plugin package for updating and improving Junior apps | | `@sentry/junior-sentry` | Sentry investigations and issue events | | `@sentry/junior-vercel` | Vercel plugin package for deployment monitoring and log investigations | diff --git a/package.json b/package.json index 5a3da217ee..d798d1c086 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "docs:dev": "pnpm --filter @sentry/junior-docs dev", "docs:build": "pnpm --filter @sentry/junior-docs build", "docs:check": "pnpm --filter @sentry/junior-docs check", - "package:lint": "for pkg in packages/junior packages/junior-plugin-api packages/junior-memory packages/junior-dashboard packages/junior-github packages/junior-google packages/junior-agent-browser packages/junior-amplitude packages/junior-cloudflare packages/junior-datadog packages/junior-tts packages/junior-hex packages/junior-linear packages/junior-maintenance packages/junior-notion packages/junior-octolens packages/junior-sentry packages/junior-vercel; do pnpm exec publint \"$pkg\" || exit $?; done", + "package:lint": "for pkg in packages/junior packages/junior-plugin-api packages/junior-memory packages/junior-dashboard packages/junior-github packages/junior-google packages/junior-agent-browser packages/junior-amplitude packages/junior-cloudflare packages/junior-datadog packages/junior-tts packages/junior-hex packages/junior-linear packages/junior-maintenance packages/junior-notion packages/junior-octolens packages/junior-pagerduty packages/junior-sentry packages/junior-vercel; do pnpm exec publint \"$pkg\" || exit $?; done", "package-types:check": "sh scripts/check-package-types.sh", "release:check": "node scripts/check-release-config.mjs", "start": "pnpm --filter @sentry/junior-example dev", diff --git a/packages/docs/astro.config.mjs b/packages/docs/astro.config.mjs index 832b786f2c..4d8a759f2d 100644 --- a/packages/docs/astro.config.mjs +++ b/packages/docs/astro.config.mjs @@ -19,6 +19,7 @@ export default defineConfig({ "/extend/cloudflare": "/extend/cloudflare-plugin", "/extend/datadog": "/extend/datadog-plugin", "/extend/hex": "/extend/hex-plugin", + "/extend/pagerduty": "/extend/pagerduty-plugin", "/plugins/overview": "/extend", "/plugins/agent-browser": "/extend/agent-browser-plugin", "/plugins/amplitude": "/extend/amplitude-plugin", @@ -136,6 +137,10 @@ export default defineConfig({ }, { label: "Memory Plugin", link: "/extend/memory-plugin/" }, { label: "Notion Plugin", link: "/extend/notion-plugin/" }, + { + label: "PagerDuty Plugin", + link: "/extend/pagerduty-plugin/", + }, { label: "Sentry Plugin", link: "/extend/sentry-plugin/" }, { label: "Vercel Plugin", link: "/extend/vercel-plugin/" }, ], diff --git a/packages/docs/src/content/docs/contribute/releasing.md b/packages/docs/src/content/docs/contribute/releasing.md index 5eb4961f50..768071dd98 100644 --- a/packages/docs/src/content/docs/contribute/releasing.md +++ b/packages/docs/src/content/docs/contribute/releasing.md @@ -27,6 +27,7 @@ Junior uses lockstep package releases for: - `@sentry/junior-memory` - `@sentry/junior-notion` - `@sentry/junior-octolens` +- `@sentry/junior-pagerduty` - `@sentry/junior-sentry` - `@sentry/junior-vercel` diff --git a/packages/docs/src/content/docs/extend/index.md b/packages/docs/src/content/docs/extend/index.md index 1776937be9..a2301961c5 100644 --- a/packages/docs/src/content/docs/extend/index.md +++ b/packages/docs/src/content/docs/extend/index.md @@ -32,6 +32,7 @@ fixed behavior that code must control. | [Maintenance](/extend/maintenance-plugin/) | Repository maintenance workflows | | [Memory](/extend/memory-plugin/) | Long-term scoped memory | | [Notion](/extend/notion-plugin/) | Notion content | +| [PagerDuty](/extend/pagerduty-plugin/) | On-call, schedules, and incidents (read-only) | | [Scheduler](/extend/scheduler-plugin/) | Durable scheduled automations | | [Sentry](/extend/sentry-plugin/) | Sentry issues, telemetry, and issue webhooks | | [Vercel](/extend/vercel-plugin/) | Vercel projects and deployments | diff --git a/packages/docs/src/content/docs/extend/pagerduty-plugin.md b/packages/docs/src/content/docs/extend/pagerduty-plugin.md new file mode 100644 index 0000000000..da0666f132 --- /dev/null +++ b/packages/docs/src/content/docs/extend/pagerduty-plugin.md @@ -0,0 +1,85 @@ +--- +title: PagerDuty Plugin +description: Configure read-only PagerDuty lookups for on-call, handoffs, schedules, escalation policies, services, and incidents. +type: tutorial +prerequisites: + - /extend/ +related: + - /concepts/credentials-and-oauth/ + - /operate/security-hardening/ +--- + +The PagerDuty plugin lets Slack users ask Junior who is on call, who rotates in or out next, and what is open in PagerDuty. Junior calls the PagerDuty REST API from the sandbox. + +The plugin is read-only. It uses one deployment-level API key, and the bundled skill uses only `GET` requests. + +## Install + +Install the plugin package alongside `@sentry/junior`: + +```bash +pnpm add @sentry/junior @sentry/junior-pagerduty +``` + +## Runtime setup + +Add the package name to the plugin set exported from `plugins.ts`: + +```ts title="plugins.ts" +import { defineJuniorPlugins } from "@sentry/junior"; + +export const plugins = defineJuniorPlugins(["@sentry/junior-pagerduty"]); +``` + +## Config + +Set deployment variables in the Junior environment, then redeploy. + +### Environment variables + +
+PAGERDUTY_API_KEY + +PagerDuty REST API key for read-only requests. + +- **Define:** Set `PAGERDUTY_API_KEY` in the deployment environment +- **Required:** Yes +- **Environment override:** `PAGERDUTY_API_KEY` + +Use a read-only General Access REST API key. A user API key for a dedicated read-only PagerDuty user also works. + +
+ +## Auth model + +- The plugin uses one deployment-level PagerDuty API key, not per-user OAuth. +- Junior keeps the real `PAGERDUTY_API_KEY` value on the host. +- Sandbox requests to `api.pagerduty.com` receive a host-managed `Authorization: Token token=` header. +- The sandbox never receives the key in env, files, or command arguments. +- Users do not connect or disconnect individual PagerDuty accounts from Junior App Home for this plugin. + +## What users can do + +- Ask who is on call now for a schedule, escalation policy, or service. +- Ask who rotates in or out next and when the handoff happens. +- Inspect schedule members, escalation paths, and team members. +- List open incidents and read incident details, timelines, and notes. + +## Verify + +1. Set `PAGERDUTY_API_KEY` and redeploy. +2. Ask Junior in a channel: `Who is on call for right now, and who is next?` +3. Confirm the reply names the current and next responders, the handoff time, and a PagerDuty link. + +## Failure modes + +- `PAGERDUTY_API_KEY` missing: Junior cannot add the credential. Set the env var and redeploy. +- `401 Unauthorized`: the key is invalid or revoked. +- `403 Forbidden`: the key cannot read the requested resource. +- `429 Too Many Requests`: PagerDuty is throttling. Retry later or narrow the request. +- EU-region account: the manifest covers only `api.pagerduty.com`. EU accounts need a manifest change for `api.eu.pagerduty.com`. +- Mutation requests (acknowledge, resolve, override): the plugin is read-only, and the skill declines these. + +## Next step + +Review [Credentials & OAuth](/concepts/credentials-and-oauth/) and [Security Hardening](/operate/security-hardening/). diff --git a/packages/junior-pagerduty/README.md b/packages/junior-pagerduty/README.md new file mode 100644 index 0000000000..4f49d4251c --- /dev/null +++ b/packages/junior-pagerduty/README.md @@ -0,0 +1,37 @@ +# @sentry/junior-pagerduty + +`@sentry/junior-pagerduty` adds read-only PagerDuty lookups to Junior through the PagerDuty REST API. Use it to answer on-call, handoff, schedule, escalation policy, service, team, and incident questions. + +## Install + +```bash +pnpm add @sentry/junior @sentry/junior-pagerduty +``` + +## Configure + +Add the package name to the plugin set exported from `plugins.ts`: + +```ts +import { defineJuniorPlugins } from "@sentry/junior"; + +export const plugins = defineJuniorPlugins(["@sentry/junior-pagerduty"]); +``` + +Set a PagerDuty REST API key in the Junior deployment environment: + +```bash +PAGERDUTY_API_KEY=... +``` + +Use a read-only General Access REST API key, or a user API key for a dedicated read-only PagerDuty user. + +## Auth model + +- The plugin uses one deployment-level PagerDuty API key. Users do not connect individual PagerDuty accounts. +- The real key stays on the host. Junior adds `Authorization: Token token=` to sandbox requests to `api.pagerduty.com`. +- The key is the read-only boundary. The bundled skill also uses only `GET` requests. + +The manifest covers the US service region only. EU-region accounts (`api.eu.pagerduty.com`) need a manifest change. + +Full setup guide: https://junior.sentry.dev/extend/pagerduty-plugin/ diff --git a/packages/junior-pagerduty/package.json b/packages/junior-pagerduty/package.json new file mode 100644 index 0000000000..746c589788 --- /dev/null +++ b/packages/junior-pagerduty/package.json @@ -0,0 +1,18 @@ +{ + "name": "@sentry/junior-pagerduty", + "version": "0.257.0", + "private": false, + "publishConfig": { + "access": "public" + }, + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/getsentry/junior.git", + "directory": "packages/junior-pagerduty" + }, + "files": [ + "plugin.yaml", + "skills" + ] +} diff --git a/packages/junior-pagerduty/plugin.yaml b/packages/junior-pagerduty/plugin.yaml new file mode 100644 index 0000000000..dabe6fff12 --- /dev/null +++ b/packages/junior-pagerduty/plugin.yaml @@ -0,0 +1,14 @@ +name: pagerduty +display-name: PagerDuty +description: Read-only PagerDuty on-call, schedule, escalation, service, and incident lookups through the PagerDuty REST API + +# Junior injects a deployment-level PagerDuty REST API key on sandbox requests +# to the US API host. Use a read-only key. The sandbox never sees the real key. +env-vars: + PAGERDUTY_API_KEY: + +domains: + - api.pagerduty.com + +api-headers: + Authorization: Token token=${PAGERDUTY_API_KEY} diff --git a/packages/junior-pagerduty/skills/pagerduty/SKILL.md b/packages/junior-pagerduty/skills/pagerduty/SKILL.md new file mode 100644 index 0000000000..9416fc92df --- /dev/null +++ b/packages/junior-pagerduty/skills/pagerduty/SKILL.md @@ -0,0 +1,62 @@ +--- +name: pagerduty +description: Look up PagerDuty on-call state through the read-only PagerDuty REST API. Use when users ask who is on call, who is rotating in or out, when the next on-call handoff is, what a schedule or escalation policy looks like, which team or service owns paging, or for PagerDuty incident status and timelines. Do not use it to acknowledge, resolve, reassign, or create incidents, to create schedule overrides, or for Sentry issues, Datadog telemetry, or Linear tickets. +--- + +# PagerDuty + +Use this skill for read-only PagerDuty lookups: on-call, handoffs, schedules, escalation policies, services, teams, users, and incidents. + +## Call the API + +- Call `https://api.pagerduty.com` with `curl` from the sandbox. +- Do not send an `Authorization` header. Junior adds the PagerDuty credential to requests for this host. +- Send `-H 'Accept: application/vnd.pagerduty+json;version=2'`. +- Use only `GET`. The deployment key is read-only. +- Pipe responses through `jq` and keep only the fields you need. +- Lists page with `limit` (max `100`) and `offset`. Follow `more: true` only when the answer needs more rows. +- Array filters use bracket syntax, for example `schedule_ids[]=PABC123`. Pass them with `curl -G --data-urlencode`. +- Times are ISO 8601. Pass `time_zone=` when the user names a zone. + +## Workflow + +1. Resolve the target. + +- Use IDs or PagerDuty URLs from the request or the thread first. A URL such as `https://.pagerduty.com/schedules/PABC123` contains the ID. +- Otherwise resolve names with one search: `GET /schedules?query=`, `GET /escalation_policies?query=`, `GET /services?query=`, `GET /teams?query=`, or `GET /users?query=`. +- If a search returns several plausible matches, list them and ask which one the user means. + +2. Pick the most direct endpoint. + +| Question | Endpoint | +| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| Who is on call now for a schedule or policy | `GET /oncalls?schedule_ids[]=` or `escalation_policy_ids[]=`; add `earliest=true` | +| Who is rotating in or out, next handoff | `GET /schedules/?since=&until=`; read `final_schedule.rendered_schedule_entries` | +| What is a user's on-call coverage | `GET /oncalls?user_ids[]=&since=&until=` | +| Who is on a schedule | `GET /schedules//users` | +| What is the escalation path | `GET /escalation_policies/` | +| Which policy pages for a service | `GET /services/?include[]=escalation_policies` | +| Team members | `GET /teams//members?include[]=users` | +| Open incidents | `GET /incidents?statuses[]=triggered&statuses[]=acknowledged`; filter with `service_ids[]` or `team_ids[]` | +| Incident detail, timeline, or notes | `GET /incidents/`, `GET /incidents//log_entries`, `GET /incidents//notes` | + +3. Bound every query. + +- Default "now" questions to the current time. Get it with `date -u +%Y-%m-%dT%H:%M:%SZ`. +- Default handoff questions to the next 7 days. Default incident history to the last 24 hours. +- In `/oncalls` results, `escalation_level` `1` is the primary responder. Say which level you report. + +4. Report the result. + +- Lead with the answer: names, levels, and shift start and end times in the user's zone when known. +- For handoffs, name who is on call now, who is next, and the handoff time. +- Link to the object with its `html_url` when the response contains one. Do not build links from partial IDs. +- Do not dump raw JSON. + +## Guardrails + +- This skill is read-only. Decline requests to acknowledge, resolve, reassign, snooze, or create incidents, to add notes or responders, or to create overrides. Tell the user to do it in PagerDuty. +- Share only the contact details that the question needs. Do not list phone numbers or email addresses unless the user asks for them. +- `401 Unauthorized`: report that the PagerDuty credential is missing or invalid for this Junior deployment. Do not retry. +- `403 Forbidden`: report that the PagerDuty credential cannot read the resource. Do not guess at missing permissions. +- `429 Too Many Requests`: wait briefly and retry the same request once. If it fails again, report the throttle. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb545f8cf1..4f9c985365 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -675,6 +675,8 @@ importers: packages/junior-octolens: {} + packages/junior-pagerduty: {} + packages/junior-plugin-api: dependencies: commander: diff --git a/scripts/bump-release-versions.mjs b/scripts/bump-release-versions.mjs index 4e5bd8da7c..7fae8b0a65 100644 --- a/scripts/bump-release-versions.mjs +++ b/scripts/bump-release-versions.mjs @@ -24,6 +24,7 @@ const files = [ "packages/junior-memory/package.json", "packages/junior-notion/package.json", "packages/junior-octolens/package.json", + "packages/junior-pagerduty/package.json", "packages/junior-maintenance/package.json", "packages/junior-sentry/package.json", "packages/junior-vercel/package.json", From f49d080f9a87e2a8f8b157bb177733f7e92720ea Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:34:36 +0000 Subject: [PATCH 2/2] docs(pagerduty): Require an account-level read-only API key Co-Authored-By: David Cramer --- packages/docs/src/content/docs/extend/pagerduty-plugin.md | 2 +- packages/junior-pagerduty/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docs/src/content/docs/extend/pagerduty-plugin.md b/packages/docs/src/content/docs/extend/pagerduty-plugin.md index da0666f132..5f3763133b 100644 --- a/packages/docs/src/content/docs/extend/pagerduty-plugin.md +++ b/packages/docs/src/content/docs/extend/pagerduty-plugin.md @@ -46,7 +46,7 @@ PagerDuty REST API key for read-only requests. - **Required:** Yes - **Environment override:** `PAGERDUTY_API_KEY` -Use a read-only General Access REST API key. A user API key for a dedicated read-only PagerDuty user also works. +Use an account-level General Access REST API key with **Read-only API Key** enabled. An admin or account owner creates it in **Integrations > Developer Tools > API Access Keys**. diff --git a/packages/junior-pagerduty/README.md b/packages/junior-pagerduty/README.md index 4f49d4251c..20d83c36d2 100644 --- a/packages/junior-pagerduty/README.md +++ b/packages/junior-pagerduty/README.md @@ -24,7 +24,7 @@ Set a PagerDuty REST API key in the Junior deployment environment: PAGERDUTY_API_KEY=... ``` -Use a read-only General Access REST API key, or a user API key for a dedicated read-only PagerDuty user. +Use an account-level General Access REST API key with **Read-only API Key** enabled. An admin or account owner creates it in **Integrations > Developer Tools > API Access Keys**. ## Auth model