@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,103 @@
1
+ /**
2
+ * Copy product-neutral workspace skeleton for agent-kit init.
3
+ * @fileoverview templates/agent-kit/workspace → target
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+ const fs = require('../internal/fs-real-sync');
10
+ const { getKitRoot } = require('./kit-root');
11
+
12
+ /**
13
+ * @returns {string}
14
+ */
15
+ function getWorkspaceTemplateRoot() {
16
+ return path.join(getKitRoot(), 'workspace');
17
+ }
18
+
19
+ /**
20
+ * @param {string} text
21
+ * @param {{ workspaceName: string, year?: number, copyrightHolder?: string }} vars
22
+ * @returns {string}
23
+ */
24
+ function applyPlaceholders(text, vars) {
25
+ return String(text)
26
+ .replace(/\{\{workspaceName\}\}/g, vars.workspaceName)
27
+ .replace(/\{\{year\}\}/g, String(vars.year || new Date().getFullYear()))
28
+ .replace(/\{\{copyrightHolder\}\}/g, vars.copyrightHolder || 'Copyright Holder');
29
+ }
30
+
31
+ /**
32
+ * @param {string} absDir
33
+ * @param {string} relPrefix
34
+ * @returns {{ rel: string, abs: string }[]}
35
+ */
36
+ function listTemplateFiles(absDir, relPrefix) {
37
+ if (!fs.existsSync(absDir)) {
38
+ return [];
39
+ }
40
+ const out = [];
41
+ for (const ent of fs.readdirSync(absDir, { withFileTypes: true })) {
42
+ const rel = relPrefix ? `${relPrefix}/${ent.name}` : ent.name;
43
+ const abs = path.join(absDir, ent.name);
44
+ if (ent.isDirectory()) {
45
+ out.push(...listTemplateFiles(abs, rel));
46
+ } else if (ent.name !== 'LICENSE.mit') {
47
+ out.push({ rel, abs });
48
+ }
49
+ }
50
+ return out;
51
+ }
52
+
53
+ /**
54
+ * @param {string} targetAbs
55
+ * @param {{ workspaceName: string, license?: string, dryRun?: boolean }} options
56
+ * @returns {string[]} written relative paths
57
+ */
58
+ function writeWorkspaceSkeleton(targetAbs, options) {
59
+ const written = [];
60
+ const vars = { workspaceName: options.workspaceName };
61
+ const root = getWorkspaceTemplateRoot();
62
+ for (const file of listTemplateFiles(root, '')) {
63
+ const dest = path.join(targetAbs, file.rel);
64
+ if (!options.dryRun) {
65
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
66
+ const body = applyPlaceholders(fs.readFileSync(file.abs, 'utf8'), vars);
67
+ fs.writeFileSync(dest, body, 'utf8');
68
+ }
69
+ written.push(file.rel);
70
+ }
71
+ if (options.license === 'mit') {
72
+ const src = path.join(root, 'LICENSE.mit');
73
+ const dest = path.join(targetAbs, 'LICENSE');
74
+ if (!options.dryRun && fs.existsSync(src)) {
75
+ fs.writeFileSync(dest, applyPlaceholders(fs.readFileSync(src, 'utf8'), vars), 'utf8');
76
+ }
77
+ written.push('LICENSE');
78
+ }
79
+ if (!options.dryRun) {
80
+ fs.mkdirSync(path.join(targetAbs, 'integration'), { recursive: true });
81
+ fs.mkdirSync(path.join(targetAbs, 'role-assistant'), { recursive: true });
82
+ }
83
+ return written;
84
+ }
85
+
86
+ /**
87
+ * @param {string} targetAbs
88
+ * @returns {boolean}
89
+ */
90
+ function isNonEmptyDirectory(targetAbs) {
91
+ if (!fs.existsSync(targetAbs)) {
92
+ return false;
93
+ }
94
+ const entries = fs.readdirSync(targetAbs).filter((name) => name !== '.git');
95
+ return entries.length > 0;
96
+ }
97
+
98
+ module.exports = {
99
+ getWorkspaceTemplateRoot,
100
+ applyPlaceholders,
101
+ writeWorkspaceSkeleton,
102
+ isNonEmptyDirectory
103
+ };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Dataplane Role Assistant test pack CRUD + case status.
3
+ *
4
+ * @fileoverview PUT/GET pack and PATCH case status
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { createDataplaneApiClient } = require('./index');
12
+ const { throwIfApiFailed } = require('./api-response-errors');
13
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
14
+
15
+ function packEndpoint(dataplaneUrl, systemIdOrKey, suffix) {
16
+ const base = String(dataplaneUrl || '').replace(/\/$/, '');
17
+ const key = encodeURIComponent(systemIdOrKey);
18
+ return `${base}/api/v1/external/systems/${key}/role-assistant-tests${suffix || ''}`;
19
+ }
20
+
21
+ function packPath(systemIdOrKey, suffix) {
22
+ const key = encodeURIComponent(systemIdOrKey);
23
+ return `/api/v1/external/systems/${key}/role-assistant-tests${suffix || ''}`;
24
+ }
25
+
26
+ function isMissingPackResponse(res) {
27
+ return Boolean(res && res.success === false && Number(res.status) === 404);
28
+ }
29
+
30
+ /**
31
+ * PUT /api/v1/external/systems/{systemIdOrKey}/role-assistant-tests
32
+ * @requiresPermission {Dataplane} external-system:update
33
+ * @async
34
+ */
35
+ async function upsertRoleAssistantTestPack(dataplaneUrl, authConfig, systemIdOrKey, pack) {
36
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
37
+ const res = await client.put(packPath(systemIdOrKey), { body: { pack } });
38
+ throwIfApiFailed(res, {
39
+ endpointUrl: packEndpoint(dataplaneUrl, systemIdOrKey, '')
40
+ });
41
+ return unwrapApiData(res);
42
+ }
43
+
44
+ /**
45
+ * GET /api/v1/external/systems/{systemIdOrKey}/role-assistant-tests
46
+ * @requiresPermission {Dataplane} external-system:read
47
+ * @async
48
+ */
49
+ async function getRoleAssistantTestPack(dataplaneUrl, authConfig, systemIdOrKey, options = {}) {
50
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
51
+ const res = await client.get(packPath(systemIdOrKey));
52
+ if (options.allowMissing === true && isMissingPackResponse(res)) {
53
+ return null;
54
+ }
55
+ throwIfApiFailed(res, {
56
+ endpointUrl: packEndpoint(dataplaneUrl, systemIdOrKey, '')
57
+ });
58
+ return unwrapApiData(res);
59
+ }
60
+
61
+ /**
62
+ * PATCH /api/v1/external/systems/{systemIdOrKey}/role-assistant-tests/cases/{caseId}/status
63
+ * @requiresPermission {Dataplane} external-system:update
64
+ * @async
65
+ */
66
+ async function patchRoleAssistantTestCaseStatus(
67
+ dataplaneUrl,
68
+ authConfig,
69
+ systemIdOrKey,
70
+ caseId,
71
+ body
72
+ ) {
73
+ const suffix = `/cases/${encodeURIComponent(caseId)}/status`;
74
+ const client = createDataplaneApiClient(dataplaneUrl, authConfig);
75
+ const res = await client.patch(packPath(systemIdOrKey, suffix), { body });
76
+ throwIfApiFailed(res, {
77
+ endpointUrl: packEndpoint(dataplaneUrl, systemIdOrKey, suffix)
78
+ });
79
+ return unwrapApiData(res);
80
+ }
81
+
82
+ module.exports = {
83
+ upsertRoleAssistantTestPack,
84
+ getRoleAssistantTestPack,
85
+ patchRoleAssistantTestCaseStatus
86
+ };
@@ -14,6 +14,7 @@ const { unwrapApiData } = require('../utils/external-system-readiness-core');
14
14
  const SEARCH_TIMEOUT_MS = 120000;
