@aifabrix/builder 2.55.0 → 2.55.2

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 (70) hide show
  1. package/lib/agent-kit/host-approve.js +48 -6
  2. package/lib/agent-kit/init.js +2 -2
  3. package/lib/agent-kit/remote-prompts.js +46 -13
  4. package/lib/agent-kit/run.js +1 -1
  5. package/lib/agent-kit/start.js +310 -0
  6. package/lib/app/deploy.js +7 -4
  7. package/lib/app/offer-create-from-template.js +184 -0
  8. package/lib/cli/setup-app.js +8 -1
  9. package/lib/cli/setup-auth.js +11 -2
  10. package/lib/commands/agent-kit-help.js +74 -4
  11. package/lib/commands/agent-kit.js +83 -12
  12. package/lib/commands/auth-status-user.js +99 -0
  13. package/lib/commands/auth-status.js +14 -4
  14. package/lib/commands/datasource-load-export-cli.js +7 -2
  15. package/lib/commands/datasource-test-trust-cli.js +5 -1
  16. package/lib/commands/governance-command-action.js +5 -4
  17. package/lib/commands/identity-membership-role-cache.js +6 -1
  18. package/lib/commands/lifecycle-command-action.js +16 -15
  19. package/lib/commands/login-credentials.js +8 -3
  20. package/lib/commands/login-device.js +14 -5
  21. package/lib/commands/login.js +14 -1
  22. package/lib/commands/logout.js +50 -15
  23. package/lib/commands/role-assistant.js +24 -12
  24. package/lib/commands/verify-governance-command-action.js +9 -7
  25. package/lib/commands/verify-operations-command-action.js +9 -8
  26. package/lib/commands/verify-trust-command-action.js +6 -5
  27. package/lib/external-system/download.js +1 -2
  28. package/lib/generator/helpers.js +12 -1
  29. package/lib/integration-definition/apply-static-enterprise.js +5 -2
  30. package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
  31. package/lib/role-assistant/knowledge-sync.js +9 -126
  32. package/lib/role-assistant/test-cases-evaluate.js +12 -1
  33. package/lib/role-assistant/test-cases-evidence.js +44 -5
  34. package/lib/role-assistant/test-cases-lifecycle.js +9 -3
  35. package/lib/role-assistant/test-runner.js +3 -0
  36. package/lib/schema/infra-parameter.schema.json +51 -3
  37. package/lib/schema/infra.parameter.yaml +476 -40
  38. package/lib/schema/type/document-storage.json +1 -1
  39. package/lib/utils/api-required-permissions.js +271 -0
  40. package/lib/utils/api.js +16 -13
  41. package/lib/utils/cli-json-exit.js +41 -0
  42. package/lib/utils/cli-utils.js +23 -5
  43. package/lib/utils/controller-url.js +78 -4
  44. package/lib/utils/deployment-errors.js +15 -0
  45. package/lib/utils/deployment-validation-helpers.js +4 -1
  46. package/lib/utils/device-code.js +31 -10
  47. package/lib/utils/error-formatters/error-parser.js +16 -1
  48. package/lib/utils/error-formatters/permission-errors.js +19 -5
  49. package/lib/utils/keycloak-realm-ready.js +5 -2
  50. package/lib/utils/logger.js +1 -0
  51. package/lib/utils/paths.js +26 -5
  52. package/lib/utils/secrets-helpers.js +2 -2
  53. package/lib/utils/write-stream-and-wait.js +63 -0
  54. package/package.json +20 -6
  55. package/templates/agent-kit/agent-kit.yaml +1 -1
  56. package/templates/agent-kit/instructions/AGENTKIT.md +25 -4
  57. package/templates/agent-kit/instructions/root.AGENTS.md +1 -1
  58. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +2 -1
  59. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +1 -1
  60. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +2 -1
  61. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +1 -0
  62. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +1 -0
  63. package/templates/agent-kit/skills/shared/hosts.md +13 -26
  64. package/templates/agent-kit/skills/shared/login.md +16 -0
  65. package/templates/agent-kit/workspace/README.md +1 -1
  66. package/templates/applications/dataplane/application.yaml +1 -2
  67. package/templates/applications/dataplane/env.template +5 -5
  68. package/templates/applications/miso-controller/application.yaml +21 -2
  69. package/templates/applications/miso-controller/env.template +89 -17
  70. package/templates/applications/miso-controller/rbac.yaml +5 -0
@@ -177,6 +177,21 @@ function handleGenericError(errorData, statusCode) {
177
177
  return createErrorResult('generic', errorData.message || errorData.error || 'Unknown error', formatGenericError(errorData, statusCode), errorData);
178
178
  }
179
179
 
