@mistweaverco/kulala-cli 0.8.3 → 0.9.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 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.8.3",
49
+ version: "0.9.0",
50
50
  repository: {
51
51
  "type": "git",
52
52
  "url": "https://github.com/mistweaverco/kulala-cli"
@@ -3474,7 +3474,7 @@ function fileWalker(inputPath, extensions) {
3474
3474
  }
3475
3475
  //#endregion
3476
3476
  //#region src/versions/backend.ts
3477
- var KULALA_CORE_VERSION = "0.24.3";
3477
+ var KULALA_CORE_VERSION = "0.24.4";
3478
3478
  //#endregion
3479
3479
  //#region src/lib/downloader/index.ts
3480
3480
  var BINARY_NAME = "kulala-core";
@@ -3632,63 +3632,8 @@ var downloader = {
3632
3632
  tryInstallBackend
3633
3633
  };
3634
3634
  //#endregion
3635
- //#region src/lib/kulala-core/index.ts
3636
- var cachedExecutable = null;
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) => {
3635
+ //#region src/lib/output/shared.ts
3636
+ var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
3692
3637
  var p = process || {}, argv = p.argv || [], env = p.env || {};
3693
3638
  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
3639
  var formatter = (open, close, replace = open) => (input) => {
@@ -3753,7 +3698,303 @@ var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3753
3698
  };
3754
3699
  module.exports = createColors();
3755
3700
  module.exports.createColors = createColors;
3756
- }));
3701
+ })))(), 1);
3702
+ function isPromptResponse(item) {
3703
+ if ("prompt" in item && item.prompt === true) return true;
3704
+ const maybe = item;
3705
+ return Boolean(maybe.promptId && maybe.promptType);
3706
+ }
3707
+ function findFirstPromptItem(wrapper) {
3708
+ return (Array.isArray(wrapper) ? wrapper : wrapper.data).find((item) => isPromptResponse(item));
3709
+ }
3710
+ function isSkippedResponse(item) {
3711
+ return "skipped" in item && item.skipped === true;
3712
+ }
3713
+ function isWebSocketResponse(item) {
3714
+ return "protocol" in item && item.protocol === "websocket";
3715
+ }
3716
+ function isErrorResponse(item) {
3717
+ return item.success === false && !isPromptResponse(item);
3718
+ }
3719
+ function isSuccessResponse(item) {
3720
+ return item.success === true && !isSkippedResponse(item) && !isWebSocketResponse(item);
3721
+ }
3722
+ function responseBodyText(body) {
3723
+ if (!body) return "";
3724
+ if (body.type === "json") return body.formatted ?? JSON.stringify(body.content, null, 2);
3725
+ if (body.type === "binary") return "";
3726
+ return body.content;
3727
+ }
3728
+ function responseBodyLanguage(body) {
3729
+ if (!body) return "text";
3730
+ if (body.type === "json") return "json";
3731
+ if (body.type === "binary") return "text";
3732
+ const mediaType = body.mediaType?.toLowerCase() ?? "";
3733
+ if (mediaType.includes("json")) return "json";
3734
+ if (mediaType.includes("xml")) return "xml";
3735
+ if (mediaType.includes("html")) return "html";
3736
+ if (mediaType.includes("javascript") || mediaType.includes("ecmascript")) return "javascript";
3737
+ if (mediaType.includes("graphql")) return "graphql";
3738
+ return "text";
3739
+ }
3740
+ function formatMs(ms) {
3741
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
3742
+ return `${(ms / 1e3).toFixed(2)}s`;
3743
+ }
3744
+ function escapeCell(value) {
3745
+ return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
3746
+ }
3747
+ function mdTable(rows) {
3748
+ if (rows.length === 0) return "";
3749
+ const header = rows[0];
3750
+ const separator = header.map(() => "---");
3751
+ const body = rows.slice(1);
3752
+ const formatRow = (row) => `| ${row.join(" | ")} |`;
3753
+ return [
3754
+ formatRow(header),
3755
+ formatRow(separator),
3756
+ ...body.map(formatRow)
3757
+ ].join("\n");
3758
+ }
3759
+ function isStructuredScriptLine(line) {
3760
+ return (line.kind === "test" || line.kind === "assert") && (line.status === "pass" || line.status === "fail");
3761
+ }
3762
+ function finalizeOpenTest(openTest, tests) {
3763
+ if (!openTest) return;
3764
+ if (!tests.includes(openTest)) tests.push(openTest);
3765
+ }
3766
+ function assertMessageAlreadyShown(message, tests) {
3767
+ return tests.some((test) => test.error === message || test.asserts.some((assert) => assert.message === message));
3768
+ }
3769
+ function parseAssertionTree(lines) {
3770
+ const tests = [];
3771
+ const standaloneAsserts = [];
3772
+ let openTest = null;
3773
+ const hasStructuredOutput = (lines ?? []).some((line) => line.kind === "assert" || line.kind === "test");
3774
+ for (const line of lines ?? []) {
3775
+ if (line.kind === "assert") {
3776
+ const assert = {
3777
+ pass: line.status === "pass",
3778
+ message: line.message
3779
+ };
3780
+ if (line.testName) {
3781
+ if (!openTest || openTest.name !== line.testName) {
3782
+ finalizeOpenTest(openTest, tests);
3783
+ openTest = {
3784
+ name: line.testName,
3785
+ pass: true,
3786
+ asserts: []
3787
+ };
3788
+ }
3789
+ openTest.asserts.push(assert);
3790
+ if (!assert.pass) openTest.pass = false;
3791
+ } else {
3792
+ finalizeOpenTest(openTest, tests);
3793
+ openTest = null;
3794
+ standaloneAsserts.push(assert);
3795
+ }
3796
+ continue;
3797
+ }
3798
+ if (line.kind === "test") {
3799
+ const parsed = {
3800
+ pass: line.status === "pass",
3801
+ name: line.testName,
3802
+ error: line.status === "fail" && line.level === "error" ? line.message : void 0
3803
+ };
3804
+ if (openTest && openTest.name === parsed.name) {
3805
+ openTest.pass = parsed.pass;
3806
+ openTest.error = parsed.error;
3807
+ if (!parsed.pass && parsed.error && openTest.asserts.length === 0) openTest.asserts.push({
3808
+ pass: false,
3809
+ message: parsed.error
3810
+ });
3811
+ finalizeOpenTest(openTest, tests);
3812
+ openTest = null;
3813
+ continue;
3814
+ }
3815
+ finalizeOpenTest(openTest, tests);
3816
+ openTest = null;
3817
+ if (!parsed.name) continue;
3818
+ const testGroup = {
3819
+ name: parsed.name,
3820
+ pass: parsed.pass,
3821
+ error: parsed.error,
3822
+ asserts: []
3823
+ };
3824
+ if (!parsed.pass && parsed.error) testGroup.asserts.push({
3825
+ pass: false,
3826
+ message: parsed.error
3827
+ });
3828
+ tests.push(testGroup);
3829
+ continue;
3830
+ }
3831
+ if (line.level === "error" && line.kind !== "log") {
3832
+ const message = line.message;
3833
+ if (hasStructuredOutput || assertMessageAlreadyShown(message, tests)) continue;
3834
+ finalizeOpenTest(openTest, tests);
3835
+ openTest = null;
3836
+ standaloneAsserts.push({
3837
+ pass: false,
3838
+ message
3839
+ });
3840
+ }
3841
+ }
3842
+ finalizeOpenTest(openTest, tests);
3843
+ return {
3844
+ tests,
3845
+ standaloneAsserts
3846
+ };
3847
+ }
3848
+ function isPreRequestPhase(origin) {
3849
+ if (!origin) return false;
3850
+ const phase = String(origin.phase);
3851
+ return phase === "preRequest" || phase === "pre-request" || phase === "pre_request";
3852
+ }
3853
+ function scriptDisplayPath(originFile, requestFile) {
3854
+ if (!originFile) return;
3855
+ if (requestFile) {
3856
+ const relative = node_path.default.relative(node_path.default.dirname(requestFile), originFile);
3857
+ if (relative && !relative.startsWith("..")) return relative;
3858
+ }
3859
+ return originFile.split("/").pop();
3860
+ }
3861
+ function formatScriptOrigin(origin, requestFile) {
3862
+ if (!origin) return "[?]";
3863
+ const source = origin.source ?? "?";
3864
+ const file = scriptDisplayPath(origin.file, requestFile);
3865
+ const location = origin.line !== void 0 ? `L${origin.line}${origin.column !== void 0 ? `:${origin.column}` : ""}` : `directive L${origin.httpDirectiveLine ?? "?"}`;
3866
+ return file ? `[${source} · ${file} · ${location}]` : `[${source} · ${location}]`;
3867
+ }
3868
+ function splitScriptConsole(lines) {
3869
+ const pre = [];
3870
+ const post = [];
3871
+ const hasStructuredOutput = (lines ?? []).some((line) => line.kind === "assert" || line.kind === "test");
3872
+ const tree = parseAssertionTree(lines);
3873
+ const skipScriptErrors = /* @__PURE__ */ new Set();
3874
+ for (const test of tree.tests) {
3875
+ if (test.error) skipScriptErrors.add(test.error);
3876
+ for (const assert of test.asserts) skipScriptErrors.add(assert.message);
3877
+ }
3878
+ for (const assert of tree.standaloneAsserts) skipScriptErrors.add(assert.message);
3879
+ for (const line of lines ?? []) {
3880
+ if (isStructuredScriptLine(line)) continue;
3881
+ if (line.level === "error" && line.message.startsWith("Error executing script: ")) {
3882
+ const message = line.message.replace(/^Error executing script: /, "");
3883
+ if (hasStructuredOutput || skipScriptErrors.has(message)) continue;
3884
+ }
3885
+ if (isPreRequestPhase(line.origin)) pre.push(line);
3886
+ else post.push(line);
3887
+ }
3888
+ return {
3889
+ pre,
3890
+ post
3891
+ };
3892
+ }
3893
+ function formatLabeledField(label, value) {
3894
+ return import_picocolors.default.bold(`${import_picocolors.default.cyan(`${label}:`)} ${value}`);
3895
+ }
3896
+ function formatRunHeader(filepath, blockName) {
3897
+ return [formatLabeledField("Filepath", filepath), formatLabeledField("Block name", blockName)].join("\n");
3898
+ }
3899
+ function itemDisplayName(item) {
3900
+ if ("blockName" in item && item.blockName) return item.blockName;
3901
+ return itemTitle(item);
3902
+ }
3903
+ function itemTitle(item) {
3904
+ if (isPromptResponse(item)) return `Prompt: ${item.promptType}`;
3905
+ if (isSkippedResponse(item)) return item.blockName ? `Skipped — ${item.blockName}` : "Skipped";
3906
+ if (isWebSocketResponse(item)) return `WebSocket — ${item.url}`;
3907
+ if (isErrorResponse(item)) return `${item.request?.method ?? "REQUEST"} ${item.url ?? item.blockName ?? "unknown"}`;
3908
+ if (isSuccessResponse(item)) return `${item.request?.method ?? "GET"} ${item.url}`;
3909
+ return "Unknown request";
3910
+ }
3911
+ //#endregion
3912
+ //#region src/lib/kulala-core/index.ts
3913
+ var cachedExecutable = null;
3914
+ async function executablePath() {
3915
+ if (!cachedExecutable) cachedExecutable = await downloader.ensureInstalled();
3916
+ if (!cachedExecutable) throw new Error("kulala-core executable not resolved");
3917
+ return cachedExecutable;
3918
+ }
3919
+ function invokeRaw(payload, options = {}) {
3920
+ const exe = cachedExecutable;
3921
+ if (!exe) throw new Error("kulala-core executable not resolved");
3922
+ const result = (0, node_child_process.spawnSync)(exe, [], {
3923
+ input: `${JSON.stringify(payload)}\n`,
3924
+ encoding: "utf-8",
3925
+ maxBuffer: 50 * 1024 * 1024,
3926
+ cwd: options.cwd,
3927
+ env: process.env
3928
+ });
3929
+ if (result.error) throw result.error;
3930
+ return {
3931
+ stdout: result.stdout ?? "",
3932
+ stderr: result.stderr ?? "",
3933
+ status: result.status
3934
+ };
3935
+ }
3936
+ function tryDecodeWrapper(stdout) {
3937
+ const raw = stdout.trim();
3938
+ if (!raw) return;
3939
+ try {
3940
+ const wrapper = JSON.parse(raw);
3941
+ if (wrapper && typeof wrapper === "object" && wrapper.type) return wrapper;
3942
+ } catch {
3943
+ return;
3944
+ }
3945
+ }
3946
+ function parseInvokeResponse(job) {
3947
+ const wrapper = tryDecodeWrapper(job.stdout);
3948
+ const first = wrapper?.type === "responses" ? wrapper.data[0] : void 0;
3949
+ const isPrompt = Boolean(first && isPromptResponse(first));
3950
+ if (job.status !== 0 && !isPrompt) throw new Error(job.stderr?.trim() || `kulala-core exited with code ${job.status ?? "unknown"}`);
3951
+ if (!wrapper) throw new Error(job.stderr?.trim() || "kulala-core returned empty or invalid output");
3952
+ return wrapper;
3953
+ }
3954
+ async function runHttp(options, invokeOptions = {}) {
3955
+ await executablePath();
3956
+ return parseInvokeResponse(invokeRaw({
3957
+ action: "run",
3958
+ content: options.content,
3959
+ filepath: options.filepath,
3960
+ env: options.env,
3961
+ limit: options.limit,
3962
+ haltOnError: options.haltOnError
3963
+ }, invokeOptions));
3964
+ }
3965
+ async function continueHttp(options, invokeOptions = {}) {
3966
+ await executablePath();
3967
+ return parseInvokeResponse(invokeRaw({
3968
+ action: "continue",
3969
+ promptId: options.promptId,
3970
+ inputs: options.inputs
3971
+ }, invokeOptions));
3972
+ }
3973
+ async function environments(options = {}, invokeOptions = {}) {
3974
+ await executablePath();
3975
+ const job = invokeRaw({
3976
+ action: "environments",
3977
+ cwd: options.cwd,
3978
+ filepath: options.filepath
3979
+ }, invokeOptions);
3980
+ if (job.status !== 0) throw new Error(job.stderr?.trim() || `kulala-core exited with code ${job.status ?? "unknown"}`);
3981
+ const raw = job.stdout.trim();
3982
+ if (!raw) throw new Error("kulala-core returned empty output");
3983
+ return JSON.parse(raw);
3984
+ }
3985
+ async function curl$1(options, invokeOptions = {}) {
3986
+ await executablePath();
3987
+ return parseInvokeResponse(invokeRaw({
3988
+ action: "curl",
3989
+ argv: options.argv
3990
+ }, invokeOptions));
3991
+ }
3992
+ var kulalaCore = {
3993
+ runHttp,
3994
+ continueHttp,
3995
+ environments,
3996
+ curl: curl$1
3997
+ };
3757
3998
  //#endregion
