@steerable/agent-shell 0.6.15
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/LICENSE +91 -0
- package/contracts/tool-contract.json +326 -0
- package/dist/attachments.d.ts +41 -0
- package/dist/attachments.js +147 -0
- package/dist/brand.d.ts +24 -0
- package/dist/brand.js +92 -0
- package/dist/host/http-routes.d.ts +21 -0
- package/dist/host/http-routes.js +55 -0
- package/dist/host/ipc.d.ts +11 -0
- package/dist/host/ipc.js +20 -0
- package/dist/host/pack-assembly.d.ts +86 -0
- package/dist/host/pack-assembly.js +32 -0
- package/dist/host/runtime.d.ts +69 -0
- package/dist/host/runtime.js +207 -0
- package/dist/host/visible-terminal-exec.d.ts +21 -0
- package/dist/host/visible-terminal-exec.js +151 -0
- package/dist/hosted-web-search.d.ts +13 -0
- package/dist/hosted-web-search.js +82 -0
- package/dist/image-attachment.d.ts +39 -0
- package/dist/image-attachment.js +133 -0
- package/dist/insights/flush.d.ts +10 -0
- package/dist/insights/flush.js +139 -0
- package/dist/insights/record.d.ts +17 -0
- package/dist/insights/record.js +44 -0
- package/dist/json-store.d.ts +9 -0
- package/dist/json-store.js +22 -0
- package/dist/llm/index.d.ts +23 -0
- package/dist/llm/index.js +105 -0
- package/dist/llm/ollama.d.ts +23 -0
- package/dist/llm/ollama.js +242 -0
- package/dist/llm/openai-compat.d.ts +20 -0
- package/dist/llm/openai-compat.js +199 -0
- package/dist/llm/sidecar-provider.d.ts +37 -0
- package/dist/llm/sidecar-provider.js +163 -0
- package/dist/llm/tool-choice.d.ts +35 -0
- package/dist/llm/tool-choice.js +85 -0
- package/dist/llm/types.d.ts +122 -0
- package/dist/llm/types.js +1 -0
- package/dist/local-backend/agent-capability.d.ts +101 -0
- package/dist/local-backend/agent-capability.js +174 -0
- package/dist/local-backend/ai-title.d.ts +43 -0
- package/dist/local-backend/ai-title.js +173 -0
- package/dist/local-backend/auto-continue-helper.d.ts +80 -0
- package/dist/local-backend/auto-continue-helper.js +83 -0
- package/dist/local-backend/branch-helper.d.ts +24 -0
- package/dist/local-backend/branch-helper.js +27 -0
- package/dist/local-backend/context-compactor.d.ts +81 -0
- package/dist/local-backend/context-compactor.js +213 -0
- package/dist/local-backend/coreloop-stream.d.ts +245 -0
- package/dist/local-backend/coreloop-stream.js +277 -0
- package/dist/local-backend/deferred-detector.d.ts +15 -0
- package/dist/local-backend/deferred-detector.js +124 -0
- package/dist/local-backend/history-helper.d.ts +30 -0
- package/dist/local-backend/history-helper.js +34 -0
- package/dist/local-backend/interrupted-helper.d.ts +29 -0
- package/dist/local-backend/interrupted-helper.js +25 -0
- package/dist/local-backend/live-stream.d.ts +36 -0
- package/dist/local-backend/live-stream.js +23 -0
- package/dist/local-backend/llm-diagnose.d.ts +37 -0
- package/dist/local-backend/llm-diagnose.js +284 -0
- package/dist/local-backend/message-triggers.d.ts +27 -0
- package/dist/local-backend/message-triggers.js +67 -0
- package/dist/local-backend/pack-backend-routes.d.ts +31 -0
- package/dist/local-backend/pack-backend-routes.js +62 -0
- package/dist/local-backend/pack-turn-hooks.d.ts +37 -0
- package/dist/local-backend/pack-turn-hooks.js +67 -0
- package/dist/local-backend/prompt-builder.d.ts +101 -0
- package/dist/local-backend/prompt-builder.js +246 -0
- package/dist/local-backend/regenerate-helper.d.ts +62 -0
- package/dist/local-backend/regenerate-helper.js +75 -0
- package/dist/local-backend/router.d.ts +175 -0
- package/dist/local-backend/router.js +3139 -0
- package/dist/local-backend/skill-install.d.ts +19 -0
- package/dist/local-backend/skill-install.js +71 -0
- package/dist/local-backend/skill-loader.d.ts +92 -0
- package/dist/local-backend/skill-loader.js +146 -0
- package/dist/local-backend/skills/00-identity/SKILL.md +32 -0
- package/dist/local-backend/skills/10-goal/SKILL.md +59 -0
- package/dist/local-backend/skills/11-loop/SKILL.md +71 -0
- package/dist/local-backend/skills/12-create-skill/SKILL.md +88 -0
- package/dist/local-backend/skills/70-plan-mode/SKILL.md +58 -0
- package/dist/local-backend/skills/80-tool-usage/SKILL.md +70 -0
- package/dist/local-backend/skills/81-anti-deferred/SKILL.md +53 -0
- package/dist/local-backend/skills/82-data-grounding/SKILL.md +56 -0
- package/dist/local-backend/skills/85-local-exec/SKILL.md +86 -0
- package/dist/local-backend/skills/86-proactive-coding/SKILL.md +51 -0
- package/dist/local-backend/subagent-profiles.d.ts +30 -0
- package/dist/local-backend/subagent-profiles.js +74 -0
- package/dist/local-backend/task-process.d.ts +12 -0
- package/dist/local-backend/task-process.js +176 -0
- package/dist/local-backend/task-service.d.ts +135 -0
- package/dist/local-backend/task-service.js +565 -0
- package/dist/local-backend/turn-duration.d.ts +2 -0
- package/dist/local-backend/turn-duration.js +9 -0
- package/dist/local-backend/turn-timeline.d.ts +16 -0
- package/dist/local-backend/turn-timeline.js +42 -0
- package/dist/local-backend/worktree-service.d.ts +84 -0
- package/dist/local-backend/worktree-service.js +243 -0
- package/dist/local-edit.d.ts +48 -0
- package/dist/local-edit.js +44 -0
- package/dist/local-executor.d.ts +255 -0
- package/dist/local-executor.js +881 -0
- package/dist/local-script-registry.d.ts +28 -0
- package/dist/local-script-registry.js +63 -0
- package/dist/log.d.ts +13 -0
- package/dist/log.js +12 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +855 -0
- package/dist/mcp-executor.d.ts +45 -0
- package/dist/mcp-executor.js +241 -0
- package/dist/mcp-server-registry.d.ts +104 -0
- package/dist/mcp-server-registry.js +234 -0
- package/dist/preload-default.d.ts +1 -0
- package/dist/preload-default.js +9 -0
- package/dist/preload.cjs +395 -0
- package/dist/preload.d.ts +20 -0
- package/dist/preload.js +411 -0
- package/dist/product-config.d.ts +43 -0
- package/dist/product-config.js +26 -0
- package/dist/project-registry.d.ts +55 -0
- package/dist/project-registry.js +106 -0
- package/dist/project-rules.d.ts +15 -0
- package/dist/project-rules.js +102 -0
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.js +217 -0
- package/dist/scenario/pack.d.ts +8 -0
- package/dist/scenario/pack.js +1 -0
- package/dist/scenario/registry.d.ts +24 -0
- package/dist/scenario/registry.js +31 -0
- package/dist/server/http-server.d.ts +39 -0
- package/dist/server/http-server.js +361 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +107 -0
- package/dist/server/sse-bus.d.ts +14 -0
- package/dist/server/sse-bus.js +31 -0
- package/dist/shell-adapt.d.ts +21 -0
- package/dist/shell-adapt.js +104 -0
- package/dist/sidecar/boot.d.ts +36 -0
- package/dist/sidecar/boot.js +343 -0
- package/dist/sidecar/egress-hint.d.ts +15 -0
- package/dist/sidecar/egress-hint.js +46 -0
- package/dist/sidecar/egress-proxy.d.ts +183 -0
- package/dist/sidecar/egress-proxy.js +419 -0
- package/dist/sidecar/errors.d.ts +22 -0
- package/dist/sidecar/errors.js +38 -0
- package/dist/sidecar/exec-sandbox.d.ts +48 -0
- package/dist/sidecar/exec-sandbox.js +94 -0
- package/dist/sidecar/handle.d.ts +32 -0
- package/dist/sidecar/handle.js +53 -0
- package/dist/sidecar/index.d.ts +14 -0
- package/dist/sidecar/index.js +13 -0
- package/dist/sidecar/proxy-detect.d.ts +50 -0
- package/dist/sidecar/proxy-detect.js +182 -0
- package/dist/sidecar/reverse-approval.d.ts +55 -0
- package/dist/sidecar/reverse-approval.js +86 -0
- package/dist/sidecar/reverse-ask-user.d.ts +34 -0
- package/dist/sidecar/reverse-ask-user.js +59 -0
- package/dist/sidecar/reverse-spawn.d.ts +19 -0
- package/dist/sidecar/reverse-spawn.js +161 -0
- package/dist/sidecar/reverse-tools.d.ts +29 -0
- package/dist/sidecar/reverse-tools.js +106 -0
- package/dist/sidecar/safety-patterns.d.ts +41 -0
- package/dist/sidecar/safety-patterns.js +157 -0
- package/dist/sidecar/storage-path.d.ts +14 -0
- package/dist/sidecar/storage-path.js +35 -0
- package/dist/sidecar/supervisor.d.ts +218 -0
- package/dist/sidecar/supervisor.js +932 -0
- package/dist/sidecar/types.d.ts +601 -0
- package/dist/sidecar/types.js +1 -0
- package/dist/single-instance.d.ts +11 -0
- package/dist/single-instance.js +21 -0
- package/dist/storage/empty-chats.d.ts +9 -0
- package/dist/storage/empty-chats.js +16 -0
- package/dist/storage/index.d.ts +373 -0
- package/dist/storage/index.js +1158 -0
- package/dist/storage/insights-redact.d.ts +2 -0
- package/dist/storage/insights-redact.js +30 -0
- package/dist/storage/insights-settings.d.ts +53 -0
- package/dist/storage/insights-settings.js +92 -0
- package/dist/storage/llm-settings.d.ts +120 -0
- package/dist/storage/llm-settings.js +233 -0
- package/dist/storage/local-store-singleton.d.ts +28 -0
- package/dist/storage/local-store-singleton.js +38 -0
- package/dist/storage/message-order.d.ts +25 -0
- package/dist/storage/message-order.js +27 -0
- package/dist/storage/pack-migrations.d.ts +22 -0
- package/dist/storage/pack-migrations.js +24 -0
- package/dist/storage/pack-seeds.d.ts +36 -0
- package/dist/storage/pack-seeds.js +42 -0
- package/dist/storage/telemetry-settings.d.ts +38 -0
- package/dist/storage/telemetry-settings.js +59 -0
- package/dist/storage/usage-summary.d.ts +55 -0
- package/dist/storage/usage-summary.js +38 -0
- package/dist/storage/web-search-settings.d.ts +38 -0
- package/dist/storage/web-search-settings.js +74 -0
- package/dist/storage/write-lease.d.ts +26 -0
- package/dist/storage/write-lease.js +74 -0
- package/dist/terminal-manager.d.ts +83 -0
- package/dist/terminal-manager.js +506 -0
- package/dist/tool-router.d.ts +228 -0
- package/dist/tool-router.js +930 -0
- package/dist/tool-search-rank.d.ts +42 -0
- package/dist/tool-search-rank.js +96 -0
- package/package.json +67 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W6-7a 项目规则文件加载(AGENTS.md / CLAUDE.md 生态约定)。
|
|
3
|
+
*
|
|
4
|
+
* 从绑定的项目目录向上遍历,收集每一层的规则文件,拼成一段注入模型上下文。
|
|
5
|
+
* 三个对照框架都有这一机制(pi 的 `ResourceLoader` 向上遍历
|
|
6
|
+
* `AGENTS.override.md`/`AGENTS.md`/`CLAUDE.md`;codex 把 AGENTS.md 当作
|
|
7
|
+
* WorldState 的一节),我们此前完全没有。
|
|
8
|
+
*
|
|
9
|
+
* 安全前提(W6-5):这些文件是「项目作者写给 agent 的指令」,属于不可信输入。
|
|
10
|
+
* 本模块只做发现并返回内容;**是否注入由调用方按项目信任状态决定**——未信任
|
|
11
|
+
* 的项目一律不调用本模块。遍历与注入都有硬上限,单个文件与总量都截断。
|
|
12
|
+
*
|
|
13
|
+
* 「热重载」按回合粒度天然成立:每个回合重新读取磁盘,规则文件保存即生效,
|
|
14
|
+
* 不需要文件 watcher。
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
/** 每层目录按此顺序查找(override 优先于通用约定)。 */
|
|
20
|
+
const RULE_FILE_NAMES = ['AGENTS.override.md', 'AGENTS.md', 'CLAUDE.md'];
|
|
21
|
+
/** 最多向上遍历的层数(含起始目录)——支持 monorepo 把规则放在父目录。 */
|
|
22
|
+
const MAX_LEVELS = 6;
|
|
23
|
+
/** 最多注入的规则文件个数。 */
|
|
24
|
+
const MAX_RULE_FILES = 8;
|
|
25
|
+
/** 单个规则文件的最大字符数(超出截断)。 */
|
|
26
|
+
const MAX_FILE_CHARS = 8_000;
|
|
27
|
+
/** 注入内容总字符上限(W4-7 注入内容必须有界)。 */
|
|
28
|
+
const MAX_TOTAL_CHARS = 20_000;
|
|
29
|
+
const EMPTY = { files: [], content: '', truncated: false };
|
|
30
|
+
/**
|
|
31
|
+
* 收集并拼接项目规则。`folderPath` 为绑定的项目根目录。
|
|
32
|
+
*
|
|
33
|
+
* 向上遍历在「用户 home 目录」处封顶(若项目不在 home 下则按 MAX_LEVELS
|
|
34
|
+
* 封顶)——不扫 `/`、`/etc` 这类与项目无关的祖先目录。
|
|
35
|
+
*/
|
|
36
|
+
export function loadProjectRuleFiles(folderPath) {
|
|
37
|
+
if (!folderPath || !folderPath.trim())
|
|
38
|
+
return EMPTY;
|
|
39
|
+
const start = path.resolve(folderPath);
|
|
40
|
+
if (!fs.existsSync(start))
|
|
41
|
+
return EMPTY;
|
|
42
|
+
// 构造 根→…→起始目录 的目录链(根在前,越往后越贴近项目)。
|
|
43
|
+
const home = os.homedir();
|
|
44
|
+
const chain = [];
|
|
45
|
+
let dir = start;
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
while (!seen.has(dir) && chain.length < MAX_LEVELS) {
|
|
48
|
+
seen.add(dir);
|
|
49
|
+
chain.unshift(dir);
|
|
50
|
+
if (dir === home)
|
|
51
|
+
break; // 不越过 home 向上
|
|
52
|
+
const parent = path.dirname(dir);
|
|
53
|
+
if (parent === dir)
|
|
54
|
+
break; // 已到文件系统根
|
|
55
|
+
dir = parent;
|
|
56
|
+
}
|
|
57
|
+
// 根→叶收集规则文件;每层内 override 优先。
|
|
58
|
+
const files = [];
|
|
59
|
+
for (const d of chain) {
|
|
60
|
+
for (const name of RULE_FILE_NAMES) {
|
|
61
|
+
const p = path.join(d, name);
|
|
62
|
+
try {
|
|
63
|
+
if (fs.statSync(p).isFile())
|
|
64
|
+
files.push(p);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// 不存在或不可读——跳过该文件。
|
|
68
|
+
}
|
|
69
|
+
if (files.length >= MAX_RULE_FILES)
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
if (files.length >= MAX_RULE_FILES)
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
if (files.length === 0)
|
|
76
|
+
return EMPTY;
|
|
77
|
+
// 拼接并施加总量上限。
|
|
78
|
+
const sections = [];
|
|
79
|
+
let total = 0;
|
|
80
|
+
let truncated = false;
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
let body;
|
|
83
|
+
try {
|
|
84
|
+
body = fs.readFileSync(file, 'utf-8');
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (body.length > MAX_FILE_CHARS) {
|
|
90
|
+
body = `${body.slice(0, MAX_FILE_CHARS)}\n…(规则文件过长,已截断)`;
|
|
91
|
+
truncated = true;
|
|
92
|
+
}
|
|
93
|
+
const section = `## 项目规则(来自 \`${file}\`)\n\n${body.trim()}`;
|
|
94
|
+
if (total + section.length > MAX_TOTAL_CHARS) {
|
|
95
|
+
truncated = true;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
sections.push(section);
|
|
99
|
+
total += section.length;
|
|
100
|
+
}
|
|
101
|
+
return { files, content: sections.join('\n\n'), truncated };
|
|
102
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Electron 主进程里 `process.versions.electron` 有值;普通 Node(BS server、vitest)没有。 */
|
|
2
|
+
export declare function isElectronRuntime(): boolean;
|
|
3
|
+
/** 应用数据目录(SQLite、JSON store、用户技能目录的根)。 */
|
|
4
|
+
export declare function getUserDataDir(): string;
|
|
5
|
+
/**
|
|
6
|
+
* 注入应用根目录(产品组装根调用)。重复注入不同值抛错(组装期笔误,
|
|
7
|
+
* fail fast——与 setProductBrand 同语义)。
|
|
8
|
+
*/
|
|
9
|
+
export declare function setAppRootDir(dir: string): void;
|
|
10
|
+
export declare function getAppRootDir(): string;
|
|
11
|
+
/**
|
|
12
|
+
* 注入 preload bundle 绝对路径(产品组装根/入口调用)。重复注入不同值
|
|
13
|
+
* 抛错(组装期笔误,fail fast——与 setAppRootDir 同语义)。
|
|
14
|
+
*/
|
|
15
|
+
export declare function setPreloadPath(file: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* 解析窗口应加载的 preload bundle:产品注入优先,其次 shell 自带默认
|
|
18
|
+
* (shellDir 一般为 shell main.js 所在目录)。
|
|
19
|
+
*/
|
|
20
|
+
export declare function getPreloadPath(shellDir: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* 产品 web 产物目录(2.3):产品入口(products/<id>/{main,server}.ts)
|
|
23
|
+
* 用本函数把 DEEPPATH_WEB_DIST 注入环境,shell 的 main/server 据此定位
|
|
24
|
+
* web dist。双平面探测:源码平面入口在 products/<id>/(web dist 在同级
|
|
25
|
+
* web/dist);编译平面入口在 products/<id>/dist/products/<id>/(web dist
|
|
26
|
+
* 在仓库根的 products/<id>/web/dist)。两处都没有返回 null(打包后的
|
|
27
|
+
* CS 走 app.getAppPath()/web-dist 约定,不经本函数)。
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveProductWebDist(entryUrl: string): string | null;
|
|
30
|
+
/** 用系统默认应用打开本地路径(Electron `shell.openPath` 的跨宿主等价物)。 */
|
|
31
|
+
export declare function shellOpenPath(target: string): Promise<string>;
|
|
32
|
+
/** 用系统浏览器打开 URL(Electron `shell.openExternal` 的跨宿主等价物)。 */
|
|
33
|
+
export declare function shellOpenExternal(url: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* 注册应用退出钩子(sidecar 的兜底清理)。BS 下没有 app 生命周期——
|
|
36
|
+
* 宿主退出由 server 的 SIGINT/SIGTERM 钩子统一负责,这里 no-op。
|
|
37
|
+
*/
|
|
38
|
+
export declare function onAppWillQuit(listener: () => void): void;
|
|
39
|
+
export declare function offAppWillQuit(listener: () => void): void;
|
|
40
|
+
interface NativeImageInstance {
|
|
41
|
+
isEmpty(): boolean;
|
|
42
|
+
getSize(): {
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
};
|
|
46
|
+
resize(o: {
|
|
47
|
+
width?: number;
|
|
48
|
+
height?: number;
|
|
49
|
+
quality?: string;
|
|
50
|
+
}): NativeImageInstance;
|
|
51
|
+
toPNG(): Buffer;
|
|
52
|
+
toJPEG(quality: number): Buffer;
|
|
53
|
+
}
|
|
54
|
+
interface NativeImageLike {
|
|
55
|
+
createFromPath(p: string): NativeImageInstance;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Electron `nativeImage`(图片解码/缩放)。BS / 单测等无 Electron 环境返回
|
|
59
|
+
* null——调用方按"不支持图片解码"降级(image-attachment 已有该分支)。
|
|
60
|
+
*/
|
|
61
|
+
export declare function getNativeImage(): NativeImageLike | null;
|
|
62
|
+
export {};
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主运行时抽象:Electron 主进程与 BS 独立 Node server 共用的环境探测。
|
|
3
|
+
*
|
|
4
|
+
* CS 模式(Electron)下路径来自 `app.getPath('userData')`(按 productName
|
|
5
|
+
* 分目录);BS 模式(`node dist/server/index.js`)没有 Electron,落到
|
|
6
|
+
* `DEEPPATH_USER_DATA_DIR` 或产品注入的 `~/<dataDirName>`(3.1)。两种模式共用
|
|
7
|
+
* 同一份 SQLite / JSON 存储代码,差异只在这一层。
|
|
8
|
+
*
|
|
9
|
+
* 本模块只允许依赖 node 内置模块(与 brand.ts 同约束):storage 会被
|
|
10
|
+
* vitest 直接 import,不能碰 electron 包。
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from 'node:child_process';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import { createRequire } from 'node:module';
|
|
15
|
+
import os from 'node:os';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { getProductConfig } from './product-config.js';
|
|
19
|
+
/** Electron 主进程里 `process.versions.electron` 有值;普通 Node(BS server、vitest)没有。 */
|
|
20
|
+
export function isElectronRuntime() {
|
|
21
|
+
return Boolean(process.versions.electron);
|
|
22
|
+
}
|
|
23
|
+
let cachedApp;
|
|
24
|
+
/**
|
|
25
|
+
* 同步拿 Electron app。普通 Node 下 `require('electron')` 返回二进制路径
|
|
26
|
+
* 字符串(没有 app),必须拒绝;Electron 主进程里才是真正的 API 对象。
|
|
27
|
+
*/
|
|
28
|
+
function tryElectronApp() {
|
|
29
|
+
if (cachedApp !== undefined)
|
|
30
|
+
return cachedApp;
|
|
31
|
+
if (!isElectronRuntime()) {
|
|
32
|
+
cachedApp = null;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const require = createRequire(import.meta.url);
|
|
37
|
+
const mod = require('electron');
|
|
38
|
+
cachedApp = mod && typeof mod.app?.getPath === 'function' ? mod.app : null;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
cachedApp = null;
|
|
42
|
+
}
|
|
43
|
+
return cachedApp;
|
|
44
|
+
}
|
|
45
|
+
/** 应用数据目录(SQLite、JSON store、用户技能目录的根)。 */
|
|
46
|
+
export function getUserDataDir() {
|
|
47
|
+
if (process.env.DEEPPATH_USER_DATA_DIR)
|
|
48
|
+
return process.env.DEEPPATH_USER_DATA_DIR;
|
|
49
|
+
const app = tryElectronApp();
|
|
50
|
+
if (app)
|
|
51
|
+
return app.getPath('userData');
|
|
52
|
+
// BS:目录名是产品注入配置(3.1,product.json dataDirName),与 Electron
|
|
53
|
+
// 下 productName 分目录的共存语义对齐;中性 shell 缺省 .agent-shell。
|
|
54
|
+
const dirName = getProductConfig().dataDirName ?? '.agent-shell';
|
|
55
|
+
return path.join(os.homedir(), dirName);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 应用根目录(消费产品的仓库/打包根:含 products/manifest.json、assets、
|
|
59
|
+
* scripts 的那层)。3.2 起 shell 是被消费的框架包,本模块自己的位置
|
|
60
|
+
* (packages/agent-shell/ts/...)不再是应用根——产品组装根在入口最早
|
|
61
|
+
* import 时经 setAppRootDir 注入;未注入时(单测)回退到 cwd 探测。
|
|
62
|
+
*/
|
|
63
|
+
let appRootOverride = null;
|
|
64
|
+
/**
|
|
65
|
+
* 注入应用根目录(产品组装根调用)。重复注入不同值抛错(组装期笔误,
|
|
66
|
+
* fail fast——与 setProductBrand 同语义)。
|
|
67
|
+
*/
|
|
68
|
+
export function setAppRootDir(dir) {
|
|
69
|
+
if (appRootOverride && appRootOverride !== dir) {
|
|
70
|
+
throw new Error('[runtime] app root already set');
|
|
71
|
+
}
|
|
72
|
+
appRootOverride = dir;
|
|
73
|
+
}
|
|
74
|
+
export function getAppRootDir() {
|
|
75
|
+
const app = tryElectronApp();
|
|
76
|
+
if (app)
|
|
77
|
+
return app.getAppPath();
|
|
78
|
+
if (appRootOverride)
|
|
79
|
+
return appRootOverride;
|
|
80
|
+
// 单测/脚本平面:从 cwd 向上找含 package.json 的目录。
|
|
81
|
+
for (let dir = process.cwd();;) {
|
|
82
|
+
if (fs.existsSync(path.join(dir, 'package.json')))
|
|
83
|
+
return dir;
|
|
84
|
+
const parent = path.dirname(dir);
|
|
85
|
+
if (parent === dir)
|
|
86
|
+
return process.cwd();
|
|
87
|
+
dir = parent;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* preload bundle 路径(3.4 修复):shell main.ts 创建窗口时需要
|
|
92
|
+
* preload.cjs——shell 自带默认面(dist/preload.cjs,buildPreloadApi()
|
|
93
|
+
* 无包贡献),但带 invoke 型包贡献的产品由产品构建把组合后的
|
|
94
|
+
* preload.cjs 出在产品 main.js 旁,产品入口在动态 import
|
|
95
|
+
* shell main 之前经本函数注入。未注入时回退 shell 自带默认。
|
|
96
|
+
*/
|
|
97
|
+
let preloadPathOverride = null;
|
|
98
|
+
/**
|
|
99
|
+
* 注入 preload bundle 绝对路径(产品组装根/入口调用)。重复注入不同值
|
|
100
|
+
* 抛错(组装期笔误,fail fast——与 setAppRootDir 同语义)。
|
|
101
|
+
*/
|
|
102
|
+
export function setPreloadPath(file) {
|
|
103
|
+
if (preloadPathOverride && preloadPathOverride !== file) {
|
|
104
|
+
throw new Error('[runtime] preload path already set');
|
|
105
|
+
}
|
|
106
|
+
preloadPathOverride = file;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 解析窗口应加载的 preload bundle:产品注入优先,其次 shell 自带默认
|
|
110
|
+
* (shellDir 一般为 shell main.js 所在目录)。
|
|
111
|
+
*/
|
|
112
|
+
export function getPreloadPath(shellDir) {
|
|
113
|
+
return preloadPathOverride ?? path.join(shellDir, 'preload.cjs');
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 产品 web 产物目录(2.3):产品入口(products/<id>/{main,server}.ts)
|
|
117
|
+
* 用本函数把 DEEPPATH_WEB_DIST 注入环境,shell 的 main/server 据此定位
|
|
118
|
+
* web dist。双平面探测:源码平面入口在 products/<id>/(web dist 在同级
|
|
119
|
+
* web/dist);编译平面入口在 products/<id>/dist/products/<id>/(web dist
|
|
120
|
+
* 在仓库根的 products/<id>/web/dist)。两处都没有返回 null(打包后的
|
|
121
|
+
* CS 走 app.getAppPath()/web-dist 约定,不经本函数)。
|
|
122
|
+
*/
|
|
123
|
+
export function resolveProductWebDist(entryUrl) {
|
|
124
|
+
const here = path.dirname(fileURLToPath(entryUrl));
|
|
125
|
+
const productId = path.basename(here);
|
|
126
|
+
for (const candidate of [
|
|
127
|
+
path.join(here, 'web', 'dist'),
|
|
128
|
+
path.resolve(here, '..', '..', '..', '..', 'products', productId, 'web', 'dist'),
|
|
129
|
+
]) {
|
|
130
|
+
if (fs.existsSync(path.join(candidate, 'index.html')))
|
|
131
|
+
return candidate;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
/** 用系统默认应用打开本地路径(Electron `shell.openPath` 的跨宿主等价物)。 */
|
|
136
|
+
export async function shellOpenPath(target) {
|
|
137
|
+
if (tryElectronApp()) {
|
|
138
|
+
const { shell } = (await import('electron'));
|
|
139
|
+
return shell.openPath(target);
|
|
140
|
+
}
|
|
141
|
+
const [cmd, args] = openCommand(target, false);
|
|
142
|
+
return spawnDetached(cmd, args);
|
|
143
|
+
}
|
|
144
|
+
/** 用系统浏览器打开 URL(Electron `shell.openExternal` 的跨宿主等价物)。 */
|
|
145
|
+
export async function shellOpenExternal(url) {
|
|
146
|
+
if (tryElectronApp()) {
|
|
147
|
+
const { shell } = (await import('electron'));
|
|
148
|
+
await shell.openExternal(url);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const [cmd, args] = openCommand(url, true);
|
|
152
|
+
const error = await spawnDetached(cmd, args);
|
|
153
|
+
if (error)
|
|
154
|
+
throw new Error(error);
|
|
155
|
+
}
|
|
156
|
+
function openCommand(target, isUrl) {
|
|
157
|
+
if (process.platform === 'darwin')
|
|
158
|
+
return ['open', [target]];
|
|
159
|
+
if (process.platform === 'win32') {
|
|
160
|
+
return isUrl
|
|
161
|
+
? ['rundll32', ['url.dll,FileProtocolHandler', target]]
|
|
162
|
+
: ['explorer', [target]];
|
|
163
|
+
}
|
|
164
|
+
return ['xdg-open', [target]];
|
|
165
|
+
}
|
|
166
|
+
/** 返回空串表示成功(对齐 shell.openPath 的约定),否则是错误消息。 */
|
|
167
|
+
function spawnDetached(cmd, args) {
|
|
168
|
+
return new Promise((resolve) => {
|
|
169
|
+
try {
|
|
170
|
+
const child = spawn(cmd, args, { detached: true, stdio: 'ignore' });
|
|
171
|
+
child.on('error', (err) => resolve(err.message));
|
|
172
|
+
child.on('spawn', () => {
|
|
173
|
+
child.unref();
|
|
174
|
+
resolve('');
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
resolve(err instanceof Error ? err.message : String(err));
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 注册应用退出钩子(sidecar 的兜底清理)。BS 下没有 app 生命周期——
|
|
184
|
+
* 宿主退出由 server 的 SIGINT/SIGTERM 钩子统一负责,这里 no-op。
|
|
185
|
+
*/
|
|
186
|
+
export function onAppWillQuit(listener) {
|
|
187
|
+
try {
|
|
188
|
+
tryElectronApp()?.on?.('will-quit', listener);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
/* 非 Electron 宿主 */
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export function offAppWillQuit(listener) {
|
|
195
|
+
try {
|
|
196
|
+
tryElectronApp()?.removeListener?.('will-quit', listener);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
/* 非 Electron 宿主 */
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Electron `nativeImage`(图片解码/缩放)。BS / 单测等无 Electron 环境返回
|
|
204
|
+
* null——调用方按"不支持图片解码"降级(image-attachment 已有该分支)。
|
|
205
|
+
*/
|
|
206
|
+
export function getNativeImage() {
|
|
207
|
+
if (!isElectronRuntime())
|
|
208
|
+
return null;
|
|
209
|
+
try {
|
|
210
|
+
const require = createRequire(import.meta.url);
|
|
211
|
+
const mod = require('electron');
|
|
212
|
+
return typeof mod.nativeImage?.createFromPath === 'function' ? mod.nativeImage : null;
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 兼容性 re-export(阶段 2.2):场景包契约类型的单一真源已迁入
|
|
3
|
+
* `@steerable/pack-sdk`(packages/pack-sdk,零依赖纯类型包)。宿主内部
|
|
4
|
+
* 既有调用方继续从本模块取类型;包代码一律直接依赖 '@steerable/pack-sdk'。
|
|
5
|
+
*
|
|
6
|
+
* 契约全文与设计约束见 packages/pack-sdk/types/index.d.ts 头注释。
|
|
7
|
+
*/
|
|
8
|
+
export type { ScenarioId, ToolMode, ToolExposure, PackToolContext, PackToolHandler, ToolContribution, ServiceFactory, ServiceCreateContext, MigrationContribution, SkillContribution, AgentSeedIdentity, AgentSeed, IpcContribution, HttpRouteContribution, HttpRouteRequest, MainContribution, PreloadContribution, ComponentRef, SettingsPanelContribution, RouteContribution, ChatSlotContribution, CardRendererContribution, RendererContribution, BrandSpec, PackagingSpec, ScenarioPack, PackBackendRouteResponse, PackBackendRouteRequest, PackBackendRoute, PackTurnObserver, PackTurnHooks, } from '@steerable/pack-sdk';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 场景包注册表:宿主装配期的单一入口。
|
|
3
|
+
*
|
|
4
|
+
* 产品的 composition root 在启动早期把选中的包 register 进来;宿主各
|
|
5
|
+
* 子系统(ToolRouter / storage / copy-skills / IPC / 渲染层槽位)从这里
|
|
6
|
+
* 按槽位读取贡献,而不是各自硬编码 import 场景代码。
|
|
7
|
+
*
|
|
8
|
+
* 注册期即校验(fail loud):重复 id、IPC 通道不带 '<packId>:' 前缀、
|
|
9
|
+
* preload 命名空间与包 id 不一致,都在 register 时抛错,而不是等到
|
|
10
|
+
* 运行时才暴露。
|
|
11
|
+
*/
|
|
12
|
+
import type { ScenarioId, ScenarioPack } from './pack.js';
|
|
13
|
+
export declare class ScenarioRegistry {
|
|
14
|
+
private readonly packs;
|
|
15
|
+
register(pack: ScenarioPack): void;
|
|
16
|
+
get(id: ScenarioId): ScenarioPack | undefined;
|
|
17
|
+
has(id: ScenarioId): boolean;
|
|
18
|
+
list(): readonly ScenarioPack[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 进程级默认注册表。composition root(main.ts / server/index.ts /
|
|
22
|
+
* 渲染层入口)在最早阶段填充;各子系统消费同一个实例。
|
|
23
|
+
*/
|
|
24
|
+
export declare const scenarioRegistry: ScenarioRegistry;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class ScenarioRegistry {
|
|
2
|
+
packs = new Map();
|
|
3
|
+
register(pack) {
|
|
4
|
+
if (this.packs.has(pack.id)) {
|
|
5
|
+
throw new Error(`[scenario] duplicate pack id: ${pack.id}`);
|
|
6
|
+
}
|
|
7
|
+
for (const ipc of pack.main?.ipc ?? []) {
|
|
8
|
+
if (!ipc.channel.startsWith(`${pack.id}:`)) {
|
|
9
|
+
throw new Error(`[scenario] pack ${pack.id} ipc channel "${ipc.channel}" must start with "${pack.id}:"`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
if (pack.preload && pack.preload.namespace !== pack.id) {
|
|
13
|
+
throw new Error(`[scenario] pack ${pack.id} preload namespace "${pack.preload.namespace}" must equal pack id`);
|
|
14
|
+
}
|
|
15
|
+
this.packs.set(pack.id, pack);
|
|
16
|
+
}
|
|
17
|
+
get(id) {
|
|
18
|
+
return this.packs.get(id);
|
|
19
|
+
}
|
|
20
|
+
has(id) {
|
|
21
|
+
return this.packs.has(id);
|
|
22
|
+
}
|
|
23
|
+
list() {
|
|
24
|
+
return [...this.packs.values()];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 进程级默认注册表。composition root(main.ts / server/index.ts /
|
|
29
|
+
* 渲染层入口)在最早阶段填充;各子系统消费同一个实例。
|
|
30
|
+
*/
|
|
31
|
+
export const scenarioRegistry = new ScenarioRegistry();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BS 模式 HTTP server:把 CS(Electron)模式下挂在 IPC 后面的同一组服务
|
|
3
|
+
* 暴露成真正的 HTTP 接口,并托管 apps/web/dist 静态产物。
|
|
4
|
+
*
|
|
5
|
+
* 路由分三层:
|
|
6
|
+
* - `/api/v2/*` → LocalBackendRouter(chat / 设置 / 技能 / MCP …),
|
|
7
|
+
* 与 Electron 下 `local-backend:request` 同一入口;
|
|
8
|
+
* 流式路径(chats/:id/run|send|agent、…/regenerate)
|
|
9
|
+
* 走 handleStream,响应即标准 SSE。
|
|
10
|
+
* - `GET /api/v2/events` → SseBus 事件总线(替代 webContents.send 广播)。
|
|
11
|
+
* - `/host/*` → preload 里 direct-IPC 能力的 HTTP 等价物
|
|
12
|
+
* (terminal / local / approval 为宿主自带;
|
|
13
|
+
* 场景包路由经 host/http-routes 注册表匹配)。
|
|
14
|
+
*
|
|
15
|
+
* 安全约定:默认绑定 127.0.0.1、无认证——本机自用定位(与 Electron 版相同
|
|
16
|
+
* 的信任模型:能到端口 = 本机用户)。不要绑到非回环地址,那会把 shell /
|
|
17
|
+
* 文件读写 / 场景包工具控制暴露给整个网络。
|
|
18
|
+
*/
|
|
19
|
+
import { type Server } from 'node:http';
|
|
20
|
+
import type { LocalBackendRouter } from '../local-backend/router.js';
|
|
21
|
+
import type { LocalExecutor, LocalExecRequest, LocalExecResult } from '../local-executor.js';
|
|
22
|
+
import type { LocalScriptRegistry } from '../local-script-registry.js';
|
|
23
|
+
import type { TerminalManager } from '../terminal-manager.js';
|
|
24
|
+
import type { createApprovalBridge } from '../sidecar/reverse-approval.js';
|
|
25
|
+
import type { createAskUserBridge } from '../sidecar/reverse-ask-user.js';
|
|
26
|
+
import type { SseBus } from './sse-bus.js';
|
|
27
|
+
export interface BsServerDeps {
|
|
28
|
+
localBackendRouter: LocalBackendRouter;
|
|
29
|
+
localExecutor: LocalExecutor;
|
|
30
|
+
localScriptRegistry: LocalScriptRegistry;
|
|
31
|
+
terminalManager: TerminalManager;
|
|
32
|
+
approvalBridge: ReturnType<typeof createApprovalBridge>;
|
|
33
|
+
askUserBridge: ReturnType<typeof createAskUserBridge>;
|
|
34
|
+
maybeExecInTerminal: (req: LocalExecRequest) => Promise<LocalExecResult | null>;
|
|
35
|
+
bus: SseBus;
|
|
36
|
+
/** apps/web/dist 的绝对路径。 */
|
|
37
|
+
webDistDir: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function createBsServer(deps: BsServerDeps): Server;
|