@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.
- package/README.md +8 -0
- package/lib/agent-kit/apply-ops.js +82 -0
- package/lib/agent-kit/checksums.js +28 -0
- package/lib/agent-kit/classify.js +113 -0
- package/lib/agent-kit/claude-shim.js +61 -0
- package/lib/agent-kit/constants.js +55 -0
- package/lib/agent-kit/file-list.js +103 -0
- package/lib/agent-kit/host-approve.js +172 -0
- package/lib/agent-kit/hosting/github.js +63 -0
- package/lib/agent-kit/hosts.js +71 -0
- package/lib/agent-kit/init.js +319 -0
- package/lib/agent-kit/kit-root.js +17 -0
- package/lib/agent-kit/managed-section.js +79 -0
- package/lib/agent-kit/package-load.js +45 -0
- package/lib/agent-kit/plan-ops.js +69 -0
- package/lib/agent-kit/remote-prompts.js +272 -0
- package/lib/agent-kit/render-host-markdown.js +40 -0
- package/lib/agent-kit/repo-guard.js +47 -0
- package/lib/agent-kit/report.js +107 -0
- package/lib/agent-kit/run.js +82 -0
- package/lib/agent-kit/scm/git.js +122 -0
- package/lib/agent-kit/start.js +310 -0
- package/lib/agent-kit/state-file.js +83 -0
- package/lib/agent-kit/status.js +50 -0
- package/lib/agent-kit/v1-detect.js +81 -0
- package/lib/agent-kit/validate-kit.js +87 -0
- package/lib/agent-kit/workspace-name.js +40 -0
- package/lib/agent-kit/workspace-skeleton.js +103 -0
- package/lib/api/role-assistant-test-pack.api.js +86 -0
- package/lib/api/work-search.api.js +31 -4
- package/lib/api/work.api.js +19 -12
- package/lib/app/deploy.js +7 -4
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/offer-create-from-template.js +184 -0
- package/lib/app/show-display.js +33 -6
- package/lib/app/show.js +25 -2
- package/lib/channels/add-channel.js +1 -0
- package/lib/channels/approval-guides/claude.js +2 -2
- package/lib/channels/channel-add-failure.js +1 -0
- package/lib/channels/channel-add-remaining.js +15 -9
- package/lib/channels/channel-definition-status.js +115 -0
- package/lib/channels/channel-description.js +2 -1
- package/lib/channels/channel-install-envelope.js +4 -2
- package/lib/channels/channel-integration-generator.js +2 -0
- package/lib/channels/channel-runtime-resolver.js +14 -0
- package/lib/channels/channel-targets.js +31 -5
- package/lib/channels/claude-setup-values.js +4 -1
- package/lib/channels/slack-channel-add.js +59 -6
- package/lib/channels/slack-mcp-callback.js +51 -0
- package/lib/channels/slack-mcp-runtime.js +156 -0
- package/lib/channels/slack-provisioning/install-cli.js +14 -2
- package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-app.js +8 -1
- package/lib/cli/setup-auth.js +11 -2
- package/lib/cli/setup-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +149 -0
- package/lib/commands/agent-kit.js +417 -0
- package/lib/commands/auth-status-user.js +99 -0
- package/lib/commands/auth-status.js +14 -4
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/datasource-load-export-cli.js +7 -2
- package/lib/commands/datasource-test-trust-cli.js +5 -1
- package/lib/commands/governance-command-action.js +5 -4
- package/lib/commands/identity-membership-role-cache.js +6 -1
- package/lib/commands/lifecycle-command-action.js +16 -15
- package/lib/commands/login-credentials.js +8 -3
- package/lib/commands/login-device.js +14 -5
- package/lib/commands/login.js +14 -1
- package/lib/commands/logout.js +50 -15
- package/lib/commands/repair-auth-apply.js +1 -1
- package/lib/commands/repair-datasource-key-skip.js +39 -0
- package/lib/commands/repair-datasource-keys.js +24 -12
- package/lib/commands/repair-rbac.js +16 -10
- package/lib/commands/role-assistant-copy-output.js +61 -0
- package/lib/commands/role-assistant-copy.js +155 -0
- package/lib/commands/role-assistant-test-output.js +8 -9
- package/lib/commands/role-assistant-test-result-rows.js +42 -0
- package/lib/commands/role-assistant.js +140 -59
- package/lib/commands/search.js +143 -0
- package/lib/commands/test-trust-external.js +7 -5
- package/lib/commands/upload-sidecar-followups.js +4 -1
- package/lib/commands/verify-governance-command-action.js +9 -7
- package/lib/commands/verify-operations-command-action.js +9 -8
- package/lib/commands/verify-operations-skip-e2e.js +131 -0
- package/lib/commands/verify-operations-step-plan.js +6 -33
- package/lib/commands/verify-operations-steps.js +3 -1
- package/lib/commands/verify-trust-command-action.js +6 -5
- package/lib/datasource/capability/relate-operations.js +28 -10
- package/lib/datasource/capability/run-capability-relate.js +48 -25
- package/lib/datasource/datasource-identifier.js +13 -5
- package/lib/deployment/deployer-poll-run.js +117 -35
- package/lib/external-system/deploy-dataplane-published.js +70 -0
- package/lib/external-system/deploy.js +12 -2
- package/lib/external-system/download-role-assistant.js +41 -15
- package/lib/external-system/download.js +8 -3
- package/lib/generator/helpers.js +12 -1
- package/lib/generator/wizard-datasource-key.js +54 -0
- package/lib/generator/wizard.js +55 -54
- package/lib/integration-definition/apply-datasource-keys.js +75 -0
- package/lib/integration-definition/apply-relationships.js +195 -0
- package/lib/integration-definition/apply-static-enterprise.js +232 -0
- package/lib/integration-definition/apply-system-file.js +71 -0
- package/lib/integration-definition/apply-test-endpoint.js +89 -0
- package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
- package/lib/integration-definition/apply.js +78 -59
- package/lib/integration-definition/enrich-openapi-operations.js +36 -10
- package/lib/integration-definition/enrich-openapi-pick.js +87 -0
- package/lib/lifecycle/role-assistant-governance.js +56 -21
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/index.js +2 -0
- package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +9 -3
- package/lib/programmatic/openapi-envelope-examples.js +7 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +5 -0
- package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/openapi-spec.js +1 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +7 -0
- package/lib/programmatic/run-channel-catalog.js +50 -8
- package/lib/programmatic/run-channel-install-options.js +5 -1
- package/lib/programmatic/run-download.js +41 -0
- package/lib/protection/auth-context.js +3 -1
- package/lib/protection/protection-display.js +3 -2
- package/lib/protection/report-exit.js +50 -0
- package/lib/protection/run-commands.js +2 -0
- package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
- package/lib/role-assistant/knowledge-sync.js +9 -126
- package/lib/role-assistant/package-validate.js +175 -0
- package/lib/role-assistant/test-case-assertion-row.js +36 -0
- package/lib/role-assistant/test-case-copy-bind.js +77 -0
- package/lib/role-assistant/test-case-copy-expected.js +127 -0
- package/lib/role-assistant/test-case-copy-from-work.js +90 -0
- package/lib/role-assistant/test-case-copy-interactions.js +67 -0
- package/lib/role-assistant/test-case-copy.js +119 -0
- package/lib/role-assistant/test-case-expected-catalog.js +135 -0
- package/lib/role-assistant/test-case-validate.js +88 -0
- package/lib/role-assistant/test-cases-bid.js +117 -4
- package/lib/role-assistant/test-cases-evaluate.js +149 -31
- package/lib/role-assistant/test-cases-evidence.js +44 -5
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +297 -0
- package/lib/role-assistant/test-cases-search-graph.js +122 -0
- package/lib/role-assistant/test-cases-search.js +20 -2
- package/lib/role-assistant/test-cases.js +34 -168
- package/lib/role-assistant/test-pack-map.js +180 -0
- package/lib/role-assistant/test-pack-sync.js +187 -0
- package/lib/role-assistant/test-runner-debug.js +54 -0
- package/lib/role-assistant/test-runner-respond-body.js +36 -0
- package/lib/role-assistant/test-runner-search.js +20 -5
- package/lib/role-assistant/test-runner-start-payload.js +63 -10
- package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
- package/lib/role-assistant/test-runner-workhub.js +45 -21
- package/lib/role-assistant/test-runner.js +53 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/infra-parameter.schema.json +51 -3
- package/lib/schema/infra.parameter.yaml +476 -40
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/schema/type/document-storage.json +1 -1
- package/lib/search/dataplane-context.js +23 -0
- package/lib/search/search-args.js +211 -0
- package/lib/search/search-display.js +238 -0
- package/lib/search/search-errors.js +50 -0
- package/lib/search/search-prompts.js +119 -0
- package/lib/search/search-roles.js +85 -0
- package/lib/search/search-run.js +241 -0
- package/lib/search/search-skills.js +198 -0
- package/lib/utils/api-required-permissions.js +271 -0
- package/lib/utils/api.js +16 -13
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/cli-json-exit.js +41 -0
- package/lib/utils/cli-utils.js +23 -5
- package/lib/utils/controller-url.js +78 -4
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/deployment-errors.js +15 -0
- package/lib/utils/deployment-validation-helpers.js +4 -1
- package/lib/utils/device-code.js +31 -10
- package/lib/utils/error-formatters/error-parser.js +16 -1
- package/lib/utils/error-formatters/permission-errors.js +19 -5
- package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
- package/lib/utils/external-system-readiness-compact-display.js +30 -1
- package/lib/utils/external-system-validators.js +2 -1
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/keycloak-realm-ready.js +5 -2
- package/lib/utils/logger.js +1 -0
- package/lib/utils/paths.js +26 -5
- package/lib/utils/schema-loader.js +37 -0
- package/lib/utils/secrets-helpers.js +2 -2
- package/lib/utils/write-stream-and-wait.js +63 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +20 -6
- package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
- package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
- package/templates/agent-kit/agent-kit.yaml +45 -0
- package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
- package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
- package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
- package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
- package/templates/agent-kit/parity-inventory.yaml +188 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +65 -0
- package/templates/agent-kit/skills/shared/login.md +16 -0
- package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
- package/templates/agent-kit/workspace/LICENSE.mit +21 -0
- package/templates/agent-kit/workspace/README.md +89 -0
- package/templates/applications/builder-api/Dockerfile +3 -1
- package/templates/applications/dataplane/application.yaml +2 -3
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +21 -2
- package/templates/applications/miso-controller/env.template +96 -25
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
- package/templates/channels/claude/channel.json +1 -1
- package/templates/channels/claude/install.template.md.hbs +1 -0
- package/templates/channels/claude/integration/claude-system.json +1 -1
- package/templates/channels/slack/channel.json +11 -0
- package/templates/channels/slack/install.template.md.hbs +9 -0
- package/templates/channels/slack/integration/slack-system.json +11 -0
- package/templates/channels/slack/manifest.template.json.hbs +6 -3
- package/templates/channels/teams-bot/channel.json +1 -0
- package/templates/python/docker-compose.hbs +14 -0
- 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:
|
|
78
|
-
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:
|
|
120
|
-
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
|
|
117
|
-
custom connector with those
|
|
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
|
|
173
|
-
custom connector with those
|
|
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
|
|
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
|
-
.
|
|
107
|
-
.requiredOption('--to <targetDatasource>', 'Target datasource 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|