@t0ken.ai/memoryx-openclaw-plugin 2.2.61 → 2.2.62
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 +2 -2
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +10 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -12
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +48 -1
- package/dist/proxy-credentials.d.ts +1 -1
- package/dist/proxy-credentials.js +6 -6
- package/dist/proxy-redirect.js +3 -3
- package/dist/sidecar.d.ts +4 -3
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +43 -58
- package/dist/vendor-normalize.d.ts +14 -0
- package/dist/vendor-normalize.d.ts.map +1 -0
- package/dist/vendor-normalize.js +82 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -175,14 +175,14 @@ Memories are categorized by the server:
|
|
|
175
175
|
**SlimClaw-style (recommended, no file changes):** Like SlimClaw, you can use a **virtual provider** so all traffic goes through the Sidecar without the plugin mutating config or files. Add this to `~/.openclaw/openclaw.json` under `models.providers`:
|
|
176
176
|
|
|
177
177
|
```json
|
|
178
|
-
"memoryx-
|
|
178
|
+
"memoryx-gateway": {
|
|
179
179
|
"baseUrl": "http://localhost:3335/v1",
|
|
180
180
|
"apiKey": "placeholder",
|
|
181
181
|
"models": [{ "id": "auto" }]
|
|
182
182
|
}
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
Then add an auth profile for `memoryx-
|
|
185
|
+
Then add an auth profile for `memoryx-gateway` (e.g. in `~/.openclaw/agents/main/agent/auth-profiles.json` or global auth) with any placeholder key. In the UI, choose model **MemoryX Gateway** (id: `memoryx-gateway/auto`). Requests will go to the Sidecar, which forwards to the MemoryX server with your real provider credentials (you configure which provider/model to use via the plugin/Sidecar logic). The plugin does not modify your config or `models.json`; it only runs the Sidecar.
|
|
186
186
|
|
|
187
187
|
## License
|
|
188
188
|
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
export declare const PLUGIN_VERSION = "2.2.
|
|
1
|
+
export declare const PLUGIN_VERSION = "2.2.62";
|
|
2
2
|
export declare const DEFAULT_API_BASE = "https://t0ken.ai/api";
|
|
3
3
|
export declare const PLUGIN_DIR: string;
|
|
4
4
|
/** 真实上游 baseUrl 缓存文件:重启后若配置已被改成 localhost,从此文件恢复各厂商真实地址。 */
|
|
5
5
|
export declare const REAL_UPSTREAM_BASEURL_CACHE_FILE: string;
|
|
6
6
|
/** Sidecar HTTP port — fixed so openclaw.json can point to localhost:37169. Do not use a range. */
|
|
7
7
|
export declare const SIDECAR_PORT = 37169;
|
|
8
|
+
/** 单条 plugin.log 最大字节数,超过则轮转为 plugin.log.old 并重新写。 */
|
|
9
|
+
export declare const MAX_LOG_FILE_BYTES: number;
|
|
8
10
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAEvD,eAAO,MAAM,UAAU,QAAgF,CAAC;AAExG,4DAA4D;AAC5D,eAAO,MAAM,gCAAgC,QAAsD,CAAC;AAEpG,mGAAmG;AACnG,eAAO,MAAM,YAAY,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAEvD,eAAO,MAAM,UAAU,QAAgF,CAAC;AAExG,4DAA4D;AAC5D,eAAO,MAAM,gCAAgC,QAAsD,CAAC;AAEpG,mGAAmG;AACnG,eAAO,MAAM,YAAY,QAAQ,CAAC;AAElC,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,QAAkB,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import * as os from "os";
|
|
6
6
|
// Plugin version - synced from package.json by prebuild script
|
|
7
|
-
export const PLUGIN_VERSION = "2.2.
|
|
7
|
+
export const PLUGIN_VERSION = "2.2.62";
|
|
8
8
|
export const DEFAULT_API_BASE = "https://t0ken.ai/api";
|
|
9
9
|
export const PLUGIN_DIR = path.join(os.homedir(), ".openclaw", "extensions", "memoryx-openclaw-plugin");
|
|
10
10
|
/** 真实上游 baseUrl 缓存文件:重启后若配置已被改成 localhost,从此文件恢复各厂商真实地址。 */
|
|
11
11
|
export const REAL_UPSTREAM_BASEURL_CACHE_FILE = path.join(PLUGIN_DIR, "real-upstream-baseurl.json");
|
|
12
12
|
/** Sidecar HTTP port — fixed so openclaw.json can point to localhost:37169. Do not use a range. */
|
|
13
13
|
export const SIDECAR_PORT = 37169;
|
|
14
|
+
/** 单条 plugin.log 最大字节数,超过则轮转为 plugin.log.old 并重新写。 */
|
|
15
|
+
export const MAX_LOG_FILE_BYTES = 2 * 1024 * 1024; // 2MB
|
package/dist/hooks.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
import type { MemoryXPlugin } from "./plugin-core.js";
|
|
5
5
|
export declare function registerHooks(api: any, plugin: MemoryXPlugin, wrapProvidersWithProxy: () => void, applySidecarRedirect: (opts?: {
|
|
6
6
|
quiet?: boolean;
|
|
7
|
-
}) => void): void;
|
|
7
|
+
}) => void, shouldApplyProxy: () => boolean): void;
|
|
8
8
|
//# sourceMappingURL=hooks.d.ts.map
|
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,wBAAgB,aAAa,CACzB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,aAAa,EACrB,sBAAsB,EAAE,MAAM,IAAI,EAClC,oBAAoB,EAAE,CAAC,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,wBAAgB,aAAa,CACzB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,aAAa,EACrB,sBAAsB,EAAE,MAAM,IAAI,EAClC,oBAAoB,EAAE,CAAC,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,IAAI,EAC1D,gBAAgB,EAAE,MAAM,OAAO,GAChC,IAAI,CAsCN"}
|
package/dist/hooks.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function registerHooks(api, plugin, wrapProvidersWithProxy, applySidecarRedirect) {
|
|
1
|
+
export function registerHooks(api, plugin, wrapProvidersWithProxy, applySidecarRedirect, shouldApplyProxy) {
|
|
2
2
|
let useVirtualProviderInLastRun = false;
|
|
3
3
|
api.on("message_received", async (event) => {
|
|
4
4
|
const { content } = event;
|
|
@@ -6,27 +6,25 @@ export function registerHooks(api, plugin, wrapProvidersWithProxy, applySidecarR
|
|
|
6
6
|
await plugin.onMessage("user", content);
|
|
7
7
|
});
|
|
8
8
|
api.on("llm_input", (_event) => {
|
|
9
|
-
useVirtualProviderInLastRun = _event?.provider === "memoryx-
|
|
9
|
+
useVirtualProviderInLastRun = _event?.provider === "memoryx-gateway";
|
|
10
10
|
});
|
|
11
11
|
api.on("llm_output", async (event) => {
|
|
12
|
-
const { assistantTexts
|
|
13
|
-
if (assistantTexts && Array.isArray(assistantTexts) && plugin
|
|
12
|
+
const { assistantTexts } = event;
|
|
13
|
+
if (assistantTexts && Array.isArray(assistantTexts) && plugin) {
|
|
14
14
|
const fullContent = assistantTexts.join("\n");
|
|
15
15
|
if (fullContent && fullContent.length >= 2)
|
|
16
16
|
await plugin.onMessage("assistant", fullContent);
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
api.on("before_agent_start", async (event) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return;
|
|
20
|
+
if (shouldApplyProxy()) {
|
|
21
|
+
wrapProvidersWithProxy();
|
|
22
|
+
applySidecarRedirect({ quiet: true });
|
|
23
|
+
}
|
|
25
24
|
try {
|
|
26
25
|
await plugin.startTimersIfNeeded();
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
// 避免 OpenClaw 把 prependContext 当成单独用户消息写入历史(role=user 的 MemoryX Context 条)。
|
|
26
|
+
// 用户消息只由 message_received 写入一次,此处不再 onMessage("user", prompt),
|
|
27
|
+
// 避免与 message_received 重复,且 before_agent_start 可能被多次触发(如 tool 多轮)导致同条 prompt 重复入队。
|
|
30
28
|
}
|
|
31
29
|
catch (error) {
|
|
32
30
|
api.logger.warn(`[MemoryX] before_agent_start failed: ${error}`);
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EAAE,aAAa,EAAU,MAAM,kBAAkB,CAAC;;;;;;kBAyBvC,GAAG,iBAAiB,YAAY,GAAG,IAAI;;AARzD,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EAAE,aAAa,EAAU,MAAM,kBAAkB,CAAC;;;;;;kBAyBvC,GAAG,iBAAiB,YAAY,GAAG,IAAI;;AARzD,wBAqGE;AAEF,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,9 @@ export default {
|
|
|
54
54
|
});
|
|
55
55
|
const getSidecarBase = () => `http://localhost:${sidecar.getPort()}`;
|
|
56
56
|
const { applySidecarRedirect, wrapProvidersWithProxy } = createProxyRedirect(api, getSidecarBase, realProviderHeader);
|
|
57
|
-
|
|
57
|
+
/** 仅当用户当前默认使用的是 memoryx-gateway 时才做拦截/注入,否则不碰配置,避免装完插件就用不了 OpenClaw。 */
|
|
58
|
+
const shouldApplyProxy = () => getDefaultModelAndProvider(extractProviderCredentials(api.config), api.config).provider === "memoryx-gateway";
|
|
59
|
+
registerHooks(api, plugin, wrapProvidersWithProxy, applySidecarRedirect, shouldApplyProxy);
|
|
58
60
|
api.registerService({
|
|
59
61
|
id: "memoryx-sidecar",
|
|
60
62
|
start: async () => {
|
|
@@ -71,9 +73,14 @@ export default {
|
|
|
71
73
|
api.logger.info("[MemoryX] Sidecar stopped");
|
|
72
74
|
},
|
|
73
75
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
if (defaultConfig.provider === "memoryx-gateway") {
|
|
77
|
+
wrapProvidersWithProxy();
|
|
78
|
+
applySidecarRedirect();
|
|
79
|
+
api.logger.info(`[MemoryX] ✅ Plugin v${PLUGIN_VERSION} ready! Default is MemoryX Gateway, redirect applied.`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
api.logger.info(`[MemoryX] ✅ Plugin v${PLUGIN_VERSION} ready! Default is ${defaultConfig.provider}/${defaultConfig.model}; switch to MemoryX Gateway to use MemoryX.`);
|
|
83
|
+
}
|
|
77
84
|
setImmediate(async () => {
|
|
78
85
|
try {
|
|
79
86
|
await plugin.init();
|
|
@@ -85,19 +92,17 @@ export default {
|
|
|
85
92
|
catch (e) {
|
|
86
93
|
// ignore
|
|
87
94
|
}
|
|
95
|
+
// 仅用「非 localhost」的 baseUrl 更新缓存,避免配置被改成 localhost 时覆盖掉真实地址
|
|
88
96
|
try {
|
|
89
97
|
const cached = await loadRealUpstreamBaseUrlCache();
|
|
90
|
-
const merged = new Map(
|
|
91
|
-
for (const [id, url] of
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
const merged = new Map(cached);
|
|
99
|
+
for (const [id, url] of realUpstreamBaseUrlMap) {
|
|
100
|
+
if (id === "memoryx-gateway")
|
|
101
|
+
continue;
|
|
102
|
+
if (url && !isLocalhostBaseUrl(url, sidecarBaseUrl)) {
|
|
94
103
|
merged.set(id, url);
|
|
95
|
-
log(`[Proxy] Restored ${id} baseUrl from cache (config was localhost)`);
|
|
96
104
|
}
|
|
97
105
|
}
|
|
98
|
-
if (cached.size > 0 && merged.size > 0) {
|
|
99
|
-
sidecar.updateRealUpstreamBaseUrlMap(merged);
|
|
100
|
-
}
|
|
101
106
|
await saveRealUpstreamBaseUrlCache(merged);
|
|
102
107
|
}
|
|
103
108
|
catch (e) {
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,QAAQ,QAAsC,CAAC;AA4B5D;;;GAGG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,IAAI,CAiD9E"}
|
package/dist/logger.js
CHANGED
|
@@ -1,17 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Layered logging: file always, optional console for important messages.
|
|
3
|
+
* Log file size is capped: when plugin.log exceeds MAX_LOG_FILE_BYTES it is rotated to plugin.log.old and a new file is started.
|
|
3
4
|
*/
|
|
4
5
|
import * as fs from "fs";
|
|
5
6
|
import * as path from "path";
|
|
6
|
-
import { PLUGIN_DIR } from "./constants.js";
|
|
7
|
+
import { PLUGIN_DIR, MAX_LOG_FILE_BYTES } from "./constants.js";
|
|
7
8
|
let logStream = null;
|
|
8
9
|
let logStreamReady = false;
|
|
10
|
+
let writeCount = 0;
|
|
11
|
+
const CHECK_ROTATE_EVERY = 500;
|
|
9
12
|
export const LOG_FILE = path.join(PLUGIN_DIR, "plugin.log");
|
|
13
|
+
const LOG_FILE_OLD = path.join(PLUGIN_DIR, "plugin.log.old");
|
|
10
14
|
function ensureDir() {
|
|
11
15
|
if (!fs.existsSync(PLUGIN_DIR)) {
|
|
12
16
|
fs.mkdirSync(PLUGIN_DIR, { recursive: true });
|
|
13
17
|
}
|
|
14
18
|
}
|
|
19
|
+
function rotateIfNeeded() {
|
|
20
|
+
try {
|
|
21
|
+
if (!fs.existsSync(LOG_FILE))
|
|
22
|
+
return;
|
|
23
|
+
const stat = fs.statSync(LOG_FILE);
|
|
24
|
+
if (stat.size < MAX_LOG_FILE_BYTES)
|
|
25
|
+
return;
|
|
26
|
+
if (logStream) {
|
|
27
|
+
try {
|
|
28
|
+
logStream.end();
|
|
29
|
+
}
|
|
30
|
+
catch (_) { }
|
|
31
|
+
logStream = null;
|
|
32
|
+
logStreamReady = false;
|
|
33
|
+
}
|
|
34
|
+
if (fs.existsSync(LOG_FILE_OLD))
|
|
35
|
+
fs.unlinkSync(LOG_FILE_OLD);
|
|
36
|
+
fs.renameSync(LOG_FILE, LOG_FILE_OLD);
|
|
37
|
+
}
|
|
38
|
+
catch (_) {
|
|
39
|
+
/* ignore */
|
|
40
|
+
}
|
|
41
|
+
}
|
|
15
42
|
/**
|
|
16
43
|
* Layered logging: all logs go to file (stream when ready, else sync append so nothing is lost).
|
|
17
44
|
* When console=true, also log to console (for important/guidance messages).
|
|
@@ -26,8 +53,28 @@ export function log(message, options = {}) {
|
|
|
26
53
|
try {
|
|
27
54
|
if (!logStreamReady) {
|
|
28
55
|
ensureDir();
|
|
56
|
+
rotateIfNeeded();
|
|
29
57
|
logStream = fs.createWriteStream(LOG_FILE, { flags: "a" });
|
|
30
58
|
logStreamReady = true;
|
|
59
|
+
writeCount = 0;
|
|
60
|
+
}
|
|
61
|
+
writeCount++;
|
|
62
|
+
if (writeCount >= CHECK_ROTATE_EVERY) {
|
|
63
|
+
writeCount = 0;
|
|
64
|
+
try {
|
|
65
|
+
if (fs.existsSync(LOG_FILE)) {
|
|
66
|
+
const stat = fs.statSync(LOG_FILE);
|
|
67
|
+
if (stat.size >= MAX_LOG_FILE_BYTES) {
|
|
68
|
+
rotateIfNeeded();
|
|
69
|
+
if (!logStreamReady) {
|
|
70
|
+
ensureDir();
|
|
71
|
+
logStream = fs.createWriteStream(LOG_FILE, { flags: "a" });
|
|
72
|
+
logStreamReady = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (_) { }
|
|
31
78
|
}
|
|
32
79
|
logStream?.write(line, (err) => {
|
|
33
80
|
if (err) {
|
|
@@ -19,7 +19,7 @@ export declare function extractProviderCredentials(config: any, providerOverride
|
|
|
19
19
|
* 在 init 时从 OpenClaw 配置(api.config)读到的「所有厂商 -> baseUrl」做一次快照,不写死任何厂商地址。
|
|
20
20
|
* - 来源唯一:插件启动时通过 api 读到的 openclaw 配置文件(openclaw.json 等)里的 models.providers[id].baseUrl。
|
|
21
21
|
* - 若某条已是 localhost(例如上次 redirect 被持久化),本次仍写入 localhost;恢复依赖 setImmediate 里从缓存文件合并(首次正常时已落盘)。
|
|
22
|
-
* 返回 Map<providerId, baseUrl>,memoryx-
|
|
22
|
+
* 返回 Map<providerId, baseUrl>,memoryx-gateway 不写入。
|
|
23
23
|
*/
|
|
24
24
|
export declare function buildRealUpstreamBaseUrlMap(credentials: Map<string, ProviderCredentials>, _sidecarBaseUrl: string): Map<string, string>;
|
|
25
25
|
/**
|
|
@@ -56,9 +56,9 @@ export function extractProviderCredentials(config, providerOverrides) {
|
|
|
56
56
|
process.env[`${id.toUpperCase()}_API_KEY`] ||
|
|
57
57
|
authKeys.get(id) ||
|
|
58
58
|
"";
|
|
59
|
-
const
|
|
59
|
+
const isMemoryxGateway = (id === "memoryx-gateway");
|
|
60
60
|
credentials.set(id, {
|
|
61
|
-
baseUrl:
|
|
61
|
+
baseUrl: isMemoryxGateway ? "" : (override?.baseUrl || pc.baseUrl),
|
|
62
62
|
apiKey,
|
|
63
63
|
models: pc.models,
|
|
64
64
|
});
|
|
@@ -72,12 +72,12 @@ export function extractProviderCredentials(config, providerOverrides) {
|
|
|
72
72
|
* 在 init 时从 OpenClaw 配置(api.config)读到的「所有厂商 -> baseUrl」做一次快照,不写死任何厂商地址。
|
|
73
73
|
* - 来源唯一:插件启动时通过 api 读到的 openclaw 配置文件(openclaw.json 等)里的 models.providers[id].baseUrl。
|
|
74
74
|
* - 若某条已是 localhost(例如上次 redirect 被持久化),本次仍写入 localhost;恢复依赖 setImmediate 里从缓存文件合并(首次正常时已落盘)。
|
|
75
|
-
* 返回 Map<providerId, baseUrl>,memoryx-
|
|
75
|
+
* 返回 Map<providerId, baseUrl>,memoryx-gateway 不写入。
|
|
76
76
|
*/
|
|
77
77
|
export function buildRealUpstreamBaseUrlMap(credentials, _sidecarBaseUrl) {
|
|
78
78
|
const map = new Map();
|
|
79
79
|
for (const [id, creds] of credentials) {
|
|
80
|
-
if (id === "memoryx-
|
|
80
|
+
if (id === "memoryx-gateway")
|
|
81
81
|
continue;
|
|
82
82
|
const base = (creds.baseUrl ?? "").trim();
|
|
83
83
|
map.set(id, base);
|
|
@@ -92,7 +92,7 @@ export function buildRealUpstreamBaseUrlMap(credentials, _sidecarBaseUrl) {
|
|
|
92
92
|
export function realUpstreamCredentialsForSidecar(credentials, realUpstreamBaseUrlMap) {
|
|
93
93
|
const out = new Map();
|
|
94
94
|
for (const [id, creds] of credentials) {
|
|
95
|
-
if (id === "memoryx-
|
|
95
|
+
if (id === "memoryx-gateway") {
|
|
96
96
|
out.set(id, creds);
|
|
97
97
|
continue;
|
|
98
98
|
}
|
|
@@ -129,7 +129,7 @@ export async function saveRealUpstreamBaseUrlCache(map) {
|
|
|
129
129
|
await fs.promises.mkdir(PLUGIN_DIR, { recursive: true });
|
|
130
130
|
const obj = {};
|
|
131
131
|
for (const [id, url] of map) {
|
|
132
|
-
if (id !== "memoryx-
|
|
132
|
+
if (id !== "memoryx-gateway" && url)
|
|
133
133
|
obj[id] = url;
|
|
134
134
|
}
|
|
135
135
|
await fs.promises.writeFile(REAL_UPSTREAM_BASEURL_CACHE_FILE, JSON.stringify(obj, null, 2), "utf8");
|
package/dist/proxy-redirect.js
CHANGED
|
@@ -15,7 +15,7 @@ export function createProxyRedirect(api, getSidecarBase, realProviderHeader) {
|
|
|
15
15
|
const providers = api.config.models.providers;
|
|
16
16
|
let n = 0;
|
|
17
17
|
for (const providerId of creds.keys()) {
|
|
18
|
-
if (providerId === "memoryx-
|
|
18
|
+
if (providerId === "memoryx-gateway")
|
|
19
19
|
continue;
|
|
20
20
|
const p = providers[providerId];
|
|
21
21
|
if (!p || typeof p !== "object")
|
|
@@ -33,7 +33,7 @@ export function createProxyRedirect(api, getSidecarBase, realProviderHeader) {
|
|
|
33
33
|
}
|
|
34
34
|
if (n > 0) {
|
|
35
35
|
for (const providerId of creds.keys()) {
|
|
36
|
-
if (providerId === "memoryx-
|
|
36
|
+
if (providerId === "memoryx-gateway")
|
|
37
37
|
continue;
|
|
38
38
|
const p = providers[providerId];
|
|
39
39
|
const readBack = (p && typeof p === "object" && p.baseUrl) ? p.baseUrl : "<missing>";
|
|
@@ -52,7 +52,7 @@ export function createProxyRedirect(api, getSidecarBase, realProviderHeader) {
|
|
|
52
52
|
const handler = {
|
|
53
53
|
get(target, prop) {
|
|
54
54
|
const val = target[prop];
|
|
55
|
-
if (val && typeof val === "object" && creds.has(prop) && prop !== "memoryx-
|
|
55
|
+
if (val && typeof val === "object" && creds.has(prop) && prop !== "memoryx-gateway") {
|
|
56
56
|
return new Proxy(val, {
|
|
57
57
|
get(t, k) {
|
|
58
58
|
if (k === "baseUrl")
|
package/dist/sidecar.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local HTTP Sidecar: 仅转发到服务端 proxy(记忆注入),无本地回落。
|
|
3
|
+
*/
|
|
1
4
|
import type { ProviderCredentials } from "./types.js";
|
|
2
5
|
import type { PluginConfig } from "./types.js";
|
|
3
6
|
export type GetSDK = (config?: PluginConfig) => Promise<any>;
|
|
@@ -31,10 +34,8 @@ export declare class SidecarServer {
|
|
|
31
34
|
private redactHeaders;
|
|
32
35
|
/** Build forward request headers from apiKey in OpenClaw style; does not overwrite user config */
|
|
33
36
|
private buildForwardHeaders;
|
|
34
|
-
/**
|
|
37
|
+
/** 仅转发到服务端 proxy(记忆注入),无本地回落。 */
|
|
35
38
|
private handleRequest;
|
|
36
|
-
private directRequest;
|
|
37
|
-
private writeResponse;
|
|
38
39
|
private readBody;
|
|
39
40
|
}
|
|
40
41
|
//# sourceMappingURL=sidecar.d.ts.map
|
package/dist/sidecar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,eAAe,CAAsC;IAC7D,OAAO,CAAC,kBAAkB,CAA6C;IACvE,OAAO,CAAC,OAAO,CAAiB;gBAG5B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC7C,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EACpD,kBAAkB,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAC9D,OAAO,EAAE,cAAc;IAQrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAa3B,OAAO,IAAI,MAAM;IAMjB;;OAEG;IACH,4BAA4B,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAU5D,+EAA+E;IAC/E,OAAO,CAAC,aAAa;IAUrB,kGAAkG;IAClG,OAAO,CAAC,mBAAmB;IAa3B,iCAAiC;YACnB,aAAa;IAmN3B,OAAO,CAAC,QAAQ;CAQnB"}
|
package/dist/sidecar.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Local HTTP Sidecar:
|
|
2
|
+
* Local HTTP Sidecar: 仅转发到服务端 proxy(记忆注入),无本地回落。
|
|
3
3
|
*/
|
|
4
4
|
import * as http from "http";
|
|
5
5
|
import { SIDECAR_PORT } from "./constants.js";
|
|
@@ -95,7 +95,7 @@ export class SidecarServer {
|
|
|
95
95
|
}
|
|
96
96
|
return h;
|
|
97
97
|
}
|
|
98
|
-
/**
|
|
98
|
+
/** 仅转发到服务端 proxy(记忆注入),无本地回落。 */
|
|
99
99
|
async handleRequest(req, res) {
|
|
100
100
|
const url = req.url || "/";
|
|
101
101
|
const method = req.method?.toUpperCase();
|
|
@@ -123,11 +123,22 @@ export class SidecarServer {
|
|
|
123
123
|
catch {
|
|
124
124
|
openaiRequest = {};
|
|
125
125
|
}
|
|
126
|
+
const messages = openaiRequest.messages || [];
|
|
127
|
+
const roleSeq = messages.map((m) => (m?.role || "?").slice(0, 1)).join("");
|
|
128
|
+
const lastMsg = messages.length ? messages[messages.length - 1] : null;
|
|
129
|
+
const lastRole = lastMsg ? (lastMsg.role || "?") : "-";
|
|
130
|
+
const lastContentLen = lastMsg && lastMsg.content != null
|
|
131
|
+
? typeof lastMsg.content === "string"
|
|
132
|
+
? lastMsg.content.length
|
|
133
|
+
: JSON.stringify(lastMsg.content).length
|
|
134
|
+
: 0;
|
|
135
|
+
const topKeys = Object.keys(openaiRequest).filter((k) => !["messages", "model"].includes(k) && openaiRequest[k] != null);
|
|
136
|
+
log(`[Sidecar] ${reqId} request detail: bodyLen=${(body || "").length} messages=${messages.length} roles=${roleSeq} lastRole=${lastRole} lastContentLen=${lastContentLen} model=${openaiRequest.model ?? "-"} stream=${!!openaiRequest.stream} extraKeys=[${topKeys.join(",")}]`);
|
|
126
137
|
const rawProvider = req.headers["x-memoryx-real-provider"]?.trim() || "";
|
|
127
138
|
const modelStr = openaiRequest.model || "";
|
|
128
139
|
let provider = rawProvider || (modelStr.includes("/") ? modelStr.split("/")[0] : "") || "";
|
|
129
|
-
if (provider === "memoryx-
|
|
130
|
-
const real = this.availableProviders.find((p) => p.provider !== "memoryx-
|
|
140
|
+
if (provider === "memoryx-gateway") {
|
|
141
|
+
const real = this.availableProviders.find((p) => p.provider !== "memoryx-gateway");
|
|
131
142
|
provider = real ? real.provider : "";
|
|
132
143
|
}
|
|
133
144
|
if (!provider || !this.credentials.has(provider)) {
|
|
@@ -135,7 +146,7 @@ export class SidecarServer {
|
|
|
135
146
|
}
|
|
136
147
|
if (!provider || !this.credentials.has(provider)) {
|
|
137
148
|
for (const [id] of this.credentials) {
|
|
138
|
-
if (id !== "memoryx-
|
|
149
|
+
if (id !== "memoryx-gateway") {
|
|
139
150
|
provider = id;
|
|
140
151
|
break;
|
|
141
152
|
}
|
|
@@ -144,7 +155,7 @@ export class SidecarServer {
|
|
|
144
155
|
let creds = provider ? this.credentials.get(provider) : undefined;
|
|
145
156
|
if (!creds?.baseUrl?.trim() || !(creds.apiKey ?? "").trim()) {
|
|
146
157
|
for (const [id, c] of this.credentials) {
|
|
147
|
-
if (id !== "memoryx-
|
|
158
|
+
if (id !== "memoryx-gateway" && (c.baseUrl || "").trim() && (c.apiKey ?? "").trim()) {
|
|
148
159
|
provider = id;
|
|
149
160
|
creds = c;
|
|
150
161
|
break;
|
|
@@ -162,7 +173,7 @@ export class SidecarServer {
|
|
|
162
173
|
const pathFromOpenClaw = (url.split("?")[0] || "/").trim() || "/";
|
|
163
174
|
const targetUrl = base + (pathFromOpenClaw.startsWith("/") ? pathFromOpenClaw : "/" + pathFromOpenClaw);
|
|
164
175
|
const currentModel = openaiRequest.model || "";
|
|
165
|
-
if (!currentModel || currentModel === "auto" || currentModel.startsWith("memoryx-
|
|
176
|
+
if (!currentModel || currentModel === "auto" || currentModel.startsWith("memoryx-gateway/")) {
|
|
166
177
|
const firstId = creds?.models?.[0]?.id || creds?.models?.[0]?.name;
|
|
167
178
|
if (firstId)
|
|
168
179
|
openaiRequest.model = firstId;
|
|
@@ -176,14 +187,14 @@ export class SidecarServer {
|
|
|
176
187
|
agentId = accountInfo.agentId || "";
|
|
177
188
|
}
|
|
178
189
|
catch (_e) {
|
|
179
|
-
/* Do not block; proxy
|
|
190
|
+
/* Do not block; proxy may still accept request */
|
|
180
191
|
}
|
|
181
192
|
const headers = this.buildForwardHeaders(provider, apiKey);
|
|
182
|
-
const messages = openaiRequest.messages || [];
|
|
183
193
|
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
184
194
|
const searchQuery = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "";
|
|
185
195
|
const proxyRequestBody = {
|
|
186
196
|
targetUrl,
|
|
197
|
+
api_base: base,
|
|
187
198
|
headers,
|
|
188
199
|
body: openaiRequest,
|
|
189
200
|
searchQuery,
|
|
@@ -212,9 +223,24 @@ export class SidecarServer {
|
|
|
212
223
|
}
|
|
213
224
|
const proxyFailed = !proxyResponse || proxyResponse.status >= 500;
|
|
214
225
|
if (proxyFailed) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
226
|
+
if (proxyResponse?.body) {
|
|
227
|
+
try {
|
|
228
|
+
if (typeof proxyResponse.body.cancel === "function") {
|
|
229
|
+
await proxyResponse.body.cancel();
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
await proxyResponse.text();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (_) {
|
|
236
|
+
/* drain failed, ignore */
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
log(`[Sidecar] ${reqId} Proxy failed, returning error (no local fallback)`, { console: true });
|
|
240
|
+
const status = proxyResponse?.status ?? 502;
|
|
241
|
+
const detail = proxyResponse?.statusText || "MemoryX proxy unavailable";
|
|
242
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
243
|
+
res.end(JSON.stringify({ error: detail }));
|
|
218
244
|
return;
|
|
219
245
|
}
|
|
220
246
|
const proxy = proxyResponse;
|
|
@@ -229,11 +255,11 @@ export class SidecarServer {
|
|
|
229
255
|
}
|
|
230
256
|
catch (firstErr) {
|
|
231
257
|
reader.releaseLock();
|
|
232
|
-
log(`[Sidecar] ${reqId} Proxy stream first chunk failed: ${firstErr?.message ?? "timeout"}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
258
|
+
log(`[Sidecar] ${reqId} Proxy stream first chunk failed: ${firstErr?.message ?? "timeout"}`, {
|
|
259
|
+
console: true,
|
|
260
|
+
});
|
|
261
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
262
|
+
res.end(JSON.stringify({ error: "MemoryX proxy stream failed" }));
|
|
237
263
|
return;
|
|
238
264
|
}
|
|
239
265
|
res.writeHead(proxy.status, {
|
|
@@ -270,47 +296,6 @@ export class SidecarServer {
|
|
|
270
296
|
});
|
|
271
297
|
res.end(text);
|
|
272
298
|
}
|
|
273
|
-
async directRequest(reqId, targetUrl, headers, openaiRequest) {
|
|
274
|
-
log(`[Sidecar] ${reqId} Direct POST ${targetUrl} headers=${JSON.stringify(this.redactHeaders(headers))} model=${openaiRequest?.model} stream=${!!openaiRequest?.stream}`);
|
|
275
|
-
try {
|
|
276
|
-
const resp = await fetch(targetUrl, {
|
|
277
|
-
method: "POST",
|
|
278
|
-
headers,
|
|
279
|
-
body: JSON.stringify(openaiRequest),
|
|
280
|
-
});
|
|
281
|
-
log(`[Sidecar] ${reqId} Direct response status=${resp.status} ${resp.statusText}`);
|
|
282
|
-
return resp;
|
|
283
|
-
}
|
|
284
|
-
catch (e) {
|
|
285
|
-
log(`[Sidecar] ${reqId} Direct fetch error: ${e?.message ?? String(e)}`, { console: true });
|
|
286
|
-
return new Response(JSON.stringify({ error: e?.message || "Direct request failed" }), {
|
|
287
|
-
status: 502,
|
|
288
|
-
headers: { "Content-Type": "application/json" },
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
async writeResponse(res, directResponse) {
|
|
293
|
-
const contentType = directResponse.headers.get("content-type") || "application/json";
|
|
294
|
-
res.writeHead(directResponse.status, { "Content-Type": contentType });
|
|
295
|
-
const body = directResponse.body;
|
|
296
|
-
if (!body) {
|
|
297
|
-
res.end();
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
const reader = body.getReader();
|
|
301
|
-
try {
|
|
302
|
-
while (true) {
|
|
303
|
-
const { done, value } = await reader.read();
|
|
304
|
-
if (done)
|
|
305
|
-
break;
|
|
306
|
-
res.write(Buffer.from(value));
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
finally {
|
|
310
|
-
reader.releaseLock();
|
|
311
|
-
}
|
|
312
|
-
res.end();
|
|
313
|
-
}
|
|
314
299
|
readBody(req) {
|
|
315
300
|
return new Promise((resolve, reject) => {
|
|
316
301
|
const chunks = [];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 与服务端 llm_proxy 一致的厂商请求体规范化。
|
|
3
|
+
* 部分 LLM 厂商非标准 OpenAI 格式,需在此做兼容后再转发;服务端用 LiteLLM,Sidecar 直连时用本模块保证同一套逻辑。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 判断是否为 Z.AI GLM-5:按 provider 或 targetUrl 与 model 判断。
|
|
7
|
+
*/
|
|
8
|
+
export declare function isZaiGlm5(provider: string, targetUrl: string, model: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* 按厂商与 URL/model 规范化请求体;与服务端逻辑一致,供 Sidecar 直连时使用。
|
|
11
|
+
* 返回规范化后的 body(不修改入参)。
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeBodyForVendor(provider: string, targetUrl: string, body: Record<string, unknown>): Record<string, unknown>;
|
|
14
|
+
//# sourceMappingURL=vendor-normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vendor-normalize.d.ts","sourceRoot":"","sources":["../src/vendor-normalize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAQrF;AA4DD;;;GAGG;AACH,wBAAgB,sBAAsB,CAClC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 与服务端 llm_proxy 一致的厂商请求体规范化。
|
|
3
|
+
* 部分 LLM 厂商非标准 OpenAI 格式,需在此做兼容后再转发;服务端用 LiteLLM,Sidecar 直连时用本模块保证同一套逻辑。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 判断是否为 Z.AI GLM-5:按 provider 或 targetUrl 与 model 判断。
|
|
7
|
+
*/
|
|
8
|
+
export function isZaiGlm5(provider, targetUrl, model) {
|
|
9
|
+
const prov = (provider || "").toLowerCase();
|
|
10
|
+
const url = (targetUrl || "").toLowerCase();
|
|
11
|
+
const m = (model || "").toLowerCase();
|
|
12
|
+
if (prov === "zai" && (m === "glm-5" || m.endsWith("/glm-5")))
|
|
13
|
+
return true;
|
|
14
|
+
if ((url.includes("open.bigmodel.cn") || url.includes("api.z.ai")) && (m === "glm-5" || m.endsWith("/glm-5")))
|
|
15
|
+
return true;
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Z.AI GLM-5 兼容:与 api/app/routers/llm_proxy.py _normalize_body_for_zai_glm5 保持一致。
|
|
20
|
+
* - messages 仅保留 role + content,role 仅允许 system/user/assistant
|
|
21
|
+
* - 顶层 system 并入 messages 首条 role=system
|
|
22
|
+
* - OpenClaw 的 developer 视为 system,其他非法 role 归为 user
|
|
23
|
+
* - 可选 thinking:未传时默认 {"type": "enabled"}
|
|
24
|
+
*/
|
|
25
|
+
function normalizeBodyForZaiGlm5(body) {
|
|
26
|
+
const out = { ...body };
|
|
27
|
+
delete out.system;
|
|
28
|
+
const rawMessages = Array.isArray(out.messages) ? [...out.messages] : [];
|
|
29
|
+
let systemContent = (typeof body.system === "string" ? body.system : "").trim();
|
|
30
|
+
if (systemContent) {
|
|
31
|
+
const existingSystem = rawMessages.find((m) => (String(m?.role || "").toLowerCase() === "system"));
|
|
32
|
+
if (existingSystem && typeof existingSystem.content === "string") {
|
|
33
|
+
systemContent = `${existingSystem.content}\n\n${systemContent}`.trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const withoutSystem = rawMessages.filter((m) => String(m?.role || "").toLowerCase() !== "system");
|
|
37
|
+
const ROLE_MAP = { developer: "system" };
|
|
38
|
+
const messages = [];
|
|
39
|
+
for (const m of withoutSystem) {
|
|
40
|
+
let r = String(m.role || "").trim().toLowerCase();
|
|
41
|
+
if (r === "system" || r === "user" || r === "assistant") {
|
|
42
|
+
// keep
|
|
43
|
+
}
|
|
44
|
+
else if (ROLE_MAP[r]) {
|
|
45
|
+
r = ROLE_MAP[r];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
r = "user";
|
|
49
|
+
}
|
|
50
|
+
let content = m.content;
|
|
51
|
+
if (content == null)
|
|
52
|
+
content = "";
|
|
53
|
+
if (Array.isArray(content)) {
|
|
54
|
+
content = content
|
|
55
|
+
.map((part) => typeof part === "object" && part != null && "text" in part
|
|
56
|
+
? String(part.text)
|
|
57
|
+
: String(part))
|
|
58
|
+
.join("\n");
|
|
59
|
+
}
|
|
60
|
+
const contentStr = String(content).trim();
|
|
61
|
+
messages.push({ role: r, content: contentStr || " " });
|
|
62
|
+
}
|
|
63
|
+
if (systemContent) {
|
|
64
|
+
messages.unshift({ role: "system", content: systemContent });
|
|
65
|
+
}
|
|
66
|
+
out.messages = messages;
|
|
67
|
+
if (!("thinking" in out) || out.thinking == null) {
|
|
68
|
+
out.thinking = { type: "enabled" };
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 按厂商与 URL/model 规范化请求体;与服务端逻辑一致,供 Sidecar 直连时使用。
|
|
74
|
+
* 返回规范化后的 body(不修改入参)。
|
|
75
|
+
*/
|
|
76
|
+
export function normalizeBodyForVendor(provider, targetUrl, body) {
|
|
77
|
+
const model = String(body?.model || "").trim();
|
|
78
|
+
if (isZaiGlm5(provider, targetUrl, model)) {
|
|
79
|
+
return normalizeBodyForZaiGlm5(body);
|
|
80
|
+
}
|
|
81
|
+
return body;
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t0ken.ai/memoryx-openclaw-plugin",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.62",
|
|
4
4
|
"description": "MemoryX real-time memory capture and recall plugin for OpenClaw (powered by @t0ken.ai/memoryx-sdk)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "MemoryX Team",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"prebuild": "node scripts/update-version.cjs",
|
|
16
16
|
"build": "tsc",
|
|
17
17
|
"test:recall": "node scripts/test-recall-capture.mjs",
|
|
18
|
-
"reset-
|
|
18
|
+
"reset-gateway-usage": "node scripts/reset-memoryx-gateway-usage.mjs"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
21
21
|
"openclaw",
|