@aifabrix/builder 2.58.0 → 2.59.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/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 +84 -0
- package/lib/agent-kit/start.js +4 -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/types/dev.types.js +3 -3
- package/lib/app/deploy.js +0 -102
- package/lib/cli/index.js +4 -0
- 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/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/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/datasource/datasource-validate-summary.js +8 -5
- package/lib/datasource/sync-defaults.js +53 -0
- package/lib/deployment/installation/azure-infra-stage.js +10 -0
- 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 +8 -2
- 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/index.js +23 -12
- 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/schema/environment-deploy-request.schema.json +1 -1
- package/lib/schema/external-datasource.schema.json +20 -5
- package/lib/schema/infra-parameter.schema.json +1 -1
- package/lib/schema/infra.parameter.yaml +2 -1
- package/lib/schema/infrastructure-schema.json +145 -1
- package/lib/schema/installation-environment.schema.json +1 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- 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-daemon-tls-ca.js +2 -2
- package/lib/utils/error-formatters/validation-errors.js +25 -5
- package/lib/utils/help-builder.js +4 -3
- package/lib/utils/token-manager.js +29 -36
- package/package.json +3 -2
- package/templates/agent-kit/instructions/AGENTKIT.md +39 -2
- package/templates/agent-kit/instructions/root.AGENTS.md +40 -1
- package/templates/agent-kit/skills/shared/hosts.md +23 -5
- package/templates/agent-kit/workspace/README.md +14 -1
- package/templates/applications/builder-api/application.yaml +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/external-system/external-datasource.yaml.hbs +7 -6
- package/templates/marketplace/main.json +438 -80
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Top-level managed Agent Kit onboarding command. */
|
|
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 { runManagedOnboarding } = require('../agent-kit/setup');
|
|
8
|
+
|
|
9
|
+
function setupOnboardingCommand(program) {
|
|
10
|
+
program.command('onboarding')
|
|
11
|
+
.description('Prepare your provisioned Agent Kit workspace for the selected coding platform')
|
|
12
|
+
.option('--coding <platform>', 'Use claude, codex, or cursor for this run')
|
|
13
|
+
.option('--name <folder>', 'Create or reuse this workspace folder under the managed work root')
|
|
14
|
+
.option('--repo <owner/name>', 'Reuse or clone this GitHub repository, or create it privately')
|
|
15
|
+
.addHelpText('after', '\nRun on your managed workspace host after connecting by SSH.\n' +
|
|
16
|
+
'Reads ~/.aifabrix/config.yaml; --coding overrides its codingPlatform for this run only.\n' +
|
|
17
|
+
'For portable onboarding use aifabrix agent-kit setup <controllerUrl>.\n\n' +
|
|
18
|
+
'Examples:\n' +
|
|
19
|
+
' $ af onboarding\n' +
|
|
20
|
+
' $ af onboarding --coding claude\n' +
|
|
21
|
+
' $ af onboarding --coding claude --name sales-workspace\n' +
|
|
22
|
+
' $ af onboarding --coding claude --name finance-workspace\n' +
|
|
23
|
+
' $ af onboarding --repo example-org/example-workspace\n' +
|
|
24
|
+
' $ af onboarding --repo my-org/new-repo\n' +
|
|
25
|
+
' $ af onboarding --coding codex\n' +
|
|
26
|
+
' $ af onboarding --coding cursor\n')
|
|
27
|
+
.action(async(options) => {
|
|
28
|
+
try {
|
|
29
|
+
await runManagedOnboarding(options);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
logger.error(formatBlockingError(maskSensitiveData(error.message)));
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { setupOnboardingCommand };
|
|
@@ -99,7 +99,9 @@ function logRepairDryRunOutcome(options, result, changedFiles) {
|
|
|
99
99
|
*/
|
|
100
100
|
function logRepairSuccessOutcome(result, changedFiles) {
|
|
101
101
|
if (result.updated) {
|
|
102
|
-
logger.log(formatSuccessParagraph(
|
|
102
|
+
logger.log(formatSuccessParagraph(result.applicationReadme
|
|
103
|
+
? 'Regenerated README.md from application configuration.'
|
|
104
|
+
: 'Repaired external integration config.'));
|
|
103
105
|
logRepairChangedFiles(changedFiles, false);
|
|
104
106
|
logRepairNextActions(changedFiles, result.warnings);
|
|
105
107
|
return;
|
|
@@ -166,7 +168,7 @@ async function handleRepairCommand(appName, options) {
|
|
|
166
168
|
*/
|
|
167
169
|
function setupRepairCommand(program) {
|
|
168
170
|
program.command('repair <systemKey>')
|
|
169
|
-
.description('Fix external integration drift
|
|
171
|
+
.description('Fix external integration drift or regenerate application documentation (--doc)')
|
|
170
172
|
.option('--all', 'Run all repair actions (api, doc, expose, rbac, sync, test)')
|
|
171
173
|
.option(
|
|
172
174
|
'--api',
|
|
@@ -176,10 +178,10 @@ function setupRepairCommand(program) {
|
|
|
176
178
|
'--auth <method>',
|
|
177
179
|
'Set authentication method (oauth2, aad, apikey, bearerToken, basic, queryParam, oidc, hmac, none); updates system file and env.template'
|
|
178
180
|
)
|
|
179
|
-
.option('--doc', 'Regenerate README.md from deployment manifest')
|
|
181
|
+
.option('--doc', 'Regenerate README.md from application config or external integration deployment manifest')
|
|
180
182
|
.option('--expose', 'Set exposed.schema on each datasource from all fieldMappings.attributes keys (metadata.<key>); removes deprecated exposed.attributes if present')
|
|
181
183
|
.option('--rbac', 'Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist; warns when capabilities lack openapi.operations (auto-syncs operations)')
|
|
182
|
-
.option('--sync', 'Add
|
|
184
|
+
.option('--sync', 'Add enabled monthly UTC sync defaults when the sync section is absent')
|
|
183
185
|
.option(
|
|
184
186
|
'--auth-webhook',
|
|
185
187
|
'Ensure authentications[] slot usage=webhookInbound (454 webhook inbound auth)'
|
|
@@ -197,7 +199,7 @@ function setupRepairCommand(program) {
|
|
|
197
199
|
'--rbac-preset <preset>',
|
|
198
200
|
'RBAC preset for facade APIs (e.g. platform — dataplane role catalog)'
|
|
199
201
|
)
|
|
200
|
-
.option('--no-backup', 'Skip timestamped copies under
|
|
202
|
+
.option('--no-backup', 'Skip timestamped copies under the application folder backup/')
|
|
201
203
|
.option('--dry-run', 'Report changes only; do not write')
|
|
202
204
|
.addHelpText('after', REPAIR_HELP_AFTER)
|
|
203
205
|
.action(async(appName, options) => {
|
|
@@ -16,6 +16,7 @@ Coding hosts (--host) are Cursor, Claude Code, and/or Codex in this workspace.
|
|
|
16
16
|
GitHub remotes are a separate command (agent-kit remote create).
|
|
17
17
|
|
|
18
18
|
Examples:
|
|
19
|
+
$ aifabrix agent-kit setup https://controller.example.com
|
|
19
20
|
$ aifabrix agent-kit init cust-demo --host all --approve-all --yes
|
|
20
21
|
$ aifabrix agent-kit install --host claude --approve-all --yes
|
|
21
22
|
$ aifabrix agent-kit install --host all --approve-all claude --yes
|
|
@@ -94,10 +95,12 @@ Silent remote: --host all --remote --org <owner> --yes
|
|
|
94
95
|
const REMOTE_GROUP_HELP = `
|
|
95
96
|
This is GitHub only: create origin and push this workspace.
|
|
96
97
|
It does not choose Cursor, Claude Code, or Codex.
|
|
98
|
+
For an existing GitHub repository on a managed host, use af onboarding --repo owner/name.
|
|
97
99
|
|
|
98
100
|
Examples:
|
|
99
|
-
$
|
|
100
|
-
$
|
|
101
|
+
$ cd ~/agent-kit-workspace && af agent-kit remote create --org my-org --name my-repo --yes
|
|
102
|
+
$ af onboarding --repo my-org/existing-repo
|
|
103
|
+
$ af onboarding --repo my-org/new-repo
|
|
101
104
|
$ aifabrix agent-kit install --host claude --approve-all --yes
|
|
102
105
|
$ aifabrix agent-kit install --host codex --approve-all --yes
|
|
103
106
|
$ aifabrix agent-kit install --host all --approve-all claude --yes
|
|
@@ -105,12 +108,16 @@ Examples:
|
|
|
105
108
|
|
|
106
109
|
const REMOTE_HELP = `
|
|
107
110
|
Creates a GitHub repository (origin) and pushes this workspace.
|
|
111
|
+
Run inside the local workspace. --name sets the new GitHub repository name;
|
|
112
|
+
omit it to use the workspace folder name. This command does not clone an
|
|
113
|
+
existing GitHub repository.
|
|
108
114
|
Not coding-host selection — use --host on install/update for Claude or Codex.
|
|
109
115
|
|
|
110
116
|
Examples:
|
|
111
|
-
$ cd
|
|
112
|
-
$
|
|
113
|
-
$
|
|
117
|
+
$ cd ~/agent-kit-workspace && af agent-kit remote create --org my-org --name my-repo --yes
|
|
118
|
+
$ af agent-kit remote create --org my-org --name my-repo --dry-run --json
|
|
119
|
+
$ af onboarding --repo my-org/existing-repo
|
|
120
|
+
$ af onboarding --repo my-org/new-repo
|
|
114
121
|
|
|
115
122
|
Interactive (TTY): omit flags to be prompted. Silent: --org <owner> --yes
|
|
116
123
|
`;
|
|
@@ -134,7 +141,10 @@ Examples:
|
|
|
134
141
|
`;
|
|
135
142
|
|
|
136
143
|
const LIST_HELP = `
|
|
137
|
-
Lists
|
|
144
|
+
Lists active tmux sessions and direct Codex app servers owned by the current
|
|
145
|
+
Unix user. On a terminal, choose a tmux session to attach or Close. Codex app
|
|
146
|
+
servers are shown as information and cannot be selected from this menu.
|
|
147
|
+
--json and non-interactive output print the report without a prompt.
|
|
138
148
|
|
|
139
149
|
Examples:
|
|
140
150
|
$ aifabrix agent-kit list
|
|
@@ -142,9 +152,13 @@ Examples:
|
|
|
142
152
|
`;
|
|
143
153
|
|
|
144
154
|
const KILL_HELP = `
|
|
145
|
-
Stops exactly one tmux session.
|
|
155
|
+
Stops exactly one tmux session by name. Omit the name in an interactive
|
|
156
|
+
terminal to choose a tmux session or Codex app server. Stopping a Codex app
|
|
157
|
+
server requires confirmation and may disconnect an active desktop chat.
|
|
158
|
+
Pass an exact tmux name for scripts or --json.
|
|
146
159
|
|
|
147
160
|
Examples:
|
|
161
|
+
$ aifabrix agent-kit kill
|
|
148
162
|
$ aifabrix agent-kit kill aifabrix-miso
|
|
149
163
|
$ aifabrix agent-kit kill aifabrix-miso --json
|
|
150
164
|
`;
|
|
@@ -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
|
|