openclaw-abacusai-auth 1.2.4 → 1.2.5

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/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2
2
  import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
@@ -15,8 +15,9 @@ const DEFAULT_MAX_TOKENS = 8192;
15
15
 
16
16
  // Proxy configuration
17
17
  const PROXY_HOST = "127.0.0.1";
18
- // Dynamic port - will be assigned when proxy starts
19
- let proxyPort = 0;
18
+ // Fixed port for the proxy so the baseUrl saved at auth time always works
19
+ const PROXY_PORT_DEFAULT = 18862;
20
+ let proxyPort = PROXY_PORT_DEFAULT;
20
21
 
21
22
  // Models available on AbacusAI RouteLLM endpoint (OpenAI-compatible, with
22
23
  // function calling support). Verified 2026-02.
@@ -443,6 +444,13 @@ function normalizeToolsForRouteLLM(tools: unknown[]): unknown[] {
443
444
  fn.parameters = cleanSchema(fn.parameters);
444
445
  }
445
446
 
447
+ // RouteLLM REQUIRES every tool to have a `parameters` field.
448
+ // If a tool has no parameters (e.g. cognitive_assess, flare_plan),
449
+ // add a default empty object schema.
450
+ if (!fn.parameters) {
451
+ fn.parameters = { type: "object", properties: {} };
452
+ }
453
+
446
454
  copy.function = fn;
447
455
 
448
456
  // Promote name and parameters to top level for RouteLLM
@@ -559,6 +567,8 @@ async function handleProxyRequest(req: IncomingMessage, res: ServerResponse) {
559
567
  const parsed = JSON.parse(raw.toString()) as Record<string, unknown>;
560
568
  // Normalize tools for RouteLLM: remove `strict` field, clean schemas
561
569
  // (remove patternProperties, add additionalProperties: false, etc.)
570
+
571
+
562
572
  if (Array.isArray(parsed.tools)) {
563
573
  parsed.tools = normalizeToolsForRouteLLM(parsed.tools);
564
574
  }
@@ -677,18 +687,34 @@ function startProxy(apiKey: string): Promise<void> {
677
687
  sendJsonResponse(res, 500, { error: { message: String(err) } });
678
688
  });
679
689
  });
680
- // Use port 0 to let the OS assign a random available port
681
- proxyServer.listen(0, PROXY_HOST, () => {
682
- const addr = proxyServer?.address();
683
- if (addr && typeof addr === "object") {
684
- proxyPort = addr.port;
685
- }
686
- console.log(`[abacusai] proxy listening on http://${PROXY_HOST}:${proxyPort}`);
687
- resolve();
688
- });
689
- proxyServer.on("error", (err: NodeJS.ErrnoException) => {
690
- reject(err);
691
- });
690
+
691
+ // Try fixed port first, then retry with port+1, +2, etc.
692
+ const tryListen = (port: number, attempt: number) => {
693
+ proxyServer!.listen(port, PROXY_HOST, () => {
694
+ proxyPort = port;
695
+ console.log(`[abacusai] proxy listening on http://${PROXY_HOST}:${proxyPort}`);
696
+ resolve();
697
+ });
698
+ proxyServer!.once("error", (err: NodeJS.ErrnoException) => {
699
+ if (err.code === "EADDRINUSE" && attempt < 10) {
700
+ console.log(`[abacusai] port ${port} in use, trying ${port + 1}...`);
701
+ proxyServer!.removeAllListeners("error");
702
+ proxyServer!.close(() => {
703
+ proxyServer = createServer((req, res) => {
704
+ handleProxyRequest(req, res).catch((e) => {
705
+ console.error("[abacusai] proxy error:", e);
706
+ sendJsonResponse(res, 500, { error: { message: String(e) } });
707
+ });
708
+ });
709
+ tryListen(port + 1, attempt + 1);
710
+ });
711
+ } else {
712
+ reject(err);
713
+ }
714
+ });
715
+ };
716
+
717
+ tryListen(PROXY_PORT_DEFAULT, 0);
692
718
  });
693
719
  }
694
720
 
@@ -742,7 +768,9 @@ function updateBaseUrlInConfig(): void {
742
768
  const configPath = join(stateDir, "openclaw.json");
743
769
  if (!existsSync(configPath)) return;
744
770
 
745
- const raw = readFileSync(configPath, "utf-8");
771
+ let raw = readFileSync(configPath, "utf-8");
772
+ // Strip UTF-8 BOM if present
773
+ if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1);
746
774
  const config = JSON.parse(raw);
747
775
  const currentUrl = config?.models?.providers?.abacusai?.baseUrl;
748
776
 
@@ -783,7 +811,7 @@ interface PluginAuthContext {
783
811
  }
784
812
 
785
813
  const abacusaiPlugin = {
786
- id: "abacusai-auth",
814
+ id: "openclaw-abacusai-auth",
787
815
  name: "AbacusAI Auth",
788
816
  description: "AbacusAI RouteLLM provider plugin with direct connection and schema normalization",
789
817
  configSchema: emptyPluginConfigSchema(),
@@ -934,7 +962,7 @@ const abacusaiPlugin = {
934
962
  // and adding `additionalProperties: false`
935
963
  baseUrl: `http://${PROXY_HOST}:${proxyPort}`,
936
964
  api: "openai-completions",
937
- auth: "token",
965
+ apiKey: "abacusai-proxy",
938
966
  models: modelIds.map((id) => buildModelDefinition(id)),
939
967
  },
940
968
  },
@@ -965,3 +993,4 @@ const abacusaiPlugin = {
965
993
  };
966
994
 
967
995
  export default abacusaiPlugin;
996
+
@@ -1,5 +1,5 @@
1
1
  {
2
- "id": "abacusai-auth",
2
+ "id": "openclaw-abacusai-auth",
3
3
  "providers": [
4
4
  "abacusai"
5
5
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-abacusai-auth",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "OpenClaw AbacusAI provider plugin - Third-party plugin for AbacusAI RouteLLM integration",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -18,6 +18,9 @@
18
18
  ],
19
19
  "author": "tonyhu2006",
20
20
  "license": "MIT",
21
+ "scripts": {
22
+ "build": "tsc"
23
+ },
21
24
  "peerDependencies": {
22
25
  "openclaw": ">=2026.2.0"
23
26
  },
@@ -25,5 +28,9 @@
25
28
  "extensions": [
26
29
  "./index.ts"
27
30
  ]
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^25.3.3",
34
+ "typescript": "^5.9.3"
28
35
  }
29
36
  }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "./dist",
11
+ "declaration": true
12
+ },
13
+ "include": [
14
+ "index.ts"
15
+ ]
16
+ }