@aifabrix/builder 2.58.0 → 2.60.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/lib/agent-kit/codex-app-server.js +44 -0
- package/lib/agent-kit/git-identity.js +180 -0
- package/lib/agent-kit/init.js +4 -17
- package/lib/agent-kit/session-report.js +15 -10
- package/lib/agent-kit/setup-auth.js +131 -0
- package/lib/agent-kit/setup-context.js +118 -0
- package/lib/agent-kit/setup-github.js +70 -0
- package/lib/agent-kit/setup-managed.js +79 -0
- package/lib/agent-kit/setup-process.js +42 -0
- package/lib/agent-kit/setup-session.js +53 -0
- package/lib/agent-kit/setup-state.js +52 -0
- package/lib/agent-kit/setup-workspace.js +113 -0
- package/lib/agent-kit/setup.js +87 -0
- package/lib/agent-kit/start.js +48 -3
- package/lib/agent-kit/tmux-sessions.js +20 -3
- package/lib/api/dev.api.js +1 -1
- package/lib/api/miso-health.api.js +26 -0
- package/lib/api/role-assistant-test-job.api.js +60 -0
- package/lib/api/types/dev.types.js +3 -3
- package/lib/api/work-search.api.js +13 -6
- package/lib/app/deploy.js +8 -103
- package/lib/app/show-display.js +1 -0
- package/lib/app/show-online.js +15 -0
- package/lib/build/standard-docker-build.js +4 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-app.js +14 -1
- package/lib/cli/setup-dev-path-commands.js +4 -0
- package/lib/cli/setup-dev.js +7 -4
- package/lib/cli/setup-help.js +86 -0
- package/lib/cli/setup-onboarding.js +37 -0
- package/lib/cli/setup-utility-repair.js +7 -5
- package/lib/cli/setup-utility.js +24 -1
- package/lib/commands/agent-kit-help.js +21 -7
- package/lib/commands/agent-kit-sessions.js +126 -0
- package/lib/commands/agent-kit-setup.js +44 -0
- package/lib/commands/agent-kit.js +5 -79
- package/lib/commands/dev-init.js +21 -23
- package/lib/commands/dev-refresh.js +2 -2
- package/lib/commands/governance-verify-external.js +12 -5
- package/lib/commands/repair-datasource.js +9 -9
- package/lib/commands/repair.js +45 -8
- package/lib/commands/role-assistant.js +7 -0
- package/lib/commands/upload-qualification-client.js +56 -11
- package/lib/commands/upload-qualification-poll.js +51 -22
- package/lib/commands/wizard-dataplane.js +16 -52
- package/lib/core/env-platform-expand.js +93 -0
- package/lib/core/secrets-env-content.js +37 -3
- package/lib/datasource/datasource-validate-summary.js +8 -5
- package/lib/datasource/sync-defaults.js +53 -0
- package/lib/deployment/installation/azure-infra-stage.js +13 -1
- package/lib/deployment/installation/azure-preflight.js +13 -2
- package/lib/deployment/installation/azure-readiness.js +24 -2
- package/lib/deployment/installation/environment-auth.js +60 -20
- package/lib/deployment/installation/environment-stage.js +9 -5
- package/lib/deployment/installation/infra-catalog.js +10 -7
- package/lib/deployment/installation/infra-manifest.js +5 -0
- package/lib/deployment/installation/registry-auth-mode.js +33 -10
- package/lib/deployment/marketplace-environment.js +26 -1
- package/lib/generator/builders.js +17 -0
- package/lib/generator/helpers.js +23 -2
- package/lib/generator/index.js +33 -18
- package/lib/integration-definition/apply.js +2 -2
- package/lib/internal/fs-real-sync.js +4 -1
- package/lib/programmatic/bearer-auth.js +3 -62
- package/lib/programmatic/builder-api-startup.js +51 -0
- package/lib/programmatic/builder-miso-runtime.js +167 -0
- package/lib/programmatic/openapi-descriptions.js +3 -3
- package/lib/programmatic/openapi-schema-descriptions-core.js +3 -3
- package/lib/programmatic/openapi-spec.js +2 -1
- package/lib/programmatic/route-handler-map.js +3 -2
- package/lib/role-assistant/test-cases-search.js +3 -0
- package/lib/role-assistant/test-job-runner.js +125 -0
- package/lib/role-assistant/test-runner-search.js +44 -2
- package/lib/schema/application-schema.json +193 -2
- package/lib/schema/environment-deploy-request.schema.json +1 -1
- package/lib/schema/external-datasource.schema.json +20 -5
- package/lib/schema/infra-parameter.schema.json +140 -34
- package/lib/schema/infra.parameter.yaml +30 -29
- package/lib/schema/infrastructure-schema.json +133 -14
- package/lib/schema/installation-environment.schema.json +1 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/compose-generate-docker-compose.js +12 -0
- package/lib/utils/config-paths.js +24 -29
- package/lib/utils/config-registry-preference.js +13 -6
- package/lib/utils/dev-init-ssh-merge.js +5 -4
- package/lib/utils/dev-user-groups.js +2 -1
- package/lib/utils/docker-build.js +29 -8
- package/lib/utils/docker-daemon-tls-ca.js +2 -2
- package/lib/utils/docker-manifest-public-port.js +36 -0
- package/lib/utils/error-formatters/validation-errors.js +25 -5
- package/lib/utils/help-builder.js +4 -3
- package/lib/utils/platform-resolution.js +226 -0
- package/lib/utils/resolve-docker-image-ref.js +8 -3
- package/lib/utils/token-manager.js +29 -36
- package/package.json +4 -3
- package/templates/README.md +2 -1
- package/templates/agent-kit/agent-kit.yaml +4 -1
- package/templates/agent-kit/instructions/AGENTKIT.md +40 -3
- package/templates/agent-kit/instructions/root.AGENTS.md +42 -1
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +33 -1
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/delivery-verdict.js +176 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +3 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +10 -3
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +22 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +21 -0
- package/templates/agent-kit/skills/shared/feedback.md +31 -0
- package/templates/agent-kit/skills/shared/hosts.md +35 -7
- package/templates/agent-kit/skills/shared/status.md +85 -0
- package/templates/agent-kit/workspace/BUILDER_IMPROVEMENT_FINDINGS.md +27 -0
- package/templates/agent-kit/workspace/README.md +14 -1
- package/templates/applications/builder-api/application.yaml +1 -1
- package/templates/applications/dataplane/application.yaml +30 -2
- package/templates/applications/dataplane/env.template +37 -4
- package/templates/applications/miso-controller/application.yaml +24 -1
- package/templates/applications/miso-controller/env.template +22 -35
- package/templates/external-system/external-datasource.yaml.hbs +7 -6
- package/templates/marketplace/main.json +281 -75
- package/templates/python/Dockerfile.hbs +2 -0
- package/templates/typescript/Dockerfile.hbs +2 -0
- package/templates/typescript/docker-compose.hbs +12 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** CLI actions for listing, attaching, and stopping Agent Kit sessions. */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const logger = require('../utils/logger');
|
|
6
|
+
const { handleCommandError } = require('../utils/cli-utils');
|
|
7
|
+
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
8
|
+
const { runList, runKill, runAttach } = require('../agent-kit/tmux-sessions');
|
|
9
|
+
const { listCodexAppServers, stopCodexAppServer } = require('../agent-kit/codex-app-server');
|
|
10
|
+
const { formatSessionList, formatMissingKillName } = require('../agent-kit/session-report');
|
|
11
|
+
const { LIST_HELP, KILL_HELP } = require('./agent-kit-help');
|
|
12
|
+
|
|
13
|
+
function emitListReport(report, asJson) {
|
|
14
|
+
logger.log(asJson ? JSON.stringify(report, null, 2) : formatSessionList(report));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function emitKillReport(report, asJson) {
|
|
18
|
+
logger.log(asJson ? JSON.stringify(report, null, 2)
|
|
19
|
+
: formatSuccessLine(report.kind === 'codex-app-server'
|
|
20
|
+
? `Sent stop signal to Codex app server PID ${report.pid}`
|
|
21
|
+
: `Stopped tmux session ${report.session}`));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function listSessions(options = {}) {
|
|
25
|
+
const report = (options.runList || runList)(options);
|
|
26
|
+
const codexServers = (options.listCodexAppServers || listCodexAppServers)(options);
|
|
27
|
+
return { ...report, codexServers, serverCount: codexServers.length };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function runListLeaf(options = {}) {
|
|
31
|
+
try {
|
|
32
|
+
const report = listSessions(options);
|
|
33
|
+
emitListReport(report, Boolean(options.json));
|
|
34
|
+
const tty = options.isTTY === undefined
|
|
35
|
+
? Boolean(process.stdin.isTTY && process.stdout.isTTY) : options.isTTY;
|
|
36
|
+
if (!tty || options.json || report.sessions.length === 0) return;
|
|
37
|
+
const choices = report.sessions.map(session => ({
|
|
38
|
+
name: `${session.name} (${session.attached ? 'attached' : 'detached'})`, value: session.name
|
|
39
|
+
}));
|
|
40
|
+
choices.push({ name: 'Close', value: null });
|
|
41
|
+
const answer = await (options.prompt || inquirer.prompt)([{
|
|
42
|
+
type: 'list', name: 'name', message: 'Attach to a tmux session:', choices
|
|
43
|
+
}]);
|
|
44
|
+
if (answer.name) (options.runAttach || runAttach)({ ...options, name: answer.name });
|
|
45
|
+
} catch (error) {
|
|
46
|
+
handleCommandError(error, 'agent-kit list');
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function selectKillSession(options = {}) {
|
|
52
|
+
const tty = options.isTTY === undefined
|
|
53
|
+
? Boolean(process.stdin.isTTY && process.stdout.isTTY) : options.isTTY;
|
|
54
|
+
if (!tty || options.json) return { state: 'missing' };
|
|
55
|
+
const report = listSessions(options);
|
|
56
|
+
emitListReport(report, false);
|
|
57
|
+
if (report.sessions.length + report.codexServers.length === 0) return { state: 'empty' };
|
|
58
|
+
const choices = report.sessions.map(session => ({
|
|
59
|
+
name: `tmux: ${session.name} (${session.attached ? 'attached' : 'detached'})`,
|
|
60
|
+
value: { kind: 'tmux', name: session.name }
|
|
61
|
+
}));
|
|
62
|
+
report.codexServers.forEach(server => choices.push({
|
|
63
|
+
name: `Codex app server (PID ${server.pid})`,
|
|
64
|
+
value: { kind: 'codex-app-server', ...server }
|
|
65
|
+
}));
|
|
66
|
+
choices.push({ name: 'Cancel', value: null });
|
|
67
|
+
const answer = await (options.prompt || inquirer.prompt)([{
|
|
68
|
+
type: 'list', name: 'selection', message: 'Select one session to stop:', choices
|
|
69
|
+
}]);
|
|
70
|
+
if (!answer.selection) return { state: 'cancelled' };
|
|
71
|
+
if (answer.selection.kind === 'codex-app-server') {
|
|
72
|
+
const confirm = await (options.prompt || inquirer.prompt)([{
|
|
73
|
+
type: 'confirm', name: 'stop', default: false,
|
|
74
|
+
message: `Stop Codex app server PID ${answer.selection.pid}? This may disconnect this chat; the client may reconnect.`
|
|
75
|
+
}]);
|
|
76
|
+
if (!confirm.stop) return { state: 'cancelled' };
|
|
77
|
+
}
|
|
78
|
+
return { state: 'selected', ...answer.selection };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function addListCommand(group) {
|
|
82
|
+
group.command('list')
|
|
83
|
+
.description('List sessions and choose a tmux session to attach')
|
|
84
|
+
.option('--json', 'Machine-readable output')
|
|
85
|
+
.addHelpText('after', LIST_HELP)
|
|
86
|
+
.action(runListLeaf);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function addKillCommand(group) {
|
|
90
|
+
group.command('kill [name]')
|
|
91
|
+
.description('Stop a tmux session by name, or choose a session interactively')
|
|
92
|
+
.option('--json', 'Machine-readable output')
|
|
93
|
+
.addHelpText('after', KILL_HELP)
|
|
94
|
+
.action(runKillLeaf);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function runKillLeaf(name, options = {}) {
|
|
98
|
+
try {
|
|
99
|
+
const selection = name ? { state: 'selected', kind: 'tmux', name } : await selectKillSession(options);
|
|
100
|
+
if (selection.state === 'missing') {
|
|
101
|
+
const report = formatMissingKillName(Boolean(options.json));
|
|
102
|
+
logger.error(report.stderr);
|
|
103
|
+
if (report.stdout) logger.log(report.stdout);
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (selection.state === 'empty' || selection.state === 'cancelled') {
|
|
108
|
+
logger.log('No session stopped.');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const report = selection.kind === 'codex-app-server'
|
|
112
|
+
? (options.stopCodexAppServer || stopCodexAppServer)(selection, options)
|
|
113
|
+
: (options.runKill || runKill)({ ...options, name: selection.name });
|
|
114
|
+
emitKillReport(report, Boolean(options.json));
|
|
115
|
+
} catch (error) {
|
|
116
|
+
handleCommandError(error, 'agent-kit kill');
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function addSessionCommands(group) {
|
|
122
|
+
addListCommand(group);
|
|
123
|
+
addKillCommand(group);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { addSessionCommands, listSessions, runListLeaf, selectKillSession, runKillLeaf };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Minimal setup command: controller required, local Codex workspace by default. */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const logger = require('../utils/logger');
|
|
5
|
+
const { formatBlockingError } = require('../utils/cli-layout-chalk');
|
|
6
|
+
const { maskSensitiveData } = require('../utils/log-redaction');
|
|
7
|
+
const { runSetup } = require('../agent-kit/setup');
|
|
8
|
+
|
|
9
|
+
const SETUP_HELP = `
|
|
10
|
+
Start onboarding with only a Controller URL. Defaults to Codex and a local Git
|
|
11
|
+
workspace under the AI Fabrix work directory; GitHub is not required.
|
|
12
|
+
If the work directory is unset, setup asks once and saves it.
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
$ aifabrix agent-kit setup https://controller.example.com
|
|
16
|
+
$ aifabrix agent-kit setup https://controller.example.com --code claude
|
|
17
|
+
$ aifabrix agent-kit setup https://controller.example.com --repo example-org/example-workspace
|
|
18
|
+
|
|
19
|
+
With --repo owner/name, setup reuses a matching local checkout, clones an accessible
|
|
20
|
+
remote, or creates a private repository and pushes. Unresolved access stops safely.
|
|
21
|
+
Without --repo, files stay local and no GitHub login or push occurs.
|
|
22
|
+
Folder name is the repository name, or workspace without --repo. Change environment with
|
|
23
|
+
aifabrix auth --set-environment <environment>.
|
|
24
|
+
Requires an interactive Linux/macOS terminal, git and tmux (gh only for GitHub).
|
|
25
|
+
Installing a missing Claude CLI through npm requires Node.js 22+.
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
function addSetupCommand(group) {
|
|
29
|
+
group.command('setup <controllerUrl>')
|
|
30
|
+
.description('Onboard a local Codex workspace; optionally use Claude or GitHub')
|
|
31
|
+
.option('--repo <owner/name>', 'GitHub repository to reuse, clone, or create privately')
|
|
32
|
+
.option('--code <agent>', 'Coding agent: codex or claude', 'codex')
|
|
33
|
+
.addHelpText('after', SETUP_HELP)
|
|
34
|
+
.action(async(controllerUrl, options) => {
|
|
35
|
+
try {
|
|
36
|
+
await runSetup({ ...options, controllerUrl });
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.error(formatBlockingError(maskSensitiveData(error.message)));
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { addSetupCommand, SETUP_HELP };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/** `aifabrix agent-kit` command group. */
|
|
2
|
-
|
|
3
2
|
'use strict';
|
|
4
3
|
|
|
5
4
|
const logger = require('../utils/logger');
|
|
@@ -11,8 +10,8 @@ const {
|
|
|
11
10
|
} = require('../utils/cli-layout-chalk');
|
|
12
11
|
const { runInstall, runUpdate } = require('../agent-kit/run');
|
|
13
12
|
const { runStart } = require('../agent-kit/start');
|
|
14
|
-
const {
|
|
15
|
-
const {
|
|
13
|
+
const { addSetupCommand } = require('./agent-kit-setup');
|
|
14
|
+
const { addSessionCommands } = require('./agent-kit-sessions');
|
|
16
15
|
const path = require('path');
|
|
17
16
|
const { runInit, runRemoteCreate } = require('../agent-kit/init');
|
|
18
17
|
const { enrichInitHostOptions, enrichInitRemoteOptions, enrichRemoteOptions, enrichApproveAllOptions } = require('../agent-kit/remote-prompts');
|
|
@@ -33,8 +32,6 @@ const {
|
|
|
33
32
|
REMOTE_GROUP_HELP,
|
|
34
33
|
REMOTE_HELP,
|
|
35
34
|
START_HELP,
|
|
36
|
-
LIST_HELP,
|
|
37
|
-
KILL_HELP,
|
|
38
35
|
HOST_OPTION_HELP,
|
|
39
36
|
APPROVE_ALL_OPTION_HELP
|
|
40
37
|
} = require('./agent-kit-help');
|
|
@@ -131,32 +128,6 @@ function emitStartReport(json, asJson) {
|
|
|
131
128
|
logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Ready'));
|
|
132
129
|
}
|
|
133
130
|
|
|
134
|
-
/**
|
|
135
|
-
* @param {object} json
|
|
136
|
-
* @param {boolean} asJson
|
|
137
|
-
* @returns {void}
|
|
138
|
-
*/
|
|
139
|
-
function emitListReport(json, asJson) {
|
|
140
|
-
if (asJson) {
|
|
141
|
-
logger.log(JSON.stringify(json, null, 2));
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
logger.log(formatSessionList(json));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* @param {object} json
|
|
149
|
-
* @param {boolean} asJson
|
|
150
|
-
* @returns {void}
|
|
151
|
-
*/
|
|
152
|
-
function emitKillReport(json, asJson) {
|
|
153
|
-
if (asJson) {
|
|
154
|
-
logger.log(JSON.stringify(json, null, 2));
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
logger.log(formatSuccessLine(`Stopped tmux session ${json.session}`));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
131
|
/**
|
|
161
132
|
* @param {Function} runner
|
|
162
133
|
* @param {object} options
|
|
@@ -185,6 +156,7 @@ function setupAgentKitCommands(program) {
|
|
|
185
156
|
.description('Install, init, and update the AI Fabrix Agent Kit in a customer workspace')
|
|
186
157
|
.addHelpText('after', GROUP_HELP);
|
|
187
158
|
|
|
159
|
+
addSetupCommand(group);
|
|
188
160
|
addInitCommand(group);
|
|
189
161
|
addInstallCommand(group);
|
|
190
162
|
addStatusCommand(group);
|
|
@@ -192,8 +164,7 @@ function setupAgentKitCommands(program) {
|
|
|
192
164
|
addValidateCommand(group);
|
|
193
165
|
addRemoteCommand(group);
|
|
194
166
|
addStartCommand(group);
|
|
195
|
-
|
|
196
|
-
addKillCommand(group);
|
|
167
|
+
addSessionCommands(group);
|
|
197
168
|
}
|
|
198
169
|
|
|
199
170
|
/**
|
|
@@ -254,7 +225,7 @@ function addRemoteCommand(group) {
|
|
|
254
225
|
.command('create')
|
|
255
226
|
.description('Create a private GitHub repository (origin) and push this workspace')
|
|
256
227
|
.option('--org <owner>', 'Hosting org or user (required when non-TTY)')
|
|
257
|
-
.option('--name <repo>', '
|
|
228
|
+
.option('--name <repo>', 'New GitHub repository name (default: workspace folder name)')
|
|
258
229
|
.option('--visibility <mode>', 'private or public', 'private')
|
|
259
230
|
.option('--yes', 'Silent: skip TTY confirmation (pair with --org)')
|
|
260
231
|
.option('--dry-run', 'Plan without creating the remote')
|
|
@@ -451,49 +422,4 @@ function addStartCommand(group) {
|
|
|
451
422
|
});
|
|
452
423
|
}
|
|
453
424
|
|
|
454
|
-
/**
|
|
455
|
-
* @param {import('commander').Command} group
|
|
456
|
-
*/
|
|
457
|
-
function addListCommand(group) {
|
|
458
|
-
group
|
|
459
|
-
.command('list')
|
|
460
|
-
.description('List active tmux sessions for the current user')
|
|
461
|
-
.option('--json', 'Machine-readable output')
|
|
462
|
-
.addHelpText('after', LIST_HELP)
|
|
463
|
-
.action(async(options) => {
|
|
464
|
-
try {
|
|
465
|
-
emitListReport(runList(options), Boolean(options.json));
|
|
466
|
-
} catch (error) {
|
|
467
|
-
handleCommandError(error, 'agent-kit list');
|
|
468
|
-
process.exitCode = 1;
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* @param {import('commander').Command} group
|
|
475
|
-
*/
|
|
476
|
-
function addKillCommand(group) {
|
|
477
|
-
group
|
|
478
|
-
.command('kill [name]')
|
|
479
|
-
.description('Stop one tmux session by exact name')
|
|
480
|
-
.option('--json', 'Machine-readable output')
|
|
481
|
-
.addHelpText('after', KILL_HELP)
|
|
482
|
-
.action(async(name, options) => {
|
|
483
|
-
if (!name) {
|
|
484
|
-
const report = formatMissingKillName(Boolean(options.json));
|
|
485
|
-
logger.error(report.stderr);
|
|
486
|
-
if (report.stdout) logger.log(report.stdout);
|
|
487
|
-
process.exitCode = 1;
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
try {
|
|
491
|
-
emitKillReport(runKill({ ...options, name }), Boolean(options.json));
|
|
492
|
-
} catch (error) {
|
|
493
|
-
handleCommandError(error, 'agent-kit kill');
|
|
494
|
-
process.exitCode = 1;
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
|
|
499
425
|
module.exports = { setupAgentKitCommands };
|
package/lib/commands/dev-init.js
CHANGED
|
@@ -29,13 +29,13 @@ const {
|
|
|
29
29
|
isLinuxCaSudoRequiredError
|
|
30
30
|
} = require('../utils/dev-ca-install');
|
|
31
31
|
const { runOptionalHostsSetup } = require('../utils/dev-hosts-helper');
|
|
32
|
-
const { mergeDevSshConfigAfterInit } = require('../utils/dev-init-ssh-merge');
|
|
32
|
+
const { mergeDevSshConfigAfterInit, hostnameFromBaseUrl } = require('../utils/dev-init-ssh-merge');
|
|
33
33
|
const { resolveInitOptions } = require('../utils/dev-init-resolve');
|
|
34
34
|
const {
|
|
35
35
|
isHealthHttpServerError,
|
|
36
36
|
formatEnsureServerTrustedFailure
|
|
37
37
|
} = require('../utils/dev-init-health-messages');
|
|
38
|
-
const { getBadRequestHint
|
|
38
|
+
const { getBadRequestHint } = require('../utils/dev-init-cert-hints');
|
|
39
39
|
const { augmentAndSaveCaPemForRemoteDocker } = require('../utils/dev-ca-docker-augment');
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -249,7 +249,7 @@ async function _runSshKeyRegistrationStep(baseUrl, issueResponse, keyPem, config
|
|
|
249
249
|
} catch (err) {
|
|
250
250
|
const msg = err.status === 400 ? getBadRequestHint() : (err.message || String(err));
|
|
251
251
|
logger.log(chalk.yellow(' ⚠ Could not register SSH key: ' + msg));
|
|
252
|
-
|
|
252
|
+
throw new Error(`Onboarding incomplete for developer ${devId}: SSH key registration failed. Ask the server administrator to check role access, then retry dev init with a new PIN.`);
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
|
|
@@ -262,24 +262,23 @@ async function _runSshKeyRegistrationStep(baseUrl, issueResponse, keyPem, config
|
|
|
262
262
|
* @param {string} [serverCaPem] - Dev root CA for Node TLS
|
|
263
263
|
*/
|
|
264
264
|
async function applySettingsFromServer(baseUrl, devId, issueResponse, keyPem, serverCaPem) {
|
|
265
|
-
const configDir = getConfigDirForPaths();
|
|
266
|
-
if (issueResponse.settings && typeof issueResponse.settings === 'object') {
|
|
267
|
-
await config.mergeRemoteSettings(issueResponse.settings);
|
|
268
|
-
logger.log(chalk.green(' ') + formatSuccessLine('Config updated from server (issue-cert response)'));
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
logger.log(chalk.gray(' Fetching settings...'));
|
|
272
265
|
try {
|
|
273
|
-
|
|
274
|
-
|
|
266
|
+
let settings = issueResponse.settings;
|
|
267
|
+
if (!settings) {
|
|
268
|
+
logger.log(chalk.gray(' Fetching settings...'));
|
|
269
|
+
settings = await devApi.getSettings(baseUrl, issueResponse.certificate, keyPem, serverCaPem);
|
|
275
270
|
}
|
|
276
|
-
|
|
277
|
-
|
|
271
|
+
if (!settings || typeof settings !== 'object' || Array.isArray(settings)) throw new Error('Server settings are missing or invalid.');
|
|
272
|
+
const fresh = { ...settings, 'developer-id': devId, 'remote-server': baseUrl,
|
|
273
|
+
'sync-ssh-user': settings['sync-ssh-user'] || `dev${devId}`,
|
|
274
|
+
'sync-ssh-host': settings['sync-ssh-host'] || hostnameFromBaseUrl(baseUrl) };
|
|
275
|
+
await config.mergeRemoteSettings(fresh);
|
|
278
276
|
logger.log(chalk.green(' ') + formatSuccessLine('Config updated from server'));
|
|
277
|
+
return fresh;
|
|
279
278
|
} catch (err) {
|
|
280
279
|
const msg = err.status === 400 ? getBadRequestHint() : (err.message || String(err));
|
|
281
|
-
logger.log(chalk.yellow(' ⚠ Could not fetch settings
|
|
282
|
-
|
|
280
|
+
logger.log(chalk.yellow(' ⚠ Could not fetch settings: ' + msg));
|
|
281
|
+
throw new Error(`Onboarding incomplete for developer ${devId}: current server settings could not be applied. Saved settings from another developer will not be used. Ask the server administrator to check role access, then retry dev init with a new PIN.`);
|
|
283
282
|
}
|
|
284
283
|
}
|
|
285
284
|
|
|
@@ -317,7 +316,7 @@ async function resolveServerCaPemForTls(baseUrl, options) {
|
|
|
317
316
|
* @param {{ hostAlias: string|null, syncUser: string, syncHost: string|null }} p
|
|
318
317
|
*/
|
|
319
318
|
function logOnboardingFinished({ hostAlias, syncUser, syncHost }) {
|
|
320
|
-
logger.log(formatSuccessParagraph('
|
|
319
|
+
logger.log(formatSuccessParagraph('Certificate, server settings and SSH key registration complete. Access depends on your server permissions.'));
|
|
321
320
|
if (hostAlias) {
|
|
322
321
|
logger.log(
|
|
323
322
|
chalk.gray(' You can also open an SSH session on the builder with ') +
|
|
@@ -367,14 +366,13 @@ async function runDevInit(options) {
|
|
|
367
366
|
);
|
|
368
367
|
await saveCertAndConfig(configDir, devId, issueResponse.certificate, keyPem, caPem);
|
|
369
368
|
|
|
370
|
-
await applySettingsFromServer(baseUrl, devId, issueResponse, keyPem, serverCaPemForTls || undefined);
|
|
371
|
-
await
|
|
372
|
-
|
|
373
|
-
|
|
369
|
+
const settings = await applySettingsFromServer(baseUrl, devId, issueResponse, keyPem, serverCaPemForTls || undefined);
|
|
370
|
+
await _runSshKeyRegistrationStep(baseUrl, issueResponse, keyPem, configDir, devId, serverCaPemForTls || undefined);
|
|
371
|
+
if (settings['docker-endpoint']) await augmentAndSaveCaPemForRemoteDocker(configDir, devId, caPem, {
|
|
372
|
+
settings,
|
|
374
373
|
warn: (msg) => logger.log(chalk.yellow(` ⚠ ${msg}`))
|
|
375
374
|
});
|
|
376
|
-
await
|
|
377
|
-
const sshInfo = await mergeDevSshConfigAfterInit(baseUrl, devId);
|
|
375
|
+
const sshInfo = await mergeDevSshConfigAfterInit(baseUrl, devId, settings);
|
|
378
376
|
logOnboardingFinished(sshInfo);
|
|
379
377
|
}
|
|
380
378
|
|
|
@@ -82,9 +82,9 @@ async function runCertificateRefresh(auth) {
|
|
|
82
82
|
const issueResponse = await requestCertificate(auth.serverUrl, devId, pin, csrPem, serverCaPem);
|
|
83
83
|
const caPem = mergeCaPemBlocks(serverCaPem, issueResponse.caCertificate, issueResponse.ca);
|
|
84
84
|
await saveCertAndConfig(configDir, devId, issueResponse.certificate, keyPem, caPem);
|
|
85
|
-
await applySettingsFromServer(auth.serverUrl, devId, issueResponse, keyPem, serverCaPem);
|
|
85
|
+
const settings = await applySettingsFromServer(auth.serverUrl, devId, issueResponse, keyPem, serverCaPem);
|
|
86
86
|
await augmentAndSaveCaPemForRemoteDocker(configDir, devId, caPem, {
|
|
87
|
-
settings
|
|
87
|
+
settings,
|
|
88
88
|
remoteServerUrl: auth.serverUrl,
|
|
89
89
|
warn: (msg) => logger.log(chalk.yellow(` ⚠ ${msg}`))
|
|
90
90
|
});
|
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
aggregateCoverage,
|
|
11
11
|
buildPackOverrideResult
|
|
12
12
|
} = require('./verify-governance-result-builders');
|
|
13
|
-
const { VERDICT
|
|
13
|
+
const { VERDICT } = require('../lifecycle/product-model');
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @async
|
|
@@ -43,14 +43,21 @@ async function runGovernanceVerifyForExternalSystem(systemKey, options = {}) {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const
|
|
46
|
+
const allNotApplicable = datasourceRows.every(
|
|
47
|
+
r => r.verdict === VERDICT.NOT_APPLICABLE
|
|
48
|
+
);
|
|
49
|
+
const allClosed = datasourceRows.every(
|
|
50
|
+
r => r.verdict === VERDICT.VERIFIED || r.verdict === VERDICT.NOT_APPLICABLE
|
|
51
|
+
);
|
|
47
52
|
const coverage = aggregateCoverage(datasourceRows);
|
|
48
53
|
return {
|
|
49
54
|
systemKey: resolvedKey,
|
|
50
55
|
command: 'governance verify',
|
|
51
|
-
verdict:
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
verdict: allNotApplicable
|
|
57
|
+
? VERDICT.NOT_APPLICABLE
|
|
58
|
+
: (allClosed ? VERDICT.VERIFIED : VERDICT.FAILED),
|
|
59
|
+
policyCoveragePercent: coverage.policy ?? (allClosed ? 100 : 0),
|
|
60
|
+
dimensionCoveragePercent: coverage.dimension ?? (allClosed ? 100 : 0),
|
|
54
61
|
enforcementScenarios: sumEnforcementScenarios(datasourceRows),
|
|
55
62
|
datasourceRows
|
|
56
63
|
};
|
|
@@ -16,11 +16,12 @@ const {
|
|
|
16
16
|
repairNoneLlmToServiceContract,
|
|
17
17
|
stripDeprecatedServiceCategory
|
|
18
18
|
} = require('./repair-datasource-service-contract');
|
|
19
|
+
const {
|
|
20
|
+
DEFAULT_DATASOURCE_SYNC,
|
|
21
|
+
createDefaultDatasourceSync
|
|
22
|
+
} = require('../datasource/sync-defaults');
|
|
19
23
|
|
|
20
|
-
const DEFAULT_SYNC =
|
|
21
|
-
mode: 'pull',
|
|
22
|
-
batchSize: 500
|
|
23
|
-
};
|
|
24
|
+
const DEFAULT_SYNC = DEFAULT_DATASOURCE_SYNC;
|
|
24
25
|
|
|
25
26
|
const MINIMAL_METADATA_SCHEMA = {
|
|
26
27
|
type: 'object',
|
|
@@ -348,7 +349,7 @@ function repairExposeFromAttributes(parsed, changes) {
|
|
|
348
349
|
}
|
|
349
350
|
|
|
350
351
|
/**
|
|
351
|
-
* Adds default sync section
|
|
352
|
+
* Adds a default sync section only when absent. Not applied for entityType 'none'.
|
|
352
353
|
*
|
|
353
354
|
* @param {Object} parsed - Parsed datasource (mutated)
|
|
354
355
|
* @param {string[]} changes - Change log
|
|
@@ -356,10 +357,9 @@ function repairExposeFromAttributes(parsed, changes) {
|
|
|
356
357
|
*/
|
|
357
358
|
function repairSyncSection(parsed, changes) {
|
|
358
359
|
if (isNoneEntityType(parsed?.entityType)) return false;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
changes.push('Added default sync section (mode: pull, batchSize: 500)');
|
|
360
|
+
if (Object.prototype.hasOwnProperty.call(parsed, 'sync')) return false;
|
|
361
|
+
parsed.sync = createDefaultDatasourceSync();
|
|
362
|
+
changes.push('Added default sync section (enabled: true, mode: pull, schedule: @monthly UTC, batchSize: 500)');
|
|
363
363
|
return true;
|
|
364
364
|
}
|
|
365
365
|
|
package/lib/commands/repair.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Repair external integration
|
|
2
|
+
* Repair application documentation and external integration configuration drift.
|
|
3
3
|
*
|
|
4
4
|
* Aligns application.yaml with actual system/datasource files, fixes system key mismatch,
|
|
5
5
|
* creates missing externalIntegration block, extracts rbac.yaml from system when needed,
|
|
6
6
|
* and regenerates the deployment manifest.
|
|
7
7
|
*
|
|
8
|
-
* @fileoverview Repair external integration drift
|
|
8
|
+
* @fileoverview Repair application README or external integration drift
|
|
9
9
|
* @author AI Fabrix Team
|
|
10
10
|
* @version 2.0.0
|
|
11
11
|
*/
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
15
|
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const { generateReadmeMd } = require('../app/readme');
|
|
18
|
+
const { backupIntegrationFile } = require('../utils/integration-file-backup');
|
|
19
|
+
const { trackRepairWrite } = require('./repair-changed-files');
|
|
16
20
|
const { detectAppType } = require('../utils/paths');
|
|
17
21
|
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
18
22
|
const { loadConfigFile, isYamlPath } = require('../utils/config-format');
|
|
@@ -43,7 +47,7 @@ function loadConfigAndDiscover(appPath, configPath) {
|
|
|
43
47
|
* Validates repair inputs and resolves app path and config path.
|
|
44
48
|
* @param {string} appName - Application name
|
|
45
49
|
* @param {Object} options - Command options (auth)
|
|
46
|
-
* @returns {Promise<{ appPath: string, configPath: string, dryRun: boolean, authOption: string|undefined }>}
|
|
50
|
+
* @returns {Promise<{ appPath: string, configPath: string, dryRun: boolean, authOption: string|undefined, isExternal: boolean }>}
|
|
47
51
|
*/
|
|
48
52
|
async function validateAndResolveRepairPaths(appName, options) {
|
|
49
53
|
if (!appName || typeof appName !== 'string') throw new Error('App name is required');
|
|
@@ -52,10 +56,42 @@ async function validateAndResolveRepairPaths(appName, options) {
|
|
|
52
56
|
throw new Error('Option --auth must be a string');
|
|
53
57
|
}
|
|
54
58
|
const { appPath, isExternal } = await detectAppType(appName, options);
|
|
55
|
-
if (!isExternal)
|
|
59
|
+
if (!isExternal) validateApplicationRepairOptions(appName, options);
|
|
56
60
|
const configPath = resolveApplicationConfigPath(appPath);
|
|
57
61
|
if (!fs.existsSync(configPath)) throw new Error(`Application config not found: ${configPath}`);
|
|
58
|
-
return { appPath, configPath, dryRun, authOption };
|
|
62
|
+
return { appPath, configPath, dryRun, authOption, isExternal };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Validate the documentation-only repair supported for normal applications. */
|
|
66
|
+
function validateApplicationRepairOptions(appName, options) {
|
|
67
|
+
const integrationOptions = ['all', 'api', 'auth', 'rbac', 'expose', 'sync', 'test',
|
|
68
|
+
'splitDatasources', 'rbacPreset', 'authWebhook', 'authSync'];
|
|
69
|
+
if (!options.doc || integrationOptions.some(key => options[key] !== undefined && options[key] !== false && options[key] !== null)) {
|
|
70
|
+
throw new Error(`App '${appName}' is not an external integration. Normal applications support repair --doc only (with --dry-run or --no-backup).`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Regenerate a normal application's README using its current local configuration. */
|
|
75
|
+
function repairApplicationReadme(appName, appPath, configPath, backupCtx) {
|
|
76
|
+
const variables = loadConfigFile(configPath);
|
|
77
|
+
const content = generateReadmeMd(appName, { ...variables, ...variables.app });
|
|
78
|
+
const readmePath = path.join(appPath, 'README.md');
|
|
79
|
+
trackRepairWrite(readmePath, backupCtx);
|
|
80
|
+
if (!backupCtx.dryRun) {
|
|
81
|
+
backupIntegrationFile(readmePath, backupCtx);
|
|
82
|
+
fs.writeFileSync(readmePath, content, { mode: 0o644, encoding: 'utf8' });
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
updated: true,
|
|
86
|
+
applicationReadme: true,
|
|
87
|
+
actionsPerformed: true,
|
|
88
|
+
readmeRegenerated: true,
|
|
89
|
+
manifestRegenerated: false,
|
|
90
|
+
changes: ['Regenerated README.md from application configuration'],
|
|
91
|
+
changedFiles: backupCtx.dryRun ? backupCtx.wouldChangeFiles : backupCtx.changedFiles,
|
|
92
|
+
backupPaths: backupCtx.backupPaths,
|
|
93
|
+
warnings: []
|
|
94
|
+
};
|
|
59
95
|
}
|
|
60
96
|
|
|
61
97
|
/**
|
|
@@ -79,17 +115,18 @@ function createRepairBackupContext(options, dryRun) {
|
|
|
79
115
|
}
|
|
80
116
|
|
|
81
117
|
/**
|
|
82
|
-
* Repairs external integration config
|
|
118
|
+
* Repairs external integration config or regenerates a normal application's README.
|
|
83
119
|
* @async
|
|
84
120
|
* @param {string} appName - Application/integration name
|
|
85
121
|
* @param {Object} [options] - Options
|
|
86
122
|
* @param {boolean} [options.dryRun] - If true, only report changes; do not write
|
|
87
|
-
* @param {boolean} [options.doc] -
|
|
123
|
+
* @param {boolean} [options.doc] - Regenerate README.md from application config or integration deployment manifest
|
|
88
124
|
* @returns {Promise<Object>}
|
|
89
125
|
*/
|
|
90
126
|
async function repairExternalIntegration(appName, options = {}) {
|
|
91
|
-
const { appPath, configPath, dryRun, authOption } = await validateAndResolveRepairPaths(appName, options);
|
|
127
|
+
const { appPath, configPath, dryRun, authOption, isExternal } = await validateAndResolveRepairPaths(appName, options);
|
|
92
128
|
const { backupPaths, backupCtx } = createRepairBackupContext(options, dryRun);
|
|
129
|
+
if (!isExternal) return repairApplicationReadme(appName, appPath, configPath, backupCtx);
|
|
93
130
|
const loaded = loadConfigAndDiscover(appPath, configPath);
|
|
94
131
|
return runRepairExternalIntegration({
|
|
95
132
|
appName,
|
|
@@ -41,6 +41,10 @@ const {
|
|
|
41
41
|
patchCaseStatusAfterRun
|
|
42
42
|
} = require('../role-assistant/test-pack-sync');
|
|
43
43
|
const { collectString } = require('./governance');
|
|
44
|
+
const {
|
|
45
|
+
hasRoleAssistantTestSubject,
|
|
46
|
+
runPublishedCaseAsSubject
|
|
47
|
+
} = require('../role-assistant/test-job-runner');
|
|
44
48
|
const {
|
|
45
49
|
sectionTitle,
|
|
46
50
|
headerKeyValue,
|
|
@@ -338,6 +342,9 @@ async function resolveExecutableCaseResult(roleKey, roleCase, opts, result, verd
|
|
|
338
342
|
run: false,
|
|
339
343
|
noPublish: true
|
|
340
344
|
});
|
|
345
|
+
if (hasRoleAssistantTestSubject(opts)) {
|
|
346
|
+
return runPublishedCaseAsSubject(roleKey, roleCase, opts, authContext);
|
|
347
|
+
}
|
|
341
348
|
const evidencePreflight = await assertRoleAssistantEvidencePreflight(roleCase, authContext);
|
|
342
349
|
const executionResult = await runRoleAssistantCaseLifecycle(roleKey, roleCase, authContext, opts);
|
|
343
350
|
const executionVerdict = lifecycleVerdictFromCaseExecution(executionResult, verdict);
|