@opencompress/opencompress 1.6.7 → 1.8.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.
Files changed (2) hide show
  1. package/dist/index.js +223 -350
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/index.ts
9
- var VERSION = "1.6.7";
9
+ var VERSION = "1.8.0";
10
10
  var DEFAULT_BASE_URL = "https://www.opencompress.ai/api";
11
11
  function getApiKey(api) {
12
12
  const auth = api.config.auth;
@@ -35,96 +35,7 @@ function getApiKey(api) {
35
35
  }
36
36
  return void 0;
37
37
  }
38
- var FALLBACK_MODELS = [
39
- { id: "gpt-4o", name: "GPT-4o", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128e3, maxTokens: 16384 },
40
- { id: "gpt-4o-mini", name: "GPT-4o Mini", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128e3, maxTokens: 16384 },
41
- { id: "gpt-4.1", name: "GPT-4.1", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1047576, maxTokens: 32768 },
42
- { id: "gpt-4.1-mini", name: "GPT-4.1 Mini", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1047576, maxTokens: 32768 },
43
- { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 128e3 },
44
- { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 128e3 },
45
- { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
46
- { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
47
- { id: "deepseek/deepseek-chat-v3-0324", name: "DeepSeek V3", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 },
48
- { id: "deepseek/deepseek-reasoner", name: "DeepSeek Reasoner", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 }
49
- ];
50
- function readExistingModels(api) {
51
- const providers = api.config.models?.providers;
52
- if (!providers) return null;
53
- const seen = /* @__PURE__ */ new Set();
54
- const models = [];
55
- for (const [providerId, providerConfig] of Object.entries(providers)) {
56
- if (providerId === "opencompress") continue;
57
- const providerModels = providerConfig.models || [];
58
- for (const m of providerModels) {
59
- if (m.name?.includes("\u2192")) continue;
60
- const rawId = m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id;
61
- if (seen.has(rawId)) continue;
62
- seen.add(rawId);
63
- const upstreamId = m.id.startsWith(`${providerId}/`) ? rawId : m.id;
64
- models.push({
65
- ...m,
66
- id: upstreamId,
67
- name: m.name || upstreamId,
68
- // Zero out cost — billing handled by OpenCompress proxy
69
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
70
- });
71
- }
72
- }
73
- return models.length > 0 ? models : null;
74
- }
75
- function buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl, models, apiKey) {
76
- const config = {
77
- baseUrl: `${baseUrl}/v1`,
78
- api: "openai-completions",
79
- apiKey: apiKey || void 0,
80
- models: models || FALLBACK_MODELS
81
- };
82
- if (upstreamKey || upstreamBaseUrl) {
83
- config.headers = {};
84
- if (upstreamKey) config.headers["X-Upstream-Key"] = upstreamKey;
85
- if (upstreamBaseUrl) config.headers["X-Upstream-Base-Url"] = upstreamBaseUrl;
86
- }
87
- return config;
88
- }
89
- function persistModelsConfig(providerModels) {
90
- try {
91
- const os = __require("os");
92
- const fs = __require("fs");
93
- const path = __require("path");
94
- const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
95
- if (!fs.existsSync(configPath)) return;
96
- const raw = fs.readFileSync(configPath, "utf-8");
97
- let config;
98
- try {
99
- config = JSON.parse(raw);
100
- } catch {
101
- return;
102
- }
103
- if (!config.models) config.models = {};
104
- const models = config.models;
105
- if (!models.providers) models.providers = {};
106
- const providers = models.providers;
107
- const configSafeModels = providerModels.models.map((m) => ({
108
- id: m.id,
109
- name: m.name
110
- }));
111
- const configEntry = {
112
- baseUrl: providerModels.baseUrl,
113
- api: providerModels.api || "openai-completions",
114
- models: configSafeModels
115
- };
116
- if (providerModels.apiKey) {
117
- configEntry.apiKey = providerModels.apiKey;
118
- }
119
- if (providerModels.headers) {
120
- configEntry.headers = providerModels.headers;
121
- }
122
- providers.opencompress = configEntry;
123
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
124
- } catch {
125
- }
126
- }
127
- function persistAgentModelsJson(providerModels) {
38
+ function persistAuthProfile(apiKey) {
128
39
  try {
129
40
  const os = __require("os");
130
41
  const fs = __require("fs");
@@ -133,37 +44,27 @@ function persistAgentModelsJson(providerModels) {
133
44
  if (!fs.existsSync(agentsDir)) return;
134
45
  const agentDirs = fs.readdirSync(agentsDir);
135
46
  for (const agent of agentDirs) {
136
- const modelsPath = path.join(agentsDir, agent, "agent", "models.json");
137
- const modelsDir = path.dirname(modelsPath);
138
- if (!fs.existsSync(modelsDir)) continue;
139
- let data = { providers: {} };
140
- if (fs.existsSync(modelsPath)) {
47
+ const authPath = path.join(agentsDir, agent, "agent", "auth-profiles.json");
48
+ const authDir = path.dirname(authPath);
49
+ if (!fs.existsSync(authDir)) {
50
+ fs.mkdirSync(authDir, { recursive: true });
51
+ }
52
+ let profiles = {
53
+ version: 1,
54
+ profiles: {}
55
+ };
56
+ if (fs.existsSync(authPath)) {
141
57
  try {
142
- data = JSON.parse(fs.readFileSync(modelsPath, "utf-8"));
143
- if (!data.providers || typeof data.providers !== "object") {
144
- data.providers = {};
145
- }
58
+ profiles = JSON.parse(fs.readFileSync(authPath, "utf-8"));
146
59
  } catch {
147
- data = { providers: {} };
148
60
  }
149
61
  }
150
- data.providers.opencompress = {
151
- baseUrl: providerModels.baseUrl,
152
- api: providerModels.api || "openai-completions",
153
- apiKey: providerModels.apiKey || void 0,
154
- models: providerModels.models.map((m) => ({
155
- id: m.id,
156
- name: m.name,
157
- api: m.api || "openai-completions",
158
- reasoning: m.reasoning ?? false,
159
- input: m.input || ["text"],
160
- cost: m.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
161
- contextWindow: m.contextWindow || 2e5,
162
- maxTokens: m.maxTokens || 8192
163
- })),
164
- ...providerModels.headers ? { headers: providerModels.headers } : {}
62
+ profiles.profiles["opencompress:default"] = {
63
+ type: "api_key",
64
+ provider: "opencompress",
65
+ key: apiKey
165
66
  };
166
- fs.writeFileSync(modelsPath, JSON.stringify(data, null, 2) + "\n");
67
+ fs.writeFileSync(authPath, JSON.stringify(profiles, null, 2) + "\n");
167
68
  }
168
69
  } catch {
169
70
  }
@@ -197,125 +98,181 @@ function persistAgentAuthJson(apiKey) {
197
98
  } catch {
198
99
  }
199
100
  }
200
- function injectModelsAllowlist(models) {
101
+ function proxyStatePath() {
102
+ const os = __require("os");
103
+ const path = __require("path");
104
+ return path.join(os.homedir(), ".openclaw", "opencompress-proxy.json");
105
+ }
106
+ function readProxyState() {
201
107
  try {
202
- const os = __require("os");
203
108
  const fs = __require("fs");
204
- const path = __require("path");
205
- const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
206
- if (!fs.existsSync(configPath)) return;
207
- const raw = fs.readFileSync(configPath, "utf-8");
208
- let config;
209
- try {
210
- config = JSON.parse(raw);
211
- } catch {
212
- return;
213
- }
214
- if (!config.agents) config.agents = {};
215
- const agents = config.agents;
216
- if (!agents.defaults) agents.defaults = {};
217
- const defaults = agents.defaults;
218
- if (!defaults.models) defaults.models = {};
219
- const allowlist = defaults.models;
220
- const existingKeys = Object.keys(allowlist);
221
- if (existingKeys.length === 0) return;
222
- let changed = false;
223
- for (const m of models) {
224
- const fullId = `opencompress/${m.id}`;
225
- if (!allowlist[fullId]) {
226
- allowlist[fullId] = {};
227
- changed = true;
228
- }
229
- }
230
- if (changed) {
231
- const tmpPath = `${configPath}.tmp.${process.pid}`;
232
- fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + "\n");
233
- fs.renameSync(tmpPath, configPath);
234
- }
109
+ const p = proxyStatePath();
110
+ if (!fs.existsSync(p)) return { enabled: false, originals: {} };
111
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
235
112
  } catch {
113
+ return { enabled: false, originals: {} };
236
114
  }
237
115
  }
238
- function persistAuthProfile(apiKey) {
116
+ function writeProxyState(state) {
239
117
  try {
240
- const os = __require("os");
241
118
  const fs = __require("fs");
242
- const path = __require("path");
119
+ fs.writeFileSync(proxyStatePath(), JSON.stringify(state, null, 2) + "\n");
120
+ } catch {
121
+ }
122
+ }
123
+ function persistProviderToDisk(providerId, config) {
124
+ const os = __require("os");
125
+ const fs = __require("fs");
126
+ const path = __require("path");
127
+ try {
128
+ const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
129
+ if (fs.existsSync(configPath)) {
130
+ const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
131
+ if (!raw.models) raw.models = {};
132
+ if (!raw.models.providers) raw.models.providers = {};
133
+ raw.models.providers[providerId] = {
134
+ baseUrl: config.baseUrl,
135
+ api: config.api || "openai-completions",
136
+ apiKey: config.apiKey || void 0,
137
+ models: config.models?.map((m) => ({ id: m.id, name: m.name })) || [],
138
+ ...config.headers ? { headers: config.headers } : {}
139
+ };
140
+ fs.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
141
+ }
142
+ } catch {
143
+ }
144
+ try {
243
145
  const agentsDir = path.join(os.homedir(), ".openclaw", "agents");
244
146
  if (!fs.existsSync(agentsDir)) return;
245
- const agentDirs = fs.readdirSync(agentsDir);
246
- for (const agent of agentDirs) {
247
- const authPath = path.join(agentsDir, agent, "agent", "auth-profiles.json");
248
- const authDir = path.dirname(authPath);
249
- if (!fs.existsSync(authDir)) {
250
- fs.mkdirSync(authDir, { recursive: true });
251
- }
252
- let profiles = {
253
- version: 1,
254
- profiles: {}
255
- };
256
- if (fs.existsSync(authPath)) {
147
+ for (const agent of fs.readdirSync(agentsDir)) {
148
+ const modelsPath = path.join(agentsDir, agent, "agent", "models.json");
149
+ if (!fs.existsSync(path.dirname(modelsPath))) continue;
150
+ let data = { providers: {} };
151
+ if (fs.existsSync(modelsPath)) {
257
152
  try {
258
- profiles = JSON.parse(fs.readFileSync(authPath, "utf-8"));
153
+ data = JSON.parse(fs.readFileSync(modelsPath, "utf-8"));
259
154
  } catch {
260
155
  }
156
+ if (!data.providers) data.providers = {};
261
157
  }
262
- profiles.profiles["opencompress:default"] = {
263
- type: "api_key",
264
- provider: "opencompress",
265
- key: apiKey
158
+ data.providers[providerId] = {
159
+ baseUrl: config.baseUrl,
160
+ api: config.api || "openai-completions",
161
+ apiKey: config.apiKey || void 0,
162
+ models: config.models?.map((m) => ({
163
+ id: m.id,
164
+ name: m.name,
165
+ api: m.api || config.api || "openai-completions",
166
+ reasoning: m.reasoning ?? false,
167
+ input: m.input || ["text"],
168
+ cost: m.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
169
+ contextWindow: m.contextWindow || 2e5,
170
+ maxTokens: m.maxTokens || 8192
171
+ })) || [],
172
+ ...config.headers ? { headers: config.headers } : {}
266
173
  };
267
- fs.writeFileSync(authPath, JSON.stringify(profiles, null, 2) + "\n");
174
+ fs.writeFileSync(modelsPath, JSON.stringify(data, null, 2) + "\n");
268
175
  }
269
176
  } catch {
270
177
  }
271
178
  }
179
+ function enableProxy(api, baseUrl) {
180
+ const occKey = getApiKey(api);
181
+ if (!occKey) return { proxied: [], skipped: [] };
182
+ const providers = api.config.models?.providers;
183
+ if (!providers) return { proxied: [], skipped: [] };
184
+ const state = readProxyState();
185
+ const proxied = [];
186
+ const skipped = [];
187
+ for (const [id, raw] of Object.entries(providers)) {
188
+ if (id === "opencompress") continue;
189
+ const provider = raw;
190
+ if (provider.baseUrl?.includes("opencompress.ai")) continue;
191
+ const pApi = provider.api || "openai-completions";
192
+ if (pApi === "anthropic-messages" || pApi === "google-generative-ai") {
193
+ skipped.push(`${id} (${pApi})`);
194
+ continue;
195
+ }
196
+ state.originals[id] = {
197
+ baseUrl: provider.baseUrl,
198
+ apiKey: provider.apiKey,
199
+ api: provider.api,
200
+ headers: provider.headers ? { ...provider.headers } : void 0
201
+ };
202
+ provider.headers = {
203
+ ...provider.headers || {},
204
+ "X-Upstream-Key": provider.apiKey || "",
205
+ "X-Upstream-Base-Url": provider.baseUrl
206
+ };
207
+ provider.baseUrl = `${baseUrl}/v1`;
208
+ provider.apiKey = occKey;
209
+ persistProviderToDisk(id, provider);
210
+ proxied.push(id);
211
+ }
212
+ state.enabled = true;
213
+ writeProxyState(state);
214
+ return { proxied, skipped };
215
+ }
216
+ function disableProxy(api) {
217
+ const state = readProxyState();
218
+ if (!state.enabled) return [];
219
+ const providers = api.config.models?.providers;
220
+ if (!providers) return [];
221
+ const restored = [];
222
+ for (const [id, orig] of Object.entries(state.originals)) {
223
+ const provider = providers[id];
224
+ if (!provider) continue;
225
+ provider.baseUrl = orig.baseUrl;
226
+ provider.apiKey = orig.apiKey;
227
+ provider.api = orig.api;
228
+ if (orig.headers) {
229
+ provider.headers = orig.headers;
230
+ } else {
231
+ const h = provider.headers;
232
+ if (h) {
233
+ delete h["X-Upstream-Key"];
234
+ delete h["X-Upstream-Base-Url"];
235
+ if (Object.keys(h).length === 0) delete provider.headers;
236
+ }
237
+ }
238
+ persistProviderToDisk(id, provider);
239
+ restored.push(id);
240
+ }
241
+ state.enabled = false;
242
+ state.originals = {};
243
+ writeProxyState(state);
244
+ return restored;
245
+ }
272
246
  var opencompressProvider = {
273
247
  id: "opencompress",
274
248
  label: "OpenCompress",
275
249
  docsPath: "https://docs.opencompress.ai",
276
250
  aliases: ["oc", "compress"],
277
251
  envVars: ["OPENCOMPRESS_API_KEY"],
278
- models: buildProviderModels(DEFAULT_BASE_URL),
252
+ // No models — we're a transparent proxy, not a router.
253
+ // Users keep their existing providers; we just compress their traffic.
254
+ models: {
255
+ baseUrl: `${DEFAULT_BASE_URL}/v1`,
256
+ api: "openai-completions",
257
+ models: []
258
+ },
279
259
  formatApiKey: (cred) => cred.apiKey || "",
280
260
  auth: [
281
261
  {
282
262
  id: "api-key",
283
263
  label: "OpenCompress",
284
- hint: "Connect your LLM key \u2014 compress every call, save 40-70%",
264
+ hint: "Compress all LLM calls automatically \u2014 save 40-60% on any provider",
285
265
  kind: "custom",
286
266
  run: async (ctx) => {
287
267
  ctx.prompter.note(
288
- "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"
268
+ "OpenCompress compresses all LLM prompts automatically.\nWorks with any provider you already have (OpenAI, Anthropic, OpenRouter, etc.).\n\nAfter setup, run `/compress on` to enable compression for all providers."
289
269
  );
290
- const llmKey = await ctx.prompter.text({
291
- message: "Enter your LLM API key (OpenAI/Anthropic/OpenRouter):",
292
- validate: (val) => {
293
- if (!val || val.length < 10) return "Please enter a valid API key";
294
- if (val.startsWith("sk-occ-")) return "Enter your LLM provider key, not an OpenCompress key";
295
- return void 0;
296
- }
297
- });
298
- if (typeof llmKey === "symbol") {
299
- throw new Error("Setup cancelled");
300
- }
301
- let provider = "openrouter";
302
- let upstreamBaseUrl = "https://openrouter.ai/api/v1";
303
- if (llmKey.startsWith("sk-proj-") || llmKey.startsWith("sk-") && !llmKey.startsWith("sk-ant-") && !llmKey.startsWith("sk-or-")) {
304
- provider = "openai";
305
- upstreamBaseUrl = "https://api.openai.com/v1";
306
- } else if (llmKey.startsWith("sk-ant-")) {
307
- provider = "anthropic";
308
- upstreamBaseUrl = "https://api.anthropic.com/v1";
309
- } else if (llmKey.startsWith("AIza")) {
310
- provider = "google";
311
- upstreamBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai";
312
- }
313
- const spinner = ctx.prompter.progress("Creating account...");
270
+ const spinner = ctx.prompter.progress("Creating OpenCompress account...");
314
271
  try {
315
272
  const res = await fetch(`${DEFAULT_BASE_URL}/v1/provision`, {
316
273
  method: "POST",
317
274
  headers: { "Content-Type": "application/json" },
318
- body: JSON.stringify({ upstreamApiKey: llmKey })
275
+ body: JSON.stringify({})
319
276
  });
320
277
  if (!res.ok) {
321
278
  const err = await res.json().catch(() => ({ error: { message: "Unknown error" } }));
@@ -326,21 +283,6 @@ var opencompressProvider = {
326
283
  }
327
284
  const data = await res.json();
328
285
  spinner.stop("Account created");
329
- try {
330
- await fetch(`${DEFAULT_BASE_URL}/v1/byok`, {
331
- method: "POST",
332
- headers: {
333
- Authorization: `Bearer ${data.apiKey}`,
334
- "Content-Type": "application/json"
335
- },
336
- body: JSON.stringify({ provider, passthrough: true })
337
- });
338
- } catch {
339
- }
340
- const onboardModels = buildProviderModels(DEFAULT_BASE_URL, llmKey, upstreamBaseUrl, void 0, data.apiKey);
341
- const modelCount = FALLBACK_MODELS.length;
342
- persistModelsConfig(onboardModels);
343
- persistAgentModelsJson(onboardModels);
344
286
  persistAuthProfile(data.apiKey);
345
287
  persistAgentAuthJson(data.apiKey);
346
288
  return {
@@ -350,18 +292,11 @@ var opencompressProvider = {
350
292
  credential: { apiKey: data.apiKey }
351
293
  }
352
294
  ],
353
- configPatch: {
354
- models: {
355
- providers: {
356
- opencompress: onboardModels
357
- }
358
- }
359
- },
360
- defaultModel: "gpt-4o-mini",
361
295
  notes: [
362
- `OpenCompress is ready! Connected to ${provider} (${modelCount} models).`,
363
- "Your LLM key is stored locally only \u2014 never on our server.",
364
- `Free credit: ${data.freeCredit}. Dashboard: opencompress.ai/dashboard`
296
+ "OpenCompress is ready!",
297
+ `Free credit: ${data.freeCredit}`,
298
+ "Run `/compress on` to enable compression for all your LLM providers.",
299
+ "Dashboard: opencompress.ai/dashboard"
365
300
  ]
366
301
  };
367
302
  } catch (err) {
@@ -375,53 +310,17 @@ var opencompressProvider = {
375
310
  var plugin = {
376
311
  id: "opencompress",
377
312
  name: "OpenCompress",
378
- description: "5-layer prompt compression \u2014 53% input reduction, 62% latency cut, 96% quality",
313
+ description: "Transparent prompt compression \u2014 save 40-60% on any LLM provider",
379
314
  version: VERSION,
380
315
  register(api) {
381
316
  const baseUrl = api.pluginConfig?.baseUrl || DEFAULT_BASE_URL;
382
- const existingHeaders = api.config.models?.providers?.opencompress?.headers;
383
- const existingUpstreamKey = existingHeaders?.["X-Upstream-Key"];
384
- const existingUpstreamBaseUrl = existingHeaders?.["X-Upstream-Base-Url"];
385
- const existingApiKey = api.config.models?.providers?.opencompress?.apiKey || getApiKey(api);
386
- const existingModels = readExistingModels(api);
387
- const providerModels = buildProviderModels(baseUrl, existingUpstreamKey, existingUpstreamBaseUrl, existingModels || void 0, existingApiKey);
388
- opencompressProvider.models = providerModels;
389
317
  api.registerProvider(opencompressProvider);
390
- if (!api.config.models) {
391
- api.config.models = { providers: {} };
392
- }
393
- if (!api.config.models.providers) {
394
- api.config.models.providers = {};
395
- }
396
- api.config.models.providers.opencompress = providerModels;
397
- persistModelsConfig(providerModels);
398
- persistAgentModelsJson(providerModels);
399
- {
400
- if (!api.config.agents) api.config.agents = {};
401
- const agents = api.config.agents;
402
- if (!agents.defaults) agents.defaults = {};
403
- const defaults = agents.defaults;
404
- if (!defaults.models) defaults.models = {};
405
- const allowlist = defaults.models;
406
- if (Object.keys(allowlist).length > 0) {
407
- for (const m of providerModels.models) {
408
- const fullId = `opencompress/${m.id}`;
409
- if (!allowlist[fullId]) {
410
- allowlist[fullId] = {};
411
- }
412
- }
413
- }
414
- }
415
- const modelsForAllowlist = providerModels.models;
416
- setTimeout(() => injectModelsAllowlist(modelsForAllowlist), 3e3);
417
318
  const apiKey = getApiKey(api);
418
319
  if (apiKey) {
419
320
  persistAuthProfile(apiKey);
420
321
  persistAgentAuthJson(apiKey);
421
322
  }
422
- const modelCount = existingModels ? existingModels.length : FALLBACK_MODELS.length;
423
- const source = existingModels ? "from existing providers" : "fallback";
424
- api.logger.info(`OpenCompress provider registered (${modelCount} models ${source}, 5-layer compression)`);
323
+ api.logger.info("OpenCompress registered (transparent proxy mode)");
425
324
  api.registerCommand({
426
325
  name: "compress-stats",
427
326
  description: "Show OpenCompress usage statistics and savings",
@@ -471,107 +370,81 @@ var plugin = {
471
370
  });
472
371
  api.logger.info("Registered /compress-stats command");
473
372
  api.registerCommand({
474
- name: "compress-byok",
475
- description: "Connect your own LLM key (OpenAI/Anthropic/OpenRouter) to save more",
373
+ name: "compress",
374
+ description: "Toggle transparent compression for all LLM providers",
476
375
  acceptsArgs: true,
477
376
  requireAuth: false,
478
377
  handler: async (ctx) => {
479
- const apiKey2 = getApiKey(api);
480
- if (!apiKey2) {
481
- return { text: "Not set up. Run `openclaw onboard opencompress` first." };
378
+ const arg = ctx.args?.trim().toLowerCase();
379
+ if (arg === "on" || arg === "enable") {
380
+ const occKey2 = getApiKey(api);
381
+ if (!occKey2) {
382
+ return { text: "No API key found. Run `openclaw onboard opencompress` first." };
383
+ }
384
+ const result = enableProxy(api, baseUrl);
385
+ if (result.proxied.length === 0 && result.skipped.length === 0) {
386
+ return { text: "No providers found to proxy. Add LLM providers first." };
387
+ }
388
+ const lines = [
389
+ "**Compression enabled** for all compatible providers.",
390
+ ""
391
+ ];
392
+ if (result.proxied.length > 0) {
393
+ lines.push(`Proxied (${result.proxied.length}): ${result.proxied.join(", ")}`);
394
+ }
395
+ if (result.skipped.length > 0) {
396
+ lines.push(`Skipped (incompatible format): ${result.skipped.join(", ")}`);
397
+ }
398
+ lines.push("", "All requests now route through OpenCompress for automatic compression.");
399
+ lines.push("To disable: `/compress off`");
400
+ return { text: lines.join("\n") };
482
401
  }
483
- const upstreamKey = ctx.args?.trim();
484
- if (!upstreamKey) {
485
- const res = await fetch(`${baseUrl}/v1/topup`, {
486
- headers: { Authorization: `Bearer ${apiKey2}` }
487
- });
488
- const data = res.ok ? await res.json() : null;
402
+ if (arg === "off" || arg === "disable") {
403
+ const restored = disableProxy(api);
404
+ if (restored.length === 0) {
405
+ return { text: "Compression proxy was not active." };
406
+ }
489
407
  return {
490
408
  text: [
491
- "**BYOK (Bring Your Own Key)**",
409
+ "**Compression disabled.** Restored original provider configs.",
492
410
  "",
493
- "Connect your LLM provider key to pay them directly \u2014 we only charge the compression fee (20% of savings).",
411
+ `Restored: ${restored.join(", ")}`,
494
412
  "",
495
- "**Usage:**",
496
- " `/compress-byok sk-proj-xxx` \u2014 Connect OpenAI key",
497
- " `/compress-byok sk-ant-xxx` \u2014 Connect Anthropic key",
498
- " `/compress-byok sk-or-xxx` \u2014 Connect OpenRouter key",
499
- " `/compress-byok off` \u2014 Switch back to router mode",
500
- "",
501
- data ? `**Balance:** $${Number(data.balance || 0).toFixed(2)}` : ""
413
+ "To re-enable: `/compress on`"
502
414
  ].join("\n")
503
415
  };
504
416
  }
505
- if (upstreamKey === "off" || upstreamKey === "disable" || upstreamKey === "router") {
506
- const cleanModels = buildProviderModels(baseUrl);
507
- if (api.config.models?.providers) {
508
- api.config.models.providers.opencompress = cleanModels;
509
- }
510
- persistModelsConfig(cleanModels);
511
- persistAgentModelsJson(cleanModels);
512
- try {
513
- await fetch(`${baseUrl}/v1/byok`, {
514
- method: "DELETE",
515
- headers: { Authorization: `Bearer ${apiKey2}` }
516
- });
517
- } catch {
518
- }
519
- return { text: "Switched back to **router mode**. Your upstream key has been removed from local config." };
520
- }
521
- if (upstreamKey.startsWith("sk-occ-")) {
522
- return { text: "That's an OpenCompress key. Provide your LLM provider key (OpenAI, Anthropic, etc.)." };
523
- }
524
- if (upstreamKey.length < 10) {
525
- return { text: "Key looks too short. Provide your full LLM API key." };
526
- }
527
- let provider = "unknown";
528
- let upstreamBaseUrl = "";
529
- if (upstreamKey.startsWith("sk-proj-") || upstreamKey.startsWith("sk-") && !upstreamKey.startsWith("sk-ant-") && !upstreamKey.startsWith("sk-or-")) {
530
- provider = "openai";
531
- upstreamBaseUrl = "https://api.openai.com/v1";
532
- } else if (upstreamKey.startsWith("sk-ant-")) {
533
- provider = "anthropic";
534
- upstreamBaseUrl = "https://api.anthropic.com/v1";
535
- } else if (upstreamKey.startsWith("sk-or-")) {
536
- provider = "openrouter";
537
- upstreamBaseUrl = "https://openrouter.ai/api/v1";
538
- } else if (upstreamKey.startsWith("AIza")) {
539
- provider = "google";
540
- upstreamBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai";
541
- }
542
- const existingModels2 = readExistingModels(api);
543
- const updatedModels = buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl, existingModels2 || void 0);
544
- if (api.config.models?.providers) {
545
- api.config.models.providers.opencompress = updatedModels;
417
+ const proxyState = readProxyState();
418
+ const occKey = getApiKey(api);
419
+ const statusLines = [
420
+ "**OpenCompress Transparent Proxy**",
421
+ "",
422
+ `Status: ${proxyState.enabled ? "**ON**" : "**OFF**"}`,
423
+ `API key: ${occKey ? `${occKey.slice(0, 12)}...` : "not set"}`
424
+ ];
425
+ if (proxyState.enabled && Object.keys(proxyState.originals).length > 0) {
426
+ statusLines.push(`Proxied providers: ${Object.keys(proxyState.originals).join(", ")}`);
546
427
  }
547
- persistModelsConfig(updatedModels);
548
- persistAgentModelsJson(updatedModels);
549
- try {
550
- await fetch(`${baseUrl}/v1/byok`, {
551
- method: "POST",
552
- headers: {
553
- Authorization: `Bearer ${apiKey2}`,
554
- "Content-Type": "application/json"
555
- },
556
- body: JSON.stringify({ provider, passthrough: true })
557
- });
558
- } catch {
559
- }
560
- const modelCount2 = existingModels2 ? existingModels2.length : FALLBACK_MODELS.length;
561
- return {
562
- text: [
563
- `Switched to **BYOK mode** (${provider}).`,
564
- `Loaded **${modelCount2} models** from your ${provider} account.`,
565
- "",
566
- "Your key is stored **locally only** \u2014 never sent to our server for storage.",
567
- "It's passed through on each request via header and discarded immediately.",
568
- "",
569
- "To switch back: `/compress-byok off`"
570
- ].join("\n")
571
- };
428
+ statusLines.push(
429
+ "",
430
+ "**Usage:**",
431
+ " `/compress on` \u2014 Route all providers through OpenCompress",
432
+ " `/compress off` \u2014 Restore original provider configs",
433
+ " `/compress-stats` \u2014 View compression savings"
434
+ );
435
+ return { text: statusLines.join("\n") };
572
436
  }
573
437
  });
574
- api.logger.info("Registered /compress-byok command");
438
+ api.logger.info("Registered /compress command");
439
+ setTimeout(() => {
440
+ const proxyState = readProxyState();
441
+ if (proxyState.enabled) {
442
+ const result = enableProxy(api, baseUrl);
443
+ if (result.proxied.length > 0) {
444
+ api.logger.info(`Proxy auto-applied on startup: ${result.proxied.length} providers (${result.proxied.join(", ")})`);
445
+ }
446
+ }
447
+ }, 2e3);
575
448
  const os = __require("os");
576
449
  const fs = __require("fs");
577
450
  const path = __require("path");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencompress/opencompress",
3
- "version": "1.6.7",
3
+ "version": "1.8.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",