@aifabrix/builder 2.52.1 → 2.53.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 (225) 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/schema/external-datasource.schema.json +13 -4
  59. package/lib/utils/compose-generator.js +10 -1
  60. package/lib/utils/compose-traefik-extra-routes.js +107 -0
  61. package/lib/utils/docker-build.js +2 -2
  62. package/lib/utils/help-builder.js +2 -1
  63. package/lib/utils/promote-dev-docker-image.js +15 -10
  64. package/package.json +14 -16
  65. package/templates/README.md +12 -1
  66. package/templates/applications/dataplane/application.yaml +8 -1
  67. package/templates/applications/dataplane/env.template +1 -1
  68. package/templates/applications/miso-controller/application.yaml +2 -1
  69. package/templates/applications/miso-controller/rbac.yaml +1 -0
  70. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +6 -2
  71. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +8 -5
  72. package/templates/channels/microsoft-copilot/fi-fi.template.json.hbs +7 -0
  73. package/templates/channels/microsoft-copilot/install.template.md.hbs +74 -22
  74. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +29 -14
  75. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +6 -2
  76. package/templates/python/docker-compose.hbs +36 -0
  77. package/templates/typescript/docker-compose.hbs +36 -0
  78. package/.cursor/plans/Archive/157.1-security-quality-violations-by-type.csv +0 -374
  79. package/.cursor/process/README.md +0 -9
  80. package/.cursor/rules/anchor-docs.mdc +0 -15
  81. package/.cursor/rules/assistant-driven-integration-build.mdc +0 -40
  82. package/.cursor/rules/cli-layout.mdc +0 -81
  83. package/.cursor/rules/cli-product-neutral.mdc +0 -43
  84. package/.cursor/rules/deploy-rules/docker-image-ip-policy.mdc +0 -49
  85. package/.cursor/rules/docs-rules.mdc +0 -32
  86. package/.cursor/rules/plan-rules/application-lifecycle.mdc +0 -30
  87. package/.cursor/rules/plan-rules/plan-frontmatter.mdc +0 -35
  88. package/.cursor/rules/plan-rules/plan-lifecycle.mdc +0 -31
  89. package/.cursor/rules/plan-rules/plan-validation-gate.mdc +0 -34
  90. package/.cursor/rules/project-rules.mdc +0 -913
  91. package/.cursor/skills/README.md +0 -15
  92. package/.dockerignore +0 -52
  93. package/.eslintignore +0 -22
  94. package/.markdownlint.json +0 -14
  95. package/anchor-docs/README.md +0 -10
  96. package/anchor-docs/_TEMPLATE +0 -24
  97. package/babel.config.js +0 -6
  98. package/bin/builder-api-cleanup.js +0 -26
  99. package/bin/builder-api-nest.js +0 -10
  100. package/bin/builder-api-spike.js +0 -19
  101. package/enterprise-knowledge/README.md +0 -50
  102. package/enterprise-knowledge/enterprise-integration-lifecycle/README.md +0 -22
  103. package/enterprise-knowledge/enterprise-integration-lifecycle/application.json +0 -15
  104. package/enterprise-knowledge/enterprise-integration-lifecycle/deploy.js +0 -69
  105. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-deploy.json +0 -52
  106. package/enterprise-knowledge/enterprise-integration-lifecycle/enterprise-integration-lifecycle-system.json +0 -29
  107. package/enterprise-knowledge/enterprise-integration-lifecycle/env.template +0 -2
  108. package/integration/README.md +0 -81
  109. package/integration/github/README.md +0 -53
  110. package/integration/github/application.json +0 -21
  111. package/integration/github/deploy.js +0 -72
  112. package/integration/github/env.template +0 -11
  113. package/integration/github/github-datasource-issue-comments.json +0 -239
  114. package/integration/github/github-datasource-issues.json +0 -399
  115. package/integration/github/github-datasource-repository.json +0 -354
  116. package/integration/github/github-deploy.json +0 -1202
  117. package/integration/github/github-system.json +0 -82
  118. package/integration/hubspot-test/README.md +0 -159
  119. package/integration/hubspot-test/application.json +0 -54
  120. package/integration/hubspot-test/companies.json +0 -2048
  121. package/integration/hubspot-test/create-hubspot.js +0 -523
  122. package/integration/hubspot-test/env.template +0 -4
  123. package/integration/hubspot-test/hubspot-test-datasource-company.json +0 -138
  124. package/integration/hubspot-test/hubspot-test-datasource-contact.json +0 -146
  125. package/integration/hubspot-test/hubspot-test-datasource-deal.json +0 -146
  126. package/integration/hubspot-test/hubspot-test-datasource-users.json +0 -76
  127. package/integration/hubspot-test/hubspot-test-deploy.json +0 -2160
  128. package/integration/hubspot-test/hubspot-test-system.json +0 -74
  129. package/integration/hubspot-test/rbac.json +0 -166
  130. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +0 -20
  131. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +0 -9
  132. package/integration/hubspot-test/test-artifacts/wizard-invalid-add-datasource.yaml +0 -5
  133. package/integration/hubspot-test/test-artifacts/wizard-invalid-app-name.yaml +0 -5
  134. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-create.yaml +0 -7
  135. package/integration/hubspot-test/test-artifacts/wizard-invalid-credential-select.yaml +0 -7
  136. package/integration/hubspot-test/test-artifacts/wizard-invalid-known-platform.yaml +0 -4
  137. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-app.yaml +0 -4
  138. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  139. package/integration/hubspot-test/test-artifacts/wizard-invalid-mode.yaml +0 -5
  140. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-file.yaml +0 -5
  141. package/integration/hubspot-test/test-artifacts/wizard-invalid-openapi-url.yaml +0 -4
  142. package/integration/hubspot-test/test-artifacts/wizard-invalid-source.yaml +0 -4
  143. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-array-test.yaml +0 -5
  144. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  145. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  146. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-test.yaml +0 -5
  147. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-test.yaml +0 -5
  148. package/integration/hubspot-test/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +0 -5
  149. package/integration/hubspot-test/test-dataplane-down-helpers.js +0 -249
  150. package/integration/hubspot-test/test-dataplane-down-tests.js +0 -386
  151. package/integration/hubspot-test/test-dataplane-down.js +0 -209
  152. package/integration/hubspot-test/test.js +0 -1588
  153. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +0 -16
  154. package/integration/hubspot-test/wizard-hubspot-platform.yaml +0 -8
  155. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +0 -23
  156. package/integration/roundtrip-test-local/README.md +0 -143
  157. package/integration/roundtrip-test-local/application.yaml +0 -13
  158. package/integration/roundtrip-test-local/env.template +0 -15
  159. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +0 -14
  160. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +0 -61
  161. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +0 -25
  162. package/integration/roundtrip-test-local2/README.md +0 -143
  163. package/integration/roundtrip-test-local2/application.yaml +0 -13
  164. package/integration/roundtrip-test-local2/env.template +0 -15
  165. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +0 -31
  166. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +0 -86
  167. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +0 -25
  168. package/integration/test/wizard.yaml +0 -8
  169. package/jest.config.coverage.js +0 -37
  170. package/jest.config.default.js +0 -11
  171. package/jest.config.integration.fixtures.js +0 -22
  172. package/jest.config.integration.js +0 -33
  173. package/jest.config.isolated.js +0 -11
  174. package/jest.config.manual.js +0 -30
  175. package/jest.global-live-fabrix-hooks.js +0 -36
  176. package/jest.isolated-projects.js +0 -283
  177. package/jest.projects.js +0 -79
  178. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +0 -205
  179. package/packages/builder-api/README.md +0 -120
  180. package/packages/builder-api/package.json +0 -30
  181. package/packages/builder-api/src/app.module.ts +0 -7
  182. package/packages/builder-api/src/auth/auth.module.ts +0 -5
  183. package/packages/builder-api/src/external-system/external-system.module.ts +0 -5
  184. package/packages/builder-api/src/main.ts +0 -16
  185. package/packages/builder-api/tsconfig.json +0 -15
  186. package/scripts/check-builder-api-architecture-grep.js +0 -79
  187. package/scripts/check-datasource-test-run-schema-sync.js +0 -34
  188. package/scripts/ci-fix.sh +0 -19
  189. package/scripts/ci-simulate.sh +0 -19
  190. package/scripts/diagnose-cli.js +0 -150
  191. package/scripts/docker_image_content_gate.js +0 -47
  192. package/scripts/docker_image_content_gate.sh +0 -5
  193. package/scripts/export-builder-api-evidence.js +0 -262
  194. package/scripts/install-local.js +0 -490
  195. package/scripts/lib/builder-api-docker-staging-copy.js +0 -60
  196. package/scripts/lib/builder-api-docker-staging-core.js +0 -183
  197. package/scripts/lib/docker-image-content-gate-core.js +0 -166
  198. package/scripts/lib/plan-actor.js +0 -109
  199. package/scripts/lib/plan-config.js +0 -20
  200. package/scripts/lib/plan-gates.js +0 -154
  201. package/scripts/lib/plan-meta.js +0 -104
  202. package/scripts/lib/plan-parse.js +0 -162
  203. package/scripts/lib/plan-prevalidate-gates.js +0 -337
  204. package/scripts/lib/plan-run-core.js +0 -125
  205. package/scripts/lib/plan-run-report.js +0 -119
  206. package/scripts/plan-approve.js +0 -24
  207. package/scripts/plan-archive.js +0 -49
  208. package/scripts/plan-run.js +0 -13
  209. package/scripts/plan-set-actor.js +0 -21
  210. package/scripts/plan-validate.js +0 -80
  211. package/scripts/pnpm/af.mjs +0 -125
  212. package/scripts/pnpm/check-quiet.mjs +0 -32
  213. package/scripts/pnpm/dev-install.mjs +0 -29
  214. package/scripts/pnpm/dev-reload.mjs +0 -18
  215. package/scripts/pnpm/help.mjs +0 -55
  216. package/scripts/pnpm/lib/dev-reload-core.mjs +0 -189
  217. package/scripts/pnpm/lib/dev-reload-log-capture.mjs +0 -67
  218. package/scripts/pnpm/lib/dev-reload-log-utils.mjs +0 -32
  219. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +0 -124
  220. package/scripts/pnpm/lib/resolve-app-url.mjs +0 -183
  221. package/scripts/pnpm/lib/spinner.mjs +0 -42
  222. package/scripts/pnpm-global-remove.js +0 -48
  223. package/scripts/stage_builder_api_for_docker.js +0 -65
  224. package/scripts/sync-builder-api-template.js +0 -44
  225. 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
- }