@mistweaverco/kulala-cli 0.8.3 → 0.10.0
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/README.md +0 -2
- package/dist/cli.cjs +621 -331
- package/dist/install-backend.cjs +53 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -229,8 +229,6 @@ docker buildx build --push \
|
|
|
229
229
|
-f Dockerfile .
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
234
232
|
[logo]: https://raw.githubusercontent.com/mistweaverco/kulala-cli/main/assets/logo.svg
|
|
235
233
|
[badge-npm]: https://img.shields.io/npm/v/@mistweaverco/kulala-cli?style=for-the-badge
|
|
236
234
|
[link-npm]: https://www.npmjs.com/package/@mistweaverco/kulala-cli
|
package/dist/cli.cjs
CHANGED
|
@@ -41,12 +41,12 @@ fs = __toESM(fs, 1);
|
|
|
41
41
|
let path = require("path");
|
|
42
42
|
path = __toESM(path, 1);
|
|
43
43
|
let stream_promises = require("stream/promises");
|
|
44
|
-
let node_async_hooks = require("node:async_hooks");
|
|
45
44
|
let node_readline = require("node:readline");
|
|
46
45
|
node_readline = __toESM(node_readline, 1);
|
|
46
|
+
let node_async_hooks = require("node:async_hooks");
|
|
47
47
|
var package_default = {
|
|
48
48
|
name: "@mistweaverco/kulala-cli",
|
|
49
|
-
version: "0.
|
|
49
|
+
version: "0.10.0",
|
|
50
50
|
repository: {
|
|
51
51
|
"type": "git",
|
|
52
52
|
"url": "https://github.com/mistweaverco/kulala-cli"
|
|
@@ -3473,8 +3473,62 @@ function fileWalker(inputPath, extensions) {
|
|
|
3473
3473
|
return filePaths;
|
|
3474
3474
|
}
|
|
3475
3475
|
//#endregion
|
|
3476
|
+
//#region src/lib/spinner.ts
|
|
3477
|
+
var SPINNER_FRAMES = [
|
|
3478
|
+
"⠋",
|
|
3479
|
+
"⠙",
|
|
3480
|
+
"⠹",
|
|
3481
|
+
"⠸",
|
|
3482
|
+
"⠼",
|
|
3483
|
+
"⠴",
|
|
3484
|
+
"⠦",
|
|
3485
|
+
"⠧",
|
|
3486
|
+
"⠇",
|
|
3487
|
+
"⠏"
|
|
3488
|
+
];
|
|
3489
|
+
function isInteractiveTerminal() {
|
|
3490
|
+
return process.stderr.isTTY === true;
|
|
3491
|
+
}
|
|
3492
|
+
function createSpinner() {
|
|
3493
|
+
let timer;
|
|
3494
|
+
let frame = 0;
|
|
3495
|
+
let message = "";
|
|
3496
|
+
const stop = () => {
|
|
3497
|
+
if (timer) {
|
|
3498
|
+
clearInterval(timer);
|
|
3499
|
+
timer = void 0;
|
|
3500
|
+
}
|
|
3501
|
+
if (isInteractiveTerminal()) process.stderr.write("\r\x1B[K");
|
|
3502
|
+
};
|
|
3503
|
+
return {
|
|
3504
|
+
start(msg) {
|
|
3505
|
+
message = msg;
|
|
3506
|
+
if (!isInteractiveTerminal()) {
|
|
3507
|
+
console.error(message);
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
const render = () => {
|
|
3511
|
+
process.stderr.write(`\r${SPINNER_FRAMES[frame]} ${message}`);
|
|
3512
|
+
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
3513
|
+
};
|
|
3514
|
+
render();
|
|
3515
|
+
timer = setInterval(render, 80);
|
|
3516
|
+
},
|
|
3517
|
+
stop
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
async function withSpinner(message, fn) {
|
|
3521
|
+
const spinner = createSpinner();
|
|
3522
|
+
spinner.start(message);
|
|
3523
|
+
try {
|
|
3524
|
+
return await fn();
|
|
3525
|
+
} finally {
|
|
3526
|
+
spinner.stop();
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
//#endregion
|
|
3476
3530
|
//#region src/versions/backend.ts
|
|
3477
|
-
var KULALA_CORE_VERSION = "0.24.
|
|
3531
|
+
var KULALA_CORE_VERSION = "0.24.4";
|
|
3478
3532
|
//#endregion
|
|
3479
3533
|
//#region src/lib/downloader/index.ts
|
|
3480
3534
|
var BINARY_NAME = "kulala-core";
|
|
@@ -3517,52 +3571,18 @@ function versionMatches() {
|
|
|
3517
3571
|
function makeExecutable(filePath) {
|
|
3518
3572
|
if (process.platform !== "win32") (0, fs.chmodSync)(filePath, 493);
|
|
3519
3573
|
}
|
|
3520
|
-
var SPINNER_FRAMES = [
|
|
3521
|
-
"⠋",
|
|
3522
|
-
"⠙",
|
|
3523
|
-
"⠹",
|
|
3524
|
-
"⠸",
|
|
3525
|
-
"⠼",
|
|
3526
|
-
"⠴",
|
|
3527
|
-
"⠦",
|
|
3528
|
-
"⠧",
|
|
3529
|
-
"⠇",
|
|
3530
|
-
"⠏"
|
|
3531
|
-
];
|
|
3532
|
-
function isInteractiveTerminal() {
|
|
3533
|
-
return process.stderr.isTTY === true;
|
|
3534
|
-
}
|
|
3535
3574
|
function createDownloadProgress() {
|
|
3536
|
-
|
|
3537
|
-
let frame = 0;
|
|
3538
|
-
let message = "";
|
|
3539
|
-
const clearLine = () => {
|
|
3540
|
-
if (timer) {
|
|
3541
|
-
clearInterval(timer);
|
|
3542
|
-
timer = void 0;
|
|
3543
|
-
}
|
|
3544
|
-
if (isInteractiveTerminal()) process.stderr.write("\r\x1B[K");
|
|
3545
|
-
};
|
|
3575
|
+
const spinner = createSpinner();
|
|
3546
3576
|
return {
|
|
3547
|
-
start(
|
|
3548
|
-
message
|
|
3549
|
-
if (!isInteractiveTerminal()) {
|
|
3550
|
-
console.error(message);
|
|
3551
|
-
return;
|
|
3552
|
-
}
|
|
3553
|
-
const render = () => {
|
|
3554
|
-
process.stderr.write(`\r${SPINNER_FRAMES[frame]} ${message}`);
|
|
3555
|
-
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
3556
|
-
};
|
|
3557
|
-
render();
|
|
3558
|
-
timer = setInterval(render, 80);
|
|
3577
|
+
start(message) {
|
|
3578
|
+
spinner.start(message);
|
|
3559
3579
|
},
|
|
3560
|
-
succeed(
|
|
3561
|
-
|
|
3562
|
-
console.error(
|
|
3580
|
+
succeed(message) {
|
|
3581
|
+
spinner.stop();
|
|
3582
|
+
console.error(message);
|
|
3563
3583
|
},
|
|
3564
3584
|
fail() {
|
|
3565
|
-
|
|
3585
|
+
spinner.stop();
|
|
3566
3586
|
}
|
|
3567
3587
|
};
|
|
3568
3588
|
}
|
|
@@ -3632,63 +3652,8 @@ var downloader = {
|
|
|
3632
3652
|
tryInstallBackend
|
|
3633
3653
|
};
|
|
3634
3654
|
//#endregion
|
|
3635
|
-
//#region src/lib/
|
|
3636
|
-
var
|
|
3637
|
-
async function executablePath() {
|
|
3638
|
-
if (!cachedExecutable) cachedExecutable = await downloader.ensureInstalled();
|
|
3639
|
-
if (!cachedExecutable) throw new Error("kulala-core executable not resolved");
|
|
3640
|
-
return cachedExecutable;
|
|
3641
|
-
}
|
|
3642
|
-
function invoke(payload, options = {}) {
|
|
3643
|
-
const exe = cachedExecutable;
|
|
3644
|
-
if (!exe) throw new Error("kulala-core executable not resolved");
|
|
3645
|
-
const result = (0, node_child_process.spawnSync)(exe, [], {
|
|
3646
|
-
input: `${JSON.stringify(payload)}\n`,
|
|
3647
|
-
encoding: "utf-8",
|
|
3648
|
-
maxBuffer: 50 * 1024 * 1024,
|
|
3649
|
-
cwd: options.cwd,
|
|
3650
|
-
env: process.env
|
|
3651
|
-
});
|
|
3652
|
-
if (result.error) throw result.error;
|
|
3653
|
-
if (result.status !== 0) throw new Error(result.stderr?.trim() || `kulala-core exited with code ${result.status ?? "unknown"}`);
|
|
3654
|
-
const stdout = result.stdout?.trim();
|
|
3655
|
-
if (!stdout) throw new Error("kulala-core returned empty output");
|
|
3656
|
-
return JSON.parse(stdout);
|
|
3657
|
-
}
|
|
3658
|
-
async function runHttp(options, invokeOptions = {}) {
|
|
3659
|
-
await executablePath();
|
|
3660
|
-
return invoke({
|
|
3661
|
-
action: "run",
|
|
3662
|
-
content: options.content,
|
|
3663
|
-
filepath: options.filepath,
|
|
3664
|
-
env: options.env,
|
|
3665
|
-
limit: options.limit,
|
|
3666
|
-
haltOnError: options.haltOnError
|
|
3667
|
-
}, invokeOptions);
|
|
3668
|
-
}
|
|
3669
|
-
async function environments(options = {}, invokeOptions = {}) {
|
|
3670
|
-
await executablePath();
|
|
3671
|
-
return invoke({
|
|
3672
|
-
action: "environments",
|
|
3673
|
-
cwd: options.cwd,
|
|
3674
|
-
filepath: options.filepath
|
|
3675
|
-
}, invokeOptions);
|
|
3676
|
-
}
|
|
3677
|
-
async function curl$1(options, invokeOptions = {}) {
|
|
3678
|
-
await executablePath();
|
|
3679
|
-
return invoke({
|
|
3680
|
-
action: "curl",
|
|
3681
|
-
argv: options.argv
|
|
3682
|
-
}, invokeOptions);
|
|
3683
|
-
}
|
|
3684
|
-
var kulalaCore = {
|
|
3685
|
-
runHttp,
|
|
3686
|
-
environments,
|
|
3687
|
-
curl: curl$1
|
|
3688
|
-
};
|
|
3689
|
-
//#endregion
|
|
3690
|
-
//#region node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js
|
|
3691
|
-
var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
3655
|
+
//#region src/lib/output/shared.ts
|
|
3656
|
+
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
3692
3657
|
var p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
3693
3658
|
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
3694
3659
|
var formatter = (open, close, replace = open) => (input) => {
|
|
@@ -3753,7 +3718,303 @@ var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
3753
3718
|
};
|
|
3754
3719
|
module.exports = createColors();
|
|
3755
3720
|
module.exports.createColors = createColors;
|
|
3756
|
-
}));
|
|
3721
|
+
})))(), 1);
|
|
3722
|
+
function isPromptResponse(item) {
|
|
3723
|
+
if ("prompt" in item && item.prompt === true) return true;
|
|
3724
|
+
const maybe = item;
|
|
3725
|
+
return Boolean(maybe.promptId && maybe.promptType);
|
|
3726
|
+
}
|
|
3727
|
+
function findFirstPromptItem(wrapper) {
|
|
3728
|
+
return (Array.isArray(wrapper) ? wrapper : wrapper.data).find((item) => isPromptResponse(item));
|
|
3729
|
+
}
|
|
3730
|
+
function isSkippedResponse(item) {
|
|
3731
|
+
return "skipped" in item && item.skipped === true;
|
|
3732
|
+
}
|
|
3733
|
+
function isWebSocketResponse(item) {
|
|
3734
|
+
return "protocol" in item && item.protocol === "websocket";
|
|
3735
|
+
}
|
|
3736
|
+
function isErrorResponse(item) {
|
|
3737
|
+
return item.success === false && !isPromptResponse(item);
|
|
3738
|
+
}
|
|
3739
|
+
function isSuccessResponse(item) {
|
|
3740
|
+
return item.success === true && !isSkippedResponse(item) && !isWebSocketResponse(item);
|
|
3741
|
+
}
|
|
3742
|
+
function responseBodyText(body) {
|
|
3743
|
+
if (!body) return "";
|
|
3744
|
+
if (body.type === "json") return body.formatted ?? JSON.stringify(body.content, null, 2);
|
|
3745
|
+
if (body.type === "binary") return "";
|
|
3746
|
+
return body.content;
|
|
3747
|
+
}
|
|
3748
|
+
function responseBodyLanguage(body) {
|
|
3749
|
+
if (!body) return "text";
|
|
3750
|
+
if (body.type === "json") return "json";
|
|
3751
|
+
if (body.type === "binary") return "text";
|
|
3752
|
+
const mediaType = body.mediaType?.toLowerCase() ?? "";
|
|
3753
|
+
if (mediaType.includes("json")) return "json";
|
|
3754
|
+
if (mediaType.includes("xml")) return "xml";
|
|
3755
|
+
if (mediaType.includes("html")) return "html";
|
|
3756
|
+
if (mediaType.includes("javascript") || mediaType.includes("ecmascript")) return "javascript";
|
|
3757
|
+
if (mediaType.includes("graphql")) return "graphql";
|
|
3758
|
+
return "text";
|
|
3759
|
+
}
|
|
3760
|
+
function formatMs(ms) {
|
|
3761
|
+
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
3762
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
3763
|
+
}
|
|
3764
|
+
function escapeCell(value) {
|
|
3765
|
+
return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
3766
|
+
}
|
|
3767
|
+
function mdTable(rows) {
|
|
3768
|
+
if (rows.length === 0) return "";
|
|
3769
|
+
const header = rows[0];
|
|
3770
|
+
const separator = header.map(() => "---");
|
|
3771
|
+
const body = rows.slice(1);
|
|
3772
|
+
const formatRow = (row) => `| ${row.join(" | ")} |`;
|
|
3773
|
+
return [
|
|
3774
|
+
formatRow(header),
|
|
3775
|
+
formatRow(separator),
|
|
3776
|
+
...body.map(formatRow)
|
|
3777
|
+
].join("\n");
|
|
3778
|
+
}
|
|
3779
|
+
function isStructuredScriptLine(line) {
|
|
3780
|
+
return (line.kind === "test" || line.kind === "assert") && (line.status === "pass" || line.status === "fail");
|
|
3781
|
+
}
|
|
3782
|
+
function finalizeOpenTest(openTest, tests) {
|
|
3783
|
+
if (!openTest) return;
|
|
3784
|
+
if (!tests.includes(openTest)) tests.push(openTest);
|
|
3785
|
+
}
|
|
3786
|
+
function assertMessageAlreadyShown(message, tests) {
|
|
3787
|
+
return tests.some((test) => test.error === message || test.asserts.some((assert) => assert.message === message));
|
|
3788
|
+
}
|
|
3789
|
+
function parseAssertionTree(lines) {
|
|
3790
|
+
const tests = [];
|
|
3791
|
+
const standaloneAsserts = [];
|
|
3792
|
+
let openTest = null;
|
|
3793
|
+
const hasStructuredOutput = (lines ?? []).some((line) => line.kind === "assert" || line.kind === "test");
|
|
3794
|
+
for (const line of lines ?? []) {
|
|
3795
|
+
if (line.kind === "assert") {
|
|
3796
|
+
const assert = {
|
|
3797
|
+
pass: line.status === "pass",
|
|
3798
|
+
message: line.message
|
|
3799
|
+
};
|
|
3800
|
+
if (line.testName) {
|
|
3801
|
+
if (!openTest || openTest.name !== line.testName) {
|
|
3802
|
+
finalizeOpenTest(openTest, tests);
|
|
3803
|
+
openTest = {
|
|
3804
|
+
name: line.testName,
|
|
3805
|
+
pass: true,
|
|
3806
|
+
asserts: []
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
3809
|
+
openTest.asserts.push(assert);
|
|
3810
|
+
if (!assert.pass) openTest.pass = false;
|
|
3811
|
+
} else {
|
|
3812
|
+
finalizeOpenTest(openTest, tests);
|
|
3813
|
+
openTest = null;
|
|
3814
|
+
standaloneAsserts.push(assert);
|
|
3815
|
+
}
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
if (line.kind === "test") {
|
|
3819
|
+
const parsed = {
|
|
3820
|
+
pass: line.status === "pass",
|
|
3821
|
+
name: line.testName,
|
|
3822
|
+
error: line.status === "fail" && line.level === "error" ? line.message : void 0
|
|
3823
|
+
};
|
|
3824
|
+
if (openTest && openTest.name === parsed.name) {
|
|
3825
|
+
openTest.pass = parsed.pass;
|
|
3826
|
+
openTest.error = parsed.error;
|
|
3827
|
+
if (!parsed.pass && parsed.error && openTest.asserts.length === 0) openTest.asserts.push({
|
|
3828
|
+
pass: false,
|
|
3829
|
+
message: parsed.error
|
|
3830
|
+
});
|
|
3831
|
+
finalizeOpenTest(openTest, tests);
|
|
3832
|
+
openTest = null;
|
|
3833
|
+
continue;
|
|
3834
|
+
}
|
|
3835
|
+
finalizeOpenTest(openTest, tests);
|
|
3836
|
+
openTest = null;
|
|
3837
|
+
if (!parsed.name) continue;
|
|
3838
|
+
const testGroup = {
|
|
3839
|
+
name: parsed.name,
|
|
3840
|
+
pass: parsed.pass,
|
|
3841
|
+
error: parsed.error,
|
|
3842
|
+
asserts: []
|
|
3843
|
+
};
|
|
3844
|
+
if (!parsed.pass && parsed.error) testGroup.asserts.push({
|
|
3845
|
+
pass: false,
|
|
3846
|
+
message: parsed.error
|
|
3847
|
+
});
|
|
3848
|
+
tests.push(testGroup);
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3851
|
+
if (line.level === "error" && line.kind !== "log") {
|
|
3852
|
+
const message = line.message;
|
|
3853
|
+
if (hasStructuredOutput || assertMessageAlreadyShown(message, tests)) continue;
|
|
3854
|
+
finalizeOpenTest(openTest, tests);
|
|
3855
|
+
openTest = null;
|
|
3856
|
+
standaloneAsserts.push({
|
|
3857
|
+
pass: false,
|
|
3858
|
+
message
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
finalizeOpenTest(openTest, tests);
|
|
3863
|
+
return {
|
|
3864
|
+
tests,
|
|
3865
|
+
standaloneAsserts
|
|
3866
|
+
};
|
|
3867
|
+
}
|
|
3868
|
+
function isPreRequestPhase(origin) {
|
|
3869
|
+
if (!origin) return false;
|
|
3870
|
+
const phase = String(origin.phase);
|
|
3871
|
+
return phase === "preRequest" || phase === "pre-request" || phase === "pre_request";
|
|
3872
|
+
}
|
|
3873
|
+
function scriptDisplayPath(originFile, requestFile) {
|
|
3874
|
+
if (!originFile) return;
|
|
3875
|
+
if (requestFile) {
|
|
3876
|
+
const relative = node_path.default.relative(node_path.default.dirname(requestFile), originFile);
|
|
3877
|
+
if (relative && !relative.startsWith("..")) return relative;
|
|
3878
|
+
}
|
|
3879
|
+
return originFile.split("/").pop();
|
|
3880
|
+
}
|
|
3881
|
+
function formatScriptOrigin(origin, requestFile) {
|
|
3882
|
+
if (!origin) return "[?]";
|
|
3883
|
+
const source = origin.source ?? "?";
|
|
3884
|
+
const file = scriptDisplayPath(origin.file, requestFile);
|
|
3885
|
+
const location = origin.line !== void 0 ? `L${origin.line}${origin.column !== void 0 ? `:${origin.column}` : ""}` : `directive L${origin.httpDirectiveLine ?? "?"}`;
|
|
3886
|
+
return file ? `[${source} · ${file} · ${location}]` : `[${source} · ${location}]`;
|
|
3887
|
+
}
|
|
3888
|
+
function splitScriptConsole(lines) {
|
|
3889
|
+
const pre = [];
|
|
3890
|
+
const post = [];
|
|
3891
|
+
const hasStructuredOutput = (lines ?? []).some((line) => line.kind === "assert" || line.kind === "test");
|
|
3892
|
+
const tree = parseAssertionTree(lines);
|
|
3893
|
+
const skipScriptErrors = /* @__PURE__ */ new Set();
|
|
3894
|
+
for (const test of tree.tests) {
|
|
3895
|
+
if (test.error) skipScriptErrors.add(test.error);
|
|
3896
|
+
for (const assert of test.asserts) skipScriptErrors.add(assert.message);
|
|
3897
|
+
}
|
|
3898
|
+
for (const assert of tree.standaloneAsserts) skipScriptErrors.add(assert.message);
|
|
3899
|
+
for (const line of lines ?? []) {
|
|
3900
|
+
if (isStructuredScriptLine(line)) continue;
|
|
3901
|
+
if (line.level === "error" && line.message.startsWith("Error executing script: ")) {
|
|
3902
|
+
const message = line.message.replace(/^Error executing script: /, "");
|
|
3903
|
+
if (hasStructuredOutput || skipScriptErrors.has(message)) continue;
|
|
3904
|
+
}
|
|
3905
|
+
if (isPreRequestPhase(line.origin)) pre.push(line);
|
|
3906
|
+
else post.push(line);
|
|
3907
|
+
}
|
|
3908
|
+
return {
|
|
3909
|
+
pre,
|
|
3910
|
+
post
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
function formatLabeledField(label, value) {
|
|
3914
|
+
return import_picocolors.default.bold(`${import_picocolors.default.cyan(`${label}:`)} ${value}`);
|
|
3915
|
+
}
|
|
3916
|
+
function formatRunHeader(filepath, blockName) {
|
|
3917
|
+
return [formatLabeledField("Filepath", filepath), formatLabeledField("Block name", blockName)].join("\n");
|
|
3918
|
+
}
|
|
3919
|
+
function itemDisplayName(item) {
|
|
3920
|
+
if ("blockName" in item && item.blockName) return item.blockName;
|
|
3921
|
+
return itemTitle(item);
|
|
3922
|
+
}
|
|
3923
|
+
function itemTitle(item) {
|
|
3924
|
+
if (isPromptResponse(item)) return `Prompt: ${item.promptType}`;
|
|
3925
|
+
if (isSkippedResponse(item)) return item.blockName ? `Skipped — ${item.blockName}` : "Skipped";
|
|
3926
|
+
if (isWebSocketResponse(item)) return `WebSocket — ${item.url}`;
|
|
3927
|
+
if (isErrorResponse(item)) return `${item.request?.method ?? "REQUEST"} ${item.url ?? item.blockName ?? "unknown"}`;
|
|
3928
|
+
if (isSuccessResponse(item)) return `${item.request?.method ?? "GET"} ${item.url}`;
|
|
3929
|
+
return "Unknown request";
|
|
3930
|
+
}
|
|
3931
|
+
//#endregion
|
|
3932
|
+
//#region src/lib/kulala-core/index.ts
|
|
3933
|
+
var cachedExecutable = null;
|
|
3934
|
+
async function executablePath() {
|
|
3935
|
+
if (!cachedExecutable) cachedExecutable = await downloader.ensureInstalled();
|
|
3936
|
+
if (!cachedExecutable) throw new Error("kulala-core executable not resolved");
|
|
3937
|
+
return cachedExecutable;
|
|
3938
|
+
}
|
|
3939
|
+
function invokeRaw(payload, options = {}) {
|
|
3940
|
+
const exe = cachedExecutable;
|
|
3941
|
+
if (!exe) throw new Error("kulala-core executable not resolved");
|
|
3942
|
+
const result = (0, node_child_process.spawnSync)(exe, [], {
|
|
3943
|
+
input: `${JSON.stringify(payload)}\n`,
|
|
3944
|
+
encoding: "utf-8",
|
|
3945
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
3946
|
+
cwd: options.cwd,
|
|
3947
|
+
env: process.env
|
|
3948
|
+
});
|
|
3949
|
+
if (result.error) throw result.error;
|
|
3950
|
+
return {
|
|
3951
|
+
stdout: result.stdout ?? "",
|
|
3952
|
+
stderr: result.stderr ?? "",
|
|
3953
|
+
status: result.status
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
function tryDecodeWrapper(stdout) {
|
|
3957
|
+
const raw = stdout.trim();
|
|
3958
|
+
if (!raw) return;
|
|
3959
|
+
try {
|
|
3960
|
+
const wrapper = JSON.parse(raw);
|
|
3961
|
+
if (wrapper && typeof wrapper === "object" && wrapper.type) return wrapper;
|
|
3962
|
+
} catch {
|
|
3963
|
+
return;
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
function parseInvokeResponse(job) {
|
|
3967
|
+
const wrapper = tryDecodeWrapper(job.stdout);
|
|
3968
|
+
const first = wrapper?.type === "responses" ? wrapper.data[0] : void 0;
|
|
3969
|
+
const isPrompt = Boolean(first && isPromptResponse(first));
|
|
3970
|
+
if (job.status !== 0 && !isPrompt) throw new Error(job.stderr?.trim() || `kulala-core exited with code ${job.status ?? "unknown"}`);
|
|
3971
|
+
if (!wrapper) throw new Error(job.stderr?.trim() || "kulala-core returned empty or invalid output");
|
|
3972
|
+
return wrapper;
|
|
3973
|
+
}
|
|
3974
|
+
async function runHttp(options, invokeOptions = {}) {
|
|
3975
|
+
await executablePath();
|
|
3976
|
+
return parseInvokeResponse(invokeRaw({
|
|
3977
|
+
action: "run",
|
|
3978
|
+
content: options.content,
|
|
3979
|
+
filepath: options.filepath,
|
|
3980
|
+
env: options.env,
|
|
3981
|
+
limit: options.limit,
|
|
3982
|
+
haltOnError: options.haltOnError
|
|
3983
|
+
}, invokeOptions));
|
|
3984
|
+
}
|
|
3985
|
+
async function continueHttp(options, invokeOptions = {}) {
|
|
3986
|
+
await executablePath();
|
|
3987
|
+
return parseInvokeResponse(invokeRaw({
|
|
3988
|
+
action: "continue",
|
|
3989
|
+
promptId: options.promptId,
|
|
3990
|
+
inputs: options.inputs
|
|
3991
|
+
}, invokeOptions));
|
|
3992
|
+
}
|
|
3993
|
+
async function environments(options = {}, invokeOptions = {}) {
|
|
3994
|
+
await executablePath();
|
|
3995
|
+
const job = invokeRaw({
|
|
3996
|
+
action: "environments",
|
|
3997
|
+
cwd: options.cwd,
|
|
3998
|
+
filepath: options.filepath
|
|
3999
|
+
}, invokeOptions);
|
|
4000
|
+
if (job.status !== 0) throw new Error(job.stderr?.trim() || `kulala-core exited with code ${job.status ?? "unknown"}`);
|
|
4001
|
+
const raw = job.stdout.trim();
|
|
4002
|
+
if (!raw) throw new Error("kulala-core returned empty output");
|
|
4003
|
+
return JSON.parse(raw);
|
|
4004
|
+
}
|
|
4005
|
+
async function curl$1(options, invokeOptions = {}) {
|
|
4006
|
+
await executablePath();
|
|
4007
|
+
return parseInvokeResponse(invokeRaw({
|
|
4008
|
+
action: "curl",
|
|
4009
|
+
argv: options.argv
|
|
4010
|
+
}, invokeOptions));
|
|
4011
|
+
}
|
|
4012
|
+
var kulalaCore = {
|
|
4013
|
+
runHttp,
|
|
4014
|
+
continueHttp,
|
|
4015
|
+
environments,
|
|
4016
|
+
curl: curl$1
|
|
4017
|
+
};
|
|
3757
4018
|
//#endregion
|
|
3758
4019
|
//#region node_modules/.pnpm/highlight.js@10.7.3/node_modules/highlight.js/lib/core.js
|
|
3759
4020
|
var require_core = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -62702,8 +62963,8 @@ var require_theme = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
62702
62963
|
exports.parse = parse;
|
|
62703
62964
|
}));
|
|
62704
62965
|
//#endregion
|
|
62705
|
-
//#region
|
|
62706
|
-
var
|
|
62966
|
+
//#region src/lib/output/highlight.ts
|
|
62967
|
+
var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
62707
62968
|
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
62708
62969
|
if (k2 === void 0) k2 = k;
|
|
62709
62970
|
Object.defineProperty(o, k2, {
|
|
@@ -62818,11 +63079,7 @@ var require_dist = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
62818
63079
|
exports.supportsLanguage = supportsLanguage;
|
|
62819
63080
|
exports.default = highlight;
|
|
62820
63081
|
__exportStar(require_theme(), exports);
|
|
62821
|
-
}));
|
|
62822
|
-
//#endregion
|
|
62823
|
-
//#region src/lib/output/highlight.ts
|
|
62824
|
-
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
62825
|
-
var import_dist = require_dist();
|
|
63082
|
+
})))();
|
|
62826
63083
|
var colorOverride;
|
|
62827
63084
|
function setColorEnabled(enabled) {
|
|
62828
63085
|
colorOverride = enabled;
|
|
@@ -66666,212 +66923,6 @@ function renderImageInline(body) {
|
|
|
66666
66923
|
return null;
|
|
66667
66924
|
}
|
|
66668
66925
|
//#endregion
|
|
66669
|
-
//#region src/lib/output/shared.ts
|
|
66670
|
-
function isPromptResponse(item) {
|
|
66671
|
-
return "prompt" in item && item.prompt === true;
|
|
66672
|
-
}
|
|
66673
|
-
function isSkippedResponse(item) {
|
|
66674
|
-
return "skipped" in item && item.skipped === true;
|
|
66675
|
-
}
|
|
66676
|
-
function isWebSocketResponse(item) {
|
|
66677
|
-
return "protocol" in item && item.protocol === "websocket";
|
|
66678
|
-
}
|
|
66679
|
-
function isErrorResponse(item) {
|
|
66680
|
-
return item.success === false && !isPromptResponse(item);
|
|
66681
|
-
}
|
|
66682
|
-
function isSuccessResponse(item) {
|
|
66683
|
-
return item.success === true && !isSkippedResponse(item) && !isWebSocketResponse(item);
|
|
66684
|
-
}
|
|
66685
|
-
function responseBodyText(body) {
|
|
66686
|
-
if (!body) return "";
|
|
66687
|
-
if (body.type === "json") return body.formatted ?? JSON.stringify(body.content, null, 2);
|
|
66688
|
-
if (body.type === "binary") return "";
|
|
66689
|
-
return body.content;
|
|
66690
|
-
}
|
|
66691
|
-
function responseBodyLanguage(body) {
|
|
66692
|
-
if (!body) return "text";
|
|
66693
|
-
if (body.type === "json") return "json";
|
|
66694
|
-
if (body.type === "binary") return "text";
|
|
66695
|
-
const mediaType = body.mediaType?.toLowerCase() ?? "";
|
|
66696
|
-
if (mediaType.includes("json")) return "json";
|
|
66697
|
-
if (mediaType.includes("xml")) return "xml";
|
|
66698
|
-
if (mediaType.includes("html")) return "html";
|
|
66699
|
-
if (mediaType.includes("javascript") || mediaType.includes("ecmascript")) return "javascript";
|
|
66700
|
-
if (mediaType.includes("graphql")) return "graphql";
|
|
66701
|
-
return "text";
|
|
66702
|
-
}
|
|
66703
|
-
function formatMs(ms) {
|
|
66704
|
-
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
66705
|
-
return `${(ms / 1e3).toFixed(2)}s`;
|
|
66706
|
-
}
|
|
66707
|
-
function escapeCell(value) {
|
|
66708
|
-
return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
66709
|
-
}
|
|
66710
|
-
function mdTable(rows) {
|
|
66711
|
-
if (rows.length === 0) return "";
|
|
66712
|
-
const header = rows[0];
|
|
66713
|
-
const separator = header.map(() => "---");
|
|
66714
|
-
const body = rows.slice(1);
|
|
66715
|
-
const formatRow = (row) => `| ${row.join(" | ")} |`;
|
|
66716
|
-
return [
|
|
66717
|
-
formatRow(header),
|
|
66718
|
-
formatRow(separator),
|
|
66719
|
-
...body.map(formatRow)
|
|
66720
|
-
].join("\n");
|
|
66721
|
-
}
|
|
66722
|
-
function isStructuredScriptLine(line) {
|
|
66723
|
-
return (line.kind === "test" || line.kind === "assert") && (line.status === "pass" || line.status === "fail");
|
|
66724
|
-
}
|
|
66725
|
-
function finalizeOpenTest(openTest, tests) {
|
|
66726
|
-
if (!openTest) return;
|
|
66727
|
-
if (!tests.includes(openTest)) tests.push(openTest);
|
|
66728
|
-
}
|
|
66729
|
-
function assertMessageAlreadyShown(message, tests) {
|
|
66730
|
-
return tests.some((test) => test.error === message || test.asserts.some((assert) => assert.message === message));
|
|
66731
|
-
}
|
|
66732
|
-
function parseAssertionTree(lines) {
|
|
66733
|
-
const tests = [];
|
|
66734
|
-
const standaloneAsserts = [];
|
|
66735
|
-
let openTest = null;
|
|
66736
|
-
const hasStructuredOutput = (lines ?? []).some((line) => line.kind === "assert" || line.kind === "test");
|
|
66737
|
-
for (const line of lines ?? []) {
|
|
66738
|
-
if (line.kind === "assert") {
|
|
66739
|
-
const assert = {
|
|
66740
|
-
pass: line.status === "pass",
|
|
66741
|
-
message: line.message
|
|
66742
|
-
};
|
|
66743
|
-
if (line.testName) {
|
|
66744
|
-
if (!openTest || openTest.name !== line.testName) {
|
|
66745
|
-
finalizeOpenTest(openTest, tests);
|
|
66746
|
-
openTest = {
|
|
66747
|
-
name: line.testName,
|
|
66748
|
-
pass: true,
|
|
66749
|
-
asserts: []
|
|
66750
|
-
};
|
|
66751
|
-
}
|
|
66752
|
-
openTest.asserts.push(assert);
|
|
66753
|
-
if (!assert.pass) openTest.pass = false;
|
|
66754
|
-
} else {
|
|
66755
|
-
finalizeOpenTest(openTest, tests);
|
|
66756
|
-
openTest = null;
|
|
66757
|
-
standaloneAsserts.push(assert);
|
|
66758
|
-
}
|
|
66759
|
-
continue;
|
|
66760
|
-
}
|
|
66761
|
-
if (line.kind === "test") {
|
|
66762
|
-
const parsed = {
|
|
66763
|
-
pass: line.status === "pass",
|
|
66764
|
-
name: line.testName,
|
|
66765
|
-
error: line.status === "fail" && line.level === "error" ? line.message : void 0
|
|
66766
|
-
};
|
|
66767
|
-
if (openTest && openTest.name === parsed.name) {
|
|
66768
|
-
openTest.pass = parsed.pass;
|
|
66769
|
-
openTest.error = parsed.error;
|
|
66770
|
-
if (!parsed.pass && parsed.error && openTest.asserts.length === 0) openTest.asserts.push({
|
|
66771
|
-
pass: false,
|
|
66772
|
-
message: parsed.error
|
|
66773
|
-
});
|
|
66774
|
-
finalizeOpenTest(openTest, tests);
|
|
66775
|
-
openTest = null;
|
|
66776
|
-
continue;
|
|
66777
|
-
}
|
|
66778
|
-
finalizeOpenTest(openTest, tests);
|
|
66779
|
-
openTest = null;
|
|
66780
|
-
if (!parsed.name) continue;
|
|
66781
|
-
const testGroup = {
|
|
66782
|
-
name: parsed.name,
|
|
66783
|
-
pass: parsed.pass,
|
|
66784
|
-
error: parsed.error,
|
|
66785
|
-
asserts: []
|
|
66786
|
-
};
|
|
66787
|
-
if (!parsed.pass && parsed.error) testGroup.asserts.push({
|
|
66788
|
-
pass: false,
|
|
66789
|
-
message: parsed.error
|
|
66790
|
-
});
|
|
66791
|
-
tests.push(testGroup);
|
|
66792
|
-
continue;
|
|
66793
|
-
}
|
|
66794
|
-
if (line.level === "error" && line.kind !== "log") {
|
|
66795
|
-
const message = line.message;
|
|
66796
|
-
if (hasStructuredOutput || assertMessageAlreadyShown(message, tests)) continue;
|
|
66797
|
-
finalizeOpenTest(openTest, tests);
|
|
66798
|
-
openTest = null;
|
|
66799
|
-
standaloneAsserts.push({
|
|
66800
|
-
pass: false,
|
|
66801
|
-
message
|
|
66802
|
-
});
|
|
66803
|
-
}
|
|
66804
|
-
}
|
|
66805
|
-
finalizeOpenTest(openTest, tests);
|
|
66806
|
-
return {
|
|
66807
|
-
tests,
|
|
66808
|
-
standaloneAsserts
|
|
66809
|
-
};
|
|
66810
|
-
}
|
|
66811
|
-
function isPreRequestPhase(origin) {
|
|
66812
|
-
if (!origin) return false;
|
|
66813
|
-
const phase = String(origin.phase);
|
|
66814
|
-
return phase === "preRequest" || phase === "pre-request" || phase === "pre_request";
|
|
66815
|
-
}
|
|
66816
|
-
function scriptDisplayPath(originFile, requestFile) {
|
|
66817
|
-
if (!originFile) return;
|
|
66818
|
-
if (requestFile) {
|
|
66819
|
-
const relative = node_path.default.relative(node_path.default.dirname(requestFile), originFile);
|
|
66820
|
-
if (relative && !relative.startsWith("..")) return relative;
|
|
66821
|
-
}
|
|
66822
|
-
return originFile.split("/").pop();
|
|
66823
|
-
}
|
|
66824
|
-
function formatScriptOrigin(origin, requestFile) {
|
|
66825
|
-
if (!origin) return "[?]";
|
|
66826
|
-
const source = origin.source ?? "?";
|
|
66827
|
-
const file = scriptDisplayPath(origin.file, requestFile);
|
|
66828
|
-
const location = origin.line !== void 0 ? `L${origin.line}${origin.column !== void 0 ? `:${origin.column}` : ""}` : `directive L${origin.httpDirectiveLine ?? "?"}`;
|
|
66829
|
-
return file ? `[${source} · ${file} · ${location}]` : `[${source} · ${location}]`;
|
|
66830
|
-
}
|
|
66831
|
-
function splitScriptConsole(lines) {
|
|
66832
|
-
const pre = [];
|
|
66833
|
-
const post = [];
|
|
66834
|
-
const hasStructuredOutput = (lines ?? []).some((line) => line.kind === "assert" || line.kind === "test");
|
|
66835
|
-
const tree = parseAssertionTree(lines);
|
|
66836
|
-
const skipScriptErrors = /* @__PURE__ */ new Set();
|
|
66837
|
-
for (const test of tree.tests) {
|
|
66838
|
-
if (test.error) skipScriptErrors.add(test.error);
|
|
66839
|
-
for (const assert of test.asserts) skipScriptErrors.add(assert.message);
|
|
66840
|
-
}
|
|
66841
|
-
for (const assert of tree.standaloneAsserts) skipScriptErrors.add(assert.message);
|
|
66842
|
-
for (const line of lines ?? []) {
|
|
66843
|
-
if (isStructuredScriptLine(line)) continue;
|
|
66844
|
-
if (line.level === "error" && line.message.startsWith("Error executing script: ")) {
|
|
66845
|
-
const message = line.message.replace(/^Error executing script: /, "");
|
|
66846
|
-
if (hasStructuredOutput || skipScriptErrors.has(message)) continue;
|
|
66847
|
-
}
|
|
66848
|
-
if (isPreRequestPhase(line.origin)) pre.push(line);
|
|
66849
|
-
else post.push(line);
|
|
66850
|
-
}
|
|
66851
|
-
return {
|
|
66852
|
-
pre,
|
|
66853
|
-
post
|
|
66854
|
-
};
|
|
66855
|
-
}
|
|
66856
|
-
function formatLabeledField(label, value) {
|
|
66857
|
-
return import_picocolors.default.bold(`${import_picocolors.default.cyan(`${label}:`)} ${value}`);
|
|
66858
|
-
}
|
|
66859
|
-
function formatRunHeader(filepath, blockName) {
|
|
66860
|
-
return [formatLabeledField("Filepath", filepath), formatLabeledField("Block name", blockName)].join("\n");
|
|
66861
|
-
}
|
|
66862
|
-
function itemDisplayName(item) {
|
|
66863
|
-
if ("blockName" in item && item.blockName) return item.blockName;
|
|
66864
|
-
return itemTitle(item);
|
|
66865
|
-
}
|
|
66866
|
-
function itemTitle(item) {
|
|
66867
|
-
if (isPromptResponse(item)) return `Prompt: ${item.promptType}`;
|
|
66868
|
-
if (isSkippedResponse(item)) return item.blockName ? `Skipped — ${item.blockName}` : "Skipped";
|
|
66869
|
-
if (isWebSocketResponse(item)) return `WebSocket — ${item.url}`;
|
|
66870
|
-
if (isErrorResponse(item)) return `${item.request?.method ?? "REQUEST"} ${item.url ?? item.blockName ?? "unknown"}`;
|
|
66871
|
-
if (isSuccessResponse(item)) return `${item.request?.method ?? "GET"} ${item.url}`;
|
|
66872
|
-
return "Unknown request";
|
|
66873
|
-
}
|
|
66874
|
-
//#endregion
|
|
66875
66926
|
//#region src/lib/output/human.ts
|
|
66876
66927
|
function statusColor(status) {
|
|
66877
66928
|
if (status >= 200 && status < 300) return import_picocolors.default.green;
|
|
@@ -67018,6 +67069,13 @@ function formatItem(item, requestFile) {
|
|
|
67018
67069
|
function formatWrapper(wrapper, requestFile) {
|
|
67019
67070
|
return (wrapper.type === "error" ? wrapper.data : wrapper.data).map((entry) => formatItem(entry, requestFile)).join("\n\n");
|
|
67020
67071
|
}
|
|
67072
|
+
function printResponseItems(filepath, items) {
|
|
67073
|
+
if (items.length === 0) return;
|
|
67074
|
+
console.log(formatWrapper({
|
|
67075
|
+
type: "responses",
|
|
67076
|
+
data: items
|
|
67077
|
+
}, filepath));
|
|
67078
|
+
}
|
|
67021
67079
|
function printHumanReadable(results) {
|
|
67022
67080
|
const blocks = results.map((result) => formatWrapper(result.response, result.filepath));
|
|
67023
67081
|
console.log(blocks.join("\n\n"));
|
|
@@ -67239,6 +67297,139 @@ function printTests(results, options) {
|
|
|
67239
67297
|
if (blocks.length > 0 && !options.quiet) console.log(blocks.join("\n\n"));
|
|
67240
67298
|
}
|
|
67241
67299
|
//#endregion
|
|
67300
|
+
//#region src/lib/prompt.ts
|
|
67301
|
+
var pipedBuffer = "";
|
|
67302
|
+
var pipedEnded = false;
|
|
67303
|
+
var pipedStdinInitialized = false;
|
|
67304
|
+
var pipedWaiters = [];
|
|
67305
|
+
function flushPipedWaiters() {
|
|
67306
|
+
while (pipedWaiters.length > 0) {
|
|
67307
|
+
const newlineIndex = pipedBuffer.indexOf("\n");
|
|
67308
|
+
if (newlineIndex === -1) {
|
|
67309
|
+
if (pipedEnded) {
|
|
67310
|
+
const line = pipedBuffer.replace(/\r?\n$/u, "");
|
|
67311
|
+
pipedBuffer = "";
|
|
67312
|
+
pipedWaiters.shift()?.(line.length > 0 ? line : void 0);
|
|
67313
|
+
continue;
|
|
67314
|
+
}
|
|
67315
|
+
break;
|
|
67316
|
+
}
|
|
67317
|
+
const line = pipedBuffer.slice(0, newlineIndex).replace(/\r$/u, "");
|
|
67318
|
+
pipedBuffer = pipedBuffer.slice(newlineIndex + 1);
|
|
67319
|
+
pipedWaiters.shift()?.(line);
|
|
67320
|
+
}
|
|
67321
|
+
if (pipedEnded) while (pipedWaiters.length > 0) pipedWaiters.shift()?.(void 0);
|
|
67322
|
+
}
|
|
67323
|
+
function initPipedStdin() {
|
|
67324
|
+
if (pipedStdinInitialized || node_process.stdin.isTTY) return;
|
|
67325
|
+
pipedStdinInitialized = true;
|
|
67326
|
+
node_process.stdin.setEncoding("utf8");
|
|
67327
|
+
node_process.stdin.on("data", (chunk) => {
|
|
67328
|
+
pipedBuffer += chunk;
|
|
67329
|
+
flushPipedWaiters();
|
|
67330
|
+
});
|
|
67331
|
+
node_process.stdin.on("end", () => {
|
|
67332
|
+
pipedEnded = true;
|
|
67333
|
+
flushPipedWaiters();
|
|
67334
|
+
});
|
|
67335
|
+
node_process.stdin.resume();
|
|
67336
|
+
}
|
|
67337
|
+
async function readPipedLine() {
|
|
67338
|
+
initPipedStdin();
|
|
67339
|
+
const newlineIndex = pipedBuffer.indexOf("\n");
|
|
67340
|
+
if (newlineIndex !== -1) {
|
|
67341
|
+
const line = pipedBuffer.slice(0, newlineIndex).replace(/\r$/u, "");
|
|
67342
|
+
pipedBuffer = pipedBuffer.slice(newlineIndex + 1);
|
|
67343
|
+
return line;
|
|
67344
|
+
}
|
|
67345
|
+
if (pipedEnded) {
|
|
67346
|
+
const line = pipedBuffer.replace(/\r?\n$/u, "");
|
|
67347
|
+
pipedBuffer = "";
|
|
67348
|
+
return line.length > 0 ? line : void 0;
|
|
67349
|
+
}
|
|
67350
|
+
return new Promise((resolve) => {
|
|
67351
|
+
pipedWaiters.push(resolve);
|
|
67352
|
+
});
|
|
67353
|
+
}
|
|
67354
|
+
async function readVisibleLine(label) {
|
|
67355
|
+
if (!node_process.stdin.isTTY) return readPipedLine();
|
|
67356
|
+
const rl = (0, node_readline.createInterface)({
|
|
67357
|
+
input: node_process.stdin,
|
|
67358
|
+
output: node_process.stdout
|
|
67359
|
+
});
|
|
67360
|
+
try {
|
|
67361
|
+
return await new Promise((resolve) => {
|
|
67362
|
+
rl.question(`${label}: `, resolve);
|
|
67363
|
+
});
|
|
67364
|
+
} finally {
|
|
67365
|
+
rl.close();
|
|
67366
|
+
}
|
|
67367
|
+
}
|
|
67368
|
+
async function readHiddenLine(label) {
|
|
67369
|
+
if (!node_process.stdin.isTTY) return readPipedLine();
|
|
67370
|
+
if (typeof node_process.stdin.setRawMode !== "function") return readVisibleLine(label);
|
|
67371
|
+
node_process.stdout.write(`${label}: `);
|
|
67372
|
+
node_process.stdin.setRawMode(true);
|
|
67373
|
+
node_process.stdin.resume();
|
|
67374
|
+
node_process.stdin.setEncoding("utf8");
|
|
67375
|
+
return new Promise((resolve) => {
|
|
67376
|
+
let value = "";
|
|
67377
|
+
const cleanup = (result) => {
|
|
67378
|
+
node_process.stdin.setRawMode(false);
|
|
67379
|
+
node_process.stdin.pause();
|
|
67380
|
+
node_process.stdin.removeListener("data", onData);
|
|
67381
|
+
node_process.stdout.write("\n");
|
|
67382
|
+
resolve(result);
|
|
67383
|
+
};
|
|
67384
|
+
const onData = (chunk) => {
|
|
67385
|
+
for (const char of chunk) {
|
|
67386
|
+
if (char === "\n" || char === "\r" || char === "") {
|
|
67387
|
+
cleanup(value);
|
|
67388
|
+
return;
|
|
67389
|
+
}
|
|
67390
|
+
if (char === "") {
|
|
67391
|
+
cleanup(void 0);
|
|
67392
|
+
return;
|
|
67393
|
+
}
|
|
67394
|
+
if (char === "" || char === "\b") {
|
|
67395
|
+
if (value.length > 0) value = value.slice(0, -1);
|
|
67396
|
+
continue;
|
|
67397
|
+
}
|
|
67398
|
+
value += char;
|
|
67399
|
+
}
|
|
67400
|
+
};
|
|
67401
|
+
node_process.stdin.on("data", onData);
|
|
67402
|
+
});
|
|
67403
|
+
}
|
|
67404
|
+
async function readPromptValue(label, type) {
|
|
67405
|
+
if (type === "password") return readHiddenLine(label);
|
|
67406
|
+
return readVisibleLine(label);
|
|
67407
|
+
}
|
|
67408
|
+
async function collectPromptInputs(prompt) {
|
|
67409
|
+
const specs = prompt.inputs ?? [];
|
|
67410
|
+
if (specs.length === 0) {
|
|
67411
|
+
console.error(chalk.yellow("Kulala prompt has no inputs."));
|
|
67412
|
+
return;
|
|
67413
|
+
}
|
|
67414
|
+
const out = [];
|
|
67415
|
+
for (const spec of specs) {
|
|
67416
|
+
const id = spec.id;
|
|
67417
|
+
if (!id) return;
|
|
67418
|
+
const label = spec.label?.trim() || id;
|
|
67419
|
+
const value = await readPromptValue(label, spec.type ?? "text");
|
|
67420
|
+
if (value === void 0) return;
|
|
67421
|
+
if (spec.required && !value.trim()) {
|
|
67422
|
+
console.error(chalk.yellow(`Required input missing: ${label}`));
|
|
67423
|
+
return;
|
|
67424
|
+
}
|
|
67425
|
+
out.push({
|
|
67426
|
+
id,
|
|
67427
|
+
value
|
|
67428
|
+
});
|
|
67429
|
+
}
|
|
67430
|
+
return out;
|
|
67431
|
+
}
|
|
67432
|
+
//#endregion
|
|
67242
67433
|
//#region src/lib/runner/limit.ts
|
|
67243
67434
|
function buildRunLimit(options) {
|
|
67244
67435
|
const hasName = options.name !== void 0 && options.name.length > 0;
|
|
@@ -67297,6 +67488,7 @@ function resolveSingleHttpFile(inputPath) {
|
|
|
67297
67488
|
}
|
|
67298
67489
|
//#endregion
|
|
67299
67490
|
//#region src/lib/runner/index.ts
|
|
67491
|
+
var MAX_PROMPT_DEPTH = 7;
|
|
67300
67492
|
var HTTP_EXTENSIONS = [".http", ".rest"];
|
|
67301
67493
|
function shuffleFiles(items) {
|
|
67302
67494
|
const copy = [...items];
|
|
@@ -67319,20 +67511,110 @@ function resolveFiles(inputPath, shuffle) {
|
|
|
67319
67511
|
function responseHasFailure(result) {
|
|
67320
67512
|
return countResults(result.response).failed > 0;
|
|
67321
67513
|
}
|
|
67322
|
-
|
|
67514
|
+
function shouldStreamOutput(ctx) {
|
|
67515
|
+
return !ctx.json && !ctx.tests;
|
|
67516
|
+
}
|
|
67517
|
+
function itemsToStream(items, quiet) {
|
|
67518
|
+
if (!quiet) return items;
|
|
67519
|
+
return items.filter((item) => !isResponseSuccessful(item));
|
|
67520
|
+
}
|
|
67521
|
+
function streamResponseItems(filepath, items, ctx) {
|
|
67522
|
+
if (!shouldStreamOutput(ctx)) return;
|
|
67523
|
+
printResponseItems(filepath, itemsToStream(items, ctx.quiet ?? false));
|
|
67524
|
+
}
|
|
67525
|
+
function continueSucceeded(response) {
|
|
67526
|
+
return (response.type === "responses" ? response.data[0] : void 0)?.success === true;
|
|
67527
|
+
}
|
|
67528
|
+
function mergeRunResponses(accumulated, response) {
|
|
67529
|
+
if (response.type === "error" || accumulated.length === 0) return response;
|
|
67530
|
+
return {
|
|
67531
|
+
type: "responses",
|
|
67532
|
+
data: [...accumulated, ...response.data]
|
|
67533
|
+
};
|
|
67534
|
+
}
|
|
67535
|
+
function completedItemsBeforePrompt(response) {
|
|
67536
|
+
if (response.type !== "responses") return [];
|
|
67537
|
+
const promptIndex = response.data.findIndex((item) => isPromptResponse(item));
|
|
67538
|
+
if (promptIndex <= 0) return [];
|
|
67539
|
+
return response.data.slice(0, promptIndex);
|
|
67540
|
+
}
|
|
67541
|
+
function newItemsSinceAccumulated(accumulated, response) {
|
|
67542
|
+
if (response.type !== "responses") return [];
|
|
67543
|
+
return response.data.slice(accumulated.length);
|
|
67544
|
+
}
|
|
67545
|
+
function promptBlockName(promptItem) {
|
|
67546
|
+
if ("blockName" in promptItem && typeof promptItem.blockName === "string") return promptItem.blockName.trim() || void 0;
|
|
67547
|
+
}
|
|
67548
|
+
async function runFileWithPromptRetry(relativePath, env, limit, halt, output, depth = 0, accumulated = []) {
|
|
67323
67549
|
const absolutePath = node_path.default.resolve(process.cwd(), relativePath);
|
|
67324
67550
|
const content = node_fs.default.readFileSync(absolutePath, "utf-8");
|
|
67325
67551
|
const cwd = node_path.default.dirname(absolutePath);
|
|
67326
|
-
|
|
67327
|
-
|
|
67328
|
-
|
|
67329
|
-
|
|
67330
|
-
|
|
67331
|
-
|
|
67332
|
-
limit,
|
|
67333
|
-
haltOnError: halt
|
|
67334
|
-
}, { cwd })
|
|
67552
|
+
const runOptions = {
|
|
67553
|
+
content,
|
|
67554
|
+
filepath: absolutePath,
|
|
67555
|
+
env,
|
|
67556
|
+
limit,
|
|
67557
|
+
haltOnError: halt
|
|
67335
67558
|
};
|
|
67559
|
+
const response = isInteractiveTerminal() ? await withSpinner(`Running requests in file ${relativePath}`, () => kulalaCore.runHttp(runOptions, { cwd })) : await kulalaCore.runHttp(runOptions, { cwd });
|
|
67560
|
+
const promptItem = response.type === "responses" ? findFirstPromptItem(response) : void 0;
|
|
67561
|
+
if (!promptItem) {
|
|
67562
|
+
const final = mergeRunResponses(accumulated, response);
|
|
67563
|
+
streamResponseItems(relativePath, newItemsSinceAccumulated(accumulated, final), output);
|
|
67564
|
+
return {
|
|
67565
|
+
filepath: relativePath,
|
|
67566
|
+
response: final,
|
|
67567
|
+
outputStreamed: shouldStreamOutput(output)
|
|
67568
|
+
};
|
|
67569
|
+
}
|
|
67570
|
+
if (depth >= MAX_PROMPT_DEPTH) {
|
|
67571
|
+
console.error(chalk.red("Kulala: exceeded prompt / retry limit."));
|
|
67572
|
+
return {
|
|
67573
|
+
filepath: relativePath,
|
|
67574
|
+
response: mergeRunResponses(accumulated, response),
|
|
67575
|
+
outputStreamed: shouldStreamOutput(output)
|
|
67576
|
+
};
|
|
67577
|
+
}
|
|
67578
|
+
const completedBefore = completedItemsBeforePrompt(response);
|
|
67579
|
+
const newAccumulated = [...accumulated, ...completedBefore];
|
|
67580
|
+
streamResponseItems(relativePath, completedBefore, output);
|
|
67581
|
+
const inputs = await collectPromptInputs(promptItem);
|
|
67582
|
+
if (!inputs || !promptItem.promptId) {
|
|
67583
|
+
console.error(chalk.yellow("Prompt cancelled or incomplete."));
|
|
67584
|
+
return {
|
|
67585
|
+
filepath: relativePath,
|
|
67586
|
+
response: mergeRunResponses(newAccumulated, response),
|
|
67587
|
+
outputStreamed: shouldStreamOutput(output)
|
|
67588
|
+
};
|
|
67589
|
+
}
|
|
67590
|
+
const continued = isInteractiveTerminal() ? await withSpinner(`Running requests in file ${relativePath}`, () => kulalaCore.continueHttp({
|
|
67591
|
+
promptId: promptItem.promptId,
|
|
67592
|
+
inputs
|
|
67593
|
+
}, { cwd })) : await kulalaCore.continueHttp({
|
|
67594
|
+
promptId: promptItem.promptId,
|
|
67595
|
+
inputs
|
|
67596
|
+
}, { cwd });
|
|
67597
|
+
if (!continueSucceeded(continued)) {
|
|
67598
|
+
let err;
|
|
67599
|
+
if (continued.type === "error") err = continued.data[0]?.error;
|
|
67600
|
+
else {
|
|
67601
|
+
const item = continued.data[0];
|
|
67602
|
+
err = item && "error" in item ? item.error : void 0;
|
|
67603
|
+
}
|
|
67604
|
+
console.error(chalk.red(err ?? "Prompt continuation failed."));
|
|
67605
|
+
return {
|
|
67606
|
+
filepath: relativePath,
|
|
67607
|
+
response: continued
|
|
67608
|
+
};
|
|
67609
|
+
}
|
|
67610
|
+
const blockName = promptBlockName(promptItem);
|
|
67611
|
+
return runFileWithPromptRetry(relativePath, env, completedBefore.length > 0 && blockName ? [{
|
|
67612
|
+
filter: "name",
|
|
67613
|
+
name: blockName
|
|
67614
|
+
}] : limit, halt, output, depth + 1, newAccumulated);
|
|
67615
|
+
}
|
|
67616
|
+
async function runFile(relativePath, env, limit, halt, output) {
|
|
67617
|
+
return runFileWithPromptRetry(relativePath, env, limit, halt, output);
|
|
67336
67618
|
}
|
|
67337
67619
|
function hasFailures(results) {
|
|
67338
67620
|
return results.some((result) => responseHasFailure(result));
|
|
@@ -67340,20 +67622,28 @@ function hasFailures(results) {
|
|
|
67340
67622
|
async function run(inputPath, options) {
|
|
67341
67623
|
if (options.color === false) setColorEnabled(false);
|
|
67342
67624
|
if (options.report && !options.tests) options.tests = true;
|
|
67625
|
+
const output = {
|
|
67626
|
+
json: options.json,
|
|
67627
|
+
tests: options.tests,
|
|
67628
|
+
quiet: options.quiet
|
|
67629
|
+
};
|
|
67343
67630
|
const limit = buildRunLimit(options);
|
|
67344
67631
|
const files = limit ? [resolveSingleHttpFile(inputPath)] : resolveFiles(inputPath, options.shuffle ?? false);
|
|
67345
67632
|
const results = [];
|
|
67346
67633
|
const halt = options.halt ?? false;
|
|
67347
67634
|
for (const file of files) {
|
|
67348
|
-
const result = await runFile(file, options.env, limit, halt);
|
|
67635
|
+
const result = await runFile(file, options.env, limit, halt, output);
|
|
67349
67636
|
results.push(result);
|
|
67350
67637
|
if (halt && responseHasFailure(result)) break;
|
|
67351
67638
|
}
|
|
67352
|
-
const
|
|
67639
|
+
const pendingOutput = results.filter((result) => !result.outputStreamed);
|
|
67640
|
+
const outputResults = options.quiet && !options.tests ? filterFailedResults(pendingOutput) : pendingOutput;
|
|
67353
67641
|
if (options.tests) printTests(results, { quiet: options.quiet ?? false });
|
|
67354
|
-
else if (!options.quiet || outputResults.length > 0)
|
|
67355
|
-
|
|
67356
|
-
|
|
67642
|
+
else if (!options.quiet || outputResults.length > 0) {
|
|
67643
|
+
if (options.json) printJson(results);
|
|
67644
|
+
else if (options.report) printReport(pendingOutput);
|
|
67645
|
+
else if (pendingOutput.length > 0) printHumanReadable(outputResults);
|
|
67646
|
+
}
|
|
67357
67647
|
if (hasFailures(results)) process.exit(1);
|
|
67358
67648
|
}
|
|
67359
67649
|
//#endregion
|
|
@@ -67398,7 +67688,7 @@ async function curl(argv, options = {}) {
|
|
|
67398
67688
|
console.error(" kulala curl -H \"Accept: application/json\" https://echo.kulala.app/get");
|
|
67399
67689
|
process.exit(1);
|
|
67400
67690
|
}
|
|
67401
|
-
const response = await kulalaCore.curl({ argv: curlArgv });
|
|
67691
|
+
const response = isInteractiveTerminal() ? await withSpinner("Running request", () => kulalaCore.curl({ argv: curlArgv })) : await kulalaCore.curl({ argv: curlArgv });
|
|
67402
67692
|
const result = {
|
|
67403
67693
|
filepath: formatCurlLabel(curlArgv),
|
|
67404
67694
|
response
|
package/dist/install-backend.cjs
CHANGED
|
@@ -28,8 +28,53 @@ fs = __toESM(fs, 1);
|
|
|
28
28
|
let path = require("path");
|
|
29
29
|
path = __toESM(path, 1);
|
|
30
30
|
let stream_promises = require("stream/promises");
|
|
31
|
+
//#region src/lib/spinner.ts
|
|
32
|
+
var SPINNER_FRAMES = [
|
|
33
|
+
"⠋",
|
|
34
|
+
"⠙",
|
|
35
|
+
"⠹",
|
|
36
|
+
"⠸",
|
|
37
|
+
"⠼",
|
|
38
|
+
"⠴",
|
|
39
|
+
"⠦",
|
|
40
|
+
"⠧",
|
|
41
|
+
"⠇",
|
|
42
|
+
"⠏"
|
|
43
|
+
];
|
|
44
|
+
function isInteractiveTerminal() {
|
|
45
|
+
return process.stderr.isTTY === true;
|
|
46
|
+
}
|
|
47
|
+
function createSpinner() {
|
|
48
|
+
let timer;
|
|
49
|
+
let frame = 0;
|
|
50
|
+
let message = "";
|
|
51
|
+
const stop = () => {
|
|
52
|
+
if (timer) {
|
|
53
|
+
clearInterval(timer);
|
|
54
|
+
timer = void 0;
|
|
55
|
+
}
|
|
56
|
+
if (isInteractiveTerminal()) process.stderr.write("\r\x1B[K");
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
start(msg) {
|
|
60
|
+
message = msg;
|
|
61
|
+
if (!isInteractiveTerminal()) {
|
|
62
|
+
console.error(message);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const render = () => {
|
|
66
|
+
process.stderr.write(`\r${SPINNER_FRAMES[frame]} ${message}`);
|
|
67
|
+
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
68
|
+
};
|
|
69
|
+
render();
|
|
70
|
+
timer = setInterval(render, 80);
|
|
71
|
+
},
|
|
72
|
+
stop
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
31
76
|
//#region src/versions/backend.ts
|
|
32
|
-
var KULALA_CORE_VERSION = "0.24.
|
|
77
|
+
var KULALA_CORE_VERSION = "0.24.4";
|
|
33
78
|
//#endregion
|
|
34
79
|
//#region src/lib/downloader/index.ts
|
|
35
80
|
var BINARY_NAME = "kulala-core";
|
|
@@ -72,52 +117,18 @@ function versionMatches() {
|
|
|
72
117
|
function makeExecutable(filePath) {
|
|
73
118
|
if (process.platform !== "win32") (0, fs.chmodSync)(filePath, 493);
|
|
74
119
|
}
|
|
75
|
-
var SPINNER_FRAMES = [
|
|
76
|
-
"⠋",
|
|
77
|
-
"⠙",
|
|
78
|
-
"⠹",
|
|
79
|
-
"⠸",
|
|
80
|
-
"⠼",
|
|
81
|
-
"⠴",
|
|
82
|
-
"⠦",
|
|
83
|
-
"⠧",
|
|
84
|
-
"⠇",
|
|
85
|
-
"⠏"
|
|
86
|
-
];
|
|
87
|
-
function isInteractiveTerminal() {
|
|
88
|
-
return process.stderr.isTTY === true;
|
|
89
|
-
}
|
|
90
120
|
function createDownloadProgress() {
|
|
91
|
-
|
|
92
|
-
let frame = 0;
|
|
93
|
-
let message = "";
|
|
94
|
-
const clearLine = () => {
|
|
95
|
-
if (timer) {
|
|
96
|
-
clearInterval(timer);
|
|
97
|
-
timer = void 0;
|
|
98
|
-
}
|
|
99
|
-
if (isInteractiveTerminal()) process.stderr.write("\r\x1B[K");
|
|
100
|
-
};
|
|
121
|
+
const spinner = createSpinner();
|
|
101
122
|
return {
|
|
102
|
-
start(
|
|
103
|
-
message
|
|
104
|
-
if (!isInteractiveTerminal()) {
|
|
105
|
-
console.error(message);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const render = () => {
|
|
109
|
-
process.stderr.write(`\r${SPINNER_FRAMES[frame]} ${message}`);
|
|
110
|
-
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
111
|
-
};
|
|
112
|
-
render();
|
|
113
|
-
timer = setInterval(render, 80);
|
|
123
|
+
start(message) {
|
|
124
|
+
spinner.start(message);
|
|
114
125
|
},
|
|
115
|
-
succeed(
|
|
116
|
-
|
|
117
|
-
console.error(
|
|
126
|
+
succeed(message) {
|
|
127
|
+
spinner.stop();
|
|
128
|
+
console.error(message);
|
|
118
129
|
},
|
|
119
130
|
fail() {
|
|
120
|
-
|
|
131
|
+
spinner.stop();
|
|
121
132
|
}
|
|
122
133
|
};
|
|
123
134
|
}
|