180
+ function unwrapNestedErrorData(errorData) {
181
+ if (!errorData || !errorData.data || typeof errorData.data !== 'object') {
182
+ return errorData;
183
+ }
184
+ const nested = errorData.data;
185
+ return {
186
+ ...nested,
187
+ cliPermissionService: errorData.cliPermissionService || nested.cliPermissionService,
188
+ requiredPermissions: errorData.requiredPermissions || nested.requiredPermissions,
189
+ method: errorData.method || nested.method,
190
+ url: nested.instance || nested.url || errorData.url,
191
+ instance: nested.instance || errorData.instance
192
+ };
193
+ }
194
+
180
195
  /**
181
196
  * Parses error response and determines error type
182
197
  * @param {string|Object} errorResponse - Error response (string or parsed JSON)
@@ -200,7 +215,7 @@ function parseErrorResponse(errorResponse, statusCode, isNetworkError) {
200
215
 
201
216
  // Handle nested response structure (some APIs wrap errors in data field)
202
217
  if (errorData.data && typeof errorData.data === 'object') {
203
- errorData = errorData.data;
218
+ errorData = unwrapNestedErrorData(errorData);
204
219
  }
205
220
 
206
221
  // Handle network errors
@@ -42,6 +42,9 @@ function extractRequiredPermissions(errorData) {
42
42
  if (errorData.requiredPermissions && Array.isArray(errorData.requiredPermissions)) {
43
43
  return errorData.requiredPermissions;
44
44
  }
45
+ if (typeof errorData.requiredPermission === 'string' && errorData.requiredPermission.trim()) {
46
+ return [errorData.requiredPermission.trim()];
47
+ }
45
48
  if (errorData.required && errorData.required.permissions && Array.isArray(errorData.required.permissions)) {
46
49
  return errorData.required.permissions;
47
50
  }
@@ -97,6 +100,19 @@ function getPermissionDetailLines(errorData) {
97
100
  return lines;
98
101
  }
99
102
 
103
+ function appendPermissionAssignHint(lines, errorData) {
104
+ const service = errorData.cliPermissionService;
105
+ if (service) {
106
+ lines.push(chalk.gray(`Required on: ${service}`));
107
+ }
108
+ lines.push(chalk.gray(
109
+ service
110
+ ? `Assign this permission on the ${service} for this environment (roles and groups).`
111
+ : 'Assign the required permission on the Controller or Dataplane for this environment (roles and groups).'
112
+ ));
113
+ lines.push('');
114
+ }
115
+
100
116
  /**
101
117
  * Formats permission error with missing and required permissions
102
118
  * @param {Object} errorData - Error response data
@@ -111,11 +127,9 @@ function formatPermissionError(errorData) {
111
127
  lines.push('');
112
128
  }
113
129
 
114
- const missingPerms = extractMissingPermissions(errorData);
115
- addPermissionList(lines, missingPerms, 'Missing permissions');
116
-
117
- const requiredPerms = extractRequiredPermissions(errorData);
118
- addPermissionList(lines, requiredPerms, 'Required permissions');
130
+ addPermissionList(lines, extractMissingPermissions(errorData), 'Missing permissions');
131
+ addPermissionList(lines, extractRequiredPermissions(errorData), 'Required permissions');
132
+ appendPermissionAssignHint(lines, errorData);
119
133
 
120
134
  const requestUrl = errorData.instance || errorData.url;
121
135
  const method = errorData.method || 'POST';
@@ -82,15 +82,18 @@ async function waitForKeycloakRealmReady(opts = {}) {
82
82
  const directUrl = directBase ? buildRealmWellKnownUrl(directBase) : null;
83
83
  const deadline = Date.now() + timeoutSeconds * 1000;
84
84
 
85
- while (Date.now() < deadline) {
85
+ do {
86
86
  if (await probeRealmWellKnown(url)) {
87
87
  return;
88
88
  }
89
89
  if (directUrl && (await probeRealmWellKnown(directUrl))) {
90
90
  return;
91
91
  }
92
+ if (Date.now() >= deadline) {
93
+ break;
94
+ }
92
95
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
93
- }
96
+ } while (Date.now() < deadline);
94
97
 
95
98
  throw new Error(
96
99
  `Keycloak realm "aifabrix" was not ready within ${timeoutSeconds}s (checked ${url}${directUrl ? ` and ${directUrl}` : ''}). ` +
@@ -83,4 +83,5 @@ const logger = {
83
83
  };
84
84
 
85
85
  module.exports = logger;
86
+ module.exports.sanitizeValue = sanitizeValue;
86
87
 
@@ -957,6 +957,24 @@ async function ensureShippedBuilderAppTemplate(appName) {
957
957
  return true;
958
958
  }
959
959
 
960
+ /**
961
+ * Finds an existing local app workspace without copying shipped templates.
962
+ *
963
+ * @param {string} appName - Application name
964
+ * @param {Object} [options] - Detection options (reserved; options.type is ignored)
965
+ * @returns {{isExternal: boolean, appPath: string, appType: string, baseDir?: string}|null}
966
+ */
967
+ function findExistingAppType(appName, options = {}) {
968
+ if (!appName || typeof appName !== 'string') {
969
+ return null;
970
+ }
971
+ const externalResult = checkExternalWorkspaceFolder(appName, options);
972
+ if (externalResult) {
973
+ return externalResult;
974
+ }
975
+ return checkBuilderFolder(appName);
976
+ }
977
+
960
978
  /**
961
979
  * Detects if an app is external type by checking application config.
962
980
  * Resolution order: integration/ first, then builder/; if neither exists, throws.
@@ -971,13 +989,15 @@ async function detectAppType(appName, options = {}) {
971
989
  if (!appName || typeof appName !== 'string') {
972
990
  throw new Error('App name is required and must be a string');
973
991
  }
974
- const externalResult = checkExternalWorkspaceFolder(appName, options);
975
- if (externalResult) return externalResult;
976
- const builderResult = checkBuilderFolder(appName);
977
- if (builderResult) return builderResult;
992
+ const existing = findExistingAppType(appName, options);
993
+ if (existing) {
994
+ return existing;
995
+ }
978
996
  await ensureShippedBuilderAppTemplate(appName);
979
997
  const builderAfterTemplate = checkBuilderFolder(appName);
980
- if (builderAfterTemplate) return builderAfterTemplate;
998
+ if (builderAfterTemplate) {
999
+ return builderAfterTemplate;
1000
+ }
981
1001
  throw new Error(
982
1002
  `App '${appName}' not found in integration/${appName}, enterprise-knowledge/${appName}, role-assistant/${appName}, or builder/${appName}`
983
1003
  );
@@ -1121,6 +1141,7 @@ module.exports = {
1121
1141
  resolveApplicationConfigPath,
1122
1142
  resolveRbacPath,
1123
1143
  ensureShippedBuilderAppTemplate,
1144
+ findExistingAppType,
1124
1145
  detectAppType,
1125
1146
  getResolveAppPath,
1126
1147
  resolveIntegrationAppKeyFromCwd,
@@ -47,7 +47,8 @@ const { resolveBashKvFromProcessEnv } = require('./secrets-bash-kv');
47
47
  const EMPTY_ALLOWED_KV_FALLBACK = new Set([
48
48
  'redis-passwordKeyVault',
49
49
  'azure-openaiapi-urlKeyVault',
50
- 'secrets-azureOpenaiApiKeyVault'
50
+ 'secrets-azureOpenaiApiKeyVault',
51
+ 'miso-controller-secrets-apiKeyVault'
51
52
  ]);
52
53
 
53
54
  /**
@@ -486,4 +487,3 @@ module.exports = {
486
487
  rewriteInfraEndpoints,
487
488
  getEnvHosts
488
489
  };
489
-
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Flush a writable stream chunk without hanging on Jest write mocks.
3
+ *
4
+ * @fileoverview Shared stdout/stderr write + wait for CLI exits.
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ /**
12
+ * @param {unknown} writeFn - Stream write implementation
13
+ * @returns {boolean}
14
+ */
15
+ function isJestMockWrite(writeFn) {
16
+ return typeof jest !== 'undefined'
17
+ && typeof jest.isMockFunction === 'function'
18
+ && jest.isMockFunction(writeFn);
19
+ }
20
+
21
+ /**
22
+ * Write `chunk` and resolve after the stream flushes.
23
+ *
24
+ * Piped stdout/stderr on Linux is asynchronous; exiting before the write
25
+ * callback can truncate output. Jest spies that ignore the callback, and
26
+ * native writes inside a Jest worker, must not hang the test.
27
+ *
28
+ * @param {NodeJS.WritableStream} stream - stdout or stderr
29
+ * @param {string} chunk - Bytes to write
30
+ * @returns {Promise<void>}
31
+ */
32
+ function writeStreamAndWait(stream, chunk) {
33
+ return new Promise(resolve => {
34
+ let settled = false;
35
+ const finish = () => {
36
+ if (settled) {
37
+ return;
38
+ }
39
+ settled = true;
40
+ resolve();
41
+ };
42
+ if (!stream || typeof stream.write !== 'function') {
43
+ finish();
44
+ return;
45
+ }
46
+ const writeFn = stream.write;
47
+ try {
48
+ writeFn.call(stream, chunk, finish);
49
+ } catch {
50
+ finish();
51
+ return;
52
+ }
53
+ if (isJestMockWrite(writeFn) && writeFn.length < 2) {
54
+ finish();
55
+ return;
56
+ }
57
+ if (!isJestMockWrite(writeFn) && process.env.JEST_WORKER_ID) {
58
+ setImmediate(finish);
59
+ }
60
+ });
61
+ }
62
+
63
+ module.exports = { writeStreamAndWait };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.55.0",
3
+ "version": "2.55.2",
4
4
  "description": "AI Fabrix Builder — CLI and developer scripts (pnpm + af)",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -86,28 +86,42 @@
