@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/bin/cli.cjs +237 -72
- package/dist/bin/cli.cjs.map +1 -1
- package/dist/bin/cli.js +1 -1
- package/dist/{chunk-HNK3FW57.js → chunk-M5PWIZZF.js} +22 -2
- package/dist/chunk-M5PWIZZF.js.map +1 -0
- package/dist/index.cjs +224 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +202 -61
- package/dist/index.js.map +1 -1
- package/package.json +25 -5
- package/dist/chunk-HNK3FW57.js.map +0 -1
package/dist/bin/cli.cjs
CHANGED
|
@@ -37,10 +37,30 @@ var init_package = __esm({
|
|
|
37
37
|
"package.json"() {
|
|
38
38
|
package_default = {
|
|
39
39
|
name: "@apicircle/cli",
|
|
40
|
-
version: "1.0.
|
|
40
|
+
version: "1.0.8",
|
|
41
41
|
private: false,
|
|
42
42
|
type: "module",
|
|
43
43
|
description: "Command-line interface for API Circle Studio. Run mock servers, drive the MCP server, and import OpenAPI / Postman / Insomnia collections from any terminal.",
|
|
44
|
+
keywords: [
|
|
45
|
+
"apicircle",
|
|
46
|
+
"api-circle",
|
|
47
|
+
"cli",
|
|
48
|
+
"command-line",
|
|
49
|
+
"api",
|
|
50
|
+
"api-client",
|
|
51
|
+
"http",
|
|
52
|
+
"http-client",
|
|
53
|
+
"mock-server",
|
|
54
|
+
"mcp",
|
|
55
|
+
"mcp-server",
|
|
56
|
+
"openapi",
|
|
57
|
+
"swagger",
|
|
58
|
+
"postman",
|
|
59
|
+
"insomnia",
|
|
60
|
+
"import",
|
|
61
|
+
"workspace",
|
|
62
|
+
"headless"
|
|
63
|
+
],
|
|
44
64
|
license: "SEE LICENSE IN LICENSE",
|
|
45
65
|
repository: {
|
|
46
66
|
type: "git",
|
|
@@ -246,8 +266,8 @@ async function ensureWorkspace(dir) {
|
|
|
246
266
|
ui: {
|
|
247
267
|
activeRequestId: null,
|
|
248
268
|
sidebarExpandedSections: [],
|
|
249
|
-
themeId: "
|
|
250
|
-
fontId: "
|
|
269
|
+
themeId: "one-dark-pro",
|
|
270
|
+
fontId: "system-sans",
|
|
251
271
|
fontSizePercent: import_shared2.FONT_SIZE_PERCENT_DEFAULT
|
|
252
272
|
},
|
|
253
273
|
settings: { validateOnSend: true, monacoConsumesWheel: false },
|
|
@@ -461,8 +481,8 @@ function buildEmptyState(workspaceId, now, withSample) {
|
|
|
461
481
|
ui: {
|
|
462
482
|
activeRequestId: sample?.id ?? null,
|
|
463
483
|
sidebarExpandedSections: [],
|
|
464
|
-
themeId: "
|
|
465
|
-
fontId: "
|
|
484
|
+
themeId: "one-dark-pro",
|
|
485
|
+
fontId: "system-sans",
|
|
466
486
|
fontSizePercent: 100
|
|
467
487
|
},
|
|
468
488
|
settings: { validateOnSend: true, monacoConsumesWheel: false },
|
|
@@ -551,7 +571,10 @@ var init_mcp = __esm({
|
|
|
551
571
|
|
|
552
572
|
// src/commands/import.ts
|
|
553
573
|
function registerImportCommand(program) {
|
|
554
|
-
program.command("import").description("Import a spec into a workspace folder").argument(
|
|
574
|
+
program.command("import").description("Import a spec into a workspace folder").argument(
|
|
575
|
+
"<type>",
|
|
576
|
+
"Source type: openapi | postman | insomnia | curl | apicircle (the apicircle.folder/v1 envelope produced by `apicircle export folder`)"
|
|
577
|
+
).argument("<input>", "Path to a spec file, or `-` to read from stdin").option(
|
|
555
578
|
"--workspace-name <name-or-id>",
|
|
556
579
|
"Registry workspace name (case-insensitive) or id. Defaults to the active workspace."
|
|
557
580
|
).option(
|
|
@@ -641,6 +664,40 @@ function registerImportCommand(program) {
|
|
|
641
664
|
})
|
|
642
665
|
);
|
|
643
666
|
}
|
|
667
|
+
} else if (type === "apicircle") {
|
|
668
|
+
let parsedEnvelope;
|
|
669
|
+
try {
|
|
670
|
+
parsedEnvelope = (0, import_core.parseApicircleFolderExport)(raw);
|
|
671
|
+
} catch (err) {
|
|
672
|
+
process.stderr.write(
|
|
673
|
+
`${import_kleur3.default.red("error")}: ${err instanceof Error ? err.message : String(err)}
|
|
674
|
+
`
|
|
675
|
+
);
|
|
676
|
+
process.exit(2);
|
|
677
|
+
}
|
|
678
|
+
const out = (0, import_core.applyMutation)(
|
|
679
|
+
{ synced: nextSynced, local: nextLocal },
|
|
680
|
+
{ kind: "folder.import_apicircle", parsed: parsedEnvelope, parentFolderId: null }
|
|
681
|
+
);
|
|
682
|
+
nextSynced = out.next.synced;
|
|
683
|
+
nextLocal = out.next.local;
|
|
684
|
+
for (const r of parsedEnvelope.requests) created.push(r.id);
|
|
685
|
+
for (const w of parsedEnvelope.warnings) {
|
|
686
|
+
process.stderr.write(`${import_kleur3.default.yellow("warning")}: ${w}
|
|
687
|
+
`);
|
|
688
|
+
}
|
|
689
|
+
await (0, import_file_backed3.saveToFile)(dir, { synced: nextSynced, local: nextLocal });
|
|
690
|
+
process.stdout.write(
|
|
691
|
+
`${import_kleur3.default.green("imported")} folder "${parsedEnvelope.rootFolder.name}" (${parsedEnvelope.subfolders.length + 1} folders, ${parsedEnvelope.requests.length} requests) into ${dir}
|
|
692
|
+
`
|
|
693
|
+
);
|
|
694
|
+
if (parsedEnvelope.dependencies.files.length > 0) {
|
|
695
|
+
process.stderr.write(
|
|
696
|
+
`${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.
|
|
697
|
+
`
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
return;
|
|
644
701
|
} else {
|
|
645
702
|
process.stderr.write(`${import_kleur3.default.red("error")}: unknown type '${String(type)}'
|
|
646
703
|
`);
|
|
@@ -655,13 +712,13 @@ function registerImportCommand(program) {
|
|
|
655
712
|
}
|
|
656
713
|
async function readInput(p) {
|
|
657
714
|
if (p === "-") {
|
|
658
|
-
return new Promise((
|
|
715
|
+
return new Promise((resolve8, reject) => {
|
|
659
716
|
let data = "";
|
|
660
717
|
process.stdin.setEncoding("utf-8");
|
|
661
718
|
process.stdin.on("data", (chunk) => {
|
|
662
719
|
data += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
663
720
|
});
|
|
664
|
-
process.stdin.on("end", () =>
|
|
721
|
+
process.stdin.on("end", () => resolve8(data));
|
|
665
722
|
process.stdin.on("error", reject);
|
|
666
723
|
});
|
|
667
724
|
}
|
|
@@ -700,14 +757,120 @@ var init_import = __esm({
|
|
|
700
757
|
}
|
|
701
758
|
});
|
|
702
759
|
|
|
760
|
+
// src/commands/export.ts
|
|
761
|
+
function registerExportCommand(program) {
|
|
762
|
+
const exportCmd = program.command("export").description("Export workspace entities to portable JSON.");
|
|
763
|
+
exportCmd.command("folder").description("Export a folder (and its subtree) as an apicircle.folder/v1 JSON envelope.").argument(
|
|
764
|
+
"<folder>",
|
|
765
|
+
"Folder id, or display name (case-insensitive). Unique within the workspace."
|
|
766
|
+
).option("-o, --out <path>", "Write the JSON to this file. Defaults to stdout.").option(
|
|
767
|
+
"--include-credential <id>",
|
|
768
|
+
"Keep the credential field with this id (repeatable). Use --list-credentials to see ids.",
|
|
769
|
+
(value, prev = []) => [...prev, value],
|
|
770
|
+
[]
|
|
771
|
+
).option(
|
|
772
|
+
"--list-credentials",
|
|
773
|
+
"Print the detected credentials + their ids and exit without writing anything."
|
|
774
|
+
).option(
|
|
775
|
+
"--workspace-name <name-or-id>",
|
|
776
|
+
"Registry workspace name (case-insensitive) or id. Defaults to the active workspace."
|
|
777
|
+
).option(
|
|
778
|
+
"-w, --workspace-path <dir>",
|
|
779
|
+
"Filesystem directory containing workspace.synced.json (skips the registry)."
|
|
780
|
+
).action(async (folder, opts) => {
|
|
781
|
+
let dir;
|
|
782
|
+
try {
|
|
783
|
+
const resolved = await resolveWorkspace({
|
|
784
|
+
name: opts.workspaceName,
|
|
785
|
+
path: opts.workspacePath
|
|
786
|
+
});
|
|
787
|
+
dir = resolved.dir;
|
|
788
|
+
} catch (err) {
|
|
789
|
+
if (err instanceof WorkspaceResolutionError) {
|
|
790
|
+
process.stderr.write(`${import_kleur4.default.red("error")}: ${err.message}
|
|
791
|
+
`);
|
|
792
|
+
process.exit(2);
|
|
793
|
+
}
|
|
794
|
+
throw err;
|
|
795
|
+
}
|
|
796
|
+
const state = await ensureWorkspace(dir);
|
|
797
|
+
const folderId = resolveFolderId(state.synced.collections.folders, folder);
|
|
798
|
+
if (!folderId) {
|
|
799
|
+
process.stderr.write(`${import_kleur4.default.red("error")}: no folder matches "${folder}" in ${dir}
|
|
800
|
+
`);
|
|
801
|
+
process.exit(2);
|
|
802
|
+
}
|
|
803
|
+
const collected = (0, import_core2.collectFolderExport)({ synced: state.synced, folderId });
|
|
804
|
+
if (!collected) {
|
|
805
|
+
process.stderr.write(`${import_kleur4.default.red("error")}: folder "${folder}" no longer exists
|
|
806
|
+
`);
|
|
807
|
+
process.exit(2);
|
|
808
|
+
}
|
|
809
|
+
if (opts.listCredentials) {
|
|
810
|
+
if (collected.report.credentials.length === 0) {
|
|
811
|
+
process.stdout.write("No credential-bearing auth fields detected.\n");
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
for (const cred of collected.report.credentials) {
|
|
815
|
+
process.stdout.write(`${cred.id} ${cred.label} ${cred.ownerName}
|
|
816
|
+
`);
|
|
817
|
+
}
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
const includeIds = new Set(opts.includeCredential ?? []);
|
|
821
|
+
const envelope = (0, import_core2.redactFolderExportCredentials)(collected.envelope, includeIds);
|
|
822
|
+
const json = (0, import_core2.serializeFolderExport)(envelope);
|
|
823
|
+
if (opts.out) {
|
|
824
|
+
const outPath = path5.resolve(opts.out);
|
|
825
|
+
await import_node_fs5.promises.writeFile(outPath, json, "utf-8");
|
|
826
|
+
process.stderr.write(
|
|
827
|
+
`${import_kleur4.default.green("exported")} folder "${collected.report.folderName}" \u2192 ${outPath}
|
|
828
|
+
`
|
|
829
|
+
);
|
|
830
|
+
} else {
|
|
831
|
+
process.stdout.write(json);
|
|
832
|
+
process.stdout.write("\n");
|
|
833
|
+
process.stderr.write(
|
|
834
|
+
`${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)
|
|
835
|
+
`
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
if (!opts.out) {
|
|
839
|
+
process.stderr.write(
|
|
840
|
+
`${import_kleur4.default.dim("hint")}: save with .apicircle.json, e.g. ${(0, import_core2.suggestFolderExportFilename)(envelope)}
|
|
841
|
+
`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
function resolveFolderId(folders, query) {
|
|
847
|
+
if (folders[query]) return query;
|
|
848
|
+
const norm = query.trim().toLowerCase();
|
|
849
|
+
const matches = Object.values(folders).filter((f) => f.name.trim().toLowerCase() === norm);
|
|
850
|
+
if (matches.length === 1) return matches[0].id;
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
var import_node_fs5, path5, import_kleur4, import_core2;
|
|
854
|
+
var init_export = __esm({
|
|
855
|
+
"src/commands/export.ts"() {
|
|
856
|
+
"use strict";
|
|
857
|
+
import_node_fs5 = require("fs");
|
|
858
|
+
path5 = __toESM(require("path"), 1);
|
|
859
|
+
import_kleur4 = __toESM(require("kleur"), 1);
|
|
860
|
+
import_core2 = require("@apicircle/core");
|
|
861
|
+
init_loadWorkspace();
|
|
862
|
+
init_resolveWorkspace();
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
|
|
703
866
|
// src/util/secrets.ts
|
|
704
867
|
async function buildSecretsFromCli(options = {}) {
|
|
705
868
|
const env = options.env ?? process.env;
|
|
706
869
|
const prefix = options.envPrefix ?? DEFAULT_PREFIX;
|
|
707
870
|
const byId = {};
|
|
708
871
|
if (options.secretsFile) {
|
|
709
|
-
const resolved =
|
|
710
|
-
const raw = await
|
|
872
|
+
const resolved = path6.resolve(options.secretsFile);
|
|
873
|
+
const raw = await import_node_fs6.promises.readFile(resolved, "utf8");
|
|
711
874
|
const parsed = JSON.parse(raw);
|
|
712
875
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
713
876
|
throw new Error(
|
|
@@ -728,21 +891,21 @@ async function buildSecretsFromCli(options = {}) {
|
|
|
728
891
|
}
|
|
729
892
|
return { byId };
|
|
730
893
|
}
|
|
731
|
-
var
|
|
894
|
+
var path6, import_node_fs6, DEFAULT_PREFIX;
|
|
732
895
|
var init_secrets = __esm({
|
|
733
896
|
"src/util/secrets.ts"() {
|
|
734
897
|
"use strict";
|
|
735
|
-
|
|
736
|
-
|
|
898
|
+
path6 = __toESM(require("path"), 1);
|
|
899
|
+
import_node_fs6 = require("fs");
|
|
737
900
|
DEFAULT_PREFIX = "APICIRCLE_SECRET_";
|
|
738
901
|
}
|
|
739
902
|
});
|
|
740
903
|
|
|
741
904
|
// src/util/executionAttachments.ts
|
|
742
905
|
async function prepareExecutionAttachments(workspaceDir, state, plan) {
|
|
743
|
-
const cacheDir =
|
|
906
|
+
const cacheDir = path7.resolve(workspaceDir, ATTACHMENTS_DIR);
|
|
744
907
|
const requirements = collectExecutionAttachmentRequirements(state, plan);
|
|
745
|
-
await
|
|
908
|
+
await import_node_fs7.promises.mkdir(cacheDir, { recursive: true });
|
|
746
909
|
let downloaded = 0;
|
|
747
910
|
let alreadyPresent = 0;
|
|
748
911
|
let failed = 0;
|
|
@@ -751,7 +914,7 @@ async function prepareExecutionAttachments(workspaceDir, state, plan) {
|
|
|
751
914
|
};
|
|
752
915
|
const entries = [];
|
|
753
916
|
for (const requirement of requirements) {
|
|
754
|
-
const localPath =
|
|
917
|
+
const localPath = path7.join(cacheDir, encodeURIComponent(requirement.slotId));
|
|
755
918
|
const present = await hasExpectedFile(localPath, requirement.sha256);
|
|
756
919
|
if (present) {
|
|
757
920
|
alreadyPresent++;
|
|
@@ -768,7 +931,7 @@ async function prepareExecutionAttachments(workspaceDir, state, plan) {
|
|
|
768
931
|
`Attachment ${attachmentLabel(requirement)} failed checksum verification.`
|
|
769
932
|
);
|
|
770
933
|
}
|
|
771
|
-
await
|
|
934
|
+
await import_node_fs7.promises.writeFile(localPath, bytes, { mode: 384 });
|
|
772
935
|
downloaded++;
|
|
773
936
|
} catch (err) {
|
|
774
937
|
failed++;
|
|
@@ -825,7 +988,7 @@ function createFileAttachmentResolver(state) {
|
|
|
825
988
|
return async (slotId) => {
|
|
826
989
|
const meta = state.local.attachmentCache?.[slotId];
|
|
827
990
|
if (!meta) return null;
|
|
828
|
-
const bytes = await
|
|
991
|
+
const bytes = await import_node_fs7.promises.readFile(meta.localPath);
|
|
829
992
|
const view = new Uint8Array(bytes);
|
|
830
993
|
const body = view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
|
|
831
994
|
return {
|
|
@@ -845,7 +1008,7 @@ function collectExecutionAttachmentRequirements(state, plan) {
|
|
|
845
1008
|
)
|
|
846
1009
|
)
|
|
847
1010
|
} : state.synced.collections;
|
|
848
|
-
const workspaceSlots = (0,
|
|
1011
|
+
const workspaceSlots = (0, import_core3.collectAttachmentSlots)({ ...state.synced, collections: localCollections });
|
|
849
1012
|
for (const slot of workspaceSlots) {
|
|
850
1013
|
const requiredBy = collectRequiredBy(localCollections.requests, slot.slotId);
|
|
851
1014
|
if (requiredBy.length === 0) continue;
|
|
@@ -877,7 +1040,7 @@ function collectExecutionAttachmentRequirements(state, plan) {
|
|
|
877
1040
|
environments: snapshot.environments,
|
|
878
1041
|
globalAssets: snapshot.globalAssets ?? state.synced.globalAssets
|
|
879
1042
|
};
|
|
880
|
-
for (const slot of (0,
|
|
1043
|
+
for (const slot of (0, import_core3.collectAttachmentSlots)(linkedSynced)) {
|
|
881
1044
|
const requiredBy = collectRequiredBy(linkedCollections.requests, slot.slotId);
|
|
882
1045
|
if (requiredBy.length === 0) continue;
|
|
883
1046
|
addRequirement(seen, {
|
|
@@ -930,7 +1093,7 @@ function bodyReferencesSlot(body, slotId) {
|
|
|
930
1093
|
}
|
|
931
1094
|
async function hasExpectedFile(localPath, sha256) {
|
|
932
1095
|
try {
|
|
933
|
-
const bytes = await
|
|
1096
|
+
const bytes = await import_node_fs7.promises.readFile(localPath);
|
|
934
1097
|
if (!sha256) return true;
|
|
935
1098
|
return sha256Hex(bytes) === sha256;
|
|
936
1099
|
} catch {
|
|
@@ -939,7 +1102,7 @@ async function hasExpectedFile(localPath, sha256) {
|
|
|
939
1102
|
}
|
|
940
1103
|
async function fileSize(localPath) {
|
|
941
1104
|
try {
|
|
942
|
-
return (await
|
|
1105
|
+
return (await import_node_fs7.promises.stat(localPath)).size;
|
|
943
1106
|
} catch {
|
|
944
1107
|
return 0;
|
|
945
1108
|
}
|
|
@@ -998,15 +1161,15 @@ function sourceLabel(requirement) {
|
|
|
998
1161
|
function requiredByLabel(requirement) {
|
|
999
1162
|
return requirement.requiredBy.map((item) => item.requestName).join(", ") || "a request";
|
|
1000
1163
|
}
|
|
1001
|
-
var import_node_crypto,
|
|
1164
|
+
var import_node_crypto, import_node_fs7, path7, import_core3, ATTACHMENTS_DIR;
|
|
1002
1165
|
var init_executionAttachments = __esm({
|
|
1003
1166
|
"src/util/executionAttachments.ts"() {
|
|
1004
1167
|
"use strict";
|
|
1005
1168
|
import_node_crypto = require("crypto");
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
ATTACHMENTS_DIR =
|
|
1169
|
+
import_node_fs7 = require("fs");
|
|
1170
|
+
path7 = __toESM(require("path"), 1);
|
|
1171
|
+
import_core3 = require("@apicircle/core");
|
|
1172
|
+
ATTACHMENTS_DIR = path7.join(".apicircle", "attachments");
|
|
1010
1173
|
}
|
|
1011
1174
|
});
|
|
1012
1175
|
|
|
@@ -1029,7 +1192,7 @@ function registerRunCommand(program) {
|
|
|
1029
1192
|
dir = resolved.dir;
|
|
1030
1193
|
if (resolved.fromRegistry) {
|
|
1031
1194
|
process.stderr.write(
|
|
1032
|
-
`${
|
|
1195
|
+
`${import_kleur5.default.dim("workspace")}: ${import_kleur5.default.cyan(resolved.name ?? resolved.id ?? "")} ${import_kleur5.default.dim(`(${dir})`)}
|
|
1033
1196
|
`
|
|
1034
1197
|
);
|
|
1035
1198
|
}
|
|
@@ -1050,7 +1213,7 @@ function registerRunCommand(program) {
|
|
|
1050
1213
|
fail(`no workspace found at ${dir} (expected workspace.synced.json)`);
|
|
1051
1214
|
return;
|
|
1052
1215
|
}
|
|
1053
|
-
const ref = (0,
|
|
1216
|
+
const ref = (0, import_core4.resolvePlanRef)(state.synced, planRef);
|
|
1054
1217
|
if (!ref.ok) {
|
|
1055
1218
|
fail(ref.error);
|
|
1056
1219
|
if (ref.available.length > 0) {
|
|
@@ -1095,7 +1258,7 @@ function registerRunCommand(program) {
|
|
|
1095
1258
|
}
|
|
1096
1259
|
let result;
|
|
1097
1260
|
try {
|
|
1098
|
-
result = await (0,
|
|
1261
|
+
result = await (0, import_core4.runPlan)(prepared.state, ref.id, {
|
|
1099
1262
|
withAssertions,
|
|
1100
1263
|
bail: opts.bail === true,
|
|
1101
1264
|
env: opts.env,
|
|
@@ -1108,7 +1271,7 @@ function registerRunCommand(program) {
|
|
|
1108
1271
|
});
|
|
1109
1272
|
} catch (err) {
|
|
1110
1273
|
process.off("SIGINT", onSigint);
|
|
1111
|
-
if (err instanceof
|
|
1274
|
+
if (err instanceof import_core4.PlanRunDeniedError) {
|
|
1112
1275
|
fail(err.message, 3, "denied");
|
|
1113
1276
|
return;
|
|
1114
1277
|
}
|
|
@@ -1147,7 +1310,7 @@ function resolveActor(local, override) {
|
|
|
1147
1310
|
if (username) return { kind: "os", name: username };
|
|
1148
1311
|
} catch {
|
|
1149
1312
|
}
|
|
1150
|
-
return
|
|
1313
|
+
return import_core4.ANONYMOUS_ACTOR;
|
|
1151
1314
|
}
|
|
1152
1315
|
function checkRunPermission(_ctx) {
|
|
1153
1316
|
}
|
|
@@ -1158,17 +1321,17 @@ function formatHeader(plan, actor, withAssertions, opts) {
|
|
|
1158
1321
|
opts.bail ? "bail" : null,
|
|
1159
1322
|
opts.env ? `env=${opts.env}` : null
|
|
1160
1323
|
].filter((f) => f !== null);
|
|
1161
|
-
return `${
|
|
1324
|
+
return `${import_kleur5.default.bold("Plan")} ${plan.name} ${import_kleur5.default.dim(
|
|
1162
1325
|
`(${enabled}/${plan.steps.length} steps \xB7 ${flags.join(" \xB7 ")})`
|
|
1163
1326
|
)}
|
|
1164
|
-
${
|
|
1327
|
+
${import_kleur5.default.dim("Run by")} ${actor.name} ${import_kleur5.default.dim(`(${actor.kind})`)}
|
|
1165
1328
|
|
|
1166
1329
|
`;
|
|
1167
1330
|
}
|
|
1168
1331
|
function formatAttachmentPreparation(summary) {
|
|
1169
1332
|
const status = `${summary.downloaded} downloaded, ${summary.alreadyPresent} already local`;
|
|
1170
1333
|
const lines = [
|
|
1171
|
-
`${
|
|
1334
|
+
`${import_kleur5.default.bold("Attachments")} ${summary.total} required ${import_kleur5.default.dim(
|
|
1172
1335
|
`(${status} - ${summary.cacheDir})`
|
|
1173
1336
|
)}`
|
|
1174
1337
|
];
|
|
@@ -1176,7 +1339,7 @@ function formatAttachmentPreparation(summary) {
|
|
|
1176
1339
|
const source = entry3.source === "linked-workspace" ? `linked:${entry3.linkedWorkspaceId ?? "unknown"}` : "workspace";
|
|
1177
1340
|
const requiredBy = entry3.requiredBy.map((item) => item.requestName).join(", ");
|
|
1178
1341
|
lines.push(
|
|
1179
|
-
` ${
|
|
1342
|
+
` ${import_kleur5.default.dim("file")} ${entry3.filename} ${import_kleur5.default.dim(
|
|
1180
1343
|
`${source} - ${requiredBy} - ${entry3.localPath}`
|
|
1181
1344
|
)}`
|
|
1182
1345
|
);
|
|
@@ -1189,31 +1352,31 @@ function formatStepLine(step) {
|
|
|
1189
1352
|
const n = `${step.stepIndex + 1}.`.padEnd(3);
|
|
1190
1353
|
const method = (step.requestMethod || "\u2014").padEnd(7);
|
|
1191
1354
|
if (step.skipped) {
|
|
1192
|
-
return ` ${
|
|
1355
|
+
return ` ${import_kleur5.default.dim("\u2013")} ${import_kleur5.default.dim(n)} ${import_kleur5.default.dim(method)} ${import_kleur5.default.dim(
|
|
1193
1356
|
`${step.requestName} skipped`
|
|
1194
1357
|
)}
|
|
1195
1358
|
`;
|
|
1196
1359
|
}
|
|
1197
|
-
const mark = step.passed ?
|
|
1360
|
+
const mark = step.passed ? import_kleur5.default.green("\u2713") : import_kleur5.default.red("\u2717");
|
|
1198
1361
|
const status = step.result?.status != null ? String(step.result.status) : "\u2014";
|
|
1199
1362
|
const duration = step.result ? `${step.result.durationMs}ms` : "";
|
|
1200
1363
|
const name = step.requestName.padEnd(28);
|
|
1201
|
-
let line = ` ${mark} ${n} ${method} ${name} ${status.padEnd(4)} ${
|
|
1364
|
+
let line = ` ${mark} ${n} ${method} ${name} ${status.padEnd(4)} ${import_kleur5.default.dim(duration)}`;
|
|
1202
1365
|
if (step.assertionResults.length > 0) {
|
|
1203
1366
|
const passed = step.assertionResults.filter((a) => a.passed).length;
|
|
1204
|
-
line += ` ${
|
|
1367
|
+
line += ` ${import_kleur5.default.dim(`${passed}/${step.assertionResults.length} assertions`)}`;
|
|
1205
1368
|
}
|
|
1206
1369
|
line += "\n";
|
|
1207
1370
|
if (step.error) {
|
|
1208
|
-
line += ` ${
|
|
1371
|
+
line += ` ${import_kleur5.default.red(step.error)}
|
|
1209
1372
|
`;
|
|
1210
1373
|
}
|
|
1211
1374
|
for (const a of step.assertionResults) {
|
|
1212
|
-
if (!a.passed) line += ` ${
|
|
1375
|
+
if (!a.passed) line += ` ${import_kleur5.default.red("\u2717")} ${a.detail ?? `${a.kind} ${a.op}`}
|
|
1213
1376
|
`;
|
|
1214
1377
|
}
|
|
1215
1378
|
if (step.missingVariables.length > 0) {
|
|
1216
|
-
line += ` ${
|
|
1379
|
+
line += ` ${import_kleur5.default.yellow("\u26A0")} unresolved: ${step.missingVariables.map((v) => `{{${v}}}`).join(", ")}
|
|
1217
1380
|
`;
|
|
1218
1381
|
}
|
|
1219
1382
|
return line;
|
|
@@ -1232,24 +1395,24 @@ function tally(result) {
|
|
|
1232
1395
|
function formatSummary(result, saved, aborted) {
|
|
1233
1396
|
if (result.steps.length === 0) {
|
|
1234
1397
|
return `
|
|
1235
|
-
${
|
|
1398
|
+
${import_kleur5.default.yellow("Plan has no steps.")}
|
|
1236
1399
|
`;
|
|
1237
1400
|
}
|
|
1238
1401
|
const { passed, failed, skipped } = tally(result);
|
|
1239
1402
|
const parts = [
|
|
1240
|
-
|
|
1241
|
-
failed > 0 ?
|
|
1242
|
-
|
|
1403
|
+
import_kleur5.default.green(`${passed} passed`),
|
|
1404
|
+
failed > 0 ? import_kleur5.default.red(`${failed} failed`) : import_kleur5.default.dim(`${failed} failed`),
|
|
1405
|
+
import_kleur5.default.dim(`${skipped} skipped`)
|
|
1243
1406
|
];
|
|
1244
|
-
const verdict = result.passed && !aborted ?
|
|
1407
|
+
const verdict = result.passed && !aborted ? import_kleur5.default.green("PASS") : import_kleur5.default.red("FAIL");
|
|
1245
1408
|
let out = `
|
|
1246
|
-
${verdict} ${parts.join(
|
|
1409
|
+
${verdict} ${parts.join(import_kleur5.default.dim(" \xB7 "))} ${import_kleur5.default.dim(
|
|
1247
1410
|
`\xB7 ${result.planRun.durationMs}ms`
|
|
1248
1411
|
)}
|
|
1249
1412
|
`;
|
|
1250
|
-
if (aborted) out += `${
|
|
1413
|
+
if (aborted) out += `${import_kleur5.default.yellow("Run aborted before every step finished.")}
|
|
1251
1414
|
`;
|
|
1252
|
-
out += saved ?
|
|
1415
|
+
out += saved ? import_kleur5.default.dim("Plan run saved to workspace history.\n") : import_kleur5.default.dim("Plan run not saved (--no-save).\n");
|
|
1253
1416
|
return out;
|
|
1254
1417
|
}
|
|
1255
1418
|
function buildJsonReport(workspace, planId, plan, actor, result, saved, aborted, attachments) {
|
|
@@ -1325,17 +1488,17 @@ ${cases.join("\n")}
|
|
|
1325
1488
|
`;
|
|
1326
1489
|
}
|
|
1327
1490
|
function fail(message, code = 2, kind = "error") {
|
|
1328
|
-
process.stderr.write(`${
|
|
1491
|
+
process.stderr.write(`${import_kleur5.default.red(kind)}: ${message}
|
|
1329
1492
|
`);
|
|
1330
1493
|
process.exitCode = code;
|
|
1331
1494
|
}
|
|
1332
|
-
var os2,
|
|
1495
|
+
var os2, import_kleur5, import_core4, import_file_backed4, REPORTERS;
|
|
1333
1496
|
var init_run = __esm({
|
|
1334
1497
|
"src/commands/run.ts"() {
|
|
1335
1498
|
"use strict";
|
|
1336
1499
|
os2 = __toESM(require("os"), 1);
|
|
1337
|
-
|
|
1338
|
-
|
|
1500
|
+
import_kleur5 = __toESM(require("kleur"), 1);
|
|
1501
|
+
import_core4 = require("@apicircle/core");
|
|
1339
1502
|
import_file_backed4 = require("@apicircle/core/workspace/file-backed");
|
|
1340
1503
|
init_secrets();
|
|
1341
1504
|
init_resolveWorkspace();
|
|
@@ -1355,15 +1518,15 @@ function registerWorkspacesCommand(program) {
|
|
|
1355
1518
|
}
|
|
1356
1519
|
if (registry.workspaces.length === 0) {
|
|
1357
1520
|
process.stdout.write(
|
|
1358
|
-
`${
|
|
1359
|
-
${
|
|
1521
|
+
`${import_kleur6.default.dim("No workspaces registered yet at")} ${root}
|
|
1522
|
+
${import_kleur6.default.dim("Run")} ${import_kleur6.default.cyan("apicircle workspaces create <name>")} ${import_kleur6.default.dim(
|
|
1360
1523
|
"or open the desktop app to seed one."
|
|
1361
1524
|
)}
|
|
1362
1525
|
`
|
|
1363
1526
|
);
|
|
1364
1527
|
return;
|
|
1365
1528
|
}
|
|
1366
|
-
process.stdout.write(`${
|
|
1529
|
+
process.stdout.write(`${import_kleur6.default.dim("registry")}: ${root}
|
|
1367
1530
|
|
|
1368
1531
|
`);
|
|
1369
1532
|
const rows = [...registry.workspaces].sort(
|
|
@@ -1372,22 +1535,22 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
|
|
|
1372
1535
|
const nameWidth = Math.max(4, ...rows.map((r) => r.name.length));
|
|
1373
1536
|
const idWidth = Math.max(2, ...rows.map((r) => r.id.length));
|
|
1374
1537
|
process.stdout.write(
|
|
1375
|
-
|
|
1538
|
+
import_kleur6.default.bold(
|
|
1376
1539
|
` ${"".padEnd(1)} ${"NAME".padEnd(nameWidth)} ${"ID".padEnd(idWidth)} LAST OPENED
|
|
1377
1540
|
`
|
|
1378
1541
|
)
|
|
1379
1542
|
);
|
|
1380
1543
|
for (const w of rows) {
|
|
1381
|
-
const mark = w.id === registry.activeWorkspaceId ?
|
|
1544
|
+
const mark = w.id === registry.activeWorkspaceId ? import_kleur6.default.green("\u25CF") : " ";
|
|
1382
1545
|
process.stdout.write(
|
|
1383
|
-
` ${mark} ${w.name.padEnd(nameWidth)} ${
|
|
1546
|
+
` ${mark} ${w.name.padEnd(nameWidth)} ${import_kleur6.default.dim(
|
|
1384
1547
|
w.id.padEnd(idWidth)
|
|
1385
|
-
)} ${
|
|
1548
|
+
)} ${import_kleur6.default.dim(w.lastOpenedAt)}
|
|
1386
1549
|
`
|
|
1387
1550
|
);
|
|
1388
1551
|
}
|
|
1389
1552
|
process.stdout.write(`
|
|
1390
|
-
${
|
|
1553
|
+
${import_kleur6.default.dim("\u25CF = active")}
|
|
1391
1554
|
`);
|
|
1392
1555
|
});
|
|
1393
1556
|
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) => {
|
|
@@ -1397,17 +1560,17 @@ ${import_kleur5.default.dim("\u25CF = active")}
|
|
|
1397
1560
|
sampleRequest: opts.sample ?? false
|
|
1398
1561
|
});
|
|
1399
1562
|
process.stdout.write(
|
|
1400
|
-
`${
|
|
1563
|
+
`${import_kleur6.default.green("created")} workspace ${import_kleur6.default.cyan(entry3.name)} ${import_kleur6.default.dim(`(${entry3.id})`)}
|
|
1401
1564
|
at ${dir}
|
|
1402
1565
|
`
|
|
1403
1566
|
);
|
|
1404
1567
|
if (registry.activeWorkspaceId === entry3.id) {
|
|
1405
|
-
process.stdout.write(`${
|
|
1568
|
+
process.stdout.write(`${import_kleur6.default.dim("marked as active")}
|
|
1406
1569
|
`);
|
|
1407
1570
|
}
|
|
1408
1571
|
} catch (err) {
|
|
1409
1572
|
process.stderr.write(
|
|
1410
|
-
`${
|
|
1573
|
+
`${import_kleur6.default.red("error")}: ${err instanceof Error ? err.message : String(err)}
|
|
1411
1574
|
`
|
|
1412
1575
|
);
|
|
1413
1576
|
process.exit(2);
|
|
@@ -1418,8 +1581,8 @@ ${import_kleur5.default.dim("\u25CF = active")}
|
|
|
1418
1581
|
const entry3 = (0, import_registry2.findWorkspaceEntry)(registry, selector);
|
|
1419
1582
|
if (!entry3) {
|
|
1420
1583
|
process.stderr.write(
|
|
1421
|
-
`${
|
|
1422
|
-
${
|
|
1584
|
+
`${import_kleur6.default.red("error")}: no workspace named "${selector}" in the registry at ${root}.
|
|
1585
|
+
${import_kleur6.default.dim("Run")} ${import_kleur6.default.cyan("apicircle workspaces list")} ${import_kleur6.default.dim("to see what is available.")}
|
|
1423
1586
|
`
|
|
1424
1587
|
);
|
|
1425
1588
|
process.exit(2);
|
|
@@ -1428,7 +1591,7 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
|
|
|
1428
1591
|
const next = await (0, import_registry2.setActiveWorkspace)(root, entry3.id);
|
|
1429
1592
|
void next;
|
|
1430
1593
|
process.stdout.write(
|
|
1431
|
-
`${
|
|
1594
|
+
`${import_kleur6.default.green("active")} workspace is now ${import_kleur6.default.cyan(entry3.name)} ${import_kleur6.default.dim(`(${entry3.id})`)}
|
|
1432
1595
|
`
|
|
1433
1596
|
);
|
|
1434
1597
|
});
|
|
@@ -1441,7 +1604,7 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
|
|
|
1441
1604
|
const entry3 = (0, import_registry2.findWorkspaceEntry)(registry, selector);
|
|
1442
1605
|
if (!entry3) {
|
|
1443
1606
|
process.stderr.write(
|
|
1444
|
-
`${
|
|
1607
|
+
`${import_kleur6.default.red("error")}: no workspace named "${selector}" in the registry at ${root}.
|
|
1445
1608
|
`
|
|
1446
1609
|
);
|
|
1447
1610
|
process.exit(2);
|
|
@@ -1452,11 +1615,11 @@ ${import_kleur5.default.dim("Run")} ${import_kleur5.default.cyan("apicircle work
|
|
|
1452
1615
|
process.stdout.write(workspaceDirFor2(root, entry3.id) + "\n");
|
|
1453
1616
|
});
|
|
1454
1617
|
}
|
|
1455
|
-
var
|
|
1618
|
+
var import_kleur6, import_registry2;
|
|
1456
1619
|
var init_workspaces = __esm({
|
|
1457
1620
|
"src/commands/workspaces.ts"() {
|
|
1458
1621
|
"use strict";
|
|
1459
|
-
|
|
1622
|
+
import_kleur6 = __toESM(require("kleur"), 1);
|
|
1460
1623
|
init_resolveWorkspace();
|
|
1461
1624
|
import_registry2 = require("@apicircle/core/workspace/registry");
|
|
1462
1625
|
}
|
|
@@ -1474,6 +1637,7 @@ function buildProgram() {
|
|
|
1474
1637
|
registerMockCommand(program);
|
|
1475
1638
|
registerMcpCommand(program);
|
|
1476
1639
|
registerImportCommand(program);
|
|
1640
|
+
registerExportCommand(program);
|
|
1477
1641
|
registerRunCommand(program);
|
|
1478
1642
|
registerWorkspacesCommand(program);
|
|
1479
1643
|
return program;
|
|
@@ -1489,6 +1653,7 @@ var init_src = __esm({
|
|
|
1489
1653
|
init_mock();
|
|
1490
1654
|
init_mcp();
|
|
1491
1655
|
init_import();
|
|
1656
|
+
init_export();
|
|
1492
1657
|
init_run();
|
|
1493
1658
|
init_workspaces();
|
|
1494
1659
|
init_packageVersion();
|