@kortyx/cli 0.10.1 → 0.11.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.1](https://github.com/kortyx-io/kortyx/compare/cli-v0.11.0...cli-v0.11.1) (2026-09-21)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * preserve interrupt control flow and Studio compatibility ([#226](https://github.com/kortyx-io/kortyx/issues/226)) ([72ba7c2](https://github.com/kortyx-io/kortyx/commit/72ba7c2b9a467db0e54b5a4ab5844c8d7f895135))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @kortyx/agent bumped to 0.26.1
16
+ * @kortyx/core bumped to 0.10.1
17
+ * @kortyx/telemetry-contracts bumped to 0.11.1
18
+
19
+ ## [0.11.0](https://github.com/kortyx-io/kortyx/compare/cli-v0.10.1...cli-v0.11.0) (2026-09-21)
20
+
21
+
22
+ ### Features
23
+
24
+ * **hooks:** add model-driven interrupt contracts to useReason ([#224](https://github.com/kortyx-io/kortyx/issues/224)) ([e0147d5](https://github.com/kortyx-io/kortyx/commit/e0147d5234ca38d450d452af924b242f8cbdeb16))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * @kortyx/agent bumped to 0.26.0
32
+ * @kortyx/core bumped to 0.10.0
33
+ * @kortyx/telemetry-contracts bumped to 0.11.0
34
+
3
35
  ## [0.10.1](https://github.com/kortyx-io/kortyx/compare/cli-v0.10.0...cli-v0.10.1) (2026-09-21)
4
36
 
5
37
 
package/dist/index.js CHANGED
@@ -29,12 +29,12 @@ var import_node_os3 = require("os");
29
29
  var import_node_path4 = require("path");
30
30
  var import_agent = require("@kortyx/agent");
31
31
  var import_errors = require("@kortyx/core/errors");
32
- var import_telemetry_contracts4 = require("@kortyx/telemetry-contracts");
32
+ var import_telemetry_contracts6 = require("@kortyx/telemetry-contracts");
33
33
  var import_commander4 = require("commander");
34
34
  var import_api = require("tsx/cjs/api");
35
35
 
36
36
  // src/connections-command.ts
37
- var import_telemetry_contracts = require("@kortyx/telemetry-contracts");
37
+ var import_telemetry_contracts2 = require("@kortyx/telemetry-contracts");
38
38
  var import_commander = require("commander");
39
39
 
40
40
  // src/connections.ts
@@ -45,6 +45,7 @@ var import_node_path2 = require("path");
45
45
  var import_zod2 = require("zod");
46
46
 
47
47
  // src/studio/read-client.ts
48
+ var import_telemetry_contracts = require("@kortyx/telemetry-contracts");
48
49
  var StudioReadError = class extends Error {
49
50
  constructor(code, message, status, requestId) {
50
51
  super(message);
@@ -101,6 +102,10 @@ var StudioReadClient = class {
101
102
  apiKey;
102
103
  request;
103
104
  apiUrl;
105
+ compatibility = {
106
+ protocolVersion: null,
107
+ release: null
108
+ };
104
109
  async get(path, schema, query = {}) {
105
110
  if (!/^\/v1\/studio\/(?:context|catalogs|workflows|(?:runs|sessions|interrupts)(?:\/[^/?#]+)?)$/.test(
106
111
  path
@@ -119,7 +124,8 @@ var StudioReadClient = class {
119
124
  method: "GET",
120
125
  headers: {
121
126
  authorization: `Bearer ${this.apiKey}`,
122
- accept: "application/json"
127
+ accept: "application/json",
128
+ "x-kortyx-studio-api-version": import_telemetry_contracts.STUDIO_API_PROTOCOL_VERSION
123
129
  },
124
130
  redirect: "error",
125
131
  signal: AbortSignal.timeout(15e3)
@@ -131,6 +137,19 @@ var StudioReadClient = class {
131
137
  );
132
138
  }
133
139
  const requestId = response.headers.get("x-request-id") ?? void 0;
140
+ this.compatibility = {
141
+ protocolVersion: response.headers.get("x-kortyx-studio-api-version"),
142
+ release: response.headers.get("x-kortyx-studio-release")
143
+ };
144
+ if (this.compatibility.protocolVersion && this.compatibility.protocolVersion !== import_telemetry_contracts.STUDIO_API_PROTOCOL_VERSION) {
145
+ await response.body?.cancel();
146
+ throw new StudioReadError(
147
+ "incompatible_studio_api",
148
+ `This CLI supports Studio API v${import_telemetry_contracts.STUDIO_API_PROTOCOL_VERSION}, but the server reports v${this.compatibility.protocolVersion}${this.compatibility.release ? ` (${this.compatibility.release})` : ""}. Install a CLI and Studio release that use the same Studio API major.`,
149
+ response.status,
150
+ requestId
151
+ );
152
+ }
134
153
  if (!response.ok) {
135
154
  await response.body?.cancel();
136
155
  const message = response.status === 401 ? "Invalid, expired, or revoked Studio read key." : response.status === 403 ? "The API key lacks studio:read permission." : response.status === 404 ? "Entity not found in this connection's project. Check the URL and selected connection." : `Studio API returned HTTP ${response.status}.`;
@@ -180,9 +199,10 @@ var StudioReadClient = class {
180
199
  }
181
200
  const result = schema.safeParse(value);
182
201
  if (!result.success) {
202
+ const server = this.compatibility.protocolVersion ? `Studio API v${this.compatibility.protocolVersion}${this.compatibility.release ? ` (${this.compatibility.release})` : ""}` : "a server that did not report its Studio API version";
183
203
  throw new StudioReadError(
184
204
  "schema_mismatch",
185
- "Studio response does not match this CLI's contracts. Check API/CLI version compatibility.",
205
+ `Studio response does not match this CLI's Studio API v${import_telemetry_contracts.STUDIO_API_PROTOCOL_VERSION} contracts. The response came from ${server}. Upgrade both components if this server predates protocol negotiation; otherwise report this as an API compatibility regression.`,
186
206
  response.status,
187
207
  requestId
188
208
  );
@@ -245,6 +265,7 @@ services:
245
265
  NODE_ENV: production
246
266
  API_HOST: 0.0.0.0
247
267
  API_PORT: 6400
268
+ KORTYX_STUDIO_RELEASE: \${KORTYX_STUDIO_IMAGE_TAG:-latest}
248
269
  ports:
249
270
  - "127.0.0.1:\${API_PORT:-6400}:6400"
250
271
  depends_on:
@@ -447,6 +468,29 @@ var writePrivateFileAtomically = async (path, contents) => {
447
468
  }
448
469
  }
449
470
  };
471
+ var readOptionalFile = async (path) => {
472
+ try {
473
+ return await (0, import_promises.readFile)(path, "utf8");
474
+ } catch (error) {
475
+ if (isMissingFile(error)) return null;
476
+ throw error;
477
+ }
478
+ };
479
+ var captureStudioState = async (home) => ({
480
+ config: await readOptionalFile(studioConfigPath(home)),
481
+ environment: await readOptionalFile(studioEnvPath(home)),
482
+ compose: await readOptionalFile(studioComposePath(home))
483
+ });
484
+ var restoreStudioState = async (home, snapshot) => {
485
+ for (const [path, contents] of [
486
+ [studioConfigPath(home), snapshot.config],
487
+ [studioEnvPath(home), snapshot.environment],
488
+ [studioComposePath(home), snapshot.compose]
489
+ ]) {
490
+ if (contents === null) await (0, import_promises.unlink)(path).catch(() => void 0);
491
+ else await writePrivateFileAtomically(path, contents);
492
+ }
493
+ };
450
494
  var readStudioEnvironment = async (home) => {
451
495
  const path = studioEnvPath(home);
452
496
  return parseEnvironmentFile(await (0, import_promises.readFile)(path, "utf8"), path);
@@ -490,17 +534,15 @@ var ensureStudioState = async (options, runtime) => {
490
534
  await (0, import_promises.mkdir)((0, import_node_path.join)(options.home, ".updates"), { recursive: true, mode: 448 });
491
535
  const existing = await readStudioConfig(options.home);
492
536
  const config = resolveConfig(existing, options, runtime);
493
- await (0, import_promises.writeFile)(
537
+ await writePrivateFileAtomically(
494
538
  studioConfigPath(options.home),
495
539
  `${JSON.stringify(config, null, 2)}
496
- `,
497
- { mode: 384 }
540
+ `
541
+ );
542
+ await writePrivateFileAtomically(
543
+ studioComposePath(options.home),
544
+ STUDIO_COMPOSE_FILE
498
545
  );
499
- await (0, import_promises.chmod)(studioConfigPath(options.home), 384);
500
- await (0, import_promises.writeFile)(studioComposePath(options.home), STUDIO_COMPOSE_FILE, {
501
- mode: 384
502
- });
503
- await (0, import_promises.chmod)(studioComposePath(options.home), 384);
504
546
  let environment;
505
547
  try {
506
548
  environment = await readStudioEnvironment(options.home);
@@ -772,7 +814,7 @@ var createConnectionsCommand = (log = console.log, request = fetch) => {
772
814
  );
773
815
  const context = await client.get(
774
816
  "/v1/studio/context",
775
- import_telemetry_contracts.StudioContextResponseSchema
817
+ import_telemetry_contracts2.StudioContextResponseSchema
776
818
  );
777
819
  if (!context.apiKey.scopes.includes("studio:read"))
778
820
  throw new StudioReadError(
@@ -838,6 +880,7 @@ var import_commander3 = require("commander");
838
880
 
839
881
  // src/studio/local-stack.ts
840
882
  var import_promises3 = require("fs/promises");
883
+ var import_telemetry_contracts3 = require("@kortyx/telemetry-contracts");
841
884
  var composeArgs = (home, args) => [
842
885
  "compose",
843
886
  "--env-file",
@@ -980,45 +1023,63 @@ var rotateStudioCredentials = async (home, runtime) => {
980
1023
  };
981
1024
  var startStudio = async (options, runtime) => {
982
1025
  await preflightDocker(runtime);
983
- const config = await ensureStudioState(options, runtime);
984
- const environment = await readStudioEnvironment(options.home);
985
- const apiImage = String(
986
- environment.KORTYX_API_IMAGE_REF ?? `${environment.KORTYX_API_IMAGE ?? "ghcr.io/kortyx-io/kortyx-api"}:${config.imageTag}`
987
- );
988
- await runtime.run("docker", ["pull", apiImage], { inherit: true });
989
- const capability = await runtime.run(
990
- "docker",
991
- [
992
- "run",
993
- "--rm",
994
- "--entrypoint",
995
- "node",
996
- apiImage,
997
- "-e",
998
- 'if (require("node:fs").existsSync("/app/apps/api/dist/updater.js")) console.log("supported")'
999
- ],
1000
- { inherit: false }
1001
- );
1002
- await (0, import_promises3.writeFile)(
1003
- studioComposePath(options.home),
1004
- studioComposeFile(
1005
- process.platform !== "win32" && capability.stdout.trim() === "supported"
1006
- ),
1007
- { mode: 384 }
1008
- );
1009
- if (!await hasRunningServices(options.home, runtime)) {
1010
- await assertPortsAvailable(config, runtime);
1026
+ const snapshot = await captureStudioState(options.home);
1027
+ try {
1028
+ const config = await ensureStudioState(options, runtime);
1029
+ const environment = await readStudioEnvironment(options.home);
1030
+ const apiImage = String(
1031
+ environment.KORTYX_API_IMAGE_REF ?? `${environment.KORTYX_API_IMAGE ?? "ghcr.io/kortyx-io/kortyx-api"}:${config.imageTag}`
1032
+ );
1033
+ const studioImage = String(
1034
+ environment.KORTYX_STUDIO_IMAGE_REF ?? `${environment.KORTYX_STUDIO_IMAGE ?? "ghcr.io/kortyx-io/kortyx-studio"}:${config.imageTag}`
1035
+ );
1036
+ await runtime.run("docker", ["pull", apiImage], { inherit: true });
1037
+ if (studioImage !== apiImage)
1038
+ await runtime.run("docker", ["pull", studioImage], { inherit: true });
1039
+ const capability = await runtime.run(
1040
+ "docker",
1041
+ [
1042
+ "run",
1043
+ "--rm",
1044
+ "--entrypoint",
1045
+ "node",
1046
+ apiImage,
1047
+ "-e",
1048
+ 'if (require("node:fs").existsSync("/app/apps/api/dist/updater.js")) console.log("supported")'
1049
+ ],
1050
+ { inherit: false }
1051
+ );
1052
+ await (0, import_promises3.writeFile)(
1053
+ studioComposePath(options.home),
1054
+ studioComposeFile(
1055
+ process.platform !== "win32" && capability.stdout.trim() === "supported"
1056
+ ),
1057
+ { mode: 384 }
1058
+ );
1059
+ if (!await hasRunningServices(options.home, runtime)) {
1060
+ await assertPortsAvailable(config, runtime);
1061
+ }
1062
+ runtime.log(
1063
+ "Starting Kortyx Studio. The first image pull can take a few minutes."
1064
+ );
1065
+ await runCompose(
1066
+ options.home,
1067
+ ["up", "-d", "--remove-orphans", "--wait", "--wait-timeout", "180"],
1068
+ runtime
1069
+ );
1070
+ runtime.log("Kortyx Studio is ready.");
1071
+ await printStudioConnection(options.home, config, runtime);
1072
+ } catch (error) {
1073
+ try {
1074
+ await restoreStudioState(options.home, snapshot);
1075
+ } catch (rollbackError) {
1076
+ throw new AggregateError(
1077
+ [error, rollbackError],
1078
+ `Studio startup failed and the previous configuration could not be restored. Inspect "${options.home}" before retrying.`
1079
+ );
1080
+ }
1081
+ throw error;
1011
1082
  }
1012
- runtime.log(
1013
- "Starting Kortyx Studio. The first image pull can take a few minutes."
1014
- );
1015
- await runCompose(
1016
- options.home,
1017
- ["up", "-d", "--remove-orphans", "--wait", "--wait-timeout", "180"],
1018
- runtime
1019
- );
1020
- runtime.log("Kortyx Studio is ready.");
1021
- await printStudioConnection(options.home, config, runtime);
1022
1083
  };
1023
1084
  var stopStudio = async (home, runtime) => {
1024
1085
  await preflightDocker(runtime);
@@ -1046,6 +1107,24 @@ var showStudioStatus = async (home, runtime) => {
1046
1107
  runtime.log();
1047
1108
  runtime.log(`Studio: http://localhost:${config.studioPort}`);
1048
1109
  runtime.log(`API: http://localhost:${config.apiPort}`);
1110
+ if (runtime.request) {
1111
+ const environment = await readStudioEnvironment(home);
1112
+ const client = new StudioReadClient(
1113
+ `http://localhost:${config.apiPort}`,
1114
+ environment.KORTYX_STUDIO_API_KEY,
1115
+ runtime.request
1116
+ );
1117
+ try {
1118
+ await client.get("/v1/studio/context", import_telemetry_contracts3.StudioContextResponseSchema);
1119
+ runtime.log(
1120
+ client.compatibility.protocolVersion ? `Compatibility: Studio API v${client.compatibility.protocolVersion} (${client.compatibility.release ?? config.imageTag}) is compatible with CLI API v${import_telemetry_contracts3.STUDIO_API_PROTOCOL_VERSION}.` : `Compatibility: response is compatible with CLI API v${import_telemetry_contracts3.STUDIO_API_PROTOCOL_VERSION}; this older server does not report its protocol version.`
1121
+ );
1122
+ } catch (error) {
1123
+ runtime.log(
1124
+ `Compatibility: ${error instanceof StudioReadError ? error.message : "could not query the Studio API."}`
1125
+ );
1126
+ }
1127
+ }
1049
1128
  runtime.log(`State: ${home}`);
1050
1129
  };
1051
1130
  var showStudioLogs = async (home, follow, runtime) => {
@@ -1072,11 +1151,11 @@ var resetStudio = async (home, confirm, runtime) => {
1072
1151
  };
1073
1152
 
1074
1153
  // src/studio/read-command.ts
1075
- var import_telemetry_contracts3 = require("@kortyx/telemetry-contracts");
1154
+ var import_telemetry_contracts5 = require("@kortyx/telemetry-contracts");
1076
1155
  var import_commander2 = require("commander");
1077
1156
 
1078
1157
  // src/studio/read-analysis.ts
1079
- var import_telemetry_contracts2 = require("@kortyx/telemetry-contracts");
1158
+ var import_telemetry_contracts4 = require("@kortyx/telemetry-contracts");
1080
1159
  var stringValue = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
1081
1160
  var numberValue = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1082
1161
  var recordValue = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
@@ -1137,6 +1216,12 @@ var buildStudioTimeline = (events, interrupts = []) => {
1137
1216
  ...interrupt?.interactionMode || stringValue(payload.interactionMode) ? {
1138
1217
  interactionMode: interrupt?.interactionMode ?? stringValue(payload.interactionMode)
1139
1218
  } : {},
1219
+ ...interrupt?.contract || stringValue(payload.contract) ? { contract: interrupt?.contract ?? stringValue(payload.contract) } : {},
1220
+ ...interrupt?.schemaId || stringValue(payload.schemaId) ? { schemaId: interrupt?.schemaId ?? stringValue(payload.schemaId) } : {},
1221
+ ...interrupt?.schemaVersion || stringValue(payload.schemaVersion) ? {
1222
+ schemaVersion: interrupt?.schemaVersion ?? stringValue(payload.schemaVersion)
1223
+ } : {},
1224
+ ...payload.request !== void 0 ? { request: payload.request } : {},
1140
1225
  ...interrupt?.question !== void 0 && interrupt.question !== null ? { question: interrupt.question } : payload.question !== void 0 ? { question: payload.question } : {}
1141
1226
  });
1142
1227
  continue;
@@ -1146,7 +1231,9 @@ var buildStudioTimeline = (events, interrupts = []) => {
1146
1231
  ...base,
1147
1232
  kind: "resume",
1148
1233
  ...stringValue(payload.interruptId) ? { interruptId: stringValue(payload.interruptId) } : {},
1149
- ...stringValue(payload.resumeOutcome) ? { resumeOutcome: stringValue(payload.resumeOutcome) } : {}
1234
+ ...stringValue(payload.resumeOutcome) ? { resumeOutcome: stringValue(payload.resumeOutcome) } : {},
1235
+ ...stringValue(payload.contract) ? { contract: stringValue(payload.contract) } : {},
1236
+ ...payload.responseValue !== void 0 ? { response: payload.responseValue } : payload.response !== void 0 ? { response: payload.response } : {}
1150
1237
  });
1151
1238
  continue;
1152
1239
  }
@@ -1184,7 +1271,7 @@ var focusStudioEvents = (events, target) => {
1184
1271
  "The URL has no event, trace, node, branch, or call selector to focus."
1185
1272
  );
1186
1273
  const callEventIds = new Set(
1187
- selection.call ? (0, import_telemetry_contracts2.projectWorkflowCalls)(events).filter(
1274
+ selection.call ? (0, import_telemetry_contracts4.projectWorkflowCalls)(events).filter(
1188
1275
  (call) => call.id === selection.call || call.callId === selection.call || call.invocationId === selection.call
1189
1276
  ).flatMap((call) => call.events.map((event) => event.id)) : []
1190
1277
  );
@@ -1472,8 +1559,12 @@ var sanitizeStudioData = (value, includeContent, key = "") => {
1472
1559
  var summarizeEvidence = (events) => {
1473
1560
  const findings = events.flatMap((event) => {
1474
1561
  const payload = event.payload;
1475
- const failed = /(?:failed|error|fault)/i.test(event.type) || payload.outcome === "fault" || payload.status === "failed";
1476
- const warning = /(?:retry|cancel|interrupt|limit_reached|denied|suspended|waiting)/i.test(
1562
+ const error = payload.error && typeof payload.error === "object" && !Array.isArray(payload.error) ? payload.error : {};
1563
+ const suspension = event.type === "span.failed" && (error.controlFlow === true || ["GraphInterrupt", "NodeInterrupt", "ParallelChildWaiting"].includes(
1564
+ String(error.name)
1565
+ ));
1566
+ const failed = !suspension && (/(?:failed|error|fault)/i.test(event.type) || payload.outcome === "fault" || payload.status === "failed");
1567
+ const warning = suspension || /(?:retry|cancel|interrupt|limit_reached|denied|suspended|waiting)/i.test(
1477
1568
  event.type
1478
1569
  ) || ["denied", "cancelled"].includes(String(payload.outcome));
1479
1570
  if (!failed && !warning) return [];
@@ -1559,10 +1650,10 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1559
1650
  const { connection, client } = await clientFor(options, target.url);
1560
1651
  const context = await client.get(
1561
1652
  "/v1/studio/context",
1562
- import_telemetry_contracts3.StudioContextResponseSchema
1653
+ import_telemetry_contracts5.StudioContextResponseSchema
1563
1654
  );
1564
1655
  const path = `/v1/studio/${target.entity}/${encodeURIComponent(target.id)}`;
1565
- const detail = target.entity === "runs" ? await client.get(path, import_telemetry_contracts3.StudioRunDetailResponseSchema) : target.entity === "sessions" ? await client.get(path, import_telemetry_contracts3.StudioSessionDetailResponseSchema) : await client.get(path, import_telemetry_contracts3.StudioInterruptDetailResponseSchema);
1656
+ const detail = target.entity === "runs" ? await client.get(path, import_telemetry_contracts5.StudioRunDetailResponseSchema) : target.entity === "sessions" ? await client.get(path, import_telemetry_contracts5.StudioSessionDetailResponseSchema) : await client.get(path, import_telemetry_contracts5.StudioInterruptDetailResponseSchema);
1566
1657
  const limit = options.eventLimit;
1567
1658
  const events = [...detail.events].sort(
1568
1659
  (a, b) => Date.parse(a.occurredAt) - Date.parse(b.occurredAt) || Date.parse(a.receivedAt) - Date.parse(b.receivedAt) || a.id.localeCompare(b.id)
@@ -1586,13 +1677,13 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1586
1677
  ),
1587
1678
  ...analyzedFindings
1588
1679
  ];
1589
- const calls = (0, import_telemetry_contracts3.projectWorkflowCalls)(selectedEvents);
1680
+ const calls = (0, import_telemetry_contracts5.projectWorkflowCalls)(selectedEvents);
1590
1681
  let catalog;
1591
1682
  if (target.entity === "runs" && run2) {
1592
1683
  try {
1593
1684
  const workflows2 = await client.get(
1594
1685
  "/v1/studio/workflows",
1595
- import_telemetry_contracts3.StudioWorkflowsResponseSchema,
1686
+ import_telemetry_contracts5.StudioWorkflowsResponseSchema,
1596
1687
  { range: "All time", workflow: run2.workflowId }
1597
1688
  );
1598
1689
  catalog = compareCatalogRuntime(run2, workflows2.workflows);
@@ -1677,7 +1768,7 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1677
1768
  const { connection, client } = await clientFor(options);
1678
1769
  const context = await client.get(
1679
1770
  "/v1/studio/context",
1680
- import_telemetry_contracts3.StudioContextResponseSchema
1771
+ import_telemetry_contracts5.StudioContextResponseSchema
1681
1772
  );
1682
1773
  print(
1683
1774
  {
@@ -1701,7 +1792,7 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1701
1792
  connection: connection.name,
1702
1793
  data: await client.get(
1703
1794
  "/v1/studio/catalogs",
1704
- import_telemetry_contracts3.StudioCatalogsResponseSchema
1795
+ import_telemetry_contracts5.StudioCatalogsResponseSchema
1705
1796
  )
1706
1797
  },
1707
1798
  options
@@ -1716,7 +1807,7 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1716
1807
  "Studio time range: 1h, 24h, 7d, 30d, all (or Studio preset name)."
1717
1808
  ).choices([
1718
1809
  ...Object.keys(rangeAliases),
1719
- ...import_telemetry_contracts3.StudioTimeRangeSchema.options
1810
+ ...import_telemetry_contracts5.StudioTimeRangeSchema.options
1720
1811
  ]).default("24h")
1721
1812
  ).option(
1722
1813
  "--started-after <iso>",
@@ -1728,7 +1819,7 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1728
1819
  const timeQuery = (options) => {
1729
1820
  const custom = Boolean(options.startedAfter || options.startedBefore);
1730
1821
  const range = custom ? "Custom range" : rangeAliases[options.range] ?? options.range;
1731
- const resolution = (0, import_telemetry_contracts3.resolveStudioTimeRange)({
1822
+ const resolution = (0, import_telemetry_contracts5.resolveStudioTimeRange)({
1732
1823
  range,
1733
1824
  startedAfter: options.startedAfter,
1734
1825
  startedBefore: options.startedBefore
@@ -1772,23 +1863,23 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1772
1863
  );
1773
1864
  const context = await client.get(
1774
1865
  "/v1/studio/context",
1775
- import_telemetry_contracts3.StudioContextResponseSchema
1866
+ import_telemetry_contracts5.StudioContextResponseSchema
1776
1867
  );
1777
1868
  const [leftDetail, rightDetail] = await Promise.all([
1778
1869
  client.get(
1779
1870
  `/v1/studio/runs/${encodeURIComponent(leftTarget.id)}`,
1780
- import_telemetry_contracts3.StudioRunDetailResponseSchema
1871
+ import_telemetry_contracts5.StudioRunDetailResponseSchema
1781
1872
  ),
1782
1873
  client.get(
1783
1874
  `/v1/studio/runs/${encodeURIComponent(rightTarget.id)}`,
1784
- import_telemetry_contracts3.StudioRunDetailResponseSchema
1875
+ import_telemetry_contracts5.StudioRunDetailResponseSchema
1785
1876
  )
1786
1877
  ]);
1787
1878
  const catalogFor = async (detail) => {
1788
1879
  try {
1789
1880
  const response = await client.get(
1790
1881
  "/v1/studio/workflows",
1791
- import_telemetry_contracts3.StudioWorkflowsResponseSchema,
1882
+ import_telemetry_contracts5.StudioWorkflowsResponseSchema,
1792
1883
  { range: "All time", workflow: detail.run.workflowId }
1793
1884
  );
1794
1885
  return compareCatalogRuntime(detail.run, response.workflows);
@@ -1894,7 +1985,7 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1894
1985
  query.cursor = String(options.cursor);
1895
1986
  if (options.includeChildren) query.includeChildren = "true";
1896
1987
  const path = `/v1/studio/${entity}`;
1897
- const data = entity === "runs" ? await client.get(path, import_telemetry_contracts3.StudioRunsResponseSchema, query) : entity === "sessions" ? await client.get(path, import_telemetry_contracts3.StudioSessionsResponseSchema, query) : await client.get(path, import_telemetry_contracts3.StudioInterruptsResponseSchema, query);
1988
+ const data = entity === "runs" ? await client.get(path, import_telemetry_contracts5.StudioRunsResponseSchema, query) : entity === "sessions" ? await client.get(path, import_telemetry_contracts5.StudioSessionsResponseSchema, query) : await client.get(path, import_telemetry_contracts5.StudioInterruptsResponseSchema, query);
1898
1989
  const items = "runs" in data ? data.runs : "sessions" in data ? data.sessions : data.interrupts;
1899
1990
  const next = options.cursor + items.length;
1900
1991
  print(
@@ -1931,7 +2022,7 @@ var registerStudioReadCommands = (studio, log, request = fetch) => {
1931
2022
  connection: connection.name,
1932
2023
  data: await client.get(
1933
2024
  "/v1/studio/workflows",
1934
- import_telemetry_contracts3.StudioWorkflowsResponseSchema,
2025
+ import_telemetry_contracts5.StudioWorkflowsResponseSchema,
1935
2026
  query
1936
2027
  ),
1937
2028
  note: "Workflow metrics span environments; connection environment defaults are not applied by this API."
@@ -1982,6 +2073,7 @@ var defaultStudioRuntime = {
1982
2073
  portAvailable,
1983
2074
  now: () => (/* @__PURE__ */ new Date()).toISOString(),
1984
2075
  random: (bytes) => (0, import_node_crypto2.randomBytes)(bytes).toString("base64url"),
2076
+ request: fetch,
1985
2077
  log: (message = "") => console.log(message),
1986
2078
  error: (message = "") => console.error(message)
1987
2079
  };
@@ -2740,7 +2832,7 @@ var pushSnapshot = async (apiUrl, apiKey, snapshot) => {
2740
2832
  }
2741
2833
  );
2742
2834
  }
2743
- return import_telemetry_contracts4.EnsureWorkflowTopologyResponseSchema.parse(await response.json());
2835
+ return import_telemetry_contracts6.EnsureWorkflowTopologyResponseSchema.parse(await response.json());
2744
2836
  };
2745
2837
  var printResults = (results, options) => {
2746
2838
  if (options.json) {
@@ -2820,7 +2912,7 @@ var runTopologyPush = async (rawOptions) => {
2820
2912
  }
2821
2913
  }
2822
2914
  const parsedSnapshots = snapshots.map(
2823
- (snapshot) => import_telemetry_contracts4.EnsureWorkflowTopologyRequestSchema.parse(snapshot)
2915
+ (snapshot) => import_telemetry_contracts6.EnsureWorkflowTopologyRequestSchema.parse(snapshot)
2824
2916
  );
2825
2917
  const results = [];
2826
2918
  for (const snapshot of parsedSnapshots) {