@use-lattice/litmus 0.121.3

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.
Files changed (199) hide show
  1. package/LICENSE +19 -0
  2. package/dist/src/accounts-Bt1oJb1Z.cjs +219 -0
  3. package/dist/src/accounts-DjOU8Rm3.js +178 -0
  4. package/dist/src/agentic-utils-D03IiXQc.js +153 -0
  5. package/dist/src/agentic-utils-Dh7xaMQM.cjs +180 -0
  6. package/dist/src/agents-C6BIMlZa.js +231 -0
  7. package/dist/src/agents-DvIpNX1L.cjs +666 -0
  8. package/dist/src/agents-ZP0RP9vV.cjs +231 -0
  9. package/dist/src/agents-maJXdjbR.js +665 -0
  10. package/dist/src/aimlapi-BTbQjG2E.cjs +30 -0
  11. package/dist/src/aimlapi-CwMxqfXP.js +30 -0
  12. package/dist/src/audio-BBUdvsde.cjs +97 -0
  13. package/dist/src/audio-D5DPZ7I-.js +97 -0
  14. package/dist/src/base-BEysXrkq.cjs +222 -0
  15. package/dist/src/base-C451JQfq.js +193 -0
  16. package/dist/src/blobs-BY8MDmpo.js +230 -0
  17. package/dist/src/blobs-BgcNn97m.cjs +256 -0
  18. package/dist/src/cache-BBE_lsTA.cjs +4 -0
  19. package/dist/src/cache-BkrqU5Ba.js +237 -0
  20. package/dist/src/cache-DsCxFlsZ.cjs +297 -0
  21. package/dist/src/chat-CPJWDP6a.cjs +289 -0
  22. package/dist/src/chat-CXX3xzkk.cjs +811 -0
  23. package/dist/src/chat-CcDgZFJ4.js +787 -0
  24. package/dist/src/chat-Dz5ZeGO2.js +289 -0
  25. package/dist/src/chatkit-Dw0mKkML.cjs +1158 -0
  26. package/dist/src/chatkit-swAIVuea.js +1157 -0
  27. package/dist/src/chunk-DEq-mXcV.js +15 -0
  28. package/dist/src/claude-agent-sdk-BXZJtOg6.js +379 -0
  29. package/dist/src/claude-agent-sdk-CkfyjDoG.cjs +383 -0
  30. package/dist/src/cloudflare-ai-BzpJcqUH.js +161 -0
  31. package/dist/src/cloudflare-ai-Cmy_R1y2.cjs +161 -0
  32. package/dist/src/cloudflare-gateway-B9tVQKok.cjs +272 -0
  33. package/dist/src/cloudflare-gateway-DrD3ew3H.js +272 -0
  34. package/dist/src/codex-sdk-Dezj9Nwm.js +1056 -0
  35. package/dist/src/codex-sdk-Dl9D4k5B.cjs +1060 -0
  36. package/dist/src/cometapi-C-9YvCHC.js +54 -0
  37. package/dist/src/cometapi-DHgDKoO2.cjs +54 -0
  38. package/dist/src/completion-B8Ctyxpr.js +120 -0
  39. package/dist/src/completion-Cxrt08sj.cjs +131 -0
  40. package/dist/src/createHash-BwgE13yv.cjs +27 -0
  41. package/dist/src/createHash-DmPQkvBh.js +15 -0
  42. package/dist/src/docker-BiqcTwLv.js +80 -0
  43. package/dist/src/docker-C7tEJnP-.cjs +80 -0
  44. package/dist/src/esm-C62Zofr1.cjs +409 -0
  45. package/dist/src/esm-DMVc93eh.js +379 -0
  46. package/dist/src/evalResult-C3NJPQOo.cjs +301 -0
  47. package/dist/src/evalResult-C7JJAPBb.js +295 -0
  48. package/dist/src/evalResult-DoVTZZWI.cjs +2 -0
  49. package/dist/src/extractor-DnMD3fwt.cjs +391 -0
  50. package/dist/src/extractor-DtlL28vL.js +374 -0
  51. package/dist/src/fetch-BTxakTSg.cjs +1133 -0
  52. package/dist/src/fetch-DQckpUFz.js +928 -0
  53. package/dist/src/fileExtensions-DnqA1y9x.js +85 -0
  54. package/dist/src/fileExtensions-bYh77CN8.cjs +114 -0
  55. package/dist/src/genaiTracer-CyZrmaK0.cjs +268 -0
  56. package/dist/src/genaiTracer-D3fD9dNV.js +256 -0
  57. package/dist/src/graders-BNscxFrU.js +13644 -0
  58. package/dist/src/graders-D2oE9Msq.js +2 -0
  59. package/dist/src/graders-c0Ez_w-9.cjs +2 -0
  60. package/dist/src/graders-d0F2M3e9.cjs +14056 -0
  61. package/dist/src/image-0ZhE0VlR.cjs +280 -0
  62. package/dist/src/image-CWE1pdNv.js +257 -0
  63. package/dist/src/image-D9ZK6hwL.js +163 -0
  64. package/dist/src/image-DKZgZITg.cjs +163 -0
  65. package/dist/src/index.cjs +11366 -0
  66. package/dist/src/index.d.cts +19640 -0
  67. package/dist/src/index.d.ts +19641 -0
  68. package/dist/src/index.js +11306 -0
  69. package/dist/src/invariant-Ddh24eXh.js +25 -0
  70. package/dist/src/invariant-kfQ8Bu82.cjs +30 -0
  71. package/dist/src/knowledgeBase-BgPyGFUd.cjs +122 -0
  72. package/dist/src/knowledgeBase-DyHilYaP.js +122 -0
  73. package/dist/src/litellm-CyMeneHS.js +135 -0
  74. package/dist/src/litellm-DWDF73yF.cjs +135 -0
  75. package/dist/src/logger-C40ZGil9.js +717 -0
  76. package/dist/src/logger-DyfK9PBt.cjs +917 -0
  77. package/dist/src/luma-ray-BAU9X_ep.cjs +315 -0
  78. package/dist/src/luma-ray-nwVseBbv.js +313 -0
  79. package/dist/src/messages-B5ADWTTv.js +245 -0
  80. package/dist/src/messages-BCnZfqrS.cjs +257 -0
  81. package/dist/src/meteor-DLZZ3osF.cjs +134 -0
  82. package/dist/src/meteor-DUiCJRC-.js +134 -0
  83. package/dist/src/modelslab-00cveB8L.cjs +163 -0
  84. package/dist/src/modelslab-D9sCU_L7.js +163 -0
  85. package/dist/src/nova-reel-CTapvqYH.js +276 -0
  86. package/dist/src/nova-reel-DlWuuroF.cjs +278 -0
  87. package/dist/src/nova-sonic-5UPWfeMv.cjs +363 -0
  88. package/dist/src/nova-sonic-BhSwQNym.js +363 -0
  89. package/dist/src/openai-BWrJK9d8.cjs +52 -0
  90. package/dist/src/openai-DumO8WQn.js +47 -0
  91. package/dist/src/openclaw-B8brrjC_.cjs +577 -0
  92. package/dist/src/openclaw-Bkayww9q.js +571 -0
  93. package/dist/src/opencode-sdk-7xjoDNiM.cjs +562 -0
  94. package/dist/src/opencode-sdk-SGwAPxht.js +558 -0
  95. package/dist/src/otlpReceiver-CoAHfAN9.cjs +15 -0
  96. package/dist/src/otlpReceiver-oO3EQwI9.js +14 -0
  97. package/dist/src/providerRegistry-4yjhaEM8.js +45 -0
  98. package/dist/src/providerRegistry-DhV4rJIc.cjs +50 -0
  99. package/dist/src/providers-B5RJVG-7.cjs +33609 -0
  100. package/dist/src/providers-BdmZCLzV.js +33262 -0
  101. package/dist/src/providers-CxtRxn8e.js +2 -0
  102. package/dist/src/providers-DnQLNbx1.cjs +3 -0
  103. package/dist/src/pythonUtils-BD0druiM.cjs +275 -0
  104. package/dist/src/pythonUtils-IBhn5YGR.js +249 -0
  105. package/dist/src/quiverai-BDOwZBsM.cjs +213 -0
  106. package/dist/src/quiverai-D3JTF5lD.js +213 -0
  107. package/dist/src/responses-B2LCDCXZ.js +667 -0
  108. package/dist/src/responses-BvNm4Xv9.cjs +685 -0
  109. package/dist/src/rubyUtils-B0NwnfpY.cjs +245 -0
  110. package/dist/src/rubyUtils-BroxzZ7c.cjs +2 -0
  111. package/dist/src/rubyUtils-hqVw5UvJ.js +222 -0
  112. package/dist/src/sagemaker-Cno2V-Sx.js +689 -0
  113. package/dist/src/sagemaker-fV_KUgs5.cjs +691 -0
  114. package/dist/src/server-BOuAXb06.cjs +238 -0
  115. package/dist/src/server-CtI-EWzm.cjs +2 -0
  116. package/dist/src/server-Cy3DZymt.js +189 -0
  117. package/dist/src/slack-CP8xBePa.js +135 -0
  118. package/dist/src/slack-DSQ1yXVb.cjs +135 -0
  119. package/dist/src/store-BwDDaBjb.cjs +246 -0
  120. package/dist/src/store-DcbLC593.cjs +2 -0
  121. package/dist/src/store-IGpqMIkv.js +240 -0
  122. package/dist/src/tables-3Q2cL7So.cjs +373 -0
  123. package/dist/src/tables-Bi2fjr4W.js +288 -0
  124. package/dist/src/telemetry-Bg2WqF79.js +161 -0
  125. package/dist/src/telemetry-D0x6u5kX.cjs +166 -0
  126. package/dist/src/telemetry-DXNimrI0.cjs +2 -0
  127. package/dist/src/text-B_UCRPp2.js +22 -0
  128. package/dist/src/text-CW1cyrwj.cjs +33 -0
  129. package/dist/src/tokenUsageUtils-NYT-WKS6.js +138 -0
  130. package/dist/src/tokenUsageUtils-bVa1ga6f.cjs +173 -0
  131. package/dist/src/transcription-Cl_W16Pr.js +122 -0
  132. package/dist/src/transcription-yt1EecY8.cjs +124 -0
  133. package/dist/src/transform-BCtGrl_W.cjs +228 -0
  134. package/dist/src/transform-Bv6gG2MJ.cjs +1688 -0
  135. package/dist/src/transform-CY1wbpRy.js +1507 -0
  136. package/dist/src/transform-DU8rUL9P.cjs +2 -0
  137. package/dist/src/transform-yWaShiKr.js +216 -0
  138. package/dist/src/transformersAvailability-BGkzavwb.js +35 -0
  139. package/dist/src/transformersAvailability-DKoRtQLy.cjs +35 -0
  140. package/dist/src/types-5aqHpBwE.cjs +3769 -0
  141. package/dist/src/types-Bn6D9c4U.js +3300 -0
  142. package/dist/src/util-BkKlTkI2.js +293 -0
  143. package/dist/src/util-CTh0bfOm.cjs +1119 -0
  144. package/dist/src/util-D17oBwo7.cjs +328 -0
  145. package/dist/src/util-DsS_-v4p.js +613 -0
  146. package/dist/src/util-DuntT1Ga.js +951 -0
  147. package/dist/src/util-aWjdCYMI.cjs +667 -0
  148. package/dist/src/utils-CisQwpjA.js +94 -0
  149. package/dist/src/utils-yWamDvmz.cjs +123 -0
  150. package/dist/tsconfig.tsbuildinfo +1 -0
  151. package/drizzle/0000_lush_hellion.sql +36 -0
  152. package/drizzle/0001_wide_calypso.sql +3 -0
  153. package/drizzle/0002_tidy_juggernaut.sql +1 -0
  154. package/drizzle/0003_lively_naoko.sql +8 -0
  155. package/drizzle/0004_minor_peter_quill.sql +19 -0
  156. package/drizzle/0005_silky_millenium_guard.sql +2 -0
  157. package/drizzle/0006_harsh_caretaker.sql +42 -0
  158. package/drizzle/0007_cloudy_wong.sql +1 -0
  159. package/drizzle/0008_broad_boomer.sql +2 -0
  160. package/drizzle/0009_strong_marten_broadcloak.sql +19 -0
  161. package/drizzle/0010_needy_bishop.sql +11 -0
  162. package/drizzle/0011_moaning_millenium_guard.sql +1 -0
  163. package/drizzle/0012_late_marten_broadcloak.sql +2 -0
  164. package/drizzle/0013_previous_dormammu.sql +9 -0
  165. package/drizzle/0014_lazy_captain_universe.sql +2 -0
  166. package/drizzle/0015_zippy_wallop.sql +29 -0
  167. package/drizzle/0016_jazzy_zemo.sql +2 -0
  168. package/drizzle/0017_reflective_praxagora.sql +4 -0
  169. package/drizzle/0018_fat_vanisher.sql +22 -0
  170. package/drizzle/0019_new_clint_barton.sql +8 -0
  171. package/drizzle/0020_skinny_maverick.sql +1 -0
  172. package/drizzle/0021_mysterious_madelyne_pryor.sql +13 -0
  173. package/drizzle/0022_sleepy_ultimo.sql +25 -0
  174. package/drizzle/0023_wooden_mandrill.sql +2 -0
  175. package/drizzle/AGENTS.md +68 -0
  176. package/drizzle/CLAUDE.md +1 -0
  177. package/drizzle/meta/0000_snapshot.json +221 -0
  178. package/drizzle/meta/0001_snapshot.json +214 -0
  179. package/drizzle/meta/0002_snapshot.json +221 -0
  180. package/drizzle/meta/0005_snapshot.json +369 -0
  181. package/drizzle/meta/0006_snapshot.json +638 -0
  182. package/drizzle/meta/0007_snapshot.json +640 -0
  183. package/drizzle/meta/0008_snapshot.json +649 -0
  184. package/drizzle/meta/0009_snapshot.json +554 -0
  185. package/drizzle/meta/0010_snapshot.json +619 -0
  186. package/drizzle/meta/0011_snapshot.json +627 -0
  187. package/drizzle/meta/0012_snapshot.json +639 -0
  188. package/drizzle/meta/0013_snapshot.json +717 -0
  189. package/drizzle/meta/0014_snapshot.json +717 -0
  190. package/drizzle/meta/0015_snapshot.json +897 -0
  191. package/drizzle/meta/0016_snapshot.json +1031 -0
  192. package/drizzle/meta/0018_snapshot.json +1210 -0
  193. package/drizzle/meta/0019_snapshot.json +1165 -0
  194. package/drizzle/meta/0020_snapshot.json +1232 -0
  195. package/drizzle/meta/0021_snapshot.json +1311 -0
  196. package/drizzle/meta/0022_snapshot.json +1481 -0
  197. package/drizzle/meta/0023_snapshot.json +1496 -0
  198. package/drizzle/meta/_journal.json +174 -0
  199. package/package.json +240 -0
