@aifabrix/builder 2.49.1 → 2.50.0

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 (413) hide show
  1. package/.cursor/process/README.md +9 -0
  2. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +49 -0
  3. package/.cursor/rules/plan-rules/application-lifecycle.mdc +30 -0
  4. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +35 -0
  5. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +31 -0
  6. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +34 -0
  7. package/.cursor/skills/README.md +1 -0
  8. package/.dockerignore +52 -0
  9. package/.eslintignore +22 -0
  10. package/README.md +2 -0
  11. package/bin/builder-api-cleanup.js +26 -0
  12. package/bin/builder-api-nest.js +10 -0
  13. package/bin/builder-api-spike.js +19 -0
  14. package/enterprise-knowledge/README.md +50 -0
  15. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +22 -0
  16. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +15 -0
  17. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +69 -0
  18. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +52 -0
  19. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +29 -0
  20. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +2 -0
  21. package/integration/README.md +81 -0
  22. package/integration/github/README.md +53 -0
  23. package/integration/github/application.json +21 -0
  24. package/integration/github/deploy.js +72 -0
  25. package/integration/github/env.template +11 -0
  26. package/integration/github/github-datasource-issue-comments.json +239 -0
  27. package/integration/github/github-datasource-issues.json +399 -0
  28. package/integration/github/github-datasource-repository.json +354 -0
  29. package/integration/github/github-deploy.json +1202 -0
  30. package/integration/github/github-system.json +82 -0
  31. package/integration/hubspot-test/README.md +1 -1
  32. package/jest.isolated-projects.js +31 -1
  33. package/jest.projects.js +87 -1
  34. package/lib/api/dev-server-https.js +1 -1
  35. package/lib/api/dev.api.js +1 -0
  36. package/lib/api/enterprise-knowledge.api.js +63 -0
  37. package/lib/api/evidence.api.js +172 -0
  38. package/lib/api/external-systems.api.js +8 -2
  39. package/lib/api/host-ops.api.js +69 -0
  40. package/lib/api/resource-types.api.js +1 -0
  41. package/lib/api/types/enterprise-knowledge.types.js +46 -0
  42. package/lib/api/types/evidence.types.js +67 -0
  43. package/lib/api/types/resource-types.types.js +2 -0
  44. package/lib/app/controller-application-filter.js +36 -0
  45. package/lib/app/deploy.js +1 -1
  46. package/lib/app/list-external-systems.js +130 -0
  47. package/lib/app/list.js +20 -8
  48. package/lib/app/run.js +3 -1
  49. package/lib/app/show-display.js +8 -3
  50. package/lib/app/show-online.js +178 -0
  51. package/lib/app/show.js +37 -88
  52. package/lib/build/builder-api-docker-staging.js +47 -0
  53. package/lib/build/miso-controller-npm-sdk-guard.js +2 -2
  54. package/lib/build/standard-docker-build.js +2 -0
  55. package/lib/certification/post-unified-cert-sync.js +4 -31
  56. package/lib/certification/sync-after-external-command.js +0 -1
  57. package/lib/certification/sync-system-certification.js +0 -3
  58. package/lib/channels/add-channel.js +40 -34
  59. package/lib/channels/approval-guides/chatgpt.js +33 -0
  60. package/lib/channels/approval-guides/credential-portal.js +38 -0
  61. package/lib/channels/approval-guides/index.js +110 -0
  62. package/lib/channels/approval-guides/microsoft-copilot.js +34 -0
  63. package/lib/channels/approval-guides/slack.js +34 -0
  64. package/lib/channels/approval-guides/teams-bot.js +35 -0
  65. package/lib/channels/approval-guides/web-embed.js +32 -0
  66. package/lib/channels/channel-approval-guide.js +11 -0
  67. package/lib/channels/channel-artifact-generator.js +16 -4
  68. package/lib/channels/channel-audit.js +3 -0
  69. package/lib/channels/channel-auth-context.js +1 -1
  70. package/lib/channels/channel-description.js +59 -0
  71. package/lib/channels/channel-integration-generator.js +13 -230
  72. package/lib/channels/channel-integration-template.js +94 -0
  73. package/lib/channels/channel-runtime-resolver.js +43 -3
  74. package/lib/channels/channel-status-display.js +66 -26
  75. package/lib/channels/channel-targets.js +3 -3
  76. package/lib/cli/index.js +27 -3
  77. package/lib/cli/infra-guided.js +5 -1
  78. package/lib/cli/setup-app-external-create.js +111 -0
  79. package/lib/cli/setup-app.help.js +1 -1
  80. package/lib/cli/setup-app.js +38 -125
  81. package/lib/cli/setup-app.test-commands.js +26 -52
  82. package/lib/cli/setup-certification-commands.js +4 -4
  83. package/lib/cli/setup-certification.help.js +9 -9
  84. package/lib/cli/setup-dev-path-commands.js +24 -0
  85. package/lib/cli/setup-dev.js +115 -16
  86. package/lib/cli/setup-external-system-help.js +4 -6
  87. package/lib/cli/setup-external-system.js +58 -52
  88. package/lib/cli/setup-infra-up-builder-api-action.js +64 -0
  89. package/lib/cli/setup-infra-up-platform-action.js +3 -1
  90. package/lib/cli/setup-infra.js +15 -0
  91. package/lib/cli/setup-list.js +43 -0
  92. package/lib/cli/setup-utility-repair.js +26 -7
  93. package/lib/cli/setup-utility.js +5 -3
  94. package/lib/commands/app.js +15 -43
  95. package/lib/commands/channel-add-setup.js +110 -0
  96. package/lib/commands/channel.js +42 -8
  97. package/lib/commands/datasource-sync-display.js +96 -0
  98. package/lib/commands/datasource-sync-policy.js +210 -0
  99. package/lib/commands/datasource-test-trust-cli.js +6 -6
  100. package/lib/commands/datasource-unified-test-cli.js +2 -1
  101. package/lib/commands/datasource-unified-test-cli.options.js +33 -23
  102. package/lib/commands/deploy-gate-failure-details.js +163 -0
  103. package/lib/commands/deploy-gate-orchestrator.js +322 -0
  104. package/lib/commands/dev-cli-handlers.js +64 -61
  105. package/lib/commands/dev-host-ops-params.js +91 -0
  106. package/lib/commands/dev-host-ops-poll.js +111 -0
  107. package/lib/commands/dev-host-ops.js +252 -0
  108. package/lib/commands/dev-init.js +3 -1
  109. package/lib/commands/enterprise-knowledge.js +284 -0
  110. package/lib/commands/evidence-cli-leaves.js +250 -0
  111. package/lib/commands/evidence.js +51 -0
  112. package/lib/commands/governance-cli-shared.js +2 -2
  113. package/lib/commands/governance-command-action.js +2 -2
  114. package/lib/commands/governance-fixture-subjects.js +85 -0
  115. package/lib/commands/governance.js +1 -1
  116. package/lib/commands/lifecycle-command-action.js +2 -2
  117. package/lib/commands/lifecycle-external.js +3 -3
  118. package/lib/commands/manifest-cli-auth.js +29 -0
  119. package/lib/commands/manifest-cli-commands.js +170 -0
  120. package/lib/commands/manifest-cli-integration-path.js +63 -0
  121. package/lib/commands/manifest.js +21 -0
  122. package/lib/commands/protection-cli-leaves.js +3 -3
  123. package/lib/commands/repair-auth-apply.js +15 -1
  124. package/lib/commands/repair-authentications-454.js +169 -0
  125. package/lib/commands/repair-capability-openapi-warnings.js +126 -0
  126. package/lib/commands/repair-datasource-apply.js +135 -0
  127. package/lib/commands/repair-datasource-auto-rbac.js +62 -1
  128. package/lib/commands/repair-datasource-http-service-contract.js +239 -0
  129. package/lib/commands/repair-datasource-operation-security.js +180 -0
  130. package/lib/commands/repair-datasource-run.js +7 -2
  131. package/lib/commands/repair-datasource-sync-operations.js +177 -0
  132. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +133 -0
  133. package/lib/commands/repair-datasource.js +12 -68
  134. package/lib/commands/repair-env-template.js +11 -1
  135. package/lib/commands/repair-external-run.js +121 -31
  136. package/lib/commands/repair-internal.js +1 -1
  137. package/lib/commands/repair-openapi-export.js +156 -0
  138. package/lib/commands/repair-openapi-sync.js +19 -2
  139. package/lib/commands/repair-orchestration-datasource-split.js +259 -0
  140. package/lib/commands/repair-persist.js +22 -1
  141. package/lib/commands/repair-rbac-platform-preset.js +204 -0
  142. package/lib/commands/repair-rbac-reader-roles.js +191 -0
  143. package/lib/commands/repair-rbac.js +163 -86
  144. package/lib/commands/repair-steps.js +83 -18
  145. package/lib/commands/repair-system-permissions.js +1 -0
  146. package/lib/commands/repair.js +1 -1
  147. package/lib/commands/resource-type.js +65 -26
  148. package/lib/commands/run-post-publish-data-sync.js +68 -0
  149. package/lib/commands/setup-modes.js +1 -1
  150. package/lib/commands/test-e2e-external.js +21 -8
  151. package/lib/commands/test-governance-cli-options.js +2 -2
  152. package/lib/commands/test-trust-cli-options.js +2 -2
  153. package/lib/commands/up-builder-api-bootstrap.js +71 -0
  154. package/lib/commands/up-builder-api-provision.js +241 -0
  155. package/lib/commands/up-builder-api.js +88 -0
  156. package/lib/commands/up-common.js +25 -1
  157. package/lib/commands/upload-credential-push-log.js +117 -0
  158. package/lib/commands/upload-data-sync-finalize.js +60 -0
  159. package/lib/commands/upload-probe.js +66 -0
  160. package/lib/commands/upload-publish-context.js +4 -1
  161. package/lib/commands/upload-sidecar-followups.js +55 -27
  162. package/lib/commands/upload.js +116 -157
  163. package/lib/commands/verify-governance-combined-external.js +1 -1
  164. package/lib/commands/verify-governance-db.js +2 -2
  165. package/lib/commands/verify-operations-command-action.js +2 -2
  166. package/lib/commands/verify-operations-step-plan.js +2 -2
  167. package/lib/commands/verify-operations-steps.js +2 -2
  168. package/lib/commands/verify-trust-command-action.js +1 -1
  169. package/lib/commands/wizard-core.js +1 -1
  170. package/lib/commands/wizard-headless.js +2 -1
  171. package/lib/core/admin-secrets.js +2 -6
  172. package/lib/core/secrets-env-content.js +10 -4
  173. package/lib/core/secrets-env-write.js +2 -13
  174. package/lib/datasource/agent-trust-run.js +3 -3
  175. package/lib/datasource/deploy.js +3 -1
  176. package/lib/datasource/test-e2e.js +6 -1
  177. package/lib/datasource/unified-validation-run-body.js +4 -1
  178. package/lib/datasource/unified-validation-run.js +1 -1
  179. package/lib/deployment/deploy-poll-ui.js +73 -12
  180. package/lib/deployment/deployer-poll-run.js +113 -0
  181. package/lib/deployment/deployer.js +17 -87
  182. package/lib/evidence/dataplane-context.js +21 -0
  183. package/lib/evidence/evidence-display.js +78 -0
  184. package/lib/evidence/evidence-service.js +392 -0
  185. package/lib/external-system/deploy.js +290 -112
  186. package/lib/external-system/download.js +7 -3
  187. package/lib/external-system/test-execution.js +1 -1
  188. package/lib/external-system/test-system-level.js +8 -10
  189. package/lib/external-system/test.js +12 -6
  190. package/lib/generator/deploy-script.js +73 -0
  191. package/lib/generator/external.js +2 -1
  192. package/lib/generator/index.js +17 -2
  193. package/lib/generator/wizard.js +3 -36
  194. package/lib/internal/fs-real-sync.js +22 -1
  195. package/lib/internal/node-fs.js +2 -0
  196. package/lib/lifecycle/lifecycle-governance-options.js +1 -1
  197. package/lib/lifecycle/verify-step-progress.js +1 -1
  198. package/lib/programmatic/access-log.js +26 -0
  199. package/lib/programmatic/auth-config.js +44 -0
  200. package/lib/programmatic/bearer-auth.js +176 -0
  201. package/lib/programmatic/builder-api-request.js +175 -0
  202. package/lib/programmatic/builder-api-route-dispatch.js +82 -0
  203. package/lib/programmatic/builder-api-runtime-config.js +42 -0
  204. package/lib/programmatic/builder-api-server.js +230 -0
  205. package/lib/programmatic/builder-help-authentication-catalog.js +282 -0
  206. package/lib/programmatic/builder-help-connected-system-ui.js +266 -0
  207. package/lib/programmatic/builder-help-enterprise-sync-fabrix.js +60 -0
  208. package/lib/programmatic/builder-help-envelope.js +36 -0
  209. package/lib/programmatic/builder-help-fixtures.js +200 -0
  210. package/lib/programmatic/builder-help-governance.js +172 -0
  211. package/lib/programmatic/builder-help-schema-catalog.js +92 -0
  212. package/lib/programmatic/builder-help.js +361 -0
  213. package/lib/programmatic/capability-registry.js +77 -0
  214. package/lib/programmatic/command-options.js +32 -0
  215. package/lib/programmatic/enterprise-mcp-client.js +98 -0
  216. package/lib/programmatic/http-problem-details.js +99 -0
  217. package/lib/programmatic/http-route-match.js +40 -0
  218. package/lib/programmatic/idempotency-cache.js +69 -0
  219. package/lib/programmatic/index.js +53 -0
  220. package/lib/programmatic/integration-path-scope.js +39 -0
  221. package/lib/programmatic/log-redaction.js +66 -0
  222. package/lib/programmatic/manifest-get-section.js +146 -0
  223. package/lib/programmatic/manifest-patch-section.js +252 -0
  224. package/lib/programmatic/manifest-repair.js +122 -0
  225. package/lib/programmatic/manifest-section-dependency-graph.js +160 -0
  226. package/lib/programmatic/manifest-section-io.js +225 -0
  227. package/lib/programmatic/manifest-section-quality.js +171 -0
  228. package/lib/programmatic/manifest-section-registry-data.js +269 -0
  229. package/lib/programmatic/manifest-section-registry.js +205 -0
  230. package/lib/programmatic/nest-http-bridge.js +34 -0
  231. package/lib/programmatic/openapi-component-schemas.js +68 -0
  232. package/lib/programmatic/openapi-description-enricher.js +117 -0
  233. package/lib/programmatic/openapi-descriptions.js +190 -0
  234. package/lib/programmatic/openapi-docs-serving.js +123 -0
  235. package/lib/programmatic/openapi-help-schemas.js +63 -0
  236. package/lib/programmatic/openapi-manifest-schemas.js +141 -0
  237. package/lib/programmatic/openapi-operation-bindings.js +274 -0
  238. package/lib/programmatic/openapi-request-schemas.js +155 -0
  239. package/lib/programmatic/openapi-result-schemas.js +476 -0
  240. package/lib/programmatic/openapi-schema-descriptions-certify.js +139 -0
  241. package/lib/programmatic/openapi-schema-descriptions-core.js +159 -0
  242. package/lib/programmatic/openapi-schema-descriptions-help.js +40 -0
  243. package/lib/programmatic/openapi-schema-descriptions-manifest.js +143 -0
  244. package/lib/programmatic/openapi-schema-descriptions-mcp.js +118 -0
  245. package/lib/programmatic/openapi-schema-descriptions-operations.js +281 -0
  246. package/lib/programmatic/openapi-schema-helpers.js +93 -0
  247. package/lib/programmatic/openapi-spec.js +203 -0
  248. package/lib/programmatic/orchestration-openapi-meta.js +159 -0
  249. package/lib/programmatic/platform-rbac-catalog.js +90 -0
  250. package/lib/programmatic/request-timeout.js +53 -0
  251. package/lib/programmatic/route-handler-map.js +143 -0
  252. package/lib/programmatic/route-handlers-context.js +29 -0
  253. package/lib/programmatic/route-handlers.js +33 -0
  254. package/lib/programmatic/run-certify.js +57 -0
  255. package/lib/programmatic/run-enterprise-mcp.js +32 -0
  256. package/lib/programmatic/run-lifecycle.js +29 -0
  257. package/lib/programmatic/run-repair.js +41 -0
  258. package/lib/programmatic/run-repository-emcp.js +54 -0
  259. package/lib/programmatic/run-template-workspace.js +61 -0
  260. package/lib/programmatic/run-test.js +49 -0
  261. package/lib/programmatic/run-upload.js +63 -0
  262. package/lib/programmatic/run-validate.js +53 -0
  263. package/lib/programmatic/run-wizard.js +40 -0
  264. package/lib/programmatic/system-key.js +48 -0
  265. package/lib/programmatic/wizard-config-normalize.js +118 -0
  266. package/lib/programmatic/workspace-cleanup.js +128 -0
  267. package/lib/programmatic/workspace-context.js +145 -0
  268. package/lib/programmatic/workspace-lock.js +75 -0
  269. package/lib/programmatic/workspace-meta.js +64 -0
  270. package/lib/programmatic/workspace-ops.js +149 -0
  271. package/lib/protection/run-commands-upload-sync.js +35 -0
  272. package/lib/protection/run-commands.js +2 -20
  273. package/lib/protection/upload-batch.js +11 -7
  274. package/lib/resolvers/resource-type-file.js +22 -0
  275. package/lib/schema/external-datasource.schema.json +161 -1
  276. package/lib/schema/external-system.schema.json +242 -4
  277. package/lib/schema/flag-map-validation-run.json +3 -0
  278. package/lib/schema/type/credential-field-registry.json +2 -2
  279. package/lib/schema/type/document-storage.json +9 -0
  280. package/lib/utils/agent-trust-run-display.js +1 -1
  281. package/lib/utils/app-path-resolve.js +168 -0
  282. package/lib/utils/authentication-env-template.js +222 -0
  283. package/lib/utils/cli-sync-options.js +7 -11
  284. package/lib/utils/cli-utils.js +4 -0
  285. package/lib/utils/cli-workspace-options.js +29 -0
  286. package/lib/utils/compose-generate-docker-compose.js +102 -0
  287. package/lib/utils/configuration-env-resolver.js +203 -27
  288. package/lib/utils/datasource-test-run-certificate-tty.js +28 -2
  289. package/lib/utils/datasource-test-run-certification-debug.js +130 -0
  290. package/lib/utils/datasource-test-run-debug-display.js +2 -0
  291. package/lib/utils/datasource-test-run-debug-slice.js +7 -0
  292. package/lib/utils/datasource-test-run-display.js +2 -1
  293. package/lib/utils/dev-init-ssh-merge.js +165 -23
  294. package/lib/utils/dev-pin-share-instructions.js +110 -0
  295. package/lib/utils/dev-ssh-config-helper.js +177 -33
  296. package/lib/utils/enterprise-knowledge-paths.js +142 -0
  297. package/lib/utils/env-copy.js +2 -1
  298. package/lib/utils/env-dotenv-format.js +103 -0
  299. package/lib/utils/external-env-template.js +4 -1
  300. package/lib/utils/external-system-display.js +5 -1
  301. package/lib/utils/external-system-readiness-compact-blocks.js +221 -0
  302. package/lib/utils/external-system-readiness-compact-display.js +324 -0
  303. package/lib/utils/external-system-readiness-core.js +16 -0
  304. package/lib/utils/external-system-readiness-deploy-display.js +29 -11
  305. package/lib/utils/external-system-readiness-display-internals.js +3 -1
  306. package/lib/utils/external-system-readiness-display.js +20 -30
  307. package/lib/utils/external-system-system-test-tty-certification.js +135 -0
  308. package/lib/utils/external-system-system-test-tty.js +5 -41
  309. package/lib/utils/help-builder.js +6 -1
  310. package/lib/utils/host-ops-job-labels.js +236 -0
  311. package/lib/utils/host-ops-job-result.js +61 -0
  312. package/lib/utils/host-ops-poll-ui.js +163 -0
  313. package/lib/utils/integration-path-resolve.js +112 -0
  314. package/lib/utils/paths-reload-source.js +148 -0
  315. package/lib/utils/paths-system-builder-keys.js +25 -0
  316. package/lib/utils/paths.js +151 -26
  317. package/lib/utils/runtime-configuration-values.js +64 -2
  318. package/lib/utils/secrets-helpers.js +5 -1
  319. package/lib/utils/ssh-known-hosts.js +130 -0
  320. package/lib/utils/test-target-cli-options.js +75 -0
  321. package/lib/utils/upload-sync-options.js +4 -0
  322. package/lib/utils/validation-run-request.js +11 -2
  323. package/lib/validation/external-manifest-validator.js +11 -1
  324. package/lib/validation/openapi-contract-surface-validator.js +426 -0
  325. package/lib/validation/openapi-contract-vendor-validation.js +238 -0
  326. package/lib/validation/validate-external-file.js +160 -0
  327. package/lib/validation/validate.js +6 -117
  328. package/package.json +46 -29
  329. package/packages/builder-api/README.md +120 -0
  330. package/packages/builder-api/package.json +30 -0
  331. package/packages/builder-api/src/app.module.ts +7 -0
  332. package/packages/builder-api/src/auth/auth.module.ts +5 -0
  333. package/packages/builder-api/src/external-system/external-system.module.ts +5 -0
  334. package/packages/builder-api/src/main.ts +16 -0
  335. package/packages/builder-api/tsconfig.json +15 -0
  336. package/scripts/check-builder-api-architecture-grep.js +79 -0
  337. package/scripts/docker_image_content_gate.js +47 -0
  338. package/scripts/docker_image_content_gate.sh +5 -0
  339. package/scripts/export-builder-api-evidence.js +262 -0
  340. package/scripts/lib/builder-api-docker-staging-copy.js +60 -0
  341. package/scripts/lib/builder-api-docker-staging-core.js +183 -0
  342. package/scripts/lib/docker-image-content-gate-core.js +166 -0
  343. package/scripts/lib/plan-actor.js +109 -0
  344. package/scripts/lib/plan-config.js +20 -0
  345. package/scripts/lib/plan-gates.js +154 -0
  346. package/scripts/lib/plan-meta.js +104 -0
  347. package/scripts/lib/plan-parse.js +162 -0
  348. package/scripts/lib/plan-prevalidate-gates.js +326 -0
  349. package/scripts/lib/plan-run-core.js +125 -0
  350. package/scripts/lib/plan-run-report.js +108 -0
  351. package/scripts/plan-approve.js +24 -0
  352. package/scripts/plan-archive.js +49 -0
  353. package/scripts/plan-run.js +13 -0
  354. package/scripts/plan-set-actor.js +21 -0
  355. package/scripts/plan-validate.js +80 -0
  356. package/scripts/pnpm/af.mjs +110 -0
  357. package/scripts/pnpm/check-quiet.mjs +32 -0
  358. package/scripts/pnpm/dev-install.mjs +29 -0
  359. package/scripts/pnpm/dev-reload.mjs +18 -0
  360. package/scripts/pnpm/help.mjs +55 -0
  361. package/scripts/pnpm/lib/dev-reload-core.mjs +189 -0
  362. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +67 -0
  363. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +32 -0
  364. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +124 -0
  365. package/scripts/pnpm/lib/resolve-app-url.mjs +183 -0
  366. package/scripts/pnpm/lib/spinner.mjs +42 -0
  367. package/scripts/stage_builder_api_for_docker.js +65 -0
  368. package/scripts/sync-builder-api-template.js +44 -0
  369. package/templates/README.md +3 -0
  370. package/templates/applications/builder-api/Dockerfile +38 -0
  371. package/templates/applications/builder-api/application.yaml +75 -0
  372. package/templates/applications/builder-api/env.template +48 -0
  373. package/templates/applications/dataplane/application.yaml +1 -1
  374. package/templates/applications/dataplane/env.template +10 -6
  375. package/templates/applications/dataplane/rbac.yaml +21 -1
  376. package/templates/applications/miso-controller/application.yaml +1 -1
  377. package/templates/applications/miso-controller/env.template +1 -3
  378. package/templates/channels/README.md +18 -0
  379. package/templates/channels/ai-fabrix-icon.jpg +0 -0
  380. package/templates/channels/chatgpt/install.template.md.hbs +21 -24
  381. package/templates/channels/chatgpt/integration/README.md +11 -0
  382. package/templates/channels/chatgpt/integration/application.json +15 -0
  383. package/templates/channels/chatgpt/integration/chatgpt-system.json +29 -0
  384. package/templates/channels/chatgpt/integration/env.template +4 -0
  385. package/templates/channels/microsoft-copilot/install.template.md.hbs +20 -25
  386. package/templates/channels/microsoft-copilot/integration/README.md +11 -0
  387. package/templates/channels/microsoft-copilot/integration/application.json +15 -0
  388. package/templates/channels/microsoft-copilot/integration/env.template +6 -0
  389. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +34 -0
  390. package/templates/channels/slack/install.template.md.hbs +48 -18
  391. package/templates/channels/slack/integration/README.md +38 -0
  392. package/templates/channels/slack/integration/application.json +18 -0
  393. package/templates/channels/slack/integration/env.template +29 -0
  394. package/templates/channels/slack/integration/slack-datasource-conversations.json +226 -0
  395. package/templates/channels/slack/integration/slack-datasource-messages.json +269 -0
  396. package/templates/channels/slack/integration/slack-system.json +86 -0
  397. package/templates/channels/slack/manifest.template.json.hbs +20 -10
  398. package/templates/channels/teams-bot/install.template.md.hbs +31 -17
  399. package/templates/channels/teams-bot/integration/README.md +18 -0
  400. package/templates/channels/teams-bot/integration/application.json +17 -0
  401. package/templates/channels/teams-bot/integration/env.template +5 -0
  402. package/templates/channels/teams-bot/integration/teams-bot-system.json +55 -0
  403. package/templates/channels/teams-bot/manifest.template.json.hbs +27 -0
  404. package/templates/channels/web-embed/install.template.md.hbs +16 -26
  405. package/templates/channels/web-embed/integration/README.md +11 -0
  406. package/templates/channels/web-embed/integration/application.json +15 -0
  407. package/templates/channels/web-embed/integration/env.template +4 -0
  408. package/templates/channels/web-embed/integration/web-embed-system.json +29 -0
  409. package/templates/openapi/redoc_page.html +86 -0
  410. package/templates/python/docker-compose.hbs +11 -0
  411. package/templates/python/requirements.txt +1 -1
  412. package/templates/typescript/docker-compose.hbs +11 -0
  413. package/lib/certification/cli-cert-sync-skip.js +0 -21
