@appilots/cli 0.4.0 → 0.11.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/dist/cli/index.js +1932 -298
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +77 -9
- package/dist/index.d.ts +77 -9
- package/dist/index.js +829 -151
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +829 -152
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/dist/index.mjs
CHANGED
|
@@ -56,6 +56,45 @@ function hasJsxAttribute(element, name) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// src/ast/jsx/classify.ts
|
|
59
|
+
var NON_INTERACTION_HANDLERS = /* @__PURE__ */ new Set([
|
|
60
|
+
"onLayout",
|
|
61
|
+
"onScroll",
|
|
62
|
+
"onScrollBeginDrag",
|
|
63
|
+
"onScrollEndDrag",
|
|
64
|
+
"onMomentumScrollBegin",
|
|
65
|
+
"onMomentumScrollEnd",
|
|
66
|
+
"onContentSizeChange",
|
|
67
|
+
"onEndReached",
|
|
68
|
+
"onViewableItemsChanged",
|
|
69
|
+
"onLoad",
|
|
70
|
+
"onLoadStart",
|
|
71
|
+
"onLoadEnd",
|
|
72
|
+
"onError",
|
|
73
|
+
"onProgress",
|
|
74
|
+
"onFocus",
|
|
75
|
+
"onBlur"
|
|
76
|
+
]);
|
|
77
|
+
var VALUE_HANDLERS = /* @__PURE__ */ new Set([
|
|
78
|
+
"onChange",
|
|
79
|
+
"onChangeText",
|
|
80
|
+
"onValueChange",
|
|
81
|
+
"onSelect",
|
|
82
|
+
"onSelectionChange",
|
|
83
|
+
"onSubmitEditing"
|
|
84
|
+
]);
|
|
85
|
+
function pressHandlerProp(element) {
|
|
86
|
+
let fallback;
|
|
87
|
+
for (const attr of element.attributes) {
|
|
88
|
+
if (attr.type !== "JSXAttribute" || attr.name.type !== "JSXIdentifier") continue;
|
|
89
|
+
const name = attr.name.name;
|
|
90
|
+
if (!/^on[A-Z]/.test(name)) continue;
|
|
91
|
+
if (attr.value?.type !== "JSXExpressionContainer") continue;
|
|
92
|
+
if (name === "onPress") return name;
|
|
93
|
+
if (NON_INTERACTION_HANDLERS.has(name) || VALUE_HANDLERS.has(name)) continue;
|
|
94
|
+
fallback ??= name;
|
|
95
|
+
}
|
|
96
|
+
return fallback;
|
|
97
|
+
}
|
|
59
98
|
var VIEW_COMPONENTS = /* @__PURE__ */ new Set(["View", "ScrollView", "SafeAreaView", "KeyboardAvoidingView"]);
|
|
60
99
|
var INPUT_COMPONENTS = /* @__PURE__ */ new Set(["TextInput", "Input", "HocInput"]);
|
|
61
100
|
var BUTTON_COMPONENTS = /* @__PURE__ */ new Set(["TouchableOpacity", "Pressable", "Button"]);
|
|
@@ -70,17 +109,16 @@ function classifyJsxComponent(name, element) {
|
|
|
70
109
|
if (INPUT_COMPONENTS.has(name)) return "input";
|
|
71
110
|
if (BUTTON_COMPONENTS.has(name)) return "button";
|
|
72
111
|
if (element) {
|
|
73
|
-
const hasOptions = hasJsxAttribute(element, "options");
|
|
112
|
+
const hasOptions = hasJsxAttribute(element, "options") || hasJsxAttribute(element, "segments") || hasJsxAttribute(element, "choices") || hasJsxAttribute(element, "tabs");
|
|
74
113
|
const hasValue = hasJsxAttribute(element, "value");
|
|
75
114
|
const hasChecked = hasJsxAttribute(element, "checked") || hasJsxAttribute(element, "selected");
|
|
76
115
|
const hasOnChange = hasJsxAttribute(element, "onChange") || hasJsxAttribute(element, "onValueChange") || hasJsxAttribute(element, "onChangeText");
|
|
77
|
-
const hasOnPress = hasJsxAttribute(element, "onPress");
|
|
78
116
|
if (hasOptions && (hasValue || hasOnChange)) return "select";
|
|
79
117
|
if (hasChecked && hasOnChange) return "toggle";
|
|
80
118
|
if (hasOnChange && (hasJsxAttribute(element, "label") || hasJsxAttribute(element, "placeholder"))) {
|
|
81
119
|
return "input";
|
|
82
120
|
}
|
|
83
|
-
if (
|
|
121
|
+
if (pressHandlerProp(element)) return "button";
|
|
84
122
|
if (getStringAttr(element, "visible") || getExpressionIdentifierAttr(element, "visible")) {
|
|
85
123
|
return "modal";
|
|
86
124
|
}
|
|
@@ -418,12 +456,17 @@ var ScreenAnalyzer = class {
|
|
|
418
456
|
}
|
|
419
457
|
} catch (error) {
|
|
420
458
|
if (this.verbose) {
|
|
421
|
-
console.warn(
|
|
459
|
+
console.warn(
|
|
460
|
+
`[ScreenAnalyzer] Failed to parse ${file}:`,
|
|
461
|
+
error instanceof Error ? error.message : error
|
|
462
|
+
);
|
|
422
463
|
}
|
|
423
464
|
}
|
|
424
465
|
}
|
|
425
466
|
if (this.verbose && this.strictScreens) {
|
|
426
|
-
console.log(
|
|
467
|
+
console.log(
|
|
468
|
+
`[ScreenAnalyzer] Strict mode: ${screens.length} included, ${this.screensFilteredOut} filtered out`
|
|
469
|
+
);
|
|
427
470
|
}
|
|
428
471
|
return screens;
|
|
429
472
|
}
|
|
@@ -711,7 +754,11 @@ var ScreenAnalyzer = class {
|
|
|
711
754
|
for (const form of secondary) {
|
|
712
755
|
const existing = out.find((candidate) => candidate.id === form.id) ?? this.findFormWithSharedFields(out, form);
|
|
713
756
|
if (!existing) {
|
|
714
|
-
out.push({
|
|
757
|
+
out.push({
|
|
758
|
+
...form,
|
|
759
|
+
fields: [...form.fields],
|
|
760
|
+
submitAction: this.namedSubmitAction(form.submitAction)
|
|
761
|
+
});
|
|
715
762
|
continue;
|
|
716
763
|
}
|
|
717
764
|
for (const field of form.fields) {
|
|
@@ -730,9 +777,12 @@ var ScreenAnalyzer = class {
|
|
|
730
777
|
findEquivalentField(fields, incoming) {
|
|
731
778
|
return fields.find((field) => {
|
|
732
779
|
if (field.name && incoming.name && field.name === incoming.name) return true;
|
|
733
|
-
if (field.valueBinding && incoming.valueBinding && field.valueBinding === incoming.valueBinding)
|
|
734
|
-
|
|
735
|
-
if (field.
|
|
780
|
+
if (field.valueBinding && incoming.valueBinding && field.valueBinding === incoming.valueBinding)
|
|
781
|
+
return true;
|
|
782
|
+
if (field.locator?.id && incoming.locator?.id && field.locator.id === incoming.locator.id)
|
|
783
|
+
return true;
|
|
784
|
+
if (field.placeholder && incoming.placeholder && field.placeholder === incoming.placeholder)
|
|
785
|
+
return true;
|
|
736
786
|
if (field.locator?.accessibilityLabel && incoming.locator?.accessibilityLabel && field.locator.accessibilityLabel === incoming.locator.accessibilityLabel) {
|
|
737
787
|
return true;
|
|
738
788
|
}
|
|
@@ -892,9 +942,11 @@ var ScreenAnalyzer = class {
|
|
|
892
942
|
source: field.locator?.source ?? "accessibilityLabel"
|
|
893
943
|
});
|
|
894
944
|
} else if (attrName === "keyboardType") {
|
|
895
|
-
if (attr.value.value === "email-address" || attr.value.value === "email")
|
|
945
|
+
if (attr.value.value === "email-address" || attr.value.value === "email")
|
|
946
|
+
field.type = "email";
|
|
896
947
|
if (attr.value.value === "phone-pad") field.type = "phone";
|
|
897
|
-
if (attr.value.value === "numeric" || attr.value.value === "number-pad")
|
|
948
|
+
if (attr.value.value === "numeric" || attr.value.value === "number-pad")
|
|
949
|
+
field.type = "number";
|
|
898
950
|
}
|
|
899
951
|
} else if (BabelTypes.isJSXExpressionContainer(attr.value)) {
|
|
900
952
|
if (attrName === "value" && BabelTypes.isIdentifier(attr.value.expression)) {
|
|
@@ -1060,8 +1112,10 @@ var ScreenAnalyzer = class {
|
|
|
1060
1112
|
};
|
|
1061
1113
|
if (handler.nativeConfirmationExpected) action.nativeConfirmationExpected = true;
|
|
1062
1114
|
if (handler.targetScreen && !action.targetScreen) action.targetScreen = handler.targetScreen;
|
|
1063
|
-
if (handler.successSignal && !action.successSignal)
|
|
1064
|
-
|
|
1115
|
+
if (handler.successSignal && !action.successSignal)
|
|
1116
|
+
action.successSignal = handler.successSignal;
|
|
1117
|
+
if (handler.failureSignal && !action.failureSignal)
|
|
1118
|
+
action.failureSignal = handler.failureSignal;
|
|
1065
1119
|
if (handler.opensModal && !action.opensModal) action.opensModal = handler.opensModal;
|
|
1066
1120
|
if (handler.destructive || action.destructive === true || action.requiresConfirmation === true || action.effect === "destructive" || action.riskLevel === "high") {
|
|
1067
1121
|
action.destructive = true;
|
|
@@ -1079,6 +1133,7 @@ var ScreenAnalyzer = class {
|
|
|
1079
1133
|
if (!BabelTypes.isJSXIdentifier(element.name)) return;
|
|
1080
1134
|
const componentName = element.name.name;
|
|
1081
1135
|
if (classifyJsxComponent(componentName, element) !== "button") return;
|
|
1136
|
+
const pressProp = pressHandlerProp(element);
|
|
1082
1137
|
let label;
|
|
1083
1138
|
let handler;
|
|
1084
1139
|
for (const attr of element.attributes) {
|
|
@@ -1088,7 +1143,7 @@ var ScreenAnalyzer = class {
|
|
|
1088
1143
|
if ((attrName === "title" || attrName === "accessibilityLabel" || attrName === "label") && BabelTypes.isStringLiteral(attr.value)) {
|
|
1089
1144
|
label = attr.value.value;
|
|
1090
1145
|
}
|
|
1091
|
-
if (attrName ===
|
|
1146
|
+
if (attrName === pressProp && BabelTypes.isJSXExpressionContainer(attr.value) && BabelTypes.isIdentifier(attr.value.expression)) {
|
|
1092
1147
|
handler = attr.value.expression.name;
|
|
1093
1148
|
}
|
|
1094
1149
|
}
|
|
@@ -1154,10 +1209,13 @@ var ScreenAnalyzer = class {
|
|
|
1154
1209
|
}
|
|
1155
1210
|
};
|
|
1156
1211
|
if (fn.body) {
|
|
1157
|
-
traverse4(
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1212
|
+
traverse4(
|
|
1213
|
+
fn.body,
|
|
1214
|
+
{
|
|
1215
|
+
noScope: true,
|
|
1216
|
+
enter: (nodePath) => inspectNode(nodePath.node)
|
|
1217
|
+
}
|
|
1218
|
+
);
|
|
1161
1219
|
}
|
|
1162
1220
|
const expectedOutcome = targetScreen ? "navigation" : hasStateSetter ? "inline-feedback" : hasAwait || hasThen ? "mixed" : "none";
|
|
1163
1221
|
return {
|
|
@@ -1215,6 +1273,7 @@ var ScreenAnalyzer = class {
|
|
|
1215
1273
|
};
|
|
1216
1274
|
const componentName = BabelTypes.isJSXIdentifier(element.name) ? element.name.name : void 0;
|
|
1217
1275
|
action.sourceComponent = componentName;
|
|
1276
|
+
const pressProp = pressHandlerProp(element) ?? "onPress";
|
|
1218
1277
|
let handlerName;
|
|
1219
1278
|
for (const attr of element.attributes) {
|
|
1220
1279
|
if (!BabelTypes.isJSXAttribute(attr)) continue;
|
|
@@ -1249,7 +1308,7 @@ var ScreenAnalyzer = class {
|
|
|
1249
1308
|
}
|
|
1250
1309
|
}
|
|
1251
1310
|
} else if (BabelTypes.isJSXExpressionContainer(attr.value)) {
|
|
1252
|
-
if (attrName ===
|
|
1311
|
+
if (attrName === pressProp && BabelTypes.isIdentifier(attr.value.expression)) {
|
|
1253
1312
|
handlerName = attr.value.expression.name.toLowerCase();
|
|
1254
1313
|
action.handler = attr.value.expression.name;
|
|
1255
1314
|
if (handlerName.includes("submit")) {
|
|
@@ -1257,7 +1316,7 @@ var ScreenAnalyzer = class {
|
|
|
1257
1316
|
} else if (handlerName.includes("navigate")) {
|
|
1258
1317
|
action.type = "navigation";
|
|
1259
1318
|
}
|
|
1260
|
-
} else if (attrName ===
|
|
1319
|
+
} else if (attrName === pressProp) {
|
|
1261
1320
|
const inline = this.extractInlineOnPressMetadata(attr.value.expression);
|
|
1262
1321
|
if (inline.handler) {
|
|
1263
1322
|
handlerName = inline.handler.toLowerCase();
|
|
@@ -1281,7 +1340,11 @@ var ScreenAnalyzer = class {
|
|
|
1281
1340
|
}
|
|
1282
1341
|
}
|
|
1283
1342
|
if (!action.locator && action.id) {
|
|
1284
|
-
action.locator = {
|
|
1343
|
+
action.locator = {
|
|
1344
|
+
id: action.id,
|
|
1345
|
+
label: action.label,
|
|
1346
|
+
source: action.label ? "label" : "inferred"
|
|
1347
|
+
};
|
|
1285
1348
|
}
|
|
1286
1349
|
if (this.isElementDestructive(element, handlerName, action.id)) {
|
|
1287
1350
|
action.destructive = true;
|
|
@@ -1423,24 +1486,27 @@ var ScreenAnalyzer = class {
|
|
|
1423
1486
|
}
|
|
1424
1487
|
extractRowAction(fn) {
|
|
1425
1488
|
let action;
|
|
1426
|
-
traverse4(
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1489
|
+
traverse4(
|
|
1490
|
+
fn.body,
|
|
1491
|
+
{
|
|
1492
|
+
noScope: true,
|
|
1493
|
+
CallExpression: (nodePath) => {
|
|
1494
|
+
const node = nodePath.node;
|
|
1495
|
+
if (!BabelTypes.isMemberExpression(node.callee) || !BabelTypes.isIdentifier(node.callee.object) || node.callee.object.name !== "navigation" || !BabelTypes.isIdentifier(node.callee.property) || !["navigate", "push", "replace"].includes(node.callee.property.name)) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
const firstArg = node.arguments[0];
|
|
1499
|
+
if (!BabelTypes.isStringLiteral(firstArg)) return;
|
|
1500
|
+
const params = this.extractNavigationParams(node.arguments[1]);
|
|
1501
|
+
action = {
|
|
1502
|
+
type: "navigation",
|
|
1503
|
+
targetScreen: firstArg.value,
|
|
1504
|
+
...Object.keys(params).length > 0 ? { params } : {},
|
|
1505
|
+
description: `Pressing a row opens ${firstArg.value}`
|
|
1506
|
+
};
|
|
1432
1507
|
}
|
|
1433
|
-
const firstArg = node.arguments[0];
|
|
1434
|
-
if (!BabelTypes.isStringLiteral(firstArg)) return;
|
|
1435
|
-
const params = this.extractNavigationParams(node.arguments[1]);
|
|
1436
|
-
action = {
|
|
1437
|
-
type: "navigation",
|
|
1438
|
-
targetScreen: firstArg.value,
|
|
1439
|
-
...Object.keys(params).length > 0 ? { params } : {},
|
|
1440
|
-
description: `Pressing a row opens ${firstArg.value}`
|
|
1441
|
-
};
|
|
1442
1508
|
}
|
|
1443
|
-
|
|
1509
|
+
);
|
|
1444
1510
|
return action;
|
|
1445
1511
|
}
|
|
1446
1512
|
extractNavigationParams(arg) {
|
|
@@ -1473,15 +1539,18 @@ var ScreenAnalyzer = class {
|
|
|
1473
1539
|
} else if (BabelTypes.isIdentifier(firstParam)) {
|
|
1474
1540
|
itemNames.add(firstParam.name);
|
|
1475
1541
|
}
|
|
1476
|
-
traverse4(
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1542
|
+
traverse4(
|
|
1543
|
+
fn.body,
|
|
1544
|
+
{
|
|
1545
|
+
noScope: true,
|
|
1546
|
+
MemberExpression: (nodePath) => {
|
|
1547
|
+
const node = nodePath.node;
|
|
1548
|
+
if (BabelTypes.isIdentifier(node.object) && itemNames.has(node.object.name) && BabelTypes.isIdentifier(node.property)) {
|
|
1549
|
+
fields.add(node.property.name);
|
|
1550
|
+
}
|
|
1482
1551
|
}
|
|
1483
1552
|
}
|
|
1484
|
-
|
|
1553
|
+
);
|
|
1485
1554
|
return Array.from(fields).sort();
|
|
1486
1555
|
}
|
|
1487
1556
|
inferItemType(dataSource, renderItem, displayFields) {
|
|
@@ -1493,11 +1562,19 @@ var ScreenAnalyzer = class {
|
|
|
1493
1562
|
if (displayFields.length > 0) return "Item";
|
|
1494
1563
|
return void 0;
|
|
1495
1564
|
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Identity comes from how a field is NAMED, not from what the app
|
|
1567
|
+
* sells: `id`/`uuid`/`key`/`slug` are conventions any codebase uses,
|
|
1568
|
+
* `name`/`title`/`email` are how any row introduces itself. `plate`
|
|
1569
|
+
* used to sit in this list and read like one of them — but it is the
|
|
1570
|
+
* example app's schema, and no other tenant ever got its equivalent
|
|
1571
|
+
* (`mrn`, `trackingNumber`, `sku`) added here.
|
|
1572
|
+
*/
|
|
1496
1573
|
inferIdentityFields(keyField, displayFields) {
|
|
1497
1574
|
const out = /* @__PURE__ */ new Set();
|
|
1498
1575
|
if (keyField) out.add(keyField);
|
|
1499
1576
|
for (const field of displayFields) {
|
|
1500
|
-
if (/^(id|uuid|key|name|title|
|
|
1577
|
+
if (/^(id|uuid|key|name|title|email|slug)$/i.test(field)) out.add(field);
|
|
1501
1578
|
}
|
|
1502
1579
|
return Array.from(out);
|
|
1503
1580
|
}
|
|
@@ -1508,17 +1585,23 @@ var ScreenAnalyzer = class {
|
|
|
1508
1585
|
CallExpression: (nodePath) => {
|
|
1509
1586
|
const node = nodePath.node;
|
|
1510
1587
|
if (!BabelTypes.isMemberExpression(node.callee)) return;
|
|
1511
|
-
if (!BabelTypes.isIdentifier(node.callee.property) || node.callee.property.name !== "filter")
|
|
1512
|
-
|
|
1588
|
+
if (!BabelTypes.isIdentifier(node.callee.property) || node.callee.property.name !== "filter")
|
|
1589
|
+
return;
|
|
1590
|
+
if (!BabelTypes.isIdentifier(node.callee.object) || node.callee.object.name !== dataSource)
|
|
1591
|
+
return;
|
|
1513
1592
|
const fn = node.arguments[0];
|
|
1514
|
-
if (!BabelTypes.isArrowFunctionExpression(fn) && !BabelTypes.isFunctionExpression(fn))
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1593
|
+
if (!BabelTypes.isArrowFunctionExpression(fn) && !BabelTypes.isFunctionExpression(fn))
|
|
1594
|
+
return;
|
|
1595
|
+
traverse4(
|
|
1596
|
+
fn.body,
|
|
1597
|
+
{
|
|
1598
|
+
noScope: true,
|
|
1599
|
+
Identifier: (innerPath) => {
|
|
1600
|
+
const name = innerPath.node.name;
|
|
1601
|
+
if (/query|search|filter/i.test(name)) queryBinding = queryBinding ?? name;
|
|
1602
|
+
}
|
|
1520
1603
|
}
|
|
1521
|
-
|
|
1604
|
+
);
|
|
1522
1605
|
}
|
|
1523
1606
|
});
|
|
1524
1607
|
return queryBinding;
|
|
@@ -1662,11 +1745,7 @@ var NavigationAnalyzer = class {
|
|
|
1662
1745
|
if (BabelTypes.isJSXAttribute(initialRouteAttr) && BabelTypes.isStringLiteral(initialRouteAttr.value)) {
|
|
1663
1746
|
navigator.initialRouteName = initialRouteAttr.value.value;
|
|
1664
1747
|
}
|
|
1665
|
-
const screens = this.extractScreensFromNavigator(
|
|
1666
|
-
node,
|
|
1667
|
-
objectName,
|
|
1668
|
-
navigator.type
|
|
1669
|
-
);
|
|
1748
|
+
const screens = this.extractScreensFromNavigator(node, objectName, navigator.type);
|
|
1670
1749
|
screensByNavigator.set(objectName, screens);
|
|
1671
1750
|
}
|
|
1672
1751
|
}
|
|
@@ -1683,27 +1762,112 @@ var NavigationAnalyzer = class {
|
|
|
1683
1762
|
}
|
|
1684
1763
|
return navigators;
|
|
1685
1764
|
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Is this JSX element `<navigatorVarName.MEMBER …>`?
|
|
1767
|
+
*/
|
|
1768
|
+
// Deliberately NOT a type predicate (`node is t.JSXElement`): a false
|
|
1769
|
+
// result would then narrow `node` to "not a JSXElement", and the very
|
|
1770
|
+
// next check — the same node against a different member — would see
|
|
1771
|
+
// `never`. It is a question about the member name, not about the node
|
|
1772
|
+
// kind.
|
|
1773
|
+
isNavigatorMember(node, navigatorVarName, member) {
|
|
1774
|
+
if (!BabelTypes.isJSXElement(node)) return false;
|
|
1775
|
+
const name = node.openingElement.name;
|
|
1776
|
+
return BabelTypes.isJSXMemberExpression(name) && BabelTypes.isJSXIdentifier(name.object) && name.object.name === navigatorVarName && BabelTypes.isJSXIdentifier(name.property) && name.property.name === member;
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Flatten a navigator's JSX children into the `<X.Screen>` elements they
|
|
1780
|
+
* contain, unwrapping every container a real app puts in between.
|
|
1781
|
+
*
|
|
1782
|
+
* The old version compared `t.isJSXElement(child)` against the direct
|
|
1783
|
+
* children only. A ternary is a `JSXExpressionContainer`, so an
|
|
1784
|
+
* auth-gated root — the modal shape of a commercial app, and the shape
|
|
1785
|
+
* of this repo's own `apps/example-app` — contributed ZERO screens to
|
|
1786
|
+
* the graph while `appilots sync` reported success (#396).
|
|
1787
|
+
*
|
|
1788
|
+
* Both branches of a conditional are collected on purpose. The graph is
|
|
1789
|
+
* a design-time map of what routes EXIST, not a prediction of which one
|
|
1790
|
+
* a given session will render; the agent needs the destination name to
|
|
1791
|
+
* be there whether or not the user happens to be logged in right now.
|
|
1792
|
+
*/
|
|
1793
|
+
collectScreenElements(children, navigatorVarName, depth = 0) {
|
|
1794
|
+
if (depth > 12) return [];
|
|
1795
|
+
const found = [];
|
|
1796
|
+
const visit = (node) => {
|
|
1797
|
+
if (!node) return;
|
|
1798
|
+
if (this.isNavigatorMember(node, navigatorVarName, "Screen")) {
|
|
1799
|
+
found.push(node);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
if (this.isNavigatorMember(node, navigatorVarName, "Group")) {
|
|
1803
|
+
const group = node;
|
|
1804
|
+
found.push(...this.collectScreenElements(group.children, navigatorVarName, depth + 1));
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
if (BabelTypes.isJSXElement(node)) {
|
|
1808
|
+
const name = node.openingElement.name;
|
|
1809
|
+
const isForeignNavigator = BabelTypes.isJSXMemberExpression(name) && BabelTypes.isJSXIdentifier(name.property) && name.property.name === "Navigator";
|
|
1810
|
+
if (isForeignNavigator) return;
|
|
1811
|
+
found.push(...this.collectScreenElements(node.children, navigatorVarName, depth + 1));
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
if (BabelTypes.isJSXFragment(node)) {
|
|
1815
|
+
found.push(...this.collectScreenElements(node.children, navigatorVarName, depth + 1));
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1818
|
+
if (BabelTypes.isJSXExpressionContainer(node)) {
|
|
1819
|
+
visit(node.expression);
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
if (BabelTypes.isConditionalExpression(node)) {
|
|
1823
|
+
visit(node.consequent);
|
|
1824
|
+
visit(node.alternate);
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
if (BabelTypes.isLogicalExpression(node)) {
|
|
1828
|
+
visit(node.left);
|
|
1829
|
+
visit(node.right);
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
if (BabelTypes.isCallExpression(node)) {
|
|
1833
|
+
for (const arg of node.arguments) {
|
|
1834
|
+
if (BabelTypes.isArrowFunctionExpression(arg) || BabelTypes.isFunctionExpression(arg)) {
|
|
1835
|
+
if (BabelTypes.isBlockStatement(arg.body)) {
|
|
1836
|
+
for (const stmt of arg.body.body) {
|
|
1837
|
+
if (BabelTypes.isReturnStatement(stmt)) visit(stmt.argument);
|
|
1838
|
+
}
|
|
1839
|
+
} else {
|
|
1840
|
+
visit(arg.body);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
if (BabelTypes.isArrayExpression(node)) {
|
|
1847
|
+
for (const el of node.elements) visit(el);
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
if (BabelTypes.isTSAsExpression(node) || BabelTypes.isTSNonNullExpression(node)) {
|
|
1851
|
+
visit(node.expression);
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1854
|
+
for (const child of children) visit(child);
|
|
1855
|
+
return found;
|
|
1856
|
+
}
|
|
1686
1857
|
/** Extract screens from a navigator JSX element */
|
|
1687
1858
|
extractScreensFromNavigator(navigatorElement, navigatorVarName, navigatorType) {
|
|
1688
1859
|
const screens = [];
|
|
1689
1860
|
if (!navigatorElement.children) return screens;
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
name: screenName,
|
|
1701
|
-
navigatorName: navigatorVarName,
|
|
1702
|
-
navigatorType
|
|
1703
|
-
});
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1861
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1862
|
+
for (const element of this.collectScreenElements(navigatorElement.children, navigatorVarName)) {
|
|
1863
|
+
const screenName = this.extractAttributeValue(element.openingElement.attributes, "name");
|
|
1864
|
+
if (!screenName || seen.has(screenName)) continue;
|
|
1865
|
+
seen.add(screenName);
|
|
1866
|
+
screens.push({
|
|
1867
|
+
name: screenName,
|
|
1868
|
+
navigatorName: navigatorVarName,
|
|
1869
|
+
navigatorType
|
|
1870
|
+
});
|
|
1707
1871
|
}
|
|
1708
1872
|
return screens;
|
|
1709
1873
|
}
|
|
@@ -1762,9 +1926,7 @@ var NavigationAnalyzer = class {
|
|
|
1762
1926
|
if (member.type === "TSPropertySignature" && member.key) {
|
|
1763
1927
|
const keyName = member.key.type === "Identifier" ? member.key.name : null;
|
|
1764
1928
|
if (keyName && member.typeAnnotation) {
|
|
1765
|
-
const params = this.extractParamsFromType(
|
|
1766
|
-
member.typeAnnotation.typeAnnotation
|
|
1767
|
-
);
|
|
1929
|
+
const params = this.extractParamsFromType(member.typeAnnotation.typeAnnotation);
|
|
1768
1930
|
entries.set(keyName, params);
|
|
1769
1931
|
}
|
|
1770
1932
|
}
|
|
@@ -1843,6 +2005,11 @@ var NavigationAnalyzer = class {
|
|
|
1843
2005
|
let initialScreen = "";
|
|
1844
2006
|
for (const navigator of parsedNavigators) {
|
|
1845
2007
|
const screenNames = navigator.screens.map((s) => s.name);
|
|
2008
|
+
if (screenNames.length === 0) {
|
|
2009
|
+
console.warn(
|
|
2010
|
+
`[MCPGenerator] navigator "${navigator.name}" (${navigator.type}) declared no reachable screens. Its routes will be missing from the app map, so the agent will have no destination name for them. If the screens are there in the source, this is a parser gap \u2014 please report it with the file.`
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
1846
2013
|
navigators.push({
|
|
1847
2014
|
name: navigator.name,
|
|
1848
2015
|
type: navigator.type,
|
|
@@ -2630,7 +2797,9 @@ var WebScreenAnalyzer = class {
|
|
|
2630
2797
|
include = ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
|
|
2631
2798
|
exclude = ["**/node_modules/**", "**/dist/**", "**/build/**"]
|
|
2632
2799
|
} = this.config;
|
|
2633
|
-
const files = await glob(include, { cwd: this.config.rootDir, ignore: exclude })
|
|
2800
|
+
const files = (await glob(include, { cwd: this.config.rootDir, ignore: exclude })).filter(
|
|
2801
|
+
(file) => !file.endsWith(".d.ts")
|
|
2802
|
+
);
|
|
2634
2803
|
const patternMatches = await glob(this.screenPatterns, {
|
|
2635
2804
|
cwd: this.config.rootDir,
|
|
2636
2805
|
ignore: exclude
|
|
@@ -3001,7 +3170,8 @@ var WebScreenAnalyzer = class {
|
|
|
3001
3170
|
if (type === "email") return "email";
|
|
3002
3171
|
if (type === "tel") return "phone";
|
|
3003
3172
|
if (type === "number") return "number";
|
|
3004
|
-
if (type === "date" || type === "datetime-local" || type === "month" || type === "week")
|
|
3173
|
+
if (type === "date" || type === "datetime-local" || type === "month" || type === "week")
|
|
3174
|
+
return "date";
|
|
3005
3175
|
const inputMode = getStringAttr(opening, "inputMode") ?? getStringAttr(opening, "inputmode");
|
|
3006
3176
|
if (inputMode === "email") return "email";
|
|
3007
3177
|
if (inputMode === "tel") return "phone";
|
|
@@ -3315,7 +3485,9 @@ var WebScreenAnalyzer = class {
|
|
|
3315
3485
|
}
|
|
3316
3486
|
const callback = expr.arguments[0];
|
|
3317
3487
|
if (!BabelTypes.isArrowFunctionExpression(callback) && !BabelTypes.isFunctionExpression(callback)) return;
|
|
3318
|
-
const enclosing = nodePath.findParent(
|
|
3488
|
+
const enclosing = nodePath.findParent(
|
|
3489
|
+
(p) => p.isJSXElement()
|
|
3490
|
+
);
|
|
3319
3491
|
const enclosingName = enclosing ? getJsxElementName(enclosing.node.openingElement) : null;
|
|
3320
3492
|
const enclosingRole = enclosing && enclosingName ? classifyWebJsxComponent(enclosingName, enclosing.node.openingElement) : null;
|
|
3321
3493
|
const returnsListItem = callbackReturnsTag(callback, /* @__PURE__ */ new Set(["li", "tr"]));
|
|
@@ -3417,16 +3589,19 @@ function firstCalledFunctionName(node) {
|
|
|
3417
3589
|
}
|
|
3418
3590
|
function containsWindowConfirm(body) {
|
|
3419
3591
|
let found = false;
|
|
3420
|
-
traverse4(
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
found = true;
|
|
3592
|
+
traverse4(
|
|
3593
|
+
body,
|
|
3594
|
+
{
|
|
3595
|
+
noScope: true,
|
|
3596
|
+
CallExpression: (nodePath) => {
|
|
3597
|
+
const callee = nodePath.node.callee;
|
|
3598
|
+
if (BabelTypes.isIdentifier(callee) && callee.name === "confirm") found = true;
|
|
3599
|
+
if (BabelTypes.isMemberExpression(callee) && BabelTypes.isIdentifier(callee.object) && callee.object.name === "window" && BabelTypes.isIdentifier(callee.property) && callee.property.name === "confirm") {
|
|
3600
|
+
found = true;
|
|
3601
|
+
}
|
|
3427
3602
|
}
|
|
3428
3603
|
}
|
|
3429
|
-
|
|
3604
|
+
);
|
|
3430
3605
|
return found;
|
|
3431
3606
|
}
|
|
3432
3607
|
function mergeLocator(current, next) {
|
|
@@ -3466,32 +3641,38 @@ function collectionItemNames(callback) {
|
|
|
3466
3641
|
function collectionDisplayFields(callback, itemNames) {
|
|
3467
3642
|
const fields = /* @__PURE__ */ new Set();
|
|
3468
3643
|
if (!callback.body) return [];
|
|
3469
|
-
traverse4(
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3644
|
+
traverse4(
|
|
3645
|
+
callback.body,
|
|
3646
|
+
{
|
|
3647
|
+
noScope: true,
|
|
3648
|
+
MemberExpression: (nodePath) => {
|
|
3649
|
+
const node = nodePath.node;
|
|
3650
|
+
if (BabelTypes.isIdentifier(node.object) && itemNames.has(node.object.name) && BabelTypes.isIdentifier(node.property)) {
|
|
3651
|
+
fields.add(node.property.name);
|
|
3652
|
+
}
|
|
3475
3653
|
}
|
|
3476
3654
|
}
|
|
3477
|
-
|
|
3655
|
+
);
|
|
3478
3656
|
return Array.from(fields).sort();
|
|
3479
3657
|
}
|
|
3480
3658
|
function collectionKeyField(callback, itemNames) {
|
|
3481
3659
|
let keyField;
|
|
3482
3660
|
if (!callback.body) return void 0;
|
|
3483
|
-
traverse4(
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3661
|
+
traverse4(
|
|
3662
|
+
callback.body,
|
|
3663
|
+
{
|
|
3664
|
+
noScope: true,
|
|
3665
|
+
JSXAttribute: (nodePath) => {
|
|
3666
|
+
const attr = nodePath.node;
|
|
3667
|
+
if (!BabelTypes.isJSXIdentifier(attr.name) || attr.name.name !== "key") return;
|
|
3668
|
+
if (!attr.value || !BabelTypes.isJSXExpressionContainer(attr.value)) return;
|
|
3669
|
+
const expr = attr.value.expression;
|
|
3670
|
+
if (BabelTypes.isMemberExpression(expr) && BabelTypes.isIdentifier(expr.object) && itemNames.has(expr.object.name) && BabelTypes.isIdentifier(expr.property)) {
|
|
3671
|
+
keyField = keyField ?? expr.property.name;
|
|
3672
|
+
}
|
|
3492
3673
|
}
|
|
3493
3674
|
}
|
|
3494
|
-
|
|
3675
|
+
);
|
|
3495
3676
|
return keyField;
|
|
3496
3677
|
}
|
|
3497
3678
|
function collectionRowAction(callback) {
|
|
@@ -3539,7 +3720,7 @@ function inferIdentityFields(keyField, displayFields) {
|
|
|
3539
3720
|
const out = /* @__PURE__ */ new Set();
|
|
3540
3721
|
if (keyField) out.add(keyField);
|
|
3541
3722
|
for (const field of displayFields) {
|
|
3542
|
-
if (/^(id|uuid|key|name|title|
|
|
3723
|
+
if (/^(id|uuid|key|name|title|email|slug)$/i.test(field)) out.add(field);
|
|
3543
3724
|
}
|
|
3544
3725
|
return Array.from(out);
|
|
3545
3726
|
}
|
|
@@ -7923,7 +8104,7 @@ var coerce = {
|
|
|
7923
8104
|
};
|
|
7924
8105
|
var NEVER = INVALID;
|
|
7925
8106
|
|
|
7926
|
-
// ../shared/dist/chunk-
|
|
8107
|
+
// ../shared/dist/chunk-MKT45LRA.mjs
|
|
7927
8108
|
var locatorSourceSchema = external_exports.enum([
|
|
7928
8109
|
"appilotsId",
|
|
7929
8110
|
"testID",
|
|
@@ -7949,7 +8130,16 @@ var locatorDescriptorSchema = external_exports.object({
|
|
|
7949
8130
|
source: locatorSourceSchema.optional()
|
|
7950
8131
|
}).passthrough();
|
|
7951
8132
|
var signalDescriptorSchema = external_exports.object({
|
|
7952
|
-
type: external_exports.enum([
|
|
8133
|
+
type: external_exports.enum([
|
|
8134
|
+
"navigation",
|
|
8135
|
+
"goBack",
|
|
8136
|
+
"toast",
|
|
8137
|
+
"modal",
|
|
8138
|
+
"inline-error",
|
|
8139
|
+
"data-arrival",
|
|
8140
|
+
"loading",
|
|
8141
|
+
"none"
|
|
8142
|
+
]).or(external_exports.string().min(1).max(60)),
|
|
7953
8143
|
target: external_exports.string().optional(),
|
|
7954
8144
|
description: external_exports.string().optional()
|
|
7955
8145
|
}).passthrough();
|
|
@@ -7963,7 +8153,20 @@ var waitPolicyDescriptorSchema = external_exports.object({
|
|
|
7963
8153
|
signals: external_exports.array(signalDescriptorSchema).optional(),
|
|
7964
8154
|
maxMs: external_exports.number().finite().optional()
|
|
7965
8155
|
}).passthrough();
|
|
7966
|
-
var targetDescriptorRoleSchema = external_exports.enum([
|
|
8156
|
+
var targetDescriptorRoleSchema = external_exports.enum([
|
|
8157
|
+
"button",
|
|
8158
|
+
"submit",
|
|
8159
|
+
"input",
|
|
8160
|
+
"toggle",
|
|
8161
|
+
"select",
|
|
8162
|
+
"date",
|
|
8163
|
+
"list",
|
|
8164
|
+
"row",
|
|
8165
|
+
"menuItem",
|
|
8166
|
+
"modal",
|
|
8167
|
+
"custom"
|
|
8168
|
+
]).or(external_exports.string().min(1).max(60));
|
|
8169
|
+
var targetOriginSchema = external_exports.enum(["analyzer", "manifest"]).or(external_exports.string().min(1).max(40));
|
|
7967
8170
|
var targetDescriptorSchema = external_exports.object({
|
|
7968
8171
|
id: external_exports.string(),
|
|
7969
8172
|
role: targetDescriptorRoleSchema,
|
|
@@ -7977,10 +8180,20 @@ var targetDescriptorSchema = external_exports.object({
|
|
|
7977
8180
|
requiresConfirmation: external_exports.boolean().optional(),
|
|
7978
8181
|
opensModal: external_exports.string().optional(),
|
|
7979
8182
|
opensBottomSheet: external_exports.string().optional(),
|
|
7980
|
-
sourceComponent: external_exports.string().optional()
|
|
8183
|
+
sourceComponent: external_exports.string().optional(),
|
|
8184
|
+
origin: targetOriginSchema.optional()
|
|
7981
8185
|
}).passthrough();
|
|
7982
8186
|
var flowStepDescriptorSchema = external_exports.object({
|
|
7983
|
-
type: external_exports.enum([
|
|
8187
|
+
type: external_exports.enum([
|
|
8188
|
+
"navigate",
|
|
8189
|
+
"fill",
|
|
8190
|
+
"press",
|
|
8191
|
+
"select",
|
|
8192
|
+
"toggle",
|
|
8193
|
+
"wait",
|
|
8194
|
+
"confirm",
|
|
8195
|
+
"choose-list-item"
|
|
8196
|
+
]).or(external_exports.string().min(1).max(60)),
|
|
7984
8197
|
target: external_exports.string().optional(),
|
|
7985
8198
|
label: external_exports.string().optional(),
|
|
7986
8199
|
description: external_exports.string().optional(),
|
|
@@ -8202,15 +8415,31 @@ external_exports.object({
|
|
|
8202
8415
|
password: external_exports.string().min(1, "Password is required"),
|
|
8203
8416
|
code: totpCodeSchema
|
|
8204
8417
|
});
|
|
8418
|
+
external_exports.object({
|
|
8419
|
+
password: external_exports.string().min(1, "Password is required"),
|
|
8420
|
+
code: totpCodeSchema.optional(),
|
|
8421
|
+
recoveryCode: recoveryCodeSchema.optional(),
|
|
8422
|
+
confirm: external_exports.literal("DELETE MY ACCOUNT")
|
|
8423
|
+
}).refine((v) => !(v.code && v.recoveryCode), {
|
|
8424
|
+
message: "Provide either a TOTP code or a recovery code, not both",
|
|
8425
|
+
path: ["code"]
|
|
8426
|
+
});
|
|
8205
8427
|
var createProjectSchema = external_exports.object({
|
|
8206
8428
|
name: external_exports.string().min(1).max(100),
|
|
8207
8429
|
description: external_exports.string().max(500).optional(),
|
|
8208
8430
|
// Reference to a tenant-scoped provider (preferred). Optional at
|
|
8209
8431
|
// creation so a project can be created and configured later.
|
|
8210
8432
|
providerId: external_exports.string().min(1).optional(),
|
|
8211
|
-
|
|
8433
|
+
// 16k chars (~4k tokens): um prompt de CS real não cabe em 4096 — o
|
|
8434
|
+
// primeiro cliente de produção chegou com 6.385 e tomou 'Invalid input'
|
|
8435
|
+
// sem saber por quê. O teto continua existindo porque o systemPrompt
|
|
8436
|
+
// entra em TODO hop do relay: é proteção de TPM, não de gosto.
|
|
8437
|
+
systemPrompt: external_exports.string().max(16e3).optional()
|
|
8438
|
+
});
|
|
8439
|
+
createProjectSchema.partial().extend({
|
|
8440
|
+
providerId: external_exports.string().min(1).nullable().optional(),
|
|
8441
|
+
systemPrompt: external_exports.string().max(16e3).nullable().optional()
|
|
8212
8442
|
});
|
|
8213
|
-
createProjectSchema.partial();
|
|
8214
8443
|
var providerKindSchema = external_exports.enum(["openai", "anthropic", "custom"]);
|
|
8215
8444
|
external_exports.object({
|
|
8216
8445
|
name: external_exports.string().min(1).max(100),
|
|
@@ -8237,6 +8466,7 @@ external_exports.object({
|
|
|
8237
8466
|
content: external_exports.record(external_exports.unknown())
|
|
8238
8467
|
});
|
|
8239
8468
|
var apiKeyScopeSchema = external_exports.enum(["sdk", "operator"]);
|
|
8469
|
+
var apiKeyEnvironmentSchema = external_exports.enum(["test", "live"]);
|
|
8240
8470
|
external_exports.object({
|
|
8241
8471
|
name: external_exports.string().min(1).max(100),
|
|
8242
8472
|
// Required for `sdk` (default), forbidden/ignored for `operator` —
|
|
@@ -8244,6 +8474,7 @@ external_exports.object({
|
|
|
8244
8474
|
// missing scope still validates as the pre-existing SDK shape.
|
|
8245
8475
|
projectId: external_exports.string().min(1).optional(),
|
|
8246
8476
|
scope: apiKeyScopeSchema.default("sdk"),
|
|
8477
|
+
environment: apiKeyEnvironmentSchema.optional(),
|
|
8247
8478
|
expiresAt: external_exports.string().datetime().optional()
|
|
8248
8479
|
}).refine((v) => v.scope !== "sdk" || !!v.projectId, {
|
|
8249
8480
|
message: 'projectId is required for scope "sdk"',
|
|
@@ -8251,13 +8482,23 @@ external_exports.object({
|
|
|
8251
8482
|
});
|
|
8252
8483
|
var boundedString = (max) => external_exports.string().max(max);
|
|
8253
8484
|
var clientPlatformSchema = external_exports.enum(["react-native", "web", "android", "ios"]).or(external_exports.string().min(1).max(40));
|
|
8485
|
+
var identityProvenanceSchema = external_exports.enum(["declared", "derived", "positional"]).or(external_exports.string().min(1).max(40));
|
|
8254
8486
|
var snapshotInputSchema = external_exports.object({
|
|
8255
8487
|
id: boundedString(160).optional(),
|
|
8488
|
+
provenance: identityProvenanceSchema.optional(),
|
|
8489
|
+
/**
|
|
8490
|
+
* False when the control is mounted but currently OUTSIDE the window —
|
|
8491
|
+
* below the fold, scrolled off, pushed sideways. Absent means the client
|
|
8492
|
+
* cannot tell (old React Native architecture, or any SDK published
|
|
8493
|
+
* before the field existed), which must never be read as `false`.
|
|
8494
|
+
*/
|
|
8495
|
+
onScreen: external_exports.boolean().optional(),
|
|
8256
8496
|
label: boundedString(300).optional(),
|
|
8257
8497
|
value: boundedString(4096).optional(),
|
|
8258
8498
|
placeholder: boundedString(300).optional(),
|
|
8259
8499
|
editable: external_exports.boolean().optional(),
|
|
8260
8500
|
secure: external_exports.boolean().optional(),
|
|
8501
|
+
submitsOnReturn: external_exports.boolean().optional(),
|
|
8261
8502
|
type: boundedString(40).optional(),
|
|
8262
8503
|
required: external_exports.boolean().optional(),
|
|
8263
8504
|
invalid: external_exports.boolean().optional(),
|
|
@@ -8265,6 +8506,14 @@ var snapshotInputSchema = external_exports.object({
|
|
|
8265
8506
|
}).passthrough();
|
|
8266
8507
|
var snapshotButtonSchema = external_exports.object({
|
|
8267
8508
|
id: boundedString(160).optional(),
|
|
8509
|
+
provenance: identityProvenanceSchema.optional(),
|
|
8510
|
+
/**
|
|
8511
|
+
* False when the control is mounted but currently OUTSIDE the window —
|
|
8512
|
+
* below the fold, scrolled off, pushed sideways. Absent means the client
|
|
8513
|
+
* cannot tell (old React Native architecture, or any SDK published
|
|
8514
|
+
* before the field existed), which must never be read as `false`.
|
|
8515
|
+
*/
|
|
8516
|
+
onScreen: external_exports.boolean().optional(),
|
|
8268
8517
|
label: boundedString(300).optional(),
|
|
8269
8518
|
disabled: external_exports.boolean().optional(),
|
|
8270
8519
|
inModal: external_exports.boolean().optional(),
|
|
@@ -8274,12 +8523,28 @@ var snapshotButtonSchema = external_exports.object({
|
|
|
8274
8523
|
}).passthrough();
|
|
8275
8524
|
var snapshotToggleSchema = external_exports.object({
|
|
8276
8525
|
id: boundedString(160).optional(),
|
|
8526
|
+
provenance: identityProvenanceSchema.optional(),
|
|
8527
|
+
/**
|
|
8528
|
+
* False when the control is mounted but currently OUTSIDE the window —
|
|
8529
|
+
* below the fold, scrolled off, pushed sideways. Absent means the client
|
|
8530
|
+
* cannot tell (old React Native architecture, or any SDK published
|
|
8531
|
+
* before the field existed), which must never be read as `false`.
|
|
8532
|
+
*/
|
|
8533
|
+
onScreen: external_exports.boolean().optional(),
|
|
8277
8534
|
label: boundedString(300).optional(),
|
|
8278
8535
|
value: external_exports.boolean().optional(),
|
|
8279
8536
|
inModal: external_exports.boolean().optional()
|
|
8280
8537
|
}).passthrough();
|
|
8281
8538
|
var snapshotSliderSchema = external_exports.object({
|
|
8282
8539
|
id: boundedString(160).optional(),
|
|
8540
|
+
provenance: identityProvenanceSchema.optional(),
|
|
8541
|
+
/**
|
|
8542
|
+
* False when the control is mounted but currently OUTSIDE the window —
|
|
8543
|
+
* below the fold, scrolled off, pushed sideways. Absent means the client
|
|
8544
|
+
* cannot tell (old React Native architecture, or any SDK published
|
|
8545
|
+
* before the field existed), which must never be read as `false`.
|
|
8546
|
+
*/
|
|
8547
|
+
onScreen: external_exports.boolean().optional(),
|
|
8283
8548
|
label: boundedString(300).optional(),
|
|
8284
8549
|
value: external_exports.number().finite().optional(),
|
|
8285
8550
|
min: external_exports.number().finite().optional(),
|
|
@@ -8320,6 +8585,26 @@ var snapshotListSchema = external_exports.object({
|
|
|
8320
8585
|
// should chase, and the relay only reads a known subset.
|
|
8321
8586
|
items: external_exports.array(external_exports.record(external_exports.unknown())).max(500).optional()
|
|
8322
8587
|
}).passthrough();
|
|
8588
|
+
var snapshotNativeDialogSchema = external_exports.object({
|
|
8589
|
+
id: boundedString(160),
|
|
8590
|
+
title: boundedString(300).optional(),
|
|
8591
|
+
message: boundedString(2e3).optional(),
|
|
8592
|
+
buttons: external_exports.array(
|
|
8593
|
+
external_exports.object({
|
|
8594
|
+
label: boundedString(160),
|
|
8595
|
+
style: external_exports.enum(["default", "cancel", "destructive"]).optional()
|
|
8596
|
+
}).passthrough()
|
|
8597
|
+
).max(10)
|
|
8598
|
+
}).passthrough();
|
|
8599
|
+
var snapshotScrollableSchema = external_exports.object({
|
|
8600
|
+
id: boundedString(160),
|
|
8601
|
+
containerType: boundedString(60).optional(),
|
|
8602
|
+
label: boundedString(300).optional(),
|
|
8603
|
+
scrollOffsetY: external_exports.number().finite().optional(),
|
|
8604
|
+
canScrollUp: external_exports.boolean().optional(),
|
|
8605
|
+
canScrollDown: external_exports.boolean().optional(),
|
|
8606
|
+
horizontal: external_exports.boolean().optional()
|
|
8607
|
+
}).passthrough();
|
|
8323
8608
|
var snapshotChoiceGroupSchema = external_exports.object({
|
|
8324
8609
|
index: external_exports.number().int().optional(),
|
|
8325
8610
|
id: boundedString(160).optional(),
|
|
@@ -8336,6 +8621,14 @@ var snapshotElementSchema = external_exports.object({
|
|
|
8336
8621
|
disabled: external_exports.boolean().optional(),
|
|
8337
8622
|
selected: external_exports.boolean().optional(),
|
|
8338
8623
|
source: boundedString(40).optional(),
|
|
8624
|
+
provenance: identityProvenanceSchema.optional(),
|
|
8625
|
+
/**
|
|
8626
|
+
* False when the control is mounted but currently OUTSIDE the window —
|
|
8627
|
+
* below the fold, scrolled off, pushed sideways. Absent means the client
|
|
8628
|
+
* cannot tell (old React Native architecture, or any SDK published
|
|
8629
|
+
* before the field existed), which must never be read as `false`.
|
|
8630
|
+
*/
|
|
8631
|
+
onScreen: external_exports.boolean().optional(),
|
|
8339
8632
|
targetId: boundedString(160).optional(),
|
|
8340
8633
|
listContext: external_exports.record(external_exports.unknown()).optional(),
|
|
8341
8634
|
inModal: external_exports.boolean().optional()
|
|
@@ -8352,7 +8645,34 @@ var agentSnapshotSchema = external_exports.object({
|
|
|
8352
8645
|
modalOpen: external_exports.boolean().optional(),
|
|
8353
8646
|
lists: external_exports.array(snapshotListSchema).max(100).optional(),
|
|
8354
8647
|
choiceGroups: external_exports.array(snapshotChoiceGroupSchema).max(100).optional(),
|
|
8355
|
-
|
|
8648
|
+
scrollables: external_exports.array(snapshotScrollableSchema).max(20).optional(),
|
|
8649
|
+
nativeDialog: snapshotNativeDialogSchema.optional(),
|
|
8650
|
+
elements: external_exports.array(snapshotElementSchema).max(1e3).optional(),
|
|
8651
|
+
/**
|
|
8652
|
+
* The client clamped this observation to the caps above and lost
|
|
8653
|
+
* data doing it (`clampSnapshotToWireLimits` in
|
|
8654
|
+
* `@appilots/client-core`). Read by the prompt serializer so the
|
|
8655
|
+
* model is told the view is partial — a screen truncated at 500
|
|
8656
|
+
* texts must not be read as a screen that only has 500 things on
|
|
8657
|
+
* it. Optional: older SDKs never clamp and never send it.
|
|
8658
|
+
*/
|
|
8659
|
+
truncated: external_exports.boolean().optional(),
|
|
8660
|
+
/**
|
|
8661
|
+
* How many of this screen's controls the client could name, split by
|
|
8662
|
+
* `identityProvenance`. Diagnostic only — the relay never grounds an
|
|
8663
|
+
* action on it — but it is the number that says whether an app needs
|
|
8664
|
+
* annotating, and comparing it between a debug and a release build is
|
|
8665
|
+
* the first direct measurement of what minification costs the agent.
|
|
8666
|
+
*/
|
|
8667
|
+
stats: external_exports.object({
|
|
8668
|
+
visitedFibers: external_exports.number().int().nonnegative().optional(),
|
|
8669
|
+
skippedHidden: external_exports.number().int().nonnegative().optional(),
|
|
8670
|
+
identity: external_exports.object({
|
|
8671
|
+
declared: external_exports.number().int().nonnegative().optional(),
|
|
8672
|
+
derived: external_exports.number().int().nonnegative().optional(),
|
|
8673
|
+
positional: external_exports.number().int().nonnegative().optional()
|
|
8674
|
+
}).passthrough().optional()
|
|
8675
|
+
}).passthrough().optional()
|
|
8356
8676
|
}).passthrough();
|
|
8357
8677
|
var agentContextSchema = external_exports.object({
|
|
8358
8678
|
/**
|
|
@@ -8402,6 +8722,19 @@ var agentContextSchema = external_exports.object({
|
|
|
8402
8722
|
).max(200).optional(),
|
|
8403
8723
|
loadingPending: external_exports.boolean().optional()
|
|
8404
8724
|
}).passthrough();
|
|
8725
|
+
var introspectionFailureReasonSchema = external_exports.enum([
|
|
8726
|
+
"sentinel-missing-internals",
|
|
8727
|
+
"never-mounted",
|
|
8728
|
+
"render-crashed",
|
|
8729
|
+
"names-mangled",
|
|
8730
|
+
"walk-recognized-nothing"
|
|
8731
|
+
]);
|
|
8732
|
+
var introspectionDiagnosticsSchema = external_exports.object({
|
|
8733
|
+
captured: external_exports.boolean(),
|
|
8734
|
+
failureReason: introspectionFailureReasonSchema.nullish(),
|
|
8735
|
+
reactVersion: boundedString(32).nullish(),
|
|
8736
|
+
failureCount: external_exports.number().int().min(0).max(1e6).optional()
|
|
8737
|
+
});
|
|
8405
8738
|
external_exports.object({
|
|
8406
8739
|
content: external_exports.string().min(1).max(4096),
|
|
8407
8740
|
sessionId: external_exports.string().optional(),
|
|
@@ -8411,7 +8744,20 @@ external_exports.object({
|
|
|
8411
8744
|
* the active MCP doc and emits `mcp_version_mismatch` telemetry when
|
|
8412
8745
|
* they differ. Optional — older SDKs simply skip the check.
|
|
8413
8746
|
*/
|
|
8414
|
-
mcpVersion: external_exports.string().max(64).optional()
|
|
8747
|
+
mcpVersion: external_exports.string().max(64).optional(),
|
|
8748
|
+
/**
|
|
8749
|
+
* SDK-side introspection health. Only sent when it FAILED — a client
|
|
8750
|
+
* that reached the React tree omits the field entirely, so the happy
|
|
8751
|
+
* path costs nothing on the wire.
|
|
8752
|
+
*
|
|
8753
|
+
* Without this, an app whose introspection is dead sends a snapshot
|
|
8754
|
+
* that is empty but perfectly well-formed, indistinguishable from a
|
|
8755
|
+
* genuinely empty screen. The server emits
|
|
8756
|
+
* `introspection_unavailable` so the failure is visible to us instead
|
|
8757
|
+
* of waiting for a bug report. Optional — older SDKs simply never
|
|
8758
|
+
* send it.
|
|
8759
|
+
*/
|
|
8760
|
+
introspection: introspectionDiagnosticsSchema.optional()
|
|
8415
8761
|
});
|
|
8416
8762
|
var agentActionTypeSchema = external_exports.enum([
|
|
8417
8763
|
"navigate",
|
|
@@ -8422,11 +8768,22 @@ var agentActionTypeSchema = external_exports.enum([
|
|
|
8422
8768
|
"custom"
|
|
8423
8769
|
]);
|
|
8424
8770
|
var navigationPayloadSchema = external_exports.object({
|
|
8425
|
-
|
|
8771
|
+
// Optional ONLY so `goBack` can exist (#398): it returns to whatever
|
|
8772
|
+
// is beneath on the stack and has no destination to name. Every other
|
|
8773
|
+
// action is still required to carry one — enforced by the refine
|
|
8774
|
+
// below rather than by the field, so the error names the real problem
|
|
8775
|
+
// ("navigate needs a screenName") instead of a missing key.
|
|
8776
|
+
screenName: external_exports.string().min(1).optional(),
|
|
8426
8777
|
params: external_exports.record(external_exports.unknown()).optional(),
|
|
8427
8778
|
navigationAction: external_exports.enum(["push", "navigate", "replace", "goBack", "reset"]).default("navigate").optional(),
|
|
8428
8779
|
path: external_exports.array(external_exports.string().min(1)).optional()
|
|
8429
|
-
}).passthrough()
|
|
8780
|
+
}).passthrough().refine(
|
|
8781
|
+
(payload) => payload.navigationAction === "goBack" || typeof payload.screenName === "string" && payload.screenName.length > 0,
|
|
8782
|
+
{
|
|
8783
|
+
message: 'screenName is required for every navigationAction except "goBack"',
|
|
8784
|
+
path: ["screenName"]
|
|
8785
|
+
}
|
|
8786
|
+
);
|
|
8430
8787
|
var formFillFieldSchema = external_exports.object({
|
|
8431
8788
|
fieldId: external_exports.string().min(1),
|
|
8432
8789
|
fieldType: external_exports.enum(["text", "select", "toggle", "date", "number", "custom"]).default("text").optional(),
|
|
@@ -8760,6 +9117,8 @@ var sandboxObservationSchema = external_exports.object({
|
|
|
8760
9117
|
inputs: external_exports.array(
|
|
8761
9118
|
external_exports.object({
|
|
8762
9119
|
id: external_exports.string().max(120),
|
|
9120
|
+
provenance: identityProvenanceSchema.optional(),
|
|
9121
|
+
onScreen: external_exports.boolean().optional(),
|
|
8763
9122
|
label: external_exports.string().max(200).optional(),
|
|
8764
9123
|
value: external_exports.string().max(2e3).optional(),
|
|
8765
9124
|
disabled: external_exports.boolean().optional(),
|
|
@@ -8773,6 +9132,8 @@ var sandboxObservationSchema = external_exports.object({
|
|
|
8773
9132
|
buttons: external_exports.array(
|
|
8774
9133
|
external_exports.object({
|
|
8775
9134
|
id: external_exports.string().max(120),
|
|
9135
|
+
provenance: identityProvenanceSchema.optional(),
|
|
9136
|
+
onScreen: external_exports.boolean().optional(),
|
|
8776
9137
|
label: external_exports.string().max(200).optional(),
|
|
8777
9138
|
disabled: external_exports.boolean().optional(),
|
|
8778
9139
|
/** Risk metadata (e.g. "high") the SDK forwards for danger UI. */
|
|
@@ -8794,6 +9155,8 @@ var sandboxObservationSchema = external_exports.object({
|
|
|
8794
9155
|
sliders: external_exports.array(
|
|
8795
9156
|
external_exports.object({
|
|
8796
9157
|
id: external_exports.string().max(120),
|
|
9158
|
+
provenance: identityProvenanceSchema.optional(),
|
|
9159
|
+
onScreen: external_exports.boolean().optional(),
|
|
8797
9160
|
label: external_exports.string().max(200).optional(),
|
|
8798
9161
|
value: external_exports.number().finite().optional(),
|
|
8799
9162
|
min: external_exports.number().finite().optional(),
|
|
@@ -8823,6 +9186,44 @@ var sandboxObservationSchema = external_exports.object({
|
|
|
8823
9186
|
).max(200).optional()
|
|
8824
9187
|
}).passthrough()
|
|
8825
9188
|
).max(20).optional(),
|
|
9189
|
+
/**
|
|
9190
|
+
* Toggles exactly as the SDK snapshot reports them (#390).
|
|
9191
|
+
*
|
|
9192
|
+
* Their absence is why `generality-component-control-smarthome`
|
|
9193
|
+
* came back `errored` with a 422 instead of measuring anything:
|
|
9194
|
+
* this schema is `.strict()`, so a scenario that declares a switch
|
|
9195
|
+
* is rejected before the agent is ever asked. Which means the
|
|
9196
|
+
* corpus could not express the single control behind failure 02 of
|
|
9197
|
+
* the functional audit — "disable an agent; the switch never
|
|
9198
|
+
* moves". The one defect the eval most needed to catch was the one
|
|
9199
|
+
* shape it could not describe.
|
|
9200
|
+
*/
|
|
9201
|
+
toggles: external_exports.array(
|
|
9202
|
+
external_exports.object({
|
|
9203
|
+
id: external_exports.string().max(120),
|
|
9204
|
+
provenance: identityProvenanceSchema.optional(),
|
|
9205
|
+
onScreen: external_exports.boolean().optional(),
|
|
9206
|
+
label: external_exports.string().max(200).optional(),
|
|
9207
|
+
value: external_exports.boolean().optional(),
|
|
9208
|
+
disabled: external_exports.boolean().optional()
|
|
9209
|
+
})
|
|
9210
|
+
).max(100).optional(),
|
|
9211
|
+
/**
|
|
9212
|
+
* The interaction graph — the `el:*` ids the prompt tells the model
|
|
9213
|
+
* to prefer over ordinal handles. Passthrough per entry for the
|
|
9214
|
+
* same reason `lists` is: the SDK adds fields faster than this
|
|
9215
|
+
* schema should chase them.
|
|
9216
|
+
*/
|
|
9217
|
+
elements: external_exports.array(
|
|
9218
|
+
external_exports.object({
|
|
9219
|
+
id: external_exports.string().max(160),
|
|
9220
|
+
role: external_exports.string().max(40).optional(),
|
|
9221
|
+
label: external_exports.string().max(300).optional()
|
|
9222
|
+
}).passthrough()
|
|
9223
|
+
).max(500).optional(),
|
|
9224
|
+
/** Screen-level state the relay renders and branches on. */
|
|
9225
|
+
loading: external_exports.boolean().optional(),
|
|
9226
|
+
modalOpen: external_exports.boolean().optional(),
|
|
8826
9227
|
extra: external_exports.record(external_exports.unknown()).optional()
|
|
8827
9228
|
}).strict();
|
|
8828
9229
|
external_exports.object({
|
|
@@ -8863,13 +9264,29 @@ external_exports.object({
|
|
|
8863
9264
|
* heuristics, forces tool choice when the last user message carries a
|
|
8864
9265
|
* failure marker). Only meaningful alongside `history`.
|
|
8865
9266
|
*/
|
|
8866
|
-
isRecoveryHop: external_exports.boolean().optional()
|
|
9267
|
+
isRecoveryHop: external_exports.boolean().optional(),
|
|
9268
|
+
/**
|
|
9269
|
+
* Run this call against the application structure supplied here
|
|
9270
|
+
* instead of the project's active MCP document.
|
|
9271
|
+
*
|
|
9272
|
+
* Sandbox-only, and the relay honours it only on a `dryRun` call —
|
|
9273
|
+
* see `resolveMcpOverride` in apps/api. It exists because the eval
|
|
9274
|
+
* corpus could otherwise only ever describe ONE app: every scenario
|
|
9275
|
+
* resolves to the same seeded project, so every navigate destination,
|
|
9276
|
+
* every screen title and every bit of domain priming in the prompt
|
|
9277
|
+
* came from the same vehicle-management demo. A suite that can only
|
|
9278
|
+
* pose questions about one app cannot measure whether the agent works
|
|
9279
|
+
* on another one.
|
|
9280
|
+
*
|
|
9281
|
+
* Deliberately loose (`z.record`): the MCP document's shape is owned
|
|
9282
|
+
* by the generator, and pinning it here would turn every additive
|
|
9283
|
+
* generator change into a 422 in the sandbox.
|
|
9284
|
+
*/
|
|
9285
|
+
applicationStructure: external_exports.record(external_exports.unknown()).optional()
|
|
8867
9286
|
}).strict();
|
|
8868
9287
|
var relayDiagnosticsSchema = external_exports.object({
|
|
8869
9288
|
unverifiedTargets: external_exports.array(external_exports.string()),
|
|
8870
|
-
guardsFired: external_exports.array(
|
|
8871
|
-
external_exports.object({ guard: external_exports.string(), detail: external_exports.string().optional() })
|
|
8872
|
-
),
|
|
9289
|
+
guardsFired: external_exports.array(external_exports.object({ guard: external_exports.string(), detail: external_exports.string().optional() })),
|
|
8873
9290
|
repairMode: external_exports.enum(["repair", "strict", "disabled"]),
|
|
8874
9291
|
promptVersion: external_exports.string(),
|
|
8875
9292
|
// Defaulted for producers that predate the field: before it existed
|
|
@@ -9128,6 +9545,30 @@ external_exports.object({
|
|
|
9128
9545
|
name: external_exports.string().min(1).max(200).optional(),
|
|
9129
9546
|
identifiers: endUserIdentifiersSchema.optional()
|
|
9130
9547
|
});
|
|
9548
|
+
var MAX_EXTERNAL_USER_ID_LENGTH = 128;
|
|
9549
|
+
function normalizeExternalUserId(value) {
|
|
9550
|
+
let raw;
|
|
9551
|
+
if (typeof value === "string") raw = value;
|
|
9552
|
+
else if (typeof value === "number" && Number.isFinite(value)) raw = String(value);
|
|
9553
|
+
else return void 0;
|
|
9554
|
+
const trimmed = raw.trim();
|
|
9555
|
+
if (!trimmed) return void 0;
|
|
9556
|
+
if (trimmed.length > MAX_EXTERNAL_USER_ID_LENGTH) return void 0;
|
|
9557
|
+
return trimmed;
|
|
9558
|
+
}
|
|
9559
|
+
var MAX_DEVICE_INFO_BYTES = 4096;
|
|
9560
|
+
var sessionDeviceInfoSchema = external_exports.object({
|
|
9561
|
+
platform: external_exports.string().max(64).optional(),
|
|
9562
|
+
osVersion: external_exports.string().max(64).optional(),
|
|
9563
|
+
appVersion: external_exports.string().max(64).optional(),
|
|
9564
|
+
sdkVersion: external_exports.string().max(64).optional()
|
|
9565
|
+
}).passthrough().refine((info) => JSON.stringify(info).length <= MAX_DEVICE_INFO_BYTES, {
|
|
9566
|
+
message: `device_info exceeds ${MAX_DEVICE_INFO_BYTES} bytes`
|
|
9567
|
+
});
|
|
9568
|
+
external_exports.object({
|
|
9569
|
+
userId: external_exports.preprocess(normalizeExternalUserId, external_exports.string().optional()),
|
|
9570
|
+
deviceInfo: sessionDeviceInfoSchema.optional().catch(void 0)
|
|
9571
|
+
});
|
|
9131
9572
|
external_exports.object({
|
|
9132
9573
|
projectId: external_exports.string().min(1).max(64),
|
|
9133
9574
|
externalId: external_exports.string().min(1).max(128),
|
|
@@ -9135,6 +9576,27 @@ external_exports.object({
|
|
|
9135
9576
|
identifiers: endUserIdentifiersSchema.optional()
|
|
9136
9577
|
});
|
|
9137
9578
|
|
|
9579
|
+
// src/utils/stringify.ts
|
|
9580
|
+
function stringifyUnknown(value) {
|
|
9581
|
+
if (value instanceof Error) {
|
|
9582
|
+
const message = value.message || value.name;
|
|
9583
|
+
const cause = value.cause;
|
|
9584
|
+
if (cause instanceof Error && cause.message && cause.message !== message) {
|
|
9585
|
+
return `${message} (${cause.message})`;
|
|
9586
|
+
}
|
|
9587
|
+
return message;
|
|
9588
|
+
}
|
|
9589
|
+
if (typeof value === "string") return value;
|
|
9590
|
+
if (value === null || value === void 0) return String(value);
|
|
9591
|
+
if (typeof value !== "object") return String(value);
|
|
9592
|
+
try {
|
|
9593
|
+
const json = JSON.stringify(value);
|
|
9594
|
+
if (json && json !== "{}") return json;
|
|
9595
|
+
} catch {
|
|
9596
|
+
}
|
|
9597
|
+
return String(value);
|
|
9598
|
+
}
|
|
9599
|
+
|
|
9138
9600
|
// src/manifest/loadManifest.ts
|
|
9139
9601
|
var DEFAULT_MANIFEST_FILENAME = "appilots.manifest.json";
|
|
9140
9602
|
var manifestSchema = external_exports.object({
|
|
@@ -9151,14 +9613,16 @@ async function loadManifest(rootDir, manifestPath) {
|
|
|
9151
9613
|
if (error?.code === "ENOENT") {
|
|
9152
9614
|
return { manifest: null, resolvedPath };
|
|
9153
9615
|
}
|
|
9154
|
-
throw new Error(
|
|
9616
|
+
throw new Error(
|
|
9617
|
+
`Failed to read Appilots manifest at ${resolvedPath}: ${stringifyUnknown(error)}`
|
|
9618
|
+
);
|
|
9155
9619
|
}
|
|
9156
9620
|
let parsedJson;
|
|
9157
9621
|
try {
|
|
9158
9622
|
parsedJson = JSON.parse(raw);
|
|
9159
9623
|
} catch (error) {
|
|
9160
9624
|
throw new Error(
|
|
9161
|
-
`Appilots manifest at ${resolvedPath} is not valid JSON: ${
|
|
9625
|
+
`Appilots manifest at ${resolvedPath} is not valid JSON: ${stringifyUnknown(error)}`
|
|
9162
9626
|
);
|
|
9163
9627
|
}
|
|
9164
9628
|
const result = manifestSchema.safeParse(parsedJson);
|
|
@@ -9172,11 +9636,47 @@ Each entry in "screens" must match the ScreenDescriptor shape (see docs/platform
|
|
|
9172
9636
|
}
|
|
9173
9637
|
return { manifest: result.data, resolvedPath };
|
|
9174
9638
|
}
|
|
9639
|
+
function mergeScreen(base, declared) {
|
|
9640
|
+
const fields = { ...base };
|
|
9641
|
+
for (const [key, value] of Object.entries(declared)) {
|
|
9642
|
+
if (value === void 0) continue;
|
|
9643
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
9644
|
+
fields[key] = value;
|
|
9645
|
+
}
|
|
9646
|
+
const merged = fields;
|
|
9647
|
+
merged.targets = mergeById(base.targets, markManifestOrigin(declared.targets));
|
|
9648
|
+
merged.actions = mergeById(base.actions, declared.actions) ?? [];
|
|
9649
|
+
if (base.agentHints || declared.agentHints) {
|
|
9650
|
+
merged.agentHints = { ...base.agentHints, ...declared.agentHints };
|
|
9651
|
+
}
|
|
9652
|
+
return merged;
|
|
9653
|
+
}
|
|
9654
|
+
function mergeById(base, overlay) {
|
|
9655
|
+
if (!overlay || overlay.length === 0) return base;
|
|
9656
|
+
if (!base || base.length === 0) return overlay;
|
|
9657
|
+
const byId = /* @__PURE__ */ new Map();
|
|
9658
|
+
for (const entry of base) byId.set(entry.id, entry);
|
|
9659
|
+
for (const entry of overlay) {
|
|
9660
|
+
const existing = byId.get(entry.id);
|
|
9661
|
+
byId.set(entry.id, existing ? { ...existing, ...entry } : entry);
|
|
9662
|
+
}
|
|
9663
|
+
return Array.from(byId.values());
|
|
9664
|
+
}
|
|
9665
|
+
function markManifestOrigin(targets) {
|
|
9666
|
+
if (!targets || targets.length === 0) return targets;
|
|
9667
|
+
return targets.map((target) => ({ origin: "manifest", ...target }));
|
|
9668
|
+
}
|
|
9175
9669
|
function mergeManifestScreens(analyzerScreens, manifestScreens) {
|
|
9176
9670
|
if (!manifestScreens || manifestScreens.length === 0) return analyzerScreens;
|
|
9177
9671
|
const byName = /* @__PURE__ */ new Map();
|
|
9178
9672
|
for (const screen of analyzerScreens) byName.set(screen.name, screen);
|
|
9179
|
-
for (const
|
|
9673
|
+
for (const declared of manifestScreens) {
|
|
9674
|
+
const base = byName.get(declared.name);
|
|
9675
|
+
byName.set(
|
|
9676
|
+
declared.name,
|
|
9677
|
+
base ? mergeScreen(base, declared) : { ...declared, targets: markManifestOrigin(declared.targets) }
|
|
9678
|
+
);
|
|
9679
|
+
}
|
|
9180
9680
|
return Array.from(byName.values());
|
|
9181
9681
|
}
|
|
9182
9682
|
function mergeManifestNavigation(analyzerNavigation, manifestNavigation) {
|
|
@@ -9466,7 +9966,12 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
9466
9966
|
}
|
|
9467
9967
|
const mergedScreens = mergeManifestScreens(analyzed.screens, manifest?.screens);
|
|
9468
9968
|
const navigation = mergeManifestNavigation(analyzed.navigation, manifest?.navigation);
|
|
9469
|
-
const agentReadyScreens = mergedScreens.map(
|
|
9969
|
+
const agentReadyScreens = mergedScreens.map(
|
|
9970
|
+
(screen) => enrichScreenForAgent({
|
|
9971
|
+
...screen,
|
|
9972
|
+
filePath: screen.filePath ? path8__default.relative(this.generatorConfig.rootDir, screen.filePath) : screen.filePath
|
|
9973
|
+
})
|
|
9974
|
+
);
|
|
9470
9975
|
const projectInfo = await this.getProjectInfo();
|
|
9471
9976
|
const projectName = projectInfo.name;
|
|
9472
9977
|
console.log(`[MCPGenerator] Project name: ${projectName}`);
|
|
@@ -9489,10 +9994,7 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
9489
9994
|
};
|
|
9490
9995
|
const serialized = JSON.stringify(document, null, 2);
|
|
9491
9996
|
const checksum = this.calculateChecksum(serialized);
|
|
9492
|
-
const filePath = path8__default.resolve(
|
|
9493
|
-
outputDir,
|
|
9494
|
-
`mcp-document.${this.options.format}`
|
|
9495
|
-
);
|
|
9997
|
+
const filePath = path8__default.resolve(outputDir, `mcp-document.${this.options.format}`);
|
|
9496
9998
|
await writeFile(filePath, serialized, "utf-8");
|
|
9497
9999
|
console.log(`[MCPGenerator] Document written to: ${filePath}`);
|
|
9498
10000
|
const checksumFilePath = path8__default.resolve(outputDir, ".appilots-checksum");
|
|
@@ -9547,6 +10049,70 @@ var MCPGenerator = class _MCPGenerator {
|
|
|
9547
10049
|
}
|
|
9548
10050
|
}
|
|
9549
10051
|
};
|
|
10052
|
+
var DEFAULT_SERVER_URL = "http://localhost:4000";
|
|
10053
|
+
var KNOWN_CONFIG_KEYS = [
|
|
10054
|
+
"apiKey",
|
|
10055
|
+
"projectId",
|
|
10056
|
+
"serverUrl",
|
|
10057
|
+
"outputDir",
|
|
10058
|
+
"include",
|
|
10059
|
+
"exclude",
|
|
10060
|
+
"autoActivate",
|
|
10061
|
+
"strictScreens",
|
|
10062
|
+
"screenPatterns",
|
|
10063
|
+
"navigationInclude",
|
|
10064
|
+
"navigationExclude",
|
|
10065
|
+
"platform",
|
|
10066
|
+
"manifestPath",
|
|
10067
|
+
"eval"
|
|
10068
|
+
];
|
|
10069
|
+
var KEY_ALIASES = {
|
|
10070
|
+
apiUrl: "serverUrl",
|
|
10071
|
+
// `@appilots/sdk`'s own name for this. One `.appilotsrc` feeds both
|
|
10072
|
+
// packages, so an integrator who wrote the SDK's key should not be told
|
|
10073
|
+
// it is unrecognized.
|
|
10074
|
+
apiBaseUrl: "serverUrl",
|
|
10075
|
+
apiURL: "serverUrl",
|
|
10076
|
+
baseUrl: "serverUrl",
|
|
10077
|
+
url: "serverUrl",
|
|
10078
|
+
host: "serverUrl",
|
|
10079
|
+
endpoint: "serverUrl",
|
|
10080
|
+
server: "serverUrl",
|
|
10081
|
+
key: "apiKey",
|
|
10082
|
+
token: "apiKey",
|
|
10083
|
+
project: "projectId",
|
|
10084
|
+
sourceDir: "include",
|
|
10085
|
+
srcDir: "include",
|
|
10086
|
+
source: "include",
|
|
10087
|
+
outDir: "outputDir"
|
|
10088
|
+
};
|
|
10089
|
+
function editDistance(a, b) {
|
|
10090
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
10091
|
+
for (let i = 1; i <= a.length; i++) {
|
|
10092
|
+
const current = [i];
|
|
10093
|
+
for (let j = 1; j <= b.length; j++) {
|
|
10094
|
+
const deletion = (previous[j] ?? 0) + 1;
|
|
10095
|
+
const insertion = (current[j - 1] ?? 0) + 1;
|
|
10096
|
+
const substitution = (previous[j - 1] ?? 0) + (a[i - 1] === b[j - 1] ? 0 : 1);
|
|
10097
|
+
current.push(Math.min(deletion, insertion, substitution));
|
|
10098
|
+
}
|
|
10099
|
+
previous = current;
|
|
10100
|
+
}
|
|
10101
|
+
return previous[b.length] ?? 0;
|
|
10102
|
+
}
|
|
10103
|
+
function suggestConfigKey(key) {
|
|
10104
|
+
const alias = KEY_ALIASES[key];
|
|
10105
|
+
if (alias) return alias;
|
|
10106
|
+
const lower = key.toLowerCase();
|
|
10107
|
+
let best;
|
|
10108
|
+
for (const known of KNOWN_CONFIG_KEYS) {
|
|
10109
|
+
const distance = editDistance(lower, known.toLowerCase());
|
|
10110
|
+
if (distance <= 2 && (!best || distance < best.distance)) {
|
|
10111
|
+
best = { key: known, distance };
|
|
10112
|
+
}
|
|
10113
|
+
}
|
|
10114
|
+
return best?.key;
|
|
10115
|
+
}
|
|
9550
10116
|
function getEnvOverrides(env = process.env) {
|
|
9551
10117
|
const clean = (value) => {
|
|
9552
10118
|
const trimmed = value?.trim();
|
|
@@ -9558,7 +10124,7 @@ function getEnvOverrides(env = process.env) {
|
|
|
9558
10124
|
serverUrl: clean(env.APPILOTS_SERVER_URL)
|
|
9559
10125
|
};
|
|
9560
10126
|
}
|
|
9561
|
-
function loadConfig() {
|
|
10127
|
+
function loadConfig(onWarn) {
|
|
9562
10128
|
const configPath = getConfigPath();
|
|
9563
10129
|
const env = getEnvOverrides();
|
|
9564
10130
|
let fileConfig = {};
|
|
@@ -9566,13 +10132,27 @@ function loadConfig() {
|
|
|
9566
10132
|
try {
|
|
9567
10133
|
fileConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
9568
10134
|
} catch (error) {
|
|
9569
|
-
throw new Error(
|
|
10135
|
+
throw new Error(
|
|
10136
|
+
`Failed to parse .appilotsrc at ${configPath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
10137
|
+
);
|
|
9570
10138
|
}
|
|
9571
10139
|
} else if (!env.apiKey) {
|
|
9572
10140
|
return null;
|
|
9573
10141
|
}
|
|
10142
|
+
const legacyWarnings = [];
|
|
10143
|
+
if (fileConfig.serverUrl === void 0) {
|
|
10144
|
+
for (const [alias, canonical] of Object.entries(KEY_ALIASES)) {
|
|
10145
|
+
if (canonical !== "serverUrl" || fileConfig[alias] === void 0) continue;
|
|
10146
|
+
fileConfig.serverUrl = fileConfig[alias];
|
|
10147
|
+
delete fileConfig[alias];
|
|
10148
|
+
legacyWarnings.push(
|
|
10149
|
+
`legacy key "${alias}" \u2014 using it as "serverUrl"; rename it in .appilotsrc`
|
|
10150
|
+
);
|
|
10151
|
+
break;
|
|
10152
|
+
}
|
|
10153
|
+
}
|
|
9574
10154
|
const merged = {
|
|
9575
|
-
serverUrl:
|
|
10155
|
+
serverUrl: DEFAULT_SERVER_URL,
|
|
9576
10156
|
outputDir: ".appilots",
|
|
9577
10157
|
autoActivate: true,
|
|
9578
10158
|
strictScreens: true,
|
|
@@ -9582,6 +10162,26 @@ function loadConfig() {
|
|
|
9582
10162
|
...env.serverUrl ? { serverUrl: env.serverUrl } : {}
|
|
9583
10163
|
};
|
|
9584
10164
|
const validation = validateConfig(merged);
|
|
10165
|
+
validation.warnings.unshift(...legacyWarnings);
|
|
10166
|
+
if (fileConfig.serverUrl === void 0 && env.serverUrl === void 0) {
|
|
10167
|
+
for (const key of Object.keys(fileConfig)) {
|
|
10168
|
+
if (KNOWN_CONFIG_KEYS.includes(key)) continue;
|
|
10169
|
+
if (suggestConfigKey(key) !== "serverUrl") continue;
|
|
10170
|
+
validation.warnings = validation.warnings.filter(
|
|
10171
|
+
(warning) => !warning.startsWith(`unknown key "${key}"`)
|
|
10172
|
+
);
|
|
10173
|
+
validation.errors.push(
|
|
10174
|
+
`unknown key "${key}" looks like "serverUrl" \u2014 not falling back to ${DEFAULT_SERVER_URL}; rename the key to "serverUrl"`
|
|
10175
|
+
);
|
|
10176
|
+
validation.valid = false;
|
|
10177
|
+
}
|
|
10178
|
+
}
|
|
10179
|
+
if (onWarn) {
|
|
10180
|
+
const source = configPath ?? "environment variables";
|
|
10181
|
+
for (const warning of validation.warnings) {
|
|
10182
|
+
onWarn(`${source}: ${warning}`);
|
|
10183
|
+
}
|
|
10184
|
+
}
|
|
9585
10185
|
if (!validation.valid) {
|
|
9586
10186
|
const source = configPath ?? "environment variables";
|
|
9587
10187
|
throw new Error(
|
|
@@ -9597,7 +10197,7 @@ function saveConfig(dir, config) {
|
|
|
9597
10197
|
const mergedConfig = {
|
|
9598
10198
|
apiKey: config.apiKey || existingConfig?.apiKey || "",
|
|
9599
10199
|
projectId: config.projectId || existingConfig?.projectId || "",
|
|
9600
|
-
serverUrl: config.serverUrl || existingConfig?.serverUrl ||
|
|
10200
|
+
serverUrl: config.serverUrl || existingConfig?.serverUrl || DEFAULT_SERVER_URL,
|
|
9601
10201
|
outputDir: config.outputDir || existingConfig?.outputDir || ".appilots",
|
|
9602
10202
|
autoActivate: config.autoActivate !== void 0 ? config.autoActivate : existingConfig?.autoActivate ?? true,
|
|
9603
10203
|
include: config.include || existingConfig?.include,
|
|
@@ -9613,7 +10213,9 @@ function saveConfig(dir, config) {
|
|
|
9613
10213
|
try {
|
|
9614
10214
|
writeFileSync(configPath, JSON.stringify(mergedConfig, null, 2), "utf-8");
|
|
9615
10215
|
} catch (error) {
|
|
9616
|
-
throw new Error(
|
|
10216
|
+
throw new Error(
|
|
10217
|
+
`Failed to write .appilotsrc to ${configPath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
10218
|
+
);
|
|
9617
10219
|
}
|
|
9618
10220
|
}
|
|
9619
10221
|
function getConfigPath() {
|
|
@@ -9640,14 +10242,25 @@ function getConfigPath() {
|
|
|
9640
10242
|
}
|
|
9641
10243
|
function validateConfig(config) {
|
|
9642
10244
|
const errors = [];
|
|
10245
|
+
const warnings = [];
|
|
9643
10246
|
if (!config || typeof config !== "object") {
|
|
9644
10247
|
return {
|
|
9645
10248
|
valid: false,
|
|
9646
|
-
errors: ["Configuration must be an object"]
|
|
10249
|
+
errors: ["Configuration must be an object"],
|
|
10250
|
+
warnings
|
|
9647
10251
|
};
|
|
9648
10252
|
}
|
|
10253
|
+
for (const key of Object.keys(config)) {
|
|
10254
|
+
if (KNOWN_CONFIG_KEYS.includes(key)) continue;
|
|
10255
|
+
const suggestion = suggestConfigKey(key);
|
|
10256
|
+
warnings.push(
|
|
10257
|
+
`unknown key "${key}" \u2014 ignored` + (suggestion ? ` (did you mean "${suggestion}"?)` : "")
|
|
10258
|
+
);
|
|
10259
|
+
}
|
|
9649
10260
|
if (!config.apiKey || typeof config.apiKey !== "string") {
|
|
9650
|
-
errors.push(
|
|
10261
|
+
errors.push(
|
|
10262
|
+
"apiKey is required and must be a string (set it in .appilotsrc or via APPILOTS_API_KEY)"
|
|
10263
|
+
);
|
|
9651
10264
|
} else if (!config.apiKey.startsWith("ak_")) {
|
|
9652
10265
|
errors.push('apiKey must start with "ak_"');
|
|
9653
10266
|
}
|
|
@@ -9709,7 +10322,8 @@ function validateConfig(config) {
|
|
|
9709
10322
|
}
|
|
9710
10323
|
return {
|
|
9711
10324
|
valid: errors.length === 0,
|
|
9712
|
-
errors
|
|
10325
|
+
errors,
|
|
10326
|
+
warnings
|
|
9713
10327
|
};
|
|
9714
10328
|
}
|
|
9715
10329
|
|
|
@@ -9755,19 +10369,76 @@ function formatMetadataWarnings(warnings) {
|
|
|
9755
10369
|
);
|
|
9756
10370
|
}
|
|
9757
10371
|
|
|
10372
|
+
// ../shared/dist/chunk-UEMUBXFH.mjs
|
|
10373
|
+
var DEFAULT_API_BASE_URL = "https://api.appilots.com";
|
|
10374
|
+
var APPILOTS_API_PATH_PREFIX = "/api/v1";
|
|
10375
|
+
function normalizeApiBaseUrl(configured) {
|
|
10376
|
+
const raw = (configured ?? DEFAULT_API_BASE_URL).trim().replace(/\/+$/, "");
|
|
10377
|
+
if (!raw) return `${DEFAULT_API_BASE_URL}${APPILOTS_API_PATH_PREFIX}`;
|
|
10378
|
+
if (new RegExp(`${APPILOTS_API_PATH_PREFIX}(/|$)`).test(raw)) return raw;
|
|
10379
|
+
return `${raw}${APPILOTS_API_PATH_PREFIX}`;
|
|
10380
|
+
}
|
|
10381
|
+
|
|
9758
10382
|
// src/services/api-client.ts
|
|
9759
|
-
var
|
|
10383
|
+
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
9760
10384
|
var DEFAULT_MAX_RETRIES = 2;
|
|
9761
10385
|
var RETRY_BASE_DELAY_MS = 500;
|
|
10386
|
+
function describeNetworkError(url, err) {
|
|
10387
|
+
let origin = url;
|
|
10388
|
+
try {
|
|
10389
|
+
origin = new URL(url).origin;
|
|
10390
|
+
} catch {
|
|
10391
|
+
}
|
|
10392
|
+
const cause = err?.cause;
|
|
10393
|
+
const code = typeof cause?.code === "string" ? cause.code : void 0;
|
|
10394
|
+
const detail = code ?? stringifyUnknown(err);
|
|
10395
|
+
return new Error(`Cannot reach Appilots server at ${origin} (${detail})`);
|
|
10396
|
+
}
|
|
10397
|
+
function errorCodeOf(payload) {
|
|
10398
|
+
const error = payload?.error;
|
|
10399
|
+
if (error && typeof error === "object") {
|
|
10400
|
+
const { code } = error;
|
|
10401
|
+
if (typeof code === "string" && code.trim()) return code;
|
|
10402
|
+
}
|
|
10403
|
+
return void 0;
|
|
10404
|
+
}
|
|
10405
|
+
function describeApiError(payload, fallback) {
|
|
10406
|
+
const error = payload?.error;
|
|
10407
|
+
if (typeof error === "string" && error.trim()) return error;
|
|
10408
|
+
if (error && typeof error === "object") {
|
|
10409
|
+
const { code, message: message2 } = error;
|
|
10410
|
+
const text = typeof message2 === "string" && message2.trim() ? message2 : void 0;
|
|
10411
|
+
const tag = typeof code === "string" && code.trim() ? code : void 0;
|
|
10412
|
+
if (text && tag) return `${text} (${tag})`;
|
|
10413
|
+
if (text) return text;
|
|
10414
|
+
if (tag) return tag;
|
|
10415
|
+
}
|
|
10416
|
+
const message = payload?.message;
|
|
10417
|
+
if (typeof message === "string" && message.trim()) return message;
|
|
10418
|
+
return fallback;
|
|
10419
|
+
}
|
|
9762
10420
|
var AppilotsAPIClient = class {
|
|
9763
|
-
|
|
10421
|
+
/**
|
|
10422
|
+
* `serverUrl` with the API's mount prefix resolved — every path below
|
|
10423
|
+
* is relative to THIS, not to the configured origin.
|
|
10424
|
+
*
|
|
10425
|
+
* It used to be the raw `serverUrl` with `/api/v1` hardcoded into each
|
|
10426
|
+
* path, which made the field mean something different here than in the
|
|
10427
|
+
* SDK. Both read the same `.appilotsrc`: the SDK completes the prefix
|
|
10428
|
+
* when it is missing, so `https://api.appilots.com/api/v1` is correct
|
|
10429
|
+
* there — and here that same value produced
|
|
10430
|
+
* `/api/v1/api/v1/cli/sync`, a 404 whose body reads `Route not found`.
|
|
10431
|
+
* Sharing `normalizeApiBaseUrl` is what makes one file mean one thing:
|
|
10432
|
+
* with or without the prefix now works in both.
|
|
10433
|
+
*/
|
|
10434
|
+
baseUrl;
|
|
9764
10435
|
apiKey;
|
|
9765
10436
|
timeoutMs;
|
|
9766
10437
|
maxRetries;
|
|
9767
10438
|
constructor(config) {
|
|
9768
|
-
this.
|
|
10439
|
+
this.baseUrl = normalizeApiBaseUrl(config.serverUrl);
|
|
9769
10440
|
this.apiKey = config.apiKey;
|
|
9770
|
-
this.timeoutMs = config.timeoutMs ??
|
|
10441
|
+
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
9771
10442
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
9772
10443
|
}
|
|
9773
10444
|
/**
|
|
@@ -9791,7 +10462,7 @@ var AppilotsAPIClient = class {
|
|
|
9791
10462
|
}
|
|
9792
10463
|
return response;
|
|
9793
10464
|
} catch (err) {
|
|
9794
|
-
lastError = controller.signal.aborted ? new Error(`Request timed out after ${this.timeoutMs}ms: ${url}`) : err;
|
|
10465
|
+
lastError = controller.signal.aborted ? new Error(`Request timed out after ${this.timeoutMs}ms: ${url}`) : describeNetworkError(url, err);
|
|
9795
10466
|
} finally {
|
|
9796
10467
|
clearTimeout(timer);
|
|
9797
10468
|
}
|
|
@@ -9810,7 +10481,7 @@ var AppilotsAPIClient = class {
|
|
|
9810
10481
|
*/
|
|
9811
10482
|
async sync(content, version, appVersion) {
|
|
9812
10483
|
try {
|
|
9813
|
-
const response = await this.request(`${this.
|
|
10484
|
+
const response = await this.request(`${this.baseUrl}/cli/sync`, {
|
|
9814
10485
|
method: "POST",
|
|
9815
10486
|
headers: {
|
|
9816
10487
|
"Content-Type": "application/json",
|
|
@@ -9824,7 +10495,9 @@ var AppilotsAPIClient = class {
|
|
|
9824
10495
|
return {
|
|
9825
10496
|
success: false,
|
|
9826
10497
|
unchanged: false,
|
|
9827
|
-
error: errorData
|
|
10498
|
+
error: describeApiError(errorData, `HTTP ${response.status}: ${response.statusText}`),
|
|
10499
|
+
errorCode: errorCodeOf(errorData),
|
|
10500
|
+
errorStatus: response.status
|
|
9828
10501
|
};
|
|
9829
10502
|
}
|
|
9830
10503
|
const json = await response.json();
|
|
@@ -9837,7 +10510,8 @@ var AppilotsAPIClient = class {
|
|
|
9837
10510
|
return {
|
|
9838
10511
|
success: false,
|
|
9839
10512
|
unchanged: false,
|
|
9840
|
-
error: error instanceof Error ? error.message : "Failed to sync with Appilots API"
|
|
10513
|
+
error: error instanceof Error ? error.message : "Failed to sync with Appilots API",
|
|
10514
|
+
errorStatus: 0
|
|
9841
10515
|
};
|
|
9842
10516
|
}
|
|
9843
10517
|
}
|
|
@@ -9848,7 +10522,7 @@ var AppilotsAPIClient = class {
|
|
|
9848
10522
|
*/
|
|
9849
10523
|
async status() {
|
|
9850
10524
|
try {
|
|
9851
|
-
const response = await this.request(`${this.
|
|
10525
|
+
const response = await this.request(`${this.baseUrl}/cli/status`, {
|
|
9852
10526
|
method: "GET",
|
|
9853
10527
|
headers: {
|
|
9854
10528
|
Authorization: `Bearer ${this.apiKey}`
|
|
@@ -9857,7 +10531,7 @@ var AppilotsAPIClient = class {
|
|
|
9857
10531
|
if (!response.ok) {
|
|
9858
10532
|
const errorData = await response.json().catch(() => ({}));
|
|
9859
10533
|
return {
|
|
9860
|
-
error: errorData
|
|
10534
|
+
error: describeApiError(errorData, `HTTP ${response.status}: ${response.statusText}`)
|
|
9861
10535
|
};
|
|
9862
10536
|
}
|
|
9863
10537
|
const json = await response.json();
|
|
@@ -9879,7 +10553,7 @@ var AppilotsAPIClient = class {
|
|
|
9879
10553
|
*/
|
|
9880
10554
|
async evalRun(scenarios) {
|
|
9881
10555
|
try {
|
|
9882
|
-
const response = await this.request(`${this.
|
|
10556
|
+
const response = await this.request(`${this.baseUrl}/cli/eval/run`, {
|
|
9883
10557
|
method: "POST",
|
|
9884
10558
|
headers: {
|
|
9885
10559
|
"Content-Type": "application/json",
|
|
@@ -9910,7 +10584,7 @@ var AppilotsAPIClient = class {
|
|
|
9910
10584
|
*/
|
|
9911
10585
|
async health() {
|
|
9912
10586
|
try {
|
|
9913
|
-
const response = await this.request(`${this.
|
|
10587
|
+
const response = await this.request(`${this.baseUrl}/health`, {
|
|
9914
10588
|
method: "GET"
|
|
9915
10589
|
});
|
|
9916
10590
|
return response.ok;
|
|
@@ -9920,6 +10594,9 @@ var AppilotsAPIClient = class {
|
|
|
9920
10594
|
}
|
|
9921
10595
|
};
|
|
9922
10596
|
|
|
9923
|
-
|
|
10597
|
+
// src/version.ts
|
|
10598
|
+
var CLI_VERSION = "0.11.0";
|
|
10599
|
+
|
|
10600
|
+
export { AppilotsAPIClient, CLI_VERSION, ComponentAnalyzer, DEFAULT_MANIFEST_FILENAME, DEFAULT_WEB_SCREEN_PATTERNS, FormAnalyzer, GenericPlatformAnalyzer, MCPGenerator, NavigationAnalyzer, ReactNativePlatformAnalyzer, ReactWebPlatformAnalyzer, ScreenAnalyzer, WebNavigationAnalyzer, WebScreenAnalyzer, formatMetadataWarnings, getConfigPath, getEnvOverrides, lintActionMetadata, loadConfig, loadManifest, mergeManifestNavigation, mergeManifestScreens, resolvePathToScreen, saveConfig, screenNameFromPath, validateConfig };
|
|
9924
10601
|
//# sourceMappingURL=index.mjs.map
|
|
9925
10602
|
//# sourceMappingURL=index.mjs.map
|