@steerable/agent-shell 0.6.20 → 0.6.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/auth/index.d.ts +38 -0
- package/dist/auth/index.js +20 -0
- package/dist/host/pack-assembly.d.ts +7 -7
- package/dist/host/runtime.d.ts +6 -2
- package/dist/host/runtime.js +54 -15
- package/dist/host/visible-terminal-exec.d.ts +0 -2
- package/dist/host/visible-terminal-exec.js +4 -8
- package/dist/insights/flush.d.ts +5 -4
- package/dist/insights/flush.js +14 -15
- package/dist/insights/record.d.ts +6 -5
- package/dist/insights/record.js +9 -10
- package/dist/llm/index.d.ts +6 -2
- package/dist/llm/index.js +19 -9
- package/dist/local-backend/ai-suggestions.d.ts +32 -0
- package/dist/local-backend/ai-suggestions.js +160 -0
- package/dist/local-backend/coreloop-stream.js +1 -1
- package/dist/local-backend/mentioned-paths.d.ts +28 -0
- package/dist/local-backend/mentioned-paths.js +67 -0
- package/dist/local-backend/prompt-builder.d.ts +10 -4
- package/dist/local-backend/prompt-builder.js +13 -12
- package/dist/local-backend/router.d.ts +26 -4
- package/dist/local-backend/router.js +243 -96
- package/dist/local-backend/skills/00-identity/SKILL.md +1 -1
- package/dist/local-backend/task-service.d.ts +14 -13
- package/dist/local-backend/task-service.js +32 -29
- package/dist/local-backend/turn-files.d.ts +57 -0
- package/dist/local-backend/turn-files.js +285 -0
- package/dist/local-backend/worktree-service.d.ts +1 -1
- package/dist/local-backend/worktree-service.js +6 -6
- package/dist/main.js +45 -26
- package/dist/preload.cjs +30 -10
- package/dist/preload.js +33 -10
- package/dist/server/http-server.d.ts +13 -1
- package/dist/server/http-server.js +45 -8
- package/dist/server/index.js +4 -90
- package/dist/server/sse-bus.d.ts +1 -1
- package/dist/server/start.d.ts +26 -0
- package/dist/server/start.js +111 -0
- package/dist/sidecar/boot.d.ts +3 -1
- package/dist/sidecar/boot.js +6 -7
- package/dist/sidecar/reverse-approval.d.ts +2 -0
- package/dist/sidecar/reverse-approval.js +2 -1
- package/dist/sidecar/reverse-ask-user.d.ts +2 -0
- package/dist/sidecar/reverse-ask-user.js +2 -1
- package/dist/sidecar/reverse-spawn.js +12 -4
- package/dist/sidecar/reverse-tools.d.ts +1 -1
- package/dist/sidecar/reverse-tools.js +1 -1
- package/dist/sidecar/storage-path.js +2 -3
- package/dist/storage/driver.d.ts +57 -0
- package/dist/storage/driver.js +104 -0
- package/dist/storage/index.d.ts +80 -67
- package/dist/storage/index.js +528 -296
- package/dist/storage/pack-migrations.d.ts +2 -4
- package/dist/storage/pack-migrations.js +2 -4
- package/dist/storage/scoped-store.d.ts +125 -0
- package/dist/storage/scoped-store.js +1 -0
- package/dist/storage/sqlite-driver.d.ts +16 -0
- package/dist/storage/sqlite-driver.js +135 -0
- package/dist/storage/write-lease-error.d.ts +11 -0
- package/dist/storage/write-lease-error.js +18 -0
- package/package.json +14 -5
- package/dist/storage/local-store-singleton.d.ts +0 -28
- package/dist/storage/local-store-singleton.js +0 -38
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 包迁移注册表 —— ScenarioPack 的存储槽位(0.3b)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (main.ts / server/index.ts)把 `packs/active.ts` 作为第一个 import
|
|
7
|
-
* 来保证这个顺序。
|
|
4
|
+
* The host initializes the selected driver only after product composition,
|
|
5
|
+
* so every active pack can register migrations before any database opens.
|
|
8
6
|
*
|
|
9
7
|
* 本模块刻意不 import `storage/index.js`,避免反向依赖。
|
|
10
8
|
*/
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 包迁移注册表 —— ScenarioPack 的存储槽位(0.3b)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (main.ts / server/index.ts)把 `packs/active.ts` 作为第一个 import
|
|
7
|
-
* 来保证这个顺序。
|
|
4
|
+
* The host initializes the selected driver only after product composition,
|
|
5
|
+
* so every active pack can register migrations before any database opens.
|
|
8
6
|
*
|
|
9
7
|
* 本模块刻意不 import `storage/index.js`,避免反向依赖。
|
|
10
8
|
*/
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { ChatAgentRecord, ChatMessageRecord, ChatSessionRecord, HarnessTraceRecord, InsightOutboxRow, TaskRecord } from './index.js';
|
|
2
|
+
import type { InsightKind, InsightsSettings, InsightsSettingsPatch } from './insights-settings.js';
|
|
3
|
+
import type { LlmSettings } from './llm-settings.js';
|
|
4
|
+
import type { TelemetrySettings } from './telemetry-settings.js';
|
|
5
|
+
import type { UsageSummary } from './usage-summary.js';
|
|
6
|
+
import type { WebSearchSettings } from './web-search-settings.js';
|
|
7
|
+
import type { TenantScope } from './driver.js';
|
|
8
|
+
/** Explicit asynchronous storage API bound to one tenant and user. */
|
|
9
|
+
export interface ScopedStore {
|
|
10
|
+
readonly scope: TenantScope;
|
|
11
|
+
listChats(page?: number, limit?: number): Promise<{
|
|
12
|
+
chats: ChatSessionRecord[];
|
|
13
|
+
total: number;
|
|
14
|
+
}>;
|
|
15
|
+
createChat(title?: string, agentId?: string | null, projectId?: string | null): Promise<ChatSessionRecord>;
|
|
16
|
+
createChatWithId(chatId: string, title?: string, agentId?: string | null, projectId?: string | null): Promise<ChatSessionRecord>;
|
|
17
|
+
clearProjectAssignment(projectId: string): Promise<number>;
|
|
18
|
+
getChat(chatId: string): Promise<ChatSessionRecord | null>;
|
|
19
|
+
updateChat(chatId: string, updates: Partial<Pick<ChatSessionRecord, 'title' | 'systemPrompt' | 'pinnedRefs' | 'isPinned' | 'projectId'>>): Promise<ChatSessionRecord | null>;
|
|
20
|
+
deleteChat(chatId: string): Promise<boolean>;
|
|
21
|
+
chatHasMessages(chatId: string): Promise<boolean>;
|
|
22
|
+
deleteChatIfEmpty(chatId: string): Promise<boolean>;
|
|
23
|
+
deleteEmptyChats(exceptChatId?: string | null): Promise<string[]>;
|
|
24
|
+
getChatRecordId(chatId: string): Promise<string | null>;
|
|
25
|
+
setChatRecordId(chatId: string, recordId: string): Promise<void>;
|
|
26
|
+
setTurnActive(chatId: string): Promise<void>;
|
|
27
|
+
clearTurnActive(chatId: string): Promise<void>;
|
|
28
|
+
getTurnActive(chatId: string): Promise<{
|
|
29
|
+
startedAt: string;
|
|
30
|
+
} | null>;
|
|
31
|
+
listMessages(chatId: string, limit?: number): Promise<ChatMessageRecord[]>;
|
|
32
|
+
getMessage(chatId: string, messageId: string): Promise<ChatMessageRecord | null>;
|
|
33
|
+
deleteMessagesFrom(chatId: string, messageId: string): Promise<number>;
|
|
34
|
+
addMessage(chatId: string, role: ChatMessageRecord['role'], content: string, messageMetadata?: string | null): Promise<ChatMessageRecord>;
|
|
35
|
+
patchMessageMetadata(chatId: string, messageId: string, patch: Record<string, unknown>): Promise<ChatMessageRecord | null>;
|
|
36
|
+
replaceChatMessages(chatId: string, messages: Array<{
|
|
37
|
+
role: ChatMessageRecord['role'];
|
|
38
|
+
content: string;
|
|
39
|
+
}>): Promise<void>;
|
|
40
|
+
listChatAgents(includeArchived?: boolean): Promise<ChatAgentRecord[]>;
|
|
41
|
+
getChatAgent(agentId: string): Promise<ChatAgentRecord | null>;
|
|
42
|
+
createChatAgent(input: Partial<ChatAgentRecord> & {
|
|
43
|
+
name: string;
|
|
44
|
+
}): Promise<ChatAgentRecord>;
|
|
45
|
+
updateChatAgent(agentId: string, updates: Partial<ChatAgentRecord>): Promise<ChatAgentRecord | null>;
|
|
46
|
+
archiveChatAgent(agentId: string): Promise<boolean>;
|
|
47
|
+
saveTrace(input: {
|
|
48
|
+
id: string;
|
|
49
|
+
chatId: string;
|
|
50
|
+
messageId?: string | null;
|
|
51
|
+
startedAtMs: number;
|
|
52
|
+
durationMs?: number | null;
|
|
53
|
+
status: string;
|
|
54
|
+
payload: Record<string, unknown>;
|
|
55
|
+
}): Promise<HarnessTraceRecord>;
|
|
56
|
+
listTracesByChat(chatId: string, limit?: number): Promise<HarnessTraceRecord[]>;
|
|
57
|
+
getTrace(traceId: string): Promise<HarnessTraceRecord | null>;
|
|
58
|
+
createTask(input: {
|
|
59
|
+
chatId: string;
|
|
60
|
+
task: string;
|
|
61
|
+
worktreePath?: string | null;
|
|
62
|
+
worktreeBranch?: string | null;
|
|
63
|
+
recordId?: string | null;
|
|
64
|
+
dependsOn?: string[] | null;
|
|
65
|
+
initialStatus?: 'blocked' | 'running';
|
|
66
|
+
}): Promise<TaskRecord>;
|
|
67
|
+
getTask(taskId: string): Promise<TaskRecord | null>;
|
|
68
|
+
listTasks(chatId?: string, limit?: number): Promise<TaskRecord[]>;
|
|
69
|
+
updateTask(taskId: string, updates: Partial<Pick<TaskRecord, 'status' | 'answer' | 'error' | 'worktreeState' | 'traceId' | 'recordId'>>): Promise<TaskRecord | null>;
|
|
70
|
+
saveTaskProcess(taskId: string, processJson: string): Promise<void>;
|
|
71
|
+
failRunningTasks(reason: string): Promise<number>;
|
|
72
|
+
recordUsageEvent(input: {
|
|
73
|
+
chatId?: string | null;
|
|
74
|
+
kind: string;
|
|
75
|
+
provider?: string | null;
|
|
76
|
+
model?: string | null;
|
|
77
|
+
promptTokens: number;
|
|
78
|
+
completionTokens: number;
|
|
79
|
+
totalTokens: number;
|
|
80
|
+
cachedPromptTokens?: number;
|
|
81
|
+
costUsd?: number | null;
|
|
82
|
+
}): Promise<void>;
|
|
83
|
+
getUsageSummary(sinceDays?: number): Promise<UsageSummary>;
|
|
84
|
+
getLlmSettings(): Promise<LlmSettings | null>;
|
|
85
|
+
setLlmSettings(settings: LlmSettings): Promise<LlmSettings>;
|
|
86
|
+
getTelemetrySettings(): Promise<TelemetrySettings | null>;
|
|
87
|
+
setTelemetrySettings(settings: Partial<TelemetrySettings>): Promise<TelemetrySettings>;
|
|
88
|
+
getWebSearchSettings(): Promise<WebSearchSettings | null>;
|
|
89
|
+
setWebSearchSettings(settings: Partial<WebSearchSettings>): Promise<WebSearchSettings>;
|
|
90
|
+
getInsightsSettings(): Promise<InsightsSettings | null>;
|
|
91
|
+
ensureInsightsSettings(): Promise<InsightsSettings>;
|
|
92
|
+
setInsightsSettings(settings: InsightsSettingsPatch): Promise<InsightsSettings>;
|
|
93
|
+
enqueueInsight(kind: InsightKind, payload: Record<string, unknown>): Promise<InsightOutboxRow>;
|
|
94
|
+
listInsightOutbox(opts?: {
|
|
95
|
+
uploaded?: boolean;
|
|
96
|
+
kind?: InsightKind;
|
|
97
|
+
limit?: number;
|
|
98
|
+
}): Promise<InsightOutboxRow[]>;
|
|
99
|
+
markInsightUploaded(id: string): Promise<void>;
|
|
100
|
+
markInsightUploadError(id: string, error: string): Promise<void>;
|
|
101
|
+
insightStats(): Promise<{
|
|
102
|
+
events: number;
|
|
103
|
+
turns: number;
|
|
104
|
+
profile: number;
|
|
105
|
+
pending: number;
|
|
106
|
+
}>;
|
|
107
|
+
exportInsightsBundle(): Promise<{
|
|
108
|
+
schema: string;
|
|
109
|
+
exportedAt: string;
|
|
110
|
+
installId: string;
|
|
111
|
+
settings: {
|
|
112
|
+
shareBehavior: boolean;
|
|
113
|
+
shareConversation: boolean;
|
|
114
|
+
shareProfile: boolean;
|
|
115
|
+
};
|
|
116
|
+
profile: import('./insights-settings.js').InsightsProfile;
|
|
117
|
+
stats: {
|
|
118
|
+
events: number;
|
|
119
|
+
turns: number;
|
|
120
|
+
profile: number;
|
|
121
|
+
pending: number;
|
|
122
|
+
};
|
|
123
|
+
records: InsightOutboxRow[];
|
|
124
|
+
}>;
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PackDbAccess, StorageDriver, TenantScope } from './driver.js';
|
|
2
|
+
import type { ScopedStore } from './scoped-store.js';
|
|
3
|
+
/** Default local SQLite storage driver. */
|
|
4
|
+
export declare class SqliteStorageDriver implements StorageDriver {
|
|
5
|
+
private db;
|
|
6
|
+
private writeLease;
|
|
7
|
+
private localSqliteStore;
|
|
8
|
+
private readonly stores;
|
|
9
|
+
initialize(): Promise<void>;
|
|
10
|
+
scoped(scope: TenantScope): ScopedStore;
|
|
11
|
+
packAccess(scope: TenantScope): PackDbAccess;
|
|
12
|
+
applyPackMigrations(): Promise<void>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
private requireDb;
|
|
15
|
+
private scopeKey;
|
|
16
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
import { getProductConfig } from '../product-config.js';
|
|
4
|
+
import { getUserDataDir } from '../runtime.js';
|
|
5
|
+
import { LOCAL_SCOPE } from './driver.js';
|
|
6
|
+
import { SqliteScopedStore } from './index.js';
|
|
7
|
+
import { acquireWriteLease, lockPathForDb, } from './write-lease.js';
|
|
8
|
+
import { acquireWriteLeaseOrExit } from './write-lease-error.js';
|
|
9
|
+
function invokeStatement(statement, operation, params) {
|
|
10
|
+
if (Array.isArray(params))
|
|
11
|
+
return statement[operation](...params);
|
|
12
|
+
if (params)
|
|
13
|
+
return statement[operation](params);
|
|
14
|
+
return statement[operation]();
|
|
15
|
+
}
|
|
16
|
+
function assertScopedPackStatement(sql, params, scope) {
|
|
17
|
+
if (/^\s*PRAGMA\b/i.test(sql) || /\bsqlite_master\b/i.test(sql))
|
|
18
|
+
return;
|
|
19
|
+
if (!/\btenant_id\b/i.test(sql) || !/\buser_id\b/i.test(sql)) {
|
|
20
|
+
throw new Error('[storage] pack SQL must bind tenant_id and user_id');
|
|
21
|
+
}
|
|
22
|
+
if (!params) {
|
|
23
|
+
throw new Error('[storage] scoped pack parameters are required');
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(params)) {
|
|
26
|
+
if (!params.includes(scope.tenantId) || !params.includes(scope.userId)) {
|
|
27
|
+
throw new Error('[storage] pack parameters do not match the bound scope');
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const named = params;
|
|
32
|
+
const tenantId = named.tenantId ?? named.tenant_id;
|
|
33
|
+
const userId = named.userId ?? named.user_id;
|
|
34
|
+
if (tenantId !== scope.tenantId || userId !== scope.userId) {
|
|
35
|
+
throw new Error('[storage] pack parameters do not match the bound scope');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
class SqlitePackDbAccess {
|
|
39
|
+
db;
|
|
40
|
+
scope;
|
|
41
|
+
constructor(db, scope) {
|
|
42
|
+
this.db = db;
|
|
43
|
+
this.scope = scope;
|
|
44
|
+
}
|
|
45
|
+
async get(sql, params) {
|
|
46
|
+
assertScopedPackStatement(sql, params, this.scope);
|
|
47
|
+
return invokeStatement(this.db.prepare(sql), 'get', params);
|
|
48
|
+
}
|
|
49
|
+
async all(sql, params) {
|
|
50
|
+
assertScopedPackStatement(sql, params, this.scope);
|
|
51
|
+
return invokeStatement(this.db.prepare(sql), 'all', params);
|
|
52
|
+
}
|
|
53
|
+
async run(sql, params) {
|
|
54
|
+
assertScopedPackStatement(sql, params, this.scope);
|
|
55
|
+
const result = invokeStatement(this.db.prepare(sql), 'run', params);
|
|
56
|
+
return { changes: result.changes, lastInsertRowid: result.lastInsertRowid };
|
|
57
|
+
}
|
|
58
|
+
async exec(sql) {
|
|
59
|
+
this.db.exec(sql);
|
|
60
|
+
}
|
|
61
|
+
async transaction(operation) {
|
|
62
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
63
|
+
try {
|
|
64
|
+
const value = await operation(this);
|
|
65
|
+
this.db.exec('COMMIT');
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
this.db.exec('ROLLBACK');
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Default local SQLite storage driver. */
|
|
75
|
+
export class SqliteStorageDriver {
|
|
76
|
+
db = null;
|
|
77
|
+
writeLease = null;
|
|
78
|
+
localSqliteStore = null;
|
|
79
|
+
stores = new Map();
|
|
80
|
+
async initialize() {
|
|
81
|
+
if (this.db)
|
|
82
|
+
return;
|
|
83
|
+
const dbPath = path.join(getUserDataDir(), getProductConfig().dbFileName ?? 'agent-shell.db');
|
|
84
|
+
this.writeLease = acquireWriteLeaseOrExit(() => acquireWriteLease(lockPathForDb(dbPath)));
|
|
85
|
+
let db = null;
|
|
86
|
+
try {
|
|
87
|
+
db = new Database(dbPath);
|
|
88
|
+
db.pragma('journal_mode = WAL');
|
|
89
|
+
db.pragma('foreign_keys = ON');
|
|
90
|
+
this.db = db;
|
|
91
|
+
const local = new SqliteScopedStore(db, LOCAL_SCOPE);
|
|
92
|
+
await local.initialize();
|
|
93
|
+
this.localSqliteStore = local;
|
|
94
|
+
this.stores.set(this.scopeKey(LOCAL_SCOPE), local);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
db?.close();
|
|
98
|
+
this.writeLease.release();
|
|
99
|
+
this.writeLease = null;
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
scoped(scope) {
|
|
104
|
+
const db = this.requireDb();
|
|
105
|
+
const key = this.scopeKey(scope);
|
|
106
|
+
const existing = this.stores.get(key);
|
|
107
|
+
if (existing)
|
|
108
|
+
return existing;
|
|
109
|
+
const store = new SqliteScopedStore(db, scope);
|
|
110
|
+
this.stores.set(key, store);
|
|
111
|
+
return store;
|
|
112
|
+
}
|
|
113
|
+
packAccess(scope) {
|
|
114
|
+
return new SqlitePackDbAccess(this.requireDb(), scope);
|
|
115
|
+
}
|
|
116
|
+
async applyPackMigrations() {
|
|
117
|
+
this.localSqliteStore?.applyPackMigrations();
|
|
118
|
+
}
|
|
119
|
+
async close() {
|
|
120
|
+
this.stores.clear();
|
|
121
|
+
this.db?.close();
|
|
122
|
+
this.db = null;
|
|
123
|
+
this.localSqliteStore = null;
|
|
124
|
+
this.writeLease?.release();
|
|
125
|
+
this.writeLease = null;
|
|
126
|
+
}
|
|
127
|
+
requireDb() {
|
|
128
|
+
if (!this.db)
|
|
129
|
+
throw new Error('[storage] SQLite driver is not initialized');
|
|
130
|
+
return this.db;
|
|
131
|
+
}
|
|
132
|
+
scopeKey(scope) {
|
|
133
|
+
return `${scope.tenantId}\u0000${scope.userId}`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface ElectronExit {
|
|
2
|
+
app: {
|
|
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 {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StoreAlreadyOwnedError } from './write-lease.js';
|
|
2
|
+
/** Maps write-lease contention to the desktop's user-facing startup failure. */
|
|
3
|
+
export function acquireWriteLeaseOrExit(acquire, loadElectron = () => import('electron'), exitProcess = (code) => process.exit(code)) {
|
|
4
|
+
try {
|
|
5
|
+
return acquire();
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
if (error instanceof StoreAlreadyOwnedError) {
|
|
9
|
+
void loadElectron()
|
|
10
|
+
.then(({ app, dialog }) => {
|
|
11
|
+
dialog.showErrorBox('无法启动', '另一个实例正在运行并占用本地数据库。请先关闭该实例,再重新打开应用。');
|
|
12
|
+
app.exit(1);
|
|
13
|
+
})
|
|
14
|
+
.catch(() => exitProcess(1));
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerable/agent-shell",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.22",
|
|
4
4
|
"description": "Steerable framework — product-neutral desktop/headless agent host (Tier 5). Electron main + preload + headless HTTP server (BS) sharing one storage/sidecar/tooling core; scenario packs extend it through the @steerable/pack-sdk contract and are composed at build time by the consuming product.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://steerableframework.com/",
|
|
@@ -15,6 +15,14 @@
|
|
|
15
15
|
"type": "module",
|
|
16
16
|
"main": "dist/runtime.js",
|
|
17
17
|
"exports": {
|
|
18
|
+
"./auth": {
|
|
19
|
+
"types": "./dist/auth/index.d.ts",
|
|
20
|
+
"default": "./dist/auth/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./server/start": {
|
|
23
|
+
"types": "./dist/server/start.d.ts",
|
|
24
|
+
"default": "./dist/server/start.js"
|
|
25
|
+
},
|
|
18
26
|
"./*": {
|
|
19
27
|
"types": "./dist/*.d.ts",
|
|
20
28
|
"default": "./dist/*.js"
|
|
@@ -41,9 +49,9 @@
|
|
|
41
49
|
"electron-store": "^8.1.0",
|
|
42
50
|
"he": "^1.2.0",
|
|
43
51
|
"node-pty": "^1.1.0",
|
|
44
|
-
"@steerable/agent-
|
|
45
|
-
"@steerable/agent-
|
|
46
|
-
"@steerable/pack-sdk": "0.6.
|
|
52
|
+
"@steerable/agent-harness": "0.6.22",
|
|
53
|
+
"@steerable/agent-protocol": "0.6.22",
|
|
54
|
+
"@steerable/pack-sdk": "0.6.22"
|
|
47
55
|
},
|
|
48
56
|
"devDependencies": {
|
|
49
57
|
"@types/better-sqlite3": "^7.6.13",
|
|
@@ -58,9 +66,10 @@
|
|
|
58
66
|
"gen": "echo agent-shell-ts",
|
|
59
67
|
"build": "tsc -p tsconfig.json && node copy-skills.mjs && node scripts/build-preload.mjs",
|
|
60
68
|
"test": "vitest run",
|
|
61
|
-
"lint": "tsc -p tsconfig.json --noEmit && node scripts/check-shell-neutral.mjs",
|
|
69
|
+
"lint": "tsc -p tsconfig.json --noEmit && node scripts/check-shell-neutral.mjs && node scripts/check-storage-boundaries.mjs",
|
|
62
70
|
"check:drift": "echo ok",
|
|
63
71
|
"shell:neutral": "node scripts/check-shell-neutral.mjs",
|
|
72
|
+
"storage:boundaries": "node scripts/check-storage-boundaries.mjs",
|
|
64
73
|
"web": "ELECTRON_RUN_AS_NODE=1 DEEPPATH_WEB_DIST=${DEEPPATH_WEB_DIST:-../web/app/dist} node node_modules/electron/cli.js dist/server/index.js",
|
|
65
74
|
"client": "DEEPPATH_FORCE_BUNDLED_WEB=1 DEEPPATH_WEB_DIST=${DEEPPATH_WEB_DIST:-../web/app/dist} node node_modules/electron/cli.js dist/main.js"
|
|
66
75
|
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `localStore` singleton's friendly handling of a held write lease.
|
|
3
|
-
*
|
|
4
|
-
* `createLocalStore` turns a `StoreAlreadyOwnedError` into a readable dialog
|
|
5
|
-
* + exit instead of a raw stack. The single-instance lock (single-instance.ts)
|
|
6
|
-
* already stops a second OS process earlier; this is the deeper kernel-lock
|
|
7
|
-
* backstop for the cases it cannot reach (a bypassed lock, two userData dirs
|
|
8
|
-
* pointed at one database, a crashed-then-relaunched app racing its own
|
|
9
|
-
* cleanup).
|
|
10
|
-
*
|
|
11
|
-
* This lives in its own module — and takes an injectable constructor — so the
|
|
12
|
-
* error-mapping path is testable in a plain-node vitest worker. Importing
|
|
13
|
-
* `storage/index.ts` constructs the real `LocalStore`, which loads
|
|
14
|
-
* better-sqlite3 (Electron ABI) and cannot resolve outside Electron; this
|
|
15
|
-
* module imports only the error class, so it loads anywhere. `dialog`/`app`
|
|
16
|
-
* are loaded lazily for the same reason.
|
|
17
|
-
*/
|
|
18
|
-
/** The slice of electron's app/dialog the friendly exit uses. */
|
|
19
|
-
interface ElectronExit {
|
|
20
|
-
app: {
|
|
21
|
-
exit(code: number): void;
|
|
22
|
-
};
|
|
23
|
-
dialog: {
|
|
24
|
-
showErrorBox(title: string, message: string): void;
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
export declare function createLocalStore<T>(construct: () => T, loadElectron?: () => Promise<ElectronExit>, exitProcess?: (code: number) => void): T;
|
|
28
|
-
export {};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `localStore` singleton's friendly handling of a held write lease.
|
|
3
|
-
*
|
|
4
|
-
* `createLocalStore` turns a `StoreAlreadyOwnedError` into a readable dialog
|
|
5
|
-
* + exit instead of a raw stack. The single-instance lock (single-instance.ts)
|
|
6
|
-
* already stops a second OS process earlier; this is the deeper kernel-lock
|
|
7
|
-
* backstop for the cases it cannot reach (a bypassed lock, two userData dirs
|
|
8
|
-
* pointed at one database, a crashed-then-relaunched app racing its own
|
|
9
|
-
* cleanup).
|
|
10
|
-
*
|
|
11
|
-
* This lives in its own module — and takes an injectable constructor — so the
|
|
12
|
-
* error-mapping path is testable in a plain-node vitest worker. Importing
|
|
13
|
-
* `storage/index.ts` constructs the real `LocalStore`, which loads
|
|
14
|
-
* better-sqlite3 (Electron ABI) and cannot resolve outside Electron; this
|
|
15
|
-
* module imports only the error class, so it loads anywhere. `dialog`/`app`
|
|
16
|
-
* are loaded lazily for the same reason.
|
|
17
|
-
*/
|
|
18
|
-
import { StoreAlreadyOwnedError } from './write-lease.js';
|
|
19
|
-
export function createLocalStore(construct, loadElectron = () => import('electron'), exitProcess = (code) => process.exit(code)) {
|
|
20
|
-
try {
|
|
21
|
-
return construct();
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
if (err instanceof StoreAlreadyOwnedError) {
|
|
25
|
-
void loadElectron()
|
|
26
|
-
.then(({ app, dialog }) => {
|
|
27
|
-
dialog.showErrorBox('无法启动', '另一个实例正在运行并占用本地数据库。请先关闭该实例,再重新打开应用。');
|
|
28
|
-
app.exit(1);
|
|
29
|
-
})
|
|
30
|
-
.catch(() => exitProcess(1));
|
|
31
|
-
// Unreachable once electron resolves (app.exit ends the process); the
|
|
32
|
-
// rethrow is the non-electron fallback so a plain-node import still
|
|
33
|
-
// fails loud rather than returning a half-built store.
|
|
34
|
-
throw err;
|
|
35
|
-
}
|
|
36
|
-
throw err;
|
|
37
|
-
}
|
|
38
|
-
}
|