@cjhyy/code-shell-server 0.9.8 → 0.9.9
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 +85 -23
- package/dist/bin/code-shell-project-runtime.d.ts +2 -0
- package/dist/bin/code-shell-project-runtime.js +8 -0
- package/dist/desktop-web/http-api.d.ts +43 -0
- package/dist/desktop-web/http-api.js +376 -0
- package/dist/hub/approval-lease.d.ts +30 -0
- package/dist/hub/approval-lease.js +89 -0
- package/dist/hub/auth-http.d.ts +23 -0
- package/dist/hub/auth-http.js +259 -0
- package/dist/hub/auth-store.d.ts +52 -0
- package/dist/hub/auth-store.js +316 -0
- package/dist/hub/configuration.d.ts +64 -0
- package/dist/hub/configuration.js +555 -0
- package/dist/hub/files.d.ts +9 -0
- package/dist/hub/files.js +278 -0
- package/dist/hub/mcp-configuration.d.ts +51 -0
- package/dist/hub/mcp-configuration.js +558 -0
- package/dist/hub/mcp-probe.d.ts +24 -0
- package/dist/hub/mcp-probe.js +392 -0
- package/dist/hub/model-probe.d.ts +20 -0
- package/dist/hub/model-probe.js +193 -0
- package/dist/hub/outbound-transport.d.ts +36 -0
- package/dist/hub/outbound-transport.js +122 -0
- package/dist/hub/run-replay.d.ts +42 -0
- package/dist/hub/run-replay.js +89 -0
- package/dist/hub/session-management.d.ts +36 -0
- package/dist/hub/session-management.js +405 -0
- package/dist/hub/skills-management.d.ts +43 -0
- package/dist/hub/skills-management.js +487 -0
- package/dist/hub/uploads.d.ts +22 -0
- package/dist/hub/uploads.js +241 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.desktop-web.d.ts +2 -0
- package/dist/index.desktop-web.js +2 -0
- package/dist/index.js +3 -0
- package/dist/index.links.d.ts +6 -0
- package/dist/index.links.js +6 -0
- package/dist/index.panels.d.ts +9 -0
- package/dist/index.panels.js +8 -0
- package/dist/index.serve.d.ts +1 -0
- package/dist/index.serve.js +1 -0
- package/dist/index.storage.d.ts +2 -0
- package/dist/index.storage.js +2 -0
- package/dist/links/catalog.d.ts +10 -0
- package/dist/links/catalog.js +41 -0
- package/dist/links/device-oauth.d.ts +52 -0
- package/dist/links/device-oauth.js +255 -0
- package/dist/links/http.d.ts +34 -0
- package/dist/links/http.js +144 -0
- package/dist/links/service.d.ts +54 -0
- package/dist/links/service.js +578 -0
- package/dist/links/types.d.ts +6 -0
- package/dist/links/types.js +1 -0
- package/dist/mobile-remote/mobile-client-event-validator.js +5 -2
- package/dist/mobile-remote/pending-approvals.js +2 -0
- package/dist/mobile-remote/remote-host-manager.d.ts +8 -0
- package/dist/mobile-remote/remote-host-manager.js +90 -15
- package/dist/mobile-remote/room-manager.js +17 -5
- package/dist/panels/agent-task-host.d.ts +32 -0
- package/dist/panels/agent-task-host.js +457 -0
- package/dist/panels/agent-task-models.d.ts +27 -0
- package/dist/panels/agent-task-models.js +51 -0
- package/dist/panels/agent-task-service.d.ts +105 -0
- package/dist/panels/agent-task-service.js +283 -0
- package/dist/panels/http.d.ts +34 -0
- package/dist/panels/http.js +37 -0
- package/dist/panels/hub-binding.d.ts +12 -0
- package/dist/panels/hub-binding.js +174 -0
- package/dist/panels/management-http.d.ts +29 -0
- package/dist/panels/management-http.js +119 -0
- package/dist/panels/management.d.ts +39 -0
- package/dist/panels/management.js +494 -0
- package/dist/panels/process-files.d.ts +14 -0
- package/dist/panels/process-files.js +299 -0
- package/dist/panels/process-service.d.ts +96 -0
- package/dist/panels/process-service.js +501 -0
- package/dist/panels/runtime-services.d.ts +24 -0
- package/dist/panels/runtime-services.js +462 -0
- package/dist/panels/runtime.d.ts +51 -0
- package/dist/panels/runtime.js +1063 -0
- package/dist/panels/storage-store.d.ts +9 -0
- package/dist/panels/storage-store.js +154 -0
- package/dist/panels/types.d.ts +52 -0
- package/dist/panels/types.js +1 -0
- package/dist/project-runtime/control-server.d.ts +19 -0
- package/dist/project-runtime/control-server.js +300 -0
- package/dist/project-runtime/docker-provider.d.ts +33 -0
- package/dist/project-runtime/docker-provider.js +487 -0
- package/dist/project-runtime/lock.d.ts +6 -0
- package/dist/project-runtime/lock.js +43 -0
- package/dist/project-runtime/managed-entry.d.ts +10 -0
- package/dist/project-runtime/managed-entry.js +113 -0
- package/dist/project-runtime/manager.d.ts +40 -0
- package/dist/project-runtime/manager.js +249 -0
- package/dist/project-runtime/proxy.d.ts +22 -0
- package/dist/project-runtime/proxy.js +706 -0
- package/dist/project-runtime/registry.d.ts +54 -0
- package/dist/project-runtime/registry.js +286 -0
- package/dist/project-runtime/types.d.ts +43 -0
- package/dist/project-runtime/types.js +1 -0
- package/dist/serve/cli.d.ts +5 -0
- package/dist/serve/cli.js +82 -23
- package/dist/serve/headless-server.d.ts +11 -0
- package/dist/serve/headless-server.js +737 -83
- package/dist/session-titles-store.d.ts +17 -0
- package/dist/session-titles-store.js +174 -0
- package/dist/static-files.js +1 -0
- package/dist/worker-bridge-core.js +17 -6
- package/package.json +19 -5
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { lstat, open, opendir, realpath } from "node:fs/promises";
|
|
3
|
+
import { basename, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { pipeline } from "node:stream/promises";
|
|
5
|
+
import { hubJson } from "./uploads.js";
|
|
6
|
+
const MAX_DOWNLOAD_BYTES = 100 * 1024 * 1024;
|
|
7
|
+
const MAX_PREVIEW_BYTES = 512 * 1024;
|
|
8
|
+
const IMAGE_TYPES = {
|
|
9
|
+
".png": "image/png",
|
|
10
|
+
".jpg": "image/jpeg",
|
|
11
|
+
".jpeg": "image/jpeg",
|
|
12
|
+
".gif": "image/gif",
|
|
13
|
+
".webp": "image/webp",
|
|
14
|
+
};
|
|
15
|
+
class FileRequestError extends Error {
|
|
16
|
+
status;
|
|
17
|
+
constructor(status, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.status = status;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Authenticated, read-only workspace files. Downloads never execute as Hub pages. */
|
|
23
|
+
export function createHubFiles(options) {
|
|
24
|
+
const lexicalRoot = resolve(options.cwd);
|
|
25
|
+
let canonicalRoot;
|
|
26
|
+
let rootIdentity;
|
|
27
|
+
const active = new Set();
|
|
28
|
+
let closed = false;
|
|
29
|
+
function allowedPath(parts) {
|
|
30
|
+
return (!parts.some((part) => [".git", "node_modules", ".ssh", ".aws", ".npmrc", ".netrc"].includes(part.toLowerCase()) || /^\.env(?:\.|$)/i.test(part)) &&
|
|
31
|
+
(!parts.some((part) => part.toLowerCase() === ".code-shell") ||
|
|
32
|
+
(parts[0] === ".code-shell" &&
|
|
33
|
+
parts[1] === "attachments" &&
|
|
34
|
+
!parts.slice(2).some((part) => part.toLowerCase() === ".code-shell"))));
|
|
35
|
+
}
|
|
36
|
+
async function locate(input) {
|
|
37
|
+
if (input.length > 4096 || input.includes("\0") || input.includes("\\"))
|
|
38
|
+
throw new FileRequestError(400, "文件路径无效。");
|
|
39
|
+
const root = canonicalRoot ?? (canonicalRoot = await realpath(lexicalRoot));
|
|
40
|
+
const target = resolve(isAbsolute(input) ? input : join(root, input || "."));
|
|
41
|
+
// The configured workspace may itself be an alias (for example /var on macOS).
|
|
42
|
+
let rel = relative(root, target);
|
|
43
|
+
if (isAbsolute(input) && (rel === ".." || rel.startsWith(`..${sep}`))) {
|
|
44
|
+
const lexical = relative(lexicalRoot, target);
|
|
45
|
+
if (lexical !== ".." && !lexical.startsWith(`..${sep}`) && !isAbsolute(lexical))
|
|
46
|
+
rel = lexical;
|
|
47
|
+
}
|
|
48
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel))
|
|
49
|
+
throw new FileRequestError(403, "只能访问当前服务器工作区内的文件。");
|
|
50
|
+
const parts = rel.split(sep).filter(Boolean);
|
|
51
|
+
if (parts.length > 128)
|
|
52
|
+
throw new FileRequestError(400, "文件路径层级过深。");
|
|
53
|
+
if (!allowedPath(parts))
|
|
54
|
+
throw new FileRequestError(403, "这个位置包含服务配置或内部数据,不能从文件页面读取。");
|
|
55
|
+
const path = join(root, ...parts);
|
|
56
|
+
await checkSegments(root, parts);
|
|
57
|
+
return { root, path, relativePath: parts.join("/"), parts };
|
|
58
|
+
}
|
|
59
|
+
async function openLocation(location, directory) {
|
|
60
|
+
const held = [];
|
|
61
|
+
const close = async () => {
|
|
62
|
+
await Promise.all(held.map((entry) => entry.handle.close().catch(() => undefined)));
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
let path = location.root;
|
|
66
|
+
for (let index = -1; index < location.parts.length; index++) {
|
|
67
|
+
if (index >= 0)
|
|
68
|
+
path = join(path, location.parts[index]);
|
|
69
|
+
const parent = held.at(-1);
|
|
70
|
+
// Linux exposes stable directory descriptors; each child is opened relative
|
|
71
|
+
// to its held parent. Other Node hosts keep all ancestor descriptors and
|
|
72
|
+
// reject replaced paths before returning bytes or directory metadata.
|
|
73
|
+
const openPath = process.platform === "linux" && parent
|
|
74
|
+
? `/proc/self/fd/${parent.handle.fd}/${location.parts[index]}`
|
|
75
|
+
: path;
|
|
76
|
+
const isDirectory = index < location.parts.length - 1 || directory;
|
|
77
|
+
const handle = await open(openPath, constants.O_RDONLY |
|
|
78
|
+
(constants.O_NOFOLLOW ?? 0) |
|
|
79
|
+
(constants.O_NONBLOCK ?? 0) |
|
|
80
|
+
(isDirectory ? (constants.O_DIRECTORY ?? 0) : 0));
|
|
81
|
+
const info = await handle.stat().catch(async (error) => {
|
|
82
|
+
await handle.close().catch(() => undefined);
|
|
83
|
+
throw error;
|
|
84
|
+
});
|
|
85
|
+
held.push({ handle, path, dev: info.dev, ino: info.ino });
|
|
86
|
+
if (isDirectory ? !info.isDirectory() : !info.isFile())
|
|
87
|
+
throw new FileRequestError(400, isDirectory ? "这个路径不是文件夹。" : "只能读取普通文件。");
|
|
88
|
+
if (index === -1) {
|
|
89
|
+
rootIdentity ??= { dev: info.dev, ino: info.ino };
|
|
90
|
+
if (info.dev !== rootIdentity.dev || info.ino !== rootIdentity.ino)
|
|
91
|
+
throw new FileRequestError(409, "服务器工作目录已经改变,请重新启动服务。");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const handle = held.at(-1).handle;
|
|
95
|
+
const verify = async () => {
|
|
96
|
+
for (const entry of held) {
|
|
97
|
+
const info = await lstat(entry.path);
|
|
98
|
+
if (info.isSymbolicLink() || info.dev !== entry.dev || info.ino !== entry.ino)
|
|
99
|
+
throw new FileRequestError(409, "文件或所在目录正在变化,请刷新后重试。");
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
await verify();
|
|
103
|
+
return {
|
|
104
|
+
handle,
|
|
105
|
+
close,
|
|
106
|
+
verify,
|
|
107
|
+
readPath: process.platform === "linux" ? `/proc/self/fd/${handle.fd}` : location.path,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
await close();
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function handle(req, res) {
|
|
116
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
117
|
+
if (!["/api/v1/files", "/api/v1/files/content"].includes(url.pathname))
|
|
118
|
+
return false;
|
|
119
|
+
let handle;
|
|
120
|
+
let release;
|
|
121
|
+
try {
|
|
122
|
+
if (closed)
|
|
123
|
+
throw new FileRequestError(503, "服务正在关闭。");
|
|
124
|
+
if (req.method !== "GET")
|
|
125
|
+
throw new FileRequestError(405, "文件页面只支持读取。");
|
|
126
|
+
if (!(await options.isAuthorized(req)))
|
|
127
|
+
throw new FileRequestError(401, "请重新登录。");
|
|
128
|
+
if (active.size >= 16)
|
|
129
|
+
throw new FileRequestError(429, "正在读取的文件过多,请稍后重试。");
|
|
130
|
+
active.add(res);
|
|
131
|
+
const location = await locate(url.searchParams.get("path") ?? "");
|
|
132
|
+
const directory = url.pathname === "/api/v1/files";
|
|
133
|
+
if (!directory && !location.parts.length)
|
|
134
|
+
throw new FileRequestError(400, "请选择一个文件。");
|
|
135
|
+
const opened = await openLocation(location, directory);
|
|
136
|
+
handle = opened.handle;
|
|
137
|
+
release = opened.close;
|
|
138
|
+
const info = await handle.stat();
|
|
139
|
+
if (directory) {
|
|
140
|
+
const files = [];
|
|
141
|
+
let examined = 0;
|
|
142
|
+
let truncated = false;
|
|
143
|
+
const listing = await opendir(opened.readPath, { bufferSize: 64 });
|
|
144
|
+
try {
|
|
145
|
+
for await (const entry of listing) {
|
|
146
|
+
if (++examined > 4096) {
|
|
147
|
+
truncated = true;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
if (!(entry.isFile() || entry.isDirectory()) ||
|
|
151
|
+
!allowedPath([...location.parts, entry.name]))
|
|
152
|
+
continue;
|
|
153
|
+
if (files.length >= 500) {
|
|
154
|
+
truncated = true;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const child = await lstat(join(opened.readPath, entry.name));
|
|
159
|
+
if (!child.isFile() && !child.isDirectory())
|
|
160
|
+
continue;
|
|
161
|
+
files.push({
|
|
162
|
+
name: entry.name,
|
|
163
|
+
path: [...location.parts, entry.name].join("/"),
|
|
164
|
+
kind: child.isDirectory() ? "directory" : "file",
|
|
165
|
+
size: child.size,
|
|
166
|
+
modifiedAt: child.mtimeMs,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
/* A concurrent rename must not hide the remaining directory. */
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
try {
|
|
176
|
+
await listing.close();
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
/* The async iterator also closes on completion or early return. */
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
files.sort((a, b) => Number(b.kind === "directory") - Number(a.kind === "directory") ||
|
|
183
|
+
a.name.localeCompare(b.name));
|
|
184
|
+
if (!(await options.isAuthorized(req)))
|
|
185
|
+
throw new FileRequestError(401, "请重新登录。");
|
|
186
|
+
await opened.verify();
|
|
187
|
+
hubJson(res, 200, { path: location.relativePath, files, truncated });
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
if (info.size > MAX_DOWNLOAD_BYTES)
|
|
191
|
+
throw new FileRequestError(413, "文件超过 100 MB,请从服务器工作目录获取。");
|
|
192
|
+
if (!(await options.isAuthorized(req)))
|
|
193
|
+
throw new FileRequestError(401, "请重新登录。");
|
|
194
|
+
await opened.verify();
|
|
195
|
+
const imageType = IMAGE_TYPES[extname(location.path).toLowerCase()];
|
|
196
|
+
const preview = url.searchParams.get("preview") === "true";
|
|
197
|
+
if (preview) {
|
|
198
|
+
const buffer = Buffer.alloc(Math.min(info.size, MAX_PREVIEW_BYTES));
|
|
199
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
200
|
+
const bytes = buffer.subarray(0, bytesRead);
|
|
201
|
+
const binary = bytes.includes(0) || !!imageType;
|
|
202
|
+
hubJson(res, 200, {
|
|
203
|
+
path: location.relativePath,
|
|
204
|
+
name: basename(location.path),
|
|
205
|
+
size: info.size,
|
|
206
|
+
kind: imageType ? "image" : binary ? "binary" : "text",
|
|
207
|
+
...(imageType ? { mime: imageType } : {}),
|
|
208
|
+
...(!binary
|
|
209
|
+
? { content: bytes.toString("utf8"), truncated: info.size > MAX_PREVIEW_BYTES }
|
|
210
|
+
: {}),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const inline = url.searchParams.get("inline") === "true" && !!imageType;
|
|
215
|
+
const filename = basename(location.path);
|
|
216
|
+
const encoded = encodeURIComponent(filename).replace(/[!'()*]/g, (value) => `%${value.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
217
|
+
res.writeHead(200, {
|
|
218
|
+
"content-type": inline ? imageType : "application/octet-stream",
|
|
219
|
+
"content-length": info.size,
|
|
220
|
+
"content-disposition": `${inline ? "inline" : "attachment"}; filename="download${extname(filename).replace(/[^.A-Za-z0-9]/g, "")}"; filename*=UTF-8''${encoded}`,
|
|
221
|
+
"cache-control": "no-store",
|
|
222
|
+
"x-content-type-options": "nosniff",
|
|
223
|
+
"content-security-policy": "default-src 'none'; sandbox",
|
|
224
|
+
});
|
|
225
|
+
if (info.size === 0)
|
|
226
|
+
res.end();
|
|
227
|
+
else
|
|
228
|
+
await pipeline(handle.createReadStream({ autoClose: false, end: Math.max(0, info.size - 1) }), res);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch (cause) {
|
|
233
|
+
const code = cause.code;
|
|
234
|
+
const failure = cause instanceof FileRequestError
|
|
235
|
+
? cause
|
|
236
|
+
: code === "ENOENT"
|
|
237
|
+
? new FileRequestError(404, "文件不存在或已移动。")
|
|
238
|
+
: ["ELOOP", "ENOTDIR"].includes(code ?? "")
|
|
239
|
+
? new FileRequestError(409, "文件或所在目录正在变化,请刷新后重试。")
|
|
240
|
+
: code === "EACCES" || code === "EPERM"
|
|
241
|
+
? new FileRequestError(403, "服务器没有读取这个文件的权限。")
|
|
242
|
+
: new FileRequestError(500, "无法读取这个文件,请刷新后重试。");
|
|
243
|
+
if (res.headersSent)
|
|
244
|
+
res.destroy();
|
|
245
|
+
else
|
|
246
|
+
hubJson(res, failure.status, { error: failure.message });
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
if (release)
|
|
250
|
+
await release();
|
|
251
|
+
else
|
|
252
|
+
await handle?.close().catch(() => undefined);
|
|
253
|
+
active.delete(res);
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
handle,
|
|
259
|
+
close() {
|
|
260
|
+
closed = true;
|
|
261
|
+
for (const response of active)
|
|
262
|
+
response.destroy();
|
|
263
|
+
active.clear();
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
async function checkSegments(root, parts) {
|
|
268
|
+
const rootInfo = await lstat(root);
|
|
269
|
+
if (rootInfo.isSymbolicLink() || !rootInfo.isDirectory())
|
|
270
|
+
throw new FileRequestError(403, "工作目录已经改变,请重新连接。");
|
|
271
|
+
let path = root;
|
|
272
|
+
for (let index = 0; index < parts.length; index++) {
|
|
273
|
+
path = join(path, parts[index]);
|
|
274
|
+
const info = await lstat(path);
|
|
275
|
+
if (info.isSymbolicLink() || (index < parts.length - 1 && !info.isDirectory()))
|
|
276
|
+
throw new FileRequestError(403, "不能从文件页面访问符号链接。");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
type Scope = "local" | "project" | "user" | "managed";
|
|
3
|
+
export declare class HubMcpConfigurationError extends Error {
|
|
4
|
+
readonly status: number;
|
|
5
|
+
constructor(status: number, message: string);
|
|
6
|
+
}
|
|
7
|
+
export interface HubMcpConfigurationOptions {
|
|
8
|
+
cwd: string;
|
|
9
|
+
isAuthorized?: (request: IncomingMessage) => Promise<boolean>;
|
|
10
|
+
ownerId?: (request: IncomingMessage) => Promise<string | undefined>;
|
|
11
|
+
withMutation?: <T>(write: () => Promise<T>) => Promise<T>;
|
|
12
|
+
probeTimeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface HubMcpServerView {
|
|
15
|
+
name: string;
|
|
16
|
+
source: "settings" | "plugin";
|
|
17
|
+
scope: Scope | "plugin";
|
|
18
|
+
editable: boolean;
|
|
19
|
+
deletable: boolean;
|
|
20
|
+
hasLocalOverride: boolean;
|
|
21
|
+
pluginDisabled: boolean;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
transport: string;
|
|
24
|
+
command?: string;
|
|
25
|
+
argsCount: number;
|
|
26
|
+
url?: string;
|
|
27
|
+
urlHasHiddenParts?: boolean;
|
|
28
|
+
envKeys: string[];
|
|
29
|
+
headerKeys: string[];
|
|
30
|
+
envVars: string[];
|
|
31
|
+
bearerTokenEnvVar?: string;
|
|
32
|
+
envHeaders: Record<string, string>;
|
|
33
|
+
credentialRef?: string;
|
|
34
|
+
allowedTools?: string[];
|
|
35
|
+
disabledTools: string[];
|
|
36
|
+
}
|
|
37
|
+
export interface HubMcpSnapshot {
|
|
38
|
+
workspacePath: string;
|
|
39
|
+
servers: HubMcpServerView[];
|
|
40
|
+
removed: {
|
|
41
|
+
name: string;
|
|
42
|
+
scope: Scope;
|
|
43
|
+
}[];
|
|
44
|
+
}
|
|
45
|
+
/** Authenticated administrator surface. The host checks its standard Origin policy before dispatch. */
|
|
46
|
+
export declare function createHubMcpConfiguration(options: HubMcpConfigurationOptions): {
|
|
47
|
+
handle: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
|
|
48
|
+
cancelOwner(owner: string): void;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
};
|
|
51
|
+
export {};
|