forked from obsidianmd/obsidian-sample-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
132 lines (114 loc) · 80.8 KB
/
main.js
File metadata and controls
132 lines (114 loc) · 80.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var B=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var Q=(v,e)=>{for(var t in e)B(v,t,{get:e[t],enumerable:!0})},Y=(v,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of K(e))!J.call(v,i)&&i!==t&&B(v,i,{get:()=>e[i],enumerable:!(r=q(e,i))||r.enumerable});return v};var X=v=>Y(B({},"__esModule",{value:!0}),v);var Z={};Q(Z,{default:()=>F});module.exports=X(Z);var o=require("obsidian"),C=["subprocessor","sub-processor","sub_processor","vendor-list","vendorlist","third-party-list","thirdpartylist","service-providers","serviceproviders","dpa-exhibit","dpa/exhibit","data-processing-addendum/exhibit","trust-center/sub","legal/subprocessors"],U={serpApiKey:"",rightbrainClientId:"",rightbrainClientSecret:"",rightbrainOrgId:"",rightbrainProjectId:"",rightbrainApiUrl:"https://app.rightbrain.ai/api/v1",rightbrainOauth2Url:"https://oauth.rightbrain.ai",rightbrainVerifyUrlTaskId:"",rightbrainExtractEntitiesTaskId:"",rightbrainExtractInputField:"page_text",rightbrainExtractOutputThirdPartyField:"third_party_subprocessors",rightbrainExtractOutputOwnEntitiesField:"own_entities",rightbrainDeduplicateSubprocessorsTaskId:"",rightbrainDuckDuckGoSearchTaskId:"",createPagesForOwnEntities:!1,verboseDebug:!1,maxResultsPerProcessor:1,maxRecursiveDepth:2,discoveryCacheDays:30,processorsFolderPath:"Processors",analysisLogsFolderPath:"Analysis Logs",rightbrainFindDpaTaskId:"",rightbrainFindTosTaskId:"",rightbrainFindSecurityTaskId:"",autoSynchronizeTasks:!0,llmModelList:[],llmModelListLastUpdated:0,verifyUrlModelId:"",extractEntitiesModelId:"",deduplicateSubprocessorsModelId:"",duckDuckGoSearchModelId:"",findDpaModelId:"",findTosModelId:"",findSecurityModelId:""},F=class extends o.Plugin{async onload(){this.processedInCurrentRecursiveSearch=new Set,await this.loadSettings(),this.updateLlmModelList(!1),this.settings.autoSynchronizeTasks&&setTimeout(()=>this.synchronizeRightBrainTasks(),1e3),this.addRibbonIcon("link","Manually Add Subprocessor List URL",e=>{new x(this.app,async(t,r,i)=>{if(t&&r){new o.Notice(`Processing manual URL input for: ${t}`);let n=await this.ensureProcessorFile(t,!0,i);if(n){let s=await this.fetchDataFromDirectUrl(t,r);s?(await this.persistSubprocessorInfo(t,n,s,i),s.flaggedCandidateUrlCount>0&&new o.Notice(`${s.flaggedCandidateUrlCount} URL(s) looked promising but couldn't be verified. Check logs.`)):new o.Notice(`Could not process data from direct URL for ${t}.`)}else new o.Notice(`Could not create or find file for ${t} in ${this.settings.processorsFolderPath}`)}}).open()}),this.addRibbonIcon("paste","Input Subprocessor List from Text",e=>{this.openManualTextEntryModal()}),this.addCommand({id:"run-processor-search-global",name:"Search for Subprocessors (Discover)",callback:()=>{new I(this.app,this.settings,async e=>{if(e){new o.Notice(`Starting discovery search for: ${e}`);let t=await this.ensureProcessorFile(e,!0);t?await this.discoverAndProcessProcessorPage(e,t):new o.Notice(`Could not create or find file for ${e} in ${this.settings.processorsFolderPath}`)}}).open()}}),this.addCommand({id:"input-subprocessor-list-from-text",name:"Input Subprocessor List from Text",callback:()=>{this.openManualTextEntryModal()}}),this.addCommand({id:"run-processor-search-recursive",name:"Search for Subprocessors (Recursive Discover)",callback:()=>{new I(this.app,this.settings,async e=>{e&&await this.discoverRecursively(e,void 0,this.settings.maxRecursiveDepth)}).open()}}),this.addCommand({id:"force-merge-processors-from-palette",name:"Force Merge processor files...",callback:()=>{this.openFileSelectorMergeModal()}}),this.addCommand({id:"synchronize-rightbrain-tasks",name:"Synchronize RightBrain Tasks",callback:()=>{this.synchronizeRightBrainTasks()}}),this.addCommand({id:"complete-first-time-setup",name:"Complete First-Time Setup (Credentials & Tasks)",callback:()=>{new j(this.app,this).open()}}),this.addCommand({id:"select-default-model",name:"Select Default Model for All Tasks",callback:async()=>{if(await this.updateLlmModelList(!0),this.settings.llmModelList.length===0){new o.Notice("No models available. Please check your RightBrain credentials and try again.");return}new A(this.app,this,this.settings.llmModelList,async e=>{var r;let t=["verifyUrlModelId","extractEntitiesModelId","deduplicateSubprocessorsModelId","duckDuckGoSearchModelId","findDpaModelId","findTosModelId","findSecurityModelId"];for(let i of t)this.settings[i]=e;await this.saveSettings(),new o.Notice(`All tasks will now use ${((r=this.settings.llmModelList.find(i=>i.id===e))==null?void 0:r.alias)||e}. Synchronizing with RightBrain...`),setTimeout(()=>{this.synchronizeRightBrainTasks()},1e3)}).open()}}),this.addCommand({id:"apply-recommended-graph-settings",name:"Apply Recommended Graph Settings",callback:()=>{this.applyRecommendedGraphSettings()}}),this.registerEvent(this.app.workspace.on("file-menu",(e,t,r)=>{if(t instanceof o.TFolder){let i=t;i.path===this.settings.processorsFolderPath&&e.addItem(n=>{n.setTitle("Deduplicate Subprocessor Pages").setIcon("git-pull-request-draft").onClick(async()=>{if(!this.settings.rightbrainDeduplicateSubprocessorsTaskId){new o.Notice("Deduplication Task ID not set in plugin settings.");return}new o.Notice(`Starting deduplication for folder: ${i.path}`),await this.runDeduplicationForFolder(i)})})}else if(t instanceof o.TFile&&t.extension==="md"){let i=t;if(i.path.startsWith((0,o.normalizePath)(this.settings.processorsFolderPath)+"/")){let n=this.app.metadataCache.getFileCache(i),s=n==null?void 0:n.frontmatter,a=s!=null&&s.aliases&&Array.isArray(s.aliases)&&s.aliases.length>0?s.aliases[0]:i.basename;e.addItem(c=>{c.setTitle("Map Subprocessor Relationships").setIcon("chevrons-down-up").onClick(async()=>{new o.Notice(`Starting recursive discovery from: ${a}`),await this.discoverRecursively(a,i,this.settings.maxRecursiveDepth)})}),e.addItem(c=>{c.setTitle("Discover Subprocessor List").setIcon("wand").onClick(async()=>{new o.Notice(`Discovering subprocessor list for: ${a}`),await this.discoverAndProcessProcessorPage(a,i)})}),e.addItem(c=>{c.setTitle("Enrich Processor Documentation").setIcon("book-plus").onClick(async()=>{new o.Notice(`Enriching documentation for: ${a}`),await this.enrichProcessorFile(a,i)})}),e.addItem(c=>{c.setTitle("Add Subprocessor List URL").setIcon("plus-circle").onClick(async()=>{new x(this.app,async(l,d,g)=>{if(d){new o.Notice(`Processing manual URL input for: ${a} using URL: ${d}`);let p=await this.fetchDataFromDirectUrl(a,d);p?(await this.persistSubprocessorInfo(a,i,p,g),p.flaggedCandidateUrlCount>0&&new o.Notice(`${p.flaggedCandidateUrlCount} URL(s) looked promising but couldn't be verified. Check logs.`)):new o.Notice(`Could not process data from direct URL for ${a}.`)}},a).open()})}),e.addItem(c=>{c.setTitle("Input Subprocessor List from Text").setIcon("file-input").onClick(async()=>{this.openManualTextEntryModal(a)})})}}})),this.addSettingTab(new z(this.app,this))}onunload(){}async loadSettings(){this.settings=Object.assign({},U,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}async fetchLlmModels(){let e=await this.getRightBrainAccessToken();if(!e)return console.error("Cannot fetch LLM models without an access token."),[];let t=`${this.settings.rightbrainApiUrl}/org/${this.settings.rightbrainOrgId}/project/${this.settings.rightbrainProjectId}/model`,r={Authorization:`Bearer ${e}`};try{let i=await(0,o.requestUrl)({url:t,method:"GET",headers:r,throw:!1});return i.status===200&&Array.isArray(i.json)?i.json.map(n=>({id:n.id,alias:n.alias})):(console.error("Failed to list RightBrain LLM models:",i.status,i.text),[])}catch(i){return console.error("Error fetching RightBrain LLM models:",i),[]}}async updateLlmModelList(e=!1){let t=Date.now(),r=24*60*60*1e3,i=this.settings.rightbrainClientId&&this.settings.rightbrainOrgId&&this.settings.rightbrainProjectId;if(!(!e&&this.settings.llmModelList.length>0&&t-this.settings.llmModelListLastUpdated<r)&&i)try{let n=await this.fetchLlmModels();n.length>0&&(this.settings.llmModelList=n,this.settings.llmModelListLastUpdated=t,await this.saveSettings(),e&&new o.Notice("LLM model list has been updated."))}catch(n){console.error("Failed to update LLM model list:",n),e&&new o.Notice("Failed to update LLM model list. Check console.")}}openManualTextEntryModal(e){if(!this.settings.rightbrainExtractEntitiesTaskId){new o.Notice("RightBrain Task ID for entity extraction is not configured. Please set it in plugin settings.");return}new M(this.app,async(t,r,i)=>{if(t&&r){new o.Notice(`Processing pasted text for: ${t}`);let n=await this.ensureProcessorFile(t,!0,i);if(n){let s=await this.fetchDataFromPastedText(t,r);s?await this.persistSubprocessorInfo(t,n,s,i):new o.Notice(`Could not process data from pasted text for ${t}.`)}else new o.Notice(`Could not create or find file for ${t} in ${this.settings.processorsFolderPath}`)}},e).open()}sanitizeNameForFilePathAndAlias(e){let t=(e||"Unknown Entity").trim(),r=t,i=/^(.*?)\s+(?:dba|d\/b\/a|doing business as)\s+(.*)$/i,n=t.match(i);n&&n[2]&&(r=n[2].trim());let s=r.replace(/,/g,"");return s=s.replace(/[\\/:*?"<>|]/g,"").trim(),s||(s=t.replace(/[\\/:*?"<>|,]/g,"").replace(/\s+/g,"_")||"Sanitized_Entity"),s||(s="Sanitized_Entity_"+Date.now()),{filePathName:s,originalNameAsAlias:t}}scrubHyperlinks(e){if(!e)return"N/A";let t=String(e);return t=t.replace(/\[(.*?)\]\((?:.*?)\)/g,"$1"),t=t.replace(/<a[^>]*>(.*?)<\/a>/gi,"$1"),t=t.replace(/<[^>]+>/g,""),t=t.replace(/\s+/g," ").trim(),t||"N/A"}addRelationship(e,t,r,i,n,s,a){var g;let c=(g=i.name)==null?void 0:g.trim();if(!c)return 0;let l=c;if(r.toLowerCase()==="openai"&&n==="is_own_entity"&&(["openai global","openai, opco","openai ireland","openai uk","openai japan","openaiglobal","openai opco","openai llc"].some(u=>c.toLowerCase().includes(u))||c.toLowerCase()==="openai"))return 0;let d=`${r}|${l}|${n}`;return t.has(d)?0:(e.push({PrimaryProcessor:r,SubprocessorName:l,ProcessingFunction:this.scrubHyperlinks(i.processing_function),Location:this.scrubHyperlinks(i.location),RelationshipType:n,SourceURL:s,VerificationReasoning:this.scrubHyperlinks(a)}),t.add(d),1)}async discoverAndProcessProcessorPage(e,t){new o.Notice(`Processing (discovery): ${e}...`);let r=await this.fetchProcessorSearchDataWithDiscovery(e);r?(await this.persistSubprocessorInfo(e,t,r),r.flaggedCandidateUrlCount>0&&new o.Notice(`${r.flaggedCandidateUrlCount} URL(s) looked promising but couldn't be verified. Check Analysis Log for details and consider using the 'Input from Text' feature.`)):new o.Notice(`Failed to fetch data via discovery for ${e}.`)}async enrichProcessorFile(e,t){var c;new o.Notice(`Fetching compliance documents for ${e}...`,5e3);let r=await this.getRightBrainAccessToken();if(!r){new o.Notice("Failed to get RightBrain token. Aborting enrichment.");return}let i=[{type:"DPA",taskId:this.settings.rightbrainFindDpaTaskId,title:"Data Processing Agreement"},{type:"ToS",taskId:this.settings.rightbrainFindTosTaskId,title:"Terms of Service"},{type:"Security",taskId:this.settings.rightbrainFindSecurityTaskId,title:"Security Documentation"}],n=[];for(let l of i){if(!l.taskId){this.settings.verboseDebug&&console.log(`Skipping ${l.type} search for ${e}, no Task ID set.`);continue}let d={company_name:e},g=await this.callRightBrainTask(l.taskId,d,r);(c=g==null?void 0:g.response)!=null&&c.url&&this.isValidUrl(g.response.url)?(n.push({title:l.title,url:g.response.url}),this.settings.verboseDebug&&console.log(`Found ${l.type} for ${e}: ${g.response.url}`)):this.settings.verboseDebug&&console.warn(`Could not find valid URL for ${l.type} for ${e}. Result:`,g),await new Promise(p=>setTimeout(p,500))}if(n.length===0){new o.Notice(`No new compliance documents found for ${e}.`);return}let s=`
`;n.forEach(l=>{s+=`- **${l.title}:** [${l.url}](${l.url})
`});let a="Compliance Documentation";await this.app.vault.process(t,l=>this.ensureHeadingAndSection(l,a,s,null,null,!0)),new o.Notice(`Successfully added ${n.length} document link(s) to ${e}.`)}async synchronizeRightBrainTasks(){new o.Notice("Starting RightBrain task synchronization...",4e3);let e={apiUrl:this.settings.rightbrainApiUrl,oauthUrl:this.settings.rightbrainOauth2Url,clientId:this.settings.rightbrainClientId,clientSecret:this.settings.rightbrainClientSecret,orgId:this.settings.rightbrainOrgId,projectId:this.settings.rightbrainProjectId},t=await this.getRightBrainAccessToken(e);if(!t){new o.Notice("Task sync failed: Could not get RightBrain Access Token.",1e4);return}let r;try{let l=this.app.vault.adapter,d=`${this.manifest.dir}/task_definitions.json`;if(!await l.exists(d)){new o.Notice("Task sync failed: task_definitions.json not found.",1e4);return}r=JSON.parse(await l.read(d))}catch(l){new o.Notice("Task sync failed: Could not read task_definitions.json. Check console.",1e4),console.error("ProcessorProcessor: Failed to load local task definitions:",l);return}let i=await this.listAllRightBrainTasks(t,e);if(i===null){new o.Notice("Task sync failed: Could not retrieve tasks from RightBrain.",1e4);return}let n=new Map(i.map(l=>[l.name,l]));for(let l of r){let d=n.get(l.name),p={rightbrainVerifyUrlTaskId:"verifyUrlModelId",rightbrainExtractEntitiesTaskId:"extractEntitiesModelId",rightbrainDeduplicateSubprocessorsTaskId:"deduplicateSubprocessorsModelId",rightbrainDuckDuckGoSearchTaskId:"duckDuckGoSearchModelId",rightbrainFindDpaTaskId:"findDpaModelId",rightbrainFindTosTaskId:"findTosModelId",rightbrainFindSecurityTaskId:"findSecurityModelId"}[l.setting_key],u=p?this.settings[p]:null;this.settings.verboseDebug&&console.log(`Task '${l.name}': Setting key: ${p}, User selected model: ${u}, Default model: ${l.llm_model_id}`);let f={system_prompt:l.system_prompt,user_prompt:l.user_prompt,output_format:l.output_format,input_processors:l.input_processors||[],enabled:l.enabled,llm_model_id:u||l.llm_model_id||null};if(d){this.settings.verboseDebug&&console.log(`Server task structure for '${l.name}':`,JSON.stringify(d,null,2));let m=d.task_revisions||d.revisions,h=m==null?void 0:m.sort((y,P)=>new Date(P.created_at).getTime()-new Date(y.created_at).getTime())[0],w=!h||h.system_prompt!==f.system_prompt||h.user_prompt!==f.user_prompt||h.llm_model_id!==f.llm_model_id||JSON.stringify(h.output_format)!==JSON.stringify(f.output_format);this.settings.verboseDebug&&(console.log(`Task '${l.name}': Current model: ${h==null?void 0:h.llm_model_id}, New model: ${f.llm_model_id}, Needs update: ${w}`),(h==null?void 0:h.llm_model_id)!==f.llm_model_id&&console.log(`Model change detected for '${l.name}': ${h==null?void 0:h.llm_model_id} -> ${f.llm_model_id}`)),w&&(this.settings.verboseDebug&&console.log(`Task '${l.name}' has updates. Creating new revision...`),await this.updateRightBrainTask(t,d.id,f,l.name,e))}else{this.settings.verboseDebug&&console.log(`Task '${l.name}' not found on server. Creating...`);let m={...f,name:l.name,description:l.description};await this.createRightBrainTask(t,m,e)}}let s=await this.listAllRightBrainTasks(t,e);if(s===null){new o.Notice("Task sync failed: Could not fetch final task list to save IDs.",1e4);return}let a=new Map(s.map(l=>[l.name,l.id])),c=0;for(let l of r){let d=l.setting_key;d&&a.has(l.name)&&(this.settings[d]=a.get(l.name),c++)}await this.saveSettings(),new o.Notice(`RightBrain tasks synchronized successfully. ${c} tasks configured.`,1e4)}async setupRightBrainTasksWithModel(e,t){new o.Notice("Step 1: Verifying tasks on RightBrain...",4e3);let r=await this.getRightBrainAccessToken(e);if(!r){new o.Notice("Setup failed: Could not get RightBrain Access Token.");return}let i;try{let d=this.app.vault.adapter,p=`${this.manifest.dir}/task_definitions.json`;if(!await d.exists(p)){new o.Notice("Error: task_definitions.json not found in plugin folder.",7e3);return}let u=await d.read(p);i=JSON.parse(u)}catch(d){new o.Notice("Error reading or parsing task_definitions.json. Check console.",7e3),console.error("ProcessorProcessor: Failed to load task definitions from file:",d);return}let n=await this.listAllRightBrainTasks(r,e);if(n===null){new o.Notice("Setup failed: Could not retrieve existing tasks from RightBrain.");return}let s=new Set(n.map(d=>d.name));for(let d of i)if(!s.has(d.name)){new o.Notice(`Creating missing task: '${d.name}'...`);let g={...d,llm_model_id:t};await this.createRightBrainTask(r,g,e),await new Promise(p=>setTimeout(p,500))}new o.Notice("Step 2: Fetching all task IDs...",4e3);let a=await this.listAllRightBrainTasks(r,e);if(a===null){new o.Notice("Error: Could not fetch the final list of tasks to save their IDs.");return}let c=new Map(a.map(d=>[d.name,d.id])),l=0;for(let d of i){let g=d.setting_key;if(g&&c.has(d.name)){let p=c.get(d.name);p&&(this.settings[g]=p,l++)}else console.warn(`Could not find a matching task on the server for local definition: "${d.name}"`)}await this.saveSettings(),l===i.length?new o.Notice(`Success! All ${l} task IDs have been configured and saved.`):new o.Notice(`Setup finished, but only ${l} of ${i.length} task IDs could be saved.`)}async setupRightBrainTasks(e){new o.Notice("Step 1: Verifying tasks on RightBrain...",4e3);let t=await this.getRightBrainAccessToken(e);if(!t){new o.Notice("Setup failed: Could not get RightBrain Access Token.");return}let r;try{let l=this.app.vault.adapter,g=`${this.manifest.dir}/task_definitions.json`;if(!await l.exists(g)){new o.Notice("Error: task_definitions.json not found in plugin folder.",7e3);return}let p=await l.read(g);r=JSON.parse(p)}catch(l){new o.Notice("Error reading or parsing task_definitions.json. Check console.",7e3),console.error("ProcessorProcessor: Failed to load task definitions from file:",l);return}let i=await this.listAllRightBrainTasks(t,e);if(i===null){new o.Notice("Setup failed: Could not retrieve existing tasks from RightBrain.");return}let n=new Set(i.map(l=>l.name));for(let l of r)n.has(l.name)||(new o.Notice(`Creating missing task: '${l.name}'...`),await this.createRightBrainTask(t,l,e),await new Promise(d=>setTimeout(d,500)));new o.Notice("Step 2: Fetching all task IDs...",4e3);let s=await this.listAllRightBrainTasks(t,e);if(s===null){new o.Notice("Error: Could not fetch the final list of tasks to save their IDs.");return}let a=new Map(s.map(l=>[l.name,l.id])),c=0;for(let l of r){let d=l.setting_key;if(d&&a.has(l.name)){let g=a.get(l.name);g&&(this.settings[d]=g,c++)}else console.warn(`Could not find a matching task on the server for local definition: "${l.name}"`)}await this.saveSettings(),c===r.length?new o.Notice(`Success! All ${c} task IDs have been configured and saved.`):new o.Notice(`Setup finished, but only ${c} of ${r.length} task IDs could be saved.`)}async listAllRightBrainTasks(e,t){let r=`${t.apiUrl}/org/${t.orgId}/project/${t.projectId}/task`,i={Authorization:`Bearer ${e}`};try{let n=await(0,o.requestUrl)({url:r,method:"GET",headers:i,throw:!1});return n.status===200?n.json.results||[]:(console.error("Failed to list RightBrain tasks:",n.status,n.text),null)}catch(n){return console.error("Error fetching RightBrain tasks:",n),null}}async createRightBrainTask(e,t,r){let i=`${r.apiUrl}/org/${r.orgId}/project/${r.projectId}/task`,n={Authorization:`Bearer ${e}`,"Content-Type":"application/json"};try{let s=await(0,o.requestUrl)({url:i,method:"POST",headers:n,body:JSON.stringify(t),throw:!1});return s.status===201||s.status===200?(new o.Notice(`Successfully created task: '${t.name}'`),s.json):(new o.Notice(`Failed to create task '${t.name}': ${s.status}`,7e3),console.error(`Error creating task '${t.name}':`,s.status,s.text),null)}catch(s){return console.error(`Network error creating task '${t.name}':`,s),null}}async persistSubprocessorInfo(e,t,r,i=!0,n=[]){new o.Notice(`Persisting info for: ${e}...`),await this.ensureFolderExists(this.settings.processorsFolderPath),await this.ensureFolderExists(this.settings.analysisLogsFolderPath);let{collectedRelationships:s,processedUrlDetails:a}=r;await this.updateProcessorFile(t,e,s,i);let c=Array.from(new Set(s.map(d=>d.SubprocessorName))),l=new Set;for(let d of c){let{filePathName:g}=this.sanitizeNameForFilePathAndAlias(d);if(l.has(g))continue;let p=s.filter(h=>h.SubprocessorName===d);if(p.length===0)continue;let u=p.some(h=>h.RelationshipType==="uses_subprocessor"),f=p.some(h=>h.PrimaryProcessor===e&&h.RelationshipType==="is_own_entity"),m=!1;if((u||f&&this.settings.createPagesForOwnEntities)&&(m=!0),m){let h=s.filter(w=>w.SubprocessorName===d&&w.RelationshipType==="uses_subprocessor");await this.createOrUpdateSubprocessorFile(d,e,h),l.add(g)}}await this.updateAnalysisLogPage(e,a,s,n),new o.Notice(`Finished persisting info for ${e}.`)}async searchViaRightBrainDuckDuckGo(e,t){var a;if(!this.settings.rightbrainDuckDuckGoSearchTaskId)return new o.Notice("DuckDuckGo Search Task ID is not configured. Please run the setup command or configure it in settings.",1e4),[];let r=this.settings.rightbrainDuckDuckGoSearchTaskId,i=this.generateSearchQueries(e),n=[],s=i.slice(0,Math.min(i.length,2));new o.Notice(`Performing up to ${s.length} DuckDuckGo searches for ${e}...`,5e3);for(let c of s){let l=`https://duckduckgo.com/?q=${encodeURIComponent(c)}&ia=web&kl=us-en&kp=-2`,d={search_url_to_process:l,target_company_name:e};this.settings.verboseDebug&&console.log(`Calling RightBrain Task ${r} for DDG search. URL: ${l}, Target: ${e}`);let g=await this.callRightBrainTask(r,d,t);if(this.settings.verboseDebug&&g&&console.log(`Full RightBrain Response for DDG search query "${c}":`,JSON.stringify(g,null,2)),(a=g==null?void 0:g.response)!=null&&a.search_results&&Array.isArray(g.response.search_results)){let p=g.response.search_results;for(let u of p)u.url&&u.title&&(String(u.url).startsWith("http://")||String(u.url).startsWith("https://"))&&n.push({processorName:e,searchQuery:c,title:String(u.title),url:String(u.url),snippet:String(u.snippet||""),documentType:"duckduckgo_rb_search_result"});this.settings.verboseDebug&&console.log(`Successfully processed ${p.length} search results for query "${c}"`)}else new o.Notice(`DDG search via RB for "${c.substring(0,20)}..." yielded no valid results.`,3e3),this.settings.verboseDebug&&console.warn(`RB Task for DDG Search for query "${c}" did not return expected '{ "search_results": [...] }' array or failed. Full taskRunResult:`,g);await new Promise(p=>setTimeout(p,700+Math.random()*500))}return this.settings.verboseDebug&&console.log(`searchViaRightBrainDuckDuckGo collected ${n.length} filtered candidates for ${e}`),n}async fetchProcessorSearchDataWithDiscovery(e){let t=[],r=new Set,i=[],n=[],s=0,a=await this.getRightBrainAccessToken();if(!a)return new o.Notice("Could not get RightBrain Access Token. Aborting discovery.",7e3),null;if(this.settings.serpApiKey){new o.Notice(`Using SerpAPI for primary search for: ${e}`,5e3);let u=this.generateSearchQueries(e),f=await this.searchSerpApiForDpas(e,u,this.settings.maxResultsPerProcessor);n.push(...f)}else this.settings.rightbrainOrgId&&this.settings.rightbrainProjectId?(new o.Notice(`SerpAPI key not configured. Using DuckDuckGo (Filtered Extractor Task) via RightBrain for: ${e}`,5e3),n=await this.searchViaRightBrainDuckDuckGo(e,a)):new o.Notice("No search method configured (SerpAPI or RightBrain for DDG). Aborting discovery.",7e3);let c={};this.settings.verboseDebug&&c[e.toLowerCase()]&&(this.settings.verboseDebug&&console.log(`Adding hardcoded test URLs for ${e}`),n.push(...c[e.toLowerCase()]));let l=[],d=n.filter(u=>u.documentType==="dpa_or_subprocessor_list"||C.some(f=>u.url.toLowerCase().includes(f)));for(let u of d){let f=await this.extractUrlsFromDpaPage(u.url,e,u.title);l.push(...f)}n.push(...l);let g=new Map;n.forEach(u=>{u.url&&(u.url.startsWith("http://")||u.url.startsWith("https://"))&&!g.has(u.url.replace(/\/$/,""))&&g.set(u.url.replace(/\/$/,""),u)});let p=Array.from(g.values());this.settings.verboseDebug&&console.log(`Total unique URLs to verify for ${e}: ${p.length}`),p.length===0&&n.length>0?this.settings.verboseDebug&&console.warn(`All candidate URLs were invalid or duplicates for ${e}. Original count: ${n.length}`):p.length===0&&new o.Notice(`No candidate URLs found to process for ${e}.`);for(let u of p){if(i.some(w=>w.url.replace(/\/$/,"")===u.url.replace(/\/$/,""))){this.settings.verboseDebug&&console.log(`URL ${u.url} already processed in processedUrlDetails, skipping re-verification.`);continue}let f=0,m={...u,documentType:u.documentType||"duckduckgo_rb_search_result"},h=await this.verifySubprocessorListUrl(u.url,e,a);if(m={...m,verificationMethod:"rightbrain",isList:(h==null?void 0:h.isList)||!1,isCurrent:(h==null?void 0:h.isCurrent)||!1,verificationReasoning:(h==null?void 0:h.reasoning)||"N/A"},h!=null&&h.isList&&h.isCurrent&&h.isCorrectProcessor){if(m.documentType="verified_current_subprocessor_list",h.pageContent){let w=await this.extractEntitiesFromPageContent(h.pageContent,a);if(w){let{thirdPartySubprocessors:y,ownEntities:P}=w;y.forEach(b=>{f+=this.addRelationship(t,r,e,b,"uses_subprocessor",u.url,h.reasoning)}),P.forEach(b=>{f+=this.addRelationship(t,r,e,b,"is_own_entity",u.url,h.reasoning)})}else m.documentType="verified_current_subprocessor_list (rb_extraction_failed)"}else m.documentType="verified_current_subprocessor_list (no_content_for_extraction)";if(m.extractedSubprocessorsCount=f,i.push(m),f>0){this.settings.verboseDebug&&console.log(`Found and processed a valid subprocessor list at ${u.url}. Stopping search.`),new o.Notice(`Found valid list for ${e}. Finishing process.`);break}}else{let w=u.url.toLowerCase(),y=C.some(P=>w.includes(P));!(h!=null&&h.isList)&&y?(m.documentType="keyword_match_not_verified_list",s++):h!=null&&h.isList&&!h.isCorrectProcessor?(m.documentType="verified_list_for_wrong_processor",s++):h!=null&&h.isList?m.documentType="verified_subprocessor_list (not_current)":m.documentType="not_a_subprocessor_list",m.extractedSubprocessorsCount=0,i.push(m)}}return{collectedRelationships:t,processedUrlDetails:i,flaggedCandidateUrlCount:s}}async fetchDataFromDirectUrl(e,t){if(this.settings.verboseDebug&&console.log(`Fetching data from direct URL for ${e}: ${t}`),!this.isValidUrl(t,e))return new o.Notice(`The provided URL for ${e} is not valid: ${t}`),null;let r=[],i=new Set,n=[],s=0,c={...{title:`Manually Provided List for ${e}`,url:t,snippet:"Manually provided URL",processorName:e,documentType:"direct_input_list"},url:t,documentType:"direct_input_list"},l=await this.getRightBrainAccessToken();if(!l)return new o.Notice("Could not obtain RightBrain token for direct URL processing."),c.verificationMethod="N/A (No RB Token)",n.push(c),{collectedRelationships:r,processedUrlDetails:n,flaggedCandidateUrlCount:s};let d=0,g=await this.verifySubprocessorListUrl(t,e,l);if(c.verificationMethod="rightbrain",c.isList=(g==null?void 0:g.isList)||!1,c.isCurrent=(g==null?void 0:g.isCurrent)||!1,c.verificationReasoning=(g==null?void 0:g.reasoning)||"N/A",g&&g.isList&&g.isCurrent)if(new o.Notice(`Verified manual URL: ${t} as current list.`),c.documentType="verified_current_subprocessor_list (manual_url_input)",g.pageContent){let p=await this.extractEntitiesFromPageContent(g.pageContent,l);if(p){let{thirdPartySubprocessors:u,ownEntities:f}=p;u.forEach(m=>{d+=this.addRelationship(r,i,e,m,"uses_subprocessor",t,g.reasoning)}),f.forEach(m=>{d+=this.addRelationship(r,i,e,m,"is_own_entity",t,g.reasoning)})}else c.documentType="verified_current_subprocessor_list (manual_url_input_rb_extraction_failed)"}else c.documentType="verified_current_subprocessor_list (manual_url_input_no_content)";else{let p=t.toLowerCase(),u=C.some(f=>p.includes(f));!(g!=null&&g.isList)&&u?(c.documentType="keyword_match_not_verified_list (manual_url_input)",s++,new o.Notice(`Manual URL ${t} looks like a subprocessor list but couldn't be verified. Reason: ${this.scrubHyperlinks(g==null?void 0:g.reasoning)||"Details unavailable."}`),this.settings.verboseDebug&&console.log(`Flagged Manual URL (keyword match, not verified): ${t}`)):g!=null&&g.isList?(c.documentType="verified_subprocessor_list (manual_url_input_not_current)",new o.Notice(`Manual URL ${t} verified as a list, but not current. Reason: ${this.scrubHyperlinks(g==null?void 0:g.reasoning)||"Details unavailable."}`)):(c.documentType="not_a_subprocessor_list (manual_url_input)",new o.Notice(`Manual URL ${t} could not be verified as a list. Reason: ${this.scrubHyperlinks(g==null?void 0:g.reasoning)||"Details unavailable."}`))}return c.extractedSubprocessorsCount=d,n.push(c),{collectedRelationships:r,processedUrlDetails:n,flaggedCandidateUrlCount:s}}async fetchDataFromPastedText(e,t){if(this.settings.verboseDebug&&console.log(`Fetching data from pasted text for ${e}`),!this.settings.rightbrainExtractEntitiesTaskId)return new o.Notice("RightBrain Task ID for entity extraction is not configured. Please set it in plugin settings."),null;let r=[],i=new Set,n=[],s=await this.getRightBrainAccessToken();if(!s)return new o.Notice("Could not obtain RightBrain token for pasted text processing."),n.push({url:`text_input_for_${this.sanitizeNameForFilePathAndAlias(e).filePathName}`,title:`Pasted Text for ${e}`,documentType:"manual_text_submission_failed (no_rb_token)"}),{collectedRelationships:r,processedUrlDetails:n,flaggedCandidateUrlCount:0};let a={[this.settings.rightbrainExtractInputField]:t},c=await this.callRightBrainTask(this.settings.rightbrainExtractEntitiesTaskId,a,s),l=0,d=`manual_text_input:${e}`;if(c&&typeof c.response=="object"&&c.response!==null){let g=c.response,p=g[this.settings.rightbrainExtractOutputThirdPartyField]||[],u=g[this.settings.rightbrainExtractOutputOwnEntitiesField]||[];p.forEach(f=>{l+=this.addRelationship(r,i,e,f,"uses_subprocessor",d,"Processed from manually pasted text.")}),u.forEach(f=>{l+=this.addRelationship(r,i,e,f,"is_own_entity",d,"Processed from manually pasted text.")}),n.push({url:d,title:`Pasted Text for ${e}`,documentType:"manual_text_submission_processed",verificationMethod:"rightbrain_text_task",extractedSubprocessorsCount:l,verificationReasoning:`Extracted ${l} entities from pasted text.`})}else new o.Notice(`Failed to extract entities from pasted text for ${e}. Check console.`),console.error("ProcessorProcessor: RB Extract From Text task did not return expected 'response' object or failed. Full task result:",JSON.stringify(c).substring(0,500)),n.push({url:d,title:`Pasted Text for ${e}`,documentType:"manual_text_submission_failed (rb_task_error)",verificationMethod:"rightbrain_text_task",verificationReasoning:"RightBrain task for text processing failed or returned an unexpected response."});return{collectedRelationships:r,processedUrlDetails:n,flaggedCandidateUrlCount:0}}async ensureFolderExists(e){try{let t=e.startsWith("/")?e.substring(1):e;if(t==="")return;this.app.vault.getAbstractFileByPath(t)||(await this.app.vault.createFolder(t),this.settings.verboseDebug&&console.log(`Folder created: ${t}`))}catch(t){console.error(`Error ensuring folder ${e} exists:`,t),new o.Notice(`Error creating folder: ${e}`)}}async ensureProcessorFile(e,t=!1,r=!0){var l;await this.ensureFolderExists(this.settings.processorsFolderPath);let{filePathName:i,originalNameAsAlias:n}=this.sanitizeNameForFilePathAndAlias(e),a=`${this.settings.processorsFolderPath.startsWith("/")?this.settings.processorsFolderPath.substring(1):this.settings.processorsFolderPath}/${i}.md`,c=this.app.vault.getAbstractFileByPath(a);if(!c)try{let d="";if(t){let g=r?"processor":"subprocessor",p=n.replace(/[:[\]",]/g,"");d=`---
tags: [${g}]
aliases: ["${p}"]
---
# ${n}
`}else d=`# ${n}
`;c=await this.app.vault.create(a,d)}catch(d){if((l=d.message)!=null&&l.toLowerCase().includes("file already exists")){if(await new Promise(g=>setTimeout(g,100)),c=this.app.vault.getAbstractFileByPath(a),!c&&(await new Promise(g=>setTimeout(g,500)),c=this.app.vault.getAbstractFileByPath(a),!c))return this.settings.verboseDebug&&console.warn(`Could not retrieve processor file ${a} after 'already exists' error. This is likely a temporary issue.`),null}else return console.error(`Error creating processor file ${a}:`,d),null}if(c&&t){let d=r?"processor":"subprocessor",g=n.replace(/[:[\]",]/g,"");await this.app.vault.process(c,p=>{let u=this.updateFrontmatter(p,{tags:[d],aliases:[g]},n);if(!u.trim().includes(`# ${n}`)){let f=u.indexOf(`
---`)>0?u.indexOf(`
---`,u.indexOf(`
---`)+3)+4:0,m=u.substring(f),h=u.substring(0,f);u=h+(h.endsWith(`
`)?"":`
`)+`# ${n}
`+m.trimStart()}return u})}return c}async updateProcessorFile(e,t,r,i){let n="Subprocessors",s=`| Subprocessor Entity Name | Processing Function | Location |
`;s+=`|---|---|---|
`,r.filter(f=>f.RelationshipType==="uses_subprocessor"&&f.PrimaryProcessor===t).forEach(f=>{let{filePathName:m,originalNameAsAlias:h}=this.sanitizeNameForFilePathAndAlias(f.SubprocessorName),w=h.replace(/\n/g," ").replace(/[\[\]()|]/g,""),y=this.settings.processorsFolderPath,P=encodeURI(`${y}/${m}.md`),b=`[${w}](${P})`,k=(f.ProcessingFunction||"N/A").replace(/\n/g,"<br>").replace(/\|/g,"\\|"),T=(f.Location||"N/A").replace(/\n/g,"<br>").replace(/\|/g,"\\|");s+=`| ${b} | ${k} | ${T} |
`});let c="Analysis Logs",{filePathName:l}=this.sanitizeNameForFilePathAndAlias(t),d=this.settings.analysisLogsFolderPath,g=`${l} Subprocessor Logs.md`,u=`
- ${`[[${d}/${g}|${t} Subprocessor Logs]]`}
`;await this.app.vault.process(e,f=>{let m=i?"processor":"subprocessor",h=this.updateFrontmatter(f,{tags:[m],aliases:[t.replace(/[:\[\],"]/g,"")]},t);if(!h.trim().includes(`# ${t}`)){let w=h.indexOf(`
---`)>0?h.indexOf(`
---`,h.indexOf(`
---`)+3)+4:0,y=h.substring(w),P=h.substring(0,w);h=P+(P.endsWith(`
`)?"":`
`)+`# ${t}
`+y.trimStart()}return h=this.ensureHeadingAndSection(h,n,s,null,null),h=this.ensureHeadingAndSection(h,c,u,null,null,!0),h})}async createOrUpdateSubprocessorFile(e,t,r){var l;await this.ensureFolderExists(this.settings.processorsFolderPath);let{filePathName:i,originalNameAsAlias:n}=this.sanitizeNameForFilePathAndAlias(e),a=`${this.settings.processorsFolderPath.startsWith("/")?this.settings.processorsFolderPath.substring(1):this.settings.processorsFolderPath}/${i}.md`,c=this.app.vault.getAbstractFileByPath(a);if(!c){let g=`---
tags: [subprocessor]
aliases: ["${n.replace(/[:\[\],"]/g,"")}"]
---
# ${n}
## Used By
`;try{c=await this.app.vault.create(a,g)}catch(p){if((l=p.message)!=null&&l.toLowerCase().includes("file already exists")){if(await new Promise(u=>setTimeout(u,100)),c=this.app.vault.getAbstractFileByPath(a),!c&&(await new Promise(u=>setTimeout(u,500)),c=this.app.vault.getAbstractFileByPath(a),!c)){this.settings.verboseDebug&&console.warn(`Could not retrieve subprocessor file ${a} after 'already exists' error. This is likely a temporary issue.`);return}}else{console.error(`Error creating subprocessor file ${a}:`,p);return}}}c&&await this.app.vault.process(c,d=>{let g=this.updateFrontmatter(d,{tags:["subprocessor"],aliases:[n.replace(/[:\[\],"]/g,"")]},n);if(!g.trim().includes(`# ${n}`)){let h=g.indexOf(`
---`)>0?g.indexOf(`
---`,g.indexOf(`
---`)+3)+4:0,w=g.substring(h),y=g.substring(0,h);g=y+(y.endsWith(`
`)?"":`
`)+`# ${n}
`+w.trimStart()}let p="Used By",u=this.extractClientTableRows(d),f=new Set(u);r.forEach(h=>{let{originalNameAsAlias:w}=this.sanitizeNameForFilePathAndAlias(h.PrimaryProcessor),y=w.replace(/\|/g,"\\|"),P=(h.ProcessingFunction||"N/A").replace(/\n/g,"<br>").replace(/\|/g,"\\|"),b=(h.Location||"N/A").replace(/\n/g,"<br>").replace(/\|/g,"\\|"),k=h.SourceURL.startsWith("http")?`[Source](${h.SourceURL})`:h.SourceURL,T=` ${y} | ${P} | ${b} | ${k} `;f.add(T)});let m=`| Primary Processor | Processing Function | Location | Source URL |
`;return m+=`|---|---|---|---|
`,f.forEach(h=>{m+=`|${h}|
`}),g=this.ensureHeadingAndSection(g,p,m,null,null),g})}updateFrontmatter(e,t,r){let i={},n=/^---\s*\n([\s\S]*?)\n---\s*\n/,s=e.match(n),a=e;if(s&&s[1]){try{s[1].split(`
`).forEach(d=>{let g=d.split(":");if(g.length>=2){let p=g[0].trim(),u=g.slice(1).join(":").trim();p==="tags"||p==="aliases"?u.startsWith("[")&&u.endsWith("]")?i[p]=u.substring(1,u.length-1).split(",").map(f=>f.trim().replace(/^["']|["']$/g,"")):i[p]=[u.replace(/^["']|["']$/g,"")]:i[p]=u.replace(/^["']|["']$/g,"")}})}catch(l){console.warn("ProcessorProcessor: Could not parse existing frontmatter, will overwrite relevant keys.",l),i={}}a=e.substring(s[0].length)}if(t.tags){let l=new Set(Array.isArray(i.tags)?i.tags.map(d=>String(d).toLowerCase()):[]);t.tags.forEach(d=>l.add(String(d).toLowerCase())),i.tags=Array.from(l)}if(t.aliases){let l=new Set(Array.isArray(i.aliases)?i.aliases.map(g=>String(g)):[]);t.aliases.forEach(g=>{let p=String(g).replace(/[:\[\],"]/g,"");p&&l.add(p)});let d=String(r).replace(/[:\[\],"]/g,"");d&&l.add(d),i.aliases=Array.from(l)}let c=`---
`;for(let l in i)i.hasOwnProperty(l)&&(Array.isArray(i[l])?i[l].length>0&&(c+=`${l}: [${i[l].map(d=>`"${d}"`).join(", ")}]
`):c+=`${l}: "${i[l]}"
`);return c+=`---
`,c===`---
---
`&&!s?a:c+a}async updateAnalysisLogPage(e,t,r,i){await this.ensureFolderExists(this.settings.analysisLogsFolderPath);let{filePathName:n}=this.sanitizeNameForFilePathAndAlias(e),s=this.settings.analysisLogsFolderPath,a=`${n} Subprocessor Logs.md`,c=`${s}/${a}`,l=this.formatResultsForObsidianLog(e,r,t,i);await this.writeResultsToObsidianNote(c,l,"ensure_exists_and_append",e)}ensureHeadingAndSection(e,t,r,i=null,n=null,s=!1){let a=new RegExp(`^(#+)\\s*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(\\s*\\n|$)`,"im"),c=e.match(a),l=`
## ${t}
${r.trim()}
`;if(i&&n){let d=e.indexOf(i),g=e.indexOf(n);if(d!==-1&&g!==-1&&d<g)return e.substring(0,d+i.length)+`
${r.trim()}
`+e.substring(g)}if(c){let d=c[1].length,g=new RegExp(`^#{1,${d}}\\s+.*(\\s*\\n|$)`,"im"),p=c.index+c[0].length,u=e.substring(p),f=e.length,m=u.match(g);return m&&(f=p+m.index),s?e.substring(0,f)+`
${r.trim()}
`+e.substring(f):e.substring(0,p)+`${r.trim()}
`+e.substring(f)}else return e.trimEnd()+`
`+l.trimStart()}formatResultsForObsidianLog(e,t,r,i=[]){let n=`
---
### Log Entry: ${new Date().toISOString()} for ${e}
`;return i.length>0&&(n+=`#### Proactive Deduplication Decisions (${i.length}):
`,i.forEach(s=>{n+=`- ${s}
`}),n+=`
`),n+=`#### Processed URLs (${r.length}):
`,r.length===0?n+=`- No URLs were processed.
`:(n+=`| URL | Title | Type | Verified List? | Current? | Extracted # | Reasoning |
`,n+=`|---|---|---|---|---|---|---|
`,r.forEach(s=>{let a=this.scrubHyperlinks(s.title||"N/A").substring(0,70),c=s.url.startsWith("http")?`[Link](${s.url})`:s.url,l=this.scrubHyperlinks(s.verificationReasoning||"N/A").substring(0,100);n+=`| ${c} | ${a}... | ${s.documentType||"N/A"} | ${s.isList?"Yes":"No"} | ${s.isCurrent?"Yes":"No"} | ${s.extractedSubprocessorsCount||0} | ${l}... |
`})),n+=`
`,n+=`#### Extracted Relationships (${t.length}):
`,t.length===0?n+=`- No new relationships were extracted in this run.
`:(n+=`| Primary Processor | Target Entity | Type | Function | Location | Source URL |
`,n+=`|---|---|---|---|---|---|
`,t.forEach(s=>{let a=this.scrubHyperlinks(s.SubprocessorName).substring(0,50),c=this.scrubHyperlinks(s.PrimaryProcessor).substring(0,50),l=this.scrubHyperlinks(s.ProcessingFunction).substring(0,70),d=this.scrubHyperlinks(s.Location).substring(0,50),g=s.SourceURL.startsWith("http")?`[Source](${s.SourceURL})`:s.SourceURL;n+=`| ${c} | ${a} | ${s.RelationshipType} | ${l}... | ${d}... | ${g} |
`})),n+=`
`,n}async writeResultsToObsidianNote(e,t,r="ensure_exists_and_append",i){var s;let n=this.app.vault.getAbstractFileByPath(e);if(!n&&(r==="ensure_exists_and_append"||r==="overwrite")){let a="";i&&(a+=`# Analysis Log: ${i}
`),a+=t;try{n=await this.app.vault.create(e,a),this.settings.verboseDebug&&console.log(`Log file created: ${e}`)}catch(c){if((s=c.message)!=null&&s.toLowerCase().includes("file already exists")){if(n=this.app.vault.getAbstractFileByPath(e),!n){console.error(`Failed to get log file ${e} after 'already exists' error.`);return}}else{console.error(`Error creating log file ${e}:`,c),new o.Notice(`Error creating log file: ${e}`);return}}if(n&&r==="ensure_exists_and_append")return}if(n)if(r==="overwrite"){let a="";i&&(a+=`# Analysis Log: ${i}
`),a+=t,await this.app.vault.modify(n,a),this.settings.verboseDebug&&console.log(`Log file overwritten: ${e}`)}else(r==="append"||r==="ensure_exists_and_append"&&n)&&(await this.app.vault.append(n,t),this.settings.verboseDebug&&console.log(`Content appended to log file: ${e}`));else r==="append"&&(new o.Notice(`Log file ${e} not found. Cannot append.`),this.settings.verboseDebug&&console.log(`Log file not found for append: ${e}`))}async getRightBrainAccessToken(e){let t=(e==null?void 0:e.clientId)||this.settings.rightbrainClientId,r=(e==null?void 0:e.clientSecret)||this.settings.rightbrainClientSecret,i=(e==null?void 0:e.oauthUrl)||this.settings.rightbrainOauth2Url;if(!t||!r)return new o.Notice("RightBrain Client ID or Secret not configured."),null;if(this._rbToken&&this._rbTokenExpiry>Date.now())return this.settings.verboseDebug&&console.log("Using cached RightBrain token."),this._rbToken;let n=`${i}/oauth2/token`,s=new URLSearchParams;s.append("grant_type","client_credentials");let a=`${t}:${r}`,l={Authorization:`Basic ${btoa(a)}`,"Content-Type":"application/x-www-form-urlencoded","User-Agent":`ObsidianProcessorProcessorPlugin/${this.manifest.version}`};try{this.settings.verboseDebug&&console.log("Requesting new RightBrain token.");let d=await(0,o.requestUrl)({url:n,method:"POST",headers:l,body:s.toString(),throw:!1});return d.status===200&&d.json&&d.json.access_token?(this.settings.verboseDebug&&console.log("Successfully obtained new RightBrain token."),this._rbToken=d.json.access_token,this._rbTokenExpiry=Date.now()+(d.json.expires_in||3600)*1e3-6e5,d.json.access_token):(console.error("ProcessorProcessor: Failed to get RightBrain token.",d.status,d.text),new o.Notice(`Failed to get RightBrain token: ${d.status}.`),this._rbToken=null,this._rbTokenExpiry=0,null)}catch(d){return console.error("ProcessorProcessor: Network error fetching RightBrain token:",d),new o.Notice("Network error fetching RightBrain token."),this._rbToken=null,this._rbTokenExpiry=0,null}}generateSearchQueries(e){let t=e.replace(/\b(?:inc\.?|llc\.?|ltd\.?|corp\.?|gmbh\.?|incorporated|limited|corporation)\b/gi,"").replace(/[,.]/g,"").trim();return[`"${t}" sub-processor list`,`"${t}" subprocessors`,`"${t}" data processing addendum exhibit`,`"${t}" DPA subprocessors`,`"${t}" third-party vendors`,`"${t}" service providers list`,`"${t}" trust center subprocessors`,`"${t}" legal subprocessors`]}async searchSerpApiForDpas(e,t,r){var a,c,l;if(!this.settings.serpApiKey)return new o.Notice("SerpAPI key not set. Cannot perform SerpAPI search."),[];let i=[],n=new Set,s=t.slice(0,Math.min(t.length,3));new o.Notice(`Searching SerpAPI for ${e} using ${s.length} queries...`,3e3);for(let d of s){if(i.length>=r&&r>0)break;let p=`https://serpapi.com/search?${new URLSearchParams({api_key:this.settings.serpApiKey,q:d,engine:"google",num:"10"}).toString()}`;try{let u=await(0,o.requestUrl)({url:p,method:"GET",throw:!1});if(u.status===200&&u.json&&u.json.organic_results){let f=u.json.organic_results;for(let m of f)if(m.link&&!n.has(m.link)){let h=m.link.toLowerCase(),w=((a=m.title)==null?void 0:a.toLowerCase())||"",y=((c=m.snippet)==null?void 0:c.toLowerCase())||"";if(C.some(b=>h.includes(b)||w.includes(b)||y.includes(b))&&(i.push({processorName:e,title:m.title||"No Title",url:m.link,snippet:m.snippet||"No Snippet",searchQuery:d,documentType:"serpapi_dpa_or_subprocessor_list_candidate"}),n.add(m.link),i.length>=r&&r>0))break}}else console.error(`SerpAPI error for query "${d}": ${u.status}`,(l=u.text)==null?void 0:l.substring(0,200)),new o.Notice(`SerpAPI query failed for "${d.substring(0,20)}...". Status: ${u.status}`)}catch(u){console.error(`Network error during SerpAPI search for query "${d}":`,u),new o.Notice(`Network error during SerpAPI search for "${d.substring(0,20)}...".`)}await new Promise(u=>setTimeout(u,500+Math.random()*300))}return this.settings.verboseDebug&&console.log(`SerpAPI search for ${e} found ${i.length} relevant candidates.`),i}getCompanyDomain(e){let t=e.toLowerCase();t=t.replace(/\b(?:inc\.?|llc\.?|ltd\.?|corp\.?|gmbh\.?)\b/g,"").trim(),t=t.replace(/[,.]/g,"");try{if(t.includes(".")&&!t.includes(" "))return new URL(t.startsWith("http")?t:`http://${t}`).hostname.replace(/^www\./,"")}catch(i){}return t.split(/\s+/).length>1?"":t}isValidUrl(e,t=""){if(!e||typeof e!="string")return!1;try{let r=new URL(e);if(!["http:","https:"].includes(r.protocol))return!1;if(t){let i=this.getCompanyDomain(t);i&&r.hostname.toLowerCase().includes(i.replace(/^www\./,""))}return!0}catch(r){return!1}}async extractUrlsFromDpaPage(e,t,r){var l;if(!this.settings.rightbrainVerifyUrlTaskId)return[];if(!await this.getRightBrainAccessToken())return[];let n=[],s="";try{let d=await(0,o.requestUrl)({url:e,method:"GET",throw:!1});if(d.status===200)s=d.text;else return[]}catch(d){return[]}if(!s)return[];let a=/<a\s+(?:[^>]*?\s+)?href="([^"]*)"/gi,c;for(;(c=a.exec(s))!==null;){let d=c[1].trim();if(d&&!d.startsWith("#")&&!d.startsWith("mailto:")&&!d.startsWith("javascript:"))try{let g=new URL(d,e).toString();if(this.isValidUrl(g,t)){let p=g.toLowerCase(),u=(((l=c[0].match(/>(.*?)</))==null?void 0:l[1])||"").toLowerCase();C.some(m=>p.includes(m)||u.includes(m))&&n.push({processorName:t,title:`Linked from: ${r||e}`,url:g,snippet:`Found on page: ${e}`,documentType:"linked_subprocessor_list_candidate",sourceDpaUrl:e})}}catch(g){}}return n}async callRightBrainTask(e,t,r){if(!e)return new o.Notice("RightBrain Task ID is missing for the call."),console.error("ProcessorProcessor: Attempted to call RightBrain task with no Task ID."),null;if(!this.settings.rightbrainOrgId||!this.settings.rightbrainProjectId)return new o.Notice("RightBrain Org ID or Project ID not set. Cannot call task."),console.error("ProcessorProcessor: RB OrgID or ProjectID missing for task call."),null;let i=`${this.settings.rightbrainApiUrl}/org/${this.settings.rightbrainOrgId}/project/${this.settings.rightbrainProjectId}/task/${e}/run`,n={Authorization:`Bearer ${r}`,"Content-Type":"application/json","User-Agent":`ObsidianProcessorProcessorPlugin/${this.manifest.version}`},s={task_input:t};this.settings.verboseDebug&&console.log(`[callRightBrainTask] Sending Request to Task ID ${e.substring(0,8)}... Payload:`,JSON.stringify(s,null,2));try{let a=await(0,o.requestUrl)({url:i,method:"POST",headers:n,body:JSON.stringify(s),throw:!1});return a.json&&(a.status===200||a.status===201)?(this.settings.verboseDebug&&console.log(`[callRightBrainTask] Success for Task ID ${e.substring(0,8)}... Full Response:`,JSON.stringify(a.json,null,2)),a.json):(new o.Notice(`RightBrain Task ${e.substring(0,8)}... failed: ${a.status}. Check console.`,7e3),console.error(`RB Task Call [${e}] Error: ${a.status}`,a.text?a.text.substring(0,1e3):"No body","Payload Sent:",s),null)}catch(a){return new o.Notice(`Network error calling RightBrain Task ${e.substring(0,8)}.... Check console.`,7e3),console.error(`RB Task Call [${e}] Network Error:`,a,"Payload Sent:",s),null}}async verifySubprocessorListUrl(e,t,r){if(!this.settings.rightbrainVerifyUrlTaskId)return new o.Notice("RightBrain Verify URL Task ID is not configured. Cannot verify URL."),null;let i={url_content:e,expected_processor_name:t};this.settings.verboseDebug&&console.log(`Verifying URL ${e} with RB Task ${this.settings.rightbrainVerifyUrlTaskId}. Input:`,JSON.stringify(i));let n=await this.callRightBrainTask(this.settings.rightbrainVerifyUrlTaskId,i,r);if(this.settings.verboseDebug&&console.log(`RB Verify Task [${this.settings.rightbrainVerifyUrlTaskId}] Full Result for URL ${e}:`,JSON.stringify(n,null,2)),n&&typeof n.response=="object"&&n.response!==null){let s=n.response,a=String(s.isSubprocessorList).toLowerCase()==="true",c=String(s.isCorrectProcessor).toLowerCase()==="true",l=String(s.isCurrentVersion).toLowerCase()==="true",d=s.reasoning||"N/A",g;return n.run_data&&n.run_data.submitted&&typeof n.run_data.submitted.url_content=="string"&&n.run_data.submitted.url_content.toLowerCase().includes("<html")?(g=n.run_data.submitted.url_content,this.settings.verboseDebug&&console.log("Retrieved pageContent from run_data.submitted.url_content for verify task")):typeof s.fetched_page_html=="string"?(g=s.fetched_page_html,this.settings.verboseDebug&&console.log("Retrieved pageContent from rbResponse.fetched_page_html for verify task")):typeof s.page_content=="string"&&(g=s.page_content,this.settings.verboseDebug&&console.log("Retrieved pageContent from rbResponse.page_content (fallback) for verify task")),this.settings.verboseDebug&&console.log(`RB Verify for ${e}: List=${a}, Current=${l}, Content available: ${!!g}, Content snippet: ${g?g.substring(0,100)+"...":"N/A"}`),{isList:a,isCurrent:a&&l,isCorrectProcessor:c,reasoning:d,pageContent:g}}return this.settings.verboseDebug&&console.warn(`RB Verify task for ${e} failed or returned unexpected response format. TaskResult:`,n),null}async extractEntitiesFromPageContent(e,t){if(!this.settings.rightbrainExtractEntitiesTaskId)return new o.Notice("RB Extract Entities Task ID missing. Cannot extract from content."),null;if(!e.trim())return{thirdPartySubprocessors:[],ownEntities:[]};let r={[this.settings.rightbrainExtractInputField]:e},i=await this.callRightBrainTask(this.settings.rightbrainExtractEntitiesTaskId,r,t);if(i&&typeof i.response=="object"&&i.response!==null){let n=i.response,s=n[this.settings.rightbrainExtractOutputThirdPartyField]||[],a=n[this.settings.rightbrainExtractOutputOwnEntitiesField]||[];return{thirdPartySubprocessors:Array.isArray(s)?s:[],ownEntities:Array.isArray(a)?a:[]}}return null}async updateDiscoveryStatus(e,t){e&&await this.app.vault.process(e,r=>{let i={"discovery-status":t};return t==="complete"&&(i["last-discovered"]=new Date().toISOString().split("T")[0]),this.updateFrontmatter(r,i,e.basename)})}async buildAliasMap(){var r;let e=new Map,t=this.app.vault.getAbstractFileByPath(this.settings.processorsFolderPath);if(!(t!=null&&t.children))return e;for(let i of t.children)if(i instanceof o.TFile&&i.extension==="md"){let n=this.app.metadataCache.getFileCache(i),s=(n==null?void 0:n.frontmatter)||{},a=((r=s.aliases)==null?void 0:r[0])||i.basename,c=(s.aliases||[]).map(l=>String(l).toLowerCase());c.push(i.basename.toLowerCase());for(let l of new Set(c))l&&e.set(l,{path:i.path,canonicalName:a})}return e}async updateRightBrainTask(e,t,r,i,n){var c;let s=`${n.apiUrl}/org/${n.orgId}/project/${n.projectId}/task/${t}`,a={Authorization:`Bearer ${e}`,"Content-Type":"application/json"};try{this.settings.verboseDebug&&console.log(`Creating revision for task '${i}' with payload:`,JSON.stringify(r,null,2));let l=await(0,o.requestUrl)({url:s,method:"POST",headers:a,body:JSON.stringify(r),throw:!1});if(l.status!==200)return new o.Notice(`Error creating task revision for '${i}': ${l.status} ${l.text.substring(0,100)}`,1e4),console.error(`Error creating task revision for '${i}':`,l.status,l.text),null;this.settings.verboseDebug&&console.log(`Successfully created revision for '${i}'. Response:`,JSON.stringify(l.json,null,2));let d=l.json,g=(c=d.revisions)==null?void 0:c.sort((f,m)=>new Date(m.created_at).getTime()-new Date(f.created_at).getTime())[0];if(!g)return new o.Notice(`Could not find the new revision for '${i}' in the API response.`,7e3),console.error("Could not find 'revisions' array in the response from creating a revision:",d),null;let p={active_revisions:[{task_revision_id:g.id,weight:1}]},u=await(0,o.requestUrl)({url:s,method:"POST",headers:a,body:JSON.stringify(p),throw:!1});return u.status===200?(new o.Notice(`Successfully updated and activated task: '${i}'`),u.json):(new o.Notice(`Failed to activate new revision for '${i}': ${u.status}`,7e3),console.error(`Error activating revision for '${i}':`,u.status,u.text),null)}catch(l){return console.error(`Network error updating task '${i}':`,l),null}}async runDeduplicationForFolder(e){if(new o.Notice(`Preparing to deduplicate pages in ${e.path}...`),!this.settings.rightbrainDeduplicateSubprocessorsTaskId){new o.Notice("Deduplication Task ID not set. Cannot proceed.");return}let t=await this.getRightBrainAccessToken();if(!t){new o.Notice("Could not get RightBrain token for deduplication.");return}let r=e.children.filter(a=>a instanceof o.TFile&&a.extension==="md");if(r.length<2){new o.Notice("Not enough Markdown files in the folder to perform deduplication.");return}let i=[];for(let a of r){let c=this.app.metadataCache.getFileCache(a),l=c==null?void 0:c.frontmatter,d=l!=null&&l.aliases&&Array.isArray(l.aliases)?l.aliases.map(String):[];l!=null&&l.company_name&&d.push(String(l.company_name)),d.push(a.basename),i.push({file_path:a.path,page_name:a.basename,aliases:Array.from(new Set(d.filter(g=>g)))})}if(i.length<2){new o.Notice("Not enough processable pages with aliases found for deduplication.");return}let n={subprocessor_pages:i};new o.Notice(`Sending ${i.length} pages to RightBrain for deduplication analysis... This may take a while.`);let s=await this.callRightBrainTask(this.settings.rightbrainDeduplicateSubprocessorsTaskId,n,t);if(s&&s.response&&Array.isArray(s.response.deduplication_results)){let a=s.response.deduplication_results;if(a.length===0){new o.Notice("No duplicates found by RightBrain task.");return}new o.Notice(`Deduplication analysis complete. Found ${a.length} potential duplicate sets. Processing merges...`),await this.processDeduplicationResults(a)}else new o.Notice("Deduplication task failed or returned an unexpected response. Check console."),console.error("Deduplication task error. Response:",s)}async processDeduplicationResults(e){var r,i,n;let t=0;for(let s of e){if(!s.survivor_file_path||s.duplicate_file_paths.length===0){this.settings.verboseDebug&&console.warn("Skipping invalid deduplication result set:",s);continue}let a=this.app.vault.getAbstractFileByPath(s.survivor_file_path);if(!a){this.settings.verboseDebug&&console.warn(`Survivor file not found: ${s.survivor_file_path}`);continue}let c=await this.app.vault.read(a),l=this.app.metadataCache.getFileCache(a),d=new Set((((r=l==null?void 0:l.frontmatter)==null?void 0:r.aliases)||[]).map(String));d.add(a.basename);let g=new Set(this.extractClientTableRows(c)),p=`${this.settings.processorsFolderPath}/_Archive`;await this.ensureFolderExists(p);let u=[];for(let S of s.duplicate_file_paths){if(S===a.path)continue;let R=this.app.vault.getAbstractFileByPath(S);if(R){let G=await this.app.vault.read(R),L=this.app.metadataCache.getFileCache(R);(((i=L==null?void 0:L.frontmatter)==null?void 0:i.aliases)||[]).map(String).forEach(D=>d.add(D)),d.add(R.basename),this.extractClientTableRows(G).forEach(D=>g.add(D));try{let D=`${p}/${R.name}`,V=1;for(;this.app.vault.getAbstractFileByPath(D);){let H=R.basename,W=R.extension;D=`${p}/${H} (v${V}).${W}`,V++}await this.app.vault.rename(R,D),u.push({originalPath:S,archivedPath:D})}catch(D){console.error(`Failed to move duplicate file ${S} to archive:`,D)}}}let f=/^---\s*\n([\s\S]*?)\n---\s*\n/,m=c.match(f),h=m?c.substring(m[0].length):c,w=new Set((((n=l==null?void 0:l.frontmatter)==null?void 0:n.tags)||[]).map(String));w.add("merged-processor");let y=`---
`;y+=`aliases: [${Array.from(d).map(S=>`"${S.replace(/"/g,'\\"')}"`).join(", ")}]
`,y+=`tags: [${Array.from(w).map(S=>`"${S}"`).join(", ")}]
`,y+=`---
`;let P="";g.size>0&&(P+=`| Primary Processor | Processing Function | Location | Source URL |
`,P+=`|---|---|---|---|
`,g.forEach(S=>{P+=`|${S}|
`}));let b=this.ensureHeadingAndSection(h,"Used By",P,null,null),k=y+b,T=new Date().toISOString(),$=`${this.settings.analysisLogsFolderPath}/${this.sanitizeNameForFilePathAndAlias(a.basename).filePathName} Subprocessor Logs.md`,_=`
---
### Deduplication Merge Event
**Date:** ${T}
**Survivor:** [[${a.path}|${a.basename}]]
**RightBrain Reasoning:** ${s.reasoning||"No reasoning provided."}
**Archived Files (${s.duplicate_file_paths.length}):**
`;for(let S of s.duplicate_file_paths){let R=`${p}/${S.split("/").pop()}`;_+=`- [[${R}]]
`}await this.writeResultsToObsidianNote($,_,"ensure_exists_and_append",a.basename);let E=`
<details>
<summary>Merge History</summary>
This note was the result of an automated deduplication event on ${new Date().toLocaleDateString()}.
- **RightBrain Reasoning:** ${s.reasoning||"N/A"}
- For a full audit, see the [[${$}|Analysis Log]].
</details>
`;if(k+=`
${E}`,await this.app.vault.modify(a,k),u.length>0){let S=new Map;u.forEach(({originalPath:R})=>{S.set(R,a.path)}),await this.updateFileReferences(u,S)}t++,new o.Notice(`Merged ${s.duplicate_file_paths.length} duplicate(s) into ${a.basename}.`)}t>0?new o.Notice(`Deduplication finished. ${t} merge operations performed.`):new o.Notice("Deduplication process finished, but no actionable merges were made.")}async processManualMerge(e,t){var r,i,n;if(!e||t.length===0){new o.Notice("Merge cancelled: No survivor or duplicates selected.");return}new o.Notice(`Merging ${t.length} file(s) into ${e.basename}...`,6e3);try{let s=await this.app.vault.read(e),a=this.app.metadataCache.getFileCache(e),c=new Set((((r=a==null?void 0:a.frontmatter)==null?void 0:r.aliases)||[]).map(String));c.add(e.basename);let l=new Set(this.extractClientTableRows(s));for(let b of t){let k=await this.app.vault.read(b),T=this.app.metadataCache.getFileCache(b);(((i=T==null?void 0:T.frontmatter)==null?void 0:i.aliases)||[]).map(String).forEach($=>c.add($)),c.add(b.basename),this.extractClientTableRows(k).forEach($=>l.add($))}let d=/^---\s*\n([\s\S]*?)\n---\s*\n/,g=s.match(d),p=g?s.substring(g[0].length):s,u=new Set((((n=a==null?void 0:a.frontmatter)==null?void 0:n.tags)||[]).map(String)),f=`---
`;f+=`aliases: [${Array.from(c).map(b=>`"${b.replace(/"/g,'\\"')}"`).join(", ")}]
`,u.size>0&&(f+=`tags: [${Array.from(u).map(b=>`"${b}"`).join(", ")}]
`),f+=`---
`;let m="";l.size>0&&(m+=`| Primary Processor | Processing Function | Location | Source URL |
`,m+=`|---|---|---|---|
`,l.forEach(b=>{m+=`|${b}|
`}));let h=this.ensureHeadingAndSection(p,"Used By",m,null,null),w=f+h;await this.app.vault.modify(e,w);let y=`${this.settings.processorsFolderPath}/_Archive`;await this.ensureFolderExists(y);let P=[];for(let b of t)try{let k=`${y}/${b.name}`,T=1;for(;this.app.vault.getAbstractFileByPath(k);){let $=b.basename,_=b.extension;k=`${y}/${$} (v${T}).${_}`,T++}await this.app.vault.rename(b,k),P.push({originalPath:b.path,archivedPath:k})}catch(k){console.error(`Failed to archive duplicate file ${b.path}:`,k),await this.app.vault.delete(b)}if(P.length>0){let b=new Map;P.forEach(({originalPath:k})=>{b.set(k,e.path)}),await this.updateFileReferences(P,b)}new o.Notice(`Successfully merged ${t.length} file(s) into ${e.basename}.`)}catch(s){console.error("Error during manual merge:",s),new o.Notice("An error occurred during the merge. Check the developer console.")}}extractClientTableRows(e){let t=[],r=e.split(`
`),i=!1,n=!1;for(let s of r){if(s.match(/^##+\s*Used By\s*$/i)){i=!0,n=!1;continue}if(i){let a=s.trim();if(a.startsWith("##")){i=!1;break}if(a.match(/^\|---\|/)){n=!0;continue}if(n&&a.startsWith("|")&&a.endsWith("|")){let c=a.match(/^\|(.*)\|$/);c&&c[1]&&(c[1].match(/^---\|/)||t.push(c[1]))}else if(n&&a!=="")break}}return t}async updateFileReferences(e,t){var i,n;let r=this.app.vault.getMarkdownFiles();for(let s of r){let a=await this.app.vault.read(s),c=!1;for(let{originalPath:l,archivedPath:d}of e){let g=((i=l.split("/").pop())==null?void 0:i.replace(".md",""))||"",p=((n=d.split("/").pop())==null?void 0:n.replace(".md",""))||"",u=new RegExp(`\\[\\[${l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(\\|[^\\]]*)?\\]\\]`,"g");u.test(a)&&(a=a.replace(u,(h,w)=>{let y=`[[${d}${w||""}]]`;return c=!0,y}));let f=new RegExp(`\\[([^\\]]+)\\]\\(${l.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\)`,"g");f.test(a)&&(a=a.replace(f,(h,w)=>{let y=t==null?void 0:t.get(l);if(y){let P=`[${w}](${y})`;return c=!0,P}else{let P=`[${w} (archived)](${d})`;return c=!0,P}}));let m=new RegExp(`\\b${g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`,"g");m.test(a)&&(a=a.replace(m,h=>(c=!0,`${h} (archived)`)))}c&&await this.app.vault.modify(s,a)}}async discoverRecursively(e,t,r=3){var c,l;new o.Notice(`Starting smart recursive discovery for: ${e}. Max depth: ${r}`,1e4);let i=await this.buildAliasMap();this.processedInCurrentRecursiveSearch=new Set;let n=[{processorName:e,depth:0}],s=0,a=0;for(;n.length>0;){let d=n.shift();if(!d)continue;let{processorName:g,depth:p}=d,u=i.get(g.toLowerCase()),f=u?this.app.vault.getAbstractFileByPath(u.path):null;if(f){let y=this.app.metadataCache.getFileCache(f);if(((c=y==null?void 0:y.frontmatter)==null?void 0:c["discovery-status"])==="complete"&&((l=y==null?void 0:y.frontmatter)!=null&&l["last-discovered"])){let P=new Date(y.frontmatter["last-discovered"]),b=new Date;if(b.setDate(b.getDate()-this.settings.discoveryCacheDays),P>b){this.settings.verboseDebug&&console.log(`Skipping recently processed: ${g}`),a++;continue}}}new o.Notice(`Recursive (depth ${p}): Processing ${g}...`);let{filePathName:m}=this.sanitizeNameForFilePathAndAlias(g);if(this.processedInCurrentRecursiveSearch.has(m))continue;this.processedInCurrentRecursiveSearch.add(m);let h=p===0;if(f||(f=await this.ensureProcessorFile(g,!0,h)),!f)continue;s++;let w=await this.fetchProcessorSearchDataWithDiscovery(g);if(w!=null&&w.collectedRelationships){let y=Array.from(new Set(w.collectedRelationships.filter(b=>b.PrimaryProcessor===g&&b.RelationshipType==="uses_subprocessor").map(b=>b.SubprocessorName.trim()).filter(b=>b))),P=[];if(p<r-1)for(let b of y){let k=this.sanitizeNameForFilePathAndAlias(b).filePathName;if(this.processedInCurrentRecursiveSearch.has(k))continue;let T=i.get(b.toLowerCase()),$=b;if(T){if($=T.canonicalName,b!==$){let _=`Mapped discovered name "${b}" to existing processor "${$}".`;P.push(_)}}else{let{filePathName:_,originalNameAsAlias:E}=this.sanitizeNameForFilePathAndAlias(b),S=`${this.settings.processorsFolderPath}/${_}.md`;i.set(b.toLowerCase(),{path:S,canonicalName:E})}n.some(_=>_.processorName===$)||n.push({processorName:$,depth:p+1})}await this.persistSubprocessorInfo(g,f,w,h,P),await this.updateDiscoveryStatus(f,"complete")}else await this.updateDiscoveryStatus(f,"incomplete");await new Promise(y=>setTimeout(y,500))}new o.Notice(`Recursive discovery complete. Processed ${s} entities, skipped ${a} recent ones.`,1e4),this.processedInCurrentRecursiveSearch.clear()}openFileSelectorMergeModal(){let e=this.app.vault.getMarkdownFiles().filter(t=>t.path.startsWith((0,o.normalizePath)(this.settings.processorsFolderPath)+"/"));if(e.length<2){new o.Notice("There are not enough processor files to perform a merge.");return}new O(this.app,e,t=>{new N(this.app,t,(r,i)=>{this.processManualMerge(r,i)}).open()}).open()}async applyRecommendedGraphSettings(){let e={"collapse-filter":!0,search:`path:"${this.settings.processorsFolderPath}" -path:"${this.settings.processorsFolderPath}/_Archive"`,showTags:!1,showAttachments:!1,hideUnresolved:!0,showOrphans:!1,"collapse-color-groups":!0,colorGroups:[{query:"tag:#processor",color:{a:1,rgb:14025728}},{query:"tag:#subprocessor",color:{a:1,rgb:6084182}},{query:"tag:#merged-processor",color:{a:1,rgb:6069962}}],"collapse-display":!1,showArrow:!0,textFadeMultiplier:-2.3,nodeSizeMultiplier:1.2,lineSizeMultiplier:1,"collapse-forces":!1,centerStrength:.5,repelStrength:12,linkStrength:1,linkDistance:250,scale:.5,close:!1},t=this.app.vault.configDir+"/graph.json";try{await this.app.vault.adapter.write(t,JSON.stringify(e,null,2)),new o.Notice("Recommended graph settings have been applied. Please reopen the graph view to see the changes.")}catch(r){console.error("Failed to write graph settings:",r),new o.Notice("Error: Could not apply graph settings.")}}},x=class extends o.Modal{constructor(t,r,i){super(t);this.processorName="";this.listUrl="";this.isPrimaryProcessor=!0;this.onSubmit=r,this.initialProcessorName=i,this.initialProcessorName&&(this.processorName=this.initialProcessorName)}onOpen(){let{contentEl:t}=this;t.createEl("h2",{text:"Manually Add Subprocessor List URL"}),new o.Setting(t).setName("Processor name").setDesc("Enter the name of the primary processor (e.g., OpenAI).").addText(r=>{r.setPlaceholder("Enter processor name").setValue(this.processorName).onChange(i=>this.processorName=i).inputEl.setAttr("required","true"),this.initialProcessorName&&r.setDisabled(!0)}),new o.Setting(t).setName("Subprocessor list URL").setDesc("Enter the direct URL to the subprocessor list or DPA page.").addText(r=>r.setPlaceholder("https://example.com/subprocessors").setValue(this.listUrl).onChange(i=>this.listUrl=i).inputEl.setAttr("required","true")),new o.Setting(t).setName("Is a primary processor?").setDesc("Enable this if you are initiating a search on this processor. Disable if you are adding a subprocessor of another entity.").addToggle(r=>r.setValue(this.isPrimaryProcessor).onChange(i=>this.isPrimaryProcessor=i)),new o.Setting(t).addButton(r=>r.setButtonText("Process URL").setCta().onClick(()=>{this.close(),this.onSubmit(this.processorName,this.listUrl,this.isPrimaryProcessor)}))}onClose(){this.contentEl.empty()}},I=class extends o.Modal{constructor(t,r,i){super(t);this.processorName="";this.settings=r,this.onSubmit=i}onOpen(){let{contentEl:t}=this;t.createEl("h2",{text:"Discover Subprocessors"});let r="Search will be performed using available configured methods.";this.settings.serpApiKey?r="Search will primarily use SerpAPI.":this.settings.rightbrainOrgId&&this.settings.rightbrainProjectId&&this.settings.rightbrainDuckDuckGoSearchTaskId?r="SerpAPI key not found. Search will use DuckDuckGo via RightBrain.":r="Neither SerpAPI nor RightBrain DuckDuckGo search is fully configured. Discovery might be limited.",t.createEl("p",{text:r}),new o.Setting(t).setName("Processor name").setDesc("Enter the name of the processor to search for (e.g., Stripe).").addText(i=>i.setPlaceholder("Enter processor name").setValue(this.processorName).onChange(n=>this.processorName=n).inputEl.setAttr("required","true")),new o.Setting(t).addButton(i=>i.setButtonText("Start Discovery").setCta().onClick(()=>{if(!this.processorName.trim()){new o.Notice("Processor Name is required.");return}this.close(),this.onSubmit(this.processorName)}))}onClose(){this.contentEl.empty()}},M=class extends o.Modal{constructor(t,r,i){super(t);this.processorName="";this.pastedText="";this.isPrimaryProcessor=!0;this.onSubmit=r,this.initialProcessorName=i,this.initialProcessorName&&(this.processorName=this.initialProcessorName)}onOpen(){let{contentEl:t}=this;t.createEl("h2",{text:"Input Subprocessor List from Text"}),new o.Setting(t).setName("Processor Name").setDesc("Enter the name of the primary processor this text belongs to.").addText(i=>{i.setPlaceholder("Enter processor name").setValue(this.processorName).onChange(n=>this.processorName=n).inputEl.setAttr("required","true"),this.initialProcessorName&&i.setDisabled(!0)}),new o.Setting(t).setName("Is a primary processor?").setDesc("Enable this if you are initiating a search on this processor. Disable if you are adding a subprocessor of another entity.").addToggle(i=>i.setValue(this.isPrimaryProcessor).onChange(n=>this.isPrimaryProcessor=n)),t.createEl("p",{text:"Paste the subprocessor list text below:"});let r=new o.TextAreaComponent(t).setPlaceholder("Paste text here...").setValue(this.pastedText).onChange(i=>this.pastedText=i);r.inputEl.rows=10,r.inputEl.addClass("processor-textarea"),r.inputEl.setAttr("required","true"),new o.Setting(t).addButton(i=>i.setButtonText("Process Text").setCta().onClick(()=>{this.close(),this.onSubmit(this.processorName,this.pastedText,this.isPrimaryProcessor)}))}onClose(){this.contentEl.empty()}},N=class extends o.Modal{constructor(t,r,i){super(t);this.survivor=null;this.files=r.sort((n,s)=>n.basename.localeCompare(s.basename)),this.onSubmit=i}onOpen(){let{contentEl:t}=this;t.createEl("h2",{text:"Force Merge Processors"}),t.createEl("p",{text:'Select the file to keep (the "survivor"). All other selected files will be merged into it and then deleted.'});let r,i=t.createDiv();this.files.forEach(n=>{let s=new o.Setting(i).setName(n.basename).setDesc(n.path),a=createEl("input",{type:"radio",cls:"force-merge-radio"});a.name="survivor-selection",a.value=n.path,a.onchange=()=>{this.survivor=n,r.setDisabled(!1).setCta()},s.controlEl.appendChild(a)}),new o.Setting(t).addButton(n=>n.setButtonText("Cancel").onClick(()=>this.close())).addButton(n=>{r=n,n.setButtonText("Merge").setDisabled(!0).onClick(()=>{if(this.survivor){let s=this.files.filter(a=>a.path!==this.survivor.path);this.close(),this.onSubmit(this.survivor,s)}})})}onClose(){this.contentEl.empty()}},O=class extends o.Modal{constructor(t,r,i){super(t);this.selectedFilePaths=new Set;this.files=r.sort((n,s)=>n.basename.localeCompare(s.basename)),this.onSubmit=i}onOpen(){let{contentEl:t}=this;t.createEl("h2",{text:"Select Files to Merge"}),t.createEl("p",{text:"Choose two or more processor files from the list below."});let r,i=t.createDiv();i.addClass("processor-file-selector-list"),this.files.forEach(n=>{new o.Setting(i).setName(n.basename).setDesc(n.path).addToggle(a=>{a.onChange(c=>{c?this.selectedFilePaths.add(n.path):this.selectedFilePaths.delete(n.path),r.setDisabled(this.selectedFilePaths.size<2)})})}),new o.Setting(t).addButton(n=>n.setButtonText("Cancel").onClick(()=>this.close())).addButton(n=>{r=n,n.setButtonText("Next: Select Survivor").setCta().setDisabled(!0).onClick(()=>{let s=this.files.filter(a=>this.selectedFilePaths.has(a.path));this.close(),this.onSubmit(s)})})}},A=class extends o.Modal{constructor(t,r,i,n){var a;super(t);this.selectedModelId="";this.availableModels=[];this.plugin=r,this.availableModels=i,this.onSubmit=n;let s=i.find(c=>c.alias.toLowerCase().includes("gemini")&&c.alias.toLowerCase().includes("flash"));this.selectedModelId=(s==null?void 0:s.id)||((a=i[0])==null?void 0:a.id)||""}onOpen(){let{contentEl:t}=this;t.createEl("h2",{text:"Select Default Model"}),t.createEl("p",{text:"Choose a default AI model for all tasks. We recommend Gemini 2.5 Flash for cost-effectiveness and good performance. You can change individual task models later in the settings."});let r=t.createDiv();r.addClass("model-selection-group"),this.availableModels.forEach(i=>{let n=new o.Setting(r).setName(i.alias).setDesc(this.getModelDescription(i.alias)),s=createEl("input",{type:"radio",cls:"model-selection-radio"});s.name="model-selection",s.value=i.id,s.checked=i.id===this.selectedModelId,s.onchange=()=>{this.selectedModelId=i.id},n.controlEl.appendChild(s)}),new o.Setting(t).addButton(i=>i.setButtonText("Cancel").onClick(()=>this.close())).addButton(i=>i.setButtonText("Continue with Setup").setCta().onClick(async()=>{this.selectedModelId?(this.close(),await this.onSubmit(this.selectedModelId)):new o.Notice("Please select a model to continue.")}))}getModelDescription(t){let r=t.toLowerCase();return r.includes("gemini")&&r.includes("flash")?"Recommended: Fast, cost-effective, good for most tasks":r.includes("gemini")&&r.includes("pro")?"High performance, higher cost":r.includes("claude")?"Excellent reasoning, good for complex tasks":r.includes("gpt")?"Reliable performance, widely used":"AI model for task processing"}onClose(){this.contentEl.empty()}},j=class extends o.Modal{constructor(t,r){super(t);this.pastedText="";this.plugin=r}onOpen(){let{contentEl:t}=this;t.createEl("h2",{text:"Complete Plugin Setup"}),t.createEl("p",{text:"Paste the entire block of environment variables from your RightBrain dashboard below. This will save your credentials and then automatically create the necessary AI tasks in your project."});let r=new o.TextAreaComponent(t).setPlaceholder(`RB_ORG_ID="..."
RB_PROJECT_ID="..."`).onChange(i=>this.pastedText=i);r.inputEl.rows=12,r.inputEl.addClass("processor-textarea"),r.inputEl.addClass("processor-monospace"),new o.Setting(t).addButton(i=>i.setButtonText("Begin Setup").setCta().onClick(()=>{this.pastedText.trim()?(this.runFullSetup(),this.close()):new o.Notice("Text area is empty.")}))}onClose(){this.contentEl.empty()}async runFullSetup(){let t=this.pastedText.trim().split(`
`),r={},i={RB_ORG_ID:"rightbrainOrgId",RB_PROJECT_ID:"rightbrainProjectId",RB_CLIENT_ID:"rightbrainClientId",RB_CLIENT_SECRET:"rightbrainClientSecret",RB_API_URL:"rightbrainApiUrl",RB_OAUTH2_URL:"rightbrainOauth2Url"};for(let n of t){let s=n.split("=");if(s.length<2)continue;let a=s[0].trim(),c=s.slice(1).join("=").trim().replace(/["']/g,"");if(a in i&&c){let l=i[a];r[l]=c}}if(!r.rightbrainOrgId||!r.rightbrainProjectId||!r.rightbrainClientId||!r.rightbrainClientSecret||!r.rightbrainApiUrl||!r.rightbrainOauth2Url){new o.Notice("Setup failed. Pasted text is missing one or more required values.",7e3);return}if(this.plugin.settings=Object.assign(this.plugin.settings,r),await this.plugin.saveSettings(),new o.Notice("Credentials saved."),await new Promise(n=>setTimeout(n,1e3)),new o.Notice("Fetching available models..."),await this.plugin.updateLlmModelList(!0),this.plugin.settings.llmModelList.length===0){new o.Notice("Failed to fetch available models. Please check your credentials and try again.",7e3);return}new A(this.app,this.plugin,this.plugin.settings.llmModelList,async n=>{await this.completeSetupWithModel(n,r)}).open()}async completeSetupWithModel(t,r){var i;new o.Notice(`Selected model: ${((i=this.plugin.settings.llmModelList.find(n=>n.id===t))==null?void 0:i.alias)||t}`),await this.plugin.setupRightBrainTasksWithModel({apiUrl:r.rightbrainApiUrl,oauthUrl:r.rightbrainOauth2Url,clientId:r.rightbrainClientId,clientSecret:r.rightbrainClientSecret,orgId:r.rightbrainOrgId,projectId:r.rightbrainProjectId},t),await this.plugin.applyRecommendedGraphSettings()}},z=class extends o.PluginSettingTab{constructor(e,t){super(e,t),this.plugin=t}async display(){let{containerEl:e}=this;e.empty(),new o.Setting(e).setName("Processor Processor Settings").setHeading(),new o.Setting(e).setName("API Keys & Credentials").setHeading(),new o.Setting(e).setName("SerpAPI key").setDesc("Your SerpAPI Key for Google search functionality.").addText(s=>s.setPlaceholder("Enter your SerpAPI key").setValue(this.plugin.settings.serpApiKey).onChange(async a=>{this.plugin.settings.serpApiKey=a,await this.plugin.saveSettings()})),new o.Setting(e).setName("RightBrain Task Configuration").setHeading(),new o.Setting(e).setName("Automatically Synchronize Tasks on Load").setDesc("If enabled, the plugin will check for and apply updates from its local task definitions on startup. Disable this if you prefer to manage and customize your tasks directly in the RightBrain dashboard without them being overwritten.").addToggle(s=>s.setValue(this.plugin.settings.autoSynchronizeTasks).onChange(async a=>{this.plugin.settings.autoSynchronizeTasks=a,await this.plugin.saveSettings()})),new o.Setting(e).setName("RB Extract Entities: Input Field Name").setDesc('The parameter name your RB Extract Entities task expects for the input text (e.g., "page_text", "document_content").').addText(s=>s.setValue(this.plugin.settings.rightbrainExtractInputField).setPlaceholder("e.g., page_text").onChange(async a=>{this.plugin.settings.rightbrainExtractInputField=a,await this.plugin.saveSettings()})),new o.Setting(e).setName("RB Extract Entities: Output Field (Third-Party)").setDesc(`The field name in your RB Extract Entities task's JSON output for the list of third-party subprocessors (e.g., "third_party_subprocessors").`).addText(s=>s.setValue(this.plugin.settings.rightbrainExtractOutputThirdPartyField).setPlaceholder("e.g., third_party_subprocessors").onChange(async a=>{this.plugin.settings.rightbrainExtractOutputThirdPartyField=a,await this.plugin.saveSettings()})),new o.Setting(e).setName("RB Extract Entities: Output Field (Own Entities)").setDesc(`The field name in your RB Extract Entities task's JSON output for the list of own/affiliated entities (e.g., "own_entities").`).addText(s=>s.setValue(this.plugin.settings.rightbrainExtractOutputOwnEntitiesField).setPlaceholder("e.g., own_entities").onChange(async a=>{this.plugin.settings.rightbrainExtractOutputOwnEntitiesField=a,await this.plugin.saveSettings()})),new o.Setting(e).setName("General Settings").setHeading(),new o.Setting(e).setName("Create Pages for Own Entities").setDesc('If enabled, separate Markdown pages will also be created for "own entities" identified during processing, not just third-party subprocessors.').addToggle(s=>s.setValue(this.plugin.settings.createPagesForOwnEntities).onChange(async a=>{this.plugin.settings.createPagesForOwnEntities=a,await this.plugin.saveSettings()})),new o.Setting(e).setName("Verbose Debug Logging").setDesc("Enable detailed logging to the developer console for debugging purposes.").addToggle(s=>s.setValue(this.plugin.settings.verboseDebug).onChange(async a=>{this.plugin.settings.verboseDebug=a,await this.plugin.saveSettings()})),new o.Setting(e).setName("Max Results Per Processor (Discovery)").setDesc("Maximum search results to process for each processor during initial discovery. Currently, the logic stops on the first verified list, effectively making this 1.").addText(s=>s.setValue(this.plugin.settings.maxResultsPerProcessor.toString()).setDisabled(!0).onChange(async a=>{})),new o.Setting(e).setName("Mapping Depth").setDesc("Set the maximum depth for the Map Subprocessor Relationships function (e.g., 2-5). Higher numbers will take much longer and use more API calls.").addText(s=>s.setPlaceholder("e.g., 3").setValue(this.plugin.settings.maxRecursiveDepth.toString()).onChange(async a=>{let c=parseInt(a);!isNaN(c)&&c>0&&(this.plugin.settings.maxRecursiveDepth=c,await this.plugin.saveSettings())})),new o.Setting(e).setName("Discovery Cache Duration (Days)").setDesc(`How many days to consider a processor's data "fresh". A processor with a "complete" status discovered within this period will be skipped during recursive runs.`).addText(s=>s.setPlaceholder("e.g., 30").setValue(this.plugin.settings.discoveryCacheDays.toString()).onChange(async a=>{let c=parseInt(a);!isNaN(c)&&c>=0&&(this.plugin.settings.discoveryCacheDays=c,await this.plugin.saveSettings())})),new o.Setting(e).setName("Processors Folder Path").setDesc('Path to the folder where processor and subprocessor notes will be stored (e.g., "Third Parties/Processors").').addText(s=>s.setPlaceholder("e.g., Processors").setValue(this.plugin.settings.processorsFolderPath).onChange(async a=>{this.plugin.settings.processorsFolderPath=a||U.processorsFolderPath,await this.plugin.saveSettings()})),new o.Setting(e).setName("Analysis Logs Folder Path").setDesc('Path to the folder where analysis log notes for each processor will be stored (e.g., "Compliance/Logs").').addText(s=>s.setPlaceholder("e.g., Analysis Logs").setValue(this.plugin.settings.analysisLogsFolderPath).onChange(async a=>{this.plugin.settings.analysisLogsFolderPath=a||U.analysisLogsFolderPath,await this.plugin.saveSettings()})),new o.Setting(e).setName("RightBrain Model Configuration").setHeading(),new o.Setting(e).setDesc((()=>{let s=this.plugin.settings.rightbrainApiUrl||"",a=s.includes("stag")||s.includes("leftbrain")?"https://stag.leftbrain.me":"https://app.rightbrain.ai",c=new DocumentFragment;return c.appendText("You can configure basic RightBrain settings here, or log in to the "),c.createEl("a",{text:"Rightbrain Dashboard",href:a,attr:{target:"_blank",rel:"noopener noreferrer"}}),c.appendText(" for more fine-tuned control."),c})()),new o.Setting(e).setName("Refresh Model List").setDesc("Fetch the latest available LLM models from your RightBrain project. The list is automatically cached for 24 hours.").addButton(s=>s.setButtonText("Refresh Now").onClick(async()=>{await this.plugin.updateLlmModelList(!0),this.display()})),new o.Setting(e).setName("Synchronize Tasks with RightBrain").setDesc("Force synchronization of all tasks with RightBrain. This will apply any model changes and update task configurations.").addButton(s=>s.setButtonText("Sync Now").onClick(async()=>{new o.Notice("Synchronizing tasks with RightBrain..."),await this.plugin.synchronizeRightBrainTasks()}));let t=[{name:"Verify Subprocessor List URL",settingKey:"verifyUrlModelId"},{name:"Extract Entities From Page Content",settingKey:"extractEntitiesModelId"},{name:"Deduplicate Subprocessors",settingKey:"deduplicateSubprocessorsModelId"},{name:"DDG SERP Parser",settingKey:"duckDuckGoSearchModelId"},{name:"Find DPA URL",settingKey:"findDpaModelId"},{name:"Find ToS URL",settingKey:"findTosModelId"},{name:"Find Security Page URL",settingKey:"findSecurityModelId"}],r=this.plugin.settings.llmModelList,i=r.find(s=>s.alias.toLowerCase().includes("gemini 1.5 flash")),n=new Map;if(this.plugin.settings.rightbrainOrgId&&this.plugin.settings.rightbrainProjectId)try{let s=await this.plugin.getRightBrainAccessToken();if(s){let a={apiUrl:this.plugin.settings.rightbrainApiUrl,oauthUrl:this.plugin.settings.rightbrainOauth2Url,clientId:this.plugin.settings.rightbrainClientId,clientSecret:this.plugin.settings.rightbrainClientSecret,orgId:this.plugin.settings.rightbrainOrgId,projectId:this.plugin.settings.rightbrainProjectId},c=await this.plugin.listAllRightBrainTasks(s,a);if(c)for(let l of c){let d=l.task_revisions||l.revisions;if(d&&d.length>0){let g=d.sort((p,u)=>new Date(u.created_at).getTime()-new Date(p.created_at).getTime())[0];g.llm_model_id&&(n.set(l.name,g.llm_model_id),this.plugin.settings.verboseDebug&&console.log(`Found current model for task '${l.name}': ${g.llm_model_id}`))}}}}catch(s){console.warn("Could not fetch current task configurations:",s)}t.forEach(s=>{new o.Setting(e).setName(s.name).setDesc(`Select the LLM to use for the "${s.name}" task.`).addDropdown(a=>{if(r.length===0){a.addOption("","No models loaded. Refresh list or check credentials."),a.setDisabled(!0);return}r.forEach(l=>{a.addOption(l.id,l.alias)});let c=n.get(s.name)||this.plugin.settings[s.settingKey]||(i?i.id:r.length>0?r[0].id:"");this.plugin.settings.verboseDebug&&(console.log(`Setting dropdown for '${s.name}':`),console.log(` - From RightBrain: ${n.get(s.name)||"not found"}`),console.log(` - From saved setting: ${this.plugin.settings[s.settingKey]||"not set"}`),console.log(` - Final selection: ${c}`)),a.setValue(c),a.onChange(async l=>{this.plugin.settings[s.settingKey]=l,await this.plugin.saveSettings(),new o.Notice(`${s.name} will now use ${a.selectEl.options[a.selectEl.selectedIndex].text}.`),this.plugin.settings.autoSynchronizeTasks?(new o.Notice(`Applying model change for ${s.name} to RightBrain...`),setTimeout(()=>{this.plugin.synchronizeRightBrainTasks()},1e3)):new o.Notice(`Model changed for ${s.name}. Run "Synchronize Tasks with RightBrain" to apply changes.`)})})})}};