@cursor/july 0.1.2 → 0.1.3

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 (174) 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 +389 -152
  6. package/dist/docs/404.html +2 -2
  7. package/dist/docs/ab.html +3 -3
  8. package/dist/docs/assets/{app.Oje4vhlk.js → app.BR0RbIdx.js} +1 -1
  9. package/dist/docs/assets/chunks/@localSearchIndexroot.DtIOQzGj.js +1 -0
  10. package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.qjsVTneI.js} +1 -1
  11. package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.2Kg8jIp_.js} +2 -2
  12. package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
  13. package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
  14. package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
  15. package/dist/docs/building-with-agents.html +3 -3
  16. package/dist/docs/concepts.html +3 -3
  17. package/dist/docs/deployment.html +3 -3
  18. package/dist/docs/evals.html +3 -3
  19. package/dist/docs/guides/agent-to-agent.html +3 -3
  20. package/dist/docs/guides/cloud-runtime.html +3 -3
  21. package/dist/docs/guides/github.html +3 -3
  22. package/dist/docs/guides/human-in-the-loop.html +3 -3
  23. package/dist/docs/guides/slack.html +3 -3
  24. package/dist/docs/guides/webhooks.html +3 -3
  25. package/dist/docs/hashmap.json +1 -1
  26. package/dist/docs/hillclimbing.html +3 -3
  27. package/dist/docs/index.html +3 -3
  28. package/dist/docs/quickstart.html +22 -10
  29. package/dist/docs/reference/agent-config.html +3 -3
  30. package/dist/docs/reference/channels.html +3 -3
  31. package/dist/docs/reference/cli.html +14 -9
  32. package/dist/docs/reference/connections.html +3 -3
  33. package/dist/docs/reference/hooks.html +3 -3
  34. package/dist/docs/reference/http-api.html +3 -3
  35. package/dist/docs/reference/instructions.html +3 -3
  36. package/dist/docs/reference/playground.html +3 -3
  37. package/dist/docs/reference/project-layout.html +3 -3
  38. package/dist/docs/reference/schedules.html +3 -3
  39. package/dist/docs/reference/sessions.html +3 -3
  40. package/dist/docs/reference/skills.html +3 -3
  41. package/dist/docs/reference/subagents.html +3 -3
  42. package/dist/docs/reference/tools.html +3 -3
  43. package/dist/docs/scaffolding-agents.html +3 -3
  44. package/dist/docs/storage.html +3 -3
  45. package/dist/docs/troubleshooting.html +3 -3
  46. package/dist/internal/chat-client.d.ts +29 -3
  47. package/dist/internal/chat-client.d.ts.map +1 -1
  48. package/dist/internal/chat-client.js +180 -27
  49. package/dist/internal/cli-ax.d.ts +75 -2
  50. package/dist/internal/cli-ax.d.ts.map +1 -1
  51. package/dist/internal/cli-ax.js +600 -52
  52. package/dist/internal/cli-cursor.d.ts.map +1 -1
  53. package/dist/internal/cli-cursor.js +8 -36
  54. package/dist/internal/cli-deploy.d.ts +59 -3
  55. package/dist/internal/cli-deploy.d.ts.map +1 -1
  56. package/dist/internal/cli-deploy.js +316 -38
  57. package/dist/internal/cursor/credentials.d.ts +9 -0
  58. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  59. package/dist/internal/cursor/credentials.js +12 -0
  60. package/dist/internal/deploy-client.d.ts +9 -1
  61. package/dist/internal/deploy-client.d.ts.map +1 -1
  62. package/dist/internal/deploy-client.js +23 -3
  63. package/dist/internal/deploy-source.d.ts +35 -0
  64. package/dist/internal/deploy-source.d.ts.map +1 -0
  65. package/dist/internal/deploy-source.js +118 -0
  66. package/dist/internal/discovery.d.ts +11 -0
  67. package/dist/internal/discovery.d.ts.map +1 -1
  68. package/dist/internal/discovery.js +43 -14
  69. package/dist/internal/eval-run-store.d.ts.map +1 -1
  70. package/dist/internal/eval-run-store.js +2 -1
  71. package/dist/internal/eval-runner.d.ts +2 -0
  72. package/dist/internal/eval-runner.d.ts.map +1 -1
  73. package/dist/internal/eval-runner.js +2 -0
  74. package/dist/internal/event-mapper.d.ts +54 -1
  75. package/dist/internal/event-mapper.d.ts.map +1 -1
  76. package/dist/internal/event-mapper.js +151 -41
  77. package/dist/internal/init-project.d.ts +88 -1
  78. package/dist/internal/init-project.d.ts.map +1 -1
  79. package/dist/internal/init-project.js +221 -31
  80. package/dist/internal/install-cursor-skills.d.ts +64 -0
  81. package/dist/internal/install-cursor-skills.d.ts.map +1 -0
  82. package/dist/internal/install-cursor-skills.js +274 -0
  83. package/dist/internal/logs-client.d.ts +60 -0
  84. package/dist/internal/logs-client.d.ts.map +1 -0
  85. package/dist/internal/logs-client.js +311 -0
  86. package/dist/internal/open-browser.d.ts +5 -0
  87. package/dist/internal/open-browser.d.ts.map +1 -0
  88. package/dist/internal/open-browser.js +34 -0
  89. package/dist/internal/playground/toolchain.d.ts.map +1 -1
  90. package/dist/internal/playground/toolchain.js +16 -11
  91. package/dist/internal/playground-proxy.d.ts +69 -0
  92. package/dist/internal/playground-proxy.d.ts.map +1 -0
  93. package/dist/internal/playground-proxy.js +468 -0
  94. package/dist/internal/prompt-context.d.ts +35 -0
  95. package/dist/internal/prompt-context.d.ts.map +1 -0
  96. package/dist/internal/prompt-context.js +71 -0
  97. package/dist/internal/request-headers.d.ts +11 -0
  98. package/dist/internal/request-headers.d.ts.map +1 -0
  99. package/dist/internal/request-headers.js +14 -0
  100. package/dist/internal/resolve-prod-target.d.ts +42 -0
  101. package/dist/internal/resolve-prod-target.d.ts.map +1 -0
  102. package/dist/internal/resolve-prod-target.js +111 -0
  103. package/dist/internal/run-client.d.ts +2 -2
  104. package/dist/internal/run-client.d.ts.map +1 -1
  105. package/dist/internal/run-client.js +38 -23
  106. package/dist/internal/server.d.ts.map +1 -1
  107. package/dist/internal/server.js +10 -4
  108. package/dist/internal/session-engine.d.ts +1 -1
  109. package/dist/internal/session-engine.d.ts.map +1 -1
  110. package/dist/internal/session-engine.js +21 -6
  111. package/dist/internal/sessions-client.d.ts +21 -0
  112. package/dist/internal/sessions-client.d.ts.map +1 -0
  113. package/dist/internal/sessions-client.js +168 -0
  114. package/dist/internal/stream-progress.d.ts.map +1 -1
  115. package/dist/internal/stream-progress.js +31 -3
  116. package/dist/internal/terminal-style.d.ts +22 -0
  117. package/dist/internal/terminal-style.d.ts.map +1 -0
  118. package/dist/internal/terminal-style.js +49 -0
  119. package/dist/internal/trajectory.d.ts +10 -5
  120. package/dist/internal/trajectory.d.ts.map +1 -1
  121. package/dist/internal/trajectory.js +82 -10
  122. package/dist/internal/workspace.d.ts +11 -0
  123. package/dist/internal/workspace.d.ts.map +1 -1
  124. package/dist/internal/workspace.js +38 -4
  125. package/dist/playground/assets/index-D-vo2lV_.css +1 -0
  126. package/dist/playground/assets/index-x60b9q2j.js +312 -0
  127. package/dist/playground/index.html +2 -2
  128. package/dist/types.d.ts +26 -1
  129. package/dist/types.d.ts.map +1 -1
  130. package/docs/quickstart.md +22 -7
  131. package/docs/reference/cli.md +51 -3
  132. package/package.json +3 -1
  133. package/skills/ab/SKILL.md +8 -8
  134. package/skills/create-agent/SKILL.md +28 -22
  135. package/skills/debug/SKILL.md +11 -11
  136. package/skills/evals/SKILL.md +16 -16
  137. package/skills/framework-map/SKILL.md +6 -6
  138. package/skills/github/SKILL.md +13 -13
  139. package/skills/hillclimb/SKILL.md +10 -10
  140. package/skills/setup-slack/SKILL.md +13 -13
  141. package/src/bin/agent-serve.ts +422 -188
  142. package/src/internal/chat-client.ts +252 -37
  143. package/src/internal/cli-ax.ts +722 -72
  144. package/src/internal/cli-cursor.ts +14 -42
  145. package/src/internal/cli-deploy.ts +428 -49
  146. package/src/internal/cursor/credentials.ts +14 -0
  147. package/src/internal/deploy-client.ts +45 -4
  148. package/src/internal/deploy-source.ts +133 -0
  149. package/src/internal/discovery.ts +53 -16
  150. package/src/internal/eval-run-store.ts +2 -1
  151. package/src/internal/eval-runner.ts +5 -0
  152. package/src/internal/event-mapper.ts +222 -42
  153. package/src/internal/init-project.ts +333 -51
  154. package/src/internal/install-cursor-skills.ts +327 -0
  155. package/src/internal/logs-client.ts +442 -0
  156. package/src/internal/open-browser.ts +41 -0
  157. package/src/internal/playground/toolchain.ts +15 -13
  158. package/src/internal/playground-proxy.ts +576 -0
  159. package/src/internal/prompt-context.ts +95 -0
  160. package/src/internal/request-headers.ts +23 -0
  161. package/src/internal/resolve-prod-target.ts +150 -0
  162. package/src/internal/run-client.ts +39 -36
  163. package/src/internal/server.ts +12 -3
  164. package/src/internal/session-engine.ts +22 -3
  165. package/src/internal/sessions-client.ts +182 -0
  166. package/src/internal/stream-progress.ts +36 -2
  167. package/src/internal/terminal-style.ts +74 -0
  168. package/src/internal/trajectory.ts +91 -13
  169. package/src/internal/workspace.ts +42 -4
  170. package/src/types.ts +42 -6
  171. package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
  172. package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
  173. package/dist/playground/assets/index-CpDYCj8W.js +0 -79
  174. /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
