@apicircle/cli 1.0.6 → 1.0.7

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/bin/cli.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-TOUXWWC5.js";
5
5
 
6
6
  // src/bin/args.ts
7
7
  function hasRootVersionFlag(args) {
@@ -3,10 +3,30 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@apicircle/cli",
6
- version: "1.0.6",
6
+ version: "1.0.7",
7
7
  private: false,
8
8
  type: "module",
9
9
  description: "Command-line interface for API Circle Studio. Run mock servers, drive the MCP server, and import OpenAPI / Postman / Insomnia collections from any terminal.",
10
+ keywords: [
11
+ "apicircle",
12
+ "api-circle",
13
+ "cli",
14
+ "command-line",
15
+ "api",
16
+ "api-client",
17
+ "http",
18
+ "http-client",
19
+ "mock-server",
20
+ "mcp",
21
+ "mcp-server",
22
+ "openapi",
23
+ "swagger",
24
+ "postman",
25
+ "insomnia",
26
+ "import",
27
+ "workspace",
28
+ "headless"
29
+ ],
10
30
  license: "SEE LICENSE IN LICENSE",
11
31
  repository: {
12
32
  type: "git",
@@ -81,4 +101,4 @@ var CLI_PACKAGE_VERSION = readPackageVersion();
81
101
  export {
82
102
  CLI_PACKAGE_VERSION
83
103
  };
84
- //# sourceMappingURL=chunk-HNK3FW57.js.map
104
+ //# sourceMappingURL=chunk-TOUXWWC5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../package.json","../src/packageVersion.ts"],"sourcesContent":["{\n \"name\": \"@apicircle/cli\",\n \"version\": \"1.0.7\",\n \"private\": false,\n \"type\": \"module\",\n \"description\": \"Command-line interface for API Circle Studio. Run mock servers, drive the MCP server, and import OpenAPI / Postman / Insomnia collections from any terminal.\",\n \"keywords\": [\n \"apicircle\",\n \"api-circle\",\n \"cli\",\n \"command-line\",\n \"api\",\n \"api-client\",\n \"http\",\n \"http-client\",\n \"mock-server\",\n \"mcp\",\n \"mcp-server\",\n \"openapi\",\n \"swagger\",\n \"postman\",\n \"insomnia\",\n \"import\",\n \"workspace\",\n \"headless\"\n ],\n \"license\": \"SEE LICENSE IN LICENSE\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/apicircle/studio.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://github.com/apicircle/studio#readme\",\n \"bugs\": \"https://github.com/apicircle/studio/issues\",\n \"engines\": {\n \"node\": \">=20\"\n },\n \"main\": \"./src/index.ts\",\n \"types\": \"./src/index.ts\",\n \"bin\": {\n \"apicircle\": \"./dist/bin/cli.cjs\"\n },\n \"exports\": {\n \".\": \"./src/index.ts\"\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.cts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.cts\",\n \"default\": \"./dist/index.cjs\"\n }\n }\n }\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"check\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"clean\": \"rm -rf dist node_modules\"\n },\n \"dependencies\": {\n \"@apicircle/core\": \"workspace:*\",\n \"@apicircle/mcp-server\": \"workspace:*\",\n \"@apicircle/mock-server-core\": \"workspace:*\",\n \"@apicircle/shared\": \"workspace:*\",\n \"commander\": \"^12.0.0\",\n \"kleur\": \"^4.1.5\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"tsup\": \"^8.3.0\",\n \"typescript\": \"^5.4.0\",\n \"vitest\": \"^2.0.0\"\n }\n}\n","import packageJson from '../package.json';\n\nexport function readPackageVersion(): string {\n const version = (packageJson as { version?: unknown }).version;\n if (typeof version !== 'string' || version.length === 0) {\n throw new Error('Unable to read @apicircle/cli package version');\n }\n return version;\n}\n\nexport const CLI_PACKAGE_VERSION = readPackageVersion();\n"],"mappings":";;;AAAA;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,aAAe;AAAA,EACf,UAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,EACX,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,WAAa;AAAA,EACf;AAAA,EACA,UAAY;AAAA,EACZ,MAAQ;AAAA,EACR,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AAAA,EACA,MAAQ;AAAA,EACR,OAAS;AAAA,EACT,KAAO;AAAA,IACL,WAAa;AAAA,EACf;AAAA,EACA,SAAW;AAAA,IACT,KAAK;AAAA,EACP;AAAA,EACA,OAAS;AAAA,IACP;AAAA,EACF;AAAA,EACA,eAAiB;AAAA,IACf,MAAQ;AAAA,IACR,QAAU;AAAA,IACV,OAAS;AAAA,IACT,SAAW;AAAA,MACT,KAAK;AAAA,QACH,QAAU;AAAA,UACR,OAAS;AAAA,UACT,SAAW;AAAA,QACb;AAAA,QACA,SAAW;AAAA,UACT,OAAS;AAAA,UACT,SAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,OAAS;AAAA,IACT,MAAQ;AAAA,IACR,OAAS;AAAA,EACX;AAAA,EACA,cAAgB;AAAA,IACd,mBAAmB;AAAA,IACnB,yBAAyB;AAAA,IACzB,+BAA+B;AAAA,IAC/B,qBAAqB;AAAA,IACrB,WAAa;AAAA,IACb,OAAS;AAAA,EACX;AAAA,EACA,iBAAmB;AAAA,IACjB,eAAe;AAAA,IACf,MAAQ;AAAA,IACR,YAAc;AAAA,IACd,QAAU;AAAA,EACZ;AACF;;;ACnFO,SAAS,qBAA6B;AAC3C,QAAM,UAAW,gBAAsC;AACvD,MAAI,OAAO,YAAY,YAAY,QAAQ,WAAW,GAAG;AACvD,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AACA,SAAO;AACT;AAEO,IAAM,sBAAsB,mBAAmB;","names":[]}
package/dist/index.cjs CHANGED
@@ -164,8 +164,8 @@ async function ensureWorkspace(dir) {
164
164
  ui: {
165
165
  activeRequestId: null,
166
166
  sidebarExpandedSections: [],
167
- themeId: "command-center",
168
- fontId: "cascadia-code",
167
+ themeId: "one-dark-pro",
168
+ fontId: "system-sans",
169
169
  fontSizePercent: import_shared2.FONT_SIZE_PERCENT_DEFAULT
170
170
  },
171
171
  settings: { validateOnSend: true, monacoConsumesWheel: false },
@@ -386,8 +386,8 @@ function buildEmptyState(workspaceId, now, withSample) {
386
386
  ui: {
387
387
  activeRequestId: sample?.id ?? null,
388
388
  sidebarExpandedSections: [],
389
- themeId: "command-center",
390
- fontId: "cascadia-code",
389
+ themeId: "one-dark-pro",
390
+ fontId: "system-sans",
391
391
  fontSizePercent: 100
392
392
  },
393
393
  settings: { validateOnSend: true, monacoConsumesWheel: false },
@@ -450,7 +450,10 @@ var import_file_backed3 = require("@apicircle/core/workspace/file-backed");
450
450
  var import_mock_server_core2 = require("@apicircle/mock-server-core");
451
451
  var import_shared4 = require("@apicircle/shared");
452
452
  function registerImportCommand(program) {
453
- 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(
453
+ program.command("import").description("Import a spec into a workspace folder").argument(
454
+ "<type>",
455
+ "Source type: openapi | postman | insomnia | curl | apicircle (the apicircle.folder/v1 envelope produced by `apicircle export folder`)"
456
+ ).argument("<input>", "Path to a spec file, or `-` to read from stdin").option(
454
457
  "--workspace-name <name-or-id>",
455
458
  "Registry workspace name (case-insensitive) or id. Defaults to the active workspace."
456
459
  ).option(
@@ -540,6 +543,40 @@ function registerImportCommand(program) {
540
543
  })
541
544
  );
542
545
  }
546
+ } else if (type === "apicircle") {
547
+ let parsedEnvelope;
548
+ try {
549
+ parsedEnvelope = (0, import_core.parseApicircleFolderExport)(raw);
550
+ } catch (err) {
551
+ process.stderr.write(
552
+ `${import_kleur3.default.red("error")}: ${err instanceof Error ? err.message : String(err)}
553
+ `
554
+ );
555
+ process.exit(2);
556
+ }
557
+ const out = (0, import_core.applyMutation)(
558
+ { synced: nextSynced, local: nextLocal },
559
+ { kind: "folder.import_apicircle", parsed: parsedEnvelope, parentFolderId: null }
560
+ );
561
+ nextSynced = out.next.synced;
562
+ nextLocal = out.next.local;
563
+ for (const r of parsedEnvelope.requests) created.push(r.id);
564
+ for (const w of parsedEnvelope.warnings) {
565
+ process.stderr.write(`${import_kleur3.default.yellow("warning")}: ${w}
566
+ `);
567
+ }
568
+ await (0, import_file_backed3.saveToFile)(dir, { synced: nextSynced, local: nextLocal });
569
+ process.stdout.write(
570
+ `${import_kleur3.default.green("imported")} folder "${parsedEnvelope.rootFolder.name}" (${parsedEnvelope.subfolders.length + 1} folders, ${parsedEnvelope.requests.length} requests) into ${dir}
571
+ `
572
+ );
573
+ if (parsedEnvelope.dependencies.files.length > 0) {
574
+ process.stderr.write(
575
+ `${import_kleur3.default.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.
576
+ `
577
+ );
578
+ }
579
+ return;
543
580
  } else {
544
581
  process.stderr.write(`${import_kleur3.default.red("error")}: unknown type '${String(type)}'
545
582
  `);
@@ -554,13 +591,13 @@ function registerImportCommand(program) {
554
591
  }
555
592
  async function readInput(p) {
556
593
  if (p === "-") {
557
- return new Promise((resolve7, reject) => {
594
+ return new Promise((resolve8, reject) => {
558
595
  let data = "";
559
596
  process.stdin.setEncoding("utf-8");
560
597
  process.stdin.on("data", (chunk) => {
561
598
  data += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
562
599
  });
563
- process.stdin.on("end", () => resolve7(data));
600
+ process.stdin.on("end", () => resolve8(data));
564
601
  process.stdin.on("error", reject);
565
602
  });
566
603
  }
@@ -584,23 +621,121 @@ function blankRequest(partial) {
584
621
  };
585
622
  }
586
623
 
624
+ // src/commands/export.ts
625
+ var import_node_fs5 = require("fs");
626
+ var path5 = __toESM(require("path"), 1);
627
+ var import_kleur4 = __toESM(require("kleur"), 1);
628
+ var import_core2 = require("@apicircle/core");
629
+ function registerExportCommand(program) {
630
+ const exportCmd = program.command("export").description("Export workspace entities to portable JSON.");
631
+ exportCmd.command("folder").description("Export a folder (and its subtree) as an apicircle.folder/v1 JSON envelope.").argument(
632
+ "<folder>",
633
+ "Folder id, or display name (case-insensitive). Unique within the workspace."
634
+ ).option("-o, --out <path>", "Write the JSON to this file. Defaults to stdout.").option(
635
+ "--include-credential <id>",
636
+ "Keep the credential field with this id (repeatable). Use --list-credentials to see ids.",
637
+ (value, prev = []) => [...prev, value],
638
+ []
639
+ ).option(
640
+ "--list-credentials",
641
+ "Print the detected credentials + their ids and exit without writing anything."
642
+ ).option(
643
+ "--workspace-name <name-or-id>",
644
+ "Registry workspace name (case-insensitive) or id. Defaults to the active workspace."
645
+ ).option(
646
+ "-w, --workspace-path <dir>",
647
+ "Filesystem directory containing workspace.synced.json (skips the registry)."
648
+ ).action(async (folder, opts) => {
649
+ let dir;
650
+ try {
651
+ const resolved = await resolveWorkspace({
652
+ name: opts.workspaceName,
653
+ path: opts.workspacePath
654
+ });
655
+ dir = resolved.dir;
656
+ } catch (err) {
657
+ if (err instanceof WorkspaceResolutionError) {
658
+ process.stderr.write(`${import_kleur4.default.red("error")}: ${err.message}
659
+ `);
660
+ process.exit(2);
661
+ }
662
+ throw err;
663
+ }
664
+ const state = await ensureWorkspace(dir);
665
+ const folderId = resolveFolderId(state.synced.collections.folders, folder);
666
+ if (!folderId) {
667
+ process.stderr.write(`${import_kleur4.default.red("error")}: no folder matches "${folder}" in ${dir}
668
+ `);
669
+ process.exit(2);
670
+ }
671
+ const collected = (0, import_core2.collectFolderExport)({ synced: state.synced, folderId });
672
+ if (!collected) {
673
+ process.stderr.write(`${import_kleur4.default.red("error")}: folder "${folder}" no longer exists
674
+ `);
675
+ process.exit(2);
676
+ }
677
+ if (opts.listCredentials) {
678
+ if (collected.report.credentials.length === 0) {
679
+ process.stdout.write("No credential-bearing auth fields detected.\n");
680
+ return;
681
+ }
682
+ for (const cred of collected.report.credentials) {
683
+ process.stdout.write(`${cred.id} ${cred.label} ${cred.ownerName}
684
+ `);
685
+ }
686
+ return;
687
+ }
688
+ const includeIds = new Set(opts.includeCredential ?? []);
689
+ const envelope = (0, import_core2.redactFolderExportCredentials)(collected.envelope, includeIds);
690
+ const json = (0, import_core2.serializeFolderExport)(envelope);
691
+ if (opts.out) {
692
+ const outPath = path5.resolve(opts.out);
693
+ await import_node_fs5.promises.writeFile(outPath, json, "utf-8");
694
+ process.stderr.write(
695
+ `${import_kleur4.default.green("exported")} folder "${collected.report.folderName}" \u2192 ${outPath}
696
+ `
697
+ );
698
+ } else {
699
+ process.stdout.write(json);
700
+ process.stdout.write("\n");
701
+ process.stderr.write(
702
+ `${import_kleur4.default.green("exported")} folder "${collected.report.folderName}" (${collected.report.totalFolderCount} folders, ${collected.report.requestCount} requests, ${collected.report.credentials.length - includeIds.size} credentials redacted)
703
+ `
704
+ );
705
+ }
706
+ if (!opts.out) {
707
+ process.stderr.write(
708
+ `${import_kleur4.default.dim("hint")}: save with .apicircle.json, e.g. ${(0, import_core2.suggestFolderExportFilename)(envelope)}
709
+ `
710
+ );
711
+ }
712
+ });
713
+ }
714
+ function resolveFolderId(folders, query) {
715
+ if (folders[query]) return query;
716
+ const norm = query.trim().toLowerCase();
717
+ const matches = Object.values(folders).filter((f) => f.name.trim().toLowerCase() === norm);
718
+ if (matches.length === 1) return matches[0].id;
719
+ return null;
720
+ }
721
+
587
722
  // src/commands/run.ts
588
723
  var os2 = __toESM(require("os"), 1);
589
- var import_kleur4 = __toESM(require("kleur"), 1);
590
- var import_core3 = require("@apicircle/core");
724
+ var import_kleur5 = __toESM(require("kleur"), 1);
725
+ var import_core4 = require("@apicircle/core");
591
726
  var import_file_backed4 = require("@apicircle/core/workspace/file-backed");
592
727
 
593
728
  // src/util/secrets.ts
594
- var path5 = __toESM(require("path"), 1);
595
- var import_node_fs5 = require("fs");
729
+ var path6 = __toESM(require("path"), 1);
730
+ var import_node_fs6 = require("fs");
596
731
  var DEFAULT_PREFIX = "APICIRCLE_SECRET_";
597
732
  async function buildSecretsFromCli(options = {}) {
598
733
  const env = options.env ?? process.env;
599
734
  const prefix = options.envPrefix ?? DEFAULT_PREFIX;
600
735
  const byId = {};
601
736
  if (options.secretsFile) {
602
- const resolved = path5.resolve(options.secretsFile);
603
- const raw = await import_node_fs5.promises.readFile(resolved, "utf8");
737
+ const resolved = path6.resolve(options.secretsFile);
738
+ const raw = await import_node_fs6.promises.readFile(resolved, "utf8");
604
739
  const parsed = JSON.parse(raw);
605
740
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
606
741
  throw new Error(
@@ -624,14 +759,14 @@ async function buildSecretsFromCli(options = {}) {
624
759
 
625
760
  // src/util/executionAttachments.ts
626
761
  var import_node_crypto = require("crypto");
627
- var import_node_fs6 = require("fs");
628
- var path6 = __toESM(require("path"), 1);
629
- var import_core2 = require("@apicircle/core");
630
- var ATTACHMENTS_DIR = path6.join(".apicircle", "attachments");
762
+ var import_node_fs7 = require("fs");
763
+ var path7 = __toESM(require("path"), 1);
764
+ var import_core3 = require("@apicircle/core");
765
+ var ATTACHMENTS_DIR = path7.join(".apicircle", "attachments");
631
766
  async function prepareExecutionAttachments(workspaceDir, state, plan) {
632
- const cacheDir = path6.resolve(workspaceDir, ATTACHMENTS_DIR);
767
+ const cacheDir = path7.resolve(workspaceDir, ATTACHMENTS_DIR);
633
768
  const requirements = collectExecutionAttachmentRequirements(state, plan);
634
- await import_node_fs6.promises.mkdir(cacheDir, { recursive: true });
769
+ await import_node_fs7.promises.mkdir(cacheDir, { recursive: true });
635
770
  let downloaded = 0;
636
771
  let alreadyPresent = 0;
637
772
  let failed = 0;
@@ -640,7 +775,7 @@ async function prepareExecutionAttachments(workspaceDir, state, plan) {
640
775
  };
641
776
  const entries = [];
642
777
  for (const requirement of requirements) {
643
- const localPath = path6.join(cacheDir, encodeURIComponent(requirement.slotId));
778
+ const localPath = path7.join(cacheDir, encodeURIComponent(requirement.slotId));
644
779
  const present = await hasExpectedFile(localPath, requirement.sha256);
645
780
  if (present) {
646
781
  alreadyPresent++;
@@ -657,7 +792,7 @@ async function prepareExecutionAttachments(workspaceDir, state, plan) {
657
792
  `Attachment ${attachmentLabel(requirement)} failed checksum verification.`
658
793
  );
659
794
  }
660
- await import_node_fs6.promises.writeFile(localPath, bytes, { mode: 384 });
795
+ await import_node_fs7.promises.writeFile(localPath, bytes, { mode: 384 });
661
796
  downloaded++;
662
797
  } catch (err) {
663
798
  failed++;
@@ -714,7 +849,7 @@ function createFileAttachmentResolver(state) {
714
849
  return async (slotId) => {
715
850
  const meta = state.local.attachmentCache?.[slotId];
716
851
  if (!meta) return null;
717
- const bytes = await import_node_fs6.promises.readFile(meta.localPath);
852
+ const bytes = await import_node_fs7.promises.readFile(meta.localPath);
718
853
  const view = new Uint8Array(bytes);
719
854
  const body = view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
720
855
  return {
@@ -734,7 +869,7 @@ function collectExecutionAttachmentRequirements(state, plan) {
734
869
  )
735
870
  )
736
871
  } : state.synced.collections;
737
- const workspaceSlots = (0, import_core2.collectAttachmentSlots)({ ...state.synced, collections: localCollections });
872
+ const workspaceSlots = (0, import_core3.collectAttachmentSlots)({ ...state.synced, collections: localCollections });
738
873
  for (const slot of workspaceSlots) {
739
874
  const requiredBy = collectRequiredBy(localCollections.requests, slot.slotId);
740
875
  if (requiredBy.length === 0) continue;
@@ -766,7 +901,7 @@ function collectExecutionAttachmentRequirements(state, plan) {
766
901
  environments: snapshot.environments,
767
902
  globalAssets: snapshot.globalAssets ?? state.synced.globalAssets
768
903
  };
769
- for (const slot of (0, import_core2.collectAttachmentSlots)(linkedSynced)) {
904
+ for (const slot of (0, import_core3.collectAttachmentSlots)(linkedSynced)) {
770
905
  const requiredBy = collectRequiredBy(linkedCollections.requests, slot.slotId);
771
906
  if (requiredBy.length === 0) continue;
772
907
  addRequirement(seen, {
@@ -819,7 +954,7 @@ function bodyReferencesSlot(body, slotId) {
819
954
  }
820
955
  async function hasExpectedFile(localPath, sha256) {
821
956
  try {
822
- const bytes = await import_node_fs6.promises.readFile(localPath);
957
+ const bytes = await import_node_fs7.promises.readFile(localPath);
823
958
  if (!sha256) return true;
824
959
  return sha256Hex(bytes) === sha256;
825
960
  } catch {
@@ -828,7 +963,7 @@ async function hasExpectedFile(localPath, sha256) {
828
963
  }
829
964
  async function fileSize(localPath) {
830
965
  try {
831
- return (await import_node_fs6.promises.stat(localPath)).size;
966
+ return (await import_node_fs7.promises.stat(localPath)).size;
832
967
  } catch {
833
968
  return 0;
834
969
  }
@@ -908,7 +1043,7 @@ function registerRunCommand(program) {
908
1043
  dir = resolved.dir;
909
1044
  if (resolved.fromRegistry) {
910
1045
  process.stderr.write(
911
- `${import_kleur4.default.dim("workspace")}: ${import_kleur4.default.cyan(resolved.name ?? resolved.id ?? "")} ${import_kleur4.default.dim(`(${dir})`)}
1046
+ `${import_kleur5.default.dim("workspace")}: ${import_kleur5.default.cyan(resolved.name ?? resolved.id ?? "")} ${import_kleur5.default.dim(`(${dir})`)}
912
1047
  `
913
1048
  );
914
1049
  }
@@ -929,7 +1064,7 @@ function registerRunCommand(program) {
929
1064
  fail(`no workspace found at ${dir} (expected workspace.synced.json)`);
930
1065
  return;
931
1066
  }
932
- const ref = (0, import_core3.resolvePlanRef)(state.synced, planRef);
1067
+ const ref = (0, import_core4.resolvePlanRef)(state.synced, planRef);
933
1068
  if (!ref.ok) {
934
1069
  fail(ref.error);
935
1070
  if (ref.available.length > 0) {
@@ -974,7 +1109,7 @@ function registerRunCommand(program) {
974
1109
  }
975
1110
  let result;
976
1111
  try {
977
- result = await (0, import_core3.runPlan)(prepared.state, ref.id, {
1112
+ result = await (0, import_core4.runPlan)(prepared.state, ref.id, {
978
1113
  withAssertions,
979
1114
  bail: opts.bail === true,
980
1115
  env: opts.env,
@@ -987,7 +1122,7 @@ function registerRunCommand(program) {
987
1122
  });
988
1123
  } catch (err) {
989
1124
  process.off("SIGINT", onSigint);
990
- if (err instanceof import_core3.PlanRunDeniedError) {
1125
+ if (err instanceof import_core4.PlanRunDeniedError) {
991
1126
  fail(err.message, 3, "denied");
992
1127
  return;
993
1128
  }
@@ -1026,7 +1161,7 @@ function resolveActor(local, override) {
1026
1161
  if (username) return { kind: "os", name: username };
1027
1162
  } catch {
1028
1163
  }
1029
- return import_core3.ANONYMOUS_ACTOR;
1164
+ return import_core4.ANONYMOUS_ACTOR;
1030
1165
  }
1031
1166
  function checkRunPermission(_ctx) {
1032
1167
  }
@@ -1037,17 +1172,17 @@ function formatHeader(plan, actor, withAssertions, opts) {
1037
1172
  opts.bail ? "bail" : null,
1038
1173
  opts.env ? `env=${opts.env}` : null
1039
1174
  ].filter((f) => f !== null);
1040
- return `${import_kleur4.default.bold("Plan")} ${plan.name} ${import_kleur4.default.dim(
1175
+ return `${import_kleur5.default.bold("Plan")} ${plan.name} ${import_kleur5.default.dim(
1041
1176
  `(${enabled}/${plan.steps.length} steps \xB7 ${flags.join(" \xB7 ")})`
1042
1177
  )}
1043
- ${import_kleur4.default.dim("Run by")} ${actor.name} ${import_kleur4.default.dim(`(${actor.kind})`)}
1178
+ ${import_kleur5.default.dim("Run by")} ${actor.name} ${import_kleur5.default.dim(`(${actor.kind})`)}
1044
1179
 
1045
1180
  `;
1046
1181
  }
1047
1182
  function formatAttachmentPreparation(summary) {
1048
1183
  const status = `${summary.downloaded} downloaded, ${summary.alreadyPresent} already local`;
1049
1184
  const lines = [
1050
- `${import_kleur4.default.bold("Attachments")} ${summary.total} required ${import_kleur4.default.dim(
1185
+ `${import_kleur5.default.bold("Attachments")} ${summary.total} required ${import_kleur5.default.dim(
1051
1186
  `(${status} - ${summary.cacheDir})`
1052
1187
  )}`
1053
1188
  ];
@@ -1055,7 +1190,7 @@ function formatAttachmentPreparation(summary) {
1055
1190
  const source = entry2.source === "linked-workspace" ? `linked:${entry2.linkedWorkspaceId ?? "unknown"}` : "workspace";
1056
1191
  const requiredBy = entry2.requiredBy.map((item) => item.requestName).join(", ");
1057
1192
  lines.push(
1058
- ` ${import_kleur4.default.dim("file")} ${entry2.filename} ${import_kleur4.default.dim(
1193
+ ` ${import_kleur5.default.dim("file")} ${entry2.filename} ${import_kleur5.default.dim(
1059
1194
  `${source} - ${requiredBy} - ${entry2.localPath}`
1060
1195
  )}`
1061
1196
  );
@@ -1068,31 +1203,31 @@ function formatStepLine(step) {
1068
1203
  const n = `${step.stepIndex + 1}.`.padEnd(3);
1069
1204
  const method = (step.requestMethod || "\u2014").padEnd(7);
1070
1205
  if (step.skipped) {
1071
- return ` ${import_kleur4.default.dim("\u2013")} ${import_kleur4.default.dim(n)} ${import_kleur4.default.dim(method)} ${import_kleur4.default.dim(
1206
+ return ` ${import_kleur5.default.dim("\u2013")} ${import_kleur5.default.dim(n)} ${import_kleur5.default.dim(method)} ${import_kleur5.default.dim(
1072
1207
  `${step.requestName} skipped`
1073
1208
  )}
1074
1209
  `;
1075
1210
  }
1076
- const mark = step.passed ? import_kleur4.default.green("\u2713") : import_kleur4.default.red("\u2717");
1211
+ const mark = step.passed ? import_kleur5.default.green("\u2713") : import_kleur5.default.red("\u2717");
1077
1212
  const status = step.result?.status != null ? String(step.result.status) : "\u2014";
1078
1213
  const duration = step.result ? `${step.result.durationMs}ms` : "";
1079
1214
  const name = step.requestName.padEnd(28);
1080
- let line = ` ${mark} ${n} ${method} ${name} ${status.padEnd(4)} ${import_kleur4.default.dim(duration)}`;
1215
+ let line = ` ${mark} ${n} ${method} ${name} ${status.padEnd(4)} ${import_kleur5.default.dim(duration)}`;
1081
1216
  if (step.assertionResults.length > 0) {
1082
1217
  const passed = step.assertionResults.filter((a) => a.passed).length;
1083
- line += ` ${import_kleur4.default.dim(`${passed}/${step.assertionResults.length} assertions`)}`;
1218
+ line += ` ${import_kleur5.default.dim(`${passed}/${step.assertionResults.length} assertions`)}`;
1084
1219
  }
1085
1220
  line += "\n";
1086
1221
  if (step.error) {
1087
- line += ` ${import_kleur4.default.red(step.error)}
1222
+ line += ` ${import_kleur5.default.red(step.error)}
1088
1223
  `;
1089
1224
  }
1090
1225
  for (const a of step.assertionResults) {
1091
- if (!a.passed) line += ` ${import_kleur4.default.red("\u2717")} ${a.detail ?? `${a.kind} ${a.op}`}
1226
+ if (!a.passed) line += ` ${import_kleur5.default.red("\u2717")} ${a.detail ?? `${a.kind} ${a.op}`}
1092
1227
  `;
1093
1228
  }
1094
1229
  if (step.missingVariables.length > 0) {
1095
- line += ` ${import_kleur4.default.yellow("\u26A0")} unresolved: ${step.missingVariables.map((v) => `{{${v}}}`).join(", ")}
1230
+ line += ` ${import_kleur5.default.yellow("\u26A0")} unresolved: ${step.missingVariables.map((v) => `{{${v}}}`).join(", ")}
1096
1231
  `;
1097
1232
  }
1098
1233
  return line;
@@ -1111,24 +1246,24 @@ function tally(result) {
1111
1246
  function formatSummary(result, saved, aborted) {
1112
1247
  if (result.steps.length === 0) {
1113
1248
  return `
1114
- ${import_kleur4.default.yellow("Plan has no steps.")}
1249
+ ${import_kleur5.default.yellow("Plan has no steps.")}
1115
1250
  `;
1116
1251
  }
1117
1252
  const { passed, failed, skipped } = tally(result);
1118
1253
  const parts = [
1119
- import_kleur4.default.green(`${passed} passed`),
1120
- failed > 0 ? import_kleur4.default.red(`${failed} failed`) : import_kleur4.default.dim(`${failed} failed`),
1121
- import_kleur4.default.dim(`${skipped} skipped`)
1254
+ import_kleur5.default.green(`${passed} passed`),
1255
+ failed > 0 ? import_kleur5.default.red(`${failed} failed`) : import_kleur5.default.dim(`${failed} failed`),
1256
+ import_kleur5.default.dim(`${skipped} skipped`)
1122
1257
  ];
1123
- const verdict = result.passed && !aborted ? import_kleur4.default.green("PASS") : import_kleur4.default.red("FAIL");
1258
+ const verdict = result.passed && !aborted ? import_kleur5.default.green("PASS") : import_kleur5.default.red("FAIL");
1124
1259
  let out = `
1125
- ${verdict} ${parts.join(import_kleur4.default.dim(" \xB7 "))} ${import_kleur4.default.dim(
1260
+ ${verdict} ${parts.join(import_kleur5.default.dim(" \xB7 "))} ${import_kleur5.default.dim(
1126
1261
  `\xB7 ${result.planRun.durationMs}ms`
1127
1262
  )}
1128
1263
  `;
1129
- if (aborted) out += `${import_kleur4.default.yellow("Run aborted before every step finished.")}
1264
+ if (aborted) out += `${import_kleur5.default.yellow("Run aborted before every step finished.")}
1130
1265
  `;
1131
- out += saved ? import_kleur4.default.dim("Plan run saved to workspace history.\n") : import_kleur4.default.dim("Plan run not saved (--no-save).\n");
1266
+ out += saved ? import_kleur5.default.dim("Plan run saved to workspace history.\n") : import_kleur5.default.dim("Plan run not saved (--no-save).\n");
1132
1267
  return out;
1133
1268
  }
1134
1269
  function buildJsonReport(workspace, planId, plan, actor, result, saved, aborted, attachments) {
@@ -1204,13 +1339,13 @@ ${cases.join("\n")}
1204
1339
  `;
1205
1340
  }
1206
1341
  function fail(message, code = 2, kind = "error") {
1207
- process.stderr.write(`${import_kleur4.default.red(kind)}: ${message}
1342
+ process.stderr.write(`${import_kleur5.default.red(kind)}: ${message}
1208
1343
  `);
1209
1344
  process.exitCode = code;
1210
1345
  }
1211
1346
 
1212
1347
  // src/commands/workspaces.ts
1213
- var import_kleur5 = __toESM(require("kleur"), 1);
1348
+ var import_kleur6 = __toESM(require("kleur"), 1);
1214
1349
  var import_registry2 = require("@apicircle/core/workspace/registry");
1215
1350
  function registerWorkspacesCommand(program) {
1216
1351
  const ws = program.command("workspaces").description("List, create, or switch the active workspace");
@@ -1222,15 +1357,15 @@ function registerWorkspacesCommand(program) {
1222
1357
  }
1223
1358
  if (registry.workspaces.length === 0) {
1224
1359
  process.stdout.write(
1225
- `${import_kleur5.default.dim("No workspaces registered yet at")} ${root}
1226
- ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle workspaces create <name>")} ${import_kleur5.default.dim(
1360
+ `${import_kleur6.default.dim("No workspaces registered yet at")} ${root}
1361
+ ${import_kleur6.default.dim("Run")} ${import_kleur6.default.cyan("apicircle workspaces create <name>")} ${import_kleur6.default.dim(
1227
1362
  "or open the desktop app to seed one."
1228
1363
  )}
1229
1364
  `
1230
1365
  );
1231
1366
  return;
1232
1367
  }
1233
- process.stdout.write(`${import_kleur5.default.dim("registry")}: ${root}
1368
+ process.stdout.write(`${import_kleur6.default.dim("registry")}: ${root}
1234
1369
 
1235
1370
  `);
1236
1371
  const rows = [...registry.workspaces].sort(
@@ -1239,22 +1374,22 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
1239
1374
  const nameWidth = Math.max(4, ...rows.map((r) => r.name.length));
1240
1375
  const idWidth = Math.max(2, ...rows.map((r) => r.id.length));
1241
1376
  process.stdout.write(
1242
- import_kleur5.default.bold(
1377
+ import_kleur6.default.bold(
1243
1378
  ` ${"".padEnd(1)} ${"NAME".padEnd(nameWidth)} ${"ID".padEnd(idWidth)} LAST OPENED
1244
1379
  `
1245
1380
  )
1246
1381
  );
1247
1382
  for (const w of rows) {
1248
- const mark = w.id === registry.activeWorkspaceId ? import_kleur5.default.green("\u25CF") : " ";
1383
+ const mark = w.id === registry.activeWorkspaceId ? import_kleur6.default.green("\u25CF") : " ";
1249
1384
  process.stdout.write(
1250
- ` ${mark} ${w.name.padEnd(nameWidth)} ${import_kleur5.default.dim(
1385
+ ` ${mark} ${w.name.padEnd(nameWidth)} ${import_kleur6.default.dim(
1251
1386
  w.id.padEnd(idWidth)
1252
- )} ${import_kleur5.default.dim(w.lastOpenedAt)}
1387
+ )} ${import_kleur6.default.dim(w.lastOpenedAt)}
1253
1388
  `
1254
1389
  );
1255
1390
  }
1256
1391
  process.stdout.write(`
1257
- ${import_kleur5.default.dim("\u25CF = active")}
1392
+ ${import_kleur6.default.dim("\u25CF = active")}
1258
1393
  `);
1259
1394
  });
1260
1395
  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) => {
@@ -1264,17 +1399,17 @@ ${import_kleur5.default.dim("\u25CF = active")}
1264
1399
  sampleRequest: opts.sample ?? false
1265
1400
  });
1266
1401
  process.stdout.write(
1267
- `${import_kleur5.default.green("created")} workspace ${import_kleur5.default.cyan(entry2.name)} ${import_kleur5.default.dim(`(${entry2.id})`)}
1402
+ `${import_kleur6.default.green("created")} workspace ${import_kleur6.default.cyan(entry2.name)} ${import_kleur6.default.dim(`(${entry2.id})`)}
1268
1403
  at ${dir}
1269
1404
  `
1270
1405
  );
1271
1406
  if (registry.activeWorkspaceId === entry2.id) {
1272
- process.stdout.write(`${import_kleur5.default.dim("marked as active")}
1407
+ process.stdout.write(`${import_kleur6.default.dim("marked as active")}
1273
1408
  `);
1274
1409
  }
1275
1410
  } catch (err) {
1276
1411
  process.stderr.write(
1277
- `${import_kleur5.default.red("error")}: ${err instanceof Error ? err.message : String(err)}
1412
+ `${import_kleur6.default.red("error")}: ${err instanceof Error ? err.message : String(err)}
1278
1413
  `
1279
1414
  );
1280
1415
  process.exit(2);
@@ -1285,8 +1420,8 @@ ${import_kleur5.default.dim("\u25CF = active")}
1285
1420
  const entry2 = (0, import_registry2.findWorkspaceEntry)(registry, selector);
1286
1421
  if (!entry2) {
1287
1422
  process.stderr.write(
1288
- `${import_kleur5.default.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1289
- ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle workspaces list")} ${import_kleur5.default.dim("to see what is available.")}
1423
+ `${import_kleur6.default.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1424
+ ${import_kleur6.default.dim("Run")} ${import_kleur6.default.cyan("apicircle workspaces list")} ${import_kleur6.default.dim("to see what is available.")}
1290
1425
  `
1291
1426
  );
1292
1427
  process.exit(2);
@@ -1295,7 +1430,7 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
1295
1430
  const next = await (0, import_registry2.setActiveWorkspace)(root, entry2.id);
1296
1431
  void next;
1297
1432
  process.stdout.write(
1298
- `${import_kleur5.default.green("active")} workspace is now ${import_kleur5.default.cyan(entry2.name)} ${import_kleur5.default.dim(`(${entry2.id})`)}
1433
+ `${import_kleur6.default.green("active")} workspace is now ${import_kleur6.default.cyan(entry2.name)} ${import_kleur6.default.dim(`(${entry2.id})`)}
1299
1434
  `
1300
1435
  );
1301
1436
  });
@@ -1308,7 +1443,7 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
1308
1443
  const entry2 = (0, import_registry2.findWorkspaceEntry)(registry, selector);
1309
1444
  if (!entry2) {
1310
1445
  process.stderr.write(
1311
- `${import_kleur5.default.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1446
+ `${import_kleur6.default.red("error")}: no workspace named "${selector}" in the registry at ${root}.
1312
1447
  `
1313
1448
  );
1314
1449
  process.exit(2);
@@ -1323,10 +1458,30 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
1323
1458
  // package.json
1324
1459
  var package_default = {
1325
1460
  name: "@apicircle/cli",
1326
- version: "1.0.6",
1461
+ version: "1.0.7",
1327
1462
  private: false,
1328
1463
  type: "module",
1329
1464
  description: "Command-line interface for API Circle Studio. Run mock servers, drive the MCP server, and import OpenAPI / Postman / Insomnia collections from any terminal.",
1465
+ keywords: [
1466
+ "apicircle",
1467
+ "api-circle",
1468
+ "cli",
1469
+ "command-line",
1470
+ "api",
1471
+ "api-client",
1472
+ "http",
1473
+ "http-client",
1474
+ "mock-server",
1475
+ "mcp",
1476
+ "mcp-server",
1477
+ "openapi",
1478
+ "swagger",
1479
+ "postman",
1480
+ "insomnia",
1481
+ "import",
1482
+ "workspace",
1483
+ "headless"
1484
+ ],
1330
1485
  license: "SEE LICENSE IN LICENSE",
1331
1486
  repository: {
1332
1487
  type: "git",
@@ -1405,6 +1560,7 @@ function buildProgram() {
1405
1560
  registerMockCommand(program);
1406
1561
  registerMcpCommand(program);
1407
1562
  registerImportCommand(program);
1563
+ registerExportCommand(program);
1408
1564
  registerRunCommand(program);
1409
1565
  registerWorkspacesCommand(program);
1410
1566
  return program;