@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.
Files changed (120) hide show
  1. package/lib/agent-kit/codex-app-server.js +44 -0
  2. package/lib/agent-kit/git-identity.js +180 -0
  3. package/lib/agent-kit/init.js +4 -17
  4. package/lib/agent-kit/session-report.js +15 -10
  5. package/lib/agent-kit/setup-auth.js +131 -0
  6. package/lib/agent-kit/setup-context.js +118 -0
  7. package/lib/agent-kit/setup-github.js +70 -0
  8. package/lib/agent-kit/setup-managed.js +79 -0
  9. package/lib/agent-kit/setup-process.js +42 -0
  10. package/lib/agent-kit/setup-session.js +53 -0
  11. package/lib/agent-kit/setup-state.js +52 -0
  12. package/lib/agent-kit/setup-workspace.js +113 -0
  13. package/lib/agent-kit/setup.js +87 -0
  14. package/lib/agent-kit/start.js +48 -3
  15. package/lib/agent-kit/tmux-sessions.js +20 -3
  16. package/lib/api/dev.api.js +1 -1
  17. package/lib/api/miso-health.api.js +26 -0
  18. package/lib/api/role-assistant-test-job.api.js +60 -0
  19. package/lib/api/types/dev.types.js +3 -3
  20. package/lib/api/work-search.api.js +13 -6
  21. package/lib/app/deploy.js +8 -103
  22. package/lib/app/show-display.js +1 -0
  23. package/lib/app/show-online.js +15 -0
  24. package/lib/build/standard-docker-build.js +4 -1
  25. package/lib/cli/index.js +4 -0
  26. package/lib/cli/setup-app.js +14 -1
  27. package/lib/cli/setup-dev-path-commands.js +4 -0
  28. package/lib/cli/setup-dev.js +7 -4
  29. package/lib/cli/setup-help.js +86 -0
  30. package/lib/cli/setup-onboarding.js +37 -0
  31. package/lib/cli/setup-utility-repair.js +7 -5
  32. package/lib/cli/setup-utility.js +24 -1
  33. package/lib/commands/agent-kit-help.js +21 -7
  34. package/lib/commands/agent-kit-sessions.js +126 -0
  35. package/lib/commands/agent-kit-setup.js +44 -0
  36. package/lib/commands/agent-kit.js +5 -79
  37. package/lib/commands/dev-init.js +21 -23
  38. package/lib/commands/dev-refresh.js +2 -2
  39. package/lib/commands/governance-verify-external.js +12 -5
  40. package/lib/commands/repair-datasource.js +9 -9
  41. package/lib/commands/repair.js +45 -8
  42. package/lib/commands/role-assistant.js +7 -0
  43. package/lib/commands/upload-qualification-client.js +56 -11
  44. package/lib/commands/upload-qualification-poll.js +51 -22
  45. package/lib/commands/wizard-dataplane.js +16 -52
  46. package/lib/core/env-platform-expand.js +93 -0
  47. package/lib/core/secrets-env-content.js +37 -3
  48. package/lib/datasource/datasource-validate-summary.js +8 -5
  49. package/lib/datasource/sync-defaults.js +53 -0
  50. package/lib/deployment/installation/azure-infra-stage.js +13 -1
  51. package/lib/deployment/installation/azure-preflight.js +13 -2
  52. package/lib/deployment/installation/azure-readiness.js +24 -2
  53. package/lib/deployment/installation/environment-auth.js +60 -20
  54. package/lib/deployment/installation/environment-stage.js +9 -5
  55. package/lib/deployment/installation/infra-catalog.js +10 -7
  56. package/lib/deployment/installation/infra-manifest.js +5 -0
  57. package/lib/deployment/installation/registry-auth-mode.js +33 -10
  58. package/lib/deployment/marketplace-environment.js +26 -1
  59. package/lib/generator/builders.js +17 -0
  60. package/lib/generator/helpers.js +23 -2
  61. package/lib/generator/index.js +33 -18
  62. package/lib/integration-definition/apply.js +2 -2
  63. package/lib/internal/fs-real-sync.js +4 -1
  64. package/lib/programmatic/bearer-auth.js +3 -62
  65. package/lib/programmatic/builder-api-startup.js +51 -0
  66. package/lib/programmatic/builder-miso-runtime.js +167 -0
  67. package/lib/programmatic/openapi-descriptions.js +3 -3
  68. package/lib/programmatic/openapi-schema-descriptions-core.js +3 -3
  69. package/lib/programmatic/openapi-spec.js +2 -1
  70. package/lib/programmatic/route-handler-map.js +3 -2
  71. package/lib/role-assistant/test-cases-search.js +3 -0
  72. package/lib/role-assistant/test-job-runner.js +125 -0
  73. package/lib/role-assistant/test-runner-search.js +44 -2
  74. package/lib/schema/application-schema.json +193 -2
  75. package/lib/schema/environment-deploy-request.schema.json +1 -1
  76. package/lib/schema/external-datasource.schema.json +20 -5
  77. package/lib/schema/infra-parameter.schema.json +140 -34
  78. package/lib/schema/infra.parameter.yaml +30 -29
  79. package/lib/schema/infrastructure-schema.json +133 -14
  80. package/lib/schema/installation-environment.schema.json +1 -1
  81. package/lib/schema/wizard-config.schema.json +1 -1
  82. package/lib/utils/compose-generate-docker-compose.js +12 -0
  83. package/lib/utils/config-paths.js +24 -29
  84. package/lib/utils/config-registry-preference.js +13 -6
  85. package/lib/utils/dev-init-ssh-merge.js +5 -4
  86. package/lib/utils/dev-user-groups.js +2 -1
  87. package/lib/utils/docker-build.js +29 -8
  88. package/lib/utils/docker-daemon-tls-ca.js +2 -2
  89. package/lib/utils/docker-manifest-public-port.js +36 -0
  90. package/lib/utils/error-formatters/validation-errors.js +25 -5
  91. package/lib/utils/help-builder.js +4 -3
  92. package/lib/utils/platform-resolution.js +226 -0
  93. package/lib/utils/resolve-docker-image-ref.js +8 -3
  94. package/lib/utils/token-manager.js +29 -36
  95. package/package.json +4 -3
  96. package/templates/README.md +2 -1
  97. package/templates/agent-kit/agent-kit.yaml +4 -1
  98. package/templates/agent-kit/instructions/AGENTKIT.md +40 -3
  99. package/templates/agent-kit/instructions/root.AGENTS.md +42 -1
  100. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +33 -1
  101. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/delivery-verdict.js +176 -0
  102. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +3 -0
  103. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +10 -3
  104. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +22 -0
  105. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +21 -0
  106. package/templates/agent-kit/skills/shared/feedback.md +31 -0
  107. package/templates/agent-kit/skills/shared/hosts.md +35 -7
  108. package/templates/agent-kit/skills/shared/status.md +85 -0
  109. package/templates/agent-kit/workspace/BUILDER_IMPROVEMENT_FINDINGS.md +27 -0
  110. package/templates/agent-kit/workspace/README.md +14 -1
  111. package/templates/applications/builder-api/application.yaml +1 -1
  112. package/templates/applications/dataplane/application.yaml +30 -2
  113. package/templates/applications/dataplane/env.template +37 -4
  114. package/templates/applications/miso-controller/application.yaml +24 -1
  115. package/templates/applications/miso-controller/env.template +22 -35
  116. package/templates/external-system/external-datasource.yaml.hbs +7 -6
  117. package/templates/marketplace/main.json +281 -75
  118. package/templates/python/Dockerfile.hbs +2 -0
  119. package/templates/typescript/Dockerfile.hbs +2 -0
  120. package/templates/typescript/docker-compose.hbs +12 -0
