@apicircle/cli 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CLI_PACKAGE_VERSION
4
- } from "./chunk-HNK3FW57.js";
4
+ } from "./chunk-M5PWIZZF.js";
5
5
 
6
6
  // src/index.ts
7
7
  import { Command } from "commander";
@@ -140,8 +140,8 @@ async function ensureWorkspace(dir) {
140
140
  ui: {
141
141
  activeRequestId: null,
142
142
  sidebarExpandedSections: [],
143
- themeId: "command-center",
144
- fontId: "cascadia-code",
143
+ themeId: "one-dark-pro",
144
+ fontId: "system-sans",
145
145
  fontSizePercent: FONT_SIZE_PERCENT_DEFAULT
146
146
  },
147
147
  settings: { validateOnSend: true, monacoConsumesWheel: false },
@@ -368,8 +368,8 @@ function buildEmptyState(workspaceId, now, withSample) {
368
368
  ui: {
369
369
  activeRequestId: sample?.id ?? null,
370
370
  sidebarExpandedSections: [],
371
- themeId: "command-center",
372
- fontId: "cascadia-code",
371
+ themeId: "one-dark-pro",
372
+ fontId: "system-sans",
373
373
  fontSizePercent: 100
374
374
  },
375
375
  settings: { validateOnSend: true, monacoConsumesWheel: false },
@@ -427,7 +427,7 @@ function registerMcpCommand(program) {
427
427
  import { promises as fs4 } from "fs";
428
428
  import * as path4 from "path";
429
429
  import kleur3 from "kleur";
430
- import { applyMutation } from "@apicircle/core";
430
+ import { applyMutation, parseApicircleFolderExport } from "@apicircle/core";
431
431
  import { saveToFile as saveToFile3 } from "@apicircle/core/workspace/file-backed";
432
432
  import {
433
433
  parseInsomniaToEndpoints,
@@ -436,7 +436,10 @@ import {
436
436
  } from "@apicircle/mock-server-core";
437
437
  import { generateId as generateId4 } from "@apicircle/shared";
438
438
  function registerImportCommand(program) {
439
- program.command("import").description("Import a spec into a workspace folder").argument("<type>", "Source type: openapi | postman | insomnia | curl").argument("<input>", "Path to a spec file, or `-` to read from stdin").option(
439
+ program.command("import").description("Import a spec into a workspace folder").argument(
440
+ "<type>",
441
+ "Source type: openapi | postman | insomnia | curl | apicircle (the apicircle.folder/v1 envelope produced by `apicircle export folder`)"
442
+ ).argument("<input>", "Path to a spec file, or `-` to read from stdin").option(
440
443
  "--workspace-name <name-or-id>",
441
444
  "Registry workspace name (case-insensitive) or id. Defaults to the active workspace."
442
445
  ).option(
@@ -526,6 +529,40 @@ function registerImportCommand(program) {
526
529
  })
527
530
  );
528
531
  }
532
+ } else if (type === "apicircle") {
533
+ let parsedEnvelope;
534
+ try {
535
+ parsedEnvelope = parseApicircleFolderExport(raw);
536
+ } catch (err) {
537
+ process.stderr.write(
538
+ `${kleur3.red("error")}: ${err instanceof Error ? err.message : String(err)}
539
+ `
540
+ );
541
+ process.exit(2);
542
+ }
543
+ const out = applyMutation(
544
+ { synced: nextSynced, local: nextLocal },
545
+ { kind: "folder.import_apicircle", parsed: parsedEnvelope, parentFolderId: null }
546
+ );
547
+ nextSynced = out.next.synced;
548
+ nextLocal = out.next.local;
549
+ for (const r of parsedEnvelope.requests) created.push(r.id);
550
+ for (const w of parsedEnvelope.warnings) {
551
+ process.stderr.write(`${kleur3.yellow("warning")}: ${w}
552
+ `);
553
+ }
554
+ await saveToFile3(dir, { synced: nextSynced, local: nextLocal });
555
+ process.stdout.write(
556
+ `${kleur3.green("imported")} folder "${parsedEnvelope.rootFolder.name}" (${parsedEnvelope.subfolders.length + 1} folders, ${parsedEnvelope.requests.length} requests) into ${dir}
557
+ `
558
+ );
559
+ if (parsedEnvelope.dependencies.files.length > 0) {
560
+ process.stderr.write(
561
+ `${kleur3.yellow("note")}: ${parsedEnvelope.dependencies.files.length} file asset${parsedEnvelope.dependencies.files.length === 1 ? "" : "s"} landed without bytes \u2014 re-attach them inside Global Assets \u2192 Global Files.
562
+ `
563
+ );
564
+ }
565
+ return;
529
566
  } else {
530
567
  process.stderr.write(`${kleur3.red("error")}: unknown type '${String(type)}'
531
568
  `);
@@ -540,13 +577,13 @@ function registerImportCommand(program) {
540
577
  }
541
578
  async function readInput(p) {
542
579
  if (p === "-") {
543
- return new Promise((resolve7, reject) => {
580
+ return new Promise((resolve8, reject) => {
544
581
  let data = "";
545
582
  process.stdin.setEncoding("utf-8");
546
583
  process.stdin.on("data", (chunk) => {
547
584
  data += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
548
585
  });
549
- process.stdin.on("end", () => resolve7(data));
586
+ process.stdin.on("end", () => resolve8(data));
550
587
  process.stdin.on("error", reject);
551
588
  });
552
589
  }
@@ -570,9 +607,112 @@ function blankRequest(partial) {
570
607
  };
571
608
  }
572
609
 
610
+ // src/commands/export.ts
611
+ import { promises as fs5 } from "fs";
612
+ import * as path5 from "path";
613
+ import kleur4 from "kleur";
614
+ import {
615
+ collectFolderExport,
616
+ redactFolderExportCredentials,
617
+ serializeFolderExport,
618
+ suggestFolderExportFilename
619
+ } from "@apicircle/core";
620
+ function registerExportCommand(program) {
621
+ const exportCmd = program.command("export").description("Export workspace entities to portable JSON.");
622
+ exportCmd.command("folder").description("Export a folder (and its subtree) as an apicircle.folder/v1 JSON envelope.").argument(
623
+ "<folder>",
624
+ "Folder id, or display name (case-insensitive). Unique within the workspace."
625
+ ).option("-o, --out <path>", "Write the JSON to this file. Defaults to stdout.").option(
626
+ "--include-credential <id>",
627
+ "Keep the credential field with this id (repeatable). Use --list-credentials to see ids.",
628
+ (value, prev = []) => [...prev, value],
629
+ []
630
+ ).option(
631
+ "--list-credentials",
632
+ "Print the detected credentials + their ids and exit without writing anything."
633
+ ).option(
634
+ "--workspace-name <name-or-id>",
635
+ "Registry workspace name (case-insensitive) or id. Defaults to the active workspace."
636
+ ).option(
637
+ "-w, --workspace-path <dir>",
638
+ "Filesystem directory containing workspace.synced.json (skips the registry)."
639
+ ).action(async (folder, opts) => {
640
+ let dir;
641
+ try {
642
+ const resolved = await resolveWorkspace({
643
+ name: opts.workspaceName,
644
+ path: opts.workspacePath
645
+ });
646
+ dir = resolved.dir;
647
+ } catch (err) {
648
+ if (err instanceof WorkspaceResolutionError) {
649
+ process.stderr.write(`${kleur4.red("error")}: ${err.message}
650
+ `);
651
+ process.exit(2);
652
+ }
653
+ throw err;
654
+ }
655
+ const state = await ensureWorkspace(dir);
656
+ const folderId = resolveFolderId(state.synced.collections.folders, folder);
657
+ if (!folderId) {
658
+ process.stderr.write(`${kleur4.red("error")}: no folder matches "${folder}" in ${dir}
659
+ `);
660
+ process.exit(2);
661
+ }
662
+ const collected = collectFolderExport({ synced: state.synced, folderId });
663
+ if (!collected) {
664
+ process.stderr.write(`${kleur4.red("error")}: folder "${folder}" no longer exists
665
+ `);
666
+ process.exit(2);
667
+ }
668
+ if (opts.listCredentials) {
669
+ if (collected.report.credentials.length === 0) {
670
+ process.stdout.write("No credential-bearing auth fields detected.\n");
671
+ return;
672
+ }
673
+ for (const cred of collected.report.credentials) {
674
+ process.stdout.write(`${cred.id} ${cred.label} ${cred.ownerName}
675
+ `);
676
+ }
677
+ return;
678
+ }
679
+ const includeIds = new Set(opts.includeCredential ?? []);
680
+ const envelope = redactFolderExportCredentials(collected.envelope, includeIds);
681
+ const json = serializeFolderExport(envelope);
682
+ if (opts.out) {
683
+ const outPath = path5.resolve(opts.out);
684
+ await fs5.writeFile(outPath, json, "utf-8");
685
+ process.stderr.write(
686
+ `${kleur4.green("exported")} folder "${collected.report.folderName}" \u2192 ${outPath}
687
+ `
688
+ );
689
+ } else {
690
+ process.stdout.write(json);
691
+ process.stdout.write("\n");
692
+ process.stderr.write(
693
+ `${kleur4.green("exported")} folder "${collected.report.folderName}" (${collected.report.totalFolderCount} folders, ${collected.report.requestCount} requests, ${collected.report.credentials.length - includeIds.size} credentials redacted)
694
+ `
695
+ );
696
+ }
697
+ if (!opts.out) {
698
+ process.stderr.write(
699
+ `${kleur4.dim("hint")}: save with .apicircle.json, e.g. ${suggestFolderExportFilename(envelope)}
700
+ `
701
+ );
702
+ }
703
+ });
704
+ }
705
+ function resolveFolderId(folders, query) {
706
+ if (folders[query]) return query;
707
+ const norm = query.trim().toLowerCase();
708
+ const matches = Object.values(folders).filter((f) => f.name.trim().toLowerCase() === norm);
709
+ if (matches.length === 1) return matches[0].id;
710
+ return null;
711
+ }
712
+
573
713
  // src/commands/run.ts
574
714
  import * as os2 from "os";
575
- import kleur4 from "kleur";
715
+ import kleur5 from "kleur";
576
716
  import {
577
717
  ANONYMOUS_ACTOR,
578
718
  PlanRunDeniedError,
@@ -582,16 +722,16 @@ import {
582
722
  import { loadFromFile as loadFromFile2, saveToFile as saveToFile4 } from "@apicircle/core/workspace/file-backed";
583
723
 
584
724
  // src/util/secrets.ts
585
- import * as path5 from "path";
586
- import { promises as fs5 } from "fs";
725
+ import * as path6 from "path";
726
+ import { promises as fs6 } from "fs";
587
727
  var DEFAULT_PREFIX = "APICIRCLE_SECRET_";
588
728
  async function buildSecretsFromCli(options = {}) {
589
729
  const env = options.env ?? process.env;
590
730
  const prefix = options.envPrefix ?? DEFAULT_PREFIX;
591
731
  const byId = {};
592
732
  if (options.secretsFile) {
593
- const resolved = path5.resolve(options.secretsFile);
594
- const raw = await fs5.readFile(resolved, "utf8");
733
+ const resolved = path6.resolve(options.secretsFile);
734
+ const raw = await fs6.readFile(resolved, "utf8");
595
735
  const parsed = JSON.parse(raw);
596
736
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
597
737
  throw new Error(
@@ -615,16 +755,16 @@ async function buildSecretsFromCli(options = {}) {
615
755
 
616
756
  // src/util/executionAttachments.ts
617
757
  import { createHash } from "crypto";
618
- import { promises as fs6 } from "fs";
619
- import * as path6 from "path";
758
+ import { promises as fs7 } from "fs";
759
+ import * as path7 from "path";
620
760
  import {
621
761
  collectAttachmentSlots
622
762
  } from "@apicircle/core";
623
- var ATTACHMENTS_DIR = path6.join(".apicircle", "attachments");
763
+ var ATTACHMENTS_DIR = path7.join(".apicircle", "attachments");
624
764
  async function prepareExecutionAttachments(workspaceDir, state, plan) {
625
- const cacheDir = path6.resolve(workspaceDir, ATTACHMENTS_DIR);
765
+ const cacheDir = path7.resolve(workspaceDir, ATTACHMENTS_DIR);
626
766
  const requirements = collectExecutionAttachmentRequirements(state, plan);
627
- await fs6.mkdir(cacheDir, { recursive: true });
767
+ await fs7.mkdir(cacheDir, { recursive: true });
628
768
  let downloaded = 0;
629
769
  let alreadyPresent = 0;
630
770
  let failed = 0;
@@ -633,7 +773,7 @@ async function prepareExecutionAttachments(workspaceDir, state, plan) {
633
773
  };
634
774
  const entries = [];
635
775
  for (const requirement of requirements) {
636
- const localPath = path6.join(cacheDir, encodeURIComponent(requirement.slotId));
776
+ const localPath = path7.join(cacheDir, encodeURIComponent(requirement.slotId));
637
777
  const present = await hasExpectedFile(localPath, requirement.sha256);
638
778
  if (present) {
639
779
  alreadyPresent++;
@@ -650,7 +790,7 @@ async function prepareExecutionAttachments(workspaceDir, state, plan) {
650
790
  `Attachment ${attachmentLabel(requirement)} failed checksum verification.`
651
791
  );
652
792
  }
653
- await fs6.writeFile(localPath, bytes, { mode: 384 });
793
+ await fs7.writeFile(localPath, bytes, { mode: 384 });
654
794
  downloaded++;
655
795
  } catch (err) {
656
796
  failed++;
@@ -707,7 +847,7 @@ function createFileAttachmentResolver(state) {
707
847
  return async (slotId) => {
708
848
  const meta = state.local.attachmentCache?.[slotId];
709
849
  if (!meta) return null;
710
- const bytes = await fs6.readFile(meta.localPath);
850
+ const bytes = await fs7.readFile(meta.localPath);
711
851
  const view = new Uint8Array(bytes);
712
852
  const body = view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
713
853
  return {
@@ -812,7 +952,7 @@ function bodyReferencesSlot(body, slotId) {
812
952
  }
813
953
  async function hasExpectedFile(localPath, sha256) {
814
954
  try {
815
- const bytes = await fs6.readFile(localPath);
955
+ const bytes = await fs7.readFile(localPath);
816
956
  if (!sha256) return true;
817
957
  return sha256Hex(bytes) === sha256;
818
958
  } catch {
@@ -821,7 +961,7 @@ async function hasExpectedFile(localPath, sha256) {
821
961
  }
822
962
  async function fileSize(localPath) {
823
963
  try {
824
- return (await fs6.stat(localPath)).size;
964
+ return (await fs7.stat(localPath)).size;
825
965
  } catch {
826
966
  return 0;
827
967
  }
@@ -901,7 +1041,7 @@ function registerRunCommand(program) {
901
1041
  dir = resolved.dir;
902
1042
  if (resolved.fromRegistry) {
903
1043
  process.stderr.write(
904
- `${kleur4.dim("workspace")}: ${kleur4.cyan(resolved.name ?? resolved.id ?? "")} ${kleur4.dim(`(${dir})`)}
1044
+ `${kleur5.dim("workspace")}: ${kleur5.cyan(resolved.name ?? resolved.id ?? "")} ${kleur5.dim(`(${dir})`)}
905
1045
  `
906
1046
  );
907
1047
  }
@@ -1030,17 +1170,17 @@ function formatHeader(plan, actor, withAssertions, opts) {
1030
1170
  opts.bail ? "bail" : null,
1031
1171
  opts.env ? `env=${opts.env}` : null
1032
1172
  ].filter((f) => f !== null);
1033
- return `${kleur4.bold("Plan")} ${plan.name} ${kleur4.dim(
1173
+ return `${kleur5.bold("Plan")} ${plan.name} ${kleur5.dim(
1034
1174
  `(${enabled}/${plan.steps.length} steps \xB7 ${flags.join(" \xB7 ")})`
1035
1175
  )}
1036
- ${kleur4.dim("Run by")} ${actor.name} ${kleur4.dim(`(${actor.kind})`)}
1176
+ ${kleur5.dim("Run by")} ${actor.name} ${kleur5.dim(`(${actor.kind})`)}
1037
1177
 
1038
1178
  `;
1039
1179
  }
1040
1180
  function formatAttachmentPreparation(summary) {
1041
1181
  const status = `${summary.downloaded} downloaded, ${summary.alreadyPresent} already local`;
1042
1182
  const lines = [
1043
- `${kleur4.bold("Attachments")} ${summary.total} required ${kleur4.dim(
1183
+ `${kleur5.bold("Attachments")} ${summary.total} required ${kleur5.dim(
1044
1184
  `(${status} - ${summary.cacheDir})`
1045
1185
  )}`
1046
1186
  ];
@@ -1048,7 +1188,7 @@ function formatAttachmentPreparation(summary) {
1048
1188
  const source = entry2.source === "linked-workspace" ? `linked:${entry2.linkedWorkspaceId ?? "unknown"}` : "workspace";
1049
1189
  const requiredBy = entry2.requiredBy.map((item) => item.requestName).join(", ");
1050
1190
  lines.push(
1051
- ` ${kleur4.dim("file")} ${entry2.filename} ${kleur4.dim(
1191
+ ` ${kleur5.dim("file")} ${entry2.filename} ${kleur5.dim(
1052
1192
  `${source} - ${requiredBy} - ${entry2.localPath}`
1053
1193
  )}`
1054
1194
  );
@@ -1061,31 +1201,31 @@ function formatStepLine(step) {
1061
1201
  const n = `${step.stepIndex + 1}.`.padEnd(3);
1062
1202
  const method = (step.requestMethod || "\u2014").padEnd(7);
1063
1203
  if (step.skipped) {
1064
- return ` ${kleur4.dim("\u2013")} ${kleur4.dim(n)} ${kleur4.dim(method)} ${kleur4.dim(
1204
+ return ` ${kleur5.dim("\u2013")} ${kleur5.dim(n)} ${kleur5.dim(method)} ${kleur5.dim(
1065
1205
  `${step.requestName} skipped`
1066
1206
  )}
1067
1207
  `;
1068
1208
  }
1069
- const mark = step.passed ? kleur4.green("\u2713") : kleur4.red("\u2717");
1209
+ const mark = step.passed ? kleur5.green("\u2713") : kleur5.red("\u2717");
1070
1210
  const status = step.result?.status != null ? String(step.result.status) : "\u2014";
1071
1211
  const duration = step.result ? `${step.result.durationMs}ms` : "";
1072
1212
  const name = step.requestName.padEnd(28);
1073
- let line = ` ${mark} ${n} ${method} ${name} ${status.padEnd(4)} ${kleur4.dim(duration)}`;
1213
+ let line = ` ${mark} ${n} ${method} ${name} ${status.padEnd(4)} ${kleur5.dim(duration)}`;
1074
1214
  if (step.assertionResults.length > 0) {
1075
1215
  const passed = step.assertionResults.filter((a) => a.passed).length;
1076
- line += ` ${kleur4.dim(`${passed}/${step.assertionResults.length} assertions`)}`;
1216
+ line += ` ${kleur5.dim(`${passed}/${step.assertionResults.length} assertions`)}`;
1077
1217
  }
1078
1218
  line += "\n";
1079
1219
  if (step.error) {
1080
- line += ` ${kleur4.red(step.error)}
1220
+ line += ` ${kleur5.red(step.error)}
1081
1221
  `;
1082
1222
  }
1083
1223
  for (const a of step.assertionResults) {
1084
- if (!a.passed) line += ` ${kleur4.red("\u2717")} ${a.detail ?? `${a.kind} ${a.op}`}
1224
+ if (!a.passed) line += ` ${kleur5.red("\u2717")} ${a.detail ?? `${a.kind} ${a.op}`}
1085
1225
  `;
1086
1226
  }
1087
1227
  if (step.missingVariables.length > 0) {
1088
- line += ` ${kleur4.yellow("\u26A0")} unresolved: ${step.missingVariables.map((v) => `{{${v}}}`).join(", ")}
1228
+ line += ` ${kleur5.yellow("\u26A0")} unresolved: ${step.missingVariables.map((v) => `{{${v}}}`).join(", ")}
1089
1229
  `;
1090
1230
  }
1091
1231
  return line;
@@ -1104,24 +1244,24 @@ function tally(result) {
1104
1244
  function formatSummary(result, saved, aborted) {
1105
1245
  if (result.steps.length === 0) {
1106
1246
  return `
1107
- ${kleur4.yellow("Plan has no steps.")}
1247
+ ${kleur5.yellow("Plan has no steps.")}
1108
1248
  `;
1109
1249
  }
1110
1250
  const { passed, failed, skipped } = tally(result);
1111
1251
  const parts = [
1112
- kleur4.green(`${passed} passed`),
1113
- failed > 0 ? kleur4.red(`${failed} failed`) : kleur4.dim(`${failed} failed`),
1114
- kleur4.dim(`${skipped} skipped`)
1252
+ kleur5.green(`${passed} passed`),
1253
+ failed > 0 ? kleur5.red(`${failed} failed`) : kleur5.dim(`${failed} failed`),
1254
+ kleur5.dim(`${skipped} skipped`)
1115
1255
  ];
1116
- const verdict = result.passed && !aborted ? kleur4.green("PASS") : kleur4.red("FAIL");
1256
+ const verdict = result.passed && !aborted ? kleur5.green("PASS") : kleur5.red("FAIL");
1117
1257
  let out = `
1118
- ${verdict} ${parts.join(kleur4.dim(" \xB7 "))} ${kleur4.dim(
1258
+ ${verdict} ${parts.join(kleur5.dim(" \xB7 "))} ${kleur5.dim(
1119
1259
  `\xB7 ${result.planRun.durationMs}ms`
1120
1260
  )}
1121
1261
  `;
1122
- if (aborted) out += `${kleur4.yellow("Run aborted before every step finished.")}
1262
+ if (aborted) out += `${kleur5.yellow("Run aborted before every step finished.")}
1123
1263
  `;
1124
- out += saved ? kleur4.dim("Plan run saved to workspace history.\n") : kleur4.dim("Plan run not saved (--no-save).\n");
1264
+ out += saved ? kleur5.dim("Plan run saved to workspace history.\n") : kleur5.dim("Plan run not saved (--no-save).\n");
1125
1265
  return out;
1126
1266
  }
1127
1267
  function buildJsonReport(workspace, planId, plan, actor, result, saved, aborted, attachments) {
@@ -1197,13 +1337,13 @@ ${cases.join("\n")}
1197
1337
  `;
1198
1338
  }
1199
1339
  function fail(message, code = 2, kind = "error") {
1200
- process.stderr.write(`${kleur4.red(kind)}: ${message}
1340
+ process.stderr.write(`${kleur5.red(kind)}: ${message}
1201
1341
  `);
1202
1342
  process.exitCode = code;
1203
1343
  }
1204
1344
 
1205
1345
  // src/commands/workspaces.ts
1206
- import kleur5 from "kleur";
1346
+ import kleur6 from "kleur";
1207
1347
  import { findWorkspaceEntry as findWorkspaceEntry2, setActiveWorkspace } from "@apicircle/core/workspace/registry";
1208
1348
  function registerWorkspacesCommand(program) {
1209
1349
  const ws = program.command("workspaces").description("List, create, or switch the active workspace");
@@ -1215,15 +1355,15 @@ function registerWorkspacesCommand(program) {
1215
1355
  }
1216
1356
  if (registry.workspaces.length === 0) {
1217
1357
  process.stdout.write(
1218
- `${kleur5.dim("No workspaces registered yet at")} ${root}
1219
- ${kleur5.dim("Run")} ${kleur5.cyan("apicircle workspaces create <name>")} ${kleur5.dim(
1358
+ `${kleur6.dim("No workspaces registered yet at")} ${root}
1359
+ ${kleur6.dim("Run")} ${kleur6.cyan("apicircle workspaces create <name>")} ${kleur6.dim(
1220
1360
  "or open the desktop app to seed one."
1221
1361
  )}
1222
1362
  `
1223
1363
  );
1224
1364
  return;
1225
1365
  }
1226
- process.stdout.write(`${kleur5.dim("registry")}: ${root}
1366
+ process.stdout.write(`${kleur6.dim("registry")}: ${root}
1227
1367
 
1228
1368
  `);
1229
1369
  const rows = [...registry.workspaces].sort(
@@ -1232,22 +1372,22 @@ ${kleur5.dim("Run")} ${kleur5.cyan("apicircle workspaces create <name>")} ${kleu
1232
1372
  const nameWidth = Math.max(4, ...rows.map((r) => r.name.length));
1233
1373
  const idWidth = Math.max(2, ...rows.map((r) => r.id.length));
1234
1374
  process.stdout.write(
1235
- kleur5.bold(
1375
+ kleur6.bold(
1236
1376
  ` ${"".padEnd(1)} ${"NAME".padEnd(nameWidth)} ${"ID".padEnd(idWidth)} LAST OPENED
1237
1377
  `
1238
1378
  )
1239
1379
  );
1240
1380
  for (const w of rows) {
1241
- const mark = w.id === registry.activeWorkspaceId ? kleur5.green("\u25CF") : " ";
1381
+ const mark = w.id === registry.activeWorkspaceId ? kleur6.green("\u25CF") : " ";
1242
1382
  process.stdout.write(
1243
- ` ${mark} ${w.name.padEnd(nameWidth)} ${kleur5.dim(
1383
+ ` ${mark} ${w.name.padEnd(nameWidth)} ${kleur6.dim(
1244
1384
  w.id.padEnd(idWidth)
1245
- )} ${kleur5.dim(w.lastOpenedAt)}
1385
+ )} ${kleur6.dim(w.lastOpenedAt)}
1246
1386
  `
1247
1387
  );
1248
1388
  }
1249
1389
  process.stdout.write(`
1250
- ${kleur5.dim("\u25CF = active")}
1390
+ ${kleur6.dim("\u25CF = active")}
1251
1391
  `);
1252
1392
  });
1253
1393
  ws.command("create").description("Create a new workspace and add it to the registry").argument("<name>", "Human-readable label for the workspace").option("--sample", "Seed the workspace with one sample request", false).action(async (name, opts) => {
@@ -1257,17 +1397,17 @@ ${kleur5.dim("\u25CF = active")}
1257
1397
  sampleRequest: opts.sample ?? false
1258
1398
  });
1259
1399
  process.stdout.write(
1260
- `${kleur5.green("created")} workspace ${kleur5.cyan(entry2.name)} ${kleur5.dim(`(${entry2.id})`)}
1400
+ `${kleur6.green("created")} workspace ${kleur6.cyan(entry2.name)} ${kleur6.dim(`(${entry2.id})`)}
1261
1401
  at ${dir}
1262
1402
  `
1263
1403
  );
1264
1404
  if (registry.activeWorkspaceId === entry2.id) {
1265
- process.stdout.write(`${kleur5.dim("marked as active")}
1405
+ process.stdout.write(`${kleur6.dim("marked as active")}
1266
1406
  `);
1267
1407
  }
1268
1408
  } catch (err) {
1269
1409
  process.stderr.write(
1270
- `${kleur5.red("error")}: ${err instanceof Error ? err.message : String(err)}
1410
+ `${kleur6.red("error")}: ${err instanceof Error ? err.message : String(err)}
1271
1411
  `
1272
1412
  );
1273
1413
  process.exit(2);
@@ -1278,8 +1418,8 @@ ${kleur5.dim("\u25CF = active")}
1278
1418
  const entry2 = findWorkspaceEntry2(registry, selector);
1279
1419
  if (!entry2) {
1280
1420
  process.stderr.write(
1281
- `${kleur5.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1282
- ${kleur5.dim("Run")} ${kleur5.cyan("apicircle workspaces list")} ${kleur5.dim("to see what is available.")}
1421
+ `${kleur6.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1422
+ ${kleur6.dim("Run")} ${kleur6.cyan("apicircle workspaces list")} ${kleur6.dim("to see what is available.")}
1283
1423
  `
1284
1424
  );
1285
1425
  process.exit(2);
@@ -1288,7 +1428,7 @@ ${kleur5.dim("Run")} ${kleur5.cyan("apicircle workspaces list")} ${kleur5.dim("t
1288
1428
  const next = await setActiveWorkspace(root, entry2.id);
1289
1429
  void next;
1290
1430
  process.stdout.write(
1291
- `${kleur5.green("active")} workspace is now ${kleur5.cyan(entry2.name)} ${kleur5.dim(`(${entry2.id})`)}
1431
+ `${kleur6.green("active")} workspace is now ${kleur6.cyan(entry2.name)} ${kleur6.dim(`(${entry2.id})`)}
1292
1432
  `
1293
1433
  );
1294
1434
  });
@@ -1301,7 +1441,7 @@ ${kleur5.dim("Run")} ${kleur5.cyan("apicircle workspaces list")} ${kleur5.dim("t
1301
1441
  const entry2 = findWorkspaceEntry2(registry, selector);
1302
1442
  if (!entry2) {
1303
1443
  process.stderr.write(
1304
- `${kleur5.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1444
+ `${kleur6.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1305
1445
  `
1306
1446
  );
1307
1447
  process.exit(2);
@@ -1320,6 +1460,7 @@ function buildProgram() {
1320
1460
  registerMockCommand(program);
1321
1461
  registerMcpCommand(program);
1322
1462
  registerImportCommand(program);
1463
+ registerExportCommand(program);
1323
1464
  registerRunCommand(program);
1324
1465
  registerWorkspacesCommand(program);
1325
1466
  return program;