version mise à jour par claude , à tester

This commit is contained in:
Arthur Ria
2026-05-20 09:38:07 +02:00
commit b59cbb3546
58 changed files with 24548 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
/**
* Constants for WMS MCP Server
*/
// WMS Entity Types available via Query API
// Based on queries api.php reference
const WMS_ENTITY_TYPES = [
// Master Data
'Products',
'Containers',
'Accounts',
'Suppliers',
'Kits',
'Aliases',
// Operations
'Tasks',
'Stocks',
'ProductLocations',
// Inbound
'InboundOrders',
'Receptions',
// Outbound
'OutboundOrders'
];
// Entity Categories for documentation
const ENTITY_CATEGORIES = {
'Master Data': ['Products', 'Containers', 'Accounts', 'Suppliers', 'Kits', 'Aliases'],
'Operations': ['Tasks', 'Stocks', 'ProductLocations'],
'Inbound': ['InboundOrders', 'Receptions'],
'Outbound': ['OutboundOrders']
};
// Common WMS Commands
// These can be used with call_command_api tool
const WMS_COMMANDS = {
'ProductRemove': 'Mecalux.ITSW.EasyWMS.Modules.MasterData.Contracts.Commands.ProductRemoveCommand',
'ProductUpdate': 'Mecalux.ITSW.EasyWMS.Modules.MasterData.Contracts.Commands.ProductUpdateCommand',
'ContainerCreate': 'Mecalux.ITSW.EasyWMS.Modules.MasterData.Contracts.Commands.ContainerCreateCommand',
'TaskCancel': 'Mecalux.ITSW.EasyWMS.Modules.Tasks.Contracts.Commands.TaskCancelCommand'
// Add more as needed
};
// MCP Resource URIs
const RESOURCE_URIS = {
WMS_ENTITIES: 'wms://entities',
ENTITY_SCHEMAS: 'wms://entity-schemas',
QUERY_EXAMPLES: 'wms://query-examples',
WORKFLOWS_OVERVIEW: 'workflows://overview',
WORKFLOWS_CATEGORIES: 'workflows://categories',
API_CATALOG: 'api://catalog',
LOGS_GUIDE: 'logs://guide'
};
// Log patterns for error detection
const LOG_ERROR_PATTERNS = [
'ERROR',
'EXCEPTION',
'FATAL',
'CRITICAL',
'FAILED',
'FAILURE',
'WARNING'
];
// Query limits
const QUERY_LIMITS = {
MAX_ROWS: parseInt(process.env.MAX_QUERY_ROWS) || 1000,
DEFAULT_LIMIT: 100,
TIMEOUT_MS: parseInt(process.env.QUERY_TIMEOUT) || 30000
};
module.exports = {
WMS_ENTITY_TYPES,
ENTITY_CATEGORIES,
WMS_COMMANDS,
RESOURCE_URIS,
LOG_ERROR_PATTERNS,
QUERY_LIMITS
};
+167
View File
@@ -0,0 +1,167 @@
/**
* Profile Manager
* Handles multiple WMS profiles (AD, LIMAGRAIN, ...) defined in .env.
*
* Shared settings (same for all profiles):
* - WMS_API_AUTH OAuth client Basic auth header
* - WMS_APPLICATION Application name (e.g. "EasyWMS")
* - WMS_API_PATH URL path to the ApplicationService API
* - WMS_TOKEN_PATH URL path to the OAuth token endpoint
* - WORKFLOW_API_PATH URL path to the AD API
*
* Per-profile settings (prefixed by profile name):
* - <PROFILE>_HOST Hostname or IP (e.g. "10.255.255.2", "p4swms.mss.mecalux.com")
* - <PROFILE>_USERNAME
* - <PROFILE>_PASSWORD
* - <PROFILE>_TENANT
* - <PROFILE>_SAAS "true" | "false" (default false). When true the WMS is
* cloud-hosted — local filesystem logs are not reachable,
* so log tools return an error instead of probing paths.
*
* Profile registry:
* - WMS_PROFILES Comma-separated list of profile names (e.g. "AD,LIMAGRAIN")
* - DEFAULT_WMS_PROFILE Profile activated at startup (optional)
*/
const DEFAULTS = {
apiPath: '/ApplicationService/api',
tokenPath: '/EasySTS/OAuth/Token',
workflowApiPath: '/AD/api',
application: 'EasyWMS',
protocol: 'https',
};
const listeners = [];
let profiles = {};
let currentProfileName = null;
/**
* Parse .env into an in-memory profile registry.
* Called once at startup; safe to call again after env changes (tests).
*/
function loadProfiles() {
profiles = {};
currentProfileName = null;
const names = (process.env.WMS_PROFILES || '')
.split(',')
.map(s => s.trim())
.filter(Boolean);
if (names.length === 0) {
console.error('[Profile] WARNING: WMS_PROFILES is empty — no profiles loaded');
return;
}
const shared = {
auth: process.env.WMS_API_AUTH,
application: process.env.WMS_APPLICATION || DEFAULTS.application,
apiPath: process.env.WMS_API_PATH || DEFAULTS.apiPath,
tokenPath: process.env.WMS_TOKEN_PATH || DEFAULTS.tokenPath,
workflowApiPath: process.env.WORKFLOW_API_PATH || DEFAULTS.workflowApiPath,
};
for (const name of names) {
const host = process.env[`${name}_HOST`];
const username = process.env[`${name}_USERNAME`];
const password = process.env[`${name}_PASSWORD`];
const tenant = process.env[`${name}_TENANT`];
const saasRaw = process.env[`${name}_SAAS`];
const saas = saasRaw != null && /^(true|1|yes)$/i.test(saasRaw.trim());
if (!host || !username || !password || !tenant) {
console.error(`[Profile] WARNING: profile "${name}" is incomplete (missing HOST/USERNAME/PASSWORD/TENANT) — skipped`);
continue;
}
const base = `${DEFAULTS.protocol}://${host}`;
profiles[name] = {
name,
host,
username,
password,
tenant,
saas,
auth: shared.auth,
application: shared.application,
apiBaseUrl: `${base}${shared.apiPath}`,
tokenUrl: `${base}${shared.tokenPath}`,
workflowApiBase: `${base}${shared.workflowApiPath}`,
};
}
const defaultName = process.env.DEFAULT_WMS_PROFILE;
if (defaultName && profiles[defaultName]) {
currentProfileName = defaultName;
console.error(`[Profile] Loaded ${Object.keys(profiles).length} profile(s). Active: ${currentProfileName}`);
} else if (defaultName) {
console.error(`[Profile] DEFAULT_WMS_PROFILE="${defaultName}" is not a valid profile — no profile active`);
} else {
console.error(`[Profile] Loaded ${Object.keys(profiles).length} profile(s). No default selected — Claude must call switch_wms_profile before any API call`);
}
}
function listProfiles() {
return Object.keys(profiles).sort();
}
function getCurrentName() {
return currentProfileName;
}
/**
* Return the active profile config. Throws a helpful error if none is selected,
* so tool responses tell Claude exactly what to do next.
*/
function getCurrent() {
if (!currentProfileName) {
const available = listProfiles();
const hint = available.length > 0
? `Available profiles: ${available.join(', ')}. Call switch_wms_profile with one of them.`
: 'No profiles configured in .env (WMS_PROFILES is empty).';
throw new Error(`No WMS profile selected. ${hint}`);
}
return profiles[currentProfileName];
}
/**
* Switch to a different profile. Notifies listeners (services) so they can
* reset token + invalidate caches tied to the previous profile.
*/
function switchTo(name) {
if (!profiles[name]) {
const available = listProfiles();
throw new Error(`Unknown profile "${name}". Available: ${available.join(', ') || '(none)'}`);
}
if (name === currentProfileName) {
return profiles[name];
}
const previous = currentProfileName;
currentProfileName = name;
console.error(`[Profile] Switched: ${previous || '(none)'}${name}`);
for (const listener of listeners) {
try {
listener({ previous, current: name });
} catch (err) {
console.error(`[Profile] Listener error: ${err.message}`);
}
}
return profiles[name];
}
/**
* Register a callback fired whenever the active profile changes.
* Used by services to invalidate caches / reset tokens.
*/
function onSwitch(listener) {
listeners.push(listener);
}
module.exports = {
loadProfiles,
listProfiles,
getCurrent,
getCurrentName,
switchTo,
onSwitch,
};