@aifabrix/builder 2.54.1 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) 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 +130 -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 +239 -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/state-file.js +83 -0
  23. package/lib/agent-kit/status.js +50 -0
  24. package/lib/agent-kit/v1-detect.js +81 -0
  25. package/lib/agent-kit/validate-kit.js +87 -0
  26. package/lib/agent-kit/workspace-name.js +40 -0
  27. package/lib/agent-kit/workspace-skeleton.js +103 -0
  28. package/lib/api/role-assistant-test-pack.api.js +86 -0
  29. package/lib/api/work-search.api.js +31 -4
  30. package/lib/api/work.api.js +19 -12
  31. package/lib/app/list-external-systems.js +2 -1
  32. package/lib/app/show-display.js +33 -6
  33. package/lib/app/show.js +25 -2
  34. package/lib/channels/add-channel.js +1 -0
  35. package/lib/channels/approval-guides/claude.js +2 -2
  36. package/lib/channels/channel-add-failure.js +1 -0
  37. package/lib/channels/channel-add-remaining.js +15 -9
  38. package/lib/channels/channel-definition-status.js +115 -0
  39. package/lib/channels/channel-description.js +2 -1
  40. package/lib/channels/channel-install-envelope.js +4 -2
  41. package/lib/channels/channel-integration-generator.js +2 -0
  42. package/lib/channels/channel-runtime-resolver.js +14 -0
  43. package/lib/channels/channel-targets.js +31 -5
  44. package/lib/channels/claude-setup-values.js +4 -1
  45. package/lib/channels/slack-channel-add.js +59 -6
  46. package/lib/channels/slack-mcp-callback.js +51 -0
  47. package/lib/channels/slack-mcp-runtime.js +156 -0
  48. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  49. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  50. package/lib/cli/index.js +4 -0
  51. package/lib/cli/setup-certification-commands.js +2 -2
  52. package/lib/cli/setup-certification.help.js +3 -1
  53. package/lib/commands/agent-kit-help.js +79 -0
  54. package/lib/commands/agent-kit.js +346 -0
  55. package/lib/commands/channel-add-help.js +5 -5
  56. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  57. package/lib/commands/repair-auth-apply.js +1 -1
  58. package/lib/commands/repair-datasource-key-skip.js +39 -0
  59. package/lib/commands/repair-datasource-keys.js +24 -12
  60. package/lib/commands/repair-rbac.js +16 -10
  61. package/lib/commands/role-assistant-copy-output.js +61 -0
  62. package/lib/commands/role-assistant-copy.js +155 -0
  63. package/lib/commands/role-assistant-test-output.js +8 -9
  64. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  65. package/lib/commands/role-assistant.js +117 -48
  66. package/lib/commands/search.js +143 -0
  67. package/lib/commands/test-trust-external.js +7 -5
  68. package/lib/commands/upload-sidecar-followups.js +4 -1
  69. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  70. package/lib/commands/verify-operations-step-plan.js +6 -33
  71. package/lib/commands/verify-operations-steps.js +3 -1
  72. package/lib/datasource/capability/relate-operations.js +28 -10
  73. package/lib/datasource/capability/run-capability-relate.js +48 -25
  74. package/lib/datasource/datasource-identifier.js +13 -5
  75. package/lib/deployment/deployer-poll-run.js +117 -35
  76. package/lib/external-system/deploy-dataplane-published.js +70 -0
  77. package/lib/external-system/deploy.js +12 -2
  78. package/lib/external-system/download-role-assistant.js +41 -15
  79. package/lib/external-system/download.js +7 -1
  80. package/lib/generator/wizard-datasource-key.js +54 -0
  81. package/lib/generator/wizard.js +55 -54
  82. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  83. package/lib/integration-definition/apply-relationships.js +195 -0
  84. package/lib/integration-definition/apply-static-enterprise.js +229 -0
  85. package/lib/integration-definition/apply-system-file.js +71 -0
  86. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  87. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  88. package/lib/integration-definition/apply.js +78 -59
  89. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  90. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  91. package/lib/lifecycle/role-assistant-governance.js +56 -21
  92. package/lib/programmatic/capability-registry.js +1 -0
  93. package/lib/programmatic/index.js +2 -0
  94. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  95. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  96. package/lib/programmatic/openapi-component-schemas.js +1 -0
  97. package/lib/programmatic/openapi-descriptions.js +9 -3
  98. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  99. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  100. package/lib/programmatic/openapi-request-schemas.js +5 -0
  101. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  102. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  103. package/lib/programmatic/openapi-spec.js +1 -0
  104. package/lib/programmatic/request-timeout.js +3 -0
  105. package/lib/programmatic/route-handler-map.js +7 -0
  106. package/lib/programmatic/run-channel-catalog.js +50 -8
  107. package/lib/programmatic/run-channel-install-options.js +5 -1
  108. package/lib/programmatic/run-download.js +41 -0
  109. package/lib/protection/auth-context.js +3 -1
  110. package/lib/protection/protection-display.js +3 -2
  111. package/lib/protection/report-exit.js +50 -0
  112. package/lib/protection/run-commands.js +2 -0
  113. package/lib/role-assistant/package-validate.js +175 -0
  114. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  115. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  116. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  117. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  118. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  119. package/lib/role-assistant/test-case-copy.js +119 -0
  120. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  121. package/lib/role-assistant/test-case-validate.js +88 -0
  122. package/lib/role-assistant/test-cases-bid.js +117 -4
  123. package/lib/role-assistant/test-cases-evaluate.js +137 -30
  124. package/lib/role-assistant/test-cases-honesty.js +155 -6
  125. package/lib/role-assistant/test-cases-lifecycle.js +291 -0
  126. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  127. package/lib/role-assistant/test-cases-search.js +20 -2
  128. package/lib/role-assistant/test-cases.js +34 -168
  129. package/lib/role-assistant/test-pack-map.js +180 -0
  130. package/lib/role-assistant/test-pack-sync.js +187 -0
  131. package/lib/role-assistant/test-runner-debug.js +54 -0
  132. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  133. package/lib/role-assistant/test-runner-search.js +20 -5
  134. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  135. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  136. package/lib/role-assistant/test-runner-workhub.js +45 -21
  137. package/lib/role-assistant/test-runner.js +50 -10
  138. package/lib/schema/external-datasource.schema.json +36 -5
  139. package/lib/schema/external-system.schema.json +20 -2
  140. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  141. package/lib/search/dataplane-context.js +23 -0
  142. package/lib/search/search-args.js +211 -0
  143. package/lib/search/search-display.js +238 -0
  144. package/lib/search/search-errors.js +50 -0
  145. package/lib/search/search-prompts.js +119 -0
  146. package/lib/search/search-roles.js +85 -0
  147. package/lib/search/search-run.js +241 -0
  148. package/lib/search/search-skills.js +198 -0
  149. package/lib/utils/authentication-slot-show.js +153 -0
  150. package/lib/utils/credential-display.js +43 -1
  151. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  152. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  153. package/lib/utils/external-system-validators.js +2 -1
  154. package/lib/utils/help-builder.js +3 -1
  155. package/lib/utils/schema-loader.js +37 -0
  156. package/lib/validation/validate-external-file.js +5 -1
  157. package/lib/validation/validate.js +3 -2
  158. package/package.json +1 -1
  159. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  160. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  161. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  162. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  163. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  164. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  165. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  166. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  167. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  168. package/templates/agent-kit/agent-kit.yaml +45 -0
  169. package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
  170. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  171. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  172. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  173. package/templates/agent-kit/parity-inventory.yaml +188 -0
  174. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
  175. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  176. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  177. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  178. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  179. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
  180. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  181. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  182. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  183. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
  184. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  185. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  186. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  187. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
  188. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  189. package/templates/agent-kit/skills/shared/hosts.md +78 -0
  190. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  191. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  192. package/templates/agent-kit/workspace/README.md +89 -0
  193. package/templates/applications/builder-api/Dockerfile +3 -1
  194. package/templates/applications/dataplane/application.yaml +2 -2
  195. package/templates/applications/dataplane/env.template +1 -1
  196. package/templates/applications/dataplane/rbac.yaml +28 -0
  197. package/templates/applications/keycloak/env.template +3 -3
  198. package/templates/applications/miso-controller/application.yaml +1 -1
  199. package/templates/applications/miso-controller/env.template +8 -9
  200. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  201. package/templates/channels/claude/channel.json +1 -1
  202. package/templates/channels/claude/install.template.md.hbs +1 -0
  203. package/templates/channels/claude/integration/claude-system.json +1 -1
  204. package/templates/channels/slack/channel.json +11 -0
  205. package/templates/channels/slack/install.template.md.hbs +9 -0
  206. package/templates/channels/slack/integration/slack-system.json +11 -0
  207. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  208. package/templates/channels/teams-bot/channel.json +1 -0
  209. package/templates/python/docker-compose.hbs +14 -0
  210. package/templates/typescript/docker-compose.hbs +14 -0
