@l4yercak3/cli 1.1.12 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +6 -0
- package/docs/mcp_server/MCP_SERVER_ARCHITECTURE.md +1481 -0
- package/docs/mcp_server/applicationOntology.ts +817 -0
- package/docs/mcp_server/cliApplications.ts +639 -0
- package/docs/mcp_server/crmOntology.ts +1063 -0
- package/docs/mcp_server/eventOntology.ts +1183 -0
- package/docs/mcp_server/formsOntology.ts +1401 -0
- package/docs/mcp_server/ontologySchemas.ts +185 -0
- package/docs/mcp_server/schema.ts +250 -0
- package/package.json +5 -2
- package/src/commands/login.js +0 -6
- package/src/commands/mcp-server.js +32 -0
- package/src/commands/spread.js +54 -1
- package/src/detectors/expo-detector.js +163 -0
- package/src/detectors/registry.js +2 -0
- package/src/mcp/auth.js +127 -0
- package/src/mcp/registry/domains/applications.js +516 -0
- package/src/mcp/registry/domains/codegen.js +894 -0
- package/src/mcp/registry/domains/core.js +326 -0
- package/src/mcp/registry/domains/crm.js +591 -0
- package/src/mcp/registry/domains/events.js +649 -0
- package/src/mcp/registry/domains/forms.js +696 -0
- package/src/mcp/registry/index.js +162 -0
- package/src/mcp/server.js +116 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expo/React Native Project Detector
|
|
3
|
+
* Detects Expo and React Native projects and their configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const BaseDetector = require('./base-detector');
|
|
9
|
+
|
|
10
|
+
class ExpoDetector extends BaseDetector {
|
|
11
|
+
get name() {
|
|
12
|
+
return 'expo';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get priority() {
|
|
16
|
+
return 95; // High priority - specific framework
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Detect if current directory is an Expo or React Native project
|
|
21
|
+
*/
|
|
22
|
+
detect(projectPath = process.cwd()) {
|
|
23
|
+
const results = {
|
|
24
|
+
isExpo: false,
|
|
25
|
+
isReactNative: false,
|
|
26
|
+
expoVersion: null,
|
|
27
|
+
reactNativeVersion: null,
|
|
28
|
+
hasTypeScript: false,
|
|
29
|
+
routerType: null, // 'expo-router' or 'react-navigation' or null
|
|
30
|
+
sdkVersion: null,
|
|
31
|
+
config: null,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Check for package.json
|
|
35
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
36
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
37
|
+
return {
|
|
38
|
+
detected: false,
|
|
39
|
+
confidence: 0,
|
|
40
|
+
metadata: {},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
46
|
+
const dependencies = {
|
|
47
|
+
...packageJson.dependencies,
|
|
48
|
+
...packageJson.devDependencies,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Check for Expo
|
|
52
|
+
if (dependencies.expo) {
|
|
53
|
+
results.isExpo = true;
|
|
54
|
+
results.expoVersion = dependencies.expo;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for React Native (with or without Expo)
|
|
58
|
+
if (dependencies['react-native']) {
|
|
59
|
+
results.isReactNative = true;
|
|
60
|
+
results.reactNativeVersion = dependencies['react-native'];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If neither, not a React Native project
|
|
64
|
+
if (!results.isExpo && !results.isReactNative) {
|
|
65
|
+
return {
|
|
66
|
+
detected: false,
|
|
67
|
+
confidence: 0,
|
|
68
|
+
metadata: {},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check for TypeScript
|
|
73
|
+
if (dependencies.typescript || fs.existsSync(path.join(projectPath, 'tsconfig.json'))) {
|
|
74
|
+
results.hasTypeScript = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Detect router type
|
|
78
|
+
if (dependencies['expo-router']) {
|
|
79
|
+
results.routerType = 'expo-router';
|
|
80
|
+
} else if (dependencies['@react-navigation/native']) {
|
|
81
|
+
results.routerType = 'react-navigation';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check for app.json or app.config.js (Expo config)
|
|
85
|
+
const appJsonPath = path.join(projectPath, 'app.json');
|
|
86
|
+
const appConfigJsPath = path.join(projectPath, 'app.config.js');
|
|
87
|
+
const appConfigTsPath = path.join(projectPath, 'app.config.ts');
|
|
88
|
+
|
|
89
|
+
if (fs.existsSync(appJsonPath)) {
|
|
90
|
+
try {
|
|
91
|
+
const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf8'));
|
|
92
|
+
results.config = 'app.json';
|
|
93
|
+
if (appJson.expo?.sdkVersion) {
|
|
94
|
+
results.sdkVersion = appJson.expo.sdkVersion;
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// Could not parse app.json
|
|
98
|
+
}
|
|
99
|
+
} else if (fs.existsSync(appConfigJsPath)) {
|
|
100
|
+
results.config = 'app.config.js';
|
|
101
|
+
} else if (fs.existsSync(appConfigTsPath)) {
|
|
102
|
+
results.config = 'app.config.ts';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Determine confidence based on what we found
|
|
106
|
+
let confidence = 0.7; // Base confidence for React Native
|
|
107
|
+
if (results.isExpo) {
|
|
108
|
+
confidence = 0.9; // Higher for Expo
|
|
109
|
+
if (results.config) {
|
|
110
|
+
confidence = 0.95; // Even higher with config file
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
detected: true,
|
|
116
|
+
confidence,
|
|
117
|
+
metadata: {
|
|
118
|
+
isExpo: results.isExpo,
|
|
119
|
+
expoVersion: results.expoVersion,
|
|
120
|
+
reactNativeVersion: results.reactNativeVersion,
|
|
121
|
+
hasTypeScript: results.hasTypeScript,
|
|
122
|
+
routerType: results.routerType,
|
|
123
|
+
sdkVersion: results.sdkVersion,
|
|
124
|
+
config: results.config,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
} catch (error) {
|
|
128
|
+
// Error reading package.json
|
|
129
|
+
return {
|
|
130
|
+
detected: false,
|
|
131
|
+
confidence: 0,
|
|
132
|
+
metadata: {},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get supported features for Expo/React Native
|
|
139
|
+
*/
|
|
140
|
+
getSupportedFeatures() {
|
|
141
|
+
return {
|
|
142
|
+
oauth: true, // OAuth via expo-auth-session or similar
|
|
143
|
+
stripe: true, // Stripe via @stripe/stripe-react-native
|
|
144
|
+
crm: true, // CRM API access
|
|
145
|
+
projects: true, // Projects API access
|
|
146
|
+
invoices: true, // Invoices API access
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get available generators for Expo/React Native
|
|
152
|
+
*/
|
|
153
|
+
getAvailableGenerators() {
|
|
154
|
+
return [
|
|
155
|
+
'api-client', // TypeScript API client
|
|
156
|
+
'env', // Environment variables
|
|
157
|
+
'oauth-guide', // OAuth setup guide for mobile
|
|
158
|
+
// Future: 'expo-auth', 'stripe-mobile'
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = new ExpoDetector();
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const nextJsDetector = require('./nextjs-detector');
|
|
9
|
+
const expoDetector = require('./expo-detector');
|
|
9
10
|
// Future detectors will be added here:
|
|
10
11
|
// const reactDetector = require('./react-detector');
|
|
11
12
|
// const vueDetector = require('./vue-detector');
|
|
@@ -16,6 +17,7 @@ const nextJsDetector = require('./nextjs-detector');
|
|
|
16
17
|
*/
|
|
17
18
|
const detectors = [
|
|
18
19
|
nextJsDetector,
|
|
20
|
+
expoDetector,
|
|
19
21
|
// Future: reactDetector, vueDetector, etc.
|
|
20
22
|
];
|
|
21
23
|
|
package/src/mcp/auth.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Authentication Layer
|
|
3
|
+
*
|
|
4
|
+
* Reads CLI session from ~/.l4yercak3/config.json and validates with backend.
|
|
5
|
+
* Provides auth context for tool execution.
|
|
6
|
+
*
|
|
7
|
+
* @module mcp/auth
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const configManager = require('../config/config-manager');
|
|
11
|
+
const backendClient = require('../api/backend-client');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} AuthContext
|
|
15
|
+
* @property {string} userId - The authenticated user's ID
|
|
16
|
+
* @property {string} organizationId - Current organization ID
|
|
17
|
+
* @property {string} organizationName - Current organization name
|
|
18
|
+
* @property {string} sessionToken - The CLI session token
|
|
19
|
+
* @property {string[]} permissions - User's permissions in the organization
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get authentication context from CLI session
|
|
24
|
+
* Returns null if not authenticated or session expired
|
|
25
|
+
*
|
|
26
|
+
* @returns {Promise<AuthContext|null>}
|
|
27
|
+
*/
|
|
28
|
+
async function getAuthContext() {
|
|
29
|
+
// Check if session exists locally
|
|
30
|
+
if (!configManager.isLoggedIn()) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const session = configManager.getSession();
|
|
35
|
+
if (!session || !session.token) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check if session is expired locally
|
|
40
|
+
if (session.expiresAt && session.expiresAt < Date.now()) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Validate session with backend
|
|
46
|
+
const validation = await backendClient.validateSession();
|
|
47
|
+
|
|
48
|
+
if (!validation || !validation.valid) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Build auth context
|
|
53
|
+
return {
|
|
54
|
+
userId: validation.userId || session.userId,
|
|
55
|
+
organizationId: validation.organizationId || session.organizationId,
|
|
56
|
+
organizationName: validation.organizationName || session.organizationName || 'Unknown',
|
|
57
|
+
sessionToken: session.token,
|
|
58
|
+
email: validation.email || session.email,
|
|
59
|
+
permissions: validation.permissions || [],
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// Log error to stderr (stdout is for MCP protocol)
|
|
63
|
+
console.error('[L4YERCAK3 MCP] Auth validation error:', error.message);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Require authentication for a tool
|
|
70
|
+
* Throws if not authenticated
|
|
71
|
+
*
|
|
72
|
+
* @param {AuthContext|null} authContext
|
|
73
|
+
* @returns {AuthContext}
|
|
74
|
+
* @throws {Error} If not authenticated
|
|
75
|
+
*/
|
|
76
|
+
function requireAuth(authContext) {
|
|
77
|
+
if (!authContext) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
'Not authenticated with L4YERCAK3. Please run "l4yercak3 login" first.'
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return authContext;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if user has a specific permission
|
|
87
|
+
*
|
|
88
|
+
* @param {AuthContext} authContext
|
|
89
|
+
* @param {string} permission
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
function hasPermission(authContext, permission) {
|
|
93
|
+
if (!authContext || !authContext.permissions) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check for wildcard permission
|
|
98
|
+
if (authContext.permissions.includes('*')) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return authContext.permissions.includes(permission);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Require a specific permission
|
|
107
|
+
*
|
|
108
|
+
* @param {AuthContext} authContext
|
|
109
|
+
* @param {string} permission
|
|
110
|
+
* @throws {Error} If permission not granted
|
|
111
|
+
*/
|
|
112
|
+
function requirePermission(authContext, permission) {
|
|
113
|
+
requireAuth(authContext);
|
|
114
|
+
|
|
115
|
+
if (!hasPermission(authContext, permission)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Permission denied: ${permission} required. Contact your organization admin.`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = {
|
|
123
|
+
getAuthContext,
|
|
124
|
+
requireAuth,
|
|
125
|
+
hasPermission,
|
|
126
|
+
requirePermission,
|
|
127
|
+
};
|