15
15
  const LAYER_B_SYSTEM_KEY = process.env.RUNTIME_ORCHESTRATION_SYSTEM_KEY || 'runtime-orchestration';
16
16
  const LAYER_B_TOOLS = Object.freeze({
17
+ roles: { resourceType: 'work-roles', toolName: 'work_roles.list' },
17
18
  skills: { resourceType: 'work-skills', toolName: 'work_skills.get' },
18
19
  search: { resourceType: 'work-search', toolName: 'work_search.create' }
19
20
  });
@@ -45,19 +46,31 @@ function extractErrorText(res) {
45
46
  return parts.join(' ');
46
47
  }
47
48
 
49
+ /**
50
+ * MCP execute sometimes wraps a single JSON object as a one-element array.
51
+ * @param {*} value
52
+ * @returns {*}
53
+ */
54
+ function unwrapSingletonObject(value) {
55
+ if (Array.isArray(value) && value.length === 1 && value[0] && typeof value[0] === 'object') {
56
+ return value[0];
57
+ }
58
+ return value;
59
+ }
60
+
48
61
  function unwrapExecutePayload(res) {
49
- const body = unwrapApiData(res);
62
+ const body = unwrapSingletonObject(unwrapApiData(res));
50
63
  if (!body || typeof body !== 'object') {
51
64
  return body;
52
65
  }
53
66
  if (body.result && typeof body.result === 'object') {
54
- return body.result;
67
+ return unwrapSingletonObject(body.result);
55
68
  }
56
69
  if (body.json && typeof body.json === 'object') {
57
- return body.json;
70
+ return unwrapSingletonObject(body.json);
58
71
  }
59
72
  if (body.root && typeof body.root === 'object') {
60
- return body.root;
73
+ return unwrapSingletonObject(body.root);
61
74
  }
62
75
  return body;
63
76
  }
