language-operator 0.1.81 → 0.1.82

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 (427) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/agents/README.md +16 -0
  3. data/.claude/agents/changelog-writer.md +181 -0
  4. data/.claude/hooks/README.md +43 -0
  5. data/.claude/hooks/node_modules/.bin/esbuild +1 -0
  6. data/.claude/hooks/node_modules/.bin/tsc +1 -0
  7. data/.claude/hooks/node_modules/.bin/tsserver +1 -0
  8. data/.claude/hooks/node_modules/.bin/tsx +1 -0
  9. data/.claude/hooks/node_modules/.package-lock.json +136 -0
  10. data/.claude/hooks/node_modules/@esbuild/linux-x64/README.md +3 -0
  11. data/.claude/hooks/node_modules/@esbuild/linux-x64/bin/esbuild +0 -0
  12. data/.claude/hooks/node_modules/@esbuild/linux-x64/package.json +20 -0
  13. data/.claude/hooks/node_modules/@types/node/LICENSE +21 -0
  14. data/.claude/hooks/node_modules/@types/node/README.md +15 -0
  15. data/.claude/hooks/node_modules/@types/node/assert/strict.d.ts +105 -0
  16. data/.claude/hooks/node_modules/@types/node/assert.d.ts +955 -0
  17. data/.claude/hooks/node_modules/@types/node/async_hooks.d.ts +623 -0
  18. data/.claude/hooks/node_modules/@types/node/buffer.buffer.d.ts +466 -0
  19. data/.claude/hooks/node_modules/@types/node/buffer.d.ts +1810 -0
  20. data/.claude/hooks/node_modules/@types/node/child_process.d.ts +1428 -0
  21. data/.claude/hooks/node_modules/@types/node/cluster.d.ts +486 -0
  22. data/.claude/hooks/node_modules/@types/node/compatibility/iterators.d.ts +21 -0
  23. data/.claude/hooks/node_modules/@types/node/console.d.ts +151 -0
  24. data/.claude/hooks/node_modules/@types/node/constants.d.ts +20 -0
  25. data/.claude/hooks/node_modules/@types/node/crypto.d.ts +4065 -0
  26. data/.claude/hooks/node_modules/@types/node/dgram.d.ts +564 -0
  27. data/.claude/hooks/node_modules/@types/node/diagnostics_channel.d.ts +576 -0
  28. data/.claude/hooks/node_modules/@types/node/dns/promises.d.ts +503 -0
  29. data/.claude/hooks/node_modules/@types/node/dns.d.ts +922 -0
  30. data/.claude/hooks/node_modules/@types/node/domain.d.ts +166 -0
  31. data/.claude/hooks/node_modules/@types/node/events.d.ts +1054 -0
  32. data/.claude/hooks/node_modules/@types/node/fs/promises.d.ts +1316 -0
  33. data/.claude/hooks/node_modules/@types/node/fs.d.ts +4676 -0
  34. data/.claude/hooks/node_modules/@types/node/globals.d.ts +150 -0
  35. data/.claude/hooks/node_modules/@types/node/globals.typedarray.d.ts +101 -0
  36. data/.claude/hooks/node_modules/@types/node/http.d.ts +2143 -0
  37. data/.claude/hooks/node_modules/@types/node/http2.d.ts +2480 -0
  38. data/.claude/hooks/node_modules/@types/node/https.d.ts +399 -0
  39. data/.claude/hooks/node_modules/@types/node/index.d.ts +115 -0
  40. data/.claude/hooks/node_modules/@types/node/inspector/promises.d.ts +41 -0
  41. data/.claude/hooks/node_modules/@types/node/inspector.d.ts +224 -0
  42. data/.claude/hooks/node_modules/@types/node/inspector.generated.d.ts +4226 -0
  43. data/.claude/hooks/node_modules/@types/node/module.d.ts +819 -0
  44. data/.claude/hooks/node_modules/@types/node/net.d.ts +933 -0
  45. data/.claude/hooks/node_modules/@types/node/os.d.ts +507 -0
  46. data/.claude/hooks/node_modules/@types/node/package.json +155 -0
  47. data/.claude/hooks/node_modules/@types/node/path/posix.d.ts +8 -0
  48. data/.claude/hooks/node_modules/@types/node/path/win32.d.ts +8 -0
  49. data/.claude/hooks/node_modules/@types/node/path.d.ts +187 -0
  50. data/.claude/hooks/node_modules/@types/node/perf_hooks.d.ts +621 -0
  51. data/.claude/hooks/node_modules/@types/node/process.d.ts +2097 -0
  52. data/.claude/hooks/node_modules/@types/node/punycode.d.ts +117 -0
  53. data/.claude/hooks/node_modules/@types/node/querystring.d.ts +152 -0
  54. data/.claude/hooks/node_modules/@types/node/quic.d.ts +910 -0
  55. data/.claude/hooks/node_modules/@types/node/readline/promises.d.ts +161 -0
  56. data/.claude/hooks/node_modules/@types/node/readline.d.ts +541 -0
  57. data/.claude/hooks/node_modules/@types/node/repl.d.ts +415 -0
  58. data/.claude/hooks/node_modules/@types/node/sea.d.ts +162 -0
  59. data/.claude/hooks/node_modules/@types/node/sqlite.d.ts +937 -0
  60. data/.claude/hooks/node_modules/@types/node/stream/consumers.d.ts +38 -0
  61. data/.claude/hooks/node_modules/@types/node/stream/promises.d.ts +211 -0
  62. data/.claude/hooks/node_modules/@types/node/stream/web.d.ts +296 -0
  63. data/.claude/hooks/node_modules/@types/node/stream.d.ts +1760 -0
  64. data/.claude/hooks/node_modules/@types/node/string_decoder.d.ts +67 -0
  65. data/.claude/hooks/node_modules/@types/node/test/reporters.d.ts +96 -0
  66. data/.claude/hooks/node_modules/@types/node/test.d.ts +2239 -0
  67. data/.claude/hooks/node_modules/@types/node/timers/promises.d.ts +108 -0
  68. data/.claude/hooks/node_modules/@types/node/timers.d.ts +159 -0
  69. data/.claude/hooks/node_modules/@types/node/tls.d.ts +1194 -0
  70. data/.claude/hooks/node_modules/@types/node/trace_events.d.ts +197 -0
  71. data/.claude/hooks/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +462 -0
  72. data/.claude/hooks/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
  73. data/.claude/hooks/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
  74. data/.claude/hooks/node_modules/@types/node/ts5.6/index.d.ts +117 -0
  75. data/.claude/hooks/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
  76. data/.claude/hooks/node_modules/@types/node/ts5.7/index.d.ts +117 -0
  77. data/.claude/hooks/node_modules/@types/node/tty.d.ts +250 -0
  78. data/.claude/hooks/node_modules/@types/node/url.d.ts +519 -0
  79. data/.claude/hooks/node_modules/@types/node/util/types.d.ts +558 -0
  80. data/.claude/hooks/node_modules/@types/node/util.d.ts +1653 -0
  81. data/.claude/hooks/node_modules/@types/node/v8.d.ts +979 -0
  82. data/.claude/hooks/node_modules/@types/node/vm.d.ts +1180 -0
  83. data/.claude/hooks/node_modules/@types/node/wasi.d.ts +202 -0
  84. data/.claude/hooks/node_modules/@types/node/web-globals/abortcontroller.d.ts +59 -0
  85. data/.claude/hooks/node_modules/@types/node/web-globals/blob.d.ts +23 -0
  86. data/.claude/hooks/node_modules/@types/node/web-globals/console.d.ts +9 -0
  87. data/.claude/hooks/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
  88. data/.claude/hooks/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
  89. data/.claude/hooks/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
  90. data/.claude/hooks/node_modules/@types/node/web-globals/events.d.ts +106 -0
  91. data/.claude/hooks/node_modules/@types/node/web-globals/fetch.d.ts +54 -0
  92. data/.claude/hooks/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
  93. data/.claude/hooks/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
  94. data/.claude/hooks/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
  95. data/.claude/hooks/node_modules/@types/node/web-globals/performance.d.ts +45 -0
  96. data/.claude/hooks/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  97. data/.claude/hooks/node_modules/@types/node/web-globals/streams.d.ts +115 -0
  98. data/.claude/hooks/node_modules/@types/node/web-globals/timers.d.ts +44 -0
  99. data/.claude/hooks/node_modules/@types/node/web-globals/url.d.ts +24 -0
  100. data/.claude/hooks/node_modules/@types/node/worker_threads.d.ts +714 -0
  101. data/.claude/hooks/node_modules/@types/node/zlib.d.ts +618 -0
  102. data/.claude/hooks/node_modules/esbuild/LICENSE.md +21 -0
  103. data/.claude/hooks/node_modules/esbuild/README.md +3 -0
  104. data/.claude/hooks/node_modules/esbuild/bin/esbuild +0 -0
  105. data/.claude/hooks/node_modules/esbuild/install.js +289 -0
  106. data/.claude/hooks/node_modules/esbuild/lib/main.d.ts +716 -0
  107. data/.claude/hooks/node_modules/esbuild/lib/main.js +2242 -0
  108. data/.claude/hooks/node_modules/esbuild/package.json +49 -0
  109. data/.claude/hooks/node_modules/get-tsconfig/LICENSE +21 -0
  110. data/.claude/hooks/node_modules/get-tsconfig/README.md +235 -0
  111. data/.claude/hooks/node_modules/get-tsconfig/dist/index.cjs +7 -0
  112. data/.claude/hooks/node_modules/get-tsconfig/dist/index.d.cts +2088 -0
  113. data/.claude/hooks/node_modules/get-tsconfig/dist/index.d.mts +2088 -0
  114. data/.claude/hooks/node_modules/get-tsconfig/dist/index.mjs +7 -0
  115. data/.claude/hooks/node_modules/get-tsconfig/package.json +46 -0
  116. data/.claude/hooks/node_modules/resolve-pkg-maps/LICENSE +21 -0
  117. data/.claude/hooks/node_modules/resolve-pkg-maps/README.md +216 -0
  118. data/.claude/hooks/node_modules/resolve-pkg-maps/dist/index.cjs +1 -0
  119. data/.claude/hooks/node_modules/resolve-pkg-maps/dist/index.d.cts +11 -0
  120. data/.claude/hooks/node_modules/resolve-pkg-maps/dist/index.d.mts +11 -0
  121. data/.claude/hooks/node_modules/resolve-pkg-maps/dist/index.mjs +1 -0
  122. data/.claude/hooks/node_modules/resolve-pkg-maps/package.json +42 -0
  123. data/.claude/hooks/node_modules/tsx/LICENSE +21 -0
  124. data/.claude/hooks/node_modules/tsx/README.md +32 -0
  125. data/.claude/hooks/node_modules/tsx/dist/cjs/api/index.cjs +1 -0
  126. data/.claude/hooks/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
  127. data/.claude/hooks/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
  128. data/.claude/hooks/node_modules/tsx/dist/cjs/api/index.mjs +1 -0
  129. data/.claude/hooks/node_modules/tsx/dist/cjs/index.cjs +1 -0
  130. data/.claude/hooks/node_modules/tsx/dist/cjs/index.mjs +1 -0
  131. data/.claude/hooks/node_modules/tsx/dist/cli.cjs +54 -0
  132. data/.claude/hooks/node_modules/tsx/dist/cli.mjs +55 -0
  133. data/.claude/hooks/node_modules/tsx/dist/client-BQVF1NaW.mjs +1 -0
  134. data/.claude/hooks/node_modules/tsx/dist/client-D6NvIMSC.cjs +1 -0
  135. data/.claude/hooks/node_modules/tsx/dist/esm/api/index.cjs +1 -0
  136. data/.claude/hooks/node_modules/tsx/dist/esm/api/index.d.cts +35 -0
  137. data/.claude/hooks/node_modules/tsx/dist/esm/api/index.d.mts +35 -0
  138. data/.claude/hooks/node_modules/tsx/dist/esm/api/index.mjs +1 -0
  139. data/.claude/hooks/node_modules/tsx/dist/esm/index.cjs +2 -0
  140. data/.claude/hooks/node_modules/tsx/dist/esm/index.mjs +2 -0
  141. data/.claude/hooks/node_modules/tsx/dist/get-pipe-path-BHW2eJdv.mjs +1 -0
  142. data/.claude/hooks/node_modules/tsx/dist/get-pipe-path-BoR10qr8.cjs +1 -0
  143. data/.claude/hooks/node_modules/tsx/dist/index-7AaEi15b.mjs +14 -0
  144. data/.claude/hooks/node_modules/tsx/dist/index-BWFBUo6r.cjs +1 -0
  145. data/.claude/hooks/node_modules/tsx/dist/index-gbaejti9.mjs +1 -0
  146. data/.claude/hooks/node_modules/tsx/dist/index-gckBtVBf.cjs +14 -0
  147. data/.claude/hooks/node_modules/tsx/dist/lexer-DQCqS3nf.mjs +3 -0
  148. data/.claude/hooks/node_modules/tsx/dist/lexer-DgIbo0BU.cjs +3 -0
  149. data/.claude/hooks/node_modules/tsx/dist/loader.cjs +1 -0
  150. data/.claude/hooks/node_modules/tsx/dist/loader.mjs +1 -0
  151. data/.claude/hooks/node_modules/tsx/dist/node-features-_8ZFwP_x.mjs +1 -0
  152. data/.claude/hooks/node_modules/tsx/dist/node-features-roYmp9jK.cjs +1 -0
  153. data/.claude/hooks/node_modules/tsx/dist/package-CeBgXWuR.mjs +1 -0
  154. data/.claude/hooks/node_modules/tsx/dist/package-Dxt5kIHw.cjs +1 -0
  155. data/.claude/hooks/node_modules/tsx/dist/patch-repl.cjs +1 -0
  156. data/.claude/hooks/node_modules/tsx/dist/patch-repl.mjs +1 -0
  157. data/.claude/hooks/node_modules/tsx/dist/preflight.cjs +1 -0
  158. data/.claude/hooks/node_modules/tsx/dist/preflight.mjs +1 -0
  159. data/.claude/hooks/node_modules/tsx/dist/register-2sWVXuRQ.cjs +1 -0
  160. data/.claude/hooks/node_modules/tsx/dist/register-B7jrtLTO.mjs +1 -0
  161. data/.claude/hooks/node_modules/tsx/dist/register-CFH5oNdT.mjs +4 -0
  162. data/.claude/hooks/node_modules/tsx/dist/register-D46fvsV_.cjs +4 -0
  163. data/.claude/hooks/node_modules/tsx/dist/repl.cjs +3 -0
  164. data/.claude/hooks/node_modules/tsx/dist/repl.mjs +3 -0
  165. data/.claude/hooks/node_modules/tsx/dist/require-D4F1Lv60.cjs +1 -0
  166. data/.claude/hooks/node_modules/tsx/dist/require-DQxpCAr4.mjs +1 -0
  167. data/.claude/hooks/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
  168. data/.claude/hooks/node_modules/tsx/dist/suppress-warnings.mjs +1 -0
  169. data/.claude/hooks/node_modules/tsx/dist/temporary-directory-B83uKxJF.cjs +1 -0
  170. data/.claude/hooks/node_modules/tsx/dist/temporary-directory-CwHp0_NW.mjs +1 -0
  171. data/.claude/hooks/node_modules/tsx/dist/types-Cxp8y2TL.d.ts +5 -0
  172. data/.claude/hooks/node_modules/tsx/package.json +68 -0
  173. data/.claude/hooks/node_modules/typescript/LICENSE.txt +55 -0
  174. data/.claude/hooks/node_modules/typescript/README.md +50 -0
  175. data/.claude/hooks/node_modules/typescript/SECURITY.md +41 -0
  176. data/.claude/hooks/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
  177. data/.claude/hooks/node_modules/typescript/bin/tsc +2 -0
  178. data/.claude/hooks/node_modules/typescript/bin/tsserver +2 -0
  179. data/.claude/hooks/node_modules/typescript/lib/_tsc.js +133818 -0
  180. data/.claude/hooks/node_modules/typescript/lib/_tsserver.js +659 -0
  181. data/.claude/hooks/node_modules/typescript/lib/_typingsInstaller.js +222 -0
  182. data/.claude/hooks/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2122 -0
  183. data/.claude/hooks/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2122 -0
  184. data/.claude/hooks/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2122 -0
  185. data/.claude/hooks/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2122 -0
  186. data/.claude/hooks/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2122 -0
  187. data/.claude/hooks/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2122 -0
  188. data/.claude/hooks/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2122 -0
  189. data/.claude/hooks/node_modules/typescript/lib/lib.d.ts +22 -0
  190. data/.claude/hooks/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
  191. data/.claude/hooks/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
  192. data/.claude/hooks/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
  193. data/.claude/hooks/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
  194. data/.claude/hooks/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
  195. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
  196. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
  197. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
  198. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
  199. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
  200. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
  201. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
  202. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
  203. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
  204. data/.claude/hooks/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
  205. data/.claude/hooks/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
  206. data/.claude/hooks/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
  207. data/.claude/hooks/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
  208. data/.claude/hooks/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
  209. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
  210. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
  211. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
  212. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
  213. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
  214. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
  215. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
  216. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
  217. data/.claude/hooks/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
  218. data/.claude/hooks/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
  219. data/.claude/hooks/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
  220. data/.claude/hooks/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
  221. data/.claude/hooks/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
  222. data/.claude/hooks/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
  223. data/.claude/hooks/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
  224. data/.claude/hooks/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
  225. data/.claude/hooks/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
  226. data/.claude/hooks/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
  227. data/.claude/hooks/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
  228. data/.claude/hooks/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
  229. data/.claude/hooks/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
  230. data/.claude/hooks/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
  231. data/.claude/hooks/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
  232. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
  233. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
  234. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
  235. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
  236. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
  237. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
  238. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
  239. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
  240. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
  241. data/.claude/hooks/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
  242. data/.claude/hooks/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
  243. data/.claude/hooks/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
  244. data/.claude/hooks/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
  245. data/.claude/hooks/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
  246. data/.claude/hooks/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
  247. data/.claude/hooks/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
  248. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
  249. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
  250. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
  251. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
  252. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
  253. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
  254. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
  255. data/.claude/hooks/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
  256. data/.claude/hooks/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
  257. data/.claude/hooks/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
  258. data/.claude/hooks/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
  259. data/.claude/hooks/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
  260. data/.claude/hooks/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
  261. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
  262. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
  263. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
  264. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
  265. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
  266. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
  267. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
  268. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
  269. data/.claude/hooks/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
  270. data/.claude/hooks/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
  271. data/.claude/hooks/node_modules/typescript/lib/lib.es6.d.ts +23 -0
  272. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
  273. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
  274. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
  275. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
  276. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
  277. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
  278. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
  279. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
  280. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
  281. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
  282. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
  283. data/.claude/hooks/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
  284. data/.claude/hooks/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
  285. data/.claude/hooks/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
  286. data/.claude/hooks/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
  287. data/.claude/hooks/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
  288. data/.claude/hooks/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
  289. data/.claude/hooks/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2122 -0
  290. data/.claude/hooks/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2122 -0
  291. data/.claude/hooks/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2122 -0
  292. data/.claude/hooks/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2122 -0
  293. data/.claude/hooks/node_modules/typescript/lib/tsc.js +8 -0
  294. data/.claude/hooks/node_modules/typescript/lib/tsserver.js +8 -0
  295. data/.claude/hooks/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
  296. data/.claude/hooks/node_modules/typescript/lib/tsserverlibrary.js +21 -0
  297. data/.claude/hooks/node_modules/typescript/lib/typesMap.json +497 -0
  298. data/.claude/hooks/node_modules/typescript/lib/typescript.d.ts +11437 -0
  299. data/.claude/hooks/node_modules/typescript/lib/typescript.js +200276 -0
  300. data/.claude/hooks/node_modules/typescript/lib/typingsInstaller.js +8 -0
  301. data/.claude/hooks/node_modules/typescript/lib/watchGuard.js +53 -0
  302. data/.claude/hooks/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2122 -0
  303. data/.claude/hooks/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2122 -0
  304. data/.claude/hooks/node_modules/typescript/package.json +120 -0
  305. data/.claude/hooks/node_modules/undici-types/LICENSE +21 -0
  306. data/.claude/hooks/node_modules/undici-types/README.md +6 -0
  307. data/.claude/hooks/node_modules/undici-types/agent.d.ts +32 -0
  308. data/.claude/hooks/node_modules/undici-types/api.d.ts +43 -0
  309. data/.claude/hooks/node_modules/undici-types/balanced-pool.d.ts +29 -0
  310. data/.claude/hooks/node_modules/undici-types/cache-interceptor.d.ts +172 -0
  311. data/.claude/hooks/node_modules/undici-types/cache.d.ts +36 -0
  312. data/.claude/hooks/node_modules/undici-types/client-stats.d.ts +15 -0
  313. data/.claude/hooks/node_modules/undici-types/client.d.ts +108 -0
  314. data/.claude/hooks/node_modules/undici-types/connector.d.ts +34 -0
  315. data/.claude/hooks/node_modules/undici-types/content-type.d.ts +21 -0
  316. data/.claude/hooks/node_modules/undici-types/cookies.d.ts +30 -0
  317. data/.claude/hooks/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
  318. data/.claude/hooks/node_modules/undici-types/dispatcher.d.ts +276 -0
  319. data/.claude/hooks/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
  320. data/.claude/hooks/node_modules/undici-types/errors.d.ts +161 -0
  321. data/.claude/hooks/node_modules/undici-types/eventsource.d.ts +66 -0
  322. data/.claude/hooks/node_modules/undici-types/fetch.d.ts +211 -0
  323. data/.claude/hooks/node_modules/undici-types/formdata.d.ts +108 -0
  324. data/.claude/hooks/node_modules/undici-types/global-dispatcher.d.ts +9 -0
  325. data/.claude/hooks/node_modules/undici-types/global-origin.d.ts +7 -0
  326. data/.claude/hooks/node_modules/undici-types/h2c-client.d.ts +73 -0
  327. data/.claude/hooks/node_modules/undici-types/handlers.d.ts +15 -0
  328. data/.claude/hooks/node_modules/undici-types/header.d.ts +160 -0
  329. data/.claude/hooks/node_modules/undici-types/index.d.ts +80 -0
  330. data/.claude/hooks/node_modules/undici-types/interceptors.d.ts +39 -0
  331. data/.claude/hooks/node_modules/undici-types/mock-agent.d.ts +68 -0
  332. data/.claude/hooks/node_modules/undici-types/mock-call-history.d.ts +111 -0
  333. data/.claude/hooks/node_modules/undici-types/mock-client.d.ts +27 -0
  334. data/.claude/hooks/node_modules/undici-types/mock-errors.d.ts +12 -0
  335. data/.claude/hooks/node_modules/undici-types/mock-interceptor.d.ts +94 -0
  336. data/.claude/hooks/node_modules/undici-types/mock-pool.d.ts +27 -0
  337. data/.claude/hooks/node_modules/undici-types/package.json +55 -0
  338. data/.claude/hooks/node_modules/undici-types/patch.d.ts +29 -0
  339. data/.claude/hooks/node_modules/undici-types/pool-stats.d.ts +19 -0
  340. data/.claude/hooks/node_modules/undici-types/pool.d.ts +41 -0
  341. data/.claude/hooks/node_modules/undici-types/proxy-agent.d.ts +29 -0
  342. data/.claude/hooks/node_modules/undici-types/readable.d.ts +68 -0
  343. data/.claude/hooks/node_modules/undici-types/retry-agent.d.ts +8 -0
  344. data/.claude/hooks/node_modules/undici-types/retry-handler.d.ts +125 -0
  345. data/.claude/hooks/node_modules/undici-types/snapshot-agent.d.ts +109 -0
  346. data/.claude/hooks/node_modules/undici-types/util.d.ts +18 -0
  347. data/.claude/hooks/node_modules/undici-types/utility.d.ts +7 -0
  348. data/.claude/hooks/node_modules/undici-types/webidl.d.ts +341 -0
  349. data/.claude/hooks/node_modules/undici-types/websocket.d.ts +186 -0
  350. data/.claude/hooks/package-lock.json +562 -0
  351. data/.claude/hooks/package.json +19 -0
  352. data/.claude/hooks/skill-activation-prompt.sh +11 -0
  353. data/.claude/hooks/skill-activation-prompt.ts +185 -0
  354. data/.claude/hooks/tsconfig.json +12 -0
  355. data/.claude/settings.json +33 -0
  356. data/.claude/skills/README.md +29 -0
  357. data/.claude/skills/ruby-gem-development/SKILL.md +293 -0
  358. data/.claude/skills/skill-rules.json +117 -0
  359. data/.claude/skills/thor-cli-development/SKILL.md +431 -0
  360. data/CHANGELOG.md +7 -0
  361. data/Gemfile.lock +3 -1
  362. data/completions/{_aictl → _langop} +37 -37
  363. data/completions/{aictl.bash → langop.bash} +16 -16
  364. data/completions/langop.fish +114 -0
  365. data/components/agent/Gemfile +1 -1
  366. data/components/tool/Gemfile +1 -1
  367. data/docs/README.md +10 -10
  368. data/docs/agent-internals.md +1 -1
  369. data/docs/cheat-sheet.md +76 -76
  370. data/docs/cli-reference.md +71 -71
  371. data/docs/how-agents-work.md +1 -1
  372. data/docs/installation.md +30 -30
  373. data/docs/quickstart.md +30 -30
  374. data/docs/using-tools.md +11 -11
  375. data/docs/webhooks.md +6 -6
  376. data/examples/ux_helpers_demo.rb +4 -4
  377. data/lib/language_operator/agent/task_executor.rb +0 -37
  378. data/lib/language_operator/agent/web_server.rb +311 -323
  379. data/lib/language_operator/cli/commands/agent/base.rb +16 -15
  380. data/lib/language_operator/cli/commands/agent/code_operations.rb +6 -6
  381. data/lib/language_operator/cli/commands/agent/helpers/synthesis_watcher.rb +1 -1
  382. data/lib/language_operator/cli/commands/agent/lifecycle.rb +2 -2
  383. data/lib/language_operator/cli/commands/agent/logs.rb +2 -2
  384. data/lib/language_operator/cli/commands/agent/workspace.rb +8 -8
  385. data/lib/language_operator/cli/commands/cluster.rb +172 -158
  386. data/lib/language_operator/cli/commands/install.rb +878 -81
  387. data/lib/language_operator/cli/commands/model/base.rb +7 -6
  388. data/lib/language_operator/cli/commands/organization.rb +61 -0
  389. data/lib/language_operator/cli/commands/persona.rb +10 -8
  390. data/lib/language_operator/cli/commands/status.rb +83 -13
  391. data/lib/language_operator/cli/commands/system/exec.rb +10 -10
  392. data/lib/language_operator/cli/commands/system/schema.rb +6 -6
  393. data/lib/language_operator/cli/commands/system/synthesis_template.rb +6 -6
  394. data/lib/language_operator/cli/commands/system/synthesize.rb +14 -14
  395. data/lib/language_operator/cli/commands/system/validate_template.rb +4 -4
  396. data/lib/language_operator/cli/commands/tool/base.rb +1 -1
  397. data/lib/language_operator/cli/commands/tool/install.rb +2 -2
  398. data/lib/language_operator/cli/commands/tool/search.rb +3 -3
  399. data/lib/language_operator/cli/commands/ui.rb +173 -0
  400. data/lib/language_operator/cli/commands/use.rb +80 -9
  401. data/lib/language_operator/cli/errors/suggestions.rb +21 -21
  402. data/lib/language_operator/cli/formatters/progress_formatter.rb +2 -1
  403. data/lib/language_operator/cli/formatters/table_formatter.rb +21 -1
  404. data/lib/language_operator/cli/helpers/cluster_validator.rb +2 -2
  405. data/lib/language_operator/cli/helpers/health_checker.rb +263 -0
  406. data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +3 -3
  407. data/lib/language_operator/cli/helpers/ux_helper.rb +4 -3
  408. data/lib/language_operator/cli/main.rb +30 -21
  409. data/lib/language_operator/cli/wizards/model_wizard.rb +7 -5
  410. data/lib/language_operator/config/cluster_config.rb +3 -3
  411. data/lib/language_operator/constants/kubernetes_labels.rb +1 -1
  412. data/lib/language_operator/kubernetes/client.rb +53 -109
  413. data/lib/language_operator/kubernetes/resource_builder.rb +46 -14
  414. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
  415. data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
  416. data/lib/language_operator/utils/org_context.rb +100 -0
  417. data/lib/language_operator/version.rb +1 -1
  418. data/synth/001/Makefile +1 -1
  419. data/synth/002/Makefile +1 -1
  420. data/synth/003/Makefile +1 -1
  421. data/synth/004/Makefile +1 -1
  422. metadata +384 -11
  423. data/completions/aictl.fish +0 -114
  424. data/lib/language_operator/agent/event_config.rb +0 -172
  425. data/lib/language_operator/cli/commands/quickstart.rb +0 -22
  426. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +0 -561
  427. /data/bin/{aictl → langop} +0 -0
