@kafca/agentdock 0.1.43 → 0.1.45
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 +77 -11
- package/bin/agentdock.mjs +5 -5
- package/dist/renderer/assets/{Badge-CbG6yRSm.js → Badge-BIojwD6s.js} +1 -1
- package/dist/renderer/assets/{Chat-BvNNWGGX.js → Chat-wjBR3XEX.js} +1 -1
- package/dist/renderer/assets/{ChatMarkdown-Ct-PvJB8.js → ChatMarkdown-DqszsPB_.js} +1 -1
- package/dist/renderer/assets/Config-BDCHs5A2.js +21 -0
- package/dist/renderer/assets/{CronList-Dj_3muVG.js → CronList-CEWQYTov.js} +1 -1
- package/dist/renderer/assets/Dashboard-DrWftfk3.js +26 -0
- package/dist/renderer/assets/{EmptyState-Bn2Qh14U.js → EmptyState-BqCx1oWr.js} +1 -1
- package/dist/renderer/assets/{KnowledgeDetail-Bsz6Ou41.js → KnowledgeDetail-BwK8Gpfa.js} +2 -2
- package/dist/renderer/assets/{KnowledgeHome-Cgj4XFXn.js → KnowledgeHome-hhDTZMDO.js} +3 -3
- package/dist/renderer/assets/{Logs-Dp-NF6GN.js → Logs-C9xPEhFT.js} +1 -1
- package/dist/renderer/assets/{Modal-B6YPPBN6.js → Modal-D2U4r2V0.js} +1 -1
- package/dist/renderer/assets/{MonitorList-xEZWg460.js → MonitorList-CayKbfwJ.js} +1 -1
- package/dist/renderer/assets/{Page-Bn7RjGOS.js → Page-urf5GcT_.js} +1 -1
- package/dist/renderer/assets/ProjectDetail-B1GyAr7a.js +11 -0
- package/dist/renderer/assets/{ProjectList-BDk5_gqY.js → ProjectList-BoipQypE.js} +1 -1
- package/dist/renderer/assets/{Select-xshfMIEQ.js → Select-CAscWpy5.js} +1 -1
- package/dist/renderer/assets/{SessionChat-BkHy90z3.js → SessionChat-BAVy5221.js} +1 -1
- package/dist/renderer/assets/{SessionList-BZyXT92L.js → SessionList-BZpuMvh0.js} +1 -1
- package/dist/renderer/assets/{ThreadChat-DUOIyXrS.js → ThreadChat-JX7m9biG.js} +1 -1
- package/dist/renderer/assets/Workspace-CFW8otjU.js +14 -0
- package/dist/renderer/assets/{arrow-left-C54ZI-AS.js → arrow-left-7HrNuS-V.js} +1 -1
- package/dist/renderer/assets/{book-open-Dl7GO0zn.js → book-open-BDoAu82s.js} +1 -1
- package/dist/renderer/assets/{check-CiqgtrOL.js → check-DGlyq4xX.js} +1 -1
- package/dist/renderer/assets/{chevron-down-DTL2OuVh.js → chevron-down-Bb_3EHq9.js} +1 -1
- package/dist/renderer/assets/{circle-DaVKfa3P.js → circle-DIpqZnYw.js} +1 -1
- package/dist/renderer/assets/desktop-Ci1fnRDR.js +1 -0
- package/dist/renderer/assets/{desktop-C6q6wBae.js → desktop-Do_UrC-o.js} +5 -5
- package/dist/renderer/assets/en-CmAr_0e6.js +1 -0
- package/dist/renderer/assets/es-Cs9x52ca.js +1 -0
- package/dist/renderer/assets/{funnel-CrmnLMJI.js → funnel-Bbf0ERLP.js} +1 -1
- package/dist/renderer/assets/{heart-BMfQI7y_.js → heart-CGqU-qfw.js} +1 -1
- package/dist/renderer/assets/{index-nreiKY1f.js → index-Df-Trovt.js} +2 -2
- package/dist/renderer/assets/{index-HPNb1zfx.js → index-DwhTjLA1.js} +36 -36
- package/dist/renderer/assets/index-RaTga2BC.css +11 -0
- package/dist/renderer/assets/ja-DtvtBRTW.js +1 -0
- package/dist/renderer/assets/{pencil-wTQ4l465.js → pencil-CHiy1MvK.js} +1 -1
- package/dist/renderer/assets/{play-BK--MXmw.js → play-CGYnmC4Q.js} +1 -1
- package/dist/renderer/assets/{plug-BIx9fjip.js → plug-Mexu4gyV.js} +1 -1
- package/dist/renderer/assets/{plus-Dn7JFWQ8.js → plus-B_Q5-Oj7.js} +1 -1
- package/dist/renderer/assets/projects-C6bbkC-O.js +1 -0
- package/dist/renderer/assets/{save-CC3zOSWb.js → save-CJ_QFPw0.js} +1 -1
- package/dist/renderer/assets/{search-CDTy9Czu.js → search-D8Dkhsyq.js} +1 -1
- package/dist/renderer/assets/{server-DtQk3t2a.js → server-Cd96R7WL.js} +1 -1
- package/dist/renderer/assets/{session-utils-4ep5D66T.js → session-utils-BNUbQBfF.js} +1 -1
- package/dist/renderer/assets/{sessions-Dor1LteH.js → sessions-BACfV8s1.js} +1 -1
- package/dist/renderer/assets/{shield-check-Cz2LpU6r.js → shield-check-Cnn-yQUA.js} +1 -1
- package/dist/renderer/assets/{trash-2-C2EmWFZV.js → trash-2-oWCxwQ9D.js} +1 -1
- package/dist/renderer/assets/{user-I8B-cs5A.js → user-BzE9BwsY.js} +1 -1
- package/dist/renderer/assets/{wifi-off-CPevTQJn.js → wifi-off-DHxnNkdY.js} +1 -1
- package/dist/renderer/assets/zh-CcJ9vBZj.js +1 -0
- package/dist/renderer/assets/zh-TW-elI72AC1.js +1 -0
- package/dist/renderer/index.html +2 -2
- package/dist-electron/electron/main.js +26 -1
- package/dist-electron/packages/core-sdk/src/index.js +28 -0
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +111 -62
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +80 -7
- package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +78 -5
- package/dist-electron/services/local-ai-core/src/acp/store/external-store.js +98 -0
- package/dist-electron/services/local-ai-core/src/acp/store/local-core-acp-store.js +33 -0
- package/dist-electron/services/local-ai-core/src/acp/store/model-provider-store.js +88 -0
- package/dist-electron/services/local-ai-core/src/acp/store/schema.js +43 -0
- package/dist-electron/services/local-ai-core/src/agents/pi/launch.js +2 -119
- package/dist-electron/services/local-ai-core/src/agents/pi/provider-materializer.js +148 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/config.js +2 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/inbound.js +114 -0
- package/dist-electron/services/local-ai-core/src/channel/lark/local-core-lark-gateway.js +117 -75
- package/dist-electron/services/local-ai-core/src/channel/shared/session-command-runtime.js +4 -2
- package/dist-electron/services/local-ai-core/src/channel/shared/thread-routing.js +34 -0
- package/dist-electron/services/local-ai-core/src/channel/weixin/local-core-weixin-gateway.js +151 -47
- package/dist-electron/services/local-ai-core/src/execution/agent-execution-backend.js +56 -0
- package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +18 -57
- package/dist-electron/services/local-ai-core/src/kernel/local-core-errors.js +255 -0
- package/dist-electron/services/local-ai-core/src/plugins/builtin/catalog.js +78 -0
- package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +3 -1
- package/dist-electron/services/local-ai-core/src/router/workspace-router.js +64 -9
- package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +5 -2
- package/dist-electron/services/local-ai-core/src/runtime/config-migration.js +152 -0
- package/dist-electron/services/local-ai-core/src/runtime/deployment-diagnostics.js +175 -0
- package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +330 -3
- package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +11 -2
- package/dist-electron/services/local-ai-core/src/runtime/provider-config-migration.js +32 -0
- package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +76 -8
- package/dist-electron/services/local-ai-core/src/runtime/server-routes.js +62 -0
- package/dist-electron/services/local-ai-core/src/runtime/server.js +708 -4
- package/dist-electron/services/local-ai-core/src/runtime/standalone.js +13 -3
- package/dist-electron/services/local-ai-core/src/sandbox/opensandbox-client.js +113 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-config.js +273 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-manager.js +215 -0
- package/dist-electron/services/local-ai-core/src/sandbox/sandbox-stdio-proxy.js +188 -0
- package/dist-electron/services/local-ai-core/src/thread/slash-command-registry.js +3 -0
- package/dist-electron/services/local-ai-core/src/thread/thread-command-service.js +32 -0
- package/dist-electron/services/local-ai-core/src/thread/thread-slash-command-dispatcher.js +71 -0
- package/dist-electron/shared/desktop.js +85 -1
- package/dist-electron/tests/contracts/architecture-docs.test.js +83 -0
- package/dist-electron/tests/electron/plugin-kernel.test.js +225 -18
- package/dist-electron/tests/electron/runtime-detection-service.test.js +57 -5
- package/dist-electron/tests/electron/sandbox-config.test.js +541 -0
- package/dist-electron/tests/electron/workspace-task-store.test.js +257 -0
- package/dist-electron/tests/integration/channel-gateways.test.js +32 -0
- package/dist-electron/tests/integration/lark-gateway.test.js +1977 -0
- package/dist-electron/tests/integration/local-core-acp-progress.test.js +1772 -0
- package/dist-electron/tests/integration/local-core-refactor.test.js +775 -5156
- package/dist-electron/tests/integration/local-core-routes.test.js +238 -0
- package/dist-electron/tests/integration/weixin-gateway.test.js +927 -0
- package/package.json +2 -3
- package/dist/renderer/assets/Config-lQX2qVVV.js +0 -16
- package/dist/renderer/assets/Dashboard-eh_RhGYb.js +0 -26
- package/dist/renderer/assets/ProjectDetail-Cv_jIE61.js +0 -11
- package/dist/renderer/assets/Workspace-Dl80fNEC.js +0 -14
- package/dist/renderer/assets/desktop-jaAiayNB.js +0 -1
- package/dist/renderer/assets/en-D2SkCrhG.js +0 -1
- package/dist/renderer/assets/es-C8UBPhxM.js +0 -1
- package/dist/renderer/assets/index-CaQZSRbg.css +0 -11
- package/dist/renderer/assets/ja-ChF4vbBL.js +0 -1
- package/dist/renderer/assets/projects-brJZgwEJ.js +0 -1
- package/dist/renderer/assets/zh-DIcdHycM.js +0 -1
- package/dist/renderer/assets/zh-TW-BtRwreY_.js +0 -1
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const node_process_1 = __importDefault(require("node:process"));
|
|
8
|
+
const node_readline_1 = __importDefault(require("node:readline"));
|
|
9
|
+
const sandbox_manager_js_1 = require("./sandbox-manager.js");
|
|
10
|
+
async function main() {
|
|
11
|
+
const config = parseConfig();
|
|
12
|
+
const manager = new sandbox_manager_js_1.SandboxManager({
|
|
13
|
+
config,
|
|
14
|
+
env: node_process_1.default.env,
|
|
15
|
+
log: (message) => {
|
|
16
|
+
node_process_1.default.stderr.write(`[agentdock-sandbox] ${message}\n`);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
let run;
|
|
20
|
+
let abortController;
|
|
21
|
+
let closed = false;
|
|
22
|
+
const cleanup = async () => {
|
|
23
|
+
if (closed) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
closed = true;
|
|
27
|
+
abortController?.abort();
|
|
28
|
+
await manager.cleanup(run);
|
|
29
|
+
};
|
|
30
|
+
const signalHandler = () => {
|
|
31
|
+
void cleanup().finally(() => node_process_1.default.exit(0));
|
|
32
|
+
};
|
|
33
|
+
node_process_1.default.once('SIGTERM', signalHandler);
|
|
34
|
+
node_process_1.default.once('SIGINT', signalHandler);
|
|
35
|
+
node_process_1.default.once('disconnect', signalHandler);
|
|
36
|
+
try {
|
|
37
|
+
run = await manager.start();
|
|
38
|
+
abortController = new AbortController();
|
|
39
|
+
node_process_1.default.stderr.write(`[agentdock-sandbox] HTTP ACP bridge endpoint: ${run.endpoint}\n`);
|
|
40
|
+
await pipeHttpNdjson(run.endpoint, abortController, cleanup);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
node_process_1.default.stderr.write(`[agentdock-sandbox] ${error instanceof Error ? error.message : String(error)}\n`);
|
|
44
|
+
await cleanup();
|
|
45
|
+
node_process_1.default.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function pipeHttpNdjson(endpoint, abortController, cleanup) {
|
|
49
|
+
await waitForHttpReady(endpoint, abortController.signal);
|
|
50
|
+
void readHttpOutput(endpoint, abortController.signal)
|
|
51
|
+
.then(() => cleanup().finally(() => node_process_1.default.exit(0)))
|
|
52
|
+
.catch((error) => {
|
|
53
|
+
if (abortController.signal.aborted) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
node_process_1.default.stderr.write(`[agentdock-sandbox] ACP bridge output error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
57
|
+
void cleanup().finally(() => node_process_1.default.exit(1));
|
|
58
|
+
});
|
|
59
|
+
let inputQueue = Promise.resolve();
|
|
60
|
+
const rl = node_readline_1.default.createInterface({
|
|
61
|
+
input: node_process_1.default.stdin,
|
|
62
|
+
crlfDelay: Infinity,
|
|
63
|
+
});
|
|
64
|
+
rl.on('line', (line) => {
|
|
65
|
+
if (!line.trim()) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
inputQueue = inputQueue
|
|
69
|
+
.then(() => postHttpInput(endpoint, line, abortController.signal))
|
|
70
|
+
.catch((error) => {
|
|
71
|
+
if (abortController.signal.aborted) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
node_process_1.default.stderr.write(`[agentdock-sandbox] ACP bridge input error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
75
|
+
void cleanup().finally(() => node_process_1.default.exit(1));
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
node_process_1.default.stdin.on('end', () => {
|
|
79
|
+
inputQueue
|
|
80
|
+
.then(() => closeHttpRuntime(endpoint, abortController.signal))
|
|
81
|
+
.catch(() => undefined)
|
|
82
|
+
.finally(() => cleanup().finally(() => node_process_1.default.exit(0)));
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function parseConfig() {
|
|
86
|
+
const raw = node_process_1.default.env.AGENTDOCK_SANDBOX_CONFIG || '';
|
|
87
|
+
if (!raw) {
|
|
88
|
+
throw new Error('AGENTDOCK_SANDBOX_CONFIG is required.');
|
|
89
|
+
}
|
|
90
|
+
return JSON.parse(raw);
|
|
91
|
+
}
|
|
92
|
+
async function waitForHttpReady(endpoint, signal) {
|
|
93
|
+
const deadline = Date.now() + 30000;
|
|
94
|
+
let lastError;
|
|
95
|
+
while (Date.now() < deadline && !signal.aborted) {
|
|
96
|
+
try {
|
|
97
|
+
const response = await fetchWithTimeout(joinEndpointPath(endpoint, '/healthz'), {
|
|
98
|
+
method: 'GET',
|
|
99
|
+
signal,
|
|
100
|
+
}, Math.min(3000, Math.max(1000, deadline - Date.now())));
|
|
101
|
+
const payload = await response.json().catch(() => ({}));
|
|
102
|
+
if (response.ok && payload?.runtimeRunning !== false) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
lastError = new Error(`Health check failed with ${response.status}${payload?.runtimeRunning === false ? ' runtime not running' : ''}`);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
lastError = error;
|
|
109
|
+
}
|
|
110
|
+
await delay(200);
|
|
111
|
+
}
|
|
112
|
+
throw lastError instanceof Error ? lastError : new Error(`Timed out connecting to sandbox ACP bridge: ${endpoint}`);
|
|
113
|
+
}
|
|
114
|
+
async function readHttpOutput(endpoint, signal) {
|
|
115
|
+
const response = await fetch(joinEndpointPath(endpoint, '/v1/acp/output'), { signal });
|
|
116
|
+
if (!response.ok || !response.body) {
|
|
117
|
+
throw new Error(`Output stream failed with ${response.status}`);
|
|
118
|
+
}
|
|
119
|
+
const reader = response.body.getReader();
|
|
120
|
+
const decoder = new TextDecoder();
|
|
121
|
+
let buffer = '';
|
|
122
|
+
while (!signal.aborted) {
|
|
123
|
+
const { value, done } = await reader.read();
|
|
124
|
+
if (done) {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
buffer += decoder.decode(value, { stream: true });
|
|
128
|
+
const lastNewline = Math.max(buffer.lastIndexOf('\n'), buffer.lastIndexOf('\r'));
|
|
129
|
+
if (lastNewline >= 0) {
|
|
130
|
+
writeOutputLines(buffer.slice(0, lastNewline + 1));
|
|
131
|
+
buffer = buffer.slice(lastNewline + 1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
buffer += decoder.decode();
|
|
135
|
+
writeOutputLines(buffer);
|
|
136
|
+
}
|
|
137
|
+
async function postHttpInput(endpoint, line, signal) {
|
|
138
|
+
const response = await fetchWithTimeout(joinEndpointPath(endpoint, '/v1/acp/input'), {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: {
|
|
141
|
+
'content-type': 'application/x-ndjson',
|
|
142
|
+
},
|
|
143
|
+
body: `${line.replace(/\r?\n$/, '')}\n`,
|
|
144
|
+
signal,
|
|
145
|
+
}, 30000);
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
throw new Error(`Input write failed with ${response.status}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function closeHttpRuntime(endpoint, signal) {
|
|
151
|
+
if (signal.aborted) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
await fetchWithTimeout(joinEndpointPath(endpoint, '/v1/acp/close'), {
|
|
155
|
+
method: 'POST',
|
|
156
|
+
signal,
|
|
157
|
+
}, 3000);
|
|
158
|
+
}
|
|
159
|
+
async function fetchWithTimeout(input, init, timeoutMs) {
|
|
160
|
+
const timeoutController = new AbortController();
|
|
161
|
+
const timeout = setTimeout(() => timeoutController.abort(), timeoutMs);
|
|
162
|
+
const abort = () => timeoutController.abort();
|
|
163
|
+
init.signal?.addEventListener('abort', abort, { once: true });
|
|
164
|
+
try {
|
|
165
|
+
return await fetch(input, {
|
|
166
|
+
...init,
|
|
167
|
+
signal: timeoutController.signal,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
init.signal?.removeEventListener('abort', abort);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function writeOutputLines(text) {
|
|
176
|
+
for (const line of text.split(/\r?\n/)) {
|
|
177
|
+
if (line.trim()) {
|
|
178
|
+
node_process_1.default.stdout.write(`${line}\n`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function joinEndpointPath(endpoint, path) {
|
|
183
|
+
return `${endpoint.replace(/\/+$/, '')}${path}`;
|
|
184
|
+
}
|
|
185
|
+
function delay(ms) {
|
|
186
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
187
|
+
}
|
|
188
|
+
void main();
|
|
@@ -9,6 +9,13 @@ class ThreadCommandService {
|
|
|
9
9
|
registry = new slash_command_registry_js_1.SlashCommandRegistry();
|
|
10
10
|
constructor(options) {
|
|
11
11
|
this.options = options;
|
|
12
|
+
this.registry.register({
|
|
13
|
+
names: ['stop'],
|
|
14
|
+
execute: async (_command, input) => ({
|
|
15
|
+
handled: true,
|
|
16
|
+
displayText: await this.executeStopCommand(input.threadId, input.workspaceId),
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
12
19
|
this.registry.register({
|
|
13
20
|
names: ['mode'],
|
|
14
21
|
execute: async (command, input) => ({
|
|
@@ -126,6 +133,31 @@ class ThreadCommandService {
|
|
|
126
133
|
});
|
|
127
134
|
return `已将当前线程 Agent 切换为 ${canonicalAgent}。\n后续消息将使用 ${canonicalAgent} 处理。${runningNote}`;
|
|
128
135
|
}
|
|
136
|
+
async executeStopCommand(threadId, workspaceId) {
|
|
137
|
+
const latestRun = this.options.getLatestRunForThread(threadId);
|
|
138
|
+
if (!latestRun || !['queued', 'running', 'awaiting_input'].includes(latestRun.status)) {
|
|
139
|
+
return '当前没有正在运行的任务。';
|
|
140
|
+
}
|
|
141
|
+
if (!this.options.interruptRun) {
|
|
142
|
+
return '当前运行时不支持通过 `/stop` 停止任务。';
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const result = await this.options.interruptRun(latestRun.id);
|
|
146
|
+
this.options.createAuditEvent({
|
|
147
|
+
type: 'task.updated',
|
|
148
|
+
workspaceId,
|
|
149
|
+
actor: 'local',
|
|
150
|
+
summary: `Stop requested for run ${latestRun.id}.`,
|
|
151
|
+
metadata: { threadId, runId: latestRun.id, interrupted: result.interrupted },
|
|
152
|
+
});
|
|
153
|
+
return result.interrupted
|
|
154
|
+
? '已请求停止当前任务。'
|
|
155
|
+
: '已将当前任务标记为停止;运行时可能已经结束或无法接收取消信号。';
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return `停止任务失败:${error instanceof Error ? error.message : String(error)}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
129
161
|
hasActiveRun(threadId) {
|
|
130
162
|
const latestRun = this.options.getLatestRunForThread(threadId);
|
|
131
163
|
return Boolean(latestRun && ['queued', 'running', 'awaiting_input'].includes(latestRun.status));
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ThreadSlashCommandDispatcher = exports.LOCAL_SLASH_COMMANDS = void 0;
|
|
4
|
+
exports.slashHelpText = slashHelpText;
|
|
5
|
+
const local_core_slash_commands_js_1 = require("../acp/local-core-slash-commands.js");
|
|
6
|
+
const session_command_service_js_1 = require("./session-command-service.js");
|
|
7
|
+
const thread_command_service_js_1 = require("./thread-command-service.js");
|
|
8
|
+
exports.LOCAL_SLASH_COMMANDS = [
|
|
9
|
+
{ names: ['help'], usage: '/help', summary: '查看这份帮助。', group: 'thread' },
|
|
10
|
+
{ names: ['stop'], usage: '/stop', summary: '停止当前正在运行的任务。', group: 'thread' },
|
|
11
|
+
{ names: ['mode'], usage: '/mode [default|auto|acceptEdits|dontAsk|plan|yolo]', summary: '查看或切换当前线程权限模式。', group: 'thread' },
|
|
12
|
+
{ names: ['agent'], usage: '/agent [list|current|use <agent-id>|reset]', summary: '查看或切换当前线程 Agent。', group: 'thread' },
|
|
13
|
+
{ names: ['new'], usage: '/new [标题]', summary: '新建会话。', group: 'session' },
|
|
14
|
+
{ names: ['list', 'sessions'], usage: '/list', summary: '查看会话列表。', group: 'session' },
|
|
15
|
+
{ names: ['switch', 'sw'], usage: '/switch <编号 | id前缀 | 标题>', summary: '切换会话。', group: 'session' },
|
|
16
|
+
{ names: ['history', 'his'], usage: '/history [条数]', summary: '查看当前会话最近消息。', group: 'session' },
|
|
17
|
+
{ names: ['name'], usage: '/name <标题>', summary: '重命名当前会话。', group: 'session' },
|
|
18
|
+
{ names: ['search'], usage: '/search <关键词>', summary: '搜索会话。', group: 'session' },
|
|
19
|
+
{ names: ['del', 'delete', 'rm'], usage: '/del <编号 | id前缀 | 标题> [--confirm]', summary: '删除会话。', group: 'session' },
|
|
20
|
+
];
|
|
21
|
+
const SESSION_COMMAND_NAMES = namesForGroup('session');
|
|
22
|
+
const THREAD_COMMAND_NAMES = namesForGroup('thread');
|
|
23
|
+
class ThreadSlashCommandDispatcher {
|
|
24
|
+
sessionService;
|
|
25
|
+
threadService;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.sessionService = new session_command_service_js_1.SessionCommandService(options.session);
|
|
28
|
+
this.threadService = new thread_command_service_js_1.ThreadCommandService(options.thread);
|
|
29
|
+
}
|
|
30
|
+
async execute(context) {
|
|
31
|
+
const command = (0, local_core_slash_commands_js_1.parseSlashCommand)(context.content);
|
|
32
|
+
if (!command) {
|
|
33
|
+
return { handled: false, displayText: '' };
|
|
34
|
+
}
|
|
35
|
+
if (command.name === 'help') {
|
|
36
|
+
return { handled: true, displayText: slashHelpText() };
|
|
37
|
+
}
|
|
38
|
+
if (SESSION_COMMAND_NAMES.has(command.name)) {
|
|
39
|
+
return this.sessionService.execute(context.content, {
|
|
40
|
+
workspaceId: context.workspaceId,
|
|
41
|
+
currentThreadId: context.threadId,
|
|
42
|
+
defaultTitle: context.defaultTitle,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (THREAD_COMMAND_NAMES.has(command.name)) {
|
|
46
|
+
return this.threadService.execute({
|
|
47
|
+
threadId: context.threadId,
|
|
48
|
+
workspaceId: context.workspaceId,
|
|
49
|
+
content: context.content,
|
|
50
|
+
defaultAgentType: context.defaultAgentType,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return { handled: false, displayText: '' };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.ThreadSlashCommandDispatcher = ThreadSlashCommandDispatcher;
|
|
57
|
+
function slashHelpText(definitions = exports.LOCAL_SLASH_COMMANDS) {
|
|
58
|
+
return [
|
|
59
|
+
'可用命令:',
|
|
60
|
+
...definitions.map((definition) => {
|
|
61
|
+
const aliases = definition.names.slice(1).map((name) => `/${name}`).join('、');
|
|
62
|
+
const aliasText = aliases ? `(别名:${aliases})` : '';
|
|
63
|
+
return `\`${definition.usage}\`${aliasText} ${definition.summary}`;
|
|
64
|
+
}),
|
|
65
|
+
].join('\n');
|
|
66
|
+
}
|
|
67
|
+
function namesForGroup(group) {
|
|
68
|
+
return new Set(exports.LOCAL_SLASH_COMMANDS
|
|
69
|
+
.filter((definition) => definition.group === group)
|
|
70
|
+
.flatMap((definition) => definition.names));
|
|
71
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CHANNEL_PROTOCOL_INSTRUCTION = exports.MONITOR_PROTOCOL_INSTRUCTION = exports.SCHEDULER_PROTOCOL_INSTRUCTION = exports.LOCALCORE_ACP_AGENT_TYPE = exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = exports.DESKTOP_PROVIDER_THINKING_OPTIONS = exports.DESKTOP_PROVIDER_PRESET_OPTIONS = exports.DESKTOP_PLATFORM_TYPE_OPTIONS = exports.DESKTOP_AGENT_TYPE_OPTIONS = exports.DESKTOP_LARK_SDK_PACKAGE = exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = exports.DESKTOP_CODEX_ACP_PACKAGE = exports.DESKTOP_PI_CODING_AGENT_PACKAGE = exports.DESKTOP_PI_ACP_PACKAGE = exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = exports.DEFAULT_DESKTOP_OPENCODE_MODEL = exports.DEFAULT_DESKTOP_AGENT_TYPE = void 0;
|
|
3
|
+
exports.DEFAULT_SANDBOX_RUNTIME_IMAGES = exports.DESKTOP_DEPLOYMENT_PROFILES = exports.DEFAULT_SANDBOX_STATE_MOUNT_PATH = exports.DEFAULT_SANDBOX_WORKSPACE_MOUNT_PATH = exports.DEFAULT_SANDBOX_RUNTIME_IMAGE_ID = exports.DEFAULT_SANDBOX_PROVIDER_ID = exports.CHANNEL_PROTOCOL_INSTRUCTION = exports.MONITOR_PROTOCOL_INSTRUCTION = exports.SCHEDULER_PROTOCOL_INSTRUCTION = exports.LOCALCORE_ACP_AGENT_TYPE = exports.DESKTOP_INTERACTIVE_PERMISSION_AGENT_TYPES = exports.DESKTOP_PROVIDER_THINKING_OPTIONS = exports.DESKTOP_PROVIDER_PRESET_OPTIONS = exports.DESKTOP_PLATFORM_TYPE_OPTIONS = exports.DESKTOP_AGENT_TYPE_OPTIONS = exports.DESKTOP_LARK_SDK_PACKAGE = exports.DESKTOP_CLAUDECODE_ACP_PACKAGE = exports.DESKTOP_CODEX_ACP_PACKAGE = exports.DESKTOP_PI_CODING_AGENT_PACKAGE = exports.DESKTOP_PI_ACP_PACKAGE = exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = exports.DEFAULT_DESKTOP_OPENCODE_MODEL = exports.DEFAULT_DESKTOP_AGENT_TYPE = void 0;
|
|
4
4
|
exports.deriveDesktopRuntimePhase = deriveDesktopRuntimePhase;
|
|
5
5
|
exports.deriveDesktopRuntimeRoles = deriveDesktopRuntimeRoles;
|
|
6
6
|
exports.normalizeDesktopPlatformType = normalizeDesktopPlatformType;
|
|
@@ -12,6 +12,9 @@ exports.isPermissionButtonOption = isPermissionButtonOption;
|
|
|
12
12
|
exports.normalizeDesktopBridgeButtonOption = normalizeDesktopBridgeButtonOption;
|
|
13
13
|
exports.supportsInteractivePermission = supportsInteractivePermission;
|
|
14
14
|
exports.isAcpAgentType = isAcpAgentType;
|
|
15
|
+
exports.getDesktopDeploymentProfile = getDesktopDeploymentProfile;
|
|
16
|
+
exports.defaultSandboxProviderForProfile = defaultSandboxProviderForProfile;
|
|
17
|
+
exports.defaultSandboxRuntimeImage = defaultSandboxRuntimeImage;
|
|
15
18
|
exports.DEFAULT_DESKTOP_AGENT_TYPE = 'pi';
|
|
16
19
|
exports.DEFAULT_DESKTOP_OPENCODE_MODEL = 'opencode/minimax-m2.5-free';
|
|
17
20
|
exports.DEFAULT_DESKTOP_CLAUDECODE_MODEL = '';
|
|
@@ -266,3 +269,84 @@ function isAcpAgentType(agentType) {
|
|
|
266
269
|
|| normalized === 'hermes'
|
|
267
270
|
|| normalized === exports.LOCALCORE_ACP_AGENT_TYPE;
|
|
268
271
|
}
|
|
272
|
+
exports.DEFAULT_SANDBOX_PROVIDER_ID = 'opensandbox-default';
|
|
273
|
+
exports.DEFAULT_SANDBOX_RUNTIME_IMAGE_ID = 'pi-acp-local';
|
|
274
|
+
exports.DEFAULT_SANDBOX_WORKSPACE_MOUNT_PATH = '/workspace';
|
|
275
|
+
exports.DEFAULT_SANDBOX_STATE_MOUNT_PATH = '/agent-state';
|
|
276
|
+
exports.DESKTOP_DEPLOYMENT_PROFILES = [
|
|
277
|
+
{
|
|
278
|
+
id: 'local-desktop',
|
|
279
|
+
label: 'Local Desktop',
|
|
280
|
+
corePublicOrigin: 'http://127.0.0.1:9831',
|
|
281
|
+
coreBindHost: '127.0.0.1',
|
|
282
|
+
openSandboxServerUrl: 'http://127.0.0.1:8080',
|
|
283
|
+
defaultWorkspaceRoot: '',
|
|
284
|
+
sandboxImagePrefix: 'agentdock',
|
|
285
|
+
workspaceMountPath: exports.DEFAULT_SANDBOX_WORKSPACE_MOUNT_PATH,
|
|
286
|
+
stateMountPath: exports.DEFAULT_SANDBOX_STATE_MOUNT_PATH,
|
|
287
|
+
defaultSandboxProviderId: exports.DEFAULT_SANDBOX_PROVIDER_ID,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'docker-compose',
|
|
291
|
+
label: 'Docker Compose',
|
|
292
|
+
corePublicOrigin: 'http://127.0.0.1:9831',
|
|
293
|
+
coreBindHost: '0.0.0.0',
|
|
294
|
+
openSandboxServerUrl: 'http://opensandbox-server:8080',
|
|
295
|
+
defaultWorkspaceRoot: '/workspace',
|
|
296
|
+
sandboxImagePrefix: 'agentdock',
|
|
297
|
+
workspaceMountPath: exports.DEFAULT_SANDBOX_WORKSPACE_MOUNT_PATH,
|
|
298
|
+
stateMountPath: exports.DEFAULT_SANDBOX_STATE_MOUNT_PATH,
|
|
299
|
+
defaultSandboxProviderId: exports.DEFAULT_SANDBOX_PROVIDER_ID,
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
id: 'remote-cloud',
|
|
303
|
+
label: 'Remote Cloud',
|
|
304
|
+
corePublicOrigin: '',
|
|
305
|
+
coreBindHost: '0.0.0.0',
|
|
306
|
+
openSandboxServerUrl: '',
|
|
307
|
+
defaultWorkspaceRoot: '/workspace',
|
|
308
|
+
sandboxImagePrefix: 'agentdock',
|
|
309
|
+
workspaceMountPath: exports.DEFAULT_SANDBOX_WORKSPACE_MOUNT_PATH,
|
|
310
|
+
stateMountPath: exports.DEFAULT_SANDBOX_STATE_MOUNT_PATH,
|
|
311
|
+
defaultSandboxProviderId: exports.DEFAULT_SANDBOX_PROVIDER_ID,
|
|
312
|
+
},
|
|
313
|
+
];
|
|
314
|
+
exports.DEFAULT_SANDBOX_RUNTIME_IMAGES = [
|
|
315
|
+
{
|
|
316
|
+
id: exports.DEFAULT_SANDBOX_RUNTIME_IMAGE_ID,
|
|
317
|
+
agent_type: 'pi',
|
|
318
|
+
image: 'agentdock/pi-acp:local',
|
|
319
|
+
transport: 'http-ndjson',
|
|
320
|
+
acp_port: 8080,
|
|
321
|
+
entrypoint: ['node', '/opt/agentdock/acp-bridge.mjs'],
|
|
322
|
+
workspace_mount_path: exports.DEFAULT_SANDBOX_WORKSPACE_MOUNT_PATH,
|
|
323
|
+
state_mount_path: exports.DEFAULT_SANDBOX_STATE_MOUNT_PATH,
|
|
324
|
+
},
|
|
325
|
+
];
|
|
326
|
+
function getDesktopDeploymentProfile(profileId) {
|
|
327
|
+
const normalized = String(profileId || '').trim();
|
|
328
|
+
return exports.DESKTOP_DEPLOYMENT_PROFILES.find((profile) => profile.id === normalized) || exports.DESKTOP_DEPLOYMENT_PROFILES[0];
|
|
329
|
+
}
|
|
330
|
+
function defaultSandboxProviderForProfile(profileId) {
|
|
331
|
+
const profile = getDesktopDeploymentProfile(profileId);
|
|
332
|
+
return {
|
|
333
|
+
id: profile.defaultSandboxProviderId,
|
|
334
|
+
type: 'opensandbox',
|
|
335
|
+
name: 'OpenSandbox',
|
|
336
|
+
server_url: profile.openSandboxServerUrl,
|
|
337
|
+
api_key_env: 'OPEN_SANDBOX_API_KEY',
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function defaultSandboxRuntimeImage(agentType) {
|
|
341
|
+
const normalized = String(agentType || '').trim().toLowerCase() || 'pi';
|
|
342
|
+
return exports.DEFAULT_SANDBOX_RUNTIME_IMAGES.find((image) => image.agent_type === normalized) || {
|
|
343
|
+
id: `${normalized}-acp-local`,
|
|
344
|
+
agent_type: normalized,
|
|
345
|
+
image: `agentdock/${normalized}-acp:local`,
|
|
346
|
+
transport: 'http-ndjson',
|
|
347
|
+
acp_port: 8080,
|
|
348
|
+
entrypoint: ['node', '/opt/agentdock/acp-bridge.mjs'],
|
|
349
|
+
workspace_mount_path: exports.DEFAULT_SANDBOX_WORKSPACE_MOUNT_PATH,
|
|
350
|
+
state_mount_path: exports.DEFAULT_SANDBOX_STATE_MOUNT_PATH,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
@@ -9,6 +9,62 @@ const node_fs_1 = require("node:fs");
|
|
|
9
9
|
const node_path_1 = require("node:path");
|
|
10
10
|
const node_process_1 = __importDefault(require("node:process"));
|
|
11
11
|
const rootDir = node_process_1.default.cwd();
|
|
12
|
+
const sourceFileExtensions = new Set(['.ts', '.tsx', '.mts', '.mjs']);
|
|
13
|
+
function listSourceFiles(dir) {
|
|
14
|
+
const entries = (0, node_fs_1.readdirSync)(dir, { withFileTypes: true });
|
|
15
|
+
const files = [];
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const path = (0, node_path_1.join)(dir, entry.name);
|
|
18
|
+
if (entry.isDirectory()) {
|
|
19
|
+
if (['dist', 'dist-electron', 'node_modules', 'release'].includes(entry.name)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
files.push(...listSourceFiles(path));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (entry.isFile() && sourceFileExtensions.has(path.slice(path.lastIndexOf('.')))) {
|
|
26
|
+
files.push(path);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
function readSourceFiles(scope) {
|
|
32
|
+
const dir = (0, node_path_1.join)(rootDir, scope);
|
|
33
|
+
if (!(0, node_fs_1.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
return listSourceFiles(dir).map((path) => ({
|
|
37
|
+
path,
|
|
38
|
+
relativePath: (0, node_path_1.relative)(rootDir, path),
|
|
39
|
+
content: (0, node_fs_1.readFileSync)(path, 'utf8'),
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
const rendererPackageSourceImportAllowlist = new Set([
|
|
43
|
+
'src/api/cron.ts',
|
|
44
|
+
'src/api/desktop.ts',
|
|
45
|
+
'src/api/knowledge.ts',
|
|
46
|
+
'src/api/monitors.ts',
|
|
47
|
+
'src/api/runtime-bootstrap.ts',
|
|
48
|
+
'src/app/runtime.ts',
|
|
49
|
+
'src/pages/Automation/MonitorList.tsx',
|
|
50
|
+
'src/pages/Cron/CronList.tsx',
|
|
51
|
+
'src/pages/Dashboard.tsx',
|
|
52
|
+
'src/pages/Knowledge/KnowledgeDetail.tsx',
|
|
53
|
+
'src/pages/Knowledge/KnowledgeHome.tsx',
|
|
54
|
+
'src/pages/System/Config.tsx',
|
|
55
|
+
'src/pages/Threads/thread-chat-action-types.ts',
|
|
56
|
+
'src/pages/Threads/thread-chat-model.ts',
|
|
57
|
+
'src/pages/Threads/thread-chat-page-state.ts',
|
|
58
|
+
'src/pages/Threads/thread-chat-permission.test.ts',
|
|
59
|
+
'src/pages/Threads/thread-chat-permission.ts',
|
|
60
|
+
'src/pages/Threads/useThreadChatActions.ts',
|
|
61
|
+
'src/pages/Threads/useThreadChatController.ts',
|
|
62
|
+
'src/pages/Threads/useThreadChatConversationState.ts',
|
|
63
|
+
'src/pages/Threads/useThreadChatSendingActions.ts',
|
|
64
|
+
'src/pages/Threads/useThreadChatSessionBrowser.ts',
|
|
65
|
+
'src/pages/Threads/useThreadChatThreadActions.ts',
|
|
66
|
+
'src/types/window.d.ts',
|
|
67
|
+
]);
|
|
12
68
|
(0, node_test_1.default)('state ownership docs identify Local AI Core owners for durable chat and channel state', () => {
|
|
13
69
|
const content = (0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, 'docs', 'architecture', 'state-ownership.md'), 'utf8');
|
|
14
70
|
for (const state of [
|
|
@@ -54,3 +110,30 @@ const rootDir = node_process_1.default.cwd();
|
|
|
54
110
|
strict_1.default.match(permissionSource, /export type PendingPermissionRequest = ThreadPendingPermissionRequest;/, 'renderer pending permission state must alias the shared thread permission contract');
|
|
55
111
|
strict_1.default.doesNotMatch(permissionSource, /type PermissionTaskState\s*=/, 'permission helpers must not carry a second task-state union');
|
|
56
112
|
});
|
|
113
|
+
(0, node_test_1.default)('renderer does not add new direct imports of package source internals', () => {
|
|
114
|
+
const offenders = readSourceFiles('src')
|
|
115
|
+
.filter(({ content }) => /from ['"][^'"]*packages\/(?:contracts|core-sdk|plugin-sdk)\/src/.test(content))
|
|
116
|
+
.map(({ relativePath }) => relativePath)
|
|
117
|
+
.filter((relativePath) => !rendererPackageSourceImportAllowlist.has(relativePath));
|
|
118
|
+
strict_1.default.deepEqual(offenders, [], 'renderer code should import package APIs through stable public entrypoints; add migrations instead of new packages/*/src imports');
|
|
119
|
+
});
|
|
120
|
+
(0, node_test_1.default)('renderer does not import Electron or Local AI Core internals', () => {
|
|
121
|
+
const offenders = readSourceFiles('src')
|
|
122
|
+
.filter(({ content }) => {
|
|
123
|
+
return (/from ['"]electron(?:\/[^'"]*)?['"]/.test(content) ||
|
|
124
|
+
/from ['"][^'"]*\/electron(?:\/[^'"]*)?['"]/.test(content) ||
|
|
125
|
+
/from ['"][^'"]*services\/local-ai-core\/src/.test(content));
|
|
126
|
+
})
|
|
127
|
+
.map(({ relativePath }) => relativePath);
|
|
128
|
+
strict_1.default.deepEqual(offenders, [], 'renderer code must communicate with Electron and Local AI Core through shared contracts and API adapters');
|
|
129
|
+
});
|
|
130
|
+
(0, node_test_1.default)('workspace packages declare public package entrypoints', () => {
|
|
131
|
+
for (const packagePath of [
|
|
132
|
+
'packages/contracts/package.json',
|
|
133
|
+
'packages/core-sdk/package.json',
|
|
134
|
+
'packages/plugin-sdk/package.json',
|
|
135
|
+
]) {
|
|
136
|
+
const packageJson = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(rootDir, packagePath), 'utf8'));
|
|
137
|
+
strict_1.default.equal(packageJson.exports?.['.'], './src/index.ts', `${packagePath} must expose its public source entrypoint for workspace consumers`);
|
|
138
|
+
}
|
|
139
|
+
});
|