@@ -30,7 +30,7 @@
30
30
  * @property {string} aifabrix-secrets - Local filesystem path or https URL for shared secrets (file vs remote API)
31
31
  * @property {string} aifabrix-env-config - Env config path
32
32
  * @property {string} remote-server - Builder-server base URL
33
- * @property {string} docker-endpoint - Docker API endpoint
33
+ * @property {string} [docker-endpoint] - Docker API endpoint; omit for SSH-only/integrator users
34
34
  * @property {string} [developer-root-domain] - App hostname root (e.g. aifabrix.dev → dev01.aifabrix.dev)
35
35
  * @property {boolean} [docker-tls-skip-verify] - When true, Docker uses DOCKER_TLS_VERIFY=0 even if ca.pem exists (client mTLS unchanged)
36
36
  * @property {string} sync-ssh-user - SSH user for Mutagen
@@ -46,7 +46,7 @@
46
46
  * @property {string} createdAt - ISO 8601
47
47
  * @property {boolean} certificateIssued - Whether cert was issued
48
48
  * @property {string} [certificateValidNotAfter] - Cert validity end (optional)
49
- * @property {string[]} groups - Access groups (admin, secret-manager, secret-reader, developer, docker)
49
+ * @property {string[]} groups - Access groups (admin, secret-manager, secret-reader, developer, docker, integrator)
50
50
  */
