mcp-osp-prompt 1.0.0

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,206 @@
1
+ /**
2
+ * Platform utilities for detecting and handling different Git platforms
3
+ * Shared between config.js and fetcher.js to avoid code duplication
4
+ */
5
+
6
+ // Platform detection from RESOURCE_PATH (or legacy PROMPT_PATH)
7
+ export function detectPlatformFromPath(promptPath) {
8
+ if (!promptPath) {
9
+ return null;
10
+ }
11
+
12
+ // Check for GitHub patterns
13
+ if (promptPath.includes('github.com') || promptPath.match(/^[\w-]+\/[\w-]+$/)) {
14
+ return 'github';
15
+ }
16
+
17
+ // Check for GitLab patterns
18
+ if (promptPath.includes('gitlab.com')) {
19
+ return 'gitlab';
20
+ }
21
+
22
+ // Check if it's a local path
23
+ if (promptPath.startsWith('/') || promptPath.startsWith('./') || promptPath.startsWith('~')) {
24
+ return 'local';
25
+ }
26
+
27
+ // Default to local for relative paths
28
+ return 'local';
29
+ }
30
+
31
+ // Parse remote path information with universal support for complex URLs
32
+ // 🟢 GREEN: Task 1.1 - 支持子目录参数,用于多目录资源拉取
33
+ export function parseRemotePath(promptPath, subDirectory = '') {
34
+ // 🟢 GREEN: Task 1.1 - 添加null/undefined检查和详细错误信息
35
+ if (!promptPath) {
36
+ throw new Error('RESOURCE_PATH cannot be null or empty. Please set a valid remote URL or local path.');
37
+ }
38
+
39
+ if (typeof promptPath !== 'string') {
40
+ throw new Error(`RESOURCE_PATH must be a string, received: ${typeof promptPath}`); // Note: parameter name 'promptPath' kept for compatibility
41
+ }
42
+
43
+ const cleanPath = promptPath.replace(/^https?:\/\//, '');
44
+
45
+ // GitHub URL parsing: github.com/owner/repo/tree/branch/path
46
+ if (cleanPath.includes('github.com')) {
47
+ // Match GitHub tree browsing URLs - handle branches with slashes
48
+ const treeMatch = cleanPath.match(/github\.com\/([^\/]+\/[^\/]+)\/tree\/(.*)/);
49
+ if (treeMatch) {
50
+ const [, project, remainingPath] = treeMatch;
51
+
52
+ // Split remaining path and try to identify branch vs file path
53
+ // Common strategy: assume first 1-2 segments are branch, rest is path
54
+ const segments = remainingPath.split('/');
55
+ let branch, path;
56
+
57
+ if (segments.length === 1) {
58
+ // Only branch, no path
59
+ branch = segments[0];
60
+ path = '';
61
+ } else if (segments.length >= 2) {
62
+ // Try to handle common branch patterns
63
+ if (segments[0].match(/^(main|master|develop|dev)$/) ||
64
+ segments[0].match(/^v?\d+/) || // version tags
65
+ !segments[1].match(/^[a-zA-Z]/) // if second segment starts with non-letter, likely part of branch
66
+ ) {
67
+ // Single segment branch
68
+ branch = segments[0];
69
+ path = segments.slice(1).join('/');
70
+ } else {
71
+ // Multi-segment branch (like feature/new-ui)
72
+ branch = segments.slice(0, 2).join('/');
73
+ path = segments.slice(2).join('/');
74
+ }
75
+ }
76
+
77
+ return appendSubDirectory({
78
+ platform: 'github',
79
+ project,
80
+ repo: project, // Alias for backward compatibility
81
+ branch: branch || 'main',
82
+ path: path || '', // File/directory path
83
+ apiUrl: `https://api.github.com/repos/${project}/contents/${path || ''}`
84
+ }, subDirectory);
85
+ }
86
+
87
+ // Match simple GitHub project URLs
88
+ const simpleMatch = cleanPath.match(/github\.com\/([^\/]+\/[^\/]+)/);
89
+ if (simpleMatch) {
90
+ return appendSubDirectory({
91
+ platform: 'github',
92
+ project: simpleMatch[1],
93
+ repo: simpleMatch[1],
94
+ branch: 'main',
95
+ path: '',
96
+ apiUrl: `https://api.github.com/repos/${simpleMatch[1]}/contents`
97
+ }, subDirectory);
98
+ }
99
+ }
100
+
101
+ // GitLab URL parsing: gitlab.com/group/project/-/tree/branch/path
102
+ if (cleanPath.includes('gitlab.com')) {
103
+ // Match GitLab tree browsing URLs with /-/tree/ pattern
104
+ const treeMatch = cleanPath.match(/gitlab\.com\/(.*?)\/-\/tree\/([^\/]+)\/?(.*)/);
105
+ if (treeMatch) {
106
+ const [, project, branch, path] = treeMatch;
107
+ return appendSubDirectory({
108
+ platform: 'gitlab',
109
+ project,
110
+ group: project, // Alias for backward compatibility
111
+ branch,
112
+ path: path || '', // File/directory path
113
+ apiUrl: `https://gitlab.com/api/v4/projects/${encodeURIComponent(project)}/repository/tree?path=${path || ''}&ref=${branch}&recursive=true&per_page=100`
114
+ }, subDirectory);
115
+ }
116
+
117
+ // Match simple GitLab project URLs
118
+ const simpleMatch = cleanPath.match(/gitlab\.com\/(.+?)(?:\/|$)/);
119
+ if (simpleMatch) {
120
+ const project = simpleMatch[1];
121
+ return appendSubDirectory({
122
+ platform: 'gitlab',
123
+ project,
124
+ group: project,
125
+ branch: 'master',
126
+ path: '',
127
+ apiUrl: `https://gitlab.com/api/v4/projects/${encodeURIComponent(project)}/repository/tree?ref=master&recursive=true&per_page=100`
128
+ }, subDirectory);
129
+ }
130
+ }
131
+
132
+ // Handle simple format like "user/repo" (defaults to GitHub)
133
+ if (promptPath.match(/^[\w-]+\/[\w-]+$/)) {
134
+ return appendSubDirectory({
135
+ platform: 'github',
136
+ project: promptPath,
137
+ repo: promptPath,
138
+ branch: 'main',
139
+ path: '',
140
+ apiUrl: `https://api.github.com/repos/${promptPath}/contents`
141
+ }, subDirectory);
142
+ }
143
+
144
+ return null;
145
+ }
146
+
147
+ // 🟢 GREEN: Task 1.1 - 辅助函数:追加子目录到路径信息
148
+ function appendSubDirectory(pathInfo, subDirectory) {
149
+ if (!subDirectory || !pathInfo) {
150
+ return pathInfo;
151
+ }
152
+
153
+ // 追加子目录到path
154
+ const newPath = pathInfo.path ? `${pathInfo.path}/${subDirectory}` : subDirectory;
155
+
156
+ // 根据平台更新apiUrl
157
+ let newApiUrl = pathInfo.apiUrl;
158
+ if (pathInfo.platform === 'github') {
159
+ // GitHub: https://api.github.com/repos/{project}/contents/{path}
160
+ newApiUrl = `https://api.github.com/repos/${pathInfo.project}/contents/${newPath}`;
161
+ } else if (pathInfo.platform === 'gitlab') {
162
+ // GitLab: https://gitlab.com/api/v4/projects/{project}/repository/tree?path={path}&ref={branch}&recursive=true&per_page=100
163
+ newApiUrl = `https://gitlab.com/api/v4/projects/${encodeURIComponent(pathInfo.project)}/repository/tree?path=${newPath}&ref=${pathInfo.branch}&recursive=true&per_page=100`;
164
+ }
165
+
166
+ return {
167
+ ...pathInfo,
168
+ path: newPath,
169
+ apiUrl: newApiUrl
170
+ };
171
+ }
172
+
173
+ // Create authentication headers based on platform
174
+ export function createAuthHeaders(platform, token) {
175
+ if (!token) {
176
+ return {};
177
+ }
178
+
179
+ const headers = {};
180
+
181
+ if (platform === 'github') {
182
+ headers['Authorization'] = `token ${token}`;
183
+ headers['Accept'] = 'application/vnd.github.v3+json';
184
+ } else if (platform === 'gitlab') {
185
+ headers['PRIVATE-TOKEN'] = token;
186
+ headers['User-Agent'] = 'curl/7.68.0';
187
+ headers['Accept'] = '*/*';
188
+ }
189
+
190
+ return headers;
191
+ }
192
+
193
+ // Validate configuration for given platform
194
+ export function validatePlatformConfiguration(resourcePath, token) {
195
+ if (!resourcePath) {
196
+ throw new Error('RESOURCE_PATH is required. Set it to a local directory path, GitHub repo (user/repo), or full URL.');
197
+ }
198
+
199
+ const platform = detectPlatformFromPath(resourcePath);
200
+
201
+ if ((platform === 'github' || platform === 'gitlab') && !token) {
202
+ throw new Error('GIT_TOKEN is required for remote repositories. Please set your GitHub or GitLab access token.');
203
+ }
204
+
205
+ return platform;
206
+ }