@geminixiang/mikan 1.0.0-beta.2 → 1.0.0-beta.21
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 +388 -0
- package/README.md +49 -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/buffered-responder.d.ts +60 -0
- package/dist/adapters/buffered-responder.d.ts.map +1 -0
- package/dist/adapters/buffered-responder.js +201 -0
- package/dist/adapters/buffered-responder.js.map +1 -0
- 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 +44 -204
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +86 -0
- package/dist/adapters/github/bot.d.ts.map +1 -0
- package/dist/adapters/github/bot.js +640 -0
- package/dist/adapters/github/bot.js.map +1 -0
- package/dist/adapters/github/client.d.ts +122 -0
- package/dist/adapters/github/client.d.ts.map +1 -0
- package/dist/adapters/github/client.js +314 -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 +103 -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/github-ops.d.ts +90 -0
- package/dist/adapters/github/github-ops.d.ts.map +1 -0
- package/dist/adapters/github/github-ops.js +405 -0
- package/dist/adapters/github/github-ops.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 +32 -0
- package/dist/adapters/github/repo.d.ts.map +1 -0
- package/dist/adapters/github/repo.js +141 -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 +105 -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 +48 -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 +48 -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 +141 -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 +38 -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 +34 -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 +82 -134
- 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 +37 -174
- 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 +37 -14
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +359 -229
- package/dist/agent.js.map +1 -1
- package/dist/cli/arg-grammar.d.ts +25 -0
- package/dist/cli/arg-grammar.d.ts.map +1 -0
- package/dist/cli/arg-grammar.js +59 -0
- package/dist/cli/arg-grammar.js.map +1 -0
- package/dist/cli/boot.d.ts +16 -0
- package/dist/cli/boot.d.ts.map +1 -0
- package/dist/cli/boot.js +119 -0
- package/dist/cli/boot.js.map +1 -0
- 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 +215 -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 +18 -8
- 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 +35 -3
- 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 +11 -9
- 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 +23 -17
- 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 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +98 -95
- package/dist/config.js.map +1 -1
- package/dist/env-manifest.d.ts +35 -0
- package/dist/env-manifest.d.ts.map +1 -0
- package/dist/env-manifest.js +225 -0
- package/dist/env-manifest.js.map +1 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +9 -73
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts +4 -2
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +54 -41
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/event-format.d.ts +66 -0
- package/dist/harness/event-format.d.ts.map +1 -0
- package/dist/harness/event-format.js +112 -0
- package/dist/harness/event-format.js.map +1 -0
- package/dist/harness/extensions/loader.d.ts +44 -11
- package/dist/harness/extensions/loader.d.ts.map +1 -1
- package/dist/harness/extensions/loader.js +273 -84
- 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 +165 -22
- 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 +6 -5
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +3 -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 +37 -2
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +107 -23
- 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 +34 -0
- package/dist/harness/subagent-runner.d.ts.map +1 -0
- package/dist/harness/subagent-runner.js +339 -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 +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.js +260 -109
- package/dist/main.js.map +1 -1
- package/dist/observability/instrument.js +6 -4
- package/dist/observability/instrument.js.map +1 -1
- package/dist/observability/sentry.d.ts.map +1 -1
- package/dist/observability/sentry.js +3 -2
- package/dist/observability/sentry.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 +276 -43
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +36 -5
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts +3 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +10 -0
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +4 -0
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +11 -2
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/firecracker.d.ts +3 -0
- package/dist/sandbox/firecracker.d.ts.map +1 -1
- package/dist/sandbox/firecracker.js +10 -1
- package/dist/sandbox/firecracker.js.map +1 -1
- package/dist/sandbox/gondolin-bootstrap.d.ts +26 -0
- package/dist/sandbox/gondolin-bootstrap.d.ts.map +1 -0
- package/dist/sandbox/gondolin-bootstrap.js +27 -0
- package/dist/sandbox/gondolin-bootstrap.js.map +1 -0
- package/dist/sandbox/gondolin-contract.d.ts +125 -0
- package/dist/sandbox/gondolin-contract.d.ts.map +1 -0
- package/dist/sandbox/gondolin-contract.js +155 -0
- package/dist/sandbox/gondolin-contract.js.map +1 -0
- 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 +86 -0
- package/dist/sandbox/gondolin-gateway.d.ts.map +1 -0
- package/dist/sandbox/gondolin-gateway.js +407 -0
- package/dist/sandbox/gondolin-gateway.js.map +1 -0
- package/dist/sandbox/gondolin-inventory.d.ts +75 -0
- package/dist/sandbox/gondolin-inventory.d.ts.map +1 -0
- package/dist/sandbox/gondolin-inventory.js +257 -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 +30 -0
- package/dist/sandbox/gondolin-placement.d.ts.map +1 -0
- package/dist/sandbox/gondolin-placement.js +77 -0
- package/dist/sandbox/gondolin-placement.js.map +1 -0
- package/dist/sandbox/gondolin-recovery.d.ts +33 -0
- package/dist/sandbox/gondolin-recovery.d.ts.map +1 -0
- package/dist/sandbox/gondolin-recovery.js +39 -0
- package/dist/sandbox/gondolin-recovery.js.map +1 -0
- package/dist/sandbox/gondolin-remote.d.ts +70 -0
- package/dist/sandbox/gondolin-remote.d.ts.map +1 -0
- package/dist/sandbox/gondolin-remote.js +325 -0
- package/dist/sandbox/gondolin-remote.js.map +1 -0
- package/dist/sandbox/gondolin-worker-client.d.ts +135 -0
- package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -0
- package/dist/sandbox/gondolin-worker-client.js +323 -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 +17 -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 +53 -0
- package/dist/sandbox/gondolin.d.ts.map +1 -0
- package/dist/sandbox/gondolin.js +429 -0
- package/dist/sandbox/gondolin.js.map +1 -0
- package/dist/sandbox/host.d.ts +3 -0
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +14 -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 +13 -4
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +19 -3
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/types.d.ts +31 -1
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +12 -0
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +38 -0
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/sessions/chat-history-sync.d.ts +32 -0
- package/dist/sessions/chat-history-sync.d.ts.map +1 -0
- package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +40 -113
- package/dist/sessions/chat-history-sync.js.map +1 -0
- package/dist/sessions/conversation-log.d.ts +13 -0
- package/dist/sessions/conversation-log.d.ts.map +1 -0
- package/dist/sessions/conversation-log.js +47 -0
- package/dist/sessions/conversation-log.js.map +1 -0
- 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/rotation.d.ts +2 -0
- package/dist/sessions/rotation.d.ts.map +1 -0
- package/dist/sessions/rotation.js +39 -0
- package/dist/sessions/rotation.js.map +1 -0
- 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 +6 -12
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +2 -2
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/settings-mutation.d.ts +23 -0
- package/dist/settings-mutation.d.ts.map +1 -0
- package/dist/settings-mutation.js +42 -0
- package/dist/settings-mutation.js.map +1 -0
- 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 +53 -43
- 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/host-fn-tool.d.ts +21 -0
- package/dist/tools/host-fn-tool.d.ts.map +1 -0
- package/dist/tools/host-fn-tool.js +32 -0
- package/dist/tools/host-fn-tool.js.map +1 -0
- package/dist/tools/index.d.ts +17 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +28 -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 +29 -0
- package/dist/tools/react.js.map +1 -0
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +7 -26
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/sandbox.d.ts +2 -2
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +13 -9
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/subagent-slots.d.ts +28 -0
- package/dist/tools/subagent-slots.d.ts.map +1 -0
- package/dist/tools/subagent-slots.js +57 -0
- package/dist/tools/subagent-slots.js.map +1 -0
- package/dist/tools/subagent.d.ts +74 -0
- package/dist/tools/subagent.d.ts.map +1 -0
- package/dist/tools/subagent.js +335 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/types.d.ts +40 -6
- 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 +144 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/date.d.ts +1 -1
- package/dist/utils/date.js +1 -1
- package/dist/utils/date.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 +22 -0
- package/dist/vault/disabled.js.map +1 -0
- package/dist/vault/index.d.ts +0 -2
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +1 -32
- package/dist/vault/index.js.map +1 -1
- package/dist/vault/injection.d.ts +20 -0
- package/dist/vault/injection.d.ts.map +1 -0
- package/dist/vault/injection.js +27 -0
- package/dist/vault/injection.js.map +1 -0
- 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/types.d.ts +0 -2
- package/dist/vault/types.d.ts.map +1 -1
- package/dist/vault/types.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 +117 -154
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/admin/types.d.ts +6 -0
- package/dist/web/admin/types.d.ts.map +1 -1
- package/dist/web/admin/types.js.map +1 -1
- package/dist/web/agent-events/portal.d.ts.map +1 -1
- package/dist/web/agent-events/portal.js +2 -1
- package/dist/web/agent-events/portal.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/sessions/agent-memory-file-manager.d.ts +0 -19
- package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
- package/dist/sessions/agent-memory-file-manager.js.map +0 -1
- package/dist/vault/routing.d.ts +0 -3
- package/dist/vault/routing.d.ts.map +0 -1
- package/dist/vault/routing.js +0 -13
- package/dist/vault/routing.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 @@
|
|
|
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 { withRetry } from \"../shared.js\";\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\n/** Standard retry wrapper for GitHub API calls: backs off on rate limits. */\nexport const githubRetry = <T>(fn: () => Promise<T>): Promise<T> =>\n withRetry(fn, { isRateLimited: githubIsRateLimited });\n\n/** GitHub comment bodies cap at 65536 chars; leave headroom for markup. */\nexport const GITHUB_MAX_COMMENT_LENGTH = 60000;\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,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIlG,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,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,CAwGA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { resolveChatSessionKey } from "../../sessions/policy.js";
|
|
2
|
+
import { createBufferedResponder, formatMarkdownToolResult } from "../buffered-responder.js";
|
|
3
|
+
import { createChatResponseErrorReporter } from "../shared.js";
|
|
4
|
+
import { formatGithubContinuation } from "./bot.js";
|
|
5
|
+
import { GITHUB_MAX_COMMENT_LENGTH } from "./client.js";
|
|
6
|
+
import { parseGithubConversationId } from "./ids.js";
|
|
7
|
+
export function createGithubAdapters(event, bot) {
|
|
8
|
+
const conversationId = event.conversationId;
|
|
9
|
+
const ref = parseGithubConversationId(conversationId);
|
|
10
|
+
const message = {
|
|
11
|
+
id: event.ts,
|
|
12
|
+
sessionKey: event.sessionKey ??
|
|
13
|
+
resolveChatSessionKey({
|
|
14
|
+
conversationId,
|
|
15
|
+
conversationKind: event.conversationKind,
|
|
16
|
+
messageId: event.ts,
|
|
17
|
+
persistentTopLevel: true,
|
|
18
|
+
threadTs: event.thread_ts,
|
|
19
|
+
}),
|
|
20
|
+
conversationKind: event.conversationKind,
|
|
21
|
+
userId: event.user,
|
|
22
|
+
userName: event.userName,
|
|
23
|
+
text: event.text,
|
|
24
|
+
attachments: event.attachments,
|
|
25
|
+
threadTs: event.thread_ts,
|
|
26
|
+
};
|
|
27
|
+
// The bot's getMessagingInfo() is the single authority for platform info;
|
|
28
|
+
// the conversation-scoped context below is an explicit append, not a fork.
|
|
29
|
+
const baseInfo = bot.getMessagingInfo();
|
|
30
|
+
const platform = {
|
|
31
|
+
...baseInfo,
|
|
32
|
+
formattingGuide: `${baseInfo.formattingGuide}\n\n` +
|
|
33
|
+
`## Conversation context\n` +
|
|
34
|
+
`This conversation IS GitHub issue/PR ${ref.owner}/${ref.repo}#${ref.number}: the ` +
|
|
35
|
+
`first message in the history is its title and body, and the following messages are ` +
|
|
36
|
+
`its comments. When the user says "this issue", they mean #${ref.number}. Messages ` +
|
|
37
|
+
`tagged [PR review comment rc-<id> …] are inline review threads on a diff line: ` +
|
|
38
|
+
`answer those with the github_review_reply tool (comment_id = that id) so the reply ` +
|
|
39
|
+
`lands in-thread — your normal response posts as a plain PR comment.\n\n` +
|
|
40
|
+
`## Repository & pull requests\n` +
|
|
41
|
+
`The repository is cloned at ./repo — a snapshot from this conversation's first ` +
|
|
42
|
+
`trigger; run github_sync when it may be stale to pull the latest PR head or base ` +
|
|
43
|
+
`branch. If this conversation is a pull request, its head branch is checked out ` +
|
|
44
|
+
`under its real name (run git branch --show-current in ./repo to see it; fork PRs ` +
|
|
45
|
+
`fall back to pr-${ref.number}). You have no git credentials, so git fetch/push ` +
|
|
46
|
+
`fail by design.\n` +
|
|
47
|
+
`To ship code changes: commit inside ./repo (the git author is preconfigured) on a ` +
|
|
48
|
+
`pi/<name> branch, then call the github_pr tool to push it. When this conversation's ` +
|
|
49
|
+
`checked-out PR head branch is already named pi/<name> — e.g. a PR you opened ` +
|
|
50
|
+
`earlier — commit directly on it and pass that branch to github_pr: the push ` +
|
|
51
|
+
`updates THIS pull request instead of opening a new one. Any other branch opens a ` +
|
|
52
|
+
`new pull request (draft: true for a draft); calling github_pr again with the same ` +
|
|
53
|
+
`branch pushes new commits to its existing PR. Use github_checks to read CI results ` +
|
|
54
|
+
`for your branch (or this PR) — pass a failing check's job id to read its log — and ` +
|
|
55
|
+
`iterate until they pass. You cannot push the default branch or ` +
|
|
56
|
+
`merge — humans review and merge every PR.\n` +
|
|
57
|
+
`github_read looks up PR/issue metadata this clone cannot show (diff stats, changed ` +
|
|
58
|
+
`files, review state, other issues in this repo); github_issue manages labels, ` +
|
|
59
|
+
`assignees, and close/reopen for triage.`,
|
|
60
|
+
diagnostics: {
|
|
61
|
+
showUsageSummary: false,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
let currentResponseId = null;
|
|
65
|
+
const reportResponseError = createChatResponseErrorReporter(() => ({
|
|
66
|
+
platform: "github",
|
|
67
|
+
conversationId,
|
|
68
|
+
messageId: message.id,
|
|
69
|
+
sessionKey: message.sessionKey,
|
|
70
|
+
responseMessageId: currentResponseId === null ? null : Number(currentResponseId),
|
|
71
|
+
conversationKind: message.conversationKind,
|
|
72
|
+
}));
|
|
73
|
+
// streaming: false — GitHub gets the finished response in one comment.
|
|
74
|
+
// Streaming would edit the comment on every flush: API churn and "edited"
|
|
75
|
+
// noise for readers who refresh rather than watch typing. There is also no
|
|
76
|
+
// typing indicator or working suffix; progress shows through comment edits.
|
|
77
|
+
const { responder } = createBufferedResponder({
|
|
78
|
+
label: "GitHub",
|
|
79
|
+
maxLength: GITHUB_MAX_COMMENT_LENGTH,
|
|
80
|
+
formatContinuation: formatGithubContinuation,
|
|
81
|
+
errorPrefix: "**Error:** ",
|
|
82
|
+
streaming: false,
|
|
83
|
+
formatToolResult: formatMarkdownToolResult,
|
|
84
|
+
reportError: reportResponseError,
|
|
85
|
+
post: async (text) => {
|
|
86
|
+
currentResponseId = String(await bot.postComment(ref, text));
|
|
87
|
+
return currentResponseId;
|
|
88
|
+
},
|
|
89
|
+
update: (id, text) => bot.updateMessage(conversationId, id, text),
|
|
90
|
+
postExtra: (text) => bot.postComment(ref, text),
|
|
91
|
+
delete: async (id) => {
|
|
92
|
+
await bot.deleteComment(ref, Number(id));
|
|
93
|
+
currentResponseId = null;
|
|
94
|
+
},
|
|
95
|
+
logBotResponse: (text, id) => bot.logBotResponse(conversationId, text, id),
|
|
96
|
+
// The REST API cannot attach files to comments (uploads are a browser
|
|
97
|
+
// feature); leave a pointer instead of failing the run.
|
|
98
|
+
uploadFallbackNote: (name) => `*(file \`${name}\` was produced, but the GitHub adapter cannot attach files to comments)*`,
|
|
99
|
+
react: (emoji) => bot.addReaction(conversationId, event.ts, emoji),
|
|
100
|
+
});
|
|
101
|
+
return { message, responder, platform };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/adapters/github/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConversationMessage, ConversationResponder, MessagingInfo } from \"../../adapter.js\";\nimport { resolveChatSessionKey } from \"../../sessions/policy.js\";\nimport { createBufferedResponder, formatMarkdownToolResult } from \"../buffered-responder.js\";\nimport { createChatResponseErrorReporter } from \"../shared.js\";\nimport { formatGithubContinuation, type GithubMessagingBot } from \"./bot.js\";\nimport { GITHUB_MAX_COMMENT_LENGTH } from \"./client.js\";\nimport { parseGithubConversationId } from \"./ids.js\";\nimport type { GithubEvent } from \"./types.js\";\n\nexport function createGithubAdapters(\n event: GithubEvent,\n bot: GithubMessagingBot,\n): {\n message: ConversationMessage;\n responder: ConversationResponder;\n platform: MessagingInfo;\n} {\n const conversationId = event.conversationId;\n const ref = parseGithubConversationId(conversationId);\n\n const message: ConversationMessage = {\n id: event.ts,\n sessionKey:\n event.sessionKey ??\n resolveChatSessionKey({\n conversationId,\n conversationKind: event.conversationKind,\n messageId: event.ts,\n persistentTopLevel: true,\n threadTs: event.thread_ts,\n }),\n conversationKind: event.conversationKind,\n userId: event.user,\n userName: event.userName,\n text: event.text,\n attachments: event.attachments,\n threadTs: event.thread_ts,\n };\n\n // The bot's getMessagingInfo() is the single authority for platform info;\n // the conversation-scoped context below is an explicit append, not a fork.\n const baseInfo = bot.getMessagingInfo();\n const platform: MessagingInfo = {\n ...baseInfo,\n formattingGuide:\n `${baseInfo.formattingGuide}\\n\\n` +\n `## Conversation context\\n` +\n `This conversation IS GitHub issue/PR ${ref.owner}/${ref.repo}#${ref.number}: the ` +\n `first message in the history is its title and body, and the following messages are ` +\n `its comments. When the user says \"this issue\", they mean #${ref.number}. Messages ` +\n `tagged [PR review comment rc-<id> …] are inline review threads on a diff line: ` +\n `answer those with the github_review_reply tool (comment_id = that id) so the reply ` +\n `lands in-thread — your normal response posts as a plain PR comment.\\n\\n` +\n `## Repository & pull requests\\n` +\n `The repository is cloned at ./repo — a snapshot from this conversation's first ` +\n `trigger; run github_sync when it may be stale to pull the latest PR head or base ` +\n `branch. If this conversation is a pull request, its head branch is checked out ` +\n `under its real name (run git branch --show-current in ./repo to see it; fork PRs ` +\n `fall back to pr-${ref.number}). You have no git credentials, so git fetch/push ` +\n `fail by design.\\n` +\n `To ship code changes: commit inside ./repo (the git author is preconfigured) on a ` +\n `pi/<name> branch, then call the github_pr tool to push it. When this conversation's ` +\n `checked-out PR head branch is already named pi/<name> — e.g. a PR you opened ` +\n `earlier — commit directly on it and pass that branch to github_pr: the push ` +\n `updates THIS pull request instead of opening a new one. Any other branch opens a ` +\n `new pull request (draft: true for a draft); calling github_pr again with the same ` +\n `branch pushes new commits to its existing PR. Use github_checks to read CI results ` +\n `for your branch (or this PR) — pass a failing check's job id to read its log — and ` +\n `iterate until they pass. You cannot push the default branch or ` +\n `merge — humans review and merge every PR.\\n` +\n `github_read looks up PR/issue metadata this clone cannot show (diff stats, changed ` +\n `files, review state, other issues in this repo); github_issue manages labels, ` +\n `assignees, and close/reopen for triage.`,\n diagnostics: {\n showUsageSummary: false,\n },\n };\n\n let currentResponseId: string | null = null;\n\n const reportResponseError = createChatResponseErrorReporter(() => ({\n platform: \"github\",\n conversationId,\n messageId: message.id,\n sessionKey: message.sessionKey,\n responseMessageId: currentResponseId === null ? null : Number(currentResponseId),\n conversationKind: message.conversationKind,\n }));\n\n // streaming: false — GitHub gets the finished response in one comment.\n // Streaming would edit the comment on every flush: API churn and \"edited\"\n // noise for readers who refresh rather than watch typing. There is also no\n // typing indicator or working suffix; progress shows through comment edits.\n const { responder } = createBufferedResponder({\n label: \"GitHub\",\n maxLength: GITHUB_MAX_COMMENT_LENGTH,\n formatContinuation: formatGithubContinuation,\n errorPrefix: \"**Error:** \",\n streaming: false,\n formatToolResult: formatMarkdownToolResult,\n reportError: reportResponseError,\n post: async (text) => {\n currentResponseId = String(await bot.postComment(ref, text));\n return currentResponseId;\n },\n update: (id, text) => bot.updateMessage(conversationId, id, text),\n postExtra: (text) => bot.postComment(ref, text),\n delete: async (id) => {\n await bot.deleteComment(ref, Number(id));\n currentResponseId = null;\n },\n logBotResponse: (text, id) => bot.logBotResponse(conversationId, text, id),\n // The REST API cannot attach files to comments (uploads are a browser\n // feature); leave a pointer instead of failing the run.\n uploadFallbackNote: (name) =>\n `*(file \\`${name}\\` was produced, but the GitHub adapter cannot attach files to comments)*`,\n react: (emoji) => bot.addReaction(conversationId, event.ts, emoji),\n });\n\n return { message, responder, platform };\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface GcpTokenProviderOptions {
|
|
2
|
+
/** Path to the ADC JSON (usually $GOOGLE_APPLICATION_CREDENTIALS). */
|
|
3
|
+
credentialsPath: string;
|
|
4
|
+
fetchImpl?: typeof fetch;
|
|
5
|
+
}
|
|
6
|
+
export declare class GcpAuthError extends Error {
|
|
7
|
+
constructor(message: string, options?: {
|
|
8
|
+
cause?: unknown;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export declare class GcpTokenProvider {
|
|
12
|
+
private readonly credentialsPath;
|
|
13
|
+
private readonly fetchImpl;
|
|
14
|
+
private credentials;
|
|
15
|
+
private cached;
|
|
16
|
+
private inFlight;
|
|
17
|
+
constructor(options: GcpTokenProviderOptions);
|
|
18
|
+
/** A valid cloud-platform access token, cached until near expiry. */
|
|
19
|
+
getAccessToken(): Promise<string>;
|
|
20
|
+
private loadCredentials;
|
|
21
|
+
private mintToken;
|
|
22
|
+
private postForm;
|
|
23
|
+
/** WIF: read the subject token, exchange at STS, optionally impersonate. */
|
|
24
|
+
private mintExternalAccount;
|
|
25
|
+
private readSubjectToken;
|
|
26
|
+
/** Service-account key: self-signed RS256 JWT → assertion grant. */
|
|
27
|
+
private mintServiceAccount;
|
|
28
|
+
/** gcloud user ADC: refresh-token grant. */
|
|
29
|
+
private mintAuthorizedUser;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=gcp-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-auth.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/gcp-auth.ts"],"names":[],"mappings":"AAwDA,MAAM,WAAW,uBAAuB;IACtC,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAMD,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAGzD;CACF;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,MAAM,CAAqD;IACnE,OAAO,CAAC,QAAQ,CAAgC;IAEhD,YAAY,OAAO,EAAE,uBAAuB,EAG3C;IAED,qEAAqE;IAC/D,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAStC;IAED,OAAO,CAAC,eAAe;YAqBT,SAAS;YAqBT,QAAQ;IAiBtB,4EAA4E;YAC9D,mBAAmB;YA4CnB,gBAAgB;IA+B9B,oEAAoE;YACtD,kBAAkB;IAiChC,4CAA4C;YAC9B,kBAAkB;CAkBjC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { createSign } from "crypto";
|
|
3
|
+
/**
|
|
4
|
+
* Minimal Google Application Default Credentials provider — enough to mint
|
|
5
|
+
* cloud-platform access tokens on the host without pulling in
|
|
6
|
+
* google-auth-library (this repo hand-rolls its GitHub App JWTs the same way).
|
|
7
|
+
*
|
|
8
|
+
* Supported `GOOGLE_APPLICATION_CREDENTIALS` JSON shapes:
|
|
9
|
+
* - `external_account` (Workload Identity Federation) with a file or url
|
|
10
|
+
* credential source: STS token exchange, plus optional service-account
|
|
11
|
+
* impersonation. AWS/executable sources are rejected with a clear error.
|
|
12
|
+
* - `service_account` key: self-signed JWT assertion grant.
|
|
13
|
+
* - `authorized_user` (gcloud user ADC): refresh-token grant. Local dev.
|
|
14
|
+
*/
|
|
15
|
+
const CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
16
|
+
const OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
17
|
+
/** Refresh cached tokens this long before expiry — STS/impersonation tokens
|
|
18
|
+
* live ~1h; minting per request would hammer STS in a CI-debug loop. */
|
|
19
|
+
const TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
20
|
+
function base64Url(data) {
|
|
21
|
+
return Buffer.from(data).toString("base64url");
|
|
22
|
+
}
|
|
23
|
+
export class GcpAuthError extends Error {
|
|
24
|
+
constructor(message, options) {
|
|
25
|
+
super(message, options);
|
|
26
|
+
this.name = "GcpAuthError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class GcpTokenProvider {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.credentials = null;
|
|
32
|
+
this.cached = null;
|
|
33
|
+
this.inFlight = null;
|
|
34
|
+
this.credentialsPath = options.credentialsPath;
|
|
35
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
36
|
+
}
|
|
37
|
+
/** A valid cloud-platform access token, cached until near expiry. */
|
|
38
|
+
async getAccessToken() {
|
|
39
|
+
if (this.cached && Date.now() < this.cached.expiresAt - TOKEN_REFRESH_MARGIN_MS) {
|
|
40
|
+
return this.cached.token;
|
|
41
|
+
}
|
|
42
|
+
// Single-flight: concurrent tool calls must not each hit STS.
|
|
43
|
+
this.inFlight ??= this.mintToken().finally(() => {
|
|
44
|
+
this.inFlight = null;
|
|
45
|
+
});
|
|
46
|
+
return this.inFlight;
|
|
47
|
+
}
|
|
48
|
+
loadCredentials() {
|
|
49
|
+
if (this.credentials)
|
|
50
|
+
return this.credentials;
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(readFileSync(this.credentialsPath, "utf-8"));
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
throw new GcpAuthError(`Cannot read GCP credentials at ${this.credentialsPath}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
57
|
+
}
|
|
58
|
+
if (!["external_account", "service_account", "authorized_user"].includes(parsed.type)) {
|
|
59
|
+
throw new GcpAuthError(`Unsupported GCP credential type '${parsed.type}' — expected ` +
|
|
60
|
+
`external_account (WIF), service_account, or authorized_user.`);
|
|
61
|
+
}
|
|
62
|
+
this.credentials = parsed;
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
async mintToken() {
|
|
66
|
+
const credentials = this.loadCredentials();
|
|
67
|
+
let minted;
|
|
68
|
+
switch (credentials.type) {
|
|
69
|
+
case "external_account":
|
|
70
|
+
minted = await this.mintExternalAccount(credentials);
|
|
71
|
+
break;
|
|
72
|
+
case "service_account":
|
|
73
|
+
minted = await this.mintServiceAccount(credentials);
|
|
74
|
+
break;
|
|
75
|
+
case "authorized_user":
|
|
76
|
+
minted = await this.mintAuthorizedUser(credentials);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
this.cached = {
|
|
80
|
+
token: minted.token,
|
|
81
|
+
expiresAt: Date.now() + minted.expiresInSeconds * 1000,
|
|
82
|
+
};
|
|
83
|
+
return minted.token;
|
|
84
|
+
}
|
|
85
|
+
async postForm(url, form, context) {
|
|
86
|
+
const response = await this.fetchImpl(url, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
89
|
+
body: new URLSearchParams(form).toString(),
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
const detail = (await response.text().catch(() => "")).slice(0, 300);
|
|
93
|
+
throw new GcpAuthError(`${context} failed with ${response.status}: ${detail}`);
|
|
94
|
+
}
|
|
95
|
+
return (await response.json());
|
|
96
|
+
}
|
|
97
|
+
/** WIF: read the subject token, exchange at STS, optionally impersonate. */
|
|
98
|
+
async mintExternalAccount(credentials) {
|
|
99
|
+
const subjectToken = await this.readSubjectToken(credentials);
|
|
100
|
+
const sts = await this.postForm(credentials.token_url, {
|
|
101
|
+
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
102
|
+
audience: credentials.audience,
|
|
103
|
+
scope: CLOUD_PLATFORM_SCOPE,
|
|
104
|
+
requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
|
|
105
|
+
subject_token: subjectToken,
|
|
106
|
+
subject_token_type: credentials.subject_token_type,
|
|
107
|
+
}, "GCP STS token exchange");
|
|
108
|
+
const stsToken = sts.access_token;
|
|
109
|
+
const stsExpires = sts.expires_in ?? 3600;
|
|
110
|
+
if (!credentials.service_account_impersonation_url) {
|
|
111
|
+
return { token: stsToken, expiresInSeconds: stsExpires };
|
|
112
|
+
}
|
|
113
|
+
const response = await this.fetchImpl(credentials.service_account_impersonation_url, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: {
|
|
116
|
+
"Content-Type": "application/json",
|
|
117
|
+
Authorization: `Bearer ${stsToken}`,
|
|
118
|
+
},
|
|
119
|
+
body: JSON.stringify({ scope: [CLOUD_PLATFORM_SCOPE] }),
|
|
120
|
+
});
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
const detail = (await response.text().catch(() => "")).slice(0, 300);
|
|
123
|
+
throw new GcpAuthError(`GCP service-account impersonation failed with ${response.status}: ${detail}`);
|
|
124
|
+
}
|
|
125
|
+
const impersonated = (await response.json());
|
|
126
|
+
const expiresInSeconds = Math.max(0, Math.floor((Date.parse(impersonated.expireTime) - Date.now()) / 1000));
|
|
127
|
+
return { token: impersonated.accessToken, expiresInSeconds };
|
|
128
|
+
}
|
|
129
|
+
async readSubjectToken(credentials) {
|
|
130
|
+
const source = credentials.credential_source;
|
|
131
|
+
let raw;
|
|
132
|
+
if (source.file) {
|
|
133
|
+
raw = readFileSync(source.file, "utf-8").trim();
|
|
134
|
+
}
|
|
135
|
+
else if (source.url) {
|
|
136
|
+
const response = await this.fetchImpl(source.url, { headers: source.headers });
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
throw new GcpAuthError(`WIF credential_source url returned ${response.status} — cannot read subject token`);
|
|
139
|
+
}
|
|
140
|
+
raw = (await response.text()).trim();
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
throw new GcpAuthError("Unsupported WIF credential_source: only file and url sources are supported " +
|
|
144
|
+
"(aws/executable sources are not).");
|
|
145
|
+
}
|
|
146
|
+
if (source.format?.type === "json") {
|
|
147
|
+
const field = source.format.subject_token_field_name ?? "token";
|
|
148
|
+
const parsed = JSON.parse(raw);
|
|
149
|
+
const token = parsed[field];
|
|
150
|
+
if (!token) {
|
|
151
|
+
throw new GcpAuthError(`WIF subject token field '${field}' missing from JSON source`);
|
|
152
|
+
}
|
|
153
|
+
return token;
|
|
154
|
+
}
|
|
155
|
+
return raw;
|
|
156
|
+
}
|
|
157
|
+
/** Service-account key: self-signed RS256 JWT → assertion grant. */
|
|
158
|
+
async mintServiceAccount(credentials) {
|
|
159
|
+
const tokenUrl = credentials.token_uri ?? OAUTH_TOKEN_URL;
|
|
160
|
+
const now = Math.floor(Date.now() / 1000);
|
|
161
|
+
const header = base64Url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
|
|
162
|
+
const payload = base64Url(JSON.stringify({
|
|
163
|
+
iss: credentials.client_email,
|
|
164
|
+
scope: CLOUD_PLATFORM_SCOPE,
|
|
165
|
+
aud: tokenUrl,
|
|
166
|
+
iat: now,
|
|
167
|
+
exp: now + 3600,
|
|
168
|
+
}));
|
|
169
|
+
const signature = createSign("RSA-SHA256")
|
|
170
|
+
.update(`${header}.${payload}`)
|
|
171
|
+
.sign(credentials.private_key)
|
|
172
|
+
.toString("base64url");
|
|
173
|
+
const data = await this.postForm(tokenUrl, {
|
|
174
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
175
|
+
assertion: `${header}.${payload}.${signature}`,
|
|
176
|
+
}, "GCP service-account token grant");
|
|
177
|
+
return {
|
|
178
|
+
token: data.access_token,
|
|
179
|
+
expiresInSeconds: data.expires_in ?? 3600,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** gcloud user ADC: refresh-token grant. */
|
|
183
|
+
async mintAuthorizedUser(credentials) {
|
|
184
|
+
const data = await this.postForm(OAUTH_TOKEN_URL, {
|
|
185
|
+
grant_type: "refresh_token",
|
|
186
|
+
client_id: credentials.client_id,
|
|
187
|
+
client_secret: credentials.client_secret,
|
|
188
|
+
refresh_token: credentials.refresh_token,
|
|
189
|
+
}, "GCP user-credential refresh");
|
|
190
|
+
return {
|
|
191
|
+
token: data.access_token,
|
|
192
|
+
expiresInSeconds: data.expires_in ?? 3600,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=gcp-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-auth.js","sourceRoot":"","sources":["../../../src/adapters/github/gcp-auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"fs\";\nimport { createSign } from \"crypto\";\n\n/**\n * Minimal Google Application Default Credentials provider — enough to mint\n * cloud-platform access tokens on the host without pulling in\n * google-auth-library (this repo hand-rolls its GitHub App JWTs the same way).\n *\n * Supported `GOOGLE_APPLICATION_CREDENTIALS` JSON shapes:\n * - `external_account` (Workload Identity Federation) with a file or url\n * credential source: STS token exchange, plus optional service-account\n * impersonation. AWS/executable sources are rejected with a clear error.\n * - `service_account` key: self-signed JWT assertion grant.\n * - `authorized_user` (gcloud user ADC): refresh-token grant. Local dev.\n */\n\nconst CLOUD_PLATFORM_SCOPE = \"https://www.googleapis.com/auth/cloud-platform\";\nconst OAUTH_TOKEN_URL = \"https://oauth2.googleapis.com/token\";\n\n/** Refresh cached tokens this long before expiry — STS/impersonation tokens\n * live ~1h; minting per request would hammer STS in a CI-debug loop. */\nconst TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;\n\ninterface ExternalAccountCredentials {\n type: \"external_account\";\n audience: string;\n subject_token_type: string;\n token_url: string;\n service_account_impersonation_url?: string;\n credential_source: {\n file?: string;\n url?: string;\n headers?: Record<string, string>;\n format?: { type?: string; subject_token_field_name?: string };\n };\n}\n\ninterface ServiceAccountCredentials {\n type: \"service_account\";\n client_email: string;\n private_key: string;\n token_uri?: string;\n}\n\ninterface AuthorizedUserCredentials {\n type: \"authorized_user\";\n client_id: string;\n client_secret: string;\n refresh_token: string;\n}\n\ntype AdcCredentials =\n | ExternalAccountCredentials\n | ServiceAccountCredentials\n | AuthorizedUserCredentials;\n\nexport interface GcpTokenProviderOptions {\n /** Path to the ADC JSON (usually $GOOGLE_APPLICATION_CREDENTIALS). */\n credentialsPath: string;\n fetchImpl?: typeof fetch;\n}\n\nfunction base64Url(data: string): string {\n return Buffer.from(data).toString(\"base64url\");\n}\n\nexport class GcpAuthError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"GcpAuthError\";\n }\n}\n\nexport class GcpTokenProvider {\n private readonly credentialsPath: string;\n private readonly fetchImpl: typeof fetch;\n private credentials: AdcCredentials | null = null;\n private cached: { token: string; expiresAt: number } | null = null;\n private inFlight: Promise<string> | null = null;\n\n constructor(options: GcpTokenProviderOptions) {\n this.credentialsPath = options.credentialsPath;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n /** A valid cloud-platform access token, cached until near expiry. */\n async getAccessToken(): Promise<string> {\n if (this.cached && Date.now() < this.cached.expiresAt - TOKEN_REFRESH_MARGIN_MS) {\n return this.cached.token;\n }\n // Single-flight: concurrent tool calls must not each hit STS.\n this.inFlight ??= this.mintToken().finally(() => {\n this.inFlight = null;\n });\n return this.inFlight;\n }\n\n private loadCredentials(): AdcCredentials {\n if (this.credentials) return this.credentials;\n let parsed: AdcCredentials;\n try {\n parsed = JSON.parse(readFileSync(this.credentialsPath, \"utf-8\")) as AdcCredentials;\n } catch (err) {\n throw new GcpAuthError(\n `Cannot read GCP credentials at ${this.credentialsPath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n if (![\"external_account\", \"service_account\", \"authorized_user\"].includes(parsed.type)) {\n throw new GcpAuthError(\n `Unsupported GCP credential type '${(parsed as { type?: string }).type}' — expected ` +\n `external_account (WIF), service_account, or authorized_user.`,\n );\n }\n this.credentials = parsed;\n return parsed;\n }\n\n private async mintToken(): Promise<string> {\n const credentials = this.loadCredentials();\n let minted: { token: string; expiresInSeconds: number };\n switch (credentials.type) {\n case \"external_account\":\n minted = await this.mintExternalAccount(credentials);\n break;\n case \"service_account\":\n minted = await this.mintServiceAccount(credentials);\n break;\n case \"authorized_user\":\n minted = await this.mintAuthorizedUser(credentials);\n break;\n }\n this.cached = {\n token: minted.token,\n expiresAt: Date.now() + minted.expiresInSeconds * 1000,\n };\n return minted.token;\n }\n\n private async postForm(\n url: string,\n form: Record<string, string>,\n context: string,\n ): Promise<Record<string, unknown>> {\n const response = await this.fetchImpl(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams(form).toString(),\n });\n if (!response.ok) {\n const detail = (await response.text().catch(() => \"\")).slice(0, 300);\n throw new GcpAuthError(`${context} failed with ${response.status}: ${detail}`);\n }\n return (await response.json()) as Record<string, unknown>;\n }\n\n /** WIF: read the subject token, exchange at STS, optionally impersonate. */\n private async mintExternalAccount(\n credentials: ExternalAccountCredentials,\n ): Promise<{ token: string; expiresInSeconds: number }> {\n const subjectToken = await this.readSubjectToken(credentials);\n const sts = await this.postForm(\n credentials.token_url,\n {\n grant_type: \"urn:ietf:params:oauth:grant-type:token-exchange\",\n audience: credentials.audience,\n scope: CLOUD_PLATFORM_SCOPE,\n requested_token_type: \"urn:ietf:params:oauth:token-type:access_token\",\n subject_token: subjectToken,\n subject_token_type: credentials.subject_token_type,\n },\n \"GCP STS token exchange\",\n );\n const stsToken = sts.access_token as string;\n const stsExpires = (sts.expires_in as number | undefined) ?? 3600;\n if (!credentials.service_account_impersonation_url) {\n return { token: stsToken, expiresInSeconds: stsExpires };\n }\n\n const response = await this.fetchImpl(credentials.service_account_impersonation_url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${stsToken}`,\n },\n body: JSON.stringify({ scope: [CLOUD_PLATFORM_SCOPE] }),\n });\n if (!response.ok) {\n const detail = (await response.text().catch(() => \"\")).slice(0, 300);\n throw new GcpAuthError(\n `GCP service-account impersonation failed with ${response.status}: ${detail}`,\n );\n }\n const impersonated = (await response.json()) as { accessToken: string; expireTime: string };\n const expiresInSeconds = Math.max(\n 0,\n Math.floor((Date.parse(impersonated.expireTime) - Date.now()) / 1000),\n );\n return { token: impersonated.accessToken, expiresInSeconds };\n }\n\n private async readSubjectToken(credentials: ExternalAccountCredentials): Promise<string> {\n const source = credentials.credential_source;\n let raw: string;\n if (source.file) {\n raw = readFileSync(source.file, \"utf-8\").trim();\n } else if (source.url) {\n const response = await this.fetchImpl(source.url, { headers: source.headers });\n if (!response.ok) {\n throw new GcpAuthError(\n `WIF credential_source url returned ${response.status} — cannot read subject token`,\n );\n }\n raw = (await response.text()).trim();\n } else {\n throw new GcpAuthError(\n \"Unsupported WIF credential_source: only file and url sources are supported \" +\n \"(aws/executable sources are not).\",\n );\n }\n if (source.format?.type === \"json\") {\n const field = source.format.subject_token_field_name ?? \"token\";\n const parsed = JSON.parse(raw) as Record<string, string>;\n const token = parsed[field];\n if (!token) {\n throw new GcpAuthError(`WIF subject token field '${field}' missing from JSON source`);\n }\n return token;\n }\n return raw;\n }\n\n /** Service-account key: self-signed RS256 JWT → assertion grant. */\n private async mintServiceAccount(\n credentials: ServiceAccountCredentials,\n ): Promise<{ token: string; expiresInSeconds: number }> {\n const tokenUrl = credentials.token_uri ?? OAUTH_TOKEN_URL;\n const now = Math.floor(Date.now() / 1000);\n const header = base64Url(JSON.stringify({ alg: \"RS256\", typ: \"JWT\" }));\n const payload = base64Url(\n JSON.stringify({\n iss: credentials.client_email,\n scope: CLOUD_PLATFORM_SCOPE,\n aud: tokenUrl,\n iat: now,\n exp: now + 3600,\n }),\n );\n const signature = createSign(\"RSA-SHA256\")\n .update(`${header}.${payload}`)\n .sign(credentials.private_key)\n .toString(\"base64url\");\n const data = await this.postForm(\n tokenUrl,\n {\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n assertion: `${header}.${payload}.${signature}`,\n },\n \"GCP service-account token grant\",\n );\n return {\n token: data.access_token as string,\n expiresInSeconds: (data.expires_in as number | undefined) ?? 3600,\n };\n }\n\n /** gcloud user ADC: refresh-token grant. */\n private async mintAuthorizedUser(\n credentials: AuthorizedUserCredentials,\n ): Promise<{ token: string; expiresInSeconds: number }> {\n const data = await this.postForm(\n OAUTH_TOKEN_URL,\n {\n grant_type: \"refresh_token\",\n client_id: credentials.client_id,\n client_secret: credentials.client_secret,\n refresh_token: credentials.refresh_token,\n },\n \"GCP user-credential refresh\",\n );\n return {\n token: data.access_token as string,\n expiresInSeconds: (data.expires_in as number | undefined) ?? 3600,\n };\n }\n}\n"]}
|