@cursor/july 0.1.61 → 0.1.68
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 +8 -0
- package/dist/channels/github/github-channel.d.ts +1 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +14 -1
- package/dist/channels/github/types.d.ts +2 -0
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.d.ts +2 -1
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +7 -3
- package/dist/channels/slack/dispatch.d.ts +9 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +41 -21
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +1 -0
- package/dist/channels/slack/nudge-store.d.ts +16 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -1
- package/dist/channels/slack/nudge-store.js +53 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +4 -1
- package/dist/channels/slack/types.d.ts +22 -5
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.CW3KJh72.js → app.C5XUTCw9.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DvUR3lv9.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.Bk6EWpPj.js → VPLocalSearchBox.CGDTAYHY.js} +1 -1
- package/dist/docs/assets/chunks/{theme.Bz9AodBA.js → theme.lPixh250.js} +2 -2
- package/dist/docs/assets/{reference_connections.md.CSQiNzre.js → reference_connections.md.Bwswbm6H.js} +13 -3
- package/dist/docs/assets/{reference_connections.md.CSQiNzre.lean.js → reference_connections.md.Bwswbm6H.lean.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.kJfJAP2m.js → reference_tools.md.Wmvb7Lob.js} +2 -2
- package/dist/docs/assets/{reference_tools.md.kJfJAP2m.lean.js → reference_tools.md.Wmvb7Lob.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/convert-automation.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/opentelemetry.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +15 -5
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +5 -5
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/templates/agentic-owners.html +3 -3
- package/dist/docs/templates/demo.html +3 -3
- package/dist/docs/templates/pr-autofixer.html +3 -3
- package/dist/docs/templates/security-reviewer.html +3 -3
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/files-backends/agent-store-presigned-url.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/advertise-tools.d.ts +74 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -0
- package/dist/internal/advertise-tools.js +152 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +11 -3
- package/dist/internal/cli-manifest.d.ts +19 -0
- package/dist/internal/cli-manifest.d.ts.map +1 -0
- package/dist/internal/cli-manifest.js +49 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +9 -1
- package/dist/internal/cursor-agent-template.d.ts +9 -0
- package/dist/internal/cursor-agent-template.d.ts.map +1 -0
- package/dist/internal/cursor-agent-template.js +37 -0
- package/dist/internal/cursor-event-relay.d.ts +5 -1
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +5 -0
- 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 +21 -3
- package/dist/internal/deploy-manifest.d.ts +85 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -0
- package/dist/internal/deploy-manifest.js +106 -0
- package/dist/internal/deployment-scm-event-stream.d.ts +113 -0
- package/dist/internal/deployment-scm-event-stream.d.ts.map +1 -0
- package/dist/internal/deployment-scm-event-stream.js +460 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +69 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +5 -17
- package/dist/internal/mcp-host.d.ts +18 -0
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +41 -5
- package/dist/internal/mcp-oauth.d.ts +0 -4
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +0 -4
- 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 +36 -13
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +20 -2
- package/dist/internal/session-engine.d.ts +66 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +430 -110
- package/dist/internal/shallow-clone.d.ts +115 -0
- package/dist/internal/shallow-clone.d.ts.map +1 -0
- package/dist/internal/shallow-clone.js +292 -0
- package/dist/internal/slack-provision-client.d.ts +2 -0
- package/dist/internal/slack-provision-client.d.ts.map +1 -1
- package/dist/internal/slack-provision-client.js +1 -0
- package/dist/internal/turn-lease-store.d.ts +44 -0
- package/dist/internal/turn-lease-store.d.ts.map +1 -0
- package/dist/internal/turn-lease-store.js +139 -0
- package/dist/internal/workspace.d.ts +12 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +28 -0
- package/dist/playground/assets/index-CoL7xCJl.css +1 -0
- package/dist/playground/assets/index-yxs1heea.js +85 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-protocol.d.ts +122 -0
- package/dist/storage-protocol.d.ts.map +1 -0
- package/dist/storage-protocol.js +173 -0
- package/dist/storage.d.ts +2 -72
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +2 -82
- package/dist/types.d.ts +114 -4
- package/dist/types.d.ts.map +1 -1
- package/docs/reference/connections.md +80 -0
- package/docs/reference/tools.md +14 -0
- package/package.json +4 -2
- package/skills/github/SKILL.md +2 -1
- package/src/bin/agent-serve.ts +9 -0
- package/src/channels/github/github-channel.ts +18 -2
- package/src/channels/github/types.ts +2 -0
- package/src/channels/slack/channel-watch.ts +6 -3
- package/src/channels/slack/dispatch.ts +40 -3
- package/src/channels/slack/manifest.ts +1 -0
- package/src/channels/slack/nudge-store.ts +56 -0
- package/src/channels/slack/slack-channel.ts +3 -0
- package/src/channels/slack/types.ts +22 -5
- package/src/index.ts +5 -0
- package/src/internal/advertise-tools.ts +229 -0
- package/src/internal/cli-deploy.ts +22 -1
- package/src/internal/cli-manifest.ts +63 -0
- package/src/internal/cli-slack.ts +10 -1
- package/src/internal/cursor-agent-template.ts +46 -0
- package/src/internal/cursor-event-relay.ts +8 -1
- package/src/internal/deploy-client.ts +35 -6
- package/src/internal/deploy-manifest.ts +167 -0
- package/src/internal/deployment-scm-event-stream.ts +577 -0
- package/src/internal/discovery.ts +85 -0
- package/src/internal/init-project.ts +11 -20
- package/src/internal/mcp-host.ts +66 -9
- package/src/internal/mcp-oauth.ts +0 -4
- package/src/internal/sdk-runner.ts +50 -11
- package/src/internal/server.ts +21 -2
- package/src/internal/session-engine.ts +385 -19
- package/src/internal/shallow-clone.ts +407 -0
- package/src/internal/slack-provision-client.ts +3 -0
- package/src/internal/turn-lease-store.ts +147 -0
- package/src/internal/workspace.ts +27 -0
- package/src/storage-protocol.ts +247 -0
- package/src/storage.ts +16 -110
- package/src/types.ts +118 -4
- package/templates/pr-autofixer/agent/lib/drive-schemas.ts +4 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BuymfBAo.js +0 -1
- package/dist/playground/assets/index-ClbuaLpm.css +0 -1
- package/dist/playground/assets/index-SqkR13k_.js +0 -85
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shallow GitHub PR checkout for SDK agents that review a PR's code.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors Bugbot's `shallow_fetch` prefetch strategy
|
|
5
|
+
* (`backend/crates/napi_bugbot/src/temp_clone.rs`): instead of cloning the
|
|
6
|
+
* whole repository, run a single `git fetch --depth=1 --no-tags origin <head>
|
|
7
|
+
* <merge_base>` into a fresh checkout so the agents get the PR head working
|
|
8
|
+
* tree (plus the merge-base commit for `git diff baseSha headSha`) with one
|
|
9
|
+
* bounded network round-trip. Bugbot keeps a bare `--filter=tree:0` partial
|
|
10
|
+
* clone because its tools stream objects on demand; SDK agents read a real
|
|
11
|
+
* working tree, so we materialize the head checkout instead.
|
|
12
|
+
*
|
|
13
|
+
* Auth also mirrors Bugbot's provider model (`IGitProvider.getAccessToken`
|
|
14
|
+
* → token handed straight to git): the clone takes one explicit repo-scoped
|
|
15
|
+
* bearer token. The default source is the serve host's Cursor-account
|
|
16
|
+
* GitHub lease — the same backend `GithubAccessService` mint Bugbot uses —
|
|
17
|
+
* which `CursorGithubCredentialProvider` installs as `GH_TOKEN` for child
|
|
18
|
+
* processes. Where Bugbot embeds the token in the clone URL, this module
|
|
19
|
+
* serves it through an inline credential helper that reads a child-process
|
|
20
|
+
* env var, so the token never lands in argv or in `.git/config` inside a
|
|
21
|
+
* working tree that untrusted PR code later executes in.
|
|
22
|
+
*
|
|
23
|
+
* INTERNAL: first-party host plumbing shared by agents in `factory/*` (e.g.
|
|
24
|
+
* the security reviewer's `prepare_review` stage). Consumed via relative
|
|
25
|
+
* source imports, following `tenant-mcp/run-mcp-registry.ts`.
|
|
26
|
+
*/
|
|
27
|
+
export type ShallowPrCheckout = {
|
|
28
|
+
/** Directory containing the PR head working tree (detached at headSha). */
|
|
29
|
+
checkoutDir: string;
|
|
30
|
+
headSha: string;
|
|
31
|
+
/** True when the merge-base commit was also fetched (enables local diffs). */
|
|
32
|
+
baseFetched: boolean;
|
|
33
|
+
/** How the head commit was obtained. */
|
|
34
|
+
headSource: "sha_fetch" | "pr_ref_fetch";
|
|
35
|
+
};
|
|
36
|
+
/** Repo-scoped bearer for git-over-HTTPS (installation token or PAT). */
|
|
37
|
+
export type ShallowCloneAuth = {
|
|
38
|
+
token: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Default clone credential: the serve host's Cursor-account GitHub lease.
|
|
42
|
+
* `CursorGithubCredentialProvider` mints it repo-scoped from the backend and
|
|
43
|
+
* keeps `GH_TOKEN` refreshed for child processes; `GITHUB_TOKEN` covers
|
|
44
|
+
* local/CI runs that export the conventional variable instead.
|
|
45
|
+
*/
|
|
46
|
+
export declare function serveHostGithubAuth(env?: NodeJS.ProcessEnv): ShallowCloneAuth | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Create a fresh shallow checkout of a GitHub PR head at `checkoutDir`.
|
|
49
|
+
*
|
|
50
|
+
* Fetch order (both single depth-1 round-trips):
|
|
51
|
+
* 1. Fetch `headSha` (and `baseSha` when known) directly — GitHub advertises
|
|
52
|
+
* `allow-reachable-sha1-in-want`, and this pins the exact reviewed commit
|
|
53
|
+
* the way Bugbot's `shallow_fetch` does.
|
|
54
|
+
* 2. Fall back to `refs/pull/<n>/head`, verifying it still resolves to
|
|
55
|
+
* `headSha` so the working tree cannot silently drift from the pinned
|
|
56
|
+
* patch in the review bundle.
|
|
57
|
+
*
|
|
58
|
+
* A missing merge-base commit is non-fatal (agents still have the pinned
|
|
59
|
+
* patch text in their prompt); a missing head checkout throws.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createShallowPrCheckout(args: {
|
|
62
|
+
/** `owner/name`. */
|
|
63
|
+
repo: string;
|
|
64
|
+
prNumber?: number | undefined;
|
|
65
|
+
headSha: string;
|
|
66
|
+
/** Merge-base SHA; fetched too so `git diff baseSha headSha` works locally. */
|
|
67
|
+
baseSha?: string | undefined;
|
|
68
|
+
checkoutDir: string;
|
|
69
|
+
/** Clone credential; required for GitHub remotes, unused for file fixtures. */
|
|
70
|
+
auth?: ShallowCloneAuth | undefined;
|
|
71
|
+
/** Test seam: clone from this URL instead of `https://github.com/<repo>.git`. */
|
|
72
|
+
remoteUrl?: string | undefined;
|
|
73
|
+
}): Promise<ShallowPrCheckout>;
|
|
74
|
+
/** True for `https://github.com/<owner>/<repo>/pull/<n>` URLs. */
|
|
75
|
+
export declare function isGithubPrUrl(prUrl: string): boolean;
|
|
76
|
+
export type TryShallowPrCheckoutOptions = {
|
|
77
|
+
/** PR URL; only GitHub PR URLs are cloned (others skip). */
|
|
78
|
+
prUrl: string;
|
|
79
|
+
/** `owner/name`. */
|
|
80
|
+
repo: string;
|
|
81
|
+
prNumber?: number | undefined;
|
|
82
|
+
/** Pinned head SHA; skipped (undefined result) when unknown. */
|
|
83
|
+
headSha?: string | undefined;
|
|
84
|
+
/** Merge-base SHA; fetched too so `git diff baseSha headSha` works locally. */
|
|
85
|
+
baseSha?: string | undefined;
|
|
86
|
+
checkoutDir: string;
|
|
87
|
+
/** Clone credential; defaults to {@link serveHostGithubAuth}. */
|
|
88
|
+
auth?: ShallowCloneAuth | undefined;
|
|
89
|
+
/** Test seam: clone from this URL instead of `https://github.com/<repo>.git`. */
|
|
90
|
+
remoteUrl?: string | undefined;
|
|
91
|
+
};
|
|
92
|
+
export type TryShallowPrCheckoutResult = {
|
|
93
|
+
/** Set when the shallow checkout succeeded. */
|
|
94
|
+
checkout?: ShallowPrCheckout;
|
|
95
|
+
/**
|
|
96
|
+
* Why there is no checkout: a skip gate or the clone failure's first error
|
|
97
|
+
* line. Callers surface this in their own results/logs so the reason is
|
|
98
|
+
* visible on hosts that do not capture this module's stderr.
|
|
99
|
+
*/
|
|
100
|
+
fallbackReason?: string;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Best-effort shallow PR checkout for hosts whose SDK agents should read the
|
|
104
|
+
* reviewed repo instead of the host's own checkout.
|
|
105
|
+
*
|
|
106
|
+
* Returns no checkout — leaving the caller on its fallback working
|
|
107
|
+
* directory — when the PR is not on GitHub (e.g. Origin PRs, where the host
|
|
108
|
+
* checkout is the reviewed repo), when no head SHA is pinned, when no clone
|
|
109
|
+
* credential is available, or when the clone fails; `fallbackReason` says
|
|
110
|
+
* which. Failures are logged, never thrown: agents still have the pinned
|
|
111
|
+
* patch in their prompts, so a missing checkout degrades quality rather
|
|
112
|
+
* than the run.
|
|
113
|
+
*/
|
|
114
|
+
export declare function tryCreateShallowPrCheckout(options: TryShallowPrCheckoutOptions): Promise<TryShallowPrCheckoutResult>;
|
|
115
|
+
//# sourceMappingURL=shallow-clone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shallow-clone.d.ts","sourceRoot":"","sources":["../../src/internal/shallow-clone.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,EAAE,OAAO,CAAC;IACrB,wCAAwC;IACxC,UAAU,EAAE,WAAW,GAAG,cAAc,CAAC;CAC1C,CAAC;AAEF,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,gBAAgB,GAAG,SAAS,CAG9B;AA0GD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgH7B;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAapD;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,IAAI,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CA2CrC"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { execFile } from "node:child_process";
|
|
11
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
12
|
+
import { promisify } from "node:util";
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
/**
|
|
15
|
+
* Default clone credential: the serve host's Cursor-account GitHub lease.
|
|
16
|
+
* `CursorGithubCredentialProvider` mints it repo-scoped from the backend and
|
|
17
|
+
* keeps `GH_TOKEN` refreshed for child processes; `GITHUB_TOKEN` covers
|
|
18
|
+
* local/CI runs that export the conventional variable instead.
|
|
19
|
+
*/
|
|
20
|
+
export function serveHostGithubAuth(env = process.env) {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
const token = ((_b = (_a = env.GH_TOKEN) !== null && _a !== void 0 ? _a : env.GITHUB_TOKEN) !== null && _b !== void 0 ? _b : "").trim();
|
|
23
|
+
return token === "" ? undefined : { token };
|
|
24
|
+
}
|
|
25
|
+
function logShallowClone(message, fields = {}) {
|
|
26
|
+
const suffix = Object.entries(fields)
|
|
27
|
+
.filter(([, value]) => value !== undefined)
|
|
28
|
+
.map(([key, value]) => `${key}=${String(value)}`)
|
|
29
|
+
.join(" ");
|
|
30
|
+
process.stderr.write(`[shallow-clone] ${message}${suffix ? ` ${suffix}` : ""}\n`);
|
|
31
|
+
}
|
|
32
|
+
function errorSummary(error) {
|
|
33
|
+
var _a;
|
|
34
|
+
return error instanceof Error
|
|
35
|
+
? ((_a = error.message.split("\n")[0]) !== null && _a !== void 0 ? _a : String(error))
|
|
36
|
+
: String(error);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Single-line git failure: the invoked git args (without the credential
|
|
40
|
+
* config noise) plus the first meaningful stderr line — the actual git
|
|
41
|
+
* diagnostic (auth, ref, network) — so fallback reasons surfaced to tool
|
|
42
|
+
* results say why, not just what ran.
|
|
43
|
+
*/
|
|
44
|
+
function gitError(args, error) {
|
|
45
|
+
var _a;
|
|
46
|
+
const stderr = error.stderr;
|
|
47
|
+
const detail = typeof stderr === "string"
|
|
48
|
+
? stderr
|
|
49
|
+
.split("\n")
|
|
50
|
+
.map((line) => line.trim())
|
|
51
|
+
.find((line) => line.length > 0)
|
|
52
|
+
: error instanceof Error
|
|
53
|
+
? ((_a = error.message.split("\n")[0]) !== null && _a !== void 0 ? _a : String(error))
|
|
54
|
+
: String(error);
|
|
55
|
+
return new Error(`git ${args.join(" ")} failed — ${detail}`);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The one credential helper git may use: serve the caller-provided token
|
|
59
|
+
* from the child process env at request time. The shell reads
|
|
60
|
+
* `$SHALLOW_CLONE_GIT_TOKEN` when git asks, keeping the token out of argv.
|
|
61
|
+
*/
|
|
62
|
+
const TOKEN_CREDENTIAL_HELPER = '!f() { test "$1" = get && printf "username=x-access-token\\npassword=$SHALLOW_CLONE_GIT_TOKEN\\n"; }; f';
|
|
63
|
+
function git(cwd, args, auth) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
// Async on purpose: fetches/checkouts of large repos take minutes, and the
|
|
66
|
+
// serve host runs the clone in-process. A synchronous git call blocks the
|
|
67
|
+
// event loop for the whole clone, freezing /v1/health and session streams
|
|
68
|
+
// long enough for the platform's probe budget to replace the engine.
|
|
69
|
+
try {
|
|
70
|
+
const { stdout } = yield execFileAsync("git", [
|
|
71
|
+
...(auth === undefined
|
|
72
|
+
? []
|
|
73
|
+
: ["-c", `credential.helper=${TOKEN_CREDENTIAL_HELPER}`]),
|
|
74
|
+
...args,
|
|
75
|
+
], {
|
|
76
|
+
cwd,
|
|
77
|
+
encoding: "utf8",
|
|
78
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
79
|
+
env: Object.assign(Object.assign(Object.assign({}, process.env), {
|
|
80
|
+
// Hermetic git: ignore global/system config so host-provisioned
|
|
81
|
+
// credential rewrites (`url.https://x-access-token:<token>@github
|
|
82
|
+
// .com/.insteadOf` entries scoped to a *different* repo, as cloud
|
|
83
|
+
// VMs bake in) cannot hijack auth away from the explicit token,
|
|
84
|
+
// and no ambient credential helpers run against PR-author input.
|
|
85
|
+
GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_SYSTEM: "/dev/null", GIT_TERMINAL_PROMPT: "0" }), (auth === undefined
|
|
86
|
+
? {}
|
|
87
|
+
: { SHALLOW_CLONE_GIT_TOKEN: auth.token })),
|
|
88
|
+
});
|
|
89
|
+
return stdout;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw gitError(args, error);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function revParse(cwd, rev) {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
try {
|
|
99
|
+
const sha = (yield git(cwd, ["rev-parse", "--verify", `${rev}^{commit}`])).trim();
|
|
100
|
+
return sha === "" ? undefined : sha;
|
|
101
|
+
}
|
|
102
|
+
catch (_a) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const FETCH_ARGS = ["fetch", "--depth=1", "--no-tags", "--quiet", "origin"];
|
|
108
|
+
/**
|
|
109
|
+
* Create a fresh shallow checkout of a GitHub PR head at `checkoutDir`.
|
|
110
|
+
*
|
|
111
|
+
* Fetch order (both single depth-1 round-trips):
|
|
112
|
+
* 1. Fetch `headSha` (and `baseSha` when known) directly — GitHub advertises
|
|
113
|
+
* `allow-reachable-sha1-in-want`, and this pins the exact reviewed commit
|
|
114
|
+
* the way Bugbot's `shallow_fetch` does.
|
|
115
|
+
* 2. Fall back to `refs/pull/<n>/head`, verifying it still resolves to
|
|
116
|
+
* `headSha` so the working tree cannot silently drift from the pinned
|
|
117
|
+
* patch in the review bundle.
|
|
118
|
+
*
|
|
119
|
+
* A missing merge-base commit is non-fatal (agents still have the pinned
|
|
120
|
+
* patch text in their prompt); a missing head checkout throws.
|
|
121
|
+
*/
|
|
122
|
+
export function createShallowPrCheckout(args) {
|
|
123
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
var _a;
|
|
125
|
+
const { repo, prNumber, headSha, baseSha, checkoutDir, auth } = args;
|
|
126
|
+
const startedAt = Date.now();
|
|
127
|
+
logShallowClone("cloning PR head", { repo, prNumber, headSha, baseSha });
|
|
128
|
+
rmSync(checkoutDir, { recursive: true, force: true });
|
|
129
|
+
mkdirSync(checkoutDir, { recursive: true });
|
|
130
|
+
yield git(checkoutDir, ["init", "--quiet"]);
|
|
131
|
+
yield git(checkoutDir, [
|
|
132
|
+
"remote",
|
|
133
|
+
"add",
|
|
134
|
+
"origin",
|
|
135
|
+
(_a = args.remoteUrl) !== null && _a !== void 0 ? _a : `https://github.com/${repo}.git`,
|
|
136
|
+
]);
|
|
137
|
+
const shaFetch = () => __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
const extraBase = baseSha === undefined || baseSha === headSha ? [] : [baseSha];
|
|
139
|
+
try {
|
|
140
|
+
yield git(checkoutDir, [...FETCH_ARGS, headSha, ...extraBase], auth);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (extraBase.length === 0) {
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
// The merge-base SHA may have aged out of reachability; the head commit
|
|
147
|
+
// is the part the review cannot proceed without, so retry it alone.
|
|
148
|
+
logShallowClone("combined sha fetch failed; retrying head sha alone", {
|
|
149
|
+
repo,
|
|
150
|
+
headSha,
|
|
151
|
+
baseSha,
|
|
152
|
+
error: errorSummary(error),
|
|
153
|
+
});
|
|
154
|
+
yield git(checkoutDir, [...FETCH_ARGS, headSha], auth);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
let headSource;
|
|
158
|
+
let shaFetchError;
|
|
159
|
+
try {
|
|
160
|
+
yield shaFetch();
|
|
161
|
+
headSource = "sha_fetch";
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
shaFetchError = errorSummary(error);
|
|
165
|
+
if (prNumber === undefined) {
|
|
166
|
+
throw new Error(`Shallow SHA fetch failed for ${repo}@${headSha} and no PR number is available for a ref fallback: ${shaFetchError}`);
|
|
167
|
+
}
|
|
168
|
+
logShallowClone("sha fetch failed; falling back to PR head ref", {
|
|
169
|
+
repo,
|
|
170
|
+
prNumber,
|
|
171
|
+
headSha,
|
|
172
|
+
error: shaFetchError,
|
|
173
|
+
});
|
|
174
|
+
const prRef = `refs/pull/${prNumber}/head`;
|
|
175
|
+
yield git(checkoutDir, [...FETCH_ARGS, `+${prRef}:refs/remotes/origin/pr/${prNumber}/head`], auth);
|
|
176
|
+
const fetchedSha = yield revParse(checkoutDir, `refs/remotes/origin/pr/${prNumber}/head`);
|
|
177
|
+
if (fetchedSha !== headSha) {
|
|
178
|
+
throw new Error(`Shallow PR ref fetch for ${repo}#${prNumber} resolved to ${fetchedSha !== null && fetchedSha !== void 0 ? fetchedSha : "nothing"}, expected pinned head ${headSha} (sha fetch error: ${shaFetchError})`);
|
|
179
|
+
}
|
|
180
|
+
headSource = "pr_ref_fetch";
|
|
181
|
+
}
|
|
182
|
+
if ((yield revParse(checkoutDir, headSha)) === undefined) {
|
|
183
|
+
throw new Error(`Shallow fetch completed but head commit ${headSha} is missing from ${checkoutDir}`);
|
|
184
|
+
}
|
|
185
|
+
logShallowClone("fetch complete; materializing working tree", {
|
|
186
|
+
repo,
|
|
187
|
+
headSha,
|
|
188
|
+
fetchMs: Date.now() - startedAt,
|
|
189
|
+
});
|
|
190
|
+
yield git(checkoutDir, ["checkout", "--quiet", "--detach", headSha]);
|
|
191
|
+
let baseFetched = baseSha !== undefined &&
|
|
192
|
+
(yield revParse(checkoutDir, baseSha)) !== undefined;
|
|
193
|
+
if (baseSha !== undefined && baseSha !== headSha && !baseFetched) {
|
|
194
|
+
// Best-effort: local diffs are nice-to-have, the pinned patch in the
|
|
195
|
+
// prompt is the authoritative delta either way.
|
|
196
|
+
try {
|
|
197
|
+
yield git(checkoutDir, [...FETCH_ARGS, baseSha], auth);
|
|
198
|
+
baseFetched = (yield revParse(checkoutDir, baseSha)) !== undefined;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
logShallowClone("merge-base fetch failed; continuing without it", {
|
|
202
|
+
repo,
|
|
203
|
+
baseSha,
|
|
204
|
+
error: errorSummary(error),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
logShallowClone("checkout ready", {
|
|
209
|
+
repo,
|
|
210
|
+
prNumber,
|
|
211
|
+
headSha,
|
|
212
|
+
baseSha,
|
|
213
|
+
headSource,
|
|
214
|
+
baseFetched,
|
|
215
|
+
durationMs: Date.now() - startedAt,
|
|
216
|
+
checkoutDir,
|
|
217
|
+
});
|
|
218
|
+
return { checkoutDir, headSha, baseFetched, headSource };
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/** True for `https://github.com/<owner>/<repo>/pull/<n>` URLs. */
|
|
222
|
+
export function isGithubPrUrl(prUrl) {
|
|
223
|
+
try {
|
|
224
|
+
const url = new URL(prUrl);
|
|
225
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
226
|
+
return (url.hostname === "github.com" &&
|
|
227
|
+
parts.length >= 4 &&
|
|
228
|
+
parts[2] === "pull" &&
|
|
229
|
+
Number.isInteger(Number(parts[3])));
|
|
230
|
+
}
|
|
231
|
+
catch (_a) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Best-effort shallow PR checkout for hosts whose SDK agents should read the
|
|
237
|
+
* reviewed repo instead of the host's own checkout.
|
|
238
|
+
*
|
|
239
|
+
* Returns no checkout — leaving the caller on its fallback working
|
|
240
|
+
* directory — when the PR is not on GitHub (e.g. Origin PRs, where the host
|
|
241
|
+
* checkout is the reviewed repo), when no head SHA is pinned, when no clone
|
|
242
|
+
* credential is available, or when the clone fails; `fallbackReason` says
|
|
243
|
+
* which. Failures are logged, never thrown: agents still have the pinned
|
|
244
|
+
* patch in their prompts, so a missing checkout degrades quality rather
|
|
245
|
+
* than the run.
|
|
246
|
+
*/
|
|
247
|
+
export function tryCreateShallowPrCheckout(options) {
|
|
248
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
var _a;
|
|
250
|
+
const { prUrl, repo, prNumber, headSha, baseSha, checkoutDir, remoteUrl } = options;
|
|
251
|
+
if (remoteUrl === undefined && !isGithubPrUrl(prUrl)) {
|
|
252
|
+
logShallowClone("skipped: not a GitHub PR URL", { prUrl });
|
|
253
|
+
return { fallbackReason: "skipped: not a GitHub PR URL" };
|
|
254
|
+
}
|
|
255
|
+
if (headSha === undefined) {
|
|
256
|
+
logShallowClone("skipped: no pinned head SHA", { repo, prUrl });
|
|
257
|
+
return { fallbackReason: "skipped: no pinned head SHA" };
|
|
258
|
+
}
|
|
259
|
+
const auth = (_a = options.auth) !== null && _a !== void 0 ? _a : serveHostGithubAuth();
|
|
260
|
+
if (remoteUrl === undefined && auth === undefined) {
|
|
261
|
+
// Fail fast instead of letting an unauthenticated fetch 404/hang: real
|
|
262
|
+
// GitHub clones need the serve host's lease (or an explicit token).
|
|
263
|
+
logShallowClone("skipped: no GitHub clone credential", { repo, prUrl });
|
|
264
|
+
return {
|
|
265
|
+
fallbackReason: "skipped: no GitHub clone credential (serve host GH_TOKEN lease not installed)",
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
return {
|
|
270
|
+
checkout: yield createShallowPrCheckout({
|
|
271
|
+
repo,
|
|
272
|
+
prNumber,
|
|
273
|
+
headSha,
|
|
274
|
+
baseSha,
|
|
275
|
+
checkoutDir,
|
|
276
|
+
auth,
|
|
277
|
+
remoteUrl,
|
|
278
|
+
}),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
const reason = errorSummary(error);
|
|
283
|
+
logShallowClone("failed; caller falls back to host checkout", {
|
|
284
|
+
repo,
|
|
285
|
+
prNumber,
|
|
286
|
+
headSha,
|
|
287
|
+
error: reason,
|
|
288
|
+
});
|
|
289
|
+
return { fallbackReason: `clone failed: ${reason}` };
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
@@ -92,6 +92,8 @@ export interface SlackWizardPollResult {
|
|
|
92
92
|
status: string;
|
|
93
93
|
appId?: string;
|
|
94
94
|
oauthAuthorizeUrl?: string;
|
|
95
|
+
/** "pending" means the admin-approval request is already in Slack's queue. */
|
|
96
|
+
approvalRequest?: string;
|
|
95
97
|
error?: string;
|
|
96
98
|
localEnv?: {
|
|
97
99
|
botToken: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-provision-client.d.ts","sourceRoot":"","sources":["../../src/internal/slack-provision-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,CAAC;AAEzC,gEAAgE;AAChE,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,uBAAuB,EAAE,CAAC;IACtC,IAAI,EAAE,mBAAmB,EAAE,CAAC;CAC7B;AAED,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,CAAC;AAE/E,yEAAyE;AACzE,MAAM,MAAM,iBAAiB,GACzB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAC/B;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC/B,GACD;IACE,MAAM,EAAE,wBAAwB,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC/B,CAAC;AAEN,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,iEAAiE;IACjE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjC,YACE,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,EAQF;CACF;
|
|
1
|
+
{"version":3,"file":"slack-provision-client.d.ts","sourceRoot":"","sources":["../../src/internal/slack-provision-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable per-session turn ownership: `activeTurns` is in-memory, so servers
|
|
3
|
+
* sharing a state root would otherwise both run the same session.
|
|
4
|
+
*
|
|
5
|
+
* Exclusion, staleness and liveness refresh come from `proper-lockfile`, which
|
|
6
|
+
* the monorepo already uses for the git cache. Hand-rolling them means
|
|
7
|
+
* inferring that a process died — timeouts, heartbeats, and a takeover path
|
|
8
|
+
* that has to stay atomic while removing another owner's claim. That is where
|
|
9
|
+
* the subtle races live, and none of it is specific to turns.
|
|
10
|
+
*
|
|
11
|
+
* Liveness is lock age, not pid: containers have separate PID namespaces.
|
|
12
|
+
*/
|
|
13
|
+
/** A held turn lease. Releasing is idempotent. */
|
|
14
|
+
export interface TurnLease {
|
|
15
|
+
release(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare class TurnLeaseStore {
|
|
18
|
+
private readonly dir;
|
|
19
|
+
private readonly staleMs;
|
|
20
|
+
/** Locks this process holds. Their release closures cannot live on disk. */
|
|
21
|
+
private readonly held;
|
|
22
|
+
constructor(dir: string, options?: {
|
|
23
|
+
ttlMs?: number;
|
|
24
|
+
});
|
|
25
|
+
private path;
|
|
26
|
+
/**
|
|
27
|
+
* Undefined when another process holds this session and is still refreshing.
|
|
28
|
+
*
|
|
29
|
+
* `onCompromised` fires when the lock is lost mid-turn — the owner stalled
|
|
30
|
+
* past the stale window and a successor took over. No lease scheme can stop
|
|
31
|
+
* that; the callback is how the displaced turn learns to stand down instead
|
|
32
|
+
* of answering alongside its successor.
|
|
33
|
+
*/
|
|
34
|
+
acquire(sessionId: string, onCompromised?: () => void): Promise<TurnLease | undefined>;
|
|
35
|
+
private handle;
|
|
36
|
+
/** Graceful shutdown: drop every claim so a successor need not wait it out. */
|
|
37
|
+
releaseAll(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* The session is running in another server process. Mirrors the engine's
|
|
40
|
+
* in-memory `isTurnRunning`, which only ever sees this process.
|
|
41
|
+
*/
|
|
42
|
+
runningElsewhere(sessionId: string): Promise<boolean>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=turn-lease-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-lease-store.d.ts","sourceRoot":"","sources":["../../src/internal/turn-lease-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,kDAAkD;AAClD,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAmBD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IAEpD,YAAY,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,EAIxD;IAED,OAAO,CAAC,IAAI;IAIZ;;;;;;;OAOG;IACG,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,MAAM,IAAI,GACzB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CA6BhC;IAED,OAAO,CAAC,MAAM;IAwBd,+EAA+E;IACzE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhC;IAED;;;OAGG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAY1D;CACF"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable per-session turn ownership: `activeTurns` is in-memory, so servers
|
|
3
|
+
* sharing a state root would otherwise both run the same session.
|
|
4
|
+
*
|
|
5
|
+
* Exclusion, staleness and liveness refresh come from `proper-lockfile`, which
|
|
6
|
+
* the monorepo already uses for the git cache. Hand-rolling them means
|
|
7
|
+
* inferring that a process died — timeouts, heartbeats, and a takeover path
|
|
8
|
+
* that has to stay atomic while removing another owner's claim. That is where
|
|
9
|
+
* the subtle races live, and none of it is specific to turns.
|
|
10
|
+
*
|
|
11
|
+
* Liveness is lock age, not pid: containers have separate PID namespaces.
|
|
12
|
+
*/
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
import { mkdir } from "node:fs/promises";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import * as lockfile from "proper-lockfile";
|
|
25
|
+
/** Reject path traversal; session ids are opaque strings from the caller. */
|
|
26
|
+
function safeKey(sessionId) {
|
|
27
|
+
const sanitized = sessionId
|
|
28
|
+
.trim()
|
|
29
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "_")
|
|
30
|
+
.replace(/^\.+/, "")
|
|
31
|
+
.replace(/\.+/g, "_")
|
|
32
|
+
.slice(0, 180);
|
|
33
|
+
return sanitized === "" ? "_empty" : sanitized;
|
|
34
|
+
}
|
|
35
|
+
export class TurnLeaseStore {
|
|
36
|
+
constructor(dir, options = {}) {
|
|
37
|
+
var _a;
|
|
38
|
+
/** Locks this process holds. Their release closures cannot live on disk. */
|
|
39
|
+
this.held = new Map();
|
|
40
|
+
this.dir = dir;
|
|
41
|
+
// Several refreshes, so a slow owner is not mistaken for a dead one.
|
|
42
|
+
this.staleMs = (_a = options.ttlMs) !== null && _a !== void 0 ? _a : 30000;
|
|
43
|
+
}
|
|
44
|
+
path(sessionId) {
|
|
45
|
+
return join(this.dir, `${safeKey(sessionId)}.lease`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Undefined when another process holds this session and is still refreshing.
|
|
49
|
+
*
|
|
50
|
+
* `onCompromised` fires when the lock is lost mid-turn — the owner stalled
|
|
51
|
+
* past the stale window and a successor took over. No lease scheme can stop
|
|
52
|
+
* that; the callback is how the displaced turn learns to stand down instead
|
|
53
|
+
* of answering alongside its successor.
|
|
54
|
+
*/
|
|
55
|
+
acquire(sessionId, onCompromised) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
const existing = this.held.get(sessionId);
|
|
58
|
+
if (existing !== undefined) {
|
|
59
|
+
existing.refs += 1;
|
|
60
|
+
return this.handle(sessionId);
|
|
61
|
+
}
|
|
62
|
+
yield mkdir(this.dir, { recursive: true });
|
|
63
|
+
let release;
|
|
64
|
+
try {
|
|
65
|
+
release = yield lockfile.lock(this.path(sessionId), {
|
|
66
|
+
realpath: false,
|
|
67
|
+
retries: 0,
|
|
68
|
+
stale: this.staleMs,
|
|
69
|
+
// How often the library refreshes the lock's mtime while we hold it.
|
|
70
|
+
update: 5000,
|
|
71
|
+
onCompromised: () => {
|
|
72
|
+
this.held.delete(sessionId);
|
|
73
|
+
onCompromised === null || onCompromised === void 0 ? void 0 : onCompromised();
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (_a) {
|
|
78
|
+
// Held elsewhere, or the store is unusable. Either way this process must
|
|
79
|
+
// not run the turn: coalescing costs latency, two servers on one session
|
|
80
|
+
// costs the conversation.
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
this.held.set(sessionId, { release, refs: 1 });
|
|
84
|
+
return this.handle(sessionId);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
handle(sessionId) {
|
|
88
|
+
let released = false;
|
|
89
|
+
return {
|
|
90
|
+
release: () => __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
if (released) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
released = true;
|
|
95
|
+
const lock = this.held.get(sessionId);
|
|
96
|
+
if (lock === undefined) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
lock.refs -= 1;
|
|
100
|
+
if (lock.refs > 0) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.held.delete(sessionId);
|
|
104
|
+
// Throws if the lock was already compromised, which is not our problem
|
|
105
|
+
// to solve here: the successor owns it.
|
|
106
|
+
yield lock.release().catch(() => { });
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Graceful shutdown: drop every claim so a successor need not wait it out. */
|
|
111
|
+
releaseAll() {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
for (const [sessionId, lock] of [...this.held]) {
|
|
114
|
+
this.held.delete(sessionId);
|
|
115
|
+
yield lock.release().catch(() => { });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The session is running in another server process. Mirrors the engine's
|
|
121
|
+
* in-memory `isTurnRunning`, which only ever sees this process.
|
|
122
|
+
*/
|
|
123
|
+
runningElsewhere(sessionId) {
|
|
124
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
if (this.held.has(sessionId)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
return yield lockfile.check(this.path(sessionId), {
|
|
130
|
+
realpath: false,
|
|
131
|
+
stale: this.staleMs,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (_a) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -99,6 +99,18 @@ export declare function buildAgentsMdContent(agent: ResolvedAgent, options: {
|
|
|
99
99
|
* are skipped by nested rule discovery.
|
|
100
100
|
*/
|
|
101
101
|
export declare function isNestedInGitRepo(dir: string): Promise<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* Give a session workspace its own git root so the harness roots *there*
|
|
104
|
+
* rather than at the enclosing repo (see {@link isNestedInGitRepo}). Without
|
|
105
|
+
* it the harness reads the outer repo's `.cursor/hooks.json`, rules, skills
|
|
106
|
+
* and `mcp.json`, and never sees the ones written into this workspace —
|
|
107
|
+
* instructions have an inline fallback, hooks have none.
|
|
108
|
+
*
|
|
109
|
+
* Best effort: a workspace that already has `.git` (a borrowed checkout or
|
|
110
|
+
* worktree) is left alone, and a failed `git init` leaves the harness where
|
|
111
|
+
* it already was.
|
|
112
|
+
*/
|
|
113
|
+
export declare function ensureOwnGitRoot(dir: string): Promise<boolean>;
|
|
102
114
|
export declare function materializeWorkspace(options: MaterializeWorkspaceOptions): Promise<void>;
|
|
103
115
|
/**
|
|
104
116
|
* Generate the SKILL.md the harness reads: `name` + `description`
|