@aifabrix/builder 2.58.0 → 2.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agent-kit/codex-app-server.js +44 -0
- package/lib/agent-kit/init.js +4 -17
- package/lib/agent-kit/session-report.js +15 -10
- package/lib/agent-kit/setup-auth.js +131 -0
- package/lib/agent-kit/setup-context.js +118 -0
- package/lib/agent-kit/setup-github.js +70 -0
- package/lib/agent-kit/setup-managed.js +79 -0
- package/lib/agent-kit/setup-process.js +42 -0
- package/lib/agent-kit/setup-session.js +53 -0
- package/lib/agent-kit/setup-state.js +52 -0
- package/lib/agent-kit/setup-workspace.js +113 -0
- package/lib/agent-kit/setup.js +84 -0
- package/lib/agent-kit/start.js +4 -3
- package/lib/agent-kit/tmux-sessions.js +20 -3
- package/lib/api/dev.api.js +1 -1
- package/lib/api/miso-health.api.js +26 -0
- package/lib/api/types/dev.types.js +3 -3
- package/lib/app/deploy.js +0 -102
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-dev-path-commands.js +4 -0
- package/lib/cli/setup-dev.js +7 -4
- package/lib/cli/setup-help.js +86 -0
- package/lib/cli/setup-onboarding.js +37 -0
- package/lib/cli/setup-utility-repair.js +7 -5
- package/lib/commands/agent-kit-help.js +21 -7
- package/lib/commands/agent-kit-sessions.js +126 -0
- package/lib/commands/agent-kit-setup.js +44 -0
- package/lib/commands/agent-kit.js +5 -79
- package/lib/commands/dev-init.js +21 -23
- package/lib/commands/dev-refresh.js +2 -2
- package/lib/commands/governance-verify-external.js +12 -5
- package/lib/commands/repair-datasource.js +9 -9
- package/lib/commands/repair.js +45 -8
- package/lib/commands/upload-qualification-client.js +56 -11
- package/lib/commands/upload-qualification-poll.js +51 -22
- package/lib/commands/wizard-dataplane.js +16 -52
- package/lib/datasource/datasource-validate-summary.js +8 -5
- package/lib/datasource/sync-defaults.js +53 -0
- package/lib/deployment/installation/azure-infra-stage.js +10 -0
- package/lib/deployment/installation/azure-preflight.js +13 -2
- package/lib/deployment/installation/azure-readiness.js +24 -2
- package/lib/deployment/installation/environment-auth.js +60 -20
- package/lib/deployment/installation/environment-stage.js +9 -5
- package/lib/deployment/installation/infra-catalog.js +8 -2
- package/lib/deployment/installation/infra-manifest.js +5 -0
- package/lib/deployment/installation/registry-auth-mode.js +33 -10
- package/lib/deployment/marketplace-environment.js +26 -1
- package/lib/generator/index.js +23 -12
- package/lib/integration-definition/apply.js +2 -2
- package/lib/internal/fs-real-sync.js +4 -1
- package/lib/programmatic/bearer-auth.js +3 -62
- package/lib/programmatic/builder-api-startup.js +51 -0
- package/lib/programmatic/builder-miso-runtime.js +167 -0
- package/lib/programmatic/openapi-descriptions.js +3 -3
- package/lib/programmatic/openapi-schema-descriptions-core.js +3 -3
- package/lib/programmatic/openapi-spec.js +2 -1
- package/lib/programmatic/route-handler-map.js +3 -2
- package/lib/schema/environment-deploy-request.schema.json +1 -1
- package/lib/schema/external-datasource.schema.json +20 -5
- package/lib/schema/infra-parameter.schema.json +1 -1
- package/lib/schema/infra.parameter.yaml +2 -1
- package/lib/schema/infrastructure-schema.json +145 -1
- package/lib/schema/installation-environment.schema.json +1 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/config-paths.js +24 -29
- package/lib/utils/config-registry-preference.js +13 -6
- package/lib/utils/dev-init-ssh-merge.js +5 -4
- package/lib/utils/dev-user-groups.js +2 -1
- package/lib/utils/docker-daemon-tls-ca.js +2 -2
- package/lib/utils/error-formatters/validation-errors.js +25 -5
- package/lib/utils/help-builder.js +4 -3
- package/lib/utils/token-manager.js +29 -36
- package/package.json +3 -2
- package/templates/agent-kit/instructions/AGENTKIT.md +39 -2
- package/templates/agent-kit/instructions/root.AGENTS.md +40 -1
- package/templates/agent-kit/skills/shared/hosts.md +23 -5
- package/templates/agent-kit/workspace/README.md +14 -1
- package/templates/applications/builder-api/application.yaml +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/external-system/external-datasource.yaml.hbs +7 -6
- package/templates/marketplace/main.json +438 -80
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder's process-owned SDK runtime and application-token health check.
|
|
3
|
+
* @fileoverview SDK owns providers, secrets, renewal and connections.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const { validateMisoApplicationToken } = require('../api/miso-health.api');
|
|
8
|
+
let activeRuntime;
|
|
9
|
+
|
|
10
|
+
/** @param {string} code Safe category @returns {Error} Service-unavailable error */
|
|
11
|
+
function runtimeError(code) {
|
|
12
|
+
const messages = {
|
|
13
|
+
miso_authentication_failed: 'Miso application authentication failed',
|
|
14
|
+
miso_timeout: 'Miso application token check timed out',
|
|
15
|
+
miso_unreachable: 'Miso Controller is unavailable',
|
|
16
|
+
miso_runtime_unavailable: 'Miso application runtime is unavailable'
|
|
17
|
+
};
|
|
18
|
+
return Object.assign(new Error(messages[code]), {
|
|
19
|
+
statusCode: 503, code, errorType: `/Errors/${code}`
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @param {unknown} error SDK error @returns {string} Safe category */
|
|
24
|
+
function failureCode(error) {
|
|
25
|
+
const status = error?.statusCode || error?.errorResponse?.status || error?.response?.status;
|
|
26
|
+
if ([401, 403].includes(status) || error?.code === 'authorization-denied') {
|
|
27
|
+
return 'miso_authentication_failed';
|
|
28
|
+
}
|
|
29
|
+
if (['ECONNABORTED', 'ETIMEDOUT', 'ERR_CANCELED'].includes(error?.code)) {
|
|
30
|
+
return 'miso_timeout';
|
|
31
|
+
}
|
|
32
|
+
return 'miso_unreachable';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** SDK resource ownership and fail-closed application state. */
|
|
36
|
+
class RuntimeController {
|
|
37
|
+
/** @param {Object} runtime SDK runtime */
|
|
38
|
+
constructor(runtime) {
|
|
39
|
+
this.runtime = runtime;
|
|
40
|
+
this.unsubscribe = runtime.onInvalidated(reason => {
|
|
41
|
+
this.unavailable = reason === 'authorization-denied'
|
|
42
|
+
? 'miso_authentication_failed' : 'miso_runtime_unavailable';
|
|
43
|
+
this.abort?.abort();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @returns {void} Throws when the SDK snapshot is unavailable */
|
|
48
|
+
assertAvailable() {
|
|
49
|
+
if (this.unavailable) throw runtimeError(this.unavailable);
|
|
50
|
+
try {
|
|
51
|
+
this.runtime.secrets.get('');
|
|
52
|
+
} catch {
|
|
53
|
+
throw runtimeError(this.unavailable || 'miso_runtime_unavailable');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** @returns {Object} Initialized client */
|
|
58
|
+
getClient() {
|
|
59
|
+
this.assertAvailable();
|
|
60
|
+
return this.runtime.client;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @returns {Promise<void>} Idempotent cleanup */
|
|
64
|
+
close() {
|
|
65
|
+
if (!this.closing) {
|
|
66
|
+
this.unavailable = this.unavailable || 'miso_runtime_unavailable';
|
|
67
|
+
this.abort?.abort();
|
|
68
|
+
this.unsubscribe();
|
|
69
|
+
this.closing = Promise.resolve().then(() => this.runtime.close()).catch(() => {
|
|
70
|
+
throw runtimeError('miso_runtime_unavailable');
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return this.closing;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @returns {Promise<void>} Fresh, coalesced token proof; never caches success */
|
|
77
|
+
health() {
|
|
78
|
+
this.assertAvailable();
|
|
79
|
+
if (!this.pending) {
|
|
80
|
+
this.abort = new AbortController();
|
|
81
|
+
this.pending = runProbe(this.runtime.client, this.abort,
|
|
82
|
+
() => this.assertAvailable(), code => {
|
|
83
|
+
this.unavailable = code;
|
|
84
|
+
void this.close().catch(() => {});
|
|
85
|
+
}, () => {
|
|
86
|
+
this.pending = undefined;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return this.pending;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @param {Object} runtime SDK runtime @returns {RuntimeController} Consumer controls */
|
|
94
|
+
function createRuntimeController(runtime) {
|
|
95
|
+
return new RuntimeController(runtime);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Coalesce until transport settles, including SDK-owned token acquisition after a deadline.
|
|
100
|
+
* @param {Object} client SDK client
|
|
101
|
+
* @param {AbortController} abort Cancellation
|
|
102
|
+
* @param {Function} assertAvailable Runtime guard
|
|
103
|
+
* @param {Function} invalidate Terminal rejection
|
|
104
|
+
* @param {Function} settled Release in-flight work
|
|
105
|
+
* @returns {Promise<void>} Bounded health result
|
|
106
|
+
*/
|
|
107
|
+
function runProbe(client, abort, assertAvailable, invalidate, settled) {
|
|
108
|
+
let timer;
|
|
109
|
+
const deadline = new Promise((_, reject) => {
|
|
110
|
+
timer = setTimeout(() => {
|
|
111
|
+
abort.abort(); reject(runtimeError('miso_timeout'));
|
|
112
|
+
}, 3000);
|
|
113
|
+
});
|
|
114
|
+
const request = validateMisoApplicationToken(client, abort.signal).then(() => {
|
|
115
|
+
assertAvailable();
|
|
116
|
+
}).catch(error => {
|
|
117
|
+
let code = failureCode(error);
|
|
118
|
+
try {
|
|
119
|
+
assertAvailable();
|
|
120
|
+
} catch (unavailable) {
|
|
121
|
+
code = unavailable.code;
|
|
122
|
+
}
|
|
123
|
+
if (code === 'miso_authentication_failed') invalidate(code);
|
|
124
|
+
throw runtimeError(code);
|
|
125
|
+
}).finally(() => {
|
|
126
|
+
clearTimeout(timer); settled();
|
|
127
|
+
});
|
|
128
|
+
return Promise.race([request, deadline]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** @param {Function} [init] SDK factory for tests @returns {Promise<Object>} Runtime controls */
|
|
132
|
+
async function initializeBuilderMisoRuntime(init) {
|
|
133
|
+
if (activeRuntime) throw runtimeError('miso_runtime_unavailable');
|
|
134
|
+
const factory = init || require('@aifabrix/miso-client/bootstrap').initSecrets;
|
|
135
|
+
try {
|
|
136
|
+
activeRuntime = createRuntimeController(await factory());
|
|
137
|
+
return activeRuntime;
|
|
138
|
+
} catch {
|
|
139
|
+
throw runtimeError('miso_runtime_unavailable');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** @returns {Object} Active guarded runtime */
|
|
144
|
+
function getBuilderMisoRuntime() {
|
|
145
|
+
if (!activeRuntime) throw runtimeError('miso_runtime_unavailable');
|
|
146
|
+
return activeRuntime;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** @returns {Promise<void>} Close and release process-owned runtime */
|
|
150
|
+
async function closeBuilderMisoRuntime() {
|
|
151
|
+
const current = activeRuntime;
|
|
152
|
+
activeRuntime = undefined;
|
|
153
|
+
await current?.close();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** @param {boolean} [ready] Readiness response @returns {Promise<Object>} Healthy payload */
|
|
157
|
+
async function checkBuilderMisoHealth(ready = false) {
|
|
158
|
+
await getBuilderMisoRuntime().health();
|
|
159
|
+
return ready
|
|
160
|
+
? { status: 'ready', dataplaneUrl: process.env.DATAPLANE_URL || null }
|
|
161
|
+
: { status: 'ok' };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
initializeBuilderMisoRuntime, getBuilderMisoRuntime, closeBuilderMisoRuntime,
|
|
166
|
+
checkBuilderMisoHealth, createRuntimeController
|
|
167
|
+
};
|
|
@@ -213,14 +213,14 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
|
|
|
213
213
|
tags: ['repository']
|
|
214
214
|
},
|
|
215
215
|
health: {
|
|
216
|
-
summary: '
|
|
216
|
+
summary: 'Application-token health probe',
|
|
217
217
|
description:
|
|
218
|
-
'Returns ok
|
|
218
|
+
'Returns ok only after Miso accepts the application client token. Returns 503 when authentication, runtime validity or connectivity fails. No caller authentication; user credentials cannot substitute for the application token.',
|
|
219
219
|
tags: ['infra']
|
|
220
220
|
},
|
|
221
221
|
ready: {
|
|
222
222
|
summary: 'Readiness probe',
|
|
223
|
-
description: '
|
|
223
|
+
description: 'Verifies the application client token with Miso and returns ready with configured dataplaneUrl. Returns 503 for an unusable token or unavailable Miso runtime. Does not verify Dataplane connectivity.',
|
|
224
224
|
tags: ['infra']
|
|
225
225
|
},
|
|
226
226
|
getBuilderHelp: {
|
|
@@ -113,11 +113,11 @@ const CORE_SCHEMA_DESCRIPTIONS = {
|
|
|
113
113
|
deleted: 'Always true when the workspace folder was removed.'
|
|
114
114
|
},
|
|
115
115
|
HealthData: {
|
|
116
|
-
_schema: '
|
|
117
|
-
status: 'Always ok
|
|
116
|
+
_schema: 'Health payload after Miso accepts the application client token.',
|
|
117
|
+
status: 'Always ok after successful application-token validation.'
|
|
118
118
|
},
|
|
119
119
|
ReadyData: {
|
|
120
|
-
_schema: 'Readiness
|
|
120
|
+
_schema: 'Readiness after Miso application-token validation; does not verify Dataplane connectivity.',
|
|
121
121
|
status: 'Always ready when the service accepts traffic.',
|
|
122
122
|
dataplaneUrl: 'Configured dev Dataplane base URL, or null when unset.'
|
|
123
123
|
},
|
|
@@ -68,7 +68,8 @@ function buildStandardErrorResponses() {
|
|
|
68
68
|
408: problemResponseContent('Request exceeded capability timeout.'),
|
|
69
69
|
409: problemResponseContent('Workspace lock held by another in-flight operation.'),
|
|
70
70
|
413: problemResponseContent('Request body exceeds MAX_BODY_BYTES.'),
|
|
71
|
-
500: problemResponseContent('Unexpected server error.')
|
|
71
|
+
500: problemResponseContent('Unexpected server error.'),
|
|
72
|
+
503: problemResponseContent('Miso application authentication failed, token check timed out, or runtime/Controller unavailable. Safe failure category appears in type.')
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
+
const { checkBuilderMisoHealth } = require('./builder-miso-runtime');
|
|
11
12
|
const { buildContext } = require('./route-handlers-context');
|
|
12
13
|
const { listSystems, createSystem, getSystemManifest, deleteSystem } = require('./workspace-ops');
|
|
13
14
|
const { persistSuppliedOpenApi } = require('./persist-supplied-openapi');
|
|
@@ -230,8 +231,8 @@ const HANDLER_MAP = {
|
|
|
230
231
|
getRepositoryStatus: (params, auth) => getRepositoryStatus(buildContext(auth, params.systemKey)),
|
|
231
232
|
runRepositoryDispatchPipeline: (params, auth, body) =>
|
|
232
233
|
runRepositoryDispatchPipeline(buildContext(auth, params.systemKey), body),
|
|
233
|
-
health: () => (
|
|
234
|
-
ready: () => (
|
|
234
|
+
health: () => checkBuilderMisoHealth(),
|
|
235
|
+
ready: () => checkBuilderMisoHealth(true),
|
|
235
236
|
getBuilderHelp: (_params, _auth, _body, query) => handleGetBuilderHelp(query || {}),
|
|
236
237
|
getEvidenceMetadata: (params, auth) =>
|
|
237
238
|
getEvidenceMetadata(buildContext(auth, '_evidence'), params.roleKey),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "
|
|
3
|
+
"$id": "aifabrix://schema/environment-deploy-request.schema.json",
|
|
4
4
|
"title": "Environment Deploy Request",
|
|
5
5
|
"description": "Request body for POST /api/v1/environments/{envKey}/deploy",
|
|
6
6
|
"type": "object",
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"key":"external-datasource-schema",
|
|
8
8
|
"name":"External Data Source Configuration Schema",
|
|
9
9
|
"description":"JSON schema for validating ExternalDataSource configuration files",
|
|
10
|
-
"version":"2.9.
|
|
10
|
+
"version":"2.9.12",
|
|
11
11
|
"type":"schema",
|
|
12
12
|
"category":"integration",
|
|
13
13
|
"author":"AI Fabrix Team",
|
|
14
14
|
"createdAt":"2024-01-01T00:00:00Z",
|
|
15
|
-
"updatedAt":"2026-
|
|
15
|
+
"updatedAt":"2026-09-17T00:00:00Z",
|
|
16
16
|
"compatibility":{
|
|
17
17
|
"minVersion":"1.0.0",
|
|
18
18
|
"maxVersion":"3.0.0",
|
|
@@ -31,6 +31,15 @@
|
|
|
31
31
|
"external-system.schema.json"
|
|
32
32
|
],
|
|
33
33
|
"changelog":[
|
|
34
|
+
{
|
|
35
|
+
"version":"2.9.12",
|
|
36
|
+
"date":"2026-09-17T00:00:00Z",
|
|
37
|
+
"changes":[
|
|
38
|
+
"Added cipOperation.shape enum value 'upsert' (manifest-declared only; composes the datasource's standard create + update via fetch.source datasource; existence-resolved branch with fail-closed per-action permissions). COM writeShape and published x-ai-intent gain the same value. 543 O-SHAPE updated.",
|
|
39
|
+
"Added optional sync.enabled (default true) to disable scheduled, manual, webhook, and syncBridge synchronization without disabling datasource reads. Added @monthly as the authoring default for new full-set schedules; existing schedule-less manifests remain valid and are not migrated. CIP $defs unchanged (543 visual N/A)."
|
|
40
|
+
],
|
|
41
|
+
"breaking":false
|
|
42
|
+
},
|
|
34
43
|
{
|
|
35
44
|
"version":"2.9.11",
|
|
36
45
|
"date":"2026-08-30T00:00:00Z",
|
|
@@ -862,6 +871,11 @@
|
|
|
862
871
|
"type":"object",
|
|
863
872
|
"description":"Record synchronization rules.",
|
|
864
873
|
"properties":{
|
|
874
|
+
"enabled":{
|
|
875
|
+
"type":"boolean",
|
|
876
|
+
"description":"Whether synchronization is enabled for this datasource. False blocks scheduled, manual, webhook, and syncBridge synchronization while preserving normal datasource access.",
|
|
877
|
+
"default":true
|
|
878
|
+
},
|
|
865
879
|
"mode":{
|
|
866
880
|
"type":"string",
|
|
867
881
|
"enum":[
|
|
@@ -874,6 +888,7 @@
|
|
|
874
888
|
"schedule":{
|
|
875
889
|
"type":"string",
|
|
876
890
|
"description":"Cron for full-set (sync all) jobs. Supports 5-field cron syntax or @hourly/@daily/@weekly/@monthly.",
|
|
891
|
+
"default":"@monthly",
|
|
877
892
|
"pattern":"^(@hourly|@daily|@weekly|@monthly|([0-9*/,-]+\\s+){4}[0-9*/,-]+)$"
|
|
878
893
|
},
|
|
879
894
|
"batchSize":{
|
|
@@ -2547,9 +2562,10 @@
|
|
|
2547
2562
|
"enum":[
|
|
2548
2563
|
"create",
|
|
2549
2564
|
"update",
|
|
2550
|
-
"delete"
|
|
2565
|
+
"delete",
|
|
2566
|
+
"upsert"
|
|
2551
2567
|
],
|
|
2552
|
-
"description":"Manifest classification for custom operations when fetch step inference is insufficient. Resolved by resolve_cip_write_shape (precedence over fetch inference) and drives x-ai-intent, x-ai-sideEffects, and x-ai-contract.riskLevel on published OpenAPI. shape delete marks the operation as dangerous for agent policy (with core capability delete)."
|
|
2568
|
+
"description":"Manifest classification for custom operations when fetch step inference is insufficient. Resolved by resolve_cip_write_shape (precedence over fetch inference) and drives x-ai-intent, x-ai-sideEffects, and x-ai-contract.riskLevel on published OpenAPI. shape delete marks the operation as dangerous for agent policy (with core capability delete). shape upsert is never inferred: the operation must compose exactly this datasource's standard create and update operations as two fetch.source 'datasource' steps; at execution the generic CIP branch resolves record existence from validation.repeatingValues reject fields and runs exactly one half, enforcing that half's permissions fail-closed (no cross-action fallback)."
|
|
2553
2569
|
},
|
|
2554
2570
|
"steps":{
|
|
2555
2571
|
"type":"array",
|
|
@@ -3467,4 +3483,3 @@
|
|
|
3467
3483
|
},
|
|
3468
3484
|
"additionalProperties":false
|
|
3469
3485
|
}
|
|
3470
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "
|
|
3
|
+
"$id": "aifabrix://schema/infra-parameter.schema.json",
|
|
4
4
|
"title": "Infra parameter catalog",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"required": ["version", "parameters"],
|
|
@@ -98,7 +98,8 @@ parameters:
|
|
|
98
98
|
category: Redis
|
|
99
99
|
sensitive: true
|
|
100
100
|
valueSource: providerOutput
|
|
101
|
-
|
|
101
|
+
# Not needed at first-time install: Redis is provisioned later (env deploy / TA), not at marketplace bootstrap.
|
|
102
|
+
requiredOnSystemOnboarding: false
|
|
102
103
|
scope: system
|
|
103
104
|
generator:
|
|
104
105
|
type: literal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "
|
|
3
|
+
"$id": "aifabrix://schema/infrastructure-schema.json",
|
|
4
4
|
"title": "AI Fabrix Installation Infrastructure Manifest",
|
|
5
5
|
"description": "Committed, non-secret environment-infra.json for an installation workspace under infrastructure/<resource-group>/. Secrets appear only as valueFromEnv references.",
|
|
6
6
|
"metadata": {
|
|
@@ -507,6 +507,7 @@
|
|
|
507
507
|
"entraIdClientSecret",
|
|
508
508
|
"entraIdTenantId",
|
|
509
509
|
"licenseEdition",
|
|
510
|
+
"capacityProfile",
|
|
510
511
|
"onboardingMode",
|
|
511
512
|
"misoImageTag",
|
|
512
513
|
"keycloakImageTag"
|
|
@@ -981,6 +982,51 @@
|
|
|
981
982
|
}
|
|
982
983
|
}
|
|
983
984
|
},
|
|
985
|
+
"deployRegistryRoleAssignment": {
|
|
986
|
+
"type": "object",
|
|
987
|
+
"required": [
|
|
988
|
+
"value",
|
|
989
|
+
"source",
|
|
990
|
+
"description"
|
|
991
|
+
],
|
|
992
|
+
"additionalProperties": false,
|
|
993
|
+
"properties": {
|
|
994
|
+
"value": {
|
|
995
|
+
"type": [
|
|
996
|
+
"string",
|
|
997
|
+
"boolean",
|
|
998
|
+
"number",
|
|
999
|
+
"object",
|
|
1000
|
+
"array"
|
|
1001
|
+
]
|
|
1002
|
+
},
|
|
1003
|
+
"source": {
|
|
1004
|
+
"type": "string",
|
|
1005
|
+
"minLength": 1
|
|
1006
|
+
},
|
|
1007
|
+
"description": {
|
|
1008
|
+
"type": "string",
|
|
1009
|
+
"minLength": 1
|
|
1010
|
+
},
|
|
1011
|
+
"required": {
|
|
1012
|
+
"type": "boolean"
|
|
1013
|
+
},
|
|
1014
|
+
"restoreValue": {
|
|
1015
|
+
"type": [
|
|
1016
|
+
"string",
|
|
1017
|
+
"boolean",
|
|
1018
|
+
"number",
|
|
1019
|
+
"object",
|
|
1020
|
+
"array"
|
|
1021
|
+
],
|
|
1022
|
+
"description": "Value to restore when credential mode no longer applies."
|
|
1023
|
+
},
|
|
1024
|
+
"restoreSource": {
|
|
1025
|
+
"type": "string",
|
|
1026
|
+
"minLength": 1
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
984
1030
|
"moriBaseUrl": {
|
|
985
1031
|
"type": "object",
|
|
986
1032
|
"required": [
|
|
@@ -1242,6 +1288,98 @@
|
|
|
1242
1288
|
"type": "boolean"
|
|
1243
1289
|
}
|
|
1244
1290
|
}
|
|
1291
|
+
},
|
|
1292
|
+
"moriLicensePublicKey": {
|
|
1293
|
+
"type": "object",
|
|
1294
|
+
"required": [
|
|
1295
|
+
"value",
|
|
1296
|
+
"source",
|
|
1297
|
+
"description"
|
|
1298
|
+
],
|
|
1299
|
+
"additionalProperties": false,
|
|
1300
|
+
"properties": {
|
|
1301
|
+
"value": {
|
|
1302
|
+
"type": [
|
|
1303
|
+
"string",
|
|
1304
|
+
"boolean",
|
|
1305
|
+
"number",
|
|
1306
|
+
"object",
|
|
1307
|
+
"array"
|
|
1308
|
+
]
|
|
1309
|
+
},
|
|
1310
|
+
"source": {
|
|
1311
|
+
"type": "string",
|
|
1312
|
+
"minLength": 1
|
|
1313
|
+
},
|
|
1314
|
+
"description": {
|
|
1315
|
+
"type": "string",
|
|
1316
|
+
"minLength": 1
|
|
1317
|
+
},
|
|
1318
|
+
"required": {
|
|
1319
|
+
"type": "boolean"
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
"moriLicenseIssuer": {
|
|
1324
|
+
"type": "object",
|
|
1325
|
+
"required": [
|
|
1326
|
+
"value",
|
|
1327
|
+
"source",
|
|
1328
|
+
"description"
|
|
1329
|
+
],
|
|
1330
|
+
"additionalProperties": false,
|
|
1331
|
+
"properties": {
|
|
1332
|
+
"value": {
|
|
1333
|
+
"type": [
|
|
1334
|
+
"string",
|
|
1335
|
+
"boolean",
|
|
1336
|
+
"number",
|
|
1337
|
+
"object",
|
|
1338
|
+
"array"
|
|
1339
|
+
]
|
|
1340
|
+
},
|
|
1341
|
+
"source": {
|
|
1342
|
+
"type": "string",
|
|
1343
|
+
"minLength": 1
|
|
1344
|
+
},
|
|
1345
|
+
"description": {
|
|
1346
|
+
"type": "string",
|
|
1347
|
+
"minLength": 1
|
|
1348
|
+
},
|
|
1349
|
+
"required": {
|
|
1350
|
+
"type": "boolean"
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
"capacityProfile": {
|
|
1355
|
+
"type": "object",
|
|
1356
|
+
"required": [
|
|
1357
|
+
"value",
|
|
1358
|
+
"source",
|
|
1359
|
+
"description"
|
|
1360
|
+
],
|
|
1361
|
+
"additionalProperties": false,
|
|
1362
|
+
"properties": {
|
|
1363
|
+
"value": {
|
|
1364
|
+
"enum": [
|
|
1365
|
+
"s",
|
|
1366
|
+
"m",
|
|
1367
|
+
"l",
|
|
1368
|
+
"xl"
|
|
1369
|
+
]
|
|
1370
|
+
},
|
|
1371
|
+
"source": {
|
|
1372
|
+
"type": "string",
|
|
1373
|
+
"minLength": 1
|
|
1374
|
+
},
|
|
1375
|
+
"description": {
|
|
1376
|
+
"type": "string",
|
|
1377
|
+
"minLength": 1
|
|
1378
|
+
},
|
|
1379
|
+
"required": {
|
|
1380
|
+
"type": "boolean"
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1245
1383
|
}
|
|
1246
1384
|
}
|
|
1247
1385
|
},
|
|
@@ -1326,6 +1464,12 @@
|
|
|
1326
1464
|
},
|
|
1327
1465
|
"portalUrl": {
|
|
1328
1466
|
"$ref": "#/definitions/output"
|
|
1467
|
+
},
|
|
1468
|
+
"azureOpenAiCreated": {
|
|
1469
|
+
"$ref": "#/definitions/output"
|
|
1470
|
+
},
|
|
1471
|
+
"azureOpenAiSkipReason": {
|
|
1472
|
+
"$ref": "#/definitions/output"
|
|
1329
1473
|
}
|
|
1330
1474
|
}
|
|
1331
1475
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "
|
|
3
|
+
"$id": "aifabrix://schema/installation-environment.schema.json",
|
|
4
4
|
"title": "AI Fabrix Installation Environment File",
|
|
5
5
|
"description": "Committed, non-secret infrastructure/<resource-group>/environment-dev|tst|pro.json. Inherited shared values name environment-infra.json as their source; request is the exact non-secret Miso deploy projection.",
|
|
6
6
|
"metadata": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "
|
|
3
|
+
"$id": "aifabrix://schema/wizard-config.schema.json",
|
|
4
4
|
"title": "AI Fabrix Wizard Configuration Schema",
|
|
5
5
|
"description": "Schema for validating wizard.yaml configuration files for headless external system creation",
|
|
6
6
|
"type": "object",
|
|
@@ -101,13 +101,8 @@ function createHomeAndSecretsPathFunctions(getConfigFn, saveConfigFn) {
|
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
const resolved = path.resolve(trimmed);
|
|
104
|
-
await setPathConfig(
|
|
105
|
-
|
|
106
|
-
saveConfigFn,
|
|
107
|
-
'aifabrix-work',
|
|
108
|
-
resolved,
|
|
109
|
-
'Work path must be a non-empty string'
|
|
110
|
-
);
|
|
104
|
+
await setPathConfig(getConfigFn, saveConfigFn, 'aifabrix-work', resolved,
|
|
105
|
+
'Work path must be a non-empty string');
|
|
111
106
|
},
|
|
112
107
|
async getAifabrixSecretsPath() {
|
|
113
108
|
return getPathConfig(getConfigFn, 'aifabrix-secrets');
|
|
@@ -168,6 +163,17 @@ async function resolveEnvConfigPathToAbsolute(raw, getConfigFn) {
|
|
|
168
163
|
return path.normalize(path.resolve(base, trimmed));
|
|
169
164
|
}
|
|
170
165
|
|
|
166
|
+
function findBuilderDirectory(base) {
|
|
167
|
+
if (!base) return null;
|
|
168
|
+
const candidate = path.join(base, 'builder');
|
|
169
|
+
try {
|
|
170
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) return candidate;
|
|
171
|
+
} catch {
|
|
172
|
+
/* Missing or inaccessible builder directory. */
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
171
177
|
function createEnvConfigPathFunctions(getConfigFn, saveConfigFn) {
|
|
172
178
|
return {
|
|
173
179
|
/**
|
|
@@ -199,30 +205,16 @@ function createEnvConfigPathFunctions(getConfigFn, saveConfigFn) {
|
|
|
199
205
|
return path.dirname(absolute);
|
|
200
206
|
}
|
|
201
207
|
const pathsMod = require('./paths');
|
|
202
|
-
const
|
|
203
|
-
if (!base) {
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
const b = path.join(base, 'builder');
|
|
207
|
-
try {
|
|
208
|
-
if (fs.existsSync(b) && fs.statSync(b).isDirectory()) {
|
|
209
|
-
return b;
|
|
210
|
-
}
|
|
211
|
-
} catch {
|
|
212
|
-
/* ignore */
|
|
213
|
-
}
|
|
214
|
-
return null;
|
|
215
|
-
};
|
|
216
|
-
const fromProject = tryDir(pathsMod.getProjectRoot());
|
|
208
|
+
const fromProject = findBuilderDirectory(pathsMod.getProjectRoot());
|
|
217
209
|
if (fromProject) {
|
|
218
210
|
return fromProject;
|
|
219
211
|
}
|
|
220
|
-
const fromEffective =
|
|
212
|
+
const fromEffective = findBuilderDirectory(pathsMod.getBuilderRoot());
|
|
221
213
|
if (fromEffective) {
|
|
222
214
|
return fromEffective;
|
|
223
215
|
}
|
|
224
216
|
const work = pathsMod.getAifabrixWork();
|
|
225
|
-
return
|
|
217
|
+
return findBuilderDirectory(work);
|
|
226
218
|
}
|
|
227
219
|
};
|
|
228
220
|
}
|
|
@@ -368,11 +360,10 @@ function applySecretsUrlFromRemote(config) {
|
|
|
368
360
|
config['aifabrix-secrets'] = `${base}/api/dev/secrets`;
|
|
369
361
|
}
|
|
370
362
|
|
|
371
|
-
function
|
|
363
|
+
function applySyncHost(config) {
|
|
372
364
|
const host = hostnameFromUrl(config['remote-server']);
|
|
373
365
|
if (!host) return;
|
|
374
366
|
if (!config['sync-ssh-host']) config['sync-ssh-host'] = host;
|
|
375
|
-
if (!config['docker-endpoint']) config['docker-endpoint'] = `tcp://${host}:2376`;
|
|
376
367
|
}
|
|
377
368
|
|
|
378
369
|
async function mergeRemoteSettingsImpl(getConfigFn, saveConfigFn, settings) {
|
|
@@ -386,8 +377,13 @@ async function mergeRemoteSettingsImpl(getConfigFn, saveConfigFn, settings) {
|
|
|
386
377
|
if (value === '') continue;
|
|
387
378
|
config[key] = value;
|
|
388
379
|
}
|
|
380
|
+
// Current settings are authoritative: never inherit Docker access from another identity.
|
|
381
|
+
if (typeof settings['docker-endpoint'] !== 'string' || !settings['docker-endpoint'].trim()) {
|
|
382
|
+
delete config['docker-endpoint'];
|
|
383
|
+
delete config['docker-tls-skip-verify'];
|
|
384
|
+
}
|
|
389
385
|
applySecretsUrlFromRemote(config);
|
|
390
|
-
|
|
386
|
+
applySyncHost(config);
|
|
391
387
|
await saveConfigFn(config);
|
|
392
388
|
}
|
|
393
389
|
|
|
@@ -428,7 +424,6 @@ module.exports = {
|
|
|
428
424
|
resolveEnvConfigPathToAbsolute,
|
|
429
425
|
SETTINGS_RESPONSE_KEYS,
|
|
430
426
|
applySecretsUrlFromRemote,
|
|
431
|
-
applySyncAndDockerFromHost,
|
|
427
|
+
applySyncAndDockerFromHost: applySyncHost,
|
|
432
428
|
hostnameFromUrl
|
|
433
429
|
};
|
|
434
|
-
|
|
@@ -14,13 +14,15 @@ const { getRuntimeConfigFile } = require('../core/config-runtime-paths');
|
|
|
14
14
|
|
|
15
15
|
const REGISTRY_CONFIG_KEY = 'registry';
|
|
16
16
|
const HOST_LABEL = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?';
|
|
17
|
-
const REGISTRY_HOST_RE = new RegExp(`^${HOST_LABEL}(?:\\.${HOST_LABEL})
|
|
17
|
+
const REGISTRY_HOST_RE = new RegExp(`^${HOST_LABEL}(?:\\.${HOST_LABEL})*$`);
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Normalizes and validates a registry host
|
|
21
|
-
* Rejects schemes, paths, embedded credentials, query/fragment text and whitespace.
|
|
20
|
+
* Normalizes and validates a registry host.
|
|
21
|
+
* Rejects schemes, paths, embedded credentials, query/fragment text and whitespace. A port is
|
|
22
|
+
* rejected because the deployment `image` contract (application-schema.json, shared with the
|
|
23
|
+
* Controller) accepts only one colon, which separates the tag.
|
|
22
24
|
* @param {string} value - Raw registry value
|
|
23
|
-
* @returns {string} Lower-case host
|
|
25
|
+
* @returns {string} Lower-case host
|
|
24
26
|
* @throws {Error} When the value is empty or not a plain host
|
|
25
27
|
*/
|
|
26
28
|
function normalizeRegistryHost(value) {
|
|
@@ -33,8 +35,13 @@ function normalizeRegistryHost(value) {
|
|
|
33
35
|
throw new Error(`Invalid registry "${raw}": use a host name such as myregistry.azurecr.io (no scheme, path or credentials)`);
|
|
34
36
|
}
|
|
35
37
|
const host = withoutSlash.toLowerCase();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
if (host.includes(':')) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Invalid registry "${raw}": a port is not supported because the deployed image reference keeps one colon for the tag. ` +
|
|
41
|
+
'Use a host name such as myregistry.azurecr.io.'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (!REGISTRY_HOST_RE.test(host)) {
|
|
38
45
|
throw new Error(`Invalid registry "${raw}": use a host name such as myregistry.azurecr.io`);
|
|
39
46
|
}
|
|
40
47
|
return host;
|