@tryarcanist/cli 0.1.160 → 0.1.162

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 (2) hide show
  1. package/dist/index.js +223 -273
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -138,6 +138,16 @@ async function apiFetchText(config, path, init) {
138
138
  const res = await apiRequest(config, path, init);
139
139
  return res.text();
140
140
  }
141
+ async function resolveBusinessId(config, options) {
142
+ if (options.business && options.business.trim()) return options.business.trim();
143
+ const whoami = await apiFetch(config, "/api/auth/whoami");
144
+ if (whoami.businessId && whoami.businessId.trim()) return whoami.businessId;
145
+ throw new CliError("user", "--business is required when the authenticated token has no business context.");
146
+ }
147
+
148
+ // src/runtime.ts
149
+ import { randomUUID } from "crypto";
150
+ import { createInterface } from "readline/promises";
141
151
 
142
152
  // src/config.ts
143
153
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -289,8 +299,6 @@ function isLoopbackHost(parsed) {
289
299
  }
290
300
 
291
301
  // src/runtime.ts
292
- import { randomUUID } from "crypto";
293
- import { createInterface } from "readline/promises";
294
302
  var ASCII_FIRST_PRINTABLE = 32;
295
303
  var ASCII_DELETE = 127;
296
304
  var ANSI_CONTROL_SEQUENCE = /\u001b\[[0-9:;<=>?]*[ -/]*[@-~]/g;
@@ -306,6 +314,17 @@ function writeJson(value) {
306
314
  process.stdout.write(`${JSON.stringify(value)}
307
315
  `);
308
316
  }
317
+ function emit(command, options, payload, printer) {
318
+ if (isJson(command, options)) {
319
+ writeJson(payload);
320
+ return;
321
+ }
322
+ printer(payload);
323
+ }
324
+ function resolveBusinessContext(command, options = {}) {
325
+ const runtime = getRuntimeOptions(command, options);
326
+ return { runtime, config: requireConfig(runtime) };
327
+ }
309
328
  async function readStdin() {
310
329
  const chunks = [];
311
330
  for await (const chunk of process.stdin) {
@@ -468,15 +487,12 @@ function isControlCharacter(char) {
468
487
 
469
488
  // src/commands/auth.ts
470
489
  async function whoamiCommand(options, command) {
471
- const runtime = getRuntimeOptions(command, options);
472
- const config = requireConfig(runtime);
490
+ const { config } = resolveBusinessContext(command, options);
473
491
  const payload = await apiFetch(config, "/api/auth/whoami");
474
- if (isJson(command, options)) {
475
- writeJson(payload);
476
- return;
477
- }
478
- console.log(`User: ${String(payload.email ?? payload.userId ?? "unknown")}`);
479
- if (payload.tokenScope) console.log(`Token scope: ${String(payload.tokenScope)}`);
492
+ emit(command, options, payload, (whoami) => {
493
+ console.log(`User: ${String(whoami.email ?? whoami.userId ?? "unknown")}`);
494
+ if (whoami.tokenScope) console.log(`Token scope: ${String(whoami.tokenScope)}`);
495
+ });
480
496
  }
481
497
 
482
498
  // ../../shared/github/repo-url.ts
@@ -991,9 +1007,6 @@ function buildModelProviderGroups(models) {
991
1007
  var SESSION_START_MODEL_ID_SET_ANY_BACKEND = new Set(
992
1008
  AGENT_RUNTIME_BACKENDS.flatMap((backend) => [...SESSION_START_MODEL_IDS_BY_BACKEND[backend]])
993
1009
  );
994
- var SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
995
- MODEL_REGISTRY.filter((model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id))
996
- );
997
1010
  var PUBLIC_SESSION_START_MODEL_PROVIDER_GROUPS = buildModelProviderGroups(
998
1011
  MODEL_REGISTRY.filter(
999
1012
  (model) => SESSION_START_MODEL_ID_SET_ANY_BACKEND.has(model.id) && model.visibility !== "internal_probe"
@@ -1444,7 +1457,8 @@ function createStreamCoalescerState() {
1444
1457
  return {
1445
1458
  streamableIndexById: /* @__PURE__ */ new Map(),
1446
1459
  segmentOrdinalByStreamId: /* @__PURE__ */ new Map(),
1447
- concatByStreamId: /* @__PURE__ */ new Map()
1460
+ concatByStreamId: /* @__PURE__ */ new Map(),
1461
+ interstitialScanEndByStreamId: /* @__PURE__ */ new Map()
1448
1462
  };
1449
1463
  }
1450
1464
  function streamableKey(type, streamId) {
@@ -1462,6 +1476,27 @@ function resolveTextValue(data) {
1462
1476
  const content = data?.content;
1463
1477
  return typeof content === "string" ? content : "";
1464
1478
  }
1479
+ function isNonContentInterstitial(event) {
1480
+ switch (event.type) {
1481
+ case "agent_progress":
1482
+ case "retry_status":
1483
+ case "memory_usage":
1484
+ case "memory_recall_usage":
1485
+ case "agent_timeline":
1486
+ return true;
1487
+ default:
1488
+ return false;
1489
+ }
1490
+ }
1491
+ function canAppendAcrossNonContentInterstitials(events, state, key, existingIdx) {
1492
+ if (existingIdx === events.length - 1) return true;
1493
+ const scanStart = Math.max(existingIdx + 1, state.interstitialScanEndByStreamId.get(key) ?? existingIdx + 1);
1494
+ for (let i = scanStart; i < events.length; i++) {
1495
+ if (!isNonContentInterstitial(events[i])) return false;
1496
+ }
1497
+ state.interstitialScanEndByStreamId.set(key, events.length);
1498
+ return true;
1499
+ }
1465
1500
  function resolvePromptId(data) {
1466
1501
  return typeof data?.promptId === "string" ? data.promptId : void 0;
1467
1502
  }
@@ -1469,22 +1504,24 @@ function coalesceStreamDelta(events, state, type, streamId, text, promptId) {
1469
1504
  const key = streamableKey(type, streamId);
1470
1505
  const existingText = state.concatByStreamId.get(key) ?? "";
1471
1506
  const existingIdx = state.streamableIndexById.get(key);
1472
- if (existingIdx !== void 0 && existingIdx === events.length - 1) {
1507
+ const appendableText = shouldAppendTextDelta(existingText, text);
1508
+ if (existingIdx !== void 0) {
1473
1509
  const existing = events[existingIdx];
1474
- if (existing.type === type && shouldAppendTextDelta(existingText, text)) {
1510
+ if (existing.type === type && appendableText && canAppendAcrossNonContentInterstitials(events, state, key, existingIdx)) {
1475
1511
  existing.text += text;
1476
1512
  state.concatByStreamId.set(key, existingText + text);
1477
1513
  if (!existing.promptId && promptId) existing.promptId = promptId;
1478
1514
  return true;
1479
1515
  }
1480
- return false;
1516
+ if (existingIdx === events.length - 1 || !appendableText) return false;
1481
1517
  }
1482
- if (existingText && !shouldAppendTextDelta(existingText, text)) return false;
1518
+ if (existingText && !appendableText) return false;
1483
1519
  const previousOrdinal = state.segmentOrdinalByStreamId.get(key);
1484
1520
  const segmentOrdinal = previousOrdinal === void 0 ? 0 : previousOrdinal + 1;
1485
1521
  state.segmentOrdinalByStreamId.set(key, segmentOrdinal);
1486
1522
  state.streamableIndexById.set(key, events.length);
1487
1523
  state.concatByStreamId.set(key, existingText + text);
1524
+ state.interstitialScanEndByStreamId.set(key, events.length + 1);
1488
1525
  if (type === "text") {
1489
1526
  events.push({
1490
1527
  type: "text",
@@ -1517,6 +1554,7 @@ function mergeToolCall(previous, incoming) {
1517
1554
  ...incoming.inputEstimatedTokens === void 0 && previous.inputEstimatedTokens !== void 0 ? { inputEstimatedTokens: previous.inputEstimatedTokens } : {},
1518
1555
  ...incoming.outputEstimatedTokens === void 0 && previous.outputEstimatedTokens !== void 0 ? { outputEstimatedTokens: previous.outputEstimatedTokens } : {},
1519
1556
  ...incoming.outputChars === void 0 && previous.outputChars !== void 0 ? { outputChars: previous.outputChars } : {},
1557
+ ...incoming.failure === void 0 && previous.failure !== void 0 ? { failure: previous.failure } : {},
1520
1558
  ...incoming.duplicateCount === void 0 && previous.duplicateCount !== void 0 ? { duplicateCount: previous.duplicateCount } : {}
1521
1559
  };
1522
1560
  }
@@ -1530,6 +1568,7 @@ function applyToolCallUpdate(previous, data) {
1530
1568
  ...toolStatus ? { toolStatus } : {},
1531
1569
  ...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
1532
1570
  ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
1571
+ ...isRecord(data?.failure) ? { failure: data.failure } : {},
1533
1572
  ...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {}
1534
1573
  };
1535
1574
  }
@@ -1800,7 +1839,8 @@ function projectToolCall(data, state) {
1800
1839
  ...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {},
1801
1840
  ...typeof data?.inputEstimatedTokens === "number" ? { inputEstimatedTokens: data.inputEstimatedTokens } : {},
1802
1841
  ...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
1803
- ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {}
1842
+ ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
1843
+ ...isRecord(data?.failure) ? { failure: data.failure } : {}
1804
1844
  };
1805
1845
  state.merged[existingIdx] = mergeToolCall(previous, nextEntry);
1806
1846
  }
@@ -1817,7 +1857,8 @@ function projectToolCall(data, state) {
1817
1857
  ...typeof data?.truncated === "boolean" ? { truncated: data.truncated } : {},
1818
1858
  ...typeof data?.inputEstimatedTokens === "number" ? { inputEstimatedTokens: data.inputEstimatedTokens } : {},
1819
1859
  ...typeof data?.outputEstimatedTokens === "number" ? { outputEstimatedTokens: data.outputEstimatedTokens } : {},
1820
- ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {}
1860
+ ...typeof data?.outputChars === "number" ? { outputChars: data.outputChars } : {},
1861
+ ...isRecord(data?.failure) ? { failure: data.failure } : {}
1821
1862
  };
1822
1863
  state.toolCallIndexById.set(id, state.merged.length);
1823
1864
  state.merged.push(nextEntry);
@@ -2972,105 +3013,88 @@ function parseRepoArg(value) {
2972
3013
  if (!owner || !repo || extra) throw new CliError("user", "repo must be owner/name or a GitHub URL");
2973
3014
  return { owner, repo };
2974
3015
  }
2975
- function encodePathSegment(value) {
2976
- return encodeURIComponent(value);
2977
- }
2978
- async function resolveBusinessId(config, options) {
2979
- if (options.business && options.business.trim()) return options.business.trim();
2980
- const whoami = await apiFetch(config, "/api/auth/whoami");
2981
- if (whoami.businessId && whoami.businessId.trim()) return whoami.businessId;
2982
- throw new CliError("user", "--business is required when the authenticated token has no business context.");
2983
- }
2984
3016
  async function egressSourceSetCommand(sourceRepoArg, options = {}, command) {
2985
- const runtime = getRuntimeOptions(command, options);
2986
- const config = requireConfig(runtime);
3017
+ const { config } = resolveBusinessContext(command, options);
2987
3018
  const businessId = await resolveBusinessId(config, options);
2988
3019
  const source = parseRepoArg(sourceRepoArg);
2989
3020
  const payload = await apiFetch(
2990
3021
  config,
2991
- `/api/businesses/${encodePathSegment(businessId)}/egress-allowlist/source`,
3022
+ `/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/source`,
2992
3023
  {
2993
3024
  method: "PUT",
2994
3025
  body: JSON.stringify({ sourceRepoOwner: source.owner, sourceRepoName: source.repo })
2995
3026
  }
2996
3027
  );
2997
- if (isJson(command, options)) {
2998
- writeJson(payload);
2999
- return;
3000
- }
3001
- console.log(`Set egress allowlist source to ${source.owner}/${source.repo}:${EGRESS_ALLOWLIST_SOURCE_PATH}.`);
3028
+ emit(
3029
+ command,
3030
+ options,
3031
+ payload,
3032
+ () => console.log(`Set egress allowlist source to ${source.owner}/${source.repo}:${EGRESS_ALLOWLIST_SOURCE_PATH}.`)
3033
+ );
3002
3034
  }
3003
3035
  async function egressSourceGetCommand(options = {}, command) {
3004
- const runtime = getRuntimeOptions(command, options);
3005
- const config = requireConfig(runtime);
3036
+ const { config } = resolveBusinessContext(command, options);
3006
3037
  const businessId = await resolveBusinessId(config, options);
3007
3038
  const payload = await apiFetch(
3008
3039
  config,
3009
- `/api/businesses/${encodePathSegment(businessId)}/egress-allowlist/source`
3040
+ `/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/source`
3010
3041
  );
3011
- if (isJson(command, options)) {
3012
- writeJson(payload);
3013
- return;
3014
- }
3015
- if (!payload.source) {
3016
- console.log("No egress allowlist source configured.");
3017
- return;
3018
- }
3019
- console.log(
3020
- `${payload.source.sourceRepoOwner}/${payload.source.sourceRepoName}:${payload.path} (${payload.defaultBranch ?? "default branch"})`
3021
- );
3022
- console.log(`${payload.domains.length} ${payload.domains.length === 1 ? "domain" : "domains"}.`);
3042
+ emit(command, options, payload, (sourcePayload) => {
3043
+ if (!sourcePayload.source) {
3044
+ console.log("No egress allowlist source configured.");
3045
+ return;
3046
+ }
3047
+ console.log(
3048
+ `${sourcePayload.source.sourceRepoOwner}/${sourcePayload.source.sourceRepoName}:${sourcePayload.path} (${sourcePayload.defaultBranch ?? "default branch"})`
3049
+ );
3050
+ console.log(`${sourcePayload.domains.length} ${sourcePayload.domains.length === 1 ? "domain" : "domains"}.`);
3051
+ });
3023
3052
  }
3024
3053
  async function egressAddCommand(domains, options = {}, command) {
3025
- const runtime = getRuntimeOptions(command, options);
3026
- const config = requireConfig(runtime);
3054
+ const { config } = resolveBusinessContext(command, options);
3027
3055
  const businessId = await resolveBusinessId(config, options);
3028
3056
  const normalized = normalizeEgressDomains(domains, "domains");
3029
3057
  const payload = await apiFetch(
3030
3058
  config,
3031
- `/api/businesses/${encodePathSegment(businessId)}/egress-allowlist/pull-request`,
3059
+ `/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/pull-request`,
3032
3060
  {
3033
3061
  method: "POST",
3034
3062
  body: JSON.stringify({ domains: normalized, ...options.reason ? { reason: options.reason } : {} })
3035
3063
  }
3036
3064
  );
3037
- if (isJson(command, options)) {
3038
- writeJson(payload);
3039
- return;
3040
- }
3041
- if (payload.status === "unchanged") {
3042
- console.log("All requested domains are already present in the source file.");
3043
- return;
3044
- }
3045
- console.log(`Egress allowlist PR ${payload.status}: ${payload.prUrl}`);
3046
- console.log(`Added: ${payload.addedDomains.join(", ")}`);
3065
+ emit(command, options, payload, (addPayload) => {
3066
+ if (addPayload.status === "unchanged") {
3067
+ console.log("All requested domains are already present in the source file.");
3068
+ return;
3069
+ }
3070
+ console.log(`Egress allowlist PR ${addPayload.status}: ${addPayload.prUrl}`);
3071
+ console.log(`Added: ${addPayload.addedDomains.join(", ")}`);
3072
+ });
3047
3073
  }
3048
3074
  async function egressSyncCommand(options = {}, command) {
3049
- const runtime = getRuntimeOptions(command, options);
3050
- const config = requireConfig(runtime);
3075
+ const { config } = resolveBusinessContext(command, options);
3051
3076
  const businessId = await resolveBusinessId(config, options);
3052
3077
  const payload = await apiFetch(
3053
3078
  config,
3054
- `/api/businesses/${encodePathSegment(businessId)}/egress-allowlist/sync`,
3079
+ `/api/businesses/${encodeURIComponent(businessId)}/egress-allowlist/sync`,
3055
3080
  { method: "POST" }
3056
3081
  );
3057
- if (isJson(command, options)) {
3058
- writeJson(payload);
3059
- return;
3060
- }
3061
- console.log(
3062
- `Applied ${payload.egressAllowlist.length} egress allowlist domains from ${payload.source.sourceRepoOwner}/${payload.source.sourceRepoName}:${payload.path}.`
3082
+ emit(
3083
+ command,
3084
+ options,
3085
+ payload,
3086
+ (syncPayload) => console.log(
3087
+ `Applied ${syncPayload.egressAllowlist.length} egress allowlist domains from ${syncPayload.source.sourceRepoOwner}/${syncPayload.source.sourceRepoName}:${syncPayload.path}.`
3088
+ )
3063
3089
  );
3064
3090
  }
3065
3091
  async function egressValidateCommand(path = EGRESS_ALLOWLIST_SOURCE_PATH, options = {}, command) {
3066
3092
  const content = readFileSync2(path, "utf8");
3067
3093
  const domains = parseEgressAllowlistSourceFile(content, path);
3068
- if (isJson(command, options)) {
3069
- writeJson({ ok: true, path, domains });
3070
- return;
3071
- }
3072
- console.log(`Valid egress allowlist: ${path}`);
3073
- console.log(`${domains.length} ${domains.length === 1 ? "domain" : "domains"}.`);
3094
+ emit(command, options, { ok: true, path, domains }, (payload) => {
3095
+ console.log(`Valid egress allowlist: ${payload.path}`);
3096
+ console.log(`${payload.domains.length} ${payload.domains.length === 1 ? "domain" : "domains"}.`);
3097
+ });
3074
3098
  }
3075
3099
 
3076
3100
  // src/commands/login.ts
@@ -3714,15 +3738,6 @@ function parseRepoArg2(value, fallback) {
3714
3738
  function repoPath(repo) {
3715
3739
  return `${repo.owner}/${repo.repo}`;
3716
3740
  }
3717
- function encodePathSegment2(value) {
3718
- return encodeURIComponent(value);
3719
- }
3720
- async function resolveBusinessId2(config, options) {
3721
- if (options.business && options.business.trim()) return options.business.trim();
3722
- const whoami = await apiFetch(config, "/api/auth/whoami");
3723
- if (whoami.businessId && whoami.businessId.trim()) return whoami.businessId;
3724
- throw new CliError("user", "--business is required when the authenticated token has no business context.");
3725
- }
3726
3741
  function parsePollInterval2(value) {
3727
3742
  if (!value) return DEFAULT_POLL_INTERVAL_MS;
3728
3743
  const parsed = Number(value);
@@ -3731,9 +3746,6 @@ function parsePollInterval2(value) {
3731
3746
  }
3732
3747
  return parsed;
3733
3748
  }
3734
- function sleep2(ms) {
3735
- return new Promise((resolve) => setTimeout(resolve, ms));
3736
- }
3737
3749
  function printBuildRequest(buildRequest) {
3738
3750
  console.log(
3739
3751
  [
@@ -3856,7 +3868,7 @@ function buildLogsPath(businessId, buildId, options = {}) {
3856
3868
  if (options.afterSequence != null) params.set("afterSequence", String(options.afterSequence));
3857
3869
  if (options.limit != null) params.set("limit", String(options.limit));
3858
3870
  const suffix = params.toString() ? `?${params.toString()}` : "";
3859
- return `/api/businesses/${encodePathSegment2(businessId)}/sandbox-layer/build-requests/${encodePathSegment2(
3871
+ return `/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/build-requests/${encodeURIComponent(
3860
3872
  buildId
3861
3873
  )}/logs${suffix}`;
3862
3874
  }
@@ -3873,12 +3885,12 @@ async function waitForBuild(config, businessId, buildId, options) {
3873
3885
  }
3874
3886
  const status = await apiFetch(
3875
3887
  config,
3876
- `/api/businesses/${encodePathSegment2(businessId)}/sandbox-layer/build-requests/${encodePathSegment2(buildId)}`
3888
+ `/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/build-requests/${encodeURIComponent(buildId)}`
3877
3889
  );
3878
3890
  if (TERMINAL_BUILD_STATUSES.has(status.buildRequest.status)) {
3879
3891
  return status.buildRequest;
3880
3892
  }
3881
- await sleep2(options.pollIntervalMs);
3893
+ await sleep(options.pollIntervalMs);
3882
3894
  }
3883
3895
  }
3884
3896
  function formatValidationPayload(input) {
@@ -3953,11 +3965,7 @@ async function sandboxInitCommand(options = {}, command) {
3953
3965
  "utf8"
3954
3966
  );
3955
3967
  const payload = { ok: true, manifestPath: DEFAULT_MANIFEST_PATH, layerPath };
3956
- if (isJson(command, runtime)) {
3957
- writeJson(payload);
3958
- return;
3959
- }
3960
- console.log(`Created ${DEFAULT_MANIFEST_PATH} and ${layerPath}`);
3968
+ emit(command, runtime, payload, () => console.log(`Created ${DEFAULT_MANIFEST_PATH} and ${layerPath}`));
3961
3969
  }
3962
3970
  async function sandboxValidateCommand(options = {}, command) {
3963
3971
  const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
@@ -3970,34 +3978,31 @@ async function sandboxValidateCommand(options = {}, command) {
3970
3978
  const layerText = readFileSync3(layerPath, "utf8");
3971
3979
  const parsed = await parseSandboxLayerSource({ manifestPath, manifestText, layerPath, layerText });
3972
3980
  const payload = formatValidationPayload({ manifestPath, layerPath, parsed });
3973
- if (isJson(command, options)) {
3974
- writeJson(payload);
3975
- return;
3976
- }
3977
- console.log("Valid sandbox template.");
3978
- console.log(`Manifest: ${payload.manifestPath}`);
3979
- console.log(`Layer: ${payload.layerPath}`);
3980
- console.log(`Normalized source hash: ${payload.normalizedSourceHash}`);
3981
- console.log(`Instructions: ${payload.instructionCount}`);
3982
- console.log(`Smoke commands: ${payload.smokeCommandCount}`);
3983
- console.log(`Next: ${nextBuildCommand(manifestPath)}`);
3981
+ emit(command, options, payload, (validationPayload) => {
3982
+ console.log("Valid sandbox template.");
3983
+ console.log(`Manifest: ${validationPayload.manifestPath}`);
3984
+ console.log(`Layer: ${validationPayload.layerPath}`);
3985
+ console.log(`Normalized source hash: ${validationPayload.normalizedSourceHash}`);
3986
+ console.log(`Instructions: ${validationPayload.instructionCount}`);
3987
+ console.log(`Smoke commands: ${validationPayload.smokeCommandCount}`);
3988
+ console.log(`Next: ${nextBuildCommand(manifestPath)}`);
3989
+ });
3984
3990
  } catch (err) {
3985
3991
  handleValidationError(err, options, command);
3986
3992
  }
3987
3993
  }
3988
3994
  async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
3989
- const runtime = getRuntimeOptions(command, options);
3990
- const config = requireConfig(runtime);
3995
+ const { config } = resolveBusinessContext(command, options);
3991
3996
  const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
3992
3997
  assertManifestExists(manifestPath);
3993
3998
  if (!options.ref) assertCleanAndPushed();
3994
3999
  const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
3995
4000
  const targetRepo = options.targetRepo ? parseRepoArg2(options.targetRepo, currentRepo) : null;
3996
- const businessId = await resolveBusinessId2(config, options);
4001
+ const businessId = await resolveBusinessId(config, options);
3997
4002
  const ref = options.ref ?? currentRef();
3998
4003
  const payload = await apiFetch(
3999
4004
  config,
4000
- `/api/businesses/${encodePathSegment2(businessId)}/repos/${encodePathSegment2(sourceRepo.owner)}/${encodePathSegment2(
4005
+ `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(sourceRepo.owner)}/${encodeURIComponent(
4001
4006
  sourceRepo.repo
4002
4007
  )}/sandbox-layer/build-requests`,
4003
4008
  {
@@ -4009,8 +4014,7 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
4009
4014
  })
4010
4015
  }
4011
4016
  );
4012
- if (isJson(command, options)) writeJson(payload);
4013
- else printBuildRequest(payload.buildRequest);
4017
+ emit(command, options, payload, (buildPayload) => printBuildRequest(buildPayload.buildRequest));
4014
4018
  if (!options.wait && !options.follow) return;
4015
4019
  const json = isJson(command, options);
4016
4020
  const finalBuild = await waitForBuild(config, businessId, payload.buildRequest.id, {
@@ -4025,26 +4029,20 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
4025
4029
  }
4026
4030
  }
4027
4031
  async function sandboxStatusCommand(repoArg, options = {}, command) {
4028
- const runtime = getRuntimeOptions(command, options);
4029
- const config = requireConfig(runtime);
4030
- const businessId = await resolveBusinessId2(config, options);
4032
+ const { config } = resolveBusinessContext(command, options);
4033
+ const businessId = await resolveBusinessId(config, options);
4031
4034
  const repo = parseRepoArg2(repoArg, currentRepo);
4032
4035
  const payload = await apiFetch(
4033
4036
  config,
4034
- `/api/businesses/${encodePathSegment2(businessId)}/repos/${encodePathSegment2(repo.owner)}/${encodePathSegment2(
4037
+ `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(
4035
4038
  repo.repo
4036
4039
  )}/sandbox-layer/resolution`
4037
4040
  );
4038
- if (isJson(command, options)) {
4039
- writeJson(payload);
4040
- return;
4041
- }
4042
- printSandboxStatus(payload);
4041
+ emit(command, options, payload, printSandboxStatus);
4043
4042
  }
4044
4043
  async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
4045
- const runtime = getRuntimeOptions(command, options);
4046
- const config = requireConfig(runtime);
4047
- const businessId = await resolveBusinessId2(config, options);
4044
+ const { config } = resolveBusinessContext(command, options);
4045
+ const businessId = await resolveBusinessId(config, options);
4048
4046
  const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
4049
4047
  const params = new URLSearchParams({ sourceRepo: repoPath(sourceRepo) });
4050
4048
  if (options.targetRepo) params.set("targetRepo", repoPath(parseRepoArg2(options.targetRepo, currentRepo)));
@@ -4058,24 +4056,19 @@ async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
4058
4056
  }
4059
4057
  const payload = await apiFetch(
4060
4058
  config,
4061
- `/api/businesses/${encodePathSegment2(businessId)}/sandbox-layer/build-requests?${params.toString()}`
4059
+ `/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/build-requests?${params.toString()}`
4062
4060
  );
4063
- if (isJson(command, options)) {
4064
- writeJson(payload);
4065
- return;
4066
- }
4067
- printBuildHistory(payload.builds);
4061
+ emit(command, options, payload, (historyPayload) => printBuildHistory(historyPayload.builds));
4068
4062
  }
4069
4063
  async function sandboxRebuildStaleCommand(options = {}, command) {
4070
- const runtime = getRuntimeOptions(command, options);
4071
- const config = requireConfig(runtime);
4064
+ const { config } = resolveBusinessContext(command, options);
4072
4065
  if (!options.dryRun && !options.yes) {
4073
4066
  throw new CliError("user", "Use --dry-run to preview or --yes to start a rebuild campaign.");
4074
4067
  }
4075
4068
  if (options.all && options.business) {
4076
4069
  throw new CliError("user", "Use either --all or --business, not both.");
4077
4070
  }
4078
- const businessId = options.all ? null : await resolveBusinessId2(config, options);
4071
+ const businessId = options.all ? null : await resolveBusinessId(config, options);
4079
4072
  let payload = await apiFetch(config, "/api/admin/sandbox-layer/rebuild-campaigns", {
4080
4073
  method: "POST",
4081
4074
  body: JSON.stringify({
@@ -4093,20 +4086,16 @@ async function sandboxRebuildStaleCommand(options = {}, command) {
4093
4086
  if (options.follow && !options.dryRun) {
4094
4087
  payload = await followRebuildCampaign(config, payload.campaign.id);
4095
4088
  }
4096
- if (isJson(command, options)) {
4097
- writeJson(payload);
4098
- return;
4099
- }
4100
- printRebuildCampaign(payload);
4089
+ emit(command, options, payload, printRebuildCampaign);
4101
4090
  }
4102
4091
  async function followRebuildCampaign(config, campaignId) {
4103
4092
  for (; ; ) {
4104
4093
  const payload = await apiFetch(
4105
4094
  config,
4106
- `/api/admin/sandbox-layer/rebuild-campaigns/${encodePathSegment2(campaignId)}`
4095
+ `/api/admin/sandbox-layer/rebuild-campaigns/${encodeURIComponent(campaignId)}`
4107
4096
  );
4108
4097
  if (isCampaignTerminal(payload.campaign.status)) return payload;
4109
- await sleep2(DEFAULT_POLL_INTERVAL_MS);
4098
+ await sleep(DEFAULT_POLL_INTERVAL_MS);
4110
4099
  }
4111
4100
  }
4112
4101
  function isCampaignTerminal(status) {
@@ -4130,9 +4119,8 @@ function printRebuildCampaign(payload) {
4130
4119
  }
4131
4120
  }
4132
4121
  async function sandboxLogsCommand(buildId, options = {}, command) {
4133
- const runtime = getRuntimeOptions(command, options);
4134
- const config = requireConfig(runtime);
4135
- const businessId = await resolveBusinessId2(config, options);
4122
+ const { config } = resolveBusinessContext(command, options);
4123
+ const businessId = await resolveBusinessId(config, options);
4136
4124
  let afterSequence = options.afterSequence == null ? void 0 : Number(options.afterSequence);
4137
4125
  if (afterSequence != null && (!Number.isInteger(afterSequence) || afterSequence < 0)) {
4138
4126
  throw new CliError("user", "--after-sequence must be a non-negative integer.");
@@ -4150,88 +4138,72 @@ async function sandboxLogsCommand(buildId, options = {}, command) {
4150
4138
  else printLogs(payload.logs);
4151
4139
  if (!options.follow) return;
4152
4140
  if (payload.logs.length > 0) afterSequence = payload.logs[payload.logs.length - 1].sequence;
4153
- await sleep2(parsePollInterval2(options.pollInterval));
4141
+ await sleep(parsePollInterval2(options.pollInterval));
4154
4142
  }
4155
4143
  }
4156
4144
  async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command) {
4157
- const runtime = getRuntimeOptions(command, options);
4158
- const config = requireConfig(runtime);
4159
- const businessId = await resolveBusinessId2(config, options);
4145
+ const { config } = resolveBusinessContext(command, options);
4146
+ const businessId = await resolveBusinessId(config, options);
4160
4147
  const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
4161
4148
  const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
4162
4149
  const payload = await apiFetch(
4163
4150
  config,
4164
- `/api/businesses/${encodePathSegment2(businessId)}/sandbox-layer/default-source`,
4151
+ `/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/default-source`,
4165
4152
  { method: "PUT", body: JSON.stringify(sourceBody(sourceRepo, manifestPath)) }
4166
4153
  );
4167
- if (isJson(command, options)) {
4168
- writeJson(payload);
4169
- return;
4170
- }
4171
- console.log(`Set default sandbox layer source to ${repoPath(sourceRepo)}.`);
4172
- console.log(
4173
- `Build missing target coverage with: arcanist sandbox build ${repoPath(sourceRepo)} --business ${businessId}`
4174
- );
4154
+ emit(command, options, payload, () => {
4155
+ console.log(`Set default sandbox layer source to ${repoPath(sourceRepo)}.`);
4156
+ console.log(
4157
+ `Build missing target coverage with: arcanist sandbox build ${repoPath(sourceRepo)} --business ${businessId}`
4158
+ );
4159
+ });
4175
4160
  }
4176
4161
  async function sandboxAssignRepoCommand(targetRepoArg, sourceRepoArg, options = {}, command) {
4177
- const runtime = getRuntimeOptions(command, options);
4178
- const config = requireConfig(runtime);
4179
- const businessId = await resolveBusinessId2(config, options);
4162
+ const { config } = resolveBusinessContext(command, options);
4163
+ const businessId = await resolveBusinessId(config, options);
4180
4164
  const targetRepo = parseRepoArg2(targetRepoArg, currentRepo);
4181
4165
  const sourceRepo = parseRepoArg2(sourceRepoArg, currentRepo);
4182
4166
  const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
4183
4167
  const payload = await apiFetch(
4184
4168
  config,
4185
- `/api/businesses/${encodePathSegment2(businessId)}/repos/${encodePathSegment2(targetRepo.owner)}/${encodePathSegment2(
4169
+ `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(targetRepo.owner)}/${encodeURIComponent(
4186
4170
  targetRepo.repo
4187
4171
  )}/sandbox-layer/assignment`,
4188
4172
  { method: "PUT", body: JSON.stringify(sourceBody(sourceRepo, manifestPath)) }
4189
4173
  );
4190
- if (isJson(command, options)) {
4191
- writeJson(payload);
4192
- return;
4193
- }
4194
- console.log(`Assigned ${repoPath(sourceRepo)} as sandbox layer source for ${repoPath(targetRepo)}.`);
4195
- console.log(
4196
- `Build target coverage with: arcanist sandbox build ${repoPath(sourceRepo)} --target-repo ${repoPath(
4197
- targetRepo
4198
- )} --business ${businessId}`
4199
- );
4174
+ emit(command, options, payload, () => {
4175
+ console.log(`Assigned ${repoPath(sourceRepo)} as sandbox layer source for ${repoPath(targetRepo)}.`);
4176
+ console.log(
4177
+ `Build target coverage with: arcanist sandbox build ${repoPath(sourceRepo)} --target-repo ${repoPath(
4178
+ targetRepo
4179
+ )} --business ${businessId}`
4180
+ );
4181
+ });
4200
4182
  }
4201
4183
  async function sandboxUnassignDefaultCommand(options = {}, command) {
4202
- const runtime = getRuntimeOptions(command, options);
4203
- const config = requireConfig(runtime);
4204
- const businessId = await resolveBusinessId2(config, options);
4184
+ const { config } = resolveBusinessContext(command, options);
4185
+ const businessId = await resolveBusinessId(config, options);
4205
4186
  if (!options.yes) await confirmOrThrow("Clear the business default sandbox layer source?");
4206
4187
  const payload = await apiFetch(
4207
4188
  config,
4208
- `/api/businesses/${encodePathSegment2(businessId)}/sandbox-layer/default-source`,
4189
+ `/api/businesses/${encodeURIComponent(businessId)}/sandbox-layer/default-source`,
4209
4190
  { method: "DELETE" }
4210
4191
  );
4211
- if (isJson(command, options)) {
4212
- writeJson(payload);
4213
- return;
4214
- }
4215
- console.log("Cleared default sandbox layer source.");
4192
+ emit(command, options, payload, () => console.log("Cleared default sandbox layer source."));
4216
4193
  }
4217
4194
  async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command) {
4218
- const runtime = getRuntimeOptions(command, options);
4219
- const config = requireConfig(runtime);
4220
- const businessId = await resolveBusinessId2(config, options);
4195
+ const { config } = resolveBusinessContext(command, options);
4196
+ const businessId = await resolveBusinessId(config, options);
4221
4197
  const targetRepo = parseRepoArg2(targetRepoArg, currentRepo);
4222
4198
  if (!options.yes) await confirmOrThrow(`Clear sandbox layer assignment for ${repoPath(targetRepo)}?`);
4223
4199
  const payload = await apiFetch(
4224
4200
  config,
4225
- `/api/businesses/${encodePathSegment2(businessId)}/repos/${encodePathSegment2(targetRepo.owner)}/${encodePathSegment2(
4201
+ `/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(targetRepo.owner)}/${encodeURIComponent(
4226
4202
  targetRepo.repo
4227
4203
  )}/sandbox-layer/assignment`,
4228
4204
  { method: "DELETE" }
4229
4205
  );
4230
- if (isJson(command, options)) {
4231
- writeJson(payload);
4232
- return;
4233
- }
4234
- console.log(`Cleared sandbox layer assignment for ${repoPath(targetRepo)}.`);
4206
+ emit(command, options, payload, () => console.log(`Cleared sandbox layer assignment for ${repoPath(targetRepo)}.`));
4235
4207
  }
4236
4208
 
4237
4209
  // src/commands/sessions.ts
@@ -4369,8 +4341,7 @@ async function usageCommand(sessionId, options, command) {
4369
4341
 
4370
4342
  // src/commands/stop.ts
4371
4343
  async function stopCommand(sessionId, options = {}, command) {
4372
- const runtime = getRuntimeOptions(command, options);
4373
- const config = requireConfig(runtime);
4344
+ const { config } = resolveBusinessContext(command, options);
4374
4345
  let status;
4375
4346
  try {
4376
4347
  const response = await apiFetch(config, `/api/sessions/${sessionId}/stop`, {
@@ -4382,17 +4353,15 @@ async function stopCommand(sessionId, options = {}, command) {
4382
4353
  if (!parsed) throw err;
4383
4354
  status = parsed.reason;
4384
4355
  }
4385
- if (isJson(command, options)) {
4386
- writeJson({ sessionId, status });
4387
- return;
4388
- }
4389
- if (status === "already_stopped") {
4390
- console.log(`Session ${sessionId} is already stopped.`);
4391
- } else if (status === "stopping" || status === "stopped") {
4392
- console.log(`Stop requested for session ${sessionId}.`);
4393
- } else {
4394
- console.log(`Session ${sessionId} cannot be stopped from phase=${status}.`);
4395
- }
4356
+ emit(command, options, { sessionId, status }, (payload) => {
4357
+ if (payload.status === "already_stopped") {
4358
+ console.log(`Session ${payload.sessionId} is already stopped.`);
4359
+ } else if (payload.status === "stopping" || payload.status === "stopped") {
4360
+ console.log(`Stop requested for session ${payload.sessionId}.`);
4361
+ } else {
4362
+ console.log(`Session ${payload.sessionId} cannot be stopped from phase=${payload.status}.`);
4363
+ }
4364
+ });
4396
4365
  }
4397
4366
  function parseStopBlocked(err) {
4398
4367
  if (!(err instanceof ApiError) || err.status !== 409) return null;
@@ -4428,26 +4397,22 @@ function readValue(options) {
4428
4397
  return raw.replace(/\r?\n$/, "");
4429
4398
  }
4430
4399
  async function listTestCredentialsCommand(repo, options, command) {
4431
- const runtime = getRuntimeOptions(command, options);
4432
- const config = requireConfig(runtime);
4400
+ const { config } = resolveBusinessContext(command, options);
4433
4401
  const repoArg = parseRepoArg3(repo);
4434
4402
  const payload = await apiFetch(config, basePath(options.business, repoArg));
4435
- if (isJson(command, options)) {
4436
- writeJson(payload);
4437
- return;
4438
- }
4439
- if (payload.credentials.length === 0) {
4440
- console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.name}.`);
4441
- return;
4442
- }
4443
- for (const cred of payload.credentials) {
4444
- const ts = new Date(cred.updatedAt).toISOString();
4445
- console.log(`${cred.name} ${ts} ${cred.rotatedByUserId ?? "-"}`);
4446
- }
4403
+ emit(command, options, payload, (listPayload) => {
4404
+ if (listPayload.credentials.length === 0) {
4405
+ console.log(`No test credentials configured for ${repoArg.owner}/${repoArg.name}.`);
4406
+ return;
4407
+ }
4408
+ for (const cred of listPayload.credentials) {
4409
+ const ts = new Date(cred.updatedAt).toISOString();
4410
+ console.log(`${cred.name} ${ts} ${cred.rotatedByUserId ?? "-"}`);
4411
+ }
4412
+ });
4447
4413
  }
4448
4414
  async function setTestCredentialCommand(repo, name, options, command) {
4449
- const runtime = getRuntimeOptions(command, options);
4450
- const config = requireConfig(runtime);
4415
+ const { config } = resolveBusinessContext(command, options);
4451
4416
  const repoArg = parseRepoArg3(repo);
4452
4417
  const value = readValue(options);
4453
4418
  const payload = await apiFetch(
@@ -4455,11 +4420,12 @@ async function setTestCredentialCommand(repo, name, options, command) {
4455
4420
  `${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
4456
4421
  { method: "PUT", body: JSON.stringify({ value }) }
4457
4422
  );
4458
- if (isJson(command, options)) {
4459
- writeJson(payload);
4460
- return;
4461
- }
4462
- console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.name}.`);
4423
+ emit(
4424
+ command,
4425
+ options,
4426
+ payload,
4427
+ () => console.log(`Set test credential '${name}' for ${repoArg.owner}/${repoArg.name}.`)
4428
+ );
4463
4429
  }
4464
4430
  async function deleteTestCredentialCommand(repo, name, options, command) {
4465
4431
  if (isJson(command, options) && options.yes !== true) {
@@ -4469,25 +4435,24 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
4469
4435
  const repoArg2 = parseRepoArg3(repo);
4470
4436
  await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.name}?`);
4471
4437
  }
4472
- const runtime = getRuntimeOptions(command, options);
4473
- const config = requireConfig(runtime);
4438
+ const { config } = resolveBusinessContext(command, options);
4474
4439
  const repoArg = parseRepoArg3(repo);
4475
4440
  const payload = await apiFetch(
4476
4441
  config,
4477
4442
  `${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
4478
4443
  { method: "DELETE" }
4479
4444
  );
4480
- if (isJson(command, options)) {
4481
- writeJson(payload);
4482
- return;
4483
- }
4484
- console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.name}.`);
4445
+ emit(
4446
+ command,
4447
+ options,
4448
+ payload,
4449
+ () => console.log(`Deleted test credential '${name}' for ${repoArg.owner}/${repoArg.name}.`)
4450
+ );
4485
4451
  }
4486
4452
 
4487
4453
  // src/commands/tokens.ts
4488
4454
  async function listTokensCommand(options, command) {
4489
- const runtime = getRuntimeOptions(command, options);
4490
- const config = requireConfig(runtime);
4455
+ const { config } = resolveBusinessContext(command, options);
4491
4456
  const query = new URLSearchParams();
4492
4457
  if (options.limit) query.set("limit", options.limit);
4493
4458
  if (options.cursor) query.set("cursor", options.cursor);
@@ -4495,24 +4460,21 @@ async function listTokensCommand(options, command) {
4495
4460
  config,
4496
4461
  `/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
4497
4462
  );
4498
- if (isJson(command, options)) {
4499
- writeJson(payload);
4500
- return;
4501
- }
4502
- if (payload.data.length === 0) {
4503
- console.log("No CLI tokens found.");
4504
- return;
4505
- }
4506
- for (const token of payload.data) {
4507
- console.log(
4508
- `${String(token.id)} ${String(token.scope)} ${String(token.tokenPrefix)} ${String(token.revokedAt ? "revoked" : "active")}`
4509
- );
4510
- }
4511
- if (payload.nextCursor) console.log(`Next cursor: ${payload.nextCursor}`);
4463
+ emit(command, options, payload, (listPayload) => {
4464
+ if (listPayload.data.length === 0) {
4465
+ console.log("No CLI tokens found.");
4466
+ return;
4467
+ }
4468
+ for (const token of listPayload.data) {
4469
+ console.log(
4470
+ `${String(token.id)} ${String(token.scope)} ${String(token.tokenPrefix)} ${String(token.revokedAt ? "revoked" : "active")}`
4471
+ );
4472
+ }
4473
+ if (listPayload.nextCursor) console.log(`Next cursor: ${listPayload.nextCursor}`);
4474
+ });
4512
4475
  }
4513
4476
  async function createTokenCommand(options, command) {
4514
- const runtime = getRuntimeOptions(command, options);
4515
- const config = requireConfig(runtime);
4477
+ const { config } = resolveBusinessContext(command, options);
4516
4478
  const expiresInDays = options.expiresInDays === void 0 ? void 0 : Number(options.expiresInDays);
4517
4479
  if (expiresInDays !== void 0 && (!Number.isInteger(expiresInDays) || expiresInDays <= 0)) {
4518
4480
  throw new CliError("user", "expiresInDays must be a positive integer.");
@@ -4524,13 +4486,11 @@ async function createTokenCommand(options, command) {
4524
4486
  ...expiresInDays !== void 0 ? { expiresInDays } : {}
4525
4487
  })
4526
4488
  });
4527
- if (isJson(command, options)) {
4528
- writeJson(payload);
4529
- return;
4530
- }
4531
- console.log(`Token: ${String(payload.token)}`);
4532
- console.log(`ID: ${String(payload.id)}`);
4533
- console.log(`Scope: ${String(payload.scope)}`);
4489
+ emit(command, options, payload, (tokenPayload) => {
4490
+ console.log(`Token: ${String(tokenPayload.token)}`);
4491
+ console.log(`ID: ${String(tokenPayload.id)}`);
4492
+ console.log(`Scope: ${String(tokenPayload.scope)}`);
4493
+ });
4534
4494
  }
4535
4495
  async function revokeTokenCommand(tokenId, options, command) {
4536
4496
  if (isJson(command, options) && options.yes !== true) {
@@ -4539,28 +4499,18 @@ async function revokeTokenCommand(tokenId, options, command) {
4539
4499
  if (options.yes !== true) {
4540
4500
  await confirmOrThrow(`Revoke CLI token ${tokenId}?`);
4541
4501
  }
4542
- const runtime = getRuntimeOptions(command, options);
4543
- const config = requireConfig(runtime);
4502
+ const { config } = resolveBusinessContext(command, options);
4544
4503
  const payload = await apiFetch(config, `/api/cli-tokens/${tokenId}/revoke`, {
4545
4504
  method: "POST"
4546
4505
  });
4547
- if (isJson(command, options)) {
4548
- writeJson(payload);
4549
- return;
4550
- }
4551
- console.log(`Revoked token ${tokenId}.`);
4506
+ emit(command, options, payload, () => console.log(`Revoked token ${tokenId}.`));
4552
4507
  }
4553
4508
 
4554
4509
  // src/commands/transcript.ts
4555
4510
  async function transcriptCommand(sessionId, options, command) {
4556
- const runtime = getRuntimeOptions(command, options);
4557
- const config = requireConfig(runtime);
4511
+ const { config } = resolveBusinessContext(command, options);
4558
4512
  const exportData = await apiFetch(config, `/api/sessions/${sessionId}/export`);
4559
- if (isJson(command, options)) {
4560
- writeJson(exportData);
4561
- return;
4562
- }
4563
- console.log(renderSessionTranscript(exportData));
4513
+ emit(command, options, exportData, (payload) => console.log(renderSessionTranscript(payload)));
4564
4514
  }
4565
4515
 
4566
4516
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tryarcanist/cli",
3
- "version": "0.1.160",
3
+ "version": "0.1.162",
4
4
  "description": "CLI for Arcanist — create and manage coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {