{"_id":"@copperbox/railyard-monitor-github","_rev":"2-84f95f98722760118d713a54eb583d19","name":"@copperbox/railyard-monitor-github","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@copperbox/railyard-monitor-github","version":"1.0.0","license":"MIT","_id":"@copperbox/railyard-monitor-github@1.0.0","maintainers":[{"name":"dantheuber","email":"dan.essig@gmail.com"}],"homepage":"https://github.com/copperbox/railyard#readme","bugs":{"url":"https://github.com/copperbox/railyard/issues"},"dist":{"shasum":"5adf0c3b4d7216e3930a1af4ffa3965d02fa3a44","tarball":"https://registry.npmjs.org/@copperbox/railyard-monitor-github/-/railyard-monitor-github-1.0.0.tgz","fileCount":9,"integrity":"sha512-zJjLBsogiUyZCvEQuCFw/mSuAUpRz/1v+I2f1JeU6lkJVCoscvSDyeq8YI7d1eDpV4BaFt4plG7sIlqx1Qc+Sw==","signatures":[{"sig":"MEYCIQDIrvNbMLWnpsMdG9p1lPR095Opc9f2CMTmgF8XjMErRgIhAOhn3BmYhZPd5dPESxKIVtdlESPBVFn98JKbivcdR1Jc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57505},"type":"module","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./schemas/*":"./schemas/*"},"gitHead":"3142c12ca7418e6f10adb47d441744023cc3f5d9","scripts":{"test":"vitest run","build":"tsup src/index.ts --format esm --dts --clean","typecheck":"tsc -p tsconfig.json","test:docker":"cross-env RAILYARD_DOCKER_TESTS=1 vitest run","test:github":"cross-env RAILYARD_GITHUB_TESTS=1 vitest run"},"_npmUser":{"name":"dantheuber","email":"dan.essig@gmail.com"},"repository":{"url":"git+https://github.com/copperbox/railyard.git","type":"git","directory":"packages/railyard-monitor-github"},"_npmVersion":"11.17.0","description":"First-party GitHub issues monitor for railyard: polls issue events, emits github.issue.* signals with published JSON Schemas.","directories":{},"_nodeVersion":"26.4.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","cross-env":"^10.0.0","typescript":"^5.9.0","@types/node":"^26.0.0","@copperbox/railyard":"workspace:*"},"peerDependencies":{"@copperbox/railyard":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/railyard-monitor-github_1.0.0_1784595347514_0.5602915240331694","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"_id":"@copperbox/railyard-monitor-github@1.1.0","bugs":{"url":"https://github.com/copperbox/railyard/issues"},"dist":{"shasum":"b98787f92e84b429cf339b81acc7ead4d6926a97","tarball":"https://registry.npmjs.org/@copperbox/railyard-monitor-github/-/railyard-monitor-github-1.1.0.tgz","fileCount":9,"integrity":"sha512-qzhqeAk3PqMotYrDWL/0IwSqyxcvA8ajT7jE9KJ0XRa6cU2fo8KaUdr0Sncxfov1O/c2WARjuT5rZHnjjKgJVg==","signatures":[{"sig":"MEQCICJ0dP05b2QWVvusyYdEiGKwDFeubZN4O53LvW6APb5VAiBMhM+F32UzfglvGtXxC0HL3mZaZL2pL7VTuJStzY9KSw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICa1hf8poPHhNHoZFzK71a0P1D9nFP3dHTqRwSc+BJTwAiEAzlLooPmv4TeME6qR0gpB+s5Kn7zgN9c0aGIrGwRXtOo="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@copperbox%2frailyard-monitor-github@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":58652},"name":"@copperbox/railyard-monitor-github","type":"module","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./schemas/*":"./schemas/*"},"gitHead":"0cde23cca4e3ae96f1d76d1d2ffdd187a08d3914","license":"MIT","scripts":{"test":"vitest run","build":"tsup src/index.ts --format esm --dts --clean","typecheck":"tsc -p tsconfig.json","test:docker":"cross-env RAILYARD_DOCKER_TESTS=1 vitest run","test:github":"cross-env RAILYARD_GITHUB_TESTS=1 vitest run"},"version":"1.1.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:2996ed8f-e33b-463b-81df-228b676ba20c"}},"homepage":"https://github.com/copperbox/railyard#readme","repository":{"url":"git+https://github.com/copperbox/railyard.git","type":"git","directory":"packages/railyard-monitor-github"},"_npmVersion":"12.0.2","description":"First-party GitHub issues monitor for railyard: polls issue events, emits github.issue.* signals with published JSON Schemas.","directories":{},"maintainers":[{"name":"dantheuber","email":"dan.essig@gmail.com"}],"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^3.2.0","cross-env":"^10.0.0","typescript":"^5.9.0","@types/node":"^26.0.0","@copperbox/railyard":"workspace:*"},"peerDependencies":{"@copperbox/railyard":"^2.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/railyard-monitor-github_1.1.0_1789408083879_0.8012735909603261"}}},"time":{"created":"2026-07-21T00:55:47.335Z","modified":"2026-09-14T17:48:04.844Z","1.0.0":"2026-07-21T00:55:47.641Z","1.1.0":"2026-09-14T17:48:03.985Z"},"bugs":{"url":"https://github.com/copperbox/railyard/issues"},"license":"MIT","homepage":"https://github.com/copperbox/railyard#readme","repository":{"url":"git+https://github.com/copperbox/railyard.git","type":"git","directory":"packages/railyard-monitor-github"},"description":"First-party GitHub issues monitor for railyard: polls issue events, emits github.issue.* signals with published JSON Schemas.","maintainers":[{"name":"dantheuber","email":"dan.essig@gmail.com"}],"readme":"# @copperbox/railyard-monitor-github\n\nFirst-party GitHub issues monitor for [railyard](../../README.md): polls a repository's\nissue events and emits `github.issue.*` signals with published JSON Schemas. Built\nstrictly against `@copperbox/railyard`'s public exports — core is a peer dependency and\nevery import from it is type-only; this package has zero runtime dependencies.\n\n```ts\nimport { Orchestrator } from '@copperbox/railyard'\nimport { GitHubIssuesMonitor } from '@copperbox/railyard-monitor-github'\n\nconst orchestrator = new Orchestrator({ agentsDir, runsDir, stateDir })\norchestrator.register(\n  new GitHubIssuesMonitor({\n    repos: ['copperbox/railyard'],\n    token: process.env.GITHUB_TOKEN,\n  }),\n)\nawait orchestrator.start()\n```\n\n## Signals\n\nv1 emits a fixed allowlist of four types — everything else the events API yields\n(assigned, renamed, milestoned, …) advances the cursor but emits nothing. Comments are\nnot issue events and are out of scope for this endpoint; a future\n`github.issue.commented` would be an additive second poll.\n\n| Type | When | Payload |\n|---|---|---|\n| `github.issue.labeled` | a label was applied | shape A (has `label`) |\n| `github.issue.unlabeled` | a label was removed | shape A |\n| `github.issue.closed` | issue closed | shape B (no `label`) |\n| `github.issue.reopened` | issue reopened | shape B |\n\nBoth shapes carry `repo` (identity from the boot preflight: owner, name, fullName, url,\nprivate), `issue` (a snapshot **at poll time** — number, title, body, state, author,\nlabel names, assignee logins, urls, timestamps), `actor` (who did it), `eventId`\n(GitHub's issue-event id — the dedup key), and `occurredAt`. Shape A adds\n`label: { name, color }`. Users are login strings; `issue.body`, `label.color`,\n`actor`, and `issue.author` are nullable (deleted \"ghost\" users).\n\nApplying three labels at once is three GitHub events → three independently routable\nsignals. A manifest filter like `$.label.name == \"needs-review\"` fires only for that\nlabel.\n\n## Consuming the schemas (important)\n\nCore's boot-time schema compatibility check is **deep structural equality**, so an\nagent subscribing to these signals must reference a schema *structurally identical* to\nthe published one. Copy the file verbatim into your agent folder:\n\n```sh\ncp node_modules/@copperbox/railyard-monitor-github/schemas/github-issue-labeled.schema.json \\\n   agents/my-reviewer/issue-labeled.schema.json\n```\n\n```yaml\n# agents/my-reviewer/manifest.yaml\nname: my-reviewer\non:\n  - type: github.issue.labeled\n    filter: '$.label.name == \"needs-review\"'\n    payloadSchema: ./issue-labeled.schema.json\n```\n\nThe files are also importable as `@copperbox/railyard-monitor-github/schemas/<file>`,\nand exported as TS values (`GITHUB_ISSUE_LABELED_SCHEMA`, …) with payload interfaces\n(`GitHubIssueLabeledPayload`, …).\n\n## Options\n\n| Option | Default | Notes |\n|---|---|---|\n| `repos` | — (required) | `\"owner/name\"[]`, non-empty |\n| `token` | none | see below; absent ⇒ unauthenticated (60 req/h, public repos only) with a loud warning |\n| `pollIntervalMs` | `60_000` | one events request per repo per poll (usually a free 304) |\n| `includePullRequests` | `false` | the issues API surfaces PR events too; opt in to emit them |\n| `name` | `\"github-issues\"` | signal `source.name` and the state-file identity — override to run multiple instances |\n| `apiBaseUrl` | `https://api.github.com` | GitHub Enterprise |\n| `pageLimit` | `10` | max event pages (of 100) per repo per poll; hitting it logs a **loud gap error** |\n| `fetchImpl` | global `fetch` | test seam |\n\n### Token\n\nMonitors are host-side user code, so you pass the token in — core's `SecretsProvider`\nis agent-container machinery and does not apply here.\n\n- Local dev, zero ceremony: `GITHUB_TOKEN=$(gh auth token)` and pass\n  `process.env.GITHUB_TOKEN`.\n- Deployments: a fine-grained PAT scoped to the polled repos with read-only\n  Issues/Metadata permissions — least privilege, same posture as SPEC §8's\n  credential-scoping guidance.\n\nA bad token or unreachable repo fails `orchestrator.start()` loudly (each repo is\nprobed at boot), not as a 401 loop at 2am.\n\n## Dedup, cursor, and delivery semantics\n\nDedup is the monitor's job (SPEC §9), and this monitor's rule is: **each GitHub\nissue-event id is emitted at most once**, tracked by a per-repo cursor (the highest\nprocessed event id) in `ctx.state`.\n\n- State keys, per repo: `cursor:<owner>/<repo>` (number) and `etag:<owner>/<repo>`\n  (string). The ETag makes quiet polls HTTP 304s, which cost **zero** rate limit.\n- **First start baselines**: the cursor is set to the newest event id and nothing is\n  emitted — history is never replayed. Delete the state file to re-baseline.\n- **Delivery is at-least-once across a crash**: the cursor is persisted *after* each\n  emission, so a crash in between re-emits that event on restart. Since railyard 2.0 the\n  run that emission triggered may have *survived* the restart, so every emission carries\n  the work identity `work: { key: \"<owner/name>#<eventId>\" }`; the orchestrator's\n  delivery ledger suppresses the re-emission per target agent while the first delivery is\n  queued, active, or recently done (journaled `run.skipped` / `duplicate`). Requires\n  `@copperbox/railyard` ≥ 2.0 (peer dependency). Agents with external side effects should\n  still key their own idempotency on `signal.work.key` — see core's\n  [lifecycle & recovery](https://github.com/copperbox/railyard/blob/main/docs/lifecycle-and-recovery.md).\n- Rate limits (403/429): polling pauses monitor-wide until `retry-after` /\n  `x-ratelimit-reset`, with a warning naming the resume time. Other errors log and\n  retry on the next interval; the cursor is untouched, so nothing is lost.\n\n## Testing\n\nUnit tests run offline against canned responses (`pnpm test`). Real-API tests are\ngated: `pnpm test:github` sets `RAILYARD_GITHUB_TESTS=1` and then **requires**\n`GITHUB_TOKEN` (process env or repo-root `.env`) — read-only requests against a stable\npublic repo, never silently skipped. The Docker-gated e2e (`pnpm test:docker`) proves\nmonitor → filter → container end-to-end with a stubbed API.\n\nTo unit-test your own monitors the same way, core exports the harness this package\nuses: `createMonitorTestContext(monitor.emits)` returns a real `MonitorContext` with\norchestrator-identical emit validation, captured emissions/logs, and an in-memory\n`ctx.state`.\n\n## Documentation\n\n- [Authoring monitors](https://github.com/copperbox/railyard/blob/main/docs/authoring-monitors.md)\n- [`github.issue.*` payload contract](https://github.com/copperbox/railyard/blob/main/docs/contracts/github-issue-signals.md)\n  — the versioned, language-neutral payload spec.\n","readmeFilename":"README.md"}