@aifabrix/builder 2.52.1 → 2.53.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 (224) hide show
  1. package/lib/app/deploy-config.js +3 -2
  2. package/lib/channels/add-channel.js +11 -41
  3. package/lib/channels/approval-guides/index.js +3 -0
  4. package/lib/channels/approval-guides/microsoft-copilot.js +26 -17
  5. package/lib/channels/channel-add-existing.js +72 -0
  6. package/lib/channels/channel-runtime-resolver.js +5 -1
  7. package/lib/channels/channel-status-display.js +30 -3
  8. package/lib/channels/microsoft-copilot-entra-sso-form.js +73 -2
  9. package/lib/channels/microsoft-copilot-entra-sso.js +26 -5
  10. package/lib/channels/microsoft-copilot-limits.js +22 -3
  11. package/lib/channels/microsoft-copilot-package.js +59 -14
  12. package/lib/channels/microsoft-copilot-validate.js +40 -10
  13. package/lib/cli/attach-default-help-examples.js +102 -0
  14. package/lib/cli/index.js +2 -0
  15. package/lib/cli/setup-credential-deployment.js +7 -5
  16. package/lib/cli/setup-utility-validate.js +1 -0
  17. package/lib/commands/credential-push.js +37 -5
  18. package/lib/commands/package-score.js +72 -0
  19. package/lib/commands/role-assistant.js +46 -8
  20. package/lib/deployment/deployer-poll-run.js +2 -2
  21. package/lib/deployment/deployer.js +1 -1
  22. package/lib/deployment/environment.js +2 -2
  23. package/lib/external-system/deploy.js +11 -21
  24. package/lib/integration-definition/apply.js +12 -14
  25. package/lib/integration-definition/enrich-openapi-operations.js +9 -10
  26. package/lib/lab/package-parity-score.js +417 -0
  27. package/lib/programmatic/capability-registry.js +1 -0
  28. package/lib/programmatic/help-content/channel-onboarding.md +35 -0
  29. package/lib/programmatic/help-content/cip-overview.md +27 -0
  30. package/lib/programmatic/help-content/connected-system-ui.md +70 -0
  31. package/lib/programmatic/help-content/dimensions-guide.md +23 -0
  32. package/lib/programmatic/help-content/enterprise-sync-fabrix.md +123 -0
  33. package/lib/programmatic/help-content/overview.md +25 -0
  34. package/lib/programmatic/help-content/subscription-guide.md +27 -0
  35. package/lib/programmatic/help-content/workflow.md +50 -0
  36. package/lib/programmatic/openapi-component-schemas.js +1 -0
  37. package/lib/programmatic/openapi-descriptions.js +6 -0
  38. package/lib/programmatic/openapi-envelope-examples.js +11 -0
  39. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  40. package/lib/programmatic/openapi-request-schemas.js +39 -13
  41. package/lib/programmatic/openapi-result-schemas.js +30 -0
  42. package/lib/programmatic/openapi-schema-descriptions-core.js +18 -0
  43. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  44. package/lib/programmatic/persist-supplied-openapi.js +180 -0
  45. package/lib/programmatic/route-handler-map.js +6 -0
  46. package/lib/programmatic/run-integration-definition.js +13 -5
  47. package/lib/role-assistant/evidence-sync.js +55 -7
  48. package/lib/role-assistant/test-runner-attachments.js +118 -0
  49. package/lib/role-assistant/test-runner-bid-apply.js +100 -21
  50. package/lib/role-assistant/test-runner-bid-document.js +1 -1
  51. package/lib/role-assistant/test-runner-start-payload.js +96 -0
  52. package/lib/role-assistant/test-runner-workhub-answers.js +11 -7
  53. package/lib/role-assistant/test-runner-workhub-helpers.js +100 -0
  54. package/lib/role-assistant/test-runner-workhub-soft.js +86 -0
  55. package/lib/role-assistant/test-runner-workhub.js +68 -105
  56. package/lib/role-assistant/test-runner.js +5 -63
  57. package/lib/role-assistant/test-suite.js +33 -40
  58. package/lib/utils/compose-generator.js +10 -1
  59. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  60. package/lib/utils/docker-build.js +2 -2
  61. package/lib/utils/help-builder.js +2 -1
  62. package/lib/utils/promote-dev-docker-image.js +15 -10
  63. package/package.json +14 -16
  64. package/templates/README.md +12 -1
  65. package/templates/applications/dataplane/application.yaml +8 -1
  66. package/templates/applications/dataplane/env.template +1 -1
  67. package/templates/applications/miso-controller/application.yaml +2 -1
  68. package/templates/applications/miso-controller/rbac.yaml +1 -0
  69. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +6 -2
  70. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +8 -5
  71. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  72. package/templates/channels/microsoft-copilot/install.template.md.hbs +74 -22
  73. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +29 -14
  74. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +6 -2
  75. package/templates/python/docker-compose.hbs +36 -0
  76. package/templates/typescript/docker-compose.hbs +36 -0
  77. package/.cursor/plans/Archive/157.1-security-quality-violations-by-type.csv +0 -374
  78. package/.cursor/process/README.md +0 -9
  79. package/.cursor/rules/anchor-docs.mdc +0 -15
  80. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  81. package/.cursor/rules/cli-layout.mdc +0 -81
  82. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  83. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  84. package/.cursor/rules/docs-rules.mdc +0 -32
  85. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  86. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  87. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  88. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  89. package/.cursor/rules/project-rules.mdc +0 -913
  90. package/.cursor/skills/README.md +0 -15
  91. package/.dockerignore +0 -52
  92. package/.eslintignore +0 -22
  93. package/.markdownlint.json +0 -14
  94. package/anchor-docs/README.md +0 -10
  95. package/anchor-docs/_TEMPLATE +0 -24
  96. package/babel.config.js +0 -6
  97. package/bin/builder-api-cleanup.js +0 -26
  98. package/bin/builder-api-nest.js +0 -10
  99. package/bin/builder-api-spike.js +0 -19
  100. package/enterprise-knowledge/README.md +0 -50
  101. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  102. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  103. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  104. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  105. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  106. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  107. package/integration/README.md +0 -81
  108. package/integration/github/README.md +0 -53
  109. package/integration/github/application.json +0 -21
  110. package/integration/github/deploy.js +0 -72
  111. package/integration/github/env.template +0 -11
  112. package/integration/github/github-datasource-issue-comments.json +0 -239
  113. package/integration/github/github-datasource-issues.json +0 -399
  114. package/integration/github/github-datasource-repository.json +0 -354
  115. package/integration/github/github-deploy.json +0 -1202
  116. package/integration/github/github-system.json +0 -82
  117. package/integration/hubspot-test/README.md +0 -159
  118. package/integration/hubspot-test/application.json +0 -54
  119. package/integration/hubspot-test/companies.json +0 -2048
  120. package/integration/hubspot-test/create-hubspot.js +0 -523
  121. package/integration/hubspot-test/env.template +0 -4
  122. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  123. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  124. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  125. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  126. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  127. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  128. package/integration/hubspot-test/rbac.json +0 -166
  129. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  130. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  131. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  132. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  133. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  134. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  135. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  136. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  137. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  138. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  139. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  140. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  141. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  142. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  143. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  144. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  145. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  146. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  147. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  148. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  149. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  150. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  151. package/integration/hubspot-test/test.js +0 -1588
  152. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  153. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  154. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  155. package/integration/roundtrip-test-local/README.md +0 -143
  156. package/integration/roundtrip-test-local/application.yaml +0 -13
  157. package/integration/roundtrip-test-local/env.template +0 -15
  158. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  159. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  160. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  161. package/integration/roundtrip-test-local2/README.md +0 -143
  162. package/integration/roundtrip-test-local2/application.yaml +0 -13
  163. package/integration/roundtrip-test-local2/env.template +0 -15
  164. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  165. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  166. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  167. package/integration/test/wizard.yaml +0 -8
  168. package/jest.config.coverage.js +0 -37
  169. package/jest.config.default.js +0 -11
  170. package/jest.config.integration.fixtures.js +0 -22
  171. package/jest.config.integration.js +0 -33
  172. package/jest.config.isolated.js +0 -11
  173. package/jest.config.manual.js +0 -30
  174. package/jest.global-live-fabrix-hooks.js +0 -36
  175. package/jest.isolated-projects.js +0 -283
  176. package/jest.projects.js +0 -79
  177. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +0 -205
  178. package/packages/builder-api/README.md +0 -120
  179. package/packages/builder-api/package.json +0 -30
  180. package/packages/builder-api/src/app.module.ts +0 -7
  181. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  182. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  183. package/packages/builder-api/src/main.ts +0 -16
  184. package/packages/builder-api/tsconfig.json +0 -15
  185. package/scripts/check-builder-api-architecture-grep.js +0 -79
  186. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  187. package/scripts/ci-fix.sh +0 -19
  188. package/scripts/ci-simulate.sh +0 -19
  189. package/scripts/diagnose-cli.js +0 -150
  190. package/scripts/docker_image_content_gate.js +0 -47
  191. package/scripts/docker_image_content_gate.sh +0 -5
  192. package/scripts/export-builder-api-evidence.js +0 -262
  193. package/scripts/install-local.js +0 -490
  194. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  195. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  196. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  197. package/scripts/lib/plan-actor.js +0 -109
  198. package/scripts/lib/plan-config.js +0 -20
  199. package/scripts/lib/plan-gates.js +0 -154
  200. package/scripts/lib/plan-meta.js +0 -104
  201. package/scripts/lib/plan-parse.js +0 -162
  202. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  203. package/scripts/lib/plan-run-core.js +0 -125
  204. package/scripts/lib/plan-run-report.js +0 -119
  205. package/scripts/plan-approve.js +0 -24
  206. package/scripts/plan-archive.js +0 -49
  207. package/scripts/plan-run.js +0 -13
  208. package/scripts/plan-set-actor.js +0 -21
  209. package/scripts/plan-validate.js +0 -80
  210. package/scripts/pnpm/af.mjs +0 -125
  211. package/scripts/pnpm/check-quiet.mjs +0 -32
  212. package/scripts/pnpm/dev-install.mjs +0 -29
  213. package/scripts/pnpm/dev-reload.mjs +0 -18
  214. package/scripts/pnpm/help.mjs +0 -55
  215. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  216. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  217. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  218. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  219. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  220. package/scripts/pnpm/lib/spinner.mjs +0 -42
  221. package/scripts/pnpm-global-remove.js +0 -48
  222. package/scripts/stage_builder_api_for_docker.js +0 -65
  223. package/scripts/sync-builder-api-template.js +0 -44
  224. package/scripts/test-dataplane-bootstrap.js +0 -331
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env node
2
- const showAll = process.argv.includes('--all');
3
-
4
- const publicSections = [
5
- {
6
- title: 'Start here (80/20)',
7
- lines: [
8
- 'aifabrix setup → once (any directory)',
9
- 'dev-install → after clone (npm install + install:local)',
10
- 'dev-reload → daily (Docker live reload)',
11
- 'check-quiet → before commit (build:ci → .temp/validation/)',
12
- 'Full guide: QUICK-START.md'
13
- ]
14
- },
15
- {
16
- title: 'Sometimes',
17
- lines: [
18
- 'dev → builder-api on host (aifabrix resolve builder-api first)',
19
- 'ui-build → N/A (no UI in this repo)',
20
- 'check → lint + test (fast, not full CI)',
21
- 'pnpm run af ba-logs | ba-down'
22
- ]
23
- }
24
- ];
25
-
26
- const advancedLines = [
27
- '--- Advanced (help -- --all) ---',
28
- 'dev-build / dev-promote (tags main + version) / af ba-*',
29
- 'build:ci → lint + schema-sync + flags + test:ci (same as GitHub)',
30
- 'sync-json → publish builder/builder-api → templates/',
31
- 'test:154 → builder-api scoped Jest + arch grep',
32
- 'test:manual → live API tests (aifabrix login first)',
33
- 'test:integration → integration fixtures (TEST_LANGUAGE=python|typescript optional)',
34
- 'test:hubspot-wizard [-- --type negative|positive]',
35
- 'docker-image-gate-builder-api',
36
- 'builder-api:build | builder-api:nest | builder-api:cleanup',
37
- 'install:global / uninstall:global / diagnose:cli',
38
- 'Docs: docs/development/pnpm-commands.md'
39
- ];
40
-
41
- console.log('AI Fabrix Builder — pnpm commands\n');
42
- for (const section of publicSections) {
43
- console.log(section.title);
44
- for (const line of section.lines) {
45
- console.log(` ${line}`);
46
- }
47
- console.log('');
48
- }
49
- if (!showAll) {
50
- console.log('Run: pnpm run help -- --all');
51
- process.exit(0);
52
- }
53
- for (const line of advancedLines) {
54
- console.log(line);
55
- }
@@ -1,189 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import { resolveAifabrixBin } from './resolve-aifabrix-bin.mjs';
6
- import { resolveAppUrl } from './resolve-app-url.mjs';
7
- import { createSpinner } from './spinner.mjs';
8
-
9
- const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
10
- const LOG_FILE = 'dev-reload.log';
11
- const CAPTURE_PID_FILE = 'dev-reload-capture.pid';
12
-
13
- /**
14
- * @typedef {Object} DevReloadProfile
15
- * @property {string} appKey
16
- * @property {string} displayName
17
- * @property {number} portBase
18
- * @property {string} [webServerUrlEnvKey]
19
- * @property {string} [healthPath]
20
- * @property {boolean} [apiOnly]
21
- * @property {string} [uiBuildCommand]
22
- * @property {string} [uiOutputDir]
23
- */
24
-
25
- /**
26
- * @param {DevReloadProfile} profile
27
- * @returns {Promise<void>}
28
- */
29
- export async function runDevReload(profile) {
30
- const logPath = prepareLogFile();
31
- appendSessionBanner(profile.appKey, logPath);
32
-
33
- const aifabrix = resolveAifabrixBin();
34
- const spinner = createSpinner(`Starting ${profile.displayName} (live reload)…`);
35
- spinner.start();
36
-
37
- await runToLogFile(aifabrix, ['resolve', profile.appKey], logPath);
38
- const runExit = await runToLogFile(aifabrix, ['run', profile.appKey, '--reload'], logPath);
39
- if (runExit !== 0) {
40
- spinner.stop();
41
- printRunFailure(logPath);
42
- process.exit(runExit);
43
- }
44
- spinner.stop();
45
-
46
- startBackgroundLogCapture(profile.appKey, logPath);
47
- printDevReloadSummary(profile, logPath);
48
- }
49
-
50
- /**
51
- * @returns {string}
52
- */
53
- function prepareLogFile() {
54
- const logDir = path.join(repoRoot, '.temp');
55
- fs.mkdirSync(logDir, { recursive: true });
56
- const logPath = path.join(logDir, LOG_FILE);
57
- fs.writeFileSync(logPath, '');
58
- return logPath;
59
- }
60
-
61
- /**
62
- * @returns {string}
63
- */
64
- function capturePidPath() {
65
- return path.join(repoRoot, '.temp', CAPTURE_PID_FILE);
66
- }
67
-
68
- function stopPreviousCapture() {
69
- const pidPath = capturePidPath();
70
- if (!fs.existsSync(pidPath)) {
71
- return;
72
- }
73
- const pid = Number.parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
74
- if (!pid) {
75
- return;
76
- }
77
- try {
78
- process.kill(pid, 'SIGTERM');
79
- } catch {
80
- // already stopped
81
- }
82
- }
83
-
84
- /**
85
- * @param {string} appKey
86
- * @param {string} logPath
87
- */
88
- function startBackgroundLogCapture(appKey, logPath) {
89
- stopPreviousCapture();
90
-
91
- const captureScript = path.join(
92
- path.dirname(fileURLToPath(import.meta.url)),
93
- 'dev-reload-log-capture.mjs'
94
- );
95
- const child = spawn(process.execPath, [captureScript, appKey, logPath], {
96
- cwd: repoRoot,
97
- env: process.env,
98
- detached: true,
99
- stdio: 'ignore'
100
- });
101
- child.unref();
102
- fs.writeFileSync(capturePidPath(), String(child.pid));
103
- }
104
-
105
- /**
106
- * @param {DevReloadProfile} profile
107
- * @param {string} logPath
108
- */
109
- function printDevReloadSummary(profile, logPath) {
110
- const appUrl = resolveAppUrl(profile, { repoRoot }).replace(/\/+$/, '');
111
- const absLog = path.resolve(logPath);
112
- const healthUrl = profile.healthPath ? `${appUrl}${profile.healthPath}` : `${appUrl}/`;
113
-
114
- console.log('');
115
- console.log('═'.repeat(60));
116
- console.log(` ${profile.displayName} — live reload (Docker)`);
117
- console.log('═'.repeat(60));
118
- console.log('');
119
- console.log(` App URL: ${appUrl}/`);
120
- if (profile.healthPath) {
121
- console.log(` Health: ${healthUrl}`);
122
- }
123
- console.log(` Logs: ${absLog}`);
124
- console.log('');
125
- console.log(' Backend code on mounted paths reloads automatically.');
126
- if (!profile.apiOnly && profile.uiBuildCommand && profile.uiOutputDir) {
127
- console.log('');
128
- console.log(' UI changes: run');
129
- console.log(` ${profile.uiBuildCommand}`);
130
- console.log(` → ${profile.uiOutputDir}`);
131
- }
132
- console.log('');
133
- console.log(' Log capture runs in the background (this command exits).');
134
- console.log('═'.repeat(60));
135
- console.log('');
136
- }
137
-
138
- /**
139
- * @param {string} logPath
140
- */
141
- function printRunFailure(logPath) {
142
- console.error(`\n[dev-reload] Start failed. Logs: ${path.resolve(logPath)}\n`);
143
- if (!fs.existsSync(logPath)) {
144
- return;
145
- }
146
- const lines = fs.readFileSync(logPath, 'utf8').trim().split('\n').filter(Boolean);
147
- const tail = lines.slice(-15).join('\n');
148
- if (tail) {
149
- console.error(tail);
150
- console.error('');
151
- }
152
- }
153
-
154
- /**
155
- * @param {string} appKey
156
- * @param {string} logPath
157
- */
158
- function appendSessionBanner(appKey, logPath) {
159
- const banner =
160
- `\n${'='.repeat(72)}\n` +
161
- `[${new Date().toISOString()}] dev-reload session: ${appKey}\n` +
162
- `${'='.repeat(72)}\n`;
163
- fs.appendFileSync(logPath, banner);
164
- }
165
-
166
- /**
167
- * @param {string} command
168
- * @param {string[]} args
169
- * @param {string} logPath
170
- * @returns {Promise<number>}
171
- */
172
- function runToLogFile(command, args, logPath) {
173
- return new Promise((resolve) => {
174
- const proc = spawn(command, args, {
175
- cwd: repoRoot,
176
- env: process.env,
177
- stdio: ['ignore', 'pipe', 'pipe']
178
- });
179
-
180
- const append = (chunk) => {
181
- fs.appendFileSync(logPath, chunk);
182
- };
183
-
184
- proc.stdout.on('data', append);
185
- proc.stderr.on('data', append);
186
- proc.on('close', (code) => resolve(typeof code === 'number' ? code : 1));
187
- proc.on('error', () => resolve(1));
188
- });
189
- }
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Background log capture for dev-reload (detached child).
4
- * Usage: node dev-reload-log-capture.mjs <appKey> <logPath>
5
- */
6
- import { spawn } from 'node:child_process';
7
- import fs from 'node:fs';
8
- import path from 'node:path';
9
- import readline from 'node:readline';
10
- import { fileURLToPath } from 'node:url';
11
- import { resolveAifabrixBin } from './resolve-aifabrix-bin.mjs';
12
- import { resolveLogKeys } from './dev-reload-log-utils.mjs';
13
-
14
- const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
15
- const appKey = process.argv[2];
16
- const logPath = process.argv[3];
17
-
18
- if (!appKey || !logPath) {
19
- console.error('Usage: node dev-reload-log-capture.mjs <appKey> <logPath>');
20
- process.exit(2);
21
- }
22
-
23
- const aifabrix = resolveAifabrixBin();
24
- const logKeys = resolveLogKeys(repoRoot, appKey);
25
-
26
- await Promise.all(
27
- logKeys.map((key) => followAppLogsToFile(aifabrix, key, logPath, key === appKey))
28
- );
29
-
30
- /**
31
- * @param {string} aifabrix
32
- * @param {string} key
33
- * @param {string} filePath
34
- * @param {boolean} required
35
- */
36
- function followAppLogsToFile(aifabrix, key, filePath, required) {
37
- return new Promise((resolve, reject) => {
38
- const proc = spawn(aifabrix, ['logs', key, '-f', '--tail', '100'], {
39
- cwd: repoRoot,
40
- env: process.env,
41
- stdio: ['ignore', 'pipe', 'pipe']
42
- });
43
-
44
- const prefix = `[${key}] `;
45
- const onLine = (line) => {
46
- fs.appendFileSync(filePath, `${new Date().toISOString()} ${prefix}${line}\n`);
47
- };
48
-
49
- readline.createInterface({ input: proc.stdout }).on('line', onLine);
50
- readline.createInterface({ input: proc.stderr }).on('line', onLine);
51
-
52
- proc.on('close', (code) => {
53
- if (code !== 0 && required) {
54
- reject(new Error(`Log capture ended for ${key} (exit ${code ?? 1})`));
55
- return;
56
- }
57
- resolve();
58
- });
59
- proc.on('error', (err) => {
60
- if (required) {
61
- reject(err);
62
- return;
63
- }
64
- resolve();
65
- });
66
- });
67
- }
@@ -1,32 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- /**
5
- * @param {string} repoRoot
6
- * @param {string} appKey
7
- * @returns {string[]}
8
- */
9
- export function resolveLogKeys(repoRoot, appKey) {
10
- return [appKey, ...readCompanionKeys(repoRoot, appKey)];
11
- }
12
-
13
- /**
14
- * @param {string} repoRoot
15
- * @param {string} appKey
16
- * @returns {string[]}
17
- */
18
- function readCompanionKeys(repoRoot, appKey) {
19
- const yamlPath = path.join(repoRoot, 'builder', appKey, 'application.yaml');
20
- if (!fs.existsSync(yamlPath)) {
21
- return [];
22
- }
23
- const text = fs.readFileSync(yamlPath, 'utf8');
24
- if (!/^\s*worker:\s*true\s*$/m.test(text)) {
25
- return [];
26
- }
27
- const companions = [`${appKey}-worker`];
28
- if (/^\s*imageRunBeat:/m.test(text)) {
29
- companions.push(`${appKey}-beat`);
30
- }
31
- return companions;
32
- }
@@ -1,124 +0,0 @@
1
- import { execFileSync } from 'node:child_process';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
-
7
- const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
8
-
9
- /** application.yaml / platform apps need manifest-aware builder (not variables.yaml-only CLIs). */
10
- const MIN_AIFABRIX_VERSION = '2.45.0';
11
-
12
- /**
13
- * @param {string} version - Semver string.
14
- * @returns {number[]} Parsed numeric semver segments.
15
- */
16
- function parseSemver(version) {
17
- return String(version)
18
- .trim()
19
- .split('.')
20
- .map((part) => parseInt(part, 10) || 0);
21
- }
22
-
23
- /**
24
- * @param {string} left - Left semver.
25
- * @param {string} right - Right semver.
26
- * @returns {number} Compare result (1, 0, -1).
27
- */
28
- function compareSemver(left, right) {
29
- const a = parseSemver(left);
30
- const b = parseSemver(right);
31
- for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
32
- const av = a[i] || 0;
33
- const bv = b[i] || 0;
34
- if (av > bv) return 1;
35
- if (av < bv) return -1;
36
- }
37
- return 0;
38
- }
39
-
40
- /**
41
- * @param {string} bin - Candidate executable path.
42
- * @returns {string|null} CLI version output or null.
43
- */
44
- function readAifabrixVersion(bin) {
45
- try {
46
- return execFileSync(bin, ['--version'], { encoding: 'utf8' }).trim();
47
- } catch {
48
- return null;
49
- }
50
- }
51
-
52
- /**
53
- * @returns {string[]} Candidate aifabrix binary paths.
54
- */
55
- function collectAifabrixCandidates() {
56
- const candidates = [];
57
- const seen = new Set();
58
-
59
- const push = (candidate) => {
60
- if (!candidate || seen.has(candidate)) {
61
- return;
62
- }
63
- seen.add(candidate);
64
- candidates.push(candidate);
65
- };
66
-
67
- push(path.join(repoRoot, 'bin', 'aifabrix.js'));
68
-
69
- if (process.env.AIFABRIX_CLI) {
70
- push(process.env.AIFABRIX_CLI);
71
- }
72
-
73
- const home = os.homedir();
74
- push(path.join(home, '.npm-global', 'bin', 'aifabrix'));
75
- push(path.join(home, '.local', 'bin', 'aifabrix'));
76
-
77
- for (const dir of (process.env.PATH || '').split(path.delimiter)) {
78
- if (!dir) continue;
79
- push(path.join(dir, 'aifabrix'));
80
- }
81
-
82
- return candidates;
83
- }
84
-
85
- /**
86
- * Prefer this repo's bin/aifabrix.js when developing the builder package locally.
87
- *
88
- * @returns {string} Resolved aifabrix executable path.
89
- */
90
- export function resolveAifabrixBin() {
91
- let bestBin = null;
92
- let bestVersion = null;
93
-
94
- for (const candidate of collectAifabrixCandidates()) {
95
- let resolved;
96
- try {
97
- if (fs.existsSync(candidate)) {
98
- resolved = fs.realpathSync(candidate);
99
- } else {
100
- continue;
101
- }
102
- } catch {
103
- continue;
104
- }
105
-
106
- const version = readAifabrixVersion(resolved);
107
- if (!version) {
108
- continue;
109
- }
110
- if (!bestVersion || compareSemver(version, bestVersion) > 0) {
111
- bestBin = resolved;
112
- bestVersion = version;
113
- }
114
- }
115
-
116
- if (!bestBin || !bestVersion || compareSemver(bestVersion, MIN_AIFABRIX_VERSION) < 0) {
117
- throw new Error(
118
- `@aifabrix/builder >= ${MIN_AIFABRIX_VERSION} is required (found ${bestVersion || 'none'}). ` +
119
- 'Install: npm run install:local — or set AIFABRIX_CLI to a newer aifabrix binary.'
120
- );
121
- }
122
-
123
- return bestBin;
124
- }
@@ -1,183 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import fs from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
-
6
- /**
7
- * @typedef {Object} AppUrlProfile
8
- * @property {string} appKey
9
- * @property {number} portBase
10
- * @property {string} [webServerUrlEnvKey]
11
- * @property {string} [healthPath]
12
- */
13
-
14
- /**
15
- * @returns {string}
16
- */
17
- function readDeveloperIdRaw() {
18
- const configPath = path.join(os.homedir(), '.aifabrix', 'config.yaml');
19
- if (!fs.existsSync(configPath)) {
20
- return '0';
21
- }
22
- const match = fs.readFileSync(configPath, 'utf8').match(/^developer-id:\s*['"]?(\d+)['"]?\s*$/m);
23
- return match ? match[1] : '0';
24
- }
25
-
26
- /**
27
- * @param {string} appKey
28
- * @param {string} developerIdRaw
29
- * @returns {string}
30
- */
31
- function buildContainerName(appKey, developerIdRaw) {
32
- const idNum = Number.parseInt(developerIdRaw, 10) || 0;
33
- if (idNum === 0) {
34
- return `aifabrix-${appKey}`;
35
- }
36
- return `aifabrix-dev${developerIdRaw}-${appKey}`;
37
- }
38
-
39
- /**
40
- * @param {string} filePath
41
- * @returns {Record<string, string>}
42
- */
43
- function parseEnvFile(filePath) {
44
- const values = {};
45
- for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
46
- const trimmed = line.trim();
47
- if (!trimmed || trimmed.startsWith('#')) {
48
- continue;
49
- }
50
- const eq = trimmed.indexOf('=');
51
- if (eq === -1) {
52
- continue;
53
- }
54
- const key = trimmed.slice(0, eq).trim();
55
- let value = trimmed.slice(eq + 1).trim();
56
- if (
57
- (value.startsWith('"') && value.endsWith('"')) ||
58
- (value.startsWith("'") && value.endsWith("'"))
59
- ) {
60
- value = value.slice(1, -1);
61
- }
62
- values[key] = value;
63
- }
64
- return values;
65
- }
66
-
67
- /**
68
- * @param {string} raw
69
- * @returns {string|null}
70
- */
71
- function normalizeHttpUrl(raw) {
72
- const trimmed = raw?.trim();
73
- if (!trimmed || trimmed.startsWith('kv://')) {
74
- return null;
75
- }
76
- try {
77
- const parsed = new URL(trimmed);
78
- if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
79
- return null;
80
- }
81
- return parsed.toString().replace(/\/+$/, '');
82
- } catch {
83
- return null;
84
- }
85
- }
86
-
87
- /**
88
- * @param {string} repoRoot
89
- * @param {string} appKey
90
- * @param {string} envKey
91
- * @returns {string|null}
92
- */
93
- function readBuilderEnvUrl(repoRoot, appKey, envKey) {
94
- const envPath = path.join(repoRoot, 'builder', appKey, '.env');
95
- if (!fs.existsSync(envPath)) {
96
- return null;
97
- }
98
- return normalizeHttpUrl(parseEnvFile(envPath)[envKey]);
99
- }
100
-
101
- /**
102
- * @param {string} containerName
103
- * @param {string} envKey
104
- * @returns {string|null}
105
- */
106
- function readContainerEnvUrl(containerName, envKey) {
107
- const result = spawnSync('docker', ['exec', containerName, 'env'], {
108
- encoding: 'utf8',
109
- timeout: 8000
110
- });
111
- if (result.status !== 0) {
112
- return null;
113
- }
114
- for (const line of result.stdout.split('\n')) {
115
- if (!line.startsWith(`${envKey}=`)) {
116
- continue;
117
- }
118
- return normalizeHttpUrl(line.slice(envKey.length + 1));
119
- }
120
- return null;
121
- }
122
-
123
- /**
124
- * @param {string} containerName
125
- * @returns {string|null}
126
- */
127
- function readDockerPublishedUrl(containerName) {
128
- const result = spawnSync(
129
- 'docker',
130
- ['ps', '--filter', `name=^/${containerName}$`, '--format', '{{.Ports}}'],
131
- { encoding: 'utf8', timeout: 8000 }
132
- );
133
- if (result.status !== 0) {
134
- return null;
135
- }
136
- const portMatch = result.stdout.match(/:(\d+)->\d+\//);
137
- if (!portMatch) {
138
- return null;
139
- }
140
- return `http://localhost:${portMatch[1]}`;
141
- }
142
-
143
- /**
144
- * @param {number} portBase
145
- * @returns {number}
146
- */
147
- function resolveHostPort(portBase) {
148
- const devId = Number.parseInt(readDeveloperIdRaw(), 10) || 0;
149
- return devId === 0 ? portBase : portBase + devId * 100;
150
- }
151
-
152
- /**
153
- * @param {AppUrlProfile} profile
154
- * @param {{ repoRoot: string }} options
155
- * @returns {string}
156
- */
157
- export function resolveAppUrl(profile, options) {
158
- const { repoRoot } = options;
159
- const envKey = profile.webServerUrlEnvKey;
160
- const containerName = buildContainerName(profile.appKey, readDeveloperIdRaw());
161
-
162
- if (envKey) {
163
- const fromContainer = readContainerEnvUrl(containerName, envKey);
164
- if (fromContainer) {
165
- return `${fromContainer}/`;
166
- }
167
- }
168
-
169
- const fromDockerPort = readDockerPublishedUrl(containerName);
170
- if (fromDockerPort) {
171
- return `${fromDockerPort}/`;
172
- }
173
-
174
- if (envKey) {
175
- const fromBuilderEnv = readBuilderEnvUrl(repoRoot, profile.appKey, envKey);
176
- if (fromBuilderEnv) {
177
- return `${fromBuilderEnv}/`;
178
- }
179
- }
180
-
181
- const port = resolveHostPort(profile.portBase);
182
- return `http://localhost:${port}/`;
183
- }
@@ -1,42 +0,0 @@
1
- /** Minimal TTY spinner (no extra dependencies). */
2
-
3
- const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
4
-
5
- /**
6
- * @param {string} label
7
- * @returns {{ start: () => void, stop: (finalLine?: string) => void }}
8
- */
9
- export function createSpinner(label) {
10
- let frameIndex = 0;
11
- /** @type {NodeJS.Timeout | null} */
12
- let timer = null;
13
- const enabled = process.stdout.isTTY && process.env.CI !== 'true';
14
-
15
- function clearLine() {
16
- if (enabled) {
17
- process.stdout.write('\r\x1b[K');
18
- }
19
- }
20
-
21
- return {
22
- start() {
23
- if (!enabled) {
24
- console.log(label);
25
- return;
26
- }
27
- timer = setInterval(() => {
28
- process.stdout.write(`\r${FRAMES[frameIndex++ % FRAMES.length]} ${label}`);
29
- }, 80);
30
- },
31
- stop(finalLine) {
32
- if (timer) {
33
- clearInterval(timer);
34
- timer = null;
35
- }
36
- clearLine();
37
- if (finalLine) {
38
- console.log(finalLine);
39
- }
40
- }
41
- };
42
- }