@@ -94,6 +107,18 @@ async function executeLayerBTool(dataplaneUrl, authConfig, tool, args = {}) {
94
107
  return normalizeLayerBResult(res);
95
108
  }
96
109
 
110
+ /**
111
+ * GET-equivalent work.roles for the current caller.
112
+ * @requiresPermission {Dataplane} Authenticated (oauth2: [])
113
+ * @async
114
+ * @param {string} dataplaneUrl
115
+ * @param {Object} authConfig
116
+ * @returns {Promise<{ status: number, payload: Object|null, errorText: string, raw: Object }>}
117
+ */
118
+ async function getWorkRoles(dataplaneUrl, authConfig) {
119
+ return executeLayerBTool(dataplaneUrl, authConfig, LAYER_B_TOOLS.roles, {});
120
+ }
121
+
97
122
  /**
98
123
  * GET-equivalent work.skills for one roleRef.
99
124
  * Requests detail=full so search.resourceTypes (executable grammar) is present.
@@ -128,9 +153,11 @@ module.exports = {
128
153
  SEARCH_TIMEOUT_MS,
129
154
  buildLayerBExecutePath,
130
155
  extractErrorText,
156
+ unwrapSingletonObject,
131
157
  unwrapExecutePayload,
132
158
  normalizeLayerBResult,
133
159
  executeLayerBTool,
160
+ getWorkRoles,
134
161
  getWorkSkills,
135
162
  searchWork
136
163
  };
@@ -7,7 +7,9 @@
7
7
  'use strict';
8
8
 
9
9
  const { createDataplaneApiClient } = require('./index');
10
+ const { throwIfApiFailed } = require('./api-response-errors');
10
11
  const { unwrapApiData } = require('../utils/external-system-readiness-core');
12
+ const { pickHumanRespondBody } = require('../role-assistant/test-runner-respond-body');
11
13
 
12
14
  /** Work Hub plan create runs LLM planning; 30s default is too short under load/reload. */
13
15
  const WORK_PLAN_TIMEOUT_MS = 120000;
