@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
package/src/cli.ts ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Kaseki Agent CLI
5
+ *
6
+ * Main entry point for @cyanautomation/kaseki-agent npm package
7
+ * Provides subcommands for setup, running agents, health checks, configuration, and reporting
8
+ */
9
+
10
+ import process from 'process';
11
+ import fs from 'fs/promises';
12
+ import path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import { createLogger } from './logger';
15
+ import { ConfigManager } from './config/ConfigManager';
16
+ import { KasekiCLI } from './cli/KasekiCLI';
17
+
18
+ const logger = createLogger('kaseki-cli');
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+
21
+ /**
22
+ * Main CLI entry point
23
+ */
24
+ async function main() {
25
+ try {
26
+ // Parse CLI arguments and dispatch to appropriate subcommand
27
+ const args = process.argv.slice(2);
28
+
29
+ // Handle --version first (quick check)
30
+ if (args.includes('--version') || args.includes('-v')) {
31
+ const version = await getVersion();
32
+ console.log(version);
33
+ process.exit(0);
34
+ }
35
+
36
+ // Handle --help first (quick check)
37
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
38
+ printHelp();
39
+ process.exit(0);
40
+ }
41
+
42
+ // Initialize CLI
43
+ const configManager = new ConfigManager();
44
+ const cli = new KasekiCLI(configManager);
45
+
46
+ // Get subcommand (first non-flag argument)
47
+ const subcommand = args[0];
48
+
49
+ // Dispatch to appropriate command handler
50
+ const exitCode = await cli.dispatch(subcommand, args.slice(1));
51
+ process.exit(exitCode);
52
+ } catch (error) {
53
+ if (error instanceof Error) {
54
+ logger.error(error.message);
55
+ if (process.env.DEBUG === '1') {
56
+ console.error(error);
57
+ }
58
+ } else {
59
+ logger.error('Unknown error occurred');
60
+ console.error(error);
61
+ }
62
+ process.exit(1);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Get version from package.json
68
+ */
69
+ async function getVersion(): Promise<string> {
70
+ try {
71
+ const packagePath = path.join(__dirname, '../package.json');
72
+ const content = await fs.readFile(packagePath, 'utf-8');
73
+ const pkg = JSON.parse(content);
74
+ return pkg.version || '0.0.0';
75
+ } catch {
76
+ return '0.0.0';
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Print CLI help message
82
+ */
83
+ function printHelp(): void {
84
+ console.log(`
85
+ kaseki-agent - Ephemeral coding-agent runner
86
+
87
+ USAGE
88
+ kaseki-agent <command> [options]
89
+
90
+ COMMANDS
91
+ setup Interactive setup wizard (first-time configuration)
92
+ run [REPO] [REF] Run kaseki agent on target repository
93
+ doctor [--json] [--fix] Health checks and dependency validation
94
+ serve [--port N] Start REST API service for async execution
95
+ config [--get|--set] Manage configuration
96
+ list [--status STATE] List all kaseki instances
97
+ report <INSTANCE_ID> Generate report for completed instance
98
+ secrets Manage stored secrets (keyring/file)
99
+
100
+ COMMON OPTIONS
101
+ --help, -h Show this help message
102
+ --version, -v Show version number
103
+ --config FILE Use alternative config file
104
+ --verbose Verbose output
105
+ --json JSON output format
106
+
107
+ EXAMPLES
108
+ # First-time setup
109
+ kaseki-agent setup
110
+
111
+ # Verify environment
112
+ kaseki-agent doctor --verbose
113
+
114
+ # Run agent on a repository
115
+ kaseki-agent run https://github.com/CyanAutomation/crudmapper main
116
+
117
+ # Start API service
118
+ kaseki-agent serve --port 8080
119
+
120
+ # List completed instances
121
+ kaseki-agent list --status completed
122
+
123
+ DOCUMENTATION
124
+ For more information, visit: https://github.com/CyanAutomation/kaseki-agent
125
+ Check README.md and docs/ for detailed guides
126
+
127
+ ENVIRONMENT
128
+ KASEKI_ROOT Base directory for runs/results (default: /agents)
129
+ OPENROUTER_API_KEY_FILE Path to API key file (default: ~/.kaseki/secrets/openrouter_api_key)
130
+ DEBUG Enable debug output (set to 1)
131
+ `);
132
+ }
133
+
134
+ // Run main
135
+ main().catch((error) => {
136
+ console.error('Fatal error:', error);
137
+ process.exit(1);
138
+ });
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Configuration Manager
3
+ *
4
+ * Handles 4-tier precedence:
5
+ * 1. CLI flags/arguments (highest)
6
+ * 2. Config file: ./kaseki-agent.json (project-local)
7
+ * 3. Config file: ~/.kaseki/config.json (user-global)
8
+ * 4. Environment variables
9
+ * 5. Built-in defaults (lowest)
10
+ */
11
+
12
+ import fs from 'fs/promises';
13
+ import path from 'path';
14
+ import os from 'os';
15
+ import { z } from 'zod';
16
+ import { createLogger } from '../logger';
17
+
18
+ const logger = createLogger('config');
19
+
20
+ /**
21
+ * Full configuration schema with validation
22
+ */
23
+ export const ConfigSchema = z.object({
24
+ // Authentication
25
+ auth: z.object({
26
+ openrouter_api_key_file: z.string().optional(),
27
+ github_app_id_file: z.string().optional(),
28
+ github_app_client_id_file: z.string().optional(),
29
+ github_app_private_key_file: z.string().optional(),
30
+ }).partial(),
31
+
32
+ // Agent execution
33
+ agent: z.object({
34
+ model: z.string().optional(),
35
+ provider: z.string().optional(),
36
+ timeout_seconds: z.number().int().positive().optional(),
37
+ guardrails: z.boolean().optional(),
38
+ }).partial(),
39
+
40
+ // Repository targeting
41
+ repo: z.object({
42
+ url: z.string().optional(),
43
+ ref: z.string().optional(),
44
+ task_mode: z.enum(['patch', 'inspect']).optional(),
45
+ }).partial(),
46
+
47
+ // Validation & quality gates
48
+ validation: z.object({
49
+ commands: z.array(z.string()).optional(),
50
+ skip_missing_npm_scripts: z.boolean().optional(),
51
+ fail_fast: z.boolean().optional(),
52
+ validate_after_agent_failure: z.boolean().optional(),
53
+ allowlist: z.array(z.string()).optional(),
54
+ validation_allowlist: z.array(z.string()).optional(),
55
+ restore_disallowed_changes: z.boolean().optional(),
56
+ max_diff_bytes: z.number().int().positive().optional(),
57
+ allow_empty_diff: z.boolean().optional(),
58
+ }).partial(),
59
+
60
+ // Dependency caching
61
+ caching: z.object({
62
+ dependency_cache_dir: z.string().optional(),
63
+ dependency_restore_mode: z.enum(['copy', 'link']).optional(),
64
+ install_ignore_scripts: z.boolean().optional(),
65
+ npm_omit_dev: z.boolean().optional(),
66
+ image_dependency_cache_dir: z.string().optional(),
67
+ git_cache_mode: z.enum(['off', 'mirror']).optional(),
68
+ git_cache_root: z.string().optional(),
69
+ }).partial(),
70
+
71
+ // Repository memory
72
+ repo_memory: z.object({
73
+ mode: z.enum(['off', 'read', 'read-write']).optional(),
74
+ ttl_days: z.number().int().positive().optional(),
75
+ max_bytes: z.number().int().positive().optional(),
76
+ }).partial(),
77
+
78
+ // GitHub integration
79
+ github: z.object({
80
+ app_enabled: z.boolean().optional(),
81
+ publish_mode: z.enum(['auto', 'on', 'off']).optional(),
82
+ }).partial(),
83
+
84
+ // Docker configuration
85
+ docker: z.object({
86
+ image: z.string().optional(),
87
+ auto_pull: z.boolean().optional(),
88
+ container_user: z.string().optional(),
89
+ }).partial(),
90
+
91
+ // REST API service
92
+ api: z.object({
93
+ port: z.number().int().min(1).max(65535).optional(),
94
+ keys: z.array(z.string()).optional(),
95
+ log_level: z.enum(['debug', 'info', 'warn', 'error']).optional(),
96
+ max_concurrent_runs: z.number().int().positive().optional(),
97
+ }).partial(),
98
+
99
+ // Directory paths
100
+ directories: z.object({
101
+ root: z.string().optional(),
102
+ log_dir: z.string().optional(),
103
+ cache_dir: z.string().optional(),
104
+ }).partial(),
105
+
106
+ // Debug & behavior flags
107
+ debug: z.object({
108
+ stream_progress: z.boolean().optional(),
109
+ keep_workspace: z.boolean().optional(),
110
+ debug_raw_events: z.boolean().optional(),
111
+ dry_run: z.boolean().optional(),
112
+ strict_script_check: z.boolean().optional(),
113
+ strict_host_logging: z.boolean().optional(),
114
+ }).partial(),
115
+ }).partial();
116
+
117
+ export type Config = z.infer<typeof ConfigSchema>;
118
+
119
+ /**
120
+ * Default configuration values
121
+ */
122
+ const DEFAULT_CONFIG: Config = {
123
+ auth: {
124
+ openrouter_api_key_file: path.join(os.homedir(), '.kaseki', 'secrets', 'openrouter_api_key'),
125
+ },
126
+ agent: {
127
+ model: 'openrouter/free',
128
+ provider: 'openrouter',
129
+ timeout_seconds: 1200,
130
+ guardrails: true,
131
+ },
132
+ repo: {
133
+ url: 'https://github.com/CyanAutomation/crudmapper',
134
+ ref: 'main',
135
+ task_mode: 'patch',
136
+ },
137
+ validation: {
138
+ commands: ['npm run check', 'npm run test', 'npm run build'],
139
+ skip_missing_npm_scripts: true,
140
+ fail_fast: true,
141
+ validate_after_agent_failure: false,
142
+ allowlist: ['src/lib/parser.ts', 'tests/parser.validation.ts'],
143
+ restore_disallowed_changes: true,
144
+ max_diff_bytes: 200000,
145
+ allow_empty_diff: false,
146
+ },
147
+ caching: {
148
+ dependency_cache_dir: '/workspace/.kaseki-cache',
149
+ dependency_restore_mode: 'copy',
150
+ install_ignore_scripts: true,
151
+ npm_omit_dev: false,
152
+ git_cache_mode: 'off',
153
+ },
154
+ repo_memory: {
155
+ mode: 'off',
156
+ ttl_days: 30,
157
+ max_bytes: 8000,
158
+ },
159
+ github: {
160
+ app_enabled: false,
161
+ publish_mode: 'auto',
162
+ },
163
+ docker: {
164
+ image: 'docker.io/cyanautomation/kaseki-agent:latest',
165
+ auto_pull: true,
166
+ container_user: '10001:10001',
167
+ },
168
+ api: {
169
+ port: 8080,
170
+ log_level: 'info',
171
+ max_concurrent_runs: 3,
172
+ },
173
+ directories: {
174
+ root: '/agents',
175
+ log_dir: '/var/log/kaseki',
176
+ },
177
+ debug: {
178
+ stream_progress: true,
179
+ keep_workspace: false,
180
+ debug_raw_events: false,
181
+ dry_run: false,
182
+ strict_script_check: false,
183
+ strict_host_logging: false,
184
+ },
185
+ };
186
+
187
+ export class ConfigManager {
188
+ private config: Config = { ...DEFAULT_CONFIG };
189
+ private configFilePath: string | null = null;
190
+ private loaded = false;
191
+
192
+ /**
193
+ * Initialize and load configuration
194
+ * Loads from: project-local config > user config > environment > defaults
195
+ */
196
+ async load(overrideConfigPath?: string): Promise<void> {
197
+ if (this.loaded) {
198
+ return;
199
+ }
200
+
201
+ try {
202
+ // Start with defaults
203
+ this.config = this.deepMerge(DEFAULT_CONFIG, {});
204
+
205
+ // Load project-local config
206
+ const projectConfigPath = path.join(process.cwd(), 'kaseki-agent.json');
207
+ const projectConfig = await this.tryLoadConfigFile(projectConfigPath);
208
+ if (projectConfig) {
209
+ this.config = this.deepMerge(this.config, projectConfig);
210
+ this.configFilePath = projectConfigPath;
211
+ logger.debug(`Loaded project config: ${projectConfigPath}`);
212
+ }
213
+
214
+ // Load user-global config
215
+ const userConfigPath = path.join(os.homedir(), '.kaseki', 'config.json');
216
+ const userConfig = await this.tryLoadConfigFile(userConfigPath);
217
+ if (userConfig) {
218
+ this.config = this.deepMerge(this.config, userConfig);
219
+ // Only set configFilePath to user config if project config wasn't found
220
+ if (!projectConfig) {
221
+ this.configFilePath = userConfigPath;
222
+ }
223
+ logger.debug(`Loaded user config: ${userConfigPath}`);
224
+ }
225
+
226
+ // Load override config if provided
227
+ if (overrideConfigPath) {
228
+ const overrideConfig = await this.tryLoadConfigFile(overrideConfigPath);
229
+ if (overrideConfig) {
230
+ this.config = this.deepMerge(this.config, overrideConfig);
231
+ this.configFilePath = overrideConfigPath;
232
+ logger.debug(`Loaded override config: ${overrideConfigPath}`);
233
+ }
234
+ }
235
+
236
+ // Apply environment variables (override config files)
237
+ this.applyEnvironmentVariables();
238
+
239
+ // Validate final configuration
240
+ this.config = ConfigSchema.parse(this.config);
241
+
242
+ this.loaded = true;
243
+ logger.debug('Configuration loaded successfully');
244
+ } catch (error) {
245
+ if (error instanceof z.ZodError) {
246
+ logger.error(`Configuration validation error: ${error.message}`);
247
+ throw new Error(`Invalid configuration: ${error.errors[0]?.message}`);
248
+ }
249
+ throw error;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Try to load config from file, return null if file doesn't exist
255
+ */
256
+ private async tryLoadConfigFile(filePath: string): Promise<Config | null> {
257
+ try {
258
+ const content = await fs.readFile(filePath, 'utf-8');
259
+ const parsed = JSON.parse(content);
260
+ return parsed;
261
+ } catch (error) {
262
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
263
+ return null;
264
+ }
265
+ if (error instanceof SyntaxError) {
266
+ logger.warn(`Malformed config file: ${filePath}`);
267
+ return null;
268
+ }
269
+ throw error;
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Apply environment variables to config (lowest priority after files)
275
+ * Maps KASEKI_* and OPENROUTER_* env vars to config keys
276
+ */
277
+ private applyEnvironmentVariables(): void {
278
+ const envMap: Record<string, (value: string) => any> = {
279
+ // Auth
280
+ OPENROUTER_API_KEY_FILE: (v) => {
281
+ if (!this.config.auth) this.config.auth = {};
282
+ this.config.auth.openrouter_api_key_file = v;
283
+ },
284
+ GITHUB_APP_ID_FILE: (v) => {
285
+ if (!this.config.auth) this.config.auth = {};
286
+ this.config.auth.github_app_id_file = v;
287
+ },
288
+ GITHUB_APP_CLIENT_ID_FILE: (v) => {
289
+ if (!this.config.auth) this.config.auth = {};
290
+ this.config.auth.github_app_client_id_file = v;
291
+ },
292
+ GITHUB_APP_PRIVATE_KEY_FILE: (v) => {
293
+ if (!this.config.auth) this.config.auth = {};
294
+ this.config.auth.github_app_private_key_file = v;
295
+ },
296
+
297
+ // Agent
298
+ KASEKI_MODEL: (v) => {
299
+ if (!this.config.agent) this.config.agent = {};
300
+ this.config.agent.model = v;
301
+ },
302
+ KASEKI_PROVIDER: (v) => {
303
+ if (!this.config.agent) this.config.agent = {};
304
+ this.config.agent.provider = v;
305
+ },
306
+ KASEKI_AGENT_TIMEOUT_SECONDS: (v) => {
307
+ if (!this.config.agent) this.config.agent = {};
308
+ this.config.agent.timeout_seconds = parseInt(v, 10);
309
+ },
310
+
311
+ // Repo
312
+ REPO_URL: (v) => {
313
+ if (!this.config.repo) this.config.repo = {};
314
+ this.config.repo.url = v;
315
+ },
316
+ GIT_REF: (v) => {
317
+ if (!this.config.repo) this.config.repo = {};
318
+ this.config.repo.ref = v;
319
+ },
320
+
321
+ // Validation
322
+ KASEKI_VALIDATION_COMMANDS: (v) => {
323
+ if (!this.config.validation) this.config.validation = {};
324
+ this.config.validation.commands = v.split(';').map((c) => c.trim());
325
+ },
326
+ KASEKI_MAX_DIFF_BYTES: (v) => {
327
+ if (!this.config.validation) this.config.validation = {};
328
+ this.config.validation.max_diff_bytes = parseInt(v, 10);
329
+ },
330
+
331
+ // Docker
332
+ KASEKI_IMAGE: (v) => {
333
+ if (!this.config.docker) this.config.docker = {};
334
+ this.config.docker.image = v;
335
+ },
336
+
337
+ // API
338
+ KASEKI_API_PORT: (v) => {
339
+ if (!this.config.api) this.config.api = {};
340
+ this.config.api.port = parseInt(v, 10);
341
+ },
342
+ KASEKI_API_KEYS: (v) => {
343
+ if (!this.config.api) this.config.api = {};
344
+ this.config.api.keys = v.split(',').map((k) => k.trim());
345
+ },
346
+
347
+ // Directories
348
+ KASEKI_ROOT: (v) => {
349
+ if (!this.config.directories) this.config.directories = {};
350
+ this.config.directories.root = v;
351
+ },
352
+ KASEKI_LOG_DIR: (v) => {
353
+ if (!this.config.directories) this.config.directories = {};
354
+ this.config.directories.log_dir = v;
355
+ },
356
+ };
357
+
358
+ for (const [envKey, setter] of Object.entries(envMap)) {
359
+ const value = process.env[envKey];
360
+ if (value !== undefined) {
361
+ setter(value);
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Deep merge objects (right overwrites left)
368
+ */
369
+ private deepMerge(target: any, source: any): any {
370
+ if (!source) return target;
371
+
372
+ const result = { ...target };
373
+
374
+ for (const key in source) {
375
+ if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
376
+ result[key] = this.deepMerge(result[key] || {}, source[key]);
377
+ } else {
378
+ result[key] = source[key];
379
+ }
380
+ }
381
+
382
+ return result;
383
+ }
384
+
385
+ /**
386
+ * Get entire configuration
387
+ */
388
+ getConfig(): Readonly<Config> {
389
+ if (!this.loaded) {
390
+ throw new Error('Configuration not loaded. Call load() first.');
391
+ }
392
+ return this.config;
393
+ }
394
+
395
+ /**
396
+ * Get specific config value by dot-notation path
397
+ */
398
+ get<T = any>(path: string, defaultValue?: T): T {
399
+ if (!this.loaded) {
400
+ throw new Error('Configuration not loaded. Call load() first.');
401
+ }
402
+
403
+ const parts = path.split('.');
404
+ let current: any = this.config;
405
+
406
+ for (const part of parts) {
407
+ if (current?.[part] === undefined) {
408
+ if (defaultValue !== undefined) {
409
+ return defaultValue;
410
+ }
411
+ throw new Error(`Configuration key not found: ${path}`);
412
+ }
413
+ current = current[part];
414
+ }
415
+
416
+ return current;
417
+ }
418
+
419
+ /**
420
+ * Set configuration value
421
+ */
422
+ set(path: string, value: any): void {
423
+ const parts = path.split('.');
424
+ const lastPart = parts.pop();
425
+
426
+ if (!lastPart) {
427
+ throw new Error('Invalid configuration path');
428
+ }
429
+
430
+ let current: any = this.config;
431
+
432
+ for (const part of parts) {
433
+ if (!current[part]) {
434
+ current[part] = {};
435
+ }
436
+ current = current[part];
437
+ }
438
+
439
+ current[lastPart] = value;
440
+ }
441
+
442
+ /**
443
+ * Save configuration to file
444
+ */
445
+ async save(filePath?: string): Promise<void> {
446
+ const targetPath = filePath || this.configFilePath;
447
+ if (!targetPath) {
448
+ throw new Error('No config file path specified for saving');
449
+ }
450
+
451
+ try {
452
+ const dir = path.dirname(targetPath);
453
+ await fs.mkdir(dir, { recursive: true });
454
+ await fs.writeFile(targetPath, JSON.stringify(this.config, null, 2), 'utf-8');
455
+ logger.debug(`Configuration saved to: ${targetPath}`);
456
+ } catch (error) {
457
+ throw new Error(`Failed to save configuration: ${error}`);
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Get config file path (for reference/logging)
463
+ */
464
+ getConfigFilePath(): string | null {
465
+ return this.configFilePath;
466
+ }
467
+
468
+ /**
469
+ * Reset to defaults
470
+ */
471
+ reset(): void {
472
+ this.config = { ...DEFAULT_CONFIG };
473
+ this.configFilePath = null;
474
+ this.loaded = false;
475
+ }
476
+ }