@cursor/july 0.1.1
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/AGENTS.md +290 -0
- package/LICENSE.md +3 -0
- package/README.md +1233 -0
- package/dist/ab.d.ts +290 -0
- package/dist/ab.d.ts.map +1 -0
- package/dist/ab.js +386 -0
- package/dist/bin/agent-serve.d.ts +15 -0
- package/dist/bin/agent-serve.d.ts.map +1 -0
- package/dist/bin/agent-serve.js +754 -0
- package/dist/channels/github/api.d.ts +101 -0
- package/dist/channels/github/api.d.ts.map +1 -0
- package/dist/channels/github/api.js +229 -0
- package/dist/channels/github/auth.d.ts +67 -0
- package/dist/channels/github/auth.d.ts.map +1 -0
- package/dist/channels/github/auth.js +239 -0
- package/dist/channels/github/binding.d.ts +73 -0
- package/dist/channels/github/binding.d.ts.map +1 -0
- package/dist/channels/github/binding.js +114 -0
- package/dist/channels/github/defaults.d.ts +24 -0
- package/dist/channels/github/defaults.d.ts.map +1 -0
- package/dist/channels/github/defaults.js +100 -0
- package/dist/channels/github/events.d.ts +18 -0
- package/dist/channels/github/events.d.ts.map +1 -0
- package/dist/channels/github/events.js +59 -0
- package/dist/channels/github/forward.d.ts +137 -0
- package/dist/channels/github/forward.d.ts.map +1 -0
- package/dist/channels/github/forward.js +271 -0
- package/dist/channels/github/github-channel.d.ts +127 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -0
- package/dist/channels/github/github-channel.js +1688 -0
- package/dist/channels/github/index.d.ts +52 -0
- package/dist/channels/github/index.d.ts.map +1 -0
- package/dist/channels/github/index.js +51 -0
- package/dist/channels/github/replay.d.ts +115 -0
- package/dist/channels/github/replay.d.ts.map +1 -0
- package/dist/channels/github/replay.js +282 -0
- package/dist/channels/github/state.d.ts +26 -0
- package/dist/channels/github/state.d.ts.map +1 -0
- package/dist/channels/github/state.js +50 -0
- package/dist/channels/github/types.d.ts +385 -0
- package/dist/channels/github/types.d.ts.map +1 -0
- package/dist/channels/github/types.js +1 -0
- package/dist/channels/slack/api.d.ts +117 -0
- package/dist/channels/slack/api.d.ts.map +1 -0
- package/dist/channels/slack/api.js +437 -0
- package/dist/channels/slack/approvals.d.ts +32 -0
- package/dist/channels/slack/approvals.d.ts.map +1 -0
- package/dist/channels/slack/approvals.js +157 -0
- package/dist/channels/slack/assistant.d.ts +23 -0
- package/dist/channels/slack/assistant.d.ts.map +1 -0
- package/dist/channels/slack/assistant.js +67 -0
- package/dist/channels/slack/auth.d.ts +40 -0
- package/dist/channels/slack/auth.d.ts.map +1 -0
- package/dist/channels/slack/auth.js +71 -0
- package/dist/channels/slack/channel-watch.d.ts +46 -0
- package/dist/channels/slack/channel-watch.d.ts.map +1 -0
- package/dist/channels/slack/channel-watch.js +180 -0
- package/dist/channels/slack/constants.d.ts +17 -0
- package/dist/channels/slack/constants.d.ts.map +1 -0
- package/dist/channels/slack/constants.js +27 -0
- package/dist/channels/slack/credentials.d.ts +35 -0
- package/dist/channels/slack/credentials.d.ts.map +1 -0
- package/dist/channels/slack/credentials.js +88 -0
- package/dist/channels/slack/cursor-account.d.ts +76 -0
- package/dist/channels/slack/cursor-account.d.ts.map +1 -0
- package/dist/channels/slack/cursor-account.js +88 -0
- package/dist/channels/slack/defaults.d.ts +32 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -0
- package/dist/channels/slack/defaults.js +380 -0
- package/dist/channels/slack/dispatch.d.ts +43 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -0
- package/dist/channels/slack/dispatch.js +185 -0
- package/dist/channels/slack/doctor.d.ts +24 -0
- package/dist/channels/slack/doctor.d.ts.map +1 -0
- package/dist/channels/slack/doctor.js +115 -0
- package/dist/channels/slack/event-id-dedupe.d.ts +10 -0
- package/dist/channels/slack/event-id-dedupe.d.ts.map +1 -0
- package/dist/channels/slack/event-id-dedupe.js +23 -0
- package/dist/channels/slack/inbound.d.ts +41 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -0
- package/dist/channels/slack/inbound.js +166 -0
- package/dist/channels/slack/index.d.ts +45 -0
- package/dist/channels/slack/index.d.ts.map +1 -0
- package/dist/channels/slack/index.js +44 -0
- package/dist/channels/slack/init.d.ts +32 -0
- package/dist/channels/slack/init.d.ts.map +1 -0
- package/dist/channels/slack/init.js +82 -0
- package/dist/channels/slack/interactive.d.ts +19 -0
- package/dist/channels/slack/interactive.d.ts.map +1 -0
- package/dist/channels/slack/interactive.js +142 -0
- package/dist/channels/slack/links.d.ts +10 -0
- package/dist/channels/slack/links.d.ts.map +1 -0
- package/dist/channels/slack/links.js +19 -0
- package/dist/channels/slack/live-delivery.d.ts +75 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -0
- package/dist/channels/slack/live-delivery.js +143 -0
- package/dist/channels/slack/log.d.ts +25 -0
- package/dist/channels/slack/log.d.ts.map +1 -0
- package/dist/channels/slack/log.js +47 -0
- package/dist/channels/slack/manifest.d.ts +57 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -0
- package/dist/channels/slack/manifest.js +110 -0
- package/dist/channels/slack/pr-ref.d.ts +18 -0
- package/dist/channels/slack/pr-ref.d.ts.map +1 -0
- package/dist/channels/slack/pr-ref.js +36 -0
- package/dist/channels/slack/setup.d.ts +14 -0
- package/dist/channels/slack/setup.d.ts.map +1 -0
- package/dist/channels/slack/setup.js +86 -0
- package/dist/channels/slack/slack-channel.d.ts +15 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -0
- package/dist/channels/slack/slack-channel.js +281 -0
- package/dist/channels/slack/socket-mode.d.ts +26 -0
- package/dist/channels/slack/socket-mode.d.ts.map +1 -0
- package/dist/channels/slack/socket-mode.js +116 -0
- package/dist/channels/slack/stream.d.ts +26 -0
- package/dist/channels/slack/stream.d.ts.map +1 -0
- package/dist/channels/slack/stream.js +39 -0
- package/dist/channels/slack/tool-approval-events.d.ts +33 -0
- package/dist/channels/slack/tool-approval-events.d.ts.map +1 -0
- package/dist/channels/slack/tool-approval-events.js +129 -0
- package/dist/channels/slack/types.d.ts +326 -0
- package/dist/channels/slack/types.d.ts.map +1 -0
- package/dist/channels/slack/types.js +4 -0
- package/dist/channels.d.ts +116 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +294 -0
- package/dist/connections.d.ts +75 -0
- package/dist/connections.d.ts.map +1 -0
- package/dist/connections.js +75 -0
- package/dist/docs/404.html +22 -0
- package/dist/docs/ab.html +87 -0
- package/dist/docs/assets/ab.md.COdXkces.js +63 -0
- package/dist/docs/assets/ab.md.COdXkces.lean.js +1 -0
- package/dist/docs/assets/app.DqfFEmJd.js +1 -0
- package/dist/docs/assets/building-with-agents.md.CJCtZCyi.js +13 -0
- package/dist/docs/assets/building-with-agents.md.CJCtZCyi.lean.js +1 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.CcVk1uKq.js +1 -0
- package/dist/docs/assets/chunks/VPLocalSearchBox.BaLEdS15.js +9 -0
- package/dist/docs/assets/chunks/framework.CAZyNGu9.js +19 -0
- package/dist/docs/assets/chunks/theme.CZRvu_0q.js +2 -0
- package/dist/docs/assets/concepts.md.Cfb9b-k1.js +4 -0
- package/dist/docs/assets/concepts.md.Cfb9b-k1.lean.js +1 -0
- package/dist/docs/assets/deployment.md.Dx1TYNk5.js +14 -0
- package/dist/docs/assets/deployment.md.Dx1TYNk5.lean.js +1 -0
- package/dist/docs/assets/evals.md.DPZ_MAnI.js +85 -0
- package/dist/docs/assets/evals.md.DPZ_MAnI.lean.js +1 -0
- package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.js +10 -0
- package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.lean.js +1 -0
- package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.js +9 -0
- package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.lean.js +1 -0
- package/dist/docs/assets/guides_github.md.DwbKhCeS.js +20 -0
- package/dist/docs/assets/guides_github.md.DwbKhCeS.lean.js +1 -0
- package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.js +28 -0
- package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.lean.js +1 -0
- package/dist/docs/assets/guides_slack.md.bv41fHfW.js +37 -0
- package/dist/docs/assets/guides_slack.md.bv41fHfW.lean.js +1 -0
- package/dist/docs/assets/guides_webhooks.md.hFTik3lf.js +50 -0
- package/dist/docs/assets/guides_webhooks.md.hFTik3lf.lean.js +1 -0
- package/dist/docs/assets/hillclimbing.md.DHNast08.js +4 -0
- package/dist/docs/assets/hillclimbing.md.DHNast08.lean.js +1 -0
- package/dist/docs/assets/index.md.BPKcj5AI.js +20 -0
- package/dist/docs/assets/index.md.BPKcj5AI.lean.js +1 -0
- package/dist/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/dist/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/dist/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/dist/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/dist/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/dist/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/dist/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/dist/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/dist/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/dist/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/dist/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/dist/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/dist/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/dist/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/dist/docs/assets/quickstart.md.tVPiGK_L.js +35 -0
- package/dist/docs/assets/quickstart.md.tVPiGK_L.lean.js +1 -0
- package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.js +34 -0
- package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.lean.js +1 -0
- package/dist/docs/assets/reference_channels.md.D7JTR03W.js +53 -0
- package/dist/docs/assets/reference_channels.md.D7JTR03W.lean.js +1 -0
- package/dist/docs/assets/reference_cli.md.DA730zCu.js +33 -0
- package/dist/docs/assets/reference_cli.md.DA730zCu.lean.js +1 -0
- package/dist/docs/assets/reference_connections.md.C3vNH_DE.js +26 -0
- package/dist/docs/assets/reference_connections.md.C3vNH_DE.lean.js +1 -0
- package/dist/docs/assets/reference_hooks.md.BCEc3MyM.js +12 -0
- package/dist/docs/assets/reference_hooks.md.BCEc3MyM.lean.js +1 -0
- package/dist/docs/assets/reference_http-api.md.DBAahtdz.js +11 -0
- package/dist/docs/assets/reference_http-api.md.DBAahtdz.lean.js +1 -0
- package/dist/docs/assets/reference_instructions.md.BC05LEQ8.js +12 -0
- package/dist/docs/assets/reference_instructions.md.BC05LEQ8.lean.js +1 -0
- package/dist/docs/assets/reference_playground.md.CPZhfYaO.js +1 -0
- package/dist/docs/assets/reference_playground.md.CPZhfYaO.lean.js +1 -0
- package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.js +31 -0
- package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.lean.js +1 -0
- package/dist/docs/assets/reference_schedules.md.D7qijxLk.js +82 -0
- package/dist/docs/assets/reference_schedules.md.D7qijxLk.lean.js +1 -0
- package/dist/docs/assets/reference_sessions.md.DdjWabnN.js +8 -0
- package/dist/docs/assets/reference_sessions.md.DdjWabnN.lean.js +1 -0
- package/dist/docs/assets/reference_skills.md.VQnlBT3Q.js +15 -0
- package/dist/docs/assets/reference_skills.md.VQnlBT3Q.lean.js +1 -0
- package/dist/docs/assets/reference_subagents.md.CIRAVcPK.js +10 -0
- package/dist/docs/assets/reference_subagents.md.CIRAVcPK.lean.js +1 -0
- package/dist/docs/assets/reference_tools.md.DF5kwlt0.js +46 -0
- package/dist/docs/assets/reference_tools.md.DF5kwlt0.lean.js +1 -0
- package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.js +1 -0
- package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.lean.js +1 -0
- package/dist/docs/assets/style.Bj-IXa5W.css +1 -0
- package/dist/docs/assets/troubleshooting.md.DkovjS7h.js +1 -0
- package/dist/docs/assets/troubleshooting.md.DkovjS7h.lean.js +1 -0
- package/dist/docs/building-with-agents.html +37 -0
- package/dist/docs/concepts.html +28 -0
- package/dist/docs/deployment.html +38 -0
- package/dist/docs/evals.html +109 -0
- package/dist/docs/guides/agent-to-agent.html +34 -0
- package/dist/docs/guides/cloud-runtime.html +33 -0
- package/dist/docs/guides/github.html +44 -0
- package/dist/docs/guides/human-in-the-loop.html +52 -0
- package/dist/docs/guides/slack.html +61 -0
- package/dist/docs/guides/webhooks.html +74 -0
- package/dist/docs/hashmap.json +1 -0
- package/dist/docs/hillclimbing.html +28 -0
- package/dist/docs/index.html +44 -0
- package/dist/docs/quickstart.html +59 -0
- package/dist/docs/reference/agent-config.html +58 -0
- package/dist/docs/reference/channels.html +77 -0
- package/dist/docs/reference/cli.html +57 -0
- package/dist/docs/reference/connections.html +50 -0
- package/dist/docs/reference/hooks.html +36 -0
- package/dist/docs/reference/http-api.html +35 -0
- package/dist/docs/reference/instructions.html +36 -0
- package/dist/docs/reference/playground.html +25 -0
- package/dist/docs/reference/project-layout.html +55 -0
- package/dist/docs/reference/schedules.html +106 -0
- package/dist/docs/reference/sessions.html +32 -0
- package/dist/docs/reference/skills.html +39 -0
- package/dist/docs/reference/subagents.html +34 -0
- package/dist/docs/reference/tools.html +70 -0
- package/dist/docs/scaffolding-agents.html +25 -0
- package/dist/docs/troubleshooting.html +25 -0
- package/dist/docs/vp-icons.css +0 -0
- package/dist/evals.d.ts +312 -0
- package/dist/evals.d.ts.map +1 -0
- package/dist/evals.js +254 -0
- package/dist/hooks.d.ts +23 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +23 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/internal/ab-collector.d.ts +42 -0
- package/dist/internal/ab-collector.d.ts.map +1 -0
- package/dist/internal/ab-collector.js +153 -0
- package/dist/internal/ab-fold.d.ts +36 -0
- package/dist/internal/ab-fold.d.ts.map +1 -0
- package/dist/internal/ab-fold.js +171 -0
- package/dist/internal/ab-snapshot.d.ts +70 -0
- package/dist/internal/ab-snapshot.d.ts.map +1 -0
- package/dist/internal/ab-snapshot.js +208 -0
- package/dist/internal/approval-gate.d.ts +34 -0
- package/dist/internal/approval-gate.d.ts.map +1 -0
- package/dist/internal/approval-gate.js +85 -0
- package/dist/internal/auth.d.ts +8 -0
- package/dist/internal/auth.d.ts.map +1 -0
- package/dist/internal/auth.js +27 -0
- package/dist/internal/authored-alias-hooks.d.ts +31 -0
- package/dist/internal/authored-alias-hooks.d.ts.map +1 -0
- package/dist/internal/authored-alias-hooks.js +43 -0
- package/dist/internal/authored-loaders.d.ts +25 -0
- package/dist/internal/authored-loaders.d.ts.map +1 -0
- package/dist/internal/authored-loaders.js +129 -0
- package/dist/internal/bounded-int.d.ts +11 -0
- package/dist/internal/bounded-int.d.ts.map +1 -0
- package/dist/internal/bounded-int.js +13 -0
- package/dist/internal/brand.d.ts +6 -0
- package/dist/internal/brand.d.ts.map +1 -0
- package/dist/internal/brand.js +17 -0
- package/dist/internal/chat-client.d.ts +19 -0
- package/dist/internal/chat-client.d.ts.map +1 -0
- package/dist/internal/chat-client.js +327 -0
- package/dist/internal/cli-ax.d.ts +58 -0
- package/dist/internal/cli-ax.d.ts.map +1 -0
- package/dist/internal/cli-ax.js +345 -0
- package/dist/internal/cli-cursor.d.ts +20 -0
- package/dist/internal/cli-cursor.d.ts.map +1 -0
- package/dist/internal/cli-cursor.js +227 -0
- package/dist/internal/cli-github.d.ts +47 -0
- package/dist/internal/cli-github.d.ts.map +1 -0
- package/dist/internal/cli-github.js +644 -0
- package/dist/internal/cli-slack.d.ts +18 -0
- package/dist/internal/cli-slack.d.ts.map +1 -0
- package/dist/internal/cli-slack.js +144 -0
- package/dist/internal/cloud-merge.d.ts +23 -0
- package/dist/internal/cloud-merge.d.ts.map +1 -0
- package/dist/internal/cloud-merge.js +28 -0
- package/dist/internal/cron.d.ts +17 -0
- package/dist/internal/cron.d.ts.map +1 -0
- package/dist/internal/cron.js +60 -0
- package/dist/internal/cursor/account-mcp.d.ts +95 -0
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -0
- package/dist/internal/cursor/account-mcp.js +342 -0
- package/dist/internal/cursor/backend-client.d.ts +118 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -0
- package/dist/internal/cursor/backend-client.js +477 -0
- package/dist/internal/cursor/credentials.d.ts +112 -0
- package/dist/internal/cursor/credentials.d.ts.map +1 -0
- package/dist/internal/cursor/credentials.js +329 -0
- package/dist/internal/cursor-event-relay.d.ts +111 -0
- package/dist/internal/cursor-event-relay.d.ts.map +1 -0
- package/dist/internal/cursor-event-relay.js +181 -0
- package/dist/internal/cursor-relay-core.d.ts +113 -0
- package/dist/internal/cursor-relay-core.d.ts.map +1 -0
- package/dist/internal/cursor-relay-core.js +364 -0
- package/dist/internal/cursor-slack-relay.d.ts +94 -0
- package/dist/internal/cursor-slack-relay.d.ts.map +1 -0
- package/dist/internal/cursor-slack-relay.js +171 -0
- package/dist/internal/describe-error.d.ts +3 -0
- package/dist/internal/describe-error.d.ts.map +1 -0
- package/dist/internal/describe-error.js +4 -0
- package/dist/internal/dev-mode.d.ts +13 -0
- package/dist/internal/dev-mode.d.ts.map +1 -0
- package/dist/internal/dev-mode.js +17 -0
- package/dist/internal/discovery.d.ts +55 -0
- package/dist/internal/discovery.d.ts.map +1 -0
- package/dist/internal/discovery.js +1427 -0
- package/dist/internal/distribution.d.ts +47 -0
- package/dist/internal/distribution.d.ts.map +1 -0
- package/dist/internal/distribution.js +82 -0
- package/dist/internal/docs-site.d.ts +28 -0
- package/dist/internal/docs-site.d.ts.map +1 -0
- package/dist/internal/docs-site.js +169 -0
- package/dist/internal/duration.d.ts +8 -0
- package/dist/internal/duration.d.ts.map +1 -0
- package/dist/internal/duration.js +35 -0
- package/dist/internal/eval-run-store.d.ts +81 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -0
- package/dist/internal/eval-run-store.js +396 -0
- package/dist/internal/eval-runner.d.ts +76 -0
- package/dist/internal/eval-runner.d.ts.map +1 -0
- package/dist/internal/eval-runner.js +336 -0
- package/dist/internal/event-log.d.ts +51 -0
- package/dist/internal/event-log.d.ts.map +1 -0
- package/dist/internal/event-log.js +167 -0
- package/dist/internal/event-mapper.d.ts +46 -0
- package/dist/internal/event-mapper.d.ts.map +1 -0
- package/dist/internal/event-mapper.js +226 -0
- package/dist/internal/frontmatter.d.ts +12 -0
- package/dist/internal/frontmatter.d.ts.map +1 -0
- package/dist/internal/frontmatter.js +61 -0
- package/dist/internal/github-fanout.d.ts +32 -0
- package/dist/internal/github-fanout.d.ts.map +1 -0
- package/dist/internal/github-fanout.js +122 -0
- package/dist/internal/handleAgentServeTrigger.d.ts +59 -0
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -0
- package/dist/internal/handleAgentServeTrigger.js +115 -0
- package/dist/internal/host-platforms.d.ts +40 -0
- package/dist/internal/host-platforms.d.ts.map +1 -0
- package/dist/internal/host-platforms.js +113 -0
- package/dist/internal/http-channel.d.ts +54 -0
- package/dist/internal/http-channel.d.ts.map +1 -0
- package/dist/internal/http-channel.js +382 -0
- package/dist/internal/ids.d.ts +19 -0
- package/dist/internal/ids.d.ts.map +1 -0
- package/dist/internal/ids.js +34 -0
- package/dist/internal/init-project.d.ts +22 -0
- package/dist/internal/init-project.d.ts.map +1 -0
- package/dist/internal/init-project.js +125 -0
- package/dist/internal/json-dir-store.d.ts +32 -0
- package/dist/internal/json-dir-store.d.ts.map +1 -0
- package/dist/internal/json-dir-store.js +100 -0
- package/dist/internal/json-schema.d.ts +18 -0
- package/dist/internal/json-schema.d.ts.map +1 -0
- package/dist/internal/json-schema.js +53 -0
- package/dist/internal/log-ring.d.ts +23 -0
- package/dist/internal/log-ring.d.ts.map +1 -0
- package/dist/internal/log-ring.js +42 -0
- package/dist/internal/map-pool.d.ts +6 -0
- package/dist/internal/map-pool.d.ts.map +1 -0
- package/dist/internal/map-pool.js +41 -0
- package/dist/internal/mcp-endpoint.d.ts +59 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -0
- package/dist/internal/mcp-endpoint.js +355 -0
- package/dist/internal/mcp-host.d.ts +28 -0
- package/dist/internal/mcp-host.d.ts.map +1 -0
- package/dist/internal/mcp-host.js +162 -0
- package/dist/internal/net.d.ts +5 -0
- package/dist/internal/net.d.ts.map +1 -0
- package/dist/internal/net.js +30 -0
- package/dist/internal/peer-connections.d.ts +44 -0
- package/dist/internal/peer-connections.d.ts.map +1 -0
- package/dist/internal/peer-connections.js +67 -0
- package/dist/internal/playground/static.d.ts +35 -0
- package/dist/internal/playground/static.d.ts.map +1 -0
- package/dist/internal/playground/static.js +162 -0
- package/dist/internal/playground/toolchain.d.ts +57 -0
- package/dist/internal/playground/toolchain.d.ts.map +1 -0
- package/dist/internal/playground/toolchain.js +226 -0
- package/dist/internal/reminder-runner.d.ts +62 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -0
- package/dist/internal/reminder-runner.js +582 -0
- package/dist/internal/reminder-schedule.d.ts +22 -0
- package/dist/internal/reminder-schedule.d.ts.map +1 -0
- package/dist/internal/reminder-schedule.js +82 -0
- package/dist/internal/reminder-store.d.ts +58 -0
- package/dist/internal/reminder-store.d.ts.map +1 -0
- package/dist/internal/reminder-store.js +106 -0
- package/dist/internal/resolved-connections.d.ts +41 -0
- package/dist/internal/resolved-connections.d.ts.map +1 -0
- package/dist/internal/resolved-connections.js +73 -0
- package/dist/internal/review-comments.d.ts +50 -0
- package/dist/internal/review-comments.d.ts.map +1 -0
- package/dist/internal/review-comments.js +194 -0
- package/dist/internal/router.d.ts +33 -0
- package/dist/internal/router.d.ts.map +1 -0
- package/dist/internal/router.js +93 -0
- package/dist/internal/run-client.d.ts +49 -0
- package/dist/internal/run-client.d.ts.map +1 -0
- package/dist/internal/run-client.js +208 -0
- package/dist/internal/schedule-runner.d.ts +31 -0
- package/dist/internal/schedule-runner.d.ts.map +1 -0
- package/dist/internal/schedule-runner.js +126 -0
- package/dist/internal/sdk-runner.d.ts +41 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -0
- package/dist/internal/sdk-runner.js +221 -0
- package/dist/internal/server.d.ts +25 -0
- package/dist/internal/server.d.ts.map +1 -0
- package/dist/internal/server.js +1374 -0
- package/dist/internal/session-engine.d.ts +287 -0
- package/dist/internal/session-engine.d.ts.map +1 -0
- package/dist/internal/session-engine.js +1377 -0
- package/dist/internal/session-store.d.ts +29 -0
- package/dist/internal/session-store.d.ts.map +1 -0
- package/dist/internal/session-store.js +140 -0
- package/dist/internal/session-urls.d.ts +10 -0
- package/dist/internal/session-urls.d.ts.map +1 -0
- package/dist/internal/session-urls.js +25 -0
- package/dist/internal/stream-progress.d.ts +14 -0
- package/dist/internal/stream-progress.d.ts.map +1 -0
- package/dist/internal/stream-progress.js +195 -0
- package/dist/internal/tool-result.d.ts +20 -0
- package/dist/internal/tool-result.d.ts.map +1 -0
- package/dist/internal/tool-result.js +45 -0
- package/dist/internal/trajectory.d.ts +56 -0
- package/dist/internal/trajectory.d.ts.map +1 -0
- package/dist/internal/trajectory.js +248 -0
- package/dist/internal/workspace.d.ts +42 -0
- package/dist/internal/workspace.d.ts.map +1 -0
- package/dist/internal/workspace.js +199 -0
- package/dist/playground/assets/cursor-icons-outline-oY2V_mvK.woff2 +0 -0
- package/dist/playground/assets/index-1K-hG-7p.css +1 -0
- package/dist/playground/assets/index-FlWjhg3x.js +79 -0
- package/dist/playground/index.html +17 -0
- package/dist/reminders.d.ts +23 -0
- package/dist/reminders.d.ts.map +1 -0
- package/dist/reminders.js +22 -0
- package/dist/schedules.d.ts +23 -0
- package/dist/schedules.d.ts.map +1 -0
- package/dist/schedules.js +26 -0
- package/dist/skills.d.ts +22 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +22 -0
- package/dist/tools.d.ts +92 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +68 -0
- package/dist/types.d.ts +1609 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +80 -0
- package/docs/README.md +174 -0
- package/docs/ab.md +355 -0
- package/docs/building-with-agents.md +129 -0
- package/docs/concepts.md +177 -0
- package/docs/deployment.md +218 -0
- package/docs/evals.md +383 -0
- package/docs/guides/agent-to-agent.md +118 -0
- package/docs/guides/cloud-runtime.md +122 -0
- package/docs/guides/github.md +172 -0
- package/docs/guides/human-in-the-loop.md +131 -0
- package/docs/guides/slack.md +245 -0
- package/docs/guides/webhooks.md +201 -0
- package/docs/hillclimbing.md +93 -0
- package/docs/quickstart.md +183 -0
- package/docs/reference/agent-config.md +154 -0
- package/docs/reference/channels.md +206 -0
- package/docs/reference/cli.md +257 -0
- package/docs/reference/connections.md +137 -0
- package/docs/reference/hooks.md +74 -0
- package/docs/reference/http-api.md +219 -0
- package/docs/reference/instructions.md +77 -0
- package/docs/reference/playground.md +67 -0
- package/docs/reference/project-layout.md +119 -0
- package/docs/reference/schedules.md +220 -0
- package/docs/reference/sessions.md +165 -0
- package/docs/reference/skills.md +79 -0
- package/docs/reference/subagents.md +74 -0
- package/docs/reference/tools.md +203 -0
- package/docs/scaffolding-agents.md +113 -0
- package/docs/troubleshooting.md +95 -0
- package/package.json +185 -0
- package/skills/ab/SKILL.md +139 -0
- package/skills/create-agent/SKILL.md +219 -0
- package/skills/debug/SKILL.md +71 -0
- package/skills/evals/SKILL.md +217 -0
- package/skills/framework-map/SKILL.md +142 -0
- package/skills/github/SKILL.md +144 -0
- package/skills/hillclimb/SKILL.md +162 -0
- package/skills/setup-slack/SKILL.md +180 -0
- package/src/ab.ts +675 -0
- package/src/bin/agent-serve.ts +949 -0
- package/src/channels/github/api.ts +374 -0
- package/src/channels/github/auth.ts +275 -0
- package/src/channels/github/binding.ts +223 -0
- package/src/channels/github/defaults.ts +111 -0
- package/src/channels/github/events.ts +72 -0
- package/src/channels/github/forward.ts +381 -0
- package/src/channels/github/github-channel.ts +2270 -0
- package/src/channels/github/index.ts +52 -0
- package/src/channels/github/replay.ts +387 -0
- package/src/channels/github/state.ts +95 -0
- package/src/channels/github/types.ts +490 -0
- package/src/channels/slack/api.ts +580 -0
- package/src/channels/slack/approvals.ts +198 -0
- package/src/channels/slack/assistant.ts +76 -0
- package/src/channels/slack/auth.ts +112 -0
- package/src/channels/slack/channel-watch.ts +219 -0
- package/src/channels/slack/constants.ts +30 -0
- package/src/channels/slack/credentials.ts +90 -0
- package/src/channels/slack/cursor-account.ts +166 -0
- package/src/channels/slack/defaults.ts +450 -0
- package/src/channels/slack/dispatch.ts +293 -0
- package/src/channels/slack/doctor.ts +135 -0
- package/src/channels/slack/event-id-dedupe.ts +30 -0
- package/src/channels/slack/inbound.ts +219 -0
- package/src/channels/slack/index.ts +45 -0
- package/src/channels/slack/init.ts +133 -0
- package/src/channels/slack/interactive.ts +194 -0
- package/src/channels/slack/links.ts +23 -0
- package/src/channels/slack/live-delivery.ts +201 -0
- package/src/channels/slack/log.ts +83 -0
- package/src/channels/slack/manifest.ts +179 -0
- package/src/channels/slack/pr-ref.ts +50 -0
- package/src/channels/slack/setup.ts +97 -0
- package/src/channels/slack/slack-channel.ts +374 -0
- package/src/channels/slack/socket-mode.ts +154 -0
- package/src/channels/slack/stream.ts +63 -0
- package/src/channels/slack/tool-approval-events.ts +136 -0
- package/src/channels/slack/types.ts +349 -0
- package/src/channels.ts +385 -0
- package/src/connections.ts +89 -0
- package/src/evals.ts +501 -0
- package/src/hooks.ts +33 -0
- package/src/index.ts +184 -0
- package/src/internal/ab-collector.ts +210 -0
- package/src/internal/ab-fold.ts +228 -0
- package/src/internal/ab-snapshot.ts +335 -0
- package/src/internal/approval-gate.ts +115 -0
- package/src/internal/auth.ts +39 -0
- package/src/internal/authored-alias-hooks.ts +69 -0
- package/src/internal/authored-loaders.ts +138 -0
- package/src/internal/bounded-int.ts +27 -0
- package/src/internal/brand.ts +29 -0
- package/src/internal/chat-client.ts +407 -0
- package/src/internal/cli-ax.ts +474 -0
- package/src/internal/cli-cursor.ts +275 -0
- package/src/internal/cli-github.ts +849 -0
- package/src/internal/cli-slack.ts +175 -0
- package/src/internal/cloud-merge.ts +41 -0
- package/src/internal/cron.ts +78 -0
- package/src/internal/cursor/account-mcp.ts +472 -0
- package/src/internal/cursor/backend-client.ts +614 -0
- package/src/internal/cursor/credentials.ts +401 -0
- package/src/internal/cursor-event-relay.ts +281 -0
- package/src/internal/cursor-relay-core.ts +444 -0
- package/src/internal/cursor-slack-relay.ts +242 -0
- package/src/internal/describe-error.ts +4 -0
- package/src/internal/dev-mode.ts +20 -0
- package/src/internal/discovery.ts +1709 -0
- package/src/internal/distribution.ts +97 -0
- package/src/internal/docs-site.ts +191 -0
- package/src/internal/duration.ts +39 -0
- package/src/internal/eval-run-store.ts +519 -0
- package/src/internal/eval-runner.ts +522 -0
- package/src/internal/event-log.ts +175 -0
- package/src/internal/event-mapper.ts +272 -0
- package/src/internal/frontmatter.ts +77 -0
- package/src/internal/github-fanout.ts +159 -0
- package/src/internal/handleAgentServeTrigger.ts +156 -0
- package/src/internal/host-platforms.ts +144 -0
- package/src/internal/http-channel.ts +546 -0
- package/src/internal/ids.ts +44 -0
- package/src/internal/init-project.ts +165 -0
- package/src/internal/json-dir-store.ts +109 -0
- package/src/internal/json-schema.ts +69 -0
- package/src/internal/log-ring.ts +59 -0
- package/src/internal/map-pool.ts +32 -0
- package/src/internal/mcp-endpoint.ts +518 -0
- package/src/internal/mcp-host.ts +211 -0
- package/src/internal/net.ts +31 -0
- package/src/internal/peer-connections.ts +100 -0
- package/src/internal/playground/static.ts +189 -0
- package/src/internal/playground/toolchain.ts +277 -0
- package/src/internal/reminder-runner.ts +756 -0
- package/src/internal/reminder-schedule.ts +106 -0
- package/src/internal/reminder-store.ts +145 -0
- package/src/internal/resolved-connections.ts +145 -0
- package/src/internal/review-comments.ts +268 -0
- package/src/internal/router.ts +145 -0
- package/src/internal/run-client.ts +320 -0
- package/src/internal/schedule-runner.ts +161 -0
- package/src/internal/sdk-runner.ts +315 -0
- package/src/internal/server.ts +1737 -0
- package/src/internal/session-engine.ts +1810 -0
- package/src/internal/session-store.ts +147 -0
- package/src/internal/session-urls.ts +40 -0
- package/src/internal/stream-progress.ts +254 -0
- package/src/internal/tool-result.ts +54 -0
- package/src/internal/trajectory.ts +306 -0
- package/src/internal/workspace.ts +223 -0
- package/src/reminders.ts +35 -0
- package/src/schedules.ts +37 -0
- package/src/skills.ts +27 -0
- package/src/tools.ts +127 -0
- package/src/types.ts +1848 -0
|
@@ -0,0 +1,1374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP host: a plain node:http server that adapts requests to the Fetch
|
|
3
|
+
* API, resolves route auth, and dispatches to the built-in HTTP channel
|
|
4
|
+
* routes, custom channel routes (under `/v1/channels/<id>`), and the
|
|
5
|
+
* server-level health / info / dev-dispatch routes.
|
|
6
|
+
*/
|
|
7
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
17
|
+
var t = {};
|
|
18
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
19
|
+
t[p] = s[p];
|
|
20
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
21
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
22
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
23
|
+
t[p[i]] = s[p[i]];
|
|
24
|
+
}
|
|
25
|
+
return t;
|
|
26
|
+
};
|
|
27
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
28
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
29
|
+
var m = o[Symbol.asyncIterator], i;
|
|
30
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
31
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
32
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
33
|
+
};
|
|
34
|
+
import { Buffer } from "node:buffer";
|
|
35
|
+
import { once } from "node:events";
|
|
36
|
+
import { createServer, } from "node:http";
|
|
37
|
+
import { join } from "node:path";
|
|
38
|
+
import { findGitHubChannelTargets } from "../channels/github/forward.js";
|
|
39
|
+
import { dispatchCursorSlackEnvelope, getSlackCursorAccountRuntime, } from "../channels/slack/cursor-account.js";
|
|
40
|
+
import { allowAll, bearerAuth, localDevStrict } from "../channels.js";
|
|
41
|
+
import { CursorAccountMcpBridge, collectCursorAccountConnections, cursorAccountMcpPath, } from "./cursor/account-mcp.js";
|
|
42
|
+
import { CursorBackendClient } from "./cursor/backend-client.js";
|
|
43
|
+
import { cursorBackendUrl, resolveApiKey, SIGN_IN_HINT, } from "./cursor/credentials.js";
|
|
44
|
+
import { CursorEventRelay, formatCursorScmEventLog, scmMetadataToGitHubPayload, } from "./cursor-event-relay.js";
|
|
45
|
+
import { CursorSlackRelay, createCursorSlackApiCaller, formatCursorSlackEventLog, } from "./cursor-slack-relay.js";
|
|
46
|
+
import { formatDiagnostics, hasErrorDiagnostics, projectInfo, } from "./discovery.js";
|
|
47
|
+
import { packageVersion } from "./distribution.js";
|
|
48
|
+
import { docsSiteExists, ensureDocsSiteBundle, renderDocsFile, } from "./docs-site.js";
|
|
49
|
+
import { EvalRunConflictError, EvalRunEmptyError, EvalRunStore, } from "./eval-run-store.js";
|
|
50
|
+
import { collectGitHubTriggerTargets, handleAgentServeTrigger, } from "./handleAgentServeTrigger.js";
|
|
51
|
+
import { warmupHostGitHubAuth } from "./host-platforms.js";
|
|
52
|
+
import { buildHttpChannelRoutes, errorResponse, jsonResponse, readJsonObject, requireSessionOwner, } from "./http-channel.js";
|
|
53
|
+
import { validateToolInput } from "./json-schema.js";
|
|
54
|
+
import { createLogRing } from "./log-ring.js";
|
|
55
|
+
import { createAgentMcpEndpoint, MCP_ENDPOINT_PATH } from "./mcp-endpoint.js";
|
|
56
|
+
import { isLoopbackAddress } from "./net.js";
|
|
57
|
+
import { collectPeerConnections } from "./peer-connections.js";
|
|
58
|
+
import { renderAgentsIndexHtml, renderPlaygroundAsset, renderPlaygroundIndexHtml, } from "./playground/static.js";
|
|
59
|
+
import { ensurePlaygroundBundle, playgroundDevBaseFromEnv, playgroundDevBasePath, startPlaygroundDevServer, } from "./playground/toolchain.js";
|
|
60
|
+
import { ReminderRunner, ReminderValidationError, UnknownReminderError, } from "./reminder-runner.js";
|
|
61
|
+
import { assertAccountConnectionsNotAnonymous, resolveMountConnections, validateMountConnectionsConfig, } from "./resolved-connections.js";
|
|
62
|
+
import { Router } from "./router.js";
|
|
63
|
+
import { ScheduleRunner, UnknownScheduleError } from "./schedule-runner.js";
|
|
64
|
+
import { CursorSdkRunner } from "./sdk-runner.js";
|
|
65
|
+
import { SessionAuthError, SessionBusyError, SessionEngine, ToolInputError, ToolNotDirectlyCallableError, UnknownToolError, } from "./session-engine.js";
|
|
66
|
+
import { sessionUrlsForRequest } from "./session-urls.js";
|
|
67
|
+
const MAX_BODY_BYTES = 32 * 1024 * 1024;
|
|
68
|
+
/**
|
|
69
|
+
* Start an HTTP server hosting one or more agents. A single agent with an
|
|
70
|
+
* empty slug serves at `/v1/*` and `/playground`; multiple agents each serve
|
|
71
|
+
* under `/<slug>/v1/*` and `/<slug>/playground`, with an index at `/`.
|
|
72
|
+
*/
|
|
73
|
+
export function startServer(mounts_1) {
|
|
74
|
+
return __awaiter(this, arguments, void 0, function* (mounts, options = {}) {
|
|
75
|
+
var _a, _b;
|
|
76
|
+
var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
77
|
+
var _q;
|
|
78
|
+
if (mounts.length === 0) {
|
|
79
|
+
throw new Error("startServer requires at least one agent mount");
|
|
80
|
+
}
|
|
81
|
+
const multi = mounts.length > 1 || ((_a = mounts[0]) === null || _a === void 0 ? void 0 : _a.slug) !== "";
|
|
82
|
+
for (const { slug, project } of mounts) {
|
|
83
|
+
if (hasErrorDiagnostics(project)) {
|
|
84
|
+
const label = slug === "" ? project.name : `${slug} (${project.name})`;
|
|
85
|
+
throw new Error(`Refusing to serve agent ${label} — it has errors:\n${formatDiagnostics(project.diagnostics.filter((d) => d.severity === "error"))}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const logRing = createLogRing({ capacity: 2000 });
|
|
89
|
+
const logger = logRing.wrap((_c = options.logger) !== null && _c !== void 0 ? _c : ((line) => console.log(line)));
|
|
90
|
+
const dev = (_d = options.dev) !== null && _d !== void 0 ? _d : false;
|
|
91
|
+
const playground = (_e = options.playground) !== null && _e !== void 0 ? _e : true;
|
|
92
|
+
const runSchedules = (_f = options.schedules) !== null && _f !== void 0 ? _f : !dev;
|
|
93
|
+
const runReminders = (_g = options.reminders) !== null && _g !== void 0 ? _g : !dev;
|
|
94
|
+
// Identify this process to the Cursor backend as agentkit (the SDK reads
|
|
95
|
+
// this when building its client-version header) so the internal-only
|
|
96
|
+
// agentkit_enabled gate applies to every turn.
|
|
97
|
+
(_h = (_q = process.env).CURSOR_AGENTKIT_VERSION) !== null && _h !== void 0 ? _h : (_q.CURSOR_AGENTKIT_VERSION = packageVersion());
|
|
98
|
+
// One resolution for every Cursor credential consumer (SDK runner, account
|
|
99
|
+
// MCP bridge, SCM event relay): explicit option → CURSOR_API_KEY → stored
|
|
100
|
+
// `agent-serve login`.
|
|
101
|
+
const accountKey = yield resolveApiKey(options.apiKey === undefined ? {} : { apiKey: options.apiKey });
|
|
102
|
+
// The SCM relay reads the event stream as the signed-in user, so
|
|
103
|
+
// `cursorEvents` cannot work signed out. Require the credential before any
|
|
104
|
+
// slow or stateful startup work (playground build, channel onStart, cron).
|
|
105
|
+
const cursorEventsAuth = options.cursorEvents === undefined
|
|
106
|
+
? undefined
|
|
107
|
+
: requireCursorEventsAuth(options.cursorEvents, accountKey);
|
|
108
|
+
if (playground) {
|
|
109
|
+
// Auto-build dist/playground when missing (monorepo / source checkout).
|
|
110
|
+
// Published installs already ship the bundle; vite may be absent there.
|
|
111
|
+
yield ensurePlaygroundBundle(logger);
|
|
112
|
+
}
|
|
113
|
+
const docsSite = (_j = options.docs) !== null && _j !== void 0 ? _j : true;
|
|
114
|
+
if (docsSite) {
|
|
115
|
+
// Same pattern for the documentation site at /docs.
|
|
116
|
+
yield ensureDocsSiteBundle(logger);
|
|
117
|
+
}
|
|
118
|
+
if (options.allowAnonymous === true) {
|
|
119
|
+
logger("[agent-serve] warning: --allow-anonymous admits every HTTP caller as the same principal; use only on a trusted network (e.g. Tailscale) and prefer --bearer-token for shared hosts");
|
|
120
|
+
}
|
|
121
|
+
const runner = (_k = options.runner) !== null && _k !== void 0 ? _k : new CursorSdkRunner(accountKey === undefined ? {} : { apiKey: accountKey.apiKey });
|
|
122
|
+
const router = new Router();
|
|
123
|
+
const runtimes = [];
|
|
124
|
+
const channelStopHooks = [];
|
|
125
|
+
let githubAuthWarmed = false;
|
|
126
|
+
// Validate peer MCP connections (defineConnection({ agent })) against the
|
|
127
|
+
// mount set before anything starts: unknown slugs and self-references are
|
|
128
|
+
// authoring errors. URLs resolve after listen, once the port is known.
|
|
129
|
+
const peerRefsBySlug = collectPeerConnections(mounts);
|
|
130
|
+
// Cursor account MCP connections (defineConnection({ cursorAccount: true }))
|
|
131
|
+
// need a signed-in host: their tools execute on the Cursor backend with
|
|
132
|
+
// the account's stored OAuth credentials. Fail fast at serve start instead
|
|
133
|
+
// of at the first turn.
|
|
134
|
+
const accountRefsBySlug = collectCursorAccountConnections(mounts);
|
|
135
|
+
assertAccountConnectionsNotAnonymous({
|
|
136
|
+
allowAnonymous: options.allowAnonymous,
|
|
137
|
+
hasAccountConnections: accountRefsBySlug.size > 0,
|
|
138
|
+
});
|
|
139
|
+
let accountBridge;
|
|
140
|
+
if (accountRefsBySlug.size > 0) {
|
|
141
|
+
if (accountKey === undefined) {
|
|
142
|
+
throw new Error(`This project has Cursor account MCP connections (defineConnection({ cursorAccount: true })) but the host is not signed in to Cursor. ${SIGN_IN_HINT}`);
|
|
143
|
+
}
|
|
144
|
+
accountBridge = new CursorAccountMcpBridge({
|
|
145
|
+
client: new CursorBackendClient({
|
|
146
|
+
backendUrl: accountKey.backendUrl,
|
|
147
|
+
apiKey: accountKey.apiKey,
|
|
148
|
+
logger,
|
|
149
|
+
}),
|
|
150
|
+
logger,
|
|
151
|
+
// Cloud turns reach the bridge through the serve publicUrl; without
|
|
152
|
+
// one, the bridge admits loopback callers only.
|
|
153
|
+
allowRemoteCallers: options.publicUrl !== undefined,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
for (const { slug, project } of mounts) {
|
|
157
|
+
// Each agent gets an isolated state root so their sessions never mix.
|
|
158
|
+
const baseStateRoot = (_l = options.stateRoot) !== null && _l !== void 0 ? _l : join(project.rootDir, ".agent-serve");
|
|
159
|
+
const stateRoot = slug === "" ? baseStateRoot : join(baseStateRoot, slug);
|
|
160
|
+
const engine = new SessionEngine({ project, stateRoot, runner, logger });
|
|
161
|
+
if (!githubAuthWarmed) {
|
|
162
|
+
// Soft ambient GitHub auth check before channel onStart (which may call
|
|
163
|
+
// host.github). Missing auth is a warning only — serve still starts.
|
|
164
|
+
yield warmupHostGitHubAuth({ logger, github: engine.host.github });
|
|
165
|
+
githubAuthWarmed = true;
|
|
166
|
+
}
|
|
167
|
+
const scheduleRunner = new ScheduleRunner(engine, project, logger);
|
|
168
|
+
const reminderRunner = new ReminderRunner(engine, logger);
|
|
169
|
+
const evalRuns = new EvalRunStore(project.rootDir, logger);
|
|
170
|
+
engine.setReminders({
|
|
171
|
+
create: (input) => reminderRunner.create(input),
|
|
172
|
+
list: (filter) => reminderRunner.list(filter),
|
|
173
|
+
cancel: (id, reason) => reminderRunner.cancel(id, reason),
|
|
174
|
+
cancelForContinuation: (token, reason) => reminderRunner.cancelForContinuation(token, reason),
|
|
175
|
+
dispatch: (id) => reminderRunner.dispatch(id),
|
|
176
|
+
});
|
|
177
|
+
const runtime = {
|
|
178
|
+
slug,
|
|
179
|
+
project,
|
|
180
|
+
engine,
|
|
181
|
+
scheduleRunner,
|
|
182
|
+
reminderRunner,
|
|
183
|
+
evalRuns,
|
|
184
|
+
};
|
|
185
|
+
runtimes.push(runtime);
|
|
186
|
+
const accountRefs = accountRefsBySlug.get(slug);
|
|
187
|
+
mountAgentRoutes(router, runtime, Object.assign(Object.assign(Object.assign({ dev,
|
|
188
|
+
playground,
|
|
189
|
+
multi,
|
|
190
|
+
logger,
|
|
191
|
+
logRing }, (options.authToken === undefined
|
|
192
|
+
? {}
|
|
193
|
+
: { authToken: options.authToken })), (options.allowAnonymous === true ? { allowAnonymous: true } : {})), (accountRefs !== undefined && accountBridge !== undefined
|
|
194
|
+
? { accountBridge, accountRefs }
|
|
195
|
+
: {})));
|
|
196
|
+
for (const channel of project.channels) {
|
|
197
|
+
const definition = channel.definition;
|
|
198
|
+
if (definition.onStop !== undefined) {
|
|
199
|
+
channelStopHooks.push(() => { var _a; return (_a = definition.onStop) === null || _a === void 0 ? void 0 : _a.call(definition); });
|
|
200
|
+
}
|
|
201
|
+
if (definition.onStart !== undefined) {
|
|
202
|
+
const startArgs = {
|
|
203
|
+
send: (message, sendOptions) => engine.send(channel.id, message, sendOptions),
|
|
204
|
+
getSession: (sessionId) => engine.getChannelSession(channel.id, sessionId),
|
|
205
|
+
receive: (target, input) => engine.receive(target, input),
|
|
206
|
+
callTool: (toolName, input, callOptions) => engine.callTool(toolName, input, callOptions),
|
|
207
|
+
emitAssistantMessage: (sessionId, text) => engine.emitAssistantMessage(sessionId, text),
|
|
208
|
+
host: engine.host,
|
|
209
|
+
waitUntil: (promise) => {
|
|
210
|
+
engine.trackBackground(promise);
|
|
211
|
+
},
|
|
212
|
+
resolveApproval: (sessionId, callId, decision, auth) => engine.resolveApproval(sessionId, callId, decision, auth),
|
|
213
|
+
logger,
|
|
214
|
+
};
|
|
215
|
+
try {
|
|
216
|
+
yield definition.onStart(startArgs);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
logger(`[agent-serve] channel "${channel.id}" onStart failed: ${error instanceof Error
|
|
220
|
+
? ((_m = error.stack) !== null && _m !== void 0 ? _m : error.message)
|
|
221
|
+
: String(error)}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (runSchedules) {
|
|
226
|
+
scheduleRunner.start();
|
|
227
|
+
}
|
|
228
|
+
if (runReminders) {
|
|
229
|
+
yield reminderRunner.start();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (docsSite) {
|
|
233
|
+
// The framework documentation site, mounted at /docs in both layouts
|
|
234
|
+
// ("docs" is a reserved slug). Static, no secrets; unauthenticated like
|
|
235
|
+
// the playground assets.
|
|
236
|
+
router.add({
|
|
237
|
+
method: "GET",
|
|
238
|
+
pattern: "/docs",
|
|
239
|
+
auth: "none",
|
|
240
|
+
handler: () => renderDocsFile(""),
|
|
241
|
+
});
|
|
242
|
+
router.add({
|
|
243
|
+
method: "GET",
|
|
244
|
+
pattern: "/docs/*path",
|
|
245
|
+
auth: "none",
|
|
246
|
+
handler: (_request, ctx) => { var _a; return renderDocsFile((_a = ctx.params.path) !== null && _a !== void 0 ? _a : ""); },
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (multi) {
|
|
250
|
+
// Host-level liveness for ALB / ECS health checks. Per-agent health stays
|
|
251
|
+
// at `/<slug>/v1/health`; without this, multi-mode has no `/v1/health`.
|
|
252
|
+
router.add({
|
|
253
|
+
method: "GET",
|
|
254
|
+
pattern: "/v1/health",
|
|
255
|
+
auth: "none",
|
|
256
|
+
handler: () => jsonResponse({
|
|
257
|
+
ok: true,
|
|
258
|
+
agents: runtimes.map(({ slug, project }) => ({
|
|
259
|
+
slug,
|
|
260
|
+
name: project.name,
|
|
261
|
+
})),
|
|
262
|
+
}),
|
|
263
|
+
});
|
|
264
|
+
if (playground) {
|
|
265
|
+
const cursorSlackBySlug = new Map(collectCursorSlackTargets(runtimes).map((target) => [
|
|
266
|
+
target.runtime.slug,
|
|
267
|
+
target.agentName,
|
|
268
|
+
]));
|
|
269
|
+
const entries = runtimes.map(({ slug, project }) => {
|
|
270
|
+
var _a;
|
|
271
|
+
var _b;
|
|
272
|
+
const cursorName = cursorSlackBySlug.get(slug);
|
|
273
|
+
const hasSocketSlack = project.channels.some((channel) => { var _a, _b; return ((_a = channel.definition.meta) === null || _a === void 0 ? void 0 : _a.kind) === "slack" &&
|
|
274
|
+
((_b = channel.definition.meta) === null || _b === void 0 ? void 0 : _b.slackCursorAccount) !== true; });
|
|
275
|
+
const slack = cursorName !== undefined
|
|
276
|
+
? {
|
|
277
|
+
kind: "cursor-account",
|
|
278
|
+
handle: `@Cursor ${cursorName}`,
|
|
279
|
+
}
|
|
280
|
+
: hasSocketSlack
|
|
281
|
+
? { kind: "socket" }
|
|
282
|
+
: undefined;
|
|
283
|
+
return Object.assign({ slug, name: project.name, model: typeof project.agent.model === "string"
|
|
284
|
+
? project.agent.model
|
|
285
|
+
: ((_b = (_a = project.agent.model) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : ""), tools: project.agent.tools.length, channels: project.channels.length + 1 }, (slack !== undefined ? { slack } : {}));
|
|
286
|
+
});
|
|
287
|
+
const indexHtml = renderAgentsIndexHtml(entries);
|
|
288
|
+
router.add({
|
|
289
|
+
method: "GET",
|
|
290
|
+
pattern: "/",
|
|
291
|
+
auth: "none",
|
|
292
|
+
handler: () => new Response(indexHtml, {
|
|
293
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
294
|
+
}),
|
|
295
|
+
});
|
|
296
|
+
router.add({
|
|
297
|
+
method: "GET",
|
|
298
|
+
pattern: "/v1/agents",
|
|
299
|
+
auth: "none",
|
|
300
|
+
handler: () => jsonResponse({ agents: entries }),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Shared GitHub webhook ingress: one URL fans out to every mounted agent
|
|
305
|
+
// that declared a github channel. Signature verification is the caller's
|
|
306
|
+
// job (backend `/github_webhook` already verified); we only admit loopback
|
|
307
|
+
// so this route is not a public unsigned endpoint. Agents decide whether
|
|
308
|
+
// to run via their onPullRequest / onComment / … hooks.
|
|
309
|
+
const collectTriggerTargets = () => collectGitHubTriggerTargets({
|
|
310
|
+
mounts: runtimes.map((runtime) => ({
|
|
311
|
+
slug: runtime.slug,
|
|
312
|
+
channels: runtime.project.channels,
|
|
313
|
+
buildHandlerArgs: (channelId) => buildChannelHandlerArgs({ runtime, channelId }),
|
|
314
|
+
})),
|
|
315
|
+
});
|
|
316
|
+
router.add({
|
|
317
|
+
method: "POST",
|
|
318
|
+
pattern: "/v1/webhooks/github",
|
|
319
|
+
auth: [localDevStrict()],
|
|
320
|
+
handler: (request) => __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
var _a;
|
|
322
|
+
var _b, _c;
|
|
323
|
+
const bodyText = yield request.text();
|
|
324
|
+
const result = yield handleAgentServeTrigger({
|
|
325
|
+
type: "github",
|
|
326
|
+
bodyText,
|
|
327
|
+
eventName: request.headers.get("x-github-event"),
|
|
328
|
+
deliveryId: (_b = request.headers.get("x-github-delivery")) !== null && _b !== void 0 ? _b : undefined,
|
|
329
|
+
targets: collectTriggerTargets(),
|
|
330
|
+
});
|
|
331
|
+
const status = result.ok ? 202 : ((_c = (_a = result.targets[0]) === null || _a === void 0 ? void 0 : _a.status) !== null && _c !== void 0 ? _c : 500);
|
|
332
|
+
return jsonResponse(result, { status });
|
|
333
|
+
}),
|
|
334
|
+
});
|
|
335
|
+
for (const { slug, project } of mounts) {
|
|
336
|
+
validateMountConnectionsConfig(Object.assign({ slug,
|
|
337
|
+
project, accountRefs: accountRefsBySlug.get(slug) }, (options.publicUrl === undefined
|
|
338
|
+
? {}
|
|
339
|
+
: { publicUrl: options.publicUrl })));
|
|
340
|
+
}
|
|
341
|
+
// Cursor SCM events relay (opt-in: serve({ cursorEvents }) / --cursor-events).
|
|
342
|
+
const cursorEventRelay = startCursorEventRelay({
|
|
343
|
+
options,
|
|
344
|
+
mounts,
|
|
345
|
+
auth: cursorEventsAuth,
|
|
346
|
+
logger,
|
|
347
|
+
collectTriggerTargets,
|
|
348
|
+
});
|
|
349
|
+
// Cursor Slack relay: automatic when a mounted agent authored
|
|
350
|
+
// `slackChannel({ cursorAccount: true })`. Inbound rides the account's
|
|
351
|
+
// Slack connection (`/v0/slack-events`); the classic Socket-Mode path is
|
|
352
|
+
// untouched for channels with their own Slack app tokens.
|
|
353
|
+
const cursorSlackRelay = yield startCursorSlackRelay({
|
|
354
|
+
options,
|
|
355
|
+
runtimes,
|
|
356
|
+
accountKey,
|
|
357
|
+
logger,
|
|
358
|
+
});
|
|
359
|
+
const server = createServer((req, res) => {
|
|
360
|
+
void handleRequest(router, req, res, logger);
|
|
361
|
+
});
|
|
362
|
+
const host = (_o = options.host) !== null && _o !== void 0 ? _o : "127.0.0.1";
|
|
363
|
+
const port = (_p = options.port) !== null && _p !== void 0 ? _p : 3000;
|
|
364
|
+
server.listen(port, host);
|
|
365
|
+
yield once(server, "listening");
|
|
366
|
+
const address = server.address();
|
|
367
|
+
const displayHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
|
|
368
|
+
const url = `http://${displayHost}:${address.port}`;
|
|
369
|
+
// Resolve peer + Cursor account MCP connections now that the listen port is
|
|
370
|
+
// known (see resolved-connections.ts for the URL/runtime rules).
|
|
371
|
+
for (const runtime of runtimes) {
|
|
372
|
+
const peerRefs = peerRefsBySlug.get(runtime.slug);
|
|
373
|
+
const accountRefs = accountRefsBySlug.get(runtime.slug);
|
|
374
|
+
const resolved = resolveMountConnections(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ slug: runtime.slug, project: runtime.project, serverUrl: url }, (peerRefs === undefined ? {} : { peerRefs })), (accountRefs !== undefined && accountBridge !== undefined
|
|
375
|
+
? { accountRefs, accountBridge }
|
|
376
|
+
: {})), (options.publicUrl === undefined
|
|
377
|
+
? {}
|
|
378
|
+
: { publicUrl: options.publicUrl })), (options.authToken === undefined
|
|
379
|
+
? {}
|
|
380
|
+
: { authToken: options.authToken })), { logger }));
|
|
381
|
+
if (resolved.length > 0) {
|
|
382
|
+
runtime.engine.setResolvedConnections(resolved);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const agents = runtimes.map(({ slug, project }) => ({
|
|
386
|
+
slug,
|
|
387
|
+
project,
|
|
388
|
+
url: slug === "" ? url : `${url}/${slug}`,
|
|
389
|
+
}));
|
|
390
|
+
for (const runtime of runtimes) {
|
|
391
|
+
runtime.evalRuns.loopbackUrl =
|
|
392
|
+
runtime.slug === "" ? url : `${url}/${runtime.slug}`;
|
|
393
|
+
}
|
|
394
|
+
const runtimeBySlug = new Map(runtimes.map((r) => [r.slug, r]));
|
|
395
|
+
// --dev: spawn Vite HMR. Single-agent infers the slug base; multi-agent
|
|
396
|
+
// serves `/<slug>/playground` on the HMR origin (optional AGENT_SERVE_BASE
|
|
397
|
+
// pins one slug at the HMR root instead).
|
|
398
|
+
let playgroundDev;
|
|
399
|
+
if (playground && dev) {
|
|
400
|
+
const pinnedBase = mounts.length === 1
|
|
401
|
+
? playgroundDevBasePath(mounts)
|
|
402
|
+
: playgroundDevBaseFromEnv();
|
|
403
|
+
const multiAgent = mounts.length > 1 && pinnedBase === undefined;
|
|
404
|
+
playgroundDev = startPlaygroundDevServer({
|
|
405
|
+
targetUrl: url,
|
|
406
|
+
basePath: pinnedBase,
|
|
407
|
+
multiAgent,
|
|
408
|
+
logger,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
const handle = {
|
|
412
|
+
url,
|
|
413
|
+
port: address.port,
|
|
414
|
+
project: (_b = runtimes[0]) === null || _b === void 0 ? void 0 : _b.project,
|
|
415
|
+
agents,
|
|
416
|
+
dispatchSchedule: (name, slug) => {
|
|
417
|
+
var _a;
|
|
418
|
+
var _b;
|
|
419
|
+
if (slug === void 0) { slug = (_b = (_a = runtimes[0]) === null || _a === void 0 ? void 0 : _a.slug) !== null && _b !== void 0 ? _b : ""; }
|
|
420
|
+
const runtime = runtimeBySlug.get(slug);
|
|
421
|
+
if (runtime === undefined) {
|
|
422
|
+
throw new Error(`Unknown agent slug "${slug}"`);
|
|
423
|
+
}
|
|
424
|
+
return runtime.scheduleRunner.dispatch(name);
|
|
425
|
+
},
|
|
426
|
+
createReminder: (input, slug) => {
|
|
427
|
+
var _a;
|
|
428
|
+
var _b;
|
|
429
|
+
if (slug === void 0) { slug = (_b = (_a = runtimes[0]) === null || _a === void 0 ? void 0 : _a.slug) !== null && _b !== void 0 ? _b : ""; }
|
|
430
|
+
const runtime = runtimeBySlug.get(slug);
|
|
431
|
+
if (runtime === undefined) {
|
|
432
|
+
throw new Error(`Unknown agent slug "${slug}"`);
|
|
433
|
+
}
|
|
434
|
+
return runtime.reminderRunner.create(input);
|
|
435
|
+
},
|
|
436
|
+
listReminders: (filter, slug) => {
|
|
437
|
+
var _a;
|
|
438
|
+
var _b;
|
|
439
|
+
if (slug === void 0) { slug = (_b = (_a = runtimes[0]) === null || _a === void 0 ? void 0 : _a.slug) !== null && _b !== void 0 ? _b : ""; }
|
|
440
|
+
const runtime = runtimeBySlug.get(slug);
|
|
441
|
+
if (runtime === undefined) {
|
|
442
|
+
throw new Error(`Unknown agent slug "${slug}"`);
|
|
443
|
+
}
|
|
444
|
+
return runtime.reminderRunner.list(filter);
|
|
445
|
+
},
|
|
446
|
+
cancelReminder: (id, options) => {
|
|
447
|
+
var _a;
|
|
448
|
+
var _b, _c;
|
|
449
|
+
const slug = (_c = (_b = options === null || options === void 0 ? void 0 : options.slug) !== null && _b !== void 0 ? _b : (_a = runtimes[0]) === null || _a === void 0 ? void 0 : _a.slug) !== null && _c !== void 0 ? _c : "";
|
|
450
|
+
const runtime = runtimeBySlug.get(slug);
|
|
451
|
+
if (runtime === undefined) {
|
|
452
|
+
throw new Error(`Unknown agent slug "${slug}"`);
|
|
453
|
+
}
|
|
454
|
+
return runtime.reminderRunner.cancel(id, options === null || options === void 0 ? void 0 : options.reason);
|
|
455
|
+
},
|
|
456
|
+
dispatchReminder: (id, slug) => {
|
|
457
|
+
var _a;
|
|
458
|
+
var _b;
|
|
459
|
+
if (slug === void 0) { slug = (_b = (_a = runtimes[0]) === null || _a === void 0 ? void 0 : _a.slug) !== null && _b !== void 0 ? _b : ""; }
|
|
460
|
+
const runtime = runtimeBySlug.get(slug);
|
|
461
|
+
if (runtime === undefined) {
|
|
462
|
+
throw new Error(`Unknown agent slug "${slug}"`);
|
|
463
|
+
}
|
|
464
|
+
return runtime.reminderRunner.dispatch(id);
|
|
465
|
+
},
|
|
466
|
+
callTool: (toolName, input, options) => {
|
|
467
|
+
var _a;
|
|
468
|
+
var _b;
|
|
469
|
+
const _c = options !== null && options !== void 0 ? options : {}, { slug } = _c, callOptions = __rest(_c, ["slug"]);
|
|
470
|
+
const runtime = runtimeBySlug.get((_b = slug !== null && slug !== void 0 ? slug : (_a = runtimes[0]) === null || _a === void 0 ? void 0 : _a.slug) !== null && _b !== void 0 ? _b : "");
|
|
471
|
+
if (runtime === undefined) {
|
|
472
|
+
throw new Error(`Unknown agent slug "${slug}"`);
|
|
473
|
+
}
|
|
474
|
+
return runtime.engine.callTool(toolName, input, callOptions);
|
|
475
|
+
},
|
|
476
|
+
close: () => __awaiter(this, void 0, void 0, function* () {
|
|
477
|
+
var _a;
|
|
478
|
+
playgroundDev === null || playgroundDev === void 0 ? void 0 : playgroundDev.stop();
|
|
479
|
+
yield cursorEventRelay === null || cursorEventRelay === void 0 ? void 0 : cursorEventRelay.stop();
|
|
480
|
+
yield cursorSlackRelay === null || cursorSlackRelay === void 0 ? void 0 : cursorSlackRelay.stop();
|
|
481
|
+
for (const runtime of runtimes) {
|
|
482
|
+
runtime.scheduleRunner.stop();
|
|
483
|
+
runtime.reminderRunner.stop();
|
|
484
|
+
}
|
|
485
|
+
for (const stop of channelStopHooks) {
|
|
486
|
+
try {
|
|
487
|
+
yield stop();
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
logger(`[agent-serve] channel onStop failed: ${error instanceof Error
|
|
491
|
+
? ((_a = error.stack) !== null && _a !== void 0 ? _a : error.message)
|
|
492
|
+
: String(error)}`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
server.close();
|
|
496
|
+
server.closeAllConnections();
|
|
497
|
+
yield Promise.all(runtimes.map((runtime) => runtime.engine.close()));
|
|
498
|
+
}),
|
|
499
|
+
};
|
|
500
|
+
if ((playgroundDev === null || playgroundDev === void 0 ? void 0 : playgroundDev.url) !== undefined) {
|
|
501
|
+
handle.playgroundDevUrl = playgroundDev.url;
|
|
502
|
+
}
|
|
503
|
+
if (docsSite && docsSiteExists()) {
|
|
504
|
+
handle.docsUrl = `${handle.url}/docs`;
|
|
505
|
+
}
|
|
506
|
+
return handle;
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
function mountAgentRoutes(router, runtime, args) {
|
|
510
|
+
var _a, _b, _c;
|
|
511
|
+
const { slug, project, engine, scheduleRunner, reminderRunner } = runtime;
|
|
512
|
+
const { dev, playground, multi, logger, logRing } = args;
|
|
513
|
+
// "" (single) -> "/v1/..."; "my-agent" (multi) -> "/my-agent/v1/...".
|
|
514
|
+
const base = slug === "" ? "" : `/${slug}`;
|
|
515
|
+
// The Cursor account MCP bridge for this mount's account MCP connections:
|
|
516
|
+
// loopback-only + per-boot bearer secret, never on the public auth chain.
|
|
517
|
+
if (args.accountBridge !== undefined && args.accountRefs !== undefined) {
|
|
518
|
+
const bridge = args.accountBridge;
|
|
519
|
+
const refByName = new Map(args.accountRefs.map((ref) => [ref.name, ref]));
|
|
520
|
+
router.add({
|
|
521
|
+
method: "POST",
|
|
522
|
+
pattern: `${base}${cursorAccountMcpPath(":connection")}`,
|
|
523
|
+
auth: [bridge.authPolicy()],
|
|
524
|
+
handler: (request, ctx) => {
|
|
525
|
+
var _a, _b;
|
|
526
|
+
const ref = refByName.get((_a = ctx.params.connection) !== null && _a !== void 0 ? _a : "");
|
|
527
|
+
if (ref === undefined) {
|
|
528
|
+
return errorResponse(404, "not_found", `No Cursor account MCP connection named "${(_b = ctx.params.connection) !== null && _b !== void 0 ? _b : ""}" on this agent.`);
|
|
529
|
+
}
|
|
530
|
+
return bridge.handle(request, ref);
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
const defaultAuth = args.authToken !== undefined
|
|
535
|
+
? [bearerAuth(args.authToken)]
|
|
536
|
+
: args.allowAnonymous === true
|
|
537
|
+
? [allowAll()]
|
|
538
|
+
: [localDevStrict()];
|
|
539
|
+
const httpAuth = (_a = project.httpChannel.auth) !== null && _a !== void 0 ? _a : defaultAuth;
|
|
540
|
+
// Playground lists/opens Slack (and other channel) sessions across
|
|
541
|
+
// principals when `--dev` (loopback) or `--allow-anonymous` (trusted
|
|
542
|
+
// shared host, e.g. corp Okta ALB). Never when bearer / custom channel
|
|
543
|
+
// auth is set — those keep strict per-principal isolation.
|
|
544
|
+
const relaxSessionReadAuth = args.authToken === undefined &&
|
|
545
|
+
project.httpChannel.auth === undefined &&
|
|
546
|
+
(dev || args.allowAnonymous === true);
|
|
547
|
+
const relaxSessionReadAuthBeyondLoopback = args.allowAnonymous === true;
|
|
548
|
+
// Separate from read-relax: playground Approve/Deny on Slack-owned parked
|
|
549
|
+
// tools (Slack principals never match local/anonymous HTTP auth).
|
|
550
|
+
const relaxSessionApprovalAuth = relaxSessionReadAuth;
|
|
551
|
+
router.add({
|
|
552
|
+
method: "GET",
|
|
553
|
+
pattern: `${base}/v1/health`,
|
|
554
|
+
auth: "none",
|
|
555
|
+
handler: () => jsonResponse({ ok: true, name: project.name }),
|
|
556
|
+
});
|
|
557
|
+
// Bootstrap the SPA can't derive from its own URL (agent name, dev mode, and
|
|
558
|
+
// — in multi-agent mode — where the agents index lives).
|
|
559
|
+
router.add({
|
|
560
|
+
method: "GET",
|
|
561
|
+
pattern: `${base}/v1/meta`,
|
|
562
|
+
auth: "none",
|
|
563
|
+
handler: () => jsonResponse(Object.assign({ name: project.name, dev, basePath: base }, (multi ? { indexPath: "/" } : {}))),
|
|
564
|
+
});
|
|
565
|
+
router.add({
|
|
566
|
+
method: "GET",
|
|
567
|
+
pattern: `${base}/v1/info`,
|
|
568
|
+
auth: httpAuth,
|
|
569
|
+
handler: () => jsonResponse(projectInfo(project)),
|
|
570
|
+
});
|
|
571
|
+
// Playground Logs tab: recent ServeOptions.logger lines (channels, sessions,
|
|
572
|
+
// webhook capture). `after` is an exclusive index cursor for polling.
|
|
573
|
+
router.add({
|
|
574
|
+
method: "GET",
|
|
575
|
+
pattern: `${base}/v1/logs`,
|
|
576
|
+
auth: httpAuth,
|
|
577
|
+
handler: (request) => {
|
|
578
|
+
var _a;
|
|
579
|
+
var _b;
|
|
580
|
+
const url = new URL(request.url);
|
|
581
|
+
const afterRaw = url.searchParams.get("after");
|
|
582
|
+
const after = afterRaw === null || afterRaw === ""
|
|
583
|
+
? -1
|
|
584
|
+
: Number.parseInt(afterRaw, 10);
|
|
585
|
+
const cursor = Number.isFinite(after) ? after : -1;
|
|
586
|
+
const entries = logRing.since(cursor);
|
|
587
|
+
return jsonResponse({
|
|
588
|
+
entries,
|
|
589
|
+
nextAfter: entries.length === 0
|
|
590
|
+
? cursor
|
|
591
|
+
: ((_b = (_a = entries[entries.length - 1]) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : cursor),
|
|
592
|
+
});
|
|
593
|
+
},
|
|
594
|
+
});
|
|
595
|
+
router.add({
|
|
596
|
+
method: "GET",
|
|
597
|
+
pattern: `${base}/v1/sessions`,
|
|
598
|
+
auth: httpAuth,
|
|
599
|
+
handler: (_request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
600
|
+
// Same gate as requireSessionOwner / relaxSessionReadAuth: never with
|
|
601
|
+
// bearer or custom channel auth (strict per-principal isolation).
|
|
602
|
+
// `--allow-anonymous` alone is a shared-principal trust boundary (corp
|
|
603
|
+
// Okta ALB); `--dev` alone stays loopback-only.
|
|
604
|
+
const includeAll = relaxSessionReadAuth &&
|
|
605
|
+
(relaxSessionReadAuthBeyondLoopback ||
|
|
606
|
+
(typeof ctx.requestIp === "string" &&
|
|
607
|
+
isLoopbackAddress(ctx.requestIp)));
|
|
608
|
+
return jsonResponse({
|
|
609
|
+
sessions: yield engine.listSessions(ctx.auth, { includeAll }),
|
|
610
|
+
});
|
|
611
|
+
}),
|
|
612
|
+
});
|
|
613
|
+
// Playground A/Bs tab: fold durable event streams into per-session
|
|
614
|
+
// assignments and aggregate arm metrics (built-ins + derive).
|
|
615
|
+
router.add({
|
|
616
|
+
method: "GET",
|
|
617
|
+
pattern: `${base}/v1/abs`,
|
|
618
|
+
auth: httpAuth,
|
|
619
|
+
handler: (_request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
620
|
+
const includeAll = dev === true &&
|
|
621
|
+
(args.allowAnonymous === true ||
|
|
622
|
+
(typeof ctx.requestIp === "string" &&
|
|
623
|
+
isLoopbackAddress(ctx.requestIp)));
|
|
624
|
+
return jsonResponse(yield engine.abSnapshot(ctx.auth, { includeAll }));
|
|
625
|
+
}),
|
|
626
|
+
});
|
|
627
|
+
// The agent's MCP surface: peers and external MCP clients delegate to
|
|
628
|
+
// this agent (ask / check / call_tool) over streamable HTTP. Stateless —
|
|
629
|
+
// POST only; GET/DELETE get a spec-compliant 405 from the endpoint.
|
|
630
|
+
const mcpEndpoint = createAgentMcpEndpoint({ engine, logger });
|
|
631
|
+
for (const method of ["POST", "GET", "DELETE"]) {
|
|
632
|
+
router.add({
|
|
633
|
+
method,
|
|
634
|
+
pattern: `${base}${MCP_ENDPOINT_PATH}`,
|
|
635
|
+
auth: httpAuth,
|
|
636
|
+
handler: (request, ctx) => mcpEndpoint.handle(request, ctx.auth),
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
// Deterministic tool invocation: run an authored server tool directly,
|
|
640
|
+
// without a model turn deciding to call it.
|
|
641
|
+
router.add({
|
|
642
|
+
method: "POST",
|
|
643
|
+
pattern: `${base}/v1/tools/:toolName`,
|
|
644
|
+
auth: httpAuth,
|
|
645
|
+
handler: (request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
646
|
+
var _a, _b;
|
|
647
|
+
const toolName = (_a = ctx.params.toolName) !== null && _a !== void 0 ? _a : "";
|
|
648
|
+
const body = yield readJsonObject(request);
|
|
649
|
+
if (body === undefined) {
|
|
650
|
+
return errorResponse(400, "invalid_request", 'Expected a JSON object body, e.g. {"input": {...}}.');
|
|
651
|
+
}
|
|
652
|
+
if (body.sessionId !== undefined && typeof body.sessionId !== "string") {
|
|
653
|
+
return errorResponse(400, "invalid_request", "`sessionId` must be a string when provided.");
|
|
654
|
+
}
|
|
655
|
+
const sessionId = body.sessionId;
|
|
656
|
+
if (sessionId !== undefined) {
|
|
657
|
+
const record = yield engine.sessions.get(sessionId);
|
|
658
|
+
if (record === undefined) {
|
|
659
|
+
return errorResponse(404, "session_not_found", `Unknown session ${sessionId}.`);
|
|
660
|
+
}
|
|
661
|
+
const ownership = requireSessionOwner(record, ctx.auth, {
|
|
662
|
+
relax: relaxSessionReadAuth,
|
|
663
|
+
beyondLoopback: relaxSessionReadAuthBeyondLoopback,
|
|
664
|
+
requestIp: ctx.requestIp,
|
|
665
|
+
});
|
|
666
|
+
if (ownership !== undefined) {
|
|
667
|
+
return ownership;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
try {
|
|
671
|
+
const outcome = yield engine.callTool(toolName, (_b = body.input) !== null && _b !== void 0 ? _b : {}, Object.assign({ auth: ctx.auth }, (sessionId === undefined ? {} : { sessionId })));
|
|
672
|
+
return jsonResponse(Object.assign({ ok: !outcome.isError }, outcome));
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
if (error instanceof UnknownToolError) {
|
|
676
|
+
return jsonResponse({
|
|
677
|
+
ok: false,
|
|
678
|
+
error: "unknown_tool",
|
|
679
|
+
message: error.message,
|
|
680
|
+
availableTools: error.availableToolNames,
|
|
681
|
+
}, { status: 404 });
|
|
682
|
+
}
|
|
683
|
+
if (error instanceof ToolNotDirectlyCallableError) {
|
|
684
|
+
return errorResponse(400, "tool_not_callable", error.message);
|
|
685
|
+
}
|
|
686
|
+
if (error instanceof ToolInputError) {
|
|
687
|
+
return errorResponse(400, "invalid_tool_input", error.message);
|
|
688
|
+
}
|
|
689
|
+
throw error;
|
|
690
|
+
}
|
|
691
|
+
}),
|
|
692
|
+
});
|
|
693
|
+
if (playground) {
|
|
694
|
+
// Serve the built Vite SPA. `<base href>` is injected so the SPA's relative
|
|
695
|
+
// asset URLs resolve under this mount regardless of the trailing slash the
|
|
696
|
+
// browser lands on. Render per request so a mid-run `build:playground`
|
|
697
|
+
// is picked up without restarting. In dev, run `pnpm run dev:playground`
|
|
698
|
+
// (Vite + HMR) instead; it proxies `/v1` here.
|
|
699
|
+
router.add({
|
|
700
|
+
method: "GET",
|
|
701
|
+
pattern: `${base}/playground`,
|
|
702
|
+
auth: "none",
|
|
703
|
+
handler: () => new Response(renderPlaygroundIndexHtml(`${base}/playground/`), {
|
|
704
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
705
|
+
}),
|
|
706
|
+
});
|
|
707
|
+
router.add({
|
|
708
|
+
method: "GET",
|
|
709
|
+
pattern: `${base}/playground/assets/:file`,
|
|
710
|
+
auth: "none",
|
|
711
|
+
handler: (_request, ctx) => { var _a; return renderPlaygroundAsset((_a = ctx.params.file) !== null && _a !== void 0 ? _a : ""); },
|
|
712
|
+
});
|
|
713
|
+
if (!multi) {
|
|
714
|
+
router.add({
|
|
715
|
+
method: "GET",
|
|
716
|
+
pattern: "/",
|
|
717
|
+
auth: "none",
|
|
718
|
+
handler: () => new Response(null, {
|
|
719
|
+
status: 302,
|
|
720
|
+
headers: { location: "/playground" },
|
|
721
|
+
}),
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
for (const route of buildHttpChannelRoutes({
|
|
726
|
+
engine,
|
|
727
|
+
config: project.httpChannel,
|
|
728
|
+
auth: httpAuth,
|
|
729
|
+
basePath: base,
|
|
730
|
+
// Dev playground lists every session (`includeAll`); allow opening them
|
|
731
|
+
// (see relaxSessionReadAuth above).
|
|
732
|
+
relaxSessionReadAuth,
|
|
733
|
+
relaxSessionReadAuthBeyondLoopback,
|
|
734
|
+
relaxSessionApprovalAuth,
|
|
735
|
+
})) {
|
|
736
|
+
router.add(route);
|
|
737
|
+
}
|
|
738
|
+
// Playground eval list/run — available without `--dev` (corp / allow-anonymous).
|
|
739
|
+
// Schedule/reminder hand-dispatch stays `--dev` only below.
|
|
740
|
+
router.add({
|
|
741
|
+
method: "GET",
|
|
742
|
+
pattern: `${base}/v1/dev/evals`,
|
|
743
|
+
auth: httpAuth,
|
|
744
|
+
handler: () => __awaiter(this, void 0, void 0, function* () { return jsonResponse(yield runtime.evalRuns.list()); }),
|
|
745
|
+
});
|
|
746
|
+
router.add({
|
|
747
|
+
method: "GET",
|
|
748
|
+
pattern: `${base}/v1/dev/evals/runs`,
|
|
749
|
+
auth: httpAuth,
|
|
750
|
+
handler: () => __awaiter(this, void 0, void 0, function* () {
|
|
751
|
+
return jsonResponse(yield runtime.evalRuns.listRunsWithActive());
|
|
752
|
+
}),
|
|
753
|
+
});
|
|
754
|
+
router.add({
|
|
755
|
+
method: "POST",
|
|
756
|
+
pattern: `${base}/v1/dev/evals/runs`,
|
|
757
|
+
auth: httpAuth,
|
|
758
|
+
handler: (request) => __awaiter(this, void 0, void 0, function* () {
|
|
759
|
+
const body = yield readJsonObject(request);
|
|
760
|
+
const filterIds = asStringArray(body === null || body === void 0 ? void 0 : body.filterIds);
|
|
761
|
+
const tags = asStringArray(body === null || body === void 0 ? void 0 : body.tags);
|
|
762
|
+
const authHeader = request.headers.get("authorization");
|
|
763
|
+
const bearerToken = (authHeader === null || authHeader === void 0 ? void 0 : authHeader.toLowerCase().startsWith("bearer "))
|
|
764
|
+
? authHeader.slice("bearer ".length).trim()
|
|
765
|
+
: undefined;
|
|
766
|
+
try {
|
|
767
|
+
const snap = yield runtime.evalRuns.start(Object.assign(Object.assign(Object.assign({}, (filterIds === undefined ? {} : { filterIds })), (tags === undefined ? {} : { tags })), (bearerToken === undefined || bearerToken === ""
|
|
768
|
+
? {}
|
|
769
|
+
: { bearerToken })));
|
|
770
|
+
return jsonResponse(snap, { status: 202 });
|
|
771
|
+
}
|
|
772
|
+
catch (error) {
|
|
773
|
+
if (error instanceof EvalRunConflictError) {
|
|
774
|
+
return jsonResponse({
|
|
775
|
+
ok: false,
|
|
776
|
+
error: "eval_run_in_progress",
|
|
777
|
+
activeRunId: error.activeRunId,
|
|
778
|
+
}, { status: 409 });
|
|
779
|
+
}
|
|
780
|
+
if (error instanceof EvalRunEmptyError) {
|
|
781
|
+
return jsonResponse({ ok: false, error: "no_matching_evals" }, { status: 404 });
|
|
782
|
+
}
|
|
783
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
784
|
+
return jsonResponse({ ok: false, error: "eval_start_failed", message }, { status: 400 });
|
|
785
|
+
}
|
|
786
|
+
}),
|
|
787
|
+
});
|
|
788
|
+
router.add({
|
|
789
|
+
method: "GET",
|
|
790
|
+
pattern: `${base}/v1/dev/evals/runs/:runId`,
|
|
791
|
+
auth: httpAuth,
|
|
792
|
+
handler: (_request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
793
|
+
var _a;
|
|
794
|
+
const runId = (_a = ctx.params.runId) !== null && _a !== void 0 ? _a : "";
|
|
795
|
+
const snap = yield runtime.evalRuns.get(runId);
|
|
796
|
+
if (snap === undefined) {
|
|
797
|
+
return jsonResponse({ ok: false, error: "unknown_eval_run" }, { status: 404 });
|
|
798
|
+
}
|
|
799
|
+
return jsonResponse(snap);
|
|
800
|
+
}),
|
|
801
|
+
});
|
|
802
|
+
if (dev) {
|
|
803
|
+
router.add({
|
|
804
|
+
method: "POST",
|
|
805
|
+
pattern: `${base}/v1/dev/schedules/:scheduleId`,
|
|
806
|
+
auth: httpAuth,
|
|
807
|
+
handler: (_request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
808
|
+
var _a;
|
|
809
|
+
const scheduleId = (_a = ctx.params.scheduleId) !== null && _a !== void 0 ? _a : "";
|
|
810
|
+
try {
|
|
811
|
+
const { sessionIds } = yield scheduleRunner.dispatch(scheduleId);
|
|
812
|
+
return jsonResponse({ scheduleId, sessionIds });
|
|
813
|
+
}
|
|
814
|
+
catch (error) {
|
|
815
|
+
if (error instanceof UnknownScheduleError) {
|
|
816
|
+
return jsonResponse({
|
|
817
|
+
ok: false,
|
|
818
|
+
error: "unknown_schedule",
|
|
819
|
+
availableScheduleIds: error.availableScheduleIds,
|
|
820
|
+
}, { status: 404 });
|
|
821
|
+
}
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
}),
|
|
825
|
+
});
|
|
826
|
+
router.add({
|
|
827
|
+
method: "POST",
|
|
828
|
+
pattern: `${base}/v1/dev/reminders/:reminderId`,
|
|
829
|
+
auth: httpAuth,
|
|
830
|
+
handler: (_request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
831
|
+
var _a;
|
|
832
|
+
const reminderId = (_a = ctx.params.reminderId) !== null && _a !== void 0 ? _a : "";
|
|
833
|
+
try {
|
|
834
|
+
const listed = yield reminderRunner.list();
|
|
835
|
+
const reminder = listed.find((r) => r.id === reminderId);
|
|
836
|
+
if (reminder === undefined) {
|
|
837
|
+
const ids = listed.map((r) => r.id);
|
|
838
|
+
throw new UnknownReminderError(reminderId, ids);
|
|
839
|
+
}
|
|
840
|
+
const ownership = yield assertReminderReadable({
|
|
841
|
+
engine,
|
|
842
|
+
reminder,
|
|
843
|
+
auth: ctx.auth,
|
|
844
|
+
requestIp: ctx.requestIp,
|
|
845
|
+
relax: relaxSessionReadAuth,
|
|
846
|
+
beyondLoopback: relaxSessionReadAuthBeyondLoopback,
|
|
847
|
+
});
|
|
848
|
+
if (ownership !== undefined) {
|
|
849
|
+
return ownership;
|
|
850
|
+
}
|
|
851
|
+
const result = yield reminderRunner.dispatch(reminderId);
|
|
852
|
+
return jsonResponse(result);
|
|
853
|
+
}
|
|
854
|
+
catch (error) {
|
|
855
|
+
if (error instanceof UnknownReminderError) {
|
|
856
|
+
return jsonResponse({
|
|
857
|
+
ok: false,
|
|
858
|
+
error: "unknown_reminder",
|
|
859
|
+
availableReminderIds: error.availableReminderIds,
|
|
860
|
+
}, { status: 404 });
|
|
861
|
+
}
|
|
862
|
+
if (error instanceof ReminderValidationError) {
|
|
863
|
+
return jsonResponse({ ok: false, error: "invalid_reminder", message: error.message }, { status: 400 });
|
|
864
|
+
}
|
|
865
|
+
throw error;
|
|
866
|
+
}
|
|
867
|
+
}),
|
|
868
|
+
});
|
|
869
|
+
router.add({
|
|
870
|
+
method: "GET",
|
|
871
|
+
pattern: `${base}/v1/dev/reminders`,
|
|
872
|
+
auth: httpAuth,
|
|
873
|
+
handler: (_request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
874
|
+
const all = yield reminderRunner.list();
|
|
875
|
+
const reminders = [];
|
|
876
|
+
for (const reminder of all) {
|
|
877
|
+
const denied = yield assertReminderReadable({
|
|
878
|
+
engine,
|
|
879
|
+
reminder,
|
|
880
|
+
auth: ctx.auth,
|
|
881
|
+
requestIp: ctx.requestIp,
|
|
882
|
+
relax: relaxSessionReadAuth,
|
|
883
|
+
beyondLoopback: relaxSessionReadAuthBeyondLoopback,
|
|
884
|
+
});
|
|
885
|
+
if (denied === undefined) {
|
|
886
|
+
reminders.push(reminder);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return jsonResponse({ reminders });
|
|
890
|
+
}),
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
for (const channel of project.channels) {
|
|
894
|
+
const channelAuth = (_b = channel.definition.auth) !== null && _b !== void 0 ? _b : defaultAuth;
|
|
895
|
+
for (const channelRoute of (_c = channel.definition.routes) !== null && _c !== void 0 ? _c : []) {
|
|
896
|
+
router.add({
|
|
897
|
+
method: channelRoute.method,
|
|
898
|
+
pattern: `${base}/v1/channels/${channel.id}${channelRoute.path}`,
|
|
899
|
+
auth: channelAuth,
|
|
900
|
+
handler: (request, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
901
|
+
const handlerArgs = buildChannelHandlerArgs({
|
|
902
|
+
runtime,
|
|
903
|
+
channelId: channel.id,
|
|
904
|
+
params: ctx.params,
|
|
905
|
+
requestIp: ctx.requestIp,
|
|
906
|
+
auth: ctx.auth,
|
|
907
|
+
});
|
|
908
|
+
if (channelRoute.querySchema !== undefined) {
|
|
909
|
+
const rawQuery = Object.fromEntries(new URL(request.url).searchParams.entries());
|
|
910
|
+
const queryValidation = validateToolInput(channelRoute.querySchema, rawQuery);
|
|
911
|
+
if (!queryValidation.ok) {
|
|
912
|
+
return jsonResponse({ error: queryValidation.message }, { status: 400 });
|
|
913
|
+
}
|
|
914
|
+
handlerArgs.query = queryValidation.value;
|
|
915
|
+
}
|
|
916
|
+
if (channelRoute.bodySchema !== undefined) {
|
|
917
|
+
// Buffer once so handlers that need the raw body (e.g. GitHub
|
|
918
|
+
// HMAC) can still read `request.text()` / `request.json()`.
|
|
919
|
+
let bodyText;
|
|
920
|
+
try {
|
|
921
|
+
bodyText = yield request.text();
|
|
922
|
+
}
|
|
923
|
+
catch (_a) {
|
|
924
|
+
return jsonResponse({ error: "Failed to read request body" }, { status: 400 });
|
|
925
|
+
}
|
|
926
|
+
let rawBody;
|
|
927
|
+
try {
|
|
928
|
+
rawBody = bodyText === "" ? {} : JSON.parse(bodyText);
|
|
929
|
+
}
|
|
930
|
+
catch (_b) {
|
|
931
|
+
return jsonResponse({ error: "Request body must be valid JSON" }, { status: 400 });
|
|
932
|
+
}
|
|
933
|
+
const bodyValidation = validateToolInput(channelRoute.bodySchema, rawBody);
|
|
934
|
+
if (!bodyValidation.ok) {
|
|
935
|
+
return jsonResponse({ error: bodyValidation.message }, { status: 400 });
|
|
936
|
+
}
|
|
937
|
+
handlerArgs.body = bodyValidation.value;
|
|
938
|
+
request = new Request(request.url, {
|
|
939
|
+
method: request.method,
|
|
940
|
+
headers: request.headers,
|
|
941
|
+
body: bodyText === "" ? undefined : bodyText,
|
|
942
|
+
// node: undici / fetch may require duplex for streamed bodies;
|
|
943
|
+
// a string body does not.
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
return channelRoute.handler(request, handlerArgs);
|
|
947
|
+
}),
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* `cursorEvents` (`--cursor-events`) long-polls `/v0/scm-events` as the host's
|
|
954
|
+
* Cursor user, so a signed-out host has no way to receive deliveries. Fail the
|
|
955
|
+
* serve instead of starting with a relay that could never run.
|
|
956
|
+
*/
|
|
957
|
+
function requireCursorEventsAuth(config, accountKey) {
|
|
958
|
+
var _a, _b, _c, _d;
|
|
959
|
+
const apiKey = ((_b = (_a = config.apiKey) !== null && _a !== void 0 ? _a : accountKey === null || accountKey === void 0 ? void 0 : accountKey.apiKey) !== null && _b !== void 0 ? _b : "").trim();
|
|
960
|
+
if (apiKey === "") {
|
|
961
|
+
throw new Error(`[agent-serve] --cursor-events (serve({ cursorEvents })) requires a signed-in Cursor account: the SCM event stream is read as the host's user. ${SIGN_IN_HINT}`);
|
|
962
|
+
}
|
|
963
|
+
return {
|
|
964
|
+
apiKey,
|
|
965
|
+
baseUrl: (_d = (_c = config.baseUrl) !== null && _c !== void 0 ? _c : accountKey === null || accountKey === void 0 ? void 0 : accountKey.backendUrl) !== null && _d !== void 0 ? _d : cursorBackendUrl(),
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Opt-in Cursor SCM events relay. Credentials are already required by
|
|
970
|
+
* {@link requireCursorEventsAuth}; the remaining unusable configs (no repos,
|
|
971
|
+
* no github channel) warn and leave the relay off — serve still starts.
|
|
972
|
+
*/
|
|
973
|
+
function startCursorEventRelay(input) {
|
|
974
|
+
var _a;
|
|
975
|
+
var _b, _c;
|
|
976
|
+
const { options, mounts, auth, logger, collectTriggerTargets } = input;
|
|
977
|
+
const config = options.cursorEvents;
|
|
978
|
+
if (config === undefined || auth === undefined) {
|
|
979
|
+
return undefined;
|
|
980
|
+
}
|
|
981
|
+
const repos = [...new Set(config.repos.map((repo) => repo.trim()))].filter((repo) => repo.length > 0);
|
|
982
|
+
if (repos.length === 0) {
|
|
983
|
+
logger("[agent-serve] cursor-events: no repos configured (pass --repo owner/name); relay disabled");
|
|
984
|
+
return undefined;
|
|
985
|
+
}
|
|
986
|
+
const githubTargets = findGitHubChannelTargets(mounts);
|
|
987
|
+
const events = [...new Set(githubTargets.flatMap((target) => target.events))];
|
|
988
|
+
if (events.length === 0) {
|
|
989
|
+
logger("[agent-serve] cursor-events: no mounted agent declares a github channel; relay disabled");
|
|
990
|
+
return undefined;
|
|
991
|
+
}
|
|
992
|
+
const baseStateRoot = (_b = options.stateRoot) !== null && _b !== void 0 ? _b : join((_c = (_a = mounts[0]) === null || _a === void 0 ? void 0 : _a.project.rootDir) !== null && _c !== void 0 ? _c : process.cwd(), ".agent-serve");
|
|
993
|
+
const relay = new CursorEventRelay({
|
|
994
|
+
baseUrl: auth.baseUrl,
|
|
995
|
+
apiKey: auth.apiKey,
|
|
996
|
+
repos,
|
|
997
|
+
events,
|
|
998
|
+
stateDir: join(baseStateRoot, "cursor-events"),
|
|
999
|
+
logger,
|
|
1000
|
+
// Stream is compact metadata (Sand-style); stub a GitHub body from the
|
|
1001
|
+
// envelope — rich pullRequest meta when present for onPullRequest Zod.
|
|
1002
|
+
dispatch: (event) => __awaiter(this, void 0, void 0, function* () {
|
|
1003
|
+
if (event.provider !== "github")
|
|
1004
|
+
return;
|
|
1005
|
+
const result = yield handleAgentServeTrigger({
|
|
1006
|
+
type: "github",
|
|
1007
|
+
bodyText: JSON.stringify(scmMetadataToGitHubPayload(event)),
|
|
1008
|
+
eventName: event.eventName,
|
|
1009
|
+
deliveryId: event.deliveryId,
|
|
1010
|
+
targets: collectTriggerTargets(),
|
|
1011
|
+
});
|
|
1012
|
+
const outcomes = result.targets
|
|
1013
|
+
.map((target) => `${target.slug || "default"}:${target.status}`)
|
|
1014
|
+
.join(", ");
|
|
1015
|
+
logger(`[agent-serve] cursor-events: dispatched ${formatCursorScmEventLog(event)}${outcomes === "" ? "" : ` → ${outcomes}`}`);
|
|
1016
|
+
}),
|
|
1017
|
+
});
|
|
1018
|
+
relay.start();
|
|
1019
|
+
logger(`[agent-serve] cursor-events: relaying ${events.length} event kind(s) for ${repos.join(", ")} from ${auth.baseUrl}`);
|
|
1020
|
+
return relay;
|
|
1021
|
+
}
|
|
1022
|
+
/** `weather-agent` → `Weather Agent` (Slack display name default). */
|
|
1023
|
+
function slackAgentNameFromSlug(slug) {
|
|
1024
|
+
return slug
|
|
1025
|
+
.split(/[-_\s]+/)
|
|
1026
|
+
.filter((word) => word.length > 0)
|
|
1027
|
+
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
1028
|
+
.join(" ");
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Mounted Slack channels riding the Cursor account connection
|
|
1032
|
+
* (`slackChannel({ cursorAccount: true })`), with their registered names
|
|
1033
|
+
* resolved. Duplicate names are a startup error — the backend routes
|
|
1034
|
+
* `@Cursor <name> …` by name, so two mounts cannot share one.
|
|
1035
|
+
*/
|
|
1036
|
+
function collectCursorSlackTargets(runtimes) {
|
|
1037
|
+
var _a;
|
|
1038
|
+
const targets = [];
|
|
1039
|
+
const byName = new Map();
|
|
1040
|
+
for (const runtime of runtimes) {
|
|
1041
|
+
for (const channel of runtime.project.channels) {
|
|
1042
|
+
const accountRuntime = getSlackCursorAccountRuntime(channel.definition);
|
|
1043
|
+
if (accountRuntime === undefined) {
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
const agentName = ((_a = accountRuntime.options.agentName) !== null && _a !== void 0 ? _a : slackAgentNameFromSlug(runtime.slug === "" ? runtime.project.name : runtime.slug)).trim();
|
|
1047
|
+
if (agentName === "") {
|
|
1048
|
+
throw new Error(`slackChannel({ cursorAccount: true }) on agent "${runtime.slug || runtime.project.name}" resolved an empty agent name; set agentName.`);
|
|
1049
|
+
}
|
|
1050
|
+
const nameKey = agentName.toLowerCase();
|
|
1051
|
+
const existing = byName.get(nameKey);
|
|
1052
|
+
if (existing !== undefined) {
|
|
1053
|
+
throw new Error(`Two agents register the Slack name "${agentName}" (${existing} and ${runtime.slug || runtime.project.name}); set agentName to disambiguate.`);
|
|
1054
|
+
}
|
|
1055
|
+
byName.set(nameKey, runtime.slug || runtime.project.name);
|
|
1056
|
+
targets.push({
|
|
1057
|
+
runtime,
|
|
1058
|
+
channelId: channel.id,
|
|
1059
|
+
agentName,
|
|
1060
|
+
accountRuntime,
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return targets;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Automatic Cursor Slack relay for cursor-account Slack channels: registers
|
|
1068
|
+
* the mounted agents with `/v0/slack-events`, binds each channel's outbound
|
|
1069
|
+
* API transport to the backend proxy, and dispatches relayed envelopes into
|
|
1070
|
+
* the pack's normal inbound path. Requires a signed-in host — fail serve
|
|
1071
|
+
* fast rather than mounting channels that can never hear or speak.
|
|
1072
|
+
*/
|
|
1073
|
+
function startCursorSlackRelay(input) {
|
|
1074
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1075
|
+
var _a, _b;
|
|
1076
|
+
const { options, runtimes, accountKey, logger } = input;
|
|
1077
|
+
const targets = collectCursorSlackTargets(runtimes);
|
|
1078
|
+
if (targets.length === 0) {
|
|
1079
|
+
return undefined;
|
|
1080
|
+
}
|
|
1081
|
+
if (accountKey === undefined) {
|
|
1082
|
+
throw new Error(`This project has Slack channels using the Cursor account connection (slackChannel({ cursorAccount: true })) but the host is not signed in to Cursor. ${SIGN_IN_HINT}`);
|
|
1083
|
+
}
|
|
1084
|
+
const baseUrl = (_a = accountKey.backendUrl) !== null && _a !== void 0 ? _a : cursorBackendUrl();
|
|
1085
|
+
const baseStateRoot = (_b = options.stateRoot) !== null && _b !== void 0 ? _b : join(targets[0].runtime.project.rootDir, ".agent-serve");
|
|
1086
|
+
const stateDir = join(baseStateRoot, "cursor-slack");
|
|
1087
|
+
// Optional fields may be undefined — the relay JSON-serializes the
|
|
1088
|
+
// registration body, which drops them.
|
|
1089
|
+
const agents = targets.map((target) => { var _a, _b; return ({
|
|
1090
|
+
name: target.agentName,
|
|
1091
|
+
iconUrl: (_a = target.accountRuntime.options.agentIcon) === null || _a === void 0 ? void 0 : _a.url,
|
|
1092
|
+
iconEmoji: (_b = target.accountRuntime.options.agentIcon) === null || _b === void 0 ? void 0 : _b.emoji,
|
|
1093
|
+
}); });
|
|
1094
|
+
const targetByName = new Map(targets.map((target) => [target.agentName.toLowerCase(), target]));
|
|
1095
|
+
const relay = new CursorSlackRelay({
|
|
1096
|
+
baseUrl,
|
|
1097
|
+
apiKey: accountKey.apiKey,
|
|
1098
|
+
agents,
|
|
1099
|
+
stateDir,
|
|
1100
|
+
logger,
|
|
1101
|
+
dispatch: (envelope) => __awaiter(this, void 0, void 0, function* () {
|
|
1102
|
+
const target = targetByName.get(envelope.agentName.toLowerCase());
|
|
1103
|
+
if (target === undefined) {
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
const args = buildChannelHandlerArgs({
|
|
1107
|
+
runtime: target.runtime,
|
|
1108
|
+
channelId: target.channelId,
|
|
1109
|
+
});
|
|
1110
|
+
logger(`[agent-serve] cursor-slack: dispatching ${formatCursorSlackEventLog(envelope)} → ${target.runtime.slug || "default"}`);
|
|
1111
|
+
yield dispatchCursorSlackEnvelope({
|
|
1112
|
+
runtime: target.accountRuntime,
|
|
1113
|
+
envelope,
|
|
1114
|
+
send: args.send,
|
|
1115
|
+
host: args.host,
|
|
1116
|
+
});
|
|
1117
|
+
}),
|
|
1118
|
+
});
|
|
1119
|
+
// Bind each channel's outbound transport before consumption starts so the
|
|
1120
|
+
// first reply can post. The consumer id keys thread claims server-side.
|
|
1121
|
+
const consumerId = yield relay.resolveConsumerId();
|
|
1122
|
+
for (const target of targets) {
|
|
1123
|
+
target.accountRuntime.transport.bind(createCursorSlackApiCaller({
|
|
1124
|
+
baseUrl,
|
|
1125
|
+
apiKey: accountKey.apiKey,
|
|
1126
|
+
agentName: target.agentName,
|
|
1127
|
+
consumerId,
|
|
1128
|
+
logger,
|
|
1129
|
+
}));
|
|
1130
|
+
}
|
|
1131
|
+
relay.start();
|
|
1132
|
+
logger(`[agent-serve] cursor-slack: relaying Slack for ${targets
|
|
1133
|
+
.map((target) => `${target.agentName} (${target.runtime.slug || "default"})`)
|
|
1134
|
+
.join(", ")} from ${baseUrl} — mention "@Cursor <agent name> …" in the linked workspace`);
|
|
1135
|
+
return relay;
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
function buildChannelHandlerArgs(input) {
|
|
1139
|
+
var _a, _b, _c;
|
|
1140
|
+
const { runtime, channelId } = input;
|
|
1141
|
+
const base = runtime.slug === "" ? "" : `/${runtime.slug}`;
|
|
1142
|
+
const auth = (_a = input.auth) !== null && _a !== void 0 ? _a : null;
|
|
1143
|
+
return {
|
|
1144
|
+
params: (_b = input.params) !== null && _b !== void 0 ? _b : {},
|
|
1145
|
+
requestIp: (_c = input.requestIp) !== null && _c !== void 0 ? _c : null,
|
|
1146
|
+
auth,
|
|
1147
|
+
host: runtime.engine.host,
|
|
1148
|
+
send: (message, sendOptions = {}) => runtime.engine.send(channelId, message, Object.assign(Object.assign({}, sendOptions), { auth: sendOptions.auth !== undefined ? sendOptions.auth : auth })),
|
|
1149
|
+
getSession: (sessionId) => runtime.engine.getChannelSession(channelId, sessionId),
|
|
1150
|
+
receive: (target, inputReceive) => runtime.engine.receive(target, inputReceive),
|
|
1151
|
+
callTool: (toolName, toolInput, callOptions) => runtime.engine.callTool(toolName, toolInput, callOptions),
|
|
1152
|
+
waitUntil: (promise) => {
|
|
1153
|
+
runtime.engine.trackBackground(promise);
|
|
1154
|
+
},
|
|
1155
|
+
sessionUrls: (req, sessionId) => sessionUrlsForRequest(req, base, sessionId),
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function handleRequest(router, req, res, logger) {
|
|
1159
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1160
|
+
var _a, _b, _c;
|
|
1161
|
+
let response;
|
|
1162
|
+
const method = (_a = req.method) !== null && _a !== void 0 ? _a : "GET";
|
|
1163
|
+
try {
|
|
1164
|
+
const request = yield toFetchRequest(req);
|
|
1165
|
+
const url = new URL(request.url);
|
|
1166
|
+
const match = router.match(request.method, url.pathname);
|
|
1167
|
+
if (match === undefined) {
|
|
1168
|
+
response = router.hasPath(url.pathname)
|
|
1169
|
+
? errorResponse(405, "method_not_allowed", `${request.method} is not supported on ${url.pathname}.`)
|
|
1170
|
+
: errorResponse(404, "not_found", `No route for ${request.method} ${url.pathname}.`);
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
const requestIp = (_b = req.socket.remoteAddress) !== null && _b !== void 0 ? _b : null;
|
|
1174
|
+
let auth = null;
|
|
1175
|
+
let unauthorized = false;
|
|
1176
|
+
if (match.route.auth !== "none") {
|
|
1177
|
+
auth = yield resolveAuth(match.route.auth, request, requestIp);
|
|
1178
|
+
unauthorized = auth === null;
|
|
1179
|
+
}
|
|
1180
|
+
response = unauthorized
|
|
1181
|
+
? errorResponse(401, "unauthorized", "No auth policy admitted this request. In production, configure channel auth (e.g. bearerAuth) — the default localDevStrict() policy only admits loopback callers with a loopback Host.")
|
|
1182
|
+
: yield match.route.handler(request, {
|
|
1183
|
+
params: match.params,
|
|
1184
|
+
requestIp,
|
|
1185
|
+
auth,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
catch (error) {
|
|
1190
|
+
if (error instanceof SessionBusyError) {
|
|
1191
|
+
response = errorResponse(409, "session_busy", error.message);
|
|
1192
|
+
}
|
|
1193
|
+
else if (error instanceof SessionAuthError) {
|
|
1194
|
+
response = errorResponse(403, "forbidden", error.message);
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
logger(`[agent-serve] request failed: ${error instanceof Error
|
|
1198
|
+
? ((_c = error.stack) !== null && _c !== void 0 ? _c : error.message)
|
|
1199
|
+
: String(error)}`);
|
|
1200
|
+
response = errorResponse(500, "internal_error", "Internal server error.");
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
try {
|
|
1204
|
+
yield writeFetchResponse(response, res, method);
|
|
1205
|
+
}
|
|
1206
|
+
catch (error) {
|
|
1207
|
+
logger(`[agent-serve] response write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1208
|
+
res.destroy();
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
function resolveAuth(policies, request, requestIp) {
|
|
1213
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1214
|
+
for (const policy of policies) {
|
|
1215
|
+
const result = yield policy(request, { requestIp });
|
|
1216
|
+
if (result !== null) {
|
|
1217
|
+
return result;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
return null;
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
function toFetchRequest(req) {
|
|
1224
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1225
|
+
var _a, _b, _c;
|
|
1226
|
+
const method = (_a = req.method) !== null && _a !== void 0 ? _a : "GET";
|
|
1227
|
+
const hostHeader = (_b = req.headers.host) !== null && _b !== void 0 ? _b : "127.0.0.1";
|
|
1228
|
+
const url = new URL((_c = req.url) !== null && _c !== void 0 ? _c : "/", `http://${hostHeader}`);
|
|
1229
|
+
const headers = new Headers();
|
|
1230
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
1231
|
+
if (value === undefined) {
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
if (Array.isArray(value)) {
|
|
1235
|
+
for (const item of value) {
|
|
1236
|
+
headers.append(key, item);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
else {
|
|
1240
|
+
headers.set(key, value);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
if (method === "GET" || method === "HEAD") {
|
|
1244
|
+
return new Request(url, { method, headers });
|
|
1245
|
+
}
|
|
1246
|
+
const body = yield readBody(req);
|
|
1247
|
+
// Buffer is a Uint8Array at runtime; TS's BodyInit wants ArrayBuffer-backed
|
|
1248
|
+
// typed arrays, so re-view without copying.
|
|
1249
|
+
return new Request(url, {
|
|
1250
|
+
method,
|
|
1251
|
+
headers,
|
|
1252
|
+
body: new Uint8Array(body.buffer, body.byteOffset, body.byteLength),
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
function readBody(req) {
|
|
1257
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1258
|
+
var _a, req_1, req_1_1;
|
|
1259
|
+
var _b, e_1, _c, _d;
|
|
1260
|
+
const chunks = [];
|
|
1261
|
+
let total = 0;
|
|
1262
|
+
try {
|
|
1263
|
+
for (_a = true, req_1 = __asyncValues(req); req_1_1 = yield req_1.next(), _b = req_1_1.done, !_b; _a = true) {
|
|
1264
|
+
_d = req_1_1.value;
|
|
1265
|
+
_a = false;
|
|
1266
|
+
const chunk = _d;
|
|
1267
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1268
|
+
total += buffer.length;
|
|
1269
|
+
if (total > MAX_BODY_BYTES) {
|
|
1270
|
+
throw new Error("Request body too large");
|
|
1271
|
+
}
|
|
1272
|
+
chunks.push(buffer);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1276
|
+
finally {
|
|
1277
|
+
try {
|
|
1278
|
+
if (!_a && !_b && (_c = req_1.return)) yield _c.call(req_1);
|
|
1279
|
+
}
|
|
1280
|
+
finally { if (e_1) throw e_1.error; }
|
|
1281
|
+
}
|
|
1282
|
+
return Buffer.concat(chunks);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
function writeFetchResponse(response_1, res_1) {
|
|
1286
|
+
return __awaiter(this, arguments, void 0, function* (response, res, method = "GET") {
|
|
1287
|
+
const headers = {};
|
|
1288
|
+
response.headers.forEach((value, key) => {
|
|
1289
|
+
headers[key] = value;
|
|
1290
|
+
});
|
|
1291
|
+
res.writeHead(response.status, headers);
|
|
1292
|
+
// HEAD responses must not include a body (RFC 9110 §9.3.2). Cancel any
|
|
1293
|
+
// handler-produced stream so long-lived GET handlers (e.g. session SSE)
|
|
1294
|
+
// still unsubscribe when served via the HEAD→GET fallback.
|
|
1295
|
+
if (method === "HEAD" || response.body === null) {
|
|
1296
|
+
if (response.body !== null) {
|
|
1297
|
+
void response.body.cancel().catch(() => { });
|
|
1298
|
+
}
|
|
1299
|
+
res.end();
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const reader = response.body.getReader();
|
|
1303
|
+
let clientGone = false;
|
|
1304
|
+
const closed = new Promise((resolve) => {
|
|
1305
|
+
res.once("close", () => {
|
|
1306
|
+
if (!res.writableFinished) {
|
|
1307
|
+
clientGone = true;
|
|
1308
|
+
void reader.cancel().catch(() => { });
|
|
1309
|
+
}
|
|
1310
|
+
resolve();
|
|
1311
|
+
});
|
|
1312
|
+
});
|
|
1313
|
+
try {
|
|
1314
|
+
while (!clientGone) {
|
|
1315
|
+
const { done, value } = yield reader.read();
|
|
1316
|
+
if (done) {
|
|
1317
|
+
break;
|
|
1318
|
+
}
|
|
1319
|
+
if (value !== undefined && value.length > 0) {
|
|
1320
|
+
if (!res.write(value) && !clientGone) {
|
|
1321
|
+
// Race against close so a vanished client can't wedge the loop
|
|
1322
|
+
// waiting for a drain that will never come.
|
|
1323
|
+
yield Promise.race([once(res, "drain"), closed]);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
catch (_a) {
|
|
1329
|
+
void reader.cancel().catch(() => { });
|
|
1330
|
+
}
|
|
1331
|
+
res.end();
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Dev reminder routes may only inspect/fire reminders whose bound session
|
|
1336
|
+
* belongs to the caller (same principal), matching session read routes.
|
|
1337
|
+
* Loopback `--dev` (and `--allow-anonymous`) can relax ownership like
|
|
1338
|
+
* `GET /v1/sessions`.
|
|
1339
|
+
*/
|
|
1340
|
+
function asStringArray(value) {
|
|
1341
|
+
if (!Array.isArray(value)) {
|
|
1342
|
+
return undefined;
|
|
1343
|
+
}
|
|
1344
|
+
const out = [];
|
|
1345
|
+
for (const item of value) {
|
|
1346
|
+
if (typeof item === "string" && item.trim() !== "") {
|
|
1347
|
+
out.push(item);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return out.length === 0 ? undefined : out;
|
|
1351
|
+
}
|
|
1352
|
+
function assertReminderReadable(input) {
|
|
1353
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1354
|
+
const existing = yield input.engine.sessions.findByContinuation(input.reminder.channelId, input.reminder.continuationToken);
|
|
1355
|
+
if (existing === undefined) {
|
|
1356
|
+
// No live session — still gate on relax/loopback so a shared-dev caller
|
|
1357
|
+
// cannot enumerate orphan reminder metadata across principals.
|
|
1358
|
+
const relaxOwnership = input.relax === true &&
|
|
1359
|
+
(input.beyondLoopback === true ||
|
|
1360
|
+
(typeof input.requestIp === "string" &&
|
|
1361
|
+
isLoopbackAddress(input.requestIp)));
|
|
1362
|
+
if (relaxOwnership) {
|
|
1363
|
+
return undefined;
|
|
1364
|
+
}
|
|
1365
|
+
// Without a session principal to compare, deny non-relaxed callers.
|
|
1366
|
+
return errorResponse(403, "forbidden", "This reminder is bound to a session that is not visible to the caller.");
|
|
1367
|
+
}
|
|
1368
|
+
return requireSessionOwner(existing, input.auth, {
|
|
1369
|
+
relax: input.relax,
|
|
1370
|
+
beyondLoopback: input.beyondLoopback,
|
|
1371
|
+
requestIp: input.requestIp,
|
|
1372
|
+
});
|
|
1373
|
+
});
|
|
1374
|
+
}
|