sf-intelligence 0.1.0 → 0.1.4
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/index.js +392 -55
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1053,12 +1053,34 @@ var init_resolve = __esm({
|
|
|
1053
1053
|
qt,
|
|
1054
1054
|
...scoreToken(qt, node.tokens)
|
|
1055
1055
|
}));
|
|
1056
|
+
const nameMatchedSomething = perToken.some((t) => t.score >= MATCHED_FLOOR);
|
|
1057
|
+
let parentMatched = false;
|
|
1058
|
+
if (nameMatchedSomething && node.parentApiName) {
|
|
1059
|
+
const parentTokens = tokenizeText(node.parentApiName);
|
|
1060
|
+
if (parentTokens.length > 0) {
|
|
1061
|
+
for (let i = 0; i < perToken.length; i += 1) {
|
|
1062
|
+
if (perToken[i].score >= MATCHED_FLOOR)
|
|
1063
|
+
continue;
|
|
1064
|
+
const pm = scoreToken(queryTokens[i], parentTokens);
|
|
1065
|
+
if (pm.score > perToken[i].score) {
|
|
1066
|
+
perToken[i] = {
|
|
1067
|
+
qt: queryTokens[i],
|
|
1068
|
+
score: pm.score,
|
|
1069
|
+
kind: "substring",
|
|
1070
|
+
matchedToken: pm.matchedToken
|
|
1071
|
+
};
|
|
1072
|
+
if (pm.score >= MATCHED_FLOOR)
|
|
1073
|
+
parentMatched = true;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1056
1078
|
for (let i = 0; i < perToken.length; i += 1) {
|
|
1057
1079
|
if (perToken[i].score > globalBest[i])
|
|
1058
1080
|
globalBest[i] = perToken[i].score;
|
|
1059
1081
|
}
|
|
1060
|
-
const wholeExact = normQuery.length >= 2 && node.normName === normQuery;
|
|
1061
|
-
pass1.push({ node, perToken, wholeExact });
|
|
1082
|
+
const wholeExact = normQuery.length >= 2 && node.normName === normQuery && query.includes(".") === node.apiName.includes(".");
|
|
1083
|
+
pass1.push({ node, perToken, wholeExact, parentMatched });
|
|
1062
1084
|
}
|
|
1063
1085
|
const anchorIdx = [];
|
|
1064
1086
|
for (let i = 0; i < queryTokens.length; i += 1) {
|
|
@@ -1069,6 +1091,7 @@ var init_resolve = __esm({
|
|
|
1069
1091
|
const scored = [];
|
|
1070
1092
|
const coverageById = /* @__PURE__ */ new Map();
|
|
1071
1093
|
const wholeExactIds = /* @__PURE__ */ new Set();
|
|
1094
|
+
const parentMatchedIds = /* @__PURE__ */ new Set();
|
|
1072
1095
|
for (const c of pass1) {
|
|
1073
1096
|
const matched = c.perToken.filter((t) => t.score >= MATCHED_FLOOR);
|
|
1074
1097
|
if (matched.length === 0 && !c.wholeExact)
|
|
@@ -1085,10 +1108,13 @@ var init_resolve = __esm({
|
|
|
1085
1108
|
const refs = c.node.inbound;
|
|
1086
1109
|
const score = base * typeWeight(type) * (1 + POP_K * Math.log10(1 + refs));
|
|
1087
1110
|
const kind = c.wholeExact ? "exact" : rollupKind(matched);
|
|
1088
|
-
const
|
|
1111
|
+
const nodeTokenSet = new Set(c.node.tokens);
|
|
1112
|
+
const matchedNodeTokens = new Set(matched.map((m) => m.matchedToken).filter((t) => nodeTokenSet.has(t)));
|
|
1089
1113
|
coverageById.set(c.node.id, c.wholeExact ? 1 : matchedNodeTokens.size / c.node.tokens.length);
|
|
1090
1114
|
if (c.wholeExact)
|
|
1091
1115
|
wholeExactIds.add(c.node.id);
|
|
1116
|
+
if (c.parentMatched)
|
|
1117
|
+
parentMatchedIds.add(c.node.id);
|
|
1092
1118
|
scored.push({
|
|
1093
1119
|
id: c.node.id,
|
|
1094
1120
|
type,
|
|
@@ -1110,6 +1136,10 @@ var init_resolve = __esm({
|
|
|
1110
1136
|
const tierB = b.base >= EXACT_THRESHOLD ? 0 : 1;
|
|
1111
1137
|
if (tierA !== tierB)
|
|
1112
1138
|
return tierA - tierB;
|
|
1139
|
+
const pmA = parentMatchedIds.has(a.id) ? 0 : 1;
|
|
1140
|
+
const pmB = parentMatchedIds.has(b.id) ? 0 : 1;
|
|
1141
|
+
if (pmA !== pmB)
|
|
1142
|
+
return pmA - pmB;
|
|
1113
1143
|
if (a.score !== b.score)
|
|
1114
1144
|
return b.score - a.score;
|
|
1115
1145
|
return a.id < b.id ? -1 : 1;
|
|
@@ -1121,7 +1151,7 @@ var init_resolve = __esm({
|
|
|
1121
1151
|
if (top === void 0 || bestBase < NONE_THRESHOLD) {
|
|
1122
1152
|
disposition = "none";
|
|
1123
1153
|
} else {
|
|
1124
|
-
const contenders = candidates.filter((c) => c.score >= top.score * CONTENDER_RATIO);
|
|
1154
|
+
const contenders = candidates.filter((c) => c.score >= top.score * CONTENDER_RATIO || parentMatchedIds.has(c.id));
|
|
1125
1155
|
const topCoverage = coverageById.get(top.id) ?? 0;
|
|
1126
1156
|
disposition = top.base >= EXACT_THRESHOLD && contenders.length === 1 && topCoverage >= EXACT_COVERAGE ? "exact" : "ambiguous";
|
|
1127
1157
|
}
|
|
@@ -1549,6 +1579,7 @@ var init_manifest = __esm({
|
|
|
1549
1579
|
const coveredTypes = filtered.filter((entry) => entry.requested && !entry.errored && !entry.neverModeled).map((entry) => entry.type);
|
|
1550
1580
|
const partialTypes = filtered.filter((entry) => entry.errored && !entry.neverModeled).map((entry) => entry.type);
|
|
1551
1581
|
const notModeledTypes = filtered.filter((entry) => entry.neverModeled).map((entry) => entry.type);
|
|
1582
|
+
const notRequestedTypes = filtered.filter((entry) => !entry.requested && !entry.neverModeled).map((entry) => entry.type);
|
|
1552
1583
|
if (wanted !== null) {
|
|
1553
1584
|
const knownTypes = new Set(filtered.map((entry) => entry.type));
|
|
1554
1585
|
for (const type of wanted) {
|
|
@@ -1557,7 +1588,9 @@ var init_manifest = __esm({
|
|
|
1557
1588
|
}
|
|
1558
1589
|
}
|
|
1559
1590
|
}
|
|
1560
|
-
const missingCoverage = [
|
|
1591
|
+
const missingCoverage = [
|
|
1592
|
+
.../* @__PURE__ */ new Set([...partialTypes, ...notModeledTypes, ...notRequestedTypes])
|
|
1593
|
+
].sort();
|
|
1561
1594
|
const coverageKnown = readCoverageEntries(manifest).length > 0;
|
|
1562
1595
|
const status = missingCoverage.length > 0 ? "partial" : coverageKnown ? "complete" : "unknown";
|
|
1563
1596
|
return {
|
|
@@ -7615,7 +7648,7 @@ var init_explain_field = __esm({
|
|
|
7615
7648
|
|
|
7616
7649
|
// ../mcp/dist/src/tools/explain-flow.js
|
|
7617
7650
|
import { z as z32 } from "zod";
|
|
7618
|
-
var FLOW_PREFIX, DISCLOSURE2, explainFlowInputSchema, readFlowLabel, readFlowStatus, readFlowProcessType, readFlowTriggerType, findTriggerObject, collectTriggerConditions, stripObjectPrefix, collectActionCalls, prefixOf, collectRecordLookups, classifyWriteOperation, collectRecordWrites, decisionNameOf, collectDecisions, explainFlowHandler;
|
|
7651
|
+
var FLOW_PREFIX, DISCLOSURE2, explainFlowInputSchema, readFlowLabel, readFlowStatus, readFlowProcessType, readFlowTriggerType, findTriggerObject, collectTriggerConditions, stripObjectPrefix, collectActionCalls, prefixOf, collectRecordLookups, classifyWriteOperation, collectRecordWrites, decisionNameOf, collectDecisions, ALTERNATE_TYPE_PREFIXES, findAlternateTypeId, explainFlowHandler;
|
|
7619
7652
|
var init_explain_flow = __esm({
|
|
7620
7653
|
"../mcp/dist/src/tools/explain-flow.js"() {
|
|
7621
7654
|
"use strict";
|
|
@@ -7788,6 +7821,21 @@ var init_explain_flow = __esm({
|
|
|
7788
7821
|
}
|
|
7789
7822
|
return out;
|
|
7790
7823
|
};
|
|
7824
|
+
ALTERNATE_TYPE_PREFIXES = [
|
|
7825
|
+
"ApexTrigger",
|
|
7826
|
+
"ApexClass",
|
|
7827
|
+
"WorkflowRule",
|
|
7828
|
+
"ValidationRule"
|
|
7829
|
+
];
|
|
7830
|
+
findAlternateTypeId = async (ctx, bareName) => {
|
|
7831
|
+
for (const prefix of ALTERNATE_TYPE_PREFIXES) {
|
|
7832
|
+
const candidate = `${prefix}:${bareName}`;
|
|
7833
|
+
const r = await getNodeById(ctx.graph, candidate);
|
|
7834
|
+
if (r.ok && r.value !== null)
|
|
7835
|
+
return candidate;
|
|
7836
|
+
}
|
|
7837
|
+
return null;
|
|
7838
|
+
};
|
|
7791
7839
|
explainFlowHandler = async (ctx, input2) => {
|
|
7792
7840
|
const coercedFlowId = coercePrefix(input2.flowId, [FLOW_PREFIX]);
|
|
7793
7841
|
if (!coercedFlowId.startsWith(FLOW_PREFIX)) {
|
|
@@ -7806,9 +7854,11 @@ var init_explain_flow = __esm({
|
|
|
7806
7854
|
});
|
|
7807
7855
|
}
|
|
7808
7856
|
if (nodeResult.value === null) {
|
|
7857
|
+
const bareName = flowId.slice(FLOW_PREFIX.length);
|
|
7858
|
+
const alt = await findAlternateTypeId(ctx, bareName);
|
|
7809
7859
|
return err({
|
|
7810
7860
|
kind: "component-not-found",
|
|
7811
|
-
message: `no Flow with id ${flowId}`,
|
|
7861
|
+
message: alt ? `no Flow named '${bareName}', but '${alt}' exists \u2014 it is a ${alt.slice(0, alt.indexOf(":"))}, not a Flow. explain_flow only handles Flows; use get_component (or the matching trigger/apex tool) for '${alt}'.` : `no Flow with id ${flowId}`,
|
|
7812
7862
|
path: flowId
|
|
7813
7863
|
});
|
|
7814
7864
|
}
|
|
@@ -9345,11 +9395,11 @@ var init_naming_convention = __esm({
|
|
|
9345
9395
|
parseScope = (scope) => {
|
|
9346
9396
|
if (scope === void 0 || scope === "all")
|
|
9347
9397
|
return ok(null);
|
|
9348
|
-
const match = /^CustomField:([^.]+)
|
|
9398
|
+
const match = /^CustomField:([^.]+)(?:\.\*)?$/.exec(scope);
|
|
9349
9399
|
if (match === null) {
|
|
9350
9400
|
return err({
|
|
9351
9401
|
kind: "invalid-scope",
|
|
9352
|
-
message: `unrecognized scope "${scope}"; expected 'all' or 'CustomField:{ObjectApiName}.*'`
|
|
9402
|
+
message: `unrecognized scope "${scope}"; expected 'all', 'CustomField:{ObjectApiName}', or 'CustomField:{ObjectApiName}.*'`
|
|
9353
9403
|
});
|
|
9354
9404
|
}
|
|
9355
9405
|
return ok(match[1]);
|
|
@@ -17053,7 +17103,7 @@ var init_get_edges = __esm({
|
|
|
17053
17103
|
];
|
|
17054
17104
|
getEdgesInputSchema = z65.object({
|
|
17055
17105
|
nodeId: z65.string().min(1),
|
|
17056
|
-
direction: z65.enum(["in", "out", "both"]).optional(),
|
|
17106
|
+
direction: z65.preprocess((v) => v === "incoming" ? "in" : v === "outgoing" ? "out" : v, z65.enum(["in", "out", "both"])).optional(),
|
|
17057
17107
|
edgeType: z65.enum(EDGE_TYPES).optional(),
|
|
17058
17108
|
confidence: z65.enum(CONFIDENCE_LEVELS).optional()
|
|
17059
17109
|
});
|
|
@@ -18920,7 +18970,7 @@ var init_live_consent = __esm({
|
|
|
18920
18970
|
import { execFile as execFile2 } from "node:child_process";
|
|
18921
18971
|
import { promisify as promisify2 } from "node:util";
|
|
18922
18972
|
import { z as z73 } from "zod";
|
|
18923
|
-
var nodeExecFile2, redactSecrets, LIVE_PLANE_DISCLOSURE, MAX_SAMPLE_ROWS, liveEnabledSchema, isLivePlaneEnabled, liveTrust, resolveOrg, resolveLiveAccess, liveConsentRequiredError, gateLive, getLiveAuth, runSfJson, apiPath, restGet, liveDescribeInputSchema, liveDescribeHandler, liveCountInputSchema, assertCountSoql, liveCountHandler, liveSampleInputSchema, capSampleSoql, liveSampleHandler, liveFieldPopulationInputSchema, liveFieldPopulationHandler, liveOrgLimitsInputSchema, liveOrgLimitsHandler, MAX_INACTIVE_USER_ROWS, DEFAULT_INACTIVE_DAYS, MS_PER_DAY, liveInactiveUsersInputSchema, soqlDateTime, liveInactiveUsersHandler, DEFAULT_LICENSE_INACTIVE_DAYS, MAX_RECLAIM_ROWS, LICENSE_USAGE_DISCLOSURE, liveLicenseUsageInputSchema, toUtilization, renderLicenseUsageMarkdown, liveLicenseUsageHandler, liveConsentInputSchema, consentTrust, liveConsentHandler, liveQuery, MAX_DETAIL_ROWS, daysAgoSoql, daysSince, livePlaneVaultState, UNAVAILABLE_ERROR, assertSoqlIdentifier, soqlLiteral, MAX_GROUP_BUCKETS, DEFAULT_STALE_DAYS, DEFAULT_RECENT_DAYS, liveGroupCountInputSchema, liveGroupCountHandler, liveStaleRecordsInputSchema, liveStaleRecordsHandler, liveRecentActivityInputSchema, liveRecentActivityHandler, buildEqualityWhere, aggregateCountFromRow, liveAggregateInputSchema, liveAggregateHandler, MAX_DUPLICATE_GROUPS, liveDuplicateCheckInputSchema, liveDuplicateCheckHandler, MAX_OWNER_BUCKETS, liveOwnerBreakdownInputSchema, liveOwnerBreakdownHandler, DEFAULT_REPORT_STALE_DAYS, liveReportUsageInputSchema, liveReportUsageHandler, liveFolderAccessInputSchema, liveFolderAccessHandler, DEFAULT_TEMPLATE_STALE_DAYS, CLASSIC_TEMPLATE_TYPES, liveEmailTemplateUsageInputSchema, liveEmailTemplateUsageHandler, DEFAULT_HEALTH_DAYS, LIMIT_RISK_THRESHOLD, liveOrgHealthInputSchema, liveOrgHealthHandler, liveStorageByObjectInputSchema, liveStorageByObjectHandler, liveDataSkewInputSchema, liveSetupAuditTrailInputSchema, liveSecurityExposureInputSchema;
|
|
18973
|
+
var nodeExecFile2, redactSecrets, LIVE_PLANE_DISCLOSURE, MAX_SAMPLE_ROWS, liveEnabledSchema, isLivePlaneEnabled, liveTrust, resolveOrg, resolveLiveAccess, liveConsentRequiredError, gateLive, getLiveAuth, runSfJson, apiPath, restGet, liveDescribeInputSchema, liveDescribeHandler, liveCountInputSchema, OBJECT_API_NAME_RE, resolveCountSoql, assertCountSoql, liveCountHandler, liveSampleInputSchema, capSampleSoql, liveSampleHandler, liveFieldPopulationInputSchema, liveFieldPopulationHandler, liveOrgLimitsInputSchema, liveOrgLimitsHandler, MAX_INACTIVE_USER_ROWS, DEFAULT_INACTIVE_DAYS, MS_PER_DAY, liveInactiveUsersInputSchema, soqlDateTime, liveInactiveUsersHandler, DEFAULT_LICENSE_INACTIVE_DAYS, MAX_RECLAIM_ROWS, LICENSE_USAGE_DISCLOSURE, liveLicenseUsageInputSchema, toUtilization, renderLicenseUsageMarkdown, liveLicenseUsageHandler, liveConsentInputSchema, consentTrust, liveConsentHandler, liveQuery, MAX_DETAIL_ROWS, daysAgoSoql, daysSince, livePlaneVaultState, UNAVAILABLE_ERROR, assertSoqlIdentifier, soqlLiteral, MAX_GROUP_BUCKETS, DEFAULT_STALE_DAYS, DEFAULT_RECENT_DAYS, liveGroupCountInputSchema, liveGroupCountHandler, liveStaleRecordsInputSchema, liveStaleRecordsHandler, liveRecentActivityInputSchema, liveRecentActivityHandler, buildEqualityWhere, aggregateCountFromRow, liveAggregateInputSchema, liveAggregateHandler, MAX_DUPLICATE_GROUPS, liveDuplicateCheckInputSchema, liveDuplicateCheckHandler, MAX_OWNER_BUCKETS, liveOwnerBreakdownInputSchema, liveOwnerBreakdownHandler, DEFAULT_REPORT_STALE_DAYS, liveReportUsageInputSchema, liveReportUsageHandler, liveFolderAccessInputSchema, liveFolderAccessHandler, DEFAULT_TEMPLATE_STALE_DAYS, CLASSIC_TEMPLATE_TYPES, liveEmailTemplateUsageInputSchema, liveEmailTemplateUsageHandler, DEFAULT_HEALTH_DAYS, LIMIT_RISK_THRESHOLD, liveOrgHealthInputSchema, liveOrgHealthHandler, liveStorageByObjectInputSchema, liveStorageByObjectHandler, liveDataSkewInputSchema, liveSetupAuditTrailInputSchema, liveSecurityExposureInputSchema;
|
|
18924
18974
|
var init_live_plane = __esm({
|
|
18925
18975
|
"../mcp/dist/src/tools/live-plane.js"() {
|
|
18926
18976
|
"use strict";
|
|
@@ -19037,9 +19087,33 @@ var init_live_plane = __esm({
|
|
|
19037
19087
|
});
|
|
19038
19088
|
};
|
|
19039
19089
|
liveCountInputSchema = liveEnabledSchema.extend({
|
|
19040
|
-
soql
|
|
19090
|
+
// Either `soql` (a SELECT COUNT() query) OR `objectApiName` (count every row
|
|
19091
|
+
// of that object). Both optional at the schema level; the handler requires
|
|
19092
|
+
// exactly one and turns objectApiName into `SELECT COUNT() FROM <object>`.
|
|
19093
|
+
soql: z73.string().min(1).optional(),
|
|
19094
|
+
objectApiName: z73.string().min(1).optional(),
|
|
19041
19095
|
orgAlias: z73.string().min(1).optional()
|
|
19042
19096
|
});
|
|
19097
|
+
OBJECT_API_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
19098
|
+
resolveCountSoql = (input2) => {
|
|
19099
|
+
if (input2.soql !== void 0)
|
|
19100
|
+
return ok(input2.soql);
|
|
19101
|
+
if (input2.objectApiName !== void 0) {
|
|
19102
|
+
if (!OBJECT_API_NAME_RE.test(input2.objectApiName)) {
|
|
19103
|
+
return err({
|
|
19104
|
+
kind: "invalid-query",
|
|
19105
|
+
message: `objectApiName "${input2.objectApiName}" is not a valid Salesforce object API name.`,
|
|
19106
|
+
path: "objectApiName"
|
|
19107
|
+
});
|
|
19108
|
+
}
|
|
19109
|
+
return ok(`SELECT COUNT() FROM ${input2.objectApiName}`);
|
|
19110
|
+
}
|
|
19111
|
+
return err({
|
|
19112
|
+
kind: "invalid-query",
|
|
19113
|
+
message: "live_count needs either `soql` (a SELECT COUNT() query) or `objectApiName`.",
|
|
19114
|
+
path: "soql"
|
|
19115
|
+
});
|
|
19116
|
+
};
|
|
19043
19117
|
assertCountSoql = (soql) => {
|
|
19044
19118
|
const normalized = soql.trim().replace(/\s+/g, " ");
|
|
19045
19119
|
if (!/^select\s+count\s*\(/i.test(normalized)) {
|
|
@@ -19055,7 +19129,10 @@ var init_live_plane = __esm({
|
|
|
19055
19129
|
const gate = await gateLive(ctx, input2);
|
|
19056
19130
|
if (!gate.ok)
|
|
19057
19131
|
return gate;
|
|
19058
|
-
const
|
|
19132
|
+
const soqlResult = resolveCountSoql(input2);
|
|
19133
|
+
if (!soqlResult.ok)
|
|
19134
|
+
return soqlResult;
|
|
19135
|
+
const soqlCheck = assertCountSoql(soqlResult.value);
|
|
19059
19136
|
if (!soqlCheck.ok)
|
|
19060
19137
|
return soqlCheck;
|
|
19061
19138
|
const org = resolveOrg(ctx, input2.orgAlias);
|
|
@@ -21223,7 +21300,7 @@ var init_omniuicard_widget_breakdown = __esm({
|
|
|
21223
21300
|
});
|
|
21224
21301
|
|
|
21225
21302
|
// ../mcp/dist/src/tools/soe-admission.js
|
|
21226
|
-
var OBJECT_NOT_MODELED_BOUNDARY, evaluateSoeAdmission, composeSoeDisclosure;
|
|
21303
|
+
var OBJECT_NOT_MODELED_BOUNDARY, evaluateSoeAdmission, composeSoeDisclosure, soeNotAdmittedMessage;
|
|
21227
21304
|
var init_soe_admission = __esm({
|
|
21228
21305
|
"../mcp/dist/src/tools/soe-admission.js"() {
|
|
21229
21306
|
"use strict";
|
|
@@ -21259,9 +21336,142 @@ var init_soe_admission = __esm({
|
|
|
21259
21336
|
if (parentOf.value.length > 0) {
|
|
21260
21337
|
return ok({ admitted: true, objectModeled: false });
|
|
21261
21338
|
}
|
|
21262
|
-
|
|
21339
|
+
const inbound = await listEdges(ctx.graph, objectId, { direction: "in" });
|
|
21340
|
+
if (!inbound.ok) {
|
|
21341
|
+
return err(inbound.error.message);
|
|
21342
|
+
}
|
|
21343
|
+
return ok({
|
|
21344
|
+
admitted: false,
|
|
21345
|
+
objectModeled: false,
|
|
21346
|
+
referencedButNotModeled: inbound.value.length > 0
|
|
21347
|
+
});
|
|
21263
21348
|
};
|
|
21264
21349
|
composeSoeDisclosure = (baseDisclosure, objectModeled) => objectModeled ? baseDisclosure : `${baseDisclosure} ${OBJECT_NOT_MODELED_BOUNDARY}`;
|
|
21350
|
+
soeNotAdmittedMessage = (objectId, referencedButNotModeled) => referencedButNotModeled ? `\`${objectId}\` is referenced by this org (e.g. permission-set grants) but its definition was never retrieved into this vault \u2014 typically a managed-package or filtered object. Save-order can't be composed without the object's own metadata and automation. Run \`sfi refresh\` if it is retrievable, otherwise treat it as external.` : `no automation or object definition for \`${objectId}\` in this vault`;
|
|
21351
|
+
}
|
|
21352
|
+
});
|
|
21353
|
+
|
|
21354
|
+
// ../mcp/dist/src/tools/soe-payload-bounds.js
|
|
21355
|
+
var SOE_MAX_PAYLOAD_BYTES, KEEP_ALL_AT_OR_BELOW, sizeOf, enforceSoeByteBudget, soeTruncationNote;
|
|
21356
|
+
var init_soe_payload_bounds = __esm({
|
|
21357
|
+
"../mcp/dist/src/tools/soe-payload-bounds.js"() {
|
|
21358
|
+
"use strict";
|
|
21359
|
+
SOE_MAX_PAYLOAD_BYTES = 4e4;
|
|
21360
|
+
KEEP_ALL_AT_OR_BELOW = 4;
|
|
21361
|
+
sizeOf = (payload) => Buffer.byteLength(JSON.stringify(payload), "utf8");
|
|
21362
|
+
enforceSoeByteBudget = (payload, containers) => {
|
|
21363
|
+
if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES) {
|
|
21364
|
+
return { truncated: false, actionsOmitted: 0, conditionalsTrimmed: 0, stepsOmitted: 0 };
|
|
21365
|
+
}
|
|
21366
|
+
const steps = containers.flat();
|
|
21367
|
+
let totalOmitted = 0;
|
|
21368
|
+
const trimTo = (step3, keep) => {
|
|
21369
|
+
if (keep >= step3.actions.length)
|
|
21370
|
+
return;
|
|
21371
|
+
const removed = step3.actions.length - keep;
|
|
21372
|
+
step3.actions = step3.actions.slice(0, keep);
|
|
21373
|
+
step3.actionsOmitted = (step3.actionsOmitted ?? 0) + removed;
|
|
21374
|
+
totalOmitted += removed;
|
|
21375
|
+
};
|
|
21376
|
+
const actionBytes = steps.reduce((n, s) => n + Buffer.byteLength(JSON.stringify(s.actions), "utf8"), 0);
|
|
21377
|
+
const nonActionBytes = Math.max(0, sizeOf(payload) - actionBytes);
|
|
21378
|
+
const budgetForActions = SOE_MAX_PAYLOAD_BYTES - nonActionBytes;
|
|
21379
|
+
if (budgetForActions <= 0) {
|
|
21380
|
+
for (const s of steps)
|
|
21381
|
+
trimTo(s, 0);
|
|
21382
|
+
} else if (actionBytes > budgetForActions) {
|
|
21383
|
+
const ratio = budgetForActions / actionBytes;
|
|
21384
|
+
for (const s of steps) {
|
|
21385
|
+
if (s.actions.length <= KEEP_ALL_AT_OR_BELOW)
|
|
21386
|
+
continue;
|
|
21387
|
+
trimTo(s, Math.max(0, Math.floor(s.actions.length * ratio)));
|
|
21388
|
+
}
|
|
21389
|
+
}
|
|
21390
|
+
for (let guard = 0; guard < 1e5; guard += 1) {
|
|
21391
|
+
if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
|
|
21392
|
+
break;
|
|
21393
|
+
let target;
|
|
21394
|
+
for (const s of steps) {
|
|
21395
|
+
if (s.actions.length <= KEEP_ALL_AT_OR_BELOW)
|
|
21396
|
+
continue;
|
|
21397
|
+
if (target === void 0 || s.actions.length > target.actions.length) {
|
|
21398
|
+
target = s;
|
|
21399
|
+
}
|
|
21400
|
+
}
|
|
21401
|
+
if (target === void 0)
|
|
21402
|
+
break;
|
|
21403
|
+
trimTo(target, Math.floor(target.actions.length / 2));
|
|
21404
|
+
}
|
|
21405
|
+
let conditionalsTrimmed = 0;
|
|
21406
|
+
for (let guard = 0; guard < 1e5; guard += 1) {
|
|
21407
|
+
if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
|
|
21408
|
+
break;
|
|
21409
|
+
let target;
|
|
21410
|
+
let targetBytes = 0;
|
|
21411
|
+
for (const s of steps) {
|
|
21412
|
+
const cond = s.conditional;
|
|
21413
|
+
if (cond === void 0 || s.conditionalTruncated)
|
|
21414
|
+
continue;
|
|
21415
|
+
if (cond.expression === "" && cond.fieldRefs.length === 0)
|
|
21416
|
+
continue;
|
|
21417
|
+
const b = Buffer.byteLength(JSON.stringify(cond), "utf8");
|
|
21418
|
+
if (target === void 0 || b > targetBytes) {
|
|
21419
|
+
target = s;
|
|
21420
|
+
targetBytes = b;
|
|
21421
|
+
}
|
|
21422
|
+
}
|
|
21423
|
+
if (target === void 0)
|
|
21424
|
+
break;
|
|
21425
|
+
target.conditional = {
|
|
21426
|
+
conditionContextId: target.conditional.conditionContextId,
|
|
21427
|
+
expression: "",
|
|
21428
|
+
fieldRefs: []
|
|
21429
|
+
};
|
|
21430
|
+
target.conditionalTruncated = true;
|
|
21431
|
+
conditionalsTrimmed += 1;
|
|
21432
|
+
}
|
|
21433
|
+
let stepsOmitted = 0;
|
|
21434
|
+
for (let guard = 0; guard < 1e6; guard += 1) {
|
|
21435
|
+
if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
|
|
21436
|
+
break;
|
|
21437
|
+
let target;
|
|
21438
|
+
let targetBytes = 0;
|
|
21439
|
+
for (const c of containers) {
|
|
21440
|
+
if (c.length <= 1)
|
|
21441
|
+
continue;
|
|
21442
|
+
const b = Buffer.byteLength(JSON.stringify(c), "utf8");
|
|
21443
|
+
if (target === void 0 || b > targetBytes) {
|
|
21444
|
+
target = c;
|
|
21445
|
+
targetBytes = b;
|
|
21446
|
+
}
|
|
21447
|
+
}
|
|
21448
|
+
if (target === void 0)
|
|
21449
|
+
break;
|
|
21450
|
+
target.pop();
|
|
21451
|
+
stepsOmitted += 1;
|
|
21452
|
+
}
|
|
21453
|
+
return {
|
|
21454
|
+
truncated: totalOmitted > 0 || conditionalsTrimmed > 0 || stepsOmitted > 0,
|
|
21455
|
+
actionsOmitted: totalOmitted,
|
|
21456
|
+
conditionalsTrimmed,
|
|
21457
|
+
stepsOmitted
|
|
21458
|
+
};
|
|
21459
|
+
};
|
|
21460
|
+
soeTruncationNote = (result) => {
|
|
21461
|
+
const budgetKb = Math.round(SOE_MAX_PAYLOAD_BYTES / 1e3);
|
|
21462
|
+
const parts = [];
|
|
21463
|
+
if (result.actionsOmitted > 0) {
|
|
21464
|
+
parts.push(`${result.actionsOmitted} per-step action edge(s) across the heaviest steps were omitted (see each step's \`actionsOmitted\`)`);
|
|
21465
|
+
}
|
|
21466
|
+
if (result.conditionalsTrimmed > 0) {
|
|
21467
|
+
parts.push(`${result.conditionalsTrimmed} step condition(s) had their expression/fieldRefs dropped \u2014 the \`conditionContextId\` remains, fetch it with \`get_component\` for the full condition (see each step's \`conditionalTruncated\`)`);
|
|
21468
|
+
}
|
|
21469
|
+
if (result.stepsOmitted > 0) {
|
|
21470
|
+
parts.push(`${result.stepsOmitted} trailing step(s) were dropped to fit (the tail-most async/post-save steps; \`summary.totalSteps\` still reports the true total) \u2014 query a single event with \`what_happens_on_save\` to see them all`);
|
|
21471
|
+
}
|
|
21472
|
+
const lead = result.stepsOmitted > 0 ? `Response trimmed to fit the ~${budgetKb} KB MCP response budget` : `Response trimmed to fit the ~${budgetKb} KB MCP response budget: every save-order STEP is present and in order, but`;
|
|
21473
|
+
return `${lead} ${parts.join("; ")}. Query a single object/event for full detail.`;
|
|
21474
|
+
};
|
|
21265
21475
|
}
|
|
21266
21476
|
});
|
|
21267
21477
|
|
|
@@ -21274,6 +21484,7 @@ var init_order_of_execution = __esm({
|
|
|
21274
21484
|
init_dist();
|
|
21275
21485
|
init_src();
|
|
21276
21486
|
init_soe_admission();
|
|
21487
|
+
init_soe_payload_bounds();
|
|
21277
21488
|
DISCLOSURE6 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
|
|
21278
21489
|
SOE_EVENTS = ["insert", "update", "delete", "undelete"];
|
|
21279
21490
|
orderOfExecutionInputSchema = z82.object({
|
|
@@ -21603,7 +21814,7 @@ var init_order_of_execution = __esm({
|
|
|
21603
21814
|
if (!admission.value.admitted) {
|
|
21604
21815
|
return err({
|
|
21605
21816
|
kind: "component-not-found",
|
|
21606
|
-
message:
|
|
21817
|
+
message: soeNotAdmittedMessage(objectId, admission.value.referencedButNotModeled ?? false),
|
|
21607
21818
|
path: objectId
|
|
21608
21819
|
});
|
|
21609
21820
|
}
|
|
@@ -21621,13 +21832,20 @@ var init_order_of_execution = __esm({
|
|
|
21621
21832
|
}
|
|
21622
21833
|
byEvent[event] = perEventResult.value;
|
|
21623
21834
|
}
|
|
21835
|
+
const data = {
|
|
21836
|
+
objectApiName: input2.objectApiName,
|
|
21837
|
+
objectModeled,
|
|
21838
|
+
byEvent,
|
|
21839
|
+
disclosure: composeSoeDisclosure(DISCLOSURE6, objectModeled)
|
|
21840
|
+
};
|
|
21841
|
+
const containers = SOE_EVENTS.map((event) => byEvent[event].soe);
|
|
21842
|
+
const budget = enforceSoeByteBudget(data, containers);
|
|
21843
|
+
if (budget.truncated) {
|
|
21844
|
+
data.truncated = true;
|
|
21845
|
+
data.disclosure = `${data.disclosure} ${soeTruncationNote(budget)}`;
|
|
21846
|
+
}
|
|
21624
21847
|
return ok({
|
|
21625
|
-
data
|
|
21626
|
-
objectApiName: input2.objectApiName,
|
|
21627
|
-
objectModeled,
|
|
21628
|
-
byEvent,
|
|
21629
|
-
disclosure: composeSoeDisclosure(DISCLOSURE6, objectModeled)
|
|
21630
|
-
},
|
|
21848
|
+
data,
|
|
21631
21849
|
vaultState: {
|
|
21632
21850
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
21633
21851
|
refreshedAt: ctx.manifest.refreshedAt
|
|
@@ -22755,7 +22973,9 @@ var init_intent_router = __esm({
|
|
|
22755
22973
|
/\bwho\s+can\s+(see|read|view|edit|access)\b\s+[\w\s.]+[?.!]?$/,
|
|
22756
22974
|
/\b(access|permission|fls|field[-\s]level\s+security)\b.*\b(field|object)\b/,
|
|
22757
22975
|
/\bwho\s+has\s+access\s+to\b/,
|
|
22758
|
-
|
|
22976
|
+
// "allow|read|view" added: "which permission sets allow read on X" was a
|
|
22977
|
+
// router gap (the verbs grant/access/see/edit didn't cover it).
|
|
22978
|
+
/\bwhich\s+(profiles?|permission\s+sets?)\b.*\b(grant|allow|access|see|read|view|edit)\b/
|
|
22759
22979
|
]
|
|
22760
22980
|
},
|
|
22761
22981
|
{
|
|
@@ -22880,11 +23100,15 @@ var init_intent_router = __esm({
|
|
|
22880
23100
|
tools: ["sfi.resolve", "sfi.what_happens_on_save", "sfi.order_of_execution"],
|
|
22881
23101
|
liveRequired: false,
|
|
22882
23102
|
needsResolve: true,
|
|
22883
|
-
reason: "Order of execution / what runs on save is reconstructed from the vault graph.",
|
|
23103
|
+
reason: "Order of execution / what runs on save is reconstructed from the vault graph. what_happens_on_save needs an explicit DML event \u2014 default to 'update' (or insert/delete to match the question) when none is stated.",
|
|
22884
23104
|
patterns: [
|
|
22885
23105
|
/\b(trigger\s+order|order\s+of\s+execution)\b/,
|
|
22886
23106
|
/\bwhat\s+(happens|runs|fires)\b.*\b(on\s+save|when\b.*\b(created|saved|updated|inserted|deleted))\b/,
|
|
22887
|
-
|
|
23107
|
+
// "which/what flows|triggers|VRs|workflows run|fire when ..." — the
|
|
23108
|
+
// "which" phrasing was a router gap (e.g. "which flows run when a Case is
|
|
23109
|
+
// created"), so the question fell through to unrouted.
|
|
23110
|
+
/\b(what|which)\s+(triggers?|automation|flows?|validation\s+rules?|workflows?)\b.*\b(fire|run|execute|happen)\b/,
|
|
23111
|
+
/\b(flows?|triggers?|automation)\b.*\bwhen\b.*\b\w+\s+is\b.*\b(created|updated|inserted|deleted|saved)\b/
|
|
22888
23112
|
]
|
|
22889
23113
|
},
|
|
22890
23114
|
{
|
|
@@ -26078,6 +26302,7 @@ var init_what_happens_on_save = __esm({
|
|
|
26078
26302
|
init_dist();
|
|
26079
26303
|
init_src();
|
|
26080
26304
|
init_soe_admission();
|
|
26305
|
+
init_soe_payload_bounds();
|
|
26081
26306
|
DISCLOSURE7 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
|
|
26082
26307
|
ALLOWED_EVENTS = [
|
|
26083
26308
|
"insert",
|
|
@@ -26088,7 +26313,10 @@ var init_what_happens_on_save = __esm({
|
|
|
26088
26313
|
];
|
|
26089
26314
|
whatHappensOnSaveInputSchema = z103.object({
|
|
26090
26315
|
objectApiName: z103.string().min(1),
|
|
26091
|
-
|
|
26316
|
+
// Accept "after update" / "Before Insert" etc.: lower-case and drop the
|
|
26317
|
+
// before/after timing prefix so the bare DML event matches the enum. The
|
|
26318
|
+
// SOE walker models both timings internally; the event arg selects the row.
|
|
26319
|
+
event: z103.preprocess((v) => typeof v === "string" ? v.trim().toLowerCase().replace(/^(?:before|after)\s+/, "") : v, z103.enum(ALLOWED_EVENTS)),
|
|
26092
26320
|
recordTypeId: z103.string().min(1).optional()
|
|
26093
26321
|
});
|
|
26094
26322
|
workflowMatchesEvent2 = (triggerType, event) => {
|
|
@@ -26307,7 +26535,7 @@ var init_what_happens_on_save = __esm({
|
|
|
26307
26535
|
if (!admission.value.admitted) {
|
|
26308
26536
|
return err({
|
|
26309
26537
|
kind: "component-not-found",
|
|
26310
|
-
message:
|
|
26538
|
+
message: soeNotAdmittedMessage(objectId, admission.value.referencedButNotModeled ?? false),
|
|
26311
26539
|
path: objectId
|
|
26312
26540
|
});
|
|
26313
26541
|
}
|
|
@@ -26444,20 +26672,26 @@ var init_what_happens_on_save = __esm({
|
|
|
26444
26672
|
const asyncFanOut = asyncStepsResult.value.length;
|
|
26445
26673
|
stepIndex += asyncFanOut;
|
|
26446
26674
|
const conditionalCount = soe.filter((s) => s.conditional !== void 0).length;
|
|
26447
|
-
|
|
26448
|
-
|
|
26449
|
-
|
|
26450
|
-
|
|
26451
|
-
|
|
26452
|
-
|
|
26453
|
-
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
asyncFanOut
|
|
26458
|
-
},
|
|
26459
|
-
disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
|
|
26675
|
+
const data = {
|
|
26676
|
+
objectApiName: input2.objectApiName,
|
|
26677
|
+
event: input2.event,
|
|
26678
|
+
recordTypeId: input2.recordTypeId ?? null,
|
|
26679
|
+
objectModeled,
|
|
26680
|
+
soe,
|
|
26681
|
+
summary: {
|
|
26682
|
+
totalSteps: soe.length,
|
|
26683
|
+
conditionalSteps: conditionalCount,
|
|
26684
|
+
asyncFanOut
|
|
26460
26685
|
},
|
|
26686
|
+
disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
|
|
26687
|
+
};
|
|
26688
|
+
const budget = enforceSoeByteBudget(data, [soe]);
|
|
26689
|
+
if (budget.truncated) {
|
|
26690
|
+
data.truncated = true;
|
|
26691
|
+
data.disclosure = `${data.disclosure} ${soeTruncationNote(budget)}`;
|
|
26692
|
+
}
|
|
26693
|
+
return ok({
|
|
26694
|
+
data,
|
|
26461
26695
|
vaultState: {
|
|
26462
26696
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
26463
26697
|
refreshedAt: ctx.manifest.refreshedAt
|
|
@@ -26467,6 +26701,23 @@ var init_what_happens_on_save = __esm({
|
|
|
26467
26701
|
}
|
|
26468
26702
|
});
|
|
26469
26703
|
|
|
26704
|
+
// ../mcp/dist/src/tools/phantom-node.js
|
|
26705
|
+
var phantomAwareNotFoundMessage;
|
|
26706
|
+
var init_phantom_node = __esm({
|
|
26707
|
+
"../mcp/dist/src/tools/phantom-node.js"() {
|
|
26708
|
+
"use strict";
|
|
26709
|
+
init_src();
|
|
26710
|
+
phantomAwareNotFoundMessage = async (ctx, id, kindLabel) => {
|
|
26711
|
+
const inbound = await listEdges(ctx.graph, id, { direction: "in" });
|
|
26712
|
+
const refs = inbound.ok ? inbound.value.length : 0;
|
|
26713
|
+
if (refs === 0) {
|
|
26714
|
+
return `no ${kindLabel} with id ${id}`;
|
|
26715
|
+
}
|
|
26716
|
+
return `\`${id}\` is referenced by ${refs} other component(s) in this org (e.g. code, tests, or permission grants) but its own ${kindLabel} definition was never retrieved into the vault \u2014 typically a managed-package component or one outside the retrieve scope. Run \`sfi refresh\` if it should be retrievable; otherwise treat it as external.`;
|
|
26717
|
+
};
|
|
26718
|
+
}
|
|
26719
|
+
});
|
|
26720
|
+
|
|
26470
26721
|
// ../mcp/dist/src/tools/what-if-change-method-signature.js
|
|
26471
26722
|
import { z as z104 } from "zod";
|
|
26472
26723
|
var APEX_CLASS_PREFIX9, DISCLOSURE8, whatIfChangeMethodSignatureInputSchema, readMethodName, isTestClass8, buildExplanation2, classifyCaller, aggregateVerdict4, compareImpacts, compareIds, collectCallers, collectCoveringTests, whatIfChangeMethodSignatureHandler;
|
|
@@ -26477,6 +26728,7 @@ var init_what_if_change_method_signature = __esm({
|
|
|
26477
26728
|
init_src();
|
|
26478
26729
|
init_coerce_id();
|
|
26479
26730
|
init_coverage_trust();
|
|
26731
|
+
init_phantom_node();
|
|
26480
26732
|
APEX_CLASS_PREFIX9 = "ApexClass:";
|
|
26481
26733
|
DISCLOSURE8 = "callers identified via the v1.4 apex-scanner are at heuristic confidence; dynamic dispatch via Type.forName + invoke is invisible. Test classes are identified by @isTest + naming convention (className + 'Test' suffix) and by coversTest edges; a test class that doesn't follow the naming convention and doesn't carry a @TestVisible-tagged covering reference may be missed.";
|
|
26482
26734
|
whatIfChangeMethodSignatureInputSchema = z104.object({
|
|
@@ -26616,7 +26868,7 @@ var init_what_if_change_method_signature = __esm({
|
|
|
26616
26868
|
if (nodeResult.value === null) {
|
|
26617
26869
|
return err({
|
|
26618
26870
|
kind: "component-not-found",
|
|
26619
|
-
message:
|
|
26871
|
+
message: await phantomAwareNotFoundMessage(ctx, classId, "ApexClass"),
|
|
26620
26872
|
path: classId
|
|
26621
26873
|
});
|
|
26622
26874
|
}
|
|
@@ -28731,7 +28983,8 @@ var init_tools = __esm({
|
|
|
28731
28983
|
type: "object",
|
|
28732
28984
|
properties: {
|
|
28733
28985
|
nodeId: { type: "string", minLength: 1 },
|
|
28734
|
-
|
|
28986
|
+
// 'incoming'/'outgoing' are accepted aliases (normalized to in/out).
|
|
28987
|
+
direction: { type: "string", enum: ["in", "out", "both", "incoming", "outgoing"] },
|
|
28735
28988
|
edgeType: {
|
|
28736
28989
|
type: "string",
|
|
28737
28990
|
// Single-sourced from the contracts EDGE_TYPES tuple so the advertised
|
|
@@ -28914,9 +29167,12 @@ var init_tools = __esm({
|
|
|
28914
29167
|
});
|
|
28915
29168
|
LIVE_COUNT_INPUT_SCHEMA = Object.freeze({
|
|
28916
29169
|
type: "object",
|
|
28917
|
-
|
|
29170
|
+
// Either `soql` (a SELECT COUNT() query) or `objectApiName` (count all rows).
|
|
29171
|
+
// The one-of requirement is enforced in the handler, not the JSON schema, so
|
|
29172
|
+
// the advertised shape stays simple for clients.
|
|
28918
29173
|
properties: {
|
|
28919
29174
|
soql: { type: "string", minLength: 1 },
|
|
29175
|
+
objectApiName: { type: "string", minLength: 1 },
|
|
28920
29176
|
...LIVE_ENABLED_PROPERTY
|
|
28921
29177
|
}
|
|
28922
29178
|
});
|
|
@@ -30195,7 +30451,7 @@ var init_tools = __esm({
|
|
|
30195
30451
|
},
|
|
30196
30452
|
{
|
|
30197
30453
|
name: "sfi.live_count",
|
|
30198
|
-
description: "Opt-in live org:
|
|
30454
|
+
description: "Opt-in live org: count records. Pass `objectApiName` to count every row of an object, or `soql` for a custom SELECT COUNT() query (strict shape validation). Read-only; never falls back to vault data.",
|
|
30199
30455
|
inputSchema: LIVE_COUNT_INPUT_SCHEMA
|
|
30200
30456
|
},
|
|
30201
30457
|
{
|
|
@@ -31521,15 +31777,40 @@ var runDoctor = async (opts) => {
|
|
|
31521
31777
|
const checks = [];
|
|
31522
31778
|
const vaultRoot = resolve4(opts.cwd, DEFAULT_VAULT_ROOT);
|
|
31523
31779
|
const paths = vaultPaths(vaultRoot);
|
|
31780
|
+
const SF_FALLBACK_PATHS = ["/usr/local/bin/sf", "/opt/homebrew/bin/sf"];
|
|
31781
|
+
let sfBin = "sf";
|
|
31782
|
+
let sfDetail = null;
|
|
31783
|
+
let sfOnPath = false;
|
|
31524
31784
|
try {
|
|
31525
31785
|
const { stdout } = await run("sf --version");
|
|
31526
|
-
|
|
31786
|
+
sfDetail = stdout.trim().split("\n")[0] ?? "installed";
|
|
31787
|
+
sfOnPath = true;
|
|
31527
31788
|
} catch {
|
|
31789
|
+
for (const abs of SF_FALLBACK_PATHS) {
|
|
31790
|
+
try {
|
|
31791
|
+
const { stdout } = await run(`"${abs}" --version`);
|
|
31792
|
+
sfDetail = stdout.trim().split("\n")[0] ?? "installed";
|
|
31793
|
+
sfBin = `"${abs}"`;
|
|
31794
|
+
break;
|
|
31795
|
+
} catch {
|
|
31796
|
+
}
|
|
31797
|
+
}
|
|
31798
|
+
}
|
|
31799
|
+
if (sfOnPath) {
|
|
31800
|
+
checks.push({ name: "Salesforce CLI", status: "pass", detail: sfDetail ?? "installed" });
|
|
31801
|
+
} else if (sfDetail !== null) {
|
|
31802
|
+
checks.push({
|
|
31803
|
+
name: "Salesforce CLI",
|
|
31804
|
+
status: "warn",
|
|
31805
|
+
detail: `${sfDetail} \u2014 found via absolute path, not on PATH`,
|
|
31806
|
+
fix: "Add the Salesforce CLI directory (e.g. /usr/local/bin or /opt/homebrew/bin) to the PATH of whatever launches sfi; IDE/MCP subprocesses often do not inherit it."
|
|
31807
|
+
});
|
|
31808
|
+
} else {
|
|
31528
31809
|
checks.push({
|
|
31529
31810
|
name: "Salesforce CLI",
|
|
31530
31811
|
status: "fail",
|
|
31531
|
-
detail: "`sf` not found on PATH",
|
|
31532
|
-
fix: "Install the Salesforce CLI
|
|
31812
|
+
detail: "`sf` not found on PATH or common install locations",
|
|
31813
|
+
fix: "Install the Salesforce CLI (npm install --global @salesforce/cli). If it IS installed, add its directory (/usr/local/bin or /opt/homebrew/bin) to your PATH; IDE/MCP subprocesses often do not inherit it."
|
|
31533
31814
|
});
|
|
31534
31815
|
}
|
|
31535
31816
|
const vaultInit = await pathExists2(paths.config);
|
|
@@ -31553,7 +31834,7 @@ var runDoctor = async (opts) => {
|
|
|
31553
31834
|
});
|
|
31554
31835
|
} else {
|
|
31555
31836
|
try {
|
|
31556
|
-
const { stdout } = await run(
|
|
31837
|
+
const { stdout } = await run(`${sfBin} org display --target-org "${targetOrg}" --json`);
|
|
31557
31838
|
const parsed = JSON.parse(stdout);
|
|
31558
31839
|
const status = parsed.result?.connectedStatus;
|
|
31559
31840
|
if (status === "Connected") {
|
|
@@ -35729,8 +36010,8 @@ import { XMLParser as XMLParser19, XMLValidator as XMLValidator18 } from "fast-x
|
|
|
35729
36010
|
var APPLICATION_FILE_SUFFIX = ".app-meta.xml";
|
|
35730
36011
|
var ROOT_ELEMENT18 = "CustomApplication";
|
|
35731
36012
|
var EXTRACTOR_SOURCE8 = "custom-application-extractor";
|
|
35732
|
-
var REQUIRED_ELEMENTS4 = ["label"
|
|
35733
|
-
var ALLOWED_NAV_TYPE = ["Standard", "Console"];
|
|
36013
|
+
var REQUIRED_ELEMENTS4 = ["label"];
|
|
36014
|
+
var ALLOWED_NAV_TYPE = ["Standard", "Console", "Classic"];
|
|
35734
36015
|
var unwrapSingle19 = (value) => Array.isArray(value) ? value[0] : value;
|
|
35735
36016
|
var toArray12 = (value) => {
|
|
35736
36017
|
if (value === void 0 || value === null)
|
|
@@ -35779,6 +36060,8 @@ var validateRoot10 = (parsed, path) => {
|
|
|
35779
36060
|
if (rootObj["navType"] === void 0)
|
|
35780
36061
|
rootObj["navType"] = "Standard";
|
|
35781
36062
|
}
|
|
36063
|
+
if (rootObj["navType"] === void 0)
|
|
36064
|
+
rootObj["navType"] = "Classic";
|
|
35782
36065
|
for (const required of REQUIRED_ELEMENTS4) {
|
|
35783
36066
|
if (rootObj[required] === void 0) {
|
|
35784
36067
|
return err({
|
|
@@ -35903,6 +36186,24 @@ var ROOT_ELEMENT19 = "CustomField";
|
|
|
35903
36186
|
var FIELDS_DIR_NAME = "fields";
|
|
35904
36187
|
var PICKLIST_TYPES3 = ["Picklist", "MultiselectPicklist"];
|
|
35905
36188
|
var FORMULA_ELEMENT_NAME = "formula";
|
|
36189
|
+
var STANDARD_FIELD_TYPES = {
|
|
36190
|
+
Email: "Email",
|
|
36191
|
+
Phone: "Phone",
|
|
36192
|
+
Fax: "Phone",
|
|
36193
|
+
MobilePhone: "Phone",
|
|
36194
|
+
HomePhone: "Phone",
|
|
36195
|
+
OtherPhone: "Phone",
|
|
36196
|
+
AssistantPhone: "Phone",
|
|
36197
|
+
Website: "Url",
|
|
36198
|
+
CreatedDate: "DateTime",
|
|
36199
|
+
LastModifiedDate: "DateTime",
|
|
36200
|
+
SystemModstamp: "DateTime",
|
|
36201
|
+
LastViewedDate: "DateTime",
|
|
36202
|
+
LastReferencedDate: "DateTime",
|
|
36203
|
+
LastActivityDate: "Date",
|
|
36204
|
+
Birthdate: "Date",
|
|
36205
|
+
IsDeleted: "Checkbox"
|
|
36206
|
+
};
|
|
35906
36207
|
var unwrapSingle20 = (value) => Array.isArray(value) ? value[0] : value;
|
|
35907
36208
|
var coerceBoolean12 = (value) => {
|
|
35908
36209
|
if (typeof value === "boolean")
|
|
@@ -35976,7 +36277,8 @@ var validateRoot11 = (parsed, path) => {
|
|
|
35976
36277
|
message: "missing required element: <type>"
|
|
35977
36278
|
});
|
|
35978
36279
|
}
|
|
35979
|
-
|
|
36280
|
+
const bareName = fullName.includes(".") ? fullName.split(".").pop() : fullName;
|
|
36281
|
+
rootObj["type"] = STANDARD_FIELD_TYPES[bareName] ?? "Unknown";
|
|
35980
36282
|
}
|
|
35981
36283
|
return ok(rootObj);
|
|
35982
36284
|
};
|
|
@@ -42326,6 +42628,7 @@ var QUICK_ACTIONS_DIR_NAME = "quickActions";
|
|
|
42326
42628
|
var REQUIRED_ELEMENTS20 = ["type"];
|
|
42327
42629
|
var ALLOWED_ACTION_TYPES = [
|
|
42328
42630
|
"Create",
|
|
42631
|
+
"Flow",
|
|
42329
42632
|
"LogACall",
|
|
42330
42633
|
"LightningComponent",
|
|
42331
42634
|
"LightningWebComponent",
|
|
@@ -42465,6 +42768,19 @@ var buildReferencesEdge = (fromId, actionType, rootObj) => {
|
|
|
42465
42768
|
properties: { targetKind: "page" }
|
|
42466
42769
|
};
|
|
42467
42770
|
}
|
|
42771
|
+
if (actionType === "Flow") {
|
|
42772
|
+
const name = optionalString23(rootObj, "flowDefinition");
|
|
42773
|
+
if (name === null || name.length === 0)
|
|
42774
|
+
return null;
|
|
42775
|
+
return {
|
|
42776
|
+
fromId,
|
|
42777
|
+
toId: `Flow:${name}`,
|
|
42778
|
+
edgeType: "references",
|
|
42779
|
+
confidence: "declared",
|
|
42780
|
+
source: EXTRACTOR_SOURCE25,
|
|
42781
|
+
properties: { targetKind: "flow" }
|
|
42782
|
+
};
|
|
42783
|
+
}
|
|
42468
42784
|
return null;
|
|
42469
42785
|
};
|
|
42470
42786
|
var extractQuickAction = async (path) => {
|
|
@@ -42529,6 +42845,7 @@ var extractQuickAction = async (path) => {
|
|
|
42529
42845
|
lightningComponent: optionalString23(rootObj, "lightningComponent"),
|
|
42530
42846
|
lightningWebComponent: optionalString23(rootObj, "lightningWebComponent"),
|
|
42531
42847
|
page: optionalString23(rootObj, "page"),
|
|
42848
|
+
flowDefinition: optionalString23(rootObj, "flowDefinition"),
|
|
42532
42849
|
icon: optionalString23(rootObj, "icon"),
|
|
42533
42850
|
height: optionalInteger2(rootObj, "height"),
|
|
42534
42851
|
width: optionalString23(rootObj, "width")
|
|
@@ -45879,12 +46196,17 @@ var runRefresh = async (opts) => {
|
|
|
45879
46196
|
const paths = vaultPaths(configResult.value.vaultRoot);
|
|
45880
46197
|
const targetOrg = opts.targetOrg ?? configResult.value.targetOrg;
|
|
45881
46198
|
const requestedTypes = parseTypeFilter(opts.types);
|
|
46199
|
+
const progress = opts.onProgress ?? (() => {
|
|
46200
|
+
});
|
|
45882
46201
|
if (!opts.noPull) {
|
|
46202
|
+
progress(`Retrieving metadata from ${targetOrg} (this can take several minutes)...`);
|
|
45883
46203
|
const pulled = await runSfRetrieve(targetOrg, paths.source, requestedTypes);
|
|
45884
46204
|
if (!pulled.ok)
|
|
45885
46205
|
return failed(started, pulled.error, []);
|
|
45886
46206
|
}
|
|
46207
|
+
progress("Extracting components from retrieved source...");
|
|
45887
46208
|
const walked = await walkAndExtract(paths.source, requestedTypes);
|
|
46209
|
+
progress(`Extracted ${walked.results.length} component file(s); building graph...`);
|
|
45888
46210
|
await mkdir9(paths.graph, { recursive: true });
|
|
45889
46211
|
const storeResult = await openGraph(paths.graphDb);
|
|
45890
46212
|
if (!storeResult.ok) {
|
|
@@ -45953,10 +46275,13 @@ var buildCoverageEntries2 = (counts, skippedDirectories, requestedTypes, sourceR
|
|
|
45953
46275
|
};
|
|
45954
46276
|
var runWithOpenGraph = async (args) => {
|
|
45955
46277
|
const { store, paths, started, targetOrg, walked, opts, requestedTypes } = args;
|
|
46278
|
+
const progress = opts.onProgress ?? (() => {
|
|
46279
|
+
});
|
|
45956
46280
|
const importResult = await importExtractionResults(store, walked.results);
|
|
45957
46281
|
if (!importResult.ok) {
|
|
45958
46282
|
return failed(started, `importExtractionResults: ${importResult.error.message}`, walked.failures, EMPTY_COUNTS, walked.skippedDirectories);
|
|
45959
46283
|
}
|
|
46284
|
+
progress("Rendering Markdown vault...");
|
|
45960
46285
|
let counts;
|
|
45961
46286
|
try {
|
|
45962
46287
|
counts = await renderVault(store, paths.root);
|
|
@@ -46173,7 +46498,14 @@ var loadVaultConfig = async (cwd) => {
|
|
|
46173
46498
|
};
|
|
46174
46499
|
var METADATA_API_NAME = {
|
|
46175
46500
|
VisualforcePage: "ApexPage",
|
|
46176
|
-
VisualforceComponent: "ApexComponent"
|
|
46501
|
+
VisualforceComponent: "ApexComponent",
|
|
46502
|
+
// Sharing rules are exposed by the org as the aggregate type `SharingRules`
|
|
46503
|
+
// (one file per object, e.g. `Account.sharingRules-meta.xml`).
|
|
46504
|
+
SharingRule: "SharingRules",
|
|
46505
|
+
// Custom-metadata *records* (the rows of a `__mdt` type) are retrieved under
|
|
46506
|
+
// the `CustomMetadata` type as `{Type}.{Record}.md-meta.xml` files. The
|
|
46507
|
+
// `__mdt` type definitions themselves come down separately as CustomObject.
|
|
46508
|
+
CustomMetadataRecord: "CustomMetadata"
|
|
46177
46509
|
};
|
|
46178
46510
|
var toApiName = (type) => METADATA_API_NAME[type] ?? type;
|
|
46179
46511
|
var SF_MAX_BUFFER = 256 * 1024 * 1024;
|
|
@@ -46242,7 +46574,8 @@ var runSfRetrieve = async (targetOrg, sourceDir, requestedTypes) => {
|
|
|
46242
46574
|
}
|
|
46243
46575
|
const { included: manifestTypes, dropped } = selectManifestTypes(requestedTypes, orgTypes);
|
|
46244
46576
|
if (dropped.length > 0) {
|
|
46245
|
-
|
|
46577
|
+
const labelled = dropped.map((type) => toApiName(type) === type ? type : `${type} (${toApiName(type)})`).join(", ");
|
|
46578
|
+
process.stdout.write(`Skipping ${dropped.length} metadata type(s) the ${targetOrg} describe does not expose: ${labelled}
|
|
46246
46579
|
`);
|
|
46247
46580
|
}
|
|
46248
46581
|
if (manifestTypes.length === 0) {
|
|
@@ -46344,7 +46677,11 @@ var registerRefreshCommand = (program) => {
|
|
|
46344
46677
|
noPull: flags.pull === false,
|
|
46345
46678
|
...flags.targetOrg !== void 0 ? { targetOrg: flags.targetOrg } : {},
|
|
46346
46679
|
...flags.types !== void 0 ? { types: flags.types } : {},
|
|
46347
|
-
...flags.withToolingApi === true ? { withToolingApi: true } : {}
|
|
46680
|
+
...flags.withToolingApi === true ? { withToolingApi: true } : {},
|
|
46681
|
+
// Progress goes to stderr so a multi-minute refresh isn't a silent
|
|
46682
|
+
// wait; stdout stays reserved for the final summary.
|
|
46683
|
+
onProgress: (message) => process.stderr.write(`${message}
|
|
46684
|
+
`)
|
|
46348
46685
|
});
|
|
46349
46686
|
process.stdout.write(formatRefreshSummary(result));
|
|
46350
46687
|
if (result.status !== "success")
|
|
@@ -46503,7 +46840,7 @@ var registerStatusCommand = (program) => {
|
|
|
46503
46840
|
// dist/src/program.js
|
|
46504
46841
|
var readVersion = () => {
|
|
46505
46842
|
if (true)
|
|
46506
|
-
return "0.1.
|
|
46843
|
+
return "0.1.4";
|
|
46507
46844
|
const pkgUrl = new URL("../../package.json", import.meta.url);
|
|
46508
46845
|
const raw = readFileSync2(fileURLToPath(pkgUrl), "utf8");
|
|
46509
46846
|
const parsed = JSON.parse(raw);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sf-intelligence",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Offline-first, MCP-first knowledge base for a Salesforce org. Ask about your org's metadata, dependencies, permissions, and automation — grounded in real retrieved metadata. Ships the sfi CLI and an MCP server.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/PranavNagrecha/Salesforce-Intelligence",
|
|
@@ -52,15 +52,15 @@
|
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"esbuild": "^0.28.0",
|
|
54
54
|
"vitest": "^1.6.0",
|
|
55
|
-
"@sf-intelligence/contracts": "0.1.0",
|
|
56
|
-
"@sf-intelligence/core": "0.1.0",
|
|
57
55
|
"@sf-intelligence/extractors": "0.1.0",
|
|
58
56
|
"@sf-intelligence/graph": "0.1.0",
|
|
59
|
-
"@sf-intelligence/mcp": "0.1.0",
|
|
60
57
|
"@sf-intelligence/patterns": "0.1.0",
|
|
58
|
+
"@sf-intelligence/core": "0.1.0",
|
|
59
|
+
"@sf-intelligence/vault": "0.1.0",
|
|
60
|
+
"@sf-intelligence/contracts": "0.1.0",
|
|
61
|
+
"@sf-intelligence/mcp": "0.1.0",
|
|
61
62
|
"@sf-intelligence/renderers": "0.1.0",
|
|
62
|
-
"@sf-intelligence/tooling-api": "0.1.0"
|
|
63
|
-
"@sf-intelligence/vault": "0.1.0"
|
|
63
|
+
"@sf-intelligence/tooling-api": "0.1.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "tsc --build && node build.mjs",
|