@geminixiang/mikan 1.0.0-beta.2 → 1.0.0-beta.20
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/CHANGELOG.md +354 -0
- package/README.md +42 -16
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +1 -0
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +36 -67
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +17 -42
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +148 -0
- package/dist/adapters/github/bot.d.ts.map +1 -0
- package/dist/adapters/github/bot.js +1031 -0
- package/dist/adapters/github/bot.js.map +1 -0
- package/dist/adapters/github/client.d.ts +118 -0
- package/dist/adapters/github/client.d.ts.map +1 -0
- package/dist/adapters/github/client.js +309 -0
- package/dist/adapters/github/client.js.map +1 -0
- package/dist/adapters/github/cloudbuild.d.ts +24 -0
- package/dist/adapters/github/cloudbuild.d.ts.map +1 -0
- package/dist/adapters/github/cloudbuild.js +71 -0
- package/dist/adapters/github/cloudbuild.js.map +1 -0
- package/dist/adapters/github/context.d.ts +9 -0
- package/dist/adapters/github/context.d.ts.map +1 -0
- package/dist/adapters/github/context.js +175 -0
- package/dist/adapters/github/context.js.map +1 -0
- package/dist/adapters/github/gcp-auth.d.ts +31 -0
- package/dist/adapters/github/gcp-auth.d.ts.map +1 -0
- package/dist/adapters/github/gcp-auth.js +196 -0
- package/dist/adapters/github/gcp-auth.js.map +1 -0
- package/dist/adapters/github/ids.d.ts +34 -0
- package/dist/adapters/github/ids.d.ts.map +1 -0
- package/dist/adapters/github/ids.js +53 -0
- package/dist/adapters/github/ids.js.map +1 -0
- package/dist/adapters/github/repo.d.ts +30 -0
- package/dist/adapters/github/repo.d.ts.map +1 -0
- package/dist/adapters/github/repo.js +136 -0
- package/dist/adapters/github/repo.js.map +1 -0
- package/dist/adapters/github/tool-pack.d.ts +14 -0
- package/dist/adapters/github/tool-pack.d.ts.map +1 -0
- package/dist/adapters/github/tool-pack.js +56 -0
- package/dist/adapters/github/tool-pack.js.map +1 -0
- package/dist/adapters/github/tools/checks.d.ts +24 -0
- package/dist/adapters/github/tools/checks.d.ts.map +1 -0
- package/dist/adapters/github/tools/checks.js +116 -0
- package/dist/adapters/github/tools/checks.js.map +1 -0
- package/dist/adapters/github/tools/issue.d.ts +22 -0
- package/dist/adapters/github/tools/issue.d.ts.map +1 -0
- package/dist/adapters/github/tools/issue.js +59 -0
- package/dist/adapters/github/tools/issue.js.map +1 -0
- package/dist/adapters/github/tools/pr.d.ts +22 -0
- package/dist/adapters/github/tools/pr.d.ts.map +1 -0
- package/dist/adapters/github/tools/pr.js +59 -0
- package/dist/adapters/github/tools/pr.js.map +1 -0
- package/dist/adapters/github/tools/read.d.ts +22 -0
- package/dist/adapters/github/tools/read.d.ts.map +1 -0
- package/dist/adapters/github/tools/read.js +152 -0
- package/dist/adapters/github/tools/read.js.map +1 -0
- package/dist/adapters/github/tools/review-reply.d.ts +19 -0
- package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
- package/dist/adapters/github/tools/review-reply.js +49 -0
- package/dist/adapters/github/tools/review-reply.js.map +1 -0
- package/dist/adapters/github/tools/sync.d.ts +17 -0
- package/dist/adapters/github/tools/sync.d.ts.map +1 -0
- package/dist/adapters/github/tools/sync.js +45 -0
- package/dist/adapters/github/tools/sync.js.map +1 -0
- package/dist/adapters/github/types.d.ts +350 -0
- package/dist/adapters/github/types.d.ts.map +1 -0
- package/dist/adapters/github/types.js +2 -0
- package/dist/adapters/github/types.js.map +1 -0
- package/dist/adapters/intake.d.ts +16 -5
- package/dist/adapters/intake.d.ts.map +1 -1
- package/dist/adapters/intake.js +79 -22
- package/dist/adapters/intake.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +8 -6
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +81 -133
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +3 -14
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +14 -17
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js +5 -8
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/streaming.d.ts +4 -0
- package/dist/adapters/streaming.d.ts.map +1 -1
- package/dist/adapters/streaming.js +11 -0
- package/dist/adapters/streaming.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +1 -2
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +27 -62
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +8 -27
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/types.d.ts +28 -2
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/agent.d.ts +38 -14
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +358 -224
- package/dist/agent.js.map +1 -1
- package/dist/cli/download.d.ts.map +1 -0
- package/dist/cli/download.js.map +1 -0
- package/dist/cli/ext-git.d.ts +22 -0
- package/dist/cli/ext-git.d.ts.map +1 -0
- package/dist/cli/ext-git.js +76 -0
- package/dist/cli/ext-git.js.map +1 -0
- package/dist/cli/ext.d.ts +2 -0
- package/dist/cli/ext.d.ts.map +1 -0
- package/dist/cli/ext.js +217 -0
- package/dist/cli/ext.js.map +1 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +2 -1
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/auto-reply.d.ts.map +1 -1
- package/dist/commands/auto-reply.js +2 -6
- package/dist/commands/auto-reply.js.map +1 -1
- package/dist/commands/extensions.d.ts.map +1 -1
- package/dist/commands/extensions.js +16 -6
- package/dist/commands/extensions.js.map +1 -1
- package/dist/commands/login.d.ts +2 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +30 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/manifest.d.ts +72 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/manifest.js +121 -0
- package/dist/commands/manifest.js.map +1 -0
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +3 -1
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +2 -1
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/sandbox.d.ts.map +1 -1
- package/dist/commands/sandbox.js +16 -12
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/commands/text.d.ts +2 -0
- package/dist/commands/text.d.ts.map +1 -0
- package/dist/commands/text.js +16 -0
- package/dist/commands/text.js.map +1 -0
- package/dist/commands/types.d.ts +14 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +90 -54
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts +10 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +14 -5
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts +3 -2
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +28 -12
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/extensions/loader.d.ts +39 -9
- package/dist/harness/extensions/loader.d.ts.map +1 -1
- package/dist/harness/extensions/loader.js +258 -54
- package/dist/harness/extensions/loader.js.map +1 -1
- package/dist/harness/extensions/registry.d.ts +48 -1
- package/dist/harness/extensions/registry.d.ts.map +1 -1
- package/dist/harness/extensions/registry.js +146 -0
- package/dist/harness/extensions/registry.js.map +1 -1
- package/dist/harness/extensions/types.d.ts +161 -14
- package/dist/harness/extensions/types.d.ts.map +1 -1
- package/dist/harness/extensions/types.js.map +1 -1
- package/dist/harness/index.d.ts +5 -5
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +2 -2
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/models.d.ts +5 -0
- package/dist/harness/models.d.ts.map +1 -1
- package/dist/harness/models.js +10 -0
- package/dist/harness/models.js.map +1 -1
- package/dist/harness/runner.d.ts +21 -2
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +80 -22
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session-store.d.ts +4 -1
- package/dist/harness/session-store.d.ts.map +1 -1
- package/dist/harness/session-store.js +11 -2
- package/dist/harness/session-store.js.map +1 -1
- package/dist/harness/subagent-runner.d.ts +17 -0
- package/dist/harness/subagent-runner.d.ts.map +1 -0
- package/dist/harness/subagent-runner.js +307 -0
- package/dist/harness/subagent-runner.js.map +1 -0
- package/dist/harness/types.d.ts +53 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.js +232 -29
- package/dist/main.js.map +1 -1
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +2 -5
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/conversation-runtime.d.ts +1 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +264 -41
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +32 -5
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts +2 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +7 -0
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +3 -0
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +8 -2
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/firecracker.d.ts +2 -0
- package/dist/sandbox/firecracker.d.ts.map +1 -1
- package/dist/sandbox/firecracker.js +7 -1
- package/dist/sandbox/firecracker.js.map +1 -1
- package/dist/sandbox/gondolin-fleet.d.ts +104 -0
- package/dist/sandbox/gondolin-fleet.d.ts.map +1 -0
- package/dist/sandbox/gondolin-fleet.js +330 -0
- package/dist/sandbox/gondolin-fleet.js.map +1 -0
- package/dist/sandbox/gondolin-gateway.d.ts +85 -0
- package/dist/sandbox/gondolin-gateway.d.ts.map +1 -0
- package/dist/sandbox/gondolin-gateway.js +400 -0
- package/dist/sandbox/gondolin-gateway.js.map +1 -0
- package/dist/sandbox/gondolin-inventory.d.ts +89 -0
- package/dist/sandbox/gondolin-inventory.d.ts.map +1 -0
- package/dist/sandbox/gondolin-inventory.js +267 -0
- package/dist/sandbox/gondolin-inventory.js.map +1 -0
- package/dist/sandbox/gondolin-join.d.ts +62 -0
- package/dist/sandbox/gondolin-join.d.ts.map +1 -0
- package/dist/sandbox/gondolin-join.js +261 -0
- package/dist/sandbox/gondolin-join.js.map +1 -0
- package/dist/sandbox/gondolin-nfs-advisory.d.ts +15 -0
- package/dist/sandbox/gondolin-nfs-advisory.d.ts.map +1 -0
- package/dist/sandbox/gondolin-nfs-advisory.js +60 -0
- package/dist/sandbox/gondolin-nfs-advisory.js.map +1 -0
- package/dist/sandbox/gondolin-placement.d.ts +32 -0
- package/dist/sandbox/gondolin-placement.d.ts.map +1 -0
- package/dist/sandbox/gondolin-placement.js +83 -0
- package/dist/sandbox/gondolin-placement.js.map +1 -0
- package/dist/sandbox/gondolin-remote.d.ts +73 -0
- package/dist/sandbox/gondolin-remote.d.ts.map +1 -0
- package/dist/sandbox/gondolin-remote.js +352 -0
- package/dist/sandbox/gondolin-remote.js.map +1 -0
- package/dist/sandbox/gondolin-worker-client.d.ts +139 -0
- package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -0
- package/dist/sandbox/gondolin-worker-client.js +325 -0
- package/dist/sandbox/gondolin-worker-client.js.map +1 -0
- package/dist/sandbox/gondolin-worker-main.d.ts +2 -0
- package/dist/sandbox/gondolin-worker-main.d.ts.map +1 -0
- package/dist/sandbox/gondolin-worker-main.js +22 -0
- package/dist/sandbox/gondolin-worker-main.js.map +1 -0
- package/dist/sandbox/gondolin-worker.d.ts +55 -0
- package/dist/sandbox/gondolin-worker.d.ts.map +1 -0
- package/dist/sandbox/gondolin-worker.js +201 -0
- package/dist/sandbox/gondolin-worker.js.map +1 -0
- package/dist/sandbox/gondolin.d.ts +52 -0
- package/dist/sandbox/gondolin.d.ts.map +1 -0
- package/dist/sandbox/gondolin.js +425 -0
- package/dist/sandbox/gondolin.js.map +1 -0
- package/dist/sandbox/host.d.ts +2 -0
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +11 -0
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/identity.d.ts +21 -0
- package/dist/sandbox/identity.d.ts.map +1 -0
- package/dist/sandbox/identity.js +36 -0
- package/dist/sandbox/identity.js.map +1 -0
- package/dist/sandbox/index.d.ts +4 -3
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +5 -3
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/types.d.ts +26 -1
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +11 -0
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +35 -0
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/sessions/agent-memory-file-manager.d.ts.map +1 -1
- package/dist/sessions/agent-memory-file-manager.js +16 -30
- package/dist/sessions/agent-memory-file-manager.js.map +1 -1
- package/dist/sessions/history-line.d.ts +20 -0
- package/dist/sessions/history-line.d.ts.map +1 -0
- package/dist/sessions/history-line.js +24 -0
- package/dist/sessions/history-line.js.map +1 -0
- package/dist/sessions/policy.d.ts.map +1 -1
- package/dist/sessions/policy.js +5 -3
- package/dist/sessions/policy.js.map +1 -1
- package/dist/sessions/session-key.d.ts +31 -0
- package/dist/sessions/session-key.d.ts.map +1 -0
- package/dist/sessions/session-key.js +44 -0
- package/dist/sessions/session-key.js.map +1 -0
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +2 -2
- package/dist/sessions/store.js.map +1 -1
- package/dist/tools/bash.d.ts +3 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +24 -17
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +9 -12
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/event.d.ts +1 -1
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +33 -4
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/generate-image.d.ts +17 -0
- package/dist/tools/generate-image.d.ts.map +1 -0
- package/dist/tools/generate-image.js +63 -0
- package/dist/tools/generate-image.js.map +1 -0
- package/dist/tools/index.d.ts +16 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +27 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/react.d.ts +15 -0
- package/dist/tools/react.d.ts.map +1 -0
- package/dist/tools/react.js +40 -0
- package/dist/tools/react.js.map +1 -0
- package/dist/tools/sandbox.d.ts +2 -2
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +8 -7
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/subagent.d.ts +69 -0
- package/dist/tools/subagent.d.ts.map +1 -0
- package/dist/tools/subagent.js +286 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/types.d.ts +34 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +1 -8
- package/dist/tools/write.js.map +1 -1
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js +1 -2
- package/dist/trigger.js.map +1 -1
- package/dist/types.d.ts +139 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/disabled.d.ts +9 -0
- package/dist/vault/disabled.d.ts.map +1 -0
- package/dist/vault/disabled.js +23 -0
- package/dist/vault/disabled.js.map +1 -0
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +3 -24
- package/dist/vault/index.js.map +1 -1
- package/dist/vault/policy.d.ts +20 -0
- package/dist/vault/policy.d.ts.map +1 -0
- package/dist/vault/policy.js +20 -0
- package/dist/vault/policy.js.map +1 -0
- package/dist/vault/routing.d.ts +1 -0
- package/dist/vault/routing.d.ts.map +1 -1
- package/dist/vault/routing.js +3 -10
- package/dist/vault/routing.js.map +1 -1
- package/dist/web/admin/portal.d.ts +27 -0
- package/dist/web/admin/portal.d.ts.map +1 -1
- package/dist/web/admin/portal.js +91 -130
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/admin/types.d.ts +3 -0
- package/dist/web/admin/types.d.ts.map +1 -1
- package/dist/web/admin/types.js.map +1 -1
- package/dist/web/login/oauth.d.ts +2 -3
- package/dist/web/login/oauth.d.ts.map +1 -1
- package/dist/web/login/oauth.js +0 -31
- package/dist/web/login/oauth.js.map +1 -1
- package/dist/web/login/portal.js +1 -1
- package/dist/web/login/portal.js.map +1 -1
- package/dist/web/login/types.d.ts +0 -11
- package/dist/web/login/types.d.ts.map +1 -1
- package/dist/web/login/types.js.map +1 -1
- package/dist/web/portal-shell.d.ts +4 -0
- package/dist/web/portal-shell.d.ts.map +1 -0
- package/dist/{portal-shell.js → web/portal-shell.js} +2 -2
- package/dist/web/portal-shell.js.map +1 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +7 -0
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-view/command.d.ts.map +1 -1
- package/dist/web/session-view/command.js +4 -1
- package/dist/web/session-view/command.js.map +1 -1
- package/dist/web/session-view/portal.d.ts.map +1 -1
- package/dist/web/session-view/portal.js +4 -3
- package/dist/web/session-view/portal.js.map +1 -1
- package/dist/web/session-view/service.d.ts.map +1 -1
- package/dist/web/session-view/service.js +2 -1
- package/dist/web/session-view/service.js.map +1 -1
- package/dist/web/session-view/types.d.ts +2 -1
- package/dist/web/session-view/types.d.ts.map +1 -1
- package/dist/web/session-view/types.js.map +1 -1
- package/package.json +7 -4
- package/dist/download.d.ts.map +0 -1
- package/dist/download.js.map +0 -1
- package/dist/model-registry.d.ts +0 -4
- package/dist/model-registry.d.ts.map +0 -1
- package/dist/model-registry.js +0 -7
- package/dist/model-registry.js.map +0 -1
- package/dist/portal-shell.d.ts +0 -4
- package/dist/portal-shell.d.ts.map +0 -1
- package/dist/portal-shell.js.map +0 -1
- package/dist/runtime/agent-run-controller.d.ts +0 -31
- package/dist/runtime/agent-run-controller.d.ts.map +0 -1
- package/dist/runtime/agent-run-controller.js +0 -195
- package/dist/runtime/agent-run-controller.js.map +0 -1
- /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
- /package/dist/{download.js → cli/download.js} +0 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { createSign } from "crypto";
|
|
2
|
+
export class GithubApiError extends Error {
|
|
3
|
+
constructor(status, method, path, detail) {
|
|
4
|
+
super(`GitHub ${method} ${path} failed with ${status}: ${detail}`);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "GithubApiError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* GitHub rate-limit responses are 429, or 403 with a rate-limit message
|
|
11
|
+
* (secondary limits). Used as the `withRetry` predicate.
|
|
12
|
+
*/
|
|
13
|
+
export function githubIsRateLimited(err) {
|
|
14
|
+
if (!(err instanceof GithubApiError))
|
|
15
|
+
return false;
|
|
16
|
+
return err.status === 429 || (err.status === 403 && /rate limit/i.test(err.message));
|
|
17
|
+
}
|
|
18
|
+
function base64Url(data) {
|
|
19
|
+
return (typeof data === "string" ? Buffer.from(data) : data).toString("base64url");
|
|
20
|
+
}
|
|
21
|
+
/** Refresh the cached installation token this long before it expires. */
|
|
22
|
+
const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
23
|
+
/**
|
|
24
|
+
* Minimal GitHub REST client authenticated as a GitHub App installation.
|
|
25
|
+
* Mints short-lived installation tokens from an RS256 app JWT and supports
|
|
26
|
+
* ETag conditional polling (304 responses don't count against the rate limit).
|
|
27
|
+
*/
|
|
28
|
+
export class GithubClient {
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.installationToken = null;
|
|
31
|
+
this.etags = new Map();
|
|
32
|
+
this.appId = options.appId;
|
|
33
|
+
this.privateKey = options.privateKey;
|
|
34
|
+
this.installationId = options.installationId;
|
|
35
|
+
this.baseUrl = (options.baseUrl ?? "https://api.github.com").replace(/\/+$/, "");
|
|
36
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
37
|
+
}
|
|
38
|
+
/** Short-lived JWT identifying the App itself (not an installation). */
|
|
39
|
+
appJwt() {
|
|
40
|
+
const now = Math.floor(Date.now() / 1000);
|
|
41
|
+
const header = base64Url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
|
|
42
|
+
// 60s clock-drift backdate; GitHub caps exp at 10 minutes.
|
|
43
|
+
const payload = base64Url(JSON.stringify({ iat: now - 60, exp: now + 540, iss: this.appId }));
|
|
44
|
+
const signature = base64Url(createSign("RSA-SHA256").update(`${header}.${payload}`).sign(this.privateKey));
|
|
45
|
+
return `${header}.${payload}.${signature}`;
|
|
46
|
+
}
|
|
47
|
+
async getInstallationToken() {
|
|
48
|
+
const cached = this.installationToken;
|
|
49
|
+
if (cached && Date.now() < cached.expiresAt - TOKEN_REFRESH_MARGIN_MS) {
|
|
50
|
+
return cached.value;
|
|
51
|
+
}
|
|
52
|
+
const data = await this.rawRequest("POST", `/app/installations/${this.installationId}/access_tokens`, { auth: `Bearer ${this.appJwt()}` });
|
|
53
|
+
this.installationToken = { value: data.token, expiresAt: Date.parse(data.expires_at) };
|
|
54
|
+
return this.installationToken.value;
|
|
55
|
+
}
|
|
56
|
+
async rawRequest(method, path, options) {
|
|
57
|
+
const headers = {
|
|
58
|
+
Accept: "application/vnd.github+json",
|
|
59
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
60
|
+
"User-Agent": "mikan",
|
|
61
|
+
Authorization: options.auth,
|
|
62
|
+
};
|
|
63
|
+
if (options.conditional) {
|
|
64
|
+
const etag = this.etags.get(path);
|
|
65
|
+
if (etag)
|
|
66
|
+
headers["If-None-Match"] = etag;
|
|
67
|
+
}
|
|
68
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
69
|
+
method,
|
|
70
|
+
headers,
|
|
71
|
+
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
72
|
+
});
|
|
73
|
+
if (response.status === 304) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
const detail = (await response.text().catch(() => "")).slice(0, 300);
|
|
78
|
+
throw new GithubApiError(response.status, method, path, detail || response.statusText);
|
|
79
|
+
}
|
|
80
|
+
if (options.conditional) {
|
|
81
|
+
const etag = response.headers.get("etag");
|
|
82
|
+
if (etag)
|
|
83
|
+
this.etags.set(path, etag);
|
|
84
|
+
}
|
|
85
|
+
if (response.status === 204) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return (await response.json());
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Installation-authenticated request. `conditional` GETs return null on 304
|
|
92
|
+
* (nothing changed since the last call to the same path).
|
|
93
|
+
*/
|
|
94
|
+
async request(method, path, options = {}) {
|
|
95
|
+
const token = await this.getInstallationToken();
|
|
96
|
+
return this.rawRequest(method, path, { ...options, auth: `Bearer ${token}` });
|
|
97
|
+
}
|
|
98
|
+
/** The App's mention slug; its comments author as `<slug>[bot]`. */
|
|
99
|
+
async getAppSlug() {
|
|
100
|
+
const app = await this.rawRequest("GET", "/app", {
|
|
101
|
+
auth: `Bearer ${this.appJwt()}`,
|
|
102
|
+
});
|
|
103
|
+
return app.slug;
|
|
104
|
+
}
|
|
105
|
+
/** Database id of a user/bot login (for noreply commit-author emails). */
|
|
106
|
+
async getUserId(login) {
|
|
107
|
+
const user = await this.request("GET", `/users/${encodeURIComponent(login)}`);
|
|
108
|
+
return user.id;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Mint a one-off installation token narrowed to a single repo and an
|
|
112
|
+
* explicit permission subset. Used for host-side git operations so the
|
|
113
|
+
* broad installation identity never leaves this process.
|
|
114
|
+
*/
|
|
115
|
+
async createScopedInstallationToken(repoName, permissions) {
|
|
116
|
+
const data = await this.rawRequest("POST", `/app/installations/${this.installationId}/access_tokens`, {
|
|
117
|
+
auth: `Bearer ${this.appJwt()}`,
|
|
118
|
+
body: { repositories: [repoName], permissions },
|
|
119
|
+
});
|
|
120
|
+
return data.token;
|
|
121
|
+
}
|
|
122
|
+
async getRepository(owner, repo) {
|
|
123
|
+
const details = await this.request("GET", `/repos/${owner}/${repo}`);
|
|
124
|
+
return details;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A user's effective role on a repo: granular `role_name` plus the legacy
|
|
128
|
+
* `permission` field (callers rank whichever is stronger, so custom roles
|
|
129
|
+
* still resolve via their legacy mapping). Needs only Metadata: Read — the
|
|
130
|
+
* mandatory App permission. Unknown users resolve to none.
|
|
131
|
+
*/
|
|
132
|
+
async getCollaboratorPermission(owner, repo, username) {
|
|
133
|
+
try {
|
|
134
|
+
const data = await this.request("GET", `/repos/${owner}/${repo}/collaborators/${encodeURIComponent(username)}/permission`);
|
|
135
|
+
return data;
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (err instanceof GithubApiError && err.status === 404) {
|
|
139
|
+
return { permission: "none" };
|
|
140
|
+
}
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async createPullRequest(owner, repo, params) {
|
|
145
|
+
const pr = await this.request("POST", `/repos/${owner}/${repo}/pulls`, {
|
|
146
|
+
body: params,
|
|
147
|
+
});
|
|
148
|
+
return pr;
|
|
149
|
+
}
|
|
150
|
+
async getPullRequest(owner, repo, number) {
|
|
151
|
+
const pr = await this.request("GET", `/repos/${owner}/${repo}/pulls/${number}`);
|
|
152
|
+
return pr;
|
|
153
|
+
}
|
|
154
|
+
/** Changed files of one PR (first 100; enough for triage-level reads). */
|
|
155
|
+
async listPullRequestFiles(owner, repo, number) {
|
|
156
|
+
const files = await this.request("GET", `/repos/${owner}/${repo}/pulls/${number}/files?per_page=100`);
|
|
157
|
+
return files;
|
|
158
|
+
}
|
|
159
|
+
/** Submitted reviews of one PR (approvals, change requests, comments). */
|
|
160
|
+
async listPullRequestReviews(owner, repo, number) {
|
|
161
|
+
const reviews = await this.request("GET", `/repos/${owner}/${repo}/pulls/${number}/reviews?per_page=100`);
|
|
162
|
+
return reviews;
|
|
163
|
+
}
|
|
164
|
+
/** Recent comments of one issue/PR conversation, oldest first. */
|
|
165
|
+
async listIssueComments(owner, repo, number) {
|
|
166
|
+
const comments = await this.request("GET", `/repos/${owner}/${repo}/issues/${number}/comments?per_page=30`);
|
|
167
|
+
return comments;
|
|
168
|
+
}
|
|
169
|
+
/** Issues/PRs of a repo filtered for triage-style listing. */
|
|
170
|
+
async listIssues(owner, repo, filters = {}) {
|
|
171
|
+
const params = new URLSearchParams({
|
|
172
|
+
state: filters.state ?? "open",
|
|
173
|
+
sort: "updated",
|
|
174
|
+
direction: "desc",
|
|
175
|
+
per_page: "30",
|
|
176
|
+
});
|
|
177
|
+
if (filters.labels)
|
|
178
|
+
params.set("labels", filters.labels);
|
|
179
|
+
if (filters.creator)
|
|
180
|
+
params.set("creator", filters.creator);
|
|
181
|
+
const issues = await this.request("GET", `/repos/${owner}/${repo}/issues?${params}`);
|
|
182
|
+
return issues;
|
|
183
|
+
}
|
|
184
|
+
/** The open PR whose head is `branch`, or null when none exists. */
|
|
185
|
+
async findOpenPullRequestByBranch(owner, repo, branch) {
|
|
186
|
+
const prs = await this.request("GET", `/repos/${owner}/${repo}/pulls?state=open&head=${encodeURIComponent(`${owner}:${branch}`)}&per_page=1`);
|
|
187
|
+
return prs && prs.length > 0 ? prs[0] : null;
|
|
188
|
+
}
|
|
189
|
+
/** CI check runs for a ref (branch, tag, or sha). Needs Checks: Read. */
|
|
190
|
+
async listCheckRuns(owner, repo, ref) {
|
|
191
|
+
const data = await this.request("GET", `/repos/${owner}/${repo}/commits/${encodeURIComponent(ref)}/check-runs?per_page=50`);
|
|
192
|
+
return data.check_runs;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Plaintext log of one Actions job (a check-run id). Needs Actions: Read.
|
|
196
|
+
* GitHub responds with a redirect to blob storage; fetch follows it and the
|
|
197
|
+
* Authorization header is dropped cross-origin, which is what we want.
|
|
198
|
+
*/
|
|
199
|
+
async getJobLog(owner, repo, jobId) {
|
|
200
|
+
const path = `/repos/${owner}/${repo}/actions/jobs/${jobId}/logs`;
|
|
201
|
+
const token = await this.getInstallationToken();
|
|
202
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
203
|
+
headers: {
|
|
204
|
+
Accept: "application/vnd.github+json",
|
|
205
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
206
|
+
"User-Agent": "mikan",
|
|
207
|
+
Authorization: `Bearer ${token}`,
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
if (!response.ok) {
|
|
211
|
+
const detail = (await response.text().catch(() => "")).slice(0, 300);
|
|
212
|
+
throw new GithubApiError(response.status, "GET", path, detail || response.statusText);
|
|
213
|
+
}
|
|
214
|
+
return response.text();
|
|
215
|
+
}
|
|
216
|
+
async listInstallationRepositories() {
|
|
217
|
+
const data = await this.request("GET", "/installation/repositories?per_page=100");
|
|
218
|
+
return data.repositories;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* All issue/PR conversation comments in a repo updated at or after `since`,
|
|
222
|
+
* oldest first. Returns null when nothing changed (ETag 304). Capped at one
|
|
223
|
+
* page; the poll cursor advances to the newest item seen, so a burst larger
|
|
224
|
+
* than 100 comments drains across consecutive polls.
|
|
225
|
+
*/
|
|
226
|
+
async listIssueCommentsSince(owner, repo, since) {
|
|
227
|
+
return this.request("GET", `/repos/${owner}/${repo}/issues/comments?sort=updated&direction=asc&since=${encodeURIComponent(since)}&per_page=100`, { conditional: true });
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* All inline PR review comments in a repo updated at or after `since`,
|
|
231
|
+
* oldest first. Same shape and caveats as listIssueCommentsSince (one page,
|
|
232
|
+
* ETag 304 → null); review comments live in their own id space.
|
|
233
|
+
*/
|
|
234
|
+
async listPullReviewCommentsSince(owner, repo, since) {
|
|
235
|
+
return this.request("GET", `/repos/${owner}/${repo}/pulls/comments?sort=updated&direction=asc&since=${encodeURIComponent(since)}&per_page=100`, { conditional: true });
|
|
236
|
+
}
|
|
237
|
+
/** All review comments on one PR (for reconstructing a thread's history). */
|
|
238
|
+
async listPullReviewComments(owner, repo, number) {
|
|
239
|
+
const comments = await this.request("GET", `/repos/${owner}/${repo}/pulls/${number}/comments?per_page=100`);
|
|
240
|
+
return comments;
|
|
241
|
+
}
|
|
242
|
+
/** Issues and PRs updated at or after `since` (GitHub lists PRs as issues). */
|
|
243
|
+
async listIssuesSince(owner, repo, since) {
|
|
244
|
+
return this.request("GET", `/repos/${owner}/${repo}/issues?state=all&sort=updated&direction=asc&since=${encodeURIComponent(since)}&per_page=100`, { conditional: true });
|
|
245
|
+
}
|
|
246
|
+
async getIssue(owner, repo, number) {
|
|
247
|
+
const issue = await this.request("GET", `/repos/${owner}/${repo}/issues/${number}`);
|
|
248
|
+
return issue;
|
|
249
|
+
}
|
|
250
|
+
async addIssueLabels(owner, repo, number, labels) {
|
|
251
|
+
await this.request("POST", `/repos/${owner}/${repo}/issues/${number}/labels`, {
|
|
252
|
+
body: { labels },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
async removeIssueLabel(owner, repo, number, label) {
|
|
256
|
+
await this.request("DELETE", `/repos/${owner}/${repo}/issues/${number}/labels/${encodeURIComponent(label)}`);
|
|
257
|
+
}
|
|
258
|
+
async addIssueAssignees(owner, repo, number, assignees) {
|
|
259
|
+
await this.request("POST", `/repos/${owner}/${repo}/issues/${number}/assignees`, {
|
|
260
|
+
body: { assignees },
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async removeIssueAssignees(owner, repo, number, assignees) {
|
|
264
|
+
await this.request("DELETE", `/repos/${owner}/${repo}/issues/${number}/assignees`, {
|
|
265
|
+
body: { assignees },
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
async updateIssueState(owner, repo, number, state, stateReason) {
|
|
269
|
+
await this.request("PATCH", `/repos/${owner}/${repo}/issues/${number}`, {
|
|
270
|
+
body: { state, ...(stateReason ? { state_reason: stateReason } : {}) },
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async createIssueComment(owner, repo, number, body) {
|
|
274
|
+
const comment = await this.request("POST", `/repos/${owner}/${repo}/issues/${number}/comments`, { body: { body } });
|
|
275
|
+
return comment;
|
|
276
|
+
}
|
|
277
|
+
async updateIssueComment(owner, repo, commentId, body) {
|
|
278
|
+
await this.request("PATCH", `/repos/${owner}/${repo}/issues/comments/${commentId}`, {
|
|
279
|
+
body: { body },
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
async deleteIssueComment(owner, repo, commentId) {
|
|
283
|
+
await this.request("DELETE", `/repos/${owner}/${repo}/issues/comments/${commentId}`);
|
|
284
|
+
}
|
|
285
|
+
async createCommentReaction(owner, repo, commentId, content) {
|
|
286
|
+
await this.request("POST", `/repos/${owner}/${repo}/issues/comments/${commentId}/reactions`, {
|
|
287
|
+
body: { content },
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Reply inside one review thread. GitHub 404s when `commentId` is not a
|
|
292
|
+
* review comment on PR `number` — server-side same-PR validation for free.
|
|
293
|
+
*/
|
|
294
|
+
async replyToReviewComment(owner, repo, number, commentId, body) {
|
|
295
|
+
const comment = await this.request("POST", `/repos/${owner}/${repo}/pulls/${number}/comments/${commentId}/replies`, { body: { body } });
|
|
296
|
+
return comment;
|
|
297
|
+
}
|
|
298
|
+
async createReviewCommentReaction(owner, repo, commentId, content) {
|
|
299
|
+
await this.request("POST", `/repos/${owner}/${repo}/pulls/comments/${commentId}/reactions`, {
|
|
300
|
+
body: { content },
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
async createIssueReaction(owner, repo, number, content) {
|
|
304
|
+
await this.request("POST", `/repos/${owner}/${repo}/issues/${number}/reactions`, {
|
|
305
|
+
body: { content },
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/adapters/github/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { createSign } from \"crypto\";\nimport type {\n GithubCheckRun,\n GithubClientOptions,\n GithubCollaboratorPermission,\n GithubIssue,\n GithubIssueComment,\n GithubPullRequest,\n GithubPullRequestFile,\n GithubPullRequestReview,\n GithubReactionContent,\n GithubRepository,\n GithubRepositoryDetails,\n GithubReviewComment,\n GithubTokenPermissions,\n} from \"./types.js\";\n\nexport class GithubApiError extends Error {\n constructor(\n public readonly status: number,\n method: string,\n path: string,\n detail: string,\n ) {\n super(`GitHub ${method} ${path} failed with ${status}: ${detail}`);\n this.name = \"GithubApiError\";\n }\n}\n\n/**\n * GitHub rate-limit responses are 429, or 403 with a rate-limit message\n * (secondary limits). Used as the `withRetry` predicate.\n */\nexport function githubIsRateLimited(err: Error): boolean {\n if (!(err instanceof GithubApiError)) return false;\n return err.status === 429 || (err.status === 403 && /rate limit/i.test(err.message));\n}\n\nfunction base64Url(data: string | Buffer): string {\n return (typeof data === \"string\" ? Buffer.from(data) : data).toString(\"base64url\");\n}\n\n/** Refresh the cached installation token this long before it expires. */\nconst TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;\n\n/**\n * Minimal GitHub REST client authenticated as a GitHub App installation.\n * Mints short-lived installation tokens from an RS256 app JWT and supports\n * ETag conditional polling (304 responses don't count against the rate limit).\n */\nexport class GithubClient {\n private readonly appId: string;\n private readonly privateKey: string;\n private readonly installationId: string;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private installationToken: { value: string; expiresAt: number } | null = null;\n private etags = new Map<string, string>();\n\n constructor(options: GithubClientOptions) {\n this.appId = options.appId;\n this.privateKey = options.privateKey;\n this.installationId = options.installationId;\n this.baseUrl = (options.baseUrl ?? \"https://api.github.com\").replace(/\\/+$/, \"\");\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n /** Short-lived JWT identifying the App itself (not an installation). */\n private appJwt(): string {\n const now = Math.floor(Date.now() / 1000);\n const header = base64Url(JSON.stringify({ alg: \"RS256\", typ: \"JWT\" }));\n // 60s clock-drift backdate; GitHub caps exp at 10 minutes.\n const payload = base64Url(JSON.stringify({ iat: now - 60, exp: now + 540, iss: this.appId }));\n const signature = base64Url(\n createSign(\"RSA-SHA256\").update(`${header}.${payload}`).sign(this.privateKey),\n );\n return `${header}.${payload}.${signature}`;\n }\n\n private async getInstallationToken(): Promise<string> {\n const cached = this.installationToken;\n if (cached && Date.now() < cached.expiresAt - TOKEN_REFRESH_MARGIN_MS) {\n return cached.value;\n }\n const data = await this.rawRequest<{ token: string; expires_at: string }>(\n \"POST\",\n `/app/installations/${this.installationId}/access_tokens`,\n { auth: `Bearer ${this.appJwt()}` },\n );\n this.installationToken = { value: data!.token, expiresAt: Date.parse(data!.expires_at) };\n return this.installationToken.value;\n }\n\n private async rawRequest<T>(\n method: string,\n path: string,\n options: { auth: string; body?: unknown; conditional?: boolean },\n ): Promise<T | null> {\n const headers: Record<string, string> = {\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"mikan\",\n Authorization: options.auth,\n };\n if (options.conditional) {\n const etag = this.etags.get(path);\n if (etag) headers[\"If-None-Match\"] = etag;\n }\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: options.body !== undefined ? JSON.stringify(options.body) : undefined,\n });\n if (response.status === 304) {\n return null;\n }\n if (!response.ok) {\n const detail = (await response.text().catch(() => \"\")).slice(0, 300);\n throw new GithubApiError(response.status, method, path, detail || response.statusText);\n }\n if (options.conditional) {\n const etag = response.headers.get(\"etag\");\n if (etag) this.etags.set(path, etag);\n }\n if (response.status === 204) {\n return null;\n }\n return (await response.json()) as T;\n }\n\n /**\n * Installation-authenticated request. `conditional` GETs return null on 304\n * (nothing changed since the last call to the same path).\n */\n private async request<T>(\n method: string,\n path: string,\n options: { body?: unknown; conditional?: boolean } = {},\n ): Promise<T | null> {\n const token = await this.getInstallationToken();\n return this.rawRequest<T>(method, path, { ...options, auth: `Bearer ${token}` });\n }\n\n /** The App's mention slug; its comments author as `<slug>[bot]`. */\n async getAppSlug(): Promise<string> {\n const app = await this.rawRequest<{ slug: string }>(\"GET\", \"/app\", {\n auth: `Bearer ${this.appJwt()}`,\n });\n return app!.slug;\n }\n\n /** Database id of a user/bot login (for noreply commit-author emails). */\n async getUserId(login: string): Promise<number> {\n const user = await this.request<{ id: number }>(\"GET\", `/users/${encodeURIComponent(login)}`);\n return user!.id;\n }\n\n /**\n * Mint a one-off installation token narrowed to a single repo and an\n * explicit permission subset. Used for host-side git operations so the\n * broad installation identity never leaves this process.\n */\n async createScopedInstallationToken(\n repoName: string,\n permissions: GithubTokenPermissions,\n ): Promise<string> {\n const data = await this.rawRequest<{ token: string }>(\n \"POST\",\n `/app/installations/${this.installationId}/access_tokens`,\n {\n auth: `Bearer ${this.appJwt()}`,\n body: { repositories: [repoName], permissions },\n },\n );\n return data!.token;\n }\n\n async getRepository(owner: string, repo: string): Promise<GithubRepositoryDetails> {\n const details = await this.request<GithubRepositoryDetails>(\"GET\", `/repos/${owner}/${repo}`);\n return details!;\n }\n\n /**\n * A user's effective role on a repo: granular `role_name` plus the legacy\n * `permission` field (callers rank whichever is stronger, so custom roles\n * still resolve via their legacy mapping). Needs only Metadata: Read — the\n * mandatory App permission. Unknown users resolve to none.\n */\n async getCollaboratorPermission(\n owner: string,\n repo: string,\n username: string,\n ): Promise<GithubCollaboratorPermission> {\n try {\n const data = await this.request<GithubCollaboratorPermission>(\n \"GET\",\n `/repos/${owner}/${repo}/collaborators/${encodeURIComponent(username)}/permission`,\n );\n return data!;\n } catch (err) {\n if (err instanceof GithubApiError && err.status === 404) {\n return { permission: \"none\" };\n }\n throw err;\n }\n }\n\n async createPullRequest(\n owner: string,\n repo: string,\n params: { title: string; head: string; base: string; body?: string; draft?: boolean },\n ): Promise<GithubPullRequest> {\n const pr = await this.request<GithubPullRequest>(\"POST\", `/repos/${owner}/${repo}/pulls`, {\n body: params,\n });\n return pr!;\n }\n\n async getPullRequest(owner: string, repo: string, number: number): Promise<GithubPullRequest> {\n const pr = await this.request<GithubPullRequest>(\n \"GET\",\n `/repos/${owner}/${repo}/pulls/${number}`,\n );\n return pr!;\n }\n\n /** Changed files of one PR (first 100; enough for triage-level reads). */\n async listPullRequestFiles(\n owner: string,\n repo: string,\n number: number,\n ): Promise<GithubPullRequestFile[]> {\n const files = await this.request<GithubPullRequestFile[]>(\n \"GET\",\n `/repos/${owner}/${repo}/pulls/${number}/files?per_page=100`,\n );\n return files!;\n }\n\n /** Submitted reviews of one PR (approvals, change requests, comments). */\n async listPullRequestReviews(\n owner: string,\n repo: string,\n number: number,\n ): Promise<GithubPullRequestReview[]> {\n const reviews = await this.request<GithubPullRequestReview[]>(\n \"GET\",\n `/repos/${owner}/${repo}/pulls/${number}/reviews?per_page=100`,\n );\n return reviews!;\n }\n\n /** Recent comments of one issue/PR conversation, oldest first. */\n async listIssueComments(\n owner: string,\n repo: string,\n number: number,\n ): Promise<GithubIssueComment[]> {\n const comments = await this.request<GithubIssueComment[]>(\n \"GET\",\n `/repos/${owner}/${repo}/issues/${number}/comments?per_page=30`,\n );\n return comments!;\n }\n\n /** Issues/PRs of a repo filtered for triage-style listing. */\n async listIssues(\n owner: string,\n repo: string,\n filters: { state?: string; labels?: string; creator?: string } = {},\n ): Promise<GithubIssue[]> {\n const params = new URLSearchParams({\n state: filters.state ?? \"open\",\n sort: \"updated\",\n direction: \"desc\",\n per_page: \"30\",\n });\n if (filters.labels) params.set(\"labels\", filters.labels);\n if (filters.creator) params.set(\"creator\", filters.creator);\n const issues = await this.request<GithubIssue[]>(\n \"GET\",\n `/repos/${owner}/${repo}/issues?${params}`,\n );\n return issues!;\n }\n\n /** The open PR whose head is `branch`, or null when none exists. */\n async findOpenPullRequestByBranch(\n owner: string,\n repo: string,\n branch: string,\n ): Promise<GithubPullRequest | null> {\n const prs = await this.request<GithubPullRequest[]>(\n \"GET\",\n `/repos/${owner}/${repo}/pulls?state=open&head=${encodeURIComponent(`${owner}:${branch}`)}&per_page=1`,\n );\n return prs && prs.length > 0 ? prs[0] : null;\n }\n\n /** CI check runs for a ref (branch, tag, or sha). Needs Checks: Read. */\n async listCheckRuns(owner: string, repo: string, ref: string): Promise<GithubCheckRun[]> {\n const data = await this.request<{ check_runs: GithubCheckRun[] }>(\n \"GET\",\n `/repos/${owner}/${repo}/commits/${encodeURIComponent(ref)}/check-runs?per_page=50`,\n );\n return data!.check_runs;\n }\n\n /**\n * Plaintext log of one Actions job (a check-run id). Needs Actions: Read.\n * GitHub responds with a redirect to blob storage; fetch follows it and the\n * Authorization header is dropped cross-origin, which is what we want.\n */\n async getJobLog(owner: string, repo: string, jobId: number): Promise<string> {\n const path = `/repos/${owner}/${repo}/actions/jobs/${jobId}/logs`;\n const token = await this.getInstallationToken();\n const response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n headers: {\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n \"User-Agent\": \"mikan\",\n Authorization: `Bearer ${token}`,\n },\n });\n if (!response.ok) {\n const detail = (await response.text().catch(() => \"\")).slice(0, 300);\n throw new GithubApiError(response.status, \"GET\", path, detail || response.statusText);\n }\n return response.text();\n }\n\n async listInstallationRepositories(): Promise<GithubRepository[]> {\n const data = await this.request<{ repositories: GithubRepository[] }>(\n \"GET\",\n \"/installation/repositories?per_page=100\",\n );\n return data!.repositories;\n }\n\n /**\n * All issue/PR conversation comments in a repo updated at or after `since`,\n * oldest first. Returns null when nothing changed (ETag 304). Capped at one\n * page; the poll cursor advances to the newest item seen, so a burst larger\n * than 100 comments drains across consecutive polls.\n */\n async listIssueCommentsSince(\n owner: string,\n repo: string,\n since: string,\n ): Promise<GithubIssueComment[] | null> {\n return this.request<GithubIssueComment[]>(\n \"GET\",\n `/repos/${owner}/${repo}/issues/comments?sort=updated&direction=asc&since=${encodeURIComponent(since)}&per_page=100`,\n { conditional: true },\n );\n }\n\n /**\n * All inline PR review comments in a repo updated at or after `since`,\n * oldest first. Same shape and caveats as listIssueCommentsSince (one page,\n * ETag 304 → null); review comments live in their own id space.\n */\n async listPullReviewCommentsSince(\n owner: string,\n repo: string,\n since: string,\n ): Promise<GithubReviewComment[] | null> {\n return this.request<GithubReviewComment[]>(\n \"GET\",\n `/repos/${owner}/${repo}/pulls/comments?sort=updated&direction=asc&since=${encodeURIComponent(since)}&per_page=100`,\n { conditional: true },\n );\n }\n\n /** All review comments on one PR (for reconstructing a thread's history). */\n async listPullReviewComments(\n owner: string,\n repo: string,\n number: number,\n ): Promise<GithubReviewComment[]> {\n const comments = await this.request<GithubReviewComment[]>(\n \"GET\",\n `/repos/${owner}/${repo}/pulls/${number}/comments?per_page=100`,\n );\n return comments!;\n }\n\n /** Issues and PRs updated at or after `since` (GitHub lists PRs as issues). */\n async listIssuesSince(owner: string, repo: string, since: string): Promise<GithubIssue[] | null> {\n return this.request<GithubIssue[]>(\n \"GET\",\n `/repos/${owner}/${repo}/issues?state=all&sort=updated&direction=asc&since=${encodeURIComponent(since)}&per_page=100`,\n { conditional: true },\n );\n }\n\n async getIssue(owner: string, repo: string, number: number): Promise<GithubIssue> {\n const issue = await this.request<GithubIssue>(\n \"GET\",\n `/repos/${owner}/${repo}/issues/${number}`,\n );\n return issue!;\n }\n\n async addIssueLabels(\n owner: string,\n repo: string,\n number: number,\n labels: string[],\n ): Promise<void> {\n await this.request(\"POST\", `/repos/${owner}/${repo}/issues/${number}/labels`, {\n body: { labels },\n });\n }\n\n async removeIssueLabel(\n owner: string,\n repo: string,\n number: number,\n label: string,\n ): Promise<void> {\n await this.request(\n \"DELETE\",\n `/repos/${owner}/${repo}/issues/${number}/labels/${encodeURIComponent(label)}`,\n );\n }\n\n async addIssueAssignees(\n owner: string,\n repo: string,\n number: number,\n assignees: string[],\n ): Promise<void> {\n await this.request(\"POST\", `/repos/${owner}/${repo}/issues/${number}/assignees`, {\n body: { assignees },\n });\n }\n\n async removeIssueAssignees(\n owner: string,\n repo: string,\n number: number,\n assignees: string[],\n ): Promise<void> {\n await this.request(\"DELETE\", `/repos/${owner}/${repo}/issues/${number}/assignees`, {\n body: { assignees },\n });\n }\n\n async updateIssueState(\n owner: string,\n repo: string,\n number: number,\n state: \"open\" | \"closed\",\n stateReason?: string,\n ): Promise<void> {\n await this.request(\"PATCH\", `/repos/${owner}/${repo}/issues/${number}`, {\n body: { state, ...(stateReason ? { state_reason: stateReason } : {}) },\n });\n }\n\n async createIssueComment(\n owner: string,\n repo: string,\n number: number,\n body: string,\n ): Promise<GithubIssueComment> {\n const comment = await this.request<GithubIssueComment>(\n \"POST\",\n `/repos/${owner}/${repo}/issues/${number}/comments`,\n { body: { body } },\n );\n return comment!;\n }\n\n async updateIssueComment(\n owner: string,\n repo: string,\n commentId: number,\n body: string,\n ): Promise<void> {\n await this.request(\"PATCH\", `/repos/${owner}/${repo}/issues/comments/${commentId}`, {\n body: { body },\n });\n }\n\n async deleteIssueComment(owner: string, repo: string, commentId: number): Promise<void> {\n await this.request(\"DELETE\", `/repos/${owner}/${repo}/issues/comments/${commentId}`);\n }\n\n async createCommentReaction(\n owner: string,\n repo: string,\n commentId: number,\n content: GithubReactionContent,\n ): Promise<void> {\n await this.request(\"POST\", `/repos/${owner}/${repo}/issues/comments/${commentId}/reactions`, {\n body: { content },\n });\n }\n\n /**\n * Reply inside one review thread. GitHub 404s when `commentId` is not a\n * review comment on PR `number` — server-side same-PR validation for free.\n */\n async replyToReviewComment(\n owner: string,\n repo: string,\n number: number,\n commentId: number,\n body: string,\n ): Promise<GithubReviewComment> {\n const comment = await this.request<GithubReviewComment>(\n \"POST\",\n `/repos/${owner}/${repo}/pulls/${number}/comments/${commentId}/replies`,\n { body: { body } },\n );\n return comment!;\n }\n\n async createReviewCommentReaction(\n owner: string,\n repo: string,\n commentId: number,\n content: GithubReactionContent,\n ): Promise<void> {\n await this.request(\"POST\", `/repos/${owner}/${repo}/pulls/comments/${commentId}/reactions`, {\n body: { content },\n });\n }\n\n async createIssueReaction(\n owner: string,\n repo: string,\n number: number,\n content: GithubReactionContent,\n ): Promise<void> {\n await this.request(\"POST\", `/repos/${owner}/${repo}/issues/${number}/reactions`, {\n body: { content },\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { GcpTokenProvider } from "./gcp-auth.js";
|
|
2
|
+
/** Check runs reported by this app slug are Cloud Build builds. */
|
|
3
|
+
export declare const CLOUD_BUILD_APP_SLUG = "google-cloud-build";
|
|
4
|
+
export declare function isCloudBuildId(value: string): boolean;
|
|
5
|
+
/** GCP project (id or number) from a Cloud Build details_url, or null. */
|
|
6
|
+
export declare function projectFromDetailsUrl(detailsUrl: string | null | undefined): string | null;
|
|
7
|
+
export interface CloudBuildLogOptions {
|
|
8
|
+
tokenProvider: GcpTokenProvider;
|
|
9
|
+
project: string;
|
|
10
|
+
buildId: string;
|
|
11
|
+
fetchImpl?: typeof fetch;
|
|
12
|
+
}
|
|
13
|
+
export declare class CloudBuildLogUnavailableError extends Error {
|
|
14
|
+
readonly consoleUrl?: string | undefined;
|
|
15
|
+
constructor(message: string, consoleUrl?: string | undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fetch one build's log text: builds.get → the `log-<id>.txt` object in the
|
|
19
|
+
* build's logs bucket. Throws CloudBuildLogUnavailableError (with the console
|
|
20
|
+
* url when known) whenever the log cannot be read — access denied, build not
|
|
21
|
+
* found, or CLOUD_LOGGING_ONLY builds that write no GCS object.
|
|
22
|
+
*/
|
|
23
|
+
export declare function fetchCloudBuildLog(options: CloudBuildLogOptions): Promise<string>;
|
|
24
|
+
//# sourceMappingURL=cloudbuild.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudbuild.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/cloudbuild.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAetD,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAIzD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQ1F;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,gBAAgB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAQD,qBAAa,6BAA8B,SAAQ,KAAK;aAGpC,UAAU,CAAC,EAAE,MAAM;IAFrC,YACE,OAAO,EAAE,MAAM,EACC,UAAU,CAAC,EAAE,MAAM,YAAA,EAIpC;CACF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyCvF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side Cloud Build log retrieval for `github_checks`. Cloud Build's
|
|
3
|
+
* GitHub App reports check runs whose `external_id` is the build UUID and
|
|
4
|
+
* whose `details_url` carries the GCP project; with ADC on the host (e.g. a
|
|
5
|
+
* WIF external_account file) the build's log object can be read from GCS.
|
|
6
|
+
*
|
|
7
|
+
* Required IAM for the ADC principal: roles/cloudbuild.builds.viewer on the
|
|
8
|
+
* project and roles/storage.objectViewer on the logs bucket.
|
|
9
|
+
*/
|
|
10
|
+
const CLOUDBUILD_API = "https://cloudbuild.googleapis.com/v1";
|
|
11
|
+
const GCS_API = "https://storage.googleapis.com/storage/v1";
|
|
12
|
+
/** Check runs reported by this app slug are Cloud Build builds. */
|
|
13
|
+
export const CLOUD_BUILD_APP_SLUG = "google-cloud-build";
|
|
14
|
+
const BUILD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
15
|
+
export function isCloudBuildId(value) {
|
|
16
|
+
return BUILD_ID_PATTERN.test(value);
|
|
17
|
+
}
|
|
18
|
+
/** GCP project (id or number) from a Cloud Build details_url, or null. */
|
|
19
|
+
export function projectFromDetailsUrl(detailsUrl) {
|
|
20
|
+
if (!detailsUrl)
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
const project = new URL(detailsUrl).searchParams.get("project");
|
|
24
|
+
return project || null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class CloudBuildLogUnavailableError extends Error {
|
|
31
|
+
constructor(message, consoleUrl) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.consoleUrl = consoleUrl;
|
|
34
|
+
this.name = "CloudBuildLogUnavailableError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fetch one build's log text: builds.get → the `log-<id>.txt` object in the
|
|
39
|
+
* build's logs bucket. Throws CloudBuildLogUnavailableError (with the console
|
|
40
|
+
* url when known) whenever the log cannot be read — access denied, build not
|
|
41
|
+
* found, or CLOUD_LOGGING_ONLY builds that write no GCS object.
|
|
42
|
+
*/
|
|
43
|
+
export async function fetchCloudBuildLog(options) {
|
|
44
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
45
|
+
const token = await options.tokenProvider.getAccessToken();
|
|
46
|
+
const authHeaders = { Authorization: `Bearer ${token}` };
|
|
47
|
+
const buildResponse = await fetchImpl(`${CLOUDBUILD_API}/projects/${encodeURIComponent(options.project)}/builds/${encodeURIComponent(options.buildId)}`, { headers: authHeaders });
|
|
48
|
+
if (!buildResponse.ok) {
|
|
49
|
+
throw new CloudBuildLogUnavailableError(`Cloud Build builds.get failed with ${buildResponse.status} — check that the ADC ` +
|
|
50
|
+
`principal has roles/cloudbuild.builds.viewer on project ${options.project}.`);
|
|
51
|
+
}
|
|
52
|
+
const build = (await buildResponse.json());
|
|
53
|
+
if (!build.logsBucket) {
|
|
54
|
+
throw new CloudBuildLogUnavailableError("This build stores logs in Cloud Logging only (no GCS log object); read them in the console.", build.logUrl);
|
|
55
|
+
}
|
|
56
|
+
// logsBucket may be spelled gs://bucket or gs://bucket/some/prefix.
|
|
57
|
+
const bucketPath = build.logsBucket.replace(/^gs:\/\//, "");
|
|
58
|
+
const [bucket, ...prefixParts] = bucketPath.split("/");
|
|
59
|
+
const prefix = prefixParts.length > 0 ? `${prefixParts.join("/")}/` : "";
|
|
60
|
+
const objectName = encodeURIComponent(`${prefix}log-${options.buildId}.txt`);
|
|
61
|
+
const logResponse = await fetchImpl(`${GCS_API}/b/${bucket}/o/${objectName}?alt=media`, {
|
|
62
|
+
headers: authHeaders,
|
|
63
|
+
});
|
|
64
|
+
if (!logResponse.ok) {
|
|
65
|
+
throw new CloudBuildLogUnavailableError(`Cloud Build log object gs://${bucket}/${prefix}log-${options.buildId}.txt returned ` +
|
|
66
|
+
`${logResponse.status} — the ADC principal may lack roles/storage.objectViewer on the ` +
|
|
67
|
+
`logs bucket, or the build writes logs elsewhere.`, build.logUrl);
|
|
68
|
+
}
|
|
69
|
+
return logResponse.text();
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=cloudbuild.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudbuild.js","sourceRoot":"","sources":["../../../src/adapters/github/cloudbuild.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,MAAM,cAAc,GAAG,sCAAsC,CAAC;AAC9D,MAAM,OAAO,GAAG,2CAA2C,CAAC;AAE5D,mEAAmE;AACnE,MAAM,CAAC,MAAM,oBAAoB,GAAG,oBAAoB,CAAC;AAEzD,MAAM,gBAAgB,GAAG,iEAAiE,CAAC;AAE3F,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,qBAAqB,CAAC,UAAqC;IACzE,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAChE,OAAO,OAAO,IAAI,IAAI,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAeD,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IACtD,YACE,OAAe,EACC,UAAmB;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;0BAFC,UAAU;QAG1B,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;IAC9C,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAA6B;IACpE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,cAAc,EAAE,CAAC;IAC3D,MAAM,WAAW,GAAG,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC;IAEzD,MAAM,aAAa,GAAG,MAAM,SAAS,CACnC,GAAG,cAAc,aAAa,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EACjH,EAAE,OAAO,EAAE,WAAW,EAAE,CACzB,CAAC;IACF,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,6BAA6B,CACrC,sCAAsC,aAAa,CAAC,MAAM,wBAAwB;YAChF,2DAA2D,OAAO,CAAC,OAAO,GAAG,CAChF,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,CAAoB,CAAC;IAE9D,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,IAAI,6BAA6B,CACrC,6FAA6F,EAC7F,KAAK,CAAC,MAAM,CACb,CAAC;IACJ,CAAC;IACD,oEAAoE;IACpE,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,MAAM,OAAO,OAAO,CAAC,OAAO,MAAM,CAAC,CAAC;IAE7E,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,GAAG,OAAO,MAAM,MAAM,MAAM,UAAU,YAAY,EAAE;QACtF,OAAO,EAAE,WAAW;KACrB,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,6BAA6B,CACrC,+BAA+B,MAAM,IAAI,MAAM,OAAO,OAAO,CAAC,OAAO,gBAAgB;YACnF,GAAG,WAAW,CAAC,MAAM,kEAAkE;YACvF,kDAAkD,EACpD,KAAK,CAAC,MAAM,CACb,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC;AAC5B,CAAC","sourcesContent":["import type { GcpTokenProvider } from \"./gcp-auth.js\";\n\n/**\n * Host-side Cloud Build log retrieval for `github_checks`. Cloud Build's\n * GitHub App reports check runs whose `external_id` is the build UUID and\n * whose `details_url` carries the GCP project; with ADC on the host (e.g. a\n * WIF external_account file) the build's log object can be read from GCS.\n *\n * Required IAM for the ADC principal: roles/cloudbuild.builds.viewer on the\n * project and roles/storage.objectViewer on the logs bucket.\n */\n\nconst CLOUDBUILD_API = \"https://cloudbuild.googleapis.com/v1\";\nconst GCS_API = \"https://storage.googleapis.com/storage/v1\";\n\n/** Check runs reported by this app slug are Cloud Build builds. */\nexport const CLOUD_BUILD_APP_SLUG = \"google-cloud-build\";\n\nconst BUILD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport function isCloudBuildId(value: string): boolean {\n return BUILD_ID_PATTERN.test(value);\n}\n\n/** GCP project (id or number) from a Cloud Build details_url, or null. */\nexport function projectFromDetailsUrl(detailsUrl: string | null | undefined): string | null {\n if (!detailsUrl) return null;\n try {\n const project = new URL(detailsUrl).searchParams.get(\"project\");\n return project || null;\n } catch {\n return null;\n }\n}\n\nexport interface CloudBuildLogOptions {\n tokenProvider: GcpTokenProvider;\n project: string;\n buildId: string;\n fetchImpl?: typeof fetch;\n}\n\ninterface CloudBuildBuild {\n status?: string;\n logsBucket?: string;\n logUrl?: string;\n}\n\nexport class CloudBuildLogUnavailableError extends Error {\n constructor(\n message: string,\n public readonly consoleUrl?: string,\n ) {\n super(message);\n this.name = \"CloudBuildLogUnavailableError\";\n }\n}\n\n/**\n * Fetch one build's log text: builds.get → the `log-<id>.txt` object in the\n * build's logs bucket. Throws CloudBuildLogUnavailableError (with the console\n * url when known) whenever the log cannot be read — access denied, build not\n * found, or CLOUD_LOGGING_ONLY builds that write no GCS object.\n */\nexport async function fetchCloudBuildLog(options: CloudBuildLogOptions): Promise<string> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const token = await options.tokenProvider.getAccessToken();\n const authHeaders = { Authorization: `Bearer ${token}` };\n\n const buildResponse = await fetchImpl(\n `${CLOUDBUILD_API}/projects/${encodeURIComponent(options.project)}/builds/${encodeURIComponent(options.buildId)}`,\n { headers: authHeaders },\n );\n if (!buildResponse.ok) {\n throw new CloudBuildLogUnavailableError(\n `Cloud Build builds.get failed with ${buildResponse.status} — check that the ADC ` +\n `principal has roles/cloudbuild.builds.viewer on project ${options.project}.`,\n );\n }\n const build = (await buildResponse.json()) as CloudBuildBuild;\n\n if (!build.logsBucket) {\n throw new CloudBuildLogUnavailableError(\n \"This build stores logs in Cloud Logging only (no GCS log object); read them in the console.\",\n build.logUrl,\n );\n }\n // logsBucket may be spelled gs://bucket or gs://bucket/some/prefix.\n const bucketPath = build.logsBucket.replace(/^gs:\\/\\//, \"\");\n const [bucket, ...prefixParts] = bucketPath.split(\"/\");\n const prefix = prefixParts.length > 0 ? `${prefixParts.join(\"/\")}/` : \"\";\n const objectName = encodeURIComponent(`${prefix}log-${options.buildId}.txt`);\n\n const logResponse = await fetchImpl(`${GCS_API}/b/${bucket}/o/${objectName}?alt=media`, {\n headers: authHeaders,\n });\n if (!logResponse.ok) {\n throw new CloudBuildLogUnavailableError(\n `Cloud Build log object gs://${bucket}/${prefix}log-${options.buildId}.txt returned ` +\n `${logResponse.status} — the ADC principal may lack roles/storage.objectViewer on the ` +\n `logs bucket, or the build writes logs elsewhere.`,\n build.logUrl,\n );\n }\n return logResponse.text();\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConversationMessage, ConversationResponder, MessagingInfo } from "../../adapter.js";
|
|
2
|
+
import { type GithubMessagingBot } from "./bot.js";
|
|
3
|
+
import type { GithubEvent } from "./types.js";
|
|
4
|
+
export declare function createGithubAdapters(event: GithubEvent, bot: GithubMessagingBot): {
|
|
5
|
+
message: ConversationMessage;
|
|
6
|
+
responder: ConversationResponder;
|
|
7
|
+
platform: MessagingInfo;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACd,MAAM,kBAAkB,CAAC;AAU1B,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAa9C,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,kBAAkB,GACtB;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;IACjC,QAAQ,EAAE,aAAa,CAAC;CACzB,CAgNA"}
|