86
86
  "overrides": {
87
87
  "@babel/core": "^7.29.7",
88
88
  "@babel/plugin-transform-modules-systemjs": "^7.29.7",
89
- "fast-uri": "^4.1.0",
89
+ "baseline-browser-mapping": "^2.11.21",
90
+ "brace-expansion@5": "5.0.9",
91
+ "browserslist": "^4.28.9",
92
+ "fast-uri": "^4.1.4",
90
93
  "flatted": "^3.4.2",
91
94
  "follow-redirects": "^1.16.0",
92
95
  "form-data": "^4.0.6",
96
+ "js-yaml@3": "3.15.2",
97
+ "js-yaml@5": "5.4.1",
93
98
  "lodash": "^4.18.1",
94
99
  "minimatch": "^10.2.5",
100
+ "multer": "^2.3.0",
95
101
  "picomatch": "^4.0.5",
96
- "qs": "^6.15.3"
102
+ "qs": "^6.16.0",
103
+ "smol-toml": "^1.8.0"
97
104
  },
98
105
  "pnpm": {
99
106
  "overrides": {
100
107
  "@bcoe/v8-coverage": "^1.0.2",
101
108
  "@babel/core": "^7.29.7",
102
109
  "@babel/plugin-transform-modules-systemjs": "^7.29.7",
103
- "fast-uri": "^4.1.0",
110
+ "baseline-browser-mapping": "^2.11.21",
111
+ "brace-expansion@5": "5.0.9",
112
+ "browserslist": "^4.28.9",
113
+ "fast-uri": "^4.1.4",
104
114
  "flatted": "^3.4.2",
105
115
  "follow-redirects": "^1.16.0",
106
116
  "form-data": "^4.0.6",
117
+ "js-yaml@3": "3.15.2",
118
+ "js-yaml@5": "5.4.1",
107
119
  "lodash": "^4.18.1",
108
120
  "minimatch": "^10.2.5",
121
+ "multer": "^2.3.0",
109
122
  "picomatch": "^4.0.5",
110
- "qs": "^6.15.3"
123
+ "qs": "^6.16.0",
124
+ "smol-toml": "^1.8.0"
111
125
  }
112
126
  },
