@cursor/july 0.1.2 → 0.1.4

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.
Files changed (232) hide show
  1. package/AGENTS.md +16 -5
  2. package/README.md +8 -9
  3. package/dist/bin/agent-serve.d.ts +3 -1
  4. package/dist/bin/agent-serve.d.ts.map +1 -1
  5. package/dist/bin/agent-serve.js +416 -155
  6. package/dist/channels/slack/log.d.ts +1 -1
  7. package/dist/channels/slack/log.d.ts.map +1 -1
  8. package/dist/channels/slack/log.js +4 -5
  9. package/dist/channels/slack/setup.js +1 -1
  10. package/dist/channels/slack/socket-mode.d.ts.map +1 -1
  11. package/dist/channels/slack/socket-mode.js +10 -9
  12. package/dist/docs/404.html +2 -2
  13. package/dist/docs/ab.html +3 -3
  14. package/dist/docs/assets/{app.Oje4vhlk.js → app.DF-Yx4OP.js} +1 -1
  15. package/dist/docs/assets/chunks/@localSearchIndexroot.0XrheBv2.js +1 -0
  16. package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.DSeJSudL.js} +1 -1
  17. package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.DZVLTXOC.js} +2 -2
  18. package/dist/docs/assets/{index.md.m81y7TY7.js → index.md.Cylk70gg.js} +1 -1
  19. package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
  20. package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
  21. package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
  22. package/dist/docs/building-with-agents.html +3 -3
  23. package/dist/docs/concepts.html +3 -3
  24. package/dist/docs/deployment.html +3 -3
  25. package/dist/docs/evals.html +3 -3
  26. package/dist/docs/guides/agent-to-agent.html +3 -3
  27. package/dist/docs/guides/cloud-runtime.html +3 -3
  28. package/dist/docs/guides/github.html +3 -3
  29. package/dist/docs/guides/human-in-the-loop.html +3 -3
  30. package/dist/docs/guides/slack.html +3 -3
  31. package/dist/docs/guides/webhooks.html +3 -3
  32. package/dist/docs/hashmap.json +1 -1
  33. package/dist/docs/hillclimbing.html +3 -3
  34. package/dist/docs/index.html +5 -5
  35. package/dist/docs/quickstart.html +22 -10
  36. package/dist/docs/reference/agent-config.html +3 -3
  37. package/dist/docs/reference/channels.html +3 -3
  38. package/dist/docs/reference/cli.html +14 -9
  39. package/dist/docs/reference/connections.html +3 -3
  40. package/dist/docs/reference/hooks.html +3 -3
  41. package/dist/docs/reference/http-api.html +3 -3
  42. package/dist/docs/reference/instructions.html +3 -3
  43. package/dist/docs/reference/playground.html +3 -3
  44. package/dist/docs/reference/project-layout.html +3 -3
  45. package/dist/docs/reference/schedules.html +3 -3
  46. package/dist/docs/reference/sessions.html +3 -3
  47. package/dist/docs/reference/skills.html +3 -3
  48. package/dist/docs/reference/subagents.html +3 -3
  49. package/dist/docs/reference/tools.html +3 -3
  50. package/dist/docs/scaffolding-agents.html +3 -3
  51. package/dist/docs/storage.html +3 -3
  52. package/dist/docs/troubleshooting.html +3 -3
  53. package/dist/internal/ab-collector.js +3 -3
  54. package/dist/internal/ab-fold.js +1 -1
  55. package/dist/internal/chat-client.d.ts +29 -3
  56. package/dist/internal/chat-client.d.ts.map +1 -1
  57. package/dist/internal/chat-client.js +180 -27
  58. package/dist/internal/cli-ax.d.ts +83 -2
  59. package/dist/internal/cli-ax.d.ts.map +1 -1
  60. package/dist/internal/cli-ax.js +626 -52
  61. package/dist/internal/cli-cursor.d.ts.map +1 -1
  62. package/dist/internal/cli-cursor.js +8 -36
  63. package/dist/internal/cli-deploy.d.ts +59 -3
  64. package/dist/internal/cli-deploy.d.ts.map +1 -1
  65. package/dist/internal/cli-deploy.js +316 -38
  66. package/dist/internal/cli-github.js +13 -13
  67. package/dist/internal/cursor/account-mcp.js +3 -3
  68. package/dist/internal/cursor/backend-client.js +1 -1
  69. package/dist/internal/cursor/credentials.d.ts +14 -3
  70. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  71. package/dist/internal/cursor/credentials.js +21 -6
  72. package/dist/internal/cursor/github-credentials.js +3 -3
  73. package/dist/internal/cursor-event-relay.js +1 -1
  74. package/dist/internal/cursor-relay-core.d.ts +1 -1
  75. package/dist/internal/cursor-relay-core.d.ts.map +1 -1
  76. package/dist/internal/cursor-relay-core.js +2 -1
  77. package/dist/internal/cursor-slack-relay.js +1 -1
  78. package/dist/internal/deploy-client.d.ts +9 -1
  79. package/dist/internal/deploy-client.d.ts.map +1 -1
  80. package/dist/internal/deploy-client.js +23 -3
  81. package/dist/internal/deploy-source.d.ts +35 -0
  82. package/dist/internal/deploy-source.d.ts.map +1 -0
  83. package/dist/internal/deploy-source.js +118 -0
  84. package/dist/internal/discovery.d.ts +11 -0
  85. package/dist/internal/discovery.d.ts.map +1 -1
  86. package/dist/internal/discovery.js +43 -14
  87. package/dist/internal/distribution.d.ts +7 -0
  88. package/dist/internal/distribution.d.ts.map +1 -1
  89. package/dist/internal/distribution.js +7 -0
  90. package/dist/internal/docs-site.js +3 -3
  91. package/dist/internal/eval-run-store.d.ts.map +1 -1
  92. package/dist/internal/eval-run-store.js +10 -9
  93. package/dist/internal/eval-runner.d.ts +2 -0
  94. package/dist/internal/eval-runner.d.ts.map +1 -1
  95. package/dist/internal/eval-runner.js +2 -0
  96. package/dist/internal/event-mapper.d.ts +54 -1
  97. package/dist/internal/event-mapper.d.ts.map +1 -1
  98. package/dist/internal/event-mapper.js +151 -41
  99. package/dist/internal/github-fanout.js +2 -2
  100. package/dist/internal/host-platforms.js +5 -5
  101. package/dist/internal/init-project.d.ts +88 -1
  102. package/dist/internal/init-project.d.ts.map +1 -1
  103. package/dist/internal/init-project.js +221 -31
  104. package/dist/internal/install-cursor-skills.d.ts +64 -0
  105. package/dist/internal/install-cursor-skills.d.ts.map +1 -0
  106. package/dist/internal/install-cursor-skills.js +274 -0
  107. package/dist/internal/logs-client.d.ts +60 -0
  108. package/dist/internal/logs-client.d.ts.map +1 -0
  109. package/dist/internal/logs-client.js +311 -0
  110. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  111. package/dist/internal/mcp-endpoint.js +1 -1
  112. package/dist/internal/open-browser.d.ts +5 -0
  113. package/dist/internal/open-browser.d.ts.map +1 -0
  114. package/dist/internal/open-browser.js +34 -0
  115. package/dist/internal/playground/toolchain.d.ts.map +1 -1
  116. package/dist/internal/playground/toolchain.js +19 -14
  117. package/dist/internal/playground-proxy.d.ts +69 -0
  118. package/dist/internal/playground-proxy.d.ts.map +1 -0
  119. package/dist/internal/playground-proxy.js +468 -0
  120. package/dist/internal/prompt-context.d.ts +35 -0
  121. package/dist/internal/prompt-context.d.ts.map +1 -0
  122. package/dist/internal/prompt-context.js +71 -0
  123. package/dist/internal/reminder-runner.d.ts.map +1 -1
  124. package/dist/internal/reminder-runner.js +11 -11
  125. package/dist/internal/request-headers.d.ts +11 -0
  126. package/dist/internal/request-headers.d.ts.map +1 -0
  127. package/dist/internal/request-headers.js +14 -0
  128. package/dist/internal/resolve-prod-target.d.ts +44 -0
  129. package/dist/internal/resolve-prod-target.d.ts.map +1 -0
  130. package/dist/internal/resolve-prod-target.js +134 -0
  131. package/dist/internal/resolved-connections.js +5 -5
  132. package/dist/internal/run-client.d.ts +2 -2
  133. package/dist/internal/run-client.d.ts.map +1 -1
  134. package/dist/internal/run-client.js +38 -23
  135. package/dist/internal/schedule-runner.js +1 -1
  136. package/dist/internal/server.d.ts.map +1 -1
  137. package/dist/internal/server.js +23 -17
  138. package/dist/internal/session-engine.d.ts +1 -1
  139. package/dist/internal/session-engine.d.ts.map +1 -1
  140. package/dist/internal/session-engine.js +34 -19
  141. package/dist/internal/sessions-client.d.ts +34 -0
  142. package/dist/internal/sessions-client.d.ts.map +1 -0
  143. package/dist/internal/sessions-client.js +193 -0
  144. package/dist/internal/storage-coordinator.js +6 -6
  145. package/dist/internal/stream-progress.d.ts.map +1 -1
  146. package/dist/internal/stream-progress.js +31 -3
  147. package/dist/internal/terminal-style.d.ts +22 -0
  148. package/dist/internal/terminal-style.d.ts.map +1 -0
  149. package/dist/internal/terminal-style.js +49 -0
  150. package/dist/internal/trajectory.d.ts +10 -5
  151. package/dist/internal/trajectory.d.ts.map +1 -1
  152. package/dist/internal/trajectory.js +82 -10
  153. package/dist/internal/update-check.d.ts +93 -0
  154. package/dist/internal/update-check.d.ts.map +1 -0
  155. package/dist/internal/update-check.js +431 -0
  156. package/dist/internal/workspace.d.ts +11 -0
  157. package/dist/internal/workspace.d.ts.map +1 -1
  158. package/dist/internal/workspace.js +38 -4
  159. package/dist/playground/assets/index-Ct_Usdm8.css +1 -0
  160. package/dist/playground/assets/index-Dsyb-Vaj.js +319 -0
  161. package/dist/playground/index.html +2 -2
  162. package/dist/types.d.ts +29 -2
  163. package/dist/types.d.ts.map +1 -1
  164. package/docs/README.md +0 -1
  165. package/docs/quickstart.md +22 -7
  166. package/docs/reference/cli.md +51 -3
  167. package/package.json +4 -1
  168. package/skills/ab/SKILL.md +8 -8
  169. package/skills/create-agent/SKILL.md +28 -22
  170. package/skills/debug/SKILL.md +11 -11
  171. package/skills/evals/SKILL.md +16 -16
  172. package/skills/framework-map/SKILL.md +6 -6
  173. package/skills/github/SKILL.md +13 -13
  174. package/skills/hillclimb/SKILL.md +10 -10
  175. package/skills/setup-slack/SKILL.md +14 -14
  176. package/src/bin/agent-serve.ts +451 -191
  177. package/src/channels/slack/log.ts +5 -5
  178. package/src/channels/slack/setup.ts +1 -1
  179. package/src/channels/slack/socket-mode.ts +11 -9
  180. package/src/internal/ab-collector.ts +3 -3
  181. package/src/internal/ab-fold.ts +1 -1
  182. package/src/internal/chat-client.ts +252 -37
  183. package/src/internal/cli-ax.ts +761 -72
  184. package/src/internal/cli-cursor.ts +14 -42
  185. package/src/internal/cli-deploy.ts +428 -49
  186. package/src/internal/cli-github.ts +13 -15
  187. package/src/internal/cursor/account-mcp.ts +3 -3
  188. package/src/internal/cursor/backend-client.ts +1 -1
  189. package/src/internal/cursor/credentials.ts +26 -7
  190. package/src/internal/cursor/github-credentials.ts +3 -3
  191. package/src/internal/cursor-event-relay.ts +1 -1
  192. package/src/internal/cursor-relay-core.ts +4 -2
  193. package/src/internal/cursor-slack-relay.ts +1 -1
  194. package/src/internal/deploy-client.ts +45 -4
  195. package/src/internal/deploy-source.ts +133 -0
  196. package/src/internal/discovery.ts +53 -16
  197. package/src/internal/distribution.ts +8 -0
  198. package/src/internal/docs-site.ts +3 -3
  199. package/src/internal/eval-run-store.ts +10 -11
  200. package/src/internal/eval-runner.ts +5 -0
  201. package/src/internal/event-mapper.ts +222 -42
  202. package/src/internal/github-fanout.ts +2 -2
  203. package/src/internal/host-platforms.ts +5 -5
  204. package/src/internal/init-project.ts +333 -51
  205. package/src/internal/install-cursor-skills.ts +327 -0
  206. package/src/internal/logs-client.ts +442 -0
  207. package/src/internal/mcp-endpoint.ts +1 -3
  208. package/src/internal/open-browser.ts +41 -0
  209. package/src/internal/playground/toolchain.ts +18 -16
  210. package/src/internal/playground-proxy.ts +576 -0
  211. package/src/internal/prompt-context.ts +95 -0
  212. package/src/internal/reminder-runner.ts +11 -17
  213. package/src/internal/request-headers.ts +23 -0
  214. package/src/internal/resolve-prod-target.ts +182 -0
  215. package/src/internal/resolved-connections.ts +5 -5
  216. package/src/internal/run-client.ts +39 -36
  217. package/src/internal/schedule-runner.ts +1 -1
  218. package/src/internal/server.ts +25 -16
  219. package/src/internal/session-engine.ts +35 -18
  220. package/src/internal/sessions-client.ts +216 -0
  221. package/src/internal/storage-coordinator.ts +6 -6
  222. package/src/internal/stream-progress.ts +36 -2
  223. package/src/internal/terminal-style.ts +74 -0
  224. package/src/internal/trajectory.ts +91 -13
  225. package/src/internal/update-check.ts +552 -0
  226. package/src/internal/workspace.ts +42 -4
  227. package/src/types.ts +45 -7
  228. package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
  229. package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
  230. package/dist/playground/assets/index-CpDYCj8W.js +0 -79
  231. /package/dist/docs/assets/{index.md.m81y7TY7.lean.js → index.md.Cylk70gg.lean.js} +0 -0
  232. /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Best-effort browser open; callers should always print the URL as fallback.
