@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
package/dist/brand.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 包装配注册表(阶段 2.3)——纯构建期组合的宿主侧机制。
|
|
3
3
|
*
|
|
4
|
-
* 宿主(host/runtime.ts、
|
|
4
|
+
* 宿主(host/runtime.ts、server/index.ts)不再静态 import 任何
|
|
5
5
|
* 包代码:产品组装根(products/<id>/active.ts)把包的装配函数注册进本
|
|
6
6
|
* 注册表,宿主在装配期逐个调用。产品的 tsc 编译单元只 include 自己的包,
|
|
7
7
|
* 他包代码物理上不进产品 dist。
|
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
* - import 期(active.ts 顶层):迁移/种子/品牌等无宿主依赖的注册;
|
|
11
11
|
* - 装配期(createHostRuntime 内):服务/工具/路由等需要宿主能力的部分,
|
|
12
12
|
* 经 {@link PackAssemblyDeps} 注入。
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* 不出现任何包名。
|
|
13
|
+
* server/index.ts 消费 handle.httpRoutes——包的入口面贡献由宿主循环驱动,
|
|
14
|
+
* 宿主代码里不出现任何包名。
|
|
16
15
|
*/
|
|
17
|
-
import type { HttpRouteContribution,
|
|
16
|
+
import type { HttpRouteContribution, ToolContribution } from '../scenario/pack.js';
|
|
18
17
|
import type { PackDbAccess } from '../storage/driver.js';
|
|
19
18
|
/** 宿主注入包的装配能力面。包不 import 宿主装配模块,全部经回调注入。 */
|
|
20
19
|
export interface PackAssemblyDeps {
|
|
@@ -31,7 +30,7 @@ export interface PackAssemblyDeps {
|
|
|
31
30
|
name: string;
|
|
32
31
|
folderPath: string;
|
|
33
32
|
} | null>;
|
|
34
|
-
/** 面向全部用户面的事件广播(
|
|
33
|
+
/** 面向全部用户面的事件广播(SSE 总线)。 */
|
|
35
34
|
readonly broadcast: (channel: string, payload: unknown) => void;
|
|
36
35
|
/** 工具贡献注册口(toolRouter.registerToolContributions 的收窄面)。 */
|
|
37
36
|
readonly registerTools: (tools: readonly ToolContribution[]) => void;
|
|
@@ -61,20 +60,11 @@ export interface PackAssemblyDeps {
|
|
|
61
60
|
/** 装配期日志行(进宿主日志)。 */
|
|
62
61
|
readonly onLog: (line: string) => void;
|
|
63
62
|
}
|
|
64
|
-
/** CS 宿主的窗口能力:打开一个加载应用内路由的独立窗口。 */
|
|
65
|
-
export interface PackHostWindowCapabilities {
|
|
66
|
-
/** route 如 '/<pack>-debug-log';title 为窗口副标题(宿主拼品牌名)。 */
|
|
67
|
-
openRouteWindow(route: string, options: {
|
|
68
|
-
title: string;
|
|
69
|
-
}): Promise<void>;
|
|
70
|
-
}
|
|
71
63
|
/** 装配产物:宿主按入口面消费。全部槽位可选。 */
|
|
72
64
|
export interface PackAssemblyHandle {
|
|
73
65
|
/** 宿主关停钩子(进程退出时按装配逆序调用)。 */
|
|
74
66
|
readonly dispose?: () => void | Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
readonly ipc?: (caps: PackHostWindowCapabilities) => readonly IpcContribution[];
|
|
77
|
-
/** BS:/host/<packId>/* 路由贡献。 */
|
|
67
|
+
/** /host/<packId>/* 路由贡献。 */
|
|
78
68
|
readonly httpRoutes?: () => readonly HttpRouteContribution[];
|
|
79
69
|
}
|
|
80
70
|
export type PackAssembly = (deps: PackAssemblyDeps) => PackAssemblyHandle | void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 包装配注册表(阶段 2.3)——纯构建期组合的宿主侧机制。
|
|
3
3
|
*
|
|
4
|
-
* 宿主(host/runtime.ts、
|
|
4
|
+
* 宿主(host/runtime.ts、server/index.ts)不再静态 import 任何
|
|
5
5
|
* 包代码:产品组装根(products/<id>/active.ts)把包的装配函数注册进本
|
|
6
6
|
* 注册表,宿主在装配期逐个调用。产品的 tsc 编译单元只 include 自己的包,
|
|
7
7
|
* 他包代码物理上不进产品 dist。
|
|
@@ -10,9 +10,8 @@
|
|
|
10
10
|
* - import 期(active.ts 顶层):迁移/种子/品牌等无宿主依赖的注册;
|
|
11
11
|
* - 装配期(createHostRuntime 内):服务/工具/路由等需要宿主能力的部分,
|
|
12
12
|
* 经 {@link PackAssemblyDeps} 注入。
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* 不出现任何包名。
|
|
13
|
+
* server/index.ts 消费 handle.httpRoutes——包的入口面贡献由宿主循环驱动,
|
|
14
|
+
* 宿主代码里不出现任何包名。
|
|
16
15
|
*/
|
|
17
16
|
const assemblies = new Map();
|
|
18
17
|
/** 注册包的装配函数(产品组装根在 import 期调用;重复注册同包抛错)。 */
|
package/dist/host/runtime.js
CHANGED
|
@@ -16,6 +16,7 @@ import { LocalScriptRegistry } from '../local-script-registry.js';
|
|
|
16
16
|
import { TerminalManager } from '../terminal-manager.js';
|
|
17
17
|
import { ToolRouter } from '../tool-router.js';
|
|
18
18
|
import { McpServerRegistry, } from '../mcp-server-registry.js';
|
|
19
|
+
import { mcpExecutor } from '../mcp-executor.js';
|
|
19
20
|
import { ProjectRegistry } from '../project-registry.js';
|
|
20
21
|
import { getProductConfig } from '../product-config.js';
|
|
21
22
|
import { LocalBackendRouter } from '../local-backend/router.js';
|
|
@@ -264,6 +265,7 @@ export async function createHostRuntime(options) {
|
|
|
264
265
|
for (const handle of [...packHandles.values()].reverse()) {
|
|
265
266
|
await handle.dispose?.();
|
|
266
267
|
}
|
|
268
|
+
await mcpExecutor.shutdownAll();
|
|
267
269
|
await shutdownHostSidecar();
|
|
268
270
|
await closeStorage();
|
|
269
271
|
},
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 主进程界面文案。与渲染层同一规则:英文是源句,应用层 registerLocale 注入译文。
|
|
3
|
+
* 本模块不依赖 Electron,菜单在 app ready 时读取。
|
|
4
|
+
*/
|
|
5
|
+
export declare const SOURCE_LOCALE = "en";
|
|
6
|
+
export declare function normalizeLocale(raw: string | null | undefined): string | null;
|
|
7
|
+
export declare function resolveLocale(options: {
|
|
8
|
+
supported: readonly string[];
|
|
9
|
+
defaultLocale: string;
|
|
10
|
+
saved: string | null;
|
|
11
|
+
system: string | null;
|
|
12
|
+
}): string;
|
|
13
|
+
export declare function registerLocale(locale: string, messages: Record<string, string>): void;
|
|
14
|
+
export declare function configureI18n(options: {
|
|
15
|
+
locales: readonly string[];
|
|
16
|
+
defaultLocale: string;
|
|
17
|
+
systemLocale?: string | null;
|
|
18
|
+
savedLocale?: string | null;
|
|
19
|
+
}): void;
|
|
20
|
+
export declare function getLocale(): string;
|
|
21
|
+
export declare function setLocale(locale: string): boolean;
|
|
22
|
+
export declare function t(message: string, params?: Record<string, string | number>): string;
|
|
23
|
+
export declare function resetI18nForTests(): void;
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 主进程界面文案。与渲染层同一规则:英文是源句,应用层 registerLocale 注入译文。
|
|
3
|
+
* 本模块不依赖 Electron,菜单在 app ready 时读取。
|
|
4
|
+
*/
|
|
5
|
+
export const SOURCE_LOCALE = 'en';
|
|
6
|
+
export function normalizeLocale(raw) {
|
|
7
|
+
if (!raw)
|
|
8
|
+
return null;
|
|
9
|
+
const lower = raw.trim().toLowerCase().replace(/_/g, '-');
|
|
10
|
+
if (!lower)
|
|
11
|
+
return null;
|
|
12
|
+
if (lower.startsWith('zh'))
|
|
13
|
+
return 'zh';
|
|
14
|
+
if (lower.startsWith('en'))
|
|
15
|
+
return 'en';
|
|
16
|
+
const primary = lower.split('-')[0];
|
|
17
|
+
return primary || null;
|
|
18
|
+
}
|
|
19
|
+
export function resolveLocale(options) {
|
|
20
|
+
const supported = options.supported.filter((locale) => locale.length > 0);
|
|
21
|
+
const list = supported.length > 0 ? supported : [SOURCE_LOCALE];
|
|
22
|
+
const pick = (value) => {
|
|
23
|
+
if (!value)
|
|
24
|
+
return null;
|
|
25
|
+
if (list.includes(value))
|
|
26
|
+
return value;
|
|
27
|
+
const normalized = normalizeLocale(value);
|
|
28
|
+
if (normalized && list.includes(normalized))
|
|
29
|
+
return normalized;
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
return (pick(options.saved) ??
|
|
33
|
+
pick(options.system) ??
|
|
34
|
+
pick(options.defaultLocale) ??
|
|
35
|
+
list[0] ??
|
|
36
|
+
SOURCE_LOCALE);
|
|
37
|
+
}
|
|
38
|
+
function formatMessage(template, params) {
|
|
39
|
+
if (!params)
|
|
40
|
+
return template;
|
|
41
|
+
return template.replace(/\{(\w+)\}/g, (whole, name) => {
|
|
42
|
+
const value = params[name];
|
|
43
|
+
return value === undefined ? whole : String(value);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const catalogs = {};
|
|
47
|
+
let locales = [SOURCE_LOCALE];
|
|
48
|
+
let defaultLocale = SOURCE_LOCALE;
|
|
49
|
+
let current = SOURCE_LOCALE;
|
|
50
|
+
export function registerLocale(locale, messages) {
|
|
51
|
+
const normalized = locale.trim();
|
|
52
|
+
if (!normalized || normalized === SOURCE_LOCALE)
|
|
53
|
+
return;
|
|
54
|
+
catalogs[normalized] = { ...catalogs[normalized], ...messages };
|
|
55
|
+
}
|
|
56
|
+
export function configureI18n(options) {
|
|
57
|
+
const next = [...new Set(options.locales.filter((locale) => locale.length > 0))];
|
|
58
|
+
locales = next.length > 0 ? next : [SOURCE_LOCALE];
|
|
59
|
+
defaultLocale = options.defaultLocale || SOURCE_LOCALE;
|
|
60
|
+
current = resolveLocale({
|
|
61
|
+
supported: locales,
|
|
62
|
+
defaultLocale,
|
|
63
|
+
saved: options.savedLocale ?? null,
|
|
64
|
+
system: options.systemLocale ?? null,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export function getLocale() {
|
|
68
|
+
return current;
|
|
69
|
+
}
|
|
70
|
+
export function setLocale(locale) {
|
|
71
|
+
if (!locales.includes(locale))
|
|
72
|
+
return false;
|
|
73
|
+
current = locale;
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
export function t(message, params) {
|
|
77
|
+
const table = current === SOURCE_LOCALE ? undefined : catalogs[current];
|
|
78
|
+
const template = table?.[message] ?? message;
|
|
79
|
+
return formatMessage(template, params);
|
|
80
|
+
}
|
|
81
|
+
export function resetI18nForTests() {
|
|
82
|
+
for (const key of Object.keys(catalogs))
|
|
83
|
+
delete catalogs[key];
|
|
84
|
+
locales = [SOURCE_LOCALE];
|
|
85
|
+
defaultLocale = SOURCE_LOCALE;
|
|
86
|
+
current = SOURCE_LOCALE;
|
|
87
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { NativeImageLike } from './runtime.js';
|
|
2
1
|
import type { LlmImage } from './llm/types.js';
|
|
3
2
|
/** Refuse to read source files larger than this (10 MB). */
|
|
4
3
|
export declare const IMAGE_MAX_SOURCE_BYTES: number;
|
|
@@ -6,6 +5,34 @@ export declare const IMAGE_MAX_SOURCE_BYTES: number;
|
|
|
6
5
|
export declare const IMAGE_MAX_DIMENSION = 1568;
|
|
7
6
|
/** Refuse the encoded payload if it still exceeds this after re-encode (5 MB). */
|
|
8
7
|
export declare const IMAGE_MAX_ENCODED_BYTES: number;
|
|
8
|
+
/** A decoded raster that can be cropped, resized, and re-encoded synchronously. */
|
|
9
|
+
export interface DecodedImage {
|
|
10
|
+
isEmpty(): boolean;
|
|
11
|
+
getSize(): {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
crop(rect: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
}): DecodedImage;
|
|
21
|
+
resize(o: {
|
|
22
|
+
width?: number;
|
|
23
|
+
height?: number;
|
|
24
|
+
quality?: string;
|
|
25
|
+
}): DecodedImage;
|
|
26
|
+
toPNG(): Buffer;
|
|
27
|
+
toJPEG(quality: number): Buffer;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Optional image decoder. Without one, images pass through as the original
|
|
31
|
+
* bytes and crop / resize / transcode requests are refused.
|
|
32
|
+
*/
|
|
33
|
+
export interface ImageDecoder {
|
|
34
|
+
createFromPath(p: string): DecodedImage;
|
|
35
|
+
}
|
|
9
36
|
export interface ImageAttachmentInput {
|
|
10
37
|
path: string;
|
|
11
38
|
name?: string;
|
|
@@ -65,9 +92,9 @@ export declare function computeTargetSize(width: number, height: number, maxDime
|
|
|
65
92
|
* tool result. `_image` is consumed by the CoreLoop as an image content part;
|
|
66
93
|
* it is not a path or model-visible base64 string.
|
|
67
94
|
*/
|
|
68
|
-
export declare function processViewImage(input: ViewImageInput,
|
|
95
|
+
export declare function processViewImage(input: ViewImageInput, decoder?: ImageDecoder | null): ViewImageResult;
|
|
69
96
|
/**
|
|
70
|
-
* Process a batch of image attachments. Synchronous:
|
|
97
|
+
* Process a batch of image attachments. Synchronous: decoder decode /
|
|
71
98
|
* resize / encode are all synchronous, and the byte check uses `statSync`.
|
|
72
99
|
*/
|
|
73
|
-
export declare function processImageAttachments(files: ImageAttachmentInput[]): ProcessedImageAttachments;
|
|
100
|
+
export declare function processImageAttachments(files: ImageAttachmentInput[], decoder?: ImageDecoder | null): ProcessedImageAttachments;
|
package/dist/image-attachment.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* actually see, replacing the old behavior of dropping only the file *path*
|
|
4
4
|
* into the prompt text.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* enforced before the image ever reaches the model:
|
|
6
|
+
* Decoding / downscaling runs only when an {@link ImageDecoder} is supplied;
|
|
7
|
+
* otherwise the original bytes pass through under the encoded cap. These caps
|
|
8
|
+
* are enforced before the image ever reaches the model:
|
|
9
9
|
*
|
|
10
10
|
* - source bytes (`IMAGE_MAX_SOURCE_BYTES`) — refuse to read huge files;
|
|
11
11
|
* - long-edge px (`IMAGE_MAX_DIMENSION`) — downscale so the provider's
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
* line so the caller can inject it into the model-visible context — the model
|
|
18
18
|
* should know an image was attached even when it was too large to send.
|
|
19
19
|
*/
|
|
20
|
-
import { getNativeImage } from './runtime.js';
|
|
21
20
|
import { readFileSync, statSync } from 'fs';
|
|
22
21
|
import { basename, extname } from 'path';
|
|
23
22
|
/** Refuse to read source files larger than this (10 MB). */
|
|
@@ -74,7 +73,7 @@ export function computeTargetSize(width, height, maxDimension = IMAGE_MAX_DIMENS
|
|
|
74
73
|
* tool result. `_image` is consumed by the CoreLoop as an image content part;
|
|
75
74
|
* it is not a path or model-visible base64 string.
|
|
76
75
|
*/
|
|
77
|
-
export function processViewImage(input,
|
|
76
|
+
export function processViewImage(input, decoder = null) {
|
|
78
77
|
if (!isImagePath(input.path)) {
|
|
79
78
|
return {
|
|
80
79
|
success: false,
|
|
@@ -104,10 +103,10 @@ export function processViewImage(input, nativeImage = getNativeImage()) {
|
|
|
104
103
|
needsFollowup: true,
|
|
105
104
|
};
|
|
106
105
|
}
|
|
107
|
-
if (!
|
|
106
|
+
if (!decoder) {
|
|
108
107
|
return viewImageWithoutDecoder(input, sourceBytes, maxEdge);
|
|
109
108
|
}
|
|
110
|
-
let rendered =
|
|
109
|
+
let rendered = decoder.createFromPath(input.path);
|
|
111
110
|
if (rendered.isEmpty()) {
|
|
112
111
|
return { success: false, error: '不是可识别的图片', needsFollowup: true };
|
|
113
112
|
}
|
|
@@ -262,16 +261,15 @@ function formatMb(bytes) {
|
|
|
262
261
|
return (bytes / (1024 * 1024)).toFixed(1).replace(/\.0$/, '');
|
|
263
262
|
}
|
|
264
263
|
/**
|
|
265
|
-
* Process a batch of image attachments. Synchronous:
|
|
264
|
+
* Process a batch of image attachments. Synchronous: decoder decode /
|
|
266
265
|
* resize / encode are all synchronous, and the byte check uses `statSync`.
|
|
267
266
|
*/
|
|
268
|
-
export function processImageAttachments(files) {
|
|
267
|
+
export function processImageAttachments(files, decoder = null) {
|
|
269
268
|
const images = [];
|
|
270
269
|
const notes = [];
|
|
271
270
|
for (const file of files) {
|
|
272
271
|
const label = file.name || basename(file.path);
|
|
273
|
-
// Existence + source-size guards run before the decoder
|
|
274
|
-
// exercisable in a non-Electron (test) host too.
|
|
272
|
+
// Existence + source-size guards run before the decoder.
|
|
275
273
|
let sourceBytes = 0;
|
|
276
274
|
try {
|
|
277
275
|
sourceBytes = statSync(file.path).size;
|
|
@@ -284,12 +282,9 @@ export function processImageAttachments(files) {
|
|
|
284
282
|
notes.push(`- ${label}:源文件 ${formatMb(sourceBytes)}MB 超过 ${formatMb(IMAGE_MAX_SOURCE_BYTES)}MB 上限,未附加`);
|
|
285
283
|
continue;
|
|
286
284
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
//
|
|
290
|
-
// nativeImage. The bytes are already a PNG/JPEG the provider can read;
|
|
291
|
-
// pass them through when they fit the encoded cap. Resize stays on the
|
|
292
|
-
// Electron path.
|
|
285
|
+
if (!decoder) {
|
|
286
|
+
// The bytes are already an image the provider can read; pass them
|
|
287
|
+
// through when they fit the encoded cap.
|
|
293
288
|
if (sourceBytes > IMAGE_MAX_ENCODED_BYTES) {
|
|
294
289
|
notes.push(`- ${label}:源文件 ${formatMb(sourceBytes)}MB 超过 ${formatMb(IMAGE_MAX_ENCODED_BYTES)}MB,当前环境不能缩放,未附加`);
|
|
295
290
|
continue;
|
|
@@ -307,7 +302,7 @@ export function processImageAttachments(files) {
|
|
|
307
302
|
notes.push(`- ${label}(原图)`);
|
|
308
303
|
continue;
|
|
309
304
|
}
|
|
310
|
-
const image =
|
|
305
|
+
const image = decoder.createFromPath(file.path);
|
|
311
306
|
if (image.isEmpty()) {
|
|
312
307
|
notes.push(`- ${label}:不是可识别的图片,未附加`);
|
|
313
308
|
continue;
|
package/dist/json-store.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JSON 键值存储工厂(agent-mcp-servers.json / agent-projects.json /
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* 而 conf(electron-store 的底层,已是本项目直接依赖)API 完全覆盖用到的
|
|
6
|
-
* get/set + name + defaults 子集。统一走 conf + getUserDataDir(),Electron
|
|
7
|
-
* 与 BS server 得到同一个文件路径,两种模式共享同一份数据。
|
|
2
|
+
* JSON 键值存储工厂(agent-mcp-servers.json / agent-projects.json /
|
|
3
|
+
* agent-local-scripts.json)。文件在 getUserDataDir() 下,由 conf 读写;
|
|
4
|
+
* 桌面与 BS server 得到同一个文件路径。
|
|
8
5
|
*
|
|
9
6
|
* conf@10 的 d.ts 是 ESM 风格但包本身没有 `type: module`,NodeNext 下类型
|
|
10
7
|
* 解析会塌成 namespace——所以这里声明用到的最小接口并做一次强转,不依赖
|
|
@@ -69,9 +69,14 @@ export interface PortableWebSearchSection {
|
|
|
69
69
|
}
|
|
70
70
|
export interface PortableMcpSection {
|
|
71
71
|
name: string;
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
transport: 'stdio' | 'streamable-http';
|
|
73
|
+
command?: string;
|
|
74
|
+
args?: string[];
|
|
74
75
|
cwd?: string;
|
|
76
|
+
url?: string;
|
|
77
|
+
headers?: Record<string, string>;
|
|
78
|
+
headersFromEnv?: Record<string, string>;
|
|
79
|
+
bearerTokenEnvVar?: string;
|
|
75
80
|
enabled: boolean;
|
|
76
81
|
envIncluded: boolean;
|
|
77
82
|
env?: Record<string, string>;
|
|
@@ -178,11 +183,20 @@ export declare function webSearchSectionFromSettings(settings: {
|
|
|
178
183
|
}, includeSecrets: boolean): PortableWebSearchSection;
|
|
179
184
|
export declare function mcpSectionFromServer(server: {
|
|
180
185
|
name: string;
|
|
186
|
+
transport: 'stdio';
|
|
181
187
|
command: string;
|
|
182
188
|
args: string[];
|
|
183
189
|
cwd?: string;
|
|
184
190
|
enabled: boolean;
|
|
185
191
|
env: Record<string, string>;
|
|
192
|
+
} | {
|
|
193
|
+
name: string;
|
|
194
|
+
transport: 'streamable-http';
|
|
195
|
+
url: string;
|
|
196
|
+
headers: Record<string, string>;
|
|
197
|
+
headersFromEnv: Record<string, string>;
|
|
198
|
+
bearerTokenEnvVar?: string;
|
|
199
|
+
enabled: boolean;
|
|
186
200
|
}, includeSecrets: boolean): PortableMcpSection;
|
|
187
201
|
/**
|
|
188
202
|
* 把消息正文和 metadata 里的旧附件绝对路径换成新会话里的路径。
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* 钥匙(模型 Key、搜索 Key、MCP 环境变量)默认不进包;`includeSecrets`
|
|
6
6
|
* 为 true 时原样写入,导入方要自己确认。
|
|
7
7
|
*/
|
|
8
|
+
import { t } from '../i18n.js';
|
|
8
9
|
export const PORTABLE_SCHEMA_VERSION = 1;
|
|
9
10
|
export const PORTABLE_MESSAGE_LIMIT = 1000;
|
|
10
11
|
export const PORTABLE_CONFIG_KIND = 'steerable-config';
|
|
@@ -64,19 +65,19 @@ export function readPortableProject(raw) {
|
|
|
64
65
|
}
|
|
65
66
|
function thinkingLabel(mode) {
|
|
66
67
|
if (mode === 'hidden')
|
|
67
|
-
return '
|
|
68
|
+
return t('Hide');
|
|
68
69
|
if (mode === 'full')
|
|
69
|
-
return '
|
|
70
|
+
return t('Show all');
|
|
70
71
|
if (mode === 'peek')
|
|
71
|
-
return '
|
|
72
|
-
return '
|
|
72
|
+
return t('Show 5 lines');
|
|
73
|
+
return t('Unrecognized display mode');
|
|
73
74
|
}
|
|
74
75
|
function execLabel(policy) {
|
|
75
76
|
if (policy === 'full')
|
|
76
|
-
return '
|
|
77
|
+
return t('Full access');
|
|
77
78
|
if (policy === 'workspace')
|
|
78
|
-
return '
|
|
79
|
-
return '
|
|
79
|
+
return t('Workspace');
|
|
80
|
+
return t('Unrecognized permission');
|
|
80
81
|
}
|
|
81
82
|
export function llmSectionFromSettings(settings, includeSecrets) {
|
|
82
83
|
const apiKey = asString(settings.apiKey);
|
|
@@ -106,15 +107,27 @@ export function webSearchSectionFromSettings(settings, includeSecrets) {
|
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
export function mcpSectionFromServer(server, includeSecrets) {
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
return server.transport === 'stdio'
|
|
111
|
+
? {
|
|
112
|
+
name: server.name,
|
|
113
|
+
transport: 'stdio',
|
|
114
|
+
command: server.command,
|
|
115
|
+
args: [...server.args],
|
|
116
|
+
cwd: server.cwd,
|
|
117
|
+
enabled: server.enabled,
|
|
118
|
+
envIncluded: includeSecrets,
|
|
119
|
+
...(includeSecrets ? { env: { ...server.env } } : {}),
|
|
120
|
+
}
|
|
121
|
+
: {
|
|
122
|
+
name: server.name,
|
|
123
|
+
transport: 'streamable-http',
|
|
124
|
+
url: server.url,
|
|
125
|
+
headers: { ...server.headers },
|
|
126
|
+
headersFromEnv: { ...server.headersFromEnv },
|
|
127
|
+
bearerTokenEnvVar: server.bearerTokenEnvVar,
|
|
128
|
+
enabled: server.enabled,
|
|
129
|
+
envIncluded: false,
|
|
130
|
+
};
|
|
118
131
|
}
|
|
119
132
|
/**
|
|
120
133
|
* 把消息正文和 metadata 里的旧附件绝对路径换成新会话里的路径。
|
|
@@ -180,22 +180,36 @@ async function applyMcp(mcp, servers) {
|
|
|
180
180
|
return 'MCP 注册表不可用';
|
|
181
181
|
for (const server of servers) {
|
|
182
182
|
const name = server.name?.trim();
|
|
183
|
-
|
|
184
|
-
if (!name || !command)
|
|
183
|
+
if (!name)
|
|
185
184
|
continue;
|
|
186
|
-
const input =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
185
|
+
const input = server.transport === 'streamable-http'
|
|
186
|
+
? {
|
|
187
|
+
name,
|
|
188
|
+
transport: 'streamable-http',
|
|
189
|
+
url: server.url?.trim() ?? '',
|
|
190
|
+
headers: server.headers ?? {},
|
|
191
|
+
headersFromEnv: server.headersFromEnv ?? {},
|
|
192
|
+
bearerTokenEnvVar: server.bearerTokenEnvVar,
|
|
193
|
+
enabled: server.enabled !== false,
|
|
194
|
+
}
|
|
195
|
+
: {
|
|
196
|
+
name,
|
|
197
|
+
transport: 'stdio',
|
|
198
|
+
command: server.command?.trim() ?? '',
|
|
199
|
+
args: Array.isArray(server.args) ? server.args.map((arg) => String(arg)) : [],
|
|
200
|
+
cwd: server.cwd,
|
|
201
|
+
enabled: server.enabled !== false,
|
|
202
|
+
...(server.envIncluded && server.env ? { env: server.env } : {}),
|
|
203
|
+
};
|
|
204
|
+
if ((input.transport === 'stdio' && !input.command) ||
|
|
205
|
+
(input.transport === 'streamable-http' && !input.url)) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
194
208
|
const existing = mcp.list().find((item) => item.name.toLowerCase() === name.toLowerCase());
|
|
195
209
|
if (existing)
|
|
196
210
|
mcp.update(existing.id, input);
|
|
197
211
|
else
|
|
198
|
-
mcp.create(
|
|
212
|
+
mcp.create(input);
|
|
199
213
|
}
|
|
200
214
|
return null;
|
|
201
215
|
}
|