3758
3999
  //#region node_modules/.pnpm/highlight.js@10.7.3/node_modules/highlight.js/lib/core.js
3759
4000
  var require_core = /* @__PURE__ */ __commonJSMin(((exports, module) => {
@@ -62702,8 +62943,8 @@ var require_theme = /* @__PURE__ */ __commonJSMin(((exports) => {
62702
62943
  exports.parse = parse;
62703
62944
  }));
62704
62945
  //#endregion
62705
- //#region node_modules/.pnpm/cli-highlight@2.1.11/node_modules/cli-highlight/dist/index.js
62706
- var require_dist = /* @__PURE__ */ __commonJSMin(((exports) => {
62946
+ //#region src/lib/output/highlight.ts
62947
+ var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
62707
62948
  var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
62708
62949
  if (k2 === void 0) k2 = k;
62709
62950
  Object.defineProperty(o, k2, {
@@ -62818,11 +63059,7 @@ var require_dist = /* @__PURE__ */ __commonJSMin(((exports) => {
62818
63059
  exports.supportsLanguage = supportsLanguage;
62819
63060
  exports.default = highlight;
62820
63061
  __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();
63062
+ })))();
62826
63063
  var colorOverride;
62827
63064
  function setColorEnabled(enabled) {
62828
63065
  colorOverride = enabled;
@@ -66666,212 +66903,6 @@ function renderImageInline(body) {
66666
66903
  return null;
66667
66904
  }
66668
66905
  //#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
66906
  //#region src/lib/output/human.ts
66876
66907
  function statusColor(status) {
66877
66908
  if (status >= 200 && status < 300) return import_picocolors.default.green;
@@ -67018,6 +67049,13 @@ function formatItem(item, requestFile) {
67018
67049
  function formatWrapper(wrapper, requestFile) {
67019
67050
  return (wrapper.type === "error" ? wrapper.data : wrapper.data).map((entry) => formatItem(entry, requestFile)).join("\n\n");
67020
67051
  }
67052
+ function printResponseItems(filepath, items) {
67053
+ if (items.length === 0) return;
67054
+ console.log(formatWrapper({
67055
+ type: "responses",
67056
+ data: items
67057
+ }, filepath));
67058
+ }
67021
67059
  function printHumanReadable(results) {
67022
67060
  const blocks = results.map((result) => formatWrapper(result.response, result.filepath));
67023
67061
  console.log(blocks.join("\n\n"));
@@ -67239,6 +67277,139 @@ function printTests(results, options) {
67239
67277
  if (blocks.length > 0 && !options.quiet) console.log(blocks.join("\n\n"));
67240
67278
  }
67241
67279
  //#endregion
67280
+ //#region src/lib/prompt.ts
67281
+ var pipedBuffer = "";
67282
+ var pipedEnded = false;
67283
+ var pipedStdinInitialized = false;
67284
+ var pipedWaiters = [];
67285
+ function flushPipedWaiters() {
67286
+ while (pipedWaiters.length > 0) {
67287
+ const newlineIndex = pipedBuffer.indexOf("\n");
67288
+ if (newlineIndex === -1) {
67289
+ if (pipedEnded) {
67290
+ const line = pipedBuffer.replace(/\r?\n$/u, "");
67291
+ pipedBuffer = "";
67292
+ pipedWaiters.shift()?.(line.length > 0 ? line : void 0);
67293
+ continue;
67294
+ }
67295
+ break;
67296
+ }
67297
+ const line = pipedBuffer.slice(0, newlineIndex).replace(/\r$/u, "");
67298
+ pipedBuffer = pipedBuffer.slice(newlineIndex + 1);
67299
+ pipedWaiters.shift()?.(line);
67300
+ }
67301
+ if (pipedEnded) while (pipedWaiters.length > 0) pipedWaiters.shift()?.(void 0);
67302
+ }
67303
+ function initPipedStdin() {
67304
+ if (pipedStdinInitialized || node_process.stdin.isTTY) return;
67305
+ pipedStdinInitialized = true;
67306
+ node_process.stdin.setEncoding("utf8");
67307
+ node_process.stdin.on("data", (chunk) => {
67308
+ pipedBuffer += chunk;
67309
+ flushPipedWaiters();
67310
+ });
67311
+ node_process.stdin.on("end", () => {
67312
+ pipedEnded = true;
67313
+ flushPipedWaiters();
67314
+ });
67315
+ node_process.stdin.resume();
67316
+ }
67317
+ async function readPipedLine() {
67318
+ initPipedStdin();
67319
+ const newlineIndex = pipedBuffer.indexOf("\n");
67320
+ if (newlineIndex !== -1) {
67321
+ const line = pipedBuffer.slice(0, newlineIndex).replace(/\r$/u, "");
67322
+ pipedBuffer = pipedBuffer.slice(newlineIndex + 1);
67323
+ return line;
67324
+ }
67325
+ if (pipedEnded) {
67326
+ const line = pipedBuffer.replace(/\r?\n$/u, "");
67327
+ pipedBuffer = "";
67328
+ return line.length > 0 ? line : void 0;
67329
+ }
67330
+ return new Promise((resolve) => {
67331
+ pipedWaiters.push(resolve);
67332
+ });
67333
+ }
67334
+ async function readVisibleLine(label) {
67335
+ if (!node_process.stdin.isTTY) return readPipedLine();
67336
+ const rl = (0, node_readline.createInterface)({
67337
+ input: node_process.stdin,
67338
+ output: node_process.stdout
67339
+ });
67340
+ try {
67341
+ return await new Promise((resolve) => {
67342
+ rl.question(`${label}: `, resolve);
67343
+ });
67344
+ } finally {
67345
+ rl.close();
67346
+ }
67347
+ }
67348
+ async function readHiddenLine(label) {
67349
+ if (!node_process.stdin.isTTY) return readPipedLine();
67350
+ if (typeof node_process.stdin.setRawMode !== "function") return readVisibleLine(label);
67351
+ node_process.stdout.write(`${label}: `);
67352
+ node_process.stdin.setRawMode(true);
67353
+ node_process.stdin.resume();
67354
+ node_process.stdin.setEncoding("utf8");
67355
+ return new Promise((resolve) => {
67356
+ let value = "";
67357
+ const cleanup = (result) => {
67358
+ node_process.stdin.setRawMode(false);
67359
+ node_process.stdin.pause();
67360
+ node_process.stdin.removeListener("data", onData);
67361
+ node_process.stdout.write("\n");
67362
+ resolve(result);
67363
+ };
67364
+ const onData = (chunk) => {
67365
+ for (const char of chunk) {
67366
+ if (char === "\n" || char === "\r" || char === "") {
67367
+ cleanup(value);
67368
+ return;
67369
+ }
67370
+ if (char === "") {
67371
+ cleanup(void 0);
67372
+ return;
67373
+ }
67374
+ if (char === "" || char === "\b") {
67375
+ if (value.length > 0) value = value.slice(0, -1);
67376
+ continue;
67377
+ }
67378
+ value += char;
67379
+ }
67380
+ };
67381
+ node_process.stdin.on("data", onData);
67382
+ });
67383
+ }
67384
+ async function readPromptValue(label, type) {
67385
+ if (type === "password") return readHiddenLine(label);
67386
+ return readVisibleLine(label);
67387
+ }
67388
+ async function collectPromptInputs(prompt) {
67389
+ const specs = prompt.inputs ?? [];
67390
+ if (specs.length === 0) {
67391
+ console.error(chalk.yellow("Kulala prompt has no inputs."));
67392
+ return;
67393
+ }
67394
+ const out = [];
67395
+ for (const spec of specs) {
67396
+ const id = spec.id;
67397
+ if (!id) return;
67398
+ const label = spec.label?.trim() || id;
67399
+ const value = await readPromptValue(label, spec.type ?? "text");
67400
+ if (value === void 0) return;
67401
+ if (spec.required && !value.trim()) {
67402
+ console.error(chalk.yellow(`Required input missing: ${label}`));
67403
+ return;
67404
+ }
67405
+ out.push({
67406
+ id,
67407
+ value
67408
+ });
67409
+ }
67410
+ return out;
67411
+ }
67412
+ //#endregion
67242
67413
  //#region src/lib/runner/limit.ts
67243
67414
  function buildRunLimit(options) {
67244
67415
  const hasName = options.name !== void 0 && options.name.length > 0;
@@ -67297,6 +67468,7 @@ function resolveSingleHttpFile(inputPath) {
67297
67468
  }
67298
67469
  //#endregion
67299
67470
  //#region src/lib/runner/index.ts
67471
+ var MAX_PROMPT_DEPTH = 7;
67300
67472
  var HTTP_EXTENSIONS = [".http", ".rest"];
67301
67473
  function shuffleFiles(items) {
67302
67474
  const copy = [...items];
@@ -67319,20 +67491,107 @@ function resolveFiles(inputPath, shuffle) {
67319
67491
  function responseHasFailure(result) {
67320
67492
  return countResults(result.response).failed > 0;
67321
67493
  }
67322
- async function runFile(relativePath, env, limit, halt) {
67494
+ function shouldStreamOutput(ctx) {
67495
+ return !ctx.json && !ctx.tests;
67496
+ }
67497
+ function itemsToStream(items, quiet) {
67498
+ if (!quiet) return items;
67499
+ return items.filter((item) => !isResponseSuccessful(item));
67500
+ }
67501
+ function streamResponseItems(filepath, items, ctx) {
67502
+ if (!shouldStreamOutput(ctx)) return;
67503
+ printResponseItems(filepath, itemsToStream(items, ctx.quiet ?? false));
67504
+ }
67505
+ function continueSucceeded(response) {
67506
+ return (response.type === "responses" ? response.data[0] : void 0)?.success === true;
67507
+ }
67508
+ function mergeRunResponses(accumulated, response) {
67509
+ if (response.type === "error" || accumulated.length === 0) return response;
67510
+ return {
67511
+ type: "responses",
67512
+ data: [...accumulated, ...response.data]
67513
+ };
67514
+ }
67515
+ function completedItemsBeforePrompt(response) {
67516
+ if (response.type !== "responses") return [];
67517
+ const promptIndex = response.data.findIndex((item) => isPromptResponse(item));
67518
+ if (promptIndex <= 0) return [];
67519
+ return response.data.slice(0, promptIndex);
67520
+ }
67521
+ function newItemsSinceAccumulated(accumulated, response) {
67522
+ if (response.type !== "responses") return [];
67523
+ return response.data.slice(accumulated.length);
67524
+ }
67525
+ function promptBlockName(promptItem) {
67526
+ if ("blockName" in promptItem && typeof promptItem.blockName === "string") return promptItem.blockName.trim() || void 0;
67527
+ }
67528
+ async function runFileWithPromptRetry(relativePath, env, limit, halt, output, depth = 0, accumulated = []) {
67323
67529
  const absolutePath = node_path.default.resolve(process.cwd(), relativePath);
67324
67530
  const content = node_fs.default.readFileSync(absolutePath, "utf-8");
67325
67531
  const cwd = node_path.default.dirname(absolutePath);
67326
- return {
67327
- filepath: relativePath,
67328
- response: await kulalaCore.runHttp({
67329
- content,
67330
- filepath: absolutePath,
67331
- env,
67332
- limit,
67333
- haltOnError: halt
67334
- }, { cwd })
67532
+ const runOptions = {
67533
+ content,
67534
+ filepath: absolutePath,
67535
+ env,
67536
+ limit,
67537
+ haltOnError: halt
67335
67538
  };
67539
+ const response = await kulalaCore.runHttp(runOptions, { cwd });
67540
+ const promptItem = response.type === "responses" ? findFirstPromptItem(response) : void 0;
67541
+ if (!promptItem) {
67542
+ const final = mergeRunResponses(accumulated, response);
67543
+ streamResponseItems(relativePath, newItemsSinceAccumulated(accumulated, final), output);
67544
+ return {
67545
+ filepath: relativePath,
67546
+ response: final,
67547
+ outputStreamed: shouldStreamOutput(output)
67548
+ };
67549
+ }
67550
+ if (depth >= MAX_PROMPT_DEPTH) {
67551
+ console.error(chalk.red("Kulala: exceeded prompt / retry limit."));
67552
+ return {
67553
+ filepath: relativePath,
67554
+ response: mergeRunResponses(accumulated, response),
67555
+ outputStreamed: shouldStreamOutput(output)
67556
+ };
67557
+ }
67558
+ const completedBefore = completedItemsBeforePrompt(response);
67559
+ const newAccumulated = [...accumulated, ...completedBefore];
67560
+ streamResponseItems(relativePath, completedBefore, output);
67561
+ const inputs = await collectPromptInputs(promptItem);
67562
+ if (!inputs || !promptItem.promptId) {
67563
+ console.error(chalk.yellow("Prompt cancelled or incomplete."));
67564
+ return {
67565
+ filepath: relativePath,
67566
+ response: mergeRunResponses(newAccumulated, response),
67567
+ outputStreamed: shouldStreamOutput(output)
67568
+ };
67569
+ }
67570
+ const continued = await kulalaCore.continueHttp({
67571
+ promptId: promptItem.promptId,
67572
+ inputs
67573
+ }, { cwd });
67574
+ if (!continueSucceeded(continued)) {
67575
+ let err;
67576
+ if (continued.type === "error") err = continued.data[0]?.error;
67577
+ else {
67578
+ const item = continued.data[0];
67579
+ err = item && "error" in item ? item.error : void 0;
67580
+ }
67581
+ console.error(chalk.red(err ?? "Prompt continuation failed."));
67582
+ return {
67583
+ filepath: relativePath,
67584
+ response: continued
67585
+ };
67586
+ }
67587
+ const blockName = promptBlockName(promptItem);
67588
+ return runFileWithPromptRetry(relativePath, env, completedBefore.length > 0 && blockName ? [{
67589
+ filter: "name",
67590
+ name: blockName
67591
+ }] : limit, halt, output, depth + 1, newAccumulated);
67592
+ }
67593
+ async function runFile(relativePath, env, limit, halt, output) {
67594
+ return runFileWithPromptRetry(relativePath, env, limit, halt, output);
67336
67595
  }
67337
67596
  function hasFailures(results) {
67338
67597
  return results.some((result) => responseHasFailure(result));
@@ -67340,20 +67599,28 @@ function hasFailures(results) {
67340
67599
  async function run(inputPath, options) {
67341
67600
  if (options.color === false) setColorEnabled(false);
67342
67601
  if (options.report && !options.tests) options.tests = true;
67602
+ const output = {
67603
+ json: options.json,
67604
+ tests: options.tests,
67605
+ quiet: options.quiet
67606
+ };
67343
67607
  const limit = buildRunLimit(options);
67344
67608
  const files = limit ? [resolveSingleHttpFile(inputPath)] : resolveFiles(inputPath, options.shuffle ?? false);
67345
67609
  const results = [];
67346
67610
  const halt = options.halt ?? false;
67347
67611
  for (const file of files) {
67348
- const result = await runFile(file, options.env, limit, halt);
67612
+ const result = await runFile(file, options.env, limit, halt, output);
67349
67613
  results.push(result);
67350
67614
  if (halt && responseHasFailure(result)) break;
67351
67615
  }
67352
- const outputResults = options.quiet && !options.tests ? filterFailedResults(results) : results;
67616
+ const pendingOutput = results.filter((result) => !result.outputStreamed);
67617
+ const outputResults = options.quiet && !options.tests ? filterFailedResults(pendingOutput) : pendingOutput;
67353
67618
  if (options.tests) printTests(results, { quiet: options.quiet ?? false });
67354
- else if (!options.quiet || outputResults.length > 0) if (options.json) printJson(outputResults);
67355
- else if (options.report) printReport(outputResults);
67356
- else printHumanReadable(outputResults);
67619
+ else if (!options.quiet || outputResults.length > 0) {
67620
+ if (options.json) printJson(results);
67621
+ else if (options.report) printReport(pendingOutput);
67622
+ else if (pendingOutput.length > 0) printHumanReadable(outputResults);
67623
+ }
67357
67624
  if (hasFailures(results)) process.exit(1);
67358
67625
  }
67359
67626
  //#endregion
@@ -29,7 +29,7 @@ let path = require("path");
29
29
  path = __toESM(path, 1);
30
30
  let stream_promises = require("stream/promises");
31
31
  //#region src/versions/backend.ts
32
- var KULALA_CORE_VERSION = "0.24.3";
32
+ var KULALA_CORE_VERSION = "0.24.4";
33
33
  //#endregion
34
34
  //#region src/lib/downloader/index.ts
35
35
  var BINARY_NAME = "kulala-core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mistweaverco/kulala-cli",
3
- "version": "0.8.3",
3
+ "version": "0.9.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mistweaverco/kulala-cli"