@aifabrix/builder 2.54.1 → 2.55.2

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 (253) hide show
  1. package/README.md +8 -0
  2. package/lib/agent-kit/apply-ops.js +82 -0
  3. package/lib/agent-kit/checksums.js +28 -0
  4. package/lib/agent-kit/classify.js +113 -0
  5. package/lib/agent-kit/claude-shim.js +61 -0
  6. package/lib/agent-kit/constants.js +55 -0
  7. package/lib/agent-kit/file-list.js +103 -0
  8. package/lib/agent-kit/host-approve.js +172 -0
  9. package/lib/agent-kit/hosting/github.js +63 -0
  10. package/lib/agent-kit/hosts.js +71 -0
  11. package/lib/agent-kit/init.js +319 -0
  12. package/lib/agent-kit/kit-root.js +17 -0
  13. package/lib/agent-kit/managed-section.js +79 -0
  14. package/lib/agent-kit/package-load.js +45 -0
  15. package/lib/agent-kit/plan-ops.js +69 -0
  16. package/lib/agent-kit/remote-prompts.js +272 -0
  17. package/lib/agent-kit/render-host-markdown.js +40 -0
  18. package/lib/agent-kit/repo-guard.js +47 -0
  19. package/lib/agent-kit/report.js +107 -0
  20. package/lib/agent-kit/run.js +82 -0
  21. package/lib/agent-kit/scm/git.js +122 -0
  22. package/lib/agent-kit/start.js +310 -0
  23. package/lib/agent-kit/state-file.js +83 -0
  24. package/lib/agent-kit/status.js +50 -0
  25. package/lib/agent-kit/v1-detect.js +81 -0
  26. package/lib/agent-kit/validate-kit.js +87 -0
  27. package/lib/agent-kit/workspace-name.js +40 -0
  28. package/lib/agent-kit/workspace-skeleton.js +103 -0
  29. package/lib/api/role-assistant-test-pack.api.js +86 -0
  30. package/lib/api/work-search.api.js +31 -4
  31. package/lib/api/work.api.js +19 -12
  32. package/lib/app/deploy.js +7 -4
  33. package/lib/app/list-external-systems.js +2 -1
  34. package/lib/app/offer-create-from-template.js +184 -0
  35. package/lib/app/show-display.js +33 -6
  36. package/lib/app/show.js +25 -2
  37. package/lib/channels/add-channel.js +1 -0
  38. package/lib/channels/approval-guides/claude.js +2 -2
  39. package/lib/channels/channel-add-failure.js +1 -0
  40. package/lib/channels/channel-add-remaining.js +15 -9
  41. package/lib/channels/channel-definition-status.js +115 -0
  42. package/lib/channels/channel-description.js +2 -1
  43. package/lib/channels/channel-install-envelope.js +4 -2
  44. package/lib/channels/channel-integration-generator.js +2 -0
  45. package/lib/channels/channel-runtime-resolver.js +14 -0
  46. package/lib/channels/channel-targets.js +31 -5
  47. package/lib/channels/claude-setup-values.js +4 -1
  48. package/lib/channels/slack-channel-add.js +59 -6
  49. package/lib/channels/slack-mcp-callback.js +51 -0
  50. package/lib/channels/slack-mcp-runtime.js +156 -0
  51. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  52. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  53. package/lib/cli/index.js +4 -0
  54. package/lib/cli/setup-app.js +8 -1
  55. package/lib/cli/setup-auth.js +11 -2
  56. package/lib/cli/setup-certification-commands.js +2 -2
  57. package/lib/cli/setup-certification.help.js +3 -1
  58. package/lib/commands/agent-kit-help.js +149 -0
  59. package/lib/commands/agent-kit.js +417 -0
  60. package/lib/commands/auth-status-user.js +99 -0
  61. package/lib/commands/auth-status.js +14 -4
  62. package/lib/commands/channel-add-help.js +5 -5
  63. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  64. package/lib/commands/datasource-load-export-cli.js +7 -2
  65. package/lib/commands/datasource-test-trust-cli.js +5 -1
  66. package/lib/commands/governance-command-action.js +5 -4
  67. package/lib/commands/identity-membership-role-cache.js +6 -1
  68. package/lib/commands/lifecycle-command-action.js +16 -15
  69. package/lib/commands/login-credentials.js +8 -3
  70. package/lib/commands/login-device.js +14 -5
  71. package/lib/commands/login.js +14 -1
  72. package/lib/commands/logout.js +50 -15
  73. package/lib/commands/repair-auth-apply.js +1 -1
  74. package/lib/commands/repair-datasource-key-skip.js +39 -0
  75. package/lib/commands/repair-datasource-keys.js +24 -12
  76. package/lib/commands/repair-rbac.js +16 -10
  77. package/lib/commands/role-assistant-copy-output.js +61 -0
  78. package/lib/commands/role-assistant-copy.js +155 -0
  79. package/lib/commands/role-assistant-test-output.js +8 -9
  80. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  81. package/lib/commands/role-assistant.js +140 -59
  82. package/lib/commands/search.js +143 -0
  83. package/lib/commands/test-trust-external.js +7 -5
  84. package/lib/commands/upload-sidecar-followups.js +4 -1
  85. package/lib/commands/verify-governance-command-action.js +9 -7
  86. package/lib/commands/verify-operations-command-action.js +9 -8
  87. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  88. package/lib/commands/verify-operations-step-plan.js +6 -33
  89. package/lib/commands/verify-operations-steps.js +3 -1
  90. package/lib/commands/verify-trust-command-action.js +6 -5
  91. package/lib/datasource/capability/relate-operations.js +28 -10
  92. package/lib/datasource/capability/run-capability-relate.js +48 -25
  93. package/lib/datasource/datasource-identifier.js +13 -5
  94. package/lib/deployment/deployer-poll-run.js +117 -35
  95. package/lib/external-system/deploy-dataplane-published.js +70 -0
  96. package/lib/external-system/deploy.js +12 -2
  97. package/lib/external-system/download-role-assistant.js +41 -15
  98. package/lib/external-system/download.js +8 -3
  99. package/lib/generator/helpers.js +12 -1
  100. package/lib/generator/wizard-datasource-key.js +54 -0
  101. package/lib/generator/wizard.js +55 -54
  102. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  103. package/lib/integration-definition/apply-relationships.js +195 -0
  104. package/lib/integration-definition/apply-static-enterprise.js +232 -0
  105. package/lib/integration-definition/apply-system-file.js +71 -0
  106. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  107. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  108. package/lib/integration-definition/apply.js +78 -59
  109. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  110. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  111. package/lib/lifecycle/role-assistant-governance.js +56 -21
  112. package/lib/programmatic/capability-registry.js +1 -0
  113. package/lib/programmatic/index.js +2 -0
  114. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  115. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  116. package/lib/programmatic/openapi-component-schemas.js +1 -0
  117. package/lib/programmatic/openapi-descriptions.js +9 -3
  118. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  119. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  120. package/lib/programmatic/openapi-request-schemas.js +5 -0
  121. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  122. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  123. package/lib/programmatic/openapi-spec.js +1 -0
  124. package/lib/programmatic/request-timeout.js +3 -0
  125. package/lib/programmatic/route-handler-map.js +7 -0
  126. package/lib/programmatic/run-channel-catalog.js +50 -8
  127. package/lib/programmatic/run-channel-install-options.js +5 -1
  128. package/lib/programmatic/run-download.js +41 -0
  129. package/lib/protection/auth-context.js +3 -1
  130. package/lib/protection/protection-display.js +3 -2
  131. package/lib/protection/report-exit.js +50 -0
  132. package/lib/protection/run-commands.js +2 -0
  133. package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
  134. package/lib/role-assistant/knowledge-sync.js +9 -126
  135. package/lib/role-assistant/package-validate.js +175 -0
  136. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  137. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  138. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  139. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  140. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  141. package/lib/role-assistant/test-case-copy.js +119 -0
  142. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  143. package/lib/role-assistant/test-case-validate.js +88 -0
  144. package/lib/role-assistant/test-cases-bid.js +117 -4
  145. package/lib/role-assistant/test-cases-evaluate.js +149 -31
  146. package/lib/role-assistant/test-cases-evidence.js +44 -5
  147. package/lib/role-assistant/test-cases-honesty.js +155 -6
  148. package/lib/role-assistant/test-cases-lifecycle.js +297 -0
  149. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  150. package/lib/role-assistant/test-cases-search.js +20 -2
  151. package/lib/role-assistant/test-cases.js +34 -168
  152. package/lib/role-assistant/test-pack-map.js +180 -0
  153. package/lib/role-assistant/test-pack-sync.js +187 -0
  154. package/lib/role-assistant/test-runner-debug.js +54 -0
  155. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  156. package/lib/role-assistant/test-runner-search.js +20 -5
  157. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  158. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  159. package/lib/role-assistant/test-runner-workhub.js +45 -21
  160. package/lib/role-assistant/test-runner.js +53 -10
  161. package/lib/schema/external-datasource.schema.json +36 -5
  162. package/lib/schema/external-system.schema.json +20 -2
  163. package/lib/schema/infra-parameter.schema.json +51 -3
  164. package/lib/schema/infra.parameter.yaml +476 -40
  165. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  166. package/lib/schema/type/document-storage.json +1 -1
  167. package/lib/search/dataplane-context.js +23 -0
  168. package/lib/search/search-args.js +211 -0
  169. package/lib/search/search-display.js +238 -0
  170. package/lib/search/search-errors.js +50 -0
  171. package/lib/search/search-prompts.js +119 -0
  172. package/lib/search/search-roles.js +85 -0
  173. package/lib/search/search-run.js +241 -0
  174. package/lib/search/search-skills.js +198 -0
  175. package/lib/utils/api-required-permissions.js +271 -0
  176. package/lib/utils/api.js +16 -13
  177. package/lib/utils/authentication-slot-show.js +153 -0
  178. package/lib/utils/cli-json-exit.js +41 -0
  179. package/lib/utils/cli-utils.js +23 -5
  180. package/lib/utils/controller-url.js +78 -4
  181. package/lib/utils/credential-display.js +43 -1
  182. package/lib/utils/deployment-errors.js +15 -0
  183. package/lib/utils/deployment-validation-helpers.js +4 -1
  184. package/lib/utils/device-code.js +31 -10
  185. package/lib/utils/error-formatters/error-parser.js +16 -1
  186. package/lib/utils/error-formatters/permission-errors.js +19 -5
  187. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  188. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  189. package/lib/utils/external-system-validators.js +2 -1
  190. package/lib/utils/help-builder.js +3 -1
  191. package/lib/utils/keycloak-realm-ready.js +5 -2
  192. package/lib/utils/logger.js +1 -0
  193. package/lib/utils/paths.js +26 -5
  194. package/lib/utils/schema-loader.js +37 -0
  195. package/lib/utils/secrets-helpers.js +2 -2
  196. package/lib/utils/write-stream-and-wait.js +63 -0
  197. package/lib/validation/validate-external-file.js +5 -1
  198. package/lib/validation/validate.js +3 -2
  199. package/package.json +20 -6
  200. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  201. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  202. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  203. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  204. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  205. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  206. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  207. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  208. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  209. package/templates/agent-kit/agent-kit.yaml +45 -0
  210. package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
  211. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  212. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  213. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  214. package/templates/agent-kit/parity-inventory.yaml +188 -0
  215. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
  216. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  217. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  218. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  219. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  220. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
  221. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  222. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  223. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  224. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
  225. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  226. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  227. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  228. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
  229. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  230. package/templates/agent-kit/skills/shared/hosts.md +65 -0
  231. package/templates/agent-kit/skills/shared/login.md +16 -0
  232. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  233. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  234. package/templates/agent-kit/workspace/README.md +89 -0
  235. package/templates/applications/builder-api/Dockerfile +3 -1
  236. package/templates/applications/dataplane/application.yaml +2 -3
  237. package/templates/applications/dataplane/env.template +5 -5
  238. package/templates/applications/dataplane/rbac.yaml +28 -0
  239. package/templates/applications/keycloak/env.template +3 -3
  240. package/templates/applications/miso-controller/application.yaml +21 -2
  241. package/templates/applications/miso-controller/env.template +96 -25
  242. package/templates/applications/miso-controller/rbac.yaml +5 -0
  243. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  244. package/templates/channels/claude/channel.json +1 -1
  245. package/templates/channels/claude/install.template.md.hbs +1 -0
  246. package/templates/channels/claude/integration/claude-system.json +1 -1
  247. package/templates/channels/slack/channel.json +11 -0
  248. package/templates/channels/slack/install.template.md.hbs +9 -0
  249. package/templates/channels/slack/integration/slack-system.json +11 -0
  250. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  251. package/templates/channels/teams-bot/channel.json +1 -0
  252. package/templates/python/docker-compose.hbs +14 -0
  253. package/templates/typescript/docker-compose.hbs +14 -0
