@modootoday/datalab-extension-mcp 1.7.0 → 1.8.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.en.md +55 -9
- package/README.md +47 -9
- package/dist/browsers-KHRZKQVP.js +119 -0
- package/dist/{dist-WMNHKGZR.js → chunk-5S7RFXIU.js} +1032 -442
- package/dist/chunk-6JBLBCLU.js +17524 -0
- package/dist/chunk-NKUU3DAF.js +15888 -0
- package/dist/cli.js +64 -4
- package/dist/dist-JICXADYR.js +105 -0
- package/dist/index.d.ts +22 -4
- package/dist/index.js +2 -1
- package/dist/skills-ACWHUGX7.js +101 -0
- package/package.json +3 -5
- package/skills/datalab-ad-disclosure-check/SKILL.md +74 -0
- package/skills/datalab-ad-disclosure-check/references/ad-disclosure-rules.md +98 -0
- package/skills/datalab-ad-disclosure-check/references/naver-terms-excerpt.md +28 -0
- package/skills/datalab-blog-widget/SKILL.md +65 -0
- package/skills/datalab-blog-widget/references/naver-constraints.md +51 -0
- package/skills/datalab-blog-widget/references/templates.md +123 -0
- package/skills/datalab-card-news/SKILL.md +93 -0
- package/skills/datalab-card-news/references/slide-structure.md +48 -0
- package/skills/datalab-cta-rewrite/SKILL.md +55 -0
- package/skills/datalab-cta-rewrite/references/output-format.md +24 -0
- package/skills/datalab-cta-rewrite/references/principles.md +36 -0
- package/skills/datalab-material-suggestion/SKILL.md +70 -0
- package/skills/datalab-material-suggestion/references/discovery.md +47 -0
- package/skills/datalab-material-suggestion/references/output-format.md +41 -0
- package/skills/datalab-photo-prompt/SKILL.md +52 -0
- package/skills/datalab-photo-prompt/references/prompt-dimensions.md +43 -0
- package/skills/datalab-photo-prompt/references/style-vocabulary.md +35 -0
- package/skills/datalab-reader-simulation/SKILL.md +83 -0
- package/skills/datalab-reader-simulation/references/checklist-format.md +36 -0
- package/skills/datalab-research-brief/SKILL.md +77 -0
- package/skills/datalab-research-brief/references/brief-format.md +39 -0
- package/skills/datalab-social-repurpose/SKILL.md +70 -0
- package/skills/datalab-social-repurpose/references/output-format.md +43 -0
- package/skills/datalab-social-repurpose/references/platform-specs.md +44 -0
- package/skills/datalab-tone-manner/SKILL.md +59 -0
- package/skills/datalab-tone-manner/references/analysis-dimensions.md +51 -0
- package/skills/datalab-tone-manner/references/spec-format.md +58 -0
- package/skills/datalab-tutorial-post/SKILL.md +61 -0
- package/skills/datalab-tutorial-post/references/analogy-and-case-templates.md +37 -0
- package/skills/datalab-tutorial-post/references/structure-and-length.md +22 -0
- package/skills/datalab-tutorial-post/references/tone-and-hooks.md +26 -0
- package/skills/datalab-video-script/SKILL.md +84 -0
- package/skills/datalab-video-script/references/scene-structure.md +41 -0
- package/skills/datalab-video-script/references/writing-for-the-ear.md +22 -0
- package/dist/chunk-NYUIYZG6.js +0 -32468
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./chunk-MLKGABMK.js";
|
|
2
|
-
|
|
3
1
|
// ../mcp-installer/dist/index.js
|
|
2
|
+
import { join as joinNative } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
4
|
import { spawn as nodeSpawn } from "child_process";
|
|
5
5
|
import { promises as fs } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
@@ -48,6 +48,16 @@ function buildEnv(opts) {
|
|
|
48
48
|
}
|
|
49
49
|
return env;
|
|
50
50
|
}
|
|
51
|
+
function envPairs(opts) {
|
|
52
|
+
const pairs = [
|
|
53
|
+
`DATALAB_MCP_TOKEN=${opts.token}`,
|
|
54
|
+
`DATALAB_MCP_EXTENSION_ID=${opts.extensionId}`
|
|
55
|
+
];
|
|
56
|
+
if (isCustomPort(opts.port)) {
|
|
57
|
+
pairs.push(`DATALAB_MCP_PORT=${opts.port}`);
|
|
58
|
+
}
|
|
59
|
+
return pairs;
|
|
60
|
+
}
|
|
51
61
|
function daemonMcpUrl(opts) {
|
|
52
62
|
const port = isCustomPort(opts.port) ? opts.port : DEFAULT_PORT;
|
|
53
63
|
return `http://127.0.0.1:${port}/mcp`;
|
|
@@ -70,90 +80,6 @@ function buildFileEntry(opts, platform) {
|
|
|
70
80
|
env: buildEnv(opts)
|
|
71
81
|
};
|
|
72
82
|
}
|
|
73
|
-
var CODEX_CHATGPT_NOTE = "ChatGPT \uB370\uC2A4\uD06C\uD1B1\uACFC \uD568\uAED8 \uC5F0\uACB0\uB3FC\uC694.";
|
|
74
|
-
function envPairs(opts) {
|
|
75
|
-
const pairs = [
|
|
76
|
-
`DATALAB_MCP_TOKEN=${opts.token}`,
|
|
77
|
-
`DATALAB_MCP_EXTENSION_ID=${opts.extensionId}`
|
|
78
|
-
];
|
|
79
|
-
if (isCustomPort(opts.port)) {
|
|
80
|
-
pairs.push(`DATALAB_MCP_PORT=${opts.port}`);
|
|
81
|
-
}
|
|
82
|
-
return pairs;
|
|
83
|
-
}
|
|
84
|
-
var CLI_HOSTS = [
|
|
85
|
-
{
|
|
86
|
-
id: "claude",
|
|
87
|
-
tier: 1,
|
|
88
|
-
displayName: "Claude Code",
|
|
89
|
-
bin: "claude",
|
|
90
|
-
// 🔴 User scope is mandatory: the default local scope binds the server to
|
|
91
|
-
// whatever directory the command was pasted in, which reads as success and
|
|
92
|
-
// then does nothing everywhere else.
|
|
93
|
-
buildAddArgs(opts) {
|
|
94
|
-
const args = ["mcp", "add", SERVER_NAME, "--scope", "user"];
|
|
95
|
-
for (const pair of envPairs(opts)) {
|
|
96
|
-
args.push("--env", pair);
|
|
97
|
-
}
|
|
98
|
-
args.push("--", "npx", "-y", packageSpec(opts.version));
|
|
99
|
-
return args;
|
|
100
|
-
},
|
|
101
|
-
buildRemoveArgs() {
|
|
102
|
-
return ["mcp", "remove", SERVER_NAME, "--scope", "user"];
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
id: "codex",
|
|
107
|
-
tier: 1,
|
|
108
|
-
displayName: "ChatGPT \uB370\uC2A4\uD06C\uD1B1 / Codex",
|
|
109
|
-
bin: "codex",
|
|
110
|
-
// The CLI and the desktop app share one config, so a single registration
|
|
111
|
-
// covers both — worth saying out loud in the output.
|
|
112
|
-
note: CODEX_CHATGPT_NOTE,
|
|
113
|
-
buildAddArgs(opts) {
|
|
114
|
-
const args = ["mcp", "add", SERVER_NAME];
|
|
115
|
-
for (const pair of envPairs(opts)) {
|
|
116
|
-
args.push("--env", pair);
|
|
117
|
-
}
|
|
118
|
-
args.push("--", "npx", "-y", packageSpec(opts.version));
|
|
119
|
-
return args;
|
|
120
|
-
},
|
|
121
|
-
buildRemoveArgs() {
|
|
122
|
-
return ["mcp", "remove", SERVER_NAME];
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
id: "gemini",
|
|
127
|
-
tier: 1,
|
|
128
|
-
displayName: "Gemini CLI",
|
|
129
|
-
bin: "gemini",
|
|
130
|
-
// User scope, for the same cwd-binding reason as the entry above.
|
|
131
|
-
buildAddArgs(opts) {
|
|
132
|
-
const args = ["mcp", "add", "-s", "user"];
|
|
133
|
-
for (const pair of envPairs(opts)) {
|
|
134
|
-
args.push("-e", pair);
|
|
135
|
-
}
|
|
136
|
-
args.push(SERVER_NAME, "npx", "-y", packageSpec(opts.version));
|
|
137
|
-
return args;
|
|
138
|
-
},
|
|
139
|
-
buildRemoveArgs() {
|
|
140
|
-
return ["mcp", "remove", "-s", "user", SERVER_NAME];
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
];
|
|
144
|
-
var INSTALLABLE_CLIS = [
|
|
145
|
-
{
|
|
146
|
-
id: "claude",
|
|
147
|
-
displayName: "Claude Code",
|
|
148
|
-
npmPackage: "@anthropic-ai/claude-code"
|
|
149
|
-
},
|
|
150
|
-
{ id: "gemini", displayName: "Gemini CLI", npmPackage: "@google/gemini-cli" },
|
|
151
|
-
{
|
|
152
|
-
id: "codex",
|
|
153
|
-
displayName: "ChatGPT \uB370\uC2A4\uD06C\uD1B1 / Codex",
|
|
154
|
-
npmPackage: "@openai/codex"
|
|
155
|
-
}
|
|
156
|
-
];
|
|
157
83
|
function joinPath(io, ...parts) {
|
|
158
84
|
if (io.platform === "win32") {
|
|
159
85
|
return parts.join("\\");
|
|
@@ -191,6 +117,70 @@ function resolveHostPath(io, spec) {
|
|
|
191
117
|
const segments = io.platform === "win32" && spec.win32Segments !== void 0 ? spec.win32Segments : spec.segments;
|
|
192
118
|
return joinPath(io, root, ...segments);
|
|
193
119
|
}
|
|
120
|
+
var claudeCliHost = {
|
|
121
|
+
id: "claude",
|
|
122
|
+
tier: 1,
|
|
123
|
+
displayName: "Claude Code",
|
|
124
|
+
bin: "claude",
|
|
125
|
+
// User scope is mandatory: the default local scope binds the server to
|
|
126
|
+
// whatever directory the command was pasted in, which reads as success and
|
|
127
|
+
// then does nothing everywhere else.
|
|
128
|
+
buildAddArgs(opts) {
|
|
129
|
+
const args = ["mcp", "add", SERVER_NAME, "--scope", "user"];
|
|
130
|
+
for (const pair of envPairs(opts)) {
|
|
131
|
+
args.push("--env", pair);
|
|
132
|
+
}
|
|
133
|
+
args.push("--", "npx", "-y", packageSpec(opts.version));
|
|
134
|
+
return args;
|
|
135
|
+
},
|
|
136
|
+
buildRemoveArgs() {
|
|
137
|
+
return ["mcp", "remove", SERVER_NAME, "--scope", "user"];
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var claudeInstallable = {
|
|
141
|
+
id: "claude",
|
|
142
|
+
displayName: "Claude Code",
|
|
143
|
+
npmPackage: "@anthropic-ai/claude-code"
|
|
144
|
+
};
|
|
145
|
+
var claudeSkills = {
|
|
146
|
+
tier: 2,
|
|
147
|
+
skillsDir(io) {
|
|
148
|
+
return resolveHostPath(io, {
|
|
149
|
+
kind: "home",
|
|
150
|
+
segments: [".claude", "skills"]
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
function jsonSnippet(topKey, opts, platform) {
|
|
155
|
+
const entry = buildFileEntry(opts, platform);
|
|
156
|
+
return JSON.stringify({ [topKey]: { [SERVER_NAME]: entry } }, null, 2);
|
|
157
|
+
}
|
|
158
|
+
function tomlSnippet(opts) {
|
|
159
|
+
const lines = [
|
|
160
|
+
`[mcp_servers.${SERVER_NAME}]`,
|
|
161
|
+
`command = "npx"`,
|
|
162
|
+
`args = ["-y", "${packageSpec(opts.version)}"]`,
|
|
163
|
+
"",
|
|
164
|
+
`[mcp_servers.${SERVER_NAME}.env]`
|
|
165
|
+
];
|
|
166
|
+
for (const [key, value] of Object.entries(buildEnv(opts))) {
|
|
167
|
+
lines.push(`${key} = "${value}"`);
|
|
168
|
+
}
|
|
169
|
+
return lines.join("\n");
|
|
170
|
+
}
|
|
171
|
+
async function fileOrParentExists(io, path) {
|
|
172
|
+
if (path === null) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
if (await io.exists(path)) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
const cut = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
179
|
+
if (cut <= 0) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return io.exists(path.slice(0, cut));
|
|
183
|
+
}
|
|
194
184
|
async function msixRoamingDir(io, familyPrefix, appDir) {
|
|
195
185
|
if (io.platform !== "win32") {
|
|
196
186
|
return null;
|
|
@@ -217,186 +207,144 @@ async function msixRoamingDir(io, familyPrefix, appDir) {
|
|
|
217
207
|
}
|
|
218
208
|
return null;
|
|
219
209
|
}
|
|
220
|
-
|
|
221
|
-
{
|
|
222
|
-
id: "claude-desktop",
|
|
223
|
-
tier: 2,
|
|
224
|
-
displayName: "Claude Desktop",
|
|
225
|
-
configKey: "mcpServers",
|
|
226
|
-
entryKind: "stdio",
|
|
227
|
-
// 🔴 Linux has no documented path, so a snippet host covers it; guessing
|
|
228
|
-
// one here would manufacture a false success.
|
|
229
|
-
configPath(io) {
|
|
230
|
-
return resolveHostPath(io, {
|
|
231
|
-
kind: "appData",
|
|
232
|
-
segments: ["Claude", "claude_desktop_config.json"],
|
|
233
|
-
platforms: ["darwin", "win32"]
|
|
234
|
-
});
|
|
235
|
-
},
|
|
236
|
-
/**
|
|
237
|
-
* 🔴 A Store install writes inside its package container instead. The two
|
|
238
|
-
* install kinds use entirely different locations and the app does not say
|
|
239
|
-
* which it is, so an existing container wins and its absence falls back.
|
|
240
|
-
*/
|
|
241
|
-
async resolveConfigPath(io) {
|
|
242
|
-
const dir = await msixRoamingDir(io, "Claude_", "Claude");
|
|
243
|
-
if (dir === null) {
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
return joinPath(io, dir, "claude_desktop_config.json");
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
id: "cursor",
|
|
251
|
-
tier: 2,
|
|
252
|
-
displayName: "Cursor",
|
|
253
|
-
configKey: "mcpServers",
|
|
254
|
-
// Speaks local HTTP MCP, so it connects to the daemon directly and no
|
|
255
|
-
// adapter process is spawned for it.
|
|
256
|
-
entryKind: "url",
|
|
257
|
-
configPath(io) {
|
|
258
|
-
return resolveHostPath(io, {
|
|
259
|
-
kind: "home",
|
|
260
|
-
segments: [".cursor", "mcp.json"]
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
// Documented strict JSON, so a single-key merge is safe. The hygiene floor
|
|
266
|
-
// still refuses and falls back to a snippet if an install turns out to
|
|
267
|
-
// carry comments.
|
|
268
|
-
id: "windsurf",
|
|
269
|
-
tier: 2,
|
|
270
|
-
displayName: "Windsurf",
|
|
271
|
-
configKey: "mcpServers",
|
|
272
|
-
entryKind: "stdio",
|
|
273
|
-
configPath(io) {
|
|
274
|
-
return joinPath(
|
|
275
|
-
io,
|
|
276
|
-
io.homedir(),
|
|
277
|
-
".codeium",
|
|
278
|
-
"windsurf",
|
|
279
|
-
"mcp_config.json"
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
// 🔴 A home override env var wins when set. Ignoring it would write beside
|
|
285
|
-
// the config the user moved, reporting success the app never sees.
|
|
286
|
-
id: "copilot-cli",
|
|
287
|
-
tier: 2,
|
|
288
|
-
displayName: "GitHub Copilot CLI",
|
|
289
|
-
configKey: "mcpServers",
|
|
290
|
-
entryKind: "stdio",
|
|
291
|
-
configPath(io) {
|
|
292
|
-
const home = io.env["COPILOT_HOME"];
|
|
293
|
-
if (home !== void 0 && home !== "") {
|
|
294
|
-
return joinPath(io, home, "mcp-config.json");
|
|
295
|
-
}
|
|
296
|
-
return resolveHostPath(io, {
|
|
297
|
-
kind: "home",
|
|
298
|
-
segments: [".copilot", "mcp-config.json"]
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
},
|
|
302
|
-
{
|
|
303
|
-
// Documented strict-JSON user config, home-relative on every OS.
|
|
304
|
-
id: "junie",
|
|
305
|
-
tier: 2,
|
|
306
|
-
displayName: "JetBrains Junie",
|
|
307
|
-
configKey: "mcpServers",
|
|
308
|
-
entryKind: "stdio",
|
|
309
|
-
configPath(io) {
|
|
310
|
-
return resolveHostPath(io, {
|
|
311
|
-
kind: "home",
|
|
312
|
-
segments: [".junie", "mcp", "mcp.json"]
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
// Documented strict-JSON user settings, home-relative on every OS.
|
|
318
|
-
id: "kiro",
|
|
319
|
-
tier: 2,
|
|
320
|
-
displayName: "Kiro",
|
|
321
|
-
configKey: "mcpServers",
|
|
322
|
-
entryKind: "stdio",
|
|
323
|
-
configPath(io) {
|
|
324
|
-
return resolveHostPath(io, {
|
|
325
|
-
kind: "home",
|
|
326
|
-
segments: [".kiro", "settings", "mcp.json"]
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
];
|
|
331
|
-
function buildEntryForHost(host, opts, platform) {
|
|
332
|
-
if (host.entryKind === "url") {
|
|
333
|
-
return buildUrlEntry(opts);
|
|
334
|
-
}
|
|
335
|
-
return buildFileEntry(opts, platform);
|
|
336
|
-
}
|
|
337
|
-
function jsonSnippet(topKey, opts, platform) {
|
|
338
|
-
const entry = buildFileEntry(opts, platform);
|
|
339
|
-
return JSON.stringify({ [topKey]: { [SERVER_NAME]: entry } }, null, 2);
|
|
340
|
-
}
|
|
341
|
-
function vscodeUserDir(io) {
|
|
342
|
-
return resolveHostPath(io, { kind: "appData", segments: ["Code", "User"] });
|
|
343
|
-
}
|
|
344
|
-
function vscodeConfigPath(io) {
|
|
345
|
-
const dir = vscodeUserDir(io);
|
|
346
|
-
if (dir === null) {
|
|
347
|
-
return null;
|
|
348
|
-
}
|
|
349
|
-
return joinPath(io, dir, "mcp.json");
|
|
350
|
-
}
|
|
351
|
-
async function fileOrParentExists(io, path) {
|
|
352
|
-
if (path === null) {
|
|
210
|
+
async function msixPackageInstalled(io, familyPrefix) {
|
|
211
|
+
if (io.platform !== "win32") {
|
|
353
212
|
return false;
|
|
354
213
|
}
|
|
355
|
-
|
|
356
|
-
|
|
214
|
+
const localAppData = io.env["LOCALAPPDATA"];
|
|
215
|
+
if (localAppData === void 0 || localAppData === "") {
|
|
216
|
+
return false;
|
|
357
217
|
}
|
|
358
|
-
|
|
359
|
-
|
|
218
|
+
try {
|
|
219
|
+
const names = await io.listDir(joinPath(io, localAppData, "Packages"));
|
|
220
|
+
return names.some((name) => name.startsWith(familyPrefix));
|
|
221
|
+
} catch {
|
|
360
222
|
return false;
|
|
361
223
|
}
|
|
362
|
-
return io.exists(path.slice(0, cut));
|
|
363
224
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
225
|
+
var claudeDesktopFileHost = {
|
|
226
|
+
id: "claude-desktop",
|
|
227
|
+
tier: 2,
|
|
228
|
+
displayName: "Claude Desktop",
|
|
229
|
+
configKey: "mcpServers",
|
|
230
|
+
entryKind: "stdio",
|
|
231
|
+
// Linux has no documented path, so a snippet host covers it; guessing
|
|
232
|
+
// one here would manufacture a false success.
|
|
233
|
+
configPath(io) {
|
|
234
|
+
return resolveHostPath(io, {
|
|
235
|
+
kind: "appData",
|
|
236
|
+
segments: ["Claude", "claude_desktop_config.json"],
|
|
237
|
+
platforms: ["darwin", "win32"]
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
/**
|
|
241
|
+
* A Store install writes inside its package container instead. The two
|
|
242
|
+
* install kinds use entirely different locations and the app does not say
|
|
243
|
+
* which it is, so an existing container wins and its absence falls back.
|
|
244
|
+
*/
|
|
245
|
+
async resolveConfigPath(io) {
|
|
246
|
+
const dir = await msixRoamingDir(io, "Claude_", "Claude");
|
|
247
|
+
if (dir === null) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return joinPath(io, dir, "claude_desktop_config.json");
|
|
374
251
|
}
|
|
375
|
-
return lines.join("\n");
|
|
376
|
-
}
|
|
377
|
-
function zedConfigPath(io) {
|
|
378
|
-
return resolveHostPath(io, {
|
|
379
|
-
kind: "xdg",
|
|
380
|
-
segments: ["zed", "settings.json"],
|
|
381
|
-
win32Segments: ["Zed", "settings.json"]
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
var LMSTUDIO_PATH = {
|
|
385
|
-
kind: "home",
|
|
386
|
-
segments: [".lmstudio", "mcp.json"]
|
|
387
252
|
};
|
|
388
|
-
var
|
|
389
|
-
|
|
390
|
-
|
|
253
|
+
var claudeDesktopLinuxSnippetHost = {
|
|
254
|
+
id: "claude-desktop-linux",
|
|
255
|
+
tier: 3,
|
|
256
|
+
displayName: "Claude Desktop (Linux)",
|
|
257
|
+
async detect(io) {
|
|
258
|
+
if (io.platform !== "linux") {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
return io.exists(joinPath(io, io.homedir(), ".config", "Claude"));
|
|
262
|
+
},
|
|
263
|
+
detectedPath(io) {
|
|
264
|
+
return joinPath(io, io.homedir(), ".config", "Claude");
|
|
265
|
+
},
|
|
266
|
+
// Guessing a path here would manufacture a false success that only
|
|
267
|
+
// surfaces after a restart.
|
|
268
|
+
reason: "\uB9AC\uB205\uC2A4\uC6A9 \uACF5\uC2DD \uC124\uC815 \uD30C\uC77C \uACBD\uB85C\uAC00 \uD655\uC778\uB418\uC9C0 \uC54A\uC544 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
269
|
+
pasteWhere: "Claude Desktop \uACF5\uC2DD \uBB38\uC11C\uAC00 \uC548\uB0B4\uD558\uB294 \uC124\uC815 \uD30C\uC77C(claude_desktop_config.json)\uC5D0 \uC544\uB798 \uB0B4\uC6A9\uC744 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
270
|
+
buildSnippet(opts, platform) {
|
|
271
|
+
return jsonSnippet("mcpServers", opts, platform);
|
|
272
|
+
}
|
|
391
273
|
};
|
|
392
|
-
var
|
|
393
|
-
|
|
394
|
-
|
|
274
|
+
var claudeDesktopSkills = {
|
|
275
|
+
tier: 2,
|
|
276
|
+
skillsDir(io) {
|
|
277
|
+
return resolveHostPath(io, {
|
|
278
|
+
kind: "home",
|
|
279
|
+
segments: [".claude", "skills"]
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var claudeDesktopLinuxSkills = {
|
|
284
|
+
tier: 3,
|
|
285
|
+
reason: "\uB9AC\uB205\uC2A4\uC6A9 Claude \uB370\uC2A4\uD06C\uD1B1\uC758 \uC2A4\uD0AC \uACBD\uB85C\uAC00 \uACF5\uC2DD \uBB38\uC11C\uB85C \uD655\uC778\uB418\uC9C0 \uC54A\uC558\uC5B4\uC694.",
|
|
286
|
+
instruction: "\uD655\uC778\uB418\uBA74 \uC790\uB3D9 \uC124\uCE58 \uB300\uC0C1\uC5D0 \uB123\uC744\uAC8C\uC694."
|
|
395
287
|
};
|
|
396
288
|
var CLINE_PATH = {
|
|
397
289
|
kind: "home",
|
|
398
290
|
segments: [".cline", "mcp.json"]
|
|
399
291
|
};
|
|
292
|
+
var clineSnippetHost = {
|
|
293
|
+
// One documented path only. An unverified path from a third-party table
|
|
294
|
+
// would have the user paste into a file nothing reads.
|
|
295
|
+
id: "cline",
|
|
296
|
+
tier: 3,
|
|
297
|
+
displayName: "Cline",
|
|
298
|
+
async detect(io) {
|
|
299
|
+
return fileOrParentExists(io, resolveHostPath(io, CLINE_PATH));
|
|
300
|
+
},
|
|
301
|
+
detectedPath(io) {
|
|
302
|
+
return resolveHostPath(io, CLINE_PATH);
|
|
303
|
+
},
|
|
304
|
+
reason: "\uC124\uC815 \uD30C\uC77C\uC744 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
305
|
+
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcpServers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
306
|
+
buildSnippet(opts, platform) {
|
|
307
|
+
return jsonSnippet("mcpServers", opts, platform);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
var clineSkills = {
|
|
311
|
+
tier: 2,
|
|
312
|
+
skillsDir(io) {
|
|
313
|
+
return resolveHostPath(io, {
|
|
314
|
+
kind: "home",
|
|
315
|
+
segments: [".cline", "skills"]
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
function agentsSkillsDir(io) {
|
|
320
|
+
return resolveHostPath(io, { kind: "home", segments: [".agents", "skills"] });
|
|
321
|
+
}
|
|
322
|
+
var CODEX_CHATGPT_NOTE = "ChatGPT \uB370\uC2A4\uD06C\uD1B1\uACFC \uD568\uAED8 \uC5F0\uACB0\uB3FC\uC694.";
|
|
323
|
+
var codexCliHost = {
|
|
324
|
+
id: "codex",
|
|
325
|
+
tier: 1,
|
|
326
|
+
displayName: "ChatGPT \uB370\uC2A4\uD06C\uD1B1 / Codex",
|
|
327
|
+
bin: "codex",
|
|
328
|
+
// The CLI and the desktop app share one config, so a single registration
|
|
329
|
+
// covers both — worth saying out loud in the output.
|
|
330
|
+
note: CODEX_CHATGPT_NOTE,
|
|
331
|
+
buildAddArgs(opts) {
|
|
332
|
+
const args = ["mcp", "add", SERVER_NAME];
|
|
333
|
+
for (const pair of envPairs(opts)) {
|
|
334
|
+
args.push("--env", pair);
|
|
335
|
+
}
|
|
336
|
+
args.push("--", "npx", "-y", packageSpec(opts.version));
|
|
337
|
+
return args;
|
|
338
|
+
},
|
|
339
|
+
buildRemoveArgs() {
|
|
340
|
+
return ["mcp", "remove", SERVER_NAME];
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
var codexInstallable = {
|
|
344
|
+
id: "codex",
|
|
345
|
+
displayName: "ChatGPT \uB370\uC2A4\uD06C\uD1B1 / Codex",
|
|
346
|
+
npmPackage: "@openai/codex"
|
|
347
|
+
};
|
|
400
348
|
var CODEX_PATH = {
|
|
401
349
|
kind: "home",
|
|
402
350
|
segments: [".codex", "config.toml"]
|
|
@@ -408,6 +356,193 @@ function codexConfigPath(io) {
|
|
|
408
356
|
}
|
|
409
357
|
return resolveHostPath(io, CODEX_PATH);
|
|
410
358
|
}
|
|
359
|
+
var CHATGPT_DESKTOP_PACKAGE = "OpenAI.Codex_";
|
|
360
|
+
var codexConfigOnlySnippetHost = {
|
|
361
|
+
id: "codex-config-only",
|
|
362
|
+
tier: 3,
|
|
363
|
+
displayName: "ChatGPT \uB370\uC2A4\uD06C\uD1B1 / Codex (\uC124\uC815 \uD30C\uC77C\uB9CC \uBC1C\uACAC)",
|
|
364
|
+
async detect(io, ctx) {
|
|
365
|
+
if (ctx.cliDetected.has("codex")) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (await fileOrParentExists(io, codexConfigPath(io))) {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
return msixPackageInstalled(io, CHATGPT_DESKTOP_PACKAGE);
|
|
372
|
+
},
|
|
373
|
+
detectedPath(io) {
|
|
374
|
+
return codexConfigPath(io);
|
|
375
|
+
},
|
|
376
|
+
reason: "Codex \uBA85\uB839\uC5B4(CLI)\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC544 TOML \uC124\uC815 \uD30C\uC77C\uC744 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
377
|
+
pasteWhere: "\uC544\uB798 \uB0B4\uC6A9\uC744 \uC704 \uC124\uC815 \uD30C\uC77C\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
378
|
+
// The desktop app, the CLI, and the IDE extension share this one file.
|
|
379
|
+
createHint: "\uD30C\uC77C\uC774 \uC5C6\uC73C\uBA74 ChatGPT \uB370\uC2A4\uD06C\uD1B1\uC5D0\uC11C \uC124\uC815 \u2192 MCP \uC11C\uBC84 \u2192 \uC11C\uBC84 \uCD94\uAC00\uB97C \uD55C \uBC88 \uD558\uBA74 \uB9CC\uB4E4\uC5B4\uC838\uC694. \uD3F4\uB354\uAC00 \uC5C6\uC73C\uBA74 \uC9C1\uC811 \uB9CC\uB4E4\uC5B4\uB3C4 \uB3FC\uC694.",
|
|
380
|
+
bootstrapWhenAbsent: true,
|
|
381
|
+
buildSnippet(opts) {
|
|
382
|
+
return tomlSnippet(opts);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
var codexSkills = {
|
|
386
|
+
tier: 2,
|
|
387
|
+
skillsDir(io) {
|
|
388
|
+
return agentsSkillsDir(io);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
var codexConfigOnlySkills = {
|
|
392
|
+
tier: 3,
|
|
393
|
+
reason: "Codex CLI \uB97C \uCC3E\uC9C0 \uBABB\uD574 \uC2A4\uD0AC \uC704\uCE58\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC5B4\uC694.",
|
|
394
|
+
instruction: "Codex \uB97C \uC124\uCE58\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694."
|
|
395
|
+
};
|
|
396
|
+
var copilotCliFileHost = {
|
|
397
|
+
// A home override env var wins when set. Ignoring it would write beside
|
|
398
|
+
// the config the user moved, reporting success the app never sees.
|
|
399
|
+
id: "copilot-cli",
|
|
400
|
+
tier: 2,
|
|
401
|
+
displayName: "GitHub Copilot CLI",
|
|
402
|
+
configKey: "mcpServers",
|
|
403
|
+
entryKind: "stdio",
|
|
404
|
+
configPath(io) {
|
|
405
|
+
const home = io.env["COPILOT_HOME"];
|
|
406
|
+
if (home !== void 0 && home !== "") {
|
|
407
|
+
return joinPath(io, home, "mcp-config.json");
|
|
408
|
+
}
|
|
409
|
+
return resolveHostPath(io, {
|
|
410
|
+
kind: "home",
|
|
411
|
+
segments: [".copilot", "mcp-config.json"]
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
var copilotCliSkills = {
|
|
416
|
+
tier: 2,
|
|
417
|
+
skillsDir(io) {
|
|
418
|
+
return agentsSkillsDir(io);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
var cursorFileHost = {
|
|
422
|
+
id: "cursor",
|
|
423
|
+
tier: 2,
|
|
424
|
+
displayName: "Cursor",
|
|
425
|
+
configKey: "mcpServers",
|
|
426
|
+
// Speaks local HTTP MCP, so it connects to the daemon directly and no
|
|
427
|
+
// adapter process is spawned for it.
|
|
428
|
+
entryKind: "url",
|
|
429
|
+
configPath(io) {
|
|
430
|
+
return resolveHostPath(io, {
|
|
431
|
+
kind: "home",
|
|
432
|
+
segments: [".cursor", "mcp.json"]
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
var cursorSkills = {
|
|
437
|
+
tier: 2,
|
|
438
|
+
skillsDir(io) {
|
|
439
|
+
return agentsSkillsDir(io);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var geminiCliHost = {
|
|
443
|
+
id: "gemini",
|
|
444
|
+
tier: 1,
|
|
445
|
+
displayName: "Gemini CLI",
|
|
446
|
+
bin: "gemini",
|
|
447
|
+
// User scope, for the same cwd-binding reason as the Claude Code host.
|
|
448
|
+
buildAddArgs(opts) {
|
|
449
|
+
const args = ["mcp", "add", "-s", "user"];
|
|
450
|
+
for (const pair of envPairs(opts)) {
|
|
451
|
+
args.push("-e", pair);
|
|
452
|
+
}
|
|
453
|
+
args.push(SERVER_NAME, "npx", "-y", packageSpec(opts.version));
|
|
454
|
+
return args;
|
|
455
|
+
},
|
|
456
|
+
buildRemoveArgs() {
|
|
457
|
+
return ["mcp", "remove", "-s", "user", SERVER_NAME];
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
var geminiInstallable = {
|
|
461
|
+
id: "gemini",
|
|
462
|
+
displayName: "Gemini CLI",
|
|
463
|
+
npmPackage: "@google/gemini-cli"
|
|
464
|
+
};
|
|
465
|
+
var geminiSkills = {
|
|
466
|
+
tier: 2,
|
|
467
|
+
skillsDir(io) {
|
|
468
|
+
return agentsSkillsDir(io);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
var junieFileHost = {
|
|
472
|
+
// Documented strict-JSON user config, home-relative on every OS.
|
|
473
|
+
id: "junie",
|
|
474
|
+
tier: 2,
|
|
475
|
+
displayName: "JetBrains Junie",
|
|
476
|
+
configKey: "mcpServers",
|
|
477
|
+
entryKind: "stdio",
|
|
478
|
+
configPath(io) {
|
|
479
|
+
return resolveHostPath(io, {
|
|
480
|
+
kind: "home",
|
|
481
|
+
segments: [".junie", "mcp", "mcp.json"]
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
var junieSkills = {
|
|
486
|
+
tier: 2,
|
|
487
|
+
skillsDir(io) {
|
|
488
|
+
return resolveHostPath(io, {
|
|
489
|
+
kind: "home",
|
|
490
|
+
segments: [".junie", "skills"]
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
var kiroFileHost = {
|
|
495
|
+
// Documented strict-JSON user settings, home-relative on every OS.
|
|
496
|
+
id: "kiro",
|
|
497
|
+
tier: 2,
|
|
498
|
+
displayName: "Kiro",
|
|
499
|
+
configKey: "mcpServers",
|
|
500
|
+
entryKind: "stdio",
|
|
501
|
+
configPath(io) {
|
|
502
|
+
return resolveHostPath(io, {
|
|
503
|
+
kind: "home",
|
|
504
|
+
segments: [".kiro", "settings", "mcp.json"]
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
var kiroSkills = {
|
|
509
|
+
tier: 2,
|
|
510
|
+
skillsDir(io) {
|
|
511
|
+
return resolveHostPath(io, { kind: "home", segments: [".kiro", "skills"] });
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
var LMSTUDIO_PATH = {
|
|
515
|
+
kind: "home",
|
|
516
|
+
segments: [".lmstudio", "mcp.json"]
|
|
517
|
+
};
|
|
518
|
+
var lmstudioSnippetHost = {
|
|
519
|
+
// Same entry shape, but the exact path is only medium-confidence, so it
|
|
520
|
+
// stays a snippet: a wrong detection costs a missing card, while a wrong
|
|
521
|
+
// auto-write would be a false success.
|
|
522
|
+
id: "lmstudio",
|
|
523
|
+
tier: 3,
|
|
524
|
+
displayName: "LM Studio",
|
|
525
|
+
async detect(io) {
|
|
526
|
+
return fileOrParentExists(io, resolveHostPath(io, LMSTUDIO_PATH));
|
|
527
|
+
},
|
|
528
|
+
detectedPath(io) {
|
|
529
|
+
return resolveHostPath(io, LMSTUDIO_PATH);
|
|
530
|
+
},
|
|
531
|
+
reason: "\uC124\uC815 \uD30C\uC77C \uACBD\uB85C\uAC00 \uACF5\uC2DD\uC801\uC73C\uB85C \uD655\uC815\uB418\uC9C0 \uC54A\uC544 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
532
|
+
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcpServers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
533
|
+
buildSnippet(opts, platform) {
|
|
534
|
+
return jsonSnippet("mcpServers", opts, platform);
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
var lmstudioSkills = {
|
|
538
|
+
tier: 3,
|
|
539
|
+
reason: "LM Studio \uB294 \uC2A4\uD0AC(SKILL.md) \uAC1C\uB150\uC774 \uC5C6\uC5B4\uC694.",
|
|
540
|
+
instruction: "MCP \uC11C\uBC84 \uC5F0\uACB0\uB9CC \uC9C0\uC6D0\uB3FC\uC694."
|
|
541
|
+
};
|
|
542
|
+
var OPENCODE_PATH = {
|
|
543
|
+
kind: "homeConfig",
|
|
544
|
+
segments: ["opencode", "opencode.json"]
|
|
545
|
+
};
|
|
411
546
|
function opencodeConfigPath(io) {
|
|
412
547
|
const dir = io.env["OPENCODE_CONFIG_DIR"];
|
|
413
548
|
if (dir !== void 0 && dir !== "") {
|
|
@@ -431,186 +566,276 @@ function opencodeSnippet(opts) {
|
|
|
431
566
|
2
|
|
432
567
|
);
|
|
433
568
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
569
|
+
var opencodeSnippetHost = {
|
|
570
|
+
id: "opencode",
|
|
571
|
+
tier: 3,
|
|
572
|
+
displayName: "OpenCode",
|
|
573
|
+
async detect(io) {
|
|
574
|
+
return fileOrParentExists(io, opencodeConfigPath(io));
|
|
575
|
+
},
|
|
576
|
+
detectedPath(io) {
|
|
577
|
+
return opencodeConfigPath(io);
|
|
578
|
+
},
|
|
579
|
+
reason: "\uB3C4\uAD6C \uC804\uCCB4 \uC124\uC815 \uD30C\uC77C\uC774\uB77C \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
580
|
+
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcp" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
581
|
+
// Never created for this host: the file is the tool's whole config and a
|
|
582
|
+
// sibling dialect may already hold it, so creating ours would leave two
|
|
583
|
+
// config files with no way for the user to know which wins.
|
|
584
|
+
createHint: "\uC124\uC815 \uD30C\uC77C\uC740 opencode.json \uB610\uB294 opencode.jsonc \uC608\uC694. \uC4F0\uACE0 \uC788\uB294 \uCABD\uC5D0 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
585
|
+
buildSnippet(opts) {
|
|
586
|
+
return opencodeSnippet(opts);
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
var opencodeSkills = {
|
|
590
|
+
tier: 2,
|
|
591
|
+
skillsDir(io) {
|
|
592
|
+
return agentsSkillsDir(io);
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
function vscodeUserDir(io) {
|
|
596
|
+
return resolveHostPath(io, { kind: "appData", segments: ["Code", "User"] });
|
|
597
|
+
}
|
|
598
|
+
function vscodeConfigPath(io) {
|
|
599
|
+
const dir = vscodeUserDir(io);
|
|
600
|
+
if (dir === null) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
return joinPath(io, dir, "mcp.json");
|
|
604
|
+
}
|
|
605
|
+
var vscodeSnippetHost = {
|
|
606
|
+
id: "vscode",
|
|
607
|
+
tier: 3,
|
|
608
|
+
displayName: "VS Code",
|
|
609
|
+
async detect(io) {
|
|
610
|
+
const path = vscodeConfigPath(io);
|
|
611
|
+
if (path === null) {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
return fileOrParentExists(io, path);
|
|
615
|
+
},
|
|
616
|
+
detectedPath(io) {
|
|
617
|
+
return vscodeConfigPath(io);
|
|
618
|
+
},
|
|
619
|
+
reason: "\uC8FC\uC11D\uC774 \uC788\uB294 \uC124\uC815 \uD30C\uC77C\uC774\uB77C \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
620
|
+
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "servers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
621
|
+
// The hint names the profile too: a non-default profile keeps its own
|
|
622
|
+
// config elsewhere, so pasting into the path above would not reach it.
|
|
623
|
+
createHint: "\uD30C\uC77C\uC774 \uC5C6\uC73C\uBA74 VS Code \uC5D0\uC11C \uBA85\uB839 \uD314\uB808\uD2B8(Ctrl+Shift+P) \u2192 'MCP: Open User Configuration' \uC744 \uC2E4\uD589\uD558\uBA74 \uB9CC\uB4E4\uC5B4\uC838\uC694. \uAE30\uBCF8 \uD504\uB85C\uD544\uC774 \uC544\uB2C8\uBA74 \uADF8 \uD504\uB85C\uD544\uC758 mcp.json \uC774 User/profiles \uC544\uB798\uC5D0 \uB530\uB85C \uC788\uC73C\uB2C8, \uADF8 \uBA85\uB839\uC73C\uB85C \uC5F4\uB9B0 \uD30C\uC77C\uC5D0 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
624
|
+
bootstrapWhenAbsent: true,
|
|
625
|
+
buildSnippet(opts, platform) {
|
|
626
|
+
return jsonSnippet("servers", opts, platform);
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
var vscodeSkills = {
|
|
630
|
+
tier: 2,
|
|
631
|
+
skillsDir(io) {
|
|
632
|
+
return agentsSkillsDir(io);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
var WARP_PATH = {
|
|
636
|
+
kind: "home",
|
|
637
|
+
segments: [".warp", ".mcp.json"]
|
|
638
|
+
};
|
|
639
|
+
var warpSnippetHost = {
|
|
640
|
+
// Same entry shape, medium-confidence path — snippet, not auto-write.
|
|
641
|
+
id: "warp",
|
|
642
|
+
tier: 3,
|
|
643
|
+
displayName: "Warp",
|
|
644
|
+
async detect(io) {
|
|
645
|
+
return fileOrParentExists(io, resolveHostPath(io, WARP_PATH));
|
|
646
|
+
},
|
|
647
|
+
detectedPath(io) {
|
|
648
|
+
return resolveHostPath(io, WARP_PATH);
|
|
649
|
+
},
|
|
650
|
+
reason: "\uC124\uC815 \uD30C\uC77C \uACBD\uB85C\uAC00 \uACF5\uC2DD\uC801\uC73C\uB85C \uD655\uC815\uB418\uC9C0 \uC54A\uC544 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
651
|
+
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcpServers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
652
|
+
buildSnippet(opts, platform) {
|
|
653
|
+
return jsonSnippet("mcpServers", opts, platform);
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
var warpSkills = {
|
|
657
|
+
tier: 2,
|
|
658
|
+
skillsDir(io) {
|
|
659
|
+
return agentsSkillsDir(io);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
var windsurfFileHost = {
|
|
663
|
+
// Documented strict JSON, so a single-key merge is safe. The hygiene floor
|
|
664
|
+
// still refuses and falls back to a snippet if an install turns out to
|
|
665
|
+
// carry comments.
|
|
666
|
+
id: "windsurf",
|
|
667
|
+
tier: 2,
|
|
668
|
+
displayName: "Windsurf",
|
|
669
|
+
configKey: "mcpServers",
|
|
670
|
+
entryKind: "stdio",
|
|
671
|
+
configPath(io) {
|
|
672
|
+
return joinPath(
|
|
673
|
+
io,
|
|
674
|
+
io.homedir(),
|
|
675
|
+
".codeium",
|
|
676
|
+
"windsurf",
|
|
677
|
+
"mcp_config.json"
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
var windsurfSkills = {
|
|
682
|
+
tier: 2,
|
|
683
|
+
skillsDir(io) {
|
|
684
|
+
return agentsSkillsDir(io);
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
function zedConfigPath(io) {
|
|
688
|
+
return resolveHostPath(io, {
|
|
689
|
+
kind: "xdg",
|
|
690
|
+
segments: ["zed", "settings.json"],
|
|
691
|
+
win32Segments: ["Zed", "settings.json"]
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
var zedSnippetHost = {
|
|
695
|
+
id: "zed",
|
|
696
|
+
tier: 3,
|
|
697
|
+
displayName: "Zed",
|
|
698
|
+
async detect(io) {
|
|
699
|
+
const path = zedConfigPath(io);
|
|
700
|
+
if (path === null) {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
return fileOrParentExists(io, path);
|
|
704
|
+
},
|
|
705
|
+
detectedPath(io) {
|
|
706
|
+
return zedConfigPath(io);
|
|
707
|
+
},
|
|
708
|
+
reason: "\uC8FC\uC11D\uC774 \uC788\uB294 \uC124\uC815 \uD30C\uC77C\uC774\uB77C \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
709
|
+
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "context_servers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
710
|
+
buildSnippet(opts, platform) {
|
|
711
|
+
return jsonSnippet("context_servers", opts, platform);
|
|
437
712
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
713
|
+
};
|
|
714
|
+
var zedSkills = {
|
|
715
|
+
tier: 2,
|
|
716
|
+
skillsDir(io) {
|
|
717
|
+
return agentsSkillsDir(io);
|
|
441
718
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
719
|
+
};
|
|
720
|
+
var CLI_HOSTS = [
|
|
721
|
+
claudeCliHost,
|
|
722
|
+
codexCliHost,
|
|
723
|
+
geminiCliHost
|
|
724
|
+
];
|
|
725
|
+
var INSTALLABLE_CLIS = [
|
|
726
|
+
claudeInstallable,
|
|
727
|
+
geminiInstallable,
|
|
728
|
+
codexInstallable
|
|
729
|
+
];
|
|
730
|
+
var FILE_HOSTS = [
|
|
731
|
+
claudeDesktopFileHost,
|
|
732
|
+
cursorFileHost,
|
|
733
|
+
windsurfFileHost,
|
|
734
|
+
copilotCliFileHost,
|
|
735
|
+
junieFileHost,
|
|
736
|
+
kiroFileHost
|
|
737
|
+
];
|
|
738
|
+
function buildEntryForHost(host, opts, platform) {
|
|
739
|
+
if (host.entryKind === "url") {
|
|
740
|
+
return buildUrlEntry(opts);
|
|
447
741
|
}
|
|
742
|
+
return buildFileEntry(opts, platform);
|
|
448
743
|
}
|
|
449
|
-
var CHATGPT_DESKTOP_PACKAGE = "OpenAI.Codex_";
|
|
450
744
|
var SNIPPET_HOSTS = [
|
|
745
|
+
vscodeSnippetHost,
|
|
746
|
+
opencodeSnippetHost,
|
|
747
|
+
zedSnippetHost,
|
|
748
|
+
clineSnippetHost,
|
|
749
|
+
lmstudioSnippetHost,
|
|
750
|
+
warpSnippetHost,
|
|
751
|
+
claudeDesktopLinuxSnippetHost,
|
|
752
|
+
codexConfigOnlySnippetHost
|
|
753
|
+
];
|
|
754
|
+
var SKILL_HOSTS = [
|
|
451
755
|
{
|
|
452
|
-
id:
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
async detect(io) {
|
|
456
|
-
const path = vscodeConfigPath(io);
|
|
457
|
-
if (path === null) {
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
return fileOrParentExists(io, path);
|
|
461
|
-
},
|
|
462
|
-
detectedPath(io) {
|
|
463
|
-
return vscodeConfigPath(io);
|
|
464
|
-
},
|
|
465
|
-
reason: "\uC8FC\uC11D\uC774 \uC788\uB294 \uC124\uC815 \uD30C\uC77C\uC774\uB77C \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
466
|
-
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "servers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
467
|
-
// 🔴 The hint names the profile too: a non-default profile keeps its own
|
|
468
|
-
// config elsewhere, so pasting into the path above would not reach it.
|
|
469
|
-
createHint: "\uD30C\uC77C\uC774 \uC5C6\uC73C\uBA74 VS Code \uC5D0\uC11C \uBA85\uB839 \uD314\uB808\uD2B8(Ctrl+Shift+P) \u2192 'MCP: Open User Configuration' \uC744 \uC2E4\uD589\uD558\uBA74 \uB9CC\uB4E4\uC5B4\uC838\uC694. \uAE30\uBCF8 \uD504\uB85C\uD544\uC774 \uC544\uB2C8\uBA74 \uADF8 \uD504\uB85C\uD544\uC758 mcp.json \uC774 User/profiles \uC544\uB798\uC5D0 \uB530\uB85C \uC788\uC73C\uB2C8, \uADF8 \uBA85\uB839\uC73C\uB85C \uC5F4\uB9B0 \uD30C\uC77C\uC5D0 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
470
|
-
bootstrapWhenAbsent: true,
|
|
471
|
-
buildSnippet(opts, platform) {
|
|
472
|
-
return jsonSnippet("servers", opts, platform);
|
|
473
|
-
}
|
|
756
|
+
id: claudeCliHost.id,
|
|
757
|
+
displayName: claudeCliHost.displayName,
|
|
758
|
+
skills: claudeSkills
|
|
474
759
|
},
|
|
475
760
|
{
|
|
476
|
-
id:
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
async detect(io) {
|
|
480
|
-
return fileOrParentExists(io, opencodeConfigPath(io));
|
|
481
|
-
},
|
|
482
|
-
detectedPath(io) {
|
|
483
|
-
return opencodeConfigPath(io);
|
|
484
|
-
},
|
|
485
|
-
reason: "\uB3C4\uAD6C \uC804\uCCB4 \uC124\uC815 \uD30C\uC77C\uC774\uB77C \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
486
|
-
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcp" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
487
|
-
// 🔴 Never created for this host: the file is the tool's whole config and a
|
|
488
|
-
// sibling dialect may already hold it, so creating ours would leave two
|
|
489
|
-
// config files with no way for the user to know which wins.
|
|
490
|
-
createHint: "\uC124\uC815 \uD30C\uC77C\uC740 opencode.json \uB610\uB294 opencode.jsonc \uC608\uC694. \uC4F0\uACE0 \uC788\uB294 \uCABD\uC5D0 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
491
|
-
buildSnippet(opts) {
|
|
492
|
-
return opencodeSnippet(opts);
|
|
493
|
-
}
|
|
761
|
+
id: codexCliHost.id,
|
|
762
|
+
displayName: codexCliHost.displayName,
|
|
763
|
+
skills: codexSkills
|
|
494
764
|
},
|
|
495
765
|
{
|
|
496
|
-
id:
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
async detect(io) {
|
|
500
|
-
const path = zedConfigPath(io);
|
|
501
|
-
if (path === null) {
|
|
502
|
-
return false;
|
|
503
|
-
}
|
|
504
|
-
return fileOrParentExists(io, path);
|
|
505
|
-
},
|
|
506
|
-
detectedPath(io) {
|
|
507
|
-
return zedConfigPath(io);
|
|
508
|
-
},
|
|
509
|
-
reason: "\uC8FC\uC11D\uC774 \uC788\uB294 \uC124\uC815 \uD30C\uC77C\uC774\uB77C \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
510
|
-
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "context_servers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
511
|
-
buildSnippet(opts, platform) {
|
|
512
|
-
return jsonSnippet("context_servers", opts, platform);
|
|
513
|
-
}
|
|
766
|
+
id: geminiCliHost.id,
|
|
767
|
+
displayName: geminiCliHost.displayName,
|
|
768
|
+
skills: geminiSkills
|
|
514
769
|
},
|
|
515
770
|
{
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
tier: 3,
|
|
520
|
-
displayName: "Cline",
|
|
521
|
-
async detect(io) {
|
|
522
|
-
return fileOrParentExists(io, resolveHostPath(io, CLINE_PATH));
|
|
523
|
-
},
|
|
524
|
-
detectedPath(io) {
|
|
525
|
-
return resolveHostPath(io, CLINE_PATH);
|
|
526
|
-
},
|
|
527
|
-
reason: "\uC124\uC815 \uD30C\uC77C\uC744 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
528
|
-
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcpServers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
529
|
-
buildSnippet(opts, platform) {
|
|
530
|
-
return jsonSnippet("mcpServers", opts, platform);
|
|
531
|
-
}
|
|
771
|
+
id: claudeDesktopFileHost.id,
|
|
772
|
+
displayName: claudeDesktopFileHost.displayName,
|
|
773
|
+
skills: claudeDesktopSkills
|
|
532
774
|
},
|
|
533
775
|
{
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
id: "lmstudio",
|
|
538
|
-
tier: 3,
|
|
539
|
-
displayName: "LM Studio",
|
|
540
|
-
async detect(io) {
|
|
541
|
-
return fileOrParentExists(io, resolveHostPath(io, LMSTUDIO_PATH));
|
|
542
|
-
},
|
|
543
|
-
detectedPath(io) {
|
|
544
|
-
return resolveHostPath(io, LMSTUDIO_PATH);
|
|
545
|
-
},
|
|
546
|
-
reason: "\uC124\uC815 \uD30C\uC77C \uACBD\uB85C\uAC00 \uACF5\uC2DD\uC801\uC73C\uB85C \uD655\uC815\uB418\uC9C0 \uC54A\uC544 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
547
|
-
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcpServers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
548
|
-
buildSnippet(opts, platform) {
|
|
549
|
-
return jsonSnippet("mcpServers", opts, platform);
|
|
550
|
-
}
|
|
776
|
+
id: cursorFileHost.id,
|
|
777
|
+
displayName: cursorFileHost.displayName,
|
|
778
|
+
skills: cursorSkills
|
|
551
779
|
},
|
|
552
780
|
{
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
displayName: "Warp",
|
|
557
|
-
async detect(io) {
|
|
558
|
-
return fileOrParentExists(io, resolveHostPath(io, WARP_PATH));
|
|
559
|
-
},
|
|
560
|
-
detectedPath(io) {
|
|
561
|
-
return resolveHostPath(io, WARP_PATH);
|
|
562
|
-
},
|
|
563
|
-
reason: "\uC124\uC815 \uD30C\uC77C \uACBD\uB85C\uAC00 \uACF5\uC2DD\uC801\uC73C\uB85C \uD655\uC815\uB418\uC9C0 \uC54A\uC544 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
564
|
-
pasteWhere: '\uC544\uB798 \uB0B4\uC6A9\uC744 \uC124\uC815 \uD30C\uC77C\uC758 "mcpServers" \uD56D\uBAA9\uC5D0 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.',
|
|
565
|
-
buildSnippet(opts, platform) {
|
|
566
|
-
return jsonSnippet("mcpServers", opts, platform);
|
|
567
|
-
}
|
|
781
|
+
id: windsurfFileHost.id,
|
|
782
|
+
displayName: windsurfFileHost.displayName,
|
|
783
|
+
skills: windsurfSkills
|
|
568
784
|
},
|
|
569
785
|
{
|
|
570
|
-
id:
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
async detect(io) {
|
|
574
|
-
if (io.platform !== "linux") {
|
|
575
|
-
return false;
|
|
576
|
-
}
|
|
577
|
-
return io.exists(joinPath(io, io.homedir(), ".config", "Claude"));
|
|
578
|
-
},
|
|
579
|
-
detectedPath(io) {
|
|
580
|
-
return joinPath(io, io.homedir(), ".config", "Claude");
|
|
581
|
-
},
|
|
582
|
-
// 🔴 Guessing a path here would manufacture a false success that only
|
|
583
|
-
// surfaces after a restart.
|
|
584
|
-
reason: "\uB9AC\uB205\uC2A4\uC6A9 \uACF5\uC2DD \uC124\uC815 \uD30C\uC77C \uACBD\uB85C\uAC00 \uD655\uC778\uB418\uC9C0 \uC54A\uC544 \uC790\uB3D9 \uC218\uC815\uD558\uC9C0 \uC54A\uC544\uC694.",
|
|
585
|
-
pasteWhere: "Claude Desktop \uACF5\uC2DD \uBB38\uC11C\uAC00 \uC548\uB0B4\uD558\uB294 \uC124\uC815 \uD30C\uC77C(claude_desktop_config.json)\uC5D0 \uC544\uB798 \uB0B4\uC6A9\uC744 \uC9C1\uC811 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.",
|
|
586
|
-
buildSnippet(opts, platform) {
|
|
587
|
-
return jsonSnippet("mcpServers", opts, platform);
|
|
588
|
-
}
|
|
786
|
+
id: copilotCliFileHost.id,
|
|
787
|
+
displayName: copilotCliFileHost.displayName,
|
|
788
|
+
skills: copilotCliSkills
|
|
589
789
|
},
|
|
590
790
|
{
|
|
591
|
-
id:
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
791
|
+
id: junieFileHost.id,
|
|
792
|
+
displayName: junieFileHost.displayName,
|
|
793
|
+
skills: junieSkills
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
id: kiroFileHost.id,
|
|
797
|
+
displayName: kiroFileHost.displayName,
|
|
798
|
+
skills: kiroSkills
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
id: vscodeSnippetHost.id,
|
|
802
|
+
displayName: vscodeSnippetHost.displayName,
|
|
803
|
+
skills: vscodeSkills
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
id: opencodeSnippetHost.id,
|
|
807
|
+
displayName: opencodeSnippetHost.displayName,
|
|
808
|
+
skills: opencodeSkills
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
id: zedSnippetHost.id,
|
|
812
|
+
displayName: zedSnippetHost.displayName,
|
|
813
|
+
skills: zedSkills
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
id: clineSnippetHost.id,
|
|
817
|
+
displayName: clineSnippetHost.displayName,
|
|
818
|
+
skills: clineSkills
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
id: lmstudioSnippetHost.id,
|
|
822
|
+
displayName: lmstudioSnippetHost.displayName,
|
|
823
|
+
skills: lmstudioSkills
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
id: warpSnippetHost.id,
|
|
827
|
+
displayName: warpSnippetHost.displayName,
|
|
828
|
+
skills: warpSkills
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
id: claudeDesktopLinuxSnippetHost.id,
|
|
832
|
+
displayName: claudeDesktopLinuxSnippetHost.displayName,
|
|
833
|
+
skills: claudeDesktopLinuxSkills
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
id: codexConfigOnlySnippetHost.id,
|
|
837
|
+
displayName: codexConfigOnlySnippetHost.displayName,
|
|
838
|
+
skills: codexConfigOnlySkills
|
|
614
839
|
}
|
|
615
840
|
];
|
|
616
841
|
var SUPPORTED_APPS = [
|
|
@@ -631,6 +856,256 @@ var SUPPORTED_APPS = [
|
|
|
631
856
|
{ name: "LM Studio", url: "https://lmstudio.ai/" },
|
|
632
857
|
{ name: "Warp", url: "https://www.warp.dev/" }
|
|
633
858
|
];
|
|
859
|
+
var MARKER_NAME = ".datalab-installed.json";
|
|
860
|
+
var SOURCE = "@modootoday/datalab-extension-mcp";
|
|
861
|
+
function join(io, ...parts) {
|
|
862
|
+
return io.platform === "win32" ? parts.join("\\") : parts.join("/");
|
|
863
|
+
}
|
|
864
|
+
function dirOf(rel) {
|
|
865
|
+
const cut = rel.lastIndexOf("/");
|
|
866
|
+
return cut === -1 ? "" : rel.slice(0, cut);
|
|
867
|
+
}
|
|
868
|
+
async function readMarker(io, dir) {
|
|
869
|
+
const path = join(io, dir, MARKER_NAME);
|
|
870
|
+
if (!await io.exists(path)) {
|
|
871
|
+
return "none";
|
|
872
|
+
}
|
|
873
|
+
let raw;
|
|
874
|
+
try {
|
|
875
|
+
raw = await io.readFile(path);
|
|
876
|
+
} catch {
|
|
877
|
+
return "bad";
|
|
878
|
+
}
|
|
879
|
+
try {
|
|
880
|
+
const parsed = JSON.parse(raw);
|
|
881
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.source !== "string" || !Array.isArray(parsed.files)) {
|
|
882
|
+
return "bad";
|
|
883
|
+
}
|
|
884
|
+
return parsed;
|
|
885
|
+
} catch {
|
|
886
|
+
return "bad";
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
async function inspectSkillDir(io, root, slug) {
|
|
890
|
+
const dir = join(io, root, slug);
|
|
891
|
+
if (!await io.exists(dir)) {
|
|
892
|
+
return { kind: "absent" };
|
|
893
|
+
}
|
|
894
|
+
const marker = await readMarker(io, dir);
|
|
895
|
+
if (marker === "none") return { kind: "refused", refusal: "unmarked" };
|
|
896
|
+
if (marker === "bad") return { kind: "refused", refusal: "unreadable" };
|
|
897
|
+
if (marker.source !== SOURCE) return { kind: "refused", refusal: "foreign" };
|
|
898
|
+
return {
|
|
899
|
+
kind: "ours",
|
|
900
|
+
version: marker.version,
|
|
901
|
+
installedAt: marker.installedAt
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
async function installSkillDir(io, root, payload, version) {
|
|
905
|
+
const dir = join(io, root, payload.slug);
|
|
906
|
+
const files = [];
|
|
907
|
+
if (await io.exists(dir)) {
|
|
908
|
+
const marker2 = await readMarker(io, dir);
|
|
909
|
+
if (marker2 === "none") {
|
|
910
|
+
return { ok: false, path: dir, refusal: "unmarked", files };
|
|
911
|
+
}
|
|
912
|
+
if (marker2 === "bad") {
|
|
913
|
+
return { ok: false, path: dir, refusal: "unreadable", files };
|
|
914
|
+
}
|
|
915
|
+
if (marker2.source !== SOURCE) {
|
|
916
|
+
return { ok: false, path: dir, refusal: "foreign", files };
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
await io.mkdir(dir);
|
|
920
|
+
for (const [rel, content] of payload.files) {
|
|
921
|
+
const sub = dirOf(rel);
|
|
922
|
+
if (sub !== "") {
|
|
923
|
+
await io.mkdir(join(io, dir, ...sub.split("/")));
|
|
924
|
+
}
|
|
925
|
+
await io.writeFile(join(io, dir, ...rel.split("/")), content);
|
|
926
|
+
files.push(rel);
|
|
927
|
+
}
|
|
928
|
+
const marker = {
|
|
929
|
+
source: SOURCE,
|
|
930
|
+
version,
|
|
931
|
+
installedAt: io.now().toISOString(),
|
|
932
|
+
files
|
|
933
|
+
};
|
|
934
|
+
await io.writeFile(
|
|
935
|
+
join(io, dir, MARKER_NAME),
|
|
936
|
+
`${JSON.stringify(marker, null, 2)}
|
|
937
|
+
`
|
|
938
|
+
);
|
|
939
|
+
return { ok: true, path: dir, files };
|
|
940
|
+
}
|
|
941
|
+
async function removeSkillDir(io, root, slug) {
|
|
942
|
+
const dir = join(io, root, slug);
|
|
943
|
+
const removed = [];
|
|
944
|
+
if (!await io.exists(dir)) {
|
|
945
|
+
return { ok: true, path: dir, files: removed };
|
|
946
|
+
}
|
|
947
|
+
const marker = await readMarker(io, dir);
|
|
948
|
+
if (marker === "none") {
|
|
949
|
+
return { ok: false, path: dir, refusal: "unmarked", files: removed };
|
|
950
|
+
}
|
|
951
|
+
if (marker === "bad") {
|
|
952
|
+
return { ok: false, path: dir, refusal: "unreadable", files: removed };
|
|
953
|
+
}
|
|
954
|
+
if (marker.source !== SOURCE) {
|
|
955
|
+
return { ok: false, path: dir, refusal: "foreign", files: removed };
|
|
956
|
+
}
|
|
957
|
+
for (const rel of marker.files) {
|
|
958
|
+
const path = join(io, dir, ...rel.split("/"));
|
|
959
|
+
try {
|
|
960
|
+
await io.unlink(path);
|
|
961
|
+
removed.push(rel);
|
|
962
|
+
} catch {
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
try {
|
|
966
|
+
await io.unlink(join(io, dir, MARKER_NAME));
|
|
967
|
+
} catch {
|
|
968
|
+
}
|
|
969
|
+
const subs = [...new Set(marker.files.map(dirOf).filter((d) => d !== ""))];
|
|
970
|
+
for (const sub of subs.sort((a, b) => b.length - a.length)) {
|
|
971
|
+
try {
|
|
972
|
+
await io.rmdir(join(io, dir, ...sub.split("/")));
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
try {
|
|
977
|
+
await io.rmdir(dir);
|
|
978
|
+
} catch {
|
|
979
|
+
}
|
|
980
|
+
return { ok: true, path: dir, files: removed };
|
|
981
|
+
}
|
|
982
|
+
var PAYLOAD_DIR = "skills";
|
|
983
|
+
function skillsPayloadRoot() {
|
|
984
|
+
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
985
|
+
return joinNative(here, "..", PAYLOAD_DIR);
|
|
986
|
+
}
|
|
987
|
+
function join2(io, ...parts) {
|
|
988
|
+
return io.platform === "win32" ? parts.join("\\") : parts.join("/");
|
|
989
|
+
}
|
|
990
|
+
async function readPayloads(io, root) {
|
|
991
|
+
if (!await io.exists(root)) {
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
const out = [];
|
|
995
|
+
for (const slug of await io.listDir(root)) {
|
|
996
|
+
const files = /* @__PURE__ */ new Map();
|
|
997
|
+
const skillMd = join2(io, root, slug, "SKILL.md");
|
|
998
|
+
if (!await io.exists(skillMd)) {
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
files.set("SKILL.md", await io.readFile(skillMd));
|
|
1002
|
+
const refs = join2(io, root, slug, "references");
|
|
1003
|
+
if (await io.exists(refs)) {
|
|
1004
|
+
for (const name of await io.listDir(refs)) {
|
|
1005
|
+
files.set(
|
|
1006
|
+
`references/${name}`,
|
|
1007
|
+
await io.readFile(join2(io, refs, name))
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
out.push({ slug, files });
|
|
1012
|
+
}
|
|
1013
|
+
return out;
|
|
1014
|
+
}
|
|
1015
|
+
var CONDITIONAL = /* @__PURE__ */ new Map([
|
|
1016
|
+
["datalab-card-news", "\uC0AC\uC9C4 \uD3B8\uC9D1\uAE30 \uCE94\uBC84\uC2A4\uAC00 \uC5F4\uB824 \uC788\uC5B4\uC57C \uC2DC\uC791\uD574\uC694"],
|
|
1017
|
+
["datalab-video-script", "\uC601\uC0C1 \uD3B8\uC9D1\uAE30 \uD0C0\uC784\uB77C\uC778\uC774 \uC5F4\uB824 \uC788\uC5B4\uC57C \uC2DC\uC791\uD574\uC694"],
|
|
1018
|
+
["datalab-photo-prompt", "AI \uC0AC\uC9C4 \uC0DD\uC131\uC740 \uD655\uC7A5 \uD328\uB110\uC5D0\uC11C\uB9CC \uB3FC\uC694"]
|
|
1019
|
+
]);
|
|
1020
|
+
var REFUSAL_MESSAGE = {
|
|
1021
|
+
unmarked: "\uB2E4\uB978 \uB3C4\uAD6C\uAC00 \uB9CC\uB4E0 \uD3F4\uB354\uAC00 \uC788\uC5B4 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uC5B4\uC694.",
|
|
1022
|
+
foreign: "\uB2E4\uB978 \uB3C4\uAD6C\uAC00 \uC124\uCE58\uD55C \uC2A4\uD0AC\uC774 \uC788\uC5B4 \uADF8\uB300\uB85C \uB480\uC5B4\uC694.",
|
|
1023
|
+
unreadable: "\uC124\uCE58 \uD45C\uC2DD\uC744 \uC77D\uC744 \uC218 \uC5C6\uC5B4 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uC5B4\uC694."
|
|
1024
|
+
};
|
|
1025
|
+
async function placeSkills(io, payloadRoot, detectedIds, version, all = false) {
|
|
1026
|
+
const results = [];
|
|
1027
|
+
const staged = await readPayloads(io, payloadRoot);
|
|
1028
|
+
if (staged === null || staged.length === 0) {
|
|
1029
|
+
return results;
|
|
1030
|
+
}
|
|
1031
|
+
const payloads = all ? staged : staged.filter((p) => !CONDITIONAL.has(p.slug));
|
|
1032
|
+
const held = staged.length - payloads.length;
|
|
1033
|
+
for (const host of SKILL_HOSTS) {
|
|
1034
|
+
if (!detectedIds.has(host.id)) {
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
if (host.skills.tier === 3) {
|
|
1038
|
+
results.push({
|
|
1039
|
+
hostId: host.id,
|
|
1040
|
+
displayName: host.displayName,
|
|
1041
|
+
tier: 3,
|
|
1042
|
+
status: "skipped",
|
|
1043
|
+
surface: "skills",
|
|
1044
|
+
message: `${host.skills.reason} ${host.skills.instruction}`
|
|
1045
|
+
});
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
const dir = host.skills.skillsDir(io);
|
|
1049
|
+
if (dir === null) {
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
const refused = [];
|
|
1053
|
+
let placed = 0;
|
|
1054
|
+
for (const payload of payloads) {
|
|
1055
|
+
const out = await installSkillDir(io, dir, payload, version);
|
|
1056
|
+
if (out.ok) {
|
|
1057
|
+
placed += 1;
|
|
1058
|
+
} else {
|
|
1059
|
+
refused.push(`${payload.slug}: ${REFUSAL_MESSAGE[out.refusal ?? ""]}`);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
const heldNote = held > 0 ? ` ${held}\uAC1C\uB294 \uC870\uAC74\uBD80\uB77C \uBE90\uC5B4\uC694 \u2014 skills list \uB85C \uD655\uC778\uD558\uC138\uC694.` : "";
|
|
1063
|
+
results.push({
|
|
1064
|
+
hostId: host.id,
|
|
1065
|
+
displayName: host.displayName,
|
|
1066
|
+
tier: 2,
|
|
1067
|
+
status: placed > 0 ? "success" : "skipped",
|
|
1068
|
+
surface: "skills",
|
|
1069
|
+
message: refused.length > 0 ? `\uC2A4\uD0AC ${placed}\uAC1C \uC124\uCE58. ${refused.join(" ")}${heldNote}` : `\uC2A4\uD0AC ${placed}\uAC1C \uC124\uCE58.${heldNote}`
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
return results;
|
|
1073
|
+
}
|
|
1074
|
+
async function stagedSlugs(io, root) {
|
|
1075
|
+
const payloads = await readPayloads(io, root);
|
|
1076
|
+
return payloads === null ? [] : payloads.map((p) => p.slug);
|
|
1077
|
+
}
|
|
1078
|
+
async function removeSkills(io, detectedIds, slugs) {
|
|
1079
|
+
const results = [];
|
|
1080
|
+
for (const host of SKILL_HOSTS) {
|
|
1081
|
+
if (!detectedIds.has(host.id) || host.skills.tier !== 2) {
|
|
1082
|
+
continue;
|
|
1083
|
+
}
|
|
1084
|
+
const dir = host.skills.skillsDir(io);
|
|
1085
|
+
if (dir === null) {
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
let removed = 0;
|
|
1089
|
+
const kept = [];
|
|
1090
|
+
for (const slug of slugs) {
|
|
1091
|
+
const out = await removeSkillDir(io, dir, slug);
|
|
1092
|
+
if (out.ok) {
|
|
1093
|
+
removed += out.files.length > 0 ? 1 : 0;
|
|
1094
|
+
} else {
|
|
1095
|
+
kept.push(slug);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
results.push({
|
|
1099
|
+
hostId: host.id,
|
|
1100
|
+
displayName: host.displayName,
|
|
1101
|
+
tier: 2,
|
|
1102
|
+
status: "success",
|
|
1103
|
+
surface: "skills",
|
|
1104
|
+
message: kept.length > 0 ? `\uC2A4\uD0AC ${removed}\uAC1C \uC81C\uAC70. ${kept.length}\uAC1C\uB294 \uC6B0\uB9AC\uAC00 \uC124\uCE58\uD55C \uAC83\uC774 \uC544\uB2C8\uB77C \uADF8\uB300\uB85C \uB480\uC5B4\uC694.` : `\uC2A4\uD0AC ${removed}\uAC1C \uC81C\uAC70`
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
return results;
|
|
1108
|
+
}
|
|
634
1109
|
var RESTART_NOTICE = "\uB9C8\uC9C0\uB9C9 \uD55C \uB2E8\uACC4: AI \uC571\uC744 \uC644\uC804\uD788 \uC885\uB8CC\uD588\uB2E4\uAC00 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.\n(Windows: \uC791\uC5C5 \uD45C\uC2DC\uC904 \uD2B8\uB808\uC774 \uC544\uC774\uCF58\uC5D0\uC11C \uC885\uB8CC)";
|
|
635
1110
|
var NOTHING_CHANGED = "\uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC5B4\uC694.";
|
|
636
1111
|
var UNINSTALL_DONE = "\uC815\uB9AC\uAC00 \uB05D\uB0AC\uC5B4\uC694.";
|
|
@@ -1382,6 +1857,17 @@ async function runInstall(opts, io) {
|
|
|
1382
1857
|
});
|
|
1383
1858
|
}
|
|
1384
1859
|
}
|
|
1860
|
+
if (opts.skills !== false) {
|
|
1861
|
+
results.push(
|
|
1862
|
+
...await placeSkills(
|
|
1863
|
+
io,
|
|
1864
|
+
skillsPayloadRoot(),
|
|
1865
|
+
new Set(detected.map((d) => d.id)),
|
|
1866
|
+
opts.version,
|
|
1867
|
+
opts.allSkills === true
|
|
1868
|
+
)
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1385
1871
|
io.out("");
|
|
1386
1872
|
for (const result of results) {
|
|
1387
1873
|
printResult(io, result);
|
|
@@ -1457,6 +1943,16 @@ async function runUninstall(opts, io) {
|
|
|
1457
1943
|
});
|
|
1458
1944
|
}
|
|
1459
1945
|
}
|
|
1946
|
+
if (opts.skills !== false) {
|
|
1947
|
+
const root = skillsPayloadRoot();
|
|
1948
|
+
results.push(
|
|
1949
|
+
...await removeSkills(
|
|
1950
|
+
io,
|
|
1951
|
+
new Set(detected.map((d) => d.id)),
|
|
1952
|
+
await stagedSlugs(io, root)
|
|
1953
|
+
)
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1460
1956
|
io.out("");
|
|
1461
1957
|
for (const result of results) {
|
|
1462
1958
|
printResult(io, result);
|
|
@@ -1470,6 +1966,88 @@ async function runUninstall(opts, io) {
|
|
|
1470
1966
|
}
|
|
1471
1967
|
return 0;
|
|
1472
1968
|
}
|
|
1969
|
+
function descriptionOf(skillMd) {
|
|
1970
|
+
const fm = /^---\n([\s\S]*?)\n---/.exec(skillMd);
|
|
1971
|
+
const line = /^description:\s*(.*)$/m.exec(fm?.[1] ?? "");
|
|
1972
|
+
return (line?.[1] ?? "").trim();
|
|
1973
|
+
}
|
|
1974
|
+
async function listSkills(io, payloadRoot) {
|
|
1975
|
+
const payloads = await readPayloads(io, payloadRoot);
|
|
1976
|
+
if (payloads === null) return [];
|
|
1977
|
+
return payloads.map((p) => ({
|
|
1978
|
+
slug: p.slug,
|
|
1979
|
+
description: descriptionOf(p.files.get("SKILL.md") ?? "")
|
|
1980
|
+
}));
|
|
1981
|
+
}
|
|
1982
|
+
async function skillStatus(io, payloadRoot) {
|
|
1983
|
+
const shipped = (await listSkills(io, payloadRoot)).map((s) => s.slug);
|
|
1984
|
+
const byDir = /* @__PURE__ */ new Map();
|
|
1985
|
+
const unsupported = [];
|
|
1986
|
+
for (const host of SKILL_HOSTS) {
|
|
1987
|
+
if (host.skills.tier === 3) {
|
|
1988
|
+
unsupported.push({ host: host.displayName, reason: host.skills.reason });
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
const dir = host.skills.skillsDir(io);
|
|
1992
|
+
if (dir === null) continue;
|
|
1993
|
+
byDir.set(dir, [...byDir.get(dir) ?? [], host.displayName]);
|
|
1994
|
+
}
|
|
1995
|
+
const dirs = [];
|
|
1996
|
+
for (const [dir, hosts] of byDir) {
|
|
1997
|
+
const ours = [];
|
|
1998
|
+
const refused = [];
|
|
1999
|
+
const absent = [];
|
|
2000
|
+
for (const slug of shipped) {
|
|
2001
|
+
const state = await inspectSkillDir(io, dir, slug);
|
|
2002
|
+
if (state.kind === "ours") ours.push({ slug, version: state.version });
|
|
2003
|
+
else if (state.kind === "refused")
|
|
2004
|
+
refused.push({ slug, refusal: state.refusal });
|
|
2005
|
+
else absent.push(slug);
|
|
2006
|
+
}
|
|
2007
|
+
dirs.push({ dir, hosts, ours, refused, absent });
|
|
2008
|
+
}
|
|
2009
|
+
return { dirs, unsupported };
|
|
2010
|
+
}
|
|
2011
|
+
async function targetDirs(io) {
|
|
2012
|
+
const detected = new Set((await detectHosts(io)).map((d) => d.id));
|
|
2013
|
+
const byDir = /* @__PURE__ */ new Map();
|
|
2014
|
+
for (const host of SKILL_HOSTS) {
|
|
2015
|
+
if (host.skills.tier !== 2 || !detected.has(host.id)) continue;
|
|
2016
|
+
const dir = host.skills.skillsDir(io);
|
|
2017
|
+
if (dir === null) continue;
|
|
2018
|
+
byDir.set(dir, [...byDir.get(dir) ?? [], host.displayName]);
|
|
2019
|
+
}
|
|
2020
|
+
return byDir;
|
|
2021
|
+
}
|
|
2022
|
+
async function attachSkill(io, payloadRoot, slug, version) {
|
|
2023
|
+
const payloads = await readPayloads(io, payloadRoot) ?? [];
|
|
2024
|
+
const payload = payloads.find((p) => p.slug === slug);
|
|
2025
|
+
if (payload === void 0) {
|
|
2026
|
+
return { kind: "unknown-slug", shipped: payloads.map((p) => p.slug) };
|
|
2027
|
+
}
|
|
2028
|
+
const outcomes = [];
|
|
2029
|
+
for (const [dir, hosts] of await targetDirs(io)) {
|
|
2030
|
+
const out = await installSkillDir(io, dir, payload, version);
|
|
2031
|
+
outcomes.push(
|
|
2032
|
+
out.ok ? { dir, hosts } : { dir, hosts, refusal: out.refusal }
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
return { kind: "moved", outcomes };
|
|
2036
|
+
}
|
|
2037
|
+
async function detachSkill(io, payloadRoot, slug) {
|
|
2038
|
+
const shipped = (await listSkills(io, payloadRoot)).map((s) => s.slug);
|
|
2039
|
+
if (!shipped.includes(slug)) {
|
|
2040
|
+
return { kind: "unknown-slug", shipped };
|
|
2041
|
+
}
|
|
2042
|
+
const outcomes = [];
|
|
2043
|
+
for (const [dir, hosts] of await targetDirs(io)) {
|
|
2044
|
+
const out = await removeSkillDir(io, dir, slug);
|
|
2045
|
+
outcomes.push(
|
|
2046
|
+
out.ok ? { dir, hosts } : { dir, hosts, refusal: out.refusal }
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
return { kind: "moved", outcomes };
|
|
2050
|
+
}
|
|
1473
2051
|
function createNodeIo() {
|
|
1474
2052
|
return {
|
|
1475
2053
|
platform: process.platform,
|
|
@@ -1500,6 +2078,9 @@ function createNodeIo() {
|
|
|
1500
2078
|
async unlink(path) {
|
|
1501
2079
|
await fs.unlink(path);
|
|
1502
2080
|
},
|
|
2081
|
+
async rmdir(path) {
|
|
2082
|
+
await fs.rmdir(path);
|
|
2083
|
+
},
|
|
1503
2084
|
async listDir(dir) {
|
|
1504
2085
|
try {
|
|
1505
2086
|
return await fs.readdir(dir);
|
|
@@ -1568,47 +2149,56 @@ function createNodeIo() {
|
|
|
1568
2149
|
};
|
|
1569
2150
|
}
|
|
1570
2151
|
var PACKAGE_NAME = "@modootoday/extension-app-mcp-installer";
|
|
2152
|
+
|
|
1571
2153
|
export {
|
|
1572
|
-
|
|
1573
|
-
CLI_HOSTS,
|
|
1574
|
-
CODEX_CHATGPT_NOTE,
|
|
1575
|
-
DEFAULT_PORT,
|
|
1576
|
-
EXTENSION_ID_RE,
|
|
1577
|
-
FILE_HOSTS,
|
|
1578
|
-
INSTALLABLE_CLIS,
|
|
2154
|
+
printBanner,
|
|
1579
2155
|
INSTALL_SUBTITLE,
|
|
1580
|
-
|
|
1581
|
-
INVALID_PORT_MESSAGE,
|
|
1582
|
-
INVALID_TOKEN_MESSAGE,
|
|
1583
|
-
INVALID_VERSION_MESSAGE,
|
|
1584
|
-
NOTHING_CHANGED,
|
|
1585
|
-
NO_HOSTS_DETECTED,
|
|
1586
|
-
PACKAGE_NAME,
|
|
1587
|
-
PERMISSION_DENIED_HINT,
|
|
1588
|
-
PORT_RE,
|
|
1589
|
-
RESTART_NOTICE,
|
|
2156
|
+
UNINSTALL_SUBTITLE,
|
|
1590
2157
|
SERVER_NAME,
|
|
1591
2158
|
SERVER_PACKAGE,
|
|
2159
|
+
DEFAULT_PORT,
|
|
2160
|
+
packageSpec,
|
|
2161
|
+
buildEnv,
|
|
2162
|
+
buildFileEntry,
|
|
2163
|
+
CODEX_CHATGPT_NOTE,
|
|
2164
|
+
CLI_HOSTS,
|
|
2165
|
+
INSTALLABLE_CLIS,
|
|
2166
|
+
FILE_HOSTS,
|
|
1592
2167
|
SNIPPET_HOSTS,
|
|
1593
2168
|
SUPPORTED_APPS,
|
|
1594
|
-
|
|
2169
|
+
skillsPayloadRoot,
|
|
2170
|
+
RESTART_NOTICE,
|
|
2171
|
+
NOTHING_CHANGED,
|
|
1595
2172
|
UNINSTALL_DONE,
|
|
1596
|
-
UNINSTALL_SUBTITLE,
|
|
1597
2173
|
UNINSTALL_TOKEN_REMINDER,
|
|
2174
|
+
PERMISSION_DENIED_HINT,
|
|
2175
|
+
NO_HOSTS_DETECTED,
|
|
2176
|
+
TOKEN_PROMPT_GUIDE,
|
|
2177
|
+
TOKEN_PROMPT_QUESTION,
|
|
2178
|
+
TOKEN_REQUIRED_NON_INTERACTIVE,
|
|
2179
|
+
installQuestion,
|
|
2180
|
+
uninstallQuestion,
|
|
2181
|
+
TOKEN_RE,
|
|
2182
|
+
EXTENSION_ID_RE,
|
|
2183
|
+
PORT_RE,
|
|
1598
2184
|
VERSION_RE,
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
2185
|
+
INVALID_TOKEN_MESSAGE,
|
|
2186
|
+
INVALID_EXTENSION_ID_MESSAGE,
|
|
2187
|
+
INVALID_PORT_MESSAGE,
|
|
2188
|
+
INVALID_VERSION_MESSAGE,
|
|
2189
|
+
validateInstallOptions,
|
|
2190
|
+
validateUninstallOptions,
|
|
2191
|
+
BACKUP_KEEP,
|
|
1603
2192
|
formatBackupTimestamp,
|
|
1604
|
-
|
|
1605
|
-
packageSpec,
|
|
1606
|
-
printBanner,
|
|
2193
|
+
upsertServerKey,
|
|
1607
2194
|
removeServerKey,
|
|
2195
|
+
detectHosts,
|
|
1608
2196
|
runInstall,
|
|
1609
2197
|
runUninstall,
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
2198
|
+
listSkills,
|
|
2199
|
+
skillStatus,
|
|
2200
|
+
attachSkill,
|
|
2201
|
+
detachSkill,
|
|
2202
|
+
createNodeIo,
|
|
2203
|
+
PACKAGE_NAME
|
|
1614
2204
|
};
|