@steerable/agent-shell 0.6.52 → 0.6.55
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/dist/brand.js +1 -1
- package/dist/host/pack-assembly.d.ts +6 -16
- package/dist/host/pack-assembly.js +3 -4
- package/dist/host/runtime.js +2 -0
- package/dist/i18n.d.ts +23 -0
- package/dist/i18n.js +87 -0
- package/dist/image-attachment.d.ts +31 -4
- package/dist/image-attachment.js +13 -18
- package/dist/json-store.js +3 -6
- package/dist/local-backend/portable-bundle.d.ts +16 -2
- package/dist/local-backend/portable-bundle.js +29 -16
- package/dist/local-backend/portable-service.js +25 -11
- package/dist/local-backend/router.js +56 -17
- package/dist/local-backend/skills/10-goal/SKILL.md +1 -1
- package/dist/local-backend/skills/11-loop/SKILL.md +1 -1
- package/dist/local-backend/skills/12-create-skill/SKILL.md +2 -2
- package/dist/local-executor.d.ts +22 -0
- package/dist/local-executor.js +118 -1
- package/dist/local-script-registry.js +2 -2
- package/dist/mcp-executor.d.ts +42 -10
- package/dist/mcp-executor.js +248 -41
- package/dist/mcp-server-registry.d.ts +59 -11
- package/dist/mcp-server-registry.js +193 -28
- package/dist/product-config.d.ts +24 -14
- package/dist/project-registry.d.ts +3 -3
- package/dist/project-registry.js +2 -2
- package/dist/runtime.d.ts +8 -54
- package/dist/runtime.js +15 -129
- package/dist/scenario/pack.d.ts +1 -1
- package/dist/scenario/registry.d.ts +4 -5
- package/dist/scenario/registry.js +2 -10
- package/dist/server/start.js +1 -2
- package/dist/shell-exec.d.ts +62 -0
- package/dist/shell-exec.js +147 -0
- package/dist/shell-process.d.ts +3 -0
- package/dist/shell-process.js +137 -0
- package/dist/sidecar/boot.js +4 -0
- package/dist/sidecar/reverse-spawn.js +6 -15
- package/dist/sidecar/supervisor.d.ts +0 -2
- package/dist/sidecar/supervisor.js +7 -30
- package/dist/storage/index.js +1 -1
- package/dist/storage/write-lease-error.d.ts +2 -11
- package/dist/storage/write-lease-error.js +3 -8
- package/dist/tool-router.js +21 -6
- package/package.json +7 -14
- package/dist/app-update-electron.d.ts +0 -5
- package/dist/app-update-electron.js +0 -66
- package/dist/app-update.d.ts +0 -92
- package/dist/app-update.js +0 -213
- package/dist/host/ipc.d.ts +0 -11
- package/dist/host/ipc.js +0 -20
- package/dist/main.d.ts +0 -1
- package/dist/main.js +0 -967
- package/dist/preload-default.d.ts +0 -1
- package/dist/preload-default.js +0 -9
- package/dist/preload.cjs +0 -425
- package/dist/preload.d.ts +0 -20
- package/dist/preload.js +0 -442
- package/dist/single-instance.d.ts +0 -11
- package/dist/single-instance.js +0 -21
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* 持久化在 userData/agent-mcp-servers.json。已启用服务的工具会经
|
|
6
6
|
* ToolRouter 以 `mcp__<serverKey>__<toolName>` 一等工具身份暴露给模型。
|
|
7
7
|
*
|
|
8
|
-
* 存储通过 {@link McpServerKvStore}
|
|
9
|
-
*
|
|
8
|
+
* 存储通过 {@link McpServerKvStore} 接口注入:宿主用 json-store 实现,
|
|
9
|
+
* 单测用内存实现。
|
|
10
10
|
*/
|
|
11
11
|
import { randomUUID } from 'node:crypto';
|
|
12
|
-
import { mcpExecutor } from './mcp-executor.js';
|
|
12
|
+
import { MAX_MCP_SERVER_TOOLS, mcpExecutor, } from './mcp-executor.js';
|
|
13
13
|
const STORE_KEY = 'mcpServers';
|
|
14
14
|
/** 工具名只允许 [a-z0-9-];中文名会被剥光,退化为 srv-<id前6位>。 */
|
|
15
15
|
export function sanitizeServerKey(name, id) {
|
|
@@ -19,15 +19,90 @@ export function sanitizeServerKey(name, id) {
|
|
|
19
19
|
.replace(/^-+|-+$/g, '');
|
|
20
20
|
return cleaned || `srv-${id.slice(0, 6)}`;
|
|
21
21
|
}
|
|
22
|
+
function normalizeStoredEntry(entry) {
|
|
23
|
+
if ('transport' in entry)
|
|
24
|
+
return entry;
|
|
25
|
+
const legacy = entry;
|
|
26
|
+
return { ...legacy, transport: 'stdio' };
|
|
27
|
+
}
|
|
28
|
+
function normalizeTransportInput(input) {
|
|
29
|
+
if (input.transport === 'streamable-http') {
|
|
30
|
+
const raw = input;
|
|
31
|
+
if (typeof raw.command === 'string' && raw.command.trim()) {
|
|
32
|
+
throw new Error('Streamable HTTP 配置不能包含 command');
|
|
33
|
+
}
|
|
34
|
+
const url = parseMcpHttpUrl(input.url);
|
|
35
|
+
const headers = { ...(input.headers ?? {}) };
|
|
36
|
+
if (Object.keys(headers).some((name) => name.toLowerCase() === 'authorization')) {
|
|
37
|
+
throw new Error('Authorization 不允许明文保存;请使用 bearerTokenEnvVar 或 headersFromEnv');
|
|
38
|
+
}
|
|
39
|
+
const bearerTokenEnvVar = input.bearerTokenEnvVar?.trim() || undefined;
|
|
40
|
+
return {
|
|
41
|
+
transport: 'streamable-http',
|
|
42
|
+
url,
|
|
43
|
+
headers,
|
|
44
|
+
headersFromEnv: { ...(input.headersFromEnv ?? {}) },
|
|
45
|
+
bearerTokenEnvVar,
|
|
46
|
+
reconnect: input.reconnect ? { ...input.reconnect } : undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if ('url' in input && typeof input.url === 'string' && input.url.trim()) {
|
|
50
|
+
throw new Error('stdio 配置不能包含 url');
|
|
51
|
+
}
|
|
52
|
+
const command = input.command.trim();
|
|
53
|
+
if (!command)
|
|
54
|
+
throw new Error('启动命令不能为空');
|
|
55
|
+
return {
|
|
56
|
+
transport: 'stdio',
|
|
57
|
+
command,
|
|
58
|
+
args: (input.args ?? []).map(String),
|
|
59
|
+
env: { ...(input.env ?? {}) },
|
|
60
|
+
cwd: input.cwd?.trim() || undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function parseMcpHttpUrl(raw) {
|
|
64
|
+
let url;
|
|
65
|
+
try {
|
|
66
|
+
url = new URL(raw.trim());
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
throw new Error('MCP HTTP URL 无效');
|
|
70
|
+
}
|
|
71
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
72
|
+
throw new Error('MCP HTTP URL 必须使用 http 或 https');
|
|
73
|
+
}
|
|
74
|
+
if (url.username || url.password) {
|
|
75
|
+
throw new Error('MCP HTTP URL 不能包含凭据');
|
|
76
|
+
}
|
|
77
|
+
return url.toString();
|
|
78
|
+
}
|
|
79
|
+
function stringRecord(value) {
|
|
80
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
81
|
+
return {};
|
|
82
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
83
|
+
key,
|
|
84
|
+
String(item),
|
|
85
|
+
]));
|
|
86
|
+
}
|
|
87
|
+
function stringValue(value) {
|
|
88
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
89
|
+
}
|
|
22
90
|
export class McpServerRegistry {
|
|
23
91
|
store;
|
|
92
|
+
executor;
|
|
24
93
|
/** serverId → 最近一次 listTools 的结果(内存缓存,重启后由后台刷新重建)。 */
|
|
25
94
|
toolCache = new Map();
|
|
26
|
-
constructor(store) {
|
|
95
|
+
constructor(store, executor = mcpExecutor) {
|
|
27
96
|
this.store = store;
|
|
97
|
+
this.executor = executor;
|
|
28
98
|
}
|
|
29
99
|
list() {
|
|
30
|
-
|
|
100
|
+
const stored = this.store.get(STORE_KEY) ?? [];
|
|
101
|
+
const migrated = stored.map(normalizeStoredEntry);
|
|
102
|
+
if (migrated.some((entry, index) => entry !== stored[index])) {
|
|
103
|
+
this.store.set(STORE_KEY, migrated);
|
|
104
|
+
}
|
|
105
|
+
return migrated;
|
|
31
106
|
}
|
|
32
107
|
get(idOrName) {
|
|
33
108
|
const needle = idOrName.trim().toLowerCase();
|
|
@@ -43,24 +118,18 @@ export class McpServerRegistry {
|
|
|
43
118
|
}
|
|
44
119
|
create(input) {
|
|
45
120
|
const name = input.name.trim();
|
|
46
|
-
const command = input.command.trim();
|
|
47
121
|
if (!name)
|
|
48
122
|
throw new Error('服务名称不能为空');
|
|
49
|
-
if (!command)
|
|
50
|
-
throw new Error('启动命令不能为空');
|
|
51
123
|
if (this.get(name))
|
|
52
124
|
throw new Error(`已存在同名服务「${name}」`);
|
|
53
125
|
const now = new Date().toISOString();
|
|
54
126
|
const entry = {
|
|
55
127
|
id: randomUUID(),
|
|
56
128
|
name,
|
|
57
|
-
command,
|
|
58
|
-
args: (input.args ?? []).map((a) => String(a)),
|
|
59
|
-
env: { ...(input.env ?? {}) },
|
|
60
|
-
cwd: input.cwd?.trim() || undefined,
|
|
61
129
|
enabled: input.enabled ?? true,
|
|
62
130
|
createdAt: now,
|
|
63
131
|
updatedAt: now,
|
|
132
|
+
...normalizeTransportInput(input),
|
|
64
133
|
};
|
|
65
134
|
this.store.set(STORE_KEY, [...this.list(), entry]);
|
|
66
135
|
return entry;
|
|
@@ -77,18 +146,39 @@ export class McpServerRegistry {
|
|
|
77
146
|
const nameClash = servers.some((s) => s.id !== id && s.name.toLowerCase() === nextName.toLowerCase());
|
|
78
147
|
if (nameClash)
|
|
79
148
|
throw new Error(`已存在同名服务「${nextName}」`);
|
|
149
|
+
const transport = updates.transport ?? current.transport;
|
|
150
|
+
const transportInput = transport === 'stdio'
|
|
151
|
+
? {
|
|
152
|
+
name: nextName,
|
|
153
|
+
transport: 'stdio',
|
|
154
|
+
command: updates.command ?? (current.transport === 'stdio' ? current.command : ''),
|
|
155
|
+
args: updates.args ?? (current.transport === 'stdio' ? current.args : []),
|
|
156
|
+
env: updates.env ?? (current.transport === 'stdio' ? current.env : {}),
|
|
157
|
+
cwd: updates.cwd ?? (current.transport === 'stdio' ? current.cwd : undefined),
|
|
158
|
+
}
|
|
159
|
+
: {
|
|
160
|
+
name: nextName,
|
|
161
|
+
transport: 'streamable-http',
|
|
162
|
+
url: updates.url ?? (current.transport === 'streamable-http' ? current.url : ''),
|
|
163
|
+
headers: updates.headers ??
|
|
164
|
+
(current.transport === 'streamable-http' ? current.headers : {}),
|
|
165
|
+
headersFromEnv: updates.headersFromEnv ??
|
|
166
|
+
(current.transport === 'streamable-http' ? current.headersFromEnv : {}),
|
|
167
|
+
bearerTokenEnvVar: updates.bearerTokenEnvVar ??
|
|
168
|
+
(current.transport === 'streamable-http'
|
|
169
|
+
? current.bearerTokenEnvVar
|
|
170
|
+
: undefined),
|
|
171
|
+
reconnect: updates.reconnect ??
|
|
172
|
+
(current.transport === 'streamable-http' ? current.reconnect : undefined),
|
|
173
|
+
};
|
|
80
174
|
const next = {
|
|
81
|
-
|
|
175
|
+
id: current.id,
|
|
82
176
|
name: nextName,
|
|
83
|
-
command: updates.command !== undefined ? updates.command.trim() : current.command,
|
|
84
|
-
args: updates.args !== undefined ? updates.args.map((a) => String(a)) : current.args,
|
|
85
|
-
env: updates.env !== undefined ? { ...updates.env } : current.env,
|
|
86
|
-
cwd: updates.cwd !== undefined ? updates.cwd.trim() || undefined : current.cwd,
|
|
87
177
|
enabled: updates.enabled !== undefined ? updates.enabled : current.enabled,
|
|
178
|
+
createdAt: current.createdAt,
|
|
88
179
|
updatedAt: new Date().toISOString(),
|
|
180
|
+
...normalizeTransportInput(transportInput),
|
|
89
181
|
};
|
|
90
|
-
if (!next.command)
|
|
91
|
-
throw new Error('启动命令不能为空');
|
|
92
182
|
servers[idx] = next;
|
|
93
183
|
this.store.set(STORE_KEY, servers);
|
|
94
184
|
// 配置变了,旧工具缓存不可信
|
|
@@ -131,10 +221,27 @@ export class McpServerRegistry {
|
|
|
131
221
|
for (const [name, cfgRaw] of Object.entries(container)) {
|
|
132
222
|
const cfg = (cfgRaw ?? {});
|
|
133
223
|
const command = typeof cfg.command === 'string' ? cfg.command.trim() : '';
|
|
134
|
-
|
|
224
|
+
const url = typeof cfg.url === 'string' ? cfg.url.trim() : '';
|
|
225
|
+
if ((!command && !url) || (command && url) || this.get(name)) {
|
|
135
226
|
result.skipped.push(name);
|
|
136
227
|
continue;
|
|
137
228
|
}
|
|
229
|
+
if (url) {
|
|
230
|
+
const headers = stringRecord(cfg.headers);
|
|
231
|
+
const headersFromEnv = stringRecord(cfg.envHttpHeaders ?? cfg.env_http_headers ?? cfg.headersFromEnv);
|
|
232
|
+
const bearerTokenEnvVar = stringValue(cfg.bearerTokenEnvVar ?? cfg.bearer_token_env_var) || undefined;
|
|
233
|
+
const entry = this.create({
|
|
234
|
+
name: name.trim(),
|
|
235
|
+
transport: 'streamable-http',
|
|
236
|
+
url,
|
|
237
|
+
headers,
|
|
238
|
+
headersFromEnv,
|
|
239
|
+
bearerTokenEnvVar,
|
|
240
|
+
enabled: true,
|
|
241
|
+
});
|
|
242
|
+
result.added.push(entry);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
138
245
|
const env = {};
|
|
139
246
|
if (cfg.env && typeof cfg.env === 'object' && !Array.isArray(cfg.env)) {
|
|
140
247
|
for (const [k, v] of Object.entries(cfg.env)) {
|
|
@@ -160,12 +267,48 @@ export class McpServerRegistry {
|
|
|
160
267
|
return clash ? `${base}-${entry.id.slice(0, 4)}` : base;
|
|
161
268
|
}
|
|
162
269
|
toExecutorConfig(entry) {
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
270
|
+
return entry.transport === 'stdio'
|
|
271
|
+
? {
|
|
272
|
+
transport: 'stdio',
|
|
273
|
+
command: entry.command,
|
|
274
|
+
args: entry.args,
|
|
275
|
+
env: entry.env,
|
|
276
|
+
cwd: entry.cwd,
|
|
277
|
+
}
|
|
278
|
+
: {
|
|
279
|
+
transport: 'streamable-http',
|
|
280
|
+
url: entry.url,
|
|
281
|
+
headers: entry.headers,
|
|
282
|
+
headersFromEnv: entry.headersFromEnv,
|
|
283
|
+
bearerTokenEnvVar: entry.bearerTokenEnvVar,
|
|
284
|
+
reconnect: entry.reconnect,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
toPublicEntry(entry) {
|
|
288
|
+
const common = {
|
|
289
|
+
id: entry.id,
|
|
290
|
+
name: entry.name,
|
|
291
|
+
transport: entry.transport,
|
|
292
|
+
enabled: entry.enabled,
|
|
293
|
+
createdAt: entry.createdAt,
|
|
294
|
+
updatedAt: entry.updatedAt,
|
|
168
295
|
};
|
|
296
|
+
return entry.transport === 'stdio'
|
|
297
|
+
? {
|
|
298
|
+
...common,
|
|
299
|
+
command: entry.command,
|
|
300
|
+
args: [...entry.args],
|
|
301
|
+
cwd: entry.cwd,
|
|
302
|
+
envKeys: Object.keys(entry.env).sort(),
|
|
303
|
+
}
|
|
304
|
+
: {
|
|
305
|
+
...common,
|
|
306
|
+
url: entry.url,
|
|
307
|
+
headerNames: Object.keys(entry.headers).sort(),
|
|
308
|
+
headersFromEnv: { ...entry.headersFromEnv },
|
|
309
|
+
bearerTokenEnvVar: entry.bearerTokenEnvVar,
|
|
310
|
+
reconnect: entry.reconnect,
|
|
311
|
+
};
|
|
169
312
|
}
|
|
170
313
|
getCachedTools(serverId) {
|
|
171
314
|
return this.toolCache.get(serverId) ?? null;
|
|
@@ -211,8 +354,24 @@ export class McpServerRegistry {
|
|
|
211
354
|
const entry = this.list().find((s) => s.id === serverId);
|
|
212
355
|
if (!entry)
|
|
213
356
|
throw new Error('服务不存在');
|
|
214
|
-
const
|
|
215
|
-
|
|
357
|
+
const applyChanged = (tools) => {
|
|
358
|
+
if (tools.length > MAX_MCP_SERVER_TOOLS)
|
|
359
|
+
return;
|
|
360
|
+
this.toolCache.set(serverId, {
|
|
361
|
+
tools: tools.map((tool) => ({ ...tool })),
|
|
362
|
+
fetchedAt: new Date().toISOString(),
|
|
363
|
+
error: null,
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
const result = await this.executor.listTools(this.toExecutorConfig(entry), applyChanged, (error) => {
|
|
367
|
+
this.toolCache.set(serverId, {
|
|
368
|
+
tools: [],
|
|
369
|
+
fetchedAt: new Date().toISOString(),
|
|
370
|
+
error,
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
const overCap = (result.tools?.length ?? 0) > MAX_MCP_SERVER_TOOLS;
|
|
374
|
+
const cached = result.success && !overCap
|
|
216
375
|
? {
|
|
217
376
|
tools: (result.tools ?? []).map((t) => ({
|
|
218
377
|
name: t.name,
|
|
@@ -222,7 +381,13 @@ export class McpServerRegistry {
|
|
|
222
381
|
fetchedAt: new Date().toISOString(),
|
|
223
382
|
error: null,
|
|
224
383
|
}
|
|
225
|
-
: {
|
|
384
|
+
: {
|
|
385
|
+
tools: [],
|
|
386
|
+
fetchedAt: new Date().toISOString(),
|
|
387
|
+
error: overCap
|
|
388
|
+
? `服务工具数超过每服务 ${MAX_MCP_SERVER_TOOLS} 个的上限`
|
|
389
|
+
: result.error ?? '未知错误',
|
|
390
|
+
};
|
|
226
391
|
this.toolCache.set(serverId, cached);
|
|
227
392
|
return cached;
|
|
228
393
|
}
|
package/dist/product-config.d.ts
CHANGED
|
@@ -15,13 +15,6 @@ export interface ProductLinks {
|
|
|
15
15
|
/** 帮助菜单「访问官网」URL。 */
|
|
16
16
|
website?: string;
|
|
17
17
|
}
|
|
18
|
-
export interface ProductUpdates {
|
|
19
|
-
/**
|
|
20
|
-
* electron-updater generic 通道根地址(目录,不含 latest*.yml)。
|
|
21
|
-
* 打包配置里的 publishUrl 由产品组装根注入到这里。空则不检查更新。
|
|
22
|
-
*/
|
|
23
|
-
feedUrl?: string;
|
|
24
|
-
}
|
|
25
18
|
export interface ProductConfig {
|
|
26
19
|
/**
|
|
27
20
|
* 云端遥测端点(insights flush 的 API base)。空 = 不上报(中性默认);
|
|
@@ -31,13 +24,8 @@ export interface ProductConfig {
|
|
|
31
24
|
/** 帮助菜单链接;缺省的项不渲染。 */
|
|
32
25
|
links?: ProductLinks;
|
|
33
26
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*/
|
|
37
|
-
updates?: ProductUpdates;
|
|
38
|
-
/**
|
|
39
|
-
* BS 模式的 userData 目录名(~/ 下)。CS 模式由 Electron 按
|
|
40
|
-
* productName 分目录,不经此字段。
|
|
27
|
+
* 未设置 `DEEPPATH_USER_DATA_DIR` 时的 userData 目录名(~/ 下)。
|
|
28
|
+
* Tauri 宿主总是注入该环境变量,不经此字段。
|
|
41
29
|
*/
|
|
42
30
|
dataDirName?: string;
|
|
43
31
|
/**
|
|
@@ -58,6 +46,11 @@ export interface ProductConfig {
|
|
|
58
46
|
* STEERABLE_APPROVAL=0 仍是调试逃生口。
|
|
59
47
|
*/
|
|
60
48
|
approval?: 'host' | 'off';
|
|
49
|
+
/**
|
|
50
|
+
* 白名单外出网询问:host = 弹宿主审批;off = 自动加入当前代理会话。
|
|
51
|
+
* 缺省 host。自动放行仅存活到代理进程退出,不修改持久白名单。
|
|
52
|
+
*/
|
|
53
|
+
egressApproval?: 'host' | 'off';
|
|
61
54
|
/**
|
|
62
55
|
* 对话模式。缺省 `['agent','plan']`。只留一种时渲染层不显示切换。
|
|
63
56
|
*/
|
|
@@ -100,11 +93,20 @@ export interface ProductConfig {
|
|
|
100
93
|
*/
|
|
101
94
|
builtinMcp?: Array<{
|
|
102
95
|
name: string;
|
|
96
|
+
transport?: 'stdio';
|
|
103
97
|
command: string;
|
|
104
98
|
args?: string[];
|
|
105
99
|
env?: Record<string, string>;
|
|
106
100
|
cwd?: string;
|
|
107
101
|
enabled?: boolean;
|
|
102
|
+
} | {
|
|
103
|
+
name: string;
|
|
104
|
+
transport: 'streamable-http';
|
|
105
|
+
url: string;
|
|
106
|
+
headers?: Record<string, string>;
|
|
107
|
+
headersFromEnv?: Record<string, string>;
|
|
108
|
+
bearerTokenEnvVar?: string;
|
|
109
|
+
enabled?: boolean;
|
|
108
110
|
}>;
|
|
109
111
|
/**
|
|
110
112
|
* P3.1 多智能体编排六件套 (agent_spawn / agent_wait / agent_send / agent_interrupt / agent_close / agent_list)。
|
|
@@ -116,6 +118,14 @@ export interface ProductConfig {
|
|
|
116
118
|
maxDepth?: number;
|
|
117
119
|
maxParallel?: number;
|
|
118
120
|
};
|
|
121
|
+
/**
|
|
122
|
+
* 界面语言。缺省只有 `en`。译文在应用层语言包,不在本包。
|
|
123
|
+
* `locales` 是这个产品装进包里的语言;多于一种时设置页才出现切换。
|
|
124
|
+
*/
|
|
125
|
+
i18n?: {
|
|
126
|
+
locales: string[];
|
|
127
|
+
defaultLocale: string;
|
|
128
|
+
};
|
|
119
129
|
}
|
|
120
130
|
/** shell 内置智能体:只有产品显式 `true` 才开。 */
|
|
121
131
|
export declare function isShellBuiltinAgentEnabled(id: 'local-assistant' | 'all-round-assistant', config?: ProductConfig): boolean;
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* 源文件夹及其子目录同样可写。
|
|
9
9
|
*
|
|
10
10
|
* 持久化在 userData/agent-projects.json。存储通过 {@link ProjectKvStore}
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* 接口注入:宿主用 json-store 实现,单测用内存实现(与
|
|
12
|
+
* mcp-server-registry.ts 同一模式)。
|
|
13
13
|
*/
|
|
14
14
|
export interface ProjectRecord {
|
|
15
15
|
id: string;
|
|
@@ -38,7 +38,7 @@ export interface CreateProjectInput {
|
|
|
38
38
|
folderPath: string;
|
|
39
39
|
sourceFolders?: string[];
|
|
40
40
|
}
|
|
41
|
-
/** 最小 KV
|
|
41
|
+
/** 最小 KV 存储接口,避免本模块直接依赖具体存储实现。 */
|
|
42
42
|
export interface ProjectKvStore {
|
|
43
43
|
get(key: 'projects'): ProjectRecord[] | undefined;
|
|
44
44
|
set(key: 'projects', value: ProjectRecord[]): void;
|
package/dist/project-registry.js
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* 源文件夹及其子目录同样可写。
|
|
9
9
|
*
|
|
10
10
|
* 持久化在 userData/agent-projects.json。存储通过 {@link ProjectKvStore}
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* 接口注入:宿主用 json-store 实现,单测用内存实现(与
|
|
12
|
+
* mcp-server-registry.ts 同一模式)。
|
|
13
13
|
*/
|
|
14
14
|
import { randomUUID } from 'node:crypto';
|
|
15
15
|
const STORE_KEY = 'projects';
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/** Electron 主进程里 `process.versions.electron` 有值;普通 Node(BS server、vitest)没有。 */
|
|
2
|
-
export declare function isElectronRuntime(): boolean;
|
|
3
1
|
/** 应用数据目录(SQLite、JSON store、用户技能目录的根)。 */
|
|
4
2
|
export declare function getUserDataDir(): string;
|
|
5
3
|
/**
|
|
@@ -15,59 +13,15 @@ export declare function getDocumentsDir(): string;
|
|
|
15
13
|
export declare function setAppRootDir(dir: string): void;
|
|
16
14
|
export declare function getAppRootDir(): string;
|
|
17
15
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
* (shellDir 一般为 shell main.js 所在目录)。
|
|
25
|
-
*/
|
|
26
|
-
export declare function getPreloadPath(shellDir: string): string;
|
|
27
|
-
/**
|
|
28
|
-
* 产品 web 产物目录(2.3):产品入口(products/<id>/{main,server}.ts)
|
|
29
|
-
* 用本函数把 DEEPPATH_WEB_DIST 注入环境,shell 的 main/server 据此定位
|
|
30
|
-
* web dist。双平面探测:源码平面入口在 products/<id>/(web dist 在同级
|
|
31
|
-
* web/dist);编译平面入口在 products/<id>/dist/products/<id>/(web dist
|
|
32
|
-
* 在仓库根的 products/<id>/web/dist)。两处都没有返回 null(打包后的
|
|
33
|
-
* CS 走 app.getAppPath()/web-dist 约定,不经本函数)。
|
|
16
|
+
* 产品 web 产物目录(2.3):产品入口(products/<id>/server.ts)用本函数
|
|
17
|
+
* 把 DEEPPATH_WEB_DIST 注入环境,shell server 据此定位 web dist。双平面
|
|
18
|
+
* 探测:源码平面入口在 products/<id>/(web dist 在同级 web/dist);编译
|
|
19
|
+
* 平面入口在 products/<id>/dist/products/<id>/(web dist 在仓库根的
|
|
20
|
+
* products/<id>/web/dist)。两处都没有返回 null(打包后的 Tauri 宿主直接
|
|
21
|
+
* 注入 DEEPPATH_WEB_DIST,不经本函数)。
|
|
34
22
|
*/
|
|
35
23
|
export declare function resolveProductWebDist(entryUrl: string): string | null;
|
|
36
|
-
/**
|
|
24
|
+
/** 用系统默认应用打开本地路径。返回空串表示成功,否则是错误消息。 */
|
|
37
25
|
export declare function shellOpenPath(target: string): Promise<string>;
|
|
38
|
-
/** 用系统浏览器打开 URL
|
|
26
|
+
/** 用系统浏览器打开 URL。 */
|
|
39
27
|
export declare function shellOpenExternal(url: string): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* 注册应用退出钩子(sidecar 的兜底清理)。BS 下没有 app 生命周期——
|
|
42
|
-
* 宿主退出由 server 的 SIGINT/SIGTERM 钩子统一负责,这里 no-op。
|
|
43
|
-
*/
|
|
44
|
-
export declare function onAppWillQuit(listener: () => void): void;
|
|
45
|
-
export declare function offAppWillQuit(listener: () => void): void;
|
|
46
|
-
export interface NativeImageInstance {
|
|
47
|
-
isEmpty(): boolean;
|
|
48
|
-
getSize(): {
|
|
49
|
-
width: number;
|
|
50
|
-
height: number;
|
|
51
|
-
};
|
|
52
|
-
crop(rect: {
|
|
53
|
-
x: number;
|
|
54
|
-
y: number;
|
|
55
|
-
width: number;
|
|
56
|
-
height: number;
|
|
57
|
-
}): NativeImageInstance;
|
|
58
|
-
resize(o: {
|
|
59
|
-
width?: number;
|
|
60
|
-
height?: number;
|
|
61
|
-
quality?: string;
|
|
62
|
-
}): NativeImageInstance;
|
|
63
|
-
toPNG(): Buffer;
|
|
64
|
-
toJPEG(quality: number): Buffer;
|
|
65
|
-
}
|
|
66
|
-
export interface NativeImageLike {
|
|
67
|
-
createFromPath(p: string): NativeImageInstance;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Electron `nativeImage`(图片解码/缩放)。BS / 单测等无 Electron 环境返回
|
|
71
|
-
* null——调用方按"不支持图片解码"降级(image-attachment 已有该分支)。
|
|
72
|
-
*/
|
|
73
|
-
export declare function getNativeImage(): NativeImageLike | null;
|