@@ -6,6 +6,7 @@
6
6
 
7
7
  const logger = require('../utils/logger');
8
8
  const { handleCommandError } = require('../utils/cli-utils');
9
+ const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
9
10
  const { runGovernanceGenerateForExternalSystem } = require('./governance-generate-external');
10
11
  const { runGovernanceVerifyForExternalSystem } = require('./governance-verify-external');
11
12
  const { runGovernanceUploadForExternalSystem } = require('./governance-upload-external');
@@ -75,12 +76,12 @@ async function governanceVerifyHandler(systemKey, options, cmd) {
75
76
  try {
76
77
  const opts = normalizeGovernanceOptions(options, cmd);
77
78
  const result = await runGovernanceVerifyForExternalSystem(systemKey, opts);
79
+ const exitCode = exitCodeFromVerdict(result.verdict);
78
80
  if (opts.json) {
79
- logger.log(JSON.stringify(buildJsonEnvelope(result, opts.verbose), null, 2));
80
- } else {
81
- displayVerifyGovernanceTTY(result.systemKey, result, { details: opts.verbose });
81
+ await writeJsonEnvelopeAndExit(buildJsonEnvelope(result, opts.verbose), exitCode);
82
+ return;
82
83
  }
83
- const exitCode = exitCodeFromVerdict(result.verdict);
84
+ displayVerifyGovernanceTTY(result.systemKey, result, { details: opts.verbose });
84
85
  if (exitCode !== 0) {
85
86
  process.exit(exitCode);
86
87
  }
@@ -35,7 +35,12 @@ const ROLE_LIST_COLUMNS = [
35
35
  label: 'Groups',
36
36
  width: 44,
37
37
  value: (r) => {
38
- const groups = Array.isArray(r.groups) ? r.groups.filter(Boolean) : [];
38
+ const groups = Array.isArray(r.groups)
39
+ ? r.groups.filter(Boolean)
40
+ : (r.mappings || [])
41
+ .filter((mapping) => mapping?.isActive !== false)
42
+ .map((mapping) => mapping?.groupName)
43
+ .filter(Boolean);
39
44
  return groups.length > 0 ? groups.join(', ') : '—';
40
45
  }
41
46
  }
@@ -6,8 +6,8 @@
6
6
 
7
7
  'use strict';
8
8
 
9
- const logger = require('../utils/logger');
10
9
  const { handleCommandError } = require('../utils/cli-utils');
10
+ const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
11
11
  const { cliOptsSkipPublish } = require('../utils/cli-sync-options');
12
12
  const { runLifecycleForExternalSystem } = require('./lifecycle-external');
13
13
  const { displayLifecycleReportTTY } = require('../lifecycle/report-display');
@@ -65,6 +65,12 @@ async function runLifecycleCommandAction(appName, options, cmd) {
65
65
  const result = await runLifecycleForExternalSystem(appName, opts);
66
66
  const report = result.report;
67
67
 
68
+ const pillarVerdict = lifecycleVerdictFromReport(report);
69
+ const exitCode =
70
+ pillarVerdict === VERDICT.VERIFIED
71
+ ? 0
72
+ : exitCodeFromVerdict(pillarVerdict, { partial: pillarVerdict === VERDICT.NOT_VERIFIED });
73
+
68
74
  if (opts.json) {
69
75
  const envelope = {
70
76
  systemKey: result.systemKey,
@@ -82,22 +88,17 @@ async function runLifecycleCommandAction(appName, options, cmd) {
82
88
  if (opts.verbose) {
83
89
  envelope.datasources = report.datasources;
84
90
  }
85
- logger.log(JSON.stringify(envelope, null, 2));
86
- } else {
87
- displayLifecycleReportTTY(result.systemKey, report, {
88
- details: opts.verbose,
89
- openApiPublished: result.openApiPublished === true,
90
- certificationProgress: result.certificationProgress ?? null,
91
- dataplaneUrl: result.dataplaneUrl ?? null,
92
- dataplaneUiBaseUrl: result.dataplaneUiBaseUrl ?? null
93
- });
91
+ await writeJsonEnvelopeAndExit(envelope, exitCode);
92
+ return;
94
93
  }
95
94
 
96
- const pillarVerdict = lifecycleVerdictFromReport(report);
97
- const exitCode =
98
- pillarVerdict === VERDICT.VERIFIED
99
- ? 0
100
- : exitCodeFromVerdict(pillarVerdict, { partial: pillarVerdict === VERDICT.NOT_VERIFIED });
95
+ displayLifecycleReportTTY(result.systemKey, report, {
96
+ details: opts.verbose,
97
+ openApiPublished: result.openApiPublished === true,
98
+ certificationProgress: result.certificationProgress ?? null,
99
+ dataplaneUrl: result.dataplaneUrl ?? null,
100
+ dataplaneUiBaseUrl: result.dataplaneUiBaseUrl ?? null
101
+ });
101
102
  if (exitCode !== 0) {
102
103
  process.exit(exitCode);
103
104
  }
@@ -15,6 +15,7 @@ const { getToken } = require('../api/auth.api');
15
15
  const { formatApiError } = require('../utils/api-error-handler');
16
16
  const { loadClientCredentials } = require('../utils/token-manager');
17
17
  const logger = require('../utils/logger');
18
+ const { writeStreamAndWait } = require('../utils/write-stream-and-wait');
18
19
 
19
20
  /**
20
21
  * Tries to load credentials from secrets file
@@ -136,7 +137,7 @@ function handleLoginError(response, appName) {
136
137
  * @param {Object} response - API response
137
138
  * @returns {Object} Token data object
138
139
  */
139
- function extractTokenData(response) {
140
+ async function extractTokenData(response) {
140
141
  // OpenAPI spec response: { success: boolean, token: string, expiresIn: number, expiresAt: string, ... }
141
142
  // Handle both flat and nested response structures (some APIs wrap in data field)
142
143
  // If response.data exists, use it; otherwise use response directly
@@ -146,7 +147,10 @@ function extractTokenData(response) {
146
147
  if (!responseData || !responseData.token) {
147
148
  logger.error(formatBlockingError('Invalid response: missing token'));
148
149
  if (responseData) {
149
- logger.error(chalk.gray(`Response structure: ${JSON.stringify(responseData, null, 2)}`));
150
+ await writeStreamAndWait(
151
+ process.stderr,
152
+ `${chalk.gray(`Response structure: ${JSON.stringify(responseData, null, 2)}`)}\n`
153
+ );
150
154
  }
151
155
  process.exit(1);
152
156
  }
@@ -190,9 +194,10 @@ async function handleCredentialsLogin(controllerUrl, appName, clientId, clientSe
190
194
 
191
195
  if (!response.success) {
192
196
  handleLoginError(response, appName);
197
+ return;
193
198
  }
194
199
 
195
- const responseData = extractTokenData(response);
200
+ const responseData = await extractTokenData(response);
196
201
  const expiresAt = calculateTokenExpiration(responseData);
197
202
 
198
203
  return {
@@ -15,6 +15,7 @@ const ora = require('ora');
15
15
  const { setCurrentEnvironment, saveDeviceToken, setControllerUrl } = require('../core/config');
16
16
  const { initiateDeviceCodeFlow } = require('../api/auth.api');
17
17
  const { pollDeviceCodeToken, displayDeviceCodeInfo } = require('../utils/api');
18
+ const { resolveDeviceVisitUrl } = require('../utils/device-code');
18
19
  const logger = require('../utils/logger');
19
20
  const { formatSuccessLine } = require('../utils/cli-layout-chalk');
20
21
 
@@ -42,6 +43,10 @@ async function getEnvironmentKey(environment) {
42
43
  return envKey;
43
44
  }
44
45
 
46
+ if (!process.stdin || process.stdin.isTTY === false) {
47
+ return 'dev';
48
+ }
49
+
45
50
  const envPrompt = await inquirer.prompt([{
46
51
  type: 'input',
47
52
  name: 'environment',
@@ -255,10 +260,8 @@ function logDeviceFlowError(deviceError, opts) {
255
260
  }
256
261
 
257
262
  function printCompactDeviceUrl(deviceCodeResponse) {
258
- const visitUrl =
259
- deviceCodeResponse.verification_uri_complete ||
260
- `${deviceCodeResponse.verification_uri}?user_code=${deviceCodeResponse.user_code}`;
261
- logger.log('\nOpen browser to authenticate:');
263
+ const visitUrl = resolveDeviceVisitUrl(deviceCodeResponse);
264
+ logger.log('\nOpen this URL to authenticate:');
262
265
  logger.log(chalk.yellow(`${visitUrl}\n`));
263
266
  }
264
267
 
@@ -289,7 +292,13 @@ async function handleDeviceCodeLogin(controllerUrl, environment, online, scope,
289
292
  const deviceCodeResponse = convertDeviceCodeResponse(apiResponse);
290
293
 
291
294
  if (!opts.compact) {
292
- displayDeviceCodeInfo(deviceCodeResponse.user_code, deviceCodeResponse.verification_uri, logger, chalk);
295
+ displayDeviceCodeInfo(
296
+ deviceCodeResponse.user_code,
297
+ deviceCodeResponse.verification_uri,
298
+ logger,
299
+ chalk,
300
+ deviceCodeResponse.verification_uri_complete
301
+ );
293
302
  } else {
294
303
  printCompactDeviceUrl(deviceCodeResponse);
295
304
  }
@@ -16,9 +16,13 @@ const { setCurrentEnvironment, saveClientToken, setControllerUrl } = require('..
16
16
  const logger = require('../utils/logger');
17
17
  const { handleCredentialsLogin } = require('./login-credentials');
18
18
  const { handleDeviceCodeLogin } = require('./login-device');
19
- const { resolveControllerUrl } = require('../utils/controller-url');
19
+ const { resolveControllerUrl, discoverControllerBaseUrl } = require('../utils/controller-url');
20
20
  const { tryRefreshDataplaneVersionAfterLogin } = require('./auth-status-dataplane-version');
21
21
 
22
+ function isInteractiveStdin() {
23
+ return Boolean(process.stdin && process.stdin.isTTY);
24
+ }
25
+
22
26
  /**
23
27
  * Determine and validate authentication method
24
28
  * @async
@@ -34,6 +38,10 @@ async function determineAuthMethod(method) {
34
38
  return method;
35
39
  }
36
40
 
41
+ if (!isInteractiveStdin()) {
42
+ return 'device';
43
+ }
44
+
37
45
  const authMethod = await inquirer.prompt([{
38
46
  type: 'list',
39
47
  name: 'method',
@@ -86,6 +94,8 @@ async function normalizeControllerUrl(options) {
86
94
  controllerUrl = await resolveControllerUrl();
87
95
  }
88
96
  controllerUrl = String(controllerUrl).trim().replace(/\/+$/, '');
97
+ const inputUrl = controllerUrl;
98
+ controllerUrl = await discoverControllerBaseUrl(controllerUrl);
89
99
  if (!options.skipControllerUrlPersist) {
90
100
  const persistUrl = options.tokenControllerUrl
91
101
  ? String(options.tokenControllerUrl).trim().replace(/\/+$/, '')
@@ -93,6 +103,9 @@ async function normalizeControllerUrl(options) {
93
103
  await setControllerUrl(persistUrl);
94
104
  }
95
105
  if (!options.compact) {
106
+ if (controllerUrl !== inputUrl) {
107
+ logger.log(chalk.gray(`Using controller ${controllerUrl}`));
108
+ }
96
109
  logger.log(chalk.gray(`Controller URL: ${controllerUrl}`));
97
110
  }
98
111
  return controllerUrl;
@@ -132,11 +132,54 @@ async function clearClientTokens(options) {
132
132
  return 0;
133
133
  }
134
134
 
135
+ function normalizeTokenType(value) {
136
+ const type = value && String(value).toLowerCase();
137
+ if (type && type !== 'device' && type !== 'client') {
138
+ throw new Error('--type must be either "device" or "client"');
139
+ }
140
+ return type;
141
+ }
142
+
143
+ function validateLogoutOptionCombination(options, type) {
144
+ if (options.app && !options.environment) {
145
+ throw new Error('--app requires --environment option');
146
+ }
147
+ if (type === 'client' && options.controller) {
148
+ throw new Error('--controller can only be used with --type device');
149
+ }
150
+ if (type === 'device' && (options.environment || options.app)) {
151
+ throw new Error('--environment and --app can only be used with --type client');
152
+ }
153
+ }
154
+
155
+ function validateLogoutSelectors(options) {
156
+ if ('controller' in options && options.controller !== undefined && options.controller !== null) {
157
+ validateControllerUrl(options.controller);
158
+ }
159
+ if ('environment' in options && options.environment !== undefined && options.environment !== null) {
160
+ validateEnvironmentKey(options.environment);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Validate logout option combinations and normalize the token type.
166
+ * @param {Object} options - Raw logout options
167
+ * @returns {Object} Validated options with a normalized type
168
+ * @throws {Error} If an option or option combination is invalid
169
+ */
170
+ function normalizeLogoutOptions(options) {
171
+ const type = normalizeTokenType(options.type);
172
+ validateLogoutOptionCombination(options, type);
173
+ validateLogoutSelectors(options);
174
+ return { ...options, type };
175
+ }
176
+
135
177
  /**
136
178
  * Handle logout command action
137
179
  * @async
138
180
  * @function handleLogout
139
181
  * @param {Object} options - Logout options
182
+ * @param {string} [options.type] - Token type to clear (device or client)
140
183
  * @param {string} [options.controller] - Clear device tokens for specific controller
141
184
  * @param {string} [options.environment] - Clear client tokens for specific environment
142
185
  * @param {string} [options.app] - Clear client tokens for specific app (requires --environment)
@@ -147,24 +190,17 @@ async function handleLogout(options) {
147
190
  const configPath = CONFIG_FILE;
148
191
  logger.log(chalk.blue('\n🔓 Clearing authentication tokens...\n'));
149
192
 
150
- // Validate options
151
- if (options.app && !options.environment) {
152
- throw new Error('--app requires --environment option');
153
- }
154
-
155
- if ('controller' in options && options.controller !== undefined && options.controller !== null) {
156
- validateControllerUrl(options.controller);
157
- }
158
-
159
- if ('environment' in options && options.environment !== undefined && options.environment !== null) {
160
- validateEnvironmentKey(options.environment);
161
- }
193
+ const normalizedOptions = normalizeLogoutOptions(options);
162
194
 
163
195
  // Clear device tokens
164
- const deviceTokensCleared = await clearDeviceTokens(options);
196
+ const deviceTokensCleared = normalizedOptions.type === 'client'
197
+ ? 0
198
+ : await clearDeviceTokens(normalizedOptions);
165
199
 
166
200
  // Clear client tokens
167
- const clientTokensCleared = await clearClientTokens(options);
201
+ const clientTokensCleared = normalizedOptions.type === 'device'
202
+ ? 0
203
+ : await clearClientTokens(normalizedOptions);
168
204
 
169
205
  // Summary
170
206
  const totalCleared = deviceTokensCleared + clientTokensCleared;
@@ -178,4 +214,3 @@ async function handleLogout(options) {
178
214
  }
179
215
 
180
216
  module.exports = { handleLogout };
181
-
@@ -117,7 +117,7 @@ function repairAuthChangeLabel(authOption) {
117
117
  }
118
118
 
119
119
  const MISSING_TEST_ENDPOINT_WARNING =
120
- 'authentication.variables.testEndpoint is missing for apikey/bearerToken auth — add a GET URL for credential/E2E tests (e.g. /crm/v3/objects/contacts?limit=1, or a path resolved against baseUrl).';
120
+ 'authentication.variables.testEndpoint is missing for apikey/bearerToken auth — add a GET URL for credential/E2E tests (use the OpenAPI minimum for required page-size query params, or a path resolved against baseUrl).';
121
121
 
122
122
  /**
123
123
  * Warn when apikey auth (including bearerToken preset) has no testEndpoint.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Skip repair key prefix when a datasource key is already unique scoped kebab-case.
3
+ *
4
+ * @fileoverview Unique kebab datasource keys stay as-is during repair
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const SCOPED_KEBAB = /^[a-z0-9]+(-[a-z0-9]+)+$/;
10
+
11
+ /**
12
+ * @param {string} key
13
+ * @returns {boolean}
14
+ */
15
+ function isScopedKebabKey(key) {
16
+ return SCOPED_KEBAB.test(String(key || '').trim());
17
+ }
18
+
19
+ /**
20
+ * True when key already contains a hyphen and appears once in the package.
21
+ * Single-token keys (persons) must still be prefixed with the system key.
22
+ *
23
+ * @param {string} key
24
+ * @param {string[]} allKeys
25
+ * @returns {boolean}
26
+ */
27
+ function shouldSkipSystemKeyPrefix(key, allKeys) {
28
+ const trimmed = String(key || '').trim();
29
+ if (!isScopedKebabKey(trimmed) || trimmed.endsWith('-datasource')) {
30
+ return false;
31
+ }
32
+ const hits = (allKeys || []).filter((item) => String(item || '').trim() === trimmed);
33
+ return hits.length === 1;
34
+ }
35
+
36
+ module.exports = {
37
+ isScopedKebabKey,
38
+ shouldSkipSystemKeyPrefix
39
+ };
@@ -17,6 +17,7 @@ const fs = require('fs');
17
17
  const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
18
18
  const { backupIntegrationFile } = require('../utils/integration-file-backup');
19
19
  const { trackRepairWrite } = require('./repair-changed-files');
20
+ const { shouldSkipSystemKeyPrefix } = require('./repair-datasource-key-skip');
20
21
 
21
22
  /**
22
23
  * Returns suffix from a canonical-format filename: <systemKey>-datasource-<suffix>.<ext>.
@@ -53,6 +54,9 @@ function isKeyAlreadyCanonical(key, systemKey, fileName) {
53
54
  return true;
54
55
  }
55
56
  }
57
+ if (shouldSkipSystemKeyPrefix(key, [key])) {
58
+ return true;
59
+ }
56
60
  if (!key.startsWith(systemKey + '-')) return false;
57
61
  const suffix = key.slice(systemKey.length + 1);
58
62
  if (!suffix) return false;
@@ -126,6 +130,7 @@ function normalizeDatasourceKeysAndFilenames(appPath, datasourceFiles, systemKey
126
130
 
127
131
  const slugCounts = new Map();
128
132
  const fileInfos = [];
133
+ const packageKeys = [];
129
134
 
130
135
  for (const fileName of datasourceFiles) {
131
136
  const filePath = path.join(appPath, fileName);
@@ -138,23 +143,30 @@ function normalizeDatasourceKeysAndFilenames(appPath, datasourceFiles, systemKey
138
143
  continue;
139
144
  }
140
145
  const key = (parsed && typeof parsed.key === 'string' && parsed.key.trim()) ? parsed.key.trim() : null;
141
- if (isKeyAlreadyCanonical(key, systemKey, fileName) && isFilenameAlreadyCanonical(fileName, systemKey)) {
142
- fileInfos.push({ fileName, key, skip: true });
146
+ if (key) packageKeys.push(key);
147
+ fileInfos.push({ fileName, parsed, key, skip: false, slug: null, canonicalKey: null });
148
+ }
149
+
150
+ for (const info of fileInfos) {
151
+ if (info.skip) continue;
152
+ const filenameCanonical = isFilenameAlreadyCanonical(info.fileName, systemKey);
153
+ const keyCanonical = isKeyAlreadyCanonical(info.key, systemKey, info.fileName);
154
+ const skipPrefix =
155
+ shouldSkipSystemKeyPrefix(info.key, packageKeys) &&
156
+ !String(info.key || '').endsWith('-datasource');
157
+ if ((keyCanonical || skipPrefix) && filenameCanonical) {
158
+ info.skip = true;
159
+ continue;
160
+ }
161
+ if (skipPrefix) {
162
+ info.canonicalKey = info.key;
143
163
  continue;
144
164
  }
145
- const slug = slugFromKey(key || fileName, systemKey);
146
- fileInfos.push({
147
- fileName,
148
- parsed,
149
- key,
150
- slug,
151
- canonicalKey: null,
152
- skip: false
153
- });
165
+ info.slug = slugFromKey(info.key || info.fileName, systemKey);
154
166
  }
155
167
 
156
168
  for (const info of fileInfos) {
157
- if (info.skip) continue;
169
+ if (info.skip || info.canonicalKey) continue;
158
170
  const slug = info.slug;
159
171
  const n = (slugCounts.get(slug) || 0) + 1;
160
172
  slugCounts.set(slug, n);
@@ -29,6 +29,7 @@ const {
29
29
  ensureOrchestrationResourceType
30
30
  } = require('./repair-rbac-platform-preset');
31
31
  const { trackRepairWrite } = require('./repair-changed-files');
32
+ const { listCapabilityKeys } = require('../datasource/capability/capability-manifest');
32
33
  const { ensureDefaultRoles } = require('./repair-rbac-catalog-defaults');
33
34
 
34
35
  const DEFAULT_CAPABILITIES = ['list', 'get', 'create', 'update', 'delete'];
@@ -47,24 +48,29 @@ function _normalizeOperationKey(opKey) {
47
48
  return cleaned.replace(/-+/g, '-').replace(/^-+|-+$/g, '');
48
49
  }
49
50
 
51
+ function defaultCapabilitiesForDatasource(parsed) {
52
+ if (String(parsed?.storageType || '') === 'view') {
53
+ return ['list', 'get'];
54
+ }
55
+ return [...DEFAULT_CAPABILITIES];
56
+ }
57
+
50
58
  /**
51
- * Resolves capabilities from datasource (array or legacy object).
59
+ * Resolves capabilities from datasource (array, COM objects, or legacy map).
52
60
  * @param {Object} parsed - Parsed datasource
53
61
  * @returns {string[]}
54
62
  */
55
63
  function getCapabilitiesFromDatasource(parsed) {
56
- const cap = parsed?.capabilities;
57
- if (Array.isArray(cap)) {
58
- const arr = cap.filter(c => typeof c === 'string');
59
- if (arr.length > 0) return arr;
60
- return [...DEFAULT_CAPABILITIES];
64
+ const fromList = listCapabilityKeys(parsed?.capabilities);
65
+ if (fromList.length > 0) {
66
+ return fromList;
61
67
  }
62
- if (cap && typeof cap === 'object') {
63
- const keys = Object.keys(cap).filter(k => cap[k] === true);
68
+ const cap = parsed?.capabilities;
69
+ if (cap && typeof cap === 'object' && !Array.isArray(cap)) {
70
+ const keys = Object.keys(cap).filter((k) => cap[k] === true);
64
71
  if (keys.length > 0) return keys;
65
- return [...DEFAULT_CAPABILITIES];
66
72
  }
67
- return [...DEFAULT_CAPABILITIES];
73
+ return defaultCapabilitiesForDatasource(parsed);
68
74
  }
69
75
 
70
76
  /**
@@ -0,0 +1,61 @@
1
+ /**
2
+ * TTY and --json output for role-assistant copy.
3
+ *
4
+ * @fileoverview Success and blocking copy envelopes
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const logger = require('../utils/logger');
10
+ const { formatSuccessLine, formatBlockingError } = require('../utils/cli-layout-chalk');
11
+
12
+ function copyWarnings(result) {
13
+ return Array.isArray(result.warnings) ? result.warnings : [];
14
+ }
15
+
16
+ function buildCopyEnvelope(result) {
17
+ const envelope = {
18
+ success: true,
19
+ path: result.path,
20
+ caseId: result.caseId,
21
+ suiteId: result.suiteId,
22
+ processExecutionId: result.processExecutionId
23
+ };
24
+ const warnings = copyWarnings(result);
25
+ if (warnings.length > 0) {
26
+ envelope.warnings = warnings;
27
+ }
28
+ if (result.dryRun === true) {
29
+ envelope.dryRun = true;
30
+ envelope.yaml = result.yaml;
31
+ }
32
+ return envelope;
33
+ }
34
+
35
+ function emitCopySuccess(options, result) {
36
+ if (options && options.json) {
37
+ logger.log(JSON.stringify(buildCopyEnvelope(result), null, 2));
38
+ return;
39
+ }
40
+ if (result.dryRun === true) {
41
+ logger.log(result.yaml);
42
+ logger.log(formatSuccessLine(`Would write ${result.path}`));
43
+ return;
44
+ }
45
+ logger.log(formatSuccessLine(`Wrote ${result.path}`));
46
+ }
47
+
48
+ function emitCopyFailure(options, error) {
49
+ const message = error && error.message ? error.message : String(error);
50
+ if (options && options.json) {
51
+ logger.log(JSON.stringify({ success: false, error: message }));
52
+ return;
53
+ }
54
+ logger.error(formatBlockingError(message));
55
+ }
56
+
57
+ module.exports = {
58
+ buildCopyEnvelope,
59
+ emitCopySuccess,
60
+ emitCopyFailure
61
+ };