@aifabrix/builder 2.55.2 → 2.57.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 (88) hide show
  1. package/lib/agent-kit/session-report.js +76 -0
  2. package/lib/agent-kit/start.js +124 -57
  3. package/lib/agent-kit/tmux-sessions.js +163 -0
  4. package/lib/api/environments.api.js +38 -0
  5. package/lib/api/marketplace-bootstrap.api.js +31 -0
  6. package/lib/api/pipeline.api.js +32 -3
  7. package/lib/app/deploy.js +6 -5
  8. package/lib/cli/setup-app.js +7 -6
  9. package/lib/cli/setup-environment.js +76 -19
  10. package/lib/cli/setup-external-system-help.js +15 -3
  11. package/lib/cli/setup-external-system.js +21 -3
  12. package/lib/commands/agent-kit-help.js +32 -15
  13. package/lib/commands/agent-kit.js +90 -8
  14. package/lib/commands/repair-datasource-auto-rbac.js +4 -9
  15. package/lib/commands/upload-openapi-sync-followup.js +59 -0
  16. package/lib/commands/upload-qualification-client.js +131 -0
  17. package/lib/commands/upload-qualification-followups.js +104 -0
  18. package/lib/commands/upload-qualification-poll.js +143 -0
  19. package/lib/commands/upload.js +37 -156
  20. package/lib/deployment/deploy-poll-ui.js +14 -10
  21. package/lib/deployment/deployer-status.js +4 -4
  22. package/lib/deployment/installation/azure-infra-stage.js +160 -0
  23. package/lib/deployment/installation/azure-preflight.js +102 -0
  24. package/lib/deployment/installation/azure-readiness.js +71 -0
  25. package/lib/deployment/installation/docker-register.js +112 -0
  26. package/lib/deployment/installation/environment-auth.js +76 -0
  27. package/lib/deployment/installation/environment-manifest.js +165 -0
  28. package/lib/deployment/installation/environment-stage.js +191 -0
  29. package/lib/deployment/installation/index.js +39 -0
  30. package/lib/deployment/installation/infra-catalog.js +165 -0
  31. package/lib/deployment/installation/infra-manifest.js +161 -0
  32. package/lib/deployment/installation/infra-projection.js +86 -0
  33. package/lib/deployment/installation/public-ip.js +65 -0
  34. package/lib/deployment/installation/stage-input.js +169 -0
  35. package/lib/deployment/installation/stage-output.js +53 -0
  36. package/lib/deployment/installation/workspace-env.js +76 -0
  37. package/lib/deployment/installation/workspace-files.js +178 -0
  38. package/lib/deployment/installation/workspace-schema.js +40 -0
  39. package/lib/deployment/installation/workspace-secrets.js +66 -0
  40. package/lib/deployment/installation/workspace.js +128 -0
  41. package/lib/deployment/marketplace-environment.js +305 -0
  42. package/lib/external-system/deploy.js +46 -51
  43. package/lib/generator/builders.js +4 -0
  44. package/lib/generator/helpers.js +55 -99
  45. package/lib/generator/index.js +0 -4
  46. package/lib/generator/split-variables.js +11 -5
  47. package/lib/internal/fs-real-sync.js +29 -0
  48. package/lib/programmatic/builder-api-request.js +8 -4
  49. package/lib/programmatic/capability-registry.js +2 -0
  50. package/lib/programmatic/get-deploy-status.js +45 -0
  51. package/lib/programmatic/get-upload-status.js +45 -0
  52. package/lib/programmatic/http-accepted-response.js +31 -0
  53. package/lib/programmatic/openapi-descriptions.js +14 -4
  54. package/lib/programmatic/request-timeout.js +21 -15
  55. package/lib/programmatic/route-handler-map.js +6 -0
  56. package/lib/programmatic/run-deploy.js +7 -3
  57. package/lib/programmatic/run-upload.js +11 -7
  58. package/lib/role-assistant/package-validate.js +4 -0
  59. package/lib/role-assistant/test-runner-clarification-resubmit.js +149 -0
  60. package/lib/role-assistant/test-runner.js +14 -17
  61. package/lib/schema/application-schema.json +20 -2
  62. package/lib/schema/infra-parameter.schema.json +7 -0
  63. package/lib/schema/infra.parameter.yaml +32 -43
  64. package/lib/schema/infrastructure-schema.json +1480 -399
  65. package/lib/schema/installation-environment.schema.json +601 -0
  66. package/lib/utils/cli-utils.js +3 -2
  67. package/lib/utils/qualification-poll-ui.js +136 -0
  68. package/lib/utils/variable-transformer.js +3 -0
  69. package/lib/validation/required-variables.js +41 -0
  70. package/lib/validation/validator.js +8 -5
  71. package/package.json +7 -2
  72. package/templates/README.md +4 -0
  73. package/templates/agent-kit/instructions/AGENTKIT.md +6 -6
  74. package/templates/agent-kit/skills/shared/hosts.md +10 -10
  75. package/templates/applications/builder-api/env.template +4 -0
  76. package/templates/applications/dataplane/application.yaml +1 -1
  77. package/templates/applications/dataplane/env.template +1 -1
  78. package/templates/applications/keycloak/Dockerfile +12 -5
  79. package/templates/applications/keycloak/application.yaml +2 -2
  80. package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +3 -3
  81. package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +6 -6
  82. package/templates/applications/keycloak/env.template +13 -1
  83. package/templates/applications/miso-controller/application.yaml +3 -2
  84. package/templates/applications/miso-controller/env.template +28 -22
  85. package/templates/marketplace/createUiDefinition.json +77 -0
  86. package/templates/marketplace/install.parameters.example.json +9 -0
  87. package/templates/marketplace/main.json +4087 -0
  88. package/lib/generator/deploy-manifest-azure-kv.js +0 -81
