@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.cjs +237 -72
- package/dist/bin/cli.cjs.map +1 -1
- package/dist/bin/cli.js +1 -1
- package/dist/{chunk-HNK3FW57.js → chunk-TOUXWWC5.js} +22 -2
- package/dist/chunk-TOUXWWC5.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.js
CHANGED
|
@@ -3,10 +3,30 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@apicircle/cli",
|
|
6
|
-
version: "1.0.
|
|
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-
|
|
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: "
|
|
168
|
-
fontId: "
|
|
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: "
|
|
390
|
-
fontId: "
|
|
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(
|
|
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((
|
|
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", () =>
|
|
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
|
|
590
|
-
var
|
|
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
|
|
595
|
-
var
|
|
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 =
|
|
603
|
-
const raw = await
|
|
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
|
|
628
|
-
var
|
|
629
|
-
var
|
|
630
|
-
var ATTACHMENTS_DIR =
|
|
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 =
|
|
767
|
+
const cacheDir = path7.resolve(workspaceDir, ATTACHMENTS_DIR);
|
|
633
768
|
const requirements = collectExecutionAttachmentRequirements(state, plan);
|
|
634
|
-
await
|
|
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 =
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
`${
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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 `${
|
|
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
|
-
${
|
|
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
|
-
`${
|
|
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
|
-
` ${
|
|
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 ` ${
|
|
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 ?
|
|
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)} ${
|
|
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 += ` ${
|
|
1218
|
+
line += ` ${import_kleur5.default.dim(`${passed}/${step.assertionResults.length} assertions`)}`;
|
|
1084
1219
|
}
|
|
1085
1220
|
line += "\n";
|
|
1086
1221
|
if (step.error) {
|
|
1087
|
-
line += ` ${
|
|
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 += ` ${
|
|
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 += ` ${
|
|
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
|
-
${
|
|
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
|
-
|
|
1120
|
-
failed > 0 ?
|
|
1121
|
-
|
|
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 ?
|
|
1258
|
+
const verdict = result.passed && !aborted ? import_kleur5.default.green("PASS") : import_kleur5.default.red("FAIL");
|
|
1124
1259
|
let out = `
|
|
1125
|
-
${verdict} ${parts.join(
|
|
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 += `${
|
|
1264
|
+
if (aborted) out += `${import_kleur5.default.yellow("Run aborted before every step finished.")}
|
|
1130
1265
|
`;
|
|
1131
|
-
out += saved ?
|
|
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(`${
|
|
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
|
|
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
|
-
`${
|
|
1226
|
-
${
|
|
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(`${
|
|
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
|
-
|
|
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 ?
|
|
1383
|
+
const mark = w.id === registry.activeWorkspaceId ? import_kleur6.default.green("\u25CF") : " ";
|
|
1249
1384
|
process.stdout.write(
|
|
1250
|
-
` ${mark} ${w.name.padEnd(nameWidth)} ${
|
|
1385
|
+
` ${mark} ${w.name.padEnd(nameWidth)} ${import_kleur6.default.dim(
|
|
1251
1386
|
w.id.padEnd(idWidth)
|
|
1252
|
-
)} ${
|
|
1387
|
+
)} ${import_kleur6.default.dim(w.lastOpenedAt)}
|
|
1253
1388
|
`
|
|
1254
1389
|
);
|
|
1255
1390
|
}
|
|
1256
1391
|
process.stdout.write(`
|
|
1257
|
-
${
|
|
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
|
-
`${
|
|
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(`${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
1289
|
-
${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
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.
|
|
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;
|