@steerable/agent-shell 0.6.53 → 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/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.js +8 -7
- package/dist/local-backend/router.js +2 -2
- 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-script-registry.js +2 -2
- package/dist/mcp-server-registry.d.ts +3 -3
- package/dist/mcp-server-registry.js +2 -2
- package/dist/product-config.d.ts +10 -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/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/package.json +6 -13
- 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/runtime.js
CHANGED
|
@@ -1,56 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 宿主运行时抽象:
|
|
2
|
+
* 宿主运行时抽象:Tauri 监督的 Node 宿主与 BS 独立 Node server 共用的环境探测。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 同一份 SQLite / JSON 存储代码,差异只在这一层。
|
|
4
|
+
* 数据目录优先取 `DEEPPATH_USER_DATA_DIR`(Tauri 宿主总是注入),否则落到
|
|
5
|
+
* 产品注入的 `~/<dataDirName>`(3.1)。两种模式共用同一份 SQLite / JSON
|
|
6
|
+
* 存储代码,差异只在这一层。
|
|
8
7
|
*
|
|
9
8
|
* 本模块只允许依赖 node 内置模块(与 brand.ts 同约束):storage 会被
|
|
10
|
-
* vitest 直接 import
|
|
9
|
+
* vitest 直接 import。
|
|
11
10
|
*/
|
|
12
11
|
import { spawn } from 'node:child_process';
|
|
13
12
|
import fs from 'node:fs';
|
|
14
|
-
import { createRequire } from 'node:module';
|
|
15
13
|
import os from 'node:os';
|
|
16
14
|
import path from 'node:path';
|
|
17
15
|
import { fileURLToPath } from 'node:url';
|
|
18
16
|
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
17
|
/** 应用数据目录(SQLite、JSON store、用户技能目录的根)。 */
|
|
46
18
|
export function getUserDataDir() {
|
|
47
19
|
if (process.env.DEEPPATH_USER_DATA_DIR)
|
|
48
20
|
return process.env.DEEPPATH_USER_DATA_DIR;
|
|
49
|
-
|
|
50
|
-
if (app)
|
|
51
|
-
return app.getPath('userData');
|
|
52
|
-
// BS:目录名是产品注入配置(3.1,product.json dataDirName),与 Electron
|
|
53
|
-
// 下 productName 分目录的共存语义对齐;中性 shell 缺省 .agent-shell。
|
|
21
|
+
// 目录名是产品注入配置(3.1,product.json dataDirName);中性 shell 缺省 .agent-shell。
|
|
54
22
|
const dirName = getProductConfig().dataDirName ?? '.agent-shell';
|
|
55
23
|
return path.join(os.homedir(), dirName);
|
|
56
24
|
}
|
|
@@ -62,15 +30,6 @@ export function getUserDataDir() {
|
|
|
62
30
|
export function getDocumentsDir() {
|
|
63
31
|
if (process.env.STEERABLE_DOCUMENTS_DIR)
|
|
64
32
|
return process.env.STEERABLE_DOCUMENTS_DIR;
|
|
65
|
-
const app = tryElectronApp();
|
|
66
|
-
if (app) {
|
|
67
|
-
try {
|
|
68
|
-
return app.getPath('documents');
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// 个别环境 getPath('documents') 不可用,回落到 ~/Documents。
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
33
|
return path.join(os.homedir(), 'Documents');
|
|
75
34
|
}
|
|
76
35
|
/**
|
|
@@ -91,9 +50,6 @@ export function setAppRootDir(dir) {
|
|
|
91
50
|
appRootOverride = dir;
|
|
92
51
|
}
|
|
93
52
|
export function getAppRootDir() {
|
|
94
|
-
const app = tryElectronApp();
|
|
95
|
-
if (app)
|
|
96
|
-
return app.getAppPath();
|
|
97
53
|
if (appRootOverride)
|
|
98
54
|
return appRootOverride;
|
|
99
55
|
// 单测/脚本平面:从 cwd 向上找含 package.json 的目录。
|
|
@@ -107,37 +63,12 @@ export function getAppRootDir() {
|
|
|
107
63
|
}
|
|
108
64
|
}
|
|
109
65
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
|
|
116
|
-
let preloadPathOverride = null;
|
|
117
|
-
/**
|
|
118
|
-
* 注入 preload bundle 绝对路径(产品组装根/入口调用)。重复注入不同值
|
|
119
|
-
* 抛错(组装期笔误,fail fast——与 setAppRootDir 同语义)。
|
|
120
|
-
*/
|
|
121
|
-
export function setPreloadPath(file) {
|
|
122
|
-
if (preloadPathOverride && preloadPathOverride !== file) {
|
|
123
|
-
throw new Error('[runtime] preload path already set');
|
|
124
|
-
}
|
|
125
|
-
preloadPathOverride = file;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* 解析窗口应加载的 preload bundle:产品注入优先,其次 shell 自带默认
|
|
129
|
-
* (shellDir 一般为 shell main.js 所在目录)。
|
|
130
|
-
*/
|
|
131
|
-
export function getPreloadPath(shellDir) {
|
|
132
|
-
return preloadPathOverride ?? path.join(shellDir, 'preload.cjs');
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* 产品 web 产物目录(2.3):产品入口(products/<id>/{main,server}.ts)
|
|
136
|
-
* 用本函数把 DEEPPATH_WEB_DIST 注入环境,shell 的 main/server 据此定位
|
|
137
|
-
* web dist。双平面探测:源码平面入口在 products/<id>/(web dist 在同级
|
|
138
|
-
* web/dist);编译平面入口在 products/<id>/dist/products/<id>/(web dist
|
|
139
|
-
* 在仓库根的 products/<id>/web/dist)。两处都没有返回 null(打包后的
|
|
140
|
-
* CS 走 app.getAppPath()/web-dist 约定,不经本函数)。
|
|
66
|
+
* 产品 web 产物目录(2.3):产品入口(products/<id>/server.ts)用本函数
|
|
67
|
+
* 把 DEEPPATH_WEB_DIST 注入环境,shell server 据此定位 web dist。双平面
|
|
68
|
+
* 探测:源码平面入口在 products/<id>/(web dist 在同级 web/dist);编译
|
|
69
|
+
* 平面入口在 products/<id>/dist/products/<id>/(web dist 在仓库根的
|
|
70
|
+
* products/<id>/web/dist)。两处都没有返回 null(打包后的 Tauri 宿主直接
|
|
71
|
+
* 注入 DEEPPATH_WEB_DIST,不经本函数)。
|
|
141
72
|
*/
|
|
142
73
|
export function resolveProductWebDist(entryUrl) {
|
|
143
74
|
const here = path.dirname(fileURLToPath(entryUrl));
|
|
@@ -155,22 +86,13 @@ export function resolveProductWebDist(entryUrl) {
|
|
|
155
86
|
}
|
|
156
87
|
return null;
|
|
157
88
|
}
|
|
158
|
-
/**
|
|
89
|
+
/** 用系统默认应用打开本地路径。返回空串表示成功,否则是错误消息。 */
|
|
159
90
|
export async function shellOpenPath(target) {
|
|
160
|
-
if (tryElectronApp()) {
|
|
161
|
-
const { shell } = (await import('electron'));
|
|
162
|
-
return shell.openPath(target);
|
|
163
|
-
}
|
|
164
91
|
const [cmd, args] = openCommand(target, false);
|
|
165
92
|
return spawnDetached(cmd, args);
|
|
166
93
|
}
|
|
167
|
-
/** 用系统浏览器打开 URL
|
|
94
|
+
/** 用系统浏览器打开 URL。 */
|
|
168
95
|
export async function shellOpenExternal(url) {
|
|
169
|
-
if (tryElectronApp()) {
|
|
170
|
-
const { shell } = (await import('electron'));
|
|
171
|
-
await shell.openExternal(url);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
96
|
const [cmd, args] = openCommand(url, true);
|
|
175
97
|
const error = await spawnDetached(cmd, args);
|
|
176
98
|
if (error)
|
|
@@ -186,7 +108,7 @@ function openCommand(target, isUrl) {
|
|
|
186
108
|
}
|
|
187
109
|
return ['xdg-open', [target]];
|
|
188
110
|
}
|
|
189
|
-
/**
|
|
111
|
+
/** 返回空串表示成功,否则是错误消息。 */
|
|
190
112
|
function spawnDetached(cmd, args) {
|
|
191
113
|
return new Promise((resolve) => {
|
|
192
114
|
try {
|
|
@@ -202,39 +124,3 @@ function spawnDetached(cmd, args) {
|
|
|
202
124
|
}
|
|
203
125
|
});
|
|
204
126
|
}
|
|
205
|
-
/**
|
|
206
|
-
* 注册应用退出钩子(sidecar 的兜底清理)。BS 下没有 app 生命周期——
|
|
207
|
-
* 宿主退出由 server 的 SIGINT/SIGTERM 钩子统一负责,这里 no-op。
|
|
208
|
-
*/
|
|
209
|
-
export function onAppWillQuit(listener) {
|
|
210
|
-
try {
|
|
211
|
-
tryElectronApp()?.on?.('will-quit', listener);
|
|
212
|
-
}
|
|
213
|
-
catch {
|
|
214
|
-
/* 非 Electron 宿主 */
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
export function offAppWillQuit(listener) {
|
|
218
|
-
try {
|
|
219
|
-
tryElectronApp()?.removeListener?.('will-quit', listener);
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
/* 非 Electron 宿主 */
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Electron `nativeImage`(图片解码/缩放)。BS / 单测等无 Electron 环境返回
|
|
227
|
-
* null——调用方按"不支持图片解码"降级(image-attachment 已有该分支)。
|
|
228
|
-
*/
|
|
229
|
-
export function getNativeImage() {
|
|
230
|
-
if (!isElectronRuntime())
|
|
231
|
-
return null;
|
|
232
|
-
try {
|
|
233
|
-
const require = createRequire(import.meta.url);
|
|
234
|
-
const mod = require('electron');
|
|
235
|
-
return typeof mod.nativeImage?.createFromPath === 'function' ? mod.nativeImage : null;
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
}
|
package/dist/scenario/pack.d.ts
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
*
|
|
6
6
|
* 契约全文与设计约束见 packages/pack-sdk/types/index.d.ts 头注释。
|
|
7
7
|
*/
|
|
8
|
-
export type { ScenarioId, ToolMode, ToolExposure, PackToolContext, PackToolHandler, ToolContribution, ServiceFactory, ServiceCreateContext, MigrationContribution, SkillContribution, AgentSeedIdentity, AgentSeed,
|
|
8
|
+
export type { ScenarioId, ToolMode, ToolExposure, PackToolContext, PackToolHandler, ToolContribution, ServiceFactory, ServiceCreateContext, MigrationContribution, SkillContribution, AgentSeedIdentity, AgentSeed, HttpRouteContribution, HttpRouteRequest, MainContribution, ComponentRef, SettingsPanelContribution, RouteContribution, ChatSlotContribution, CardRendererContribution, RendererContribution, BrandSpec, PackagingSpec, ScenarioPack, PackBackendRouteResponse, PackBackendRouteRequest, PackBackendRoute, PackTurnObserver, PackTurnHooks, } from '@steerable/pack-sdk';
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* 场景包注册表:宿主装配期的单一入口。
|
|
3
3
|
*
|
|
4
4
|
* 产品的 composition root 在启动早期把选中的包 register 进来;宿主各
|
|
5
|
-
* 子系统(ToolRouter / storage / copy-skills /
|
|
5
|
+
* 子系统(ToolRouter / storage / copy-skills / 渲染层槽位)从这里
|
|
6
6
|
* 按槽位读取贡献,而不是各自硬编码 import 场景代码。
|
|
7
7
|
*
|
|
8
|
-
* 注册期即校验(fail loud):重复 id
|
|
9
|
-
* preload 命名空间与包 id 不一致,都在 register 时抛错,而不是等到
|
|
8
|
+
* 注册期即校验(fail loud):重复 id 在 register 时抛错,而不是等到
|
|
10
9
|
* 运行时才暴露。
|
|
11
10
|
*/
|
|
12
11
|
import type { ScenarioId, ScenarioPack } from './pack.js';
|
|
@@ -18,7 +17,7 @@ export declare class ScenarioRegistry {
|
|
|
18
17
|
list(): readonly ScenarioPack[];
|
|
19
18
|
}
|
|
20
19
|
/**
|
|
21
|
-
* 进程级默认注册表。composition root(
|
|
22
|
-
*
|
|
20
|
+
* 进程级默认注册表。composition root(server/index.ts / 渲染层入口)
|
|
21
|
+
* 在最早阶段填充;各子系统消费同一个实例。
|
|
23
22
|
*/
|
|
24
23
|
export declare const scenarioRegistry: ScenarioRegistry;
|
|
@@ -4,14 +4,6 @@ export class ScenarioRegistry {
|
|
|
4
4
|
if (this.packs.has(pack.id)) {
|
|
5
5
|
throw new Error(`[scenario] duplicate pack id: ${pack.id}`);
|
|
6
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
7
|
this.packs.set(pack.id, pack);
|
|
16
8
|
}
|
|
17
9
|
get(id) {
|
|
@@ -25,7 +17,7 @@ export class ScenarioRegistry {
|
|
|
25
17
|
}
|
|
26
18
|
}
|
|
27
19
|
/**
|
|
28
|
-
* 进程级默认注册表。composition root(
|
|
29
|
-
*
|
|
20
|
+
* 进程级默认注册表。composition root(server/index.ts / 渲染层入口)
|
|
21
|
+
* 在最早阶段填充;各子系统消费同一个实例。
|
|
30
22
|
*/
|
|
31
23
|
export const scenarioRegistry = new ScenarioRegistry();
|
package/dist/server/start.js
CHANGED
|
@@ -13,8 +13,7 @@ import { getUserDataDir } from '../runtime.js';
|
|
|
13
13
|
import { createBsServer, } from './http-server.js';
|
|
14
14
|
import { SseBus } from './sse-bus.js';
|
|
15
15
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
//
|
|
17
|
-
// process variables always win.
|
|
16
|
+
// dotenv precedence: local file first, existing process variables always win.
|
|
18
17
|
for (const file of ['.env.local', '.env']) {
|
|
19
18
|
const fullPath = path.join(path.resolve(__dirname, '..', '..'), file);
|
|
20
19
|
if (existsSync(fullPath))
|
|
@@ -33,21 +33,12 @@ export function resolveWinSpawnHelperPath() {
|
|
|
33
33
|
if (override !== undefined) {
|
|
34
34
|
return override && existsSync(override) ? override : null;
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const candidates = [
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Packaged extraResources live under resourcesPath. cwd is the user's
|
|
43
|
-
// launch directory and must not supply the confinement binary.
|
|
44
|
-
// `electron .` sets defaultApp and does not copy extraResources there;
|
|
45
|
-
// npm-published shell also cannot see the host checkout via __dirname.
|
|
46
|
-
const packagedElectron = Boolean(resourcesPath) && electron.defaultApp !== true;
|
|
47
|
-
if (!packagedElectron) {
|
|
48
|
-
candidates.push(join(process.cwd(), 'resources', 'windows-spawn-helper', binary));
|
|
49
|
-
candidates.push(join(__dirname, '..', '..', 'resources', 'windows-spawn-helper', binary));
|
|
50
|
-
}
|
|
36
|
+
// Packaged desktop hosts pass the bundled helper through the override.
|
|
37
|
+
// npm-published shell cannot see the host checkout via __dirname.
|
|
38
|
+
const candidates = [
|
|
39
|
+
join(process.cwd(), 'resources', 'windows-spawn-helper', binary),
|
|
40
|
+
join(__dirname, '..', '..', 'resources', 'windows-spawn-helper', binary),
|
|
41
|
+
];
|
|
51
42
|
for (const candidate of candidates) {
|
|
52
43
|
if (existsSync(candidate))
|
|
53
44
|
return candidate;
|
|
@@ -42,7 +42,6 @@ export declare class SidecarSupervisor extends EventEmitter {
|
|
|
42
42
|
private healthTimer;
|
|
43
43
|
private failedPings;
|
|
44
44
|
private shuttingDown;
|
|
45
|
-
private quitListener;
|
|
46
45
|
private reverseHandlers;
|
|
47
46
|
private constructor();
|
|
48
47
|
/** Spawn the sidecar and wait for the ready marker. */
|
|
@@ -190,7 +189,6 @@ export declare class SidecarSupervisor extends EventEmitter {
|
|
|
190
189
|
private wrapLinux;
|
|
191
190
|
private wrapWindows;
|
|
192
191
|
private attachListeners;
|
|
193
|
-
private installAppQuitHook;
|
|
194
192
|
private waitForReady;
|
|
195
193
|
private handleStdoutChunk;
|
|
196
194
|
private handleStderrChunk;
|
|
@@ -16,7 +16,6 @@ import { homedir } from 'node:os';
|
|
|
16
16
|
import { dirname, join } from 'node:path';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
import { promisify } from 'node:util';
|
|
19
|
-
import { onAppWillQuit, offAppWillQuit } from '../runtime.js';
|
|
20
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
20
|
const __dirname = dirname(__filename);
|
|
22
21
|
import { resolveWinSpawnHelperPath } from './reverse-spawn.js';
|
|
@@ -68,7 +67,6 @@ export class SidecarSupervisor extends EventEmitter {
|
|
|
68
67
|
healthTimer = null;
|
|
69
68
|
failedPings = 0;
|
|
70
69
|
shuttingDown = false;
|
|
71
|
-
quitListener = null;
|
|
72
70
|
reverseHandlers = new Map();
|
|
73
71
|
constructor(options) {
|
|
74
72
|
super();
|
|
@@ -351,10 +349,6 @@ export class SidecarSupervisor extends EventEmitter {
|
|
|
351
349
|
return;
|
|
352
350
|
this.shuttingDown = true;
|
|
353
351
|
this.stopHealthTimer();
|
|
354
|
-
if (this.quitListener) {
|
|
355
|
-
offAppWillQuit(this.quitListener);
|
|
356
|
-
this.quitListener = null;
|
|
357
|
-
}
|
|
358
352
|
const child = this.child;
|
|
359
353
|
if (!child)
|
|
360
354
|
return;
|
|
@@ -409,7 +403,6 @@ export class SidecarSupervisor extends EventEmitter {
|
|
|
409
403
|
this.child = null;
|
|
410
404
|
throw err;
|
|
411
405
|
}
|
|
412
|
-
this.installAppQuitHook();
|
|
413
406
|
this.startHealthTimer();
|
|
414
407
|
this.emit('ready', this.readyHealth);
|
|
415
408
|
}
|
|
@@ -641,13 +634,6 @@ export class SidecarSupervisor extends EventEmitter {
|
|
|
641
634
|
this.emit('error', err);
|
|
642
635
|
});
|
|
643
636
|
}
|
|
644
|
-
installAppQuitHook() {
|
|
645
|
-
const hook = () => {
|
|
646
|
-
void this.shutdown();
|
|
647
|
-
};
|
|
648
|
-
this.quitListener = hook;
|
|
649
|
-
onAppWillQuit(hook);
|
|
650
|
-
}
|
|
651
637
|
async waitForReady(timeoutMs) {
|
|
652
638
|
return new Promise((resolve, reject) => {
|
|
653
639
|
const timer = setTimeout(() => {
|
|
@@ -903,22 +889,13 @@ export function resolveSidecarPython(pythonExecutable) {
|
|
|
903
889
|
// build_sidecar.py 的产物布局是 <platform>/python/<exe>(Windows:
|
|
904
890
|
// python/python.exe;POSIX:python/bin/python3,见 build_sidecar.py 的
|
|
905
891
|
// python_binary())。保留无 python/ 层的旧布局候选做向后兼容。
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
catch { /* not in Electron */ }
|
|
914
|
-
runtimeBases.push(join(__dirname, '..', '..', 'python-runtime'));
|
|
915
|
-
const candidates = [];
|
|
916
|
-
for (const base of runtimeBases) {
|
|
917
|
-
candidates.push(join(base, platformTag, 'python', binaryName));
|
|
918
|
-
candidates.push(join(base, platformTag, 'python', 'bin', binaryName));
|
|
919
|
-
candidates.push(join(base, platformTag, binaryName));
|
|
920
|
-
candidates.push(join(base, platformTag, 'bin', binaryName));
|
|
921
|
-
}
|
|
892
|
+
const runtimeBase = join(__dirname, '..', '..', 'python-runtime');
|
|
893
|
+
const candidates = [
|
|
894
|
+
join(runtimeBase, platformTag, 'python', binaryName),
|
|
895
|
+
join(runtimeBase, platformTag, 'python', 'bin', binaryName),
|
|
896
|
+
join(runtimeBase, platformTag, binaryName),
|
|
897
|
+
join(runtimeBase, platformTag, 'bin', binaryName),
|
|
898
|
+
];
|
|
922
899
|
// Dev-layout convenience: the framework repo root .venv (uv sync). The
|
|
923
900
|
// shell lives inside the framework repo (packages/agent-shell/ts), so the
|
|
924
901
|
// venv is an in-repo ancestor — no sibling-checkout probing. Without this,
|
package/dist/storage/index.js
CHANGED
|
@@ -316,7 +316,7 @@ export class SqliteScopedStore {
|
|
|
316
316
|
// context_summary / context_summary_upto / context_compaction_delegated
|
|
317
317
|
// 列成为无人读写的孤儿列(SQLite 不易 drop,保留无害)。
|
|
318
318
|
// 项目模式(增量迁移):chat 可绑定 ProjectRegistry 里的项目 id。
|
|
319
|
-
// 项目记录本身存在
|
|
319
|
+
// 项目记录本身存在 json-store(agent-projects.json),这里只存外键;
|
|
320
320
|
// 删项目时由路由层把本列置 NULL(会话降级为无项目对话)。
|
|
321
321
|
this.ensureColumn('chat_sessions', 'project_id', 'TEXT');
|
|
322
322
|
// 任务编排(增量迁移):depends_on 存 JSON string[]——本任务等待的
|
|
@@ -1,11 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exit(code: number): void;
|
|
4
|
-
};
|
|
5
|
-
dialog: {
|
|
6
|
-
showErrorBox(title: string, message: string): void;
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
/** Maps write-lease contention to the desktop's user-facing startup failure. */
|
|
10
|
-
export declare function acquireWriteLeaseOrExit<T>(acquire: () => T, loadElectron?: () => Promise<ElectronExit>, exitProcess?: (code: number) => void): T;
|
|
11
|
-
export {};
|
|
1
|
+
/** Maps write-lease contention to a readable startup failure and process exit. */
|
|
2
|
+
export declare function acquireWriteLeaseOrExit<T>(acquire: () => T, exitProcess?: (code: number) => void): T;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StoreAlreadyOwnedError } from './write-lease.js';
|
|
2
|
-
/** Maps write-lease contention to
|
|
3
|
-
export function acquireWriteLeaseOrExit(acquire,
|
|
2
|
+
/** Maps write-lease contention to a readable startup failure and process exit. */
|
|
3
|
+
export function acquireWriteLeaseOrExit(acquire, exitProcess = (code) => process.exit(code)) {
|
|
4
4
|
try {
|
|
5
5
|
return acquire();
|
|
6
6
|
}
|
|
@@ -9,12 +9,7 @@ export function acquireWriteLeaseOrExit(acquire, loadElectron = () => import('el
|
|
|
9
9
|
console.error(`[bs] 本地数据库正在被另一个进程使用(${error.lockPath})。\n` +
|
|
10
10
|
'[bs] 再打开一次桌面应用会回到已经打开的窗口。\n' +
|
|
11
11
|
'[bs] 如果终端里的 pnpm dev:bs 占着数据库,请先在那个终端按 Ctrl+C 停掉,再重新打开。');
|
|
12
|
-
|
|
13
|
-
.then(({ app, dialog }) => {
|
|
14
|
-
dialog.showErrorBox('无法再开一个', '再打开一次应用时,会回到已经打开的窗口。\n\n这次没有回到那个窗口,是因为本地数据库正被另一个进程占用。如果终端里还在跑 pnpm dev:bs,先停掉它,再重新打开。');
|
|
15
|
-
app.exit(1);
|
|
16
|
-
})
|
|
17
|
-
.catch(() => exitProcess(1));
|
|
12
|
+
queueMicrotask(() => exitProcess(1));
|
|
18
13
|
}
|
|
19
14
|
throw error;
|
|
20
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerable/agent-shell",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.55",
|
|
4
4
|
"description": "Steerable framework — product-neutral Node HostRuntime and headless HTTP server shared by Tauri desktop and browser deployments; scenario packs extend it through @steerable/pack-sdk.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://steerableframework.com/",
|
|
@@ -50,34 +50,27 @@
|
|
|
50
50
|
"conf": "10.2.0",
|
|
51
51
|
"dotenv": "^17.4.2",
|
|
52
52
|
"electron-log": "^5.4.3",
|
|
53
|
-
"electron-store": "^8.1.0",
|
|
54
|
-
"electron-updater": "^6.8.9",
|
|
55
53
|
"he": "^1.2.0",
|
|
56
54
|
"node-pty": "^1.1.0",
|
|
57
|
-
"
|
|
58
|
-
"@steerable/
|
|
59
|
-
"@steerable/
|
|
60
|
-
"@steerable/agent-harness": "0.6.53"
|
|
55
|
+
"@steerable/agent-harness": "0.6.55",
|
|
56
|
+
"@steerable/pack-sdk": "0.6.55",
|
|
57
|
+
"@steerable/agent-protocol": "0.6.55"
|
|
61
58
|
},
|
|
62
59
|
"devDependencies": {
|
|
63
60
|
"@types/better-sqlite3": "^7.6.13",
|
|
64
61
|
"@types/he": "^1.2.3",
|
|
65
62
|
"@types/node": "^22.10.5",
|
|
66
|
-
"@types/semver": "^7.8.0",
|
|
67
|
-
"electron": "^28.3.3",
|
|
68
|
-
"esbuild": "^0.25.0",
|
|
69
63
|
"typescript": "^5.8.3",
|
|
70
64
|
"vitest": "^3.2.4"
|
|
71
65
|
},
|
|
72
66
|
"scripts": {
|
|
73
67
|
"gen": "echo agent-shell-ts",
|
|
74
|
-
"build": "tsc -p tsconfig.json && node copy-skills.mjs
|
|
68
|
+
"build": "tsc -p tsconfig.json && node copy-skills.mjs",
|
|
75
69
|
"test": "vitest run",
|
|
76
70
|
"lint": "tsc -p tsconfig.json --noEmit && node scripts/check-shell-neutral.mjs && node scripts/check-storage-boundaries.mjs",
|
|
77
71
|
"check:drift": "echo ok",
|
|
78
72
|
"shell:neutral": "node scripts/check-shell-neutral.mjs",
|
|
79
73
|
"storage:boundaries": "node scripts/check-storage-boundaries.mjs",
|
|
80
|
-
"web": "DEEPPATH_WEB_DIST=${DEEPPATH_WEB_DIST:-../web/app/dist} node dist/server/index.js"
|
|
81
|
-
"client": "DEEPPATH_FORCE_BUNDLED_WEB=1 DEEPPATH_WEB_DIST=${DEEPPATH_WEB_DIST:-../web/app/dist} node node_modules/electron/cli.js dist/main.js"
|
|
74
|
+
"web": "DEEPPATH_WEB_DIST=${DEEPPATH_WEB_DIST:-../web/app/dist} node dist/server/index.js"
|
|
82
75
|
}
|
|
83
76
|
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* electron-updater 适配。只在桌面主进程启用更新时加载。
|
|
3
|
-
*/
|
|
4
|
-
import log from 'electron-log';
|
|
5
|
-
import electronUpdater from 'electron-updater';
|
|
6
|
-
export function createElectronAppUpdater() {
|
|
7
|
-
const { autoUpdater } = electronUpdater;
|
|
8
|
-
autoUpdater.logger = log;
|
|
9
|
-
return {
|
|
10
|
-
get autoDownload() {
|
|
11
|
-
return autoUpdater.autoDownload;
|
|
12
|
-
},
|
|
13
|
-
set autoDownload(value) {
|
|
14
|
-
autoUpdater.autoDownload = value;
|
|
15
|
-
},
|
|
16
|
-
get autoInstallOnAppQuit() {
|
|
17
|
-
return autoUpdater.autoInstallOnAppQuit;
|
|
18
|
-
},
|
|
19
|
-
set autoInstallOnAppQuit(value) {
|
|
20
|
-
autoUpdater.autoInstallOnAppQuit = value;
|
|
21
|
-
},
|
|
22
|
-
get allowDowngrade() {
|
|
23
|
-
return autoUpdater.allowDowngrade;
|
|
24
|
-
},
|
|
25
|
-
set allowDowngrade(value) {
|
|
26
|
-
autoUpdater.allowDowngrade = value;
|
|
27
|
-
},
|
|
28
|
-
setFeedURL(options) {
|
|
29
|
-
autoUpdater.setFeedURL(options);
|
|
30
|
-
},
|
|
31
|
-
async checkForUpdates() {
|
|
32
|
-
const result = await autoUpdater.checkForUpdates();
|
|
33
|
-
if (!result)
|
|
34
|
-
return null;
|
|
35
|
-
return {
|
|
36
|
-
updateInfo: { version: result.updateInfo.version },
|
|
37
|
-
isUpdateAvailable: result.isUpdateAvailable,
|
|
38
|
-
};
|
|
39
|
-
},
|
|
40
|
-
quitAndInstall(isSilent, isForceRunAfter) {
|
|
41
|
-
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
|
42
|
-
},
|
|
43
|
-
on(event, listener) {
|
|
44
|
-
if (event === 'download-progress') {
|
|
45
|
-
autoUpdater.on('download-progress', listener);
|
|
46
|
-
}
|
|
47
|
-
else if (event === 'update-downloaded') {
|
|
48
|
-
autoUpdater.on('update-downloaded', listener);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
autoUpdater.on('error', listener);
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
off(event, listener) {
|
|
55
|
-
if (event === 'download-progress') {
|
|
56
|
-
autoUpdater.off('download-progress', listener);
|
|
57
|
-
}
|
|
58
|
-
else if (event === 'update-downloaded') {
|
|
59
|
-
autoUpdater.off('update-downloaded', listener);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
autoUpdater.off('error', listener);
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
package/dist/app-update.d.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 桌面自动更新。产品经 `ProductConfig.updates.feedUrl` 注入 generic 通道;
|
|
3
|
-
* 本模块只编排检查、下载和安装,具体传输交给 `AppUpdaterPort`
|
|
4
|
-
* (生产环境是 electron-updater)。
|
|
5
|
-
*/
|
|
6
|
-
export declare const APP_UPDATE_CHECK_INTERVAL_MS: number;
|
|
7
|
-
export type AppUpdatePhase = 'disabled' | 'idle' | 'checking' | 'downloading' | 'ready' | 'installing' | 'error';
|
|
8
|
-
export interface AppUpdateState {
|
|
9
|
-
phase: AppUpdatePhase;
|
|
10
|
-
version?: string;
|
|
11
|
-
percent?: number;
|
|
12
|
-
message?: string;
|
|
13
|
-
}
|
|
14
|
-
/** 渲染层看到的版本与更新状态。`version` 是当前安装版本。 */
|
|
15
|
-
export interface AppReleaseSnapshot {
|
|
16
|
-
version: string;
|
|
17
|
-
enabled: boolean;
|
|
18
|
-
phase: AppUpdatePhase;
|
|
19
|
-
availableVersion?: string;
|
|
20
|
-
percent?: number;
|
|
21
|
-
message?: string;
|
|
22
|
-
}
|
|
23
|
-
export declare function toAppReleaseSnapshot(currentVersion: string, state: AppUpdateState): AppReleaseSnapshot;
|
|
24
|
-
export interface AppUpdateCheckResult {
|
|
25
|
-
updateInfo: {
|
|
26
|
-
version: string;
|
|
27
|
-
};
|
|
28
|
-
isUpdateAvailable: boolean;
|
|
29
|
-
}
|
|
30
|
-
export interface AppUpdaterPort {
|
|
31
|
-
autoDownload: boolean;
|
|
32
|
-
autoInstallOnAppQuit: boolean;
|
|
33
|
-
allowDowngrade: boolean;
|
|
34
|
-
setFeedURL(options: {
|
|
35
|
-
provider: 'generic';
|
|
36
|
-
url: string;
|
|
37
|
-
}): void;
|
|
38
|
-
checkForUpdates(): Promise<AppUpdateCheckResult | null>;
|
|
39
|
-
quitAndInstall(isSilent?: boolean, isForceRunAfter?: boolean): void;
|
|
40
|
-
on(event: 'download-progress', listener: (progress: {
|
|
41
|
-
percent: number;
|
|
42
|
-
}) => void): void;
|
|
43
|
-
on(event: 'update-downloaded', listener: (info: {
|
|
44
|
-
version: string;
|
|
45
|
-
}) => void): void;
|
|
46
|
-
on(event: 'error', listener: (error: Error) => void): void;
|
|
47
|
-
off(event: 'download-progress', listener: (progress: {
|
|
48
|
-
percent: number;
|
|
49
|
-
}) => void): void;
|
|
50
|
-
off(event: 'update-downloaded', listener: (info: {
|
|
51
|
-
version: string;
|
|
52
|
-
}) => void): void;
|
|
53
|
-
off(event: 'error', listener: (error: Error) => void): void;
|
|
54
|
-
}
|
|
55
|
-
export interface AppUpdateOptions {
|
|
56
|
-
feedUrl?: string;
|
|
57
|
-
packaged: boolean;
|
|
58
|
-
currentVersion: string;
|
|
59
|
-
env: NodeJS.ProcessEnv;
|
|
60
|
-
updater: AppUpdaterPort;
|
|
61
|
-
onState?: (state: AppUpdateState) => void;
|
|
62
|
-
/** 测试注入。缺省写 `env.DEEPPATH_UPDATE_STATUS_FILE`。 */
|
|
63
|
-
statusFile?: string;
|
|
64
|
-
}
|
|
65
|
-
export declare function appUpdateMenuAction(state: AppUpdateState): 'none' | 'check' | 'install';
|
|
66
|
-
export declare function shouldNotifyUpdateReady(previous: AppUpdatePhase, next: AppUpdateState): boolean;
|
|
67
|
-
export declare class AppUpdateController {
|
|
68
|
-
private readonly options;
|
|
69
|
-
private current;
|
|
70
|
-
private enabled;
|
|
71
|
-
private started;
|
|
72
|
-
private disposed;
|
|
73
|
-
private bound;
|
|
74
|
-
private candidate;
|
|
75
|
-
private downloadedVersion;
|
|
76
|
-
private checkPromise;
|
|
77
|
-
private cancelTimer;
|
|
78
|
-
private readonly statusFile;
|
|
79
|
-
constructor(options: AppUpdateOptions);
|
|
80
|
-
get state(): AppUpdateState;
|
|
81
|
-
start(): Promise<AppUpdateState>;
|
|
82
|
-
checkNow(): Promise<AppUpdateState>;
|
|
83
|
-
installNow(): AppUpdateState;
|
|
84
|
-
dispose(): void;
|
|
85
|
-
private bind;
|
|
86
|
-
private readonly onProgress;
|
|
87
|
-
private readonly onDownloaded;
|
|
88
|
-
private readonly onError;
|
|
89
|
-
private doCheck;
|
|
90
|
-
private isNewer;
|
|
91
|
-
private publish;
|
|
92
|
-
}
|