@@ -56,20 +56,21 @@ Examples:
56
56
  --client-id "$DEV_MISO_CLIENTID" \\
57
57
  --client-secret "$DEV_MISO_CLIENTSECRET"
58
58
 
59
- # CI/CD — external integration (after upload/tests; promote to controller)
59
+ # CI/CD — external integration (qualify once via upload, then fast promote via deploy)
60
60
  $ aifabrix validate hubspot-demo
61
61
  $ aifabrix upload hubspot-demo --probe
62
62
  $ aifabrix deploy hubspot-demo --probe -v
63
- # Prove pillars in tst/CI, then promote (plain deploy seals cert on published Deploy)
64
- $ aifabrix deploy ek-sales --gate full --subject-email ops@example.com
65
63
  $ aifabrix deploy ek-sales
66
64
 
67
65
  Notes:
68
66
  - Login or set MISO_CONTROLLER_URL + environment (aifabrix login / aifabrix auth).
69
67
  - CI: register once (aifabrix app register <appKey>), store DEV_MISO_CLIENTID and DEV_MISO_CLIENTSECRET in pipeline secrets.
70
- - upload = dataplane publish for dev iteration; deploy = controller validate + deploy (Azure or local Docker).
71
- - Prove (tst/CI): use --gate full (optional --subject-email) before promote; plain deploy promotes without live E2E gates.
72
- - After a successful published Deploy, the platform seals the integration certificate (no separate certificates issue step).
68
+ - For Connected Systems, upload validates, publishes, and finalizes the system; optional
69
+ checks run only with upload --gate. Deploy promotes the completed upload — the
70
+ server decides this automatically, no flag needed. A system that has never been uploaded
71
+ falls back to the full legacy ladder at deploy time instead.
72
+ - --gate is a no-op on deploy (kept for compatibility; prints a notice) — gates now run once
73
+ during "aifabrix upload", not at deploy time.
73
74
  - Default output is a compact status card; use -v for expanded preflight and readiness detail.
74
75
  - Controller deploy always polls to a terminal status (no --poll / --no-poll on this command).
75
76
  - --repository-url overrides repo URL for pipeline validate (default: application.yaml repository.repositoryUrl).
@@ -7,39 +7,95 @@
7
7
  */
8
8
 
9
9
  const { handleCommandError } = require('../utils/cli-utils');
10
+ const { formatWarningLine } = require('../utils/cli-test-layout-chalk');
11
+ const { isWorkspaceMode, isLegacyMarketplaceMode } = require('../deployment/installation/stage-input');
10
12
 
11
13
  /**
12
- * Sets up environment deployment commands
13
- * @param {Command} program - Commander program instance
14
+ * Adds installation workspace and deprecated explicit Marketplace options.
15
+ * @param {Command} command - Commander command
16
+ * @returns {Command} The command
14
17
  */
