@opencompress/opencompress 1.1.0 → 1.3.0
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/index.js +107 -35
- package/openclaw.security.json +72 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -34,12 +34,18 @@ var OPENCOMPRESS_MODELS = [
|
|
|
34
34
|
// Mistral
|
|
35
35
|
{ id: "mistralai/mistral-large-2411", name: "Mistral Large (Compressed)", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 }
|
|
36
36
|
];
|
|
37
|
-
function buildProviderModels(baseUrl) {
|
|
38
|
-
|
|
37
|
+
function buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl) {
|
|
38
|
+
const config = {
|
|
39
39
|
baseUrl: `${baseUrl}/v1`,
|
|
40
40
|
api: "openai-completions",
|
|
41
41
|
models: OPENCOMPRESS_MODELS
|
|
42
42
|
};
|
|
43
|
+
if (upstreamKey || upstreamBaseUrl) {
|
|
44
|
+
config.headers = {};
|
|
45
|
+
if (upstreamKey) config.headers["X-Upstream-Key"] = upstreamKey;
|
|
46
|
+
if (upstreamBaseUrl) config.headers["X-Upstream-Base-Url"] = upstreamBaseUrl;
|
|
47
|
+
}
|
|
48
|
+
return config;
|
|
43
49
|
}
|
|
44
50
|
var opencompressProvider = {
|
|
45
51
|
id: "opencompress",
|
|
@@ -53,18 +59,41 @@ var opencompressProvider = {
|
|
|
53
59
|
{
|
|
54
60
|
id: "api-key",
|
|
55
61
|
label: "OpenCompress",
|
|
56
|
-
hint: "
|
|
62
|
+
hint: "Connect your LLM key \u2014 compress every call, save 40-70%",
|
|
57
63
|
kind: "custom",
|
|
58
64
|
run: async (ctx) => {
|
|
59
65
|
ctx.prompter.note(
|
|
60
|
-
"OpenCompress compresses all LLM prompts automatically.\n53% fewer tokens, 62% faster, 96% quality preserved.\
|
|
66
|
+
"OpenCompress compresses all LLM prompts automatically.\n53% fewer tokens, 62% faster, 96% quality preserved.\n\nConnect your existing LLM API key to get started.\nSupported: OpenAI, Anthropic, OpenRouter, Google"
|
|
61
67
|
);
|
|
68
|
+
const llmKey = await ctx.prompter.text({
|
|
69
|
+
message: "Enter your LLM API key (OpenAI/Anthropic/OpenRouter):",
|
|
70
|
+
validate: (val) => {
|
|
71
|
+
if (!val || val.length < 10) return "Please enter a valid API key";
|
|
72
|
+
if (val.startsWith("sk-occ-")) return "Enter your LLM provider key, not an OpenCompress key";
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
if (typeof llmKey === "symbol") {
|
|
77
|
+
throw new Error("Setup cancelled");
|
|
78
|
+
}
|
|
79
|
+
let provider = "openrouter";
|
|
80
|
+
let upstreamBaseUrl = "https://openrouter.ai/api/v1";
|
|
81
|
+
if (llmKey.startsWith("sk-proj-") || llmKey.startsWith("sk-") && !llmKey.startsWith("sk-ant-") && !llmKey.startsWith("sk-or-")) {
|
|
82
|
+
provider = "openai";
|
|
83
|
+
upstreamBaseUrl = "https://api.openai.com/v1";
|
|
84
|
+
} else if (llmKey.startsWith("sk-ant-")) {
|
|
85
|
+
provider = "anthropic";
|
|
86
|
+
upstreamBaseUrl = "https://api.anthropic.com/v1";
|
|
87
|
+
} else if (llmKey.startsWith("AIza")) {
|
|
88
|
+
provider = "google";
|
|
89
|
+
upstreamBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai";
|
|
90
|
+
}
|
|
62
91
|
const spinner = ctx.prompter.progress("Creating account...");
|
|
63
92
|
try {
|
|
64
93
|
const res = await fetch(`${DEFAULT_BASE_URL}/v1/provision`, {
|
|
65
94
|
method: "POST",
|
|
66
95
|
headers: { "Content-Type": "application/json" },
|
|
67
|
-
body:
|
|
96
|
+
body: JSON.stringify({ upstreamApiKey: llmKey })
|
|
68
97
|
});
|
|
69
98
|
if (!res.ok) {
|
|
70
99
|
const err = await res.json().catch(() => ({ error: { message: "Unknown error" } }));
|
|
@@ -75,6 +104,17 @@ var opencompressProvider = {
|
|
|
75
104
|
}
|
|
76
105
|
const data = await res.json();
|
|
77
106
|
spinner.stop("Account created");
|
|
107
|
+
try {
|
|
108
|
+
await fetch(`${DEFAULT_BASE_URL}/v1/byok`, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: {
|
|
111
|
+
Authorization: `Bearer ${data.apiKey}`,
|
|
112
|
+
"Content-Type": "application/json"
|
|
113
|
+
},
|
|
114
|
+
body: JSON.stringify({ provider, passthrough: true })
|
|
115
|
+
});
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
78
118
|
return {
|
|
79
119
|
profiles: [
|
|
80
120
|
{
|
|
@@ -82,10 +122,18 @@ var opencompressProvider = {
|
|
|
82
122
|
credential: { apiKey: data.apiKey }
|
|
83
123
|
}
|
|
84
124
|
],
|
|
125
|
+
configPatch: {
|
|
126
|
+
models: {
|
|
127
|
+
providers: {
|
|
128
|
+
opencompress: buildProviderModels(DEFAULT_BASE_URL, llmKey, upstreamBaseUrl)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
85
132
|
defaultModel: "gpt-4o-mini",
|
|
86
133
|
notes: [
|
|
87
|
-
|
|
88
|
-
|
|
134
|
+
`OpenCompress is ready! Connected to ${provider}.`,
|
|
135
|
+
"Your LLM key is stored locally only \u2014 never on our server.",
|
|
136
|
+
`Free credit: ${data.freeCredit}. Dashboard: opencompress.ai/dashboard`
|
|
89
137
|
]
|
|
90
138
|
};
|
|
91
139
|
} catch (err) {
|
|
@@ -103,9 +151,11 @@ var plugin = {
|
|
|
103
151
|
version: VERSION,
|
|
104
152
|
async register(api) {
|
|
105
153
|
const baseUrl = api.pluginConfig?.baseUrl || DEFAULT_BASE_URL;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
154
|
+
const existingHeaders = api.config.models?.providers?.opencompress?.headers;
|
|
155
|
+
const existingUpstreamKey = existingHeaders?.["X-Upstream-Key"];
|
|
156
|
+
const existingUpstreamBaseUrl = existingHeaders?.["X-Upstream-Base-Url"];
|
|
157
|
+
const providerModels = buildProviderModels(baseUrl, existingUpstreamKey, existingUpstreamBaseUrl);
|
|
158
|
+
opencompressProvider.models = providerModels;
|
|
109
159
|
api.registerProvider(opencompressProvider);
|
|
110
160
|
if (!api.config.models) {
|
|
111
161
|
api.config.models = { providers: {} };
|
|
@@ -113,7 +163,7 @@ var plugin = {
|
|
|
113
163
|
if (!api.config.models.providers) {
|
|
114
164
|
api.config.models.providers = {};
|
|
115
165
|
}
|
|
116
|
-
api.config.models.providers.opencompress =
|
|
166
|
+
api.config.models.providers.opencompress = providerModels;
|
|
117
167
|
api.logger.info("OpenCompress provider registered (20 models, 5-layer compression)");
|
|
118
168
|
api.registerCommand({
|
|
119
169
|
name: "compress-stats",
|
|
@@ -175,10 +225,10 @@ var plugin = {
|
|
|
175
225
|
}
|
|
176
226
|
const upstreamKey = ctx.args?.trim();
|
|
177
227
|
if (!upstreamKey) {
|
|
178
|
-
const
|
|
228
|
+
const res = await fetch(`${baseUrl}/v1/topup`, {
|
|
179
229
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
180
230
|
});
|
|
181
|
-
const
|
|
231
|
+
const data = res.ok ? await res.json() : null;
|
|
182
232
|
return {
|
|
183
233
|
text: [
|
|
184
234
|
"**BYOK (Bring Your Own Key)**",
|
|
@@ -191,19 +241,23 @@ var plugin = {
|
|
|
191
241
|
" `/compress-byok sk-or-xxx` \u2014 Connect OpenRouter key",
|
|
192
242
|
" `/compress-byok off` \u2014 Switch back to router mode",
|
|
193
243
|
"",
|
|
194
|
-
|
|
244
|
+
data ? `**Balance:** $${data.balance.toFixed(2)}` : ""
|
|
195
245
|
].join("\n")
|
|
196
246
|
};
|
|
197
247
|
}
|
|
198
248
|
if (upstreamKey === "off" || upstreamKey === "disable" || upstreamKey === "router") {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
249
|
+
const cleanModels = buildProviderModels(baseUrl);
|
|
250
|
+
if (api.config.models?.providers) {
|
|
251
|
+
api.config.models.providers.opencompress = cleanModels;
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
await fetch(`${baseUrl}/v1/byok`, {
|
|
255
|
+
method: "DELETE",
|
|
256
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
257
|
+
});
|
|
258
|
+
} catch {
|
|
205
259
|
}
|
|
206
|
-
return { text: "Switched back to **router mode**.
|
|
260
|
+
return { text: "Switched back to **router mode**. Your upstream key has been removed from local config." };
|
|
207
261
|
}
|
|
208
262
|
if (upstreamKey.startsWith("sk-occ-")) {
|
|
209
263
|
return { text: "That's an OpenCompress key. Provide your LLM provider key (OpenAI, Anthropic, etc.)." };
|
|
@@ -211,24 +265,42 @@ var plugin = {
|
|
|
211
265
|
if (upstreamKey.length < 10) {
|
|
212
266
|
return { text: "Key looks too short. Provide your full LLM API key." };
|
|
213
267
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
268
|
+
let provider = "unknown";
|
|
269
|
+
let upstreamBaseUrl = "";
|
|
270
|
+
if (upstreamKey.startsWith("sk-proj-") || upstreamKey.startsWith("sk-") && !upstreamKey.startsWith("sk-ant-") && !upstreamKey.startsWith("sk-or-")) {
|
|
271
|
+
provider = "openai";
|
|
272
|
+
upstreamBaseUrl = "https://api.openai.com/v1";
|
|
273
|
+
} else if (upstreamKey.startsWith("sk-ant-")) {
|
|
274
|
+
provider = "anthropic";
|
|
275
|
+
upstreamBaseUrl = "https://api.anthropic.com/v1";
|
|
276
|
+
} else if (upstreamKey.startsWith("sk-or-")) {
|
|
277
|
+
provider = "openrouter";
|
|
278
|
+
upstreamBaseUrl = "https://openrouter.ai/api/v1";
|
|
279
|
+
} else if (upstreamKey.startsWith("AIza")) {
|
|
280
|
+
provider = "google";
|
|
281
|
+
upstreamBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai";
|
|
282
|
+
}
|
|
283
|
+
const updatedModels = buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl);
|
|
284
|
+
if (api.config.models?.providers) {
|
|
285
|
+
api.config.models.providers.opencompress = updatedModels;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
await fetch(`${baseUrl}/v1/byok`, {
|
|
289
|
+
method: "POST",
|
|
290
|
+
headers: {
|
|
291
|
+
Authorization: `Bearer ${apiKey}`,
|
|
292
|
+
"Content-Type": "application/json"
|
|
293
|
+
},
|
|
294
|
+
body: JSON.stringify({ provider, passthrough: true })
|
|
295
|
+
});
|
|
296
|
+
} catch {
|
|
225
297
|
}
|
|
226
|
-
const data = await res.json();
|
|
227
298
|
return {
|
|
228
299
|
text: [
|
|
229
|
-
`Switched to **BYOK mode** (${
|
|
300
|
+
`Switched to **BYOK mode** (${provider}).`,
|
|
230
301
|
"",
|
|
231
|
-
|
|
302
|
+
"Your key is stored **locally only** \u2014 never sent to our server for storage.",
|
|
303
|
+
"It's passed through on each request via header and discarded immediately.",
|
|
232
304
|
"",
|
|
233
305
|
"To switch back: `/compress-byok off`"
|
|
234
306
|
].join("\n")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "opencompress",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"securityProfile": {
|
|
5
|
+
"dataHandling": {
|
|
6
|
+
"promptContent": "pass-through",
|
|
7
|
+
"promptStorage": "none",
|
|
8
|
+
"responseContent": "pass-through",
|
|
9
|
+
"responseStorage": "none",
|
|
10
|
+
"upstreamApiKeys": "local-only",
|
|
11
|
+
"upstreamApiKeyStorage": "never-server-side"
|
|
12
|
+
},
|
|
13
|
+
"networkAccess": {
|
|
14
|
+
"outbound": [
|
|
15
|
+
{
|
|
16
|
+
"host": "www.opencompress.ai",
|
|
17
|
+
"port": 443,
|
|
18
|
+
"protocol": "https",
|
|
19
|
+
"purpose": "API proxy — compress prompts and forward to upstream LLM provider"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"inbound": []
|
|
23
|
+
},
|
|
24
|
+
"localStorage": {
|
|
25
|
+
"authProfiles": {
|
|
26
|
+
"path": "~/.openclaw/agents/*/agent/auth-profiles.json",
|
|
27
|
+
"content": "OpenCompress API key (sk-occ-*)",
|
|
28
|
+
"sensitive": true
|
|
29
|
+
},
|
|
30
|
+
"providerConfig": {
|
|
31
|
+
"path": "~/.openclaw/openclaw.json",
|
|
32
|
+
"content": "Provider config, optional upstream key in headers (BYOK pass-through)",
|
|
33
|
+
"sensitive": true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"secrets": {
|
|
37
|
+
"required": [
|
|
38
|
+
{
|
|
39
|
+
"name": "OpenCompress API Key",
|
|
40
|
+
"format": "sk-occ-*",
|
|
41
|
+
"purpose": "Authentication and billing for compression service",
|
|
42
|
+
"serverSideStorage": "hashed (SHA-256), never plaintext"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"optional": [
|
|
46
|
+
{
|
|
47
|
+
"name": "Upstream LLM API Key",
|
|
48
|
+
"format": "sk-proj-* / sk-ant-* / sk-or-* / AIza*",
|
|
49
|
+
"purpose": "BYOK mode — forwarded to user's LLM provider",
|
|
50
|
+
"serverSideStorage": "NEVER stored. Passed via X-Upstream-Key header per-request, discarded after forwarding."
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"permissions": {
|
|
55
|
+
"fileSystem": "read/write ~/.openclaw/ config files only",
|
|
56
|
+
"environment": ["OPENCOMPRESS_API_KEY", "OPENCOMPRESS_LLM_KEY"],
|
|
57
|
+
"shell": "none",
|
|
58
|
+
"network": "outbound HTTPS to www.opencompress.ai only"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"privacyPolicy": {
|
|
62
|
+
"promptsAndResponses": "OpenCompress compresses prompts in-memory and forwards to the upstream LLM provider. Prompt and response content is NEVER stored, logged, or used for training. Only token counts are recorded for billing.",
|
|
63
|
+
"apiKeys": "Your upstream LLM API key (OpenAI, Anthropic, etc.) is stored ONLY on your local machine. It is sent to our server as a per-request header (X-Upstream-Key) and discarded immediately after forwarding. We never persist your upstream keys.",
|
|
64
|
+
"billing": "We record: timestamp, model, original token count, compressed token count, cost. We do NOT record prompt content.",
|
|
65
|
+
"thirdParties": "We do not share any data with third parties. Your requests are forwarded only to the LLM provider you specify."
|
|
66
|
+
},
|
|
67
|
+
"auditability": {
|
|
68
|
+
"pluginSource": "Open source — https://github.com/claw-compactor/openclaw-plugin",
|
|
69
|
+
"serverSource": "Forwarding logic documented in security docs. Core proxy is open for audit.",
|
|
70
|
+
"verification": "Users can inspect all network requests via OpenClaw's diagnostics plugin or standard proxy tools."
|
|
71
|
+
}
|
|
72
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencompress/opencompress",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "OpenCompress plugin for OpenClaw — automatic 5-layer prompt compression for any LLM",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
-
"openclaw.plugin.json"
|
|
21
|
+
"openclaw.plugin.json",
|
|
22
|
+
"openclaw.security.json"
|
|
22
23
|
],
|
|
23
24
|
"keywords": [
|
|
24
25
|
"openclaw",
|