@cursor/july 0.1.42 → 0.1.45
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 +1 -0
- package/dist/bin/agent-serve.js +3 -1
- package/dist/channels/github/api.d.ts +5 -0
- package/dist/channels/github/api.d.ts.map +1 -1
- package/dist/channels/github/api.js +18 -0
- package/dist/channels/github/defaults.d.ts +4 -5
- package/dist/channels/github/defaults.d.ts.map +1 -1
- package/dist/channels/github/defaults.js +27 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +41 -10
- package/dist/channels/github/index.d.ts +1 -0
- package/dist/channels/github/index.d.ts.map +1 -1
- package/dist/channels/github/index.js +1 -0
- package/dist/channels/github/progress.d.ts +54 -0
- package/dist/channels/github/progress.d.ts.map +1 -0
- package/dist/channels/github/progress.js +168 -0
- package/dist/channels/github/state.d.ts +2 -0
- package/dist/channels/github/state.d.ts.map +1 -1
- package/dist/channels/github/state.js +1 -1
- package/dist/channels/github/types.d.ts +16 -4
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.d.ts +4 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +4 -0
- package/dist/channels.d.ts +17 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +31 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.C4-Gwi7g.js → app.fHnNXEJ6.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BMT0xM4I.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DDoLm843.js → VPLocalSearchBox.Cf6ghfFY.js} +1 -1
- package/dist/docs/assets/chunks/{theme.SbjGIiM0.js → theme.B_5-VLe8.js} +2 -2
- package/dist/docs/assets/{deployment.md.BHVjVqRa.js → deployment.md.Cdp4rTE4.js} +1 -1
- package/dist/docs/assets/{guides_github.md.R2QlpR75.js → guides_github.md.DSg0j9qj.js} +29 -3
- package/dist/docs/assets/{guides_github.md.R2QlpR75.lean.js → guides_github.md.DSg0j9qj.lean.js} +1 -1
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.js +27 -0
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.lean.js +1 -0
- package/dist/docs/assets/index.md.COeoSkcF.js +5 -0
- package/dist/docs/assets/index.md.COeoSkcF.lean.js +1 -0
- package/dist/docs/assets/{quickstart.md.rIbfahJM.js → quickstart.md.DZxBu44y.js} +18 -49
- package/dist/docs/assets/quickstart.md.DZxBu44y.lean.js +1 -0
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.js → reference_agent-config.md.XRpxctsA.js} +9 -4
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.lean.js → reference_agent-config.md.XRpxctsA.lean.js} +1 -1
- package/dist/docs/assets/{reference_channels.md.BIabFUAI.js → reference_channels.md.MN8iDy8V.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.DqEqQpGn.js → reference_cli.md.BDmjPPAm.js} +2 -2
- package/dist/docs/assets/{reference_hooks.md.B40QAG6m.js → reference_hooks.md.4cbRRIIu.js} +2 -2
- package/dist/docs/assets/{reference_hooks.md.B40QAG6m.lean.js → reference_hooks.md.4cbRRIIu.lean.js} +1 -1
- package/dist/docs/assets/{reference_project-layout.md._XdeMahr.js → reference_project-layout.md.D3MdHM2z.js} +2 -1
- package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.js → reference_tools.md.kJfJAP2m.js} +1 -1
- package/dist/docs/assets/templates_triage.md.CConZt1z.js +11 -0
- package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +1 -0
- package/dist/docs/building-with-agents.html +5 -5
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +6 -6
- 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 +4 -4
- package/dist/docs/example-agents/index.html +5 -5
- 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 +4 -4
- 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 +5 -5
- package/dist/docs/guides/github.html +33 -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 +51 -0
- 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 +8 -27
- package/dist/docs/quickstart.html +24 -55
- package/dist/docs/reference/agent-config.html +12 -7
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +6 -6
- package/dist/docs/reference/cli.html +7 -7
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +6 -6
- 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 +7 -6
- 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 +6 -6
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/triage.html +35 -0
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/internal/cli-ax.d.ts +1 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +20 -5
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +38 -7
- package/dist/internal/deploy-client.d.ts +8 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +5 -2
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +82 -13
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/host-otel.d.ts +25 -0
- package/dist/internal/host-otel.d.ts.map +1 -0
- package/dist/internal/host-otel.js +162 -0
- package/dist/internal/init-project.d.ts +24 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +136 -17
- package/dist/internal/otel/attrs.d.ts +7 -0
- package/dist/internal/otel/attrs.d.ts.map +1 -0
- package/dist/internal/otel/attrs.js +13 -0
- package/dist/internal/otel/collector.d.ts +21 -0
- package/dist/internal/otel/collector.d.ts.map +1 -0
- package/dist/internal/otel/collector.js +605 -0
- package/dist/internal/otel/config.d.ts +42 -0
- package/dist/internal/otel/config.d.ts.map +1 -0
- package/dist/internal/otel/config.js +236 -0
- package/dist/internal/otel/http.d.ts +11 -0
- package/dist/internal/otel/http.d.ts.map +1 -0
- package/dist/internal/otel/http.js +97 -0
- package/dist/internal/otel/runtime.d.ts +21 -0
- package/dist/internal/otel/runtime.d.ts.map +1 -0
- package/dist/internal/otel/runtime.js +188 -0
- package/dist/internal/sdk-runner.d.ts +1 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +1 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +26 -1
- package/dist/internal/session-engine.d.ts +15 -2
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +58 -7
- package/dist/memory.d.ts +4 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +4 -0
- package/dist/otel.d.ts +88 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +64 -0
- package/dist/playground/assets/index-CZH9EFvO.css +1 -0
- package/dist/playground/assets/{index-B3HBneZZ.js → index-Dces8BDp.js} +34 -34
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +103 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +35 -105
- package/docs/deployment.md +2 -1
- package/docs/guides/github.md +79 -6
- package/docs/guides/opentelemetry.md +214 -0
- package/docs/quickstart.md +89 -227
- package/docs/reference/agent-config.md +52 -0
- package/docs/reference/channels.md +3 -1
- package/docs/reference/cli.md +2 -2
- package/docs/reference/hooks.md +21 -4
- package/docs/reference/project-layout.md +2 -0
- package/docs/reference/tools.md +5 -4
- package/docs/templates/triage.md +147 -0
- package/package.json +24 -2
- package/skills/create-agent/SKILL.md +2 -2
- package/skills/framework-map/SKILL.md +2 -0
- package/skills/otel/SKILL.md +98 -0
- package/src/bin/agent-serve.ts +3 -1
- package/src/channels/github/api.ts +22 -0
- package/src/channels/github/defaults.ts +27 -6
- package/src/channels/github/github-channel.ts +36 -1
- package/src/channels/github/index.ts +1 -0
- package/src/channels/github/progress.ts +243 -0
- package/src/channels/github/state.ts +3 -0
- package/src/channels/github/types.ts +12 -4
- package/src/channels/slack/slack-channel.ts +4 -0
- package/src/channels.ts +38 -0
- package/src/index.ts +7 -0
- package/src/internal/cli-ax.ts +21 -3
- package/src/internal/cli-deploy.ts +58 -3
- package/src/internal/deploy-client.ts +11 -0
- package/src/internal/discovery.ts +108 -4
- package/src/internal/distribution.ts +1 -0
- package/src/internal/host-otel.ts +252 -0
- package/src/internal/init-project.ts +160 -17
- package/src/internal/otel/attrs.ts +18 -0
- package/src/internal/otel/collector.ts +881 -0
- package/src/internal/otel/config.ts +307 -0
- package/src/internal/otel/http.ts +126 -0
- package/src/internal/otel/runtime.ts +271 -0
- package/src/internal/sdk-runner.ts +6 -0
- package/src/internal/server.ts +39 -1
- package/src/internal/session-engine.ts +83 -2
- package/src/memory.ts +4 -0
- package/src/otel.ts +155 -0
- package/src/types.ts +109 -3
- package/templates/triage/README.md +48 -0
- package/templates/triage/agent/agent.ts +14 -0
- package/templates/triage/agent/channels/intake.ts +20 -0
- package/templates/triage/agent/channels/slack.ts +3 -0
- package/templates/triage/agent/hooks/memory.ts +3 -0
- package/templates/triage/agent/instructions.md +27 -0
- package/templates/triage/agent/skills/triage-rubric.md +43 -0
- package/templates/triage/agent/tools/record_triage.ts +23 -0
- package/templates/triage/evals/evals.config.ts +5 -0
- package/templates/triage/evals/triage.eval.ts +43 -0
- package/templates/triage/package.json +18 -0
- package/templates/triage/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.BlU3A8EB.js +0 -1
- package/dist/docs/assets/index.md.BKpbkCa6.js +0 -24
- package/dist/docs/assets/index.md.BKpbkCa6.lean.js +0 -1
- package/dist/docs/assets/quickstart.md.rIbfahJM.lean.js +0 -1
- package/dist/playground/assets/index-Bqk9nplh.css +0 -1
- /package/dist/docs/assets/{deployment.md.BHVjVqRa.lean.js → deployment.md.Cdp4rTE4.lean.js} +0 -0
- /package/dist/docs/assets/{reference_channels.md.BIabFUAI.lean.js → reference_channels.md.MN8iDy8V.lean.js} +0 -0
- /package/dist/docs/assets/{reference_cli.md.DqEqQpGn.lean.js → reference_cli.md.BDmjPPAm.lean.js} +0 -0
- /package/dist/docs/assets/{reference_project-layout.md._XdeMahr.lean.js → reference_project-layout.md.D3MdHM2z.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.lean.js → reference_tools.md.kJfJAP2m.lean.js} +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in PR progress surfaces: commit status + sticky banner comment.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
createGitHubCommitStatus,
|
|
7
|
+
createGitHubIssueComment,
|
|
8
|
+
GitHubApiError,
|
|
9
|
+
updateGitHubIssueComment,
|
|
10
|
+
} from "./api.js";
|
|
11
|
+
import type { GitHubBindingConfig } from "./binding.js";
|
|
12
|
+
import { formatGitHubOutboundComment } from "./binding.js";
|
|
13
|
+
import type { GitHubChannelState } from "./state.js";
|
|
14
|
+
|
|
15
|
+
/** GitHub commit-status descriptions are capped at 140 characters. */
|
|
16
|
+
const STATUS_DESCRIPTION_MAX = 140;
|
|
17
|
+
|
|
18
|
+
export type GitHubProgressPhase = "pending" | "success" | "failure";
|
|
19
|
+
|
|
20
|
+
export interface GitHubProgressPhaseCopy {
|
|
21
|
+
pending?: string;
|
|
22
|
+
success?: string;
|
|
23
|
+
failure?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface GitHubCommitStatusProgress extends GitHubProgressPhaseCopy {
|
|
27
|
+
/** Commit status context (unique per check in the merge box). */
|
|
28
|
+
context: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type GitHubBannerProgress = GitHubProgressPhaseCopy;
|
|
32
|
+
|
|
33
|
+
export interface GitHubProgressConfig {
|
|
34
|
+
/** When false, skip the eyes reaction on turn.started. Defaults to true. */
|
|
35
|
+
reactions?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Opt-in commit status on the PR head. Omit or `false` keeps today's
|
|
38
|
+
* behavior (no statuses from default events). With `cursorAccount`,
|
|
39
|
+
* needs `permissions: "contents-write"` (push-equivalent).
|
|
40
|
+
*/
|
|
41
|
+
commitStatus?: false | GitHubCommitStatusProgress;
|
|
42
|
+
/**
|
|
43
|
+
* Opt-in sticky PR/issue timeline comment. Created on first pending,
|
|
44
|
+
* PATCHed on later phases. Omit or `false` disables.
|
|
45
|
+
*/
|
|
46
|
+
banner?: false | GitHubBannerProgress;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface GitHubProgressOptions extends GitHubBindingConfig {
|
|
50
|
+
botName?: string;
|
|
51
|
+
progress?: GitHubProgressConfig;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function resolveCommitStatusProgress(
|
|
55
|
+
progress: GitHubProgressConfig | undefined
|
|
56
|
+
): GitHubCommitStatusProgress | null {
|
|
57
|
+
if (progress?.commitStatus === undefined || progress.commitStatus === false) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return progress.commitStatus;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function resolveBannerProgress(
|
|
64
|
+
progress: GitHubProgressConfig | undefined
|
|
65
|
+
): GitHubBannerProgress | null {
|
|
66
|
+
if (progress?.banner === undefined || progress.banner === false) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return progress.banner;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function progressPhaseCopy(input: {
|
|
73
|
+
copy: GitHubProgressPhaseCopy | undefined;
|
|
74
|
+
phase: GitHubProgressPhase;
|
|
75
|
+
label: string;
|
|
76
|
+
}): string {
|
|
77
|
+
const custom = input.copy?.[input.phase];
|
|
78
|
+
if (custom !== undefined && custom.trim() !== "") {
|
|
79
|
+
return custom;
|
|
80
|
+
}
|
|
81
|
+
switch (input.phase) {
|
|
82
|
+
case "pending":
|
|
83
|
+
return `${input.label} running`;
|
|
84
|
+
case "success":
|
|
85
|
+
return `${input.label} finished`;
|
|
86
|
+
case "failure":
|
|
87
|
+
return `${input.label} failed`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function clipStatusDescription(text: string): string {
|
|
92
|
+
if (text.length <= STATUS_DESCRIPTION_MAX) {
|
|
93
|
+
return text;
|
|
94
|
+
}
|
|
95
|
+
return `${text.slice(0, STATUS_DESCRIPTION_MAX - 1)}…`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function progressLabel(
|
|
99
|
+
options: GitHubProgressOptions,
|
|
100
|
+
context?: string
|
|
101
|
+
): string {
|
|
102
|
+
const bot = options.botName?.trim();
|
|
103
|
+
if (bot !== undefined && bot !== "") {
|
|
104
|
+
return bot;
|
|
105
|
+
}
|
|
106
|
+
if (context !== undefined && context.trim() !== "") {
|
|
107
|
+
return context;
|
|
108
|
+
}
|
|
109
|
+
return "Agent";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Post or update opt-in progress surfaces for a turn phase.
|
|
114
|
+
* Soft-fails on API errors (same posture as eyes / reply delivery).
|
|
115
|
+
*/
|
|
116
|
+
export async function applyGitHubProgress(input: {
|
|
117
|
+
phase: GitHubProgressPhase;
|
|
118
|
+
channel: { state: GitHubChannelState };
|
|
119
|
+
options: GitHubProgressOptions;
|
|
120
|
+
}): Promise<void> {
|
|
121
|
+
const commitStatus = resolveCommitStatusProgress(input.options.progress);
|
|
122
|
+
const banner = resolveBannerProgress(input.options.progress);
|
|
123
|
+
if (commitStatus === null && banner === null) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const tasks: Array<Promise<void>> = [];
|
|
128
|
+
if (commitStatus !== null) {
|
|
129
|
+
tasks.push(
|
|
130
|
+
postCommitStatus({
|
|
131
|
+
phase: input.phase,
|
|
132
|
+
channel: input.channel,
|
|
133
|
+
options: input.options,
|
|
134
|
+
commitStatus,
|
|
135
|
+
})
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (banner !== null) {
|
|
139
|
+
tasks.push(
|
|
140
|
+
upsertBanner({
|
|
141
|
+
phase: input.phase,
|
|
142
|
+
channel: input.channel,
|
|
143
|
+
options: input.options,
|
|
144
|
+
banner,
|
|
145
|
+
context: commitStatus?.context,
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
await Promise.all(tasks);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function postCommitStatus(input: {
|
|
153
|
+
phase: GitHubProgressPhase;
|
|
154
|
+
channel: { state: GitHubChannelState };
|
|
155
|
+
options: GitHubProgressOptions;
|
|
156
|
+
commitStatus: GitHubCommitStatusProgress;
|
|
157
|
+
}): Promise<void> {
|
|
158
|
+
const state = input.channel.state;
|
|
159
|
+
if (
|
|
160
|
+
state.owner === "" ||
|
|
161
|
+
state.repo === "" ||
|
|
162
|
+
state.headSha === null ||
|
|
163
|
+
state.headSha === ""
|
|
164
|
+
) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const description = clipStatusDescription(
|
|
168
|
+
progressPhaseCopy({
|
|
169
|
+
copy: input.commitStatus,
|
|
170
|
+
phase: input.phase,
|
|
171
|
+
label: progressLabel(input.options, input.commitStatus.context),
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
try {
|
|
175
|
+
await createGitHubCommitStatus({
|
|
176
|
+
api: input.options.api,
|
|
177
|
+
credentials: input.options.credentials,
|
|
178
|
+
installationId: state.installationId ?? undefined,
|
|
179
|
+
owner: state.owner,
|
|
180
|
+
repo: state.repo,
|
|
181
|
+
sha: state.headSha,
|
|
182
|
+
state: input.phase,
|
|
183
|
+
context: input.commitStatus.context,
|
|
184
|
+
description,
|
|
185
|
+
});
|
|
186
|
+
} catch {
|
|
187
|
+
// Soft-fail: missing token / permission is fine.
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function upsertBanner(input: {
|
|
192
|
+
phase: GitHubProgressPhase;
|
|
193
|
+
channel: { state: GitHubChannelState };
|
|
194
|
+
options: GitHubProgressOptions;
|
|
195
|
+
banner: GitHubBannerProgress;
|
|
196
|
+
context?: string;
|
|
197
|
+
}): Promise<void> {
|
|
198
|
+
const state = input.channel.state;
|
|
199
|
+
const issueNumber = state.issueNumber ?? state.pullRequestNumber;
|
|
200
|
+
if (state.owner === "" || state.repo === "" || issueNumber === null) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const body = formatGitHubOutboundComment(
|
|
204
|
+
progressPhaseCopy({
|
|
205
|
+
copy: input.banner,
|
|
206
|
+
phase: input.phase,
|
|
207
|
+
label: progressLabel(input.options, input.context),
|
|
208
|
+
})
|
|
209
|
+
);
|
|
210
|
+
const resource = {
|
|
211
|
+
api: input.options.api,
|
|
212
|
+
credentials: input.options.credentials,
|
|
213
|
+
installationId: state.installationId ?? undefined,
|
|
214
|
+
owner: state.owner,
|
|
215
|
+
repo: state.repo,
|
|
216
|
+
};
|
|
217
|
+
try {
|
|
218
|
+
if (state.progressBannerCommentId !== null) {
|
|
219
|
+
try {
|
|
220
|
+
await updateGitHubIssueComment({
|
|
221
|
+
...resource,
|
|
222
|
+
body,
|
|
223
|
+
commentId: state.progressBannerCommentId,
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (!(error instanceof GitHubApiError) || error.status !== 404) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Comment gone; clear sticky id and recreate below.
|
|
231
|
+
state.progressBannerCommentId = null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const posted = await createGitHubIssueComment({
|
|
235
|
+
...resource,
|
|
236
|
+
body,
|
|
237
|
+
issueNumber,
|
|
238
|
+
});
|
|
239
|
+
state.progressBannerCommentId = posted.id;
|
|
240
|
+
} catch {
|
|
241
|
+
// Soft-fail.
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -11,6 +11,8 @@ export interface GitHubChannelState extends GitHubBindingState {
|
|
|
11
11
|
headSha: string | null;
|
|
12
12
|
baseRef: string | null;
|
|
13
13
|
headRef: string | null;
|
|
14
|
+
/** Sticky progress banner comment id (issue/PR timeline). */
|
|
15
|
+
progressBannerCommentId: number | null;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function emptyGitHubChannelState(
|
|
@@ -32,6 +34,7 @@ export function emptyGitHubChannelState(
|
|
|
32
34
|
headSha: null,
|
|
33
35
|
baseRef: null,
|
|
34
36
|
headRef: null,
|
|
37
|
+
progressBannerCommentId: null,
|
|
35
38
|
...overrides,
|
|
36
39
|
};
|
|
37
40
|
}
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
} from "../../types.js";
|
|
10
10
|
import type { GitHubApiOptions } from "./api.js";
|
|
11
11
|
import type { GitHubHandle, GitHubThread } from "./binding.js";
|
|
12
|
-
import type { GitHubProgressConfig } from "./
|
|
12
|
+
import type { GitHubProgressConfig } from "./progress.js";
|
|
13
13
|
import type { GitHubChannelState } from "./state.js";
|
|
14
14
|
|
|
15
15
|
/** A credential value: a literal or a lazy resolver fetched on demand. */
|
|
@@ -171,6 +171,8 @@ export type GitHubPullRequestPayload = {
|
|
|
171
171
|
type?: string;
|
|
172
172
|
} | null;
|
|
173
173
|
installation?: { id?: number } | null;
|
|
174
|
+
/** Present on `labeled` / `unlabeled` pull_request deliveries. */
|
|
175
|
+
label?: { name: string } | null;
|
|
174
176
|
};
|
|
175
177
|
|
|
176
178
|
/** Normalized `pull_request` event handed to {@link GitHubPullRequestHook}. */
|
|
@@ -188,6 +190,8 @@ export interface GitHubPullRequestEvent {
|
|
|
188
190
|
/** True when the PR was merged (from the webhook payload). */
|
|
189
191
|
merged: boolean;
|
|
190
192
|
authorLogin?: string;
|
|
193
|
+
/** Label added/removed on `labeled` / `unlabeled` actions. */
|
|
194
|
+
label?: { name: string };
|
|
191
195
|
}
|
|
192
196
|
|
|
193
197
|
/** Normalized `issues` event. */
|
|
@@ -448,7 +452,7 @@ export interface GitHubChannelOptions {
|
|
|
448
452
|
* Token capability tier:
|
|
449
453
|
* - `"read"` — PR/issue/status read
|
|
450
454
|
* - `"pr-write"` (default) — comments / PR+issue write, no `contents:write`
|
|
451
|
-
* - `"contents-write"` —
|
|
455
|
+
* - `"contents-write"` — push code and create commit statuses (host-held)
|
|
452
456
|
*/
|
|
453
457
|
permissions?: "read" | "pr-write" | "contents-write";
|
|
454
458
|
};
|
|
@@ -500,10 +504,14 @@ export interface GitHubChannelOptions {
|
|
|
500
504
|
onEvent?: GitHubEventHook;
|
|
501
505
|
/**
|
|
502
506
|
* Override / extend default stream-event handlers (eyes reaction, reply
|
|
503
|
-
* posting). Handlers you provide replace
|
|
507
|
+
* posting, opt-in commit status / banner). Handlers you provide replace
|
|
508
|
+
* the matching defaults.
|
|
504
509
|
*/
|
|
505
510
|
events?: ChannelEventHandlers<GitHubChannelState>;
|
|
506
|
-
/**
|
|
511
|
+
/**
|
|
512
|
+
* Acknowledgement progress surfaces. Reactions default on; commit status
|
|
513
|
+
* and sticky banner are opt-in via `commitStatus` / `banner`.
|
|
514
|
+
*/
|
|
507
515
|
progress?: GitHubProgressConfig;
|
|
508
516
|
/**
|
|
509
517
|
* When false, skip posting assistant / error comments back to GitHub.
|
|
@@ -117,6 +117,10 @@ export function resolveDedicatedSlackHostBinding(
|
|
|
117
117
|
/**
|
|
118
118
|
* Create a Slack channel. Author as `agent/channels/slack.ts` default export.
|
|
119
119
|
*
|
|
120
|
+
* Missing credentials are not an error: without a token pair (or a
|
|
121
|
+
* signed-in host for `cursorAccount`) the channel mounts idle and logs
|
|
122
|
+
* `channel idle`, so serving an agent never blocks on Slack setup.
|
|
123
|
+
*
|
|
120
124
|
* When `cursorAccount` is on, `agentName` must be a single token (no
|
|
121
125
|
* whitespace) — string literals with spaces fail at the type level;
|
|
122
126
|
* runtime values are still checked by {@link assertSlackCursorAgentName}.
|
package/src/channels.ts
CHANGED
|
@@ -383,3 +383,41 @@ export function allowAll(): AuthPolicy {
|
|
|
383
383
|
principalType: "user",
|
|
384
384
|
});
|
|
385
385
|
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* `Symbol.for` so the marker survives duplicated copies of the framework in
|
|
389
|
+
* one process (e.g. a monorepo agent resolving its own `@cursor/july`).
|
|
390
|
+
*/
|
|
391
|
+
const PUBLIC_ENDPOINT_MARKER = Symbol.for("agent-serve.public-endpoint");
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Like {@link allowAll}, but ALSO declares the channel publicly reachable on
|
|
395
|
+
* Cursor's managed hosting: `deploy` collects every channel whose auth chain
|
|
396
|
+
* contains this policy and the platform serves that channel's routes
|
|
397
|
+
* (`/v1/channels/<id>/…`) through the stable alias WITHOUT
|
|
398
|
+
* `X-Agent-Alias-Token`.
|
|
399
|
+
*
|
|
400
|
+
* Use it for webhook senders that cannot attach custom headers (Linear,
|
|
401
|
+
* Stripe, …) — **the handler must authenticate deliveries itself**, e.g. by
|
|
402
|
+
* verifying the provider's HMAC signature over the raw body. Everything
|
|
403
|
+
* else (sessions, MCP, playground) stays behind platform auth.
|
|
404
|
+
*
|
|
405
|
+
* Locally this admits every caller, exactly like `allowAll()`.
|
|
406
|
+
*/
|
|
407
|
+
export function publicEndpoint(): AuthPolicy {
|
|
408
|
+
const policy: AuthPolicy = () => ({
|
|
409
|
+
authenticator: "public-endpoint",
|
|
410
|
+
principalId: "anonymous",
|
|
411
|
+
principalType: "user",
|
|
412
|
+
});
|
|
413
|
+
return Object.assign(policy, { [PUBLIC_ENDPOINT_MARKER]: true as const });
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** True when a policy was created by {@link publicEndpoint}. */
|
|
417
|
+
export function isPublicEndpointPolicy(policy: AuthPolicy): boolean {
|
|
418
|
+
return (
|
|
419
|
+
(policy as { [PUBLIC_ENDPOINT_MARKER]?: unknown })[
|
|
420
|
+
PUBLIC_ENDPOINT_MARKER
|
|
421
|
+
] === true
|
|
422
|
+
);
|
|
423
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ export {
|
|
|
48
48
|
PATCH,
|
|
49
49
|
POST,
|
|
50
50
|
PUT,
|
|
51
|
+
publicEndpoint,
|
|
51
52
|
} from "./channels.js";
|
|
52
53
|
export { defineConnection } from "./connections.js";
|
|
53
54
|
export { defineHook } from "./hooks.js";
|
|
@@ -104,6 +105,12 @@ export {
|
|
|
104
105
|
fileMemoryBackend,
|
|
105
106
|
memoryHook,
|
|
106
107
|
} from "./memory.js";
|
|
108
|
+
export type {
|
|
109
|
+
OtelConfig,
|
|
110
|
+
OtelDefinition,
|
|
111
|
+
OtelExporterConfig,
|
|
112
|
+
} from "./otel.js";
|
|
113
|
+
export { defineOtel } from "./otel.js";
|
|
107
114
|
export type { PromptTag } from "./prompt.js";
|
|
108
115
|
export { prompt } from "./prompt.js";
|
|
109
116
|
export type {
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { lookupSessionContinuation, runChat } from "./chat-client.js";
|
|
|
23
23
|
import { cmdLogin } from "./cli-cursor.js";
|
|
24
24
|
import { resolveDeployAuth } from "./cli-deploy.js";
|
|
25
25
|
import { resolveApiKey } from "./cursor/credentials.js";
|
|
26
|
-
import { CLI_COMMAND_NAME } from "./distribution.js";
|
|
26
|
+
import { CLI_COMMAND_NAME, PACKAGE_NAME } from "./distribution.js";
|
|
27
27
|
import {
|
|
28
28
|
discoverEvals,
|
|
29
29
|
type EvalRunResult,
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
startRemoteEvalRun,
|
|
42
42
|
} from "./evals-client.js";
|
|
43
43
|
import {
|
|
44
|
+
checkInitTarget,
|
|
44
45
|
formatInitNextSteps,
|
|
45
46
|
formatInitScaffoldSummary,
|
|
46
47
|
initNextCommands,
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
installScaffoldDependencies,
|
|
49
50
|
linkCliOntoPath,
|
|
50
51
|
printInitGuide,
|
|
52
|
+
resolveTemplate,
|
|
51
53
|
} from "./init-project.js";
|
|
52
54
|
import { offerInstallCursorSkills } from "./install-cursor-skills.js";
|
|
53
55
|
import { runLogsTail, sanitizeCustomerError } from "./logs-client.js";
|
|
@@ -131,6 +133,7 @@ export interface AxCliOptions {
|
|
|
131
133
|
out?: string;
|
|
132
134
|
/** Scaffold target directory for `init`. */
|
|
133
135
|
initTarget?: string;
|
|
136
|
+
template?: string;
|
|
134
137
|
/** Tool name for `call`. */
|
|
135
138
|
toolName?: string;
|
|
136
139
|
/** JSON input for `call`. */
|
|
@@ -288,9 +291,23 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
288
291
|
printInitGuide();
|
|
289
292
|
return 0;
|
|
290
293
|
}
|
|
294
|
+
let templateDir: string | undefined;
|
|
295
|
+
if (options.template !== undefined) {
|
|
296
|
+
const template = resolveTemplate(options.template);
|
|
297
|
+
if (!template.ok) {
|
|
298
|
+
process.stderr.write(`${template.message}\n`);
|
|
299
|
+
return 1;
|
|
300
|
+
}
|
|
301
|
+
templateDir = template.dir;
|
|
302
|
+
}
|
|
291
303
|
const cwd = options.cwd ?? process.cwd();
|
|
292
304
|
const dir = resolve(cwd, target);
|
|
293
|
-
const
|
|
305
|
+
const targetCheck = checkInitTarget(dir, options.template);
|
|
306
|
+
if (!targetCheck.ok) {
|
|
307
|
+
process.stderr.write(`${targetCheck.message}\n`);
|
|
308
|
+
return 1;
|
|
309
|
+
}
|
|
310
|
+
const result = await initProject({ dir, templateDir });
|
|
294
311
|
let signedIn = (await resolveApiKey()) !== undefined;
|
|
295
312
|
|
|
296
313
|
// Machine-readable path: install deps (piped), never block on browser login.
|
|
@@ -307,6 +324,7 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
307
324
|
{
|
|
308
325
|
ok: install.ok,
|
|
309
326
|
directory: result.rootDir,
|
|
327
|
+
template: options.template ?? null,
|
|
310
328
|
created: result.created,
|
|
311
329
|
skipped: result.skipped,
|
|
312
330
|
installed: install.ok,
|
|
@@ -356,7 +374,7 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
356
374
|
const cliOnPath = linked.ok;
|
|
357
375
|
if (!cliOnPath) {
|
|
358
376
|
process.stderr.write(
|
|
359
|
-
`\nCould not put ${CLI_COMMAND_NAME} on PATH (${linked.stderr.trim() || `exit ${linked.exitCode}`}). Next steps
|
|
377
|
+
`\nCould not put ${CLI_COMMAND_NAME} on PATH (${linked.stderr.trim() || `exit ${linked.exitCode}`}). Next steps use npx ${PACKAGE_NAME} instead.\n\n`
|
|
360
378
|
);
|
|
361
379
|
} else {
|
|
362
380
|
process.stdout.write("\n");
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { basename, resolve } from "node:path";
|
|
13
|
+
import { isPublicEndpointPolicy } from "../channels.js";
|
|
13
14
|
import {
|
|
14
15
|
type AgentHostingOptions,
|
|
15
16
|
isCursorAccountTransport,
|
|
@@ -633,7 +634,8 @@ async function deployOne(
|
|
|
633
634
|
// truth for egress domains and expected secret names; flags override or
|
|
634
635
|
// augment it. Directories without an agent project (static placeholder
|
|
635
636
|
// deploys) simply have no manifest. The same load supplies the
|
|
636
|
-
// cursorAccount connection refs for the post-deploy connector auth
|
|
637
|
+
// cursorAccount connection refs for the post-deploy connector auth and
|
|
638
|
+
// the channels declared public via `publicEndpoint()`.
|
|
637
639
|
const manifest = isAgent ? await readDeployManifest(dir) : undefined;
|
|
638
640
|
const hosting = manifest?.hosting;
|
|
639
641
|
const egressAllowedDomains = [
|
|
@@ -642,6 +644,21 @@ async function deployOne(
|
|
|
642
644
|
...(options.allowDomains ?? []),
|
|
643
645
|
]),
|
|
644
646
|
];
|
|
647
|
+
const publicRoutePaths = (manifest?.publicChannelIds ?? []).map(
|
|
648
|
+
(channelId) => `/v1/channels/${channelId}`
|
|
649
|
+
);
|
|
650
|
+
if (!ctx.json && manifest?.httpChannelPublicEndpoint === true) {
|
|
651
|
+
ctx.err(
|
|
652
|
+
`warning: publicEndpoint() on the built-in HTTP channel has no effect on hosting — only custom channels (/v1/channels/…) can be public; the session API always requires the alias token.\n`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
if (!ctx.json && publicRoutePaths.length > 0) {
|
|
656
|
+
// Security-relevant surface change: say exactly what will be reachable
|
|
657
|
+
// without the alias token before the deploy is accepted.
|
|
658
|
+
ctx.err(
|
|
659
|
+
` public: ${publicRoutePaths.join(", ")} (served WITHOUT the alias token — these channels authenticate requests themselves)\n`
|
|
660
|
+
);
|
|
661
|
+
}
|
|
645
662
|
|
|
646
663
|
// Today's only source shape: the control plane builds from a git ref (or
|
|
647
664
|
// serves the static placeholder). See DeploySource for the artifact seam.
|
|
@@ -655,6 +672,8 @@ async function deployOne(
|
|
|
655
672
|
? {}
|
|
656
673
|
: { cursorEventRepos: options.cursorEventsRepos }),
|
|
657
674
|
...(egressAllowedDomains.length === 0 ? {} : { egressAllowedDomains }),
|
|
675
|
+
publicRoutePaths:
|
|
676
|
+
publicRoutePaths.length === 0 ? undefined : publicRoutePaths,
|
|
658
677
|
};
|
|
659
678
|
|
|
660
679
|
let accepted: DeployAccepted;
|
|
@@ -716,6 +735,7 @@ async function deployOne(
|
|
|
716
735
|
ctx.err(`Deployment ${slug} ended ${detail.status}${errorDetail}\n`);
|
|
717
736
|
return 1;
|
|
718
737
|
}
|
|
738
|
+
const deployedPublicRoutes = detail.publicRoutePaths ?? [];
|
|
719
739
|
ctx.out(
|
|
720
740
|
`Deployed ${slug} (team ${ctx.teamId})\n` +
|
|
721
741
|
` status: running\n` +
|
|
@@ -723,7 +743,12 @@ async function deployOne(
|
|
|
723
743
|
` kind: ${detail.deploymentKind}\n` +
|
|
724
744
|
` alias: ${aliasUrl}\n` +
|
|
725
745
|
` playground: ${aliasUrl}/playground (sign in with the alias token)\n` +
|
|
726
|
-
` open: ${CLI} playground --prod --slug ${slug} --team ${ctx.teamId}\n`
|
|
746
|
+
` open: ${CLI} playground --prod --slug ${slug} --team ${ctx.teamId}\n` +
|
|
747
|
+
// Paste-ready webhook URLs: these subtrees skip the alias token, so
|
|
748
|
+
// the channel handler must verify deliveries (webhook signatures).
|
|
749
|
+
deployedPublicRoutes
|
|
750
|
+
.map((path) => ` public: ${aliasUrl}${path} (no alias token)\n`)
|
|
751
|
+
.join("")
|
|
727
752
|
);
|
|
728
753
|
|
|
729
754
|
await runPostDeployConnectorAuth(ctx, options, {
|
|
@@ -828,11 +853,23 @@ interface DeployManifest {
|
|
|
828
853
|
name: string;
|
|
829
854
|
servers: string[] | undefined;
|
|
830
855
|
}>;
|
|
856
|
+
/**
|
|
857
|
+
* Channels whose authored auth chain contains `publicEndpoint()`: their
|
|
858
|
+
* `/v1/channels/<id>` subtree is served through the stable alias WITHOUT
|
|
859
|
+
* `X-Agent-Alias-Token` (the channel authenticates deliveries itself).
|
|
860
|
+
*/
|
|
861
|
+
publicChannelIds: string[];
|
|
862
|
+
/**
|
|
863
|
+
* `publicEndpoint()` on the built-in HTTP channel — a no-op on hosting
|
|
864
|
+
* (only `/v1/channels/…` subtrees can be public); surfaced as a warning.
|
|
865
|
+
*/
|
|
866
|
+
httpChannelPublicEndpoint: boolean;
|
|
831
867
|
}
|
|
832
868
|
|
|
833
869
|
/**
|
|
834
870
|
* Read the deploy-relevant bits of the project manifest at `dir` in one
|
|
835
|
-
* load: the `hosting` block
|
|
871
|
+
* load: the `hosting` block, the agent's cursorAccount connections, and the
|
|
872
|
+
* channels declared public via `publicEndpoint()`.
|
|
836
873
|
*/
|
|
837
874
|
async function readDeployManifest(dir: string): Promise<DeployManifest> {
|
|
838
875
|
const project = await loadAgentProject(dir);
|
|
@@ -843,6 +880,14 @@ async function readDeployManifest(dir: string): Promise<DeployManifest> {
|
|
|
843
880
|
? [{ name: connection.name, servers: connection.transport.servers }]
|
|
844
881
|
: []
|
|
845
882
|
),
|
|
883
|
+
publicChannelIds: project.channels
|
|
884
|
+
.filter((channel) =>
|
|
885
|
+
(channel.definition.auth ?? []).some(isPublicEndpointPolicy)
|
|
886
|
+
)
|
|
887
|
+
.map((channel) => channel.id),
|
|
888
|
+
httpChannelPublicEndpoint: (project.httpChannel.auth ?? []).some(
|
|
889
|
+
isPublicEndpointPolicy
|
|
890
|
+
),
|
|
846
891
|
};
|
|
847
892
|
}
|
|
848
893
|
|
|
@@ -985,6 +1030,16 @@ export async function cmdDeploymentStatus(
|
|
|
985
1030
|
) {
|
|
986
1031
|
lines.push(` egress: ${detail.egressAllowedDomains.join(", ")}`);
|
|
987
1032
|
}
|
|
1033
|
+
if (
|
|
1034
|
+
detail.publicRoutePaths !== undefined &&
|
|
1035
|
+
detail.publicRoutePaths.length > 0
|
|
1036
|
+
) {
|
|
1037
|
+
// Served through the alias WITHOUT X-Agent-Alias-Token (the channel
|
|
1038
|
+
// authenticates deliveries itself).
|
|
1039
|
+
for (const path of detail.publicRoutePaths) {
|
|
1040
|
+
lines.push(` public: ${aliasUrl}${path} (no alias token)`);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
988
1043
|
if (detail.secretNames !== undefined && detail.secretNames.length > 0) {
|
|
989
1044
|
lines.push(` secrets: ${detail.secretNames.join(", ")}`);
|
|
990
1045
|
}
|
|
@@ -69,6 +69,12 @@ export type DeploySource = {
|
|
|
69
69
|
* deploys — the backend 400s when set on a static deploy.
|
|
70
70
|
*/
|
|
71
71
|
egressAllowedDomains?: string[];
|
|
72
|
+
/**
|
|
73
|
+
* `/v1/channels/…` subtrees the stable alias serves WITHOUT
|
|
74
|
+
* `X-Agent-Alias-Token` (channels authored with `publicEndpoint()`).
|
|
75
|
+
* Only valid on repo-backed deploys.
|
|
76
|
+
*/
|
|
77
|
+
publicRoutePaths?: string[];
|
|
72
78
|
};
|
|
73
79
|
|
|
74
80
|
const SLUG_PATTERN = /^[a-z0-9_-]{1,64}$/;
|
|
@@ -158,6 +164,8 @@ export interface Deployment {
|
|
|
158
164
|
cursorEventRepos?: string[];
|
|
159
165
|
/** Normalized egress allowlist for the engine pod. */
|
|
160
166
|
egressAllowedDomains?: string[];
|
|
167
|
+
/** Channel subtrees the alias serves without `X-Agent-Alias-Token`. */
|
|
168
|
+
publicRoutePaths?: string[];
|
|
161
169
|
/** Service-account id (`sa_…`); null on legacy rows. */
|
|
162
170
|
serviceAccountId?: string | number | null;
|
|
163
171
|
/**
|
|
@@ -297,6 +305,7 @@ const deploymentSchema = z.object({
|
|
|
297
305
|
lastReconciledAt: z.string().nullable().optional(),
|
|
298
306
|
cursorEventRepos: z.array(z.string()).optional(),
|
|
299
307
|
egressAllowedDomains: z.array(z.string()).optional(),
|
|
308
|
+
publicRoutePaths: z.array(z.string()).optional(),
|
|
300
309
|
// String ids (`sa_…`) in prod; older mocks/fixtures used numbers. Null on
|
|
301
310
|
// legacy rows that predate service-account-backed deploys.
|
|
302
311
|
serviceAccountId: z.union([z.string(), z.number()]).nullable().optional(),
|
|
@@ -441,6 +450,8 @@ export class AgentServeDeployClient {
|
|
|
441
450
|
...(args.source.egressAllowedDomains === undefined
|
|
442
451
|
? {}
|
|
443
452
|
: { egressAllowedDomains: args.source.egressAllowedDomains }),
|
|
453
|
+
// JSON.stringify drops undefined keys.
|
|
454
|
+
publicRoutePaths: args.source.publicRoutePaths,
|
|
444
455
|
};
|
|
445
456
|
const raw = await this.request({
|
|
446
457
|
method: "POST",
|