@cyanautomation/kaseki-agent 1.4.1

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 (459) hide show
  1. package/.dockerignore +54 -0
  2. package/.eslintignore +11 -0
  3. package/.eslintrc.json +95 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +53 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +53 -0
  6. package/.github/ISSUE_TEMPLATE/security.md +51 -0
  7. package/.github/PULL_REQUEST_TEMPLATE/default.md +71 -0
  8. package/.github/dependabot.yml +38 -0
  9. package/.github/skills/dependency-cache-optimization/SKILL.md +526 -0
  10. package/.github/skills/docker-image-management/SKILL.md +532 -0
  11. package/.github/skills/frontend-design/SKILL.md +782 -0
  12. package/.github/skills/prompt-engineering/SKILL.md +360 -0
  13. package/.github/skills/quality-gate-config/SKILL.md +591 -0
  14. package/.github/skills/result-report-analysis/SKILL.md +576 -0
  15. package/.github/skills/test-automation/SKILL.md +593 -0
  16. package/.github/skills/workflow-diagnosis/SKILL.md +468 -0
  17. package/.github/workflows/build-docker-image.yml +453 -0
  18. package/.github/workflows/release.yml +68 -0
  19. package/.releaserc.json +135 -0
  20. package/CHANGELOG.md +117 -0
  21. package/CLAUDE.md +336 -0
  22. package/CONTRIBUTING.md +339 -0
  23. package/Dockerfile +217 -0
  24. package/README.md +1527 -0
  25. package/STYLE.md +521 -0
  26. package/add-js-extensions.d.ts +9 -0
  27. package/add-js-extensions.d.ts.map +1 -0
  28. package/add-js-extensions.js.map +1 -0
  29. package/dist/add-js-extensions.d.ts +9 -0
  30. package/dist/add-js-extensions.d.ts.map +1 -0
  31. package/dist/add-js-extensions.js +52 -0
  32. package/dist/add-js-extensions.js.map +1 -0
  33. package/dist/ansi-colors.d.ts +26 -0
  34. package/dist/ansi-colors.d.ts.map +1 -0
  35. package/dist/ansi-colors.js +51 -0
  36. package/dist/ansi-colors.js.map +1 -0
  37. package/dist/cli/BaseCommand.d.ts +18 -0
  38. package/dist/cli/BaseCommand.d.ts.map +1 -0
  39. package/dist/cli/BaseCommand.js +31 -0
  40. package/dist/cli/BaseCommand.js.map +1 -0
  41. package/dist/cli/KasekiCLI.d.ts +30 -0
  42. package/dist/cli/KasekiCLI.d.ts.map +1 -0
  43. package/dist/cli/KasekiCLI.js +134 -0
  44. package/dist/cli/KasekiCLI.js.map +1 -0
  45. package/dist/cli/commands/ConfigCommand.d.ts +13 -0
  46. package/dist/cli/commands/ConfigCommand.d.ts.map +1 -0
  47. package/dist/cli/commands/ConfigCommand.js +131 -0
  48. package/dist/cli/commands/ConfigCommand.js.map +1 -0
  49. package/dist/cli/commands/DoctorCommand.d.ts +45 -0
  50. package/dist/cli/commands/DoctorCommand.d.ts.map +1 -0
  51. package/dist/cli/commands/DoctorCommand.js +309 -0
  52. package/dist/cli/commands/DoctorCommand.js.map +1 -0
  53. package/dist/cli/commands/ListCommand.d.ts +9 -0
  54. package/dist/cli/commands/ListCommand.d.ts.map +1 -0
  55. package/dist/cli/commands/ListCommand.js +81 -0
  56. package/dist/cli/commands/ListCommand.js.map +1 -0
  57. package/dist/cli/commands/ReportCommand.d.ts +9 -0
  58. package/dist/cli/commands/ReportCommand.d.ts.map +1 -0
  59. package/dist/cli/commands/ReportCommand.js +98 -0
  60. package/dist/cli/commands/ReportCommand.js.map +1 -0
  61. package/dist/cli/commands/RunCommand.d.ts +13 -0
  62. package/dist/cli/commands/RunCommand.d.ts.map +1 -0
  63. package/dist/cli/commands/RunCommand.js +191 -0
  64. package/dist/cli/commands/RunCommand.js.map +1 -0
  65. package/dist/cli/commands/SecretsCommand.d.ts +9 -0
  66. package/dist/cli/commands/SecretsCommand.d.ts.map +1 -0
  67. package/dist/cli/commands/SecretsCommand.js +109 -0
  68. package/dist/cli/commands/SecretsCommand.js.map +1 -0
  69. package/dist/cli/commands/ServeCommand.d.ts +9 -0
  70. package/dist/cli/commands/ServeCommand.d.ts.map +1 -0
  71. package/dist/cli/commands/ServeCommand.js +50 -0
  72. package/dist/cli/commands/ServeCommand.js.map +1 -0
  73. package/dist/cli/commands/SetupCommand.d.ts +42 -0
  74. package/dist/cli/commands/SetupCommand.d.ts.map +1 -0
  75. package/dist/cli/commands/SetupCommand.js +249 -0
  76. package/dist/cli/commands/SetupCommand.js.map +1 -0
  77. package/dist/cli.d.ts +9 -0
  78. package/dist/cli.d.ts.map +1 -0
  79. package/dist/cli.js +130 -0
  80. package/dist/cli.js.map +1 -0
  81. package/dist/config/ConfigManager.d.ts +395 -0
  82. package/dist/config/ConfigManager.d.ts.map +1 -0
  83. package/dist/config/ConfigManager.js +446 -0
  84. package/dist/config/ConfigManager.js.map +1 -0
  85. package/dist/docker/DockerManager.d.ts +69 -0
  86. package/dist/docker/DockerManager.d.ts.map +1 -0
  87. package/dist/docker/DockerManager.js +266 -0
  88. package/dist/docker/DockerManager.js.map +1 -0
  89. package/dist/event-aggregator.d.ts +71 -0
  90. package/dist/event-aggregator.d.ts.map +1 -0
  91. package/dist/event-aggregator.js +95 -0
  92. package/dist/event-aggregator.js.map +1 -0
  93. package/dist/github-app-token.d.ts +16 -0
  94. package/dist/github-app-token.d.ts.map +1 -0
  95. package/dist/github-app-token.js +148 -0
  96. package/dist/github-app-token.js.map +1 -0
  97. package/dist/idempotency-store.d.ts +61 -0
  98. package/dist/idempotency-store.d.ts.map +1 -0
  99. package/dist/idempotency-store.js +321 -0
  100. package/dist/idempotency-store.js.map +1 -0
  101. package/dist/index.d.ts +25 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +31 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/instance/InstanceManager.d.ts +81 -0
  106. package/dist/instance/InstanceManager.d.ts.map +1 -0
  107. package/dist/instance/InstanceManager.js +220 -0
  108. package/dist/instance/InstanceManager.js.map +1 -0
  109. package/dist/instance-metadata-reader.d.ts +48 -0
  110. package/dist/instance-metadata-reader.d.ts.map +1 -0
  111. package/dist/instance-metadata-reader.js +94 -0
  112. package/dist/instance-metadata-reader.js.map +1 -0
  113. package/dist/instance-state-derivation.d.ts +42 -0
  114. package/dist/instance-state-derivation.d.ts.map +1 -0
  115. package/dist/instance-state-derivation.js +133 -0
  116. package/dist/instance-state-derivation.js.map +1 -0
  117. package/dist/job-scheduler.d.ts +124 -0
  118. package/dist/job-scheduler.d.ts.map +1 -0
  119. package/dist/job-scheduler.js +992 -0
  120. package/dist/job-scheduler.js.map +1 -0
  121. package/dist/kaseki-api-client.d.ts +89 -0
  122. package/dist/kaseki-api-client.d.ts.map +1 -0
  123. package/dist/kaseki-api-client.js +405 -0
  124. package/dist/kaseki-api-client.js.map +1 -0
  125. package/dist/kaseki-api-config.d.ts +34 -0
  126. package/dist/kaseki-api-config.d.ts.map +1 -0
  127. package/dist/kaseki-api-config.js +113 -0
  128. package/dist/kaseki-api-config.js.map +1 -0
  129. package/dist/kaseki-api-routes.d.ts +13 -0
  130. package/dist/kaseki-api-routes.d.ts.map +1 -0
  131. package/dist/kaseki-api-routes.js +559 -0
  132. package/dist/kaseki-api-routes.js.map +1 -0
  133. package/dist/kaseki-api-service-wrapper.d.ts +43 -0
  134. package/dist/kaseki-api-service-wrapper.d.ts.map +1 -0
  135. package/dist/kaseki-api-service-wrapper.js +150 -0
  136. package/dist/kaseki-api-service-wrapper.js.map +1 -0
  137. package/dist/kaseki-api-service.d.ts +16 -0
  138. package/dist/kaseki-api-service.d.ts.map +1 -0
  139. package/dist/kaseki-api-service.js +143 -0
  140. package/dist/kaseki-api-service.js.map +1 -0
  141. package/dist/kaseki-api-types.d.ts +440 -0
  142. package/dist/kaseki-api-types.d.ts.map +1 -0
  143. package/dist/kaseki-api-types.js +64 -0
  144. package/dist/kaseki-api-types.js.map +1 -0
  145. package/dist/kaseki-cli-lib.d.ts +219 -0
  146. package/dist/kaseki-cli-lib.d.ts.map +1 -0
  147. package/dist/kaseki-cli-lib.js +523 -0
  148. package/dist/kaseki-cli-lib.js.map +1 -0
  149. package/dist/kaseki-cli.d.ts +38 -0
  150. package/dist/kaseki-cli.d.ts.map +1 -0
  151. package/dist/kaseki-cli.js +559 -0
  152. package/dist/kaseki-cli.js.map +1 -0
  153. package/dist/kaseki-report.d.ts +3 -0
  154. package/dist/kaseki-report.d.ts.map +1 -0
  155. package/dist/kaseki-report.js +140 -0
  156. package/dist/kaseki-report.js.map +1 -0
  157. package/dist/lib/subprocess-helpers.d.ts +98 -0
  158. package/dist/lib/subprocess-helpers.d.ts.map +1 -0
  159. package/dist/lib/subprocess-helpers.js +136 -0
  160. package/dist/lib/subprocess-helpers.js.map +1 -0
  161. package/dist/logger.d.ts +39 -0
  162. package/dist/logger.d.ts.map +1 -0
  163. package/dist/logger.js +79 -0
  164. package/dist/logger.js.map +1 -0
  165. package/dist/metrics.d.ts +19 -0
  166. package/dist/metrics.d.ts.map +1 -0
  167. package/dist/metrics.js +59 -0
  168. package/dist/metrics.js.map +1 -0
  169. package/dist/middleware/job-lookup.d.ts +27 -0
  170. package/dist/middleware/job-lookup.d.ts.map +1 -0
  171. package/dist/middleware/job-lookup.js +28 -0
  172. package/dist/middleware/job-lookup.js.map +1 -0
  173. package/dist/pi-event-filter.d.ts +3 -0
  174. package/dist/pi-event-filter.d.ts.map +1 -0
  175. package/dist/pi-event-filter.js +126 -0
  176. package/dist/pi-event-filter.js.map +1 -0
  177. package/dist/pi-progress-stream.d.ts +3 -0
  178. package/dist/pi-progress-stream.d.ts.map +1 -0
  179. package/dist/pi-progress-stream.js +205 -0
  180. package/dist/pi-progress-stream.js.map +1 -0
  181. package/dist/pi-progress-summarizer.d.ts +61 -0
  182. package/dist/pi-progress-summarizer.d.ts.map +1 -0
  183. package/dist/pi-progress-summarizer.js +246 -0
  184. package/dist/pi-progress-summarizer.js.map +1 -0
  185. package/dist/pre-flight-validator.d.ts +72 -0
  186. package/dist/pre-flight-validator.d.ts.map +1 -0
  187. package/dist/pre-flight-validator.js +513 -0
  188. package/dist/pre-flight-validator.js.map +1 -0
  189. package/dist/progress-stream-utils.d.ts +3 -0
  190. package/dist/progress-stream-utils.d.ts.map +1 -0
  191. package/dist/progress-stream-utils.js +15 -0
  192. package/dist/progress-stream-utils.js.map +1 -0
  193. package/dist/result-cache.d.ts +52 -0
  194. package/dist/result-cache.d.ts.map +1 -0
  195. package/dist/result-cache.js +134 -0
  196. package/dist/result-cache.js.map +1 -0
  197. package/dist/routes/artifact-routes.d.ts +10 -0
  198. package/dist/routes/artifact-routes.d.ts.map +1 -0
  199. package/dist/routes/artifact-routes.js +126 -0
  200. package/dist/routes/artifact-routes.js.map +1 -0
  201. package/dist/routes/log-routes.d.ts +8 -0
  202. package/dist/routes/log-routes.d.ts.map +1 -0
  203. package/dist/routes/log-routes.js +345 -0
  204. package/dist/routes/log-routes.js.map +1 -0
  205. package/dist/routes/status-routes.d.ts +8 -0
  206. package/dist/routes/status-routes.d.ts.map +1 -0
  207. package/dist/routes/status-routes.js +82 -0
  208. package/dist/routes/status-routes.js.map +1 -0
  209. package/dist/routes/webhook-routes.d.ts +6 -0
  210. package/dist/routes/webhook-routes.d.ts.map +1 -0
  211. package/dist/routes/webhook-routes.js +86 -0
  212. package/dist/routes/webhook-routes.js.map +1 -0
  213. package/dist/run-artifact-metadata-cache.d.ts +42 -0
  214. package/dist/run-artifact-metadata-cache.d.ts.map +1 -0
  215. package/dist/run-artifact-metadata-cache.js +139 -0
  216. package/dist/run-artifact-metadata-cache.js.map +1 -0
  217. package/dist/secret-value-cache.d.ts +13 -0
  218. package/dist/secret-value-cache.d.ts.map +1 -0
  219. package/dist/secret-value-cache.js +44 -0
  220. package/dist/secret-value-cache.js.map +1 -0
  221. package/dist/secrets/SecretsManager.d.ts +80 -0
  222. package/dist/secrets/SecretsManager.d.ts.map +1 -0
  223. package/dist/secrets/SecretsManager.js +306 -0
  224. package/dist/secrets/SecretsManager.js.map +1 -0
  225. package/dist/test-utils.d.ts +55 -0
  226. package/dist/test-utils.d.ts.map +1 -0
  227. package/dist/test-utils.js +48 -0
  228. package/dist/test-utils.js.map +1 -0
  229. package/dist/timestamp-tracker.d.ts +75 -0
  230. package/dist/timestamp-tracker.d.ts.map +1 -0
  231. package/dist/timestamp-tracker.js +121 -0
  232. package/dist/timestamp-tracker.js.map +1 -0
  233. package/dist/utils/failure-artifact-writer.d.ts +29 -0
  234. package/dist/utils/failure-artifact-writer.d.ts.map +1 -0
  235. package/dist/utils/failure-artifact-writer.js +157 -0
  236. package/dist/utils/failure-artifact-writer.js.map +1 -0
  237. package/dist/utils/file-helpers.d.ts +41 -0
  238. package/dist/utils/file-helpers.d.ts.map +1 -0
  239. package/dist/utils/file-helpers.js +143 -0
  240. package/dist/utils/file-helpers.js.map +1 -0
  241. package/dist/utils/http-client-factory.d.ts +46 -0
  242. package/dist/utils/http-client-factory.d.ts.map +1 -0
  243. package/dist/utils/http-client-factory.js +114 -0
  244. package/dist/utils/http-client-factory.js.map +1 -0
  245. package/dist/utils/progress-normalizer.d.ts +13 -0
  246. package/dist/utils/progress-normalizer.d.ts.map +1 -0
  247. package/dist/utils/progress-normalizer.js +57 -0
  248. package/dist/utils/progress-normalizer.js.map +1 -0
  249. package/dist/utils/response-helpers.d.ts +34 -0
  250. package/dist/utils/response-helpers.d.ts.map +1 -0
  251. package/dist/utils/response-helpers.js +78 -0
  252. package/dist/utils/response-helpers.js.map +1 -0
  253. package/dist/utils/route-helpers.d.ts +17 -0
  254. package/dist/utils/route-helpers.d.ts.map +1 -0
  255. package/dist/utils/route-helpers.js +22 -0
  256. package/dist/utils/route-helpers.js.map +1 -0
  257. package/dist/utils/status-response-builder.d.ts +23 -0
  258. package/dist/utils/status-response-builder.d.ts.map +1 -0
  259. package/dist/utils/status-response-builder.js +144 -0
  260. package/dist/utils/status-response-builder.js.map +1 -0
  261. package/dist/utils/type-guards.d.ts +37 -0
  262. package/dist/utils/type-guards.d.ts.map +1 -0
  263. package/dist/utils/type-guards.js +45 -0
  264. package/dist/utils/type-guards.js.map +1 -0
  265. package/dist/utils/utf8-helpers.d.ts +32 -0
  266. package/dist/utils/utf8-helpers.d.ts.map +1 -0
  267. package/dist/utils/utf8-helpers.js +97 -0
  268. package/dist/utils/utf8-helpers.js.map +1 -0
  269. package/dist/utils/webhook-event-builder.d.ts +26 -0
  270. package/dist/utils/webhook-event-builder.d.ts.map +1 -0
  271. package/dist/utils/webhook-event-builder.js +77 -0
  272. package/dist/utils/webhook-event-builder.js.map +1 -0
  273. package/dist/webhook-manager.d.ts +56 -0
  274. package/dist/webhook-manager.d.ts.map +1 -0
  275. package/dist/webhook-manager.js +359 -0
  276. package/dist/webhook-manager.js.map +1 -0
  277. package/docker/workspace-cache/package-lock.json +13 -0
  278. package/docker/workspace-cache/package.json +7 -0
  279. package/docker-compose.yml +53 -0
  280. package/docs/API.md +708 -0
  281. package/docs/BACKLOG.md +19 -0
  282. package/docs/BUILD_STRATEGY.md +404 -0
  283. package/docs/CLI.md +569 -0
  284. package/docs/DEPLOYMENT.md +521 -0
  285. package/docs/DEVELOPMENT.md +459 -0
  286. package/docs/DOCKER_SETUP.md +522 -0
  287. package/docs/ENHANCED_PROGRESS_LOGS.md +264 -0
  288. package/docs/IMPLEMENTATION_SUMMARY.md +549 -0
  289. package/docs/INTEGRATION_EXAMPLE.md +217 -0
  290. package/docs/NPM_SETUP.md +468 -0
  291. package/docs/PHASE1-4_IMPLEMENTATION.md +302 -0
  292. package/docs/PHASE1_COMPLETION.md +192 -0
  293. package/docs/PHASE2_COMPLETION.md +134 -0
  294. package/docs/PHASE6_MIGRATION.md +392 -0
  295. package/docs/PRINTF_SAFETY_FIX.md +282 -0
  296. package/docs/QUALITY_GATES.md +369 -0
  297. package/docs/SETUP_GUIDE.md +482 -0
  298. package/docs/TASK_PROMPT_TEMPLATES.md +533 -0
  299. package/docs/VALIDATION_FIX.md +139 -0
  300. package/docs/VERIFICATION_CHECKLIST.md +335 -0
  301. package/docs/repo-maturity.md +760 -0
  302. package/fix-tests.d.ts +9 -0
  303. package/fix-tests.d.ts.map +1 -0
  304. package/fix-tests.js.map +1 -0
  305. package/fix-tests.ts +53 -0
  306. package/jest.config.ts +31 -0
  307. package/kaseki +183 -0
  308. package/kaseki-agent.sh +1961 -0
  309. package/ops/logrotate/kaseki +10 -0
  310. package/package.json +83 -0
  311. package/perf/README.md +54 -0
  312. package/perf/pi-event-filter.benchmark.test.ts +98 -0
  313. package/run-kaseki-json.test.sh +106 -0
  314. package/run-kaseki.sh +990 -0
  315. package/scripts/allowlist-helper.sh +56 -0
  316. package/scripts/cleanup-kaseki.sh +168 -0
  317. package/scripts/deploy-pi-template.sh +293 -0
  318. package/scripts/docker-entrypoint.sh +71 -0
  319. package/scripts/dry-run-allowlist.sh +161 -0
  320. package/scripts/kaseki-activate.sh +396 -0
  321. package/scripts/kaseki-api.service +62 -0
  322. package/scripts/kaseki-container-entrypoint-wrapper.sh +119 -0
  323. package/scripts/kaseki-container-setup-remote.sh +172 -0
  324. package/scripts/kaseki-container-setup.sh +193 -0
  325. package/scripts/kaseki-healthcheck.sh +95 -0
  326. package/scripts/kaseki-install.sh +50 -0
  327. package/scripts/kaseki-maturity-score.sh +291 -0
  328. package/scripts/kaseki-performance-metrics.sh +122 -0
  329. package/scripts/kaseki-preflight.sh +270 -0
  330. package/scripts/kaseki-setup.sh +265 -0
  331. package/scripts/pi-setup-remote.sh +213 -0
  332. package/scripts/setup-github-labels.sh +42 -0
  333. package/scripts/suggest-allowlist.sh +68 -0
  334. package/scripts/templates/MULTI_HOST_DISTRIBUTED.md +337 -0
  335. package/scripts/templates/REST_API_SERVICE.md +490 -0
  336. package/scripts/templates/SINGLE_HOST_CLI.md +194 -0
  337. package/scripts/test-github-app.sh +248 -0
  338. package/src/add-js-extensions.ts +61 -0
  339. package/src/ansi-colors.test.ts +62 -0
  340. package/src/ansi-colors.ts +67 -0
  341. package/src/cli/BaseCommand.ts +40 -0
  342. package/src/cli/KasekiCLI.ts +154 -0
  343. package/src/cli/commands/ConfigCommand.ts +145 -0
  344. package/src/cli/commands/DoctorCommand.ts +329 -0
  345. package/src/cli/commands/ListCommand.ts +105 -0
  346. package/src/cli/commands/ReportCommand.ts +110 -0
  347. package/src/cli/commands/RunCommand.ts +218 -0
  348. package/src/cli/commands/SecretsCommand.ts +120 -0
  349. package/src/cli/commands/ServeCommand.ts +62 -0
  350. package/src/cli/commands/SetupCommand.ts +301 -0
  351. package/src/cli.ts +138 -0
  352. package/src/config/ConfigManager.ts +476 -0
  353. package/src/docker/DockerManager.ts +319 -0
  354. package/src/docker-entrypoint-packaging.test.ts +33 -0
  355. package/src/event-aggregator.test.ts +117 -0
  356. package/src/event-aggregator.ts +126 -0
  357. package/src/github-app-token.ts +215 -0
  358. package/src/idempotency-store.test.ts +117 -0
  359. package/src/idempotency-store.ts +385 -0
  360. package/src/index.ts +89 -0
  361. package/src/instance/InstanceManager.ts +285 -0
  362. package/src/instance-metadata-reader.test.ts +190 -0
  363. package/src/instance-metadata-reader.ts +129 -0
  364. package/src/instance-state-derivation.test.ts +263 -0
  365. package/src/instance-state-derivation.ts +148 -0
  366. package/src/job-scheduler.test.ts +1236 -0
  367. package/src/job-scheduler.ts +1117 -0
  368. package/src/kaseki-api-client.ts +488 -0
  369. package/src/kaseki-api-config.test.ts +315 -0
  370. package/src/kaseki-api-config.ts +175 -0
  371. package/src/kaseki-api-routes.test.ts +1615 -0
  372. package/src/kaseki-api-routes.ts +643 -0
  373. package/src/kaseki-api-service-wrapper.ts +188 -0
  374. package/src/kaseki-api-service.test.ts +418 -0
  375. package/src/kaseki-api-service.ts +192 -0
  376. package/src/kaseki-api-types.ts +320 -0
  377. package/src/kaseki-cli-lib.test.ts +552 -0
  378. package/src/kaseki-cli-lib.ts +760 -0
  379. package/src/kaseki-cli.ts +682 -0
  380. package/src/kaseki-report.test.ts +118 -0
  381. package/src/kaseki-report.ts +192 -0
  382. package/src/lib/subprocess-helpers.ts +177 -0
  383. package/src/logger.ts +114 -0
  384. package/src/metrics.ts +66 -0
  385. package/src/middleware/job-lookup.test.ts +113 -0
  386. package/src/middleware/job-lookup.ts +45 -0
  387. package/src/pi-event-filter.test.ts +183 -0
  388. package/src/pi-event-filter.ts +183 -0
  389. package/src/pi-progress-stream.ts +287 -0
  390. package/src/pi-progress-summarizer.test.ts +302 -0
  391. package/src/pi-progress-summarizer.ts +287 -0
  392. package/src/pre-flight-validator.test.ts +512 -0
  393. package/src/pre-flight-validator.ts +618 -0
  394. package/src/progress-stream-utils.test.ts +35 -0
  395. package/src/progress-stream-utils.ts +14 -0
  396. package/src/result-cache.test.ts +195 -0
  397. package/src/result-cache.ts +181 -0
  398. package/src/routes/artifact-routes.ts +169 -0
  399. package/src/routes/log-routes.ts +391 -0
  400. package/src/routes/status-routes.ts +92 -0
  401. package/src/routes/webhook-routes.ts +97 -0
  402. package/src/run-artifact-metadata-cache.test.ts +80 -0
  403. package/src/run-artifact-metadata-cache.ts +184 -0
  404. package/src/secret-value-cache.test.ts +66 -0
  405. package/src/secret-value-cache.ts +55 -0
  406. package/src/secrets/SecretsManager.ts +343 -0
  407. package/src/test-utils.ts +81 -0
  408. package/src/timestamp-tracker.test.ts +134 -0
  409. package/src/timestamp-tracker.ts +132 -0
  410. package/src/utils/failure-artifact-writer.ts +187 -0
  411. package/src/utils/file-helpers.test.ts +235 -0
  412. package/src/utils/file-helpers.ts +150 -0
  413. package/src/utils/http-client-factory.test.ts +245 -0
  414. package/src/utils/http-client-factory.ts +157 -0
  415. package/src/utils/progress-normalizer.test.ts +442 -0
  416. package/src/utils/progress-normalizer.ts +68 -0
  417. package/src/utils/response-helpers.test.ts +122 -0
  418. package/src/utils/response-helpers.ts +101 -0
  419. package/src/utils/route-helpers.ts +30 -0
  420. package/src/utils/status-response-builder.ts +159 -0
  421. package/src/utils/type-guards.ts +52 -0
  422. package/src/utils/utf8-helpers.ts +102 -0
  423. package/src/utils/webhook-event-builder.test.ts +143 -0
  424. package/src/utils/webhook-event-builder.ts +87 -0
  425. package/src/webhook-manager.test.ts +152 -0
  426. package/src/webhook-manager.ts +445 -0
  427. package/templates/allowlist-api-route.txt +7 -0
  428. package/templates/allowlist-comprehensive.txt +8 -0
  429. package/templates/allowlist-parser-fix.txt +6 -0
  430. package/templates/allowlist-ui-component.txt +9 -0
  431. package/templates/allowlist-utility.txt +9 -0
  432. package/test/actual-model-metadata.test.sh +102 -0
  433. package/test/dry-run.test.sh +131 -0
  434. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-0.json +1 -0
  435. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-1.json +1 -0
  436. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-invalid.json +1 -0
  437. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-0.json +1 -0
  438. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-1.json +1 -0
  439. package/test/kaseki-api.integration.test.sh +165 -0
  440. package/test/pi-event-filter-failure.test.sh +83 -0
  441. package/test/printf-safety-focused.test.sh +99 -0
  442. package/test/printf-safety-results/results/restoration.jsonl +10 -0
  443. package/test/printf-safety-results/results/test.jsonl +0 -0
  444. package/test/printf-safety.test.sh +297 -0
  445. package/test/validation-fix.test.sh +79 -0
  446. package/test/validation-integration.test.sh +109 -0
  447. package/tests/allowlist-glob.test.sh +61 -0
  448. package/tests/dependency-cache-key.test.sh +48 -0
  449. package/tests/dependency-restore-mode.test.sh +48 -0
  450. package/tests/doctor-template-parity.test.sh +95 -0
  451. package/tests/github-operations.test.sh +142 -0
  452. package/tests/npm-install-flags.test.sh +58 -0
  453. package/tests/quality-gates.test.sh +178 -0
  454. package/tests/repo-memory.test.sh +103 -0
  455. package/tests/restore-disallowed-changes.test.sh +80 -0
  456. package/tests/validation-missing-npm-scripts.test.sh +93 -0
  457. package/tests/validation-strict-mode.test.sh +118 -0
  458. package/tsconfig.changed.json +7 -0
  459. package/tsconfig.json +39 -0