51
51
 
52
52
  /**
@@ -55,7 +55,7 @@
55
55
  * @property {string} developerId - Unique developer ID (numeric string)
56
56
  * @property {string} name - Display name
57
57
  * @property {string} email - Email
58
- * @property {string[]} [groups] - Default [developer]; tokens: admin, secret-manager, secret-reader, developer, docker
58
+ * @property {string[]} [groups] - Default [developer]; tokens: admin, secret-manager, secret-reader, developer, docker, integrator
59
59
  */
60
60
 
61
61
  /**
@@ -121,16 +121,23 @@ async function getWorkRoles(dataplaneUrl, authConfig) {
121
121
 
122
122
  /**
123
123
  * GET-equivalent work.skills for one roleRef.
124
- * Requests detail=full so search.resourceTypes (executable grammar) is present.
125
- * Compact summary omits that slice and cannot evaluate named viewpoint ops.
124
+ * Defaults to detail=full so search.resourceTypes (executable grammar) is present.
125
+ * A caller may request progressive resource/operation detail for skills-only checks.
126
126
  * @requiresPermission {Dataplane} Authenticated (oauth2: [])
127
127
  * @async
128
128
  */
129
- async function getWorkSkills(dataplaneUrl, authConfig, roleRef) {
130
- return executeLayerBTool(dataplaneUrl, authConfig, LAYER_B_TOOLS.skills, {
129
+ async function getWorkSkills(dataplaneUrl, authConfig, roleRef, options = {}) {
130
+ const args = {
131
131
  roleRef: String(roleRef || '').trim(),
132
- detail: 'full'
133
- });
132
+ detail: String(options.detail || 'full').trim()
133
+ };
134
+ if (typeof options.resourceType === 'string' && options.resourceType.trim()) {
135
+ args.resourceType = options.resourceType.trim();
136
+ }
137
+ if (typeof options.operation === 'string' && options.operation.trim()) {
138
+ args.operation = options.operation.trim();
139
+ }
140
+ return executeLayerBTool(dataplaneUrl, authConfig, LAYER_B_TOOLS.skills, args);
134
141
  }
135
142
 
136
143
  /**
package/lib/app/deploy.js CHANGED
@@ -18,7 +18,6 @@ const {
18
18
 
19
19
  const fs = require('fs').promises;
20
20
  const chalk = require('chalk');
21
- const pushUtils = require('../deployment/push');
22
21
 
23
22
  const SEP = chalk.gray('────────────────────────────────────────');
24
23
  const logger = require('../utils/logger');
@@ -56,106 +55,6 @@ function validateAppName(appName) {
56
55
  }
57
56
  }
58
57
 
59
- /**
60
- * Validates push prerequisites
61
- * @async
62
- * @function validatePushPrerequisites
63
- * @param {string} appName - Application name
64
- * @param {string} registry - Registry URL
65
- * @throws {Error} If prerequisites are not met
66
- */
67
- async function validatePushPrerequisites(appName, registry) {
68
- if (!pushUtils.validateRegistryURL(registry)) {
69
- throw new Error(`Invalid registry URL format: ${registry}. Expected format: *.azurecr.io`);
70
- }
71
-
72
- if (!await pushUtils.checkLocalImageExists(appName, 'latest')) {
73
- throw new Error(`Docker image ${appName}:latest not found locally.\nRun 'aifabrix build ${appName}' first`);
74
- }
75
-
76
- if (!await pushUtils.checkAzureCLIInstalled()) {
77
- throw new Error('Azure CLI is not installed. Install from: https://docs.microsoft.com/cli/azure/install-azure-cli');
78
- }
79
- }
80
-
81
- /**
82
- * Executes push operations
83
- * @async
84
- * @function executePush
85
- * @param {string} appName - Application name
86
- * @param {string} registry - Registry URL
87
- * @param {string[]} tags - Tags to push
88
- * @throws {Error} If push fails
89
- */
90
- async function executePush(appName, registry, tags) {
91
- if (await pushUtils.checkACRAuthentication(registry)) {
92
- logger.log(formatSuccessLine(`Already authenticated with ${registry}`));
93
- } else {
94
- await pushUtils.authenticateACR(registry);
95
- }
96
-
97
- await Promise.all(tags.map(async(tag) => {
98
- await pushUtils.tagImage(`${appName}:latest`, `${registry}/${appName}:${tag}`);
99
- await pushUtils.pushImage(`${registry}/${appName}:${tag}`, registry);
100
- }));
101
- }
102
-
103
- /**
104
- * Verifies push result
105
- * @function verifyPushResult
106
- * @param {string[]} tags - Tags that were pushed
107
- * @param {string} registry - Registry URL
108
- * @param {string} appName - Application name
109
- */
110
- function verifyPushResult(tags, registry, appName) {
111
- logger.log(formatSuccessParagraph(`Successfully pushed ${tags.length} tag(s) to ${registry}`));
112
- logger.log(chalk.gray(`Image: ${registry}/${appName}:*`));
113
- logger.log(chalk.gray(`Tags: ${tags.join(', ')}`));
114
- }
115
-
116
- /**
117
- * Pushes application image to Azure Container Registry
118
- * @async
119
- * @function pushApp
120
- * @param {string} appName - Name of the application
121
- * @param {Object} options - Push options (registry, tag)
122
- * @returns {Promise<void>} Resolves when push is complete
123
- */
124
- async function pushApp(appName, options = {}) {
125
- try {
126
- validateAppName(appName);
127
-
128
- const { getBuilderPath, resolveApplicationConfigPath } = require('../utils/paths');
129
- const { loadConfigFile } = require('../utils/config-format');
130
- const builderPath = getBuilderPath(appName);
131
- let config;
132
- try {
133
- const configPath = resolveApplicationConfigPath(builderPath);
134
- config = loadConfigFile(configPath);
135
- } catch (error) {
136
- throw new Error(`Failed to load configuration: ${error.message}\nRun 'aifabrix create ${appName}' first`);
137
- }
138
-
139
- const registry = options.registry || config.image?.registry;
140
- if (!registry) {
141
- throw new Error('Registry URL is required. Provide via --registry flag or configure in application.yaml under image.registry');
142
- }
143
-
144
- if (!/^[^.]+\.azurecr\.io$/.test(registry)) {
145
- throw new Error(`Invalid ACR URL format: ${registry}. Expected format: *.azurecr.io`);
146
- }
147
-
148
- const tags = options.tag ? options.tag.split(',').map(t => t.trim()) : ['latest'];
149
-
150
- await validatePushPrerequisites(appName, registry);
151
- await executePush(appName, registry, tags);
152
- verifyPushResult(tags, registry, appName);
153
-
154
- } catch (error) {
155
- throw new Error(`Failed to push application: ${error.message}`);
156
- }
157
- }
158
-
159
58
  /**
160
59
  * Generates and validates deployment manifest
161
60
  * @async
@@ -369,7 +268,14 @@ async function executeStandardDeployment(appName, options) {
369
268
  const controllerUrl = config.controllerUrl || 'unknown';
370
269
  const appExists = await checkApplicationExists(appName, controllerUrl, config.envKey, config.auth);
371
270
 
372
- const { manifest, manifestPath } = await generateAndValidateManifest(appName, options);
271
+ // A controller deployment ships tokens plus the platforms/environments sections, so the
272
+ // Controller resolves them per installation and can promote dev -> tst without regenerating.
273
+ // Local runs and an explicit --resource-group resolve here instead.
274
+ const resolveHere = Boolean(options.local || options.resourceGroup);
275
+ const { manifest, manifestPath } = await generateAndValidateManifest(appName, {
276
+ ...options,
277
+ ...(resolveHere ? { target: { environment: config.envKey, resourceGroup: options.resourceGroup } } : {})
278
+ });
373
279
  applyManifestOverrides(manifest, options);
374
280
  validateImageIsPullable(manifest.image, appName);
375
281
  displayDeploymentInfo(manifest, manifestPath);
@@ -492,7 +398,6 @@ async function deployApp(appName, options = {}) {
492
398
  }
493
399
 
494
400
  module.exports = {
495
- pushApp,
496
401
  deployApp,
497
402
  validateAppName,
498
403
  validateImageIsPullable
@@ -234,6 +234,7 @@ function logExternalSystemMain(ext) {
234
234
  logger.log(` Version: ${ext.version ?? '—'}`);
235
235
  logger.log(` Credential: ${formatCredentialShowLine(ext)}`);
236
236
  logger.log(` Status: ${formatDataplaneStatusDisplay(ext.status)}`);
237
+ logger.log(` System link: ${ext.systemUrl ?? '—'}`);
237
238
  logger.log(` API docs: ${ext.openApiDocsPageUrl ?? ext.apiDocumentUrl ?? '—'}`);
238
239
  logger.log(` MCP server: ${ext.mcpServerUrl ?? '—'}`);
239
240
  logger.log(` OpenAPI spec: ${ext.apiDocumentUrl ?? '—'}`);
@@ -89,6 +89,7 @@ function buildOnlineSummaryFromDataplane(appKey, controllerUrl, dataplaneUrl, ex
89
89
 
90
90
  async function finishOnlineShow(summary, appKey, json, permissionsOnly, verifyCert, authBundle) {
91
91
  await enrichExternalShowSummary(summary, appKey, verifyCert, authBundle);
92
+ attachSystemUrl(summary);
92
93
  attachRunDefaultsFromUserConfig(summary, appKey, await getConfig());
93
94
  await attachDeclarativeUrlsToShowApplication(summary, appKey);
94
95
  if (json) {
@@ -100,6 +101,20 @@ async function finishOnlineShow(summary, appKey, json, permissionsOnly, verifyCe
100
101
  displayShow(summary, { permissionsOnly: !!permissionsOnly });
101
102
  }
102
103
 
104
+ /**
105
+ * Expose the Dataplane system browser route for humans and agents.
106
+ * Preserve the deployment base path (for example /data).
107
+ * @param {Object} summary - Online application summary
108
+ */
109
+ function attachSystemUrl(summary) {
110
+ const ext = summary.externalSystem;
111
+ if (!summary.isExternal || !ext || ext.error || !ext.dataplaneUrl) return;
112
+ const baseUrl = ext.dataplaneUrl.replace(/\/+$/, '');
113
+ const systemKey = ext.systemKey || summary.appKey;
114
+ const section = /^(ra-|ek-)/.test(systemKey) ? 'enterprise-knowledge' : 'connected-systems';
115
+ ext.systemUrl = `${baseUrl}/${section}/${encodeURIComponent(systemKey)}`;
116
+ }
117
+
103
118
  async function runOnlineFromDataplane(appKey, json, permissionsOnly, verifyCert, authConfig, authResult) {
104
119
  const environment = await resolveEnvironment();
105
120
  const dataplaneUrl = await resolveDataplaneUrl(authResult.actualControllerUrl, environment, authConfig);
@@ -81,13 +81,16 @@ async function executeDockerImageBuild(appName, options, buildConfig) {
81
81
  await ensureRunSecretsForApp(appName);
82
82
  const buildArgs = await resolveDockerBuildArgsForApp(appName, contextPath);
83
83
  const { primary, aliases, tags } = options.tagPlan;
84
+ // `build.target` selects a stage of a multi-stage Dockerfile, so one source tree can
85
+ // produce more than one image. Absent, Docker builds the last stage as before.
86
+ const target = buildConfig && buildConfig.target;
84
87
  await dockerBuild.executeDockerBuildWithTag(
85
88
  effectiveImageName,
86
89
  imageName,
87
90
  dockerfilePath,
88
91
  contextPath,
89
92
  primary,
90
- { ...options, buildArgs, tagAliases: aliases }
93
+ { ...options, buildArgs, tagAliases: aliases, target }
91
94
  );
92
95
  await postBuildTasks(appName, buildConfig);
93
96
  logger.log(formatSuccessParagraph('Build completed successfully!'));
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);
@@ -362,7 +362,7 @@ function setupDockerfileGenerateCommand(program) {
362
362
  }
363
363
 
364
364
  function setupDeployCommand(program) {
365
- program.command('deploy <appKey|systemKey>')
365
+ addDeployTargetOptions(program.command('deploy <appKey|systemKey>'))
366
366
  .description('Deploy builder app or external system via Miso Controller (Azure or --local)')
367
367
  .addHelpText('after', DEPLOY_HELP_AFTER)
368
368
  .option('--local', 'Send manifest to controller then run app locally (app: same as aifabrix run <app>; external: restart dataplane)')
@@ -410,6 +410,19 @@ function setupDeployCommand(program) {
410
410
  });
411
411
  }
412
412
 
413
+ /**
414
+ * Deployment-target options. A controller deployment normally ships {NAME} tokens plus the
415
+ * platforms/environments sections; these flags force local resolution instead.
416
+ * @param {Command} command - Commander command
417
+ * @returns {Command} The command
418
+ */
419
+ function addDeployTargetOptions(command) {
420
+ return command.option(
421
+ '-g, --resource-group <name>',
422
+ 'Target Azure resource group; resolves platforms values in the generated manifest instead of shipping tokens'
423
+ );
424
+ }
425
+
413
426
  function setupPushDeployDockerfileCommands(program) {
414
427
  program.command('push <appKey>')
415
428
  .description('Push builder appKey image to Azure Container Registry')
@@ -134,6 +134,10 @@ function addPrintHomeWorkCommands(dev) {
134
134
  }
135
135
  });