15
- function setupEnvironmentCommands(program) {
16
- const deployEnvHandler = async(envKey, options) => {
17
- try {
18
+ function addMarketplaceOptions(command) {
19
+ return command
20
+ .option('--stage <stage>', 'Installation workspace stage: register, infra, dev, tst or pro (<env> is then the resource group)')
21
+ .option('--vendor <vendor>', 'Installation vendor: AZURE (default) or DOCKER (register only)')
22
+ .option('--allow-my-ip', 'Allow the caller public IPv4 /32 to reach PostgreSQL, Key Vault and Storage (infra only; default off)')
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('--miso-api-key-env <name>', 'Environment variable holding a temporary Miso API key (dev, tst, pro)')
25
+ .option('--controller-url <url>', 'Controller URL override for dev, tst or pro (deprecated platform stage also uses it)')
26
+ .option('--marketplace-json <file>', 'Deprecated explicit mode: ARM Marketplace infrastructure template')
27
+ .option('--parameters <file>', 'Deprecated explicit mode: secure ARM deploymentParameters JSON file')
28
+ .option('--subscription <id>', 'Deprecated explicit mode: Azure subscription ID')
29
+ .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');
32
+ }
33
+
34
+ async function runLegacyMarketplace(envKey, options) {
35
+ process.stderr.write(`${formatWarningLine('Explicit Marketplace flags are deprecated; use af env deploy <resource-group> --stage infra')}\n`);
36
+ const installer = require('../deployment/marketplace-environment');
37
+ const result = await installer.deployMarketplaceEnvironment(envKey, options);
38
+ if (result.platformComplete) {
39
+ process.stdout.write(`Platform ready: ${result.publicControllerUrl}\n`);
40
+ } else {
41
+ process.stdout.write(`Infrastructure deployed: ${result.outputs.webAppUrl} (deployment ${result.deploymentId})\n`);
42
+ process.stdout.write(`Resume after controller login and wizard activation: af env deploy ${envKey} --install-id ${result.installId} --controller-url https://<ready-public-host>/miso --stage platform\n`);
43
+ }
44
+ }
45
+
46
+ async function runInstallationStage(resourceGroup, options) {
47
+ const { deployInstallationStage } = require('../deployment/installation');
48
+ const { formatStageResult } = require('../deployment/installation/stage-output');
49
+ const result = await deployInstallationStage(resourceGroup, options);
50
+ process.stdout.write(formatStageResult(result, resourceGroup));
51
+ }
52
+
53
+ async function deployEnvHandler(envKey, options) {
54
+ try {
55
+ if (isWorkspaceMode(options)) {
56
+ await runInstallationStage(envKey, options);
57
+ } else if (isLegacyMarketplaceMode(options)) {
58
+ await runLegacyMarketplace(envKey, options);
59
+ } else {
18
60
  const environmentDeploy = require('../deployment/environment');
19
61
  await environmentDeploy.deployEnvironment(envKey, options);
20
- } catch (error) {
21
- handleCommandError(error, 'env deploy');
22
- process.exit(1);
23
62
  }
24
- };
63
+ } catch (error) {
64
+ handleCommandError(error, 'env deploy');
65
+ process.exit(1);
66
+ }
67
+ }
25
68
 
26
- const deployExamples = `
69
+ const DEPLOY_EXAMPLES = `
27
70
  Examples:
28
71
  $ aifabrix env deploy dev
29
72
  $ aifabrix env deploy tst
30
73
  $ aifabrix env deploy pro --preset m
31
- $ aifabrix env deploy dev --preset xl
32
74
  $ aifabrix env deploy dev --config ./env-config.json
33
75
  $ aifabrix env deploy dev --no-poll
34
76
 
35
- Prerequisites: aifabrix login (device token; controller permission controller:deploy).
77
+ Installation workspace (<env> is the resource group; files under infrastructure/<resource-group>/):
78
+ $ af env deploy <resource-group> --stage infra
79
+ $ af env deploy <resource-group> --stage infra --allow-my-ip
80
+ $ af env deploy <resource-group> --stage infra --license-edition ENTERPRISE
81
+ $ af env deploy <resource-group> --stage dev
82
+ $ af env deploy <resource-group> --stage pro --preset m
83
+ $ af env deploy <resource-group> --stage register --vendor DOCKER
84
+
85
+ Vendor/stage matrix:
86
+ AZURE infra, dev, tst, pro (Mori bootstrap runs inside the Marketplace template)
87
+ DOCKER register only (deploy with aifabrix setup)
88
+
89
+ Prerequisites: aifabrix login (controller permission controller:deploy); infra also needs az login.
36
90
  Environment keys: miso, dev, tst, pro. Default size preset: s (use --preset s|m|l|xl).
91
+ Tenant activation after infra is a manual step in the Miso web application.
37
92
  Run this before aifabrix deploy <app> — the environment must exist first.`;
38
93
 
39
- const ENV_GROUP_HELP_AFTER = `
94
+ const ENV_GROUP_HELP_AFTER = `
40
95
  Examples:
41
96
  $ aifabrix env deploy dev
42
97
  $ aifabrix env deploy dev --preset m
98
+ $ af env deploy <resource-group> --stage infra
43
99
  $ aifabrix env deploy dev --help
44
100
 
45
101
  Typical workflow:
@@ -51,21 +107,22 @@ Subcommands:
51
107
  deploy <env> Provision/update environment in Miso Controller (see env deploy --help)
52
108
  `;
53
109
 
110
+ function setupEnvironmentCommands(program) {
54
111
  const env = program
55
112
  .command('env')
56
113
  .description('Miso Controller environments (primary: env deploy <env>)')
57
114
  .addHelpText('after', ENV_GROUP_HELP_AFTER);
58
115
 
59
- env
116
+ addMarketplaceOptions(env
60
117
  .command('deploy <env>')
61
- .description('Deploy environment infrastructure in Miso Controller (run before deploy <app>)')
118
+ .description('Deploy environment infrastructure in Miso Controller, or an installation workspace stage with --stage')
62
119
  .option('--config <file>', 'Environment configuration file (optional if --preset is used)')
63
- .option('--preset <size>', 'Environment size preset: s, m, l, xl (default: s)', 's')
120
+ .option('--preset <size>', 'Environment size preset: s, m, l, xl (default: s, or the workspace file value)')
64
121
  .option('--skip-validation', 'Skip environment validation')
65
122
  .option('--poll', 'Poll for deployment status', true)
66
- .option('--no-poll', 'Do not poll for status')
67
- .addHelpText('after', deployExamples)
123
+ .option('--no-poll', 'Do not poll for status'))
124
+ .addHelpText('after', DEPLOY_EXAMPLES)
68
125
  .action(deployEnvHandler);
69
126
  }
70
127
 
71
- module.exports = { setupEnvironmentCommands };
128
+ module.exports = { setupEnvironmentCommands, deployEnvHandler, addMarketplaceOptions };
@@ -6,11 +6,18 @@
6
6
 
7
7
  const UPLOAD_HELP_AFTER = `
8
8
  Examples:
9
- # Publish integration/<systemKey>/ to the dataplane (typical lab flow after validate + resolve)
9
+ # Upload and wait for completion
10
10
  $ aifabrix upload hubspot-demo
11
11
  $ aifabrix upload hubspot-demo -v
12
12
  $ aifabrix upload hubspot-demo --probe
13
- $ aifabrix upload hubspot-demo --timeout 600000
13
+ $ aifabrix upload hubspot-demo --gate operations,trust
14
+ $ aifabrix upload hubspot-demo --gate full --subject-email user@example.com
15
+
16
+ # Queue the upload and return immediately (script/CI-friendly)
17
+ $ aifabrix upload hubspot-demo --no-wait
18
+
19
+ # Resume watching an existing upload job (e.g. after Ctrl-C or a closed terminal)
20
+ $ aifabrix upload hubspot-demo --resume <jobId>
14
21
 
15
22
  # Build and validate payload only (no API calls)
16
23
  $ aifabrix upload hubspot-demo --dry-run
@@ -25,9 +32,14 @@ Examples:
25
32
  Notes:
26
33
  - Requires login (aifabrix auth status).
27
34
  - Reads files under integration/<systemKey>/ or role-assistant/<roleKey>/ for digital workers.
35
+ - Upload validates, publishes, and finalizes the system. Operations, trust, and governance
36
+ checks are optional and run only when selected with --gate. The upload keeps running on
37
+ Dataplane even if this CLI process is interrupted, closed, or times out — --resume re-attaches
38
+ to it later, it is never re-submitted or lost.
28
39
  - Default output is a compact readiness summary; use -v for expanded detail and server-side validate warnings.
29
40
  - --probe runs dataplane runtime checks after publish (slower).
30
- - --timeout sets publish budget in ms (HTTP client and dataplane); default 300000.
41
+ - --timeout bounds only how long this CLI waits before printing a resume hint
42
+ (default 1800000ms/30min); it does not stop the upload job.
31
43
  - --env selects role-assistant.yaml section (dev|tst|pro) for digital workers; default dev.
32
44
  - Role Assistant packages under role-assistant/ do not use application.yaml; upload applies settings and Evidence only.
33
45
  - test-e2e and test-integration publish local files first by default; use --no-publish on those commands to skip.
@@ -50,7 +50,11 @@ async function handleUploadCommandAction(systemKey, options) {
50
50
  options.probeTimeout === undefined || options.probeTimeout === null
51
51
  ? 120000
52
52
  : Number(options.probeTimeout);
53
- const timeoutMs = resolvePipelineUploadTimeoutMs(options.timeout);
53
+ // Only resolve --timeout when the caller explicitly set it; leaving it undefined otherwise
54
+ // lets the qualification poll fall back to its own default (30min) instead of the legacy
55
+ // 300000ms HTTP/dataplane-publish budget default, which no longer applies to the async path.
56
+ const timeoutMs =
57
+ options.timeout !== undefined ? resolvePipelineUploadTimeoutMs(options.timeout) : undefined;
54
58
  await upload.uploadExternalSystem(systemKey, {
55
59
  ...options,
56
60
  probeTimeout: Number.isFinite(probeTimeout) ? probeTimeout : 120000,
@@ -101,9 +105,17 @@ function setupUploadCommand(program) {
101
105
  .option('-v, --verbose', 'Expanded upload summary and server-side pipeline validate warnings before publish')
102
106
  .option('--probe', 'After publish, run dataplane runtime checks (validation/run); slower')
103
107
  .option('--probe-timeout <ms>', 'Timeout for --probe (default: 120000)', '120000')
108
+ .option(
109
+ '--gate <gates>',
110
+ 'Optional upload checks (comma-separated): operations, trust, governance, full'
111
+ )
112
+ .option(
113
+ '--subject-email <emails>',
114
+ 'Subject email(s) for governance checks (comma-separated; used with --gate governance|full)'
115
+ )
104
116
  .option(
105
117
  '--timeout <ms>',
106
- 'Publish timeout in ms for HTTP and dataplane budget (default: 300000; min 5000, max 2700000)'
118
+ 'How long this CLI waits for the upload job before printing a resume hint (default: 1800000/30min; min 5000, max 2700000). The job keeps running regardless.'
107
119
  )
108
120
  .option(
109
121
  '--force',
@@ -114,6 +126,12 @@ function setupUploadCommand(program) {
114
126
  'Role Assistant settings environment section: dev, tst, or pro (default: dev)',
115
127
  'dev'
116
128
  )
129
+ .option('--wait', 'Wait for the upload job to complete (default)', true)
130
+ .option('--no-wait', 'Return the upload job id immediately without waiting')
131
+ .option(
132
+ '--resume <jobId>',
133
+ 'Wait for an existing upload job instead of submitting again'
134
+ )
117
135
  .action(async(systemKey, options) => {
118
136
  try {
119
137
  await handleUploadCommandAction(systemKey, options);
@@ -341,4 +359,4 @@ function setupExternalSystemCommands(program) {
341
359
  setupExternalSystemTestCommands(program);
342
360
  }
343
361
 
344
- module.exports = { setupExternalSystemCommands };
362
+ module.exports = { setupExternalSystemCommands, handleUploadCommandAction };
@@ -21,9 +21,10 @@ Examples:
21
21
  $ aifabrix agent-kit install --host all --approve-all claude --yes
22
22
  $ aifabrix agent-kit update --host all --approve-all --yes
23
23
  $ aifabrix agent-kit remote create --org my-org --yes
24
- $ aifabrix agent-kit start claude --remote-control --approve-all --yes
25
- $ aifabrix agent-kit start claude --name aifabrix-docs --remote-control --approve-all --yes
26
- $ aifabrix agent-kit start codex --approve-all --yes
24
+ $ aifabrix agent-kit start claude
25
+ $ aifabrix agent-kit start codex
26
+ $ aifabrix agent-kit list
27
+ $ aifabrix agent-kit kill aifabrix-docs
27
28
  $ aifabrix agent-kit status
28
29
  $ aifabrix agent-kit validate
29
30
  `;
@@ -117,23 +118,37 @@ Interactive (TTY): omit flags to be prompted. Silent: --org <owner> --yes
117
118
  const START_HELP = `
118
119
  Does not install Agent Kit skills. Starts claude or codex in tmux in this git
119
120
  workspace so closing the terminal does not kill the session. Detach: Ctrl-b then
120
- d. --approve-all writes that app's local permission config only. --name is the
121
- tmux session (default: last folder, e.g. aifabrix-docs). Claude also gets
122
- --name and --remote-control hostname-user-folder, e.g.
123
- builder01-dev03-aifabrix-docs. Claude --tmux is for worktrees, not this wrapper.
124
- That is not Claude --cloud (a separate cloud VM). Codex has no phone/web attach.
121
+ d. The defaults enable that app's Remote Control and workspace auto-approval.
122
+ --name is the tmux session (default: last git-root folder, e.g. aifabrix-docs).
123
+ Claude gets --name and hostname-user-folder Remote Control. Codex uses its native
124
+ remote-control daemon. Use --no-remote-control and/or --skip-approve-all to opt
125
+ out. Claude --tmux is for worktrees, not this wrapper.
125
126
 
126
127
  Examples:
127
- $ aifabrix agent-kit start claude --remote-control --approve-all --yes
128
- $ aifabrix agent-kit start claude --name aifabrix-docs --remote-control --approve-all --yes
129
- $ aifabrix agent-kit start claude --name aifabrix-miso --remote-control --approve-all --yes
130
- $ aifabrix agent-kit start claude --remote-control --detach
131
128
  $ aifabrix agent-kit start claude
132
- $ aifabrix agent-kit start claude --approve-all --yes
133
- $ aifabrix agent-kit start codex --approve-all --yes
129
+ $ aifabrix agent-kit start codex
130
+ $ aifabrix agent-kit start claude --name aifabrix-miso
131
+ $ aifabrix agent-kit start codex --no-remote-control --skip-approve-all
132
+ $ aifabrix agent-kit start claude --detach
134
133
  $ aifabrix agent-kit start claude --dry-run --json
135
134
  `;
136
135
 
136
+ const LIST_HELP = `
137
+ Lists every active tmux session visible to the current Unix user.
138
+
139
+ Examples:
140
+ $ aifabrix agent-kit list
141
+ $ aifabrix agent-kit list --json
142
+ `;
143
+
144
+ const KILL_HELP = `
145
+ Stops exactly one tmux session. Use agent-kit list to find its name.
146
+
147
+ Examples:
148
+ $ aifabrix agent-kit kill aifabrix-miso
149
+ $ aifabrix agent-kit kill aifabrix-miso --json
150
+ `;
151
+
137
152
  module.exports = {
138
153
  HOST_OPTION_HELP,
139
154
  APPROVE_ALL_OPTION_HELP,
@@ -145,5 +160,7 @@ module.exports = {
145
160
  INIT_HELP,
146
161
  REMOTE_GROUP_HELP,
147
162
  REMOTE_HELP,
148
- START_HELP
163
+ START_HELP,
164
+ LIST_HELP,
165
+ KILL_HELP
149
166
  };
@@ -1,15 +1,18 @@
1
- /**
2
- * `aifabrix agent-kit` command group.
3
- * @fileoverview Install, init, status, update, validate, remote create, start
4
- */
1
+ /** `aifabrix agent-kit` command group. */
5
2
 
6
3
  'use strict';
7
4
 
8
5
  const logger = require('../utils/logger');
9
6
  const { handleCommandError } = require('../utils/cli-utils');
10
- const { formatSuccessLine, formatBlockingError, formatWarningLine } = require('../utils/cli-layout-chalk');
7
+ const {
8
+ formatSuccessLine,
9
+ formatBlockingError,
10
+ formatWarningLine
11
+ } = require('../utils/cli-layout-chalk');
11
12
  const { runInstall, runUpdate } = require('../agent-kit/run');
12
13
  const { runStart } = require('../agent-kit/start');
14
+ const { runList, runKill } = require('../agent-kit/tmux-sessions');
15
+ const { formatSessionList, formatMissingKillName } = require('../agent-kit/session-report');
13
16
  const path = require('path');
14
17
  const { runInit, runRemoteCreate } = require('../agent-kit/init');
15
18
  const { enrichInitHostOptions, enrichInitRemoteOptions, enrichRemoteOptions, enrichApproveAllOptions } = require('../agent-kit/remote-prompts');
@@ -30,6 +33,8 @@ const {
30
33
  REMOTE_GROUP_HELP,
31
34
  REMOTE_HELP,
32
35
  START_HELP,
36
+ LIST_HELP,
37
+ KILL_HELP,
33
38
  HOST_OPTION_HELP,
34
39
  APPROVE_ALL_OPTION_HELP
35
40
  } = require('./agent-kit-help');
@@ -110,6 +115,8 @@ function emitStartReport(json, asJson) {
110
115
  logger.log(`Session ${json.session}`);
111
116
  if (json.remoteControlName) {
112
117
  logger.log(`Remote ${json.remoteControlName}`);
118
+ } else if (json.remoteControl) {
119
+ logger.log('Remote Codex Remote Control');
113
120
  }
114
121
  logger.log(`Path ${json.path}`);
115
122
  logger.log(`Launch ${json.launch.join(' ')}`);
@@ -124,6 +131,32 @@ function emitStartReport(json, asJson) {
124
131
  logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Ready'));
125
132
  }
126
133
 
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
+
127
160
  /**
128
161
  * @param {Function} runner
129
162
  * @param {object} options
@@ -159,6 +192,8 @@ function setupAgentKitCommands(program) {
159
192
  addValidateCommand(group);
160
193
  addRemoteCommand(group);
161
194
  addStartCommand(group);
195
+ addListCommand(group);
196
+ addKillCommand(group);
162
197
  }
163
198
 
164
199
  /**
@@ -387,8 +422,8 @@ async function runValidateLeaf(options) {
387
422
  function addStartCommand(group) {
388
423
  group
389
424
  .command('start <app>')
390
- .description('Start Claude Code or Codex in tmux (does not install skills)')
391
- .option('--approve-all', 'Write auto-approve config for this app only')
425
+ .description('Start Claude Code or Codex with Remote Control in named tmux')
426
+ .option('--approve-all', 'Write auto-approve config (default for start)')
392
427
  .option('--skip-approve-all', 'Do not write auto-approve configs')
393
428
  .option('--yes', 'Same as --approve-all')
394
429
  .option('--detach', 'Create the tmux session in the background (no attach)')
@@ -398,8 +433,10 @@ function addStartCommand(group) {
398
433
  )
399
434
  .option(
400
435
  '--remote-control',
401
- 'Claude Code only: same session on this PC, claude.ai/code, and the Claude mobile app'
436
+ 'Enable native Remote Control for Claude or Codex (default)',
437
+ true
402
438
  )
439
+ .option('--no-remote-control', 'Start a local tmux-only session')
403
440
  .option('--dry-run', 'Print the launch command without starting it')
404
441
  .option('--json', 'Machine-readable output')
405
442
  .addHelpText('after', START_HELP)
@@ -414,4 +451,49 @@ function addStartCommand(group) {
414
451
  });
415
452
  }
416
453
 
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
+
417
499
  module.exports = { setupAgentKitCommands };
@@ -34,18 +34,14 @@ function buildRenameMap(operationMap) {
34
34
  if (!operationMap || typeof operationMap !== 'object' || Array.isArray(operationMap)) return {};
35
35
  const renameMap = {};
36
36
  for (const k of Object.keys(operationMap)) {
37
- // If it's already schema-valid but contains capitals, normalize to lowercase to align
38
- // with RBAC permission name restrictions (external-system schema forbids A-Z).
37
+ // Operation keys use the capability naming contract, which allows camelCase.
38
+ // RBAC permission names are normalized separately when permissions are generated.
39
39
  if (/^[a-z][a-zA-Z0-9]*$/.test(k)) {
40
- if (/[A-Z]/.test(k)) {
41
- renameMap[k] = k.toLowerCase();
42
- }
43
40
  continue;
44
41
  }
45
42
  const camel = toCamelCaseKey(k);
46
43
  if (!camel || camel === k) continue;
47
- // Use lowercase key to keep RBAC permission names schema-valid.
48
- renameMap[k] = camel.toLowerCase();
44
+ renameMap[k] = camel;
49
45
  }
50
46
  return renameMap;
51
47
  }
@@ -212,7 +208,7 @@ function normalizeAutoRbacOperationKeys(parsed, changes) {
212
208
  if (updated && Array.isArray(changes)) {
213
209
  const pairs = keysToRename.map((k) => `${k}→${renameMap[k]}`).join(', ');
214
210
  changes.push(
215
- `Normalized autoRbac operation keys (permission names are lowercase per schema; kebab aliases fold into canonical keys): ${pairs}`
211
+ `Normalized schema-invalid autoRbac operation aliases to camelCase keys (RBAC permission names remain lowercase): ${pairs}`
216
212
  );
217
213
  }
218
214
 
@@ -224,4 +220,3 @@ module.exports = {
224
220
  toCamelCaseKey,
225
221
  toKebabAliasKey
226
222
  };
227
-
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @fileoverview Post-publish local OpenAPI sync follow-up, shared by the synchronous and
3
+ * qualification-async upload paths (extracted from upload.js per plan 191.0 file-size gate).
4
+ * @author AI Fabrix Team
5
+ * @version 1.0.0
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const logger = require('../utils/logger');
11
+ const { formatSuccessLine, formatWarningLine } = require('../utils/cli-test-layout-chalk');
12
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
13
+ const { maybeSyncOpenApiFilesForMcp } = require('./repair-openapi-sync');
14
+ const { exportProgrammaticOpenApiForRepair } = require('./repair-openapi-export');
15
+
16
+ /**
17
+ * Upload local `integration/<systemKey>/openapi/*.json` specs when present so dataplane can store
18
+ * vendor OpenAPI keyed by each datasource `openapi.documentKey` (same behavior as repair --api OpenAPI sync).
19
+ * Call this **after** the external system row exists (successful publish or completed qualification):
20
+ * list/upload endpoints require it. First publish still succeeds because pipeline materializes
21
+ * internal specs from `openapi.operations` when needed.
22
+ *
23
+ * @param {string} systemKey
24
+ * @param {Object} manifest
25
+ * @param {Object} [uploadContext]
26
+ * @returns {Promise<void>}
27
+ */
28
+ async function logAndSyncLocalOpenApiForUpload(systemKey, manifest, uploadContext = {}) {
29
+ const pathOptions = uploadContext.pathOptions || {};
30
+ const appPath = resolveExternalSystemPath(systemKey, pathOptions);
31
+ const ei = manifest && manifest.externalIntegration;
32
+ const datasourceFiles = ei && Array.isArray(ei.dataSources) ? ei.dataSources : [];
33
+ await exportProgrammaticOpenApiForRepair({
34
+ appPath,
35
+ systemKey,
36
+ datasourceFiles,
37
+ dryRun: false,
38
+ backupCtx: { dryRun: false, changedFiles: [] }
39
+ });
40
+ const openapiSyncLines = await maybeSyncOpenApiFilesForMcp({
41
+ enabled: true,
42
+ dryRun: false,
43
+ appPath,
44
+ systemKey,
45
+ datasourceFiles,
46
+ dataplaneUrl: uploadContext.dataplaneUrl,
47
+ authConfig: uploadContext.authConfig,
48
+ // Always replace SpecFile rows keyed by openapi.documentKey so vendor/full
49
+ // specs are not left missing while only thin internal:* slices remain.
50
+ force: true
51
+ });
52
+ if (!uploadContext.quiet) {
53
+ for (const line of openapiSyncLines) {
54
+ logger.log(line.startsWith('Skipped') ? formatWarningLine(line) : formatSuccessLine(line));
55
+ }
56
+ }
57
+ }
58
+
59
+ module.exports = { logAndSyncLocalOpenApiForUpload };