@cursor/july 0.1.54 → 0.1.61
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/dist/bin/agent-serve.js +30 -23
- package/dist/channels/github/github-channel.d.ts +12 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +67 -15
- package/dist/channels/github/types.d.ts +27 -1
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/cursor-account.d.ts +22 -0
- package/dist/channels/origin/cursor-account.d.ts.map +1 -0
- package/dist/channels/origin/cursor-account.js +35 -0
- package/dist/channels/origin/events.d.ts +10 -0
- package/dist/channels/origin/events.d.ts.map +1 -0
- package/dist/channels/origin/events.js +27 -0
- package/dist/channels/origin/index.d.ts +29 -0
- package/dist/channels/origin/index.d.ts.map +1 -0
- package/dist/channels/origin/index.js +28 -0
- package/dist/channels/origin/origin-channel.d.ts +68 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -0
- package/dist/channels/origin/origin-channel.js +567 -0
- package/dist/channels/origin/origin-webhook.d.ts +38 -0
- package/dist/channels/origin/origin-webhook.d.ts.map +1 -0
- package/dist/channels/origin/origin-webhook.js +151 -0
- package/dist/channels/origin/types.d.ts +152 -0
- package/dist/channels/origin/types.d.ts.map +1 -0
- package/dist/channels/origin/types.js +1 -0
- package/dist/channels/slack/doctor.d.ts +17 -1
- package/dist/channels/slack/doctor.d.ts.map +1 -1
- package/dist/channels/slack/doctor.js +186 -10
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DAKcDclS.js → app.CW3KJh72.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BNIaCSfG.js → VPLocalSearchBox.Bk6EWpPj.js} +1 -1
- package/dist/docs/assets/chunks/{theme.B47GzJjc.js → theme.Bz9AodBA.js} +2 -2
- package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.js → example-agents_fsd.md.ZeGEpAw_.js} +2 -2
- package/dist/docs/assets/{example-agents_fsd.md.DPz9ezO4.lean.js → example-agents_fsd.md.ZeGEpAw_.lean.js} +1 -1
- package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.js → example-agents_security-reviewer.md.DBL1TwtP.js} +2 -2
- package/dist/docs/assets/{example-agents_security-reviewer.md.Dkf1gyo6.lean.js → example-agents_security-reviewer.md.DBL1TwtP.lean.js} +1 -1
- package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.js +12 -0
- package/dist/docs/assets/guides_convert-automation.md.BhMzTkE5.lean.js +1 -0
- package/dist/docs/assets/{guides_github.md.B8QKtIpw.js → guides_github.md.CLLQJKRB.js} +4 -4
- package/dist/docs/assets/{guides_github.md.B8QKtIpw.lean.js → guides_github.md.CLLQJKRB.lean.js} +1 -1
- package/dist/docs/assets/index.md.DhRHS_-L.js +5 -0
- package/dist/docs/assets/{index.md.COeoSkcF.lean.js → index.md.DhRHS_-L.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.js → reference_cli.md.Cl14Jfo3.js} +10 -2
- package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.lean.js → reference_cli.md.Cl14Jfo3.lean.js} +1 -1
- package/dist/docs/assets/scaffolding-agents.md.CrWSA0Ko.js +1 -0
- package/dist/docs/assets/{scaffolding-agents.md.mkc3B_ZW.lean.js → scaffolding-agents.md.CrWSA0Ko.lean.js} +1 -1
- package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.js +14 -0
- package/dist/docs/assets/templates_agentic-owners.md.BkTLORaU.lean.js +1 -0
- package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.js +4 -0
- package/dist/docs/assets/templates_demo.md.Bgd6MBaZ.lean.js +1 -0
- package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.js +8 -0
- package/dist/docs/assets/templates_pr-autofixer.md.DcmoeUNZ.lean.js +1 -0
- package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.js +2 -0
- package/dist/docs/assets/templates_security-reviewer.md.C0yIUaYs.lean.js +1 -0
- package/dist/docs/assets/templates_triage.md.DWuQ1bZz.js +9 -0
- package/dist/docs/assets/templates_triage.md.DWuQ1bZz.lean.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +4 -4
- package/dist/docs/example-agents/bugbot.html +4 -4
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +5 -5
- package/dist/docs/example-agents/index.html +4 -4
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +4 -4
- package/dist/docs/example-agents/security-reviewer.html +5 -5
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +36 -0
- package/dist/docs/guides/github.html +7 -7
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +14 -6
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +38 -0
- package/dist/docs/templates/demo.html +28 -0
- package/dist/docs/templates/pr-autofixer.html +32 -0
- package/dist/docs/templates/security-reviewer.html +26 -0
- package/dist/docs/templates/triage.html +14 -16
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/cli-ax.d.ts +2 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +30 -1
- package/dist/internal/cli-deploy.js +1 -1
- package/dist/internal/cli-slack.d.ts +4 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +46 -20
- package/dist/internal/cursor-event-relay.d.ts +59 -11
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +133 -26
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +11 -2
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/handleAgentServeTrigger.d.ts +33 -6
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
- package/dist/internal/handleAgentServeTrigger.js +97 -19
- package/dist/internal/init-interview.d.ts +67 -0
- package/dist/internal/init-interview.d.ts.map +1 -0
- package/dist/internal/init-interview.js +300 -0
- package/dist/internal/init-project.d.ts +7 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +99 -43
- package/dist/internal/mcp-endpoint.d.ts +7 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -1
- package/dist/internal/mcp-endpoint.js +7 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +159 -57
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +20 -21
- package/dist/internal/session-links.d.ts +14 -0
- package/dist/internal/session-links.d.ts.map +1 -0
- package/dist/internal/session-links.js +69 -0
- package/dist/internal/sessions-client.d.ts.map +1 -1
- package/dist/internal/sessions-client.js +1 -0
- package/dist/internal/slack-provision-client.js +1 -1
- package/dist/playground/assets/index-ClbuaLpm.css +1 -0
- package/dist/playground/assets/index-SqkR13k_.js +85 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +19 -12
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +15 -0
- package/docs/example-agents/fsd.md +3 -0
- package/docs/example-agents/security-reviewer.md +3 -0
- package/docs/guides/convert-automation.md +176 -0
- package/docs/guides/github.md +6 -2
- package/docs/reference/cli.md +36 -10
- package/docs/scaffolding-agents.md +3 -0
- package/docs/templates/agentic-owners.md +97 -0
- package/docs/templates/demo.md +84 -0
- package/docs/templates/pr-autofixer.md +136 -0
- package/docs/templates/security-reviewer.md +89 -0
- package/docs/templates/triage.md +59 -101
- package/package.json +9 -1
- package/skills/github/SKILL.md +4 -5
- package/src/bin/agent-serve.ts +13 -6
- package/src/channels/github/github-channel.ts +95 -18
- package/src/channels/github/types.ts +31 -0
- package/src/channels/origin/cursor-account.ts +64 -0
- package/src/channels/origin/events.ts +41 -0
- package/src/channels/origin/index.ts +29 -0
- package/src/channels/origin/origin-channel.ts +784 -0
- package/src/channels/origin/origin-webhook.ts +202 -0
- package/src/channels/origin/types.ts +186 -0
- package/src/channels/slack/doctor.ts +218 -5
- package/src/index.ts +3 -1
- package/src/internal/cli-ax.ts +33 -1
- package/src/internal/cli-deploy.ts +2 -2
- package/src/internal/cli-slack.ts +68 -19
- package/src/internal/cursor-event-relay.ts +193 -31
- package/src/internal/discovery.ts +15 -5
- package/src/internal/distribution.ts +1 -0
- package/src/internal/handleAgentServeTrigger.ts +129 -19
- package/src/internal/init-interview.ts +384 -0
- package/src/internal/init-project.ts +146 -46
- package/src/internal/mcp-endpoint.ts +7 -0
- package/src/internal/server.ts +188 -58
- package/src/internal/session-engine.ts +5 -21
- package/src/internal/session-links.ts +88 -0
- package/src/internal/sessions-client.ts +1 -0
- package/src/internal/slack-provision-client.ts +1 -1
- package/src/types.ts +19 -12
- package/templates/agentic-owners/README.md +73 -0
- package/templates/agentic-owners/agent/agent.ts +15 -0
- package/templates/agentic-owners/agent/channels/github.ts +85 -0
- package/templates/agentic-owners/agent/instructions.md +34 -0
- package/templates/agentic-owners/agent/lib/config.ts +13 -0
- package/templates/agentic-owners/agent/lib/github.ts +241 -0
- package/templates/agentic-owners/agent/lib/owners/README.md +50 -0
- package/templates/agentic-owners/agent/lib/owners/constants.ts +22 -0
- package/templates/agentic-owners/agent/lib/owners/decision.ts +485 -0
- package/templates/agentic-owners/agent/lib/owners/handles.ts +54 -0
- package/templates/agentic-owners/agent/lib/owners/match.ts +177 -0
- package/templates/agentic-owners/agent/lib/owners/matching.ts +95 -0
- package/templates/agentic-owners/agent/lib/owners/parse.ts +589 -0
- package/templates/agentic-owners/agent/lib/owners/policyMode.ts +65 -0
- package/templates/agentic-owners/agent/lib/owners/prompt.ts +66 -0
- package/templates/agentic-owners/agent/lib/owners/prompts/base-review.md +105 -0
- package/templates/agentic-owners/agent/lib/owners/types.ts +45 -0
- package/templates/agentic-owners/agent/lib/review.ts +777 -0
- package/templates/agentic-owners/agent/tools/record_review.ts +69 -0
- package/templates/agentic-owners/evals/evals.config.ts +5 -0
- package/templates/agentic-owners/evals/owners.eval.ts +116 -0
- package/templates/agentic-owners/package.json +20 -0
- package/templates/agentic-owners/tsconfig.json +12 -0
- package/templates/demo/README.md +33 -0
- package/templates/demo/agent/agent.ts +15 -0
- package/templates/demo/agent/channels/github.ts +50 -0
- package/templates/demo/agent/channels/queue.ts +30 -0
- package/templates/demo/agent/instructions.md +8 -0
- package/templates/demo/agent/lib/collect.ts +78 -0
- package/templates/demo/agent/tools/record_demo.ts +40 -0
- package/templates/demo/evals/demo.eval.ts +33 -0
- package/templates/demo/evals/evals.config.ts +5 -0
- package/templates/demo/package.json +18 -0
- package/templates/demo/tsconfig.json +12 -0
- package/templates/pr-autofixer/README.md +44 -0
- package/templates/pr-autofixer/agent/agent.ts +22 -0
- package/templates/pr-autofixer/agent/channels/drive.ts +40 -0
- package/templates/pr-autofixer/agent/channels/github.ts +167 -0
- package/templates/pr-autofixer/agent/channels/slack.ts +17 -0
- package/templates/pr-autofixer/agent/hooks/bind-agent.ts +19 -0
- package/templates/pr-autofixer/agent/instructions.md +83 -0
- package/templates/pr-autofixer/agent/lib/affinity.ts +33 -0
- package/templates/pr-autofixer/agent/lib/drive-schemas.ts +15 -0
- package/templates/pr-autofixer/agent/lib/drive.ts +79 -0
- package/templates/pr-autofixer/agent/lib/merge-watch.ts +123 -0
- package/templates/pr-autofixer/agent/lib/pr-state.ts +54 -0
- package/templates/pr-autofixer/agent/lib/repos.ts +19 -0
- package/templates/pr-autofixer/agent/lib/start-drive.ts +122 -0
- package/templates/pr-autofixer/agent/lib/webhook-buffer.ts +225 -0
- package/templates/pr-autofixer/agent/lib/webhook-routing.ts +231 -0
- package/templates/pr-autofixer/agent/storage.ts +7 -0
- package/templates/pr-autofixer/agent/tools/drive_pr.ts +22 -0
- package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
- package/templates/pr-autofixer/evals/evals.config.ts +5 -0
- package/templates/pr-autofixer/init.json +25 -0
- package/templates/pr-autofixer/package.json +18 -0
- package/templates/pr-autofixer/tsconfig.json +12 -0
- package/templates/security-reviewer/README.md +31 -0
- package/templates/security-reviewer/agent/agent.ts +15 -0
- package/templates/security-reviewer/agent/channels/github.ts +46 -0
- package/templates/security-reviewer/agent/instructions.md +22 -0
- package/templates/security-reviewer/agent/lib/repos.ts +5 -0
- package/templates/security-reviewer/agent/tools/post_findings.ts +72 -0
- package/templates/security-reviewer/evals/evals.config.ts +5 -0
- package/templates/security-reviewer/evals/review.eval.ts +53 -0
- package/templates/security-reviewer/init.json +25 -0
- package/templates/security-reviewer/package.json +18 -0
- package/templates/security-reviewer/tsconfig.json +12 -0
- package/templates/triage/README.md +27 -29
- package/templates/triage/agent/agent.ts +1 -0
- package/templates/triage/agent/channels/queue.ts +47 -0
- package/templates/triage/agent/channels/webhook.ts +50 -0
- package/templates/triage/agent/instructions.md +24 -13
- package/templates/triage/agent/lib/actuate.ts +759 -0
- package/templates/triage/agent/lib/parse-queue-event.ts +168 -0
- package/templates/triage/agent/lib/tracker.ts +5 -0
- package/templates/triage/agent/lib/types.ts +52 -0
- package/templates/triage/agent/lib/verify-webhook.ts +81 -0
- package/templates/triage/agent/skills/triage-rubric.md +6 -5
- package/templates/triage/agent/tools/record_triage.ts +24 -5
- package/templates/triage/evals/triage.eval.ts +25 -3
- package/templates/triage/overlays/jira/README.md +21 -0
- package/templates/triage/overlays/jira/agent/lib/tracker.ts +8 -0
- package/templates/triage/overlays/jira/agent/mcp-connections/tracker.ts +7 -0
- package/templates/triage/overlays/linear/README.md +21 -0
- package/templates/triage/overlays/linear/agent/lib/tracker.ts +8 -0
- package/templates/triage/overlays/linear/agent/mcp-connections/tracker.ts +7 -0
- package/templates/triage/package.json +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +0 -1
- package/dist/docs/assets/index.md.COeoSkcF.js +0 -5
- package/dist/docs/assets/scaffolding-agents.md.mkc3B_ZW.js +0 -1
- package/dist/docs/assets/templates_triage.md.CConZt1z.js +0 -11
- package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +0 -1
- package/dist/playground/assets/index-BHAPd9KT.js +0 -85
- package/dist/playground/assets/index-DpKH0HcT.css +0 -1
- package/templates/triage/agent/channels/intake.ts +0 -20
- package/templates/triage/agent/channels/slack.ts +0 -3
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unified entry for dispatching agent-serve triggers from an external ingress
|
|
3
|
-
* (e.g. the Cursor backend GitHub webhook).
|
|
4
|
-
* `handleAutomationTrigger
|
|
3
|
+
* (e.g. the Cursor backend GitHub webhook or Origin cursor-internal tap).
|
|
4
|
+
* Mirrors Automations' `handleAutomationTrigger`.
|
|
5
5
|
*
|
|
6
6
|
* Callers must authenticate the delivery before invoking this (the backend
|
|
7
|
-
* verifies GitHub HMAC at `/github_webhook`
|
|
8
|
-
*
|
|
7
|
+
* verifies GitHub HMAC at `/github_webhook` and Origin v1ed at the
|
|
8
|
+
* cursor-internal app). This fan-out does not re-check signatures — same
|
|
9
|
+
* trust model as Bugbot after `verifyGithubRequest`.
|
|
9
10
|
*
|
|
10
11
|
* Each agent's `onPullRequest` / `onComment` / … hooks decide whether to run.
|
|
11
12
|
*/
|
|
@@ -15,6 +16,12 @@ import {
|
|
|
15
16
|
getGitHubChannelOptions,
|
|
16
17
|
} from "../channels/github/github-channel.js";
|
|
17
18
|
import type { GitHubChannelOptions } from "../channels/github/types.js";
|
|
19
|
+
import {
|
|
20
|
+
dispatchOriginWebhook,
|
|
21
|
+
getOriginChannelOptions,
|
|
22
|
+
parseOriginWebhookEnvelope,
|
|
23
|
+
} from "../channels/origin/origin-channel.js";
|
|
24
|
+
import type { OriginChannelOptions } from "../channels/origin/types.js";
|
|
18
25
|
import type { ChannelHandlerArgs } from "../types.js";
|
|
19
26
|
|
|
20
27
|
export interface AgentServeGitHubTriggerTarget {
|
|
@@ -35,7 +42,22 @@ export interface AgentServeGitHubTriggerEvent {
|
|
|
35
42
|
targets: AgentServeGitHubTriggerTarget[];
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
export
|
|
45
|
+
export interface AgentServeOriginTriggerTarget {
|
|
46
|
+
slug: string;
|
|
47
|
+
options: OriginChannelOptions;
|
|
48
|
+
args: ChannelHandlerArgs;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface AgentServeOriginTriggerEvent {
|
|
52
|
+
type: "origin";
|
|
53
|
+
/** Origin envelope JSON (signature already verified by the caller). */
|
|
54
|
+
bodyText: string;
|
|
55
|
+
targets: AgentServeOriginTriggerTarget[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type AgentServeTriggerEvent =
|
|
59
|
+
| AgentServeGitHubTriggerEvent
|
|
60
|
+
| AgentServeOriginTriggerEvent;
|
|
39
61
|
|
|
40
62
|
export interface AgentServeTriggerTargetResult {
|
|
41
63
|
slug: string;
|
|
@@ -78,17 +100,46 @@ export function collectGitHubTriggerTargets(input: {
|
|
|
78
100
|
}
|
|
79
101
|
|
|
80
102
|
/**
|
|
81
|
-
*
|
|
103
|
+
* Build fan-out targets from mounted agent projects that export an origin
|
|
104
|
+
* channel created via {@link originChannel}.
|
|
105
|
+
*/
|
|
106
|
+
export function collectOriginTriggerTargets(input: {
|
|
107
|
+
mounts: Array<{
|
|
108
|
+
slug: string;
|
|
109
|
+
channels: Array<{ id: string; definition: object }>;
|
|
110
|
+
buildHandlerArgs: (channelId: string) => ChannelHandlerArgs;
|
|
111
|
+
}>;
|
|
112
|
+
}): AgentServeOriginTriggerTarget[] {
|
|
113
|
+
const targets: AgentServeOriginTriggerTarget[] = [];
|
|
114
|
+
for (const mount of input.mounts) {
|
|
115
|
+
for (const channel of mount.channels) {
|
|
116
|
+
const options = getOriginChannelOptions(channel.definition);
|
|
117
|
+
if (options === undefined) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
targets.push({
|
|
121
|
+
slug: mount.slug,
|
|
122
|
+
options,
|
|
123
|
+
args: mount.buildHandlerArgs(channel.id),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return targets;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Dispatch an agent-serve trigger. GitHub and Origin webhook fan-out.
|
|
82
132
|
*/
|
|
83
133
|
export async function handleAgentServeTrigger(
|
|
84
134
|
event: AgentServeTriggerEvent
|
|
85
135
|
): Promise<AgentServeTriggerResult> {
|
|
86
136
|
switch (event.type) {
|
|
87
|
-
// TODO: Add support for other event types (e.g. Linear, Slack, etc.).
|
|
88
137
|
case "github":
|
|
89
138
|
return handleAgentServeGitHubTrigger(event);
|
|
139
|
+
case "origin":
|
|
140
|
+
return handleAgentServeOriginTrigger(event);
|
|
90
141
|
default: {
|
|
91
|
-
const _: never = event
|
|
142
|
+
const _: never = event;
|
|
92
143
|
throw new Error(
|
|
93
144
|
`handleAgentServeTrigger: unknown event type: ${String(_)}`
|
|
94
145
|
);
|
|
@@ -133,17 +184,7 @@ async function handleAgentServeGitHubTrigger(
|
|
|
133
184
|
deliveryId: event.deliveryId,
|
|
134
185
|
args: target.args,
|
|
135
186
|
});
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
body = await response.json();
|
|
139
|
-
} catch {
|
|
140
|
-
body = { ok: false, error: "invalid_response_json" };
|
|
141
|
-
}
|
|
142
|
-
targets.push({
|
|
143
|
-
slug: target.slug,
|
|
144
|
-
status: response.status,
|
|
145
|
-
body,
|
|
146
|
-
});
|
|
187
|
+
targets.push(await triggerTargetResult(target.slug, response));
|
|
147
188
|
}
|
|
148
189
|
|
|
149
190
|
return {
|
|
@@ -152,3 +193,72 @@ async function handleAgentServeGitHubTrigger(
|
|
|
152
193
|
targets,
|
|
153
194
|
};
|
|
154
195
|
}
|
|
196
|
+
|
|
197
|
+
async function handleAgentServeOriginTrigger(
|
|
198
|
+
event: AgentServeOriginTriggerEvent
|
|
199
|
+
): Promise<AgentServeTriggerResult> {
|
|
200
|
+
let payload: unknown;
|
|
201
|
+
try {
|
|
202
|
+
payload = JSON.parse(event.bodyText) as unknown;
|
|
203
|
+
} catch {
|
|
204
|
+
return {
|
|
205
|
+
ok: false,
|
|
206
|
+
event: null,
|
|
207
|
+
targets: [
|
|
208
|
+
{
|
|
209
|
+
slug: "",
|
|
210
|
+
status: 400,
|
|
211
|
+
body: { ok: false, error: "invalid_json" },
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const envelope = parseOriginWebhookEnvelope(payload);
|
|
218
|
+
if (envelope === null) {
|
|
219
|
+
return {
|
|
220
|
+
ok: false,
|
|
221
|
+
event: null,
|
|
222
|
+
targets: [
|
|
223
|
+
{
|
|
224
|
+
slug: "",
|
|
225
|
+
status: 400,
|
|
226
|
+
body: { ok: false, error: "invalid_origin_payload" },
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const targets: AgentServeTriggerTargetResult[] = [];
|
|
233
|
+
for (const target of event.targets) {
|
|
234
|
+
const response = await dispatchOriginWebhook({
|
|
235
|
+
options: target.options,
|
|
236
|
+
envelope,
|
|
237
|
+
args: target.args,
|
|
238
|
+
});
|
|
239
|
+
targets.push(await triggerTargetResult(target.slug, response));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
ok: true,
|
|
244
|
+
event: envelope.event.type,
|
|
245
|
+
targets,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function triggerTargetResult(
|
|
250
|
+
slug: string,
|
|
251
|
+
response: Response
|
|
252
|
+
): Promise<AgentServeTriggerTargetResult> {
|
|
253
|
+
let body: unknown;
|
|
254
|
+
try {
|
|
255
|
+
body = await response.json();
|
|
256
|
+
} catch {
|
|
257
|
+
body = { ok: false, error: "invalid_response_json" };
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
slug,
|
|
261
|
+
status: response.status,
|
|
262
|
+
body,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template `init.json`: questions `init` asks, and string replacements
|
|
3
|
+
* it applies while copying the scaffold.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { createInterface } from "node:readline";
|
|
9
|
+
import { parseCursorGithubRepoScope } from "../channels/github/cursor-account.js";
|
|
10
|
+
import { describeError } from "./describe-error.js";
|
|
11
|
+
|
|
12
|
+
export const TEMPLATE_INIT_FILE = "init.json";
|
|
13
|
+
|
|
14
|
+
export interface TemplateQuestion {
|
|
15
|
+
id: string;
|
|
16
|
+
prompt: string;
|
|
17
|
+
hint?: string;
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
default?: string;
|
|
20
|
+
choices?: readonly string[];
|
|
21
|
+
parse?: "owner-repo-list";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TemplateReplace {
|
|
25
|
+
file: string;
|
|
26
|
+
find: string;
|
|
27
|
+
answer: string;
|
|
28
|
+
as?: "json-array" | "first";
|
|
29
|
+
template?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface TemplateInitSpec {
|
|
33
|
+
questions: TemplateQuestion[];
|
|
34
|
+
replace: TemplateReplace[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function posixRel(rel: string): string {
|
|
38
|
+
return rel.replaceAll("\\", "/");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isTemplateMetaPath(rel: string): boolean {
|
|
42
|
+
const normalized = posixRel(rel);
|
|
43
|
+
const top = normalized.split("/")[0];
|
|
44
|
+
if (top === "overlays") {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return (
|
|
48
|
+
normalized === TEMPLATE_INIT_FILE ||
|
|
49
|
+
normalized.endsWith(`/${TEMPLATE_INIT_FILE}`)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function templateFileHasReplacements(
|
|
54
|
+
spec: TemplateInitSpec,
|
|
55
|
+
rel: string
|
|
56
|
+
): boolean {
|
|
57
|
+
const file = posixRel(rel);
|
|
58
|
+
return spec.replace.some((rule) => posixRel(rule.file) === file);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function loadTemplateInitSpec(
|
|
62
|
+
templateDir: string
|
|
63
|
+
): Promise<TemplateInitSpec | undefined> {
|
|
64
|
+
try {
|
|
65
|
+
const text = await readFile(join(templateDir, TEMPLATE_INIT_FILE), "utf8");
|
|
66
|
+
return parseTemplateInitSpec(JSON.parse(text));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function parseTemplateInitSpec(value: unknown): TemplateInitSpec {
|
|
76
|
+
if (!isRecord(value) || !Array.isArray(value.questions)) {
|
|
77
|
+
throw new Error(`${TEMPLATE_INIT_FILE} must have a questions array`);
|
|
78
|
+
}
|
|
79
|
+
const questions = value.questions.map((item, index) =>
|
|
80
|
+
parseQuestion(item, index)
|
|
81
|
+
);
|
|
82
|
+
const replace = Array.isArray(value.replace)
|
|
83
|
+
? value.replace.map((item, index) => parseReplace(item, index))
|
|
84
|
+
: [];
|
|
85
|
+
return { questions, replace };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function parseInitVars(
|
|
89
|
+
flags: readonly string[] | undefined
|
|
90
|
+
): Record<string, string> {
|
|
91
|
+
const vars: Record<string, string> = {};
|
|
92
|
+
if (flags === undefined) {
|
|
93
|
+
return vars;
|
|
94
|
+
}
|
|
95
|
+
for (const flag of flags) {
|
|
96
|
+
const eq = flag.indexOf("=");
|
|
97
|
+
if (eq <= 0) {
|
|
98
|
+
throw new Error(`--var must be id=value (got ${JSON.stringify(flag)})`);
|
|
99
|
+
}
|
|
100
|
+
vars[flag.slice(0, eq)] = flag.slice(eq + 1);
|
|
101
|
+
}
|
|
102
|
+
return vars;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type ParseAnswersResult =
|
|
106
|
+
| { ok: true; answers: Record<string, string> }
|
|
107
|
+
| { ok: false; error: string };
|
|
108
|
+
|
|
109
|
+
export type RunTemplateInitResult =
|
|
110
|
+
| { ok: true; spec?: TemplateInitSpec; answers?: Record<string, string> }
|
|
111
|
+
| { ok: false; error: string };
|
|
112
|
+
|
|
113
|
+
/** Load `init.json`, apply `--var`, and prompt on a TTY. */
|
|
114
|
+
export async function runTemplateInit(input: {
|
|
115
|
+
templateDir: string;
|
|
116
|
+
vars?: readonly string[];
|
|
117
|
+
interactive: boolean;
|
|
118
|
+
}): Promise<RunTemplateInitResult> {
|
|
119
|
+
try {
|
|
120
|
+
const spec = await loadTemplateInitSpec(input.templateDir);
|
|
121
|
+
if (spec === undefined) {
|
|
122
|
+
return { ok: true };
|
|
123
|
+
}
|
|
124
|
+
const collected = await collectTemplateAnswers({
|
|
125
|
+
spec,
|
|
126
|
+
vars: parseInitVars(input.vars),
|
|
127
|
+
interactive: input.interactive,
|
|
128
|
+
});
|
|
129
|
+
if (!collected.ok) {
|
|
130
|
+
return collected;
|
|
131
|
+
}
|
|
132
|
+
return { ok: true, spec, answers: collected.answers };
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return { ok: false, error: describeError(error) };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function parseTemplateAnswers(
|
|
139
|
+
spec: TemplateInitSpec,
|
|
140
|
+
raw: Record<string, string>
|
|
141
|
+
): ParseAnswersResult {
|
|
142
|
+
const answers: Record<string, string> = {};
|
|
143
|
+
for (const question of spec.questions) {
|
|
144
|
+
if (!(question.id in raw)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const parsed = parseQuestionValue(question, raw[question.id] ?? "");
|
|
148
|
+
if (!parsed.ok) {
|
|
149
|
+
return parsed;
|
|
150
|
+
}
|
|
151
|
+
answers[question.id] = parsed.value;
|
|
152
|
+
}
|
|
153
|
+
return { ok: true, answers };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface CollectAnswersInput {
|
|
157
|
+
spec: TemplateInitSpec;
|
|
158
|
+
vars?: Record<string, string>;
|
|
159
|
+
interactive: boolean;
|
|
160
|
+
ask?: (question: TemplateQuestion) => Promise<string>;
|
|
161
|
+
out?: (text: string) => void;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function collectTemplateAnswers(
|
|
165
|
+
input: CollectAnswersInput
|
|
166
|
+
): Promise<ParseAnswersResult> {
|
|
167
|
+
const vars = input.vars ?? {};
|
|
168
|
+
const answers: Record<string, string> = {};
|
|
169
|
+
const ask = input.ask ?? promptTemplateQuestion;
|
|
170
|
+
const out = input.out ?? ((text) => process.stdout.write(text));
|
|
171
|
+
|
|
172
|
+
for (const question of input.spec.questions) {
|
|
173
|
+
if (question.id in vars) {
|
|
174
|
+
const parsed = parseQuestionValue(question, vars[question.id] ?? "");
|
|
175
|
+
if (!parsed.ok) {
|
|
176
|
+
return parsed;
|
|
177
|
+
}
|
|
178
|
+
answers[question.id] = parsed.value;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (!input.interactive) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
for (;;) {
|
|
185
|
+
const line = await ask(question);
|
|
186
|
+
const value =
|
|
187
|
+
line.trim() === "" && question.default !== undefined
|
|
188
|
+
? question.default
|
|
189
|
+
: line;
|
|
190
|
+
const parsed = parseQuestionValue(question, value);
|
|
191
|
+
if (parsed.ok) {
|
|
192
|
+
answers[question.id] = parsed.value;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
out(`${parsed.error}\n`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { ok: true, answers };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function applyInitReplacements(
|
|
203
|
+
contents: string,
|
|
204
|
+
rel: string,
|
|
205
|
+
spec: TemplateInitSpec,
|
|
206
|
+
answers: Record<string, string>
|
|
207
|
+
): string {
|
|
208
|
+
const file = posixRel(rel);
|
|
209
|
+
let next = contents;
|
|
210
|
+
for (const rule of spec.replace) {
|
|
211
|
+
if (posixRel(rule.file) !== file) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const value = formatReplaceValue(rule, answers);
|
|
215
|
+
if (value === undefined) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (!next.includes(rule.find)) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`${TEMPLATE_INIT_FILE}: ${rule.find} not found in ${rel}`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
next = replaceLiteral(next, rule.find, value);
|
|
224
|
+
}
|
|
225
|
+
return next;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function promptTemplateQuestion(
|
|
229
|
+
question: TemplateQuestion,
|
|
230
|
+
options: {
|
|
231
|
+
input?: NodeJS.ReadableStream;
|
|
232
|
+
output?: NodeJS.WritableStream;
|
|
233
|
+
} = {}
|
|
234
|
+
): Promise<string> {
|
|
235
|
+
const input = options.input ?? process.stdin;
|
|
236
|
+
const output = options.output ?? process.stdout;
|
|
237
|
+
const rl = createInterface({ input, output });
|
|
238
|
+
const suffix = promptSuffix(question);
|
|
239
|
+
try {
|
|
240
|
+
if (question.hint !== undefined) {
|
|
241
|
+
output.write(` ${question.hint}\n`);
|
|
242
|
+
}
|
|
243
|
+
return await new Promise<string>((resolve) => {
|
|
244
|
+
rl.question(`${question.prompt}${suffix} `, resolve);
|
|
245
|
+
});
|
|
246
|
+
} finally {
|
|
247
|
+
rl.close();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function replaceLiteral(haystack: string, find: string, value: string): string {
|
|
252
|
+
return haystack.replaceAll(find, () => value);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function promptSuffix(question: TemplateQuestion): string {
|
|
256
|
+
if (question.choices !== undefined && question.choices.length > 0) {
|
|
257
|
+
return ` [${question.choices.join("/")}]`;
|
|
258
|
+
}
|
|
259
|
+
if (question.default !== undefined) {
|
|
260
|
+
return ` [${question.default}]`;
|
|
261
|
+
}
|
|
262
|
+
if (question.placeholder !== undefined) {
|
|
263
|
+
return ` (${question.placeholder})`;
|
|
264
|
+
}
|
|
265
|
+
return "";
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function parseQuestionValue(
|
|
269
|
+
question: TemplateQuestion,
|
|
270
|
+
raw: string
|
|
271
|
+
): { ok: true; value: string } | { ok: false; error: string } {
|
|
272
|
+
const trimmed = raw.trim();
|
|
273
|
+
if (trimmed === "") {
|
|
274
|
+
return { ok: false, error: `${question.id} is required.` };
|
|
275
|
+
}
|
|
276
|
+
if (question.choices !== undefined && question.choices.length > 0) {
|
|
277
|
+
if (!question.choices.includes(trimmed)) {
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
error: `${question.id} must be one of ${question.choices.join(", ")}.`,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return { ok: true, value: trimmed };
|
|
284
|
+
}
|
|
285
|
+
if (question.parse === "owner-repo-list") {
|
|
286
|
+
try {
|
|
287
|
+
const scope = parseCursorGithubRepoScope(
|
|
288
|
+
trimmed.split(",").map((repo) => repo.trim())
|
|
289
|
+
);
|
|
290
|
+
return { ok: true, value: scope.repos.join(", ") };
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return { ok: false, error: describeError(error) };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return { ok: true, value: trimmed };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function formatReplaceValue(
|
|
299
|
+
rule: TemplateReplace,
|
|
300
|
+
answers: Record<string, string>
|
|
301
|
+
): string | undefined {
|
|
302
|
+
const raw = answers[rule.answer];
|
|
303
|
+
if (raw === undefined || raw === "") {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
const parts = raw.split(",").map((part) => part.trim());
|
|
307
|
+
let value = raw;
|
|
308
|
+
if (rule.as === "json-array") {
|
|
309
|
+
value = `[${parts.map((part) => JSON.stringify(part)).join(", ")}]`;
|
|
310
|
+
} else if (rule.as === "first") {
|
|
311
|
+
const first = parts[0];
|
|
312
|
+
if (first === undefined || first === "") {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
value = first;
|
|
316
|
+
}
|
|
317
|
+
if (rule.template !== undefined) {
|
|
318
|
+
return replaceLiteral(rule.template, "{{value}}", value);
|
|
319
|
+
}
|
|
320
|
+
return value;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function parseQuestion(value: unknown, index: number): TemplateQuestion {
|
|
324
|
+
if (!isRecord(value) || typeof value.id !== "string" || value.id === "") {
|
|
325
|
+
throw new Error(`${TEMPLATE_INIT_FILE} questions[${index}] needs an id`);
|
|
326
|
+
}
|
|
327
|
+
if (typeof value.prompt !== "string" || value.prompt === "") {
|
|
328
|
+
throw new Error(
|
|
329
|
+
`${TEMPLATE_INIT_FILE} questions[${value.id}] needs a prompt`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
const question: TemplateQuestion = {
|
|
333
|
+
id: value.id,
|
|
334
|
+
prompt: value.prompt,
|
|
335
|
+
};
|
|
336
|
+
if (typeof value.hint === "string") {
|
|
337
|
+
question.hint = value.hint;
|
|
338
|
+
}
|
|
339
|
+
if (typeof value.placeholder === "string") {
|
|
340
|
+
question.placeholder = value.placeholder;
|
|
341
|
+
}
|
|
342
|
+
if (typeof value.default === "string") {
|
|
343
|
+
question.default = value.default;
|
|
344
|
+
}
|
|
345
|
+
if (Array.isArray(value.choices)) {
|
|
346
|
+
question.choices = value.choices.map((choice) => String(choice));
|
|
347
|
+
}
|
|
348
|
+
if (value.parse === "owner-repo-list") {
|
|
349
|
+
question.parse = "owner-repo-list";
|
|
350
|
+
}
|
|
351
|
+
return question;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function parseReplace(value: unknown, index: number): TemplateReplace {
|
|
355
|
+
if (
|
|
356
|
+
!isRecord(value) ||
|
|
357
|
+
typeof value.file !== "string" ||
|
|
358
|
+
value.file === "" ||
|
|
359
|
+
typeof value.find !== "string" ||
|
|
360
|
+
value.find === "" ||
|
|
361
|
+
typeof value.answer !== "string" ||
|
|
362
|
+
value.answer === ""
|
|
363
|
+
) {
|
|
364
|
+
throw new Error(
|
|
365
|
+
`${TEMPLATE_INIT_FILE} replace[${index}] needs file, find, and answer`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
const rule: TemplateReplace = {
|
|
369
|
+
file: value.file,
|
|
370
|
+
find: value.find,
|
|
371
|
+
answer: value.answer,
|
|
372
|
+
};
|
|
373
|
+
if (value.as === "json-array" || value.as === "first") {
|
|
374
|
+
rule.as = value.as;
|
|
375
|
+
}
|
|
376
|
+
if (typeof value.template === "string") {
|
|
377
|
+
rule.template = value.template;
|
|
378
|
+
}
|
|
379
|
+
return rule;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
383
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
384
|
+
}
|