@@ -16,6 +16,8 @@ const {
16
16
  validateDevGroups,
17
17
  augmentDevUserGroupsServerError
18
18
  } = require('../utils/dev-user-groups');
19
+ const { handleProvisionDevUser, handleRemoveDevUser } = require('./dev-host-ops');
20
+ const { logDevPinShareInstructions } = require('../utils/dev-pin-share-instructions');
19
21
 
20
22
  const REMOTE_NOT_CONFIGURED_MSG = 'Remote server is not configured. Set remote-server and run "aifabrix dev init" first.';
21
23
 
@@ -26,6 +28,19 @@ const CERT_WIDTH = 10;
26
28
  const UNTIL_WIDTH = 22; // 2026-03-20T17:31:14
27
29
  const TABLE_SEPARATOR_LENGTH = 130;
28
30
 
31
+ /**
32
+ * Normalize numeric developer id for comparison (e.g. 3 → 03).
33
+ * @param {string} id - Developer ID
34
+ * @returns {string}
35
+ */
36
+ function normalizeDeveloperIdForCompare(id) {
37
+ const trimmed = String(id ?? '').trim();
38
+ if (!/^\d+$/.test(trimmed)) {
39
+ return trimmed;
40
+ }
41
+ return String(parseInt(trimmed, 10)).padStart(2, '0');
42
+ }
43
+
29
44
  /**
30
45
  * Format certificate validity end for display (e.g. 2026-03-20T17:31:14.000Z -> 2026-03-20T17:31:14).
31
46
  * @param {string} iso - ISO 8601 date string
@@ -91,6 +106,10 @@ async function handleDevAdd(options) {
91
106
  groups
92
107
  }, auth.serverCaPem || undefined);
93
108
  logger.log(formatSuccessLine(`Developer ${user.id} created. Use "aifabrix dev pin ${user.id}" to create a PIN for onboarding.`));
109
+ const noProvision = options.noProvision || options['no-provision'];
110
+ if (!noProvision) {
111
+ await handleProvisionDevUser(user.id, options);
112
+ }
94
113
  } catch (err) {
95
114
  augmentDevUserGroupsServerError(err, groups);
96
115
  throw err;
@@ -131,64 +150,6 @@ async function handleDevUpdate(developerId, options) {
131
150
  }
132
151
  }
133
152
 
134
- function logPinHostsVariantLines(baseInit, id, hostsIp) {
135
- logger.log(
136
- chalk.cyan('2) Hosts file / administrator')
137
- + chalk.gray(
138
- ' — use when the hostname does not resolve and your organisation does not publish DNS for it. '
139
- + 'Run in an elevated terminal so the hosts file can be updated, or add the entry manually:'
140
- )
141
- );
142
- if (hostsIp) {
143
- logger.log(` ${baseInit} --add-hosts --hosts-ip ${hostsIp}`);
144
- } else {
145
- logger.log(` ${baseInit} --add-hosts --hosts-ip <server-LAN-IPv4>`);
146
- logger.log(
147
- chalk.gray(
148
- ` Replace <server-LAN-IPv4> with the Builder Server IPv4, or re-run: aifabrix dev pin ${id} --hosts-ip <ip>`
149
- )
150
- );
151
- }
152
- }
153
-
154
- /**
155
- * Log copy-paste dev init commands for the developer (standard vs hosts-file).
156
- * @param {Object} p
157
- * @param {string} p.id - Developer ID
158
- * @param {string} p.serverUrl - Builder Server base URL
159
- * @param {string} p.pin - One-time PIN
160
- * @param {string} p.expiresAt - PIN expiry (API string)
161
- * @param {string} [p.hostsIp] - Optional IPv4 for the hosts variant
162
- * @returns {void}
163
- */
164
- function logDevPinShareInstructions(p) {
165
- const { id, serverUrl, pin, expiresAt, hostsIp } = p;
166
- const baseInit = `aifabrix dev init --developer-id ${id} --server ${serverUrl} --pin ${pin}`;
167
- const pinOnly = `aifabrix dev init --pin ${pin}`;
168
- logger.log(formatSuccessLine(`PIN created for ${id}, expires ${expiresAt}.`));
169
- logger.log('');
170
- logger.log(
171
- chalk.bold('Share with the developer — they run one command (the PIN is one-time):')
172
- );
173
- logger.log('');
174
- logger.log(
175
- chalk.cyan('1) Standard')
176
- + chalk.gray(' — use when this machine can resolve the server hostname (DNS or hosts already set):')
177
- );
178
- logger.log(` ${baseInit}`);
179
- logger.log('');
180
- logPinHostsVariantLines(baseInit, id, hostsIp);
181
- logger.log('');
182
- logger.log(
183
- chalk.cyan('3) Config already has remote-server + developer-id')
184
- + chalk.gray(
185
- ' — e.g. second machine / SSH session after settings were merged; same onboarding, PIN only:'
186
- )
187
- );
188
- logger.log(` ${pinOnly}`);
189
- logger.log('');
190
- }
191
-
192
153
  /**
193
154
  * Handle dev pin – create/regenerate PIN (remote only).
194
155
  * @param {string} [developerId] - Developer ID (optional; uses config if omitted)
@@ -222,18 +183,60 @@ async function handleDevPin(developerId, options = {}) {
222
183
  });
223
184
  }
224
185
 
186
+ /**
187
+ * @param {string} question
188
+ * @returns {Promise<boolean>}
189
+ */
190
+ async function promptYesNo(question) {
191
+ if (!process.stdin.isTTY) {
192
+ return false;
193
+ }
194
+ const readline = require('readline');
195
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
196
+ return new Promise((resolve) => {
197
+ rl.question(`${question} [y/N] `, (answer) => {
198
+ rl.close();
199
+ resolve(/^y(es)?$/i.test(String(answer || '').trim()));
200
+ });
201
+ });
202
+ }
203
+
225
204
  /**
226
205
  * Handle dev delete – remove developer (remote only).
206
+ * Default: full host removal via host-ops. Use --db-only for API-only delete.
227
207
  * @param {string} developerId - Developer ID
208
+ * @param {Object} [options] - Commander options
228
209
  * @returns {Promise<void>}
229
210
  */
