@workclaw/openclaw-workclaw 1.0.342 → 1.0.343
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/{anthropic-BX4T2YIB.js → anthropic-C1jzp_Rc.js} +1 -1
- package/dist/api.js +379 -0
- package/dist/{azure-openai-responses-CRtsnQnD.js → azure-openai-responses-zym2wxMB.js} +2 -2
- package/dist/{index-ZWLWkJXc.js → channel-CWeiUgV5.js} +2162 -5548
- package/dist/channel-bootstrap.runtime-B0t3vnup-S_ZINco5.js +5 -0
- package/dist/channel-entry-api.d.ts +3 -0
- package/dist/channel-entry-api.d.ts.map +1 -0
- package/dist/channel-entry-api.js +719 -0
- package/dist/channel-entry-contract-BDMmGZzs-4ZlSsA7g.js +252 -0
- package/dist/channel-plugin-api.d.ts +2 -0
- package/dist/channel-plugin-api.d.ts.map +1 -0
- package/dist/channel-plugin-api.js +4 -0
- package/dist/{cleanup-i9QIMqva-CYcFSfZC.js → cleanup-i9QIMqva-DceifGSO.js} +2 -2
- package/dist/config-QB0JLJHX.js +94 -0
- package/dist/{cron-tasks-handler-BIpSoLIG.js → cron-tasks-handler-CIhnDbA3.js} +1 -1
- package/dist/{google-BhYq3yh7.js → google-DqQHu2NY.js} +2 -2
- package/dist/{google-shared-sAeUdThk.js → google-shared-BeRWP6yE.js} +1 -1
- package/dist/{google-vertex-CFbea9mm.js → google-vertex-Cs1hEe_j.js} +2 -2
- package/dist/{host-hook-cleanup-BNlJQu7G-DZh9GtcC.js → host-hook-cleanup-BNlJQu7G-NNZIzDgg.js} +3 -2
- package/dist/index-DLeHpawu.js +1351 -0
- package/dist/index-DvlDhBhd.js +524 -0
- package/dist/{index-CXEk4j12.js → index-mQv9vOMk.js} +1 -1
- package/dist/index.d.ts +1 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -11
- package/dist/{legacy-config-issues-Czu0_ffE-BUj03gnH.js → legacy-config-issues-Czu0_ffE-C-SKoHc6.js} +4 -2
- package/dist/{message-oTt1u1XX-BHxJ4z66.js → message-oTt1u1XX-NFsrpDss.js} +5 -4
- package/dist/message.config.runtime-DmvQNASl.js +4 -0
- package/dist/{message.gateway.runtime-Ch5zdrYr.js → message.gateway.runtime-BIIf6teh.js} +3 -2
- package/dist/{mistral-DgMDMo1J.js → mistral-B0v4IfAz.js} +1 -1
- package/dist/models-config.runtime-cY4XGOI0.js +4 -0
- package/dist/{openai-codex-responses-D6j-616z.js → openai-codex-responses-tC0hde6o.js} +2 -2
- package/dist/{openai-completions-BtT6C5eC.js → openai-completions-w3FtI3rd.js} +1 -1
- package/dist/{openai-responses-Dtuy4M7B.js → openai-responses-rRjf4OAK.js} +2 -2
- package/dist/{openai-responses-shared-DZBSlU2d.js → openai-responses-shared-ZlolmuqL.js} +1 -1
- package/dist/{openrouter-DbsgRqzU.js → openrouter-CXWxTM8p.js} +1 -1
- package/dist/{photon_rs-CbOi7Vp0.js → photon_rs-CGHeuAJ1.js} +1 -1
- package/dist/{plugin-load-profile-DOnG9jJ5-zOQuKNoi.js → plugin-load-profile-DOnG9jJ5-B0ymqOTX.js} +9936 -9990
- package/dist/plugins-C54ejDC8-qGQvFNWW.js +8 -0
- package/dist/provider-discovery.runtime-ZgcVwu5_.js +4 -0
- package/dist/runtime-api-DU-e2Pen.js +391 -0
- package/dist/runtime-api.d.ts +2 -0
- package/dist/runtime-api.d.ts.map +1 -0
- package/dist/runtime-api.js +4 -0
- package/dist/{session-archive.runtime-BAljnZWW.js → session-archive.runtime-SvY1eJq4.js} +2 -2
- package/dist/setup-entry.js +2 -194
- package/dist/src/runtime.js +37 -0
- package/dist/src/secret-contract-api.js +7 -0
- package/dist/string-coerce-DyL154ka-BEV0SzzS.js +61 -0
- package/dist/subsystem-W8wAQXn1-BIMucqMi.js +4 -0
- package/dist/tools-api.d.ts +2 -0
- package/dist/tools-api.d.ts.map +1 -0
- package/dist/tools-api.js +4 -0
- package/dist/transcript.runtime-Dchg_w3u.js +5 -0
- package/dist/workclaw-client-CMjcFjJx.js +538 -0
- package/package.json +9 -5
- package/dist/channel-bootstrap.runtime-B0t3vnup-vflkdVI8.js +0 -5
- package/dist/config-Cw8JPyMQ.js +0 -94
- package/dist/message.config.runtime-DuzEAWuz.js +0 -4
- package/dist/models-config.runtime-DLspRp8e.js +0 -4
- package/dist/plugins-C54ejDC8-Df48OaX-.js +0 -8
- package/dist/provider-discovery.runtime-Dps3xmj0.js +0 -4
- package/dist/subsystem-W8wAQXn1-Dh_4L9-w.js +0 -4
- package/dist/transcript.runtime-B6llle92.js +0 -5
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
import { readFile, stat, mkdir, writeFile, unlink } from "node:fs/promises";
|
|
2
|
+
import path__default from "node:path";
|
|
3
|
+
import process$1 from "node:process";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { n as normalizeAccountId } from "./workclaw-client-CMjcFjJx.js";
|
|
6
|
+
import { Buffer as Buffer$1 } from "node:buffer";
|
|
7
|
+
import os__default from "node:os";
|
|
8
|
+
import { l as logger, a as getWorkclawRuntime, i as isLogLevel, b as setWorkclawRuntime, d as deleteToolContext, c as setToolCallIdToRunIdMapping, e as getToolContext, f as getToolResultHint } from "./runtime-api-DU-e2Pen.js";
|
|
9
|
+
import { exec } from "node:child_process";
|
|
10
|
+
import { promisify } from "node:util";
|
|
11
|
+
import { s as sendMessageWorkclaw } from "./index-DvlDhBhd.js";
|
|
12
|
+
import { r as registerAllOpenclawWorkclawCronTools } from "./index-DLeHpawu.js";
|
|
13
|
+
function sendJson$3(res, statusCode, payload) {
|
|
14
|
+
res.statusCode = statusCode;
|
|
15
|
+
res.setHeader("Content-Type", "application/json");
|
|
16
|
+
res.end(JSON.stringify(payload));
|
|
17
|
+
}
|
|
18
|
+
async function readRequestBody$2(req) {
|
|
19
|
+
const chunks = [];
|
|
20
|
+
await new Promise((resolve, reject) => {
|
|
21
|
+
req.on("data", (chunk) => {
|
|
22
|
+
chunks.push(Buffer$1.isBuffer(chunk) ? chunk : Buffer$1.from(chunk));
|
|
23
|
+
});
|
|
24
|
+
req.on("end", () => resolve());
|
|
25
|
+
req.on("error", (err) => reject(err));
|
|
26
|
+
});
|
|
27
|
+
return Buffer$1.concat(chunks).toString("utf-8");
|
|
28
|
+
}
|
|
29
|
+
async function loadRuntimeConfig(api) {
|
|
30
|
+
const runtimeConfig = api.runtime?.config;
|
|
31
|
+
if (runtimeConfig?.current) {
|
|
32
|
+
return runtimeConfig.current();
|
|
33
|
+
}
|
|
34
|
+
if (runtimeConfig?.loadConfig) {
|
|
35
|
+
return runtimeConfig.loadConfig();
|
|
36
|
+
}
|
|
37
|
+
return api.config;
|
|
38
|
+
}
|
|
39
|
+
async function writeRuntimeConfig(api, config) {
|
|
40
|
+
const runtimeConfig = api.runtime?.config;
|
|
41
|
+
const afterWrite = { mode: "none", reason: "workclaw-accounts-api" };
|
|
42
|
+
if (runtimeConfig?.mutateConfigFile) {
|
|
43
|
+
await runtimeConfig.mutateConfigFile({
|
|
44
|
+
afterWrite,
|
|
45
|
+
mutate(draft) {
|
|
46
|
+
for (const key of Object.keys(draft)) {
|
|
47
|
+
if (!(key in config)) {
|
|
48
|
+
delete draft[key];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
Object.assign(draft, JSON.parse(JSON.stringify(config)));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (runtimeConfig?.replaceConfigFile) {
|
|
57
|
+
await runtimeConfig.replaceConfigFile(config, afterWrite);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (runtimeConfig?.writeConfigFile) {
|
|
61
|
+
await runtimeConfig.writeConfigFile(config);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
throw new Error("Config write not supported");
|
|
65
|
+
}
|
|
66
|
+
function getAccountMap(config) {
|
|
67
|
+
const channels = config?.channels && typeof config.channels === "object" ? config.channels : {};
|
|
68
|
+
const openclawWorkclaw = channels["openclaw-workclaw"] && typeof channels["openclaw-workclaw"] === "object" ? channels["openclaw-workclaw"] : {};
|
|
69
|
+
const accounts = openclawWorkclaw.accounts && typeof openclawWorkclaw.accounts === "object" ? openclawWorkclaw.accounts : {};
|
|
70
|
+
return { ...accounts };
|
|
71
|
+
}
|
|
72
|
+
function withAccounts(config, accounts) {
|
|
73
|
+
const channels = config?.channels && typeof config.channels === "object" ? config.channels : {};
|
|
74
|
+
const openclawWorkclaw = channels["openclaw-workclaw"] && typeof channels["openclaw-workclaw"] === "object" ? channels["openclaw-workclaw"] : {};
|
|
75
|
+
return {
|
|
76
|
+
...config,
|
|
77
|
+
channels: {
|
|
78
|
+
...channels,
|
|
79
|
+
"openclaw-workclaw": {
|
|
80
|
+
...openclawWorkclaw,
|
|
81
|
+
accounts
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function createAccountsApiHandler(api) {
|
|
87
|
+
return async (req, res) => {
|
|
88
|
+
const method = String(req.method ?? "GET").toUpperCase();
|
|
89
|
+
const url = new URL(req.url ?? "", "http://localhost");
|
|
90
|
+
const idRaw = url.searchParams.get("id") ?? url.searchParams.get("accountId");
|
|
91
|
+
const accountId = idRaw ? normalizeAccountId(idRaw) : null;
|
|
92
|
+
if (method === "GET") {
|
|
93
|
+
const cfg = await loadRuntimeConfig(api);
|
|
94
|
+
const accounts = getAccountMap(cfg);
|
|
95
|
+
if (accountId) {
|
|
96
|
+
const account = accounts[accountId];
|
|
97
|
+
if (!account) {
|
|
98
|
+
sendJson$3(res, 404, { ok: false, error: "Not Found" });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
sendJson$3(res, 200, { ok: true, accountId, config: account });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const entries = Object.entries(accounts).map(([id, config]) => ({
|
|
105
|
+
accountId: id,
|
|
106
|
+
config
|
|
107
|
+
}));
|
|
108
|
+
sendJson$3(res, 200, { ok: true, accounts: entries });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (method === "POST" || method === "PUT") {
|
|
112
|
+
const raw = await readRequestBody$2(req);
|
|
113
|
+
let input = {};
|
|
114
|
+
try {
|
|
115
|
+
input = raw ? JSON.parse(raw) : {};
|
|
116
|
+
} catch {
|
|
117
|
+
sendJson$3(res, 400, { ok: false, error: "Invalid JSON" });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const inputId = input?.accountId ?? input?.id;
|
|
121
|
+
const normalizedId = inputId ? normalizeAccountId(String(inputId)) : "";
|
|
122
|
+
const patch = input?.config;
|
|
123
|
+
if (!normalizedId || !patch || typeof patch !== "object") {
|
|
124
|
+
sendJson$3(res, 400, { ok: false, error: "Missing accountId or config" });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const cfg = await loadRuntimeConfig(api);
|
|
128
|
+
const accounts = getAccountMap(cfg);
|
|
129
|
+
if (method === "POST" && accounts[normalizedId]) {
|
|
130
|
+
sendJson$3(res, 409, { ok: false, error: "Account already exists" });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (method === "PUT" && !accounts[normalizedId]) {
|
|
134
|
+
sendJson$3(res, 404, { ok: false, error: "Not Found" });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const next = method === "PUT" ? { ...accounts[normalizedId], ...patch } : patch;
|
|
138
|
+
const updated = withAccounts(cfg, { ...accounts, [normalizedId]: next });
|
|
139
|
+
await writeRuntimeConfig(api, updated);
|
|
140
|
+
sendJson$3(res, 200, { ok: true, accountId: normalizedId });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (method === "DELETE") {
|
|
144
|
+
if (!accountId) {
|
|
145
|
+
sendJson$3(res, 400, { ok: false, error: "Missing accountId" });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const cfg = await loadRuntimeConfig(api);
|
|
149
|
+
const accounts = getAccountMap(cfg);
|
|
150
|
+
if (!accounts[accountId]) {
|
|
151
|
+
sendJson$3(res, 404, { ok: false, error: "Not Found" });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const { [accountId]: _ignored, ...rest } = accounts;
|
|
155
|
+
const updated = withAccounts(cfg, rest);
|
|
156
|
+
await writeRuntimeConfig(api, updated);
|
|
157
|
+
sendJson$3(res, 200, { ok: true, accountId, deleted: true });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
sendJson$3(res, 405, { ok: false, error: "Method Not Allowed" });
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function resolveWorkspaceDir(api) {
|
|
164
|
+
const agents = api.config.agents?.list ?? [];
|
|
165
|
+
const defaultAgent = agents.find((agent) => agent?.default === true) ?? agents.find((agent) => String(agent?.id ?? "").toLowerCase() === "main");
|
|
166
|
+
const workspaceRaw = typeof defaultAgent?.workspace === "string" && defaultAgent.workspace.trim() || typeof api.config.agents?.defaults?.workspace === "string" && api.config.agents.defaults.workspace.trim();
|
|
167
|
+
logger.info(`【智小途-工作区】原始路径: ${workspaceRaw}`);
|
|
168
|
+
if (workspaceRaw) {
|
|
169
|
+
const resolved = api.resolvePath(workspaceRaw);
|
|
170
|
+
if (resolved)
|
|
171
|
+
return resolved;
|
|
172
|
+
}
|
|
173
|
+
const profile = process.env.OPENCLAW_PROFILE?.trim();
|
|
174
|
+
const suffix = profile && profile.toLowerCase() !== "default" ? `workspace-${profile}` : "workspace";
|
|
175
|
+
return path__default.join(os__default.homedir(), ".openclaw", suffix);
|
|
176
|
+
}
|
|
177
|
+
function isDefaultWorkspace(api) {
|
|
178
|
+
const agents = api.config.agents?.list ?? [];
|
|
179
|
+
const defaultAgent = agents.find((agent) => agent?.default === true) ?? agents.find((agent) => String(agent?.id ?? "").toLowerCase() === "main");
|
|
180
|
+
const workspaceRaw = typeof defaultAgent?.workspace === "string" && defaultAgent.workspace.trim() || typeof api.config.agents?.defaults?.workspace === "string" && api.config.agents.defaults.workspace.trim();
|
|
181
|
+
if (workspaceRaw)
|
|
182
|
+
return false;
|
|
183
|
+
const profile = process.env.OPENCLAW_PROFILE?.trim();
|
|
184
|
+
return !profile || profile.toLowerCase() === "default";
|
|
185
|
+
}
|
|
186
|
+
const promptNames = ["SOUL.md", "USER.md"];
|
|
187
|
+
function normalizePromptName(raw) {
|
|
188
|
+
const name = String(raw ?? "").trim().toUpperCase();
|
|
189
|
+
if (name === "SOUL" || name === "SOUL.MD")
|
|
190
|
+
return "SOUL.md";
|
|
191
|
+
if (name === "USER" || name === "USER.MD")
|
|
192
|
+
return "USER.md";
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
function resolvePromptPath(api, name) {
|
|
196
|
+
return path__default.join(resolveWorkspaceDir(api), name);
|
|
197
|
+
}
|
|
198
|
+
function sendJson$2(res, statusCode, payload) {
|
|
199
|
+
res.statusCode = statusCode;
|
|
200
|
+
res.setHeader("Content-Type", "application/json");
|
|
201
|
+
res.end(JSON.stringify(payload));
|
|
202
|
+
}
|
|
203
|
+
function createPromptsApiHandler(api) {
|
|
204
|
+
return async (req, res) => {
|
|
205
|
+
const method = String(req.method ?? "GET").toUpperCase();
|
|
206
|
+
const url = new URL(req.url ?? "", "http://localhost");
|
|
207
|
+
const name = normalizePromptName(url.searchParams.get("name"));
|
|
208
|
+
if (method === "GET") {
|
|
209
|
+
if (name) {
|
|
210
|
+
const filePath = resolvePromptPath(api, name);
|
|
211
|
+
try {
|
|
212
|
+
const content = await readFile(filePath, "utf-8");
|
|
213
|
+
sendJson$2(res, 200, { ok: true, name, content });
|
|
214
|
+
return;
|
|
215
|
+
} catch {
|
|
216
|
+
sendJson$2(res, 404, { ok: false, error: "Not Found" });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const entries = await Promise.all(
|
|
221
|
+
promptNames.map(async (promptName) => {
|
|
222
|
+
const filePath = resolvePromptPath(api, promptName);
|
|
223
|
+
try {
|
|
224
|
+
const info = await stat(filePath);
|
|
225
|
+
return { name: promptName, exists: true, bytes: info.size };
|
|
226
|
+
} catch {
|
|
227
|
+
return { name: promptName, exists: false, bytes: 0 };
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
sendJson$2(res, 200, { ok: true, entries });
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (method === "PUT" || method === "POST") {
|
|
235
|
+
const raw = await readRequestBody$1(req);
|
|
236
|
+
let input = {};
|
|
237
|
+
try {
|
|
238
|
+
input = raw ? JSON.parse(raw) : {};
|
|
239
|
+
} catch {
|
|
240
|
+
sendJson$2(res, 400, { ok: false, error: "Invalid JSON" });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const targetName = normalizePromptName(input?.name);
|
|
244
|
+
const content = typeof input?.content === "string" ? input.content : null;
|
|
245
|
+
if (!targetName || content === null) {
|
|
246
|
+
sendJson$2(res, 400, { ok: false, error: "Missing name or content" });
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const workspaceDir = resolveWorkspaceDir(api);
|
|
250
|
+
await mkdir(workspaceDir, { recursive: true });
|
|
251
|
+
await writeFile(resolvePromptPath(api, targetName), content, { encoding: "utf-8" });
|
|
252
|
+
sendJson$2(res, 200, { ok: true, name: targetName });
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (method === "DELETE") {
|
|
256
|
+
if (!name) {
|
|
257
|
+
sendJson$2(res, 400, { ok: false, error: "Missing name" });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const filePath = resolvePromptPath(api, name);
|
|
261
|
+
try {
|
|
262
|
+
await unlink(filePath);
|
|
263
|
+
sendJson$2(res, 200, { ok: true, name, deleted: true });
|
|
264
|
+
return;
|
|
265
|
+
} catch {
|
|
266
|
+
sendJson$2(res, 200, { ok: true, name, deleted: false });
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
sendJson$2(res, 405, { ok: false, error: "Method Not Allowed" });
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
async function readRequestBody$1(req) {
|
|
274
|
+
const chunks = [];
|
|
275
|
+
await new Promise((resolve, reject) => {
|
|
276
|
+
req.on("data", (chunk) => {
|
|
277
|
+
chunks.push(Buffer$1.isBuffer(chunk) ? chunk : Buffer$1.from(chunk));
|
|
278
|
+
});
|
|
279
|
+
req.on("end", () => resolve());
|
|
280
|
+
req.on("error", (err) => reject(err));
|
|
281
|
+
});
|
|
282
|
+
return Buffer$1.concat(chunks).toString("utf-8");
|
|
283
|
+
}
|
|
284
|
+
function sendJson$1(res, statusCode, payload) {
|
|
285
|
+
res.statusCode = statusCode;
|
|
286
|
+
res.setHeader("Content-Type", "application/json");
|
|
287
|
+
res.end(JSON.stringify(payload));
|
|
288
|
+
}
|
|
289
|
+
async function readRequestBody(req) {
|
|
290
|
+
const chunks = [];
|
|
291
|
+
await new Promise((resolve, reject) => {
|
|
292
|
+
req.on("data", (chunk) => {
|
|
293
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
294
|
+
});
|
|
295
|
+
req.on("end", () => resolve());
|
|
296
|
+
req.on("error", (err) => reject(err));
|
|
297
|
+
});
|
|
298
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
299
|
+
}
|
|
300
|
+
function buildSessionKey(accountId, userId) {
|
|
301
|
+
return `openclaw-workclaw:${accountId}:${userId}`;
|
|
302
|
+
}
|
|
303
|
+
function parseSessionKey(sessionKey) {
|
|
304
|
+
const parts = sessionKey.split(":");
|
|
305
|
+
if (parts.length !== 3 || parts[0] !== "openclaw-workclaw") {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
channel: parts[0],
|
|
310
|
+
accountId: parts[1],
|
|
311
|
+
userId: parts[2]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
async function resetSession(sessionKey, log) {
|
|
315
|
+
try {
|
|
316
|
+
const runtime = getWorkclawRuntime();
|
|
317
|
+
runtime.system.enqueueSystemEvent("/new", {
|
|
318
|
+
sessionKey,
|
|
319
|
+
contextKey: null
|
|
320
|
+
});
|
|
321
|
+
logger.info(`【智小途-会话API】发送 /new 命令到会话: ${sessionKey}`);
|
|
322
|
+
return {
|
|
323
|
+
success: true,
|
|
324
|
+
message: `Session reset command sent to ${sessionKey}`
|
|
325
|
+
};
|
|
326
|
+
} catch (err) {
|
|
327
|
+
const errorMsg = `Failed to reset session: ${String(err)}`;
|
|
328
|
+
logger.error(`【智小途-会话API】${errorMsg}`);
|
|
329
|
+
return { success: false, message: errorMsg };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function getSessionStorePath(agentId, log) {
|
|
333
|
+
try {
|
|
334
|
+
const runtime = getWorkclawRuntime();
|
|
335
|
+
const storePath = runtime.channel.session.resolveStorePath(void 0, {
|
|
336
|
+
agentId
|
|
337
|
+
});
|
|
338
|
+
return storePath;
|
|
339
|
+
} catch (err) {
|
|
340
|
+
logger.error(`【智小途-会话API】无法解析存储路径: ${String(err)}`);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async function getSessionLastUpdated(sessionKey, log) {
|
|
345
|
+
try {
|
|
346
|
+
const runtime = getWorkclawRuntime();
|
|
347
|
+
const storePath = runtime.channel.session.resolveStorePath();
|
|
348
|
+
const updatedAt = runtime.channel.session.readSessionUpdatedAt({
|
|
349
|
+
storePath,
|
|
350
|
+
sessionKey
|
|
351
|
+
});
|
|
352
|
+
return updatedAt ?? null;
|
|
353
|
+
} catch (err) {
|
|
354
|
+
logger.error(`【智小途-会话API】无法读取会话更新时间: ${String(err)}`);
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function createSessionApiHandler(api) {
|
|
359
|
+
return async (req, res) => {
|
|
360
|
+
const method = String(req.method ?? "GET").toUpperCase();
|
|
361
|
+
const url = new URL(req.url ?? "", "http://localhost");
|
|
362
|
+
const fullPath = url.pathname.replace(/\/+$/, "");
|
|
363
|
+
const subPath = fullPath.replace(/^\/openclaw-workclaw\/sessions/, "").replace(/^\//, "") || "/";
|
|
364
|
+
logger.info(`【智小途-会话API】${method} ${fullPath} (子路径: ${subPath})`);
|
|
365
|
+
logger.info(`【智小途-会话API】${method} ${fullPath} (子路径: ${subPath})`);
|
|
366
|
+
if (method === "GET" && (subPath === "/" || subPath === "")) {
|
|
367
|
+
const sessionKey = url.searchParams.get("sessionKey");
|
|
368
|
+
if (sessionKey) {
|
|
369
|
+
const parsed = parseSessionKey(sessionKey);
|
|
370
|
+
const updatedAt = await getSessionLastUpdated(sessionKey);
|
|
371
|
+
sendJson$1(res, 200, {
|
|
372
|
+
ok: true,
|
|
373
|
+
session: {
|
|
374
|
+
sessionKey,
|
|
375
|
+
parsed,
|
|
376
|
+
lastUpdatedAt: updatedAt,
|
|
377
|
+
lastUpdatedAtFormatted: updatedAt ? new Date(updatedAt).toISOString() : null
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
sendJson$1(res, 200, {
|
|
383
|
+
ok: true,
|
|
384
|
+
message: "Session Management API",
|
|
385
|
+
endpoints: {
|
|
386
|
+
"GET /sessions": "获取会话信息 (可选参数: sessionKey)",
|
|
387
|
+
"POST /sessions/reset": "重置/开启新会话 (参数: accountId + userId 或直接提供 sessionKey)",
|
|
388
|
+
"GET /sessions/store-path": "获取会话存储路径 (可选参数: agentId)"
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (method === "POST" && subPath === "reset") {
|
|
394
|
+
const raw = await readRequestBody(req);
|
|
395
|
+
let input = {};
|
|
396
|
+
try {
|
|
397
|
+
input = raw ? JSON.parse(raw) : {};
|
|
398
|
+
} catch {
|
|
399
|
+
sendJson$1(res, 400, { ok: false, error: "Invalid JSON" });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const { accountId, userId, sessionKey: directSessionKey } = input;
|
|
403
|
+
let sessionKey;
|
|
404
|
+
if (directSessionKey) {
|
|
405
|
+
sessionKey = directSessionKey;
|
|
406
|
+
} else if (accountId && userId) {
|
|
407
|
+
sessionKey = buildSessionKey(accountId, userId);
|
|
408
|
+
} else {
|
|
409
|
+
sendJson$1(res, 400, {
|
|
410
|
+
ok: false,
|
|
411
|
+
error: "Missing required fields: either provide 'sessionKey' or both 'accountId' and 'userId'"
|
|
412
|
+
});
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const result = await resetSession(sessionKey);
|
|
416
|
+
if (result.success) {
|
|
417
|
+
sendJson$1(res, 200, {
|
|
418
|
+
ok: true,
|
|
419
|
+
message: result.message,
|
|
420
|
+
sessionKey
|
|
421
|
+
});
|
|
422
|
+
} else {
|
|
423
|
+
sendJson$1(res, 500, {
|
|
424
|
+
ok: false,
|
|
425
|
+
error: result.message,
|
|
426
|
+
sessionKey
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (method === "GET" && subPath === "store-path") {
|
|
432
|
+
const agentId = url.searchParams.get("agentId") ?? void 0;
|
|
433
|
+
const storePath = getSessionStorePath(agentId);
|
|
434
|
+
if (storePath) {
|
|
435
|
+
sendJson$1(res, 200, {
|
|
436
|
+
ok: true,
|
|
437
|
+
storePath,
|
|
438
|
+
agentId: agentId ?? "default"
|
|
439
|
+
});
|
|
440
|
+
} else {
|
|
441
|
+
sendJson$1(res, 500, {
|
|
442
|
+
ok: false,
|
|
443
|
+
error: "Failed to resolve session store path"
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
sendJson$1(res, 404, { ok: false, error: "Not Found" });
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
const execAsync = promisify(exec);
|
|
452
|
+
function sendJson(res, statusCode, payload) {
|
|
453
|
+
res.statusCode = statusCode;
|
|
454
|
+
res.setHeader("Content-Type", "application/json");
|
|
455
|
+
res.end(JSON.stringify(payload));
|
|
456
|
+
}
|
|
457
|
+
function createSkillsApiHandler(api) {
|
|
458
|
+
return async (req, res) => {
|
|
459
|
+
const method = String(req.method ?? "GET").toUpperCase();
|
|
460
|
+
if (method === "GET") {
|
|
461
|
+
try {
|
|
462
|
+
const { stdout, stderr } = await execAsync("openclaw skills list --json", {
|
|
463
|
+
timeout: 1e4
|
|
464
|
+
// 10秒超时
|
|
465
|
+
});
|
|
466
|
+
if (stderr && !stdout) {
|
|
467
|
+
logger.error(`【智小途-技能】列表错误: ${stderr}`);
|
|
468
|
+
sendJson(res, 500, { ok: false, error: "Failed to list skills" });
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
let skillsData;
|
|
472
|
+
try {
|
|
473
|
+
skillsData = JSON.parse(stdout);
|
|
474
|
+
} catch {
|
|
475
|
+
const lines = stdout.split("\n");
|
|
476
|
+
const skills = lines.filter((line) => line.trim() && !line.includes("Skills")).map((line) => {
|
|
477
|
+
const parts = line.split(/\s{2,}/).map((p) => p.trim());
|
|
478
|
+
if (parts.length >= 3) {
|
|
479
|
+
return {
|
|
480
|
+
status: parts[0]?.includes("✓") ? "ready" : "missing",
|
|
481
|
+
name: parts[1],
|
|
482
|
+
description: parts[2],
|
|
483
|
+
source: parts[3] || "openclaw-bundled"
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
return null;
|
|
487
|
+
}).filter((s) => s !== null);
|
|
488
|
+
skillsData = {
|
|
489
|
+
total: skills.length,
|
|
490
|
+
ready: skills.filter((s) => s.status === "ready").length,
|
|
491
|
+
missing: skills.filter((s) => s.status === "missing").length,
|
|
492
|
+
skills
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
sendJson(res, 200, { ok: true, ...skillsData });
|
|
496
|
+
return;
|
|
497
|
+
} catch (error) {
|
|
498
|
+
logger.error(`【智小途-技能】API错误: ${error.message}`);
|
|
499
|
+
sendJson(res, 500, { ok: false, error: error.message });
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
sendJson(res, 405, { ok: false, error: "Method Not Allowed" });
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const PLUGIN_ID = "openclaw-workclaw";
|
|
507
|
+
const templateNames = ["SOUL.md", "IDENTITY.md"];
|
|
508
|
+
const registeredRuntimeApis = /* @__PURE__ */ new WeakSet();
|
|
509
|
+
function formatToolParams(toolName, params) {
|
|
510
|
+
if (!params || typeof params !== "object") {
|
|
511
|
+
return "";
|
|
512
|
+
}
|
|
513
|
+
const normalizedToolName = (toolName || "").toLowerCase().trim();
|
|
514
|
+
const p = params;
|
|
515
|
+
try {
|
|
516
|
+
if (normalizedToolName === "exec" || normalizedToolName === "bash" || normalizedToolName === "shell") {
|
|
517
|
+
const cmd = p.command || p.cmd;
|
|
518
|
+
if (cmd)
|
|
519
|
+
return `command: ${String(cmd).slice(0, 200)}`;
|
|
520
|
+
} else if (normalizedToolName === "read" || normalizedToolName === "file_read" || normalizedToolName === "view") {
|
|
521
|
+
const p2 = p.path;
|
|
522
|
+
if (p2)
|
|
523
|
+
return `path: ${p2}`;
|
|
524
|
+
} else if (normalizedToolName === "write" || normalizedToolName === "file_write" || normalizedToolName === "edit" || normalizedToolName === "patch") {
|
|
525
|
+
const p2 = p.path;
|
|
526
|
+
if (p2)
|
|
527
|
+
return `path: ${p2}`;
|
|
528
|
+
} else if (normalizedToolName === "web_fetch" || normalizedToolName === "fetch" || normalizedToolName === "http_get" || normalizedToolName === "http_request" || normalizedToolName === "open_url" || normalizedToolName === "browser_open" || normalizedToolName === "open") {
|
|
529
|
+
const u = p.url;
|
|
530
|
+
if (u)
|
|
531
|
+
return `url: ${u}`;
|
|
532
|
+
} else if (normalizedToolName === "search" || normalizedToolName === "web_search" || normalizedToolName === "google_search" || normalizedToolName === "browser_search" || normalizedToolName === "browser.search") {
|
|
533
|
+
const q = p.query || p.q || p.search;
|
|
534
|
+
if (q)
|
|
535
|
+
return `query: ${q}`;
|
|
536
|
+
} else if (normalizedToolName === "image_generate" || normalizedToolName === "generate_image" || normalizedToolName === "dalle" || normalizedToolName === "text_to_image") {
|
|
537
|
+
const prompt = p.prompt;
|
|
538
|
+
if (prompt)
|
|
539
|
+
return `prompt: ${String(prompt).slice(0, 200)}`;
|
|
540
|
+
} else if (normalizedToolName === "code_interpreter" || normalizedToolName === "python" || normalizedToolName === "jupyter") {
|
|
541
|
+
const code = p.code;
|
|
542
|
+
if (code)
|
|
543
|
+
return `code: ${String(code).slice(0, 200)}`;
|
|
544
|
+
} else if (normalizedToolName === "mcp_deliver" || normalizedToolName === "deliver") {
|
|
545
|
+
const target = p.target || p.to;
|
|
546
|
+
if (target)
|
|
547
|
+
return `target: ${target}`;
|
|
548
|
+
} else {
|
|
549
|
+
const entries = Object.entries(p);
|
|
550
|
+
if (entries.length > 0) {
|
|
551
|
+
return entries.map(([k, v]) => {
|
|
552
|
+
const vStr = typeof v === "string" ? v : JSON.stringify(v);
|
|
553
|
+
return vStr.length > 100 ? `${k}: ${vStr.slice(0, 100)}...` : `${k}: ${vStr}`;
|
|
554
|
+
}).join("\n");
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} catch {
|
|
558
|
+
return String(params);
|
|
559
|
+
}
|
|
560
|
+
return "";
|
|
561
|
+
}
|
|
562
|
+
function formatToolOutput(toolName, params, _result) {
|
|
563
|
+
return formatToolParams(toolName, params);
|
|
564
|
+
}
|
|
565
|
+
function registerToolHooks(api) {
|
|
566
|
+
api.on("agent_end", (event, ctx) => {
|
|
567
|
+
const { runId } = ctx || {};
|
|
568
|
+
if (runId) {
|
|
569
|
+
deleteToolContext(runId);
|
|
570
|
+
logger.info(`【智小途-工具钩子】agent_end runId=${runId} 已清理`);
|
|
571
|
+
}
|
|
572
|
+
return event;
|
|
573
|
+
});
|
|
574
|
+
api.on("before_tool_call", (event, _ctx) => {
|
|
575
|
+
logger.info(`【智小途-工具钩子】before_tool_call event=${JSON.stringify(event)}`);
|
|
576
|
+
const { runId, toolCallId } = event || {};
|
|
577
|
+
if (runId && toolCallId) {
|
|
578
|
+
setToolCallIdToRunIdMapping(toolCallId, runId);
|
|
579
|
+
logger.info(`【智小途-工具钩子】已映射 toolCallId=${toolCallId} -> runId=${runId} 准备执行`);
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
api.on("after_tool_call", (event, _ctx) => {
|
|
583
|
+
logger.info(`【智小途-工具钩子】after_tool_call event=${JSON.stringify(event)}`);
|
|
584
|
+
const { runId, toolName, params, result } = event || {};
|
|
585
|
+
const toolCtx = runId ? getToolContext(runId) : void 0;
|
|
586
|
+
if (toolCtx && result) {
|
|
587
|
+
const content = formatToolOutput(toolName, params);
|
|
588
|
+
if (content) {
|
|
589
|
+
try {
|
|
590
|
+
const cfg = getWorkclawRuntime().config.loadConfig();
|
|
591
|
+
const formattedToolOutput = {
|
|
592
|
+
name: getToolResultHint(toolName),
|
|
593
|
+
toolName,
|
|
594
|
+
content,
|
|
595
|
+
state: "result"
|
|
596
|
+
};
|
|
597
|
+
sendMessageWorkclaw({
|
|
598
|
+
cfg,
|
|
599
|
+
to: toolCtx.target,
|
|
600
|
+
text: JSON.stringify(formattedToolOutput),
|
|
601
|
+
msgType: "26",
|
|
602
|
+
accountId: toolCtx.accountId,
|
|
603
|
+
openConversationId: toolCtx.openConversationId,
|
|
604
|
+
agentId: toolCtx.agentId,
|
|
605
|
+
replyToMessageId: toolCtx.replyToMessageId,
|
|
606
|
+
last: false,
|
|
607
|
+
source: "after_tool_call"
|
|
608
|
+
}).catch((err) => {
|
|
609
|
+
logger.error(`【智小途-工具钩子】after_tool_call 推送失败: ${err}`);
|
|
610
|
+
});
|
|
611
|
+
logger.info(`【智小途-工具钩子】after_tool_call 已推送 toolName=${toolName}`);
|
|
612
|
+
} catch (err) {
|
|
613
|
+
logger.error(`【智小途-工具钩子】after_tool_call 推送失败: ${err}`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return event;
|
|
618
|
+
});
|
|
619
|
+
api.on("tool_result_persist", (event, _ctx) => {
|
|
620
|
+
return { message: event.message };
|
|
621
|
+
});
|
|
622
|
+
logger.info("【智小途-工具钩子】工具执行钩子已注册");
|
|
623
|
+
}
|
|
624
|
+
function registerRuntimeCapabilities(api) {
|
|
625
|
+
const marker = api;
|
|
626
|
+
if (registeredRuntimeApis.has(marker))
|
|
627
|
+
return;
|
|
628
|
+
registeredRuntimeApis.add(marker);
|
|
629
|
+
logger.setApi(api);
|
|
630
|
+
const scene = api.config?.plugins?.entries?.[PLUGIN_ID]?.config?.scene ?? api.config?.plugins?.installs?.[PLUGIN_ID]?.scene;
|
|
631
|
+
if (isLogLevel(scene)) {
|
|
632
|
+
logger.setLevel(scene);
|
|
633
|
+
}
|
|
634
|
+
logger.info("【智小途-运行时】注册运行时能力");
|
|
635
|
+
setWorkclawRuntime(api.runtime);
|
|
636
|
+
registerAllOpenclawWorkclawCronTools(api);
|
|
637
|
+
registerToolHooks(api);
|
|
638
|
+
api.registerService({
|
|
639
|
+
id: "openclaw-workclaw-templates",
|
|
640
|
+
start: async () => {
|
|
641
|
+
await releaseTemplates(api);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
api.registerHttpRoute({
|
|
645
|
+
path: "/openclaw-workclaw/workspace/prompts",
|
|
646
|
+
handler: createPromptsApiHandler(api),
|
|
647
|
+
auth: "plugin"
|
|
648
|
+
});
|
|
649
|
+
api.registerHttpRoute({
|
|
650
|
+
path: "/openclaw-workclaw/accounts",
|
|
651
|
+
handler: createAccountsApiHandler(api),
|
|
652
|
+
auth: "plugin"
|
|
653
|
+
});
|
|
654
|
+
api.registerHttpRoute({
|
|
655
|
+
path: "/openclaw-workclaw/skills",
|
|
656
|
+
handler: createSkillsApiHandler(),
|
|
657
|
+
auth: "plugin"
|
|
658
|
+
});
|
|
659
|
+
api.registerHttpRoute({
|
|
660
|
+
path: "/openclaw-workclaw/sessions",
|
|
661
|
+
handler: createSessionApiHandler(),
|
|
662
|
+
auth: "plugin",
|
|
663
|
+
match: "prefix"
|
|
664
|
+
});
|
|
665
|
+
logger.info("【智小途-运行时】运行时能力注册完成");
|
|
666
|
+
}
|
|
667
|
+
function getPluginDir() {
|
|
668
|
+
try {
|
|
669
|
+
const url = import.meta.url;
|
|
670
|
+
if (!url.startsWith("file://")) {
|
|
671
|
+
return path__default.dirname(url);
|
|
672
|
+
}
|
|
673
|
+
return path__default.dirname(fileURLToPath(url));
|
|
674
|
+
} catch {
|
|
675
|
+
return process$1.cwd();
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async function writeFileIfMissing(filePath, content) {
|
|
679
|
+
try {
|
|
680
|
+
await writeFile(filePath, content, { encoding: "utf-8", flag: "wx" });
|
|
681
|
+
} catch (error) {
|
|
682
|
+
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
throw error;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
async function releaseTemplates(api) {
|
|
689
|
+
const workspaceDir = resolveWorkspaceDir(api);
|
|
690
|
+
logger.info(`【智小途-工作区】目录: ${workspaceDir}`);
|
|
691
|
+
const currentDir = getPluginDir();
|
|
692
|
+
const templatesDir = path__default.join(
|
|
693
|
+
currentDir.endsWith("dist") ? path__default.dirname(currentDir) : currentDir,
|
|
694
|
+
"templates"
|
|
695
|
+
);
|
|
696
|
+
const shouldOverwrite = isDefaultWorkspace(api);
|
|
697
|
+
await mkdir(workspaceDir, { recursive: true });
|
|
698
|
+
await Promise.all(
|
|
699
|
+
templateNames.map(async (name) => {
|
|
700
|
+
const sourcePath = path__default.join(templatesDir, name);
|
|
701
|
+
const targetPath = path__default.join(workspaceDir, name);
|
|
702
|
+
const content = await readFile(sourcePath, "utf-8");
|
|
703
|
+
if (shouldOverwrite) {
|
|
704
|
+
await writeFile(targetPath, content, { encoding: "utf-8" });
|
|
705
|
+
} else {
|
|
706
|
+
await writeFileIfMissing(targetPath, content);
|
|
707
|
+
}
|
|
708
|
+
})
|
|
709
|
+
);
|
|
710
|
+
logger.info(`【智小途-运行时】模板文件已就绪: ${workspaceDir}`);
|
|
711
|
+
}
|
|
712
|
+
function registerWorkclawFull(api) {
|
|
713
|
+
logger.info("【智小途-运行时】registerFull 调用");
|
|
714
|
+
registerRuntimeCapabilities(api);
|
|
715
|
+
logger.info("【智小途-运行时】registerFull 完成");
|
|
716
|
+
}
|
|
717
|
+
export {
|
|
718
|
+
registerWorkclawFull
|
|
719
|
+
};
|