@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.
- 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 +130 -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 +239 -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/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/list-external-systems.js +2 -1
- 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-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +79 -0
- package/lib/commands/agent-kit.js +346 -0
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- 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 +117 -48
- 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-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/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 +7 -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 +229 -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/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 +137 -30
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +291 -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 +50 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- 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/authentication-slot-show.js +153 -0
- package/lib/utils/credential-display.js +43 -1
- 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/schema-loader.js +37 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +1 -1
- 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 +83 -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 +58 -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 +51 -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 +50 -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 +42 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +78 -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 -2
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +8 -9
- 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,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `aifabrix agent-kit` command group.
|
|
3
|
+
* @fileoverview Install, init, status, update, validate, remote create
|
|
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 path = require('path');
|
|
13
|
+
const { runInit, runRemoteCreate } = require('../agent-kit/init');
|
|
14
|
+
const { enrichInitHostOptions, enrichInitRemoteOptions, enrichRemoteOptions, enrichApproveAllOptions } = require('../agent-kit/remote-prompts');
|
|
15
|
+
const { hostLabel } = require('../agent-kit/hosts');
|
|
16
|
+
const { runStatus } = require('../agent-kit/status');
|
|
17
|
+
const { runValidate } = require('../agent-kit/validate-kit');
|
|
18
|
+
const { formatHumanStatus } = require('../agent-kit/report');
|
|
19
|
+
const { KIT_STATE } = require('../agent-kit/constants');
|
|
20
|
+
const { loadState } = require('../agent-kit/state-file');
|
|
21
|
+
const { resolveGitRoot } = require('../agent-kit/repo-guard');
|
|
22
|
+
const {
|
|
23
|
+
GROUP_HELP,
|
|
24
|
+
INSTALL_HELP,
|
|
25
|
+
STATUS_HELP,
|
|
26
|
+
UPDATE_HELP,
|
|
27
|
+
VALIDATE_HELP,
|
|
28
|
+
INIT_HELP,
|
|
29
|
+
REMOTE_HELP
|
|
30
|
+
} = require('./agent-kit-help');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {object} json
|
|
34
|
+
* @param {boolean} asJson
|
|
35
|
+
* @returns {void}
|
|
36
|
+
*/
|
|
37
|
+
function emitReport(json, asJson) {
|
|
38
|
+
if (asJson) {
|
|
39
|
+
logger.log(JSON.stringify(json, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
logger.log(formatHumanStatus(json));
|
|
43
|
+
if (json.conflicts && json.conflicts.length) {
|
|
44
|
+
logger.log(formatWarningLine(`Conflicts: ${json.conflicts.join(', ')}`));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {object} json
|
|
50
|
+
* @param {boolean} asJson
|
|
51
|
+
* @returns {void}
|
|
52
|
+
*/
|
|
53
|
+
function emitInitReport(json, asJson) {
|
|
54
|
+
if (asJson) {
|
|
55
|
+
logger.log(JSON.stringify(json, null, 2));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
logger.log('AI Fabrix workspace init');
|
|
59
|
+
logger.log(`Path ${json.path}`);
|
|
60
|
+
logger.log(`Name ${json.name}`);
|
|
61
|
+
logger.log(`SCM ${json.scm}`);
|
|
62
|
+
if (json.hosts && json.hosts.length) {
|
|
63
|
+
logger.log(`Hosts ${json.hosts.map((h) => hostLabel(h)).join(', ')}`);
|
|
64
|
+
}
|
|
65
|
+
if (json.kit) {
|
|
66
|
+
logger.log(`Agent Kit ${json.kit.kitVersion} (${json.kit.state})`);
|
|
67
|
+
}
|
|
68
|
+
if (json.remote && !json.remote.skipped) {
|
|
69
|
+
logger.log(`Remote ${json.remote.provider}:${json.remote.fullName} (${json.remote.visibility})`);
|
|
70
|
+
} else {
|
|
71
|
+
logger.log('Remote skipped (use: aifabrix agent-kit remote create)');
|
|
72
|
+
}
|
|
73
|
+
logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {object} json
|
|
78
|
+
* @param {boolean} asJson
|
|
79
|
+
* @returns {void}
|
|
80
|
+
*/
|
|
81
|
+
function emitRemoteReport(json, asJson) {
|
|
82
|
+
if (asJson) {
|
|
83
|
+
logger.log(JSON.stringify(json, null, 2));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (json.remote && json.remote.skipped) {
|
|
87
|
+
logger.log(formatWarningLine(`Remote skipped: ${json.remote.reason || 'already configured'}`));
|
|
88
|
+
} else if (json.remote) {
|
|
89
|
+
logger.log(`Remote ${json.remote.provider}:${json.remote.fullName} (${json.remote.visibility})`);
|
|
90
|
+
}
|
|
91
|
+
logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {Function} runner
|
|
96
|
+
* @param {object} options
|
|
97
|
+
* @param {boolean} [failOnConflict]
|
|
98
|
+
* @returns {Promise<void>}
|
|
99
|
+
*/
|
|
100
|
+
async function runLeaf(runner, options, failOnConflict) {
|
|
101
|
+
const json = runner(options);
|
|
102
|
+
emitReport(json, Boolean(options.json));
|
|
103
|
+
if (failOnConflict && json.conflicts && json.conflicts.length) {
|
|
104
|
+
logger.error(formatBlockingError('Managed files were modified locally. Re-run with --force to replace kit-owned content only.'));
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!options.json && json.state && json.state !== KIT_STATE.NOT_INSTALLED) {
|
|
109
|
+
logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {import('commander').Command} program
|
|
115
|
+
*/
|
|
116
|
+
function setupAgentKitCommands(program) {
|
|
117
|
+
const group = program
|
|
118
|
+
.command('agent-kit')
|
|
119
|
+
.description('Install, init, and update the AI Fabrix Agent Kit in a customer workspace')
|
|
120
|
+
.addHelpText('after', GROUP_HELP);
|
|
121
|
+
|
|
122
|
+
addInitCommand(group);
|
|
123
|
+
addInstallCommand(group);
|
|
124
|
+
addStatusCommand(group);
|
|
125
|
+
addUpdateCommand(group);
|
|
126
|
+
addValidateCommand(group);
|
|
127
|
+
addRemoteCommand(group);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {import('commander').Command} group
|
|
132
|
+
*/
|
|
133
|
+
function addInitCommand(group) {
|
|
134
|
+
group
|
|
135
|
+
.command('init [nameOrPath]')
|
|
136
|
+
.description('Create a customer workspace skeleton, git repo, and Agent Kit')
|
|
137
|
+
.option('--host <target>', 'all, cursor, codex, or claude (omit on TTY to choose)', 'all')
|
|
138
|
+
.option('--scm <kind>', 'git or none', 'git')
|
|
139
|
+
.option('--remote', 'Create private hosting remote and push (requires auth)')
|
|
140
|
+
.option('--no-remote', 'Force local-only (skip remote prompt on TTY)')
|
|
141
|
+
.option('--org <owner>', 'Hosting org or user (required with --remote when non-TTY)')
|
|
142
|
+
.option('--visibility <mode>', 'private or public', 'private')
|
|
143
|
+
.option('--license <kind>', 'mit or none', 'none')
|
|
144
|
+
.option('--force', 'Allow non-empty target directory')
|
|
145
|
+
.option('--yes', 'Silent: skip TTY confirm (pair with --host and --org); also auto-approves host permissions')
|
|
146
|
+
.option('--approve-all', 'Write Claude Code and Codex auto-approve configs')
|
|
147
|
+
.option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
|
|
148
|
+
.option('--dry-run', 'Plan writes without changing files')
|
|
149
|
+
.option('--json', 'Machine-readable output')
|
|
150
|
+
.addHelpText('after', INIT_HELP)
|
|
151
|
+
.action(async(nameOrPath, options) => {
|
|
152
|
+
try {
|
|
153
|
+
const interactive = Boolean(process.stdout.isTTY) && !options.json;
|
|
154
|
+
const withHost = await enrichInitHostOptions(
|
|
155
|
+
{ ...options, nameOrPath },
|
|
156
|
+
{ interactive }
|
|
157
|
+
);
|
|
158
|
+
const withApprove = await enrichApproveAllOptions(withHost, { interactive });
|
|
159
|
+
const merged = await enrichInitRemoteOptions(withApprove, {
|
|
160
|
+
interactive,
|
|
161
|
+
workspaceName: path.basename(String(nameOrPath || 'workspace'))
|
|
162
|
+
});
|
|
163
|
+
if (merged.cancelled) {
|
|
164
|
+
logger.log('Cancelled.');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const json = runInit(merged);
|
|
168
|
+
emitInitReport(json, Boolean(options.json));
|
|
169
|
+
} catch (error) {
|
|
170
|
+
handleCommandError(error, 'agent-kit init');
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {import('commander').Command} group
|
|
178
|
+
*/
|
|
179
|
+
function addRemoteCommand(group) {
|
|
180
|
+
const remote = group.command('remote').description('Hosting remotes for an Agent Kit workspace');
|
|
181
|
+
remote
|
|
182
|
+
.command('create')
|
|
183
|
+
.description('Create a private GitHub remote and push the current workspace')
|
|
184
|
+
.option('--org <owner>', 'Hosting org or user (required when non-TTY)')
|
|
185
|
+
.option('--name <repo>', 'Remote repository name (default: directory basename)')
|
|
186
|
+
.option('--visibility <mode>', 'private or public', 'private')
|
|
187
|
+
.option('--yes', 'Silent: skip TTY confirmation (pair with --org)')
|
|
188
|
+
.option('--dry-run', 'Plan without creating the remote')
|
|
189
|
+
.option('--json', 'Machine-readable output')
|
|
190
|
+
.addHelpText('after', REMOTE_HELP)
|
|
191
|
+
.action(async(options) => {
|
|
192
|
+
try {
|
|
193
|
+
const interactive = Boolean(process.stdout.isTTY) && !options.json;
|
|
194
|
+
const merged = await enrichRemoteOptions(options, {
|
|
195
|
+
interactive,
|
|
196
|
+
name: options.name || path.basename(process.cwd())
|
|
197
|
+
});
|
|
198
|
+
if (merged.cancelled) {
|
|
199
|
+
logger.log('Cancelled.');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const json = runRemoteCreate(merged);
|
|
203
|
+
emitRemoteReport(json, Boolean(options.json));
|
|
204
|
+
} catch (error) {
|
|
205
|
+
handleCommandError(error, 'agent-kit remote create');
|
|
206
|
+
process.exitCode = 1;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @param {import('commander').Command} group
|
|
213
|
+
*/
|
|
214
|
+
function addInstallCommand(group) {
|
|
215
|
+
group
|
|
216
|
+
.command('install')
|
|
217
|
+
.description('Install the Agent Kit into the current git repository')
|
|
218
|
+
.option('--host <target>', 'all, cursor, codex, or claude', 'all')
|
|
219
|
+
.option('--approve-all', 'Write Claude Code and Codex auto-approve configs')
|
|
220
|
+
.option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
|
|
221
|
+
.option('--yes', 'Silent: auto-approve host permission configs without asking')
|
|
222
|
+
.option('--dry-run', 'Plan writes without changing files')
|
|
223
|
+
.option('--json', 'Machine-readable output')
|
|
224
|
+
.addHelpText('after', INSTALL_HELP)
|
|
225
|
+
.action(async(options) => {
|
|
226
|
+
try {
|
|
227
|
+
const interactive = Boolean(process.stdout.isTTY) && !options.json;
|
|
228
|
+
const merged = await enrichApproveAllOptions(options, { interactive });
|
|
229
|
+
await runLeaf(runInstall, merged, true);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
handleCommandError(error, 'agent-kit install');
|
|
232
|
+
process.exitCode = 1;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {import('commander').Command} group
|
|
239
|
+
*/
|
|
240
|
+
function addStatusCommand(group) {
|
|
241
|
+
group
|
|
242
|
+
.command('status')
|
|
243
|
+
.description('Show installed Agent Kit version and file state')
|
|
244
|
+
.option('--json', 'Machine-readable output')
|
|
245
|
+
.addHelpText('after', STATUS_HELP)
|
|
246
|
+
.action(async(options) => {
|
|
247
|
+
try {
|
|
248
|
+
await runLeaf(runStatus, options, false);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
handleCommandError(error, 'agent-kit status');
|
|
251
|
+
process.exitCode = 1;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Recorded --host from a previous install (update omits --host to keep this).
|
|
258
|
+
* @returns {string[]|undefined}
|
|
259
|
+
*/
|
|
260
|
+
function readRecordedHosts() {
|
|
261
|
+
try {
|
|
262
|
+
const state = loadState(resolveGitRoot(process.cwd()));
|
|
263
|
+
if (state && Array.isArray(state.hosts) && state.hosts.length) {
|
|
264
|
+
return state.hosts;
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Update does not prompt or auto-write host configs unless flags are set.
|
|
274
|
+
* @param {object} options
|
|
275
|
+
* @returns {Promise<object>}
|
|
276
|
+
*/
|
|
277
|
+
async function mergeUpdateApproveOptions(options) {
|
|
278
|
+
const interactive = Boolean(process.stdout.isTTY) && !options.json;
|
|
279
|
+
const host = options.host || readRecordedHosts();
|
|
280
|
+
return enrichApproveAllOptions({ ...options, host }, { interactive, ask: false });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @param {import('commander').Command} group
|
|
285
|
+
*/
|
|
286
|
+
function addUpdateCommand(group) {
|
|
287
|
+
group
|
|
288
|
+
.command('update')
|
|
289
|
+
.description('Update managed Agent Kit files from this Builder release')
|
|
290
|
+
.option('--host <target>', 'all, cursor, codex, or claude (omit to keep recorded hosts)')
|
|
291
|
+
.option('--approve-all', 'Write Claude Code and Codex auto-approve configs')
|
|
292
|
+
.option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
|
|
293
|
+
.option('--yes', 'Silent: auto-approve host permission configs without asking')
|
|
294
|
+
.option('--dry-run', 'Plan writes without changing files')
|
|
295
|
+
.option('--force', 'Replace modified kit-owned files and managed sections')
|
|
296
|
+
.option('--json', 'Machine-readable output')
|
|
297
|
+
.addHelpText('after', UPDATE_HELP)
|
|
298
|
+
.action(async(options) => {
|
|
299
|
+
try {
|
|
300
|
+
const merged = await mergeUpdateApproveOptions(options);
|
|
301
|
+
await runLeaf(runUpdate, merged, !options.force);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
handleCommandError(error, 'agent-kit update');
|
|
304
|
+
process.exitCode = 1;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* @param {import('commander').Command} group
|
|
311
|
+
*/
|
|
312
|
+
function addValidateCommand(group) {
|
|
313
|
+
group
|
|
314
|
+
.command('validate')
|
|
315
|
+
.description('Validate the bundled Agent Kit and the current repository install')
|
|
316
|
+
.option('--json', 'Machine-readable output')
|
|
317
|
+
.addHelpText('after', VALIDATE_HELP)
|
|
318
|
+
.action(async(options) => {
|
|
319
|
+
try {
|
|
320
|
+
await runValidateLeaf(options);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
handleCommandError(error, 'agent-kit validate');
|
|
323
|
+
process.exitCode = 1;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* @param {object} options
|
|
330
|
+
* @returns {Promise<void>}
|
|
331
|
+
*/
|
|
332
|
+
async function runValidateLeaf(options) {
|
|
333
|
+
const json = runValidate(options);
|
|
334
|
+
if (options.json) {
|
|
335
|
+
logger.log(JSON.stringify(json, null, 2));
|
|
336
|
+
} else if (json.success) {
|
|
337
|
+
logger.log(formatSuccessLine(`Agent Kit ${json.kitVersion} is valid`));
|
|
338
|
+
} else {
|
|
339
|
+
logger.error(formatBlockingError(json.errors.join('; ')));
|
|
340
|
+
}
|
|
341
|
+
if (!json.success) {
|
|
342
|
+
process.exitCode = 1;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
module.exports = { setupAgentKitCommands };
|
|
@@ -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)')
|
|
@@ -117,7 +117,7 @@ function repairAuthChangeLabel(authOption) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const MISSING_TEST_ENDPOINT_WARNING =
|
|
120
|
-
'authentication.variables.testEndpoint is missing for apikey/bearerToken auth — add a GET URL for credential/E2E tests (
|
|
120
|
+
'authentication.variables.testEndpoint is missing for apikey/bearerToken auth — add a GET URL for credential/E2E tests (use the OpenAPI minimum for required page-size query params, or a path resolved against baseUrl).';
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Warn when apikey auth (including bearerToken preset) has no testEndpoint.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skip repair key prefix when a datasource key is already unique scoped kebab-case.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Unique kebab datasource keys stay as-is during repair
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const SCOPED_KEBAB = /^[a-z0-9]+(-[a-z0-9]+)+$/;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} key
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isScopedKebabKey(key) {
|
|
16
|
+
return SCOPED_KEBAB.test(String(key || '').trim());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* True when key already contains a hyphen and appears once in the package.
|
|
21
|
+
* Single-token keys (persons) must still be prefixed with the system key.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} key
|
|
24
|
+
* @param {string[]} allKeys
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
function shouldSkipSystemKeyPrefix(key, allKeys) {
|
|
28
|
+
const trimmed = String(key || '').trim();
|
|
29
|
+
if (!isScopedKebabKey(trimmed) || trimmed.endsWith('-datasource')) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const hits = (allKeys || []).filter((item) => String(item || '').trim() === trimmed);
|
|
33
|
+
return hits.length === 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
isScopedKebabKey,
|
|
38
|
+
shouldSkipSystemKeyPrefix
|
|
39
|
+
};
|
|
@@ -17,6 +17,7 @@ const fs = require('fs');
|
|
|
17
17
|
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
18
18
|
const { backupIntegrationFile } = require('../utils/integration-file-backup');
|
|
19
19
|
const { trackRepairWrite } = require('./repair-changed-files');
|
|
20
|
+
const { shouldSkipSystemKeyPrefix } = require('./repair-datasource-key-skip');
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Returns suffix from a canonical-format filename: <systemKey>-datasource-<suffix>.<ext>.
|
|
@@ -53,6 +54,9 @@ function isKeyAlreadyCanonical(key, systemKey, fileName) {
|
|
|
53
54
|
return true;
|
|
54
55
|
}
|
|
55
56
|
}
|
|
57
|
+
if (shouldSkipSystemKeyPrefix(key, [key])) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
56
60
|
if (!key.startsWith(systemKey + '-')) return false;
|
|
57
61
|
const suffix = key.slice(systemKey.length + 1);
|
|
58
62
|
if (!suffix) return false;
|
|
@@ -126,6 +130,7 @@ function normalizeDatasourceKeysAndFilenames(appPath, datasourceFiles, systemKey
|
|
|
126
130
|
|
|
127
131
|
const slugCounts = new Map();
|
|
128
132
|
const fileInfos = [];
|
|
133
|
+
const packageKeys = [];
|
|
129
134
|
|
|
130
135
|
for (const fileName of datasourceFiles) {
|
|
131
136
|
const filePath = path.join(appPath, fileName);
|
|
@@ -138,23 +143,30 @@ function normalizeDatasourceKeysAndFilenames(appPath, datasourceFiles, systemKey
|
|
|
138
143
|
continue;
|
|
139
144
|
}
|
|
140
145
|
const key = (parsed && typeof parsed.key === 'string' && parsed.key.trim()) ? parsed.key.trim() : null;
|
|
141
|
-
if (
|
|
142
|
-
|
|
146
|
+
if (key) packageKeys.push(key);
|
|
147
|
+
fileInfos.push({ fileName, parsed, key, skip: false, slug: null, canonicalKey: null });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const info of fileInfos) {
|
|
151
|
+
if (info.skip) continue;
|
|
152
|
+
const filenameCanonical = isFilenameAlreadyCanonical(info.fileName, systemKey);
|
|
153
|
+
const keyCanonical = isKeyAlreadyCanonical(info.key, systemKey, info.fileName);
|
|
154
|
+
const skipPrefix =
|
|
155
|
+
shouldSkipSystemKeyPrefix(info.key, packageKeys) &&
|
|
156
|
+
!String(info.key || '').endsWith('-datasource');
|
|
157
|
+
if ((keyCanonical || skipPrefix) && filenameCanonical) {
|
|
158
|
+
info.skip = true;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (skipPrefix) {
|
|
162
|
+
info.canonicalKey = info.key;
|
|
143
163
|
continue;
|
|
144
164
|
}
|
|
145
|
-
|
|
146
|
-
fileInfos.push({
|
|
147
|
-
fileName,
|
|
148
|
-
parsed,
|
|
149
|
-
key,
|
|
150
|
-
slug,
|
|
151
|
-
canonicalKey: null,
|
|
152
|
-
skip: false
|
|
153
|
-
});
|
|
165
|
+
info.slug = slugFromKey(info.key || info.fileName, systemKey);
|
|
154
166
|
}
|
|
155
167
|
|
|
156
168
|
for (const info of fileInfos) {
|
|
157
|
-
if (info.skip) continue;
|
|
169
|
+
if (info.skip || info.canonicalKey) continue;
|
|
158
170
|
const slug = info.slug;
|
|
159
171
|
const n = (slugCounts.get(slug) || 0) + 1;
|
|
160
172
|
slugCounts.set(slug, n);
|
|
@@ -29,6 +29,7 @@ const {
|
|
|
29
29
|
ensureOrchestrationResourceType
|
|
30
30
|
} = require('./repair-rbac-platform-preset');
|
|
31
31
|
const { trackRepairWrite } = require('./repair-changed-files');
|
|
32
|
+
const { listCapabilityKeys } = require('../datasource/capability/capability-manifest');
|
|
32
33
|
const { ensureDefaultRoles } = require('./repair-rbac-catalog-defaults');
|
|
33
34
|
|
|
34
35
|
const DEFAULT_CAPABILITIES = ['list', 'get', 'create', 'update', 'delete'];
|
|
@@ -47,24 +48,29 @@ function _normalizeOperationKey(opKey) {
|
|
|
47
48
|
return cleaned.replace(/-+/g, '-').replace(/^-+|-+$/g, '');
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
function defaultCapabilitiesForDatasource(parsed) {
|
|
52
|
+
if (String(parsed?.storageType || '') === 'view') {
|
|
53
|
+
return ['list', 'get'];
|
|
54
|
+
}
|
|
55
|
+
return [...DEFAULT_CAPABILITIES];
|
|
56
|
+
}
|
|
57
|
+
|
|
50
58
|
/**
|
|
51
|
-
* Resolves capabilities from datasource (array or legacy
|
|
59
|
+
* Resolves capabilities from datasource (array, COM objects, or legacy map).
|
|
52
60
|
* @param {Object} parsed - Parsed datasource
|
|
53
61
|
* @returns {string[]}
|
|
54
62
|
*/
|
|
55
63
|
function getCapabilitiesFromDatasource(parsed) {
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
if (arr.length > 0) return arr;
|
|
60
|
-
return [...DEFAULT_CAPABILITIES];
|
|
64
|
+
const fromList = listCapabilityKeys(parsed?.capabilities);
|
|
65
|
+
if (fromList.length > 0) {
|
|
66
|
+
return fromList;
|
|
61
67
|
}
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
const cap = parsed?.capabilities;
|
|
69
|
+
if (cap && typeof cap === 'object' && !Array.isArray(cap)) {
|
|
70
|
+
const keys = Object.keys(cap).filter((k) => cap[k] === true);
|
|
64
71
|
if (keys.length > 0) return keys;
|
|
65
|
-
return [...DEFAULT_CAPABILITIES];
|
|
66
72
|
}
|
|
67
|
-
return
|
|
73
|
+
return defaultCapabilitiesForDatasource(parsed);
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
/**
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTY and --json output for role-assistant copy.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Success and blocking copy envelopes
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const logger = require('../utils/logger');
|
|
10
|
+
const { formatSuccessLine, formatBlockingError } = require('../utils/cli-layout-chalk');
|
|
11
|
+
|
|
12
|
+
function copyWarnings(result) {
|
|
13
|
+
return Array.isArray(result.warnings) ? result.warnings : [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function buildCopyEnvelope(result) {
|
|
17
|
+
const envelope = {
|
|
18
|
+
success: true,
|
|
19
|
+
path: result.path,
|
|
20
|
+
caseId: result.caseId,
|
|
21
|
+
suiteId: result.suiteId,
|
|
22
|
+
processExecutionId: result.processExecutionId
|
|
23
|
+
};
|
|
24
|
+
const warnings = copyWarnings(result);
|
|
25
|
+
if (warnings.length > 0) {
|
|
26
|
+
envelope.warnings = warnings;
|
|
27
|
+
}
|
|
28
|
+
if (result.dryRun === true) {
|
|
29
|
+
envelope.dryRun = true;
|
|
30
|
+
envelope.yaml = result.yaml;
|
|
31
|
+
}
|
|
32
|
+
return envelope;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function emitCopySuccess(options, result) {
|
|
36
|
+
if (options && options.json) {
|
|
37
|
+
logger.log(JSON.stringify(buildCopyEnvelope(result), null, 2));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (result.dryRun === true) {
|
|
41
|
+
logger.log(result.yaml);
|
|
42
|
+
logger.log(formatSuccessLine(`Would write ${result.path}`));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
logger.log(formatSuccessLine(`Wrote ${result.path}`));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function emitCopyFailure(options, error) {
|
|
49
|
+
const message = error && error.message ? error.message : String(error);
|
|
50
|
+
if (options && options.json) {
|
|
51
|
+
logger.log(JSON.stringify({ success: false, error: message }));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
logger.error(formatBlockingError(message));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
buildCopyEnvelope,
|
|
59
|
+
emitCopySuccess,
|
|
60
|
+
emitCopyFailure
|
|
61
|
+
};
|