sf-intelligence 0.1.11 → 0.1.13
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/LICENSE +20 -13
- package/demo-source/main/default/approvalProcesses/Project__c.Discount_Approval.approvalProcess-meta.xml +31 -0
- package/demo-source/main/default/classes/IncentiveBatch.cls +54 -0
- package/demo-source/main/default/classes/IncentiveBatch.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/PaymentService.cls +44 -0
- package/demo-source/main/default/classes/PaymentService.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/PaymentServiceTest.cls +57 -0
- package/demo-source/main/default/classes/PaymentServiceTest.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/ProjectTriggerHandler.cls +60 -0
- package/demo-source/main/default/classes/ProjectTriggerHandler.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/ProjectTriggerHandlerTest.cls +80 -0
- package/demo-source/main/default/classes/ProjectTriggerHandlerTest.cls-meta.xml +5 -0
- package/demo-source/main/default/flows/Installation_On_Complete.flow-meta.xml +72 -0
- package/demo-source/main/default/flows/Project_On_Approve.flow-meta.xml +48 -0
- package/demo-source/main/default/groups/Finance_Group.group-meta.xml +7 -0
- package/demo-source/main/default/groups/Ops_Group.group-meta.xml +7 -0
- package/demo-source/main/default/layouts/Installation__c-Installation Layout.layout-meta.xml +43 -0
- package/demo-source/main/default/layouts/Project__c-Residential Layout.layout-meta.xml +78 -0
- package/demo-source/main/default/objects/Battery__c/Battery__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Battery__c/fields/Capacity_kWh__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Battery__c/fields/Manufacturer__c.field-meta.xml +21 -0
- package/demo-source/main/default/objects/Battery__c/fields/Unit_Cost__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/Equipment_Allocation__c.object-meta.xml +13 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Battery__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Line_Total__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Project__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Quantity__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Solar_Panel__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Incentive__c/Incentive__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Amount__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Approved__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Type__c.field-meta.xml +26 -0
- package/demo-source/main/default/objects/Installation__c/Installation__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Installation__c/fields/Crew_Lead__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Installation__c/fields/Install_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Installation__c/fields/Panels_Installed__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Installation__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Installation__c/fields/Status__c.field-meta.xml +31 -0
- package/demo-source/main/default/objects/Invoice__c/Invoice__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Amount__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Balance__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Due_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Project__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Status__c.field-meta.xml +36 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Total_Paid__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Payment__c/Payment__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Payment__c/fields/Amount__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Payment__c/fields/Invoice__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Payment__c/fields/Method__c.field-meta.xml +36 -0
- package/demo-source/main/default/objects/Payment__c/fields/Payment_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Payment__c/validationRules/Amount_Positive.validationRule-meta.xml +9 -0
- package/demo-source/main/default/objects/Permit__c/Permit__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Permit__c/fields/Approved_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Permit__c/fields/Jurisdiction__c.field-meta.xml +8 -0
- package/demo-source/main/default/objects/Permit__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Permit__c/fields/Status__c.field-meta.xml +26 -0
- package/demo-source/main/default/objects/Permit__c/fields/Submitted_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Permit__c/validationRules/Approved_Needs_Date.validationRule-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/Project__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Project__c/fields/Account__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Project__c/fields/Contract_Value__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/fields/Expected_Completion__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Project__c/fields/Is_Complete__c.field-meta.xml +8 -0
- package/demo-source/main/default/objects/Project__c/fields/Margin_Percent__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Project__c/fields/Opportunity__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Project__c/fields/Permit_Approved__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Project__c/fields/Risk_Score__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/fields/Status__c.field-meta.xml +41 -0
- package/demo-source/main/default/objects/Project__c/fields/System_Size_kW__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/fields/Total_Invoiced__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/recordTypes/Commercial.recordType-meta.xml +6 -0
- package/demo-source/main/default/objects/Project__c/recordTypes/Residential.recordType-meta.xml +6 -0
- package/demo-source/main/default/objects/Project__c/validationRules/Complete_Requires_Permit.validationRule-meta.xml +9 -0
- package/demo-source/main/default/objects/SBQQ__ProductRule__c/SBQQ__ProductRule__c.object-meta.xml +16 -0
- package/demo-source/main/default/objects/SBQQ__ProductRule__c/fields/SBQQ__ErrorMessage__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/SBQQ__ProductRule__c/fields/SBQQ__Type__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Service_Visit__c/Service_Visit__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Installation__c.field-meta.xml +13 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Reason__c.field-meta.xml +36 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Resolved__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Technician__c.field-meta.xml +13 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Visit_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Site_Survey__c/Site_Survey__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Roof_Type__c.field-meta.xml +31 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Shading_Factor__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Survey_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Surveyor__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Solar_Panel__c/Solar_Panel__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Active__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Manufacturer__c.field-meta.xml +26 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Unit_Cost__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Wattage__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/Warranty_Claim__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Claim_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Installation__c.field-meta.xml +13 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Issue__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Status__c.field-meta.xml +36 -0
- package/demo-source/main/default/omniDataTransforms/Quote_To_Project_Map_1.rpt-meta.xml +83 -0
- package/demo-source/main/default/omniIntegrationProcedures/Project_Provision_1.oip-meta.xml +87 -0
- package/demo-source/main/default/omniScripts/Customer_Intake_English_1.os-meta.xml +69 -0
- package/demo-source/main/default/permissionsets/Finance_Team.permissionset-meta.xml +77 -0
- package/demo-source/main/default/permissionsets/Project_Manager.permissionset-meta.xml +88 -0
- package/demo-source/main/default/profiles/Verdant_Installer.profile-meta.xml +193 -0
- package/demo-source/main/default/profiles/Verdant_Read_Only.profile-meta.xml +263 -0
- package/demo-source/main/default/profiles/Verdant_Sales_Rep.profile-meta.xml +194 -0
- package/demo-source/main/default/queues/Permit_Review_Queue.queue-meta.xml +9 -0
- package/demo-source/main/default/queues/Warranty_Queue.queue-meta.xml +9 -0
- package/demo-source/main/default/roles/Installer.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Ops_Director.role-meta.xml +9 -0
- package/demo-source/main/default/roles/Ops_Manager.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Sales_Manager.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Sales_Rep.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Sales_VP.role-meta.xml +9 -0
- package/demo-source/main/default/sharingRules/Invoice__c.sharingRules-meta.xml +17 -0
- package/demo-source/main/default/sharingRules/Project__c.sharingRules-meta.xml +17 -0
- package/demo-source/main/default/triggers/ProjectTrigger.trigger +8 -0
- package/demo-source/main/default/triggers/ProjectTrigger.trigger-meta.xml +5 -0
- package/demo-source/main/default/workflows/Opportunity.workflow-meta.xml +24 -0
- package/dist/index.js +900 -475
- package/package.json +25 -6
- package/server.json +32 -0
package/dist/index.js
CHANGED
|
@@ -1345,11 +1345,11 @@ var init_resolve_index = __esm({
|
|
|
1345
1345
|
};
|
|
1346
1346
|
cache = /* @__PURE__ */ new WeakMap();
|
|
1347
1347
|
getResolveIndex = async (store, options) => {
|
|
1348
|
-
const
|
|
1349
|
-
if (
|
|
1348
|
+
const cached2 = cache.get(store);
|
|
1349
|
+
if (cached2 !== void 0) {
|
|
1350
1350
|
const [countRow] = await runRows2(store, `SELECT count(*)::INT AS c FROM nodes`, []);
|
|
1351
|
-
if (countRow !== void 0 && Number(countRow["c"]) ===
|
|
1352
|
-
return
|
|
1351
|
+
if (countRow !== void 0 && Number(countRow["c"]) === cached2.nodeCount) {
|
|
1352
|
+
return cached2;
|
|
1353
1353
|
}
|
|
1354
1354
|
}
|
|
1355
1355
|
if (options?.graphDbPath !== void 0) {
|
|
@@ -26946,7 +26946,7 @@ var init_capabilities = __esm({
|
|
|
26946
26946
|
ROUTING_GUIDANCE = {
|
|
26947
26947
|
startHere: "On a vague or broad question, call sfi.route_question first \u2014 it returns the plane (vault | live | hybrid | unknown) and the tools to run. Default to the offline vault. Use sfi.live_* only for record counts, samples, population, describe, org limits, or inactive users \u2014 and only when the org has live consent (sfi.live_consent), SFI_LIVE_PLANE_ENABLED=1, or liveEnabled: true. Before destructive verdicts, call sfi.coverage_report.",
|
|
26948
26948
|
onAmbiguous: "On ambiguous resolution, clarify the component first. If the user wants live data and live is disabled, say so and offer to enable it once with sfi.live_consent { grant: true } (read-only) \u2014 do not guess from the vault.",
|
|
26949
|
-
onNone: "On none, offer /sfi-refresh for metadata gaps. For live-record questions when offline, name sfi.live_count or sfi.live_sample and the consent requirement \u2014 never invent counts.
|
|
26949
|
+
onNone: "On none, offer /sfi-refresh for metadata gaps. For live-record questions when offline, name sfi.live_count or sfi.live_sample and the consent requirement \u2014 never invent counts. When route_question returns toolCandidates (no rule placed the question, or it matched only weakly), follow its `guidance`: those candidates are an advisory shortlist \u2014 pick the right tool(s) from them, resolve any named component, run them, then synthesize. Do NOT say the capability is unbuilt when candidates are offered. Only a true unknown with NO candidates means the capability is not built yet (the gap is logged).",
|
|
26950
26950
|
groundAnswer: "Run the routed tools, then synthesize ONE answer from their output via sfi.synthesize_answer { question, draft }. It returns hallucinatedIds \u2014 any canonical id you wrote that no tool returned. Strip those before answering; cite only ids the tools produced, with their provenance."
|
|
26951
26951
|
};
|
|
26952
26952
|
BOUNDARIES4 = [
|
|
@@ -46749,6 +46749,283 @@ var init_retrieve_blindspot_report = __esm({
|
|
|
46749
46749
|
}
|
|
46750
46750
|
});
|
|
46751
46751
|
|
|
46752
|
+
// ../mcp/dist/src/semantic-funnel.js
|
|
46753
|
+
var STOPWORDS, SYNONYMS, tokenize6, TOOL_KEYWORDS, expand, buildToolDocs, cached, buildIndex, getFunnelIndex, semanticCandidates;
|
|
46754
|
+
var init_semantic_funnel = __esm({
|
|
46755
|
+
"../mcp/dist/src/semantic-funnel.js"() {
|
|
46756
|
+
"use strict";
|
|
46757
|
+
init_capabilities();
|
|
46758
|
+
init_tools();
|
|
46759
|
+
STOPWORDS = /* @__PURE__ */ new Set([
|
|
46760
|
+
"the",
|
|
46761
|
+
"a",
|
|
46762
|
+
"an",
|
|
46763
|
+
"of",
|
|
46764
|
+
"to",
|
|
46765
|
+
"in",
|
|
46766
|
+
"on",
|
|
46767
|
+
"for",
|
|
46768
|
+
"and",
|
|
46769
|
+
"or",
|
|
46770
|
+
"is",
|
|
46771
|
+
"are",
|
|
46772
|
+
"do",
|
|
46773
|
+
"does",
|
|
46774
|
+
"did",
|
|
46775
|
+
"what",
|
|
46776
|
+
"which",
|
|
46777
|
+
"show",
|
|
46778
|
+
"me",
|
|
46779
|
+
"my",
|
|
46780
|
+
"this",
|
|
46781
|
+
"that",
|
|
46782
|
+
"these",
|
|
46783
|
+
"those",
|
|
46784
|
+
"how",
|
|
46785
|
+
"where",
|
|
46786
|
+
"when",
|
|
46787
|
+
"who",
|
|
46788
|
+
"whom",
|
|
46789
|
+
"can",
|
|
46790
|
+
"could",
|
|
46791
|
+
"i",
|
|
46792
|
+
"it",
|
|
46793
|
+
"its",
|
|
46794
|
+
"have",
|
|
46795
|
+
"has",
|
|
46796
|
+
"had",
|
|
46797
|
+
"with",
|
|
46798
|
+
"from",
|
|
46799
|
+
"get",
|
|
46800
|
+
"give",
|
|
46801
|
+
"list",
|
|
46802
|
+
"tell",
|
|
46803
|
+
"about",
|
|
46804
|
+
"all",
|
|
46805
|
+
"any",
|
|
46806
|
+
"our",
|
|
46807
|
+
"we",
|
|
46808
|
+
"us",
|
|
46809
|
+
"you",
|
|
46810
|
+
"your",
|
|
46811
|
+
"be",
|
|
46812
|
+
"been",
|
|
46813
|
+
"there",
|
|
46814
|
+
"here",
|
|
46815
|
+
"into",
|
|
46816
|
+
"at",
|
|
46817
|
+
"by",
|
|
46818
|
+
"as",
|
|
46819
|
+
"so",
|
|
46820
|
+
"if",
|
|
46821
|
+
"then",
|
|
46822
|
+
"will"
|
|
46823
|
+
]);
|
|
46824
|
+
SYNONYMS = {
|
|
46825
|
+
// permissions / access / sharing
|
|
46826
|
+
access: ["permission", "permissions", "sharing", "visibility", "visible", "crud", "grant"],
|
|
46827
|
+
permission: ["access", "permissions", "grant", "profile", "permissionset", "effective"],
|
|
46828
|
+
permissions: ["permission", "access", "effective", "profile"],
|
|
46829
|
+
see: ["access", "visibility", "visible", "sharing", "view"],
|
|
46830
|
+
visible: ["access", "visibility", "see", "sharing"],
|
|
46831
|
+
create: ["access", "crud", "object", "insert"],
|
|
46832
|
+
read: ["access", "crud", "view"],
|
|
46833
|
+
edit: ["access", "crud", "modify", "write", "update", "editable"],
|
|
46834
|
+
modify: ["edit", "write", "change", "access"],
|
|
46835
|
+
delete: ["access", "crud", "remove", "deletion", "deletable"],
|
|
46836
|
+
remove: ["delete", "deletion"],
|
|
46837
|
+
admin: ["permission", "permissions", "modify", "view", "risk", "effective"],
|
|
46838
|
+
profile: ["permission", "permissions", "permissionset", "effective"],
|
|
46839
|
+
// usage / dependency / impact
|
|
46840
|
+
references: ["usage", "usages", "used", "reference", "depends", "uses", "find"],
|
|
46841
|
+
reference: ["usage", "usages", "references", "used"],
|
|
46842
|
+
referenced: ["usage", "usages", "references", "used"],
|
|
46843
|
+
used: ["usage", "usages", "references", "uses", "find"],
|
|
46844
|
+
uses: ["usage", "usages", "used"],
|
|
46845
|
+
depends: ["dependency", "usage", "usages", "impact", "depend", "references"],
|
|
46846
|
+
depend: ["dependency", "usage", "depends", "impact"],
|
|
46847
|
+
break: ["impact", "depend", "dependency", "breaks", "affected"],
|
|
46848
|
+
// schema / enumeration
|
|
46849
|
+
objects: ["object", "sobject", "components", "schema", "list"],
|
|
46850
|
+
object: ["sobject", "table", "entity", "components"],
|
|
46851
|
+
fields: ["field", "columns", "components", "list"],
|
|
46852
|
+
list: ["components", "enumerate", "inventory"],
|
|
46853
|
+
relationship: ["lookup", "child", "parent", "schema", "related"],
|
|
46854
|
+
child: ["lookup", "relationship", "parent", "components"],
|
|
46855
|
+
// counts / live records
|
|
46856
|
+
how: ["count", "many", "number"],
|
|
46857
|
+
many: ["count", "number"],
|
|
46858
|
+
show: ["sample", "records"],
|
|
46859
|
+
sample: ["records", "show"],
|
|
46860
|
+
// change / audit / freshness
|
|
46861
|
+
changed: ["modified", "change", "history", "audit"],
|
|
46862
|
+
change: ["modified", "history", "changed"],
|
|
46863
|
+
fresh: ["health", "stale", "freshness", "vault", "current"],
|
|
46864
|
+
stale: ["fresh", "freshness", "health", "outdated", "current"],
|
|
46865
|
+
inactive: ["active", "disabled", "draft", "obsolete"],
|
|
46866
|
+
// meaning / docs / misc
|
|
46867
|
+
help: ["meaning", "explain", "description", "text"],
|
|
46868
|
+
meaning: ["explain", "field", "help"],
|
|
46869
|
+
doc: ["document", "documentation", "handbook", "overview"],
|
|
46870
|
+
compare: ["diff", "difference", "across"],
|
|
46871
|
+
pii: ["sensitive", "personal", "compliance", "privacy"],
|
|
46872
|
+
run: ["execute", "fire", "trigger", "runs", "execution"],
|
|
46873
|
+
user: ["profile", "permissionset", "member"],
|
|
46874
|
+
field: ["column", "attribute"],
|
|
46875
|
+
// Intent verbs (generalize across phrasings, unlike tool-specific keywords).
|
|
46876
|
+
touches: ["impact", "depend", "dependency", "affected", "usage", "references", "uses"],
|
|
46877
|
+
relies: ["depend", "dependency", "impact", "usage", "references"],
|
|
46878
|
+
falls: ["break", "breaks", "impact", "affected"],
|
|
46879
|
+
dump: ["list", "inventory", "enumerate", "components"],
|
|
46880
|
+
inventory: ["list", "enumerate", "components", "all"],
|
|
46881
|
+
exist: ["list", "count", "inventory", "many"],
|
|
46882
|
+
blow: ["governor", "limit", "risk", "exceed"],
|
|
46883
|
+
populated: ["population", "filled", "fill", "usage"],
|
|
46884
|
+
required: ["mandatory", "needed", "blank", "null"],
|
|
46885
|
+
walk: ["explain", "trace", "describe", "order", "execution"]
|
|
46886
|
+
};
|
|
46887
|
+
tokenize6 = (text) => {
|
|
46888
|
+
const raw = text.toLowerCase().replace(/[‘’ʼ']/g, "'").replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter((t2) => t2.length > 1 && !STOPWORDS.has(t2));
|
|
46889
|
+
const out = [];
|
|
46890
|
+
for (const t2 of raw) {
|
|
46891
|
+
out.push(t2);
|
|
46892
|
+
if (t2.length > 3 && t2.endsWith("s") && !t2.endsWith("ss"))
|
|
46893
|
+
out.push(t2.slice(0, -1));
|
|
46894
|
+
}
|
|
46895
|
+
return out;
|
|
46896
|
+
};
|
|
46897
|
+
TOOL_KEYWORDS = {
|
|
46898
|
+
"sfi.list_components": "list inventory enumerate catalog all what do we have how many exist objects fields flows classes triggers profiles permission sets layouts record types validation rules approval processes reports dashboards omniscripts custom standard relationship child parent inactive active picklist values",
|
|
46899
|
+
"sfi.capabilities": "what can you do help capabilities what can i ask how do i use",
|
|
46900
|
+
"sfi.automation_risk_report": "objects more than one multiple triggers per object trigger quality automation risk",
|
|
46901
|
+
"sfi.live_group_count": "how many users assigned to profile membership group count",
|
|
46902
|
+
"sfi.annotations": "who owns owner steward responsible curated note",
|
|
46903
|
+
"sfi.last_modified": "who changed when modified last edited touched",
|
|
46904
|
+
"sfi.search_flow_metadata": "find flows matching named sync search flow by name",
|
|
46905
|
+
"sfi.who_can_access_object": "who can access object which profiles can read create edit delete",
|
|
46906
|
+
"sfi.live_sample": "show me sample example first few records rows give me",
|
|
46907
|
+
"sfi.governor_limit_risks": "queries soql governor limit large data volume bulk dml",
|
|
46908
|
+
"sfi.what_if_change_method_signature": "change method signature parameter argument breaks",
|
|
46909
|
+
"sfi.layout_for_user": "which layout does the profile user see page on object",
|
|
46910
|
+
"sfi.integration_map": "api limits at risk integration volume callout capacity external",
|
|
46911
|
+
"sfi.find_apex_usages": "which flows invoke call use apex classes methods from",
|
|
46912
|
+
"sfi.live_folder_access": "who can access report dashboard folder pipeline see view shared"
|
|
46913
|
+
};
|
|
46914
|
+
expand = (tokens) => {
|
|
46915
|
+
const out = [...tokens];
|
|
46916
|
+
for (const t2 of tokens) {
|
|
46917
|
+
const syn = SYNONYMS[t2];
|
|
46918
|
+
if (syn !== void 0)
|
|
46919
|
+
out.push(...syn);
|
|
46920
|
+
}
|
|
46921
|
+
return out;
|
|
46922
|
+
};
|
|
46923
|
+
buildToolDocs = () => {
|
|
46924
|
+
const docs = /* @__PURE__ */ new Map();
|
|
46925
|
+
for (const tool of V01_TOOLS) {
|
|
46926
|
+
const nameWords = tool.name.replace(/^sfi\./, "").replace(/_/g, " ");
|
|
46927
|
+
const keywords = TOOL_KEYWORDS[tool.name] ?? "";
|
|
46928
|
+
docs.set(tool.name, `${nameWords} ${nameWords} ${tool.description} ${keywords}`);
|
|
46929
|
+
}
|
|
46930
|
+
for (const cat of CATEGORIES) {
|
|
46931
|
+
const catText = ` ${cat.title} ${cat.description} ${cat.exampleQuestions.join(" ")}`;
|
|
46932
|
+
for (const toolName of cat.tools) {
|
|
46933
|
+
const prev = docs.get(toolName);
|
|
46934
|
+
if (prev !== void 0)
|
|
46935
|
+
docs.set(toolName, prev + catText);
|
|
46936
|
+
}
|
|
46937
|
+
}
|
|
46938
|
+
return docs;
|
|
46939
|
+
};
|
|
46940
|
+
cached = null;
|
|
46941
|
+
buildIndex = () => {
|
|
46942
|
+
const docs = buildToolDocs();
|
|
46943
|
+
const toolCategory = /* @__PURE__ */ new Map();
|
|
46944
|
+
for (const cat of CATEGORIES) {
|
|
46945
|
+
for (const tool of cat.tools)
|
|
46946
|
+
if (!toolCategory.has(tool))
|
|
46947
|
+
toolCategory.set(tool, cat.id);
|
|
46948
|
+
}
|
|
46949
|
+
const docTokens = /* @__PURE__ */ new Map();
|
|
46950
|
+
const df = /* @__PURE__ */ new Map();
|
|
46951
|
+
for (const [tool, doc] of docs) {
|
|
46952
|
+
const toks = tokenize6(doc);
|
|
46953
|
+
docTokens.set(tool, toks);
|
|
46954
|
+
for (const term of new Set(toks))
|
|
46955
|
+
df.set(term, (df.get(term) ?? 0) + 1);
|
|
46956
|
+
}
|
|
46957
|
+
const n2 = docs.size;
|
|
46958
|
+
const idf = /* @__PURE__ */ new Map();
|
|
46959
|
+
for (const [term, d2] of df)
|
|
46960
|
+
idf.set(term, Math.log((n2 + 1) / (d2 + 1)) + 1);
|
|
46961
|
+
const vectors = /* @__PURE__ */ new Map();
|
|
46962
|
+
for (const [tool, toks] of docTokens) {
|
|
46963
|
+
if (toks.length === 0) {
|
|
46964
|
+
vectors.set(tool, /* @__PURE__ */ new Map());
|
|
46965
|
+
continue;
|
|
46966
|
+
}
|
|
46967
|
+
const tf = /* @__PURE__ */ new Map();
|
|
46968
|
+
for (const t2 of toks)
|
|
46969
|
+
tf.set(t2, (tf.get(t2) ?? 0) + 1);
|
|
46970
|
+
const vec = /* @__PURE__ */ new Map();
|
|
46971
|
+
let norm = 0;
|
|
46972
|
+
for (const [term, f2] of tf) {
|
|
46973
|
+
const w2 = f2 / toks.length * (idf.get(term) ?? 0);
|
|
46974
|
+
vec.set(term, w2);
|
|
46975
|
+
norm += w2 * w2;
|
|
46976
|
+
}
|
|
46977
|
+
norm = Math.sqrt(norm) || 1;
|
|
46978
|
+
for (const [term, w2] of vec)
|
|
46979
|
+
vec.set(term, w2 / norm);
|
|
46980
|
+
vectors.set(tool, vec);
|
|
46981
|
+
}
|
|
46982
|
+
return { vectors, idf, toolCategory };
|
|
46983
|
+
};
|
|
46984
|
+
getFunnelIndex = () => cached ??= buildIndex();
|
|
46985
|
+
semanticCandidates = (question, k2 = 8) => {
|
|
46986
|
+
const idx = getFunnelIndex();
|
|
46987
|
+
const qTokens = expand(tokenize6(question));
|
|
46988
|
+
if (qTokens.length === 0)
|
|
46989
|
+
return [];
|
|
46990
|
+
const qtf = /* @__PURE__ */ new Map();
|
|
46991
|
+
for (const t2 of qTokens)
|
|
46992
|
+
qtf.set(t2, (qtf.get(t2) ?? 0) + 1);
|
|
46993
|
+
const qvec = /* @__PURE__ */ new Map();
|
|
46994
|
+
let qnorm = 0;
|
|
46995
|
+
for (const [term, f2] of qtf) {
|
|
46996
|
+
const w2 = f2 / qTokens.length * (idx.idf.get(term) ?? 0);
|
|
46997
|
+
if (w2 > 0) {
|
|
46998
|
+
qvec.set(term, w2);
|
|
46999
|
+
qnorm += w2 * w2;
|
|
47000
|
+
}
|
|
47001
|
+
}
|
|
47002
|
+
qnorm = Math.sqrt(qnorm) || 1;
|
|
47003
|
+
if (qvec.size === 0)
|
|
47004
|
+
return [];
|
|
47005
|
+
const scored = [];
|
|
47006
|
+
for (const [tool, vec] of idx.vectors) {
|
|
47007
|
+
let dot = 0;
|
|
47008
|
+
const [small, large] = qvec.size <= vec.size ? [qvec, vec] : [vec, qvec];
|
|
47009
|
+
for (const [term, w2] of small) {
|
|
47010
|
+
const o2 = large.get(term);
|
|
47011
|
+
if (o2 !== void 0)
|
|
47012
|
+
dot += w2 * o2;
|
|
47013
|
+
}
|
|
47014
|
+
if (dot <= 0)
|
|
47015
|
+
continue;
|
|
47016
|
+
const score = dot / qnorm;
|
|
47017
|
+
scored.push({
|
|
47018
|
+
tool,
|
|
47019
|
+
score: Math.round(score * 1e3) / 1e3,
|
|
47020
|
+
category: idx.toolCategory.get(tool) ?? null
|
|
47021
|
+
});
|
|
47022
|
+
}
|
|
47023
|
+
scored.sort((a2, b2) => b2.score - a2.score || a2.tool.localeCompare(b2.tool));
|
|
47024
|
+
return scored.slice(0, k2);
|
|
47025
|
+
};
|
|
47026
|
+
}
|
|
47027
|
+
});
|
|
47028
|
+
|
|
46752
47029
|
// ../mcp/dist/src/tools/tool-profile.js
|
|
46753
47030
|
var CORE_PROFILE_TOOLS, toolProfile;
|
|
46754
47031
|
var init_tool_profile = __esm({
|
|
@@ -46781,7 +47058,7 @@ var init_tool_profile = __esm({
|
|
|
46781
47058
|
// ../mcp/dist/src/tools/route-question.js
|
|
46782
47059
|
import { createHash as createHash6 } from "node:crypto";
|
|
46783
47060
|
import { z as z113 } from "zod";
|
|
46784
|
-
var RESOLVE_FALLBACK_MAX_TOKENS, tryResolveFallback, routeQuestionInputSchema, routeTrust, clarificationIdFor, selectedEntityArgsForRoute, extractEntityQuery, inferEntityTypes, refineEntityResolution, applyGlossaryAliases, entityAmbiguityRequiresClarification, splitCompoundQuestion, mixedInventoryAndStoragePlan, routeQuestionHandler;
|
|
47061
|
+
var RESOLVE_FALLBACK_MAX_TOKENS, tryResolveFallback, routeQuestionInputSchema, routeTrust, clarificationIdFor, selectedEntityArgsForRoute, extractEntityQuery, inferEntityTypes, refineEntityResolution, applyGlossaryAliases, entityAmbiguityRequiresClarification, splitCompoundQuestion, mixedInventoryAndStoragePlan, PLAN_FAMILY, ASSESSMENT_FAMILY, rerankForMode, guidanceForMode, routerMode, routeQuestionHandler;
|
|
46785
47062
|
var init_route_question = __esm({
|
|
46786
47063
|
"../mcp/dist/src/tools/route-question.js"() {
|
|
46787
47064
|
"use strict";
|
|
@@ -46789,6 +47066,7 @@ var init_route_question = __esm({
|
|
|
46789
47066
|
init_src();
|
|
46790
47067
|
init_answer_render();
|
|
46791
47068
|
init_intent_router();
|
|
47069
|
+
init_semantic_funnel();
|
|
46792
47070
|
init_resolve2();
|
|
46793
47071
|
init_tool_profile();
|
|
46794
47072
|
RESOLVE_FALLBACK_MAX_TOKENS = 3;
|
|
@@ -46844,7 +47122,16 @@ var init_route_question = __esm({
|
|
|
46844
47122
|
clarificationResponse: z113.object({
|
|
46845
47123
|
clarificationId: z113.string().min(1),
|
|
46846
47124
|
selection: z113.string().min(1)
|
|
46847
|
-
}).optional()
|
|
47125
|
+
}).optional(),
|
|
47126
|
+
/**
|
|
47127
|
+
* CAE-04 output mode — shapes how the host LLM should answer and which candidate
|
|
47128
|
+
* tools to favor. 'ask' = a quick grounded answer (the default behavior);
|
|
47129
|
+
* 'plan' = an ordered change plan (favors the what_if_* / impact tools);
|
|
47130
|
+
* 'assessment' = a full evaluation (favors the *_risk_report / readiness /
|
|
47131
|
+
* coverage tools). When set, toolCandidates + a mode-specific guidance line are
|
|
47132
|
+
* always attached, regardless of the deterministic route's confidence.
|
|
47133
|
+
*/
|
|
47134
|
+
mode: z113.enum(["ask", "plan", "assessment"]).optional()
|
|
46848
47135
|
});
|
|
46849
47136
|
routeTrust = () => ({
|
|
46850
47137
|
provenance: "offline_snapshot",
|
|
@@ -46852,7 +47139,7 @@ var init_route_question = __esm({
|
|
|
46852
47139
|
freshness: {},
|
|
46853
47140
|
completeness: { status: "complete" },
|
|
46854
47141
|
limitations: [
|
|
46855
|
-
"
|
|
47142
|
+
"In the default hybrid mode the meaning-ranked toolCandidates are PRIMARY and the host LLM decides which to run; the deterministic route is a non-authoritative hint, not the answer (SFI_ROUTER_MODE=offline makes the route authoritative for no-LLM hosts). A short phrase that merely names a real vault component is rescued to sfi.resolve so that path also consults the vault. Resolve any named component and confirm the plane before trusting an answer."
|
|
46856
47143
|
]
|
|
46857
47144
|
});
|
|
46858
47145
|
clarificationIdFor = (sourceTreeHash, route) => {
|
|
@@ -47034,6 +47321,30 @@ var init_route_question = __esm({
|
|
|
47034
47321
|
}
|
|
47035
47322
|
];
|
|
47036
47323
|
};
|
|
47324
|
+
PLAN_FAMILY = /^sfi\.(what_if_|get_impact|safe_to_delete|downstream_effects|tests_for_change|field_lineage)/;
|
|
47325
|
+
ASSESSMENT_FAMILY = /(_risk_report$|^sfi\.release_readiness|^sfi\.promotion_readiness|^sfi\.coverage_report|^sfi\.tech_debt_score|^sfi\.governor_limit_risks|^sfi\.crud_fls_audit)/;
|
|
47326
|
+
rerankForMode = (cands, mode) => {
|
|
47327
|
+
if (mode === void 0 || mode === "ask")
|
|
47328
|
+
return cands.slice(0, 8);
|
|
47329
|
+
const fam = mode === "plan" ? PLAN_FAMILY : ASSESSMENT_FAMILY;
|
|
47330
|
+
const lead = cands.filter((c2) => fam.test(c2.tool));
|
|
47331
|
+
const rest = cands.filter((c2) => !fam.test(c2.tool));
|
|
47332
|
+
return [...lead, ...rest].slice(0, 8);
|
|
47333
|
+
};
|
|
47334
|
+
guidanceForMode = (mode) => {
|
|
47335
|
+
const tail = " The candidates are an advisory shortlist, not a route \u2014 YOU pick. Resolve any named component first, ground the final answer with sfi.synthesize_answer, and never answer from a tool name alone.";
|
|
47336
|
+
switch (mode) {
|
|
47337
|
+
case "plan":
|
|
47338
|
+
return "PLAN mode: produce an ORDERED change plan. Favor the what_if_* / get_impact / safe_to_delete candidates, sequence them by dependency, and present numbered steps, each with its risk." + tail;
|
|
47339
|
+
case "assessment":
|
|
47340
|
+
return "ASSESSMENT mode: produce a full EVALUATION. Favor the *_risk_report / readiness / coverage candidates, run them, and present findings with severity + recommended actions." + tail;
|
|
47341
|
+
case "ask":
|
|
47342
|
+
return "ASK mode: answer concisely. Pick the candidate(s) that most directly answer, run them, and synthesize ONE short grounded answer." + tail;
|
|
47343
|
+
default:
|
|
47344
|
+
return "These toolCandidates are the meaning-ranked shortlist \u2014 they are PRIMARY and YOU decide. A deterministic `route` is also attached, but only as a HINT (a suggested tool order plus any resolved entity ids / suggestedArgs) \u2014 never follow it blindly. Plan: read the question \u2192 resolve any named component with sfi.resolve \u2192 pick the tool(s) to run from the candidates (sequence them if compound) \u2192 run them \u2192 ground the answer with sfi.synthesize_answer. Never answer from a tool name alone.";
|
|
47345
|
+
}
|
|
47346
|
+
};
|
|
47347
|
+
routerMode = () => (process.env.SFI_ROUTER_MODE ?? "").trim().toLowerCase() === "offline" ? "offline" : "hybrid";
|
|
47037
47348
|
routeQuestionHandler = async (ctx, input2) => {
|
|
47038
47349
|
let route = classifyQuestion(input2.question);
|
|
47039
47350
|
if (route.plane === "unknown" && route.intent === "unrouted") {
|
|
@@ -47213,6 +47524,9 @@ var init_route_question = __esm({
|
|
|
47213
47524
|
const args = i2 === primaryIdx ? route.suggestedArgs ?? {} : {};
|
|
47214
47525
|
return CORE_PROFILE_TOOLS.has(tool) ? { tool, args } : { tool: "sfi.run_analysis", args: { name: tool, args } };
|
|
47215
47526
|
}) : void 0;
|
|
47527
|
+
const wantCandidates = routerMode() === "hybrid";
|
|
47528
|
+
const toolCandidates = wantCandidates ? rerankForMode(semanticCandidates(input2.question, input2.mode !== void 0 ? 12 : 8), input2.mode) : [];
|
|
47529
|
+
const guidance = toolCandidates.length > 0 ? guidanceForMode(input2.mode) : void 0;
|
|
47216
47530
|
return ok({
|
|
47217
47531
|
data: {
|
|
47218
47532
|
route,
|
|
@@ -47220,8 +47534,12 @@ var init_route_question = __esm({
|
|
|
47220
47534
|
...entityEvidence !== void 0 ? { entityEvidence } : {},
|
|
47221
47535
|
...clarificationResolution !== void 0 ? { clarificationResolution } : {},
|
|
47222
47536
|
gapLogged: logged !== null,
|
|
47223
|
-
rendered: renderRouteMarkdown(route)
|
|
47537
|
+
rendered: toolCandidates.length > 0 ? `${renderRouteMarkdown(route)}
|
|
47538
|
+
|
|
47539
|
+
**Candidate tools (the shortlist \u2014 ranked by meaning; the route above is only a hint). You pick, then run:** ${toolCandidates.map((c2) => c2.tool).join(", ")}` : renderRouteMarkdown(route),
|
|
47224
47540
|
trust: routeTrust(),
|
|
47541
|
+
...toolCandidates.length > 0 ? { toolCandidates } : {},
|
|
47542
|
+
...guidance !== void 0 ? { guidance } : {},
|
|
47225
47543
|
...invoke !== void 0 ? { invoke } : {}
|
|
47226
47544
|
},
|
|
47227
47545
|
vaultState: {
|
|
@@ -50293,9 +50611,9 @@ var init_tests_for_change = __esm({
|
|
|
50293
50611
|
}
|
|
50294
50612
|
const nodeCache = /* @__PURE__ */ new Map();
|
|
50295
50613
|
const loadNode2 = async (id) => {
|
|
50296
|
-
const
|
|
50297
|
-
if (
|
|
50298
|
-
return ok(
|
|
50614
|
+
const cached2 = nodeCache.get(id);
|
|
50615
|
+
if (cached2 !== void 0)
|
|
50616
|
+
return ok(cached2);
|
|
50299
50617
|
const r2 = await getNodeById(ctx.graph, id);
|
|
50300
50618
|
if (!r2.ok) {
|
|
50301
50619
|
return err({ kind: "internal", message: `graph query failed: ${r2.error.message}` });
|
|
@@ -53191,7 +53509,7 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
53191
53509
|
|
|
53192
53510
|
// ../mcp/dist/src/tools/what-if-split-profile.js
|
|
53193
53511
|
import { z as z138 } from "zod";
|
|
53194
|
-
var PROFILE_PREFIX2, PERMISSION_SET_PREFIX, SPLIT_DEFAULT_LIMIT, SPLIT_MAX_LIMIT, DISCLOSURE18, whatIfSplitProfileInputSchema,
|
|
53512
|
+
var PROFILE_PREFIX2, PERMISSION_SET_PREFIX, SPLIT_DEFAULT_LIMIT, SPLIT_MAX_LIMIT, DISCLOSURE18, whatIfSplitProfileInputSchema, tokenize7, makeCandidate, resolveTargets, overlapCount, keywordMatch, domainClusterMatch, assignGrant, splitGrants, fetchProfile2, sortAssignments, sortUnassigned, whatIfSplitProfileHandler;
|
|
53195
53513
|
var init_what_if_split_profile = __esm({
|
|
53196
53514
|
"../mcp/dist/src/tools/what-if-split-profile.js"() {
|
|
53197
53515
|
"use strict";
|
|
@@ -53209,7 +53527,7 @@ var init_what_if_split_profile = __esm({
|
|
|
53209
53527
|
limit: z138.number().int().min(1).max(SPLIT_MAX_LIMIT).optional(),
|
|
53210
53528
|
offset: z138.number().int().min(0).optional()
|
|
53211
53529
|
});
|
|
53212
|
-
|
|
53530
|
+
tokenize7 = (raw) => {
|
|
53213
53531
|
const spaced = raw.replace(/[._\-/]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
|
|
53214
53532
|
return spaced.split(/\s+/).map((t2) => t2.toLowerCase().trim()).filter((t2) => t2.length > 0 && t2 !== "c" && t2 !== "__c");
|
|
53215
53533
|
};
|
|
@@ -53218,7 +53536,7 @@ var init_what_if_split_profile = __esm({
|
|
|
53218
53536
|
return {
|
|
53219
53537
|
id,
|
|
53220
53538
|
apiName: stripped,
|
|
53221
|
-
tokens: new Set(
|
|
53539
|
+
tokens: new Set(tokenize7(stripped))
|
|
53222
53540
|
};
|
|
53223
53541
|
};
|
|
53224
53542
|
resolveTargets = async (ctx, targetIds) => {
|
|
@@ -53265,7 +53583,7 @@ var init_what_if_split_profile = __esm({
|
|
|
53265
53583
|
return count;
|
|
53266
53584
|
};
|
|
53267
53585
|
keywordMatch = (settingId, candidates) => {
|
|
53268
|
-
const settingTokens = new Set(
|
|
53586
|
+
const settingTokens = new Set(tokenize7(settingId));
|
|
53269
53587
|
if (settingTokens.size === 0)
|
|
53270
53588
|
return null;
|
|
53271
53589
|
let best = null;
|
|
@@ -53289,7 +53607,7 @@ var init_what_if_split_profile = __esm({
|
|
|
53289
53607
|
}
|
|
53290
53608
|
if (parent === null || parent.length === 0)
|
|
53291
53609
|
return null;
|
|
53292
|
-
const parentTokens = new Set(
|
|
53610
|
+
const parentTokens = new Set(tokenize7(parent));
|
|
53293
53611
|
if (parentTokens.size === 0)
|
|
53294
53612
|
return null;
|
|
53295
53613
|
for (const candidate of candidates) {
|
|
@@ -56554,7 +56872,7 @@ var init_tools = __esm({
|
|
|
56554
56872
|
},
|
|
56555
56873
|
{
|
|
56556
56874
|
name: "sfi.route_question",
|
|
56557
|
-
description: "Front-door router:
|
|
56875
|
+
description: "Front-door router: for a plain-language question, surface a meaning-ranked shortlist of the sfi.* tools that can answer it \u2014 your host LLM picks which to run \u2014 plus the plane it belongs to (vault | live | hybrid | unknown), so the user never types a tool name. Read-only; it advises, it does not answer. Compound questions carry step ids and `dependsOn` edges: independent steps may run in parallel; a `then`-linked step waits for its prerequisite. On ambiguity it fails closed with `executionBlocked`, a clarification id, and offered options; resume deterministically by calling again with the exact same question plus `clarificationResponse: { clarificationId, selection }`. Stale ids and invented selections are rejected. Tells you when to sfi.resolve a named component first, whether the opt-in live plane is required, surfaces `suggestedArgs` (heuristic per-intent hints \u2014 e.g. `event: 'update'` for a save-order question so you can call `what_happens_on_save` without guessing the DML event), and \u2014 when the question hits a capability we lack \u2014 returns an honest 'unknown'/gap and logs it for the backlog instead of fabricating; under `SFI_TOOL_PROFILE=core` the response also carries `invoke`: the routed tools as EXECUTABLE calls (core tools direct, everything else as the byte-identical `sfi.run_analysis` gateway envelope, suggestedArgs threaded) (a short phrase that merely NAMES a real vault component, with no question, is instead routed to sfi.resolve rather than 'unknown'). In the default hybrid mode the meaning-ranked `toolCandidates` are PRIMARY: every routable question carries the shortlist (offline TF-IDF over the capability map, no neural model, no network) plus a `guidance` line stating the loop YOU own \u2014 read the candidates \u2192 resolve any named component \u2192 pick/sequence the tool(s) \u2192 run them \u2192 ground via sfi.synthesize_answer. YOU decide which to run; the deterministic `route` rides along only as a non-authoritative HINT (suggested tool order + any resolved entity / suggestedArgs). Set `SFI_ROUTER_MODE=offline` for a deterministic, no-LLM route (Design A) where the route is authoritative and candidates are omitted \u2014 for CI / air-gapped hosts. An optional `mode` ('ask' | 'plan' | 'assessment') tailors the guidance and reranks the candidates toward that mode's family \u2014 'plan' favors the what_if_* / impact tools (an ordered change plan), 'assessment' favors the *_risk_report / readiness / coverage tools (a full evaluation), 'ask' is a quick grounded answer. Call this first on a vague/broad question to decide which tool(s) to run.",
|
|
56558
56876
|
inputSchema: ROUTE_QUESTION_INPUT_SCHEMA
|
|
56559
56877
|
},
|
|
56560
56878
|
{
|
|
@@ -58026,7 +58344,7 @@ var init_server = __esm({
|
|
|
58026
58344
|
|
|
58027
58345
|
How to use it well:
|
|
58028
58346
|
- To orient a fresh session, or to answer "what can you do / what can I ask?", call \`sfi.capabilities\` (no arguments). It returns the categorized capability map, the live tool count, and the recommended conversational pattern.
|
|
58029
|
-
- For a vague, broad, or compound question ("how many Accounts", "who can edit SSN", "which reports are useless", "what runs on save"), call \`sfi.route_question\` FIRST.
|
|
58347
|
+
- For a vague, broad, or compound question ("how many Accounts", "who can edit SSN", "which reports are useless", "what runs on save"), call \`sfi.route_question\` FIRST. In the default hybrid mode it returns \`toolCandidates\` \u2014 a meaning-ranked shortlist of tools \u2014 as the PRIMARY output, plus a \`guidance\` line: YOU read the candidates, resolve any named component, pick/sequence the tool(s), run them, and ground via \`sfi.synthesize_answer\`. The deterministic \`route\` (plane, ordered tools, dependency-aware plan) rides along as a non-authoritative HINT \u2014 use it to inform your pick, not as a command. If \`executionBlocked\` is true, STOP and ask \`route.clarification.question\` before running any routed tool; resume only with the exact offered \`clarificationId\` + selection, never an invented option. When neither the candidates nor the route place a question, tell the user the capability is not built rather than guessing. (A no-LLM host can set \`SFI_ROUTER_MODE=offline\` to make the deterministic route authoritative and omit candidates.)
|
|
58030
58348
|
- When the user names a component informally ("the email field", "the payment object", a typo), call \`sfi.resolve\` FIRST. It returns ranked candidates with a disposition: exact | ambiguous | none. Never guess a canonical id from memory. On \`ambiguous\`, ask the user to pick from the candidates; on \`none\`, offer \`/sfi-refresh\` (the vault may be stale) or stop \u2014 never fabricate a match.
|
|
58031
58349
|
- Every org artifact you name must come from an \`sfi.*\` tool call and be cited with its canonical id (e.g. \`CustomField:Account.Industry__c\`). Disclose provenance per claim: \`offline_snapshot\` for vault answers, \`live_org\` for live answers (stamp the as-of time from \`trust.freshness\`), \`hybrid\` when you fuse both \u2014 never let a live count imply the vault proved something, or vice-versa.
|
|
58032
58350
|
- Answers are only as fresh as the last refresh; if \`sfi.health_check\` reports stale or missing, tell the user to run \`/sfi-refresh\`. Record-level data (counts, samples, field population, org limits, inactive users) is LIVE, read-only, and queried at call time. The live plane is opt-in PER ORG: enable it once with \`sfi.live_consent { grant: true }\` (persists across sessions; still strictly read-only) \u2014 or SFI_LIVE_PLANE_ENABLED=1, or \`liveEnabled: true\` for a single call. If it is disabled, say so and offer to enable it; never infer record values from the vault.`;
|
|
@@ -58243,6 +58561,7 @@ var init_src7 = __esm({
|
|
|
58243
58561
|
init_server();
|
|
58244
58562
|
init_serve_http();
|
|
58245
58563
|
init_tools();
|
|
58564
|
+
init_semantic_funnel();
|
|
58246
58565
|
}
|
|
58247
58566
|
});
|
|
58248
58567
|
|
|
@@ -59448,7 +59767,7 @@ var init_package_version = __esm({
|
|
|
59448
59767
|
"use strict";
|
|
59449
59768
|
readCliPackageVersion = () => {
|
|
59450
59769
|
if (true)
|
|
59451
|
-
return "0.1.
|
|
59770
|
+
return "0.1.13";
|
|
59452
59771
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
59453
59772
|
try {
|
|
59454
59773
|
const raw = readFileSync3(fileURLToPath(new URL(rel, import.meta.url)), "utf8");
|
|
@@ -126264,7 +126583,7 @@ import { appendFile as appendFile4, mkdir as mkdir12, readdir as readdir6, readF
|
|
|
126264
126583
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
126265
126584
|
import { join as join26, resolve as resolve4 } from "node:path";
|
|
126266
126585
|
import { promisify as promisify4 } from "node:util";
|
|
126267
|
-
var execAsync, SF_API_VERSION2, EMPTY_COUNTS, EMPTY_SKIPPED, failed, PACKAGE_VERSION, extractCachePath, loadExtractCache, saveExtractCache, applyApexAstEdges, runRefresh, preserveFactsForSideBuild, aggregateFailuresByType, buildCoverageEntries2, decoratePendingCoverage, decorateReportsCapCoverage, countGraphTotals, fullRebuild, importGraph, persistResolveIndexBestEffort, runWithOpenGraph, appendRefreshHistory, appendRiskScores, writeOnboardingDoc, writeOrgCard, diffCounts, sumCounts, metricCounts, signedDelta, findAnnotationOrphans, buildRefreshPulse, computeChangeSummary, TOOLING_API_ENRICHED_TYPES, runToolingApiEnrichment, loadVaultConfig, METADATA_API_NAME2, toApiName, SF_MAX_BUFFER, STANDARD_OBJECTS_TO_MODEL, manifestMembersForType, appendStandardObjectDescribeFields, AUTOMATION_EDGE_TYPES, objectsToExpandManifest, buildPackageXml, getOrgSupportedTypes, selectManifestTypes, classifyRetrieveError, splitTypeBatch,
|
|
126586
|
+
var execAsync, SF_API_VERSION2, EMPTY_COUNTS, EMPTY_SKIPPED, failed, PACKAGE_VERSION, extractCachePath, loadExtractCache, saveExtractCache, applyApexAstEdges, runRefresh, preserveFactsForSideBuild, aggregateFailuresByType, buildCoverageEntries2, decoratePendingCoverage, decorateReportsCapCoverage, countGraphTotals, fullRebuild, importGraph, persistResolveIndexBestEffort, runWithOpenGraph, appendRefreshHistory, appendRiskScores, writeOnboardingDoc, writeOrgCard, diffCounts, sumCounts, metricCounts, signedDelta, findAnnotationOrphans, buildRefreshPulse, computeChangeSummary, TOOLING_API_ENRICHED_TYPES, runToolingApiEnrichment, loadVaultConfig, METADATA_API_NAME2, toApiName, SF_MAX_BUFFER, STANDARD_OBJECTS_TO_MODEL, manifestMembersForType, appendStandardObjectDescribeFields, AUTOMATION_EDGE_TYPES, objectsToExpandManifest, buildPackageXml, getOrgSupportedTypes, selectManifestTypes, classifyRetrieveError, splitTypeBatch, salientErrorLine, summarizeRetrieveFailures, retrieveWithFallback, retrieveTypeBatch, runSfRetrieve, computeReconciledTypes, runSfRetrieveObjects, buildFolderedReportManifest, REPORT_META_SUFFIXES, REPORT_DIR_MARKERS, countLandedReportMembers, collectReportMetaFiles, runSfRetrieveFolderedReports, reportsCap, runSfRetrieveSmartReports, formatFailure, formatBlock, formatChangeSummary, formatRefreshSummary, formatReportsCapSummary, SKIPPED_TOP_N, KNOWN_UNMODELED_SKIP_DIRS, formatSkippedWarning, formatToolingApiSummary, demandCoverageStatusOf, DEMAND_REFUSAL_REASON, classifyForDemandRetrieve, markDemandQueueDrains, runDemandRetrieve, formatDemandRetrieveSummary, registerRefreshCommand;
|
|
126268
126587
|
var init_refresh = __esm({
|
|
126269
126588
|
"dist/src/commands/refresh.js"() {
|
|
126270
126589
|
"use strict";
|
|
@@ -126473,10 +126792,10 @@ var init_refresh = __esm({
|
|
|
126473
126792
|
progress(`Incremental: reused ${walked.reusedCount} unchanged file(s) from cache.`);
|
|
126474
126793
|
}
|
|
126475
126794
|
if (!opts.noPull && (requestedTypes === null || requestedTypes.has("CustomObject"))) {
|
|
126476
|
-
const
|
|
126477
|
-
if (
|
|
126478
|
-
progress(`Auto-expanding retrieve: ${
|
|
126479
|
-
const pulled2 = await runSfRetrieveObjects(targetOrg, paths.source,
|
|
126795
|
+
const expand2 = objectsToExpandManifest(walked.results);
|
|
126796
|
+
if (expand2.length > 0) {
|
|
126797
|
+
progress(`Auto-expanding retrieve: ${expand2.length} object(s) your automation references but the wildcard excluded...`);
|
|
126798
|
+
const pulled2 = await runSfRetrieveObjects(targetOrg, paths.source, expand2);
|
|
126480
126799
|
if (pulled2.ok) {
|
|
126481
126800
|
walked = await walkAndExtract(paths.source, requestedTypes, prevCache);
|
|
126482
126801
|
} else {
|
|
@@ -127397,7 +127716,6 @@ ${card.body}
|
|
|
127397
127716
|
/no authorization|not authorized|no auth information|expired access\/refresh token|invalid[_ ]grant/i,
|
|
127398
127717
|
/org ?login|sfdx[_ ]?login|requires? you to (?:re-?)?authenticate|session expired/i,
|
|
127399
127718
|
/does not contain a valid salesforce dx project/i,
|
|
127400
|
-
/enotfound|etimedout|econnrefused|enetunreach|getaddrinfo|socket hang up|network error|unable to connect|connection timed out/i,
|
|
127401
127719
|
/no default (?:environment|org)|no target-?org|no org configured|requires a target org/i
|
|
127402
127720
|
];
|
|
127403
127721
|
return GLOBAL_SIGNALS.some((re2) => re2.test(message)) ? "global" : "per-type";
|
|
@@ -127406,17 +127724,24 @@ ${card.body}
|
|
|
127406
127724
|
const mid = Math.floor(types.length / 2);
|
|
127407
127725
|
return [types.slice(0, mid), types.slice(mid)];
|
|
127408
127726
|
};
|
|
127409
|
-
|
|
127727
|
+
salientErrorLine = (error) => {
|
|
127728
|
+
const lines = error.split("\n").map((l2) => l2.trim()).filter((l2) => l2.length > 0);
|
|
127729
|
+
const named = lines.find((l2) => /^Error\b|Error \(|^ERROR\b|UnsafeFilepathError|INVALID_TYPE/.test(l2));
|
|
127730
|
+
if (named !== void 0)
|
|
127731
|
+
return named;
|
|
127732
|
+
const nonWrapper = lines.find((l2) => !l2.startsWith("Command failed:"));
|
|
127733
|
+
return nonWrapper ?? lines[0] ?? error.trim();
|
|
127734
|
+
};
|
|
127410
127735
|
summarizeRetrieveFailures = (failures) => {
|
|
127411
127736
|
if (failures.length === 0)
|
|
127412
127737
|
return "";
|
|
127413
|
-
const uniqueReasons = new Set(failures.map((f2) =>
|
|
127738
|
+
const uniqueReasons = new Set(failures.map((f2) => salientErrorLine(f2.error)));
|
|
127414
127739
|
if (uniqueReasons.size === 1) {
|
|
127415
127740
|
const [reason] = [...uniqueReasons];
|
|
127416
127741
|
const [first] = failures;
|
|
127417
127742
|
return failures.length === 1 && first !== void 0 ? `${first.type}: ${reason}` : reason ?? "";
|
|
127418
127743
|
}
|
|
127419
|
-
return failures.map((f2) => `${f2.type} (${
|
|
127744
|
+
return failures.map((f2) => `${f2.type} (${salientErrorLine(f2.error)})`).join("; ");
|
|
127420
127745
|
};
|
|
127421
127746
|
retrieveWithFallback = async (allTypes, retrieveBatch) => {
|
|
127422
127747
|
const succeeded = [];
|
|
@@ -127446,19 +127771,23 @@ ${card.body}
|
|
|
127446
127771
|
retrieveTypeBatch = async (targetOrg, sourceDir, types) => {
|
|
127447
127772
|
const stamp = `${Date.now()}-${types.length}-${types[0] ?? "batch"}`;
|
|
127448
127773
|
const manifestPath = join26(tmpdir2(), `sfi-refresh-package-${stamp}.xml`);
|
|
127449
|
-
const
|
|
127774
|
+
const projectDir = join26(tmpdir2(), `sfi-retrieve-${stamp}`);
|
|
127775
|
+
const pkgDir = join26(projectDir, "force-app");
|
|
127776
|
+
await mkdir12(pkgDir, { recursive: true });
|
|
127777
|
+
await writeFile11(join26(projectDir, "sfdx-project.json"), `{"packageDirectories":[{"path":"force-app","default":true}],"sourceApiVersion":"${SF_API_VERSION2}"}
|
|
127778
|
+
`, "utf8");
|
|
127450
127779
|
await writeFile11(manifestPath, buildPackageXml(types), "utf8");
|
|
127451
127780
|
try {
|
|
127452
|
-
await execAsync(`sf project retrieve start --manifest "${manifestPath}" --target-org "${targetOrg}"
|
|
127453
|
-
const reconcile = await reconcileSourceDeletions(sourceDir,
|
|
127454
|
-
await syncAuthoritativeRetrieveIntoSource(sourceDir,
|
|
127781
|
+
await execAsync(`sf project retrieve start --manifest "${manifestPath}" --target-org "${targetOrg}"`, { maxBuffer: SF_MAX_BUFFER, cwd: projectDir });
|
|
127782
|
+
const reconcile = await reconcileSourceDeletions(sourceDir, pkgDir, new Set(types));
|
|
127783
|
+
await syncAuthoritativeRetrieveIntoSource(sourceDir, pkgDir);
|
|
127455
127784
|
return ok({ deletedCount: reconcile.deletedCount });
|
|
127456
127785
|
} catch (cause) {
|
|
127457
127786
|
return err(cause instanceof Error ? cause.message : String(cause));
|
|
127458
127787
|
} finally {
|
|
127459
127788
|
await rm5(manifestPath, { force: true }).catch(() => {
|
|
127460
127789
|
});
|
|
127461
|
-
await rm5(
|
|
127790
|
+
await rm5(projectDir, { recursive: true, force: true }).catch(() => {
|
|
127462
127791
|
});
|
|
127463
127792
|
}
|
|
127464
127793
|
};
|
|
@@ -127775,7 +128104,7 @@ ${typesXml}
|
|
|
127775
128104
|
lines.push("", `Errors (${result.errors.length}):`, ...result.errors.map(formatFailure));
|
|
127776
128105
|
}
|
|
127777
128106
|
if (result.retrieveFailures !== void 0 && result.retrieveFailures.length > 0) {
|
|
127778
|
-
lines.push("", `Partial retrieve \u2014 ${result.retrieveFailures.length} metadata type(s) skipped (the org may have rejected them; the vault is built from what landed):`, ...result.retrieveFailures.map((f2) => ` ${f2.type}: ${
|
|
128107
|
+
lines.push("", `Partial retrieve \u2014 ${result.retrieveFailures.length} metadata type(s) skipped (the org may have rejected them; the vault is built from what landed):`, ...result.retrieveFailures.map((f2) => ` ${f2.type}: ${salientErrorLine(f2.error)}`));
|
|
127779
128108
|
}
|
|
127780
128109
|
if (result.fatalError !== void 0) {
|
|
127781
128110
|
lines.push("", `Fatal: ${result.fatalError}`);
|
|
@@ -128066,8 +128395,8 @@ ${typesXml}
|
|
|
128066
128395
|
});
|
|
128067
128396
|
|
|
128068
128397
|
// dist/src/commands/stale-sweep.js
|
|
128069
|
-
import { writeFile as
|
|
128070
|
-
import { join as
|
|
128398
|
+
import { writeFile as writeFile14 } from "node:fs/promises";
|
|
128399
|
+
import { join as join30 } from "node:path";
|
|
128071
128400
|
var SOURCE_MEMBER_TYPE_RE, runStaleSweep, registerStaleSweepCommand;
|
|
128072
128401
|
var init_stale_sweep = __esm({
|
|
128073
128402
|
"dist/src/commands/stale-sweep.js"() {
|
|
@@ -128076,8 +128405,8 @@ var init_stale_sweep = __esm({
|
|
|
128076
128405
|
init_src2();
|
|
128077
128406
|
SOURCE_MEMBER_TYPE_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
128078
128407
|
runStaleSweep = async (options) => {
|
|
128079
|
-
const paths = vaultPaths(
|
|
128080
|
-
const manifestResult = await loadManifest(
|
|
128408
|
+
const paths = vaultPaths(join30(options.cwd, "org-kb"));
|
|
128409
|
+
const manifestResult = await loadManifest(join30(options.cwd, "org-kb"));
|
|
128081
128410
|
if (!manifestResult.ok) {
|
|
128082
128411
|
return { ok: false, message: `vault manifest unavailable: ${manifestResult.error.message} \u2014 run sfi init / refresh first` };
|
|
128083
128412
|
}
|
|
@@ -128133,7 +128462,7 @@ var init_stale_sweep = __esm({
|
|
|
128133
128462
|
erroredTypes: result.value.erroredTypes
|
|
128134
128463
|
};
|
|
128135
128464
|
}
|
|
128136
|
-
await
|
|
128465
|
+
await writeFile14(join30(paths.meta, "staleness.json"), `${JSON.stringify(snapshot, null, 2)}
|
|
128137
128466
|
`, "utf8");
|
|
128138
128467
|
return { ok: true, snapshot };
|
|
128139
128468
|
};
|
|
@@ -128168,8 +128497,8 @@ __export(watch_exports, {
|
|
|
128168
128497
|
watchDailyTicks: () => watchDailyTicks
|
|
128169
128498
|
});
|
|
128170
128499
|
import { spawn } from "node:child_process";
|
|
128171
|
-
import { existsSync as
|
|
128172
|
-
import { join as
|
|
128500
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
128501
|
+
import { join as join31 } from "node:path";
|
|
128173
128502
|
var PIDFILE, DEFAULT_INTERVAL_MS2, MIN_INTERVAL_MS, JITTER_RATIO, watchDailyTicks, pidPath, processAlive, readWatchState, alreadyExists, removeStaleWatchLock, claimWatchLock, releaseWatchLock, jitteredDelay, runWatchLoop, parseInterval, registerWatchCommand;
|
|
128174
128503
|
var init_watch = __esm({
|
|
128175
128504
|
"dist/src/commands/watch.js"() {
|
|
@@ -128184,7 +128513,7 @@ var init_watch = __esm({
|
|
|
128184
128513
|
const n2 = raw ? Number(raw) : NaN;
|
|
128185
128514
|
return Number.isFinite(n2) && n2 >= 1 ? Math.floor(n2) : 96;
|
|
128186
128515
|
};
|
|
128187
|
-
pidPath = (cwd) =>
|
|
128516
|
+
pidPath = (cwd) => join31(cwd, "org-kb", "meta", PIDFILE);
|
|
128188
128517
|
processAlive = (pid) => {
|
|
128189
128518
|
try {
|
|
128190
128519
|
process.kill(pid, 0);
|
|
@@ -128195,10 +128524,10 @@ var init_watch = __esm({
|
|
|
128195
128524
|
};
|
|
128196
128525
|
readWatchState = (cwd) => {
|
|
128197
128526
|
const p2 = pidPath(cwd);
|
|
128198
|
-
if (!
|
|
128527
|
+
if (!existsSync7(p2))
|
|
128199
128528
|
return { state: "absent" };
|
|
128200
128529
|
try {
|
|
128201
|
-
const parsed = JSON.parse(
|
|
128530
|
+
const parsed = JSON.parse(readFileSync5(p2, "utf8"));
|
|
128202
128531
|
if (!Number.isInteger(parsed.pid) || parsed.pid < 1)
|
|
128203
128532
|
return { state: "stale" };
|
|
128204
128533
|
return processAlive(parsed.pid) ? { state: "running", pidState: parsed } : { state: "stale", pidState: parsed };
|
|
@@ -128223,7 +128552,7 @@ var init_watch = __esm({
|
|
|
128223
128552
|
`;
|
|
128224
128553
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
128225
128554
|
try {
|
|
128226
|
-
|
|
128555
|
+
writeFileSync2(pidPath(cwd), contents, { flag: "wx" });
|
|
128227
128556
|
return true;
|
|
128228
128557
|
} catch (error) {
|
|
128229
128558
|
if (!alreadyExists(error))
|
|
@@ -128338,7 +128667,7 @@ var init_watch = __esm({
|
|
|
128338
128667
|
const watch = program.command("watch").description("Org-drift watcher: a small detached daemon running one read-only stale-sweep tick per interval (default 15m, floor 5m, \xB110% jitter, separate daily tick budget) so meta/staleness.json stays current. Single-instance per vault (stale pidfiles recovered). Subcommands: status, stop.").option("--interval <duration>", "Tick interval, e.g. 15m or 1h (floor 5m)", "15m").option("--auto-refresh <mode>", "When a tick detects drift, run `sfi refresh --incremental` automatically \u2014 at most once per hour; lock-safe (epoch side-build); a failure logs and the watcher continues. Mode: 'incremental'.").option("--foreground", "Run the loop in THIS process (used by the detached child)").option("--drain-demand-queue", "After a tick, drain queued automation-critical phantom hits (`meta/demand-queue.jsonl`, recorded by sfi.get_component) via the demand-retrieve gate \u2014 at most once per hour; a failure logs and the watcher continues.").action(async (flags) => {
|
|
128339
128668
|
const cwd = process.cwd();
|
|
128340
128669
|
const intervalMs = parseInterval(flags.interval);
|
|
128341
|
-
if (!
|
|
128670
|
+
if (!existsSync7(join31(cwd, "org-kb", "meta"))) {
|
|
128342
128671
|
process.stderr.write("watch: no vault here \u2014 run from the vault directory (org-kb/ missing).\n");
|
|
128343
128672
|
process.exit(1);
|
|
128344
128673
|
}
|
|
@@ -128370,11 +128699,11 @@ var init_watch = __esm({
|
|
|
128370
128699
|
drainQueue: {
|
|
128371
128700
|
peek: async (dir) => {
|
|
128372
128701
|
const { queuedDrainIds: queuedDrainIds2, readDemandQueue: readDemandQueue2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
128373
|
-
return queuedDrainIds2(await readDemandQueue2(
|
|
128702
|
+
return queuedDrainIds2(await readDemandQueue2(join31(dir, "org-kb"))).length;
|
|
128374
128703
|
},
|
|
128375
128704
|
drain: async (dir) => {
|
|
128376
128705
|
const { queuedDrainIds: queuedDrainIds2, readDemandQueue: readDemandQueue2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
128377
|
-
const ids = queuedDrainIds2(await readDemandQueue2(
|
|
128706
|
+
const ids = queuedDrainIds2(await readDemandQueue2(join31(dir, "org-kb")));
|
|
128378
128707
|
if (ids.length === 0)
|
|
128379
128708
|
return;
|
|
128380
128709
|
const { runDemandRetrieve: runDemandRetrieve2 } = await Promise.resolve().then(() => (init_refresh(), refresh_exports));
|
|
@@ -128407,8 +128736,8 @@ var init_watch = __esm({
|
|
|
128407
128736
|
watch.command("status").description("Report the watcher daemon state and the last sweep stamp.").action(() => {
|
|
128408
128737
|
const cwd = process.cwd();
|
|
128409
128738
|
const state = readWatchState(cwd);
|
|
128410
|
-
const stalenessPath =
|
|
128411
|
-
const lastSweep =
|
|
128739
|
+
const stalenessPath = join31(cwd, "org-kb", "meta", "staleness.json");
|
|
128740
|
+
const lastSweep = existsSync7(stalenessPath) ? JSON.parse(readFileSync5(stalenessPath, "utf8")).generatedAt : void 0;
|
|
128412
128741
|
if (state.state === "running") {
|
|
128413
128742
|
process.stdout.write(`watch: RUNNING (pid ${state.pidState?.pid} since ${state.pidState?.startedAt}, every ~${Math.round((state.pidState?.intervalMs ?? 0) / 6e4)}m). Last sweep: ${lastSweep ?? "(none yet)"}.
|
|
128414
128743
|
`);
|
|
@@ -128440,8 +128769,8 @@ var init_watch = __esm({
|
|
|
128440
128769
|
});
|
|
128441
128770
|
|
|
128442
128771
|
// dist/src/program.js
|
|
128443
|
-
import { readFileSync as
|
|
128444
|
-
import { fileURLToPath as
|
|
128772
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
128773
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
128445
128774
|
import { Command } from "commander";
|
|
128446
128775
|
|
|
128447
128776
|
// dist/src/commands/annotate.js
|
|
@@ -128770,26 +129099,506 @@ var registerCompareVaultsCommand = (program) => {
|
|
|
128770
129099
|
});
|
|
128771
129100
|
};
|
|
128772
129101
|
|
|
128773
|
-
// dist/src/commands/
|
|
129102
|
+
// dist/src/commands/demo.js
|
|
128774
129103
|
init_src7();
|
|
129104
|
+
import { cpSync, existsSync as existsSync6, mkdirSync, readFileSync as readFileSync4, writeFileSync } from "node:fs";
|
|
129105
|
+
import { homedir as homedir4 } from "node:os";
|
|
129106
|
+
import { dirname as dirname20, join as join28 } from "node:path";
|
|
129107
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
129108
|
+
|
|
129109
|
+
// dist/src/commands/init.js
|
|
129110
|
+
init_dist();
|
|
128775
129111
|
init_src2();
|
|
129112
|
+
init_package_version();
|
|
128776
129113
|
import { exec as exec2 } from "node:child_process";
|
|
128777
|
-
import {
|
|
128778
|
-
import {
|
|
128779
|
-
import { resolve as resolve8 } from "node:path";
|
|
129114
|
+
import { mkdir as mkdir13, readFile as readFile80, stat as stat11, writeFile as writeFile12 } from "node:fs/promises";
|
|
129115
|
+
import { isAbsolute as isAbsolute5, join as join27, resolve as resolve6 } from "node:path";
|
|
128780
129116
|
import { promisify as promisify5 } from "node:util";
|
|
129117
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
129118
|
+
|
|
129119
|
+
// dist/src/commands/trust-statement.js
|
|
129120
|
+
var TRUST_GUARANTEES = [
|
|
129121
|
+
{
|
|
129122
|
+
headline: "READ-ONLY to your org",
|
|
129123
|
+
detail: "sf-intelligence never writes, deploys, or modifies anything in Salesforce. It only RETRIEVES metadata (`sf project retrieve`). No create, no update, no delete."
|
|
129124
|
+
},
|
|
129125
|
+
{
|
|
129126
|
+
headline: "OFFLINE by default",
|
|
129127
|
+
detail: "Every answer comes from the local vault built at the last refresh \u2014 not a live call. The org is contacted only when you run `sfi refresh`."
|
|
129128
|
+
},
|
|
129129
|
+
{
|
|
129130
|
+
headline: "LOCAL & private",
|
|
129131
|
+
detail: "The vault lives on THIS machine (org-kb/) and is never uploaded anywhere. No telemetry, no phone-home \u2014 feedback (`sfi feedback`) is captured locally and shared only if you choose to."
|
|
129132
|
+
},
|
|
129133
|
+
{
|
|
129134
|
+
headline: "Live plane is OFF until you turn it on",
|
|
129135
|
+
detail: "The opt-in live read-only plane (capped record counts/samples) stays disabled until you set SFI_LIVE_PLANE_ENABLED=1 (or `sfi.live_consent`). Even enabled it is READ-ONLY and runs only a curated query roster \u2014 never arbitrary SOQL, never a write."
|
|
129136
|
+
},
|
|
129137
|
+
{
|
|
129138
|
+
headline: "The npm package ships NO org data",
|
|
129139
|
+
detail: "The published package contains only code (a `files` whitelist blocks the vault/source/snapshots). Every public version has been downloaded and grepped clean of org identifiers (the leak audit)."
|
|
129140
|
+
}
|
|
129141
|
+
];
|
|
129142
|
+
var formatTrustStatement = () => {
|
|
129143
|
+
const lines = [];
|
|
129144
|
+
lines.push("");
|
|
129145
|
+
lines.push(" \u250C\u2500 What sf-intelligence does (and does NOT) to your org \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
129146
|
+
lines.push(" \u2502");
|
|
129147
|
+
for (const g2 of TRUST_GUARANTEES) {
|
|
129148
|
+
lines.push(` \u2502 \u2713 ${g2.headline}`);
|
|
129149
|
+
for (const wrapped of wrap(g2.detail, 64)) {
|
|
129150
|
+
lines.push(` \u2502 ${wrapped}`);
|
|
129151
|
+
}
|
|
129152
|
+
lines.push(" \u2502");
|
|
129153
|
+
}
|
|
129154
|
+
lines.push(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
129155
|
+
lines.push("");
|
|
129156
|
+
return lines.join("\n");
|
|
129157
|
+
};
|
|
129158
|
+
var wrap = (text, width) => {
|
|
129159
|
+
const words = text.split(/\s+/);
|
|
129160
|
+
const out = [];
|
|
129161
|
+
let line = "";
|
|
129162
|
+
for (const word of words) {
|
|
129163
|
+
if (line === "") {
|
|
129164
|
+
line = word;
|
|
129165
|
+
} else if (`${line} ${word}`.length <= width) {
|
|
129166
|
+
line = `${line} ${word}`;
|
|
129167
|
+
} else {
|
|
129168
|
+
out.push(line);
|
|
129169
|
+
line = word;
|
|
129170
|
+
}
|
|
129171
|
+
}
|
|
129172
|
+
if (line !== "")
|
|
129173
|
+
out.push(line);
|
|
129174
|
+
return out;
|
|
129175
|
+
};
|
|
129176
|
+
|
|
129177
|
+
// dist/src/commands/init.js
|
|
129178
|
+
var DEFAULT_VAULT_ROOT = "org-kb";
|
|
129179
|
+
var JSON_INDENT6 = 2;
|
|
129180
|
+
var GITIGNORE_ENTRIES = ["org-kb/source/", "org-kb/graph/"];
|
|
129181
|
+
var SF_API_VERSION3 = "62.0";
|
|
129182
|
+
var DEFAULT_PACKAGE_DIR = "force-app";
|
|
129183
|
+
var execAsync2 = promisify5(exec2);
|
|
129184
|
+
var runInit = async (opts) => {
|
|
129185
|
+
const resolvedRoot = isAbsolute5(opts.vaultRoot) ? opts.vaultRoot : resolve6(opts.cwd, opts.vaultRoot);
|
|
129186
|
+
if (!opts.force && await pathExists3(resolvedRoot)) {
|
|
129187
|
+
return err({
|
|
129188
|
+
kind: "already-exists",
|
|
129189
|
+
message: `vault already exists: ${resolvedRoot}`,
|
|
129190
|
+
path: resolvedRoot
|
|
129191
|
+
});
|
|
129192
|
+
}
|
|
129193
|
+
const paths = vaultPaths(resolvedRoot);
|
|
129194
|
+
const mkdirResult = await createVaultDirs(paths);
|
|
129195
|
+
if (!mkdirResult.ok)
|
|
129196
|
+
return mkdirResult;
|
|
129197
|
+
const writeResult = await writeVaultMetadata(paths, opts.targetOrg, resolvedRoot);
|
|
129198
|
+
if (!writeResult.ok)
|
|
129199
|
+
return writeResult;
|
|
129200
|
+
const dxProjectScaffolded = await ensureDxProject(opts.cwd);
|
|
129201
|
+
const gitignoreUpdated = await updateGitignore(opts.cwd);
|
|
129202
|
+
return ok({ vaultRoot: resolvedRoot, targetOrg: opts.targetOrg, gitignoreUpdated, dxProjectScaffolded });
|
|
129203
|
+
};
|
|
129204
|
+
var ensureDxProject = async (cwd) => {
|
|
129205
|
+
const projectPath = join27(cwd, "sfdx-project.json");
|
|
129206
|
+
let wrote = false;
|
|
129207
|
+
if (!await pathExists3(projectPath)) {
|
|
129208
|
+
const project = {
|
|
129209
|
+
packageDirectories: [{ path: DEFAULT_PACKAGE_DIR, default: true }],
|
|
129210
|
+
namespace: "",
|
|
129211
|
+
sourceApiVersion: SF_API_VERSION3
|
|
129212
|
+
};
|
|
129213
|
+
try {
|
|
129214
|
+
await writeFile12(projectPath, `${JSON.stringify(project, null, JSON_INDENT6)}
|
|
129215
|
+
`, "utf8");
|
|
129216
|
+
wrote = true;
|
|
129217
|
+
} catch {
|
|
129218
|
+
}
|
|
129219
|
+
}
|
|
129220
|
+
try {
|
|
129221
|
+
await mkdir13(join27(cwd, DEFAULT_PACKAGE_DIR), { recursive: true });
|
|
129222
|
+
} catch {
|
|
129223
|
+
}
|
|
129224
|
+
return wrote;
|
|
129225
|
+
};
|
|
129226
|
+
var createVaultDirs = async (paths) => {
|
|
129227
|
+
for (const dir of [paths.root, paths.source, paths.components, paths.graph, paths.meta]) {
|
|
129228
|
+
try {
|
|
129229
|
+
await mkdir13(dir, { recursive: true });
|
|
129230
|
+
} catch (cause) {
|
|
129231
|
+
return err({ kind: "mkdir-failed", message: `failed to create directory: ${dir}`, path: dir, cause });
|
|
129232
|
+
}
|
|
129233
|
+
}
|
|
129234
|
+
return ok(void 0);
|
|
129235
|
+
};
|
|
129236
|
+
var writeVaultMetadata = async (paths, targetOrg, resolvedRoot) => {
|
|
129237
|
+
const config = {
|
|
129238
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
129239
|
+
targetOrg,
|
|
129240
|
+
vaultRoot: resolvedRoot,
|
|
129241
|
+
version: readCliPackageVersion(),
|
|
129242
|
+
snapshotOnRefresh: true
|
|
129243
|
+
};
|
|
129244
|
+
try {
|
|
129245
|
+
await writeFile12(paths.config, `${JSON.stringify(config, null, JSON_INDENT6)}
|
|
129246
|
+
`, "utf8");
|
|
129247
|
+
} catch (cause) {
|
|
129248
|
+
return err({ kind: "write-failed", message: `failed to write config: ${paths.config}`, path: paths.config, cause });
|
|
129249
|
+
}
|
|
129250
|
+
const versionTxtPath = join27(paths.meta, "version.txt");
|
|
129251
|
+
try {
|
|
129252
|
+
await writeFile12(versionTxtPath, `${readCliPackageVersion()}
|
|
129253
|
+
`, "utf8");
|
|
129254
|
+
} catch (cause) {
|
|
129255
|
+
return err({ kind: "write-failed", message: `failed to write version.txt: ${versionTxtPath}`, path: versionTxtPath, cause });
|
|
129256
|
+
}
|
|
129257
|
+
return ok(void 0);
|
|
129258
|
+
};
|
|
129259
|
+
var updateGitignore = async (cwd) => {
|
|
129260
|
+
const gitignorePath = join27(cwd, ".gitignore");
|
|
129261
|
+
let existing = "";
|
|
129262
|
+
let exists = true;
|
|
129263
|
+
try {
|
|
129264
|
+
existing = await readFile80(gitignorePath, "utf8");
|
|
129265
|
+
} catch (cause) {
|
|
129266
|
+
if (!isEnoent3(cause))
|
|
129267
|
+
return false;
|
|
129268
|
+
exists = false;
|
|
129269
|
+
}
|
|
129270
|
+
const present = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
|
|
129271
|
+
const missing = GITIGNORE_ENTRIES.filter((entry) => !present.has(entry));
|
|
129272
|
+
if (missing.length === 0)
|
|
129273
|
+
return false;
|
|
129274
|
+
const needsNewline = exists && existing.length > 0 && !existing.endsWith("\n");
|
|
129275
|
+
const next = `${existing}${needsNewline ? "\n" : ""}${missing.join("\n")}
|
|
129276
|
+
`;
|
|
129277
|
+
try {
|
|
129278
|
+
await writeFile12(gitignorePath, next, "utf8");
|
|
129279
|
+
return true;
|
|
129280
|
+
} catch {
|
|
129281
|
+
return false;
|
|
129282
|
+
}
|
|
129283
|
+
};
|
|
129284
|
+
var pathExists3 = async (path) => {
|
|
129285
|
+
try {
|
|
129286
|
+
await stat11(path);
|
|
129287
|
+
return true;
|
|
129288
|
+
} catch (cause) {
|
|
129289
|
+
return !isEnoent3(cause);
|
|
129290
|
+
}
|
|
129291
|
+
};
|
|
129292
|
+
var isEnoent3 = (cause) => typeof cause === "object" && cause !== null && "code" in cause && cause.code === "ENOENT";
|
|
129293
|
+
var ORG_CATEGORIES = ["nonScratchOrgs", "scratchOrgs", "otherOrgs", "devHubs", "sandboxes"];
|
|
129294
|
+
var getDefaultOrgAlias = async () => {
|
|
129295
|
+
try {
|
|
129296
|
+
const { stdout } = await execAsync2("sf org list --json");
|
|
129297
|
+
const parsed = JSON.parse(stdout);
|
|
129298
|
+
const result = parsed.result;
|
|
129299
|
+
if (result === void 0 || result === null)
|
|
129300
|
+
return null;
|
|
129301
|
+
for (const key of ORG_CATEGORIES) {
|
|
129302
|
+
const entries = result[key];
|
|
129303
|
+
if (!Array.isArray(entries))
|
|
129304
|
+
continue;
|
|
129305
|
+
for (const item of entries) {
|
|
129306
|
+
if (typeof item !== "object" || item === null)
|
|
129307
|
+
continue;
|
|
129308
|
+
const entry = item;
|
|
129309
|
+
if (entry["isDefaultUsername"] !== true && entry["isDefaultDevHubUsername"] !== true)
|
|
129310
|
+
continue;
|
|
129311
|
+
const alias = entry["alias"] ?? entry["username"];
|
|
129312
|
+
if (typeof alias === "string" && alias.length > 0)
|
|
129313
|
+
return alias;
|
|
129314
|
+
}
|
|
129315
|
+
}
|
|
129316
|
+
return null;
|
|
129317
|
+
} catch {
|
|
129318
|
+
return null;
|
|
129319
|
+
}
|
|
129320
|
+
};
|
|
129321
|
+
var registerInitCommand = (program) => {
|
|
129322
|
+
program.command("init").description("Initialise a new org-kb vault in the current directory").option("--target-org <alias>", "Salesforce org alias to bind to this vault").option("--vault-root <path>", "Vault root directory (relative to CWD)").option("--force", "Overwrite an existing org-kb/ vault config", false).action(async (flags) => {
|
|
129323
|
+
const cwd = process.cwd();
|
|
129324
|
+
const exitCode = await handleInit(cwd, flags);
|
|
129325
|
+
if (exitCode !== 0)
|
|
129326
|
+
process.exit(exitCode);
|
|
129327
|
+
});
|
|
129328
|
+
};
|
|
129329
|
+
var handleInit = async (cwd, flags) => {
|
|
129330
|
+
const interactive = process.stdin.isTTY === true;
|
|
129331
|
+
const vaultRoot = flags.vaultRoot ?? (interactive ? await input({ message: "Vault root directory:", default: DEFAULT_VAULT_ROOT }) : DEFAULT_VAULT_ROOT);
|
|
129332
|
+
const resolvedRoot = isAbsolute5(vaultRoot) ? vaultRoot : resolve6(cwd, vaultRoot);
|
|
129333
|
+
let force = flags.force ?? false;
|
|
129334
|
+
if (!force && await pathExists3(resolvedRoot)) {
|
|
129335
|
+
if (!interactive) {
|
|
129336
|
+
process.stderr.write(`sfi init: ${vaultRoot}/ already exists. Re-run with --force to overwrite.
|
|
129337
|
+
`);
|
|
129338
|
+
return 1;
|
|
129339
|
+
}
|
|
129340
|
+
const overwrite = await confirm({
|
|
129341
|
+
message: `${vaultRoot}/ already exists. Overwrite config?`,
|
|
129342
|
+
default: false
|
|
129343
|
+
});
|
|
129344
|
+
if (!overwrite) {
|
|
129345
|
+
process.stdout.write("Existing init preserved.\n");
|
|
129346
|
+
return 0;
|
|
129347
|
+
}
|
|
129348
|
+
force = true;
|
|
129349
|
+
}
|
|
129350
|
+
const targetOrg = await resolveTargetOrg(flags, interactive);
|
|
129351
|
+
if (targetOrg === null) {
|
|
129352
|
+
process.stderr.write("sfi init: no --target-org given and no default Salesforce org found. Pass --target-org <alias>.\n");
|
|
129353
|
+
return 1;
|
|
129354
|
+
}
|
|
129355
|
+
return runAndReport({ cwd, targetOrg, vaultRoot, force });
|
|
129356
|
+
};
|
|
129357
|
+
var resolveTargetOrg = async (flags, interactive) => {
|
|
129358
|
+
if (flags.targetOrg !== void 0)
|
|
129359
|
+
return flags.targetOrg;
|
|
129360
|
+
const detected = await getDefaultOrgAlias();
|
|
129361
|
+
if (!interactive)
|
|
129362
|
+
return detected;
|
|
129363
|
+
return input({
|
|
129364
|
+
message: "Target org alias:",
|
|
129365
|
+
...detected !== null ? { default: detected } : {}
|
|
129366
|
+
});
|
|
129367
|
+
};
|
|
129368
|
+
var runAndReport = async (opts) => {
|
|
129369
|
+
const result = await runInit(opts);
|
|
129370
|
+
if (!result.ok) {
|
|
129371
|
+
process.stderr.write(`sfi init: ${result.error.message}
|
|
129372
|
+
`);
|
|
129373
|
+
return 1;
|
|
129374
|
+
}
|
|
129375
|
+
process.stdout.write([
|
|
129376
|
+
`Initialised vault at ${result.value.vaultRoot}`,
|
|
129377
|
+
`Target org: ${result.value.targetOrg}`,
|
|
129378
|
+
result.value.gitignoreUpdated ? "Updated .gitignore" : ".gitignore already up to date (or could not be written)",
|
|
129379
|
+
result.value.dxProjectScaffolded ? "Scaffolded sfdx-project.json (ready for sf project retrieve)" : "sfdx-project.json already present",
|
|
129380
|
+
""
|
|
129381
|
+
].join("\n"));
|
|
129382
|
+
process.stdout.write(formatTrustStatement());
|
|
129383
|
+
process.stdout.write("Next: run sfi refresh\n\n");
|
|
129384
|
+
return 0;
|
|
129385
|
+
};
|
|
129386
|
+
|
|
129387
|
+
// dist/src/commands/mcp.js
|
|
129388
|
+
init_dist();
|
|
129389
|
+
init_src7();
|
|
129390
|
+
init_src2();
|
|
129391
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
129392
|
+
import { readFile as readFile81, stat as stat12 } from "node:fs/promises";
|
|
129393
|
+
import { resolve as resolve7 } from "node:path";
|
|
129394
|
+
import { promisify as promisify6 } from "node:util";
|
|
129395
|
+
var DEFAULT_VAULT_ROOT2 = "org-kb";
|
|
129396
|
+
var SHUTDOWN_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
129397
|
+
var readBoundOrg = async (configPath) => {
|
|
129398
|
+
try {
|
|
129399
|
+
const raw = await readFile81(configPath, "utf8");
|
|
129400
|
+
const parsed = JSON.parse(raw);
|
|
129401
|
+
return typeof parsed.targetOrg === "string" ? parsed.targetOrg : null;
|
|
129402
|
+
} catch {
|
|
129403
|
+
return null;
|
|
129404
|
+
}
|
|
129405
|
+
};
|
|
129406
|
+
var nodeExecFile3 = promisify6(execFile4);
|
|
129407
|
+
var defaultListOrgs = async () => {
|
|
129408
|
+
try {
|
|
129409
|
+
const { stdout } = await nodeExecFile3("sf", ["org", "list", "--json"], {
|
|
129410
|
+
maxBuffer: 10 * 1024 * 1024
|
|
129411
|
+
});
|
|
129412
|
+
const json = JSON.parse(stdout);
|
|
129413
|
+
const groups = json.result ?? {};
|
|
129414
|
+
const seen = /* @__PURE__ */ new Set();
|
|
129415
|
+
const out = [];
|
|
129416
|
+
for (const list of Object.values(groups)) {
|
|
129417
|
+
for (const org of list ?? []) {
|
|
129418
|
+
const label = org.alias ?? org.username;
|
|
129419
|
+
if (label && !seen.has(label)) {
|
|
129420
|
+
seen.add(label);
|
|
129421
|
+
out.push(label);
|
|
129422
|
+
}
|
|
129423
|
+
}
|
|
129424
|
+
}
|
|
129425
|
+
return out;
|
|
129426
|
+
} catch {
|
|
129427
|
+
return [];
|
|
129428
|
+
}
|
|
129429
|
+
};
|
|
129430
|
+
var prepareMcp = async (opts) => {
|
|
129431
|
+
const vaultRoot = opts.vaultRoot !== void 0 ? resolve7(opts.cwd, opts.vaultRoot) : resolve7(opts.cwd, DEFAULT_VAULT_ROOT2);
|
|
129432
|
+
const paths = vaultPaths(vaultRoot);
|
|
129433
|
+
if (!await pathExists4(paths.config)) {
|
|
129434
|
+
const orgs = await (opts.listOrgs ?? defaultListOrgs)();
|
|
129435
|
+
const where = opts.vaultRoot !== void 0 ? ` at ${vaultRoot}` : "";
|
|
129436
|
+
const base = `No vault${where}. Run \`sfi init\` followed by \`sfi refresh\`, or point \`sfi mcp --vault <path>\` at an existing org-kb.`;
|
|
129437
|
+
const hint = orgs.length > 0 ? ` You are authenticated to ${orgs.length} org(s): ${orgs.slice(0, 8).join(", ")}${orgs.length > 8 ? ", \u2026" : ""}. Run \`sfi init\` to pick the one you want and build its knowledge base \u2014 live answers attach to a vault's org, so the product never guesses which of your orgs to query.` : "";
|
|
129438
|
+
return err({ kind: "no-vault", message: base + hint });
|
|
129439
|
+
}
|
|
129440
|
+
const ctxResult = await buildContext(vaultRoot);
|
|
129441
|
+
if (!ctxResult.ok) {
|
|
129442
|
+
return err({ kind: "buildContext-failed", message: ctxResult.error.message });
|
|
129443
|
+
}
|
|
129444
|
+
const targetOrg = await readBoundOrg(paths.config);
|
|
129445
|
+
return ok({
|
|
129446
|
+
ctx: ctxResult.value,
|
|
129447
|
+
server: createServer(ctxResult.value),
|
|
129448
|
+
vaultRoot,
|
|
129449
|
+
targetOrg
|
|
129450
|
+
});
|
|
129451
|
+
};
|
|
129452
|
+
var pathExists4 = async (path) => {
|
|
129453
|
+
try {
|
|
129454
|
+
await stat12(path);
|
|
129455
|
+
return true;
|
|
129456
|
+
} catch (cause) {
|
|
129457
|
+
return !isEnoent4(cause);
|
|
129458
|
+
}
|
|
129459
|
+
};
|
|
129460
|
+
var isEnoent4 = (cause) => typeof cause === "object" && cause !== null && "code" in cause && cause.code === "ENOENT";
|
|
129461
|
+
var registerMcpCommand = (program) => {
|
|
129462
|
+
program.command("mcp").description("Run the MCP server backing the org-kb vault").option("--vault <path>", "Serve a specific org-kb vault instead of ./org-kb (bind a project to the right org)").action(async (cmdOpts) => {
|
|
129463
|
+
const prepared = await prepareMcp({
|
|
129464
|
+
cwd: process.cwd(),
|
|
129465
|
+
...cmdOpts.vault !== void 0 ? { vaultRoot: cmdOpts.vault } : {}
|
|
129466
|
+
});
|
|
129467
|
+
if (!prepared.ok) {
|
|
129468
|
+
process.stderr.write(`sfi mcp: ${prepared.error.message}
|
|
129469
|
+
`);
|
|
129470
|
+
process.exit(1);
|
|
129471
|
+
}
|
|
129472
|
+
const { ctx, server, vaultRoot, targetOrg } = prepared.value;
|
|
129473
|
+
process.stderr.write(`sfi mcp: serving vault ${vaultRoot}${targetOrg !== null ? ` (org: ${targetOrg})` : " (no targetOrg in config)"}
|
|
129474
|
+
`);
|
|
129475
|
+
const shutdownOnce = makeShutdownOnce(ctx);
|
|
129476
|
+
for (const signal of SHUTDOWN_SIGNALS) {
|
|
129477
|
+
process.on(signal, () => {
|
|
129478
|
+
void shutdownOnce().then(() => process.exit(0));
|
|
129479
|
+
});
|
|
129480
|
+
}
|
|
129481
|
+
await startServer(server);
|
|
129482
|
+
await shutdownOnce();
|
|
129483
|
+
});
|
|
129484
|
+
};
|
|
129485
|
+
var makeShutdownOnce = (ctx) => {
|
|
129486
|
+
let done = null;
|
|
129487
|
+
return () => {
|
|
129488
|
+
if (done === null)
|
|
129489
|
+
done = shutdown(ctx);
|
|
129490
|
+
return done;
|
|
129491
|
+
};
|
|
129492
|
+
};
|
|
129493
|
+
|
|
129494
|
+
// dist/src/commands/demo.js
|
|
129495
|
+
init_refresh();
|
|
129496
|
+
var buildVersion = () => true ? "0.1.13" : "dev";
|
|
129497
|
+
var SHUTDOWN_SIGNALS2 = ["SIGINT", "SIGTERM"];
|
|
129498
|
+
var resolveDemoSource = () => {
|
|
129499
|
+
let dir = dirname20(fileURLToPath2(import.meta.url));
|
|
129500
|
+
for (let i2 = 0; i2 < 7; i2 += 1) {
|
|
129501
|
+
const shipped = join28(dir, "demo-source", "main", "default");
|
|
129502
|
+
if (existsSync6(shipped))
|
|
129503
|
+
return shipped;
|
|
129504
|
+
const repo = join28(dir, "examples", "demo-vault", "source", "main", "default");
|
|
129505
|
+
if (existsSync6(repo))
|
|
129506
|
+
return repo;
|
|
129507
|
+
const parent = dirname20(dir);
|
|
129508
|
+
if (parent === dir)
|
|
129509
|
+
break;
|
|
129510
|
+
dir = parent;
|
|
129511
|
+
}
|
|
129512
|
+
return null;
|
|
129513
|
+
};
|
|
129514
|
+
var registerDemoCommand = (program) => {
|
|
129515
|
+
program.command("demo").description("Try sf-intelligence with no Salesforce org \u2014 serve a built-in synthetic demo org over MCP").option("--rebuild", "Force a rebuild of the cached demo vault", false).action(async (opts) => {
|
|
129516
|
+
const source = resolveDemoSource();
|
|
129517
|
+
if (source === null) {
|
|
129518
|
+
process.stderr.write("sfi demo: bundled demo source not found (expected a shipped demo-source/ or examples/demo-vault/source).\n");
|
|
129519
|
+
process.exit(1);
|
|
129520
|
+
}
|
|
129521
|
+
const cacheCwd = join28(homedir4(), ".sf-intelligence", "demo");
|
|
129522
|
+
const vaultRoot = join28(cacheCwd, "org-kb");
|
|
129523
|
+
const graphDb = join28(vaultRoot, "graph", "graph.duckdb");
|
|
129524
|
+
const stampPath = join28(vaultRoot, "meta", "demo-build.stamp");
|
|
129525
|
+
const stamp = `${buildVersion()}`;
|
|
129526
|
+
const stampOk = (() => {
|
|
129527
|
+
try {
|
|
129528
|
+
return readFileSync4(stampPath, "utf8").trim() === stamp;
|
|
129529
|
+
} catch {
|
|
129530
|
+
return false;
|
|
129531
|
+
}
|
|
129532
|
+
})();
|
|
129533
|
+
const needBuild = opts.rebuild === true || !existsSync6(graphDb) || !stampOk;
|
|
129534
|
+
if (needBuild) {
|
|
129535
|
+
process.stderr.write("sfi demo: building the synthetic demo vault (first run, ~a few seconds)...\n");
|
|
129536
|
+
const init = await runInit({
|
|
129537
|
+
cwd: cacheCwd,
|
|
129538
|
+
targetOrg: "demo-org",
|
|
129539
|
+
vaultRoot: "org-kb",
|
|
129540
|
+
force: true
|
|
129541
|
+
});
|
|
129542
|
+
if (!init.ok) {
|
|
129543
|
+
process.stderr.write(`sfi demo: could not initialise the demo vault: ${init.error.message}
|
|
129544
|
+
`);
|
|
129545
|
+
process.exit(1);
|
|
129546
|
+
}
|
|
129547
|
+
const sourceDest = join28(vaultRoot, "source", "main", "default");
|
|
129548
|
+
mkdirSync(dirname20(sourceDest), { recursive: true });
|
|
129549
|
+
cpSync(source, sourceDest, { recursive: true });
|
|
129550
|
+
await runRefresh({ cwd: cacheCwd, noPull: true });
|
|
129551
|
+
if (!existsSync6(graphDb)) {
|
|
129552
|
+
process.stderr.write("sfi demo: demo build failed \u2014 no graph was produced.\n");
|
|
129553
|
+
process.exit(1);
|
|
129554
|
+
}
|
|
129555
|
+
try {
|
|
129556
|
+
writeFileSync(stampPath, `${stamp}
|
|
129557
|
+
`);
|
|
129558
|
+
} catch {
|
|
129559
|
+
}
|
|
129560
|
+
}
|
|
129561
|
+
const prepared = await prepareMcp({ cwd: cacheCwd, vaultRoot: "org-kb" });
|
|
129562
|
+
if (!prepared.ok) {
|
|
129563
|
+
process.stderr.write(`sfi demo: ${prepared.error.message}
|
|
129564
|
+
`);
|
|
129565
|
+
process.exit(1);
|
|
129566
|
+
}
|
|
129567
|
+
const { ctx, server, vaultRoot: served } = prepared.value;
|
|
129568
|
+
process.stderr.write(`sfi demo: serving the synthetic "Verdant Energy" demo org from ${served} (read-only, offline).
|
|
129569
|
+
`);
|
|
129570
|
+
let shuttingDown = null;
|
|
129571
|
+
const shutdownOnce = () => shuttingDown ??= shutdown(ctx);
|
|
129572
|
+
for (const signal of SHUTDOWN_SIGNALS2) {
|
|
129573
|
+
process.on(signal, () => {
|
|
129574
|
+
void shutdownOnce().then(() => process.exit(0));
|
|
129575
|
+
});
|
|
129576
|
+
}
|
|
129577
|
+
await startServer(server);
|
|
129578
|
+
await shutdownOnce();
|
|
129579
|
+
});
|
|
129580
|
+
};
|
|
129581
|
+
|
|
129582
|
+
// dist/src/commands/doctor.js
|
|
129583
|
+
init_src7();
|
|
129584
|
+
init_src2();
|
|
129585
|
+
import { exec as exec3 } from "node:child_process";
|
|
129586
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
129587
|
+
import { readFile as readFile83, stat as stat14 } from "node:fs/promises";
|
|
129588
|
+
import { resolve as resolve10 } from "node:path";
|
|
129589
|
+
import { promisify as promisify7 } from "node:util";
|
|
128781
129590
|
|
|
128782
129591
|
// dist/src/commands/feedback.js
|
|
128783
129592
|
init_src7();
|
|
128784
|
-
import { appendFile as appendFile5, mkdir as
|
|
128785
|
-
import { homedir as
|
|
128786
|
-
import { dirname as
|
|
129593
|
+
import { appendFile as appendFile5, mkdir as mkdir14, readFile as readFile82, writeFile as writeFile13 } from "node:fs/promises";
|
|
129594
|
+
import { homedir as homedir5 } from "node:os";
|
|
129595
|
+
import { dirname as dirname21, join as join29, resolve as resolve8 } from "node:path";
|
|
128787
129596
|
var FEEDBACK_ISSUES_URL = "https://github.com/PranavNagrecha/Salesforce-Intelligence/issues";
|
|
128788
|
-
var feedbackLogPath = () => process.env["SFI_FEEDBACK_LOG_PATH"] ??
|
|
129597
|
+
var feedbackLogPath = () => process.env["SFI_FEEDBACK_LOG_PATH"] ?? join29(homedir5(), ".sf-intelligence", "feedback.jsonl");
|
|
128789
129598
|
var recordFeedback = async (question, rating, path = feedbackLogPath()) => {
|
|
128790
129599
|
const entry = { at: (/* @__PURE__ */ new Date()).toISOString(), question, rating };
|
|
128791
129600
|
try {
|
|
128792
|
-
await
|
|
129601
|
+
await mkdir14(dirname21(path), { recursive: true });
|
|
128793
129602
|
await appendFile5(path, `${JSON.stringify(entry)}
|
|
128794
129603
|
`, "utf8");
|
|
128795
129604
|
return true;
|
|
@@ -128801,7 +129610,7 @@ var scrubText = (s2) => s2.replace(/[\w.+-]+@[\w-]+\.[\w.-]+/g, "[email]").repla
|
|
|
128801
129610
|
var readJsonl = async (path) => {
|
|
128802
129611
|
let raw;
|
|
128803
129612
|
try {
|
|
128804
|
-
raw = await
|
|
129613
|
+
raw = await readFile82(path, "utf8");
|
|
128805
129614
|
} catch {
|
|
128806
129615
|
return [];
|
|
128807
129616
|
}
|
|
@@ -128847,7 +129656,7 @@ var registerFeedbackCommand = (program) => {
|
|
|
128847
129656
|
` : "Could not write the local feedback log.\n");
|
|
128848
129657
|
});
|
|
128849
129658
|
feedback.command("export").description("Write a scrubbed, shareable feedback file (route gaps + ratings) \u2014 no org PII, nothing uploaded. Gaps are scoped to the CURRENT vault by default; --all exports the whole machine-global log (review before sharing on a multi-org machine).").option("--out <file>", "output path", "sfi-feedback.json").option("--all", "include every vault's gaps + unstamped pre-0.1.10 entries (machine-global)").action(async (flags) => {
|
|
128850
|
-
const vaultRoot =
|
|
129659
|
+
const vaultRoot = resolve8(process.cwd(), "org-kb");
|
|
128851
129660
|
const data = await buildFeedbackExport({
|
|
128852
129661
|
vaultRoot,
|
|
128853
129662
|
...flags.all === true ? { all: true } : {}
|
|
@@ -128857,9 +129666,9 @@ var registerFeedbackCommand = (program) => {
|
|
|
128857
129666
|
` : 'Mark a weak answer first: `sfi feedback mark "<question>" --wrong` (or `--weak`).\n'));
|
|
128858
129667
|
return;
|
|
128859
129668
|
}
|
|
128860
|
-
const out =
|
|
129669
|
+
const out = resolve8(process.cwd(), flags.out);
|
|
128861
129670
|
try {
|
|
128862
|
-
await
|
|
129671
|
+
await writeFile13(out, `${JSON.stringify(data, null, 2)}
|
|
128863
129672
|
`, "utf8");
|
|
128864
129673
|
} catch (cause) {
|
|
128865
129674
|
process.stderr.write(`Could not write ${out}: ${cause instanceof Error ? cause.message : String(cause)}
|
|
@@ -128877,19 +129686,19 @@ Pick a writable path with \`--out <file>\`.
|
|
|
128877
129686
|
|
|
128878
129687
|
// dist/src/commands/status.js
|
|
128879
129688
|
init_src2();
|
|
128880
|
-
import { stat as
|
|
128881
|
-
import { resolve as
|
|
128882
|
-
var
|
|
128883
|
-
var
|
|
129689
|
+
import { stat as stat13 } from "node:fs/promises";
|
|
129690
|
+
import { resolve as resolve9 } from "node:path";
|
|
129691
|
+
var JSON_INDENT7 = 2;
|
|
129692
|
+
var DEFAULT_VAULT_ROOT3 = "org-kb";
|
|
128884
129693
|
var HASH_PREFIX_LENGTH = 12;
|
|
128885
129694
|
var LABEL_WIDTH = 20;
|
|
128886
129695
|
var SECONDS_PER_MINUTE = 60;
|
|
128887
129696
|
var MINUTES_PER_HOUR = 60;
|
|
128888
129697
|
var HOURS_PER_DAY = 24;
|
|
128889
129698
|
var runStatus = async (opts) => {
|
|
128890
|
-
const vaultRoot =
|
|
129699
|
+
const vaultRoot = resolve9(opts.cwd, DEFAULT_VAULT_ROOT3);
|
|
128891
129700
|
const paths = vaultPaths(vaultRoot);
|
|
128892
|
-
if (!await
|
|
129701
|
+
if (!await pathExists5(paths.config)) {
|
|
128893
129702
|
return {
|
|
128894
129703
|
kind: "no-vault",
|
|
128895
129704
|
message: "No vault. Run `sfi init` followed by `sfi refresh`."
|
|
@@ -128927,15 +129736,15 @@ var runStatus = async (opts) => {
|
|
|
128927
129736
|
currentSourceHash
|
|
128928
129737
|
};
|
|
128929
129738
|
};
|
|
128930
|
-
var
|
|
129739
|
+
var pathExists5 = async (path) => {
|
|
128931
129740
|
try {
|
|
128932
|
-
await
|
|
129741
|
+
await stat13(path);
|
|
128933
129742
|
return true;
|
|
128934
129743
|
} catch (cause) {
|
|
128935
|
-
return !
|
|
129744
|
+
return !isEnoent5(cause);
|
|
128936
129745
|
}
|
|
128937
129746
|
};
|
|
128938
|
-
var
|
|
129747
|
+
var isEnoent5 = (cause) => typeof cause === "object" && cause !== null && "code" in cause && cause.code === "ENOENT";
|
|
128939
129748
|
var formatAge = (iso, now = /* @__PURE__ */ new Date()) => {
|
|
128940
129749
|
const then = new Date(iso);
|
|
128941
129750
|
if (Number.isNaN(then.getTime()))
|
|
@@ -129023,7 +129832,7 @@ var registerStatusCommand = (program) => {
|
|
|
129023
129832
|
program.command("status").description("Report the freshness of the local vault").option("--json", "Print the raw status as pretty-printed JSON instead of the table", false).option("--skipped", "Print the per-directory skip inventory the refresh walker recorded. Use this when the warning at the end of `sfi refresh` flagged unknown directories.", false).action(async (flags) => {
|
|
129024
129833
|
const out = await runStatus({ cwd: process.cwd() });
|
|
129025
129834
|
if (flags.json === true) {
|
|
129026
|
-
process.stdout.write(`${JSON.stringify(out, null,
|
|
129835
|
+
process.stdout.write(`${JSON.stringify(out, null, JSON_INDENT7)}
|
|
129027
129836
|
`);
|
|
129028
129837
|
return;
|
|
129029
129838
|
}
|
|
@@ -129036,8 +129845,8 @@ var registerStatusCommand = (program) => {
|
|
|
129036
129845
|
};
|
|
129037
129846
|
|
|
129038
129847
|
// dist/src/commands/doctor.js
|
|
129039
|
-
var
|
|
129040
|
-
var
|
|
129848
|
+
var execAsync3 = promisify7(exec3);
|
|
129849
|
+
var DEFAULT_VAULT_ROOT4 = "org-kb";
|
|
129041
129850
|
var parseSfCliVersion = (versionLine) => {
|
|
129042
129851
|
const m2 = /(\d+)\.(\d+)\.(\d+)/.exec(versionLine);
|
|
129043
129852
|
if (m2 === null)
|
|
@@ -129049,7 +129858,7 @@ var formatVersion = (v2) => v2.join(".");
|
|
|
129049
129858
|
var summarizeRouteGaps = async (logFile) => {
|
|
129050
129859
|
let raw;
|
|
129051
129860
|
try {
|
|
129052
|
-
raw = await
|
|
129861
|
+
raw = await readFile83(logFile, "utf8");
|
|
129053
129862
|
} catch {
|
|
129054
129863
|
return { exists: false, count: 0, topCategory: null, topCount: 0 };
|
|
129055
129864
|
}
|
|
@@ -129076,9 +129885,9 @@ var summarizeRouteGaps = async (logFile) => {
|
|
|
129076
129885
|
}
|
|
129077
129886
|
return { exists: true, count, topCategory, topCount };
|
|
129078
129887
|
};
|
|
129079
|
-
var
|
|
129888
|
+
var pathExists6 = async (p2) => {
|
|
129080
129889
|
try {
|
|
129081
|
-
await
|
|
129890
|
+
await stat14(p2);
|
|
129082
129891
|
return true;
|
|
129083
129892
|
} catch {
|
|
129084
129893
|
return false;
|
|
@@ -129086,7 +129895,7 @@ var pathExists4 = async (p2) => {
|
|
|
129086
129895
|
};
|
|
129087
129896
|
var readTargetOrg = async (configPath) => {
|
|
129088
129897
|
try {
|
|
129089
|
-
const raw = await
|
|
129898
|
+
const raw = await readFile83(configPath, "utf8");
|
|
129090
129899
|
const parsed = JSON.parse(raw);
|
|
129091
129900
|
return typeof parsed.targetOrg === "string" ? parsed.targetOrg : null;
|
|
129092
129901
|
} catch {
|
|
@@ -129094,9 +129903,9 @@ var readTargetOrg = async (configPath) => {
|
|
|
129094
129903
|
}
|
|
129095
129904
|
};
|
|
129096
129905
|
var runDoctor = async (opts) => {
|
|
129097
|
-
const run = opts.exec ?? ((cmd) =>
|
|
129906
|
+
const run = opts.exec ?? ((cmd) => execAsync3(cmd, { maxBuffer: 64 * 1024 * 1024 }));
|
|
129098
129907
|
const checks = [];
|
|
129099
|
-
const vaultRoot =
|
|
129908
|
+
const vaultRoot = resolve10(opts.cwd, DEFAULT_VAULT_ROOT4);
|
|
129100
129909
|
const paths = vaultPaths(vaultRoot);
|
|
129101
129910
|
const SF_FALLBACK_PATHS = ["/usr/local/bin/sf", "/opt/homebrew/bin/sf"];
|
|
129102
129911
|
let sfBin = "sf";
|
|
@@ -129143,7 +129952,7 @@ var runDoctor = async (opts) => {
|
|
|
129143
129952
|
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."
|
|
129144
129953
|
});
|
|
129145
129954
|
}
|
|
129146
|
-
const vaultInit = await
|
|
129955
|
+
const vaultInit = await pathExists6(paths.config);
|
|
129147
129956
|
if (!vaultInit) {
|
|
129148
129957
|
checks.push({
|
|
129149
129958
|
name: "Vault",
|
|
@@ -129232,7 +130041,7 @@ var runDoctor = async (opts) => {
|
|
|
129232
130041
|
}
|
|
129233
130042
|
}
|
|
129234
130043
|
if (vaultInit) {
|
|
129235
|
-
const graphOk = await
|
|
130044
|
+
const graphOk = await pathExists6(paths.graphDb);
|
|
129236
130045
|
checks.push(graphOk ? { name: "Graph", status: "pass", detail: "graph.duckdb present" } : { name: "Graph", status: "warn", detail: "graph.duckdb missing", fix: "Run `sfi refresh` to rebuild the graph." });
|
|
129237
130046
|
}
|
|
129238
130047
|
const gaps = await summarizeRouteGaps(opts.gapLogFile ?? gapLogPath());
|
|
@@ -129299,7 +130108,7 @@ var runDoctor = async (opts) => {
|
|
|
129299
130108
|
});
|
|
129300
130109
|
}
|
|
129301
130110
|
const registryFile = findRegistryFile(vaultRoot);
|
|
129302
|
-
if (
|
|
130111
|
+
if (existsSync8(registryFile)) {
|
|
129303
130112
|
const listed = await listRegisteredVaults(findRegistryRoot(vaultRoot));
|
|
129304
130113
|
if (listed.ok && listed.value.length >= 2) {
|
|
129305
130114
|
checks.push({
|
|
@@ -129345,284 +130154,6 @@ var registerDoctorCommand = (program) => {
|
|
|
129345
130154
|
});
|
|
129346
130155
|
};
|
|
129347
130156
|
|
|
129348
|
-
// dist/src/commands/init.js
|
|
129349
|
-
init_dist();
|
|
129350
|
-
init_src2();
|
|
129351
|
-
init_package_version();
|
|
129352
|
-
import { exec as exec3 } from "node:child_process";
|
|
129353
|
-
import { mkdir as mkdir14, readFile as readFile82, stat as stat13, writeFile as writeFile14 } from "node:fs/promises";
|
|
129354
|
-
import { isAbsolute as isAbsolute5, join as join30, resolve as resolve9 } from "node:path";
|
|
129355
|
-
import { promisify as promisify6 } from "node:util";
|
|
129356
|
-
import { confirm, input } from "@inquirer/prompts";
|
|
129357
|
-
|
|
129358
|
-
// dist/src/commands/trust-statement.js
|
|
129359
|
-
var TRUST_GUARANTEES = [
|
|
129360
|
-
{
|
|
129361
|
-
headline: "READ-ONLY to your org",
|
|
129362
|
-
detail: "sf-intelligence never writes, deploys, or modifies anything in Salesforce. It only RETRIEVES metadata (`sf project retrieve`). No create, no update, no delete."
|
|
129363
|
-
},
|
|
129364
|
-
{
|
|
129365
|
-
headline: "OFFLINE by default",
|
|
129366
|
-
detail: "Every answer comes from the local vault built at the last refresh \u2014 not a live call. The org is contacted only when you run `sfi refresh`."
|
|
129367
|
-
},
|
|
129368
|
-
{
|
|
129369
|
-
headline: "LOCAL & private",
|
|
129370
|
-
detail: "The vault lives on THIS machine (org-kb/) and is never uploaded anywhere. No telemetry, no phone-home \u2014 feedback (`sfi feedback`) is captured locally and shared only if you choose to."
|
|
129371
|
-
},
|
|
129372
|
-
{
|
|
129373
|
-
headline: "Live plane is OFF until you turn it on",
|
|
129374
|
-
detail: "The opt-in live read-only plane (capped record counts/samples) stays disabled until you set SFI_LIVE_PLANE_ENABLED=1 (or `sfi.live_consent`). Even enabled it is READ-ONLY and runs only a curated query roster \u2014 never arbitrary SOQL, never a write."
|
|
129375
|
-
},
|
|
129376
|
-
{
|
|
129377
|
-
headline: "The npm package ships NO org data",
|
|
129378
|
-
detail: "The published package contains only code (a `files` whitelist blocks the vault/source/snapshots). Every public version has been downloaded and grepped clean of org identifiers (the leak audit)."
|
|
129379
|
-
}
|
|
129380
|
-
];
|
|
129381
|
-
var formatTrustStatement = () => {
|
|
129382
|
-
const lines = [];
|
|
129383
|
-
lines.push("");
|
|
129384
|
-
lines.push(" \u250C\u2500 What sf-intelligence does (and does NOT) to your org \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
129385
|
-
lines.push(" \u2502");
|
|
129386
|
-
for (const g2 of TRUST_GUARANTEES) {
|
|
129387
|
-
lines.push(` \u2502 \u2713 ${g2.headline}`);
|
|
129388
|
-
for (const wrapped of wrap(g2.detail, 64)) {
|
|
129389
|
-
lines.push(` \u2502 ${wrapped}`);
|
|
129390
|
-
}
|
|
129391
|
-
lines.push(" \u2502");
|
|
129392
|
-
}
|
|
129393
|
-
lines.push(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
129394
|
-
lines.push("");
|
|
129395
|
-
return lines.join("\n");
|
|
129396
|
-
};
|
|
129397
|
-
var wrap = (text, width) => {
|
|
129398
|
-
const words = text.split(/\s+/);
|
|
129399
|
-
const out = [];
|
|
129400
|
-
let line = "";
|
|
129401
|
-
for (const word of words) {
|
|
129402
|
-
if (line === "") {
|
|
129403
|
-
line = word;
|
|
129404
|
-
} else if (`${line} ${word}`.length <= width) {
|
|
129405
|
-
line = `${line} ${word}`;
|
|
129406
|
-
} else {
|
|
129407
|
-
out.push(line);
|
|
129408
|
-
line = word;
|
|
129409
|
-
}
|
|
129410
|
-
}
|
|
129411
|
-
if (line !== "")
|
|
129412
|
-
out.push(line);
|
|
129413
|
-
return out;
|
|
129414
|
-
};
|
|
129415
|
-
|
|
129416
|
-
// dist/src/commands/init.js
|
|
129417
|
-
var DEFAULT_VAULT_ROOT3 = "org-kb";
|
|
129418
|
-
var JSON_INDENT7 = 2;
|
|
129419
|
-
var GITIGNORE_ENTRIES = ["org-kb/source/", "org-kb/graph/"];
|
|
129420
|
-
var SF_API_VERSION3 = "62.0";
|
|
129421
|
-
var DEFAULT_PACKAGE_DIR = "force-app";
|
|
129422
|
-
var execAsync3 = promisify6(exec3);
|
|
129423
|
-
var runInit = async (opts) => {
|
|
129424
|
-
const resolvedRoot = isAbsolute5(opts.vaultRoot) ? opts.vaultRoot : resolve9(opts.cwd, opts.vaultRoot);
|
|
129425
|
-
if (!opts.force && await pathExists5(resolvedRoot)) {
|
|
129426
|
-
return err({
|
|
129427
|
-
kind: "already-exists",
|
|
129428
|
-
message: `vault already exists: ${resolvedRoot}`,
|
|
129429
|
-
path: resolvedRoot
|
|
129430
|
-
});
|
|
129431
|
-
}
|
|
129432
|
-
const paths = vaultPaths(resolvedRoot);
|
|
129433
|
-
const mkdirResult = await createVaultDirs(paths);
|
|
129434
|
-
if (!mkdirResult.ok)
|
|
129435
|
-
return mkdirResult;
|
|
129436
|
-
const writeResult = await writeVaultMetadata(paths, opts.targetOrg, resolvedRoot);
|
|
129437
|
-
if (!writeResult.ok)
|
|
129438
|
-
return writeResult;
|
|
129439
|
-
const dxProjectScaffolded = await ensureDxProject(opts.cwd);
|
|
129440
|
-
const gitignoreUpdated = await updateGitignore(opts.cwd);
|
|
129441
|
-
return ok({ vaultRoot: resolvedRoot, targetOrg: opts.targetOrg, gitignoreUpdated, dxProjectScaffolded });
|
|
129442
|
-
};
|
|
129443
|
-
var ensureDxProject = async (cwd) => {
|
|
129444
|
-
const projectPath = join30(cwd, "sfdx-project.json");
|
|
129445
|
-
let wrote = false;
|
|
129446
|
-
if (!await pathExists5(projectPath)) {
|
|
129447
|
-
const project = {
|
|
129448
|
-
packageDirectories: [{ path: DEFAULT_PACKAGE_DIR, default: true }],
|
|
129449
|
-
namespace: "",
|
|
129450
|
-
sourceApiVersion: SF_API_VERSION3
|
|
129451
|
-
};
|
|
129452
|
-
try {
|
|
129453
|
-
await writeFile14(projectPath, `${JSON.stringify(project, null, JSON_INDENT7)}
|
|
129454
|
-
`, "utf8");
|
|
129455
|
-
wrote = true;
|
|
129456
|
-
} catch {
|
|
129457
|
-
}
|
|
129458
|
-
}
|
|
129459
|
-
try {
|
|
129460
|
-
await mkdir14(join30(cwd, DEFAULT_PACKAGE_DIR), { recursive: true });
|
|
129461
|
-
} catch {
|
|
129462
|
-
}
|
|
129463
|
-
return wrote;
|
|
129464
|
-
};
|
|
129465
|
-
var createVaultDirs = async (paths) => {
|
|
129466
|
-
for (const dir of [paths.root, paths.source, paths.components, paths.graph, paths.meta]) {
|
|
129467
|
-
try {
|
|
129468
|
-
await mkdir14(dir, { recursive: true });
|
|
129469
|
-
} catch (cause) {
|
|
129470
|
-
return err({ kind: "mkdir-failed", message: `failed to create directory: ${dir}`, path: dir, cause });
|
|
129471
|
-
}
|
|
129472
|
-
}
|
|
129473
|
-
return ok(void 0);
|
|
129474
|
-
};
|
|
129475
|
-
var writeVaultMetadata = async (paths, targetOrg, resolvedRoot) => {
|
|
129476
|
-
const config = {
|
|
129477
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
129478
|
-
targetOrg,
|
|
129479
|
-
vaultRoot: resolvedRoot,
|
|
129480
|
-
version: readCliPackageVersion(),
|
|
129481
|
-
snapshotOnRefresh: true
|
|
129482
|
-
};
|
|
129483
|
-
try {
|
|
129484
|
-
await writeFile14(paths.config, `${JSON.stringify(config, null, JSON_INDENT7)}
|
|
129485
|
-
`, "utf8");
|
|
129486
|
-
} catch (cause) {
|
|
129487
|
-
return err({ kind: "write-failed", message: `failed to write config: ${paths.config}`, path: paths.config, cause });
|
|
129488
|
-
}
|
|
129489
|
-
const versionTxtPath = join30(paths.meta, "version.txt");
|
|
129490
|
-
try {
|
|
129491
|
-
await writeFile14(versionTxtPath, `${readCliPackageVersion()}
|
|
129492
|
-
`, "utf8");
|
|
129493
|
-
} catch (cause) {
|
|
129494
|
-
return err({ kind: "write-failed", message: `failed to write version.txt: ${versionTxtPath}`, path: versionTxtPath, cause });
|
|
129495
|
-
}
|
|
129496
|
-
return ok(void 0);
|
|
129497
|
-
};
|
|
129498
|
-
var updateGitignore = async (cwd) => {
|
|
129499
|
-
const gitignorePath = join30(cwd, ".gitignore");
|
|
129500
|
-
let existing = "";
|
|
129501
|
-
let exists = true;
|
|
129502
|
-
try {
|
|
129503
|
-
existing = await readFile82(gitignorePath, "utf8");
|
|
129504
|
-
} catch (cause) {
|
|
129505
|
-
if (!isEnoent4(cause))
|
|
129506
|
-
return false;
|
|
129507
|
-
exists = false;
|
|
129508
|
-
}
|
|
129509
|
-
const present = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
|
|
129510
|
-
const missing = GITIGNORE_ENTRIES.filter((entry) => !present.has(entry));
|
|
129511
|
-
if (missing.length === 0)
|
|
129512
|
-
return false;
|
|
129513
|
-
const needsNewline = exists && existing.length > 0 && !existing.endsWith("\n");
|
|
129514
|
-
const next = `${existing}${needsNewline ? "\n" : ""}${missing.join("\n")}
|
|
129515
|
-
`;
|
|
129516
|
-
try {
|
|
129517
|
-
await writeFile14(gitignorePath, next, "utf8");
|
|
129518
|
-
return true;
|
|
129519
|
-
} catch {
|
|
129520
|
-
return false;
|
|
129521
|
-
}
|
|
129522
|
-
};
|
|
129523
|
-
var pathExists5 = async (path) => {
|
|
129524
|
-
try {
|
|
129525
|
-
await stat13(path);
|
|
129526
|
-
return true;
|
|
129527
|
-
} catch (cause) {
|
|
129528
|
-
return !isEnoent4(cause);
|
|
129529
|
-
}
|
|
129530
|
-
};
|
|
129531
|
-
var isEnoent4 = (cause) => typeof cause === "object" && cause !== null && "code" in cause && cause.code === "ENOENT";
|
|
129532
|
-
var ORG_CATEGORIES = ["nonScratchOrgs", "scratchOrgs", "otherOrgs", "devHubs", "sandboxes"];
|
|
129533
|
-
var getDefaultOrgAlias = async () => {
|
|
129534
|
-
try {
|
|
129535
|
-
const { stdout } = await execAsync3("sf org list --json");
|
|
129536
|
-
const parsed = JSON.parse(stdout);
|
|
129537
|
-
const result = parsed.result;
|
|
129538
|
-
if (result === void 0 || result === null)
|
|
129539
|
-
return null;
|
|
129540
|
-
for (const key of ORG_CATEGORIES) {
|
|
129541
|
-
const entries = result[key];
|
|
129542
|
-
if (!Array.isArray(entries))
|
|
129543
|
-
continue;
|
|
129544
|
-
for (const item of entries) {
|
|
129545
|
-
if (typeof item !== "object" || item === null)
|
|
129546
|
-
continue;
|
|
129547
|
-
const entry = item;
|
|
129548
|
-
if (entry["isDefaultUsername"] !== true && entry["isDefaultDevHubUsername"] !== true)
|
|
129549
|
-
continue;
|
|
129550
|
-
const alias = entry["alias"] ?? entry["username"];
|
|
129551
|
-
if (typeof alias === "string" && alias.length > 0)
|
|
129552
|
-
return alias;
|
|
129553
|
-
}
|
|
129554
|
-
}
|
|
129555
|
-
return null;
|
|
129556
|
-
} catch {
|
|
129557
|
-
return null;
|
|
129558
|
-
}
|
|
129559
|
-
};
|
|
129560
|
-
var registerInitCommand = (program) => {
|
|
129561
|
-
program.command("init").description("Initialise a new org-kb vault in the current directory").option("--target-org <alias>", "Salesforce org alias to bind to this vault").option("--vault-root <path>", "Vault root directory (relative to CWD)").option("--force", "Overwrite an existing org-kb/ vault config", false).action(async (flags) => {
|
|
129562
|
-
const cwd = process.cwd();
|
|
129563
|
-
const exitCode = await handleInit(cwd, flags);
|
|
129564
|
-
if (exitCode !== 0)
|
|
129565
|
-
process.exit(exitCode);
|
|
129566
|
-
});
|
|
129567
|
-
};
|
|
129568
|
-
var handleInit = async (cwd, flags) => {
|
|
129569
|
-
const interactive = process.stdin.isTTY === true;
|
|
129570
|
-
const vaultRoot = flags.vaultRoot ?? (interactive ? await input({ message: "Vault root directory:", default: DEFAULT_VAULT_ROOT3 }) : DEFAULT_VAULT_ROOT3);
|
|
129571
|
-
const resolvedRoot = isAbsolute5(vaultRoot) ? vaultRoot : resolve9(cwd, vaultRoot);
|
|
129572
|
-
let force = flags.force ?? false;
|
|
129573
|
-
if (!force && await pathExists5(resolvedRoot)) {
|
|
129574
|
-
if (!interactive) {
|
|
129575
|
-
process.stderr.write(`sfi init: ${vaultRoot}/ already exists. Re-run with --force to overwrite.
|
|
129576
|
-
`);
|
|
129577
|
-
return 1;
|
|
129578
|
-
}
|
|
129579
|
-
const overwrite = await confirm({
|
|
129580
|
-
message: `${vaultRoot}/ already exists. Overwrite config?`,
|
|
129581
|
-
default: false
|
|
129582
|
-
});
|
|
129583
|
-
if (!overwrite) {
|
|
129584
|
-
process.stdout.write("Existing init preserved.\n");
|
|
129585
|
-
return 0;
|
|
129586
|
-
}
|
|
129587
|
-
force = true;
|
|
129588
|
-
}
|
|
129589
|
-
const targetOrg = await resolveTargetOrg(flags, interactive);
|
|
129590
|
-
if (targetOrg === null) {
|
|
129591
|
-
process.stderr.write("sfi init: no --target-org given and no default Salesforce org found. Pass --target-org <alias>.\n");
|
|
129592
|
-
return 1;
|
|
129593
|
-
}
|
|
129594
|
-
return runAndReport({ cwd, targetOrg, vaultRoot, force });
|
|
129595
|
-
};
|
|
129596
|
-
var resolveTargetOrg = async (flags, interactive) => {
|
|
129597
|
-
if (flags.targetOrg !== void 0)
|
|
129598
|
-
return flags.targetOrg;
|
|
129599
|
-
const detected = await getDefaultOrgAlias();
|
|
129600
|
-
if (!interactive)
|
|
129601
|
-
return detected;
|
|
129602
|
-
return input({
|
|
129603
|
-
message: "Target org alias:",
|
|
129604
|
-
...detected !== null ? { default: detected } : {}
|
|
129605
|
-
});
|
|
129606
|
-
};
|
|
129607
|
-
var runAndReport = async (opts) => {
|
|
129608
|
-
const result = await runInit(opts);
|
|
129609
|
-
if (!result.ok) {
|
|
129610
|
-
process.stderr.write(`sfi init: ${result.error.message}
|
|
129611
|
-
`);
|
|
129612
|
-
return 1;
|
|
129613
|
-
}
|
|
129614
|
-
process.stdout.write([
|
|
129615
|
-
`Initialised vault at ${result.value.vaultRoot}`,
|
|
129616
|
-
`Target org: ${result.value.targetOrg}`,
|
|
129617
|
-
result.value.gitignoreUpdated ? "Updated .gitignore" : ".gitignore already up to date (or could not be written)",
|
|
129618
|
-
result.value.dxProjectScaffolded ? "Scaffolded sfdx-project.json (ready for sf project retrieve)" : "sfdx-project.json already present",
|
|
129619
|
-
""
|
|
129620
|
-
].join("\n"));
|
|
129621
|
-
process.stdout.write(formatTrustStatement());
|
|
129622
|
-
process.stdout.write("Next: run sfi refresh\n\n");
|
|
129623
|
-
return 0;
|
|
129624
|
-
};
|
|
129625
|
-
|
|
129626
130157
|
// dist/src/commands/list-vaults.js
|
|
129627
130158
|
init_src2();
|
|
129628
130159
|
var JSON_INDENT8 = 2;
|
|
@@ -129668,113 +130199,6 @@ var registerListVaultsCommand = (program) => {
|
|
|
129668
130199
|
});
|
|
129669
130200
|
};
|
|
129670
130201
|
|
|
129671
|
-
// dist/src/commands/mcp.js
|
|
129672
|
-
init_dist();
|
|
129673
|
-
init_src7();
|
|
129674
|
-
init_src2();
|
|
129675
|
-
import { execFile as execFile4 } from "node:child_process";
|
|
129676
|
-
import { readFile as readFile83, stat as stat14 } from "node:fs/promises";
|
|
129677
|
-
import { resolve as resolve10 } from "node:path";
|
|
129678
|
-
import { promisify as promisify7 } from "node:util";
|
|
129679
|
-
var DEFAULT_VAULT_ROOT4 = "org-kb";
|
|
129680
|
-
var SHUTDOWN_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
129681
|
-
var readBoundOrg = async (configPath) => {
|
|
129682
|
-
try {
|
|
129683
|
-
const raw = await readFile83(configPath, "utf8");
|
|
129684
|
-
const parsed = JSON.parse(raw);
|
|
129685
|
-
return typeof parsed.targetOrg === "string" ? parsed.targetOrg : null;
|
|
129686
|
-
} catch {
|
|
129687
|
-
return null;
|
|
129688
|
-
}
|
|
129689
|
-
};
|
|
129690
|
-
var nodeExecFile3 = promisify7(execFile4);
|
|
129691
|
-
var defaultListOrgs = async () => {
|
|
129692
|
-
try {
|
|
129693
|
-
const { stdout } = await nodeExecFile3("sf", ["org", "list", "--json"], {
|
|
129694
|
-
maxBuffer: 10 * 1024 * 1024
|
|
129695
|
-
});
|
|
129696
|
-
const json = JSON.parse(stdout);
|
|
129697
|
-
const groups = json.result ?? {};
|
|
129698
|
-
const seen = /* @__PURE__ */ new Set();
|
|
129699
|
-
const out = [];
|
|
129700
|
-
for (const list of Object.values(groups)) {
|
|
129701
|
-
for (const org of list ?? []) {
|
|
129702
|
-
const label = org.alias ?? org.username;
|
|
129703
|
-
if (label && !seen.has(label)) {
|
|
129704
|
-
seen.add(label);
|
|
129705
|
-
out.push(label);
|
|
129706
|
-
}
|
|
129707
|
-
}
|
|
129708
|
-
}
|
|
129709
|
-
return out;
|
|
129710
|
-
} catch {
|
|
129711
|
-
return [];
|
|
129712
|
-
}
|
|
129713
|
-
};
|
|
129714
|
-
var prepareMcp = async (opts) => {
|
|
129715
|
-
const vaultRoot = opts.vaultRoot !== void 0 ? resolve10(opts.cwd, opts.vaultRoot) : resolve10(opts.cwd, DEFAULT_VAULT_ROOT4);
|
|
129716
|
-
const paths = vaultPaths(vaultRoot);
|
|
129717
|
-
if (!await pathExists6(paths.config)) {
|
|
129718
|
-
const orgs = await (opts.listOrgs ?? defaultListOrgs)();
|
|
129719
|
-
const where = opts.vaultRoot !== void 0 ? ` at ${vaultRoot}` : "";
|
|
129720
|
-
const base = `No vault${where}. Run \`sfi init\` followed by \`sfi refresh\`, or point \`sfi mcp --vault <path>\` at an existing org-kb.`;
|
|
129721
|
-
const hint = orgs.length > 0 ? ` You are authenticated to ${orgs.length} org(s): ${orgs.slice(0, 8).join(", ")}${orgs.length > 8 ? ", \u2026" : ""}. Run \`sfi init\` to pick the one you want and build its knowledge base \u2014 live answers attach to a vault's org, so the product never guesses which of your orgs to query.` : "";
|
|
129722
|
-
return err({ kind: "no-vault", message: base + hint });
|
|
129723
|
-
}
|
|
129724
|
-
const ctxResult = await buildContext(vaultRoot);
|
|
129725
|
-
if (!ctxResult.ok) {
|
|
129726
|
-
return err({ kind: "buildContext-failed", message: ctxResult.error.message });
|
|
129727
|
-
}
|
|
129728
|
-
const targetOrg = await readBoundOrg(paths.config);
|
|
129729
|
-
return ok({
|
|
129730
|
-
ctx: ctxResult.value,
|
|
129731
|
-
server: createServer(ctxResult.value),
|
|
129732
|
-
vaultRoot,
|
|
129733
|
-
targetOrg
|
|
129734
|
-
});
|
|
129735
|
-
};
|
|
129736
|
-
var pathExists6 = async (path) => {
|
|
129737
|
-
try {
|
|
129738
|
-
await stat14(path);
|
|
129739
|
-
return true;
|
|
129740
|
-
} catch (cause) {
|
|
129741
|
-
return !isEnoent5(cause);
|
|
129742
|
-
}
|
|
129743
|
-
};
|
|
129744
|
-
var isEnoent5 = (cause) => typeof cause === "object" && cause !== null && "code" in cause && cause.code === "ENOENT";
|
|
129745
|
-
var registerMcpCommand = (program) => {
|
|
129746
|
-
program.command("mcp").description("Run the MCP server backing the org-kb vault").option("--vault <path>", "Serve a specific org-kb vault instead of ./org-kb (bind a project to the right org)").action(async (cmdOpts) => {
|
|
129747
|
-
const prepared = await prepareMcp({
|
|
129748
|
-
cwd: process.cwd(),
|
|
129749
|
-
...cmdOpts.vault !== void 0 ? { vaultRoot: cmdOpts.vault } : {}
|
|
129750
|
-
});
|
|
129751
|
-
if (!prepared.ok) {
|
|
129752
|
-
process.stderr.write(`sfi mcp: ${prepared.error.message}
|
|
129753
|
-
`);
|
|
129754
|
-
process.exit(1);
|
|
129755
|
-
}
|
|
129756
|
-
const { ctx, server, vaultRoot, targetOrg } = prepared.value;
|
|
129757
|
-
process.stderr.write(`sfi mcp: serving vault ${vaultRoot}${targetOrg !== null ? ` (org: ${targetOrg})` : " (no targetOrg in config)"}
|
|
129758
|
-
`);
|
|
129759
|
-
const shutdownOnce = makeShutdownOnce(ctx);
|
|
129760
|
-
for (const signal of SHUTDOWN_SIGNALS) {
|
|
129761
|
-
process.on(signal, () => {
|
|
129762
|
-
void shutdownOnce().then(() => process.exit(0));
|
|
129763
|
-
});
|
|
129764
|
-
}
|
|
129765
|
-
await startServer(server);
|
|
129766
|
-
await shutdownOnce();
|
|
129767
|
-
});
|
|
129768
|
-
};
|
|
129769
|
-
var makeShutdownOnce = (ctx) => {
|
|
129770
|
-
let done = null;
|
|
129771
|
-
return () => {
|
|
129772
|
-
if (done === null)
|
|
129773
|
-
done = shutdown(ctx);
|
|
129774
|
-
return done;
|
|
129775
|
-
};
|
|
129776
|
-
};
|
|
129777
|
-
|
|
129778
130202
|
// dist/src/commands/quickstart.js
|
|
129779
130203
|
init_src();
|
|
129780
130204
|
init_src2();
|
|
@@ -130032,9 +130456,9 @@ init_vault_git();
|
|
|
130032
130456
|
init_watch();
|
|
130033
130457
|
var readVersion = () => {
|
|
130034
130458
|
if (true)
|
|
130035
|
-
return "0.1.
|
|
130459
|
+
return "0.1.13";
|
|
130036
130460
|
const pkgUrl = new URL("../../package.json", import.meta.url);
|
|
130037
|
-
const raw =
|
|
130461
|
+
const raw = readFileSync6(fileURLToPath3(pkgUrl), "utf8");
|
|
130038
130462
|
const parsed = JSON.parse(raw);
|
|
130039
130463
|
return parsed.version ?? "0.0.0";
|
|
130040
130464
|
};
|
|
@@ -130051,6 +130475,7 @@ var createProgram = () => {
|
|
|
130051
130475
|
registerVaultCommand(program);
|
|
130052
130476
|
registerDoctorCommand(program);
|
|
130053
130477
|
registerMcpCommand(program);
|
|
130478
|
+
registerDemoCommand(program);
|
|
130054
130479
|
registerServeCommand(program);
|
|
130055
130480
|
registerSelftestCommand(program);
|
|
130056
130481
|
registerFeedbackCommand(program);
|