@@ -0,0 +1,315 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { loadConfig, validateApiKey } from './kaseki-api-config';
4
+ import { secretValueCache } from './secret-value-cache';
5
+
6
+ describe('kaseki-api-config load configuration', () => {
7
+ const originalEnv = process.env;
8
+ let testDir: string;
9
+
10
+ beforeEach(() => {
11
+ jest.resetModules();
12
+ secretValueCache.clear();
13
+ process.env = { ...originalEnv };
14
+ testDir = fs.mkdtempSync(path.join('/tmp', 'kaseki-config-test-'));
15
+ });
16
+
17
+ afterEach(() => {
18
+ secretValueCache.clear();
19
+ process.env = originalEnv;
20
+ fs.rmSync(testDir, { recursive: true, force: true });
21
+ });
22
+
23
+ test('loadConfig loads valid configuration from environment variables', () => {
24
+ process.env.KASEKI_API_KEYS = 'key1,key2,key3';
25
+ process.env.KASEKI_API_PORT = '3000';
26
+ process.env.KASEKI_API_MAX_CONCURRENT_RUNS = '5';
27
+ process.env.KASEKI_AGENT_TIMEOUT_SECONDS = '600';
28
+ process.env.KASEKI_MAX_DIFF_BYTES = '500000';
29
+ process.env.KASEKI_TASK_MODE = 'inspect';
30
+ process.env.KASEKI_RESULTS_DIR = testDir;
31
+ process.env.KASEKI_API_LOG_LEVEL = 'debug';
32
+ process.env.KASEKI_API_JOB_INDEX_MAX_ENTRIES = '250';
33
+ process.env.KASEKI_ARTIFACT_CACHE_MAX_ENTRIES = '7';
34
+ process.env.KASEKI_ARTIFACT_CACHE_TTL_MS = '12345';
35
+ process.env.KASEKI_ARTIFACT_CACHE_MAX_FILE_BYTES = '4096';
36
+
37
+ const config = loadConfig();
38
+
39
+ expect(config.apiKeys).toEqual(['key1', 'key2', 'key3']);
40
+ expect(config.port).toBe(3000);
41
+ expect(config.maxConcurrentRuns).toBe(5);
42
+ expect(config.agentTimeoutSeconds).toBe(600);
43
+ expect(config.maxDiffBytes).toBe(500000);
44
+ expect(config.defaultTaskMode).toBe('inspect');
45
+ expect(config.resultsDir).toBe(testDir);
46
+ expect(config.logLevel).toBe('debug');
47
+ expect(config.jobIndexMaxEntries).toBe(250);
48
+ expect(config.artifactCacheMaxEntries).toBe(7);
49
+ expect(config.artifactCacheTtlMs).toBe(12345);
50
+ expect(config.artifactCacheMaxFileBytes).toBe(4096);
51
+ });
52
+
53
+ test('loadConfig uses default values when env vars are not set', () => {
54
+ process.env.KASEKI_API_KEYS = 'default-key';
55
+ process.env.KASEKI_RESULTS_DIR = testDir;
56
+ delete process.env.KASEKI_API_PORT;
57
+ delete process.env.KASEKI_API_MAX_CONCURRENT_RUNS;
58
+ delete process.env.KASEKI_AGENT_TIMEOUT_SECONDS;
59
+ delete process.env.KASEKI_MAX_DIFF_BYTES;
60
+ delete process.env.KASEKI_TASK_MODE;
61
+ delete process.env.KASEKI_API_LOG_LEVEL;
62
+ delete process.env.KASEKI_API_JOB_INDEX_MAX_ENTRIES;
63
+ delete process.env.KASEKI_ARTIFACT_CACHE_MAX_ENTRIES;
64
+ delete process.env.KASEKI_ARTIFACT_CACHE_TTL_MS;
65
+ delete process.env.KASEKI_ARTIFACT_CACHE_MAX_FILE_BYTES;
66
+
67
+ const config = loadConfig();
68
+
69
+ expect(config.port).toBe(8080); // default
70
+ expect(config.maxConcurrentRuns).toBe(3); // default
71
+ expect(config.agentTimeoutSeconds).toBe(5700); // default
72
+ expect(config.maxDiffBytes).toBe(200000); // default
73
+ expect(config.defaultTaskMode).toBe('patch'); // default
74
+ expect(config.logLevel).toBe('info'); // default
75
+ expect(config.jobIndexMaxEntries).toBe(1000); // default
76
+ expect(config.artifactCacheMaxEntries).toBe(20); // default
77
+ expect(config.artifactCacheTtlMs).toBe(5 * 60 * 1000); // default
78
+ expect(config.artifactCacheMaxFileBytes).toBe(10 * 1024 * 1024); // default
79
+ });
80
+
81
+ test('loadConfig throws when KASEKI_API_KEYS is not set', () => {
82
+ delete process.env.KASEKI_API_KEYS;
83
+ delete process.env.KASEKI_API_KEYS_FILE;
84
+ process.env.KASEKI_RESULTS_DIR = testDir;
85
+
86
+ expect(() => loadConfig()).toThrow('KASEKI_API_KEYS environment variable is required');
87
+ });
88
+
89
+ test('loadConfig throws when KASEKI_API_PORT is invalid', () => {
90
+ process.env.KASEKI_API_KEYS = 'test-key';
91
+ process.env.KASEKI_RESULTS_DIR = testDir;
92
+ process.env.KASEKI_API_PORT = 'not-a-port';
93
+
94
+ expect(() => loadConfig()).toThrow('KASEKI_API_PORT must be a valid port number');
95
+ });
96
+
97
+ test('loadConfig throws when KASEKI_API_PORT is out of range', () => {
98
+ process.env.KASEKI_API_KEYS = 'test-key';
99
+ process.env.KASEKI_RESULTS_DIR = testDir;
100
+ process.env.KASEKI_API_PORT = '99999';
101
+
102
+ expect(() => loadConfig()).toThrow('KASEKI_API_PORT must be a valid port number');
103
+ });
104
+
105
+ test('loadConfig throws when KASEKI_API_MAX_CONCURRENT_RUNS is invalid', () => {
106
+ process.env.KASEKI_API_KEYS = 'test-key';
107
+ process.env.KASEKI_RESULTS_DIR = testDir;
108
+ process.env.KASEKI_API_MAX_CONCURRENT_RUNS = '-1';
109
+
110
+ expect(() => loadConfig()).toThrow('KASEKI_API_MAX_CONCURRENT_RUNS must be >= 1');
111
+ });
112
+
113
+ test('loadConfig throws when KASEKI_AGENT_TIMEOUT_SECONDS is invalid', () => {
114
+ process.env.KASEKI_API_KEYS = 'test-key';
115
+ process.env.KASEKI_RESULTS_DIR = testDir;
116
+ process.env.KASEKI_AGENT_TIMEOUT_SECONDS = 'not-a-number';
117
+
118
+ expect(() => loadConfig()).toThrow('KASEKI_AGENT_TIMEOUT_SECONDS must be >= 1');
119
+ });
120
+
121
+ test('loadConfig throws when KASEKI_MAX_DIFF_BYTES is invalid', () => {
122
+ process.env.KASEKI_API_KEYS = 'test-key';
123
+ process.env.KASEKI_RESULTS_DIR = testDir;
124
+ process.env.KASEKI_MAX_DIFF_BYTES = '0';
125
+
126
+ expect(() => loadConfig()).toThrow('KASEKI_MAX_DIFF_BYTES must be >= 1');
127
+ });
128
+
129
+ test('loadConfig throws when KASEKI_API_JOB_INDEX_MAX_ENTRIES is invalid', () => {
130
+ process.env.KASEKI_API_KEYS = 'test-key';
131
+ process.env.KASEKI_RESULTS_DIR = testDir;
132
+ process.env.KASEKI_API_JOB_INDEX_MAX_ENTRIES = '-1';
133
+
134
+ expect(() => loadConfig()).toThrow('KASEKI_API_JOB_INDEX_MAX_ENTRIES must be >= 0');
135
+ });
136
+
137
+ test('loadConfig throws when artifact cache configuration is invalid', () => {
138
+ process.env.KASEKI_API_KEYS = 'test-key';
139
+ process.env.KASEKI_RESULTS_DIR = testDir;
140
+ process.env.KASEKI_ARTIFACT_CACHE_MAX_ENTRIES = '-1';
141
+ expect(() => loadConfig()).toThrow('KASEKI_ARTIFACT_CACHE_MAX_ENTRIES must be >= 0');
142
+
143
+ process.env.KASEKI_ARTIFACT_CACHE_MAX_ENTRIES = '1';
144
+ process.env.KASEKI_ARTIFACT_CACHE_TTL_MS = '-1';
145
+ expect(() => loadConfig()).toThrow('KASEKI_ARTIFACT_CACHE_TTL_MS must be >= 0');
146
+
147
+ process.env.KASEKI_ARTIFACT_CACHE_TTL_MS = '1';
148
+ process.env.KASEKI_ARTIFACT_CACHE_MAX_FILE_BYTES = '-1';
149
+ expect(() => loadConfig()).toThrow('KASEKI_ARTIFACT_CACHE_MAX_FILE_BYTES must be >= 0');
150
+ });
151
+
152
+ test('loadConfig throws when KASEKI_TASK_MODE is invalid', () => {
153
+ process.env.KASEKI_API_KEYS = 'test-key';
154
+ process.env.KASEKI_RESULTS_DIR = testDir;
155
+ process.env.KASEKI_TASK_MODE = 'invalid-mode';
156
+
157
+ expect(() => loadConfig()).toThrow("KASEKI_TASK_MODE must be 'patch' or 'inspect'");
158
+ });
159
+
160
+ test('loadConfig throws when KASEKI_RESULTS_DIR does not exist', () => {
161
+ process.env.KASEKI_API_KEYS = 'test-key';
162
+ process.env.KASEKI_RESULTS_DIR = '/nonexistent/path/to/dir';
163
+
164
+ expect(() => loadConfig()).toThrow('KASEKI_RESULTS_DIR does not exist');
165
+ });
166
+
167
+ test('loadConfig throws when KASEKI_API_LOG_LEVEL is invalid', () => {
168
+ process.env.KASEKI_API_KEYS = 'test-key';
169
+ process.env.KASEKI_RESULTS_DIR = testDir;
170
+ process.env.KASEKI_API_LOG_LEVEL = 'verbose';
171
+
172
+ expect(() => loadConfig()).toThrow('KASEKI_API_LOG_LEVEL must be debug/info/warn/error');
173
+ });
174
+
175
+ test('loadConfig strips whitespace from API keys', () => {
176
+ process.env.KASEKI_API_KEYS = ' key1 , key2 , key3 ';
177
+ process.env.KASEKI_RESULTS_DIR = testDir;
178
+
179
+ const config = loadConfig();
180
+
181
+ expect(config.apiKeys).toEqual(['key1', 'key2', 'key3']);
182
+ });
183
+ });
184
+
185
+ describe('kaseki-api-config load API keys from file', () => {
186
+ const originalEnv = process.env;
187
+ let testDir: string;
188
+
189
+ beforeEach(() => {
190
+ jest.resetModules();
191
+ secretValueCache.clear();
192
+ process.env = { ...originalEnv };
193
+ testDir = fs.mkdtempSync(path.join('/tmp', 'kaseki-config-keys-test-'));
194
+ });
195
+
196
+ afterEach(() => {
197
+ secretValueCache.clear();
198
+ process.env = originalEnv;
199
+ fs.rmSync(testDir, { recursive: true, force: true });
200
+ });
201
+
202
+ test('loadConfig reads API keys from KASEKI_API_KEYS_FILE', () => {
203
+ const keysFile = path.join(testDir, 'api-keys.txt');
204
+ fs.writeFileSync(keysFile, 'file-key-1\nfile-key-2\nfile-key-3\n');
205
+
206
+ process.env.KASEKI_API_KEYS_FILE = keysFile;
207
+ process.env.KASEKI_RESULTS_DIR = testDir;
208
+
209
+ const config = loadConfig();
210
+
211
+ expect(config.apiKeys).toEqual(['file-key-1', 'file-key-2', 'file-key-3']);
212
+ });
213
+
214
+ test('loadConfig prefers KASEKI_API_KEYS over KASEKI_API_KEYS_FILE', () => {
215
+ const keysFile = path.join(testDir, 'api-keys.txt');
216
+ fs.writeFileSync(keysFile, 'file-key-1\nfile-key-2\n');
217
+
218
+ process.env.KASEKI_API_KEYS = 'env-key-1,env-key-2';
219
+ process.env.KASEKI_API_KEYS_FILE = keysFile;
220
+ process.env.KASEKI_RESULTS_DIR = testDir;
221
+
222
+ const config = loadConfig();
223
+
224
+ expect(config.apiKeys).toEqual(['env-key-1', 'env-key-2']);
225
+ });
226
+
227
+ test('loadConfig reuses cached API key file contents when metadata is unchanged', () => {
228
+ const keysFile = path.join(testDir, 'api-keys.txt');
229
+ const fixedTime = new Date('2020-01-01T00:00:00.000Z');
230
+ fs.writeFileSync(keysFile, 'file-key-1\nfile-key-2\n');
231
+ fs.utimesSync(keysFile, fixedTime, fixedTime);
232
+
233
+ process.env.KASEKI_API_KEYS_FILE = keysFile;
234
+ process.env.KASEKI_RESULTS_DIR = testDir;
235
+
236
+ expect(loadConfig().apiKeys).toEqual(['file-key-1', 'file-key-2']);
237
+
238
+ fs.writeFileSync(keysFile, 'file-key-3\nfile-key-4\n');
239
+ fs.utimesSync(keysFile, fixedTime, fixedTime);
240
+
241
+ expect(loadConfig().apiKeys).toEqual(['file-key-1', 'file-key-2']);
242
+ });
243
+
244
+ test('loadConfig skips comments and empty lines in API keys file', () => {
245
+ const keysFile = path.join(testDir, 'api-keys.txt');
246
+ fs.writeFileSync(keysFile, '# This is a comment\nfile-key-1\n\n# Another comment\nfile-key-2\n');
247
+
248
+ process.env.KASEKI_API_KEYS_FILE = keysFile;
249
+ process.env.KASEKI_RESULTS_DIR = testDir;
250
+
251
+ const config = loadConfig();
252
+
253
+ expect(config.apiKeys).toEqual(['file-key-1', 'file-key-2']);
254
+ });
255
+
256
+ test('loadConfig strips whitespace from keys in file', () => {
257
+ const keysFile = path.join(testDir, 'api-keys.txt');
258
+ fs.writeFileSync(keysFile, ' file-key-1 \n file-key-2 \n file-key-3 \n');
259
+
260
+ process.env.KASEKI_API_KEYS_FILE = keysFile;
261
+ process.env.KASEKI_RESULTS_DIR = testDir;
262
+
263
+ const config = loadConfig();
264
+
265
+ expect(config.apiKeys).toEqual(['file-key-1', 'file-key-2', 'file-key-3']);
266
+ });
267
+
268
+ test('loadConfig falls back to KASEKI_API_KEYS when file is empty', () => {
269
+ const keysFile = path.join(testDir, 'api-keys.txt');
270
+ fs.writeFileSync(keysFile, '');
271
+
272
+ process.env.KASEKI_API_KEYS_FILE = keysFile;
273
+ process.env.KASEKI_API_KEYS = 'fallback-key';
274
+ process.env.KASEKI_RESULTS_DIR = testDir;
275
+
276
+ const config = loadConfig();
277
+
278
+ expect(config.apiKeys).toEqual(['fallback-key']);
279
+ });
280
+
281
+ test('loadConfig throws when KASEKI_API_KEYS_FILE is unreadable', () => {
282
+ process.env.KASEKI_API_KEYS_FILE = '/nonexistent/path/keys.txt';
283
+ process.env.KASEKI_RESULTS_DIR = testDir;
284
+
285
+ expect(() => loadConfig()).toThrow('Failed to read KASEKI_API_KEYS_FILE');
286
+ });
287
+ });
288
+
289
+ describe('kaseki-api-config validate API key', () => {
290
+ const config = {
291
+ port: 8080,
292
+ apiKeys: ['valid-key-1', 'valid-key-2'],
293
+ resultsDir: '/tmp',
294
+ maxConcurrentRuns: 3,
295
+ defaultTaskMode: 'patch' as const,
296
+ maxDiffBytes: 200000,
297
+ agentTimeoutSeconds: 1200,
298
+ logLevel: 'info' as const,
299
+ };
300
+
301
+ test('validateApiKey returns true for valid API key', () => {
302
+ expect(validateApiKey(config, 'valid-key-1')).toBe(true);
303
+ expect(validateApiKey(config, 'valid-key-2')).toBe(true);
304
+ });
305
+
306
+ test('validateApiKey returns false for invalid API key', () => {
307
+ expect(validateApiKey(config, 'invalid-key')).toBe(false);
308
+ expect(validateApiKey(config, '')).toBe(false);
309
+ });
310
+
311
+ test('validateApiKey is case-sensitive', () => {
312
+ expect(validateApiKey(config, 'VALID-KEY-1')).toBe(false);
313
+ expect(validateApiKey(config, 'Valid-Key-1')).toBe(false);
314
+ });
315
+ });
@@ -0,0 +1,175 @@
1
+ import * as fs from 'fs';
2
+ import { secretValueCache } from './secret-value-cache';
3
+
4
+ /**
5
+ * Configuration for the Kaseki API service.
6
+ */
7
+ export const DEFAULT_JOB_INDEX_MAX_ENTRIES = 1000;
8
+ // Artifact cache constants moved to private scope (no longer exported as they are only used internally)
9
+ const DEFAULT_ARTIFACT_CACHE_MAX_ENTRIES = 20;
10
+ const DEFAULT_ARTIFACT_CACHE_TTL_MS = 5 * 60 * 1000;
11
+ const DEFAULT_ARTIFACT_CACHE_MAX_FILE_BYTES = 10 * 1024 * 1024;
12
+
13
+ export interface KasekiApiConfig {
14
+ port: number;
15
+ apiKeys: string[];
16
+ resultsDir: string;
17
+ maxConcurrentRuns: number;
18
+ defaultTaskMode: 'patch' | 'inspect';
19
+ maxDiffBytes: number;
20
+ agentTimeoutSeconds: number;
21
+ logLevel: 'debug' | 'info' | 'warn' | 'error';
22
+ /**
23
+ * Maximum number of terminal jobs retained in the API jobs index.
24
+ * Active queued/running jobs are always retained in addition to this cap.
25
+ */
26
+ jobIndexMaxEntries?: number;
27
+ /** Maximum number of artifact content files cached in memory. Set to 0 to disable caching. */
28
+ artifactCacheMaxEntries?: number;
29
+ /** Artifact content cache time-to-live in milliseconds. */
30
+ artifactCacheTtlMs?: number;
31
+ /** Maximum artifact file size eligible for content caching, in bytes. */
32
+ artifactCacheMaxFileBytes?: number;
33
+ }
34
+
35
+ /**
36
+ * Load and validate configuration from environment variables.
37
+ */
38
+ export function loadConfig(): KasekiApiConfig {
39
+ const apiKeys = loadApiKeys();
40
+ if (!apiKeys || apiKeys.length === 0) {
41
+ throw new Error(
42
+ 'KASEKI_API_KEYS environment variable is required. ' +
43
+ 'Set it to a comma-separated list of API keys, or KASEKI_API_KEYS_FILE pointing to a file.'
44
+ );
45
+ }
46
+
47
+ const port = parseInt(process.env.KASEKI_API_PORT || '8080', 10);
48
+ if (isNaN(port) || port < 1 || port > 65535) {
49
+ throw new Error(`KASEKI_API_PORT must be a valid port number, got: ${process.env.KASEKI_API_PORT}`);
50
+ }
51
+
52
+ const maxConcurrentRuns = parseInt(process.env.KASEKI_API_MAX_CONCURRENT_RUNS || '3', 10);
53
+ if (isNaN(maxConcurrentRuns) || maxConcurrentRuns < 1) {
54
+ throw new Error(`KASEKI_API_MAX_CONCURRENT_RUNS must be >= 1, got: ${process.env.KASEKI_API_MAX_CONCURRENT_RUNS}`);
55
+ }
56
+
57
+ const agentTimeoutSeconds = parseInt(process.env.KASEKI_AGENT_TIMEOUT_SECONDS || '5700', 10);
58
+ if (isNaN(agentTimeoutSeconds) || agentTimeoutSeconds < 1) {
59
+ throw new Error(
60
+ `KASEKI_AGENT_TIMEOUT_SECONDS must be >= 1, got: ${process.env.KASEKI_AGENT_TIMEOUT_SECONDS}`
61
+ );
62
+ }
63
+
64
+ const maxDiffBytes = parseInt(process.env.KASEKI_MAX_DIFF_BYTES || '200000', 10);
65
+ if (isNaN(maxDiffBytes) || maxDiffBytes < 1) {
66
+ throw new Error(`KASEKI_MAX_DIFF_BYTES must be >= 1, got: ${process.env.KASEKI_MAX_DIFF_BYTES}`);
67
+ }
68
+
69
+ const jobIndexMaxEntries = parseInt(
70
+ process.env.KASEKI_API_JOB_INDEX_MAX_ENTRIES || String(DEFAULT_JOB_INDEX_MAX_ENTRIES),
71
+ 10
72
+ );
73
+ if (isNaN(jobIndexMaxEntries) || jobIndexMaxEntries < 0) {
74
+ throw new Error(
75
+ `KASEKI_API_JOB_INDEX_MAX_ENTRIES must be >= 0, got: ${process.env.KASEKI_API_JOB_INDEX_MAX_ENTRIES}`
76
+ );
77
+ }
78
+
79
+ const artifactCacheMaxEntries = parseInt(
80
+ process.env.KASEKI_ARTIFACT_CACHE_MAX_ENTRIES || String(DEFAULT_ARTIFACT_CACHE_MAX_ENTRIES),
81
+ 10
82
+ );
83
+ if (isNaN(artifactCacheMaxEntries) || artifactCacheMaxEntries < 0) {
84
+ throw new Error(
85
+ `KASEKI_ARTIFACT_CACHE_MAX_ENTRIES must be >= 0, got: ${process.env.KASEKI_ARTIFACT_CACHE_MAX_ENTRIES}`
86
+ );
87
+ }
88
+
89
+ const artifactCacheTtlMs = parseInt(
90
+ process.env.KASEKI_ARTIFACT_CACHE_TTL_MS || String(DEFAULT_ARTIFACT_CACHE_TTL_MS),
91
+ 10
92
+ );
93
+ if (isNaN(artifactCacheTtlMs) || artifactCacheTtlMs < 0) {
94
+ throw new Error(
95
+ `KASEKI_ARTIFACT_CACHE_TTL_MS must be >= 0, got: ${process.env.KASEKI_ARTIFACT_CACHE_TTL_MS}`
96
+ );
97
+ }
98
+
99
+ const artifactCacheMaxFileBytes = parseInt(
100
+ process.env.KASEKI_ARTIFACT_CACHE_MAX_FILE_BYTES || String(DEFAULT_ARTIFACT_CACHE_MAX_FILE_BYTES),
101
+ 10
102
+ );
103
+ if (isNaN(artifactCacheMaxFileBytes) || artifactCacheMaxFileBytes < 0) {
104
+ throw new Error(
105
+ `KASEKI_ARTIFACT_CACHE_MAX_FILE_BYTES must be >= 0, got: ${process.env.KASEKI_ARTIFACT_CACHE_MAX_FILE_BYTES}`
106
+ );
107
+ }
108
+
109
+ const taskMode = (process.env.KASEKI_TASK_MODE || 'patch') as 'patch' | 'inspect';
110
+ if (!['patch', 'inspect'].includes(taskMode)) {
111
+ throw new Error(`KASEKI_TASK_MODE must be 'patch' or 'inspect', got: ${taskMode}`);
112
+ }
113
+
114
+ const resultsDir = process.env.KASEKI_RESULTS_DIR || '/agents/kaseki-results';
115
+ if (!fs.existsSync(resultsDir)) {
116
+ throw new Error(`KASEKI_RESULTS_DIR does not exist: ${resultsDir}`);
117
+ }
118
+
119
+ const logLevel = (process.env.KASEKI_API_LOG_LEVEL || 'info') as 'debug' | 'info' | 'warn' | 'error';
120
+ if (!['debug', 'info', 'warn', 'error'].includes(logLevel)) {
121
+ throw new Error(`KASEKI_API_LOG_LEVEL must be debug/info/warn/error, got: ${logLevel}`);
122
+ }
123
+
124
+ return {
125
+ port,
126
+ apiKeys,
127
+ resultsDir,
128
+ maxConcurrentRuns,
129
+ defaultTaskMode: taskMode,
130
+ maxDiffBytes,
131
+ agentTimeoutSeconds,
132
+ logLevel,
133
+ jobIndexMaxEntries,
134
+ artifactCacheMaxEntries,
135
+ artifactCacheTtlMs,
136
+ artifactCacheMaxFileBytes,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Load API keys from environment variable or file.
142
+ */
143
+ function loadApiKeys(): string[] {
144
+ const keysEnv = process.env.KASEKI_API_KEYS;
145
+ if (keysEnv) {
146
+ return keysEnv
147
+ .split(',')
148
+ .map((key) => key.trim())
149
+ .filter((key) => key);
150
+ }
151
+
152
+ const keysFile = process.env.KASEKI_API_KEYS_FILE;
153
+ if (keysFile) {
154
+ try {
155
+ const content = secretValueCache.readFile(keysFile).trim();
156
+ if (content) {
157
+ return content
158
+ .split('\n')
159
+ .map((line) => line.trim())
160
+ .filter((line) => line && !line.startsWith('#'));
161
+ }
162
+ } catch (err) {
163
+ throw new Error(`Failed to read KASEKI_API_KEYS_FILE: ${keysFile}: ${err}`);
164
+ }
165
+ }
166
+
167
+ return [];
168
+ }
169
+
170
+ /**
171
+ * Validate that an API key is valid.
172
+ */
173
+ export function validateApiKey(config: KasekiApiConfig, token: string): boolean {
174
+ return config.apiKeys.includes(token);
175
+ }