113
127
  "dependencies": {
@@ -121,7 +135,7 @@
121
135
  "handlebars": "^4.7.9",
122
136
  "inquirer": "^8.2.5",
123
137
  "inquirer-autocomplete-prompt": "^2.0.0",
124
- "js-yaml": "^4.3.0",
138
+ "js-yaml": "^4.3.2",
125
139
  "ora": "^5.4.1",
126
140
  "yaml": "^2.9.0"
127
141
  },
@@ -1,5 +1,5 @@
1
1
  schemaVersion: "1"
2
- kitVersion: "1.1.1"
2
+ kitVersion: "1.1.2"
3
3
  minimumBuilderVersion: "2.54.2"
4
4
  skills:
5
5
  - aifabrix-plan
@@ -34,7 +34,7 @@ New workspace instead of an existing repo: `aifabrix agent-kit init <workspaceNa
34
34
  Install the coding app, then sign in. How to **run** it locally or over SSH is in `.agents/skills/shared/hosts.md` (linked from every Agent Kit skill).
35
35
  {{#if autoApprove}}
36
36
 
37
- On TTY, install **asks** to auto-approve tool permissions **in this workspace**. Claude Code also allows `Bash(aifabrix *)` (delivery CLI) and `Read(~/**)` (home Read). Silent (`--yes` / non-TTY) writes them; `--skip-approve-all` skips.
37
+ On TTY, install **asks** which hosts to support (omit `--host`) and which tools to auto-approve. `--approve-all claude` or `--approve-all codex` writes only that host's config. Claude Code also allows `Bash(aifabrix *)` (delivery CLI) and `Read(~/**)` (home Read). Silent (`--yes` / non-TTY) writes them; `--skip-approve-all` skips.
38
38
  {{#if claude}}
39
39
  - Claude Code writes `.claude/settings.local.json` (gitignored).
40
40
  {{/if}}
@@ -57,22 +57,43 @@ curl -fsSL https://claude.ai/install.sh | bash
57
57
  claude auth login
58
58
  ```
59
59
 
60
- Skills: same `/aifabrix-*` names. First `claude` must run inside this workspace, not your home directory.
60
+ **Run in tmux** from this workspace so closing the terminal does not kill Claude. `--remote-control` is the **same** session on this PC, [claude.ai/code](https://claude.ai/code), and the Claude mobile app (Code). That is not `claude --cloud` (a separate cloud VM). Claude `--tmux` is for worktrees only.
61
+
62
+ ```bash
63
+ aifabrix agent-kit start claude --remote-control --approve-all --yes
64
+ ```
65
+
66
+ tmux session name defaults to this folder. Remote Control name is `hostname-user-folder` (for example `builder01-dev03-aifabrix-docs`). Override the folder with `--name`. Detach: `Ctrl-b` then `d`. Reattach: `tmux attach -t <folder>`. Several workspaces can run at once. Same account on phone and PC (`claude auth login`, Pro/Max/Team — not an API key). On the phone: Claude app → **Code**. Skills: `/aifabrix-plan` (and the other `/aifabrix-*` names). First `claude` must run inside this workspace, not your home directory. Full notes: `.agents/skills/shared/hosts.md`.
61
67
  {{/if}}
62
68
  {{#if codex}}
63
69
 
64
70
  ### Codex
65
71
 
66
- Install the Codex CLI, then sign in with your Codex account. Skills: `$aifabrix-plan` (and the other `$aifabrix-*` names). Codex reads `AGENTS.md`.
72
+ Install the Codex CLI (`@openai/codex`, not the unrelated `codex` package), then sign in with ChatGPT (Plus, Pro, Business, Edu, or Enterprise):
73
+
74
+ ```bash
75
+ npm install -g @openai/codex
76
+ codex login
77
+ ```
78
+
79
+ **Run in tmux** from this workspace so the session stays if you close the terminal:
80
+
81
+ ```bash
82
+ aifabrix agent-kit start codex --approve-all --yes
83
+ ```
84
+
85
+ Detach: `Ctrl-b` then `d`. Reattach: `tmux attach -t <folder>`, or `aifabrix agent-kit start codex` from this workspace. Default session name is this folder. Skills: `$aifabrix-plan` (and the other `$aifabrix-*` names). Codex reads `AGENTS.md`. Full notes: `.agents/skills/shared/hosts.md`.
67
86
  {{/if}}
68
87
 
69
88
  ## Auth for delivery CLI
70
89
 
90
+ Do not tell the user to open a terminal for Builder login. Follow `.agents/skills/shared/login.md`.
91
+
71
92
  ```bash
72
93
  aifabrix auth status
73
94
  ```
74
95
 
75
- If still unauthenticated, run `aifabrix login` (the agent cannot log in for you).
96
+ If that is not authenticated, ask for the Miso Controller URL, run device login, and paste the printed authenticate URL into chat.
76
97
 
77
98
  ## How to start
78
99
 
@@ -28,7 +28,7 @@ Do not collapse these into a generic success boolean. Do not start the next life
28
28
  ## Coding hosts
29
29
 
30
30
  Cursor, Claude Code, and Codex — local machine or SSH (tmux, Claude Remote Control): `.agents/skills/shared/hosts.md`.
31
- Install and sign-in: `AGENTKIT.md`.
31
+ Install and sign-in: `AGENTKIT.md`. Builder CLI login from chat: `.agents/skills/shared/login.md`.
32
32
 
33
33
  ## Docs
34
34
 
@@ -24,6 +24,7 @@ Stop at the phase gate. Do not run the next phase without `next-yes`
24
24
 
25
25
  Read current CLI help before composing commands. You own orchestration, not CLI implementation.
26
26
  Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
27
+ CLI login from chat: [login.md](../shared/login.md).
27
28
 
28
29
  ## Always
29
30
 
@@ -31,7 +32,7 @@ Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
31
32
  aifabrix auth status
32
33
  ```
33
34
 
34
- Refresh first. User runs `aifabrix login` only if still unauthenticated. Re-run on 401, then retry once.
35
+ Refresh first. If still unauthenticated, follow [login.md](../shared/login.md). Re-run on 401, then retry once.
35
36
 
36
37
  System key: argument → active plan → sole `integration/` folder → ask once. Never invent. Reject `ra-` / `ek-` ([scripts/reserved-system-key.js](scripts/reserved-system-key.js)).
37
38
 
@@ -41,7 +41,7 @@ Never mark live E2E pass when it did not run.
41
41
 
42
42
  ## Identity and protection order
43
43
 
44
- 1. `aifabrix auth status`
44
+ 1. `aifabrix auth status` — if unauthenticated, [login.md](../../shared/login.md)
45
45
  2. Catalog roles + fixtures + `identity apply` / `identity sync` / `role set-groups`
46
46
  3. Load partitioned fixtures
47
47
  4. `aifabrix protection upload <sourceStorageDs> --sync` (storage DS, not the view)
@@ -15,6 +15,7 @@ Output: one delivery plan from [plan-template.md](references/plan-template.md).
15
15
  Score completeness with [challenge-checklist.md](references/challenge-checklist.md).
16
16
  Next-step interaction: [interaction.md](references/interaction.md).
17
17
  Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
18
+ CLI login from chat: [login.md](../shared/login.md).
18
19
 
19
20
  ## Sequence
20
21
 
@@ -44,7 +45,7 @@ aifabrix auth status
44
45
  aifabrix role list --page-size 1000
45
46
  ```
46
47
 
47
- If still unauthenticated after refresh, draft the plan; catalog-role rows stay MISSING until the user runs `aifabrix login`.
48
+ If unauthenticated after refresh, follow [login.md](../shared/login.md) before listing catalog roles.
48
49
 
49
50
  ## Trigger examples
50
51
 
@@ -19,6 +19,7 @@ Evidence rules: [evidence-lifecycle.md](references/evidence-lifecycle.md).
19
19
  Demo shape: [demo-template.md](references/demo-template.md).
20
20
  Next-step: [interaction.md](../aifabrix-plan/references/interaction.md).
21
21
  Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
22
+ CLI login from chat: [login.md](../shared/login.md).
22
23
 
23
24
  ## Keys
24
25
 
@@ -10,6 +10,7 @@ description: >
10
10
 
11
11
  See [package-boundary.md](references/package-boundary.md) and nested `role-assistant/AGENTS.md`.
12
12
  Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
13
+ CLI login from chat: [login.md](../shared/login.md).
13
14
 
14
15
  ## Preconditions
15
16
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Use this workspace from **Cursor**, **Claude Code**, and **Codex**. Skills live in `.agents/skills/`. Invoke `/aifabrix-*` in Cursor and Claude Code, and `$aifabrix-*` in Codex.
4
4
 
5
- Install and sign-in steps are in `AGENTKIT.md`. This file is how to **run** each host on a local machine or over SSH.
5
+ Install and sign-in steps are in `AGENTKIT.md`. This file is how to **run** each host on a local machine or over SSH. Builder CLI login from chat: [login.md](login.md).
6
6
 
7
7
  Do not start Claude or Codex in your home directory. `cd` into this git workspace first.
8
8
 
@@ -13,7 +13,7 @@ Do not start Claude or Codex in your home directory. `cd` into this git workspac
13
13
  - Claude Code: `.claude/settings.local.json` (`permissions.defaultMode` = `bypassPermissions`, `allow` = `Bash(aifabrix *)` and `Read(~/**)`)
14
14
  - Codex: `.codex/config.toml` (`approval_policy = "never"`, `sandbox_mode = "workspace-write"`)
15
15
 
16
- Those files stay in **this workspace**. Claude may **read** files under the developer home (`Read(~/**)`) and run the delivery CLI (`Bash(aifabrix *)`). They do not add sibling folders as extra workspaces, do not grant Write outside this repo, and do not allow every shell command. TTY asks (default yes). `--yes` or a non-TTY / `--json` run writes them. `--skip-approve-all` skips. `--force` overwrites existing local files. Builder never writes `~/.claude` or `~/.codex`.
16
+ Those files stay in **this workspace**. Claude may **read** files under the developer home (`Read(~/**)`) and run the delivery CLI (`Bash(aifabrix *)`). They do not add sibling folders as extra workspaces, do not grant Write outside this repo, and do not allow every shell command. TTY asks which host to auto-approve when both are selected. `--approve-all claude` or `--approve-all codex` writes only that host. `--yes` or a non-TTY / `--json` run writes them. `--skip-approve-all` skips. `--force` overwrites existing local files. Builder never writes `~/.claude` or `~/.codex`.
17
17
 
18
18
  ## Cursor
19
19
 
@@ -23,48 +23,35 @@ Those files stay in **this workspace**. Claude may **read** files under the deve
23
23
 
24
24
  ## Claude Code
25
25
 
26
- **Local.**
26
+ **Local or SSH — tmux.** Closing the terminal must not kill Claude. Agent Kit starts Claude **inside tmux**. `--remote-control` is the **same** session on this PC, [claude.ai/code](https://claude.ai/code), and the Claude mobile app (**Code**). Do not use `claude --cloud` for this — that starts a different machine that clones GitHub. Claude `--tmux` is only for `--worktree` sessions, not this wrapper.
27
27
 
28
28
  ```bash
29
- cd /path/to/this-workspace
30
- claude
31
- ```
32
-
33
- Then `/aifabrix-plan` (or another `/aifabrix-*` skill). First run in a project asks you to trust the folder; accept only this workspace.
34
-
35
- **SSH (keep the session after you disconnect).** tmux holds the process on the remote machine:
36
-
37
- ```bash
38
- tmux new -s claude -c /path/to/this-workspace
39
- claude --remote-control
29
+ aifabrix agent-kit start claude --remote-control --approve-all --yes
40
30
  ```
41
31
 
42
- Detach without killing Claude: `Ctrl-b` then `d`. Reattach: `tmux attach -t claude`.
32
+ tmux session name is this folder (for example `aifabrix-docs`). Remote Control name is `hostname-user-folder` (for example `builder01-dev03-aifabrix-docs`). `--name` overrides the folder. Several workspaces can run at once. Detach without killing Claude: `Ctrl-b` then `d`. Reattach: `tmux attach -t aifabrix-docs`, or run `start` again from the same folder. Then `/aifabrix-plan` (or another `/aifabrix-*` skill). First run in a project asks you to trust the folder; accept only this workspace.
43
33
 
44
- **Phone and web.** `--remote-control` (or `claude remote-control` in server mode) publishes the same session to [claude.ai/code](https://claude.ai/code) and the Claude mobile app (**Code**). Press space in server mode for a QR code. In an interactive session, `/remote-control` or `/mobile`. Requires a Claude Pro/Max/Team login (`claude auth login`), not an API key.
34
+ **Phone and PC.** Sign in with the same Claude Pro/Max/Team account (`claude auth login` — not an API key). After Remote Control starts, open **Code** in the Claude mobile app and/or [claude.ai/code](https://claude.ai/code) on the PC. In an already-running session, `/remote-control` or `/mobile`.
45
35
 
46
- Closing SSH does not stop a tmux session. Stopping tmux or the machine does.
36
+ Closing the terminal (or SSH) does not stop a tmux session. Stopping tmux or the machine does. Remote Control also stops if this host goes down.
47
37
 
48
38
  ## Codex
49
39
 
50
- **Local.**
40
+ **Local or SSH — tmux (recommended).** Same recipe on your PC and on a remote host. tmux keeps Codex running if you close the terminal or disconnect SSH. Codex has no Claude-style phone/web Remote Control.
51
41
 
52
42
  ```bash
53
- cd /path/to/this-workspace
54
- codex
43
+ aifabrix agent-kit start codex --approve-all --yes
55
44
  ```
56
45
 
57
- Then `$aifabrix-plan` (and the other `$aifabrix-*` skills). Codex reads `AGENTS.md`.
46
+ Equivalent: `tmux new -s aifabrix-docs -c /path/to/this-workspace` then `codex`. Default `--name` is the last folder. If that session already exists, `start` attaches. Detach: `Ctrl-b` then `d`. Then `$aifabrix-plan` (and the other `$aifabrix-*` skills). Codex reads `AGENTS.md`.
58
47
 
59
- **SSH (keep the session after you disconnect).**
48
+ **Foreground only (no tmux).**
60
49
 
61
50
  ```bash
62
- tmux new -s codex -c /path/to/this-workspace
51
+ cd /path/to/this-workspace
63
52
  codex
64
53
  ```
65
54
 
66
- Detach: `Ctrl-b` then `d`. Reattach: `tmux attach -t codex`. Codex has no Claude-style phone/web Remote Control; stay in tmux (or reattach later) to see the session.
67
-
68
55
  ## tmux cheat sheet
69
56
 
70
57
  | Action | Keys / command |
@@ -75,4 +62,4 @@ Detach: `Ctrl-b` then `d`. Reattach: `tmux attach -t codex`. Codex has no Claude
75
62
  | Attach | `tmux attach -t <name>` |
76
63
  | Kill session | `tmux kill-session -t <name>` |
77
64
 
78
- Use separate session names (`claude` vs `codex`) if both run on the same host.
65
+ Use a different `--name` per workspace so several Claude or Codex sessions can run on the same host. Claude and Codex in the **same** folder also need two names.
@@ -0,0 +1,16 @@
1
+ # Delivery CLI login (no terminal)
2
+
3
+ Before any online Builder command (`deploy`, `upload`, `role list`, `show --online`):
4
+
5
+ 1. Run `aifabrix auth status`. If authenticated, continue.
6
+ 2. If not, ask the user in chat for the Miso Controller URL. Do not invent it. Paste whatever they give you; extra path segments are ignored.
7
+ 3. Run (environment **dev** unless they named another):
8
+
9
+ ```bash
10
+ aifabrix login --method device -c <url> -e dev
11
+ ```
12
+
13
+ 4. As soon as the command prints **Open this URL to authenticate:**, copy that full URL into chat and ask the user to open it and approve. Do not ask them to use a terminal.
14
+ 5. Leave the login command running until it reports success (or fails). Then run `aifabrix auth status` again.
15
+
16
+ On 401 later, repeat this flow once, then retry the original command.
@@ -9,7 +9,7 @@ Vendor limits, credentials, designated test subjects, and PII live only in each
9
9
  ## How to start
10
10
 
11
11
  1. Open **this folder** in Cursor, Claude Code, or Codex. Host install and sign-in: **[AGENTKIT.md](./AGENTKIT.md)**.
12
- 2. In a terminal here, run `aifabrix auth status`. If you are not authenticated, run `aifabrix login` (the agent cannot log in for you).
12
+ 2. The agent checks `aifabrix auth status`. If you are not signed in, it asks for the Miso Controller URL and gives you a browser link. You do not need a terminal. Details: **[AGENTKIT.md](./AGENTKIT.md)**.
13
13
  3. In chat, either **invoke a skill** or **paste a prompt** from the tables below. Work in order. Wait for the gate (`PLAN_READY`, `READY`, `BUILT`, then `PASS` or `PASS_WITH_SKIPS`) before the next step.
14
14
 
15
15
  ### How to use prompts
@@ -5,7 +5,7 @@ app:
5
5
  description: "AI Fabrix Dataplane is a secure, in-tenant integration and automation layer that supplies governed, normalized, and explainable enterprise data to AI agents. Using CIP as a declarative standard, it enforces RBAC and ABAC, executes integrations, and exposes trusted data via MCP and OpenAPI."
6
6
  type: webapp
7
7
  language: python # Explicitly specify Python language
8
- version: 2.0.56
8
+ version: 2.0.65
9
9
  # Image Configuration
10
10
  # Set tag to match your build (example: aifabrix build dataplane -t 1.0.0).
11
11
  # Registry is required so the controller can pull the image (avoids docker-not-found on the controller host).
@@ -240,4 +240,3 @@ configuration:
240
240
  field: text
241
241
  label: "Default LLM provider (system key when two or more providers)"
242
242
  placeholder: "openai"
243
-
@@ -30,8 +30,8 @@ LOCAL_MODE=false
30
30
  # CELERY_WORKER_RUNTIME is set automatically by python -m app.core.celery_worker_main (NullPool per task).
31
31
  # CELERY_LOG_LEVEL=info
32
32
 
33
- # When API_KEY is set, a matching Bearer token bypasses OAuth2 validation
34
- # Same kv:// as miso-controller API_KEY so pipeline Bearer bypass uses one secrets.local entry
33
+ # When API_KEY is set, a matching Bearer token bypasses OAuth2 validation.
34
+ # Same kv:// as miso-controller API_KEY; Azure may leave this optional key empty.
35
35
  API_KEY=kv://miso-controller-secrets-apiKeyVault
36
36
  # Comma-separated realm roles attached to API_KEY bypass (JWT realm_access.roles). Default: operator.
37
37
  # Dev manual tests (437 governance, enterprise-llm llm.call): use full-access locally; restrict in tst/pro.
@@ -44,7 +44,7 @@ API_KEY_BYPASS_ROLES=full-access
44
44
 
45
45
  # API Configuration
46
46
  API_V1_STR=/api/v1
47
- VERSION=2.0.56
47
+ VERSION=2.0.65
48
48
  # Minimum supported aifabrix-builder CLI semver (advertised on /health and /api/v1/health; empty = no gate)
49
49
  # MIN_BUILDER_CLI_VERSION=2.45.0
50
50
  MIN_BUILDER_CLI_VERSION=2.45.5
@@ -258,7 +258,7 @@ DATAPLANE_PUBLIC_KEY_ID=
258
258
  CERTIFICATE_PRIVATE_KEY=kv://secrets-certificatePrivateKeyVault
259
259
  CERTIFICATE_PUBLIC_KEY=kv://secrets-certificatePublicKeyVault
260
260
  # Optional public key identifier for issued certificates; default if unset: dataplane-signing-key
261
- CERTIFICATE_PUBLIC_KEY_ID=dev01-dataplane-signing
261
+ CERTIFICATE_PUBLIC_KEY_ID=dataplane-signing-key
262
262
 
263
263
  # =============================================================================
264
264
  # VALIDATION ENGINE — YAML CONTRACTS
@@ -333,4 +333,4 @@ TRUST_AI_EXPOSURE_GATE_ENABLED=false
333
333
  # app/schemas/json/external-datasource.schema.json metadata.version. Set CERTIFICATION_RULES_VERSION
334
334
  # when 346.rules / §17 revisions must invalidate existing certificates without matching rulesVersion.
335
335
  CERTIFICATION_DATASOURCE_SCHEMA_VERSION=
336
- CERTIFICATION_RULES_VERSION=
336
+ CERTIFICATION_RULES_VERSION=
@@ -4,7 +4,7 @@ app:
4
4
  displayName: 'Miso Controller'
5
5
  description: 'Miso is the AI Fabrix in-tenant controller and portal layer for securely operating enterprise AI apps inside a customer’s Azure tenant. It provides Entra ID SSO, RBAC, audit logs, environment/app configuration via schemas, and safe secret handling via Key Vault references—ensuring governance, traceability, and predictable UX across portal, SDK, and CLI.'
6
6
  type: webapp
7
- version: '2.0.51'
7
+ version: '2.0.66'
8
8
  # Image Configuration
9
9
  # Set tag to match your build (e.g. aifabrix build miso-controller -t 1.0.0 then tag: 1.0.0)
10
10
  # Registry is required so the controller can pull the image (avoids "docker: not found" on the controller host).
@@ -72,10 +72,29 @@ repository:
72
72
  #
73
73
  # NOT included (auto-configured):
74
74
  # - DATABASE_URL, REDIS_URL, KEYCLOAK_* → auto-generated during Azure install
75
- # - MISO_*, AZURE_*, kv:// → credentials and system parameters
75
+ # - Other MISO_*, AZURE_*, kv:// → credentials and system parameters
76
76
  # - PORT, NODE_ENV, MISO_ENVIRONMENT → set by deployment target
77
77
 
78
78
  configuration:
79
+ # -------------------------------------------------------------------------
80
+ # Security & browser session
81
+ # -------------------------------------------------------------------------
82
+ - name: MISO_APPLICATION_SECRETS_UI_ENABLED
83
+ category: Security
84
+ portalInput:
85
+ field: select
86
+ label: 'Enable Settings System Secrets tab'
87
+ options:
88
+ - 'false'
89
+ - 'true'
90
+
91
+ - name: AUTH_BROWSER_SESSION_TTL_SECONDS
92
+ category: Security
93
+ portalInput:
94
+ field: text
95
+ label: 'Browser Session Idle Timeout (seconds)'
96
+ placeholder: '1800'
97
+
79
98
  # -------------------------------------------------------------------------
80
99
  # Operations & Debugging
81
100
  # -------------------------------------------------------------------------