@aifabrix/builder 2.32.1 → 2.32.3
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/api/index.js +10 -5
- package/lib/api/wizard.api.js +52 -21
- package/lib/app/list.js +62 -28
- package/lib/app/prompts.js +9 -5
- package/lib/app/rotate-secret.js +2 -1
- package/lib/cli.js +33 -4
- package/lib/commands/auth-status.js +262 -0
- package/lib/commands/login-device.js +17 -12
- package/lib/commands/login.js +16 -9
- package/lib/commands/wizard.js +63 -69
- package/lib/datasource/deploy.js +5 -2
- package/lib/external-system/deploy.js +3 -2
- package/lib/external-system/download.js +2 -1
- package/lib/external-system/test-auth.js +2 -1
- package/lib/schema/application-schema.json +1 -1
- package/lib/schema/external-datasource.schema.json +314 -18
- package/lib/schema/external-system.schema.json +2 -2
- package/lib/utils/api.js +20 -7
- package/lib/utils/app-register-display.js +2 -1
- package/lib/utils/cli-utils.js +3 -1
- package/lib/utils/controller-url.js +67 -0
- package/lib/utils/env-map.js +2 -1
- package/lib/utils/error-formatter.js +100 -28
- package/lib/utils/token-manager.js +60 -0
- package/lib/validation/validator.js +2 -1
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +2 -2
- package/templates/external-system/external-system.json.hbs +1 -1
|
@@ -22,6 +22,7 @@ const { detectAppType } = require('../utils/paths');
|
|
|
22
22
|
const logger = require('../utils/logger');
|
|
23
23
|
const { generateEnvTemplate } = require('../utils/external-system-env-helpers');
|
|
24
24
|
const { generateVariablesYaml, generateReadme } = require('./download-helpers');
|
|
25
|
+
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Validates system type from downloaded application
|
|
@@ -126,7 +127,7 @@ function handlePartialDownload(systemKey, systemData, datasourceErrors) {
|
|
|
126
127
|
*/
|
|
127
128
|
async function setupAuthenticationAndDataplane(systemKey, options, config) {
|
|
128
129
|
const environment = options.environment || 'dev';
|
|
129
|
-
const controllerUrl = options
|
|
130
|
+
const controllerUrl = await resolveControllerUrl(options, config);
|
|
130
131
|
const authConfig = await getDeploymentAuth(controllerUrl, environment, systemKey);
|
|
131
132
|
|
|
132
133
|
if (!authConfig.token && !authConfig.clientId) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const { getDeploymentAuth } = require('../utils/token-manager');
|
|
12
12
|
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
13
|
+
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Setup authentication and get dataplane URL for integration tests
|
|
@@ -22,7 +23,7 @@ const { getDataplaneUrl } = require('../datasource/deploy');
|
|
|
22
23
|
*/
|
|
23
24
|
async function setupIntegrationTestAuth(appName, options, config) {
|
|
24
25
|
const environment = options.environment || 'dev';
|
|
25
|
-
const controllerUrl = options
|
|
26
|
+
const controllerUrl = await resolveControllerUrl(options, config);
|
|
26
27
|
const authConfig = await getDeploymentAuth(controllerUrl, environment, appName);
|
|
27
28
|
|
|
28
29
|
if (!authConfig.token && !authConfig.clientId) {
|
|
@@ -3,21 +3,21 @@
|
|
|
3
3
|
"$id":"https://raw.githubusercontent.com/esystemsdev/aifabrix-builder/refs/heads/main/lib/schema/external-datasource.schema.json",
|
|
4
4
|
"title":"External Data Source",
|
|
5
5
|
"description":"Configuration for AI Fabrix ExternalDataSource entities. Includes metadata schema, data dimensions, transformation mappings, OpenAPI/MCP exposure, execution logic, and sync behavior.",
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
"metadata":{
|
|
7
|
+
"key":"external-datasource-schema",
|
|
8
|
+
"name":"External Data Source Configuration Schema",
|
|
9
|
+
"description":"JSON schema for validating ExternalDataSource configuration files",
|
|
10
|
+
"version":"2.1.0",
|
|
11
|
+
"type":"schema",
|
|
12
|
+
"category":"integration",
|
|
13
|
+
"author":"AI Fabrix Team",
|
|
14
|
+
"createdAt":"2024-01-01T00:00:00Z",
|
|
15
|
+
"updatedAt":"2026-01-19T00:00:00Z",
|
|
16
|
+
"compatibility":{
|
|
17
|
+
"minVersion":"1.0.0",
|
|
18
|
+
"maxVersion":"3.0.0",
|
|
19
|
+
"deprecated":false
|
|
20
|
+
},
|
|
21
21
|
"tags":[
|
|
22
22
|
"schema",
|
|
23
23
|
"external-datasource",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
"version":"2.0.0",
|
|
73
|
-
"date":"2026-01-
|
|
73
|
+
"date":"2026-01-18T00:00:00Z",
|
|
74
74
|
"changes":[
|
|
75
75
|
"BREAKING: Renamed fieldMappings.accessFields (array) to fieldMappings.dimensions (object)",
|
|
76
76
|
"BREAKING: Renamed fieldMappings.fields to fieldMappings.attributes",
|
|
@@ -80,6 +80,17 @@
|
|
|
80
80
|
"Updated descriptions to reflect dimension-first data model approach"
|
|
81
81
|
],
|
|
82
82
|
"breaking":true
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"version":"2.1.0",
|
|
86
|
+
"date":"2026-01-19T00:00:00Z",
|
|
87
|
+
"changes":[
|
|
88
|
+
"Added error semantics (onError object) to all CIP step types with error classification, retry, compensation, and failPipeline",
|
|
89
|
+
"Added idempotency configuration to CIP definition for execution-level replay guarantees",
|
|
90
|
+
"Added lineage configuration to field mappings for field-level explainability and compliance",
|
|
91
|
+
"Added contract versioning configuration to datasource root for CI/CD safety and agent stability"
|
|
92
|
+
],
|
|
93
|
+
"breaking":false
|
|
83
94
|
}
|
|
84
95
|
]
|
|
85
96
|
},
|
|
@@ -223,6 +234,9 @@
|
|
|
223
234
|
}
|
|
224
235
|
},
|
|
225
236
|
"additionalProperties":false
|
|
237
|
+
},
|
|
238
|
+
"lineage":{
|
|
239
|
+
"$ref":"#/$defs/fieldMappingLineage"
|
|
226
240
|
}
|
|
227
241
|
}
|
|
228
242
|
}
|
|
@@ -846,6 +860,9 @@
|
|
|
846
860
|
}
|
|
847
861
|
},
|
|
848
862
|
"additionalProperties":true
|
|
863
|
+
},
|
|
864
|
+
"contract":{
|
|
865
|
+
"$ref":"#/$defs/contractConfig"
|
|
849
866
|
}
|
|
850
867
|
},
|
|
851
868
|
"$defs":{
|
|
@@ -931,6 +948,9 @@
|
|
|
931
948
|
}
|
|
932
949
|
},
|
|
933
950
|
"additionalProperties":false
|
|
951
|
+
},
|
|
952
|
+
"idempotency":{
|
|
953
|
+
"$ref":"#/$defs/idempotencyConfig"
|
|
934
954
|
}
|
|
935
955
|
},
|
|
936
956
|
"additionalProperties":false
|
|
@@ -1024,7 +1044,10 @@
|
|
|
1024
1044
|
"type":"string",
|
|
1025
1045
|
"enum":[
|
|
1026
1046
|
"GET",
|
|
1027
|
-
"POST"
|
|
1047
|
+
"POST",
|
|
1048
|
+
"PUT",
|
|
1049
|
+
"PATCH",
|
|
1050
|
+
"DELETE"
|
|
1028
1051
|
],
|
|
1029
1052
|
"description":"Explicit HTTP method when source='http'."
|
|
1030
1053
|
},
|
|
@@ -1045,8 +1068,15 @@
|
|
|
1045
1068
|
}
|
|
1046
1069
|
},
|
|
1047
1070
|
"bodyTemplate":{
|
|
1048
|
-
"description":"Optional static request body or template for POST/PUT fetches.",
|
|
1071
|
+
"description":"Optional static request body or template for POST/PUT/PATCH fetches.",
|
|
1049
1072
|
"type":["object","string","null"]
|
|
1073
|
+
},
|
|
1074
|
+
"headers":{
|
|
1075
|
+
"type":"object",
|
|
1076
|
+
"description":"Optional HTTP headers for the request.",
|
|
1077
|
+
"additionalProperties":{
|
|
1078
|
+
"type":"string"
|
|
1079
|
+
}
|
|
1050
1080
|
}
|
|
1051
1081
|
},
|
|
1052
1082
|
"allOf":[
|
|
@@ -1093,6 +1123,14 @@
|
|
|
1093
1123
|
},
|
|
1094
1124
|
"lineage":{
|
|
1095
1125
|
"$ref":"#/$defs/cipLineage"
|
|
1126
|
+
},
|
|
1127
|
+
"stepId":{
|
|
1128
|
+
"type":"string",
|
|
1129
|
+
"pattern":"^[a-z0-9-_]+$",
|
|
1130
|
+
"description":"Unique identifier for this step within the operation"
|
|
1131
|
+
},
|
|
1132
|
+
"onError":{
|
|
1133
|
+
"$ref":"#/$defs/cipOnErrorConfig"
|
|
1096
1134
|
}
|
|
1097
1135
|
},
|
|
1098
1136
|
"additionalProperties":false
|
|
@@ -1150,6 +1188,14 @@
|
|
|
1150
1188
|
},
|
|
1151
1189
|
"lineage":{
|
|
1152
1190
|
"$ref":"#/$defs/cipLineage"
|
|
1191
|
+
},
|
|
1192
|
+
"stepId":{
|
|
1193
|
+
"type":"string",
|
|
1194
|
+
"pattern":"^[a-z0-9-_]+$",
|
|
1195
|
+
"description":"Unique identifier for this step within the operation"
|
|
1196
|
+
},
|
|
1197
|
+
"onError":{
|
|
1198
|
+
"$ref":"#/$defs/cipOnErrorConfig"
|
|
1153
1199
|
}
|
|
1154
1200
|
},
|
|
1155
1201
|
"additionalProperties":false
|
|
@@ -1187,6 +1233,14 @@
|
|
|
1187
1233
|
},
|
|
1188
1234
|
"lineage":{
|
|
1189
1235
|
"$ref":"#/$defs/cipLineage"
|
|
1236
|
+
},
|
|
1237
|
+
"stepId":{
|
|
1238
|
+
"type":"string",
|
|
1239
|
+
"pattern":"^[a-z0-9-_]+$",
|
|
1240
|
+
"description":"Unique identifier for this step within the operation"
|
|
1241
|
+
},
|
|
1242
|
+
"onError":{
|
|
1243
|
+
"$ref":"#/$defs/cipOnErrorConfig"
|
|
1190
1244
|
}
|
|
1191
1245
|
},
|
|
1192
1246
|
"additionalProperties":false
|
|
@@ -1226,6 +1280,14 @@
|
|
|
1226
1280
|
},
|
|
1227
1281
|
"lineage":{
|
|
1228
1282
|
"$ref":"#/$defs/cipLineage"
|
|
1283
|
+
},
|
|
1284
|
+
"stepId":{
|
|
1285
|
+
"type":"string",
|
|
1286
|
+
"pattern":"^[a-z0-9-_]+$",
|
|
1287
|
+
"description":"Unique identifier for this step within the operation"
|
|
1288
|
+
},
|
|
1289
|
+
"onError":{
|
|
1290
|
+
"$ref":"#/$defs/cipOnErrorConfig"
|
|
1229
1291
|
}
|
|
1230
1292
|
},
|
|
1231
1293
|
"additionalProperties":false
|
|
@@ -1262,6 +1324,14 @@
|
|
|
1262
1324
|
},
|
|
1263
1325
|
"lineage":{
|
|
1264
1326
|
"$ref":"#/$defs/cipLineage"
|
|
1327
|
+
},
|
|
1328
|
+
"stepId":{
|
|
1329
|
+
"type":"string",
|
|
1330
|
+
"pattern":"^[a-z0-9-_]+$",
|
|
1331
|
+
"description":"Unique identifier for this step within the operation"
|
|
1332
|
+
},
|
|
1333
|
+
"onError":{
|
|
1334
|
+
"$ref":"#/$defs/cipOnErrorConfig"
|
|
1265
1335
|
}
|
|
1266
1336
|
},
|
|
1267
1337
|
"additionalProperties":false
|
|
@@ -1288,6 +1358,232 @@
|
|
|
1288
1358
|
},
|
|
1289
1359
|
"lineage":{
|
|
1290
1360
|
"$ref":"#/$defs/cipLineage"
|
|
1361
|
+
},
|
|
1362
|
+
"stepId":{
|
|
1363
|
+
"type":"string",
|
|
1364
|
+
"pattern":"^[a-z0-9-_]+$",
|
|
1365
|
+
"description":"Unique identifier for this step within the operation"
|
|
1366
|
+
},
|
|
1367
|
+
"onError":{
|
|
1368
|
+
"$ref":"#/$defs/cipOnErrorConfig"
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
"additionalProperties":false
|
|
1372
|
+
},
|
|
1373
|
+
"cipRetryConfig":{
|
|
1374
|
+
"type":"object",
|
|
1375
|
+
"description":"Retry configuration for error handling.",
|
|
1376
|
+
"properties":{
|
|
1377
|
+
"attempts":{
|
|
1378
|
+
"type":"integer",
|
|
1379
|
+
"minimum":1,
|
|
1380
|
+
"maximum":10,
|
|
1381
|
+
"default":3,
|
|
1382
|
+
"description":"Maximum number of retry attempts"
|
|
1383
|
+
},
|
|
1384
|
+
"backoff":{
|
|
1385
|
+
"type":"string",
|
|
1386
|
+
"enum":["exponential","linear","fixed"],
|
|
1387
|
+
"default":"exponential",
|
|
1388
|
+
"description":"Backoff strategy: exponential (2^attempt), linear (attempt * base), fixed (constant)"
|
|
1389
|
+
},
|
|
1390
|
+
"maxDelaySeconds":{
|
|
1391
|
+
"type":"number",
|
|
1392
|
+
"minimum":1.0,
|
|
1393
|
+
"maximum":300.0,
|
|
1394
|
+
"default":60.0,
|
|
1395
|
+
"description":"Maximum delay between retries in seconds"
|
|
1396
|
+
}
|
|
1397
|
+
},
|
|
1398
|
+
"additionalProperties":false
|
|
1399
|
+
},
|
|
1400
|
+
"cipCompensationConfig":{
|
|
1401
|
+
"type":"object",
|
|
1402
|
+
"description":"Compensation (rollback) configuration for error handling.",
|
|
1403
|
+
"properties":{
|
|
1404
|
+
"enabled":{
|
|
1405
|
+
"type":"boolean",
|
|
1406
|
+
"default":false,
|
|
1407
|
+
"description":"Whether compensation is enabled"
|
|
1408
|
+
},
|
|
1409
|
+
"stepRef":{
|
|
1410
|
+
"type":"string",
|
|
1411
|
+
"pattern":"^[a-z0-9-_]+$",
|
|
1412
|
+
"description":"Reference to a previously executed step by stepId to execute as compensation"
|
|
1413
|
+
}
|
|
1414
|
+
},
|
|
1415
|
+
"additionalProperties":false
|
|
1416
|
+
},
|
|
1417
|
+
"cipOnErrorConfig":{
|
|
1418
|
+
"type":"object",
|
|
1419
|
+
"description":"Enhanced error handling configuration for CIP steps.",
|
|
1420
|
+
"required":["class"],
|
|
1421
|
+
"properties":{
|
|
1422
|
+
"class":{
|
|
1423
|
+
"type":"string",
|
|
1424
|
+
"enum":["transient","permanent","validation","auth"],
|
|
1425
|
+
"description":"Error classification: transient (retryable), permanent (non-retryable), validation (data validation failure), auth (authentication/authorization failure)"
|
|
1426
|
+
},
|
|
1427
|
+
"retry":{
|
|
1428
|
+
"$ref":"#/$defs/cipRetryConfig",
|
|
1429
|
+
"description":"Retry configuration (only applies when class='transient')"
|
|
1430
|
+
},
|
|
1431
|
+
"compensate":{
|
|
1432
|
+
"$ref":"#/$defs/cipCompensationConfig",
|
|
1433
|
+
"description":"Compensation (rollback) configuration"
|
|
1434
|
+
},
|
|
1435
|
+
"failPipeline":{
|
|
1436
|
+
"type":"boolean",
|
|
1437
|
+
"default":true,
|
|
1438
|
+
"description":"Whether step failure should fail the entire pipeline (false allows partial success)"
|
|
1439
|
+
}
|
|
1440
|
+
},
|
|
1441
|
+
"additionalProperties":false
|
|
1442
|
+
},
|
|
1443
|
+
"idempotencyConfig":{
|
|
1444
|
+
"type":"object",
|
|
1445
|
+
"description":"Idempotency configuration for CIP execution.",
|
|
1446
|
+
"required":["key"],
|
|
1447
|
+
"properties":{
|
|
1448
|
+
"key":{
|
|
1449
|
+
"type":"string",
|
|
1450
|
+
"description":"Idempotency key template. Supports variables: {{actor.userId}}, {{raw.requestId}}, {{correlationId}}, {{execution.operation}}, {{execution.datasourceId}}"
|
|
1451
|
+
},
|
|
1452
|
+
"scope":{
|
|
1453
|
+
"type":"string",
|
|
1454
|
+
"enum":["datasource","system"],
|
|
1455
|
+
"default":"datasource",
|
|
1456
|
+
"description":"Idempotency scope: datasource (same key within datasource) or system (same key across all datasources)"
|
|
1457
|
+
},
|
|
1458
|
+
"ttlSeconds":{
|
|
1459
|
+
"type":"integer",
|
|
1460
|
+
"minimum":1,
|
|
1461
|
+
"maximum":604800,
|
|
1462
|
+
"default":86400,
|
|
1463
|
+
"description":"Time to live for idempotency records in seconds (default: 86400 = 24 hours)"
|
|
1464
|
+
},
|
|
1465
|
+
"enforcement":{
|
|
1466
|
+
"type":"string",
|
|
1467
|
+
"enum":["strict","best-effort"],
|
|
1468
|
+
"default":"strict",
|
|
1469
|
+
"description":"Enforcement mode: strict (return previous result, no execution) or best-effort (warn + continue)"
|
|
1470
|
+
}
|
|
1471
|
+
},
|
|
1472
|
+
"additionalProperties":false
|
|
1473
|
+
},
|
|
1474
|
+
"transformationRule":{
|
|
1475
|
+
"type":"object",
|
|
1476
|
+
"description":"Transformation rule applied to a field.",
|
|
1477
|
+
"required":["type"],
|
|
1478
|
+
"properties":{
|
|
1479
|
+
"type":{
|
|
1480
|
+
"type":"string",
|
|
1481
|
+
"description":"Type of transformation: 'mapping', 'function', 'filter', 'aggregate'"
|
|
1482
|
+
},
|
|
1483
|
+
"function":{
|
|
1484
|
+
"type":"string",
|
|
1485
|
+
"description":"Function name (e.g., 'mapStatus', 'toLower', 'trim')"
|
|
1486
|
+
},
|
|
1487
|
+
"rule":{
|
|
1488
|
+
"type":"string",
|
|
1489
|
+
"description":"Human-readable rule description (e.g., 'open → OPEN, closed → CLOSED')"
|
|
1490
|
+
}
|
|
1491
|
+
},
|
|
1492
|
+
"additionalProperties":false
|
|
1493
|
+
},
|
|
1494
|
+
"fieldMappingLineage":{
|
|
1495
|
+
"type":"object",
|
|
1496
|
+
"description":"Lineage metadata for a field mapping.",
|
|
1497
|
+
"required":["sourceFields"],
|
|
1498
|
+
"properties":{
|
|
1499
|
+
"sourceFields":{
|
|
1500
|
+
"type":"array",
|
|
1501
|
+
"minItems":1,
|
|
1502
|
+
"items":{
|
|
1503
|
+
"type":"string"
|
|
1504
|
+
},
|
|
1505
|
+
"description":"Source field paths that contribute to this mapped field (e.g., ['raw.fields.state'])"
|
|
1506
|
+
},
|
|
1507
|
+
"transformations":{
|
|
1508
|
+
"type":"array",
|
|
1509
|
+
"items":{
|
|
1510
|
+
"$ref":"#/$defs/transformationRule"
|
|
1511
|
+
},
|
|
1512
|
+
"description":"List of transformations applied to source fields"
|
|
1513
|
+
},
|
|
1514
|
+
"confidence":{
|
|
1515
|
+
"type":"number",
|
|
1516
|
+
"minimum":0.0,
|
|
1517
|
+
"maximum":1.0,
|
|
1518
|
+
"default":1.0,
|
|
1519
|
+
"description":"Confidence score for this mapping (0.0-1.0). Default: 1.0 for deterministic transformations."
|
|
1520
|
+
}
|
|
1521
|
+
},
|
|
1522
|
+
"additionalProperties":false
|
|
1523
|
+
},
|
|
1524
|
+
"compatibilityConfig":{
|
|
1525
|
+
"type":"object",
|
|
1526
|
+
"description":"Compatibility configuration for contract versioning.",
|
|
1527
|
+
"properties":{
|
|
1528
|
+
"backwardCompatible":{
|
|
1529
|
+
"type":"boolean",
|
|
1530
|
+
"default":true,
|
|
1531
|
+
"description":"Whether this version is backward compatible with previous versions"
|
|
1532
|
+
},
|
|
1533
|
+
"breakingChanges":{
|
|
1534
|
+
"type":"array",
|
|
1535
|
+
"items":{
|
|
1536
|
+
"type":"string"
|
|
1537
|
+
},
|
|
1538
|
+
"description":"List of breaking change types: removeField, changeType, changeSemantics"
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
"additionalProperties":false
|
|
1542
|
+
},
|
|
1543
|
+
"deprecationConfig":{
|
|
1544
|
+
"type":"object",
|
|
1545
|
+
"description":"Deprecation configuration for contract versioning.",
|
|
1546
|
+
"properties":{
|
|
1547
|
+
"sunsetDate":{
|
|
1548
|
+
"type":"string",
|
|
1549
|
+
"pattern":"^\\d{4}-\\d{2}-\\d{2}$",
|
|
1550
|
+
"description":"Date when this contract version will be sunset (YYYY-MM-DD)"
|
|
1551
|
+
},
|
|
1552
|
+
"replacedBy":{
|
|
1553
|
+
"type":"string",
|
|
1554
|
+
"description":"Contract name/version that replaces this deprecated version"
|
|
1555
|
+
}
|
|
1556
|
+
},
|
|
1557
|
+
"additionalProperties":false
|
|
1558
|
+
},
|
|
1559
|
+
"contractConfig":{
|
|
1560
|
+
"type":"object",
|
|
1561
|
+
"description":"Contract versioning configuration for datasource.",
|
|
1562
|
+
"required":["name","version"],
|
|
1563
|
+
"properties":{
|
|
1564
|
+
"name":{
|
|
1565
|
+
"type":"string",
|
|
1566
|
+
"pattern":"^[a-z0-9.-]+$",
|
|
1567
|
+
"description":"Contract identifier (e.g., 'jira.issue', separate from datasource key)"
|
|
1568
|
+
},
|
|
1569
|
+
"version":{
|
|
1570
|
+
"type":"string",
|
|
1571
|
+
"pattern":"^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
|
1572
|
+
"description":"Semantic version of the contract (e.g., '2.1.0')"
|
|
1573
|
+
},
|
|
1574
|
+
"status":{
|
|
1575
|
+
"type":"string",
|
|
1576
|
+
"enum":["stable","deprecated","experimental"],
|
|
1577
|
+
"default":"stable",
|
|
1578
|
+
"description":"Contract status: stable (production-ready), deprecated (being phased out), experimental (testing)"
|
|
1579
|
+
},
|
|
1580
|
+
"compatibility":{
|
|
1581
|
+
"$ref":"#/$defs/compatibilityConfig",
|
|
1582
|
+
"description":"Compatibility information for this contract version"
|
|
1583
|
+
},
|
|
1584
|
+
"deprecation":{
|
|
1585
|
+
"$ref":"#/$defs/deprecationConfig",
|
|
1586
|
+
"description":"Deprecation information if status is 'deprecated'"
|
|
1291
1587
|
}
|
|
1292
1588
|
},
|
|
1293
1589
|
"additionalProperties":false
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"category": "integration",
|
|
13
13
|
"author": "AI Fabrix Team",
|
|
14
14
|
"createdAt": "2024-01-01T00:00:00Z",
|
|
15
|
-
"updatedAt": "
|
|
15
|
+
"updatedAt": "2025-12-01T00:00:00Z",
|
|
16
16
|
"compatibility": {
|
|
17
17
|
"minVersion": "1.0.0",
|
|
18
18
|
"maxVersion": "2.0.0",
|
|
@@ -314,7 +314,7 @@
|
|
|
314
314
|
"minLength": 1,
|
|
315
315
|
"maxLength": 500
|
|
316
316
|
},
|
|
317
|
-
"
|
|
317
|
+
"groups": {
|
|
318
318
|
"type": "array",
|
|
319
319
|
"description": "Azure AD groups mapped to this role",
|
|
320
320
|
"items": {
|
package/lib/utils/api.js
CHANGED
|
@@ -209,10 +209,20 @@ function extractControllerUrl(url) {
|
|
|
209
209
|
* Automatically refreshes device token on 401 errors if refresh token is available
|
|
210
210
|
* @param {string} url - API endpoint URL
|
|
211
211
|
* @param {Object} options - Fetch options
|
|
212
|
-
* @param {string}
|
|
212
|
+
* @param {string|Object} tokenOrAuthConfig - Bearer token string or authConfig object
|
|
213
|
+
* @param {string} [tokenOrAuthConfig.token] - Bearer token (if object)
|
|
214
|
+
* @param {string} [tokenOrAuthConfig.controller] - Controller URL for token refresh (if object)
|
|
213
215
|
* @returns {Promise<Object>} Response object
|
|
214
216
|
*/
|
|
215
|
-
async function authenticatedApiCall(url, options = {},
|
|
217
|
+
async function authenticatedApiCall(url, options = {}, tokenOrAuthConfig) {
|
|
218
|
+
// Support both string token (backward compat) and authConfig object
|
|
219
|
+
const token = typeof tokenOrAuthConfig === 'string'
|
|
220
|
+
? tokenOrAuthConfig
|
|
221
|
+
: tokenOrAuthConfig?.token;
|
|
222
|
+
const authControllerUrl = typeof tokenOrAuthConfig === 'object'
|
|
223
|
+
? tokenOrAuthConfig?.controller
|
|
224
|
+
: null;
|
|
225
|
+
|
|
216
226
|
const headers = {
|
|
217
227
|
'Content-Type': 'application/json',
|
|
218
228
|
...options.headers
|
|
@@ -230,12 +240,15 @@ async function authenticatedApiCall(url, options = {}, token) {
|
|
|
230
240
|
// Handle 401 errors with automatic token refresh for device tokens
|
|
231
241
|
if (!response.success && response.status === 401) {
|
|
232
242
|
try {
|
|
233
|
-
//
|
|
234
|
-
|
|
243
|
+
// Use controller URL from authConfig if available, otherwise extract from request URL
|
|
244
|
+
// This is important when the request URL is a dataplane URL but the token
|
|
245
|
+
// is stored under the controller URL
|
|
246
|
+
const controllerUrl = authControllerUrl || extractControllerUrl(url);
|
|
235
247
|
|
|
236
|
-
// Try to
|
|
237
|
-
|
|
238
|
-
const
|
|
248
|
+
// Try to force refresh device token on 401 (regardless of local expiry time)
|
|
249
|
+
// because the server rejected the token
|
|
250
|
+
const { forceRefreshDeviceToken } = require('./token-manager');
|
|
251
|
+
const refreshedToken = await forceRefreshDeviceToken(controllerUrl);
|
|
239
252
|
|
|
240
253
|
if (refreshedToken && refreshedToken.token) {
|
|
241
254
|
// Retry request with new token
|
|
@@ -48,7 +48,8 @@ function displayRegistrationResults(data, apiUrl, environment) {
|
|
|
48
48
|
logger.log(chalk.bold('📋 Application Details:'));
|
|
49
49
|
logger.log(` ID: ${data.application.id}`);
|
|
50
50
|
logger.log(` Key: ${data.application.key}`);
|
|
51
|
-
logger.log(` Display Name: ${data.application.displayName}
|
|
51
|
+
logger.log(` Display Name: ${data.application.displayName}`);
|
|
52
|
+
logger.log(` Controller: ${apiUrl}\n`);
|
|
52
53
|
|
|
53
54
|
logger.log(chalk.bold.yellow('🔑 CREDENTIALS (save these immediately):'));
|
|
54
55
|
logger.log(chalk.yellow(` Client ID: ${data.credentials.clientId}`));
|
package/lib/utils/cli-utils.js
CHANGED
|
@@ -135,7 +135,9 @@ function formatAzureError(errorMsg) {
|
|
|
135
135
|
' Use format: *.azurecr.io (e.g., myacr.azurecr.io)'
|
|
136
136
|
];
|
|
137
137
|
}
|
|
138
|
-
|
|
138
|
+
// Only match ACR-specific authentication errors, not general authentication failures
|
|
139
|
+
if (errorMsg.includes('ACR') || errorMsg.includes('azurecr.io') ||
|
|
140
|
+
(errorMsg.includes('authenticate') && (errorMsg.includes('registry') || errorMsg.includes('container')))) {
|
|
139
141
|
return [
|
|
140
142
|
' Azure Container Registry authentication failed.',
|
|
141
143
|
' Run: az acr login --name <registry-name>',
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controller URL Resolution Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for resolving controller URLs with developer ID-based defaults
|
|
5
|
+
* and fallback chain support.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Controller URL resolution utilities
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { getDeveloperIdNumber } = require('./env-map');
|
|
13
|
+
const devConfig = require('./dev-config');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Calculate default controller URL based on developer ID
|
|
17
|
+
* Uses getDevPorts to get the app port which is adjusted by developer ID
|
|
18
|
+
* Developer ID 0 = http://localhost:3000
|
|
19
|
+
* Developer ID 1 = http://localhost:3100
|
|
20
|
+
* Developer ID 2 = http://localhost:3200
|
|
21
|
+
* @async
|
|
22
|
+
* @function getDefaultControllerUrl
|
|
23
|
+
* @returns {Promise<string>} Default controller URL
|
|
24
|
+
*/
|
|
25
|
+
async function getDefaultControllerUrl() {
|
|
26
|
+
const developerId = await getDeveloperIdNumber(null);
|
|
27
|
+
const ports = devConfig.getDevPorts(developerId);
|
|
28
|
+
return `http://localhost:${ports.app}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve controller URL with fallback chain
|
|
33
|
+
* Priority:
|
|
34
|
+
* 1. options.controller (explicit option)
|
|
35
|
+
* 2. config.deployment?.controllerUrl (from config)
|
|
36
|
+
* 3. getDefaultControllerUrl() (developer ID-based default)
|
|
37
|
+
* @async
|
|
38
|
+
* @function resolveControllerUrl
|
|
39
|
+
* @param {Object} options - Command options
|
|
40
|
+
* @param {string} [options.controller] - Explicit controller URL option
|
|
41
|
+
* @param {Object} config - Configuration object
|
|
42
|
+
* @param {Object} [config.deployment] - Deployment configuration
|
|
43
|
+
* @param {string} [config.deployment.controllerUrl] - Controller URL from config
|
|
44
|
+
* @returns {Promise<string>} Resolved controller URL
|
|
45
|
+
*/
|
|
46
|
+
async function resolveControllerUrl(options, config) {
|
|
47
|
+
// Priority 1: Explicit option
|
|
48
|
+
if (options && (options.controller || options.url)) {
|
|
49
|
+
const explicitUrl = options.controller || options.url;
|
|
50
|
+
if (explicitUrl) {
|
|
51
|
+
return explicitUrl.replace(/\/$/, '');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Priority 2: Config file
|
|
56
|
+
if (config?.deployment?.controllerUrl) {
|
|
57
|
+
return config.deployment.controllerUrl.replace(/\/$/, '');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Priority 3: Developer ID-based default
|
|
61
|
+
return await getDefaultControllerUrl();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
getDefaultControllerUrl,
|
|
66
|
+
resolveControllerUrl
|
|
67
|
+
};
|