Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,18 @@ export class CheckPropertiesFulfilledRouter<TState extends StateType<StateDefini
}

if (unfulfilledProperties.length > 0) {
this.logger.debug('Properties not fulfilled, routing to unfulfilled node', {
unfulfilledProperties,
targetNode: this.propertiesUnfulfilledNodeName,
totalRequired: Object.keys(this.requiredProperties).length,
});
this.logger.debug(
`Properties not fulfilled, routing to ${this.propertiesUnfulfilledNodeName}`,
{
unfulfilledProperties,
targetNode: this.propertiesUnfulfilledNodeName,
totalRequired: Object.keys(this.requiredProperties).length,
}
);
return this.propertiesUnfulfilledNodeName;
}

this.logger.debug('All properties fulfilled, routing to fulfilled node', {
this.logger.debug(`All properties fulfilled, routing to ${this.propertiesFulfilledNodeName}`, {
targetNode: this.propertiesFulfilledNodeName,
totalProperties: Object.keys(this.requiredProperties).length,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
WorkflowStateManager,
type Logger,
type WorkflowEnvironment,
createWorkflowLogger,
} from '@salesforce/magen-mcp-workflow';
import { mobileNativeWorkflow } from '../../../workflow/graph.js';
import { ORCHESTRATOR_TOOL } from './metadata.js';
Expand All @@ -24,6 +25,10 @@ import { ORCHESTRATOR_TOOL } from './metadata.js';
*/
export class MobileNativeOrchestrator extends OrchestratorTool {
constructor(server: McpServer, logger?: Logger, environment: WorkflowEnvironment = 'production') {
const mobileNativeOrchestratorToolLogger =
logger ?? createWorkflowLogger('MobileNativeOrchestratorTool');
const mobileNativeWorkflowStateManagerLogger =
logger ?? createWorkflowLogger('MobileNativeWorkflowStateManager');
const config: OrchestratorConfig = {
toolId: ORCHESTRATOR_TOOL.toolId,
title: 'Salesforce Mobile Native Project Manager',
Expand All @@ -32,9 +37,9 @@ export class MobileNativeOrchestrator extends OrchestratorTool {
workflow: mobileNativeWorkflow,
stateManager: new WorkflowStateManager({
environment,
logger,
logger: mobileNativeWorkflowStateManagerLogger,
}),
logger,
logger: mobileNativeOrchestratorToolLogger,
};

super(server, config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export class CheckAndroidSetupExtractedRouter {
execute = (state: State): string => {
// Check if both androidHome and javaHome were extracted
if (state.androidHome && state.javaHome) {
this.logger.info('Android setup successfully extracted, routing to platform check');
this.logger.info(
`Android setup successfully extracted, routing to ${this.setupExtractedNodeName}`
);
return this.setupExtractedNodeName;
}

Expand All @@ -41,7 +43,7 @@ export class CheckAndroidSetupExtractedRouter {
if (!state.javaHome) missingPaths.push('javaHome');

this.logger.warn(
`Android setup extraction failed. Missing: ${missingPaths.join(', ')}. Routing to failure.`
`Android setup extraction failed. Missing: ${missingPaths.join(', ')}. Routing to ${this.failureNodeName}.`
);
return this.failureNodeName;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { State } from '../metadata.js';
import { createComponentLogger } from '@salesforce/magen-mcp-workflow';

/**
* Router node that determines the next step based on build success status.
Expand All @@ -19,6 +20,7 @@ export class CheckBuildSuccessfulRouter {
private readonly deploymentNodeName: string;
private readonly buildRecoveryNodeName: string;
private readonly failureNodeName: string;
private readonly logger = createComponentLogger('CheckBuildSuccessfulRouter');

constructor(deploymentNodeName: string, buildRecoveryNodeName: string, failureNodeName: string) {
this.deploymentNodeName = deploymentNodeName;
Expand All @@ -29,6 +31,7 @@ export class CheckBuildSuccessfulRouter {
execute = (state: State): string => {
// If build was successful, proceed to deployment
if (state.buildSuccessful) {
this.logger.info(`Build successful, routing to ${this.deploymentNodeName}`);
return this.deploymentNodeName;
}

Expand All @@ -38,15 +41,18 @@ export class CheckBuildSuccessfulRouter {

// If we've reached max retries, go to failure
if (attemptCount >= maxRetries) {
this.logger.info(`Max retries ${maxRetries} reached, routing to ${this.failureNodeName}`);
return this.failureNodeName;
}

// If we just came from recovery and it said it's not ready to retry, go to failure
if (state.recoveryReadyForRetry === false) {
this.logger.info(`Recovery not ready to retry, routing to ${this.failureNodeName}`);
return this.failureNodeName;
}

// Otherwise, attempt recovery
this.logger.info(`Attempting recovery, routing to ${this.buildRecoveryNodeName}`);
return this.buildRecoveryNodeName;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export class PlatformCheckNode extends BaseNode<State> {
: undefined,
};
} catch (error) {
this.logger.error(`Error executing platform check command: ${command}`, error as Error);
const errorMessage = error instanceof Error ? error.message : `${error}`;
return {
validPlatformSetup: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

import { createComponentLogger } from '@salesforce/magen-mcp-workflow';
import { State } from '../metadata.js';

/**
Expand All @@ -13,6 +14,7 @@ import { State } from '../metadata.js';
export class CheckPluginValidatedRouter {
private readonly pluginValidatedNodeName: string;
private readonly invalidPluginNodeName: string;
private readonly logger = createComponentLogger('CheckPluginValidatedRouter');

/**
* Creates a new CheckPluginValidatedRouter.
Expand All @@ -26,6 +28,12 @@ export class CheckPluginValidatedRouter {
}

execute = (state: State): string => {
return state.validPluginSetup ? this.pluginValidatedNodeName : this.invalidPluginNodeName;
if (state.validPluginSetup === true) {
this.logger.info(`Plugin setup valid, routing to ${this.pluginValidatedNodeName}`);
return this.pluginValidatedNodeName;
}

this.logger.info(`Plugin setup invalid, routing to ${this.invalidPluginNodeName}`);
return this.invalidPluginNodeName;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class CheckProjectGenerationRouter {

if (hasProjectPath && !hasFatalErrors) {
this.logger.info(
`Project generation successful at ${state.projectPath}, routing to build validation`
`Project generation successful at ${state.projectPath}, routing to ${this.successNodeName}`
);
return this.successNodeName;
}
Expand All @@ -48,7 +48,9 @@ export class CheckProjectGenerationRouter {
? `Fatal errors occurred: ${state.workflowFatalErrorMessages?.join(', ')}`
: 'Project path not set after generation';

this.logger.warn(`Project generation failed. Reason: ${reason}. Routing to failure.`);
this.logger.warn(
`Project generation failed. Reason: ${reason}. Routing to ${this.failureNodeName}.`
);
return this.failureNodeName;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

import { createComponentLogger } from '@salesforce/magen-mcp-workflow';
import { State } from '../metadata.js';

/**
Expand All @@ -15,6 +16,7 @@ export class CheckSetupValidatedRouter {
private readonly setupValidatedNodeName: string;
private readonly androidSetupNodeName: string;
private readonly invalidSetupNodeName: string;
private readonly logger = createComponentLogger('CheckSetupValidatedRouter');

/**
* Creates a new CheckSetupValidatedRouter.
Expand All @@ -36,15 +38,23 @@ export class CheckSetupValidatedRouter {
execute = (state: State): string => {
// If platform setup is valid, proceed
if (state.validPlatformSetup) {
this.logger.info(`Platform setup valid, routing to ${this.setupValidatedNodeName}`);
return this.setupValidatedNodeName;
}

// If platform is Android and Android/Java paths are missing, route to Android setup
if (state.platform === 'Android' && (!state.androidHome || !state.javaHome)) {
const missingSetup: string[] = [];
if (!state.androidHome) missingSetup.push('androidHome');
if (!state.javaHome) missingSetup.push('javaHome');
this.logger.info(
`Android setup missing ${missingSetup.join(', ')}, routing to ${this.androidSetupNodeName}`
);
return this.androidSetupNodeName;
}

// Otherwise, route to failure
this.logger.info(`Platform setup invalid, routing to ${this.invalidSetupNodeName}`);
return this.invalidSetupNodeName;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

import { createComponentLogger } from '@salesforce/magen-mcp-workflow';
import { State } from '../metadata.js';

/**
Expand All @@ -17,7 +18,7 @@ import { State } from '../metadata.js';
export class CheckTemplatePropertiesFulfilledRouter {
private readonly propertiesFulfilledNodeName: string;
private readonly propertiesUnfulfilledNodeName: string;

private readonly logger = createComponentLogger('CheckTemplatePropertiesFulfilledRouter');
/**
* Creates a new CheckTemplatePropertiesFulfilledRouter.
*
Expand All @@ -37,6 +38,7 @@ export class CheckTemplatePropertiesFulfilledRouter {
// If no template has been selected yet, we shouldn't be checking template properties
// This is a safety check to prevent routing to project generation before template selection
if (!state.selectedTemplate) {
this.logger.info(`No template selected, routing to ${this.propertiesUnfulfilledNodeName}`);
return this.propertiesUnfulfilledNodeName;
}

Expand All @@ -45,22 +47,34 @@ export class CheckTemplatePropertiesFulfilledRouter {
!state.templatePropertiesMetadata ||
Object.keys(state.templatePropertiesMetadata).length === 0
) {
this.logger.info(
`No template properties defined, routing to ${this.propertiesFulfilledNodeName}`
);
return this.propertiesFulfilledNodeName;
}

// If templateProperties haven't been initialized, properties are unfulfilled
if (!state.templateProperties) {
this.logger.info(
`Template properties not initialized, routing to ${this.propertiesUnfulfilledNodeName}`
);
return this.propertiesUnfulfilledNodeName;
}

// Check each required property
for (const [propertyName, metadata] of Object.entries(state.templatePropertiesMetadata)) {
// If property is required and not present in templateProperties, it's unfulfilled
if (metadata.required && !state.templateProperties[propertyName]) {
this.logger.info(
`Property ${propertyName} is required but not present in state.templateProperties["${propertyName}"], routing to ${this.propertiesUnfulfilledNodeName}`
);
return this.propertiesUnfulfilledNodeName;
}
}

this.logger.info(
`All template properties fulfilled, routing to ${this.propertiesFulfilledNodeName}`
);
return this.propertiesFulfilledNodeName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class TemplateSelectionNode extends AbstractToolNode<State> {
}

if (!state.templateOptions) {
this.logger.error('Fatal error: No template is available to select.');
return {
workflowFatalErrorMessages: ['No template options available for selection'],
};
Expand Down