@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
@@ -0,0 +1,417 @@
1
+ /**
2
+ * `aifabrix agent-kit` command group.
3
+ * @fileoverview Install, init, status, update, validate, remote create, start
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const logger = require('../utils/logger');
9
+ const { handleCommandError } = require('../utils/cli-utils');
10
+ const { formatSuccessLine, formatBlockingError, formatWarningLine } = require('../utils/cli-layout-chalk');
11
+ const { runInstall, runUpdate } = require('../agent-kit/run');
12
+ const { runStart } = require('../agent-kit/start');
13
+ const path = require('path');
14
+ const { runInit, runRemoteCreate } = require('../agent-kit/init');
15
+ const { enrichInitHostOptions, enrichInitRemoteOptions, enrichRemoteOptions, enrichApproveAllOptions } = require('../agent-kit/remote-prompts');
16
+ const { hostLabel } = require('../agent-kit/hosts');
17
+ const { runStatus } = require('../agent-kit/status');
18
+ const { runValidate } = require('../agent-kit/validate-kit');
19
+ const { formatHumanStatus } = require('../agent-kit/report');
20
+ const { KIT_STATE } = require('../agent-kit/constants');
21
+ const { loadState } = require('../agent-kit/state-file');
22
+ const { resolveGitRoot } = require('../agent-kit/repo-guard');
23
+ const {
24
+ GROUP_HELP,
25
+ INSTALL_HELP,
26
+ STATUS_HELP,
27
+ UPDATE_HELP,
28
+ VALIDATE_HELP,
29
+ INIT_HELP,
30
+ REMOTE_GROUP_HELP,
31
+ REMOTE_HELP,
32
+ START_HELP,
33
+ HOST_OPTION_HELP,
34
+ APPROVE_ALL_OPTION_HELP
35
+ } = require('./agent-kit-help');
36
+
37
+ /**
38
+ * @param {object} json
39
+ * @param {boolean} asJson
40
+ * @returns {void}
41
+ */
42
+ function emitReport(json, asJson) {
43
+ if (asJson) {
44
+ logger.log(JSON.stringify(json, null, 2));
45
+ return;
46
+ }
47
+ logger.log(formatHumanStatus(json));
48
+ if (json.conflicts && json.conflicts.length) {
49
+ logger.log(formatWarningLine(`Conflicts: ${json.conflicts.join(', ')}`));
50
+ }
51
+ }
52
+
53
+ /**
54
+ * @param {object} json
55
+ * @param {boolean} asJson
56
+ * @returns {void}
57
+ */
58
+ function emitInitReport(json, asJson) {
59
+ if (asJson) {
60
+ logger.log(JSON.stringify(json, null, 2));
61
+ return;
62
+ }
63
+ logger.log('AI Fabrix workspace init');
64
+ logger.log(`Path ${json.path}`);
65
+ logger.log(`Name ${json.name}`);
66
+ logger.log(`SCM ${json.scm}`);
67
+ if (json.hosts && json.hosts.length) {
68
+ logger.log(`Hosts ${json.hosts.map((h) => hostLabel(h)).join(', ')}`);
69
+ }
70
+ if (json.kit) {
71
+ logger.log(`Agent Kit ${json.kit.kitVersion} (${json.kit.state})`);
72
+ }
73
+ if (json.remote && !json.remote.skipped) {
74
+ logger.log(`Remote ${json.remote.provider}:${json.remote.fullName} (${json.remote.visibility})`);
75
+ } else {
76
+ logger.log('Remote skipped (use: aifabrix agent-kit remote create)');
77
+ }
78
+ logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
79
+ }
80
+
81
+ /**
82
+ * @param {object} json
83
+ * @param {boolean} asJson
84
+ * @returns {void}
85
+ */
86
+ function emitRemoteReport(json, asJson) {
87
+ if (asJson) {
88
+ logger.log(JSON.stringify(json, null, 2));
89
+ return;
90
+ }
91
+ if (json.remote && json.remote.skipped) {
92
+ logger.log(formatWarningLine(`Remote skipped: ${json.remote.reason || 'already configured'}`));
93
+ } else if (json.remote) {
94
+ logger.log(`Remote ${json.remote.provider}:${json.remote.fullName} (${json.remote.visibility})`);
95
+ }
96
+ logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
97
+ }
98
+
99
+ /**
100
+ * @param {object} json
101
+ * @param {boolean} asJson
102
+ * @returns {void}
103
+ */
104
+ function emitStartReport(json, asJson) {
105
+ if (asJson) {
106
+ logger.log(JSON.stringify(json, null, 2));
107
+ return;
108
+ }
109
+ logger.log(`App ${json.app}`);
110
+ logger.log(`Session ${json.session}`);
111
+ if (json.remoteControlName) {
112
+ logger.log(`Remote ${json.remoteControlName}`);
113
+ }
114
+ logger.log(`Path ${json.path}`);
115
+ logger.log(`Launch ${json.launch.join(' ')}`);
116
+ if (json.executed && json.detach) {
117
+ logger.log(formatSuccessLine('Session started in background'));
118
+ return;
119
+ }
120
+ if (json.executed) {
121
+ logger.log(formatSuccessLine('Session ended'));
122
+ return;
123
+ }
124
+ logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Ready'));
125
+ }
126
+
127
+ /**
128
+ * @param {Function} runner
129
+ * @param {object} options
130
+ * @param {boolean} [failOnConflict]
131
+ * @returns {Promise<void>}
132
+ */
133
+ async function runLeaf(runner, options, failOnConflict) {
134
+ const json = runner(options);
135
+ emitReport(json, Boolean(options.json));
136
+ if (failOnConflict && json.conflicts && json.conflicts.length) {
137
+ logger.error(formatBlockingError('Managed files were modified locally. Re-run with --force to replace kit-owned content only.'));
138
+ process.exitCode = 1;
139
+ return;
140
+ }
141
+ if (!options.json && json.state && json.state !== KIT_STATE.NOT_INSTALLED) {
142
+ logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
143
+ }
144
+ }
145
+
146
+ /**
147
+ * @param {import('commander').Command} program
148
+ */
149
+ function setupAgentKitCommands(program) {
150
+ const group = program
151
+ .command('agent-kit')
152
+ .description('Install, init, and update the AI Fabrix Agent Kit in a customer workspace')
153
+ .addHelpText('after', GROUP_HELP);
154
+
155
+ addInitCommand(group);
156
+ addInstallCommand(group);
157
+ addStatusCommand(group);
158
+ addUpdateCommand(group);
159
+ addValidateCommand(group);
160
+ addRemoteCommand(group);
161
+ addStartCommand(group);
162
+ }
163
+
164
+ /**
165
+ * @param {import('commander').Command} group
166
+ */
167
+ function addInitCommand(group) {
168
+ group
169
+ .command('init [nameOrPath]')
170
+ .description('Create a customer workspace skeleton, git repo, and Agent Kit')
171
+ .option('--host <target>', HOST_OPTION_HELP, 'all')
172
+ .option('--scm <kind>', 'git or none', 'git')
173
+ .option('--remote', 'Create private GitHub remote and push (requires auth)')
174
+ .option('--no-remote', 'Force local-only (skip remote prompt on TTY)')
175
+ .option('--org <owner>', 'Hosting org or user (required with --remote when non-TTY)')
176
+ .option('--visibility <mode>', 'private or public', 'private')
177
+ .option('--license <kind>', 'mit or none', 'none')
178
+ .option('--force', 'Allow non-empty target directory')
179
+ .option('--yes', 'Silent: skip TTY confirm (pair with --host and --org); also auto-approves host permissions')
180
+ .option('--approve-all [host]', APPROVE_ALL_OPTION_HELP)
181
+ .option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
182
+ .option('--dry-run', 'Plan writes without changing files')
183
+ .option('--json', 'Machine-readable output')
184
+ .addHelpText('after', INIT_HELP)
185
+ .action(async(nameOrPath, options) => {
186
+ try {
187
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
188
+ const withHost = await enrichInitHostOptions(
189
+ { ...options, nameOrPath },
190
+ { interactive }
191
+ );
192
+ const withApprove = await enrichApproveAllOptions(withHost, { interactive });
193
+ const merged = await enrichInitRemoteOptions(withApprove, {
194
+ interactive,
195
+ workspaceName: path.basename(String(nameOrPath || 'workspace'))
196
+ });
197
+ if (merged.cancelled) {
198
+ logger.log('Cancelled.');
199
+ return;
200
+ }
201
+ const json = runInit(merged);
202
+ emitInitReport(json, Boolean(options.json));
203
+ } catch (error) {
204
+ handleCommandError(error, 'agent-kit init');
205
+ process.exitCode = 1;
206
+ }
207
+ });
208
+ }
209
+
210
+ /**
211
+ * @param {import('commander').Command} group
212
+ */
213
+ function addRemoteCommand(group) {
214
+ const remote = group
215
+ .command('remote')
216
+ .description('Create a GitHub repository for this workspace and push')
217
+ .addHelpText('after', REMOTE_GROUP_HELP);
218
+ remote
219
+ .command('create')
220
+ .description('Create a private GitHub repository (origin) and push this workspace')
221
+ .option('--org <owner>', 'Hosting org or user (required when non-TTY)')
222
+ .option('--name <repo>', 'Remote repository name (default: directory basename)')
223
+ .option('--visibility <mode>', 'private or public', 'private')
224
+ .option('--yes', 'Silent: skip TTY confirmation (pair with --org)')
225
+ .option('--dry-run', 'Plan without creating the remote')
226
+ .option('--json', 'Machine-readable output')
227
+ .addHelpText('after', REMOTE_HELP)
228
+ .action(async(options) => {
229
+ try {
230
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
231
+ const merged = await enrichRemoteOptions(options, {
232
+ interactive,
233
+ name: options.name || path.basename(process.cwd())
234
+ });
235
+ if (merged.cancelled) {
236
+ logger.log('Cancelled.');
237
+ return;
238
+ }
239
+ const json = runRemoteCreate(merged);
240
+ emitRemoteReport(json, Boolean(options.json));
241
+ } catch (error) {
242
+ handleCommandError(error, 'agent-kit remote create');
243
+ process.exitCode = 1;
244
+ }
245
+ });
246
+ }
247
+
248
+ /**
249
+ * @param {import('commander').Command} group
250
+ */
251
+ function addInstallCommand(group) {
252
+ group
253
+ .command('install')
254
+ .description('Install the Agent Kit into the current git repository')
255
+ .option('--host <target>', HOST_OPTION_HELP, 'all')
256
+ .option('--approve-all [host]', APPROVE_ALL_OPTION_HELP)
257
+ .option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
258
+ .option('--yes', 'Silent: auto-approve host permission configs without asking')
259
+ .option('--dry-run', 'Plan writes without changing files')
260
+ .option('--json', 'Machine-readable output')
261
+ .addHelpText('after', INSTALL_HELP)
262
+ .action(async(options) => {
263
+ try {
264
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
265
+ const withHost = await enrichInitHostOptions(options, { interactive });
266
+ const merged = await enrichApproveAllOptions(withHost, { interactive });
267
+ await runLeaf(runInstall, merged, true);
268
+ } catch (error) {
269
+ handleCommandError(error, 'agent-kit install');
270
+ process.exitCode = 1;
271
+ }
272
+ });
273
+ }
274
+
275
+ /**
276
+ * @param {import('commander').Command} group
277
+ */
278
+ function addStatusCommand(group) {
279
+ group
280
+ .command('status')
281
+ .description('Show installed Agent Kit version and file state')
282
+ .option('--json', 'Machine-readable output')
283
+ .addHelpText('after', STATUS_HELP)
284
+ .action(async(options) => {
285
+ try {
286
+ await runLeaf(runStatus, options, false);
287
+ } catch (error) {
288
+ handleCommandError(error, 'agent-kit status');
289
+ process.exitCode = 1;
290
+ }
291
+ });
292
+ }
293
+
294
+ /**
295
+ * Recorded --host from a previous install (update omits --host to keep this).
296
+ * @returns {string[]|undefined}
297
+ */
298
+ function readRecordedHosts() {
299
+ try {
300
+ const state = loadState(resolveGitRoot(process.cwd()));
301
+ if (state && Array.isArray(state.hosts) && state.hosts.length) {
302
+ return state.hosts;
303
+ }
304
+ } catch {
305
+ return undefined;
306
+ }
307
+ return undefined;
308
+ }
309
+
310
+ /**
311
+ * Update does not prompt or auto-write host configs unless flags are set.
312
+ * @param {object} options
313
+ * @returns {Promise<object>}
314
+ */
315
+ async function mergeUpdateApproveOptions(options) {
316
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
317
+ const host = options.host || readRecordedHosts();
318
+ return enrichApproveAllOptions({ ...options, host }, { interactive, ask: false });
319
+ }
320
+
321
+ /**
322
+ * @param {import('commander').Command} group
323
+ */
324
+ function addUpdateCommand(group) {
325
+ group
326
+ .command('update')
327
+ .description('Update managed Agent Kit files from this Builder release')
328
+ .option('--host <target>', `${HOST_OPTION_HELP} (omit to keep recorded hosts)`)
329
+ .option('--approve-all [host]', APPROVE_ALL_OPTION_HELP)
330
+ .option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
331
+ .option('--yes', 'Silent: auto-approve host permission configs without asking')
332
+ .option('--dry-run', 'Plan writes without changing files')
333
+ .option('--force', 'Replace modified kit-owned files and managed sections')
334
+ .option('--json', 'Machine-readable output')
335
+ .addHelpText('after', UPDATE_HELP)
336
+ .action(async(options) => {
337
+ try {
338
+ const merged = await mergeUpdateApproveOptions(options);
339
+ await runLeaf(runUpdate, merged, !options.force);
340
+ } catch (error) {
341
+ handleCommandError(error, 'agent-kit update');
342
+ process.exitCode = 1;
343
+ }
344
+ });
345
+ }
346
+
347
+ /**
348
+ * @param {import('commander').Command} group
349
+ */
350
+ function addValidateCommand(group) {
351
+ group
352
+ .command('validate')
353
+ .description('Validate the bundled Agent Kit and the current repository install')
354
+ .option('--json', 'Machine-readable output')
355
+ .addHelpText('after', VALIDATE_HELP)
356
+ .action(async(options) => {
357
+ try {
358
+ await runValidateLeaf(options);
359
+ } catch (error) {
360
+ handleCommandError(error, 'agent-kit validate');
361
+ process.exitCode = 1;
362
+ }
363
+ });
364
+ }
365
+
366
+ /**
367
+ * @param {object} options
368
+ * @returns {Promise<void>}
369
+ */
370
+ async function runValidateLeaf(options) {
371
+ const json = runValidate(options);
372
+ if (options.json) {
373
+ logger.log(JSON.stringify(json, null, 2));
374
+ } else if (json.success) {
375
+ logger.log(formatSuccessLine(`Agent Kit ${json.kitVersion} is valid`));
376
+ } else {
377
+ logger.error(formatBlockingError(json.errors.join('; ')));
378
+ }
379
+ if (!json.success) {
380
+ process.exitCode = 1;
381
+ }
382
+ }
383
+
384
+ /**
385
+ * @param {import('commander').Command} group
386
+ */
387
+ function addStartCommand(group) {
388
+ group
389
+ .command('start <app>')
390
+ .description('Start Claude Code or Codex in tmux (does not install skills)')
391
+ .option('--approve-all', 'Write auto-approve config for this app only')
392
+ .option('--skip-approve-all', 'Do not write auto-approve configs')
393
+ .option('--yes', 'Same as --approve-all')
394
+ .option('--detach', 'Create the tmux session in the background (no attach)')
395
+ .option(
396
+ '--name <session>',
397
+ 'tmux session name (default: last folder of this git workspace)'
398
+ )
399
+ .option(
400
+ '--remote-control',
401
+ 'Claude Code only: same session on this PC, claude.ai/code, and the Claude mobile app'
402
+ )
403
+ .option('--dry-run', 'Print the launch command without starting it')
404
+ .option('--json', 'Machine-readable output')
405
+ .addHelpText('after', START_HELP)
406
+ .action(async(app, options) => {
407
+ try {
408
+ const json = runStart({ ...options, app });
409
+ emitStartReport(json, Boolean(options.json));
410
+ } catch (error) {
411
+ handleCommandError(error, 'agent-kit start');
412
+ process.exitCode = 1;
413
+ }
414
+ });
415
+ }
416
+
417
+ module.exports = { setupAgentKitCommands };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Resolve the signed-in username for `aifabrix auth status`.
3
+ *
4
+ * @fileoverview Unwrap controller auth/user payloads and pick a display username
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { unwrapControllerData } = require('../identity/identity-response-helpers');
12
+
13
+ /**
14
+ * @param {...*} values
15
+ * @returns {string}
16
+ */
17
+ function firstNonEmptyString(...values) {
18
+ for (const value of values) {
19
+ if (typeof value === 'string' && value.trim()) {
20
+ return value.trim();
21
+ }
22
+ }
23
+ return '';
24
+ }
25
+
26
+ /**
27
+ * Decode a JWT payload for display only. Does not verify the signature.
28
+ * @param {string} token
29
+ * @returns {Object|null}
30
+ */
31
+ function decodeJwtPayload(token) {
32
+ if (!token || typeof token !== 'string') {
33
+ return null;
34
+ }
35
+ const parts = token.split('.');
36
+ if (parts.length < 2) {
37
+ return null;
38
+ }
39
+ try {
40
+ const b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
41
+ const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, '=');
42
+ const payload = JSON.parse(Buffer.from(padded, 'base64').toString('utf8'));
43
+ return payload && typeof payload === 'object' ? payload : null;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * @param {Object|null|undefined} user
51
+ * @param {string} [token]
52
+ * @returns {string}
53
+ */
54
+ function resolveDisplayUsername(user, token) {
55
+ const fromUser = firstNonEmptyString(
56
+ user && user.username,
57
+ user && user.preferredUsername,
58
+ user && user.preferred_username,
59
+ user && user.email
60
+ );
61
+ if (fromUser) {
62
+ return fromUser;
63
+ }
64
+ const payload = decodeJwtPayload(token);
65
+ return firstNonEmptyString(
66
+ payload && payload.preferred_username,
67
+ payload && payload.preferredUsername,
68
+ payload && payload.email,
69
+ payload && payload.upn
70
+ );
71
+ }
72
+
73
+ /**
74
+ * @param {Object|null|undefined} response
75
+ * @param {string} [token]
76
+ * @returns {{ authenticated: boolean, user: Object|null, username: string }}
77
+ */
78
+ function parseAuthUserResponse(response, token) {
79
+ const payload = unwrapControllerData(response);
80
+ if (!payload || typeof payload !== 'object') {
81
+ return { authenticated: false, user: null, username: '' };
82
+ }
83
+ const rawUser = payload.user && typeof payload.user === 'object' ? payload.user : null;
84
+ const username = resolveDisplayUsername(rawUser, token);
85
+ const user = username && (!rawUser || !rawUser.username)
86
+ ? { ...(rawUser || {}), username }
87
+ : rawUser;
88
+ return {
89
+ authenticated: payload.authenticated !== false,
90
+ user,
91
+ username
92
+ };
93
+ }
94
+
95
+ module.exports = {
96
+ decodeJwtPayload,
97
+ resolveDisplayUsername,
98
+ parseAuthUserResponse
99
+ };
@@ -34,6 +34,7 @@ const {
34
34
  displayStoredClientTokensSection,
35
35
  tryResolveClientTokenAuth
36
36
  } = require('./auth-status-client-tokens');
37
+ const { parseAuthUserResponse } = require('./auth-status-user');
37
38
 
38
39
  /**
39
40
  * Format expiration date for display
@@ -71,11 +72,13 @@ async function checkDeviceToken(controllerUrl) {
71
72
  const response = await getAuthUser(controllerUrl, authConfig);
72
73
 
73
74
  if (response.success && response.data) {
75
+ const parsed = parseAuthUserResponse(response, deviceToken.token);
74
76
  return {
75
77
  type: 'Device Token',
76
78
  token: deviceToken.token,
77
- authenticated: response.data.authenticated !== false,
78
- user: response.data.user,
79
+ authenticated: parsed.authenticated,
80
+ user: parsed.user,
81
+ username: parsed.username,
79
82
  expiresAt: deviceToken.expiresAt
80
83
  };
81
84
  }
@@ -113,11 +116,13 @@ async function validateClientToken(token, controllerUrl, environment, appName, e
113
116
  const response = await getAuthUser(controllerUrl, authConfig);
114
117
 
115
118
  if (response.success && response.data) {
119
+ const parsed = parseAuthUserResponse(response, token);
116
120
  return {
117
121
  type: 'Client Token',
118
122
  token: token,
119
- authenticated: response.data.authenticated !== false,
120
- user: response.data.user,
123
+ authenticated: parsed.authenticated,
124
+ user: parsed.user,
125
+ username: parsed.username,
121
126
  expiresAt: expiresAt,
122
127
  appName: appName
123
128
  };
@@ -185,6 +190,11 @@ function displayTokenInfo(tokenInfo) {
185
190
  logger.log(` Status: ${statusIcon} ${statusText}`);
186
191
  logger.log(` Token Type: ${chalk.cyan(tokenInfo.type)}`);
187
192
 
193
+ if (tokenInfo.authenticated) {
194
+ const username = tokenInfo.username || (tokenInfo.user && tokenInfo.user.username);
195
+ logger.log(` User: ${username ? chalk.cyan(username) : chalk.gray('—')}`);
196
+ }
197
+
188
198
  if (tokenInfo.appName) {
189
199
  logger.log(` Application: ${chalk.cyan(tokenInfo.appName)}`);
190
200
  }
@@ -113,9 +113,9 @@ Behavior:
113
113
  - chatgpt registers the ChatGPT callback URL when provided (--callback-url or TTY prompt).
114
114
  It does not rotate the client secret. Remaining ChatGPT steps: Create with logo (cannot change later), Publish, Connect.
115
115
  - claude registers the Claude callback URL (https://claude.ai/api/mcp/auth_callback) and prints
116
- Name and Server URL. Open https://claude.ai/new#settings/customize-connectors and add a
117
- custom connector with those two values. Use channel configure claude --callback-url only if
118
- Claude shows a callback that is not already registered.
116
+ Name, Server URL, and Authentication: OAuth. Open https://claude.ai/new#settings/customize-connectors
117
+ and add a custom connector with those three values (Runtime MCP requires sign-in). Use channel
118
+ configure claude --callback-url only if Claude shows a callback that is not already registered.
119
119
  - mcp-client writes the Runtime MCP URL into the editor MCP config. Pass --target (or --profile)
120
120
  with cursor, vscode, or github-copilot. It creates or reuses the Integration Client and never
121
121
  rotates the client secret. Remaining step: Reload the editor.
@@ -169,8 +169,8 @@ Examples:
169
169
 
170
170
  ${SHARED_TARGET_BEHAVIOR}
171
171
  - claude registers the Claude callback URL (https://claude.ai/api/mcp/auth_callback) and prints
172
- Name and Server URL. Open https://claude.ai/new#settings/customize-connectors and add a
173
- custom connector with those two values.
172
+ Name, Server URL, and Authentication: OAuth. Open https://claude.ai/new#settings/customize-connectors
173
+ and add a custom connector with those three values (Runtime MCP requires sign-in).
174
174
 
175
175
  Requires: aifabrix login
176
176
 
@@ -18,9 +18,10 @@ const { runCapabilityRelate } = require('../datasource/capability/run-capability
18
18
  const { printCapabilitySuccessFooter } = require('./datasource-capability-output');
19
19
 
20
20
  const CAP_RELATE_HELP = `
21
- Adds or replaces one **foreignKeys[]** row (metadata-only; no pipeline). Optional **metadataSchema.properties** stub unless **--skip-metadata-property**.
21
+ Adds or replaces one **foreignKeys[]** row (metadata-only; no pipeline). Optional **metadataSchema.properties** stub unless **--skip-metadata-property**. **--relation-name** defaults to the carrier **--field**.
22
22
 
23
- $ aifabrix datasource capability relate hubspot-deals --relation-name company --to hubspot-companies --field companyId --target-field externalId
23
+ $ aifabrix datasource capability relate pkg-a-facts --to pkg-b-persons --field personId
24
+ $ aifabrix datasource capability relate pkg-a-facts --relation-name personId --to pkg-b-persons --field personId --target-field externalId
24
25
  `;
25
26
 
26
27
  function parseTargetFields(raw) {
@@ -103,8 +104,8 @@ function setupCapabilityRelateCommand(cap) {
103
104
  .description(
104
105
  'Add or replace foreignKeys[] metadata (+ optional metadataSchema property for relation name)'
105
106
  )
106
- .requiredOption('--relation-name <name>', 'FK name (camelCase; unique per datasource)')
107
- .requiredOption('--to <targetDatasource>', 'Target datasource key (cross-system JSON key)')
107
+ .option('--relation-name <name>', 'FK name (camelCase; defaults to --field)')
108
+ .requiredOption('--to <targetDatasource>', 'Target datasource key (sibling packages resolve by key)')
108
109
  .requiredOption('--field <name>', 'Local normalized attribute (foreignKeys.fields[])')
109
110
  .option('--description <text>', 'FK description (defaults to a generated description)')
110
111
  .option('--required', 'Mark FK required (foreignKeys[].required=true)')
@@ -7,6 +7,7 @@
7
7
  'use strict';
8
8
 
9
9
  const logger = require('../utils/logger');
10
+ const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
10
11
  const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
11
12
  const { runDatasourceLoad } = require('../datasource/bulk-loader-service');
12
13
  const { runDatasourceExport } = require('../datasource/datasource-exporter-service');
@@ -82,7 +83,9 @@ async function datasourceLoadAction(datasourceKey, options) {
82
83
 
83
84
  if (options.json) {
84
85
  const { context: _ctx, ...payload } = result;
85
- logger.log(JSON.stringify(payload, null, 2));
86
+ // exitCode 0 — write and resolve, never exit here; this action always
87
+ // exits unconditionally below with the real computed code.
88
+ await writeJsonEnvelopeAndExit(payload, 0);
86
89
  } else {
87
90
  displayDatasourceLoadTTY(result, { environment: options.env, verbose: options.verbose });
88
91
  }
@@ -112,7 +115,9 @@ async function datasourceExportAction(datasourceKey, options) {
112
115
 
113
116
  if (options.json) {
114
117
  const { context: _ctx, ...payload } = result;
115
- logger.log(JSON.stringify(payload, null, 2));
118
+ // exitCode 0 — write and resolve, never exit here; this action always
119
+ // exits unconditionally below with the real computed code.
120
+ await writeJsonEnvelopeAndExit(payload, 0);
116
121
  } else {
117
122
  displayDatasourceExportTTY(result, { environment: options.env, verbose: options.verbose });
118
123
  }
@@ -7,6 +7,7 @@
7
7
  'use strict';
8
8
 
9
9
  const logger = require('../utils/logger');
10
+ const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
10
11
  const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
11
12
  const { runDatasourceAgentTrust } = require('../datasource/agent-trust-run');
12
13
  const { writeTrustDebugLogAndPrint } = require('../datasource/agent-trust-debug-log');
@@ -52,7 +53,10 @@ async function finalizeDatasourceVerifyTrust(datasourceKey, options, result) {
52
53
  strict: options.strict === true
53
54
  };
54
55
  if (options.json) {
55
- logger.log(JSON.stringify(result.trustRun, null, 2));
56
+ // exitCode 0 here means "write and resolve, never exit" — the real exit
57
+ // code is decided below and the caller exits unconditionally, even on
58
+ // success, so process.exit() must stay solely the caller's job.
59
+ await writeJsonEnvelopeAndExit(result.trustRun, 0);
56
60
  } else if (options.summary) {
57
61
  const tr = result.trustRun;
58
62
  logger.log(