@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,218 @@
1
+ /**
2
+ * Run Command
3
+ * Execute kaseki agent on target repository
4
+ */
5
+
6
+ import { BaseCommand } from '../BaseCommand';
7
+ import { DockerManager } from '../../docker/DockerManager';
8
+ import { InstanceManager } from '../../instance/InstanceManager';
9
+ import { DoctorCommand } from './DoctorCommand';
10
+ import { createLogger } from '../../logger';
11
+
12
+ const logger = createLogger('run-cmd');
13
+
14
+ export class RunCommand extends BaseCommand {
15
+ async execute(args: string[]): Promise<number> {
16
+ try {
17
+ const { positional } = this.parseArgs(args);
18
+
19
+ // Parse arguments
20
+ const repoUrl = positional[0] || this.configManager.get('repo.url');
21
+ const gitRef = positional[1] || this.configManager.get('repo.ref');
22
+ const taskPrompt = positional[2] || this.configManager.get('repo.task_prompt', '');
23
+
24
+ if (!repoUrl || !gitRef) {
25
+ console.error('Usage: kaseki-agent run <REPO_URL> [GIT_REF] [TASK_PROMPT]');
26
+ console.error('Example: kaseki-agent run https://github.com/org/repo main');
27
+ return 1;
28
+ }
29
+
30
+ console.log('🚀 Kaseki Agent Runner\n');
31
+ console.log(`Repository: ${repoUrl}`);
32
+ console.log(`Branch: ${gitRef}\n`);
33
+
34
+ // Load configuration
35
+ await this.configManager.load();
36
+
37
+ // Step 1: Pre-flight checks
38
+ console.log('Step 1/6: Running pre-flight checks...');
39
+ const doctorCmd = new DoctorCommand(this.configManager);
40
+ const doctorResult = await doctorCmd.execute(['--json']);
41
+ if (doctorResult !== 0) {
42
+ console.error('❌ Pre-flight checks failed');
43
+ return 1;
44
+ }
45
+ console.log('✓ Pre-flight checks passed\n');
46
+
47
+ // Step 2: Pull Docker image
48
+ console.log('Step 2/6: Preparing Docker image...');
49
+ const image = this.configManager.get('docker.image');
50
+ const autoPull = this.configManager.get('docker.auto_pull', true);
51
+
52
+ if (!DockerManager.imageExists(image)) {
53
+ if (!autoPull) {
54
+ console.error(`❌ Docker image not found: ${image}`);
55
+ console.error('Enable auto-pull or pull manually: docker pull ' + image);
56
+ return 1;
57
+ }
58
+
59
+ console.log('Image not found locally, pulling from registry...');
60
+ if (!DockerManager.pullImage(image)) {
61
+ console.error(`❌ Failed to pull Docker image: ${image}`);
62
+ return 1;
63
+ }
64
+ }
65
+ console.log(`✓ Docker image ready: ${image}\n`);
66
+
67
+ // Step 3: Create instance
68
+ console.log('Step 3/6: Creating instance...');
69
+ const kasekiRoot = this.configManager.get('directories.root');
70
+ const instanceManager = new InstanceManager(kasekiRoot);
71
+ const instanceId = await instanceManager.getOrCreateInstanceId();
72
+
73
+ const { workspace, results } = await instanceManager.createDirectories();
74
+ console.log(`✓ Instance created: ${instanceId}`);
75
+ console.log(` Workspace: ${workspace}`);
76
+ console.log(` Results: ${results}\n`);
77
+
78
+ // Initialize metadata
79
+ await instanceManager.initializeMetadata({
80
+ repoUrl,
81
+ gitRef,
82
+ model: this.configManager.get('agent.model'),
83
+ provider: this.configManager.get('agent.provider'),
84
+ });
85
+
86
+ // Step 4: Prepare environment
87
+ console.log('Step 4/6: Preparing environment...');
88
+ const environment = this.buildEnvironment(repoUrl, gitRef, taskPrompt);
89
+ const apiKeyFile = this.configManager.get('auth.openrouter_api_key_file');
90
+ console.log('✓ Environment prepared\n');
91
+
92
+ // Step 5: Run agent in Docker
93
+ console.log('Step 5/6: Running kaseki agent in Docker...\n');
94
+ const timeout = this.configManager.get('agent.timeout_seconds', 1200);
95
+
96
+ const stageStart = new Date();
97
+ const containerResult = await DockerManager.runContainer({
98
+ image,
99
+ name: instanceId,
100
+ workspaceDir: workspace,
101
+ resultsDir: results,
102
+ cacheDir: this.configManager.get('directories.cache_dir'),
103
+ apiKeyFile,
104
+ environment,
105
+ timeout,
106
+ entrypoint: '/agents/kaseki-agent.sh',
107
+ command: [],
108
+ });
109
+
110
+ const stageEnd = new Date();
111
+ await instanceManager.recordStage('agent-run', containerResult.exitCode, stageStart, stageEnd);
112
+
113
+ console.log('\n✓ Agent execution completed\n');
114
+
115
+ // Step 6: Finalize
116
+ console.log('Step 6/6: Finalizing...');
117
+ await instanceManager.finalize(containerResult.exitCode);
118
+
119
+ // Report summary
120
+ const metadata = await instanceManager.getMetadata();
121
+ if (metadata) {
122
+ console.log('\n📊 Run Summary');
123
+ console.log(`Instance: ${instanceId}`);
124
+ console.log(`Status: ${metadata.status}`);
125
+ console.log(`Duration: ${metadata.stages?.['agent-run']?.duration?.toFixed(1)}s`);
126
+
127
+ if (containerResult.exitCode === 0) {
128
+ console.log('\n✅ Run completed successfully');
129
+ console.log(`View results: kaseki-agent report ${instanceId}`);
130
+ } else {
131
+ console.log(`\n❌ Run failed with exit code ${containerResult.exitCode}`);
132
+ console.log(`View logs: kaseki-agent report ${instanceId}`);
133
+ }
134
+ }
135
+
136
+ return containerResult.exitCode;
137
+ } catch (error) {
138
+ logger.error(`Run failed: ${error}`);
139
+ return 1;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Build environment variables for container
145
+ */
146
+ private buildEnvironment(repoUrl: string, gitRef: string, taskPrompt: string): Record<string, string> {
147
+ const env: Record<string, string> = {};
148
+
149
+ // Repo configuration
150
+ env.REPO_URL = repoUrl;
151
+ env.GIT_REF = gitRef;
152
+
153
+ // Agent configuration
154
+ const model = this.configManager.get('agent.model');
155
+ if (model) env.KASEKI_MODEL = model;
156
+
157
+ const provider = this.configManager.get('agent.provider');
158
+ if (provider) env.KASEKI_PROVIDER = provider;
159
+
160
+ const timeout = this.configManager.get('agent.timeout_seconds');
161
+ if (timeout) env.KASEKI_AGENT_TIMEOUT_SECONDS = String(timeout);
162
+
163
+ // Task prompt
164
+ if (taskPrompt) {
165
+ env.TASK_PROMPT = taskPrompt;
166
+ }
167
+
168
+ // Validation
169
+ const validationCommands = this.configManager.get('validation.commands', []);
170
+ if (validationCommands.length > 0) {
171
+ env.KASEKI_VALIDATION_COMMANDS = validationCommands.join(';');
172
+ }
173
+
174
+ const skipMissingScripts = this.configManager.get('validation.skip_missing_npm_scripts');
175
+ if (skipMissingScripts) env.KASEKI_SKIP_MISSING_NPM_SCRIPTS = '1';
176
+
177
+ const failFast = this.configManager.get('validation.fail_fast');
178
+ if (failFast !== undefined) env.KASEKI_VALIDATION_FAIL_FAST = failFast ? '1' : '0';
179
+
180
+ // Allowlist
181
+ const allowlist = this.configManager.get('validation.allowlist', []);
182
+ if (allowlist.length > 0) {
183
+ env.KASEKI_CHANGED_FILES_ALLOWLIST = allowlist.join(' ');
184
+ }
185
+
186
+ const maxDiffBytes = this.configManager.get('validation.max_diff_bytes');
187
+ if (maxDiffBytes) env.KASEKI_MAX_DIFF_BYTES = String(maxDiffBytes);
188
+
189
+ // Caching
190
+ const cacheMode = this.configManager.get('caching.dependency_restore_mode');
191
+ if (cacheMode) env.KASEKI_DEPENDENCY_RESTORE_MODE = cacheMode;
192
+
193
+ // GitHub integration
194
+ const ghAppId = this.configManager.get('auth.github_app_id_file');
195
+ if (ghAppId) env.GITHUB_APP_ID_FILE = ghAppId;
196
+
197
+ const ghClientId = this.configManager.get('auth.github_app_client_id_file');
198
+ if (ghClientId) env.GITHUB_APP_CLIENT_ID_FILE = ghClientId;
199
+
200
+ const ghPrivateKey = this.configManager.get('auth.github_app_private_key_file');
201
+ if (ghPrivateKey) env.GITHUB_APP_PRIVATE_KEY_FILE = ghPrivateKey;
202
+
203
+ // API key file (never inline)
204
+ const apiKeyFile = this.configManager.get('auth.openrouter_api_key_file');
205
+ if (apiKeyFile) {
206
+ env.OPENROUTER_API_KEY_FILE = '/run/secrets/openrouter_api_key';
207
+ }
208
+
209
+ // Debug flags
210
+ const streamProgress = this.configManager.get('debug.stream_progress');
211
+ if (streamProgress !== undefined) env.KASEKI_STREAM_PROGRESS = streamProgress ? '1' : '0';
212
+
213
+ const keepWorkspace = this.configManager.get('debug.keep_workspace');
214
+ if (keepWorkspace !== undefined) env.KASEKI_KEEP_WORKSPACE = keepWorkspace ? '1' : '0';
215
+
216
+ return env;
217
+ }
218
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Secrets Command
3
+ * Manage secrets (API keys, credentials)
4
+ */
5
+
6
+ import { BaseCommand } from '../BaseCommand';
7
+ import { SecretsManager } from '../../secrets/SecretsManager';
8
+ import { createLogger } from '../../logger';
9
+
10
+ const logger = createLogger('secrets-cmd');
11
+
12
+ export class SecretsCommand extends BaseCommand {
13
+ async execute(args: string[]): Promise<number> {
14
+ try {
15
+ const { positional, flags } = this.parseArgs(args);
16
+ const subcommand = positional[0];
17
+ const secretKey = positional[1];
18
+ const secretValue = positional[2];
19
+
20
+ const secretsManager = new SecretsManager();
21
+
22
+ switch (subcommand) {
23
+ case 'init': {
24
+ console.log('🔐 Initializing secrets store...\n');
25
+ await secretsManager.initializeKeyring();
26
+ console.log('✓ Secrets store initialized');
27
+ return 0;
28
+ }
29
+
30
+ case 'set': {
31
+ if (!secretKey || !secretValue) {
32
+ console.error('Usage: kaseki-agent secrets set <KEY> <VALUE>');
33
+ console.error('Example: kaseki-agent secrets set openrouter-api-key sk-or-...');
34
+ return 1;
35
+ }
36
+
37
+ await secretsManager.store(secretKey, secretValue);
38
+ console.log(`✓ Stored secret: ${secretKey}`);
39
+ return 0;
40
+ }
41
+
42
+ case 'get': {
43
+ if (!secretKey) {
44
+ console.error('Usage: kaseki-agent secrets get <KEY>');
45
+ return 1;
46
+ }
47
+
48
+ const value = await secretsManager.retrieve(secretKey);
49
+ if (value) {
50
+ // Only show if explicitly requested with --show
51
+ if (flags.has('show')) {
52
+ console.log(value);
53
+ } else {
54
+ console.log(`✓ Secret exists: ${secretKey}`);
55
+ console.log('(Use --show to display the value)');
56
+ }
57
+ } else {
58
+ console.log(`Secret not found: ${secretKey}`);
59
+ return 1;
60
+ }
61
+ return 0;
62
+ }
63
+
64
+ case 'delete': {
65
+ if (!secretKey) {
66
+ console.error('Usage: kaseki-agent secrets delete <KEY>');
67
+ return 1;
68
+ }
69
+
70
+ await secretsManager.delete(secretKey);
71
+ console.log(`✓ Deleted secret: ${secretKey}`);
72
+ return 0;
73
+ }
74
+
75
+ case 'list': {
76
+ const secrets = await secretsManager.list();
77
+ if (secrets.size === 0) {
78
+ console.log('No secrets stored');
79
+ return 0;
80
+ }
81
+
82
+ console.log('📋 Stored Secrets\n');
83
+ for (const key of secrets.keys()) {
84
+ console.log(` • ${key}`);
85
+ }
86
+ console.log(`\nTotal: ${secrets.size} secret(s)`);
87
+ return 0;
88
+ }
89
+
90
+ case 'help': {
91
+ console.log('🔐 Secrets Management\n');
92
+ console.log('Usage:');
93
+ console.log(' kaseki-agent secrets init Initialize keyring');
94
+ console.log(' kaseki-agent secrets set <KEY> <VALUE> Store a secret');
95
+ console.log(' kaseki-agent secrets get <KEY> [--show] Retrieve a secret');
96
+ console.log(' kaseki-agent secrets delete <KEY> Delete a secret');
97
+ console.log(' kaseki-agent secrets list List all secret keys');
98
+ console.log('\nCommon Keys:');
99
+ console.log(' openrouter-api-key OpenRouter API key');
100
+ console.log(' github-app-id GitHub App ID');
101
+ console.log(' github-app-client-id GitHub App Client ID');
102
+ console.log(' github-app-private-key GitHub App Private Key\n');
103
+ console.log('Storage:');
104
+ console.log(' - Uses Linux pass (password-store) by default');
105
+ console.log(' - Falls back to ~/.kaseki/secrets/ with 0600 permissions');
106
+ console.log(' - Keys are never exposed via environment variables');
107
+ return 0;
108
+ }
109
+
110
+ default:
111
+ console.error('Unknown subcommand: ' + subcommand);
112
+ console.error('\nRun: kaseki-agent secrets help');
113
+ return 1;
114
+ }
115
+ } catch (error) {
116
+ logger.error(`Secrets command failed: ${error}`);
117
+ return 1;
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Serve Command
3
+ * Start kaseki API service
4
+ */
5
+
6
+ import { BaseCommand } from '../BaseCommand';
7
+ import { KasekiAPIService } from '../../kaseki-api-service-wrapper';
8
+ import { createLogger } from '../../logger';
9
+
10
+ const logger = createLogger('serve-cmd');
11
+
12
+ export class ServeCommand extends BaseCommand {
13
+ async execute(args: string[]): Promise<number> {
14
+ try {
15
+ const { flags } = this.parseArgs(args);
16
+ const portValue = flags.get('port');
17
+ const port = parseInt(
18
+ (typeof portValue === 'string' ? portValue : undefined) || '8080',
19
+ 10
20
+ );
21
+
22
+ console.log('🚀 Kaseki API Service\n');
23
+
24
+ await this.configManager.load();
25
+
26
+ // Get API keys from config or environment
27
+ const apiKeysEnv = process.env.KASEKI_API_KEYS || '';
28
+ const apiKeys = apiKeysEnv ? apiKeysEnv.split(',') : [];
29
+
30
+ // Create and start service
31
+ const apiService = new KasekiAPIService({
32
+ port,
33
+ apiKeys,
34
+ logLevel: this.configManager.get('debug.log_level', 'info'),
35
+ });
36
+
37
+ await apiService.start();
38
+
39
+ console.log('Press Ctrl+C to stop the service\n');
40
+
41
+ // Keep the process alive
42
+ return new Promise((resolve) => {
43
+ process.on('SIGINT', async () => {
44
+ console.log('\n\nShutting down...');
45
+ await apiService.stop();
46
+ console.log('✓ Service stopped');
47
+ resolve(0);
48
+ });
49
+
50
+ process.on('SIGTERM', async () => {
51
+ console.log('\n\nShutting down...');
52
+ await apiService.stop();
53
+ console.log('✓ Service stopped');
54
+ resolve(0);
55
+ });
56
+ });
57
+ } catch (error) {
58
+ logger.error(`Serve failed: ${error}`);
59
+ return 1;
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Setup Command
3
+ * Interactive setup wizard for first-time configuration
4
+ */
5
+
6
+ import readline from 'readline';
7
+ import { execSync } from 'child_process';
8
+ import path from 'path';
9
+ import os from 'os';
10
+ import { SecretsManager } from '../../secrets/SecretsManager';
11
+ import { DoctorCommand } from './DoctorCommand';
12
+ import { BaseCommand } from '../BaseCommand';
13
+ import { createLogger } from '../../logger';
14
+
15
+ const logger = createLogger('setup-cmd');
16
+
17
+ interface SetupAnswers {
18
+ apiKey: string;
19
+ configLocation: 'project' | 'global';
20
+ validationCommands: string[];
21
+ modelName: string;
22
+ }
23
+
24
+ export class SetupCommand extends BaseCommand {
25
+ private rl: readline.Interface | null = null;
26
+
27
+ async execute(_args: string[]): Promise<number> {
28
+ try {
29
+ console.log('\n🔧 Kaseki Agent Setup Wizard\n');
30
+ console.log('This will configure kaseki-agent for your system.\n');
31
+
32
+ // Step 1: Check Docker
33
+ console.log('Step 1/5: Checking Docker installation...');
34
+ if (!this.isDockerInstalled()) {
35
+ console.error(
36
+ '❌ Docker is not installed or not accessible.\n' +
37
+ 'Please install Docker and ensure the daemon is running:\n' +
38
+ ' https://docs.docker.com/install/\n\n' +
39
+ 'After installation, run: kaseki-agent setup\n'
40
+ );
41
+ return 1;
42
+ }
43
+ console.log('✓ Docker found and accessible\n');
44
+
45
+ // Step 2: Check Node.js
46
+ console.log('Step 2/5: Checking Node.js...');
47
+ const nodeVersion = this.getNodeVersion();
48
+ if (!nodeVersion || !this.isNodeVersionValid(nodeVersion)) {
49
+ console.error(`❌ Node.js v24+ is required. Found: ${nodeVersion || 'not installed'}`);
50
+ return 1;
51
+ }
52
+ console.log(`✓ Node.js ${nodeVersion} detected\n`);
53
+
54
+ // Step 3: Collect configuration
55
+ console.log('Step 3/5: Configuring OpenRouter API key...');
56
+ const answers = await this.promptForAnswers();
57
+ if (!answers) {
58
+ console.log('Setup cancelled.');
59
+ return 1;
60
+ }
61
+
62
+ // Step 4: Store configuration
63
+ console.log('\nStep 4/5: Saving configuration...');
64
+ await this.saveConfiguration(answers);
65
+ console.log('✓ Configuration saved\n');
66
+
67
+ // Step 5: Run health check
68
+ console.log('Step 5/5: Running health check...\n');
69
+ const doctorCmd = new DoctorCommand(this.configManager);
70
+ const doctorResult = await doctorCmd.execute(['--verbose']);
71
+ if (doctorResult !== 0) {
72
+ console.log(
73
+ '\n⚠️ Health check found issues. Review the output above.\n' +
74
+ 'You can run "kaseki-agent doctor --fix" to auto-remediate.\n'
75
+ );
76
+ // Don't fail - configuration is still valid even if some checks failed
77
+ }
78
+
79
+ console.log(
80
+ '\n✅ Setup complete!\n\n' +
81
+ 'You can now use kaseki-agent:\n' +
82
+ ' kaseki-agent run <repo-url> <git-ref> # Run agent\n' +
83
+ ' kaseki-agent doctor --verbose # Health check\n' +
84
+ ' kaseki-agent serve --port 8080 # Start API\n\n' +
85
+ 'For help: kaseki-agent --help\n'
86
+ );
87
+
88
+ return 0;
89
+ } catch (error) {
90
+ logger.error(`Setup failed: ${error}`);
91
+ return 1;
92
+ } finally {
93
+ this.closeReadline();
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Prompt user for configuration answers
99
+ */
100
+ private async promptForAnswers(): Promise<SetupAnswers | null> {
101
+ try {
102
+ this.rl = readline.createInterface({
103
+ input: process.stdin,
104
+ output: process.stdout,
105
+ });
106
+
107
+ // Prompt for API key
108
+ const apiKey = await this.prompt(
109
+ '📌 Enter your OpenRouter API key (sk-or-...): ',
110
+ true
111
+ );
112
+
113
+ if (!apiKey) {
114
+ console.error('API key is required.');
115
+ return null;
116
+ }
117
+
118
+ // Verify API key format
119
+ if (!apiKey.startsWith('sk-or-')) {
120
+ console.warn('⚠️ API key should start with "sk-or-". Continuing anyway...');
121
+ }
122
+
123
+ // Prompt for config location
124
+ const configLocation = await this.promptChoice(
125
+ 'Where should config be stored?',
126
+ [
127
+ { label: 'Project-local (./kaseki-agent.json)', value: 'project' },
128
+ { label: 'User home (~/.kaseki/config.json)', value: 'global' },
129
+ ]
130
+ );
131
+
132
+ if (!configLocation) {
133
+ return null;
134
+ }
135
+
136
+ // Prompt for model
137
+ const modelName = await this.prompt(
138
+ 'Model identifier (default: openrouter/free): ',
139
+ false
140
+ );
141
+
142
+ return {
143
+ apiKey,
144
+ configLocation: configLocation as 'project' | 'global',
145
+ validationCommands: [
146
+ 'npm run check',
147
+ 'npm run test',
148
+ 'npm run build',
149
+ ],
150
+ modelName: modelName || 'openrouter/free',
151
+ };
152
+ } finally {
153
+ this.closeReadline();
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Prompt for single answer with optional masking
159
+ */
160
+ private prompt(question: string, mask: boolean = false): Promise<string | null> {
161
+ return new Promise((resolve) => {
162
+ if (!this.rl) {
163
+ resolve(null);
164
+ return;
165
+ }
166
+
167
+ if (mask) {
168
+ // Mask password input
169
+ process.stdout.write(question);
170
+ const stdin = process.stdin;
171
+ stdin.resume();
172
+ stdin.setRawMode(true);
173
+
174
+ let password = '';
175
+ stdin.on('data', (char: Buffer) => {
176
+ const charStr = char.toString('utf-8');
177
+ if (charStr === '\n' || charStr === '\r' || charStr === '\u0004') {
178
+ // Enter key or Ctrl-D
179
+ stdin.setRawMode(false);
180
+ stdin.pause();
181
+ console.log(); // Newline for cleanliness
182
+ resolve(password);
183
+ } else if (charStr === '\u0003') {
184
+ // Ctrl-C
185
+ process.exit();
186
+ } else {
187
+ password += charStr;
188
+ process.stdout.write('*');
189
+ }
190
+ });
191
+ } else {
192
+ this.rl!.question(question, (answer) => {
193
+ resolve(answer || null);
194
+ });
195
+ }
196
+ });
197
+ }
198
+
199
+ /**
200
+ * Prompt for choice from list
201
+ */
202
+ private async promptChoice(
203
+ question: string,
204
+ choices: Array<{ label: string; value: string }>
205
+ ): Promise<string | null> {
206
+ return new Promise((resolve) => {
207
+ if (!this.rl) {
208
+ resolve(null);
209
+ return;
210
+ }
211
+
212
+ console.log(`\n${question}`);
213
+ choices.forEach((choice, index) => {
214
+ console.log(` ${index + 1}) ${choice.label}`);
215
+ });
216
+
217
+ this.rl!.question('Enter number (1-' + choices.length + '): ', (answer) => {
218
+ const index = parseInt(answer, 10) - 1;
219
+ if (index >= 0 && index < choices.length) {
220
+ resolve(choices[index].value);
221
+ } else {
222
+ resolve(null);
223
+ }
224
+ });
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Save configuration to file and secrets
230
+ */
231
+ private async saveConfiguration(answers: SetupAnswers): Promise<void> {
232
+ // Store API key securely
233
+ const secretsManager = new SecretsManager();
234
+ await secretsManager.store('openrouter_api_key', answers.apiKey);
235
+
236
+ // Determine config file path
237
+ const configPath = answers.configLocation === 'project'
238
+ ? path.join(process.cwd(), 'kaseki-agent.json')
239
+ : path.join(os.homedir(), '.kaseki', 'config.json');
240
+
241
+ // Load current configuration
242
+ await this.configManager.load();
243
+
244
+ // Update configuration
245
+ this.configManager.set('agent.model', answers.modelName);
246
+ this.configManager.set('auth.openrouter_api_key_file',
247
+ path.join(os.homedir(), '.kaseki', 'secrets', 'openrouter_api_key')
248
+ );
249
+ this.configManager.set('validation.commands', answers.validationCommands);
250
+
251
+ // Save configuration
252
+ await this.configManager.save(configPath);
253
+
254
+ logger.debug(`Configuration saved to: ${configPath}`);
255
+ console.log(`Config saved: ${configPath}`);
256
+ }
257
+
258
+ /**
259
+ * Check if Docker is installed and running
260
+ */
261
+ private isDockerInstalled(): boolean {
262
+ try {
263
+ execSync('docker --version', { stdio: 'ignore' });
264
+ // Also check if daemon is accessible
265
+ execSync('docker ps', { stdio: 'ignore' });
266
+ return true;
267
+ } catch {
268
+ return false;
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Get installed Node.js version
274
+ */
275
+ private getNodeVersion(): string | null {
276
+ try {
277
+ const version = execSync('node --version', { encoding: 'utf-8' }).trim();
278
+ return version.replace('v', '');
279
+ } catch {
280
+ return null;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Check if Node.js version is v24+
286
+ */
287
+ private isNodeVersionValid(version: string): boolean {
288
+ const major = parseInt(version.split('.')[0], 10);
289
+ return major >= 24;
290
+ }
291
+
292
+ /**
293
+ * Close readline interface
294
+ */
295
+ private closeReadline(): void {
296
+ if (this.rl) {
297
+ this.rl.close();
298
+ this.rl = null;
299
+ }
300
+ }
301
+ }