@@ -0,0 +1,245 @@
1
+ const require_logger = require("./logger-DyfK9PBt.cjs");
2
+ const require_esm = require("./esm-C62Zofr1.cjs");
3
+ let fs = require("fs");
4
+ fs = require_logger.__toESM(fs);
5
+ let path = require("path");
6
+ path = require_logger.__toESM(path);
7
+ let os = require("os");
8
+ os = require_logger.__toESM(os);
9
+ let child_process = require("child_process");
10
+ //#region src/ruby/rubyUtils.ts
11
+ const execFileAsync = (0, require("util").promisify)(child_process.execFile);
12
+ /**
13
+ * Global state for Ruby executable path caching.
14
+ * Ensures consistent Ruby executable usage across multiple provider instances.
15
+ */
16
+ const state = {
17
+ cachedRubyPath: null,
18
+ validationPromise: null,
19
+ validatingPath: null
20
+ };
21
+ /**
22
+ * Attempts to find Ruby using Windows 'where' command.
23
+ * Only applicable on Windows platforms.
24
+ * @returns The validated Ruby executable path, or null if not found
25
+ */
26
+ async function tryWindowsWhere() {
27
+ try {
28
+ const output = (await execFileAsync("where", ["ruby"])).stdout.trim();
29
+ if (!output) {
30
+ require_logger.logger.debug("Windows 'where ruby' returned empty output");
31
+ return null;
32
+ }
33
+ const paths = output.split("\n").filter((path$2) => path$2.trim());
34
+ for (const rubyPath of paths) {
35
+ const trimmedPath = rubyPath.trim();
36
+ if (!trimmedPath.endsWith(".exe")) continue;
37
+ const validated = await tryPath(trimmedPath);
38
+ if (validated) return validated;
39
+ }
40
+ } catch (error) {
41
+ const errorMsg = error instanceof Error ? error.message : String(error);
42
+ require_logger.logger.debug(`Windows 'where ruby' failed: ${errorMsg}`);
43
+ if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES")) require_logger.logger.warn(`Permission denied when searching for Ruby: ${errorMsg}`);
44
+ }
45
+ return null;
46
+ }
47
+ /**
48
+ * Attempts to get Ruby executable path by running Ruby commands.
49
+ * Uses RbConfig.ruby to get the actual Ruby executable path.
50
+ * @param commands - Array of Ruby command names to try (e.g., ['ruby'])
51
+ * @returns The Ruby executable path, or null if all commands fail
52
+ */
53
+ async function tryRubyCommands(commands) {
54
+ for (const cmd of commands) try {
55
+ const executablePath = (await execFileAsync(cmd, ["-e", "puts RbConfig.ruby"])).stdout.trim();
56
+ if (executablePath && executablePath !== "None") return executablePath;
57
+ } catch (error) {
58
+ const errorMsg = error instanceof Error ? error.message : String(error);
59
+ require_logger.logger.debug(`Ruby command "${cmd}" failed: ${errorMsg}`);
60
+ if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES") || errorMsg.includes("EPERM")) require_logger.logger.warn(`Permission denied when trying Ruby command "${cmd}": ${errorMsg}`);
61
+ }
62
+ return null;
63
+ }
64
+ /**
65
+ * Attempts to validate Ruby commands directly as a final fallback.
66
+ * Validates each command by running it with --version.
67
+ * @param commands - Array of Ruby command names to try (e.g., ['ruby'])
68
+ * @returns The validated Ruby executable path, or null if all commands fail
69
+ */
70
+ async function tryDirectCommands(commands) {
71
+ for (const cmd of commands) try {
72
+ const validated = await tryPath(cmd);
73
+ if (validated) return validated;
74
+ } catch (error) {
75
+ const errorMsg = error instanceof Error ? error.message : String(error);
76
+ require_logger.logger.debug(`Direct command "${cmd}" failed: ${errorMsg}`);
77
+ if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES") || errorMsg.includes("EPERM")) require_logger.logger.warn(`Permission denied when trying Ruby command "${cmd}": ${errorMsg}`);
78
+ }
79
+ return null;
80
+ }
81
+ /**
82
+ * Attempts to get the Ruby executable path using platform-appropriate strategies.
83
+ * @returns The Ruby executable path if successful, or null if failed.
84
+ */
85
+ async function getSysExecutable() {
86
+ if (process.platform === "win32") {
87
+ const whereResult = await tryWindowsWhere();
88
+ if (whereResult) return whereResult;
89
+ const sysResult = await tryRubyCommands(["ruby"]);
90
+ if (sysResult) return sysResult;
91
+ return await tryDirectCommands(["ruby"]);
92
+ } else return await tryRubyCommands(["ruby"]);
93
+ }
94
+ /**
95
+ * Attempts to validate a Ruby executable path.
96
+ * @param path - The path to the Ruby executable to test.
97
+ * @returns The validated path if successful, or null if invalid.
98
+ */
99
+ async function tryPath(path$3) {
100
+ let timeoutId;
101
+ try {
102
+ const timeoutPromise = new Promise((_, reject) => {
103
+ timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("Command timed out")), 2500);
104
+ });
105
+ const result = await Promise.race([execFileAsync(path$3, ["--version"]), timeoutPromise]);
106
+ if (timeoutId) clearTimeout(timeoutId);
107
+ if (result.stdout.trim().toLowerCase().includes("ruby")) return path$3;
108
+ return null;
109
+ } catch {
110
+ if (timeoutId) clearTimeout(timeoutId);
111
+ return null;
112
+ }
113
+ }
114
+ /**
115
+ * Validates and caches the Ruby executable path.
116
+ *
117
+ * @param rubyPath - Path to the Ruby executable.
118
+ * @param isExplicit - If true, only tries the provided path.
119
+ * @returns Validated Ruby executable path.
120
+ * @throws {Error} If no valid Ruby executable is found.
121
+ */
122
+ async function validateRubyPath(rubyPath, isExplicit) {
123
+ if (state.cachedRubyPath && state.validatingPath === rubyPath) return state.cachedRubyPath;
124
+ if (state.validatingPath !== rubyPath) {
125
+ state.cachedRubyPath = null;
126
+ state.validationPromise = null;
127
+ state.validatingPath = rubyPath;
128
+ }
129
+ if (!state.validationPromise) state.validationPromise = (async () => {
130
+ try {
131
+ const primaryPath = await tryPath(rubyPath);
132
+ if (primaryPath) {
133
+ state.cachedRubyPath = primaryPath;
134
+ state.validationPromise = null;
135
+ return primaryPath;
136
+ }
137
+ if (isExplicit) {
138
+ const error = /* @__PURE__ */ new Error(`Ruby not found. Tried "${rubyPath}" Please ensure Ruby is installed and set the PROMPTFOO_RUBY environment variable to your Ruby executable path (e.g., '${process.platform === "win32" ? "C:\\Ruby32\\bin\\ruby.exe" : "/usr/bin/ruby"}').`);
139
+ state.validationPromise = null;
140
+ throw error;
141
+ }
142
+ const detectedPath = await getSysExecutable();
143
+ if (detectedPath) {
144
+ state.cachedRubyPath = detectedPath;
145
+ state.validationPromise = null;
146
+ return detectedPath;
147
+ }
148
+ const error = /* @__PURE__ */ new Error(`Ruby not found. Tried "${rubyPath}", ruby executable detection, and fallback commands. Please ensure Ruby is installed and set the PROMPTFOO_RUBY environment variable to your Ruby executable path (e.g., '${process.platform === "win32" ? "C:\\Ruby32\\bin\\ruby.exe" : "/usr/bin/ruby"}').`);
149
+ state.validationPromise = null;
150
+ throw error;
151
+ } catch (error) {
152
+ state.validationPromise = null;
153
+ throw error;
154
+ }
155
+ })();
156
+ return state.validationPromise;
157
+ }
158
+ /**
159
+ * Runs a Ruby script with the specified method and arguments.
160
+ *
161
+ * @param scriptPath - The path to the Ruby script to run.
162
+ * @param method - The name of the method to call in the Ruby script.
163
+ * @param args - An array of arguments to pass to the Ruby script.
164
+ * @param options - Optional settings for running the Ruby script.
165
+ * @param options.rubyExecutable - Optional path to the Ruby executable.
166
+ * @returns A promise that resolves to the output of the Ruby script.
167
+ * @throws An error if there's an issue running the Ruby script or parsing its output.
168
+ */
169
+ async function runRuby(scriptPath, method, args, options = {}) {
170
+ const absPath = path.default.resolve(scriptPath);
171
+ const tempJsonPath = path.default.join(os.default.tmpdir(), `promptfoo-ruby-input-json-${Date.now()}-${Math.random().toString(16).slice(2)}.json`);
172
+ const outputPath = path.default.join(os.default.tmpdir(), `promptfoo-ruby-output-json-${Date.now()}-${Math.random().toString(16).slice(2)}.json`);
173
+ const customPath = options.rubyExecutable || require_logger.getEnvString("PROMPTFOO_RUBY");
174
+ let rubyPath = customPath || "ruby";
175
+ rubyPath = await validateRubyPath(rubyPath, typeof customPath === "string");
176
+ const wrapperPath = path.default.join(require_esm.getWrapperDir("ruby"), "wrapper.rb");
177
+ try {
178
+ fs.default.writeFileSync(tempJsonPath, require_logger.safeJsonStringify(args), "utf-8");
179
+ require_logger.logger.debug(`Running Ruby wrapper with args: ${require_logger.safeJsonStringify(args)}`);
180
+ const { stdout, stderr } = await execFileAsync(rubyPath, [
181
+ wrapperPath,
182
+ absPath,
183
+ method,
184
+ tempJsonPath,
185
+ outputPath
186
+ ]);
187
+ if (stdout) require_logger.logger.debug(stdout.trim());
188
+ if (stderr) require_logger.logger.error(stderr.trim());
189
+ const output = fs.default.readFileSync(outputPath, "utf-8");
190
+ require_logger.logger.debug(`Ruby script ${absPath} returned: ${output}`);
191
+ let result;
192
+ try {
193
+ result = JSON.parse(output);
194
+ require_logger.logger.debug(`Ruby script ${absPath} parsed output type: ${typeof result}, structure: ${result ? JSON.stringify(Object.keys(result)) : "undefined"}`);
195
+ } catch (error) {
196
+ throw new Error(`Invalid JSON: ${error.message} when parsing result: ${output}\nStack Trace: ${error.stack}`);
197
+ }
198
+ if (result?.type !== "final_result") throw new Error("The Ruby script `call_api` function must return a hash with an `output`");
199
+ return result.data;
200
+ } catch (error) {
201
+ require_logger.logger.error(`Error running Ruby script: ${error.message}\nStack Trace: ${error.stack || "No Ruby traceback available"}`);
202
+ throw new Error(`Error running Ruby script: ${error.message}\nStack Trace: ${error.stack || "No Ruby traceback available"}`);
203
+ } finally {
204
+ [tempJsonPath, outputPath].forEach((file) => {
205
+ try {
206
+ fs.default.unlinkSync(file);
207
+ } catch (error) {
208
+ require_logger.logger.error(`Error removing ${file}: ${error}`);
209
+ }
210
+ });
211
+ }
212
+ }
213
+ //#endregion
214
+ Object.defineProperty(exports, "getSysExecutable", {
215
+ enumerable: true,
216
+ get: function() {
217
+ return getSysExecutable;
218
+ }
219
+ });
220
+ Object.defineProperty(exports, "runRuby", {
221
+ enumerable: true,
222
+ get: function() {
223
+ return runRuby;
224
+ }
225
+ });
226
+ Object.defineProperty(exports, "state", {
227
+ enumerable: true,
228
+ get: function() {
229
+ return state;
230
+ }
231
+ });
232
+ Object.defineProperty(exports, "tryPath", {
233
+ enumerable: true,
234
+ get: function() {
235
+ return tryPath;
236
+ }
237
+ });
238
+ Object.defineProperty(exports, "validateRubyPath", {
239
+ enumerable: true,
240
+ get: function() {
241
+ return validateRubyPath;
242
+ }
243
+ });
244
+
245
+ //# sourceMappingURL=rubyUtils-B0NwnfpY.cjs.map
@@ -0,0 +1,2 @@
1
+ const require_rubyUtils = require("./rubyUtils-B0NwnfpY.cjs");
2
+ exports.runRuby = require_rubyUtils.runRuby;
@@ -0,0 +1,222 @@
1
+ import { t as __exportAll } from "./chunk-DEq-mXcV.js";
2
+ import { h as safeJsonStringify, r as logger, x as getEnvString } from "./logger-C40ZGil9.js";
3
+ import { n as getWrapperDir } from "./esm-DMVc93eh.js";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import os from "os";
7
+ import { execFile } from "child_process";
8
+ import { promisify } from "util";
9
+ //#region src/ruby/rubyUtils.ts
10
+ var rubyUtils_exports = /* @__PURE__ */ __exportAll({
11
+ getSysExecutable: () => getSysExecutable,
12
+ runRuby: () => runRuby,
13
+ state: () => state,
14
+ tryPath: () => tryPath,
15
+ validateRubyPath: () => validateRubyPath
16
+ });
17
+ const execFileAsync = promisify(execFile);
18
+ /**
19
+ * Global state for Ruby executable path caching.
20
+ * Ensures consistent Ruby executable usage across multiple provider instances.
21
+ */
22
+ const state = {
23
+ cachedRubyPath: null,
24
+ validationPromise: null,
25
+ validatingPath: null
26
+ };
27
+ /**
28
+ * Attempts to find Ruby using Windows 'where' command.
29
+ * Only applicable on Windows platforms.
30
+ * @returns The validated Ruby executable path, or null if not found
31
+ */
32
+ async function tryWindowsWhere() {
33
+ try {
34
+ const output = (await execFileAsync("where", ["ruby"])).stdout.trim();
35
+ if (!output) {
36
+ logger.debug("Windows 'where ruby' returned empty output");
37
+ return null;
38
+ }
39
+ const paths = output.split("\n").filter((path) => path.trim());
40
+ for (const rubyPath of paths) {
41
+ const trimmedPath = rubyPath.trim();
42
+ if (!trimmedPath.endsWith(".exe")) continue;
43
+ const validated = await tryPath(trimmedPath);
44
+ if (validated) return validated;
45
+ }
46
+ } catch (error) {
47
+ const errorMsg = error instanceof Error ? error.message : String(error);
48
+ logger.debug(`Windows 'where ruby' failed: ${errorMsg}`);
49
+ if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES")) logger.warn(`Permission denied when searching for Ruby: ${errorMsg}`);
50
+ }
51
+ return null;
52
+ }
53
+ /**
54
+ * Attempts to get Ruby executable path by running Ruby commands.
55
+ * Uses RbConfig.ruby to get the actual Ruby executable path.
56
+ * @param commands - Array of Ruby command names to try (e.g., ['ruby'])
57
+ * @returns The Ruby executable path, or null if all commands fail
58
+ */
59
+ async function tryRubyCommands(commands) {
60
+ for (const cmd of commands) try {
61
+ const executablePath = (await execFileAsync(cmd, ["-e", "puts RbConfig.ruby"])).stdout.trim();
62
+ if (executablePath && executablePath !== "None") return executablePath;
63
+ } catch (error) {
64
+ const errorMsg = error instanceof Error ? error.message : String(error);
65
+ logger.debug(`Ruby command "${cmd}" failed: ${errorMsg}`);
66
+ if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES") || errorMsg.includes("EPERM")) logger.warn(`Permission denied when trying Ruby command "${cmd}": ${errorMsg}`);
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Attempts to validate Ruby commands directly as a final fallback.
72
+ * Validates each command by running it with --version.
73
+ * @param commands - Array of Ruby command names to try (e.g., ['ruby'])
74
+ * @returns The validated Ruby executable path, or null if all commands fail
75
+ */
76
+ async function tryDirectCommands(commands) {
77
+ for (const cmd of commands) try {
78
+ const validated = await tryPath(cmd);
79
+ if (validated) return validated;
80
+ } catch (error) {
81
+ const errorMsg = error instanceof Error ? error.message : String(error);
82
+ logger.debug(`Direct command "${cmd}" failed: ${errorMsg}`);
83
+ if (errorMsg.includes("Access is denied") || errorMsg.includes("EACCES") || errorMsg.includes("EPERM")) logger.warn(`Permission denied when trying Ruby command "${cmd}": ${errorMsg}`);
84
+ }
85
+ return null;
86
+ }
87
+ /**
88
+ * Attempts to get the Ruby executable path using platform-appropriate strategies.
89
+ * @returns The Ruby executable path if successful, or null if failed.
90
+ */
91
+ async function getSysExecutable() {
92
+ if (process.platform === "win32") {
93
+ const whereResult = await tryWindowsWhere();
94
+ if (whereResult) return whereResult;
95
+ const sysResult = await tryRubyCommands(["ruby"]);
96
+ if (sysResult) return sysResult;
97
+ return await tryDirectCommands(["ruby"]);
98
+ } else return await tryRubyCommands(["ruby"]);
99
+ }
100
+ /**
101
+ * Attempts to validate a Ruby executable path.
102
+ * @param path - The path to the Ruby executable to test.
103
+ * @returns The validated path if successful, or null if invalid.
104
+ */
105
+ async function tryPath(path) {
106
+ let timeoutId;
107
+ try {
108
+ const timeoutPromise = new Promise((_, reject) => {
109
+ timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("Command timed out")), 2500);
110
+ });
111
+ const result = await Promise.race([execFileAsync(path, ["--version"]), timeoutPromise]);
112
+ if (timeoutId) clearTimeout(timeoutId);
113
+ if (result.stdout.trim().toLowerCase().includes("ruby")) return path;
114
+ return null;
115
+ } catch {
116
+ if (timeoutId) clearTimeout(timeoutId);
117
+ return null;
118
+ }
119
+ }
120
+ /**
121
+ * Validates and caches the Ruby executable path.
122
+ *
123
+ * @param rubyPath - Path to the Ruby executable.
124
+ * @param isExplicit - If true, only tries the provided path.
125
+ * @returns Validated Ruby executable path.
126
+ * @throws {Error} If no valid Ruby executable is found.
127
+ */
128
+ async function validateRubyPath(rubyPath, isExplicit) {
129
+ if (state.cachedRubyPath && state.validatingPath === rubyPath) return state.cachedRubyPath;
130
+ if (state.validatingPath !== rubyPath) {
131
+ state.cachedRubyPath = null;
132
+ state.validationPromise = null;
133
+ state.validatingPath = rubyPath;
134
+ }
135
+ if (!state.validationPromise) state.validationPromise = (async () => {
136
+ try {
137
+ const primaryPath = await tryPath(rubyPath);
138
+ if (primaryPath) {
139
+ state.cachedRubyPath = primaryPath;
140
+ state.validationPromise = null;
141
+ return primaryPath;
142
+ }
143
+ if (isExplicit) {
144
+ const error = /* @__PURE__ */ new Error(`Ruby not found. Tried "${rubyPath}" Please ensure Ruby is installed and set the PROMPTFOO_RUBY environment variable to your Ruby executable path (e.g., '${process.platform === "win32" ? "C:\\Ruby32\\bin\\ruby.exe" : "/usr/bin/ruby"}').`);
145
+ state.validationPromise = null;
146
+ throw error;
147
+ }
148
+ const detectedPath = await getSysExecutable();
149
+ if (detectedPath) {
150
+ state.cachedRubyPath = detectedPath;
151
+ state.validationPromise = null;
152
+ return detectedPath;
153
+ }
154
+ const error = /* @__PURE__ */ new Error(`Ruby not found. Tried "${rubyPath}", ruby executable detection, and fallback commands. Please ensure Ruby is installed and set the PROMPTFOO_RUBY environment variable to your Ruby executable path (e.g., '${process.platform === "win32" ? "C:\\Ruby32\\bin\\ruby.exe" : "/usr/bin/ruby"}').`);
155
+ state.validationPromise = null;
156
+ throw error;
157
+ } catch (error) {
158
+ state.validationPromise = null;
159
+ throw error;
160
+ }
161
+ })();
162
+ return state.validationPromise;
163
+ }
164
+ /**
165
+ * Runs a Ruby script with the specified method and arguments.
166
+ *
167
+ * @param scriptPath - The path to the Ruby script to run.
168
+ * @param method - The name of the method to call in the Ruby script.
169
+ * @param args - An array of arguments to pass to the Ruby script.
170
+ * @param options - Optional settings for running the Ruby script.
171
+ * @param options.rubyExecutable - Optional path to the Ruby executable.
172
+ * @returns A promise that resolves to the output of the Ruby script.
173
+ * @throws An error if there's an issue running the Ruby script or parsing its output.
174
+ */
175
+ async function runRuby(scriptPath, method, args, options = {}) {
176
+ const absPath = path.resolve(scriptPath);
177
+ const tempJsonPath = path.join(os.tmpdir(), `promptfoo-ruby-input-json-${Date.now()}-${Math.random().toString(16).slice(2)}.json`);
178
+ const outputPath = path.join(os.tmpdir(), `promptfoo-ruby-output-json-${Date.now()}-${Math.random().toString(16).slice(2)}.json`);
179
+ const customPath = options.rubyExecutable || getEnvString("PROMPTFOO_RUBY");
180
+ let rubyPath = customPath || "ruby";
181
+ rubyPath = await validateRubyPath(rubyPath, typeof customPath === "string");
182
+ const wrapperPath = path.join(getWrapperDir("ruby"), "wrapper.rb");
183
+ try {
184
+ fs.writeFileSync(tempJsonPath, safeJsonStringify(args), "utf-8");
185
+ logger.debug(`Running Ruby wrapper with args: ${safeJsonStringify(args)}`);
186
+ const { stdout, stderr } = await execFileAsync(rubyPath, [
187
+ wrapperPath,
188
+ absPath,
189
+ method,
190
+ tempJsonPath,
191
+ outputPath
192
+ ]);
193
+ if (stdout) logger.debug(stdout.trim());
194
+ if (stderr) logger.error(stderr.trim());
195
+ const output = fs.readFileSync(outputPath, "utf-8");
196
+ logger.debug(`Ruby script ${absPath} returned: ${output}`);
197
+ let result;
198
+ try {
199
+ result = JSON.parse(output);
200
+ logger.debug(`Ruby script ${absPath} parsed output type: ${typeof result}, structure: ${result ? JSON.stringify(Object.keys(result)) : "undefined"}`);
201
+ } catch (error) {
202
+ throw new Error(`Invalid JSON: ${error.message} when parsing result: ${output}\nStack Trace: ${error.stack}`);
203
+ }
204
+ if (result?.type !== "final_result") throw new Error("The Ruby script `call_api` function must return a hash with an `output`");
205
+ return result.data;
206
+ } catch (error) {
207
+ logger.error(`Error running Ruby script: ${error.message}\nStack Trace: ${error.stack || "No Ruby traceback available"}`);
208
+ throw new Error(`Error running Ruby script: ${error.message}\nStack Trace: ${error.stack || "No Ruby traceback available"}`);
209
+ } finally {
210
+ [tempJsonPath, outputPath].forEach((file) => {
211
+ try {
212
+ fs.unlinkSync(file);
213
+ } catch (error) {
214
+ logger.error(`Error removing ${file}: ${error}`);
215
+ }
216
+ });
217
+ }
218
+ }
219
+ //#endregion
220
+ export { runRuby as n, rubyUtils_exports as t };
221
+
222
+ //# sourceMappingURL=rubyUtils-hqVw5UvJ.js.map