@@ -215,6 +215,317 @@ module LanguageOperator
215
215
  puts 'Registered /api/v1/workspace/* endpoints for file management'
216
216
  end
217
217
 
218
+ # Workspace file management handlers
219
+
220
+ # Handle GET /api/v1/workspace/files - list directory contents
221
+ def handle_workspace_list(context)
222
+ request = context[:request]
223
+ path = request.params['path'] || '/'
224
+
225
+ # Sanitize path to prevent directory traversal
226
+ safe_path = sanitize_workspace_path(path)
227
+ full_path = File.join(@workspace_path, safe_path)
228
+
229
+ return workspace_error_response(404, 'NotFound', "Path not found: #{path}") unless File.exist?(full_path)
230
+
231
+ if File.directory?(full_path)
232
+ list_directory_contents(full_path, path)
233
+ else
234
+ # If it's a file, return file info
235
+ get_file_info(full_path, path)
236
+ end
237
+ rescue StandardError => e
238
+ workspace_error_response(500, 'InternalError', e.message)
239
+ end
240
+
241
+ # Handle GET /api/v1/workspace/files/view - view file contents
242
+ def handle_workspace_view(context)
243
+ request = context[:request]
244
+ path = request.params['path']
245
+
246
+ return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path
247
+
248
+ # Sanitize path to prevent directory traversal
249
+ safe_path = sanitize_workspace_path(path)
250
+ full_path = File.join(@workspace_path, safe_path)
251
+
252
+ return workspace_error_response(404, 'NotFound', "File not found: #{path}") unless File.exist?(full_path)
253
+
254
+ return workspace_error_response(400, 'BadRequest', "Path is not a file: #{path}") unless File.file?(full_path)
255
+
256
+ # Check file size (limit to 10MB for API responses)
257
+ file_size = File.size(full_path)
258
+ return workspace_error_response(413, 'FileTooLarge', 'File too large for viewing (max 10MB)') if file_size > 10 * 1024 * 1024
259
+
260
+ # Read file contents
261
+ contents = File.read(full_path)
262
+
263
+ {
264
+ status: 200,
265
+ body: {
266
+ path: path,
267
+ size: file_size,
268
+ contents: contents
269
+ },
270
+ headers: { 'Content-Type' => 'application/json' }
271
+ }
272
+ rescue StandardError => e
273
+ workspace_error_response(500, 'InternalError', e.message)
274
+ end
275
+
276
+ # Handle POST /api/v1/workspace/files - upload file
277
+ def handle_workspace_upload(context)
278
+ request = context[:request]
279
+
280
+ # Parse multipart form data
281
+ if request.content_type&.start_with?('multipart/form-data')
282
+ boundary = request.content_type.split('boundary=')[1]
283
+ return workspace_error_response(400, 'BadRequest', 'Missing boundary in multipart data') unless boundary
284
+
285
+ # Parse the multipart data
286
+ body = request.body.read
287
+ parts = parse_multipart(body, boundary)
288
+
289
+ file_part = parts.find { |part| part[:name] == 'file' }
290
+ path_param = parts.find { |part| part[:name] == 'path' }&.dig(:content)
291
+
292
+ return workspace_error_response(400, 'BadRequest', 'file parameter required') unless file_part
293
+ return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path_param
294
+
295
+ # Sanitize path to prevent directory traversal
296
+ safe_path = sanitize_workspace_path(path_param)
297
+ full_path = File.join(@workspace_path, safe_path)
298
+
299
+ # Check file size (limit to 100MB)
300
+ content = file_part[:content]
301
+ return workspace_error_response(413, 'FileTooLarge', 'File too large (max 100MB)') if content.bytesize > 100 * 1024 * 1024
302
+
303
+ # Create directory if it doesn't exist
304
+ FileUtils.mkdir_p(File.dirname(full_path))
305
+
306
+ # Write file
307
+ File.binwrite(full_path, content)
308
+
309
+ {
310
+ status: 201,
311
+ body: {
312
+ message: 'File uploaded successfully',
313
+ path: path_param,
314
+ size: content.bytesize
315
+ },
316
+ headers: { 'Content-Type' => 'application/json' }
317
+ }
318
+ else
319
+ workspace_error_response(400, 'BadRequest', 'Expected multipart/form-data')
320
+ end
321
+ rescue StandardError => e
322
+ workspace_error_response(500, 'UploadError', "Upload failed: #{e.message}")
323
+ end
324
+
325
+ # Handle DELETE /api/v1/workspace/files - delete file
326
+ def handle_workspace_delete(context)
327
+ request = context[:request]
328
+ path = request.params['path']
329
+
330
+ return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path
331
+
332
+ # Sanitize path to prevent directory traversal
333
+ safe_path = sanitize_workspace_path(path)
334
+ full_path = File.join(@workspace_path, safe_path)
335
+
336
+ return workspace_error_response(404, 'NotFound', "File not found: #{path}") unless File.exist?(full_path)
337
+
338
+ # Delete file or directory
339
+ if File.directory?(full_path)
340
+ FileUtils.rm_rf(full_path)
341
+ else
342
+ File.delete(full_path)
343
+ end
344
+
345
+ {
346
+ status: 200,
347
+ body: {
348
+ message: 'File deleted successfully',
349
+ path: path
350
+ },
351
+ headers: { 'Content-Type' => 'application/json' }
352
+ }
353
+ rescue StandardError => e
354
+ workspace_error_response(500, 'DeleteError', "Deletion failed: #{e.message}")
355
+ end
356
+
357
+ # Handle GET /api/v1/workspace/files/download - download file or directory as tar.gz
358
+ def handle_workspace_download(context)
359
+ request = context[:request]
360
+ path = request.params['path']
361
+
362
+ return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path
363
+
364
+ # Sanitize path to prevent directory traversal
365
+ safe_path = sanitize_workspace_path(path)
366
+ full_path = File.join(@workspace_path, safe_path)
367
+
368
+ return workspace_error_response(404, 'NotFound', "Path not found: #{path}") unless File.exist?(full_path)
369
+
370
+ if File.file?(full_path)
371
+ # Single file download
372
+ content = File.binread(full_path)
373
+ filename = File.basename(full_path)
374
+
375
+ {
376
+ status: 200,
377
+ body: content,
378
+ headers: {
379
+ 'Content-Type' => 'application/octet-stream',
380
+ 'Content-Disposition' => "attachment; filename=\"#{filename}\""
381
+ }
382
+ }
383
+ else
384
+ # Directory download as tar.gz
385
+ require 'tempfile'
386
+ require 'zlib'
387
+ require 'rubygems/package'
388
+
389
+ Tempfile.create(['workspace', '.tar.gz']) do |temp_file|
390
+ Zlib::GzipWriter.open(temp_file.path) do |gz|
391
+ Gem::Package::TarWriter.new(gz) do |tar|
392
+ add_directory_to_tar(tar, full_path, safe_path)
393
+ end
394
+ end
395
+
396
+ content = File.binread(temp_file.path)
397
+ filename = "#{File.basename(path)}.tar.gz"
398
+
399
+ {
400
+ status: 200,
401
+ body: content,
402
+ headers: {
403
+ 'Content-Type' => 'application/gzip',
404
+ 'Content-Disposition' => "attachment; filename=\"#{filename}\""
405
+ }
406
+ }
407
+ end
408
+ end
409
+ rescue StandardError => e
410
+ workspace_error_response(500, 'DownloadError', "Download failed: #{e.message}")
411
+ end
412
+
413
+ # Workspace helper methods
414
+
415
+ # Sanitize workspace path to prevent directory traversal attacks
416
+ def sanitize_workspace_path(path)
417
+ # Remove leading slash and resolve relative paths
418
+ clean_path = path.sub(%r{^/+}, '')
419
+
420
+ # Resolve and normalize the path
421
+ normalized = File.expand_path(clean_path, '/')
422
+
423
+ # Ensure it doesn't escape the root
424
+ return '' if normalized == '/' || !normalized.start_with?('/')
425
+
426
+ # Remove leading slash for joining with workspace_path
427
+ normalized[1..]
428
+ end
429
+
430
+ # List directory contents
431
+ def list_directory_contents(full_path, requested_path)
432
+ entries = Dir.entries(full_path).reject { |entry| entry.start_with?('.') }
433
+
434
+ files = entries.map do |entry|
435
+ entry_path = File.join(full_path, entry)
436
+ {
437
+ name: entry,
438
+ type: File.directory?(entry_path) ? 'directory' : 'file',
439
+ size: File.file?(entry_path) ? File.size(entry_path) : nil,
440
+ path: File.join(requested_path, entry).sub(%r{^/+}, '')
441
+ }
442
+ end
443
+
444
+ {
445
+ status: 200,
446
+ body: {
447
+ path: requested_path,
448
+ files: files
449
+ },
450
+ headers: { 'Content-Type' => 'application/json' }
451
+ }
452
+ end
453
+
454
+ # Get file information
455
+ def get_file_info(full_path, requested_path)
456
+ {
457
+ status: 200,
458
+ body: {
459
+ path: requested_path,
460
+ size: File.size(full_path),
461
+ type: 'file'
462
+ },
463
+ headers: { 'Content-Type' => 'application/json' }
464
+ }
465
+ end
466
+
467
+ # Parse multipart form data
468
+ def parse_multipart(body, boundary)
469
+ parts = []
470
+ boundary = "--#{boundary}"
471
+ sections = body.split(boundary)
472
+
473
+ sections[1..-2]&.each do |section|
474
+ next if section.strip.empty?
475
+
476
+ lines = section.split("\r\n")
477
+ content_disposition = lines.find { |line| line.include?('Content-Disposition') }
478
+ next unless content_disposition
479
+
480
+ name_match = content_disposition.match(/name="([^"]+)"/)
481
+ next unless name_match
482
+
483
+ name = name_match[1]
484
+
485
+ # Find empty line separating headers from content
486
+ content_start = lines.index('')
487
+ next unless content_start
488
+
489
+ content = lines[(content_start + 1)..-1].join("\r\n")
490
+ content = content[0..-3] if content.end_with?("\r\n") # Remove trailing CRLF
491
+
492
+ parts << { name: name, content: content }
493
+ end
494
+
495
+ parts
496
+ end
497
+
498
+ # Add directory to tar archive recursively
499
+ def add_directory_to_tar(tar, source_path, archive_path)
500
+ Dir.entries(source_path).each do |entry|
501
+ next if entry.start_with?('.')
502
+
503
+ full_path = File.join(source_path, entry)
504
+ tar_path = File.join(archive_path, entry)
505
+
506
+ if File.directory?(full_path)
507
+ tar.mkdir(tar_path, 0755)
508
+ add_directory_to_tar(tar, full_path, tar_path)
509
+ else
510
+ tar.add_file(tar_path, 0644) do |io|
511
+ io.write(File.binread(full_path))
512
+ end
513
+ end
514
+ end
515
+ end
516
+
517
+ # Generate workspace error response
518
+ def workspace_error_response(status, error_type, message)
519
+ {
520
+ status: status,
521
+ body: {
522
+ error: error_type,
523
+ message: message
524
+ },
525
+ headers: { 'Content-Type' => 'application/json' }
526
+ }
527
+ end
528
+
218
529
  # Handle incoming HTTP request
