@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.
@@ -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
 
@@ -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
+ };