@probelabs/visor 0.1.107 → 0.1.111

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 (235) hide show
  1. package/README.md +6 -0
  2. package/defaults/task-refinement.yaml +7 -3
  3. package/defaults/visor.tests.yaml +13 -2
  4. package/defaults/visor.yaml +1 -0
  5. package/dist/663.index.js +3 -2
  6. package/dist/80.index.js +3 -2
  7. package/dist/ai-review-service.d.ts +13 -9
  8. package/dist/ai-review-service.d.ts.map +1 -1
  9. package/dist/cli-main.d.ts.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/debug-visualizer/ws-server.d.ts +7 -1
  13. package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
  14. package/dist/defaults/task-refinement.yaml +7 -3
  15. package/dist/defaults/visor.tests.yaml +13 -2
  16. package/dist/defaults/visor.yaml +1 -0
  17. package/dist/docs/advanced-ai.md +60 -1
  18. package/dist/docs/ai-configuration.md +67 -0
  19. package/dist/docs/ai-custom-tools-usage.md +261 -0
  20. package/dist/docs/ai-custom-tools.md +392 -0
  21. package/dist/docs/bot-transports-rfc.md +23 -0
  22. package/dist/docs/configuration.md +21 -0
  23. package/dist/docs/engine-pause-resume-rfc.md +192 -0
  24. package/dist/docs/lifecycle-hooks.md +253 -0
  25. package/dist/docs/liquid-templates.md +143 -0
  26. package/dist/docs/providers/git-checkout.md +589 -0
  27. package/dist/docs/recipes.md +458 -5
  28. package/dist/docs/rfc/git-checkout-step.md +601 -0
  29. package/dist/docs/rfc/on_init-hook.md +1294 -0
  30. package/dist/docs/rfc/workspace-isolation.md +216 -0
  31. package/dist/docs/router-patterns.md +339 -0
  32. package/dist/event-bus/types.d.ts +14 -0
  33. package/dist/event-bus/types.d.ts.map +1 -1
  34. package/dist/examples/ai-custom-tools-example.yaml +206 -0
  35. package/dist/examples/ai-custom-tools-simple.yaml +76 -0
  36. package/dist/examples/git-checkout-basic.yaml +32 -0
  37. package/dist/examples/git-checkout-compare.yaml +59 -0
  38. package/dist/examples/git-checkout-cross-repo.yaml +76 -0
  39. package/dist/examples/on-init-import-demo.yaml +179 -0
  40. package/dist/examples/reusable-tools.yaml +92 -0
  41. package/dist/examples/reusable-workflows.yaml +88 -0
  42. package/dist/examples/session-reuse-self.yaml +81 -0
  43. package/dist/examples/slack-simple-chat.yaml +775 -0
  44. package/dist/failure-condition-evaluator.d.ts +2 -0
  45. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  46. package/dist/frontends/github-frontend.d.ts +20 -0
  47. package/dist/frontends/github-frontend.d.ts.map +1 -1
  48. package/dist/frontends/host.d.ts +4 -0
  49. package/dist/frontends/host.d.ts.map +1 -1
  50. package/dist/frontends/slack-frontend.d.ts +58 -0
  51. package/dist/frontends/slack-frontend.d.ts.map +1 -0
  52. package/dist/generated/config-schema.d.ts +409 -41
  53. package/dist/generated/config-schema.d.ts.map +1 -1
  54. package/dist/generated/config-schema.json +436 -47
  55. package/dist/github-comments.d.ts +2 -0
  56. package/dist/github-comments.d.ts.map +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +83587 -56085
  59. package/dist/liquid-extensions.d.ts.map +1 -1
  60. package/dist/logger.d.ts +1 -0
  61. package/dist/logger.d.ts.map +1 -1
  62. package/dist/output/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-20T19-22-58-043Z.ndjson} +91 -91
  63. package/dist/output/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
  64. package/dist/output-formatters.d.ts.map +1 -1
  65. package/dist/providers/ai-check-provider.d.ts +12 -0
  66. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  67. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  68. package/dist/providers/check-provider.interface.d.ts +9 -0
  69. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  70. package/dist/providers/command-check-provider.d.ts.map +1 -1
  71. package/dist/providers/custom-tool-executor.d.ts.map +1 -1
  72. package/dist/providers/git-checkout-provider.d.ts +25 -0
  73. package/dist/providers/git-checkout-provider.d.ts.map +1 -0
  74. package/dist/providers/http-client-provider.d.ts +3 -0
  75. package/dist/providers/http-client-provider.d.ts.map +1 -1
  76. package/dist/providers/human-input-check-provider.d.ts +2 -0
  77. package/dist/providers/human-input-check-provider.d.ts.map +1 -1
  78. package/dist/providers/log-check-provider.d.ts.map +1 -1
  79. package/dist/providers/mcp-check-provider.d.ts +1 -1
  80. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  81. package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
  82. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
  83. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  84. package/dist/providers/script-check-provider.d.ts.map +1 -1
  85. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  86. package/dist/reviewer.d.ts.map +1 -1
  87. package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
  88. package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
  89. package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
  90. package/dist/sdk/{chunk-OOZITMRU.mjs → chunk-3OMWVM6J.mjs} +11 -1
  91. package/dist/sdk/{chunk-OOZITMRU.mjs.map → chunk-3OMWVM6J.mjs.map} +1 -1
  92. package/dist/sdk/{chunk-37ZSCMFC.mjs → chunk-7UK3NIIT.mjs} +2 -2
  93. package/dist/sdk/{chunk-VMPLF6FT.mjs → chunk-AGIZJ4UZ.mjs} +50 -4
  94. package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
  95. package/dist/sdk/{chunk-IEO6CFLG.mjs → chunk-AIVFBIS4.mjs} +161 -5
  96. package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
  97. package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
  98. package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
  99. package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
  100. package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
  101. package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
  102. package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
  103. package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
  104. package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
  105. package/dist/sdk/{chunk-6Y4YTKCF.mjs → chunk-NAW3DB3I.mjs} +2 -2
  106. package/dist/sdk/{chunk-OWUVOILT.mjs → chunk-QR7MOMJH.mjs} +4 -3
  107. package/dist/sdk/{chunk-OWUVOILT.mjs.map → chunk-QR7MOMJH.mjs.map} +1 -1
  108. package/dist/sdk/{chunk-PTL3K3PN.mjs → chunk-QY2XYPEV.mjs} +488 -60
  109. package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
  110. package/dist/sdk/{chunk-OZJ263FM.mjs → chunk-SIWNBRTK.mjs} +29 -215
  111. package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
  112. package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
  113. package/dist/sdk/{config-M4ZNO6NU.mjs → config-YNC2EOOT.mjs} +5 -3
  114. package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs → failure-condition-evaluator-YGTF2GHG.mjs} +6 -5
  115. package/dist/sdk/{github-frontend-4AWRJT7D.mjs → github-frontend-SIAEOCON.mjs} +190 -12
  116. package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
  117. package/dist/sdk/{host-7GBC3S7L.mjs → host-DXUYTNMU.mjs} +5 -2
  118. package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
  119. package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs → liquid-extensions-PKWCKK7E.mjs} +5 -4
  120. package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
  121. package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
  122. package/dist/sdk/{renderer-schema-6RF26VUS.mjs → renderer-schema-LPKN5UJS.mjs} +3 -2
  123. package/dist/sdk/{renderer-schema-6RF26VUS.mjs.map → renderer-schema-LPKN5UJS.mjs.map} +1 -1
  124. package/dist/sdk/{routing-RP56JTV2.mjs → routing-6N45MJ4F.mjs} +7 -6
  125. package/dist/sdk/sdk.d.mts +219 -5
  126. package/dist/sdk/sdk.d.ts +219 -5
  127. package/dist/sdk/sdk.js +21329 -14908
  128. package/dist/sdk/sdk.js.map +1 -1
  129. package/dist/sdk/sdk.mjs +407 -12874
  130. package/dist/sdk/sdk.mjs.map +1 -1
  131. package/dist/sdk/{session-registry-N5FFYFTM.mjs → session-registry-4E6YRQ77.mjs} +2 -2
  132. package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
  133. package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
  134. package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
  135. package/dist/sdk/{tracer-init-WP4X46IF.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
  136. package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
  137. package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
  138. package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
  139. package/dist/slack/adapter.d.ts +36 -0
  140. package/dist/slack/adapter.d.ts.map +1 -0
  141. package/dist/slack/cache-prewarmer.d.ts +31 -0
  142. package/dist/slack/cache-prewarmer.d.ts.map +1 -0
  143. package/dist/slack/client.d.ts +77 -0
  144. package/dist/slack/client.d.ts.map +1 -0
  145. package/dist/slack/markdown.d.ts +45 -0
  146. package/dist/slack/markdown.d.ts.map +1 -0
  147. package/dist/slack/prompt-state.d.ts +33 -0
  148. package/dist/slack/prompt-state.d.ts.map +1 -0
  149. package/dist/slack/rate-limiter.d.ts +56 -0
  150. package/dist/slack/rate-limiter.d.ts.map +1 -0
  151. package/dist/slack/signature.d.ts +2 -0
  152. package/dist/slack/signature.d.ts.map +1 -0
  153. package/dist/slack/socket-runner.d.ts +42 -0
  154. package/dist/slack/socket-runner.d.ts.map +1 -0
  155. package/dist/slack/thread-cache.d.ts +51 -0
  156. package/dist/slack/thread-cache.d.ts.map +1 -0
  157. package/dist/state-machine/context/build-engine-context.d.ts +8 -0
  158. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
  159. package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
  160. package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
  161. package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
  162. package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
  163. package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
  164. package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -1
  165. package/dist/state-machine/runner.d.ts +6 -0
  166. package/dist/state-machine/runner.d.ts.map +1 -1
  167. package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
  168. package/dist/state-machine/states/plan-ready.d.ts.map +1 -1
  169. package/dist/state-machine/states/routing.d.ts.map +1 -1
  170. package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
  171. package/dist/state-machine/workflow-projection.d.ts.map +1 -1
  172. package/dist/state-machine-execution-engine.d.ts +21 -9
  173. package/dist/state-machine-execution-engine.d.ts.map +1 -1
  174. package/dist/telemetry/state-capture.d.ts +5 -0
  175. package/dist/telemetry/state-capture.d.ts.map +1 -1
  176. package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
  177. package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
  178. package/dist/test-runner/evaluators.d.ts +37 -4
  179. package/dist/test-runner/evaluators.d.ts.map +1 -1
  180. package/dist/test-runner/index.d.ts +7 -0
  181. package/dist/test-runner/index.d.ts.map +1 -1
  182. package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
  183. package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
  184. package/dist/test-runner/validator.d.ts.map +1 -1
  185. package/dist/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-20T19-22-58-043Z.ndjson} +91 -91
  186. package/dist/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
  187. package/dist/types/bot.d.ts +109 -0
  188. package/dist/types/bot.d.ts.map +1 -0
  189. package/dist/types/cli.d.ts +4 -0
  190. package/dist/types/cli.d.ts.map +1 -1
  191. package/dist/types/config.d.ts +182 -5
  192. package/dist/types/config.d.ts.map +1 -1
  193. package/dist/types/engine.d.ts +5 -0
  194. package/dist/types/engine.d.ts.map +1 -1
  195. package/dist/types/git-checkout.d.ts +76 -0
  196. package/dist/types/git-checkout.d.ts.map +1 -0
  197. package/dist/utils/json-text-extractor.d.ts +17 -0
  198. package/dist/utils/json-text-extractor.d.ts.map +1 -0
  199. package/dist/utils/sandbox.d.ts +10 -0
  200. package/dist/utils/sandbox.d.ts.map +1 -1
  201. package/dist/utils/template-context.d.ts +1 -0
  202. package/dist/utils/template-context.d.ts.map +1 -1
  203. package/dist/utils/tracer-init.d.ts.map +1 -1
  204. package/dist/utils/workspace-manager.d.ts +118 -0
  205. package/dist/utils/workspace-manager.d.ts.map +1 -0
  206. package/dist/utils/worktree-cleanup.d.ts +33 -0
  207. package/dist/utils/worktree-cleanup.d.ts.map +1 -0
  208. package/dist/utils/worktree-manager.d.ts +153 -0
  209. package/dist/utils/worktree-manager.d.ts.map +1 -0
  210. package/dist/webhook-server.d.ts.map +1 -1
  211. package/dist/workflow-executor.d.ts.map +1 -1
  212. package/dist/workflow-registry.d.ts.map +1 -1
  213. package/package.json +4 -2
  214. package/dist/output/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  215. package/dist/sdk/chunk-IEO6CFLG.mjs.map +0 -1
  216. package/dist/sdk/chunk-JEHPDJIF.mjs +0 -223
  217. package/dist/sdk/chunk-JEHPDJIF.mjs.map +0 -1
  218. package/dist/sdk/chunk-OZJ263FM.mjs.map +0 -1
  219. package/dist/sdk/chunk-PTL3K3PN.mjs.map +0 -1
  220. package/dist/sdk/chunk-VMPLF6FT.mjs.map +0 -1
  221. package/dist/sdk/github-frontend-4AWRJT7D.mjs.map +0 -1
  222. package/dist/sdk/host-7GBC3S7L.mjs.map +0 -1
  223. package/dist/sdk/memory-store-GJACZC2A.mjs +0 -11
  224. package/dist/sdk/workflow-registry-2YIIXQCK.mjs +0 -11
  225. package/dist/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  226. /package/dist/sdk/{config-M4ZNO6NU.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
  227. /package/dist/sdk/{chunk-37ZSCMFC.mjs.map → chunk-7UK3NIIT.mjs.map} +0 -0
  228. /package/dist/sdk/{chunk-6Y4YTKCF.mjs.map → chunk-NAW3DB3I.mjs.map} +0 -0
  229. /package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
  230. /package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
  231. /package/dist/sdk/{memory-store-GJACZC2A.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
  232. /package/dist/sdk/{routing-RP56JTV2.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
  233. /package/dist/sdk/{session-registry-N5FFYFTM.mjs.map → memory-store-XGBB7LX7.mjs.map} +0 -0
  234. /package/dist/sdk/{tracer-init-WP4X46IF.mjs.map → prompt-state-YRJY6QAL.mjs.map} +0 -0
  235. /package/dist/sdk/{workflow-registry-2YIIXQCK.mjs.map → routing-6N45MJ4F.mjs.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/liquid-extensions.ts"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports -- this is the extensions file that wraps liquidjs\nimport { Liquid, TagToken, Context, TopLevelToken, Tag, Value, Emitter } from 'liquidjs';\nimport { AsyncLocalStorage } from 'async_hooks';\nimport fs from 'fs/promises';\nimport path from 'path';\nimport {\n hasMinPermission,\n isOwner,\n isMember,\n isCollaborator,\n isContributor,\n isFirstTimer,\n detectLocalMode,\n} from './utils/author-permissions';\nimport { MemoryStore } from './memory-store';\n\n/**\n * Sanitize label strings to only allow [A-Za-z0-9:/\\- ] characters (including spaces and hyphens)\n * @param value - Label value to sanitize\n * @returns Sanitized label string\n */\nexport function sanitizeLabel(value: unknown): string {\n if (value == null) return '';\n const s = String(value);\n // Keep only alphanumerics, colon, slash, hyphen, and space; collapse repeated slashes and trim\n return s\n .replace(/[^A-Za-z0-9:\\/\\- ]/g, '')\n .replace(/\\/{2,}/g, '/')\n .trim();\n}\n\n/**\n * Sanitize an array of labels\n * @param labels - Array of label values\n * @returns Array of sanitized, non-empty label strings\n */\nexport function sanitizeLabelList(labels: unknown): string[] {\n if (!Array.isArray(labels)) return [];\n return (labels as unknown[]).map(v => sanitizeLabel(v)).filter(s => s.length > 0);\n}\n\n/**\n * Custom ReadFile tag for Liquid templates\n * Usage: {% readfile \"path/to/file.txt\" %}\n * or with variable: {% readfile filename %}\n */\nexport class ReadFileTag extends Tag {\n private filepath: Value;\n\n constructor(token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {\n super(token, remainTokens, liquid);\n this.filepath = new Value(token.args, liquid);\n }\n\n *render(ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {\n const filePath = yield this.filepath.value(ctx, false);\n\n // Validate the path\n if (!filePath || typeof filePath !== 'string') {\n emitter.write('[Error: Invalid file path]');\n return;\n }\n\n // Security: Resolve path relative to project root to prevent directory traversal\n const projectRoot = process.cwd();\n const resolvedPath = path.resolve(projectRoot, filePath.toString());\n\n // Ensure the resolved path is within the project directory\n if (!resolvedPath.startsWith(projectRoot)) {\n emitter.write('[Error: File path escapes project directory]');\n return;\n }\n\n // Read the file content\n try {\n const content = yield fs.readFile(resolvedPath, 'utf-8');\n emitter.write(content);\n } catch (error) {\n // Handle file read errors gracefully\n const errorMessage =\n error instanceof Error\n ? error.message\n : (error as NodeJS.ErrnoException)?.code || 'Unknown error';\n emitter.write(`[Error reading file: ${errorMessage}]`);\n }\n }\n}\n\n// Async-local permissions context for filters (per-render)\nconst permissionsALS = new AsyncLocalStorage<{ authorAssociation?: string }>();\n\nexport async function withPermissionsContext<T>(\n ctx: { authorAssociation?: string },\n fn: () => Promise<T>\n): Promise<T> {\n return await permissionsALS.run(ctx, fn as any);\n}\n\n/**\n * Configure a Liquid instance with custom extensions\n */\nexport function configureLiquidWithExtensions(liquid: Liquid): void {\n // Register the readfile tag\n liquid.registerTag('readfile', ReadFileTag);\n\n // Register parse_json filter to parse JSON strings into objects\n liquid.registerFilter('parse_json', (value: string) => {\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(value);\n } catch {\n // Return original value if parsing fails\n return value;\n }\n });\n\n // Register to_json filter as alias for json (for consistency)\n liquid.registerFilter('to_json', (value: unknown) => {\n try {\n return JSON.stringify(value);\n } catch {\n return '[Error: Unable to serialize to JSON]';\n }\n });\n\n // Register base64 filter for encoding strings\n // Usage: {{ \"user:password\" | base64 }}\n liquid.registerFilter('base64', (value: unknown) => {\n if (value == null) return '';\n const str = String(value);\n return Buffer.from(str).toString('base64');\n });\n\n // Register base64_decode filter for decoding base64 strings\n // Usage: {{ encoded_value | base64_decode }}\n liquid.registerFilter('base64_decode', (value: unknown) => {\n if (value == null) return '';\n const str = String(value);\n try {\n return Buffer.from(str, 'base64').toString('utf-8');\n } catch {\n return '[Error: Invalid base64 string]';\n }\n });\n\n // Sanitize a label to allowed characters only: [A-Za-z0-9:/]\n liquid.registerFilter('safe_label', (value: unknown) => sanitizeLabel(value));\n\n // Sanitize an array of labels\n liquid.registerFilter('safe_label_list', (value: unknown) => sanitizeLabelList(value));\n\n // Convert literal escape sequences (e.g., \"\\n\") into actual newlines\n liquid.registerFilter('unescape_newlines', (value: unknown) => {\n if (value == null) return '';\n const s = String(value);\n return s.replace(/\\\\n/g, '\\n').replace(/\\\\r/g, '\\r').replace(/\\\\t/g, '\\t');\n });\n\n // JSON escape filter - escapes a string for use inside a JSON string value\n // This escapes special characters like quotes, backslashes, and control characters\n // Usage: \"jql\": \"{{ myValue | json_escape }}\"\n liquid.registerFilter('json_escape', (value: unknown) => {\n if (value == null) return '';\n const s = String(value);\n // Use JSON.stringify which handles all escaping, then strip the surrounding quotes\n const jsonStr = JSON.stringify(s);\n // Remove the first and last character (the quotes added by JSON.stringify)\n return jsonStr.slice(1, -1);\n });\n\n // Shell escape filter - wraps value in single quotes with proper escaping\n // Usage: {{ value | shell_escape }}\n // Example: \"hello'world\" becomes \"'hello'\\''world'\"\n // This is POSIX-compliant and safe for arbitrary text including mermaid diagrams\n liquid.registerFilter('shell_escape', (value: unknown) => {\n if (value == null) return \"''\";\n const s = String(value);\n // Replace single quotes with: end quote, escaped quote, start quote\n // Then wrap the entire thing in single quotes\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n });\n\n // Alias for shell_escape\n liquid.registerFilter('escape_shell', (value: unknown) => {\n if (value == null) return \"''\";\n const s = String(value);\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n });\n\n // Shell escape for double quotes (less safe but sometimes needed)\n // Usage: {{ value | shell_escape_double }}\n // Escapes: $, `, \\, \", and !\n liquid.registerFilter('shell_escape_double', (value: unknown) => {\n if (value == null) return '\"\"';\n const s = String(value);\n // Escape characters that have special meaning inside double quotes\n const escaped = s\n .replace(/\\\\/g, '\\\\\\\\') // backslash first\n .replace(/\\$/g, '\\\\$') // dollar sign\n .replace(/`/g, '\\\\`') // backticks\n .replace(/\"/g, '\\\\\"') // double quotes\n .replace(/!/g, '\\\\!'); // history expansion\n return '\"' + escaped + '\"';\n });\n\n // Register author permission filters (from main)\n // These filters check the author's permission level; detect local mode for tests\n const isLocal = detectLocalMode();\n\n const resolveAssoc = (val: unknown): string | undefined => {\n if (typeof val === 'string' && val.length > 0) return val;\n const store = permissionsALS.getStore();\n return store?.authorAssociation;\n };\n\n liquid.registerFilter('has_min_permission', (authorAssociation: unknown, level: string) => {\n return hasMinPermission(resolveAssoc(authorAssociation), level as any, isLocal);\n });\n\n liquid.registerFilter('is_owner', (authorAssociation: unknown) => {\n return isOwner(resolveAssoc(authorAssociation), isLocal);\n });\n\n liquid.registerFilter('is_member', (authorAssociation: unknown) => {\n return isMember(resolveAssoc(authorAssociation), isLocal);\n });\n\n liquid.registerFilter('is_collaborator', (authorAssociation: unknown) => {\n return isCollaborator(resolveAssoc(authorAssociation), isLocal);\n });\n\n liquid.registerFilter('is_contributor', (authorAssociation: unknown) => {\n return isContributor(resolveAssoc(authorAssociation), isLocal);\n });\n\n liquid.registerFilter('is_first_timer', (authorAssociation: unknown) => {\n return isFirstTimer(resolveAssoc(authorAssociation), isLocal);\n });\n\n // Register memory filters for accessing memory store\n const memoryStore = MemoryStore.getInstance();\n\n liquid.registerFilter('memory_get', (key: string, namespace?: string) => {\n if (typeof key !== 'string') {\n return undefined;\n }\n return memoryStore.get(key, namespace);\n });\n\n liquid.registerFilter('memory_has', (key: string, namespace?: string) => {\n if (typeof key !== 'string') {\n return false;\n }\n const has = memoryStore.has(key, namespace);\n try {\n if (process.env.VISOR_DEBUG === 'true' && key === 'fact_validation_issues') {\n console.error(\n `[liquid] memory_has('${key}', ns='${namespace || memoryStore.getDefaultNamespace()}') => ${String(\n has\n )}`\n );\n }\n } catch {}\n return has;\n });\n\n liquid.registerFilter('memory_list', (namespace?: string) => {\n return memoryStore.list(namespace);\n });\n\n // Generic helpers to radically simplify templates\n\n // get: safe nested access using dot-path (e.g., obj | get: 'a.b.c')\n liquid.registerFilter('get', (obj: any, pathExpr: unknown) => {\n if (obj == null) return undefined;\n const path = typeof pathExpr === 'string' ? pathExpr : String(pathExpr || '');\n if (!path) return obj;\n const parts = path.split('.');\n let cur: any = obj;\n for (const p of parts) {\n if (cur == null) return undefined;\n cur = cur[p as keyof typeof cur];\n }\n return cur;\n });\n\n // not_empty: true when value is a non-empty array/string/object with keys\n liquid.registerFilter('not_empty', (v: unknown) => {\n if (Array.isArray(v)) return v.length > 0;\n if (typeof v === 'string') return v.length > 0;\n if (v && typeof v === 'object') return Object.keys(v as object).length > 0;\n return false;\n });\n\n // coalesce: pick first argument (value or candidates) that is a non-empty array/object/string\n // Usage: a | coalesce: b, c, d\n liquid.registerFilter('coalesce', (first: unknown, ...rest: unknown[]) => {\n const all = [first, ...rest];\n for (const v of all) {\n if (Array.isArray(v) && v.length > 0) return v;\n if (typeof v === 'string' && v.length > 0) return v;\n if (v && typeof v === 'object' && Object.keys(v as object).length > 0) return v;\n }\n return Array.isArray(first) ? [] : (first ?? undefined);\n });\n\n // where_exp: generic expression-based filter (Shopify-style)\n // Usage: array | where_exp: 'i', 'i.is_valid != true and i.confidence != \"high\"'\n liquid.registerFilter('where_exp', (items: unknown, varName: string, expr: string) => {\n const arr = Array.isArray(items) ? (items as any[]) : [];\n const name = typeof varName === 'string' && varName.trim() ? varName.trim() : 'i';\n const body = String(expr || '');\n try {\n // Build a tiny predicate; expose only item, idx, arr\n\n const fn = new Function(\n name,\n 'idx',\n 'arr',\n `try { return (${body}); } catch { return false; }`\n );\n const out: any[] = [];\n for (let idx = 0; idx < arr.length; idx++) {\n const i = arr[idx];\n let ok = false;\n try {\n ok = !!(fn as any)(i, idx, arr);\n } catch {\n ok = false;\n }\n if (ok) out.push(i);\n }\n return out;\n } catch {\n return [];\n }\n });\n\n // chat_history: merge outputs_history from multiple steps into a normalized,\n // timestamp-sorted chat transcript.\n //\n // Usage:\n // {% assign history = '' | chat_history: 'ask', 'reply' %}\n // {% for m in history %}\n // {{ m.role }}: {{ m.text }}\n // {% endfor %}\n //\n // Advanced usage with options:\n // '' | chat_history: 'ask', 'reply',\n // direction: 'desc',\n // limit: 50,\n // roles: { by_type: { 'human-input': 'user', 'ai': 'assistant' } },\n // text: { default_field: 'text', by_step: { reply: 'text' } }\n liquid.registerFilter(\n 'chat_history',\n function (this: unknown, value: unknown, ...args: unknown[]): unknown {\n try {\n // Access Liquid rendering context to read globals like outputs_history\n const impl = this as { context?: { get: (key: string[] | string) => unknown } } | undefined;\n const ctx = impl?.context;\n\n // Parse arguments: one or more step names, optional options hash as last arg\n const allArgs = Array.isArray(args) ? args : [];\n\n if (allArgs.length === 0) {\n return [];\n }\n\n // Liquid passes keyword arguments as trailing [\"key\", value] pairs.\n // Split positional step names from an optional options hash built from those pairs.\n const positional: unknown[] = [];\n const options: any = {};\n for (const arg of allArgs) {\n if (\n Array.isArray(arg) &&\n arg.length === 2 &&\n typeof arg[0] === 'string' &&\n arg[0].length > 0\n ) {\n options[arg[0]] = arg[1];\n } else {\n positional.push(arg);\n }\n }\n const stepArgs: unknown[] = positional;\n\n const steps = stepArgs.map(s => String(s ?? '').trim()).filter(s => s.length > 0);\n if (steps.length === 0) return [];\n\n // Resolve history source: prefer outputs_history, fall back to outputs.history\n const outputsHistoryVar = (ctx?.get(['outputs_history']) || {}) as Record<\n string,\n unknown[]\n >;\n const outputsVar = (ctx?.get(['outputs']) || {}) as { history?: Record<string, unknown[]> };\n const outputsHistory: Record<string, unknown[]> =\n outputsHistoryVar && Object.keys(outputsHistoryVar).length > 0\n ? outputsHistoryVar\n : outputsVar?.history || {};\n\n // Optional checks metadata: used to infer roles by check type\n const checksMeta =\n (ctx?.get(['checks_meta']) as Record<string, { type?: string; group?: string }>) ||\n ((ctx?.get(['event']) as any)?.payload?.__checksMeta as Record<\n string,\n { type?: string; group?: string }\n >) ||\n undefined;\n\n // Direction and limit\n const directionRaw =\n typeof options.direction === 'string' ? options.direction.toLowerCase() : '';\n const direction: 'asc' | 'desc' = directionRaw === 'desc' ? 'desc' : 'asc';\n const limit =\n typeof options.limit === 'number' && options.limit > 0\n ? Math.floor(options.limit)\n : undefined;\n\n // Text mapping configuration\n const textCfg = options.text && typeof options.text === 'object' ? options.text : {};\n const defaultField =\n typeof textCfg.default_field === 'string' && textCfg.default_field.trim()\n ? textCfg.default_field.trim()\n : 'text';\n const byStepText: Record<string, string> = {};\n if (textCfg.by_step && typeof textCfg.by_step === 'object') {\n for (const [k, v] of Object.entries(textCfg.by_step)) {\n if (typeof v === 'string' && v.trim()) {\n byStepText[k] = v.trim();\n }\n }\n }\n\n // Role mapping configuration\n const rolesCfg = options.roles && typeof options.roles === 'object' ? options.roles : {};\n const byTypeRole: Record<string, string> = {};\n if (rolesCfg.by_type && typeof rolesCfg.by_type === 'object') {\n for (const [k, v] of Object.entries(rolesCfg.by_type)) {\n if (typeof v === 'string' && v.trim()) {\n byTypeRole[k] = v.trim();\n }\n }\n }\n const byStepRole: Record<string, string> = {};\n if (rolesCfg.by_step && typeof rolesCfg.by_step === 'object') {\n for (const [k, v] of Object.entries(rolesCfg.by_step)) {\n if (typeof v === 'string' && v.trim()) {\n byStepRole[k] = v.trim();\n }\n }\n }\n // Optional: step-level role map provided as a compact string, e.g. \"ask=user,reply=assistant\"\n if (typeof options.role_map === 'string' && options.role_map.trim().length > 0) {\n const parts = String(options.role_map)\n .split(',')\n .map(p => p.trim())\n .filter(Boolean);\n for (const part of parts) {\n const eqIdx = part.indexOf('=');\n if (eqIdx > 0) {\n const k = part.slice(0, eqIdx).trim();\n const v = part.slice(eqIdx + 1).trim();\n if (k && v) {\n byStepRole[k] = v;\n }\n }\n }\n }\n const defaultRole =\n typeof rolesCfg.default === 'string' && rolesCfg.default.trim()\n ? rolesCfg.default.trim()\n : undefined;\n\n const getNested = (obj: any, path: string): unknown => {\n if (!obj || !path) return undefined;\n const parts = path.split('.');\n let cur = obj;\n for (const p of parts) {\n if (cur == null) return undefined;\n cur = cur[p];\n }\n return cur;\n };\n\n const normalizeText = (step: string, raw: any): string => {\n try {\n const overrideField = byStepText[step];\n if (overrideField) {\n const val = getNested(raw, overrideField);\n if (val !== undefined && val !== null) {\n const s = String(val);\n if (s.trim().length > 0) return s;\n }\n }\n\n if (raw && typeof raw === 'object') {\n if (typeof (raw as any).text === 'string' && (raw as any).text.trim().length > 0) {\n return (raw as any).text;\n }\n if (\n typeof (raw as any).content === 'string' &&\n (raw as any).content.trim().length > 0\n ) {\n return (raw as any).content;\n }\n const dfVal = (raw as any)[defaultField];\n if (dfVal !== undefined && dfVal !== null) {\n const s = String(dfVal);\n if (s.trim().length > 0) return s;\n }\n }\n\n if (typeof raw === 'string') return raw;\n if (raw == null) return '';\n try {\n return JSON.stringify(raw);\n } catch {\n return String(raw);\n }\n } catch {\n if (typeof raw === 'string') return raw;\n return '';\n }\n };\n\n const normalizeRole = (step: string): string => {\n try {\n if (byStepRole[step]) return byStepRole[step];\n const meta = checksMeta ? (checksMeta as any)[step] : undefined;\n const type = meta?.type as string | undefined;\n if (type && byTypeRole[type]) return byTypeRole[type];\n if (type === 'human-input') return 'user';\n if (type === 'ai') return 'assistant';\n if (defaultRole) return defaultRole;\n if (type) {\n if (type === 'human-input') return 'user';\n if (type === 'ai') return 'assistant';\n }\n } catch {\n // fall through\n }\n return 'assistant';\n };\n\n type ChatMessage = {\n step: string;\n role: string;\n text: string;\n ts: number;\n raw: unknown;\n };\n\n const messages: ChatMessage[] = [];\n const tsBase = Date.now();\n let counter = 0;\n\n for (const step of steps) {\n const arr = (outputsHistory as any)?.[step] as unknown[];\n if (!Array.isArray(arr)) continue;\n for (const raw of arr) {\n let ts: number | undefined;\n if (raw && typeof raw === 'object' && typeof (raw as any).ts === 'number') {\n ts = (raw as any).ts;\n }\n if (!Number.isFinite(ts as number)) {\n ts = tsBase + counter++;\n }\n const text = normalizeText(step, raw);\n const role = normalizeRole(step);\n messages.push({ step, role, text, ts: ts as number, raw });\n }\n }\n\n // Sort by timestamp and apply direction/limit\n messages.sort((a, b) => a.ts - b.ts);\n if (direction === 'desc') {\n messages.reverse();\n }\n\n if (limit && limit > 0 && messages.length > limit) {\n if (direction === 'asc') {\n return messages.slice(messages.length - limit);\n }\n return messages.slice(0, limit);\n }\n\n return messages;\n } catch {\n return [];\n }\n }\n );\n\n // Removed: merge_sort_by filter (unused)\n}\n\n/**\n * Create a new Liquid instance with custom extensions\n */\nexport function createExtendedLiquid(options: Record<string, unknown> = {}): Liquid {\n const liquid = new Liquid({\n cache: false,\n strictFilters: false,\n strictVariables: false,\n ...options,\n });\n\n configureLiquidWithExtensions(liquid);\n return liquid;\n}\n"],"mappings":"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","names":["path"]}
@@ -0,0 +1,139 @@
1
+ import {
2
+ init_logger,
3
+ logger
4
+ } from "./chunk-AGIZJ4UZ.mjs";
5
+ import {
6
+ __esm
7
+ } from "./chunk-WMJKH4XE.mjs";
8
+
9
+ // src/utils/command-executor.ts
10
+ import { exec } from "child_process";
11
+ import { promisify } from "util";
12
+ var CommandExecutor, commandExecutor;
13
+ var init_command_executor = __esm({
14
+ "src/utils/command-executor.ts"() {
15
+ init_logger();
16
+ CommandExecutor = class _CommandExecutor {
17
+ static instance;
18
+ constructor() {
19
+ }
20
+ static getInstance() {
21
+ if (!_CommandExecutor.instance) {
22
+ _CommandExecutor.instance = new _CommandExecutor();
23
+ }
24
+ return _CommandExecutor.instance;
25
+ }
26
+ /**
27
+ * Execute a shell command with optional stdin, environment, and timeout
28
+ */
29
+ async execute(command, options = {}) {
30
+ const execAsync = promisify(exec);
31
+ const timeout = options.timeout || 3e4;
32
+ if (options.stdin) {
33
+ return this.executeWithStdin(command, options);
34
+ }
35
+ try {
36
+ const result = await execAsync(command, {
37
+ cwd: options.cwd,
38
+ env: options.env,
39
+ timeout
40
+ });
41
+ return {
42
+ stdout: result.stdout || "",
43
+ stderr: result.stderr || "",
44
+ exitCode: 0
45
+ };
46
+ } catch (error) {
47
+ return this.handleExecutionError(error, timeout);
48
+ }
49
+ }
50
+ /**
51
+ * Execute command with stdin input
52
+ */
53
+ executeWithStdin(command, options) {
54
+ return new Promise((resolve, reject) => {
55
+ const childProcess = exec(
56
+ command,
57
+ {
58
+ cwd: options.cwd,
59
+ env: options.env,
60
+ timeout: options.timeout || 3e4
61
+ },
62
+ (error, stdout, stderr) => {
63
+ if (error && error.killed && (error.code === "ETIMEDOUT" || error.signal === "SIGTERM")) {
64
+ reject(new Error(`Command timed out after ${options.timeout || 3e4}ms`));
65
+ } else {
66
+ resolve({
67
+ stdout: stdout || "",
68
+ stderr: stderr || "",
69
+ exitCode: error ? error.code || 1 : 0
70
+ });
71
+ }
72
+ }
73
+ );
74
+ if (options.stdin && childProcess.stdin) {
75
+ childProcess.stdin.write(options.stdin);
76
+ childProcess.stdin.end();
77
+ }
78
+ });
79
+ }
80
+ /**
81
+ * Handle execution errors consistently
82
+ */
83
+ handleExecutionError(error, timeout) {
84
+ const execError = error;
85
+ if (execError.killed && (execError.code === "ETIMEDOUT" || execError.signal === "SIGTERM")) {
86
+ throw new Error(`Command timed out after ${timeout}ms`);
87
+ }
88
+ let exitCode = 1;
89
+ if (execError.code) {
90
+ exitCode = typeof execError.code === "string" ? parseInt(execError.code, 10) : execError.code;
91
+ }
92
+ return {
93
+ stdout: execError.stdout || "",
94
+ stderr: execError.stderr || "",
95
+ exitCode
96
+ };
97
+ }
98
+ /**
99
+ * Build safe environment variables by merging process.env with custom env
100
+ * Ensures all values are strings (no undefined)
101
+ */
102
+ buildEnvironment(baseEnv = process.env, ...customEnvs) {
103
+ const result = {};
104
+ for (const [key, value] of Object.entries(baseEnv)) {
105
+ if (value !== void 0) {
106
+ result[key] = value;
107
+ }
108
+ }
109
+ for (const customEnv of customEnvs) {
110
+ if (customEnv) {
111
+ Object.assign(result, customEnv);
112
+ }
113
+ }
114
+ return result;
115
+ }
116
+ /**
117
+ * Log command execution for debugging
118
+ */
119
+ logExecution(command, options) {
120
+ const debugInfo = [
121
+ `Executing command: ${command}`,
122
+ options.cwd ? `cwd: ${options.cwd}` : null,
123
+ options.stdin ? "with stdin" : null,
124
+ options.timeout ? `timeout: ${options.timeout}ms` : null,
125
+ options.env ? `env vars: ${Object.keys(options.env).length}` : null
126
+ ].filter(Boolean).join(", ");
127
+ logger.debug(debugInfo);
128
+ }
129
+ };
130
+ commandExecutor = CommandExecutor.getInstance();
131
+ }
132
+ });
133
+
134
+ export {
135
+ CommandExecutor,
136
+ commandExecutor,
137
+ init_command_executor
138
+ };
139
+ //# sourceMappingURL=chunk-AUT26LHW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/command-executor.ts"],"sourcesContent":["import { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { logger } from '../logger';\n\nexport interface CommandExecutionOptions {\n stdin?: string;\n cwd?: string;\n env?: Record<string, string>;\n timeout?: number;\n}\n\nexport interface CommandExecutionResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Shared utility for executing shell commands\n * Used by both CommandCheckProvider and CustomToolExecutor\n */\nexport class CommandExecutor {\n private static instance: CommandExecutor;\n\n private constructor() {}\n\n static getInstance(): CommandExecutor {\n if (!CommandExecutor.instance) {\n CommandExecutor.instance = new CommandExecutor();\n }\n return CommandExecutor.instance;\n }\n\n /**\n * Execute a shell command with optional stdin, environment, and timeout\n */\n async execute(\n command: string,\n options: CommandExecutionOptions = {}\n ): Promise<CommandExecutionResult> {\n const execAsync = promisify(exec);\n const timeout = options.timeout || 30000;\n\n // If stdin is provided, we need to handle it differently\n if (options.stdin) {\n return this.executeWithStdin(command, options);\n }\n\n // For commands without stdin, use the simpler promisified version\n try {\n const result = await execAsync(command, {\n cwd: options.cwd,\n env: options.env as NodeJS.ProcessEnv,\n timeout,\n });\n\n return {\n stdout: result.stdout || '',\n stderr: result.stderr || '',\n exitCode: 0,\n };\n } catch (error) {\n return this.handleExecutionError(error, timeout);\n }\n }\n\n /**\n * Execute command with stdin input\n */\n private executeWithStdin(\n command: string,\n options: CommandExecutionOptions\n ): Promise<CommandExecutionResult> {\n return new Promise((resolve, reject) => {\n const childProcess = exec(\n command,\n {\n cwd: options.cwd,\n env: options.env as NodeJS.ProcessEnv,\n timeout: options.timeout || 30000,\n },\n (error, stdout, stderr) => {\n // Check if the process was killed due to timeout\n if (\n error &&\n error.killed &&\n ((error as NodeJS.ErrnoException).code === 'ETIMEDOUT' || error.signal === 'SIGTERM')\n ) {\n reject(new Error(`Command timed out after ${options.timeout || 30000}ms`));\n } else {\n resolve({\n stdout: stdout || '',\n stderr: stderr || '',\n exitCode: error ? error.code || 1 : 0,\n });\n }\n }\n );\n\n // Write stdin and close\n if (options.stdin && childProcess.stdin) {\n childProcess.stdin.write(options.stdin);\n childProcess.stdin.end();\n }\n });\n }\n\n /**\n * Handle execution errors consistently\n */\n private handleExecutionError(error: unknown, timeout: number): CommandExecutionResult {\n const execError = error as NodeJS.ErrnoException & {\n stdout?: string;\n stderr?: string;\n killed?: boolean;\n code?: string | number;\n signal?: string;\n };\n\n // Check if the process was killed due to timeout\n // Node.js sets killed: true and signal: 'SIGTERM' when timeout expires\n if (execError.killed && (execError.code === 'ETIMEDOUT' || execError.signal === 'SIGTERM')) {\n throw new Error(`Command timed out after ${timeout}ms`);\n }\n\n // Extract exit code - it might be a string or number\n let exitCode = 1;\n if (execError.code) {\n exitCode = typeof execError.code === 'string' ? parseInt(execError.code, 10) : execError.code;\n }\n\n return {\n stdout: execError.stdout || '',\n stderr: execError.stderr || '',\n exitCode,\n };\n }\n\n /**\n * Build safe environment variables by merging process.env with custom env\n * Ensures all values are strings (no undefined)\n */\n buildEnvironment(\n baseEnv: NodeJS.ProcessEnv = process.env,\n ...customEnvs: Array<Record<string, string> | undefined>\n ): Record<string, string> {\n const result: Record<string, string> = {};\n\n // Start with base environment, filtering out undefined values\n for (const [key, value] of Object.entries(baseEnv)) {\n if (value !== undefined) {\n result[key] = value;\n }\n }\n\n // Merge custom environments\n for (const customEnv of customEnvs) {\n if (customEnv) {\n Object.assign(result, customEnv);\n }\n }\n\n return result;\n }\n\n /**\n * Log command execution for debugging\n */\n logExecution(command: string, options: CommandExecutionOptions): void {\n const debugInfo = [\n `Executing command: ${command}`,\n options.cwd ? `cwd: ${options.cwd}` : null,\n options.stdin ? 'with stdin' : null,\n options.timeout ? `timeout: ${options.timeout}ms` : null,\n options.env ? `env vars: ${Object.keys(options.env).length}` : null,\n ]\n .filter(Boolean)\n .join(', ');\n\n logger.debug(debugInfo);\n }\n}\n\n// Export singleton instance for convenience\nexport const commandExecutor = CommandExecutor.getInstance();\n"],"mappings":"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","names":[]}
@@ -0,0 +1,232 @@
1
+ import {
2
+ __esm
3
+ } from "./chunk-WMJKH4XE.mjs";
4
+
5
+ // src/utils/sandbox.ts
6
+ import Sandbox from "@nyariv/sandboxjs";
7
+ function validateJsSyntax(code) {
8
+ if (!code || typeof code !== "string") {
9
+ return { valid: false, error: "Code must be a non-empty string" };
10
+ }
11
+ const trimmed = code.trim();
12
+ if (trimmed.length === 0) {
13
+ return { valid: false, error: "Code cannot be empty" };
14
+ }
15
+ const sandbox = createSecureSandbox();
16
+ const looksLikeBlock = /\breturn\b/.test(trimmed) || /;/.test(trimmed) || /\n/.test(trimmed);
17
+ const looksLikeIife = /\)\s*\(\s*\)\s*;?$/.test(trimmed);
18
+ const body = looksLikeBlock ? looksLikeIife ? `return (
19
+ ${trimmed}
20
+ );
21
+ ` : `return (() => {
22
+ ${trimmed}
23
+ })();
24
+ ` : `return (
25
+ ${trimmed}
26
+ );
27
+ `;
28
+ const header = `const __lp = "[syntax-check]"; const log = (...a) => { try { console.log(__lp, ...a); } catch {} };
29
+ `;
30
+ const fullCode = `${header}${body}`;
31
+ try {
32
+ sandbox.compile(fullCode);
33
+ return { valid: true };
34
+ } catch (e) {
35
+ const msg = e instanceof Error ? e.message : String(e);
36
+ return { valid: false, error: msg };
37
+ }
38
+ }
39
+ function createSecureSandbox() {
40
+ const globals = {
41
+ ...Sandbox.SAFE_GLOBALS,
42
+ Math,
43
+ JSON,
44
+ // Provide console with limited surface. Use trampolines so that any test
45
+ // spies (e.g., jest.spyOn(console, 'log')) see calls made inside the sandbox.
46
+ console: {
47
+ log: (...args) => {
48
+ try {
49
+ console.log(...args);
50
+ } catch {
51
+ }
52
+ },
53
+ warn: (...args) => {
54
+ try {
55
+ console.warn(...args);
56
+ } catch {
57
+ }
58
+ },
59
+ error: (...args) => {
60
+ try {
61
+ console.error(...args);
62
+ } catch {
63
+ }
64
+ }
65
+ }
66
+ };
67
+ const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);
68
+ const arrayMethods = /* @__PURE__ */ new Set([
69
+ // Query/iteration
70
+ "some",
71
+ "every",
72
+ "filter",
73
+ "map",
74
+ "reduce",
75
+ "reduceRight",
76
+ "find",
77
+ "findIndex",
78
+ "findLast",
79
+ "findLastIndex",
80
+ "includes",
81
+ "indexOf",
82
+ "lastIndexOf",
83
+ "keys",
84
+ "values",
85
+ "entries",
86
+ "forEach",
87
+ // Non‑mutating ES2023 additions
88
+ "toReversed",
89
+ "toSorted",
90
+ "toSpliced",
91
+ "with",
92
+ "at",
93
+ // Mutators and common ops
94
+ "slice",
95
+ "concat",
96
+ "join",
97
+ "push",
98
+ "pop",
99
+ "shift",
100
+ "unshift",
101
+ "sort",
102
+ "reverse",
103
+ "copyWithin",
104
+ "fill",
105
+ // Flattening
106
+ "flat",
107
+ "flatMap",
108
+ // Meta
109
+ "length"
110
+ ]);
111
+ prototypeWhitelist.set(Array.prototype, arrayMethods);
112
+ const stringMethods = /* @__PURE__ */ new Set([
113
+ "toLowerCase",
114
+ "toUpperCase",
115
+ "includes",
116
+ "indexOf",
117
+ "lastIndexOf",
118
+ "startsWith",
119
+ "endsWith",
120
+ "slice",
121
+ "substring",
122
+ "substr",
123
+ "trim",
124
+ "trimStart",
125
+ "trimEnd",
126
+ "split",
127
+ "replace",
128
+ "replaceAll",
129
+ "match",
130
+ "matchAll",
131
+ "charAt",
132
+ "charCodeAt",
133
+ "codePointAt",
134
+ "normalize",
135
+ "repeat",
136
+ "padStart",
137
+ "padEnd",
138
+ "at",
139
+ "length"
140
+ ]);
141
+ prototypeWhitelist.set(String.prototype, stringMethods);
142
+ const objectMethods = /* @__PURE__ */ new Set([
143
+ "hasOwnProperty",
144
+ "propertyIsEnumerable",
145
+ "toString",
146
+ "valueOf"
147
+ ]);
148
+ prototypeWhitelist.set(Object.prototype, objectMethods);
149
+ const mapMethods = /* @__PURE__ */ new Set([
150
+ "get",
151
+ "set",
152
+ "has",
153
+ "delete",
154
+ "entries",
155
+ "keys",
156
+ "values",
157
+ "forEach"
158
+ ]);
159
+ prototypeWhitelist.set(Map.prototype, mapMethods);
160
+ const setMethods = /* @__PURE__ */ new Set([
161
+ "add",
162
+ "has",
163
+ "delete",
164
+ "entries",
165
+ "keys",
166
+ "values",
167
+ "forEach"
168
+ ]);
169
+ prototypeWhitelist.set(Set.prototype, setMethods);
170
+ const dateMethods = /* @__PURE__ */ new Set(["toISOString", "toJSON", "getTime"]);
171
+ prototypeWhitelist.set(Date.prototype, dateMethods);
172
+ const regexpMethods = /* @__PURE__ */ new Set(["test", "exec"]);
173
+ prototypeWhitelist.set(RegExp.prototype, regexpMethods);
174
+ return new Sandbox({ globals, prototypeWhitelist });
175
+ }
176
+ function compileAndRun(sandbox, userCode, scope, opts = { injectLog: true, wrapFunction: true, logPrefix: "[sandbox]" }) {
177
+ const inject = opts?.injectLog === true;
178
+ let safePrefix = String(opts?.logPrefix ?? "[sandbox]");
179
+ safePrefix = safePrefix.replace(/[\r\n\t\0]/g, "").replace(/[`$\\]/g, "").replace(/\$\{/g, "").slice(0, 64);
180
+ const header = inject ? `const __lp = ${JSON.stringify(safePrefix)}; const log = (...a) => { try { console.log(__lp, ...a); } catch {} };
181
+ ` : "";
182
+ const src = String(userCode);
183
+ const looksLikeBlock = /\breturn\b/.test(src) || /;/.test(src) || /\n/.test(src);
184
+ const looksLikeIife = /\)\s*\(\s*\)\s*;?$/.test(src.trim());
185
+ const body = opts.wrapFunction ? looksLikeBlock ? looksLikeIife ? `return (
186
+ ${src}
187
+ );
188
+ ` : `return (() => {
189
+ ${src}
190
+ })();
191
+ ` : `return (
192
+ ${src}
193
+ );
194
+ ` : `${src}`;
195
+ const code = `${header}${body}`;
196
+ let exec;
197
+ try {
198
+ exec = sandbox.compile(code);
199
+ } catch (e) {
200
+ const msg = e instanceof Error ? e.message : String(e);
201
+ throw new Error(`sandbox_compile_error: ${msg}`);
202
+ }
203
+ let out;
204
+ try {
205
+ out = exec(scope);
206
+ } catch (e) {
207
+ const msg = e instanceof Error ? e.message : String(e);
208
+ throw new Error(`sandbox_execution_error: ${msg}`);
209
+ }
210
+ if (out && typeof out.run === "function") {
211
+ try {
212
+ return out.run();
213
+ } catch (e) {
214
+ const msg = e instanceof Error ? e.message : String(e);
215
+ throw new Error(`sandbox_runner_error: ${msg}`);
216
+ }
217
+ }
218
+ return out;
219
+ }
220
+ var init_sandbox = __esm({
221
+ "src/utils/sandbox.ts"() {
222
+ "use strict";
223
+ }
224
+ });
225
+
226
+ export {
227
+ validateJsSyntax,
228
+ createSecureSandbox,
229
+ compileAndRun,
230
+ init_sandbox
231
+ };
232
+ //# sourceMappingURL=chunk-BOVFH3LI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/sandbox.ts"],"sourcesContent":["import Sandbox from '@nyariv/sandboxjs';\n\n/**\n * Centralized helpers for creating and using SandboxJS instances consistently\n * across providers. The goal is to have one place to define allowed globals\n * and prototype whitelists, and to offer a small helper to inject a `log`\n * utility inside user-provided JS snippets.\n */\n\nexport interface CompileOptions {\n injectLog?: boolean;\n logPrefix?: string;\n /** When true, wrap the code in a function and `return` its result */\n wrapFunction?: boolean;\n}\n\nexport interface JsSyntaxValidationResult {\n valid: boolean;\n error?: string;\n}\n\n/**\n * Validate JavaScript syntax without executing it.\n * Uses the sandbox's compile method to check for syntax errors.\n * Returns validation result with error message if invalid.\n */\nexport function validateJsSyntax(code: string): JsSyntaxValidationResult {\n if (!code || typeof code !== 'string') {\n return { valid: false, error: 'Code must be a non-empty string' };\n }\n\n const trimmed = code.trim();\n if (trimmed.length === 0) {\n return { valid: false, error: 'Code cannot be empty' };\n }\n\n // Create a minimal sandbox instance for syntax checking\n const sandbox = createSecureSandbox();\n\n // Wrap code similar to compileAndRun to catch the same syntax issues\n const looksLikeBlock = /\\breturn\\b/.test(trimmed) || /;/.test(trimmed) || /\\n/.test(trimmed);\n const looksLikeIife = /\\)\\s*\\(\\s*\\)\\s*;?$/.test(trimmed);\n const body = looksLikeBlock\n ? looksLikeIife\n ? `return (\\n${trimmed}\\n);\\n`\n : `return (() => {\\n${trimmed}\\n})();\\n`\n : `return (\\n${trimmed}\\n);\\n`;\n\n // Add log injection header (same as compileAndRun)\n const header = `const __lp = \"[syntax-check]\"; const log = (...a) => { try { console.log(__lp, ...a); } catch {} };\\n`;\n const fullCode = `${header}${body}`;\n\n try {\n sandbox.compile(fullCode);\n return { valid: true };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return { valid: false, error: msg };\n }\n}\n\n/**\n * Create a hardened Sandbox with a consistent set of globals and prototype\n * whitelists. This is a superset of the sets previously used by individual\n * providers, kept intentionally minimal and side‑effect free.\n */\nexport function createSecureSandbox(): Sandbox {\n const globals = {\n ...Sandbox.SAFE_GLOBALS,\n Math,\n JSON,\n // Provide console with limited surface. Use trampolines so that any test\n // spies (e.g., jest.spyOn(console, 'log')) see calls made inside the sandbox.\n console: {\n log: (...args: unknown[]) => {\n try {\n (console as any).log(...args);\n } catch {}\n },\n warn: (...args: unknown[]) => {\n try {\n (console as any).warn(...args);\n } catch {}\n },\n error: (...args: unknown[]) => {\n try {\n (console as any).error(...args);\n } catch {}\n },\n },\n } as Record<string, unknown>;\n\n const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);\n\n // Arrays — union of methods used around the codebase\n const arrayMethods = new Set<string>([\n // Query/iteration\n 'some',\n 'every',\n 'filter',\n 'map',\n 'reduce',\n 'reduceRight',\n 'find',\n 'findIndex',\n 'findLast',\n 'findLastIndex',\n 'includes',\n 'indexOf',\n 'lastIndexOf',\n 'keys',\n 'values',\n 'entries',\n 'forEach',\n // Non‑mutating ES2023 additions\n 'toReversed',\n 'toSorted',\n 'toSpliced',\n 'with',\n 'at',\n // Mutators and common ops\n 'slice',\n 'concat',\n 'join',\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'sort',\n 'reverse',\n 'copyWithin',\n 'fill',\n // Flattening\n 'flat',\n 'flatMap',\n // Meta\n 'length',\n ]);\n prototypeWhitelist.set(Array.prototype, arrayMethods);\n\n // Strings — allow common, safe manipulation helpers\n const stringMethods = new Set<string>([\n 'toLowerCase',\n 'toUpperCase',\n 'includes',\n 'indexOf',\n 'lastIndexOf',\n 'startsWith',\n 'endsWith',\n 'slice',\n 'substring',\n 'substr',\n 'trim',\n 'trimStart',\n 'trimEnd',\n 'split',\n 'replace',\n 'replaceAll',\n 'match',\n 'matchAll',\n 'charAt',\n 'charCodeAt',\n 'codePointAt',\n 'normalize',\n 'repeat',\n 'padStart',\n 'padEnd',\n 'at',\n 'length',\n ]);\n prototypeWhitelist.set(String.prototype, stringMethods);\n\n // Objects — keep to basic safe operations\n const objectMethods = new Set<string>([\n 'hasOwnProperty',\n 'propertyIsEnumerable',\n 'toString',\n 'valueOf',\n ]);\n prototypeWhitelist.set(Object.prototype, objectMethods);\n\n // Keep native constructors from SAFE_GLOBALS; rely on prototype whitelists above.\n\n // Maps and Sets — allow common, safe operations\n const mapMethods = new Set<string>([\n 'get',\n 'set',\n 'has',\n 'delete',\n 'entries',\n 'keys',\n 'values',\n 'forEach',\n ]);\n // @ts-ignore - sandbox typings accept Map.prototype as a key\n prototypeWhitelist.set((Map as any).prototype, mapMethods);\n\n const setMethods = new Set<string>([\n 'add',\n 'has',\n 'delete',\n 'entries',\n 'keys',\n 'values',\n 'forEach',\n ]);\n // @ts-ignore\n prototypeWhitelist.set((Set as any).prototype, setMethods);\n\n // Date and RegExp — read‑only helpers\n const dateMethods = new Set<string>(['toISOString', 'toJSON', 'getTime']);\n // @ts-ignore\n prototypeWhitelist.set((Date as any).prototype, dateMethods);\n\n const regexpMethods = new Set<string>(['test', 'exec']);\n // @ts-ignore\n prototypeWhitelist.set((RegExp as any).prototype, regexpMethods);\n\n return new Sandbox({ globals, prototypeWhitelist });\n}\n\n/**\n * Compile and execute user-provided JS inside the sandbox with optional\n * helper injection. By default, code is wrapped in a function to keep the\n * global scope clean.\n */\nexport function compileAndRun<T = unknown>(\n sandbox: Sandbox,\n userCode: string,\n scope: Record<string, unknown>,\n opts: CompileOptions = { injectLog: true, wrapFunction: true, logPrefix: '[sandbox]' }\n): T {\n const inject = opts?.injectLog === true;\n let safePrefix = String(opts?.logPrefix ?? '[sandbox]');\n // Sanitize prefix aggressively: drop control chars and risky tokens, limit length\n safePrefix = safePrefix\n .replace(/[\\r\\n\\t\\0]/g, '')\n .replace(/[`$\\\\]/g, '') // strip backticks, dollar (template) and backslashes\n .replace(/\\$\\{/g, '') // remove template openings if present\n .slice(0, 64);\n // Build a safe header without string concatenation inside user code\n const header = inject\n ? `const __lp = ${JSON.stringify(safePrefix)}; const log = (...a) => { try { console.log(__lp, ...a); } catch {} };\\n`\n : '';\n // When wrapping, execute user code inside an IIFE and return its value.\n // This reliably captures the value of the last expression or any explicit\n // return statements inside the script, without requiring the caller to\n // manually `return` at top level.\n // Wrapper heuristic:\n // - If the snippet contains an explicit `return`, semicolons or newlines (likely a block),\n // run it inside an IIFE so `return` works: (() => { code })()\n // - Otherwise treat it as a pure expression and return its value directly.\n const src = String(userCode);\n const looksLikeBlock = /\\breturn\\b/.test(src) || /;/.test(src) || /\\n/.test(src);\n // Heuristic: if the snippet itself looks like an IIFE/callable expression\n // (e.g., `(() => { ... })()` or `(function(){ ... })()`), return its value\n // directly to avoid swallowing the result by nesting it inside another block.\n const looksLikeIife = /\\)\\s*\\(\\s*\\)\\s*;?$/.test(src.trim());\n const body = opts.wrapFunction\n ? looksLikeBlock\n ? looksLikeIife\n ? `return (\\n${src}\\n);\\n`\n : `return (() => {\\n${src}\\n})();\\n`\n : `return (\\n${src}\\n);\\n`\n : `${src}`;\n const code = `${header}${body}`;\n let exec: ReturnType<typeof sandbox.compile>;\n try {\n exec = sandbox.compile(code);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`sandbox_compile_error: ${msg}`);\n }\n\n let out: any;\n try {\n out = exec(scope);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`sandbox_execution_error: ${msg}`);\n }\n\n if (out && typeof out.run === 'function') {\n try {\n return out.run();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(`sandbox_runner_error: ${msg}`);\n }\n }\n return out as T;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,157 @@
1
+ import {
2
+ init_logger,
3
+ logger
4
+ } from "./chunk-AGIZJ4UZ.mjs";
5
+ import {
6
+ __esm
7
+ } from "./chunk-WMJKH4XE.mjs";
8
+
9
+ // src/slack/prompt-state.ts
10
+ function getPromptStateManager(ttlMs) {
11
+ if (!__promptState) __promptState = new PromptStateManager(ttlMs);
12
+ return __promptState;
13
+ }
14
+ function resetPromptStateManager() {
15
+ __promptState = void 0;
16
+ }
17
+ var PromptStateManager, __promptState;
18
+ var init_prompt_state = __esm({
19
+ "src/slack/prompt-state.ts"() {
20
+ init_logger();
21
+ PromptStateManager = class {
22
+ waiting = /* @__PURE__ */ new Map();
23
+ // key: `${channel}:${threadTs}`
24
+ ttlMs;
25
+ timer;
26
+ firstMessage = /* @__PURE__ */ new Map();
27
+ summaryTs = /* @__PURE__ */ new Map();
28
+ // key: threadKey -> group -> ts
29
+ constructor(ttlMs = 60 * 60 * 1e3) {
30
+ this.ttlMs = ttlMs;
31
+ this.startCleanup();
32
+ }
33
+ key(channel, threadTs) {
34
+ return `${channel}:${threadTs}`;
35
+ }
36
+ setWaiting(channel, threadTs, info) {
37
+ const key = this.key(channel, threadTs);
38
+ const value = { ...info, timestamp: Date.now(), channel, threadTs };
39
+ this.waiting.set(key, value);
40
+ try {
41
+ logger.info(
42
+ `[prompt-state] waiting set for ${key} (check=${info.checkName}, prompt="${info.prompt.substring(
43
+ 0,
44
+ 60
45
+ )}\u2026")`
46
+ );
47
+ } catch {
48
+ }
49
+ }
50
+ getWaiting(channel, threadTs) {
51
+ const key = this.key(channel, threadTs);
52
+ const info = this.waiting.get(key);
53
+ if (!info) return void 0;
54
+ const age = Date.now() - info.timestamp;
55
+ if (age > this.ttlMs) {
56
+ this.waiting.delete(key);
57
+ try {
58
+ logger.warn(`[prompt-state] expired ${key} (age=${Math.round(age / 1e3)}s)`);
59
+ } catch {
60
+ }
61
+ return void 0;
62
+ }
63
+ return info;
64
+ }
65
+ clear(channel, threadTs) {
66
+ const key = this.key(channel, threadTs);
67
+ const had = this.waiting.delete(key);
68
+ if (had) {
69
+ try {
70
+ logger.info(`[prompt-state] cleared ${key}`);
71
+ } catch {
72
+ }
73
+ }
74
+ return had;
75
+ }
76
+ /** Merge updates into an existing waiting entry */
77
+ update(channel, threadTs, patch) {
78
+ const key = this.key(channel, threadTs);
79
+ const prev = this.waiting.get(key);
80
+ if (!prev) return void 0;
81
+ const next = { ...prev, ...patch };
82
+ this.waiting.set(key, next);
83
+ try {
84
+ if (patch.snapshotPath) {
85
+ logger.info(`[prompt-state] snapshotPath set for ${key}`);
86
+ }
87
+ } catch {
88
+ }
89
+ return next;
90
+ }
91
+ // First message capture helpers
92
+ setFirstMessage(channel, threadTs, text) {
93
+ const key = this.key(channel, threadTs);
94
+ if (!text || !text.trim()) return;
95
+ const existing = this.firstMessage.get(key);
96
+ if (!existing || existing.consumed) {
97
+ this.firstMessage.set(key, { text, consumed: false });
98
+ }
99
+ }
100
+ consumeFirstMessage(channel, threadTs) {
101
+ const key = this.key(channel, threadTs);
102
+ const entry = this.firstMessage.get(key);
103
+ if (entry && !entry.consumed) {
104
+ entry.consumed = true;
105
+ this.firstMessage.set(key, entry);
106
+ return entry.text;
107
+ }
108
+ return void 0;
109
+ }
110
+ hasUnconsumedFirstMessage(channel, threadTs) {
111
+ const key = this.key(channel, threadTs);
112
+ const e = this.firstMessage.get(key);
113
+ return !!(e && !e.consumed && e.text && e.text.trim());
114
+ }
115
+ startCleanup(intervalMs = 5 * 60 * 1e3) {
116
+ if (this.timer) clearInterval(this.timer);
117
+ this.timer = setInterval(() => this.cleanup(), intervalMs);
118
+ if (this.timer.unref) this.timer.unref();
119
+ }
120
+ cleanup() {
121
+ const now = Date.now();
122
+ let removed = 0;
123
+ for (const [key, info] of this.waiting.entries()) {
124
+ if (now - info.timestamp > this.ttlMs) {
125
+ this.waiting.delete(key);
126
+ removed++;
127
+ }
128
+ }
129
+ for (const [key] of this.firstMessage.entries()) {
130
+ const waitingInfo = this.waiting.get(key);
131
+ if (!waitingInfo) {
132
+ const entry = this.firstMessage.get(key);
133
+ if (entry?.consumed) {
134
+ this.firstMessage.delete(key);
135
+ removed++;
136
+ }
137
+ }
138
+ }
139
+ if (removed) {
140
+ try {
141
+ logger.info(`[prompt-state] cleanup removed ${removed} entries`);
142
+ } catch {
143
+ }
144
+ }
145
+ return removed;
146
+ }
147
+ };
148
+ }
149
+ });
150
+
151
+ export {
152
+ PromptStateManager,
153
+ getPromptStateManager,
154
+ resetPromptStateManager,
155
+ init_prompt_state
156
+ };
157
+ //# sourceMappingURL=chunk-HTOKWMPO.mjs.map