219
530
  #
220
531
  # @param env [Hash] Rack environment
@@ -966,330 +1277,7 @@ module LanguageOperator
966
1277
  stream.close
967
1278
  end
968
1279
 
969
- # Workspace file management handlers
970
1280
 
971
- # Handle GET /api/v1/workspace/files - list directory contents
972
- def handle_workspace_list(context)
973
- request = context[:request]
974
- path = request.params['path'] || '/'
975
-
976
- # Sanitize path to prevent directory traversal
977
- safe_path = sanitize_workspace_path(path)
978
- full_path = File.join(@workspace_path, safe_path)
979
-
980
- return workspace_error_response(404, 'NotFound', "Path not found: #{path}") unless File.exist?(full_path)
981
-
982
- if File.directory?(full_path)
983
- list_directory_contents(full_path, path)
984
- else
985
- # If it's a file, return file info
986
- get_file_info(full_path, path)
987
- end
988
- rescue StandardError => e
989
- workspace_error_response(500, 'InternalError', e.message)
990
- end
991
-
992
- # Handle GET /api/v1/workspace/files/view - view file contents
993
- def handle_workspace_view(context)
994
- request = context[:request]
995
- path = request.params['path']
996
-
997
- return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path
998
-
999
- # Sanitize path to prevent directory traversal
1000
- safe_path = sanitize_workspace_path(path)
1001
- full_path = File.join(@workspace_path, safe_path)
1002
-
1003
- return workspace_error_response(404, 'NotFound', "File not found: #{path}") unless File.exist?(full_path)
1004
-
1005
- return workspace_error_response(400, 'BadRequest', "Path is not a file: #{path}") unless File.file?(full_path)
1006
-
1007
- # Check file size (limit to 10MB for API responses)
1008
- file_size = File.size(full_path)
1009
- return workspace_error_response(413, 'FileTooLarge', 'File too large for viewing (max 10MB)') if file_size > 10 * 1024 * 1024
1010
-
1011
- # Read file contents
1012
- contents = File.read(full_path)
1013
-
1014
- {
1015
- status: 200,
1016
- body: {
1017
- path: path,
1018
- size: file_size,
1019
- modified: File.mtime(full_path).iso8601,
1020
- content: contents,
1021
- content_type: detect_content_type(full_path)
1022
- }.to_json,
1023
- headers: { 'Content-Type' => 'application/json' }
1024
- }
1025
- rescue StandardError => e
1026
- workspace_error_response(500, 'InternalError', e.message)
1027
- end
1028
-
1029
- # Handle POST /api/v1/workspace/files - upload file
1030
- def handle_workspace_upload(context)
1031
- request = context[:request]
1032
-
1033
- # Check for multipart form data
1034
- content_type = request.env['CONTENT_TYPE']
1035
- unless content_type&.start_with?('multipart/form-data')
1036
- return workspace_error_response(400, 'BadRequest', 'Content-Type must be multipart/form-data')
1037
- end
1038
-
1039
- # Parse multipart data
1040
- begin
1041
- # Get file from params (Rack automatically parses multipart data)
1042
- file_param = request.params['file']
1043
- path_param = request.params['path']
1044
-
1045
- return workspace_error_response(400, 'BadRequest', 'file parameter required') unless file_param
1046
-
1047
- return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path_param
1048
-
1049
- # Handle file upload object
1050
- return workspace_error_response(400, 'BadRequest', 'Invalid file upload format') unless file_param.is_a?(Hash) && file_param[:tempfile]
1051
-
1052
- # Rack file upload format
1053
- tempfile = file_param[:tempfile]
1054
- original_filename = file_param[:filename]
1055
- content_type = file_param[:type]
1056
-
1057
- # Sanitize target path
1058
- safe_path = sanitize_workspace_path(path_param)
1059
- full_path = File.join(@workspace_path, safe_path)
1060
-
1061
- # Ensure target directory exists
1062
- target_dir = File.dirname(full_path)
1063
- FileUtils.mkdir_p(target_dir) unless File.directory?(target_dir)
1064
-
1065
- # Check file size (limit to 100MB)
1066
- file_size = tempfile.size
1067
- return workspace_error_response(413, 'FileTooLarge', 'File too large for upload (max 100MB)') if file_size > 100 * 1024 * 1024
1068
-
1069
- # Copy uploaded file to workspace
1070
- tempfile.rewind
1071
- File.open(full_path, 'wb') do |dest_file|
1072
- IO.copy_stream(tempfile, dest_file)
1073
- end
1074
-
1075
- # Set reasonable permissions
1076
- File.chmod(0o644, full_path)
1077
-
1078
- {
1079
- status: 201,
1080
- body: {
1081
- path: path_param,
1082
- size: file_size,
1083
- original_filename: original_filename,
1084
- content_type: content_type,
1085
- message: 'File uploaded successfully'
1086
- }.to_json,
1087
- headers: { 'Content-Type' => 'application/json' }
1088
- }
1089
- rescue StandardError => e
1090
- workspace_error_response(500, 'UploadError', "Upload failed: #{e.message}")
1091
- ensure
1092
- # Clean up temporary file
1093
- tempfile&.close if tempfile.respond_to?(:close)
1094
- end
1095
- end
1096
-
1097
- # Handle DELETE /api/v1/workspace/files - delete file
1098
- def handle_workspace_delete(context)
1099
- request = context[:request]
1100
- path = request.params['path']
1101
-
1102
- return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path
1103
-
1104
- # Sanitize path to prevent directory traversal
1105
- safe_path = sanitize_workspace_path(path)
1106
- full_path = File.join(@workspace_path, safe_path)
1107
-
1108
- return workspace_error_response(404, 'NotFound', "Path not found: #{path}") unless File.exist?(full_path)
1109
-
1110
- begin
1111
- deleted_info = {
1112
- path: path,
1113
- type: File.directory?(full_path) ? 'directory' : 'file',
1114
- size: File.directory?(full_path) ? nil : File.size(full_path)
1115
- }
1116
-
1117
- if File.directory?(full_path)
1118
- # Delete directory and all contents
1119
- FileUtils.rm_rf(full_path)
1120
- deleted_info[:message] = 'Directory deleted successfully'
1121
- else
1122
- # Delete single file
1123
- File.delete(full_path)
1124
- deleted_info[:message] = 'File deleted successfully'
1125
- end
1126
-
1127
- {
1128
- status: 200,
1129
- body: deleted_info.to_json,
1130
- headers: { 'Content-Type' => 'application/json' }
1131
- }
1132
- rescue StandardError => e
1133
- workspace_error_response(500, 'DeleteError', "Delete failed: #{e.message}")
1134
- end
1135
- end
1136
-
1137
- # Handle GET /api/v1/workspace/files/download - download file/archive
1138
- def handle_workspace_download(context)
1139
- request = context[:request]
1140
- path = request.params['path']
1141
-
1142
- return workspace_error_response(400, 'BadRequest', 'path parameter required') unless path
1143
-
1144
- # Sanitize path to prevent directory traversal
1145
- safe_path = sanitize_workspace_path(path)
1146
- full_path = File.join(@workspace_path, safe_path)
1147
-
1148
- return workspace_error_response(404, 'NotFound', "Path not found: #{path}") unless File.exist?(full_path)
1149
-
1150
- begin
1151
- if File.directory?(full_path)
1152
- # Create temporary tar.gz archive for directory
1153
- archive_name = "#{File.basename(safe_path.empty? ? 'workspace' : safe_path)}.tar.gz"
1154
- temp_archive = "/tmp/#{SecureRandom.hex(8)}_#{archive_name}"
1155
-
1156
- # Create tar archive
1157
- system('tar', '-czf', temp_archive, '-C', File.dirname(full_path), File.basename(full_path))
1158
-
1159
- return workspace_error_response(500, 'ArchiveError', 'Failed to create archive') unless File.exist?(temp_archive)
1160
-
1161
- # Read archive contents
1162
- archive_data = File.binread(temp_archive)
1163
-
1164
- # Clean up temporary file
1165
- File.delete(temp_archive)
1166
-
1167
- {
1168
- status: 200,
1169
- body: archive_data,
1170
- headers: {
1171
- 'Content-Type' => 'application/gzip',
1172
- 'Content-Disposition' => "attachment; filename=\"#{archive_name}\"",
1173
- 'Content-Length' => archive_data.length.to_s
1174
- }
1175
- }
1176
- else
1177
- # Direct file download
1178
- file_data = File.binread(full_path)
1179
- filename = File.basename(safe_path)
1180
- content_type = detect_content_type(full_path)
1181
-
1182
- # For binary files, use application/octet-stream
1183
- content_disposition = if content_type == 'application/octet-stream'
1184
- "attachment; filename=\"#{filename}\""
1185
- else
1186
- # For text files, allow inline viewing
1187
- "inline; filename=\"#{filename}\""
1188
- end
1189
-
1190
- {
1191
- status: 200,
1192
- body: file_data,
1193
- headers: {
1194
- 'Content-Type' => content_type,
1195
- 'Content-Disposition' => content_disposition,
1196
- 'Content-Length' => file_data.length.to_s
1197
- }
1198
- }
1199
- end
1200
- rescue StandardError => e
1201
- workspace_error_response(500, 'DownloadError', "Download failed: #{e.message}")
1202
- end
1203
- end
1204
-
1205
- private
1206
-
1207
- # Sanitize workspace path to prevent directory traversal attacks
1208
- def sanitize_workspace_path(path)
1209
- # Remove leading slash and resolve relative paths
1210
- clean_path = path.sub(%r{^/+}, '')
1211
-
1212
- # Resolve and normalize the path
1213
- normalized = File.expand_path(clean_path, '/')
1214
-
1215
- # Ensure it doesn't escape the root
1216
- return '' if normalized == '/' || !normalized.start_with?('/')
1217
-
1218
- # Remove leading slash for joining with workspace_path
1219
- normalized[1..]
1220
- end
1221
-
1222
- # List directory contents
1223
- def list_directory_contents(full_path, requested_path)
1224
- entries = Dir.entries(full_path).reject { |entry| entry.start_with?('.') }
1225
-
1226
- files = entries.map do |entry|
1227
- entry_path = File.join(full_path, entry)
1228
- relative_path = File.join(requested_path == '/' ? '' : requested_path, entry)
1229
-
1230
- {
1231
- name: entry,
1232
- path: "/#{relative_path}".gsub(%r{/+}, '/'),
1233
- type: File.directory?(entry_path) ? 'directory' : 'file',
1234
- size: File.directory?(entry_path) ? nil : File.size(entry_path),
1235
- modified: File.mtime(entry_path).iso8601
1236
- }
1237
- end
1238
-
1239
- files.sort_by! { |f| [f[:type] == 'directory' ? 0 : 1, f[:name]] }
1240
-
1241
- {
1242
- status: 200,
1243
- body: {
1244
- path: requested_path,
1245
- files: files,
1246
- total: files.length
1247
- }.to_json,
1248
- headers: { 'Content-Type' => 'application/json' }
1249
- }
1250
- end
1251
-
1252
- # Get file info for a single file
1253
- def get_file_info(full_path, requested_path)
1254
- {
1255
- status: 200,
1256
- body: {
1257
- path: requested_path,
1258
- type: 'file',
1259
- size: File.size(full_path),
1260
- modified: File.mtime(full_path).iso8601
1261
- }.to_json,
1262
- headers: { 'Content-Type' => 'application/json' }
1263
- }
1264
- end
1265
-
1266
- # Detect content type based on file extension
1267
- def detect_content_type(file_path)
1268
- case File.extname(file_path).downcase
1269
- when '.txt', '.log' then 'text/plain'
1270
- when '.json' then 'application/json'
1271
- when '.yml', '.yaml' then 'application/x-yaml'
1272
- when '.md' then 'text/markdown'
1273
- when '.html' then 'text/html'
1274
- when '.css' then 'text/css'
1275
- when '.js' then 'application/javascript'
1276
- when '.py' then 'text/x-python'
1277
- when '.rb' then 'text/x-ruby'
1278
- else 'application/octet-stream'
1279
- end
1280
- end
1281
-
1282
- # Generate workspace API error response
1283
- def workspace_error_response(status, error_type, message)
1284
- {
1285
- status: status,
1286
- body: {
1287
- error: error_type,
1288
- message: message
1289
- }.to_json,
1290
- headers: { 'Content-Type' => 'application/json' }
1291
- }
1292
- end
1293
1281
  end
1294
1282
  end
1295
1283
  end