@@ -0,0 +1,155 @@
1
+ /**
2
+ * aifabrix role-assistant copy — capture one Work Hub execution as YAML.
3
+ *
4
+ * @fileoverview Leaf command; Builder materializes the case from Work Hub reference
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { resolveLifecycleAuth } = require('./lifecycle-external');
10
+ const { fetchWorkCopyReference, buildCopyCaseFromWorkReference } = require(
11
+ '../role-assistant/test-case-copy-from-work'
12
+ );
13
+ const { roleAssistantSystemKey } = require('../utils/role-assistant-paths');
14
+ const { roleAssistantTestsDir } = require('../role-assistant/test-cases');
15
+ const {
16
+ defaultCaseIdFromReference,
17
+ assertProcessExecutionId,
18
+ assertFolderToken,
19
+ copyTargetPath,
20
+ prepareCopyDocument,
21
+ yamlFromValidatedCase,
22
+ assertCopyTargetWritable,
23
+ persistCopyCaseYaml
24
+ } = require('../role-assistant/test-case-copy');
25
+ const { emitCopySuccess, emitCopyFailure } = require('./role-assistant-copy-output');
26
+
27
+ function copyHelpAfter() {
28
+ return `
29
+ Examples:
30
+ $ aifabrix role-assistant copy customer-support-bot --suite 001 --reference a9f09fb8-d9eb-477f-8bb4-59e670fc82a4
31
+ $ aifabrix role-assistant copy customer-support-bot --suite 001 --reference a9f09fb8-d9eb-477f-8bb4-59e670fc82a4 --case address-change
32
+ $ aifabrix role-assistant copy customer-support-bot --suite 001 --reference a9f09fb8-d9eb-477f-8bb4-59e670fc82a4 --dry-run
33
+ $ aifabrix role-assistant copy customer-support-bot --suite 001 --reference a9f09fb8-d9eb-477f-8bb4-59e670fc82a4 --json
34
+
35
+ Notes:
36
+ - <roleKey> is bare; package folder is role-assistant/ra-<roleKey>/.
37
+ - --reference is the Work Hub execution id (the same id printed after a run).
38
+ - Writes tests/<suite>/<caseId>.yaml. Default caseId is copy-<first 8 hex of the execution id>.
39
+ - Existing files are not overwritten unless --force is set.
40
+ - --dry-run prints YAML and does not write.
41
+ - Builds local YAML from Work Hub execution status and result. Does not publish; use upload / deploy.
42
+ `;
43
+ }
44
+
45
+ function normalizeCopyOptions(options) {
46
+ const opts = options && typeof options === 'object' ? options : {};
47
+ return {
48
+ env: opts.env || 'dev',
49
+ suite: opts.suite,
50
+ reference: opts.reference,
51
+ case: opts.case,
52
+ force: opts.force === true,
53
+ dryRun: opts.dryRun === true,
54
+ json: opts.json === true,
55
+ testsDir: opts.testsDir || null
56
+ };
57
+ }
58
+
59
+ function requireCopyFlags(opts) {
60
+ if (!opts.suite) {
61
+ throw new Error('--suite is required');
62
+ }
63
+ if (!opts.reference) {
64
+ throw new Error('--reference is required');
65
+ }
66
+ }
67
+
68
+ async function fetchCopyCase(systemKey, opts, peId, caseId) {
69
+ const auth = await resolveLifecycleAuth(systemKey, {
70
+ env: opts.env,
71
+ run: false,
72
+ noPublish: true
73
+ });
74
+ if (!auth || !auth.dataplaneUrl || !auth.authConfig) {
75
+ throw new Error('Not authenticated. Run aifabrix login, then retry.');
76
+ }
77
+ const reference = await fetchWorkCopyReference(
78
+ auth.dataplaneUrl,
79
+ auth.authConfig,
80
+ peId
81
+ );
82
+ return buildCopyCaseFromWorkReference({
83
+ systemKey,
84
+ caseId,
85
+ processExecutionId: peId,
86
+ status: reference.status,
87
+ result: reference.result
88
+ });
89
+ }
90
+
91
+ async function runRoleAssistantCopy(roleKey, options) {
92
+ const opts = normalizeCopyOptions(options);
93
+ requireCopyFlags(opts);
94
+ const peId = assertProcessExecutionId(opts.reference);
95
+ const suiteId = assertFolderToken(opts.suite, 'Suite id');
96
+ const caseId = assertFolderToken(
97
+ opts.case || defaultCaseIdFromReference(peId),
98
+ 'Case id'
99
+ );
100
+ const systemKey = roleAssistantSystemKey(roleKey);
101
+ const testsDir = roleAssistantTestsDir(systemKey, { testsDir: opts.testsDir });
102
+ const targetPath = copyTargetPath(testsDir, suiteId, caseId);
103
+ if (!opts.dryRun) {
104
+ assertCopyTargetWritable(targetPath, opts.force);
105
+ }
106
+ const payload = await fetchCopyCase(systemKey, opts, peId, caseId);
107
+ const caseDoc = prepareCopyDocument(payload, caseId, peId);
108
+ const yamlText = yamlFromValidatedCase(caseDoc);
109
+ if (!opts.dryRun) {
110
+ persistCopyCaseYaml(targetPath, yamlText);
111
+ }
112
+ return {
113
+ success: true,
114
+ path: targetPath,
115
+ caseId,
116
+ suiteId,
117
+ processExecutionId: peId,
118
+ warnings: payload && payload.warnings,
119
+ yaml: yamlText,
120
+ dryRun: opts.dryRun
121
+ };
122
+ }
123
+
124
+ async function roleAssistantCopyHandler(roleKey, options) {
125
+ try {
126
+ const result = await runRoleAssistantCopy(roleKey, options);
127
+ emitCopySuccess(options, result);
128
+ } catch (error) {
129
+ emitCopyFailure(options, error);
130
+ process.exit(error && error.exitCode ? error.exitCode : 1);
131
+ }
132
+ }
133
+
134
+ function registerRoleAssistantCopyCommand(roleAssistant) {
135
+ roleAssistant
136
+ .command('copy <roleKey>')
137
+ .description('Capture one Work Hub execution as a local YAML test case')
138
+ .requiredOption('--suite <suiteId>', 'Suite folder under tests/ (for example 001)')
139
+ .requiredOption('--reference <executionId>', 'Work Hub execution id')
140
+ .option('--case <caseId>', 'Case id (default: copy-<first 8 hex of the execution id>)')
141
+ .option('--force', 'Overwrite an existing YAML file')
142
+ .option('--dry-run', 'Print YAML and do not write')
143
+ .option('-e, --env <env>', 'Environment: dev, tst, or pro', 'dev')
144
+ .option('--json', 'Machine-readable output envelope')
145
+ .addHelpText('after', copyHelpAfter())
146
+ .action((roleKey, options) => roleAssistantCopyHandler(roleKey, options));
147
+ }
148
+
149
+ module.exports = {
150
+ copyHelpAfter,
151
+ normalizeCopyOptions,
152
+ runRoleAssistantCopy,
153
+ roleAssistantCopyHandler,
154
+ registerRoleAssistantCopyCommand
155
+ };
@@ -16,6 +16,7 @@ const {
16
16
  formatBlockingError,
17
17
  metadata
18
18
  } = require('../utils/cli-test-layout-chalk');
19
+ const { formatCaseResultRows } = require('./role-assistant-test-result-rows');
19
20
 
20
21
  /**
21
22
  * Bucket a lifecycle step status into ok | failed | skipped | other.
@@ -157,15 +158,12 @@ function displayCaseResultSummary(caseResult) {
157
158
  }
158
159
  if (caseResult.passed) {
159
160
  logger.log(formatSuccessLine('Case assertions passed.'));
160
- const warnings = Array.isArray(caseResult.warnings) ? caseResult.warnings : [];
161
- for (const row of warnings) {
162
- logger.log(formatWarningLine(row.message || String(row)));
163
- }
164
- return;
161
+ } else {
162
+ logger.log(formatBlockingError('Case assertions failed.'));
165
163
  }
166
- logger.log(formatBlockingError('Case assertions failed.'));
167
- for (const row of caseResult.failedAssertions) {
168
- logger.log(formatWarningLine(row.message));
164
+ logger.log(metadata('Results:'));
165
+ for (const line of formatCaseResultRows(caseResult.assertions)) {
166
+ logger.log(line);
169
167
  }
170
168
  }
171
169
 
@@ -210,5 +208,6 @@ function displayRoleAssistantTestSummary(roleKey, verdict, portalUrl, result, ca
210
208
  module.exports = {
211
209
  formatStepsRunSummary,
212
210
  buildRoleAssistantTestEnvelope,
213
- displayRoleAssistantTestSummary
211
+ displayRoleAssistantTestSummary,
212
+ formatCaseResultRows
214
213
  };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Human TTY lines for Role Assistant case assertion rows.
3
+ * @fileoverview Canonical glyphs for pass/fail/warn/notes/skip
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const {
9
+ formatSuccessLine,
10
+ formatWarningLine,
11
+ formatBlockingError,
12
+ infoLine,
13
+ metadata
14
+ } = require('../utils/cli-test-layout-chalk');
15
+ const { withKind } = require('../role-assistant/test-case-assertion-row');
16
+
17
+ function formatCaseResultLine(assertion) {
18
+ const row = withKind(assertion || {});
19
+ const text = `${row.id || 'assertion'} — ${row.message || ''}`.trim();
20
+ if (row.kind === 'fail') {
21
+ return ` ${formatBlockingError(text)}`;
22
+ }
23
+ if (row.kind === 'warn') {
24
+ return ` ${formatWarningLine(text)}`;
25
+ }
26
+ if (row.kind === 'notes') {
27
+ return ` ${infoLine(`ℹ ${text}`)}`;
28
+ }
29
+ if (row.kind === 'skip') {
30
+ return ` ${metadata(`⏭ ${text}`)}`;
31
+ }
32
+ return ` ${formatSuccessLine(text)}`;
33
+ }
34
+
35
+ function formatCaseResultRows(assertions) {
36
+ return (Array.isArray(assertions) ? assertions : []).map(formatCaseResultLine);
37
+ }
38
+
39
+ module.exports = {
40
+ formatCaseResultLine,
41
+ formatCaseResultRows
42
+ };
@@ -35,6 +35,11 @@ const {
35
35
  lifecycleVerdictFromCaseExecution
36
36
  } = require('../role-assistant/test-runner');
37
37
  const { assertRoleAssistantEvidencePreflight } = require('../role-assistant/test-cases-evidence');
38
+ const { assertLocalCasesBeforeAuth } = require('../role-assistant/test-case-validate');
39
+ const {
40
+ loadPublishedTestsIntoOptions,
41
+ patchCaseStatusAfterRun
42
+ } = require('../role-assistant/test-pack-sync');
38
43
  const { collectString } = require('./governance');
39
44
  const {
40
45
  sectionTitle,
@@ -50,11 +55,13 @@ const {
50
55
  bareRoleKey,
51
56
  roleAssistantSystemKey
52
57
  } = require('../utils/role-assistant-paths');
58
+ const { registerRoleAssistantCopyCommand } = require('./role-assistant-copy');
53
59
 
54
60
  function roleAssistantGroupHelpAfter() {
55
61
  return `
56
62
  Subcommands:
57
63
  test <roleKey> Run lifecycle checks, optional YAML cases/suites, and print a portal deep link
64
+ copy <roleKey> Capture one Work Hub execution as a local YAML test case
58
65
 
59
66
  Package workspace (top-level commands — same digital-worker package):
60
67
  download / validate / upload / deploy <roleKey> Manage role-assistant/ra-<roleKey>/
@@ -64,13 +71,16 @@ Examples:
64
71
  $ aifabrix role-assistant test customer-support-bot --suite 001
65
72
  $ aifabrix role-assistant test customer-support-bot --list-cases
66
73
  $ aifabrix role-assistant test customer-support-bot --env tst --json
74
+ $ aifabrix role-assistant copy customer-support-bot --suite 001 --reference a9f09fb8-d9eb-477f-8bb4-59e670fc82a4
67
75
  $ aifabrix upload customer-support-bot --env dev
68
76
  $ aifabrix test ra-customer-support-bot
69
77
 
70
78
  Notes:
71
79
  - Local packages live under role-assistant/ra-<roleKey>/ (bare <roleKey> in CLI args).
72
80
  - test uses the public/running worker by default; pass --publish (or upload) to refresh from local files.
81
+ - copy writes tests/<suite>/<caseId>.yaml from a Work Hub execution id; it does not publish.
73
82
  - See: aifabrix role-assistant test -h
83
+ - See: aifabrix role-assistant copy -h
74
84
  `;
75
85
  }
76
86
 
@@ -84,6 +94,7 @@ Examples:
84
94
  $ aifabrix role-assistant test customer-support-bot --suite 001
85
95
  $ aifabrix role-assistant test customer-support-bot --list-cases
86
96
  $ aifabrix role-assistant test customer-support-bot --suite 001 --fail-fast --json
97
+ $ aifabrix role-assistant test customer-support-bot --from-dataplane
87
98
  $ aifabrix test ra-customer-support-bot
88
99
 
89
100
  Notes:
@@ -93,6 +104,8 @@ Notes:
93
104
  - Runs lifecycle checks, then prints a portal deep link for full step-by-step status.
94
105
  - Case files with input.message/input.content execute Work Hub plan→accept (or work.start when executionPath: intent).
95
106
  - Cases with executionPath: search|skills (input.executionPath or case-root executionPath) run work.skills / work.search instead of Work Hub.
107
+ - After each case, current status is stored on the published test pack (passed, failed, or skipped).
108
+ - --from-dataplane loads published tests, then runs them (all suites unless --case / --suite is set).
96
109
  - Role Assistant packages do not need application.yaml; test uses device login and the published dataplane worker.
97
110
  - Cases with preflight.evidence.requiredKeys read live Evidence from the dataplane before start and hard-stop if those keys are missing or not active.
98
111
  - answers[] and approvals[] are matched to runtime wait payloads (questionKey/requestKey/waitReason).
@@ -138,6 +151,7 @@ function normalizeRoleAssistantTestOptions(options, cmd) {
138
151
  casesRoot: trimOptionalString(options.casesRoot),
139
152
  listCases: optionOrFlag(options, 'listCases', cmd, '--list-cases'),
140
153
  failFast: optionOrFlag(options, 'failFast', cmd, '--fail-fast'),
154
+ fromDataplane: optionOrFlag(options, 'fromDataplane', cmd, '--from-dataplane'),
141
155
  run: true
142
156
  };
143
157
  }
@@ -220,25 +234,38 @@ async function resolveSuiteCaseResults(systemKey, opts, result, verdict, portalU
220
234
  return outcomes;
221
235
  }
222
236
 
237
+ async function maybeLoadFromDataplane(systemKey, opts) {
238
+ if (!opts.fromDataplane) {
239
+ return opts;
240
+ }
241
+ const auth = await resolveLifecycleAuth(systemKey, { ...opts, run: false, noPublish: true });
242
+ return loadPublishedTestsIntoOptions(systemKey, opts, auth);
243
+ }
244
+
245
+ async function resolveSelectedCaseResults(systemKey, opts, result, verdict, portalUrl) {
246
+ const runDiscovered = Boolean(opts.suite || (opts.fromDataplane && !opts.case));
247
+ if (runDiscovered) {
248
+ return resolveSuiteCaseResults(systemKey, opts, result, verdict, portalUrl);
249
+ }
250
+ return resolveRoleAssistantCaseResult(systemKey, opts, result, verdict, portalUrl);
251
+ }
252
+
223
253
  async function runRoleAssistantTestAction(roleKey, options, cmd) {
224
- const opts = normalizeRoleAssistantTestOptions(options, cmd);
254
+ let opts = normalizeRoleAssistantTestOptions(options, cmd);
225
255
  const systemKey = roleAssistantSystemKey(roleKey);
226
256
  const displayKey = bareRoleKey(roleKey);
257
+ opts = await maybeLoadFromDataplane(systemKey, opts);
227
258
 
228
259
  if (opts.listCases) {
229
260
  return listRoleAssistantCases(systemKey, opts);
230
261
  }
231
262
 
263
+ assertLocalCasesBeforeAuth(systemKey, opts);
264
+
232
265
  const result = await runLifecycleForExternalSystem(systemKey, opts);
233
266
  const verdict = lifecycleVerdictFromReport(result.report || {});
234
267
  const portalUrl = buildExternalSystemUiUrl(result.dataplaneUiBaseUrl, systemKey);
235
-
236
- let caseResult = null;
237
- if (opts.suite) {
238
- caseResult = await resolveSuiteCaseResults(systemKey, opts, result, verdict, portalUrl);
239
- } else {
240
- caseResult = await resolveRoleAssistantCaseResult(systemKey, opts, result, verdict, portalUrl);
241
- }
268
+ const caseResult = await resolveSelectedCaseResults(systemKey, opts, result, verdict, portalUrl);
242
269
 
243
270
  if (opts.json) {
244
271
  logger.log(
@@ -259,6 +286,28 @@ async function runRoleAssistantTestAction(roleKey, options, cmd) {
259
286
  return exitCode;
260
287
  }
261
288
 
289
+ async function patchPublishedCaseStatus(roleKey, opts, roleCase, evaluation, authContext) {
290
+ const caseId = roleCase && roleCase.caseId;
291
+ if (!caseId) {
292
+ return evaluation;
293
+ }
294
+ let auth = authContext;
295
+ if (!auth || !auth.dataplaneUrl || !auth.authConfig) {
296
+ auth = await resolveLifecycleAuth(roleKey, { ...opts, run: false, noPublish: true });
297
+ }
298
+ if (!auth || !auth.dataplaneUrl || !auth.authConfig) {
299
+ return evaluation;
300
+ }
301
+ await patchCaseStatusAfterRun({
302
+ systemKey: roleAssistantSystemKey(roleKey),
303
+ caseId,
304
+ evaluation,
305
+ dataplaneUrl: auth.dataplaneUrl,
306
+ authConfig: auth.authConfig
307
+ });
308
+ return evaluation;
309
+ }
310
+
262
311
  function attachCaseExecution(evaluation, executionResult, error) {
263
312
  const errMsg = error && (error.message || String(error));
264
313
  evaluation.execution = {
@@ -298,32 +347,35 @@ async function resolveRoleAssistantCaseResult(roleKey, opts, result, verdict, po
298
347
  const roleCase = loadRoleAssistantTestCase(roleKey, opts.case, {
299
348
  testsDir: opts.testsDir || null
300
349
  });
350
+ let evaluation;
301
351
  if (!isExecutableRoleAssistantCase(roleCase)) {
302
- return evaluateRoleAssistantTestCase(roleCase, result, verdict, portalUrl);
303
- }
304
- try {
305
- return await resolveExecutableCaseResult(
306
- roleKey,
307
- roleCase,
308
- opts,
309
- result,
310
- verdict,
311
- portalUrl
312
- );
313
- } catch (error) {
314
- const failedInput = mergeCaseExecutionIntoLifecycleResult(result, {
315
- finalStatus: { status: 'failed' },
316
- workResult: null,
317
- workResultError: error.message || String(error),
318
- interactions: []
319
- });
320
- failedInput.workResultError = error.message || String(error);
321
- return attachCaseExecution(
322
- evaluateRoleAssistantTestCase(roleCase, failedInput, verdict, portalUrl),
323
- null,
324
- error
325
- );
352
+ evaluation = evaluateRoleAssistantTestCase(roleCase, result, verdict, portalUrl);
353
+ } else {
354
+ try {
355
+ evaluation = await resolveExecutableCaseResult(
356
+ roleKey,
357
+ roleCase,
358
+ opts,
359
+ result,
360
+ verdict,
361
+ portalUrl
362
+ );
363
+ } catch (error) {
364
+ const failedInput = mergeCaseExecutionIntoLifecycleResult(result, {
365
+ finalStatus: { status: 'failed' },
366
+ workResult: null,
367
+ workResultError: error.message || String(error),
368
+ interactions: []
369
+ });
370
+ failedInput.workResultError = error.message || String(error);
371
+ evaluation = attachCaseExecution(
372
+ evaluateRoleAssistantTestCase(roleCase, failedInput, verdict, portalUrl),
373
+ null,
374
+ error
375
+ );
376
+ }
326
377
  }
378
+ return patchPublishedCaseStatus(roleKey, opts, roleCase, evaluation);
327
379
  }
328
380
 
329
381
  async function roleAssistantTestHandler(roleKey, options, cmd) {
@@ -335,19 +387,8 @@ async function roleAssistantTestHandler(roleKey, options, cmd) {
335
387
  }
336
388
  }
337
389
 
338
- function setupRoleAssistantCommands(program) {
339
- const roleAssistant = program
340
- .command('role-assistant')
341
- .description(
342
- 'Role Assistant packages: lifecycle checks, YAML cases/suites, and portal deep links'
343
- )
344
- .addHelpText('after', roleAssistantGroupHelpAfter());
345
-
346
- roleAssistant
347
- .command('test <roleKey>')
348
- .description(
349
- 'Lifecycle checks + optional cases/suites; prints portal deep link (bare <roleKey>)'
350
- )
390
+ function addRoleAssistantTestRunOptions(cmd) {
391
+ return cmd
351
392
  .option('-e, --env <env>', 'Environment: dev, tst, or pro', 'dev')
352
393
  .option('--case <caseIdOrPath>', 'Run and evaluate one role-assistant test case')
353
394
  .option('--tests-dir <dir>', 'Override role-assistant test case directory')
@@ -361,8 +402,16 @@ function setupRoleAssistantCommands(program) {
361
402
  )
362
403
  .option('--list-cases', 'List discoverable case files and exit')
363
404
  .option('--fail-fast', 'Stop suite run after the first failing case')
405
+ .option(
406
+ '--from-dataplane',
407
+ 'Load published tests from the dataplane, then run them (all suites unless --case or --suite is set)'
408
+ )
364
409
  .option('-v, --verbose', 'Include full lifecycle report payload in --json output')
365
- .option('-d, --debug', 'Write debug logs under role-assistant/<roleKey>/logs/')
410
+ .option('-d, --debug', 'Write debug logs under role-assistant/<roleKey>/logs/');
411
+ }
412
+
413
+ function addRoleAssistantTestPublishOptions(cmd) {
414
+ return cmd
366
415
  .option(
367
416
  '--publish',
368
417
  'Publish local Role Assistant package before checks (default: use public/running worker)'
@@ -385,9 +434,29 @@ function setupRoleAssistantCommands(program) {
385
434
  'Ignored for Role Assistant packages (Connected System governance generate does not run)',
386
435
  'bronze'
387
436
  )
388
- .option('--json', 'Machine-readable output envelope')
437
+ .option('--json', 'Machine-readable output envelope');
438
+ }
439
+
440
+ function registerRoleAssistantTestCommand(roleAssistant) {
441
+ const cmd = roleAssistant
442
+ .command('test <roleKey>')
443
+ .description(
444
+ 'Lifecycle checks + optional cases/suites; prints portal deep link (bare <roleKey>)'
445
+ );
446
+ addRoleAssistantTestPublishOptions(addRoleAssistantTestRunOptions(cmd))
389
447
  .addHelpText('after', roleAssistantHelpAfter())
390
- .action((roleKey, options, cmd) => roleAssistantTestHandler(roleKey, options, cmd));
448
+ .action((roleKey, options, invoked) => roleAssistantTestHandler(roleKey, options, invoked));
449
+ }
450
+
451
+ function setupRoleAssistantCommands(program) {
452
+ const roleAssistant = program
453
+ .command('role-assistant')
454
+ .description(
455
+ 'Role Assistant packages: lifecycle checks, YAML cases/suites, copy from Work Hub, and portal deep links'
456
+ )
457
+ .addHelpText('after', roleAssistantGroupHelpAfter());
458
+ registerRoleAssistantTestCommand(roleAssistant);
459
+ registerRoleAssistantCopyCommand(roleAssistant);
391
460
  }
392
461
 
393
462
  module.exports = {
@@ -0,0 +1,143 @@
1
+ /**
2
+ * aifabrix search — retrieve governed Role Assistant information.
3
+ *
4
+ * @fileoverview Commander registration for search and search skills
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const logger = require('../utils/logger');
12
+ const { runSearch, runSearchSkills } = require('../search/search-run');
13
+ const { exitCodeForSearchError } = require('../search/search-errors');
14
+ const { emitSearchError } = require('../search/search-display');
15
+
16
+ const SEARCH_HELP = `
17
+
18
+ Examples:
19
+ $ aifabrix search
20
+ $ aifabrix search --role ra-cfo --resource-type customer
21
+ $ aifabrix search --role ra-cfo --resource-type customer --operation list --json
22
+ $ aifabrix search --role ra-cfo --resource-type customer --output ./search.json
23
+ $ aifabrix search skills --role ra-cfo
24
+ `;
25
+
26
+ const SKILLS_HELP = `
27
+
28
+ Examples:
29
+ $ aifabrix search skills
30
+ $ aifabrix search skills --role ra-cfo
31
+ $ aifabrix search skills --role ra-cfo --json
32
+ $ aifabrix search skills --role ra-cfo --output ./skills.json
33
+ `;
34
+
35
+ /**
36
+ * @param {*} value
37
+ * @param {string[]} previous
38
+ * @returns {string[]}
39
+ */
40
+ function collectKnowledgeKey(value, previous) {
41
+ const next = Array.isArray(previous) ? previous : [];
42
+ next.push(value);
43
+ return next;
44
+ }
45
+
46
+ /**
47
+ * @param {import('commander').Command} cmd
48
+ */
49
+ function addSharedSearchOptions(cmd) {
50
+ cmd
51
+ .option('--role <roleRef>', 'Role Assistant key (for example ra-cfo)')
52
+ .option('--role-ref <roleRef>', 'Alias for --role')
53
+ .option('-o, --output <file>', 'Write the JSON payload to this file')
54
+ .option('--no-interactive', 'Never prompt; missing required flags fail')
55
+ .option('-e, --env <environment>', 'Environment key')
56
+ .option('--json', 'Print the JSON payload on stdout (no prompts)')
57
+ .option('-v, --verbose', 'Include extra TTY detail when available');
58
+ }
59
+
60
+ /**
61
+ * @param {import('commander').Command} cmd
62
+ */
63
+ function addSearchBodyOptions(cmd) {
64
+ cmd
65
+ .option('--resource-type <key>', 'Resource type published on that Role Assistant')
66
+ .option('--type <key>', 'Alias for --resource-type')
67
+ .option('--operation <key>', 'Named operation for that resource type')
68
+ .option('--filters <json>', 'JSON object of field filters')
69
+ .option('--filters-file <path>', 'JSON object of field filters from a file')
70
+ .option('--page <n>', 'Page number (integer ≥ 1)')
71
+ .option('--page-size <n>', 'Page size (integer 1–100)')
72
+ .option('--sort <spec>', 'Sort specification (pass-through)')
73
+ .option('--fields <csv>', 'Comma-separated field names')
74
+ .option('--graph', 'Include one-level related records')
75
+ .option('--query <text>', 'Semantic hop text when --graph is set')
76
+ .option('--knowledge-key <key>', 'Knowledge key (repeatable)', collectKnowledgeKey, [])
77
+ .option('--body <file>', 'JSON request body file (merged after flags)');
78
+ }
79
+
80
+ /**
81
+ * @param {Function} action
82
+ * @param {Object} options
83
+ * @returns {Promise<void>}
84
+ */
85
+ async function dispatchSearchLeaf(action, options) {
86
+ try {
87
+ const code = await action(options);
88
+ if (code && code !== 0) {
89
+ process.exit(code);
90
+ }
91
+ } catch (err) {
92
+ const message = err.message || String(err);
93
+ emitSearchError(options, message, logger);
94
+ process.exit(exitCodeForSearchError(err));
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @param {Object} options
100
+ * @param {import('commander').Command} command
101
+ * @returns {Object}
102
+ */
103
+ function mergeCommandOptions(options, command) {
104
+ const parentOpts =
105
+ command && command.parent && typeof command.parent.opts === 'function'
106
+ ? command.parent.opts()
107
+ : {};
108
+ return { ...parentOpts, ...options };
109
+ }
110
+
111
+ /**
112
+ * @param {import('commander').Command} program
113
+ */
114
+ function setupSearchCommands(program) {
115
+ const search = program
116
+ .command('search')
117
+ .description('Search governed information for a Role Assistant assigned to you')
118
+ .addHelpText('after', SEARCH_HELP);
119
+ addSharedSearchOptions(search);
120
+ addSearchBodyOptions(search);
121
+
122
+ const searchRun = search
123
+ .command('run', { isDefault: true, hidden: true })
124
+ .description('Search governed information for a Role Assistant assigned to you')
125
+ .addHelpText('after', SEARCH_HELP);
126
+ searchRun.action(async(options, command) => {
127
+ await dispatchSearchLeaf(runSearch, mergeCommandOptions(options, command));
128
+ });
129
+
130
+ const skills = search
131
+ .command('skills')
132
+ .description('List searchable resource types and operations for a Role Assistant')
133
+ .addHelpText('after', SKILLS_HELP);
134
+ addSharedSearchOptions(skills);
135
+ skills.action(async(options, command) => {
136
+ await dispatchSearchLeaf(runSearchSkills, mergeCommandOptions(options, command));
137
+ });
138
+ }
139
+
140
+ module.exports = {
141
+ setupSearchCommands,
142
+ collectKnowledgeKey
143
+ };
@@ -25,11 +25,13 @@ const {
25
25
  async function runTestTrustForExternalSystem(externalSystem, options = {}) {
26
26
  const { systemKey } = resolveExternalIntegrationContext(externalSystem);
27
27
 
28
- const uploadCtx = await syncLocalIfRequested(systemKey, options);
29
- if (uploadCtx?.authConfig && uploadCtx?.dataplaneUrl) {
30
- options.authConfig = uploadCtx.authConfig;
31
- options.dataplaneUrl = uploadCtx.dataplaneUrl;
32
- options.silentResolve = true;
28
+ if (options.upload === true && options.noPublish !== true) {
29
+ const uploadCtx = await syncLocalIfRequested(systemKey, options);
30
+ if (uploadCtx?.authConfig && uploadCtx?.dataplaneUrl) {
31
+ options.authConfig = uploadCtx.authConfig;
32
+ options.dataplaneUrl = uploadCtx.dataplaneUrl;
33
+ options.silentResolve = true;
34
+ }
33
35
  }
34
36
  if (options.revalidate === true) {
35
37
  logger.log(chalk.cyan('Forcing revalidation (cache bypass)…'));