@aifabrix/builder 2.57.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/app/push.js +98 -41
- package/lib/build/standard-docker-build.js +13 -11
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-app.js +16 -5
- package/lib/cli/setup-dev-path-commands.js +53 -2
- package/lib/cli/setup-dev.js +7 -4
- package/lib/cli/setup-environment.js +17 -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/dev-registry.js +155 -0
- package/lib/commands/dev-show-display.js +10 -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/setup-image-refresh.js +26 -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/config-attach-extensions.js +4 -1
- 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 +50 -9
- package/lib/deployment/installation/azure-preflight.js +63 -7
- package/lib/deployment/installation/azure-readiness.js +59 -7
- 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 +57 -5
- package/lib/deployment/installation/infra-manifest.js +22 -2
- package/lib/deployment/installation/infra-projection.js +14 -3
- package/lib/deployment/installation/registry-auth-mode.js +121 -0
- package/lib/deployment/installation/stage-input.js +21 -6
- package/lib/deployment/marketplace-environment.js +26 -1
- package/lib/generator/builders.js +8 -9
- 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 +200 -3
- package/lib/schema/installation-environment.schema.json +1 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/app-register-config.js +2 -4
- package/lib/utils/build-resolve-image.js +2 -21
- package/lib/utils/cli-utils.js +8 -1
- package/lib/utils/config-paths.js +24 -29
- package/lib/utils/config-registry-preference.js +113 -0
- 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 +53 -19
- package/lib/utils/docker-daemon-tls-ca.js +2 -2
- package/lib/utils/env-map.js +23 -6
- package/lib/utils/error-formatters/validation-errors.js +25 -5
- package/lib/utils/help-builder.js +4 -3
- package/lib/utils/image-tags.js +88 -0
- package/lib/utils/promote-dev-docker-image.js +6 -0
- package/lib/utils/registry-credentials.js +236 -0
- package/lib/utils/resolve-docker-image-ref.js +78 -9
- package/lib/utils/secrets-helpers.js +9 -1
- package/lib/utils/token-manager.js +29 -36
- package/lib/utils/variable-transformer.js +2 -4
- package/package.json +4 -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 +440 -82
package/lib/app/push.js
CHANGED
|
@@ -93,23 +93,24 @@ function extractImageName(config, appName) {
|
|
|
93
93
|
*/
|
|
94
94
|
async function loadPushConfig(appName, options) {
|
|
95
95
|
const { loadConfigFile } = require('../utils/config-format');
|
|
96
|
+
const { resolveEffectiveRegistry, stripRegistryHostFromRepository } = require('../utils/resolve-docker-image-ref');
|
|
97
|
+
let config;
|
|
96
98
|
try {
|
|
97
99
|
const { appPath } = await paths.detectAppType(appName);
|
|
98
|
-
|
|
99
|
-
const config = loadConfigFile(configPath);
|
|
100
|
-
const effective = getEffectiveConfig(config);
|
|
101
|
-
const registry = options.registry || effective.image?.registry;
|
|
102
|
-
if (!registry) {
|
|
103
|
-
throw new Error('Registry URL is required. Provide via --registry flag or configure in application config under image.registry');
|
|
104
|
-
}
|
|
105
|
-
const imageName = extractImageName(config, appName);
|
|
106
|
-
return { registry, imageName };
|
|
100
|
+
config = loadConfigFile(paths.resolveApplicationConfigPath(appPath));
|
|
107
101
|
} catch (error) {
|
|
108
|
-
if (error.message.includes('Registry URL')) {
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
111
102
|
throw new Error(`Failed to load configuration: ${error.message}\nRun 'aifabrix create ${appName}' first`);
|
|
112
103
|
}
|
|
104
|
+
const effective = getEffectiveConfig(config);
|
|
105
|
+
const { registry } = resolveEffectiveRegistry(effective, { registry: options.registry });
|
|
106
|
+
if (!registry) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
'Registry URL is required. Provide --registry, run "aifabrix dev set-registry <registry>", ' +
|
|
109
|
+
'or configure image.registry in the application config'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const imageName = stripRegistryHostFromRepository(extractImageName(config, appName));
|
|
113
|
+
return { registry, imageName, appVersion: require('../utils/image-tags').readAppVersion(effective) };
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
/**
|
|
@@ -129,10 +130,43 @@ async function validatePushConfig(registry, imageName, appName) {
|
|
|
129
130
|
throw new Error(`Invalid registry URL format: ${registry}. Expected format: *.azurecr.io`);
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
const sourceRef = await resolveLocalSourceRef(registry, imageName);
|
|
134
|
+
if (!sourceRef) {
|
|
135
|
+
throw new Error(`Docker image ${registry}/${imageName}:latest or ${imageName}:latest not found locally.\nRun 'aifabrix build ${appName}' first`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { getRegistryCredentialStatus } = require('../utils/registry-credentials');
|
|
139
|
+
const credentialStatus = getRegistryCredentialStatus(registry);
|
|
140
|
+
if (credentialStatus === 'partial') {
|
|
141
|
+
await require('../utils/registry-credentials').loadRegistryCredentials(registry);
|
|
142
|
+
}
|
|
143
|
+
if (credentialStatus === 'complete') {
|
|
144
|
+
return sourceRef;
|
|
145
|
+
}
|
|
146
|
+
await validateAzureCli();
|
|
147
|
+
return sourceRef;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Local image to push from: the registry-qualified build output first, else the plain repository.
|
|
152
|
+
* @param {string} registry - Registry host
|
|
153
|
+
* @param {string} imageName - Repository without host
|
|
154
|
+
* @returns {Promise<string|null>} `repository:latest` or null
|
|
155
|
+
*/
|
|
156
|
+
async function resolveLocalSourceRef(registry, imageName) {
|
|
157
|
+
for (const repository of [`${registry}/${imageName}`, imageName]) {
|
|
158
|
+
if (await pushUtils.checkLocalImageExists(repository, 'latest')) {
|
|
159
|
+
return `${repository}:latest`;
|
|
160
|
+
}
|
|
134
161
|
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
135
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Azure CLI prerequisites for the ACR login fallback (no stored registry credentials).
|
|
167
|
+
* @returns {Promise<void>}
|
|
168
|
+
*/
|
|
169
|
+
async function validateAzureCli() {
|
|
136
170
|
if (!await pushUtils.checkAzureCLIInstalled()) {
|
|
137
171
|
throw new Error('Azure CLI is not installed. Install from: https://docs.microsoft.com/cli/azure/install-azure-cli');
|
|
138
172
|
}
|
|
@@ -149,7 +183,7 @@ async function validatePushConfig(registry, imageName, appName) {
|
|
|
149
183
|
* @async
|
|
150
184
|
* @param {string} registry - Registry URL
|
|
151
185
|
*/
|
|
152
|
-
async function
|
|
186
|
+
async function authenticateWithAzureCli(registry) {
|
|
153
187
|
if (await pushUtils.checkACRAuthentication(registry)) {
|
|
154
188
|
logger.log(formatSuccessLine(`Already authenticated with ${registry}`));
|
|
155
189
|
} else {
|
|
@@ -157,6 +191,16 @@ async function authenticateWithRegistry(registry) {
|
|
|
157
191
|
}
|
|
158
192
|
}
|
|
159
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Stored host-scoped credentials first (stdin login), else Azure CLI. Once per command.
|
|
196
|
+
* @param {string} registry - Registry host
|
|
197
|
+
* @returns {Promise<void>}
|
|
198
|
+
*/
|
|
199
|
+
async function authenticateWithRegistry(registry) {
|
|
200
|
+
const { ensureRegistryLogin } = require('../utils/registry-credentials');
|
|
201
|
+
await ensureRegistryLogin(registry, { fallback: authenticateWithAzureCli });
|
|
202
|
+
}
|
|
203
|
+
|
|
160
204
|
/**
|
|
161
205
|
* Layout-aligned notice when push is skipped for external integrations.
|
|
162
206
|
* @param {string} appName
|
|
@@ -191,30 +235,43 @@ function logPushCommandHeader(appName, registry, imageName) {
|
|
|
191
235
|
* @param {string} registry - Registry URL
|
|
192
236
|
* @param {Array<string>} tags - Image tags
|
|
193
237
|
*/
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
238
|
+
function isAuthenticationError(error) {
|
|
239
|
+
const message = (error && error.message) || String(error);
|
|
240
|
+
return message.includes('Authentication required') ||
|
|
241
|
+
message.includes('authentication required') ||
|
|
242
|
+
(message.includes('authentication') && message.includes('401'));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function tagAndPushOne(sourceRef, targetRef, registry) {
|
|
246
|
+
if (sourceRef !== targetRef) {
|
|
247
|
+
await pushUtils.tagImage(sourceRef, targetRef);
|
|
248
|
+
}
|
|
249
|
+
await pushUtils.pushImage(targetRef, registry);
|
|
250
|
+
}
|
|
206
251
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Tags and pushes every unique target once, re-authenticating once on an auth failure.
|
|
254
|
+
* @async
|
|
255
|
+
* @param {string} sourceRef - Local source image
|
|
256
|
+
* @param {string} imageName - Repository without host
|
|
257
|
+
* @param {string} registry - Registry host
|
|
258
|
+
* @param {Array<string>} tags - Resolved tags
|
|
259
|
+
*/
|
|
260
|
+
async function pushImageTags(sourceRef, imageName, registry, tags) {
|
|
261
|
+
let reauthenticated = false;
|
|
262
|
+
for (const tag of tags) {
|
|
263
|
+
const targetRef = `${registry}/${imageName}:${tag}`;
|
|
264
|
+
try {
|
|
265
|
+
await tagAndPushOne(sourceRef, targetRef, registry);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (reauthenticated || !isAuthenticationError(error)) {
|
|
268
|
+
throw new Error(`Push failed for ${targetRef}: ${error.message}`);
|
|
269
|
+
}
|
|
270
|
+
logger.log(formatWarningLine('Registry authentication expired; re-authenticating, then retrying push.'));
|
|
271
|
+
require('../utils/registry-credentials').resetRegistryLoginCache(registry);
|
|
211
272
|
await authenticateWithRegistry(registry);
|
|
212
|
-
|
|
213
|
-
await
|
|
214
|
-
await pushUtils.pushImage(`${registry}/${imageName}:${tag}`, registry);
|
|
215
|
-
}));
|
|
216
|
-
} else {
|
|
217
|
-
throw error;
|
|
273
|
+
reauthenticated = true;
|
|
274
|
+
await tagAndPushOne(sourceRef, targetRef, registry);
|
|
218
275
|
}
|
|
219
276
|
}
|
|
220
277
|
}
|
|
@@ -252,12 +309,12 @@ async function pushApp(appName, options = {}) {
|
|
|
252
309
|
}
|
|
253
310
|
try {
|
|
254
311
|
validateAppName(appName);
|
|
255
|
-
const { registry, imageName } = await loadPushConfig(appName, options);
|
|
256
|
-
|
|
312
|
+
const { registry, imageName, appVersion } = await loadPushConfig(appName, options);
|
|
313
|
+
const tags = require('../utils/image-tags').resolveImageTags(options.tag, appVersion);
|
|
314
|
+
const sourceRef = await validatePushConfig(registry, imageName, appName);
|
|
257
315
|
logPushCommandHeader(appName, registry, imageName);
|
|
258
316
|
await authenticateWithRegistry(registry);
|
|
259
|
-
|
|
260
|
-
await pushImageTags(imageName, registry, tags);
|
|
317
|
+
await pushImageTags(sourceRef, imageName, registry, tags);
|
|
261
318
|
displayPushResults(registry, imageName, tags);
|
|
262
319
|
} catch (error) {
|
|
263
320
|
throw new Error(`Failed to push application: ${error.message}`);
|
|
@@ -80,21 +80,18 @@ async function executeDockerImageBuild(appName, options, buildConfig) {
|
|
|
80
80
|
);
|
|
81
81
|
await ensureRunSecretsForApp(appName);
|
|
82
82
|
const buildArgs = await resolveDockerBuildArgsForApp(appName, contextPath);
|
|
83
|
-
const
|
|
84
|
-
if (typeof tag === 'string' && tag.includes(',')) {
|
|
85
|
-
throw new Error('Use a single image tag per build (comma-separated multiple tags are not supported).');
|
|
86
|
-
}
|
|
83
|
+
const { primary, aliases, tags } = options.tagPlan;
|
|
87
84
|
await dockerBuild.executeDockerBuildWithTag(
|
|
88
85
|
effectiveImageName,
|
|
89
86
|
imageName,
|
|
90
87
|
dockerfilePath,
|
|
91
88
|
contextPath,
|
|
92
|
-
|
|
93
|
-
{ ...options, buildArgs }
|
|
89
|
+
primary,
|
|
90
|
+
{ ...options, buildArgs, tagAliases: aliases }
|
|
94
91
|
);
|
|
95
92
|
await postBuildTasks(appName, buildConfig);
|
|
96
93
|
logger.log(formatSuccessParagraph('Build completed successfully!'));
|
|
97
|
-
return `${effectiveImageName}:${tag}
|
|
94
|
+
return tags.map((tag) => `${effectiveImageName}:${tag}`).join(', ');
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
/**
|
|
@@ -104,12 +101,17 @@ async function executeDockerImageBuild(appName, options, buildConfig) {
|
|
|
104
101
|
* @returns {Promise<string>} Built image ref e.g. name:tag
|
|
105
102
|
*/
|
|
106
103
|
async function runStandardDockerBuild(appName, options) {
|
|
107
|
-
const
|
|
104
|
+
const { resolveBuildTagPlan, readAppVersion } = require('../utils/image-tags');
|
|
105
|
+
const { buildConfig, config: appConfig } = await buildHelpers.loadAndValidateConfig(appName);
|
|
106
|
+
// Resolve and validate every tag before any Docker build, tag, login or promote.
|
|
107
|
+
const planned = { ...options, tagPlan: resolveBuildTagPlan(options.tag, readAppVersion(appConfig)) };
|
|
108
|
+
const promotedRef = await tryCompleteDevPromoteBuild(appName, planned);
|
|
108
109
|
if (promotedRef) {
|
|
109
|
-
|
|
110
|
+
const { resolvePromoteBaseTags } = require('../utils/promote-dev-docker-image');
|
|
111
|
+
const baseName = promotedRef.slice(0, promotedRef.lastIndexOf(':'));
|
|
112
|
+
return resolvePromoteBaseTags(planned).map((tag) => `${baseName}:${tag}`).join(', ');
|
|
110
113
|
}
|
|
111
|
-
|
|
112
|
-
return executeDockerImageBuild(appName, options, buildConfig);
|
|
114
|
+
return executeDockerImageBuild(appName, planned, buildConfig);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
module.exports = { runStandardDockerBuild };
|
package/lib/cli/index.js
CHANGED
|
@@ -41,6 +41,8 @@ const { setupChannelCommands } = require('../commands/channel');
|
|
|
41
41
|
const { setupManifestCommands } = require('../commands/manifest');
|
|
42
42
|
const { setupNpmCommands } = require('./setup-npm');
|
|
43
43
|
const { setupListCommand } = require('./setup-list');
|
|
44
|
+
const { setupHelpCommand } = require('./setup-help');
|
|
45
|
+
const { setupOnboardingCommand } = require('./setup-onboarding');
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
48
|
* Dev, secrets, parameters, and platform commands (keeps setupCommands under statement limit).
|
|
@@ -101,6 +103,8 @@ function setupExtendedCliCommands(program) {
|
|
|
101
103
|
* @param {Command} program - Commander program instance
|
|
102
104
|
*/
|
|
103
105
|
function setupCommands(program) {
|
|
106
|
+
setupHelpCommand(program);
|
|
107
|
+
setupOnboardingCommand(program);
|
|
104
108
|
setupCoreCliCommands(program);
|
|
105
109
|
setupExtendedCliCommands(program);
|
|
106
110
|
setupDevAndPlatformCommands(program);
|
package/lib/cli/setup-app.js
CHANGED
|
@@ -208,6 +208,7 @@ Examples:
|
|
|
208
208
|
.option('-p, --port <port>', 'Override local port')
|
|
209
209
|
.option('-d, --debug', 'Enable debug output with detailed container information')
|
|
210
210
|
.option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides application.yaml image.tag')
|
|
211
|
+
.option('-r, --registry <registry>', 'Registry host for the image (overrides dev set-registry and application.yaml)')
|
|
211
212
|
.option('-e, --env <env>', 'Environment: dev (default), tst, or pro', 'dev')
|
|
212
213
|
.option('--base', 'Use manifest base image only (skip local developer-scoped tag preference)')
|
|
213
214
|
.option('--reload', 'In dev: mount workspace into container (Mutagen only if docker-endpoint is a remote host)')
|
|
@@ -237,7 +238,11 @@ function registerBuildCommand(program) {
|
|
|
237
238
|
.option('-l, --language <lang>', 'Override language detection')
|
|
238
239
|
.option('-f, --force-template', 'Force rebuild from template')
|
|
239
240
|
.option('--no-cache', 'Full Docker rebuild (disable layer cache); use after Dockerfile or context fixes')
|
|
240
|
-
.option(
|
|
241
|
+
.option(
|
|
242
|
+
'-t, --tag <tags>',
|
|
243
|
+
'Image tag or comma-separated tags (default: latest); {version} expands to app.version, e.g. "latest,{version}"'
|
|
244
|
+
)
|
|
245
|
+
.option('-r, --registry <registry>', 'Registry host for the image name (overrides dev set-registry and application.yaml)')
|
|
241
246
|
.option(
|
|
242
247
|
'--base',
|
|
243
248
|
'Build/run the manifest base image (no -devN suffix). Default local build uses a developer-scoped repository when developer-id > 0.'
|
|
@@ -246,10 +251,16 @@ function registerBuildCommand(program) {
|
|
|
246
251
|
'--from-dev',
|
|
247
252
|
'With --base: tag the existing developer-scoped image as the base image instead of rebuilding when possible'
|
|
248
253
|
)
|
|
254
|
+
.addHelpText('after', `
|
|
255
|
+
Examples:
|
|
256
|
+
$ aifabrix build myapp
|
|
257
|
+
$ aifabrix build myapp --tag v1.0.0 # also tags latest
|
|
258
|
+
$ aifabrix build myapp --base --from-dev --tag 'latest,{version}'
|
|
259
|
+
$ aifabrix build myapp --registry myregistry.azurecr.io`)
|
|
249
260
|
.action(async(appName, options) => {
|
|
250
261
|
try {
|
|
251
|
-
const
|
|
252
|
-
logger.log(`✔ Built image: ${
|
|
262
|
+
const imageRefs = await app.buildApp(appName, options);
|
|
263
|
+
logger.log(`✔ Built image: ${imageRefs}`);
|
|
253
264
|
} catch (error) {
|
|
254
265
|
handleCommandError(error, 'build');
|
|
255
266
|
process.exit(1);
|
|
@@ -403,8 +414,8 @@ function setupPushDeployDockerfileCommands(program) {
|
|
|
403
414
|
program.command('push <appKey>')
|
|
404
415
|
.description('Push builder appKey image to Azure Container Registry')
|
|
405
416
|
.addHelpText('after', PUSH_HELP_AFTER)
|
|
406
|
-
.option('-r, --registry <registry>', '
|
|
407
|
-
.option('-t, --tag <
|
|
417
|
+
.option('-r, --registry <registry>', 'Registry host (overrides dev set-registry and application.yaml)')
|
|
418
|
+
.option('-t, --tag <tags>', 'Image tag or comma-separated tags (default: latest); {version} expands to app.version')
|
|
408
419
|
.action(async(appName, options) => {
|
|
409
420
|
try {
|
|
410
421
|
await app.pushApp(appName, options);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dev subcommands: set-home, set-work, print-home, print-work, shell-env, set-format
|
|
2
|
+
* Dev subcommands: set-home, set-work, print-home, print-work, shell-env, set-format,
|
|
3
|
+
* set-registry, print-registry.
|
|
3
4
|
*
|
|
4
5
|
* @fileoverview Path and format CLI registration for `aifabrix dev`
|
|
5
6
|
* @author AI Fabrix Team
|
|
@@ -133,6 +134,10 @@ function addPrintHomeWorkCommands(dev) {
|
|
|
133
134
|
}
|
|
134
135
|
});
|
|
135
136
|
|
|
137
|
+
addShellEnvCommand(dev);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function addShellEnvCommand(dev) {
|
|
136
141
|
dev
|
|
137
142
|
.command('shell-env')
|
|
138
143
|
.description(
|
|
@@ -178,8 +183,53 @@ function addSetFormatCommand(dev, handleSetFormat) {
|
|
|
178
183
|
});
|
|
179
184
|
}
|
|
180
185
|
|
|
186
|
+
const SET_REGISTRY_HELP = `
|
|
187
|
+
Examples:
|
|
188
|
+
$ aifabrix dev set-registry myregistry.azurecr.io
|
|
189
|
+
$ printf '%s' "$TOKEN" | aifabrix dev set-registry myregistry.azurecr.io --username puller --password-stdin
|
|
190
|
+
$ aifabrix dev set-registry myregistry.azurecr.io --clear-credentials
|
|
191
|
+
$ aifabrix dev set-registry --clear
|
|
192
|
+
|
|
193
|
+
Registry precedence for build, run, push and deploy: --registry, then this value, then the
|
|
194
|
+
application image.registry. Credentials are stored encrypted in your local secrets file as
|
|
195
|
+
<registry>-username and <registry>-password and are used for docker login (password on stdin).`;
|
|
196
|
+
|
|
197
|
+
function addRegistryCommands(dev) {
|
|
198
|
+
const { runSetRegistry, runPrintRegistry } = require('../commands/dev-registry');
|
|
199
|
+
dev
|
|
200
|
+
.command('set-registry [registry]')
|
|
201
|
+
.description('Set, clear or add credentials for the default container registry of this CLI installation')
|
|
202
|
+
.option('--username <username>', 'Registry username (requires a password option)')
|
|
203
|
+
.option('--password <password>', 'Registry password or token (visible in shell history; prefer --password-stdin)')
|
|
204
|
+
.option('--password-stdin', 'Read the registry password or token from stdin')
|
|
205
|
+
.option('--clear', 'Remove the saved default registry (credentials are kept)')
|
|
206
|
+
.option('--clear-credentials', 'Remove only the stored credentials for <registry>')
|
|
207
|
+
.addHelpText('after', SET_REGISTRY_HELP)
|
|
208
|
+
.action(async(registry, cmdOpts) => {
|
|
209
|
+
try {
|
|
210
|
+
await runSetRegistry(registry, cmdOpts);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
handleCommandError(error, 'dev set-registry');
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
dev
|
|
218
|
+
.command('print-registry')
|
|
219
|
+
.description('Print the saved default registry or an empty line (stdout only; for scripts)')
|
|
220
|
+
.addHelpText('after', '\nExamples:\n $ aifabrix dev print-registry\n')
|
|
221
|
+
.action(async() => {
|
|
222
|
+
try {
|
|
223
|
+
await runPrintRegistry();
|
|
224
|
+
} catch (error) {
|
|
225
|
+
handleCommandError(error, 'dev print-registry');
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
181
231
|
/**
|
|
182
|
-
* Register dev set-home, set-work, print-*, set-format.
|
|
232
|
+
* Register dev set-home, set-work, print-*, set-format, set-registry, print-registry.
|
|
183
233
|
* @param {import('commander').Command} dev - dev subcommand group
|
|
184
234
|
* @param {function(string): Promise<void>} handleSetFormat - handler that updates format and refreshes display
|
|
185
235
|
* @returns {void}
|
|
@@ -190,6 +240,7 @@ function setupDevPathAndFormatCommands(dev, handleSetFormat) {
|
|
|
190
240
|
addSetWorkCommand(dev);
|
|
191
241
|
addPrintHomeWorkCommands(dev);
|
|
192
242
|
addSetFormatCommand(dev, handleSetFormat);
|
|
243
|
+
addRegistryCommands(dev);
|
|
193
244
|
}
|
|
194
245
|
|
|
195
246
|
module.exports = { setupDevPathAndFormatCommands };
|
package/lib/cli/setup-dev.js
CHANGED
|
@@ -44,8 +44,11 @@ const DEV_ADD_HELP_AFTER = `
|
|
|
44
44
|
Examples:
|
|
45
45
|
$ aifabrix dev add --developer-id 02 --name "Jane Doe" --email jane@example.com
|
|
46
46
|
$ aifabrix dev add --developer-id 03 --name "Build admin" --email admin@example.com --groups admin,developer
|
|
47
|
-
$ aifabrix dev add --developer-id 04 --name "
|
|
48
|
-
$ aifabrix dev add --developer-id 05 --name "Integrator" --email integrator@example.com --groups
|
|
47
|
+
$ aifabrix dev add --developer-id 04 --name "Docker developer" --email docker-dev@example.com --groups developer,docker
|
|
48
|
+
$ aifabrix dev add --developer-id 05 --name "Integrator" --email integrator@example.com --groups integrator
|
|
49
|
+
|
|
50
|
+
Groups: integrator is for SSH onboarding without Docker. Use developer,docker for host Docker access (root-equivalent; grant sparingly).
|
|
51
|
+
When --groups is supplied, it replaces the default developer group; developer is not added automatically.
|
|
49
52
|
|
|
50
53
|
Requires a configured remote Builder Server and an admin client certificate (same machine setup as dev list / dev pin). After add, run dev pin <id> once to create a PIN for aifabrix dev init.
|
|
51
54
|
`;
|
|
@@ -318,7 +321,7 @@ function setupDevListAddCommands(dev) {
|
|
|
318
321
|
.requiredOption('--developer-id <id>', 'Unique id, digits only (e.g. 02); used with dev init --developer-id')
|
|
319
322
|
.requiredOption('--name <name>', 'Display name')
|
|
320
323
|
.requiredOption('--email <email>', 'Email address')
|
|
321
|
-
.option('--groups <items>', 'Comma-separated roles: admin, secret-manager, secret-reader, developer, docker', 'developer')
|
|
324
|
+
.option('--groups <items>', 'Comma-separated roles: admin, secret-manager, secret-reader, developer, docker, integrator', 'developer')
|
|
322
325
|
.option('--no-provision', 'Create user in database only (skip host provision job)')
|
|
323
326
|
.option('--builder-version <ver>', 'Builder CLI version for provision job')
|
|
324
327
|
.option('--admin-email <email>', 'Platform admin email for provision job')
|
|
@@ -373,7 +376,7 @@ function setupDevUpdatePinDeleteCommands(dev) {
|
|
|
373
376
|
.option('--developer-id <id>', 'Developer ID (same as dev add)')
|
|
374
377
|
.option('--name <name>', 'Display name')
|
|
375
378
|
.option('--email <email>', 'Email address')
|
|
376
|
-
.option('--groups <items>', 'Comma-separated groups (admin, secret-manager, secret-reader, developer, docker)')
|
|
379
|
+
.option('--groups <items>', 'Comma-separated groups (admin, secret-manager, secret-reader, developer, docker, integrator)')
|
|
377
380
|
.addHelpText('after', DEV_UPDATE_HELP_AFTER)
|
|
378
381
|
.action(async(developerId, options) => {
|
|
379
382
|
try {
|
|
@@ -21,14 +21,21 @@ function addMarketplaceOptions(command) {
|
|
|
21
21
|
.option('--vendor <vendor>', 'Installation vendor: AZURE (default) or DOCKER (register only)')
|
|
22
22
|
.option('--allow-my-ip', 'Allow the caller public IPv4 /32 to reach PostgreSQL, Key Vault and Storage (infra only; default off)')
|
|
23
23
|
.option('--license-edition <edition>', 'License requested at activation: COMMUNITY, STANDARD, ENTERPRISE or a Marketplace subscription ID (infra only; saved in the workspace)')
|
|
24
|
+
.option('--location <region>', 'Azure region for infra (saved to the workspace; dev, tst and pro inherit it)')
|
|
25
|
+
.option(
|
|
26
|
+
'--acr-auth <mode>',
|
|
27
|
+
'Image pull auth for infra: auto (default: saved dev set-registry pair, else managed identity), mi, or credentials (admin is a deprecated alias)'
|
|
28
|
+
)
|
|
24
29
|
.option('--miso-api-key-env <name>', 'Environment variable holding a temporary Miso API key (dev, tst, pro)')
|
|
25
|
-
.option(
|
|
30
|
+
.option(
|
|
31
|
+
'--controller-url <url>',
|
|
32
|
+
'Controller endpoint override for dev, tst or pro (normally recorded by infra); does not authenticate'
|
|
33
|
+
)
|
|
26
34
|
.option('--marketplace-json <file>', 'Deprecated explicit mode: ARM Marketplace infrastructure template')
|
|
27
35
|
.option('--parameters <file>', 'Deprecated explicit mode: secure ARM deploymentParameters JSON file')
|
|
28
36
|
.option('--subscription <id>', 'Deprecated explicit mode: Azure subscription ID')
|
|
29
37
|
.option('--resource-group <name>', 'Deprecated explicit mode: new isolated Azure resource group')
|
|
30
|
-
.option('--install-id <id>', 'Deprecated explicit mode: installation ID and resume-journal key')
|
|
31
|
-
.option('--location <region>', 'Deprecated explicit mode: Azure region when not set in the parameter file');
|
|
38
|
+
.option('--install-id <id>', 'Deprecated explicit mode: installation ID and resume-journal key');
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
async function runLegacyMarketplace(envKey, options) {
|
|
@@ -78,6 +85,8 @@ Installation workspace (<env> is the resource group; files under infrastructure/
|
|
|
78
85
|
$ af env deploy <resource-group> --stage infra
|
|
79
86
|
$ af env deploy <resource-group> --stage infra --allow-my-ip
|
|
80
87
|
$ af env deploy <resource-group> --stage infra --license-edition ENTERPRISE
|
|
88
|
+
$ af env deploy <resource-group> --stage infra --location swedencentral
|
|
89
|
+
$ af env deploy <resource-group> --stage infra --acr-auth credentials
|
|
81
90
|
$ af env deploy <resource-group> --stage dev
|
|
82
91
|
$ af env deploy <resource-group> --stage pro --preset m
|
|
83
92
|
$ af env deploy <resource-group> --stage register --vendor DOCKER
|
|
@@ -86,7 +95,11 @@ Vendor/stage matrix:
|
|
|
86
95
|
AZURE infra, dev, tst, pro (Mori bootstrap runs inside the Marketplace template)
|
|
87
96
|
DOCKER register only (deploy with aifabrix setup)
|
|
88
97
|
|
|
89
|
-
Prerequisites: aifabrix login (controller permission controller:deploy);
|
|
98
|
+
Prerequisites: aifabrix login or MISO_API_KEY / --miso-api-key-env (controller permission controller:deploy);
|
|
99
|
+
infra also needs az login. --controller-url only selects the endpoint; it never replaces authentication.
|
|
100
|
+
Registry pull auth for infra: save a pair once with aifabrix dev set-registry <acr-login-server> --username <user> --password-stdin.
|
|
101
|
+
Deprecated explicit Marketplace flags: --marketplace-json, --parameters, --subscription, --resource-group, --install-id
|
|
102
|
+
(that route also accepts --location).
|
|
90
103
|
Environment keys: miso, dev, tst, pro. Default size preset: s (use --preset s|m|l|xl).
|
|
91
104
|
Tenant activation after infra is a manual step in the Miso web application.
|
|
92
105
|
Run this before aifabrix deploy <app> — the environment must exist first.`;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** Read-only onboarding guide; command reference remains available through --help. */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const ONBOARDING_GUIDE = `Welcome to AI Fabrix
|
|
5
|
+
|
|
6
|
+
On a managed workspace host with provisioned config, run:
|
|
7
|
+
|
|
8
|
+
af onboarding
|
|
9
|
+
af onboarding --coding claude
|
|
10
|
+
af onboarding --coding codex
|
|
11
|
+
af onboarding --coding cursor
|
|
12
|
+
af onboarding --coding claude --name sales-workspace
|
|
13
|
+
af onboarding --coding claude --name finance-workspace
|
|
14
|
+
af onboarding --repo example-org/example-workspace
|
|
15
|
+
af onboarding --repo my-org/new-repo
|
|
16
|
+
|
|
17
|
+
This prepares the coding platform selected by your organisation, or the one
|
|
18
|
+
you pass for this run. Claude starts in a detached tmux session; Codex and
|
|
19
|
+
Cursor use their own SSH clients. It does not install the local AI Fabrix platform.
|
|
20
|
+
Use --name <folder> for separate workspaces; each Claude workspace has its own
|
|
21
|
+
tmux session. Use --repo owner/name to clone an accessible GitHub repository
|
|
22
|
+
or create a new private one. agent-kit remote create creates a new GitHub
|
|
23
|
+
repository for the current local workspace; --name sets that remote name.
|
|
24
|
+
|
|
25
|
+
Start your integration workspace with Codex:
|
|
26
|
+
|
|
27
|
+
aifabrix agent-kit setup <controller-url>
|
|
28
|
+
|
|
29
|
+
Ask your administrator for the Controller URL.
|
|
30
|
+
Setup guides you through sign-in, installs the Agent Kit,
|
|
31
|
+
and starts your coding agent.
|
|
32
|
+
|
|
33
|
+
Run setup in a Linux/macOS terminal with Builder, Git and tmux installed.
|
|
34
|
+
On Windows, connect to your organisation's Builder Server first.
|
|
35
|
+
Your files stay under the AI Fabrix work directory on the machine running setup.
|
|
36
|
+
No GitHub account or Docker is required for the default local workspace.
|
|
37
|
+
|
|
38
|
+
Optional:
|
|
39
|
+
Use GitHub (requires gh):
|
|
40
|
+
aifabrix agent-kit setup <controller-url> --repo owner/project
|
|
41
|
+
|
|
42
|
+
Use Claude:
|
|
43
|
+
aifabrix agent-kit setup <controller-url> --code claude
|
|
44
|
+
|
|
45
|
+
Already have a project?
|
|
46
|
+
Open its Git repository folder and install the Agent Kit:
|
|
47
|
+
aifabrix agent-kit install --host codex
|
|
48
|
+
|
|
49
|
+
Using your organisation's Builder Server?
|
|
50
|
+
On your own computer, use the onboarding command your administrator gives you:
|
|
51
|
+
aifabrix dev init --developer-id <id> --server <builder-server-url> --pin <pin>
|
|
52
|
+
|
|
53
|
+
Use the SSH command printed after successful onboarding to connect.
|
|
54
|
+
Once connected to a managed workspace, run af onboarding.
|
|
55
|
+
For a portable server without managed config, use agent-kit setup above.
|
|
56
|
+
Integrator accounts use the integrator group; Docker is not needed.
|
|
57
|
+
|
|
58
|
+
Controller URL: connects your workspace to AI Fabrix.
|
|
59
|
+
Builder Server URL: connects your computer to the remote workspace host.
|
|
60
|
+
These are different services; ask your administrator for the correct URLs.
|
|
61
|
+
|
|
62
|
+
Explore:
|
|
63
|
+
aifabrix --help
|
|
64
|
+
aifabrix agent-kit --help
|
|
65
|
+
aifabrix <command> --help
|
|
66
|
+
|
|
67
|
+
This guide does not sign you in, install software, or change configuration.
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
function setupHelpCommand(program) {
|
|
71
|
+
program.addHelpCommand(false);
|
|
72
|
+
program.command('help [command]')
|
|
73
|
+
.description('Show the onboarding guide, or help for a command')
|
|
74
|
+
.addHelpText('after', '\nExamples:\n $ aifabrix help\n $ aifabrix help agent-kit\n')
|
|
75
|
+
.action((name, options, command) => {
|
|
76
|
+
if (!name) {
|
|
77
|
+
command.configureOutput().writeOut(ONBOARDING_GUIDE);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const target = program.commands.find(item => item.name() === name || item.aliases().includes(name));
|
|
81
|
+
if (!target) command.error(`Unknown command '${name}'. Run aifabrix --help to list commands.`);
|
|
82
|
+
target.outputHelp();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { setupHelpCommand, ONBOARDING_GUIDE };
|
|
@@ -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) => {
|