136
136
 
137
+ addShellEnvCommand(dev);
138
+ }
139
+
140
+ function addShellEnvCommand(dev) {
137
141
  dev
138
142
  .command('shell-env')
139
143
  .description(
@@ -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 "CI user" --email ci@example.com --groups secret-manager,developer
48
- $ aifabrix dev add --developer-id 05 --name "Integrator" --email integrator@example.com --groups developer,secret-reader
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 {
@@ -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('Repaired external integration config.'));
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 (files, RBAC, manifest, …)')
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 default sync section to datasources that lack it')
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 integration/<systemKey>/backup/')
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) => {
@@ -65,6 +65,23 @@ async function handleSplitJsonCommand(appName, options) {
65
65
  return generator.splitDeployJson(deployJsonPath, outputDir);
66
66
  }
67
67
 
68
+ /**
69
+ * Deployment target for manifest generation. A resource group resolves platform values; the
70
+ * environment key resolves the environments layer. Without either, {NAME} tokens are preserved.
71
+ * @param {Object} options - Command options
72
+ * @returns {Promise<{ resourceGroup: string|undefined, environment: string }>}
73
+ */
74
+ async function buildGenerationTarget(options = {}) {
75
+ // Only an explicit target resolves. The default artifact keeps {NAME} tokens and carries the
76
+ // platforms/environments sections, so the Controller resolves them per installation.
77
+ if (!options.resourceGroup && !options.env) return undefined;
78
+ const { resolveEnvironment } = require('../core/config');
79
+ const environment = options.env
80
+ ? String(options.env).trim().toLowerCase()
81
+ : await resolveEnvironment();
82
+ return { resourceGroup: options.resourceGroup, environment };
83
+ }
84
+
68
85
  /**
69
86
  * Logs split-json results
70
87
  * @param {Object} result - Generated file paths
@@ -93,6 +110,11 @@ function logSplitJsonResult(result) {
93
110
  function setupJsonCommand(program) {
94
111
  program.command('json <appKey|systemKey>')
95
112
  .description('Write deployment JSON to disk for version control')
113
+ .option(
114
+ '-g, --resource-group <name>',
115
+ 'Target Azure resource group; resolves platforms/environments values instead of leaving {NAME} tokens'
116
+ )
117
+ .option('-e, --env <env>', 'Deployment environment key for environments resolution (default: current environment)')
96
118
  .addHelpText('after', JSON_HELP_AFTER)
97
119
  .action(async(appName, options) => {
98
120
  try {
@@ -105,7 +127,8 @@ function setupJsonCommand(program) {
105
127
  json: false
106
128
  });
107
129
  const result = await generator.generateDeployJsonWithValidation(appName, {
108
- ...options
130
+ ...options,
131
+ target: await buildGenerationTarget(options)
109
132
  });
110
133
  if (result.success) {
111
134
  const fileName = result.path.includes('application-schema.json') ? 'application-schema.json' : 'deployment JSON';
@@ -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
- $ aifabrix agent-kit remote create
100
- $ aifabrix agent-kit remote create --org my-org --yes
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 cust-demo && aifabrix agent-kit remote create
112
- $ aifabrix agent-kit remote create --org my-org --yes
113
- $ aifabrix agent-kit remote create --org my-org --dry-run --json
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 every active tmux session visible to the current Unix user.
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. Use agent-kit list to find its name.
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
  `;