@@ -12,6 +12,10 @@ import { tmpdir } from "node:os";
12
12
  import { join, resolve } from "node:path";
13
13
  import { serve } from "../index.js";
14
14
  import type { AgentServeHandle } from "../types.js";
15
+ import { lookupSessionContinuation, runChat } from "./chat-client.js";
16
+ import { cmdLogin } from "./cli-cursor.js";
17
+ import { resolveDeployAuth } from "./cli-deploy.js";
18
+ import { resolveApiKey } from "./cursor/credentials.js";
15
19
  import { CLI_COMMAND_NAME } from "./distribution.js";
16
20
  import {
17
21
  discoverEvals,
@@ -19,10 +23,28 @@ import {
19
23
  filterDiscoveredEvals,
20
24
  runDiscoveredEvals,
21
25
  } from "./eval-runner.js";
22
- import { initProject, printInitGuide } from "./init-project.js";
26
+ import {
27
+ formatInitNextSteps,
28
+ formatInitScaffoldSummary,
29
+ initNextCommands,
30
+ initProject,
31
+ installScaffoldDependencies,
32
+ printInitGuide,
33
+ } from "./init-project.js";
34
+ import { offerInstallCursorSkills } from "./install-cursor-skills.js";
35
+ import { runLogsTail, sanitizeCustomerError } from "./logs-client.js";
36
+ import { openBrowser } from "./open-browser.js";
37
+ import { startPlaygroundProxy } from "./playground-proxy.js";
38
+ import { resolveProdSlug, resolveProdTarget } from "./resolve-prod-target.js";
23
39
  import { runSession } from "./run-client.js";
40
+ import {
41
+ fetchSessionEvents,
42
+ formatSessionsTable,
43
+ listSessions,
44
+ } from "./sessions-client.js";
24
45
  import { createStreamProgress } from "./stream-progress.js";
25
46
  import {
47
+ buildTrajectory,
26
48
  formatTrajectoryText,
27
49
  loadTrajectoryFromEventsFile,
28
50
  } from "./trajectory.js";
@@ -30,7 +52,11 @@ import {
30
52
  export interface AxCliOptions {
31
53
  cwd?: string;
32
54
  dir: string;
33
- /** Agent slug when multiple agents are mounted. */
55
+ /**
56
+ * Local multi-agent mount slug, or (with `--prod`) the hosted deployment
57
+ * slug. When `--prod` is set and this is omitted, the slug is derived from
58
+ * the directory name (same as `deploy`).
59
+ */
34
60
  slug?: string;
35
61
  bearerToken?: string;
36
62
  stateRoot?: string;
@@ -59,13 +85,47 @@ export interface AxCliOptions {
59
85
  toolName?: string;
60
86
  /** JSON input for `call`. */
61
87
  toolInput?: string;
62
- /** Remote agent base URL (skip local host) for run/eval. */
88
+ /** Remote agent base URL (skip local host) for run/eval/chat. */
63
89
  url?: string;
90
+ /**
91
+ * Route chat/resume/run/call/eval/logs/sessions at the team's hosted
92
+ * deployment
93
+ * instead of a local ephemeral server. Mutually exclusive with `--url`.
94
+ */
95
+ prod?: boolean;
96
+ /** Cursor team id for `--prod` (defaults to the account's team). */
97
+ team?: string;
98
+ /** Cursor API key override for `--prod` management calls. */
99
+ apiKey?: string;
100
+ /** Injected fetch for `--prod` tests. */
101
+ fetchImpl?: typeof fetch;
64
102
  /**
65
103
  * Stream live turn progress to stderr (default: stderr is a TTY).
66
104
  * Final JSON/text still goes to stdout.
67
105
  */
68
106
  stream?: boolean;
107
+ /** Force plain chat output even on a TTY. */
108
+ noColor?: boolean;
109
+ /** `logs`: dump current buffer / progress once and exit. */
110
+ once?: boolean;
111
+ /**
112
+ * `session`: print raw `{ sessionId, events }` JSON instead of a trajectory.
113
+ * (CLI flag `--events`; rewritten from colliding with run's `--events <file>`.)
114
+ */
115
+ rawEvents?: boolean;
116
+ /**
117
+ * `playground`: print the URL only; do not open a browser.
118
+ * (CLI flag `--print`.)
119
+ */
120
+ print?: boolean;
121
+ /** Abort signal (tests / programmatic stop of `playground` proxy). */
122
+ signal?: AbortSignal;
123
+ }
124
+
125
+ export interface ResolvedAgentTarget {
126
+ agentUrl: string;
127
+ headers?: Record<string, string>;
128
+ close?: () => Promise<void>;
69
129
  }
70
130
 
71
131
  function shouldStreamProgress(options: AxCliOptions): boolean {
@@ -136,12 +196,26 @@ async function startEphemeral(
136
196
  };
137
197
  }
138
198
 
139
- async function resolveTarget(
199
+ export async function resolveTarget(
140
200
  options: AxCliOptions
141
- ): Promise<{ agentUrl: string; close?: () => Promise<void> }> {
201
+ ): Promise<ResolvedAgentTarget> {
202
+ if (options.prod === true && options.url !== undefined) {
203
+ die("--prod and --url are mutually exclusive");
204
+ }
142
205
  if (options.url !== undefined) {
143
206
  return { agentUrl: options.url.replace(/\/$/, "") };
144
207
  }
208
+ if (options.prod === true) {
209
+ const prod = await resolveProdTarget({
210
+ dir: options.dir,
211
+ cwd: options.cwd,
212
+ slug: options.slug,
213
+ team: options.team,
214
+ apiKey: options.apiKey,
215
+ fetchImpl: options.fetchImpl,
216
+ });
217
+ return { agentUrl: prod.agentUrl, headers: prod.headers };
218
+ }
145
219
  return startEphemeral(options);
146
220
  }
147
221
 
@@ -151,27 +225,203 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
151
225
  printInitGuide();
152
226
  return 0;
153
227
  }
154
- const dir = resolve(options.cwd ?? process.cwd(), target);
228
+ const cwd = options.cwd ?? process.cwd();
229
+ const dir = resolve(cwd, target);
155
230
  const result = await initProject({ dir });
231
+ let signedIn = (await resolveApiKey()) !== undefined;
232
+
233
+ // Machine-readable path: install deps (piped), never block on browser login.
234
+ if (options.json) {
235
+ const install = await installScaffoldDependencies(result.rootDir, {
236
+ stdio: "pipe",
237
+ });
238
+ process.stdout.write(
239
+ `${JSON.stringify(
240
+ {
241
+ ok: install.ok,
242
+ directory: result.rootDir,
243
+ created: result.created,
244
+ skipped: result.skipped,
245
+ installed: install.ok,
246
+ installError: install.ok
247
+ ? null
248
+ : install.stderr.trim() || `npm install exited ${install.exitCode}`,
249
+ next: initNextCommands({
250
+ rootDir: result.rootDir,
251
+ cwd,
252
+ signedIn,
253
+ }),
254
+ },
255
+ null,
256
+ 2
257
+ )}\n`
258
+ );
259
+ return install.ok ? 0 : 1;
260
+ }
261
+
156
262
  process.stdout.write(
157
- `${JSON.stringify(
158
- {
159
- ok: true,
160
- directory: result.rootDir,
161
- created: result.files,
162
- next: [
163
- `cd ${dir === process.cwd() ? "." : dir}`,
164
- `${CLI_COMMAND_NAME} validate`,
165
- `${CLI_COMMAND_NAME} info --json`,
166
- `${CLI_COMMAND_NAME} run --message "hello"`,
167
- `${CLI_COMMAND_NAME} eval`,
168
- ],
169
- },
170
- null,
171
- 2
172
- )}\n`
263
+ formatInitScaffoldSummary({
264
+ rootDir: result.rootDir,
265
+ files: result.files,
266
+ })
267
+ );
268
+ process.stdout.write("Installing npm packages\n\n");
269
+ const install = await installScaffoldDependencies(result.rootDir, {
270
+ stdio: "inherit",
271
+ });
272
+ if (!install.ok) {
273
+ process.stderr.write(
274
+ `\nnpm install failed (exit ${install.exitCode}). Fix the install, then run ${CLI_COMMAND_NAME} dev.\n`
275
+ );
276
+ return install.exitCode || 1;
277
+ }
278
+ process.stdout.write("\n");
279
+
280
+ // Personal Cursor skills so coding agents can pick up create-agent / debug / …
281
+ try {
282
+ await offerInstallCursorSkills();
283
+ } catch (err) {
284
+ const message = err instanceof Error ? err.message : String(err);
285
+ process.stderr.write(
286
+ `\nCould not install Cursor skills (${message}). Continuing init.\n\n`
287
+ );
288
+ }
289
+
290
+ let loginExit = 0;
291
+ if (!signedIn) {
292
+ process.stdout.write("Signing you in to Cursor\n\n");
293
+ loginExit = await cmdLogin({});
294
+ signedIn = loginExit === 0 && (await resolveApiKey()) !== undefined;
295
+ process.stdout.write("\n");
296
+ }
297
+
298
+ process.stdout.write(
299
+ formatInitNextSteps({
300
+ rootDir: result.rootDir,
301
+ cwd,
302
+ signedIn,
303
+ })
173
304
  );
174
- return 0;
305
+ return loginExit;
306
+ }
307
+
308
+ /**
309
+ * `chat` — talk to a running (or `--prod` hosted) agent. With `--json`,
310
+ * prints a trajectory instead of the interactive UI.
311
+ */
312
+ /**
313
+ * `resume <sessionId>` — reattach the interactive chat REPL to an existing
314
+ * session (looks up the continuation token via `/v1/sessions` when omitted).
315
+ * Same flags as `chat` (`--prod`, `--message`, `--json`, …). `--session`
316
+ * is accepted as a redundant alias for the positional id.
317
+ */
318
+ export async function cmdResume(
319
+ sessionId: string | undefined,
320
+ options: AxCliOptions
321
+ ): Promise<number> {
322
+ const resolved = trimNonEmpty(sessionId) ?? trimNonEmpty(options.sessionId);
323
+ if (resolved === undefined) {
324
+ process.stderr.write(
325
+ `Usage: ${CLI_COMMAND_NAME} resume <sessionId> [--url | --prod] [--message <text>] [--json] [--text]\n` +
326
+ ` [--continuation-token <t>] [--slug <slug>] [--team <id>]\n`
327
+ );
328
+ return 2;
329
+ }
330
+ return cmdChat({
331
+ ...options,
332
+ sessionId: resolved,
333
+ });
334
+ }
335
+
336
+ function trimNonEmpty(value: string | undefined): string | undefined {
337
+ if (value === undefined) {
338
+ return undefined;
339
+ }
340
+ const trimmed = value.trim();
341
+ return trimmed === "" ? undefined : trimmed;
342
+ }
343
+
344
+ export async function cmdChat(options: AxCliOptions): Promise<number> {
345
+ const message = options.messages?.[0];
346
+ // chat never starts an ephemeral server — default to local serve URL.
347
+ const target = await resolveTarget({
348
+ ...options,
349
+ url:
350
+ options.prod === true
351
+ ? undefined
352
+ : (options.url ?? "http://127.0.0.1:3000"),
353
+ });
354
+
355
+ if (options.json) {
356
+ if (message === undefined) {
357
+ process.stderr.write("chat --json requires --message\n");
358
+ return 2;
359
+ }
360
+ let continuationToken = options.continuationToken;
361
+ if (
362
+ options.sessionId !== undefined &&
363
+ (continuationToken === undefined || continuationToken === "")
364
+ ) {
365
+ try {
366
+ continuationToken = await lookupSessionContinuation({
367
+ baseUrl: target.agentUrl,
368
+ sessionId: options.sessionId,
369
+ auth: {
370
+ bearerToken: options.bearerToken,
371
+ headers: target.headers,
372
+ },
373
+ });
374
+ } catch (error) {
375
+ process.stderr.write(
376
+ `${error instanceof Error ? error.message : String(error)}\n`
377
+ );
378
+ return 1;
379
+ }
380
+ }
381
+ const result = await runSession({
382
+ baseUrl: target.agentUrl,
383
+ messages: [message],
384
+ sessionId: options.sessionId,
385
+ continuationToken,
386
+ bearerToken: options.bearerToken,
387
+ headers: target.headers,
388
+ });
389
+ if (options.text) {
390
+ process.stdout.write(formatTrajectoryText(result.trajectory));
391
+ } else {
392
+ console.log(
393
+ JSON.stringify(
394
+ {
395
+ ok: result.ok,
396
+ sessionId: result.sessionId,
397
+ continuationToken: result.continuationToken ?? null,
398
+ trajectory: result.trajectory,
399
+ },
400
+ null,
401
+ 2
402
+ )
403
+ );
404
+ }
405
+ return result.ok ? 0 : 1;
406
+ }
407
+
408
+ try {
409
+ await runChat({
410
+ baseUrl: target.agentUrl,
411
+ message,
412
+ sessionId: options.sessionId,
413
+ continuationToken: options.continuationToken,
414
+ bearerToken: options.bearerToken,
415
+ headers: target.headers,
416
+ noColor: options.noColor,
417
+ });
418
+ return 0;
419
+ } catch (error) {
420
+ process.stderr.write(
421
+ `${error instanceof Error ? error.message : String(error)}\n`
422
+ );
423
+ return 1;
424
+ }
175
425
  }
176
426
 
177
427
  export async function cmdRun(options: AxCliOptions): Promise<number> {
@@ -191,21 +441,12 @@ export async function cmdRun(options: AxCliOptions): Promise<number> {
191
441
  const result = await runSession({
192
442
  baseUrl: target.agentUrl,
193
443
  messages,
194
- ...(options.sessionId === undefined
195
- ? {}
196
- : { sessionId: options.sessionId }),
197
- ...(options.continuationToken === undefined
198
- ? {}
199
- : { continuationToken: options.continuationToken }),
200
- ...(options.bearerToken === undefined
201
- ? {}
202
- : { bearerToken: options.bearerToken }),
203
- ...(options.timeoutMs === undefined
204
- ? {}
205
- : { timeoutMs: options.timeoutMs }),
206
- ...(progress === undefined
207
- ? {}
208
- : { onEvent: (e) => progress.onEvent(e) }),
444
+ sessionId: options.sessionId,
445
+ continuationToken: options.continuationToken,
446
+ bearerToken: options.bearerToken,
447
+ headers: target.headers,
448
+ timeoutMs: options.timeoutMs,
449
+ onEvent: progress === undefined ? undefined : (e) => progress.onEvent(e),
209
450
  });
210
451
 
211
452
  let eventsPath: string | undefined;
@@ -227,8 +468,26 @@ export async function cmdRun(options: AxCliOptions): Promise<number> {
227
468
  }
228
469
 
229
470
  const playgroundUrl =
230
- options.url !== undefined
231
- ? `${target.agentUrl.replace(/\/$/, "")}/playground?sessionId=${encodeURIComponent(result.sessionId)}`
471
+ options.prod === true
472
+ ? null
473
+ : options.url !== undefined
474
+ ? `${target.agentUrl.replace(/\/$/, "")}/playground?sessionId=${encodeURIComponent(result.sessionId)}`
475
+ : null;
476
+ const playgroundHint =
477
+ options.prod === true
478
+ ? [
479
+ CLI_COMMAND_NAME,
480
+ "playground",
481
+ "--prod",
482
+ ...(options.slug !== undefined && options.slug.trim() !== ""
483
+ ? ["--slug", options.slug.trim()]
484
+ : []),
485
+ ...(options.team !== undefined && options.team.trim() !== ""
486
+ ? ["--team", options.team.trim()]
487
+ : []),
488
+ "--session",
489
+ result.sessionId,
490
+ ].join(" ")
232
491
  : null;
233
492
 
234
493
  if (options.text) {
@@ -238,6 +497,8 @@ export async function cmdRun(options: AxCliOptions): Promise<number> {
238
497
  }
239
498
  if (playgroundUrl !== null) {
240
499
  process.stderr.write(`playground: ${playgroundUrl}\n`);
500
+ } else if (playgroundHint !== null) {
501
+ process.stderr.write(`playground: ${playgroundHint}\n`);
241
502
  }
242
503
  } else {
243
504
  console.log(
@@ -248,12 +509,15 @@ export async function cmdRun(options: AxCliOptions): Promise<number> {
248
509
  continuationToken: result.continuationToken ?? null,
249
510
  trace: eventsPath ?? null,
250
511
  playgroundUrl,
512
+ playgroundHint,
251
513
  visualize:
252
- playgroundUrl !== null
253
- ? `Open ${playgroundUrl}`
254
- : eventsPath !== undefined
255
- ? `Start the playground (${CLI_COMMAND_NAME} serve --dir ${options.dir}), then Open trace → ${eventsPath}`
256
- : null,
514
+ playgroundHint !== null
515
+ ? `Run ${playgroundHint}`
516
+ : playgroundUrl !== null
517
+ ? `Open ${playgroundUrl}`
518
+ : eventsPath !== undefined
519
+ ? `Start the playground (${CLI_COMMAND_NAME} serve --dir ${options.dir}), then Open trace → ${eventsPath}`
520
+ : null,
257
521
  trajectory: result.trajectory,
258
522
  },
259
523
  null,
@@ -296,22 +560,25 @@ export async function cmdCall(options: AxCliOptions): Promise<number> {
296
560
 
297
561
  const target = await resolveTarget(options);
298
562
  try {
563
+ const headers: Record<string, string> = {
564
+ "content-type": "application/json",
565
+ };
566
+ if (target.headers !== undefined) {
567
+ Object.assign(headers, target.headers);
568
+ }
569
+ if (options.bearerToken !== undefined) {
570
+ headers.authorization = `Bearer ${options.bearerToken}`;
571
+ }
572
+ const body: { input: unknown; sessionId?: string } = { input };
573
+ if (options.sessionId !== undefined) {
574
+ body.sessionId = options.sessionId;
575
+ }
299
576
  const response = await fetch(
300
577
  `${target.agentUrl}/v1/tools/${encodeURIComponent(toolName)}`,
301
578
  {
302
579
  method: "POST",
303
- headers: {
304
- "content-type": "application/json",
305
- ...(options.bearerToken === undefined
306
- ? {}
307
- : { authorization: `Bearer ${options.bearerToken}` }),
308
- },
309
- body: JSON.stringify({
310
- input,
311
- ...(options.sessionId === undefined
312
- ? {}
313
- : { sessionId: options.sessionId }),
314
- }),
580
+ headers,
581
+ body: JSON.stringify(body),
315
582
  }
316
583
  );
317
584
  const payload: unknown = await response
@@ -399,22 +666,19 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
399
666
  const results = await runDiscoveredEvals({
400
667
  projectRoot,
401
668
  baseUrl: target.agentUrl,
402
- ...(options.evalIds === undefined || options.evalIds.length === 0
403
- ? {}
404
- : { filterIds: options.evalIds }),
405
- ...(options.tags === undefined || options.tags.length === 0
406
- ? {}
407
- : { tags: options.tags }),
408
- ...(options.timeoutMs === undefined
409
- ? {}
410
- : { timeoutMs: options.timeoutMs }),
411
- ...(options.bearerToken === undefined
412
- ? {}
413
- : { bearerToken: options.bearerToken }),
669
+ filterIds:
670
+ options.evalIds === undefined || options.evalIds.length === 0
671
+ ? undefined
672
+ : options.evalIds,
673
+ tags:
674
+ options.tags === undefined || options.tags.length === 0
675
+ ? undefined
676
+ : options.tags,
677
+ timeoutMs: options.timeoutMs,
678
+ bearerToken: options.bearerToken,
679
+ headers: target.headers,
414
680
  verbose: Boolean(options.verbose) || stream,
415
- ...(progress === undefined
416
- ? {}
417
- : { onEvent: (e) => progress.onEvent(e) }),
681
+ onEvent: progress === undefined ? undefined : (e) => progress.onEvent(e),
418
682
  });
419
683
 
420
684
  if (results.length === 0) {
@@ -472,3 +736,389 @@ function printEvalResults(
472
736
  }
473
737
  return failed === 0 ? 0 : 1;
474
738
  }
739
+
740
+ /**
741
+ * `logs` — follow deploy progress (`--prod`) then runtime agent logs.
742
+ * Local / `--url` tails `/v1/logs` only.
743
+ */
744
+ export async function cmdLogs(options: AxCliOptions): Promise<number> {
745
+ if (options.prod === true && options.url !== undefined) {
746
+ process.stderr.write("--prod and --url are mutually exclusive\n");
747
+ return 2;
748
+ }
749
+
750
+ const controller = new AbortController();
751
+ const onSignal = (): void => {
752
+ controller.abort();
753
+ };
754
+ process.on("SIGINT", onSignal);
755
+ process.on("SIGTERM", onSignal);
756
+
757
+ try {
758
+ if (options.prod === true) {
759
+ const slug = resolveProdSlug({
760
+ dir: options.dir,
761
+ cwd: options.cwd,
762
+ slug: options.slug,
763
+ });
764
+ const auth = await resolveDeployAuth({
765
+ team: options.team,
766
+ apiKey: options.apiKey,
767
+ fetchImpl: options.fetchImpl,
768
+ });
769
+ if (typeof auth === "number") {
770
+ return auth;
771
+ }
772
+ await runLogsTail({
773
+ prod: true,
774
+ dir: options.dir,
775
+ cwd: options.cwd,
776
+ slug,
777
+ team: options.team,
778
+ apiKey: options.apiKey,
779
+ bearerToken: options.bearerToken,
780
+ once: options.once,
781
+ json: options.json,
782
+ fetchImpl: options.fetchImpl,
783
+ signal: controller.signal,
784
+ getDeployment: () =>
785
+ auth.client.getDeployment({ teamId: auth.teamId, slug }),
786
+ resolveRuntime: async () => {
787
+ const target = await resolveProdTarget({
788
+ dir: options.dir,
789
+ cwd: options.cwd,
790
+ slug,
791
+ team: options.team,
792
+ apiKey: options.apiKey,
793
+ fetchImpl: options.fetchImpl,
794
+ err: () => {},
795
+ });
796
+ return { agentUrl: target.agentUrl, headers: target.headers };
797
+ },
798
+ });
799
+ return 0;
800
+ }
801
+
802
+ await runLogsTail({
803
+ dir: options.dir,
804
+ cwd: options.cwd,
805
+ url: options.url ?? "http://127.0.0.1:3000",
806
+ bearerToken: options.bearerToken,
807
+ once: options.once,
808
+ json: options.json,
809
+ fetchImpl: options.fetchImpl,
810
+ signal: controller.signal,
811
+ });
812
+ return 0;
813
+ } catch (error) {
814
+ if (controller.signal.aborted) {
815
+ return 0;
816
+ }
817
+ const raw = error instanceof Error ? error.message : String(error);
818
+ process.stderr.write(`${sanitizeCustomerError(raw)}\n`);
819
+ return 1;
820
+ } finally {
821
+ process.off("SIGINT", onSignal);
822
+ process.off("SIGTERM", onSignal);
823
+ }
824
+ }
825
+
826
+ /** Resolve local / `--url` / `--prod` agent base URL + request headers. */
827
+ async function resolveSessionTarget(
828
+ options: AxCliOptions
829
+ ): Promise<{ agentUrl: string; headers?: Record<string, string> } | number> {
830
+ if (options.prod === true && options.url !== undefined) {
831
+ process.stderr.write("--prod and --url are mutually exclusive\n");
832
+ return 2;
833
+ }
834
+ if (options.prod === true) {
835
+ try {
836
+ const target = await resolveProdTarget({
837
+ dir: options.dir,
838
+ cwd: options.cwd,
839
+ slug: options.slug,
840
+ team: options.team,
841
+ apiKey: options.apiKey,
842
+ fetchImpl: options.fetchImpl,
843
+ });
844
+ return { agentUrl: target.agentUrl, headers: target.headers };
845
+ } catch (error) {
846
+ process.stderr.write(
847
+ `${sanitizeCustomerError(
848
+ error instanceof Error ? error.message : String(error)
849
+ )}\n`
850
+ );
851
+ return 1;
852
+ }
853
+ }
854
+ return {
855
+ agentUrl: (options.url ?? "http://127.0.0.1:3000").replace(/\/$/, ""),
856
+ };
857
+ }
858
+
859
+ /**
860
+ * `sessions` — list sessions on a running (or `--prod` hosted) agent.
861
+ */
862
+ export async function cmdSessions(options: AxCliOptions): Promise<number> {
863
+ const target = await resolveSessionTarget(options);
864
+ if (typeof target === "number") {
865
+ return target;
866
+ }
867
+ try {
868
+ const sessions = await listSessions({
869
+ baseUrl: target.agentUrl,
870
+ auth: {
871
+ bearerToken: options.bearerToken,
872
+ headers: target.headers,
873
+ },
874
+ fetchImpl: options.fetchImpl,
875
+ });
876
+ if (options.json === true) {
877
+ process.stdout.write(`${JSON.stringify({ sessions }, null, 2)}\n`);
878
+ return 0;
879
+ }
880
+ process.stdout.write(formatSessionsTable(sessions));
881
+ return 0;
882
+ } catch (error) {
883
+ process.stderr.write(
884
+ `${sanitizeCustomerError(
885
+ error instanceof Error ? error.message : String(error)
886
+ )}\n`
887
+ );
888
+ return 1;
889
+ }
890
+ }
891
+
892
+ /**
893
+ * `session <id>` — view a full session (trajectory text by default).
894
+ * `--json` → trajectory JSON; `--events` (`rawEvents`) → raw event list.
895
+ * Interactive reattach: `resume <id>` (or `chat --session`).
896
+ */
897
+ export async function cmdSession(
898
+ sessionId: string | undefined,
899
+ options: AxCliOptions
900
+ ): Promise<number> {
901
+ if (sessionId === undefined || sessionId.trim() === "") {
902
+ process.stderr.write(
903
+ `Usage: ${CLI_COMMAND_NAME} session <sessionId> [--url | --prod] [--json | --text | --events]\n`
904
+ );
905
+ return 2;
906
+ }
907
+ if (options.rawEvents === true && options.json === true) {
908
+ process.stderr.write("--events and --json are mutually exclusive\n");
909
+ return 2;
910
+ }
911
+
912
+ const target = await resolveSessionTarget(options);
913
+ if (typeof target === "number") {
914
+ return target;
915
+ }
916
+
917
+ try {
918
+ const events = await fetchSessionEvents({
919
+ baseUrl: target.agentUrl,
920
+ sessionId,
921
+ auth: {
922
+ bearerToken: options.bearerToken,
923
+ headers: target.headers,
924
+ },
925
+ fetchImpl: options.fetchImpl,
926
+ });
927
+
928
+ if (options.rawEvents === true) {
929
+ process.stdout.write(
930
+ `${JSON.stringify({ sessionId, events }, null, 2)}\n`
931
+ );
932
+ return 0;
933
+ }
934
+
935
+ const trajectory = buildTrajectory(events);
936
+ if (options.json === true) {
937
+ process.stdout.write(`${JSON.stringify(trajectory, null, 2)}\n`);
938
+ return 0;
939
+ }
940
+ process.stdout.write(formatTrajectoryText(trajectory));
941
+ return 0;
942
+ } catch (error) {
943
+ process.stderr.write(
944
+ `${sanitizeCustomerError(
945
+ error instanceof Error ? error.message : String(error)
946
+ )}\n`
947
+ );
948
+ return 1;
949
+ }
950
+ }
951
+
952
+ /**
953
+ * `playground` — open the web UI against local / `--url` / `--prod`.
954
+ *
955
+ * Hosted engines need short-lived access headers a browser cannot set, so
956
+ * `--prod` (and `--bearer-token`) run a loopback reverse proxy that injects
957
+ * them. Ctrl-C stops the proxy.
958
+ */
959
+ export async function cmdPlayground(options: AxCliOptions): Promise<number> {
960
+ if (options.prod === true && options.url !== undefined) {
961
+ process.stderr.write("--prod and --url are mutually exclusive\n");
962
+ return 2;
963
+ }
964
+
965
+ const sessionId =
966
+ options.sessionId !== undefined && options.sessionId.trim() !== ""
967
+ ? options.sessionId.trim()
968
+ : undefined;
969
+
970
+ // Hosted: mint engineAccess and refresh it for the life of the proxy.
971
+ if (options.prod === true) {
972
+ let initial: Awaited<ReturnType<typeof resolveProdTarget>>;
973
+ try {
974
+ initial = await resolveProdTarget({
975
+ dir: options.dir,
976
+ cwd: options.cwd,
977
+ slug: options.slug,
978
+ team: options.team,
979
+ apiKey: options.apiKey,
980
+ fetchImpl: options.fetchImpl,
981
+ });
982
+ } catch (error) {
983
+ process.stderr.write(
984
+ `${sanitizeCustomerError(
985
+ error instanceof Error ? error.message : String(error)
986
+ )}\n`
987
+ );
988
+ return 1;
989
+ }
990
+
991
+ let proxy: Awaited<ReturnType<typeof startPlaygroundProxy>>;
992
+ try {
993
+ proxy = await startPlaygroundProxy({
994
+ upstreamUrl: initial.agentUrl,
995
+ headers: initial.headers,
996
+ expiresAt: initial.expiresAt,
997
+ refreshUpstream: async () => {
998
+ const next = await resolveProdTarget({
999
+ dir: options.dir,
1000
+ cwd: options.cwd,
1001
+ slug: options.slug ?? initial.slug,
1002
+ team: options.team,
1003
+ apiKey: options.apiKey,
1004
+ fetchImpl: options.fetchImpl,
1005
+ err: () => {},
1006
+ });
1007
+ return {
1008
+ upstreamUrl: next.agentUrl,
1009
+ headers: next.headers,
1010
+ expiresAt: next.expiresAt,
1011
+ };
1012
+ },
1013
+ });
1014
+ } catch (error) {
1015
+ process.stderr.write(
1016
+ `${sanitizeCustomerError(
1017
+ error instanceof Error ? error.message : String(error)
1018
+ )}\n`
1019
+ );
1020
+ return 1;
1021
+ }
1022
+
1023
+ return await runPlaygroundProxySession({
1024
+ proxy,
1025
+ sessionId,
1026
+ print: options.print === true,
1027
+ signal: options.signal,
1028
+ });
1029
+ }
1030
+
1031
+ const target = await resolveSessionTarget(options);
1032
+ if (typeof target === "number") {
1033
+ return target;
1034
+ }
1035
+
1036
+ const needsProxy =
1037
+ (target.headers !== undefined && Object.keys(target.headers).length > 0) ||
1038
+ (options.bearerToken !== undefined && options.bearerToken.trim() !== "");
1039
+
1040
+ if (!needsProxy) {
1041
+ const base = `${target.agentUrl.replace(/\/$/, "")}/playground`;
1042
+ const playgroundUrl =
1043
+ sessionId === undefined
1044
+ ? base
1045
+ : `${base}?sessionId=${encodeURIComponent(sessionId)}`;
1046
+ process.stdout.write(`${playgroundUrl}\n`);
1047
+ if (options.print !== true) {
1048
+ openBrowser(playgroundUrl);
1049
+ }
1050
+ return 0;
1051
+ }
1052
+
1053
+ let proxy: Awaited<ReturnType<typeof startPlaygroundProxy>>;
1054
+ try {
1055
+ proxy = await startPlaygroundProxy({
1056
+ upstreamUrl: target.agentUrl,
1057
+ headers: target.headers,
1058
+ bearerToken: options.bearerToken,
1059
+ });
1060
+ } catch (error) {
1061
+ process.stderr.write(
1062
+ `${sanitizeCustomerError(
1063
+ error instanceof Error ? error.message : String(error)
1064
+ )}\n`
1065
+ );
1066
+ return 1;
1067
+ }
1068
+
1069
+ return await runPlaygroundProxySession({
1070
+ proxy,
1071
+ sessionId,
1072
+ print: options.print === true,
1073
+ signal: options.signal,
1074
+ });
1075
+ }
1076
+
1077
+ async function runPlaygroundProxySession(args: {
1078
+ proxy: Awaited<ReturnType<typeof startPlaygroundProxy>>;
1079
+ sessionId?: string;
1080
+ print: boolean;
1081
+ signal?: AbortSignal;
1082
+ }): Promise<number> {
1083
+ const { proxy } = args;
1084
+
1085
+ const playgroundUrl = proxy.playgroundUrl(args.sessionId);
1086
+ process.stdout.write(`${playgroundUrl}\n`);
1087
+ process.stderr.write(`Playground proxy on loopback (Ctrl-C to stop).\n`);
1088
+ if (args.print !== true) {
1089
+ openBrowser(playgroundUrl);
1090
+ }
1091
+
1092
+ const controller = new AbortController();
1093
+ const onSignal = (): void => {
1094
+ controller.abort();
1095
+ };
1096
+ process.on("SIGINT", onSignal);
1097
+ process.on("SIGTERM", onSignal);
1098
+ const external = args.signal;
1099
+ if (external !== undefined) {
1100
+ if (external.aborted) {
1101
+ controller.abort();
1102
+ } else {
1103
+ external.addEventListener("abort", onSignal, { once: true });
1104
+ }
1105
+ }
1106
+
1107
+ try {
1108
+ await new Promise<void>((resolve) => {
1109
+ if (controller.signal.aborted) {
1110
+ resolve();
1111
+ return;
1112
+ }
1113
+ controller.signal.addEventListener("abort", () => resolve(), {
1114
+ once: true,
1115
+ });
1116
+ });
1117
+ return 0;
1118
+ } finally {
1119
+ process.off("SIGINT", onSignal);
1120
+ process.off("SIGTERM", onSignal);
1121
+ external?.removeEventListener("abort", onSignal);
1122
+ await proxy.close().catch(() => {});
1123
+ }
1124
+ }