@vibecodetown/mcp-server 2.2.0 → 2.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/README.md +10 -10
- package/build/auth/index.js +0 -2
- package/build/auth/public_key.js +6 -4
- package/build/bootstrap/doctor.js +113 -5
- package/build/bootstrap/installer.js +85 -15
- package/build/bootstrap/registry.js +11 -6
- package/build/bootstrap/skills-installer.js +365 -0
- package/build/dx/activity.js +26 -3
- package/build/engine.js +151 -0
- package/build/errors.js +107 -0
- package/build/generated/bridge_build_seed_input.js +2 -0
- package/build/generated/bridge_build_seed_output.js +2 -0
- package/build/generated/bridge_confirm_reference_input.js +2 -0
- package/build/generated/bridge_confirm_reference_output.js +2 -0
- package/build/generated/bridge_confirmed_reference_file.js +2 -0
- package/build/generated/bridge_generate_references_input.js +2 -0
- package/build/generated/bridge_generate_references_output.js +2 -0
- package/build/generated/bridge_references_file.js +2 -0
- package/build/generated/bridge_work_order_seed_file.js +2 -0
- package/build/generated/contracts_bundle_info.js +3 -3
- package/build/generated/index.js +14 -0
- package/build/generated/ingress_input.js +2 -0
- package/build/generated/ingress_output.js +2 -0
- package/build/generated/ingress_resolution_file.js +2 -0
- package/build/generated/ingress_summary_file.js +2 -0
- package/build/generated/message_template_id_mapping_file.js +2 -0
- package/build/generated/run_app_input.js +1 -1
- package/build/index.js +4 -3
- package/build/local-mode/paths.js +1 -0
- package/build/local-mode/setup.js +21 -1
- package/build/path-utils.js +68 -0
- package/build/runtime/cli_invoker.js +1 -1
- package/build/tools/vibe_pm/advisory_review.js +5 -3
- package/build/tools/vibe_pm/bridge_build_seed.js +164 -0
- package/build/tools/vibe_pm/bridge_confirm_reference.js +91 -0
- package/build/tools/vibe_pm/bridge_generate_references.js +258 -0
- package/build/tools/vibe_pm/briefing.js +27 -3
- package/build/tools/vibe_pm/context.js +79 -0
- package/build/tools/vibe_pm/create_work_order.js +200 -3
- package/build/tools/vibe_pm/doctor.js +95 -0
- package/build/tools/vibe_pm/entity_gate/preflight.js +8 -3
- package/build/tools/vibe_pm/export_output.js +14 -13
- package/build/tools/vibe_pm/finalize_work.js +78 -40
- package/build/tools/vibe_pm/get_decision.js +2 -2
- package/build/tools/vibe_pm/index.js +128 -42
- package/build/tools/vibe_pm/ingress.js +645 -0
- package/build/tools/vibe_pm/ingress_gate.js +116 -0
- package/build/tools/vibe_pm/inspect_code.js +90 -20
- package/build/tools/vibe_pm/kce/doc_usage.js +4 -9
- package/build/tools/vibe_pm/kce/on_finalize.js +2 -2
- package/build/tools/vibe_pm/kce/preflight.js +11 -7
- package/build/tools/vibe_pm/memory_status.js +11 -8
- package/build/tools/vibe_pm/memory_sync.js +11 -8
- package/build/tools/vibe_pm/pm_language.js +17 -16
- package/build/tools/vibe_pm/python_error.js +115 -0
- package/build/tools/vibe_pm/run_app.js +169 -43
- package/build/tools/vibe_pm/run_app_podman.js +64 -2
- package/build/tools/vibe_pm/search_oss.js +5 -3
- package/build/tools/vibe_pm/spec_rag.js +185 -0
- package/build/tools/vibe_pm/status.js +50 -3
- package/build/tools/vibe_pm/submit_decision.js +2 -2
- package/build/tools/vibe_pm/types.js +28 -0
- package/build/tools/vibe_pm/undo_last_task.js +9 -2
- package/build/tools/vibe_pm/waiter_mapping.js +155 -0
- package/build/tools/vibe_pm/zoekt_evidence.js +5 -3
- package/build/tools.js +13 -5
- package/build/vibe-cli.js +245 -7
- package/package.json +5 -4
- package/skills/VRIP_INSTALL_MANIFEST_DOCTOR.skill.md +288 -0
- package/skills/index.json +14 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @vibecode/mcp-server
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@vibecode/mcp-server)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](#testing)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
@@ -28,13 +28,13 @@ curl -fsSL https://vibecode.town/install.sh | sh
|
|
|
28
28
|
### Manual Install
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npm install -g @
|
|
31
|
+
npm install -g @vibecode/mcp-server
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
### Add to Claude Code
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
claude mcp add
|
|
37
|
+
claude mcp add vibe-pm -- npx @vibecode/mcp-server
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
### Add to Cursor
|
|
@@ -118,8 +118,8 @@ On first tool call, the server automatically:
|
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
120
|
# Clone and install
|
|
121
|
-
git clone https://github.com/
|
|
122
|
-
cd
|
|
121
|
+
git clone https://github.com/yellowhama/vibe-pm
|
|
122
|
+
cd vibe-pm/adapters/mcp-ts
|
|
123
123
|
npm install
|
|
124
124
|
|
|
125
125
|
# Build
|
|
@@ -176,7 +176,7 @@ tests/
|
|
|
176
176
|
"mcpServers": {
|
|
177
177
|
"vibecode": {
|
|
178
178
|
"command": "npx",
|
|
179
|
-
"args": ["@
|
|
179
|
+
"args": ["@vibecode/mcp-server"]
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
}
|
|
@@ -249,7 +249,7 @@ User (natural language)
|
|
|
249
249
|
↓
|
|
250
250
|
AI Agent (Claude/Cursor/Codex)
|
|
251
251
|
↓ AGENTS.md rules
|
|
252
|
-
MCP Server (@
|
|
252
|
+
MCP Server (@vibecode/mcp-server)
|
|
253
253
|
↓ vibe_pm.* tools
|
|
254
254
|
Bootstrap Layer
|
|
255
255
|
↓ auto-download if missing
|
|
@@ -265,5 +265,5 @@ MIT
|
|
|
265
265
|
## Links
|
|
266
266
|
|
|
267
267
|
- [Documentation](https://vibecode.town/docs)
|
|
268
|
-
- [GitHub](https://github.com/
|
|
269
|
-
- [Issues](https://github.com/
|
|
268
|
+
- [GitHub](https://github.com/yellowhama/vibe-pm)
|
|
269
|
+
- [Issues](https://github.com/yellowhama/vibe-pm/issues)
|
package/build/auth/index.js
CHANGED
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
* - Token caching and persistence
|
|
6
6
|
* - Offline JWT verification
|
|
7
7
|
* - Feature gating based on entitlements
|
|
8
|
-
* - Credential storage (GitHub/NPM tokens)
|
|
9
8
|
*/
|
|
10
9
|
export { TokenCache } from './token_cache.js';
|
|
11
10
|
export { TokenVerifier } from './token_verifier.js';
|
|
12
11
|
export { AuthGate } from './gate.js';
|
|
13
12
|
export { PUBLIC_KEY } from './public_key.js';
|
|
14
|
-
export { CredentialStore, getCredentialStore } from './credential_store.js';
|
|
15
13
|
// Re-export convenience functions
|
|
16
14
|
import { TokenCache } from './token_cache.js';
|
|
17
15
|
import { TokenVerifier } from './token_verifier.js';
|
package/build/auth/public_key.js
CHANGED
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
* 3. Release new client version
|
|
13
13
|
*/
|
|
14
14
|
const ENV_PUBLIC_KEY = (process.env.VIBECODE_AUTH_PUBLIC_KEY || process.env.VIBE_AUTH_PUBLIC_KEY || "").trim();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
// Development key - REPLACE WITH PRODUCTION KEY FOR RELEASE
|
|
16
|
+
// Generate with: openssl ecparam -name prime256v1 -genkey -noout | openssl ec -pubout
|
|
17
|
+
const DEV_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
18
|
+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEE85F1OitXNxlIXw3/YD4EiBGmlTe
|
|
19
|
+
yLIZWPhyHfOxe+01IeO/iYP9lbKXMDd0u8gN8KRfBwHel1lMddRA2Hnm9Q==
|
|
19
20
|
-----END PUBLIC KEY-----`;
|
|
21
|
+
export const PUBLIC_KEY = ENV_PUBLIC_KEY || DEV_PUBLIC_KEY;
|
|
20
22
|
/**
|
|
21
23
|
* JWT issuer for verification
|
|
22
24
|
*/
|
|
@@ -1,7 +1,68 @@
|
|
|
1
1
|
// adapters/mcp-ts/src/bootstrap/doctor.ts
|
|
2
2
|
// Health check tool for installation status
|
|
3
3
|
import { ensureEngines, getEngineHealth, checkUpdates, validateCache } from "./installer.js";
|
|
4
|
-
import { ENGINE_SPECS } from "./registry.js";
|
|
4
|
+
import { ENGINE_SPECS, SKILL_SPEC } from "./registry.js";
|
|
5
|
+
import { getSkillsHealth } from "./skills-installer.js";
|
|
6
|
+
/**
|
|
7
|
+
* Build skill gates from skills health status
|
|
8
|
+
*/
|
|
9
|
+
function buildSkillGates(skillsHealth) {
|
|
10
|
+
const gates = [];
|
|
11
|
+
// Check if bundle is installed
|
|
12
|
+
if (!skillsHealth.installed) {
|
|
13
|
+
gates.push({
|
|
14
|
+
code: "SKILL_BUNDLE_MISSING",
|
|
15
|
+
severity: "FAIL",
|
|
16
|
+
message: "Skill bundle not found in package"
|
|
17
|
+
});
|
|
18
|
+
return gates;
|
|
19
|
+
}
|
|
20
|
+
// Check if index is valid
|
|
21
|
+
if (!skillsHealth.version) {
|
|
22
|
+
gates.push({
|
|
23
|
+
code: "SKILL_INDEX_INVALID",
|
|
24
|
+
severity: "FAIL",
|
|
25
|
+
message: "Skills index.json not found or invalid"
|
|
26
|
+
});
|
|
27
|
+
return gates;
|
|
28
|
+
}
|
|
29
|
+
// Check version mismatch
|
|
30
|
+
if (skillsHealth.version !== SKILL_SPEC.version) {
|
|
31
|
+
gates.push({
|
|
32
|
+
code: "SKILL_VERSION_MISMATCH",
|
|
33
|
+
severity: "WARN",
|
|
34
|
+
message: `Version mismatch: expected ${SKILL_SPEC.version}, got ${skillsHealth.version}`
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
// Check each skill
|
|
38
|
+
for (const skill of skillsHealth.skills) {
|
|
39
|
+
if (skill.status === "missing") {
|
|
40
|
+
gates.push({
|
|
41
|
+
code: "SKILL_BUNDLE_CORRUPTED",
|
|
42
|
+
severity: "FAIL",
|
|
43
|
+
message: skill.error ?? `Skill file missing: ${skill.id}`,
|
|
44
|
+
skillId: skill.id
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else if (skill.status === "sha_mismatch") {
|
|
48
|
+
gates.push({
|
|
49
|
+
code: "SKILL_SHA_MISMATCH",
|
|
50
|
+
severity: "FAIL",
|
|
51
|
+
message: skill.error ?? `SHA256 mismatch for skill: ${skill.id}`,
|
|
52
|
+
skillId: skill.id
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else if (skill.status === "ok") {
|
|
56
|
+
gates.push({
|
|
57
|
+
code: "SKILL_OK",
|
|
58
|
+
severity: "INFO",
|
|
59
|
+
message: `Skill OK: ${skill.id}`,
|
|
60
|
+
skillId: skill.id
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return gates;
|
|
65
|
+
}
|
|
5
66
|
/**
|
|
6
67
|
* Check engine installation status and return detailed health info
|
|
7
68
|
* Triggers installation if engines are missing or outdated
|
|
@@ -42,9 +103,22 @@ export async function doctor() {
|
|
|
42
103
|
missing: healthAfter.filter((h) => h.status === "missing").length,
|
|
43
104
|
corrupted: healthAfter.filter((h) => h.status === "corrupted").length
|
|
44
105
|
};
|
|
106
|
+
// Check skills health
|
|
107
|
+
const skillsHealth = getSkillsHealth();
|
|
108
|
+
const skillGates = buildSkillGates(skillsHealth);
|
|
109
|
+
// Determine skill status
|
|
110
|
+
let skillStatus = "OK";
|
|
111
|
+
const hasSkillFail = skillGates.some(g => g.severity === "FAIL");
|
|
112
|
+
const hasSkillWarn = skillGates.some(g => g.severity === "WARN");
|
|
113
|
+
if (hasSkillFail) {
|
|
114
|
+
skillStatus = "ERROR";
|
|
115
|
+
}
|
|
116
|
+
else if (hasSkillWarn) {
|
|
117
|
+
skillStatus = "WARN";
|
|
118
|
+
}
|
|
45
119
|
// Determine overall status
|
|
46
120
|
let status = "OK";
|
|
47
|
-
if (summaryAfter.missing > 0 || summaryAfter.corrupted > 0) {
|
|
121
|
+
if (summaryAfter.missing > 0 || summaryAfter.corrupted > 0 || hasSkillFail) {
|
|
48
122
|
status = "ERROR";
|
|
49
123
|
}
|
|
50
124
|
else if (summaryAfter.needsUpdate > 0) {
|
|
@@ -53,6 +127,13 @@ export async function doctor() {
|
|
|
53
127
|
return {
|
|
54
128
|
status,
|
|
55
129
|
engines,
|
|
130
|
+
skills: {
|
|
131
|
+
status: skillStatus,
|
|
132
|
+
version: skillsHealth.version,
|
|
133
|
+
bundlePath: skillsHealth.bundlePath,
|
|
134
|
+
gates: skillGates,
|
|
135
|
+
summary: skillsHealth.summary
|
|
136
|
+
},
|
|
56
137
|
summary: summaryAfter
|
|
57
138
|
};
|
|
58
139
|
}
|
|
@@ -60,6 +141,17 @@ export async function doctor() {
|
|
|
60
141
|
return {
|
|
61
142
|
status: "ERROR",
|
|
62
143
|
engines: {},
|
|
144
|
+
skills: {
|
|
145
|
+
status: "ERROR",
|
|
146
|
+
version: null,
|
|
147
|
+
bundlePath: null,
|
|
148
|
+
gates: [{
|
|
149
|
+
code: "SKILL_BUNDLE_MISSING",
|
|
150
|
+
severity: "FAIL",
|
|
151
|
+
message: "Failed to check skills health"
|
|
152
|
+
}],
|
|
153
|
+
summary: { total: 0, ok: 0, missing: 0, corrupted: 0 }
|
|
154
|
+
},
|
|
63
155
|
summary: {
|
|
64
156
|
total: Object.keys(ENGINE_SPECS).length,
|
|
65
157
|
ok: 0,
|
|
@@ -76,10 +168,26 @@ export async function doctor() {
|
|
|
76
168
|
*/
|
|
77
169
|
export async function healthCheck() {
|
|
78
170
|
const health = await getEngineHealth();
|
|
79
|
-
const
|
|
171
|
+
const skillsHealth = getSkillsHealth();
|
|
172
|
+
const engineOk = health.every((h) => h.status === "ok");
|
|
173
|
+
const skillsOk = skillsHealth.installed &&
|
|
174
|
+
skillsHealth.summary.missing === 0 &&
|
|
175
|
+
skillsHealth.summary.corrupted === 0;
|
|
176
|
+
let skillStatus = "OK";
|
|
177
|
+
if (!skillsHealth.installed || skillsHealth.summary.missing > 0 || skillsHealth.summary.corrupted > 0) {
|
|
178
|
+
skillStatus = "ERROR";
|
|
179
|
+
}
|
|
180
|
+
else if (skillsHealth.version !== SKILL_SPEC.version) {
|
|
181
|
+
skillStatus = "WARN";
|
|
182
|
+
}
|
|
80
183
|
return {
|
|
81
|
-
status:
|
|
82
|
-
engines: health
|
|
184
|
+
status: (engineOk && skillsOk) ? "OK" : "NEEDS_ATTENTION",
|
|
185
|
+
engines: health,
|
|
186
|
+
skills: {
|
|
187
|
+
status: skillStatus,
|
|
188
|
+
version: skillsHealth.version,
|
|
189
|
+
summary: skillsHealth.summary
|
|
190
|
+
}
|
|
83
191
|
};
|
|
84
192
|
}
|
|
85
193
|
/**
|
|
@@ -10,13 +10,25 @@ import { promisify } from "node:util";
|
|
|
10
10
|
import { ENGINE_SPECS } from "./registry.js";
|
|
11
11
|
import { detectPlatform, exeName } from "./platform.js";
|
|
12
12
|
import { withLock } from "./lock.js";
|
|
13
|
+
import { isDownloadTimeoutError, isDownload404Error, isDownloadFailedError, } from "../errors.js";
|
|
13
14
|
const execFileAsync = promisify(execFile);
|
|
14
15
|
// ============================================================
|
|
15
|
-
// Configuration
|
|
16
|
+
// Configuration (P2-26: Environment variable support)
|
|
16
17
|
// ============================================================
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
18
|
+
function getEnvInt(name, defaultValue) {
|
|
19
|
+
const value = process.env[name];
|
|
20
|
+
if (!value)
|
|
21
|
+
return defaultValue;
|
|
22
|
+
const parsed = parseInt(value, 10);
|
|
23
|
+
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
24
|
+
}
|
|
25
|
+
// Configurable via environment variables:
|
|
26
|
+
// VIBECODE_MAX_RETRIES - Maximum download retry attempts (default: 3)
|
|
27
|
+
// VIBECODE_RETRY_DELAY_MS - Delay between retries in ms (default: 1000)
|
|
28
|
+
// VIBECODE_DOWNLOAD_TIMEOUT_MS - Download timeout in ms (default: 60000)
|
|
29
|
+
const MAX_RETRIES = getEnvInt("VIBECODE_MAX_RETRIES", 3);
|
|
30
|
+
const RETRY_DELAY_MS = getEnvInt("VIBECODE_RETRY_DELAY_MS", 1000);
|
|
31
|
+
const DOWNLOAD_TIMEOUT_MS = getEnvInt("VIBECODE_DOWNLOAD_TIMEOUT_MS", 60_000);
|
|
20
32
|
function isTruthyEnv(value) {
|
|
21
33
|
if (!value)
|
|
22
34
|
return false;
|
|
@@ -29,6 +41,19 @@ function isOfflineMode() {
|
|
|
29
41
|
function isDebugMode() {
|
|
30
42
|
return isTruthyEnv(process.env.VIBECODE_DEBUG);
|
|
31
43
|
}
|
|
44
|
+
function getGitHubToken() {
|
|
45
|
+
return process.env.GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || null;
|
|
46
|
+
}
|
|
47
|
+
function getAuthHeaders() {
|
|
48
|
+
const token = getGitHubToken();
|
|
49
|
+
const headers = {
|
|
50
|
+
"User-Agent": "vibecode-installer",
|
|
51
|
+
};
|
|
52
|
+
if (token) {
|
|
53
|
+
headers["Authorization"] = `token ${token}`;
|
|
54
|
+
}
|
|
55
|
+
return headers;
|
|
56
|
+
}
|
|
32
57
|
// ============================================================
|
|
33
58
|
// Cache Management
|
|
34
59
|
// ============================================================
|
|
@@ -162,11 +187,12 @@ function makeBootstrapError(userMessage, internal) {
|
|
|
162
187
|
}
|
|
163
188
|
function userMessageForBootstrapFailure(err) {
|
|
164
189
|
const msg = err instanceof Error ? err.message : String(err);
|
|
165
|
-
|
|
190
|
+
// P1-4: Use centralized error patterns for consistent detection
|
|
191
|
+
if (isDownloadTimeoutError(msg)) {
|
|
166
192
|
return "다운로드 시간이 초과되었습니다. 인터넷 연결/방화벽 설정을 확인한 뒤 다시 시도해주세요.";
|
|
167
193
|
}
|
|
168
194
|
// P0-2: Better 404 error handling with recovery options
|
|
169
|
-
if (
|
|
195
|
+
if (isDownload404Error(msg)) {
|
|
170
196
|
return `릴리스를 찾지 못했습니다.
|
|
171
197
|
|
|
172
198
|
해결 방법:
|
|
@@ -174,7 +200,7 @@ function userMessageForBootstrapFailure(err) {
|
|
|
174
200
|
2. VIBECODE_OFFLINE=1로 업데이트 스킵
|
|
175
201
|
3. VIBECODE_DEBUG=1로 상세 로그 확인`;
|
|
176
202
|
}
|
|
177
|
-
if (
|
|
203
|
+
if (isDownloadFailedError(msg)) {
|
|
178
204
|
return "다운로드에 실패했습니다. 인터넷 연결/방화벽 설정을 확인한 뒤 다시 시도해주세요.";
|
|
179
205
|
}
|
|
180
206
|
if (msg.startsWith("sha_mismatch")) {
|
|
@@ -206,16 +232,17 @@ function sha256File(filePath) {
|
|
|
206
232
|
async function sleep(ms) {
|
|
207
233
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
208
234
|
}
|
|
209
|
-
async function fetchWithRetry(url, retries = MAX_RETRIES) {
|
|
235
|
+
async function fetchWithRetry(url, retries = MAX_RETRIES, extraHeaders) {
|
|
210
236
|
if (isOfflineMode()) {
|
|
211
237
|
throw makeBootstrapError("오프라인 모드에서는 다운로드를 진행할 수 없습니다. 인터넷을 연결하거나, 사전 설치된 도구 경로(VIBECODE_BIN_PATH)를 지정해주세요.");
|
|
212
238
|
}
|
|
213
239
|
let lastError = null;
|
|
240
|
+
const headers = { ...getAuthHeaders(), ...extraHeaders };
|
|
214
241
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
215
242
|
try {
|
|
216
243
|
const controller = new AbortController();
|
|
217
244
|
const timeoutId = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
|
|
218
|
-
const res = await fetch(url, { signal: controller.signal });
|
|
245
|
+
const res = await fetch(url, { signal: controller.signal, headers });
|
|
219
246
|
clearTimeout(timeoutId);
|
|
220
247
|
if (res.ok) {
|
|
221
248
|
return res;
|
|
@@ -276,8 +303,8 @@ async function discoverRelease(repo, version) {
|
|
|
276
303
|
const res = await fetch(`${apiBase}/tags/${encodeURIComponent(tag)}`, {
|
|
277
304
|
signal: controller.signal,
|
|
278
305
|
headers: {
|
|
306
|
+
...getAuthHeaders(),
|
|
279
307
|
"Accept": "application/vnd.github+json",
|
|
280
|
-
"User-Agent": "vibecode-installer",
|
|
281
308
|
},
|
|
282
309
|
});
|
|
283
310
|
clearTimeout(timeoutId);
|
|
@@ -300,8 +327,8 @@ async function discoverRelease(repo, version) {
|
|
|
300
327
|
const res = await fetch(`${apiBase}/latest`, {
|
|
301
328
|
signal: controller.signal,
|
|
302
329
|
headers: {
|
|
330
|
+
...getAuthHeaders(),
|
|
303
331
|
"Accept": "application/vnd.github+json",
|
|
304
|
-
"User-Agent": "vibecode-installer",
|
|
305
332
|
},
|
|
306
333
|
});
|
|
307
334
|
clearTimeout(timeoutId);
|
|
@@ -321,6 +348,24 @@ async function discoverRelease(repo, version) {
|
|
|
321
348
|
function findAsset(release, name) {
|
|
322
349
|
return release.assets.find(a => a.name === name);
|
|
323
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Download asset from GitHub release (supports private repos with GITHUB_TOKEN)
|
|
353
|
+
*/
|
|
354
|
+
async function downloadAsset(asset, outPath) {
|
|
355
|
+
const token = getGitHubToken();
|
|
356
|
+
// For private repos, use API URL with Accept: application/octet-stream
|
|
357
|
+
if (token) {
|
|
358
|
+
const res = await fetchWithRetry(asset.url, MAX_RETRIES, {
|
|
359
|
+
"Accept": "application/octet-stream",
|
|
360
|
+
});
|
|
361
|
+
await fs.promises.mkdir(path.dirname(outPath), { recursive: true });
|
|
362
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
363
|
+
await fs.promises.writeFile(outPath, buf);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// For public repos, use browser_download_url
|
|
367
|
+
await download(asset.browser_download_url, outPath);
|
|
368
|
+
}
|
|
324
369
|
async function readShaSumsFromUrl(url) {
|
|
325
370
|
const res = await fetchWithRetry(url);
|
|
326
371
|
const text = await res.text();
|
|
@@ -533,15 +578,35 @@ async function ensureOne(name, platform) {
|
|
|
533
578
|
// P0-2: Try GitHub API discovery first for better 404 tolerance
|
|
534
579
|
let assetUrl;
|
|
535
580
|
let shaMap;
|
|
581
|
+
let assetInfo;
|
|
536
582
|
const release = await discoverRelease(spec.repo, spec.version);
|
|
537
583
|
if (release) {
|
|
538
|
-
|
|
584
|
+
assetInfo = findAsset(release, asset);
|
|
539
585
|
const shaInfo = findAsset(release, spec.shaSumsAsset);
|
|
540
586
|
if (assetInfo) {
|
|
541
587
|
assetUrl = assetInfo.browser_download_url;
|
|
542
588
|
// Read SHA from discovered release if available
|
|
543
589
|
if (shaInfo) {
|
|
544
|
-
|
|
590
|
+
// Use API URL for SHA file too (for private repos)
|
|
591
|
+
const token = getGitHubToken();
|
|
592
|
+
if (token) {
|
|
593
|
+
const res = await fetchWithRetry(shaInfo.url, MAX_RETRIES, {
|
|
594
|
+
"Accept": "application/octet-stream",
|
|
595
|
+
});
|
|
596
|
+
const text = await res.text();
|
|
597
|
+
shaMap = new Map();
|
|
598
|
+
for (const line of text.split("\n")) {
|
|
599
|
+
const t = line.trim();
|
|
600
|
+
if (!t)
|
|
601
|
+
continue;
|
|
602
|
+
const m = t.match(/^([a-fA-F0-9]{64})\s+(.+)$/);
|
|
603
|
+
if (m)
|
|
604
|
+
shaMap.set(m[2].trim(), m[1].toLowerCase());
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
shaMap = await readShaSumsFromUrl(shaInfo.browser_download_url);
|
|
609
|
+
}
|
|
545
610
|
}
|
|
546
611
|
else {
|
|
547
612
|
// Fallback to legacy URL for SHA
|
|
@@ -566,8 +631,13 @@ async function ensureOne(name, platform) {
|
|
|
566
631
|
// Clean and prepare directory
|
|
567
632
|
await fs.promises.rm(dir, { recursive: true, force: true }).catch(() => { });
|
|
568
633
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
569
|
-
// Download archive (with retry)
|
|
570
|
-
|
|
634
|
+
// Download archive (with retry) - use API URL for private repos
|
|
635
|
+
if (assetInfo && getGitHubToken()) {
|
|
636
|
+
await downloadAsset(assetInfo, archivePath);
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
await download(assetUrl, archivePath);
|
|
640
|
+
}
|
|
571
641
|
// Verify SHA256
|
|
572
642
|
const got = (await sha256File(archivePath)).toLowerCase();
|
|
573
643
|
if (got !== expected)
|
|
@@ -14,11 +14,11 @@ export const ENGINE_SPECS = {
|
|
|
14
14
|
assetPrefix: "spec-high",
|
|
15
15
|
shaSumsAsset: "SHA256SUMS.txt"
|
|
16
16
|
},
|
|
17
|
-
"
|
|
18
|
-
name: "
|
|
17
|
+
"vibe-execution-engine": {
|
|
18
|
+
name: "vibe-execution-engine",
|
|
19
19
|
version: "1.0.0",
|
|
20
20
|
repo: "yellowhama/vibe-pm",
|
|
21
|
-
assetPrefix: "
|
|
21
|
+
assetPrefix: "vibe-execution-engine",
|
|
22
22
|
shaSumsAsset: "SHA256SUMS.txt"
|
|
23
23
|
},
|
|
24
24
|
"clinic": {
|
|
@@ -30,8 +30,13 @@ export const ENGINE_SPECS = {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
* {assetPrefix}_{version}_{platform}.tar.gz
|
|
33
|
+
* Skill Bundle Specification (SSOT)
|
|
35
34
|
*
|
|
36
|
-
*
|
|
35
|
+
* 스킬 파일은 npm 패키지의 skills/ 디렉토리에 prebundled로 포함됨.
|
|
36
|
+
* - 오프라인 지원
|
|
37
|
+
* - CLI 버전과 스킬 버전 동기화 자동 보장
|
|
37
38
|
*/
|
|
39
|
+
export const SKILL_SPEC = {
|
|
40
|
+
version: "1.0.0",
|
|
41
|
+
bundlePath: "skills" // adapters/mcp-ts/skills/ (패키지 루트 기준)
|
|
42
|
+
};
|