@@ -59,7 +61,10 @@ function assertObjectShape(value, fallbackMessage) {
59
61
  */
60
62
  async function startWorkIntent(dataplaneUrl, authConfig, body) {
61
63
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
62
- const res = await client.post('/api/v1/work/start', { body });
64
+ const res = await client.post('/api/v1/work/start', {
65
+ body,
66
+ timeoutMs: WORK_PLAN_TIMEOUT_MS
67
+ });
63
68
  if (res && res.success === false) {
64
69
  throw new Error(extractApiErrorMessage(res, 'work.start request failed'));
65
70
  }
@@ -70,7 +75,7 @@ async function startWorkIntent(dataplaneUrl, authConfig, body) {
70
75
 
71
76
  /**
72
77
  * GET /api/v1/work/executions/{processExecutionId}
73
- * @requiresPermission {Dataplane} worker-task:read
78
+ * @requiresPermission {Dataplane} worker:read
74
79
  * @async
75
80
  * @param {string} dataplaneUrl
76
81
  * @param {Object} authConfig
@@ -80,12 +85,13 @@ async function startWorkIntent(dataplaneUrl, authConfig, body) {
80
85
  async function getWorkExecutionStatus(dataplaneUrl, authConfig, processExecutionId) {
81
86
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
82
87
  const encodedId = encodeURIComponent(String(processExecutionId));
83
- const res = await client.get(`/api/v1/work/executions/${encodedId}`, {
88
+ const endpoint = `/api/v1/work/executions/${encodedId}`;
89
+ const res = await client.get(endpoint, {
84
90
  timeoutMs: WORK_TASK_TIMEOUT_MS
85
91
  });
86
- if (res && res.success === false) {
87
- throw new Error(extractApiErrorMessage(res, 'work.status request failed'));
88
- }
92
+ throwIfApiFailed(res, {
93
+ endpointUrl: `${String(dataplaneUrl || '').replace(/\/$/, '')}${endpoint}`
94
+ });
89
95
  const unwrapped = unwrapWorkPayload(res);
90
96
  assertObjectShape(unwrapped, 'work.status returned an unexpected response shape');
91
97
  return unwrapped;
@@ -105,7 +111,7 @@ async function respondWorkExecution(dataplaneUrl, authConfig, processExecutionId
105
111
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
106
112
  const encodedId = encodeURIComponent(String(processExecutionId));
107
113
  const res = await client.post(`/api/v1/work/executions/${encodedId}/respond`, {
108
- body,
114
+ body: pickHumanRespondBody(body),
109
115
  timeoutMs: WORK_TASK_TIMEOUT_MS
110
116
  });
111
117
  if (res && res.success === false) {
@@ -143,7 +149,7 @@ async function continueWorkExecution(dataplaneUrl, authConfig, processExecutionI
143
149
 
144
150
  /**
145
151
  * GET /api/v1/work/executions/{processExecutionId}/result
146
- * @requiresPermission {Dataplane} worker-task:read
152
+ * @requiresPermission {Dataplane} worker:read
147
153
  * @async
148
154
  * @param {string} dataplaneUrl
149
155
  * @param {Object} authConfig
@@ -154,6 +160,7 @@ async function continueWorkExecution(dataplaneUrl, authConfig, processExecutionI
154
160
  async function getWorkExecutionResult(dataplaneUrl, authConfig, processExecutionId, query = {}) {
155
161
  const client = createDataplaneApiClient(dataplaneUrl, authConfig);
156
162
  const encodedId = encodeURIComponent(String(processExecutionId));
163
+ const endpoint = `/api/v1/work/executions/${encodedId}/result`;
157
164
  const params = {};
158
165
  if (query && typeof query === 'object') {
159
166
  if (query.requestedDepth) {
@@ -166,13 +173,13 @@ async function getWorkExecutionResult(dataplaneUrl, authConfig, processExecution
166
173
  params.projection = String(query.projection);
167
174
  }
168
175
  }
169
- const res = await client.get(`/api/v1/work/executions/${encodedId}/result`, {
176
+ const res = await client.get(endpoint, {
170
177
  params,
171
178
  timeoutMs: WORK_TASK_TIMEOUT_MS
172
179
  });
173
- if (res && res.success === false) {
174
- throw new Error(extractApiErrorMessage(res, 'work.result request failed'));
175
- }
180
+ throwIfApiFailed(res, {
181
+ endpointUrl: `${String(dataplaneUrl || '').replace(/\/$/, '')}${endpoint}`
182
+ });
176
183
  const unwrapped = unwrapWorkPayload(res);
177
184
  assertObjectShape(unwrapped, 'work.result returned an unexpected response shape');
178
185
  return unwrapped;
package/lib/app/deploy.js CHANGED
@@ -23,6 +23,7 @@ const pushUtils = require('../deployment/push');
23
23
  const SEP = chalk.gray('────────────────────────────────────────');
24
24
  const logger = require('../utils/logger');
25
25
  const { detectAppType, getBuilderPath, getIntegrationPath } = require('../utils/paths');
26
+ const { offerCreateFromTemplateIfMissing } = require('./offer-create-from-template');
26
27
  const { checkApplicationExists } = require('../utils/app-existence');
27
28
  const { loadDeploymentConfig } = require('./deploy-config');
28
29
  const { logOfflinePathWhenType } = require('../utils/cli-utils');
@@ -462,7 +463,7 @@ async function executeDeployPipeline(appName, options) {
462
463
  * @param {boolean} [options.poll] - Poll for deployment status
463
464
  * @param {number} [options.pollInterval] - Polling interval in milliseconds
464
465
  * @param {number} [options.pollMaxAttempts] - Max polling attempts
465
- * @returns {Promise<{ result: Object, usedExternalDeploy: boolean }>} Deployment result and whether external deploy was used
466
+ * @returns {Promise<{ result: Object, usedExternalDeploy: boolean, appKey: string }>} Deployment result and whether external deploy was used
466
467
  * @throws {Error} If deployment fails
467
468
  *
468
469
  * Controller and environment come from config.yaml (set via aifabrix login or aifabrix auth config).
@@ -473,16 +474,18 @@ async function executeDeployPipeline(appName, options) {
473
474
  async function deployApp(appName, options = {}) {
474
475
  let controllerUrl = null;
475
476
  let usedExternalDeploy = false;
477
+ let resolvedName = appName;
476
478
 
477
479
  try {
478
480
  assertDeployNotProtectionScope(appName);
479
481
  validateAppName(appName);
480
- const pipeline = await executeDeployPipeline(appName, options);
482
+ resolvedName = await offerCreateFromTemplateIfMissing(appName, options);
483
+ const pipeline = await executeDeployPipeline(resolvedName, options);
481
484
  controllerUrl = pipeline.controllerUrl ?? null;
482
485
  usedExternalDeploy = pipeline.usedExternalDeploy;
483
- return { result: pipeline.result, usedExternalDeploy };
486
+ return { result: pipeline.result, usedExternalDeploy, appKey: resolvedName };
484
487
  } catch (error) {
485
- await handleDeploymentError(error, appName, controllerUrl, usedExternalDeploy);
488
+ await handleDeploymentError(error, resolvedName, controllerUrl, usedExternalDeploy);
486
489
  }
487
490
  }
488
491
 
@@ -15,6 +15,7 @@ const { listExternalSystems } = require('../api/external-systems.api');
15
15
  const { formatApiError } = require('../utils/api-error-handler');
16
16
  const { getListAuthToken } = require('./list');
17
17
  const logger = require('../utils/logger');
18
+ const { formatLifecycleLabel } = require('../utils/external-system-readiness-compact-display');
18
19
 
19
20
  /**
20
21
  * Normalize list response shapes from the dataplane API client.
@@ -56,7 +57,7 @@ function displayExternalSystems(systems, environment, dataplaneUrl, json) {
56
57
  systems.forEach((system) => {
57
58
  const key = system.key || system.systemKey || system.id || '—';
58
59
  const displayName = system.displayName || key;
59
- const status = system.status || 'unknown';
60
+ const status = formatLifecycleLabel(system.status || 'unknown');
60
61
  logger.log(`✔ ${chalk.cyan(key)} - ${displayName} (${status})`);
61
62
  });
62
63
  logger.log(chalk.gray(' To show details: aifabrix show <systemKey> --online\n'));
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Offer to materialize a missing app from shipped application templates.
3
+ *
4
+ * @fileoverview Interactive create-from-template before deploy when the app is not on disk
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const logger = require('../utils/logger');
12
+ const { metadata } = require('../utils/cli-test-layout-chalk');
13
+ const { listAvailableTemplates } = require('../validation/template');
14
+ const { findExistingAppType } = require('../utils/paths');
15
+ const { ensureAppFromTemplate } = require('../commands/up-common');
16
+
17
+ /**
18
+ * @param {Object} [options]
19
+ * @returns {boolean}
20
+ */
21
+ function assumeYesCreate(options = {}) {
22
+ return options.yes === true || options.assumeYes === true;
23
+ }
24
+
25
+ /**
26
+ * @param {Object} [deps]
27
+ * @returns {boolean}
28
+ */
29
+ function isInteractiveCreate(deps = {}) {
30
+ if (process.env.CI === 'true' || process.env.CI === '1') {
31
+ return false;
32
+ }
33
+ if (typeof deps.isTty === 'function') {
34
+ return Boolean(deps.isTty());
35
+ }
36
+ return Boolean(process.stdin && process.stdin.isTTY);
37
+ }
38
+
39
+ /**
40
+ * @param {string[]} templates
41
+ * @returns {string}
42
+ */
43
+ function formatTemplateNames(templates) {
44
+ return templates.join(', ');
45
+ }
46
+
47
+ /**
48
+ * @param {string} appName
49
+ * @param {string[]} templates
50
+ * @returns {Error}
51
+ */
52
+ function buildMissingAppError(appName, templates) {
53
+ const listed = templates.length > 0
54
+ ? `Shipped templates: ${formatTemplateNames(templates)}.`
55
+ : 'No shipped application templates were found.';
56
+ const hint = templates.includes(appName)
57
+ ? `Create it with: aifabrix deploy ${appName} --yes`
58
+ : 'Pass a matching template name, or create the app first.';
59
+ return new Error(
60
+ `App '${appName}' was not found under integration/ or builder/. ${listed} ${hint}`
61
+ );
62
+ }
63
+
64
+ /**
65
+ * @param {string} appName
66
+ * @param {Function} promptFn
67
+ * @returns {Promise<boolean>}
68
+ */
69
+ async function confirmCreateMatchingTemplate(appName, promptFn) {
70
+ const ask = promptFn || ((qs) => require('inquirer').prompt(qs));
71
+ const { create } = await ask([{
72
+ type: 'confirm',
73
+ name: 'create',
74
+ message: `App '${appName}' is not available. Create it from the shipped '${appName}' template?`,
75
+ default: true
76
+ }]);
77
+ return Boolean(create);
78
+ }
79
+
80
+ /**
81
+ * @param {string[]} templates
82
+ * @param {Function} promptFn
83
+ * @returns {Promise<string|null>}
84
+ */
85
+ async function pickShippedTemplate(templates, promptFn) {
86
+ const ask = promptFn || ((qs) => require('inquirer').prompt(qs));
87
+ const { template } = await ask([{
88
+ type: 'list',
89
+ name: 'template',
90
+ message: 'App was not found. Create from which shipped application template?',
91
+ choices: [
92
+ ...templates.map((name) => ({ name, value: name })),
93
+ { name: 'Cancel', value: null }
94
+ ]
95
+ }]);
96
+ return template || null;
97
+ }
98
+
99
+ /**
100
+ * @param {string} appName
101
+ * @param {Object} [deps]
102
+ * @returns {Promise<string>}
103
+ */
104
+ async function materializeShippedTemplate(appName, deps = {}) {
105
+ const copy = deps.ensureAppFromTemplate || ensureAppFromTemplate;
106
+ await copy(appName);
107
+ return appName;
108
+ }
109
+
110
+ /**
111
+ * @param {string} appName
112
+ * @param {string[]} templates
113
+ * @param {Object} options
114
+ * @param {Object} deps
115
+ * @returns {Promise<string>}
116
+ */
117
+ async function createMatchingTemplateApp(appName, templates, options, deps) {
118
+ if (assumeYesCreate(options)) {
119
+ return materializeShippedTemplate(appName, deps);
120
+ }
121
+ if (!isInteractiveCreate(deps)) {
122
+ throw buildMissingAppError(appName, templates);
123
+ }
124
+ const ok = await confirmCreateMatchingTemplate(appName, deps.prompt);
125
+ if (!ok) {
126
+ throw buildMissingAppError(appName, templates);
127
+ }
128
+ return materializeShippedTemplate(appName, deps);
129
+ }
130
+
131
+ /**
132
+ * @param {string} appName
133
+ * @param {string[]} templates
134
+ * @param {Object} options
135
+ * @param {Object} deps
136
+ * @returns {Promise<string>}
137
+ */
138
+ async function createPickedTemplateApp(appName, templates, options, deps) {
139
+ if (assumeYesCreate(options) || !isInteractiveCreate(deps) || templates.length === 0) {
140
+ throw buildMissingAppError(appName, templates);
141
+ }
142
+ const chosen = await pickShippedTemplate(templates, deps.prompt);
143
+ if (!chosen) {
144
+ throw buildMissingAppError(appName, templates);
145
+ }
146
+ if (chosen !== appName) {
147
+ logger.log(metadata(
148
+ `Creating '${chosen}' from shipped template (requested '${appName}' was not found).`
149
+ ));
150
+ }
151
+ await materializeShippedTemplate(chosen, deps);
152
+ return chosen;
153
+ }
154
+
155
+ /**
156
+ * If the app is missing locally, offer to create it from shipped templates.
157
+ *
158
+ * @async
159
+ * @function offerCreateFromTemplateIfMissing
160
+ * @param {string} appName - Requested app or system key
161
+ * @param {Object} [options] - CLI options (`yes` skips the confirm when the name matches a template)
162
+ * @param {Object} [deps] - Test seams (prompt, isTty, listTemplates, findExisting, ensureAppFromTemplate)
163
+ * @returns {Promise<string>} App key to continue with (may differ if the user picked another template)
164
+ * @throws {Error} When the app is missing and the user declines or cannot be prompted
165
+ */
166
+ async function offerCreateFromTemplateIfMissing(appName, options = {}, deps = {}) {
167
+ const findExisting = deps.findExisting || findExistingAppType;
168
+ if (findExisting(appName, options)) {
169
+ return appName;
170
+ }
171
+ const listTemplates = deps.listTemplates || listAvailableTemplates;
172
+ const templates = await listTemplates();
173
+ if (templates.includes(appName)) {
174
+ return createMatchingTemplateApp(appName, templates, options, deps);
175
+ }
176
+ return createPickedTemplateApp(appName, templates, options, deps);
177
+ }
178
+
179
+ module.exports = {
180
+ offerCreateFromTemplateIfMissing,
181
+ buildMissingAppError,
182
+ assumeYesCreate,
183
+ isInteractiveCreate
184
+ };
@@ -11,6 +11,11 @@
11
11
 
12
12
  const chalk = require('chalk');
13
13
  const logger = require('../utils/logger');
14
+ const { formatCredentialShowLine } = require('../utils/authentication-slot-show');
15
+ const {
16
+ formatConnectedSystemLifecycleLabel,
17
+ formatLifecycleLabel
18
+ } = require('../utils/external-system-readiness-compact-display');
14
19
 
15
20
  function logSourceAndHeader(summary) {
16
21
  const isOffline = summary.source === 'offline';
@@ -93,14 +98,36 @@ function logApplicationFields(a) {
93
98
  logApplicationOptional(a);
94
99
  }
95
100
 
101
+ /**
102
+ * Draft / Published / Archived only — never controller `active`.
103
+ * @param {Object} a
104
+ * @param {Object} [summary]
105
+ * @returns {string|null}
106
+ */
107
+ function resolveExternalApplicationStatusLabel(a, summary) {
108
+ const ext = summary && summary.externalSystem;
109
+ const fromDataplane = ext && !ext.error ? (ext.status || ext.dataplaneStatus) : null;
110
+ return formatConnectedSystemLifecycleLabel(fromDataplane || a.status);
111
+ }
112
+
113
+ function logExternalApplicationStatus(a, summary) {
114
+ const label = resolveExternalApplicationStatusLabel(a, summary);
115
+ if (label) logger.log(` Status: ${label}`);
116
+ }
117
+
118
+ function formatDataplaneStatusDisplay(status) {
119
+ if (status === undefined || status === null || status === '' || status === '—') return '—';
120
+ return formatConnectedSystemLifecycleLabel(status) || formatLifecycleLabel(status);
121
+ }
122
+
96
123
  /** Application section for online + external: Key, Display name, Description, Deployment, Status (no Type; Dataplane block follows). */
97
- function logApplicationFieldsOnlineExternal(a) {
124
+ function logApplicationFieldsOnlineExternal(a, summary) {
98
125
  logger.log('🧷 Application - external');
99
126
  logger.log(` Key: ${a.key ?? '—'}`);
100
127
  logger.log(` Display name: ${a.displayName ?? '—'}`);
101
128
  logger.log(` Description: ${a.description ?? '—'}`);
102
129
  logger.log(' Deployment: —');
103
- if (a.status !== undefined && a.status !== '—') logger.log(` Status: ${a.status}`);
130
+ logExternalApplicationStatus(a, summary);
104
131
  }
105
132
 
106
133
  /** Application section for offline + external: Key, Display name, Description, Deployment, Status, Version when present. */
@@ -110,7 +137,7 @@ function logApplicationFieldsOfflineExternal(a) {
110
137
  logger.log(` Display name: ${a.displayName ?? '—'}`);
111
138
  logger.log(` Description: ${a.description ?? '—'}`);
112
139
  logger.log(' Deployment: —');
113
- if (a.status !== undefined && a.status !== '—') logger.log(` Status: ${a.status}`);
140
+ logExternalApplicationStatus(a);
114
141
  if (a.version !== undefined && a.version !== null) logger.log(` Version: ${a.version}`);
115
142
  }
116
143
 
@@ -129,7 +156,7 @@ function logApplicationSection(a, summary) {
129
156
  const onlineExternal = summary.source === 'online' && isExternal;
130
157
  const offlineExternal = summary.source === 'offline' && isExternal;
131
158
  if (onlineExternal) {
132
- logApplicationFieldsOnlineExternal(a);
159
+ logApplicationFieldsOnlineExternal(a, summary);
133
160
  } else if (offlineExternal) {
134
161
  logApplicationFieldsOfflineExternal(a);
135
162
  } else {
@@ -205,8 +232,8 @@ function logDatabasesSection(dbNames) {
205
232
  function logExternalSystemMain(ext) {
206
233
  logger.log('🧩 Dataplane');
207
234
  logger.log(` Version: ${ext.version ?? '—'}`);
208
- logger.log(` Credential: ${ext.credentialId ?? '—'}`);
209
- logger.log(` Status: ${ext.status ?? '—'}`);
235
+ logger.log(` Credential: ${formatCredentialShowLine(ext)}`);
236
+ logger.log(` Status: ${formatDataplaneStatusDisplay(ext.status)}`);
210
237
  logger.log(` API docs: ${ext.openApiDocsPageUrl ?? ext.apiDocumentUrl ?? '—'}`);
211
238
  logger.log(` MCP server: ${ext.mcpServerUrl ?? '—'}`);
212
239
  logger.log(` OpenAPI spec: ${ext.apiDocumentUrl ?? '—'}`);