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.
- package/README.md +358 -0
- package/config.js +340 -0
- package/dialog/browser.js +532 -0
- package/dialog/constants.js +68 -0
- package/dialog/http-server.js +95 -0
- package/dialog/index.js +106 -0
- package/dialog/native.js +345 -0
- package/dialog/objc.js +303 -0
- package/dialog/utils.js +140 -0
- package/fetcher.js +56 -0
- package/package.json +49 -0
- package/platform-utils.js +206 -0
- package/prompt-manager.js +1027 -0
- package/resource-manager.js +358 -0
- package/server.js +305 -0
- package/test.js +93 -0
- package/tools.js +701 -0
- package/utils.js +145 -0
|
@@ -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
|
+
}
|