6
6
*/
7
7
8
8
import { readFileSync } from 'node:fs' ;
9
+ import { join } from 'node:path' ;
9
10
import { XMLParser } from 'fast-xml-parser' ;
10
11
import { Connection , Logger , SfError , trimTo15 } from '@salesforce/core' ;
11
- import type { BotVersion , GenAiPlanner , GenAiPlugin } from '@salesforce/types/metadata' ;
12
+ import type { BotVersion , GenAiPlanner , GenAiPlannerFunctionDef , GenAiPlugin } from '@salesforce/types/metadata' ;
12
13
import { ensureArray } from '@salesforce/kit' ;
13
14
import { RegistryAccess } from '../../registry' ;
14
15
import { ComponentSet } from '../../collections/componentSet' ;
15
- import { SourceComponent } from '../sourceComponent' ;
16
16
import { MetadataComponent } from '../types' ;
17
17
18
18
type BotVersionExt = {
@@ -61,6 +61,9 @@ export async function resolveAgentMdEntries(agentMdInfo: {
61
61
registry ?: RegistryAccess ;
62
62
} ) : Promise < string [ ] > {
63
63
const { botName, connection, directoryPaths } = agentMdInfo ;
64
+ const v63topLevelMd = [ 'Bot' , 'GenAiPlanner' , 'GenAiPlugin' , 'GenAiFunction' ] ;
65
+ const v64topLevelMd = [ 'Bot' , 'GenAiPlannerBundle' , 'GenAiPlugin' , 'GenAiFunction' ] ;
66
+
64
67
let debugMsg = `Resolving agent metadata with botName: ${ botName } ` ;
65
68
if ( connection ) {
66
69
debugMsg += ` and org connection ${ connection . getUsername ( ) as string } ` ;
@@ -72,7 +75,10 @@ export async function resolveAgentMdEntries(agentMdInfo: {
72
75
73
76
if ( botName === '*' ) {
74
77
// Get all Agent top level metadata
75
- return Promise . resolve ( [ 'Bot' , 'GenAiPlanner' , 'GenAiPlugin' , 'GenAiFunction' ] ) ;
78
+ if ( connection && Number ( connection . getApiVersion ( ) ) > 63.0 ) {
79
+ return Promise . resolve ( v64topLevelMd ) ;
80
+ }
81
+ return Promise . resolve ( v63topLevelMd ) ;
76
82
}
77
83
78
84
if ( connection ) {
@@ -99,7 +105,8 @@ const resolveAgentFromConnection = async (connection: Connection, botName: strin
99
105
const plannerId = ( await connection . singleRecordQuery < { Id : string } > ( genAiPlannerIdQuery , { tooling : true } ) ) . Id ;
100
106
101
107
if ( plannerId ) {
102
- mdEntries . push ( `GenAiPlanner:${ botName } ` ) ;
108
+ const plannerType = Number ( connection . getApiVersion ( ) ) > 63.0 ? 'GenAiPlannerBundle' : 'GenAiPlanner' ;
109
+ mdEntries . push ( `${ plannerType } :${ botName } ` ) ;
103
110
const plannerId15 = trimTo15 ( plannerId ) ;
104
111
// Query for the GenAiPlugins associated with the 15 char GenAiPlannerId
105
112
const genAiPluginNames = (
@@ -111,11 +118,11 @@ const resolveAgentFromConnection = async (connection: Connection, botName: strin
111
118
genAiPluginNames . map ( ( r ) => mdEntries . push ( `GenAiPlugin:${ r . DeveloperName } ` ) ) ;
112
119
} else {
113
120
getLogger ( ) . debug (
114
- `No GenAiPlugin metadata matches for plannerId: ${ plannerId15 } . Reading the planner metadata for plugins...`
121
+ `No GenAiPlugin metadata matches for plannerId: ${ plannerId15 } . Reading the ${ plannerType } metadata for plugins...`
115
122
) ;
116
123
// read the planner metadata from the org
117
124
// @ts -expect-error jsForce types don't know about GenAiPlanner yet
118
- const genAiPlannerMd = await connection . metadata . read < GenAiPlanner > ( 'GenAiPlanner' , botName ) ;
125
+ const genAiPlannerMd = await connection . metadata . read < GenAiPlanner > ( plannerType , botName ) ;
119
126
const genAiPlannerMdArr = ensureArray ( genAiPlannerMd ) as unknown as GenAiPlanner [ ] ;
120
127
if ( genAiPlannerMdArr ?. length && genAiPlannerMdArr [ 0 ] ?. genAiPlugins . length ) {
121
128
genAiPlannerMdArr [ 0 ] . genAiPlugins . map ( ( plugin ) => {
@@ -162,19 +169,20 @@ const resolveAgentFromLocalMetadata = (
162
169
}
163
170
const parser = new XMLParser ( { ignoreAttributes : false } ) ;
164
171
const botFiles = botCompSet . getComponentFilenamesByNameAndType ( { type : 'Bot' , fullName : botName } ) ;
165
- const plannerType = registry . getTypeByName ( 'GenAiPlanner' ) ;
172
+ let plannerType = registry . getTypeByName ( 'GenAiPlannerBundle' ) ;
173
+ let plannerTypeName = 'GenAiPlannerBundle' ;
166
174
let plannerCompSet = ComponentSet . fromSource ( {
167
175
fsPaths : directoryPaths ,
168
176
include : new ComponentSet ( [ { type : plannerType , fullName : botName } ] , registry ) ,
169
177
registry,
170
178
} ) ;
171
- // If the plannerCompSet is empty it might be due to the GenAiPlanner having a
179
+ // If the plannerCompSet is empty it might be due to the GenAiPlannerBundle having a
172
180
// different name than the Bot. We need to search the BotVersion for the
173
181
// planner API name.
174
182
if ( plannerCompSet . size < 1 ) {
175
183
const botVersionFile = botFiles . find ( ( botFile ) => botFile . endsWith ( '.botVersion-meta.xml' ) ) ;
176
184
if ( botVersionFile ) {
177
- getLogger ( ) . debug ( `Reading and parsing ${ botVersionFile } to find all GenAiPlanner references` ) ;
185
+ getLogger ( ) . debug ( `Reading and parsing ${ botVersionFile } to find all GenAiPlanner/GenAiPlannerBundle references` ) ;
178
186
const botVersionJson = xmlToJson < BotVersionExt > ( botVersionFile , parser ) ;
179
187
// Per the schema, there can be multiple GenAiPlanners linked to a BotVersion
180
188
// but I'm not sure how that would work so for now just using the first one.
@@ -186,36 +194,61 @@ const resolveAgentFromLocalMetadata = (
186
194
include : new ComponentSet ( [ { type : plannerType , fullName : genAiPlannerName } ] , registry ) ,
187
195
registry,
188
196
} ) ;
197
+ // If the plannerCompSet is empty look for a GenAiPlanner
189
198
if ( plannerCompSet . size < 1 ) {
190
- getLogger ( ) . debug ( `Cannot find GenAiPlanner with name: ${ genAiPlannerName } ` ) ;
199
+ plannerTypeName = 'GenAiPlanner' ;
200
+ plannerType = registry . getTypeByName ( 'GenAiPlanner' ) ;
201
+ plannerCompSet = ComponentSet . fromSource ( {
202
+ fsPaths : directoryPaths ,
203
+ include : new ComponentSet ( [ { type : plannerType , fullName : genAiPlannerName } ] , registry ) ,
204
+ registry,
205
+ } ) ;
206
+ if ( plannerCompSet . size < 1 ) {
207
+ getLogger ( ) . debug ( `Cannot find GenAiPlanner or GenAiPlannerBundle with name: ${ genAiPlannerName } ` ) ;
208
+ }
191
209
}
192
- getLogger ( ) . debug ( `Adding GenAiPlanner :${ genAiPlannerName } ` ) ;
193
- mdEntries . add ( `GenAiPlanner :${ genAiPlannerName } ` ) ;
210
+ getLogger ( ) . debug ( `Adding ${ plannerTypeName } :${ genAiPlannerName } ` ) ;
211
+ mdEntries . add ( `${ plannerTypeName } :${ genAiPlannerName } ` ) ;
194
212
} else {
195
213
getLogger ( ) . debug ( `Cannot find GenAiPlannerName in BotVersion file: ${ botVersionFile } ` ) ;
196
214
}
197
215
}
198
216
} else {
199
- getLogger ( ) . debug ( `Adding GenAiPlanner :${ botName } ` ) ;
200
- mdEntries . add ( `GenAiPlanner :${ botName } ` ) ;
217
+ getLogger ( ) . debug ( `Adding ${ plannerTypeName } :${ botName } ` ) ;
218
+ mdEntries . add ( `${ plannerTypeName } :${ botName } ` ) ;
201
219
}
202
220
203
- // Read the GenAiPlanner file for GenAiPlugins
204
- const plannerComp = plannerCompSet . find ( ( mdComp ) => mdComp . type . name === 'GenAiPlanner' ) ;
205
- if ( plannerComp && 'xml' in plannerComp ) {
206
- const plannerFile = ( plannerComp as SourceComponent ) . xml ;
221
+ // Read the GenAiPlanner or GenAiPlannerBundle file for GenAiPlugins
222
+ const plannerComp = plannerCompSet . getSourceComponents ( ) . first ( ) ;
223
+ if ( plannerComp ) {
224
+ let plannerFilePath ;
225
+ if ( plannerTypeName === 'GenAiPlannerBundle' && plannerComp . content ) {
226
+ const plannerFileName = plannerComp . tree
227
+ . readDirectory ( plannerComp . content )
228
+ . find ( ( p ) => p . endsWith ( '.genAiPlannerBundle' ) ) ;
229
+ if ( plannerFileName ) {
230
+ plannerFilePath = join ( plannerComp . content , plannerFileName ) ;
231
+ } else {
232
+ getLogger ( ) . debug ( `Cannot find GenAiPlannerBundle file in ${ plannerComp . content } ` ) ;
233
+ }
234
+ } else {
235
+ plannerFilePath = plannerComp . xml ;
236
+ }
237
+
207
238
// Certain internal plugins and functions cannot be retrieved/deployed so don't include them.
208
- const internalPrefix = 'EmployeeCopilot__' ;
209
- if ( plannerFile ) {
210
- getLogger ( ) . debug ( `Reading and parsing ${ plannerFile } to find all GenAiPlugin references` ) ;
211
- const plannerJson = xmlToJson < GenAiPlannerExt > ( plannerFile , parser ) ;
239
+ const internalPrefixes = [ 'EmployeeCopilot__' , 'SvcCopilotTmpl__' ] ;
240
+ if ( plannerFilePath ) {
241
+ getLogger ( ) . debug ( `Reading and parsing ${ plannerFilePath } to find all GenAiPlugin references` ) ;
242
+ const plannerJson = xmlToJson < GenAiPlannerExt > ( plannerFilePath , parser ) ;
212
243
213
244
// Add plugins defined in the planner
214
- const genAiPlugins = ensureArray ( plannerJson . GenAiPlanner . genAiPlugins ) ;
245
+ // @ts -expect-error temporary until GenAiPlannerBundle metadata type is defined
246
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
247
+ const genAiPlugins = ensureArray ( plannerJson [ plannerTypeName ] . genAiPlugins ) as GenAiPlannerFunctionDef [ ] ;
215
248
const pluginType = registry . getTypeByName ( 'GenAiPlugin' ) ;
216
249
const genAiPluginComps : MetadataComponent [ ] = [ ] ;
217
250
genAiPlugins ?. map ( ( plugin ) => {
218
- if ( plugin . genAiPluginName && ! plugin . genAiPluginName . startsWith ( internalPrefix ) ) {
251
+ if ( plugin . genAiPluginName && ! internalPrefixes . some ( ( prefix ) => plugin . genAiPluginName ? .startsWith ( prefix ) ) ) {
219
252
genAiPluginComps . push ( { type : pluginType , fullName : plugin . genAiPluginName } ) ;
220
253
getLogger ( ) . debug ( `Adding GenAiPlugin:${ plugin . genAiPluginName } ` ) ;
221
254
mdEntries . add ( `GenAiPlugin:${ plugin . genAiPluginName } ` ) ;
@@ -237,7 +270,7 @@ const resolveAgentFromLocalMetadata = (
237
270
const genAiPlugin = xmlToJson < GenAiPluginExt > ( comp . xml , parser ) ;
238
271
const genAiFunctions = ensureArray ( genAiPlugin . GenAiPlugin . genAiFunctions ) ;
239
272
genAiFunctions . map ( ( func ) => {
240
- if ( func . functionName && ! func . functionName . startsWith ( internalPrefix ) ) {
273
+ if ( func . functionName && ! internalPrefixes . some ( ( prefix ) => func . functionName . startsWith ( prefix ) ) ) {
241
274
getLogger ( ) . debug ( `Adding GenAiFunction:${ func . functionName } ` ) ;
242
275
mdEntries . add ( `GenAiFunction:${ func . functionName } ` ) ;
243
276
}
0 commit comments