230
- async function handleDevDelete(developerId) {
211
+ async function handleDevDelete(developerId, options = {}) {
231
212
  const auth = await getRemoteDevAuth();
232
213
  if (!auth) throw new Error(REMOTE_NOT_CONFIGURED_MSG);
233
214
  const id = developerId;
234
215
  if (!id) throw new Error('Developer ID is required (positional argument or --developer-id).');
235
- await devApi.deleteUser(auth.serverUrl, auth.clientCertPem, id, auth.serverCaPem || undefined);
236
- logger.log(formatSuccessLine(`Developer ${id} removed.`));
216
+ const actorId = await config.getDeveloperId();
217
+ if (
218
+ actorId &&
219
+ normalizeDeveloperIdForCompare(actorId) === normalizeDeveloperIdForCompare(id)
220
+ ) {
221
+ throw new Error('You cannot delete your own developer account.');
222
+ }
223
+ const dbOnly = options.dbOnly || options['db-only'];
224
+ if (dbOnly) {
225
+ await devApi.deleteUser(auth.serverUrl, auth.clientCertPem, id, auth.serverCaPem || undefined);
226
+ logger.log(formatSuccessLine(`Developer ${id} removed from server database.`));
227
+ return;
228
+ }
229
+ const assumeYes = options.yes || options.y;
230
+ if (!assumeYes) {
231
+ const ok = await promptYesNo(
232
+ `Remove developer ${id} from host and server (containers, OS user, database)?`
233
+ );
234
+ if (!ok) {
235
+ logger.log(chalk.gray('Cancelled.'));
236
+ return;
237
+ }
238
+ }
239
+ await handleRemoveDevUser(id, { ...options, developerId: id });
237
240
  }
238
241
 
239
242
  module.exports = {
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @fileoverview Map Commander options to host-ops job params
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ /**
8
+ * @param {Object} options
9
+ * @param {string[]} keys
10
+ * @returns {string|undefined}
11
+ */
12
+ function firstOption(options, ...keys) {
13
+ for (const key of keys) {
14
+ const value = options[key];
15
+ if (value !== undefined && value !== null && value !== '') {
16
+ return value;
17
+ }
18
+ }
19
+ return undefined;
20
+ }
21
+
22
+ /**
23
+ * @param {Object} params
24
+ * @param {Object} options
25
+ * @param {Array<[string, ...string[]]>} mappings
26
+ */
27
+ function assignStringParams(params, options, mappings) {
28
+ for (const [paramKey, ...optionKeys] of mappings) {
29
+ const value = firstOption(options, ...optionKeys);
30
+ if (value !== undefined) {
31
+ params[paramKey] = value;
32
+ }
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @param {Object} params
38
+ * @param {Object} options
39
+ * @param {Array<[string, ...string[]]>} mappings
40
+ */
41
+ function assignFlagParams(params, options, mappings) {
42
+ for (const [paramKey, ...optionKeys] of mappings) {
43
+ if (optionKeys.some((k) => Boolean(options[k]))) {
44
+ params[paramKey] = true;
45
+ }
46
+ }
47
+ }
48
+
49
+ const STRING_PARAM_MAPPINGS = [
50
+ ['include', 'include'],
51
+ ['exclude', 'exclude'],
52
+ ['builderVersion', 'builderVersion', 'builder-version'],
53
+ ['adminEmail', 'adminEmail', 'admin-email'],
54
+ ['adminPassword', 'adminPassword', 'admin-password'],
55
+ ['platform', 'platform'],
56
+ ['installation', 'installation']
57
+ ];
58
+
59
+ const FLAG_PARAM_MAPPINGS = [
60
+ ['skipCert', 'skipCert', 'skip-cert'],
61
+ ['skipPlatform', 'skipPlatform', 'skip-platform'],
62
+ ['skipCli', 'skipCli', 'skip-cli'],
63
+ ['check', 'check'],
64
+ ['failOnBlocked', 'failOnBlocked']
65
+ ];
66
+
67
+ /**
68
+ * Build job params from Commander options.
69
+ * @param {Object} options
70
+ * @param {string} [developerId]
71
+ * @returns {Object}
72
+ */
73
+ function buildJobParams(options, developerId) {
74
+ const params = {};
75
+ const id = developerId || firstOption(options, 'developerId', 'developer-id');
76
+ if (id) {
77
+ params.developerId = String(id).trim();
78
+ }
79
+ assignStringParams(params, options, STRING_PARAM_MAPPINGS);
80
+ assignFlagParams(params, options, FLAG_PARAM_MAPPINGS);
81
+ const timeoutMinutes = firstOption(options, 'timeoutMinutes', 'timeout-minutes');
82
+ if (timeoutMinutes) {
83
+ params.timeoutMinutes = parseInt(timeoutMinutes, 10);
84
+ }
85
+ return params;
86
+ }
87
+
88
+ module.exports = {
89
+ buildJobParams,
90
+ firstOption
91
+ };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @fileoverview Host-ops job polling loop
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ const hostOpsApi = require('../api/host-ops.api');
8
+ const { assertHostOpsJobSucceeded } = require('../utils/host-ops-job-result');
9
+
10
+ const POLL_INTERVAL_MS = 3000;
11
+ const POLL_TIMEOUT_MS = 3 * 60 * 60 * 1000;
12
+ const TRANSIENT_HTTP_STATUSES = new Set([502, 503, 504]);
13
+ /** Allow ~3 min of consecutive gateway errors (container restart during remove-dev-user). */
14
+ const MAX_TRANSIENT_POLL_ERRORS = 60;
15
+
16
+ function sleep(ms) {
17
+ return new Promise((resolve) => setTimeout(resolve, ms));
18
+ }
19
+
20
+ /**
21
+ * @param {Error} err
22
+ * @returns {boolean}
23
+ */
24
+ function isTransientHostOpsPollError(err) {
25
+ return Boolean(err && TRANSIENT_HTTP_STATUSES.has(err.status));
26
+ }
27
+
28
+ /**
29
+ * @param {Error} err
30
+ * @param {string} jobId
31
+ * @returns {Error}
32
+ */
33
+ function enhanceTransientHostOpsPollError(err, jobId) {
34
+ const status = err && err.status;
35
+ const base =
36
+ status === 502
37
+ ? 'Builder server returned Bad Gateway while waiting for the host job.'
38
+ : `Builder server returned HTTP ${status} while waiting for the host job.`;
39
+ const hint =
40
+ 'The job may still be running on the host (common during platform teardown or container restart). ' +
41
+ 'Wait 1–2 minutes, then run "aifabrix dev list" to verify. ' +
42
+ `Job id: ${jobId}`;
43
+ return new Error(`${base} ${hint}`);
44
+ }
45
+
46
+ /**
47
+ * @param {Object} auth
48
+ * @param {string} jobId
49
+ * @param {Object} pollUi
50
+ * @returns {Promise<Object>}
51
+ */
52
+ async function fetchHostOpsJobWithRetry(auth, jobId, pollUi) {
53
+ const started = Date.now();
54
+ for (let attempt = 0; attempt <= MAX_TRANSIENT_POLL_ERRORS; attempt += 1) {
55
+ try {
56
+ return await hostOpsApi.getHostOpsJob(
57
+ auth.serverUrl,
58
+ auth.clientCertPem,
59
+ jobId,
60
+ auth.serverCaPem || undefined
61
+ );
62
+ } catch (err) {
63
+ if (!isTransientHostOpsPollError(err)) {
64
+ throw err;
65
+ }
66
+ if (attempt >= MAX_TRANSIENT_POLL_ERRORS) {
67
+ throw enhanceTransientHostOpsPollError(err, jobId);
68
+ }
69
+ if (pollUi && typeof pollUi.onTransientError === 'function') {
70
+ pollUi.onTransientError(err, Date.now() - started);
71
+ }
72
+ await sleep(POLL_INTERVAL_MS);
73
+ }
74
+ }
75
+ throw new Error(`Failed to fetch host-ops job ${jobId} after transient retries`);
76
+ }
77
+
78
+ /**
79
+ * @param {Object} auth
80
+ * @param {string} jobId
81
+ * @param {Object} pollUi
82
+ * @param {boolean} json
83
+ * @returns {Promise<Object>}
84
+ */
85
+ async function pollHostOpsJobUntilTerminal(auth, jobId, pollUi, json) {
86
+ const started = Date.now();
87
+ while (Date.now() - started < POLL_TIMEOUT_MS) {
88
+ const job = await fetchHostOpsJobWithRetry(auth, jobId, pollUi);
89
+ if (!json) {
90
+ pollUi.onPollProgress(job.status);
91
+ }
92
+ if (job.status === 'failed') {
93
+ throw new Error(job.error || `Host-ops job ${jobId} failed`);
94
+ }
95
+ if (job.status === 'completed') {
96
+ assertHostOpsJobSucceeded(job);
97
+ return job;
98
+ }
99
+ await sleep(POLL_INTERVAL_MS);
100
+ }
101
+ throw new Error(`Timed out waiting for host-ops job ${jobId}`);
102
+ }
103
+
104
+ module.exports = {
105
+ POLL_INTERVAL_MS,
106
+ POLL_TIMEOUT_MS,
107
+ isTransientHostOpsPollError,
108
+ enhanceTransientHostOpsPollError,
109
+ fetchHostOpsJobWithRetry,
110
+ pollHostOpsJobUntilTerminal
111
+ };
@@ -0,0 +1,252 @@
1
+ /**
2
+ * @fileoverview Dev host-ops CLI handlers (platform-install, platform-update, etc.)
3
+ * @author AI Fabrix Team
4
+ * @version 1.0.0
5
+ */
6
+
7
+ const readline = require('readline');
8
+ const chalk = require('chalk');
9
+ const logger = require('../utils/logger');
10
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
11
+ const { getRemoteDevAuth } = require('../utils/remote-dev-auth');
12
+ const { createHostOpsPollHandlers } = require('../utils/host-ops-poll-ui');
13
+ const { buildHostOpsActionLabel, resolveHostOpsTargetLabel } = require('../utils/host-ops-job-labels');
14
+ const { pollHostOpsJobUntilTerminal } = require('./dev-host-ops-poll');
15
+ const { buildJobParams } = require('./dev-host-ops-params');
16
+ const hostOpsApi = require('../api/host-ops.api');
17
+ const { assertHostOpsJobSucceeded } = require('../utils/host-ops-job-result');
18
+
19
+ const REMOTE_NOT_CONFIGURED_MSG =
20
+ 'Remote server is not configured. Set remote-server and run "aifabrix dev init" first.';
21
+
22
+ /**
23
+ * @param {string} type
24
+ * @param {Object} params
25
+ * @param {Object} options
26
+ * @returns {Object}
27
+ */
28
+ function buildHostOpsWaitOpts(type, params, options) {
29
+ return {
30
+ wait: options.wait !== false,
31
+ json: Boolean(options.json),
32
+ jobType: type,
33
+ targetLabel: resolveHostOpsTargetLabel(params),
34
+ params
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Poll host-ops job until terminal state.
40
+ * @param {Object} auth
41
+ * @param {string} jobId
42
+ * @param {Object} [opts]
43
+ * @param {boolean} [opts.wait=true]
44
+ * @param {boolean} [opts.json=false]
45
+ * @returns {Promise<Object>}
46
+ */
47
+ async function waitForHostOpsJob(auth, jobId, opts = {}) {
48
+ const wait = opts.wait !== false;
49
+ const json = Boolean(opts.json);
50
+ if (!wait) {
51
+ return { jobId, status: 'pending' };
52
+ }
53
+ const pollContext = {
54
+ jobType: opts.jobType,
55
+ targetLabel: opts.targetLabel,
56
+ params: opts.params,
57
+ startedAt: Date.now()
58
+ };
59
+ const pollUi = json
60
+ ? { onPollProgress: () => {}, onTransientError: () => {}, finish: () => {} }
61
+ : createHostOpsPollHandlers(jobId, pollContext);
62
+ try {
63
+ return await pollHostOpsJobUntilTerminal(auth, jobId, pollUi, json);
64
+ } finally {
65
+ pollUi.finish();
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @param {string} question
71
+ * @returns {Promise<boolean>}
72
+ */
73
+ function promptYesNo(question) {
74
+ if (!process.stdin.isTTY) {
75
+ return Promise.resolve(false);
76
+ }
77
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
78
+ return new Promise((resolve) => {
79
+ rl.question(`${question} [y/N] `, (answer) => {
80
+ rl.close();
81
+ resolve(/^y(es)?$/i.test(String(answer || '').trim()));
82
+ });
83
+ });
84
+ }
85
+
86
+ function platformAlreadyInstalled(outcomes) {
87
+ if (!Array.isArray(outcomes)) return false;
88
+ return outcomes.some(
89
+ (row) =>
90
+ row &&
91
+ (row.overall === 'SKIP' || row.status === 'SKIP') &&
92
+ String(row.detail || '').toLowerCase().includes('installed')
93
+ );
94
+ }
95
+
96
+ /**
97
+ * Run a host-ops job and optionally wait.
98
+ * @param {string} type
99
+ * @param {Object} params
100
+ * @param {Object} options - Commander options (yes, wait, json)
101
+ * @returns {Promise<Object>}
102
+ */
103
+ async function runHostOpsJob(type, params, options = {}) {
104
+ const auth = await getRemoteDevAuth();
105
+ if (!auth) throw new Error(REMOTE_NOT_CONFIGURED_MSG);
106
+ const { jobId } = await hostOpsApi.createHostOpsJob(
107
+ auth.serverUrl,
108
+ auth.clientCertPem,
109
+ { type, params },
110
+ auth.serverCaPem || undefined
111
+ );
112
+ const waitOpts = buildHostOpsWaitOpts(type, params, options);
113
+ if (options.wait === false) {
114
+ if (options.json) {
115
+ logger.log(JSON.stringify({ jobId, status: 'pending', type }, null, 2));
116
+ } else {
117
+ const action = buildHostOpsActionLabel(type, waitOpts.targetLabel);
118
+ logger.log(formatSuccessLine(`${action} queued (job ${jobId}).`));
119
+ }
120
+ return { jobId, status: 'pending' };
121
+ }
122
+ if (options.json) {
123
+ const job = await waitForHostOpsJob(auth, jobId, waitOpts);
124
+ assertHostOpsJobSucceeded(job);
125
+ logger.log(JSON.stringify(job, null, 2));
126
+ return job;
127
+ }
128
+ const job = await waitForHostOpsJob(auth, jobId, waitOpts);
129
+ assertHostOpsJobSucceeded(job);
130
+ const action = buildHostOpsActionLabel(type, waitOpts.targetLabel);
131
+ logger.log(formatSuccessLine(`${action} completed.`));
132
+ return job;
133
+ }
134
+
135
+ async function handlePlatformInstallCli(developerId, options) {
136
+ const params = buildJobParams(options, developerId);
137
+ if (options.check) {
138
+ params.check = true;
139
+ } else if (!params.builderVersion) {
140
+ throw new Error('--builder-version is required unless --check is set');
141
+ }
142
+ await runHostOpsJob('install-builder-cli', params, options);
143
+ }
144
+
145
+ async function handlePlatformUpdate(developerId, options) {
146
+ const params = buildJobParams(options, developerId);
147
+ if (options.check) params.check = true;
148
+ await runHostOpsJob('update-dev-platform', params, options);
149
+ }
150
+
151
+ function logPlatformInstallOutcomes(developerId, job, json) {
152
+ if (json) {
153
+ logger.log(JSON.stringify(job, null, 2));
154
+ return;
155
+ }
156
+ if (!Array.isArray(job.outcomes)) {
157
+ return;
158
+ }
159
+ job.outcomes.forEach((row) => {
160
+ logger.log(` ${row.linuxUser || developerId}: ${row.overall || row.status}${row.detail ? ` — ${row.detail}` : ''}`);
161
+ });
162
+ }
163
+
164
+ async function runPlatformInstallPreflight(auth, params) {
165
+ const { jobId } = await hostOpsApi.preflightHostOps(
166
+ auth.serverUrl,
167
+ auth.clientCertPem,
168
+ { type: 'install-dev-platform', params },
169
+ auth.serverCaPem || undefined
170
+ );
171
+ return waitForHostOpsJob(auth, jobId, buildHostOpsWaitOpts('install-dev-platform', params, {}));
172
+ }
173
+
174
+ async function handlePlatformInstallCheck(developerId, params, options) {
175
+ const auth = await getRemoteDevAuth();
176
+ if (!auth) throw new Error(REMOTE_NOT_CONFIGURED_MSG);
177
+ params.check = true;
178
+ const { jobId } = await hostOpsApi.preflightHostOps(
179
+ auth.serverUrl,
180
+ auth.clientCertPem,
181
+ { type: 'install-dev-platform', params },
182
+ auth.serverCaPem || undefined
183
+ );
184
+ const job = await waitForHostOpsJob(auth, jobId, buildHostOpsWaitOpts('install-dev-platform', params, options));
185
+ logPlatformInstallOutcomes(developerId, job, options.json);
186
+ assertHostOpsJobSucceeded(job);
187
+ return job;
188
+ }
189
+
190
+ async function confirmPlatformReinstall(developerId, params, options) {
191
+ const auth = await getRemoteDevAuth();
192
+ if (!auth) throw new Error(REMOTE_NOT_CONFIGURED_MSG);
193
+ const preflight = await runPlatformInstallPreflight(auth, { ...params, developerId });
194
+ if (!platformAlreadyInstalled(preflight.outcomes)) {
195
+ return null;
196
+ }
197
+ const ok = await promptYesNo(
198
+ `Platform already installed for dev${String(developerId).padStart(2, '0')}. Re-install?`
199
+ );
200
+ if (!ok) {
201
+ logger.log(chalk.gray('Cancelled.'));
202
+ return preflight;
203
+ }
204
+ await runHostOpsJob('reinstall-dev-platform', params, options);
205
+ return undefined;
206
+ }
207
+
208
+ async function handlePlatformInstall(developerId, options) {
209
+ if (!developerId) {
210
+ throw new Error('Developer ID is required (positional argument).');
211
+ }
212
+ const params = buildJobParams(options, developerId);
213
+
214
+ if (options.check) {
215
+ return handlePlatformInstallCheck(developerId, params, options);
216
+ }
217
+
218
+ if (!options.force && !options.yes) {
219
+ const cancelled = await confirmPlatformReinstall(developerId, params, options);
220
+ if (cancelled) {
221
+ return cancelled;
222
+ }
223
+ }
224
+
225
+ const jobType = options.force ? 'reinstall-dev-platform' : 'install-dev-platform';
226
+ await runHostOpsJob(jobType, params, options);
227
+ }
228
+
229
+ async function handleProvisionDevUser(developerId, options) {
230
+ const params = buildJobParams(options, developerId);
231
+ await runHostOpsJob('provision-dev-user', params, options);
232
+ }
233
+
234
+ async function handleRemoveDevUser(developerId, options) {
235
+ const params = buildJobParams(options, developerId);
236
+ if (!params.developerId) {
237
+ throw new Error('Developer ID is required.');
238
+ }
239
+ await runHostOpsJob('remove-dev-user', params, options);
240
+ }
241
+
242
+ module.exports = {
243
+ buildJobParams,
244
+ buildHostOpsWaitOpts,
245
+ waitForHostOpsJob,
246
+ runHostOpsJob,
247
+ handlePlatformInstallCli,
248
+ handlePlatformUpdate,
249
+ handlePlatformInstall,
250
+ handleProvisionDevUser,
251
+ handleRemoveDevUser
252
+ };
@@ -33,7 +33,7 @@ const {
33
33
  isLinuxCaSudoRequiredError
34
34
  } = require('../utils/dev-ca-install');
35
35
  const { runOptionalHostsSetup } = require('../utils/dev-hosts-helper');
36
- const { mergeDevSshConfigAfterInit } = require('../utils/dev-init-ssh-merge');
36
+ const { mergeDevSshConfigAfterInit, logDevSshConnectHint } = require('../utils/dev-init-ssh-merge');
37
37
  const { resolveInitOptions } = require('../utils/dev-init-resolve');
38
38
  const { displayDevConfig } = require('./dev-show-display');
39
39
  const {
@@ -426,6 +426,7 @@ async function runCertificateRefresh(auth) {
426
426
  warn: (msg) => logger.log(chalk.yellow(` ⚠ ${msg}`))
427
427
  });
428
428
  logger.log(formatSuccessLine('Certificate refreshed and config updated from server.\n'));
429
+ logDevSshConnectHint(await mergeDevSshConfigAfterInit(auth.serverUrl, devId));
429
430
  }
430
431
 
431
432
  /**
@@ -481,6 +482,7 @@ async function runDevRefresh(options = {}) {
481
482
  return;
482
483
  }
483
484
  await fetchAndMergeRemoteSettings(auth, clientCertPem, clientKeyPem, devId);
485
+ logDevSshConnectHint(await mergeDevSshConfigAfterInit(auth.serverUrl, devId));
484
486
  }
485
487
 
486
488
  module.exports = { runDevInit, runDevRefresh };