3
+ */
4
+ import { spawn } from "node:child_process";
5
+ import { join } from "node:path";
6
+ export function openBrowser(url) {
7
+ var _a;
8
+ const command = process.platform === "darwin"
9
+ ? { cmd: "open", args: [url] }
10
+ : process.platform === "win32"
11
+ ? {
12
+ cmd: join((_a = process.env.SYSTEMROOT) !== null && _a !== void 0 ? _a : "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
13
+ args: [
14
+ "-NoProfile",
15
+ "-NonInteractive",
16
+ "-WindowStyle",
17
+ "Hidden",
18
+ "-Command",
19
+ `Start-Process '${url.replace(/'/g, "''")}'`,
20
+ ],
21
+ }
22
+ : { cmd: "xdg-open", args: [url] };
23
+ try {
24
+ const child = spawn(command.cmd, command.args, {
25
+ stdio: "ignore",
26
+ detached: true,
27
+ });
28
+ child.on("error", () => { });
29
+ child.unref();
30
+ }
31
+ catch (_b) {
32
+ // Printed URL is the fallback.
33
+ }
34
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"toolchain.d.ts","sourceRoot":"","sources":["../../../src/internal/playground/toolchain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,4EAA4E;AAC5E,wBAAgB,WAAW,IAAI,MAAM,CAGpC;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAmCD,0EAA0E;AAC1E,wBAAgB,0BAA0B,IAAI,mBAAmB,GAAG,SAAS,CAW5E;AAED,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACtC,MAAM,GAAG,SAAS,CAKpB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CAUpB;AAED,oEAAoE;AACpE,wBAAsB,kBAAkB,CACtC,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkB,GAC3C,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkB,GAC3C,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,IAAI,IAAI,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,GAAG,mBAAmB,GAAG,SAAS,CA4ElC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAM1C"}
1
+ {"version":3,"file":"toolchain.d.ts","sourceRoot":"","sources":["../../../src/internal/playground/toolchain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,4EAA4E;AAC5E,wBAAgB,WAAW,IAAI,MAAM,CAGpC;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAmCD,0EAA0E;AAC1E,wBAAgB,0BAA0B,IAAI,mBAAmB,GAAG,SAAS,CAW5E;AAED,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACtC,MAAM,GAAG,SAAS,CAKpB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CAUpB;AAED,oEAAoE;AACpE,wBAAsB,kBAAkB,CACtC,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkB,GAC3C,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkB,GAC3C,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,IAAI,IAAI,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,GAAG,mBAAmB,GAAG,SAAS,CA8ElC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAM1C"}
@@ -109,7 +109,7 @@ export function runPlaygroundBuild() {
109
109
  if (toolchain === undefined) {
110
110
  throw new Error("playground vite toolchain unavailable (need playground/ sources and the vite package)");
111
111
  }
112
- logger("[agent-serve] building playground (vite)...");
112
+ logger("[agentkit] building playground (vite)...");
113
113
  const code = yield spawnVite(toolchain, ["build", "--config", VITE_CONFIG], {
114
114
  env: process.env,
115
115
  });
@@ -119,7 +119,7 @@ export function runPlaygroundBuild() {
119
119
  if (!playgroundBundleExists()) {
120
120
  throw new Error("playground vite build finished but dist/playground/index.html is missing");
121
121
  }
122
- logger("[agent-serve] playground build complete");
122
+ logger("[agentkit] playground build complete");
123
123
  });
124
124
  }
125
125
  /**
@@ -133,7 +133,7 @@ export function ensurePlaygroundBundle() {
133
133
  return;
134
134
  }
135
135
  if (resolvePlaygroundToolchain() === undefined) {
136
- logger("[agent-serve] playground bundle missing and vite toolchain unavailable; /playground will show a build hint");
136
+ logger("[agentkit] playground bundle missing and vite toolchain unavailable; /playground will show a build hint");
137
137
  return;
138
138
  }
139
139
  yield runPlaygroundBuild(logger);
@@ -146,7 +146,8 @@ export function ensurePlaygroundBundle() {
146
146
  */
147
147
  export function startPlaygroundDevServer(options) {
148
148
  var _a;
149
- const logger = (_a = options.logger) !== null && _a !== void 0 ? _a : console.log;
149
+ var _b;
150
+ const logger = (_b = options.logger) !== null && _b !== void 0 ? _b : console.log;
150
151
  const toolchain = resolvePlaygroundToolchain();
151
152
  if (toolchain === undefined) {
152
153
  return undefined;
@@ -167,30 +168,34 @@ export function startPlaygroundDevServer(options) {
167
168
  delete env.AGENT_SERVE_BASE;
168
169
  }
169
170
  }
170
- const modeNote = multiAgent
171
- ? " (multi-agent)"
172
- : options.basePath
173
- ? ` (base ${options.basePath})`
174
- : "";
175
- logger(`[agent-serve] playground HMR starting on http://127.0.0.1:${port}${modeNote}`);
176
- const child = spawn(process.execPath, [toolchain.viteCli, "--config", VITE_CONFIG], {
171
+ // Quiet + isolated from the TTY: the serve banner owns the terminal.
172
+ // Piped stdio keeps Vite from seeing a TTY (so it cannot clearScreen), and
173
+ // silent logging drops the "VITE ready" / URL spam. Errors still surface via
174
+ // spawn/exit handlers below.
175
+ const child = spawn(process.execPath, [toolchain.viteCli, "--config", VITE_CONFIG, "--logLevel", "silent"], {
177
176
  cwd: toolchain.packageRoot,
178
177
  env,
179
- stdio: "inherit",
178
+ stdio: ["ignore", "ignore", "pipe"],
179
+ });
180
+ (_a = child.stderr) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
181
+ const text = chunk.toString().trimEnd();
182
+ if (text !== "") {
183
+ logger(`[agentkit] playground: ${text}`);
184
+ }
180
185
  });
181
186
  let stopped = false;
182
187
  child.on("error", (err) => {
183
188
  if (stopped) {
184
189
  return;
185
190
  }
186
- logger(`[agent-serve] playground HMR spawn failed: ${err.message} — static /playground still served when built`);
191
+ logger(`[agentkit] playground spawn failed: ${err.message} — static /playground still served when built`);
187
192
  });
188
193
  child.on("exit", (code, signal) => {
189
194
  if (stopped) {
190
195
  return;
191
196
  }
192
197
  if (code !== 0 && code !== null) {
193
- logger(`[agent-serve] playground HMR exited (code ${code}${signal ? `, signal ${signal}` : ""}) — static /playground still served when built`);
198
+ logger(`[agentkit] playground exited (code ${code}${signal ? `, signal ${signal}` : ""}) — static /playground still served when built`);
194
199
  }
195
200
  });
196
201
  return {
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Loopback reverse proxy that injects engine / bearer auth so a normal
3
+ * browser can use the hosted (or remote) playground SPA.
4
+ *
5
+ * Hosted engines sit behind short-lived `engineAccess` headers that a
6
+ * browser cannot set; alias tokens are header-only too. This proxy binds
7
+ * `127.0.0.1` only and forwards every request upstream with those headers.
8
+ *
9
+ * Access is gated by a per-proxy `accessToken` (query on first hit → HttpOnly
10
+ * cookie): other local processes cannot upgrade to injected credentials just
11
+ * by knowing the port. Mutating requests also require Origin/Referer to match
12
+ * this proxy's own origin so another `http://127.0.0.1:<port>` page cannot
13
+ * CSRF the cookie (cookies are host-scoped across ports). Absolute-form
14
+ * request-targets are rejected so injected headers never leave the configured
15
+ * upstream.
16
+ *
17
+ * When `refreshUpstream` is provided (hosted `--prod`), credentials are
18
+ * re-minted shortly before `expiresAt` and once on upstream 401 so a long
19
+ * playground session does not require restarting the CLI.
20
+ */
21
+ import { createServer } from "node:http";
22
+ import { openBrowser } from "./open-browser.js";
23
+ export { openBrowser };
24
+ export interface PlaygroundProxyHandle {
25
+ /** e.g. `http://127.0.0.1:54321` (no trailing slash). */
26
+ localUrl: string;
27
+ port: number;
28
+ /** Random token required via query (first hit) or cookie thereafter. */
29
+ accessToken: string;
30
+ /**
31
+ * Build a browser entry URL for `/playground` that includes the access
32
+ * token (and optional sessionId).
33
+ */
34
+ playgroundUrl: (sessionId?: string) => string;
35
+ close: () => Promise<void>;
36
+ }
37
+ export interface PlaygroundUpstreamAccess {
38
+ upstreamUrl: string;
39
+ headers?: Record<string, string>;
40
+ expiresAt?: string;
41
+ }
42
+ export interface StartPlaygroundProxyArgs {
43
+ /** Upstream agent base URL (engineAccess or --url). */
44
+ upstreamUrl: string;
45
+ /** Headers injected on every upstream request (engine tokens, wake, …). */
46
+ headers?: Record<string, string>;
47
+ /** Optional bearer for local servers that require it. */
48
+ bearerToken?: string;
49
+ /** Initial engineAccess expiry (ISO); enables proactive refresh. */
50
+ expiresAt?: string;
51
+ /**
52
+ * Re-mint upstream URL + headers (hosted `--prod`). Single-flighted across
53
+ * concurrent browser requests; called near expiry and once on 401.
54
+ */
55
+ refreshUpstream?: () => Promise<PlaygroundUpstreamAccess>;
56
+ /** Bind port; `0` = ephemeral. */
57
+ port?: number;
58
+ /** Injected for tests. */
59
+ createServerImpl?: typeof createServer;
60
+ /** Injected clock for expiry tests. */
61
+ now?: () => number;
62
+ /** Injected token for tests (defaults to a random 32-byte value). */
63
+ accessToken?: string;
64
+ }
65
+ /**
66
+ * Start a loopback proxy. Resolves once listening.
67
+ */
68
+ export declare function startPlaygroundProxy(args: StartPlaygroundProxyArgs): Promise<PlaygroundProxyHandle>;
69
+ //# sourceMappingURL=playground-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playground-proxy.d.ts","sourceRoot":"","sources":["../../src/internal/playground-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EACL,YAAY,EAMb,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,WAAW,EAAE,CAAC;AAqBvB,MAAM,WAAW,qBAAqB;IACpC,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,aAAa,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC1D,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,gBAAgB,CAAC,EAAE,OAAO,YAAY,CAAC;IACvC,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAmGhC"}
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Loopback reverse proxy that injects engine / bearer auth so a normal
3
+ * browser can use the hosted (or remote) playground SPA.
4
+ *
5
+ * Hosted engines sit behind short-lived `engineAccess` headers that a
6
+ * browser cannot set; alias tokens are header-only too. This proxy binds
7
+ * `127.0.0.1` only and forwards every request upstream with those headers.
8
+ *
9
+ * Access is gated by a per-proxy `accessToken` (query on first hit → HttpOnly
10
+ * cookie): other local processes cannot upgrade to injected credentials just
11
+ * by knowing the port. Mutating requests also require Origin/Referer to match
12
+ * this proxy's own origin so another `http://127.0.0.1:<port>` page cannot
13
+ * CSRF the cookie (cookies are host-scoped across ports). Absolute-form
14
+ * request-targets are rejected so injected headers never leave the configured
15
+ * upstream.
16
+ *
17
+ * When `refreshUpstream` is provided (hosted `--prod`), credentials are
18
+ * re-minted shortly before `expiresAt` and once on upstream 401 so a long
19
+ * playground session does not require restarting the CLI.
20
+ */
21
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23
+ return new (P || (P = Promise))(function (resolve, reject) {
24
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
25
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
26
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
28
+ });
29
+ };
30
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
31
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
32
+ var m = o[Symbol.asyncIterator], i;
33
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
34
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
35
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
36
+ };
37
+ import { randomBytes } from "node:crypto";
38
+ import { createServer, request as httpRequest, } from "node:http";
39
+ import { request as httpsRequest } from "node:https";
40
+ import { URL } from "node:url";
41
+ import { openBrowser } from "./open-browser.js";
42
+ export { openBrowser };
43
+ const HOP_BY_HOP = new Set([
44
+ "connection",
45
+ "keep-alive",
46
+ "proxy-authenticate",
47
+ "proxy-authorization",
48
+ "te",
49
+ "trailers",
50
+ "transfer-encoding",
51
+ "upgrade",
52
+ "host",
53
+ "content-length",
54
+ ]);
55
+ /** Re-mint when the port token is within this window of expiry. */
56
+ const ACCESS_EXPIRY_LEEWAY_MS = 5 * 60 * 1000;
57
+ const PROXY_COOKIE = "agentkit_playground_proxy";
58
+ const PROXY_TOKEN_QUERY = "proxyToken";
59
+ /**
60
+ * Start a loopback proxy. Resolves once listening.
61
+ */
62
+ export function startPlaygroundProxy(args) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ var _a, _b, _c;
65
+ const upstream = parseUpstreamUrl(args.upstreamUrl);
66
+ const state = {
67
+ upstream,
68
+ injectHeaders: buildInjectHeaders(args.headers, args.bearerToken),
69
+ expiresAtMs: parseExpiresAtMs(args.expiresAt),
70
+ };
71
+ const accessToken = (_a = args.accessToken) !== null && _a !== void 0 ? _a : randomBytes(32).toString("base64url");
72
+ const now = (_b = args.now) !== null && _b !== void 0 ? _b : Date.now;
73
+ let refreshInFlight;
74
+ /** Set after listen; used for Origin CSRF checks. */
75
+ let proxyOrigin = "";
76
+ const ensureFreshAccess = (force) => __awaiter(this, void 0, void 0, function* () {
77
+ if (args.refreshUpstream === undefined) {
78
+ return;
79
+ }
80
+ const expired = state.expiresAtMs !== undefined &&
81
+ now() >= state.expiresAtMs - ACCESS_EXPIRY_LEEWAY_MS;
82
+ if (!force && !expired) {
83
+ return;
84
+ }
85
+ if (refreshInFlight !== undefined) {
86
+ yield refreshInFlight;
87
+ return;
88
+ }
89
+ refreshInFlight = (() => __awaiter(this, void 0, void 0, function* () {
90
+ const next = yield args.refreshUpstream();
91
+ state.upstream = parseUpstreamUrl(next.upstreamUrl);
92
+ state.injectHeaders = buildInjectHeaders(next.headers, args.bearerToken);
93
+ state.expiresAtMs = parseExpiresAtMs(next.expiresAt);
94
+ }))().finally(() => {
95
+ refreshInFlight = undefined;
96
+ });
97
+ yield refreshInFlight;
98
+ });
99
+ const create = (_c = args.createServerImpl) !== null && _c !== void 0 ? _c : createServer;
100
+ const server = create((req, res) => {
101
+ void handleProxyRequest({
102
+ req,
103
+ res,
104
+ getState: () => state,
105
+ ensureFreshAccess,
106
+ canRefresh: args.refreshUpstream !== undefined,
107
+ accessToken,
108
+ proxyOrigin: () => proxyOrigin,
109
+ }).catch((error) => {
110
+ if (res.headersSent) {
111
+ if (!res.writableEnded) {
112
+ res.end();
113
+ }
114
+ return;
115
+ }
116
+ const message = error instanceof Error ? error.message : String(error);
117
+ res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
118
+ res.end(`Playground proxy error: ${message}`);
119
+ });
120
+ });
121
+ yield new Promise((resolve, reject) => {
122
+ var _a;
123
+ server.once("error", reject);
124
+ server.listen((_a = args.port) !== null && _a !== void 0 ? _a : 0, "127.0.0.1", () => {
125
+ server.off("error", reject);
126
+ resolve();
127
+ });
128
+ });
129
+ const address = server.address();
130
+ const localUrl = `http://127.0.0.1:${address.port}`;
131
+ proxyOrigin = localUrl;
132
+ return {
133
+ localUrl,
134
+ port: address.port,
135
+ accessToken,
136
+ playgroundUrl: (sessionId) => {
137
+ const url = new URL(`${localUrl}/playground`);
138
+ url.searchParams.set(PROXY_TOKEN_QUERY, accessToken);
139
+ if (sessionId !== undefined && sessionId.trim() !== "") {
140
+ url.searchParams.set("sessionId", sessionId.trim());
141
+ }
142
+ return url.toString();
143
+ },
144
+ close: () => new Promise((resolve, reject) => {
145
+ // Drop keep-alive / streamed playground connections so Ctrl-C
146
+ // does not hang waiting for active sockets.
147
+ server.closeAllConnections();
148
+ server.close((error) => {
149
+ if (error) {
150
+ reject(error);
151
+ return;
152
+ }
153
+ resolve();
154
+ });
155
+ }),
156
+ };
157
+ });
158
+ }
159
+ function parseUpstreamUrl(raw) {
160
+ const upstream = new URL(raw.replace(/\/$/, ""));
161
+ if (upstream.protocol !== "http:" && upstream.protocol !== "https:") {
162
+ throw new Error(`Unsupported upstream protocol: ${upstream.protocol}`);
163
+ }
164
+ return upstream;
165
+ }
166
+ function parseExpiresAtMs(expiresAt) {
167
+ if (expiresAt === undefined || expiresAt.trim() === "") {
168
+ return undefined;
169
+ }
170
+ const ms = Date.parse(expiresAt);
171
+ return Number.isFinite(ms) ? ms : undefined;
172
+ }
173
+ function buildInjectHeaders(headers, bearerToken) {
174
+ const out = Object.assign({}, (headers !== null && headers !== void 0 ? headers : {}));
175
+ if (bearerToken !== undefined && bearerToken.trim() !== "") {
176
+ out.authorization = `Bearer ${bearerToken.trim()}`;
177
+ }
178
+ return out;
179
+ }
180
+ function readRequestBody(req) {
181
+ return __awaiter(this, void 0, void 0, function* () {
182
+ var _a, req_1, req_1_1;
183
+ var _b, e_1, _c, _d;
184
+ const chunks = [];
185
+ try {
186
+ for (_a = true, req_1 = __asyncValues(req); req_1_1 = yield req_1.next(), _b = req_1_1.done, !_b; _a = true) {
187
+ _d = req_1_1.value;
188
+ _a = false;
189
+ const chunk = _d;
190
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
191
+ }
192
+ }
193
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
194
+ finally {
195
+ try {
196
+ if (!_a && !_b && (_c = req_1.return)) yield _c.call(req_1);
197
+ }
198
+ finally { if (e_1) throw e_1.error; }
199
+ }
200
+ return Buffer.concat(chunks);
201
+ });
202
+ }
203
+ function handleProxyRequest(args) {
204
+ return __awaiter(this, void 0, void 0, function* () {
205
+ var _a, _b;
206
+ const { req, res, getState, ensureFreshAccess, canRefresh, accessToken, proxyOrigin, } = args;
207
+ const pathWithQuery = (_a = req.url) !== null && _a !== void 0 ? _a : "/";
208
+ const pathParse = parseProxyPath(pathWithQuery);
209
+ if (!pathParse.ok) {
210
+ res.writeHead(400, { "content-type": "text/plain; charset=utf-8" });
211
+ res.end("Absolute-form request targets are not allowed");
212
+ return;
213
+ }
214
+ const auth = authorizeLocalRequest({
215
+ pathWithQuery: pathParse.path,
216
+ cookieHeader: req.headers.cookie,
217
+ accessToken,
218
+ });
219
+ if (auth.kind === "deny") {
220
+ res.writeHead(401, { "content-type": "text/plain; charset=utf-8" });
221
+ res.end("Playground proxy requires the session access token");
222
+ return;
223
+ }
224
+ if (auth.kind === "bootstrap") {
225
+ res.writeHead(302, {
226
+ location: auth.redirectPath,
227
+ "set-cookie": auth.setCookie,
228
+ "cache-control": "no-store",
229
+ });
230
+ res.end();
231
+ return;
232
+ }
233
+ const method = (_b = req.method) !== null && _b !== void 0 ? _b : "GET";
234
+ if (!isTrustedBrowserOrigin({
235
+ method,
236
+ origin: headerString(req.headers.origin),
237
+ referer: headerString(req.headers.referer),
238
+ secFetchSite: headerString(req.headers["sec-fetch-site"]),
239
+ proxyOrigin: proxyOrigin(),
240
+ })) {
241
+ res.writeHead(403, { "content-type": "text/plain; charset=utf-8" });
242
+ res.end("Playground proxy rejected cross-origin request");
243
+ return;
244
+ }
245
+ let body;
246
+ try {
247
+ body = yield readRequestBody(req);
248
+ }
249
+ catch (_c) {
250
+ if (!res.headersSent) {
251
+ res.writeHead(400, { "content-type": "text/plain; charset=utf-8" });
252
+ res.end("Playground proxy failed to read request body");
253
+ }
254
+ return;
255
+ }
256
+ const forward = (state, deferWriteOnUnauthorized) => forwardOnce({
257
+ method,
258
+ pathWithQuery: pathParse.path,
259
+ inboundHeaders: req.headers,
260
+ body,
261
+ state,
262
+ res,
263
+ deferWriteOnUnauthorized,
264
+ });
265
+ try {
266
+ yield ensureFreshAccess(false);
267
+ let status = yield forward(getState(), canRefresh);
268
+ if (status === 401 && canRefresh) {
269
+ yield ensureFreshAccess(true);
270
+ // Retry once; always write this response (even if still 401).
271
+ status = yield forward(getState(), false);
272
+ }
273
+ void status;
274
+ }
275
+ catch (error) {
276
+ if (!res.headersSent) {
277
+ const message = error instanceof Error ? error.message : String(error);
278
+ res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
279
+ res.end(`Playground proxy upstream error: ${message}`);
280
+ }
281
+ else if (!res.writableEnded) {
282
+ res.end();
283
+ }
284
+ }
285
+ });
286
+ }
287
+ /**
288
+ * Cookies are host-scoped across ports on 127.0.0.1, so SameSite=Strict alone
289
+ * does not stop CSRF from another loopback origin. Require the browser's
290
+ * Origin (or Referer / Sec-Fetch-Site: same-origin) to match this proxy for
291
+ * mutating methods. Safe methods stay cookie-gated only (no side effects).
292
+ */
293
+ function isTrustedBrowserOrigin(args) {
294
+ const method = args.method.toUpperCase();
295
+ if (method === "GET" || method === "HEAD" || method === "OPTIONS") {
296
+ return true;
297
+ }
298
+ if (args.proxyOrigin === "") {
299
+ return false;
300
+ }
301
+ if (args.origin === args.proxyOrigin) {
302
+ return true;
303
+ }
304
+ if (args.referer !== undefined &&
305
+ (args.referer === args.proxyOrigin ||
306
+ args.referer.startsWith(`${args.proxyOrigin}/`))) {
307
+ return true;
308
+ }
309
+ // Some same-origin fetches omit Origin; modern browsers still send this.
310
+ if (args.secFetchSite === "same-origin") {
311
+ return true;
312
+ }
313
+ return false;
314
+ }
315
+ function headerString(value) {
316
+ if (value === undefined) {
317
+ return undefined;
318
+ }
319
+ return Array.isArray(value) ? value[0] : value;
320
+ }
321
+ function authorizeLocalRequest(args) {
322
+ const cookieToken = readCookie(args.cookieHeader, PROXY_COOKIE);
323
+ if (cookieToken === args.accessToken) {
324
+ return { kind: "allow" };
325
+ }
326
+ const url = new URL(args.pathWithQuery, "http://127.0.0.1");
327
+ const queryToken = url.searchParams.get(PROXY_TOKEN_QUERY);
328
+ if (queryToken === args.accessToken) {
329
+ url.searchParams.delete(PROXY_TOKEN_QUERY);
330
+ const redirectPath = `${url.pathname}${url.search}`;
331
+ return {
332
+ kind: "bootstrap",
333
+ redirectPath,
334
+ setCookie: `${PROXY_COOKIE}=${args.accessToken}; Path=/; HttpOnly; SameSite=Strict`,
335
+ };
336
+ }
337
+ return { kind: "deny" };
338
+ }
339
+ function readCookie(header, name) {
340
+ if (header === undefined) {
341
+ return undefined;
342
+ }
343
+ const raw = Array.isArray(header) ? header.join("; ") : header;
344
+ for (const part of raw.split(";")) {
345
+ const trimmed = part.trim();
346
+ const eq = trimmed.indexOf("=");
347
+ if (eq <= 0)
348
+ continue;
349
+ if (trimmed.slice(0, eq) === name) {
350
+ return trimmed.slice(eq + 1);
351
+ }
352
+ }
353
+ return undefined;
354
+ }
355
+ function forwardOnce(args) {
356
+ return __awaiter(this, void 0, void 0, function* () {
357
+ const { method, pathWithQuery, inboundHeaders, body, state, res } = args;
358
+ const target = buildTargetUrl(pathWithQuery, state.upstream);
359
+ const outboundHeaders = {};
360
+ for (const [name, value] of Object.entries(inboundHeaders)) {
361
+ if (value === undefined)
362
+ continue;
363
+ const lower = name.toLowerCase();
364
+ if (HOP_BY_HOP.has(lower))
365
+ continue;
366
+ if (lower === "authorization")
367
+ continue;
368
+ // Do not forward the loopback session cookie upstream.
369
+ if (lower === "cookie")
370
+ continue;
371
+ outboundHeaders[name] = value;
372
+ }
373
+ for (const [name, value] of Object.entries(state.injectHeaders)) {
374
+ outboundHeaders[name] = value;
375
+ }
376
+ outboundHeaders.host = target.host;
377
+ outboundHeaders["content-length"] = String(body.byteLength);
378
+ const isHttps = target.protocol === "https:";
379
+ const requestFn = isHttps ? httpsRequest : httpRequest;
380
+ const options = {
381
+ protocol: target.protocol,
382
+ hostname: target.hostname,
383
+ port: target.port === "" ? (isHttps ? 443 : 80) : Number(target.port),
384
+ method,
385
+ path: `${target.pathname}${target.search}`,
386
+ headers: outboundHeaders,
387
+ };
388
+ return yield new Promise((resolve, reject) => {
389
+ const upstreamReq = requestFn(options, (upstreamRes) => {
390
+ var _a;
391
+ const status = (_a = upstreamRes.statusCode) !== null && _a !== void 0 ? _a : 502;
392
+ if (status === 401 && args.deferWriteOnUnauthorized) {
393
+ // Drain so the socket can be reused; caller will refresh + retry.
394
+ upstreamRes.resume();
395
+ upstreamRes.on("end", () => resolve(status));
396
+ upstreamRes.on("error", () => resolve(status));
397
+ return;
398
+ }
399
+ const responseHeaders = Object.assign({}, upstreamRes.headers);
400
+ for (const hop of HOP_BY_HOP) {
401
+ delete responseHeaders[hop];
402
+ }
403
+ res.writeHead(status, responseHeaders);
404
+ upstreamRes.pipe(res);
405
+ upstreamRes.on("end", () => resolve(status));
406
+ upstreamRes.on("error", () => {
407
+ if (!res.writableEnded) {
408
+ res.end();
409
+ }
410
+ resolve(status);
411
+ });
412
+ });
413
+ upstreamReq.on("error", (error) => {
414
+ reject(error);
415
+ });
416
+ res.on("close", () => {
417
+ if (!res.writableEnded) {
418
+ upstreamReq.destroy();
419
+ }
420
+ });
421
+ upstreamReq.end(body);
422
+ });
423
+ });
424
+ }
425
+ /**
426
+ * Build an upstream URL that always keeps the configured host/port.
427
+ * Absolute-form or protocol-relative request-targets must not retarget
428
+ * outbound requests (injected engine/bearer credentials would follow).
429
+ */
430
+ function buildTargetUrl(pathWithQuery, upstream) {
431
+ const pathParse = parseProxyPath(pathWithQuery);
432
+ const relative = pathParse.ok ? pathParse.path : "/";
433
+ const target = new URL(relative, upstream);
434
+ // Force authority back onto the configured upstream even if `new URL`
435
+ // accepted an absolute/protocol-relative request-target.
436
+ target.protocol = upstream.protocol;
437
+ target.hostname = upstream.hostname;
438
+ target.port = upstream.port;
439
+ target.username = "";
440
+ target.password = "";
441
+ // Preserve upstream pathname prefix when the base URL has one
442
+ // (unusual for engineAccess; kept for `--url` mounts).
443
+ if (upstream.pathname !== "/" && upstream.pathname !== "") {
444
+ const basePath = upstream.pathname.replace(/\/$/, "");
445
+ if (!pathnameHasPrefix(target.pathname, basePath)) {
446
+ target.pathname =
447
+ target.pathname === "/"
448
+ ? `${basePath}/`
449
+ : `${basePath}${target.pathname}`;
450
+ }
451
+ }
452
+ return target;
453
+ }
454
+ /** Reject absolute / scheme-relative request-targets; keep path + query only. */
455
+ function parseProxyPath(pathWithQuery) {
456
+ const raw = pathWithQuery.trim();
457
+ if (raw === "") {
458
+ return { ok: true, path: "/" };
459
+ }
460
+ // Absolute-form ("http://evil/…") or protocol-relative ("//evil/…").
461
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/u.test(raw) || raw.startsWith("//")) {
462
+ return { ok: false };
463
+ }
464
+ return { ok: true, path: raw.startsWith("/") ? raw : `/${raw}` };
465
+ }
466
+ function pathnameHasPrefix(pathname, basePath) {
467
+ return pathname === basePath || pathname.startsWith(`${basePath}/`);
468
+ }