openclaw-abacusai-auth 1.2.3 → 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 +98 -20
- package/openclaw.plugin.json +1 -1
- package/package.json +8 -1
- package/tsconfig.json +16 -0
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, readdirSync } 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
|
-
//
|
|
19
|
-
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
proxyPort =
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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
|
|
|
@@ -720,6 +746,50 @@ function buildModelDefinition(modelId: string) {
|
|
|
720
746
|
};
|
|
721
747
|
}
|
|
722
748
|
|
|
749
|
+
// ---------------------------------------------------------------------------
|
|
750
|
+
// Dynamic baseUrl updater — keep config in sync with current proxy port
|
|
751
|
+
// ---------------------------------------------------------------------------
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Update the `models.providers.abacusai.baseUrl` in openclaw.json to match
|
|
755
|
+
* the current proxy port. This is necessary because the proxy uses port 0
|
|
756
|
+
* (OS-assigned random port) and gets a new port every time the gateway starts,
|
|
757
|
+
* but the config still stores the port from when `openclaw models auth login`
|
|
758
|
+
* was first run.
|
|
759
|
+
*/
|
|
760
|
+
function updateBaseUrlInConfig(): void {
|
|
761
|
+
if (!proxyPort) return;
|
|
762
|
+
const newBaseUrl = `http://${PROXY_HOST}:${proxyPort}`;
|
|
763
|
+
try {
|
|
764
|
+
const stateDir =
|
|
765
|
+
process.env.OPENCLAW_STATE_DIR ||
|
|
766
|
+
process.env.CLAWDBOT_STATE_DIR ||
|
|
767
|
+
join(homedir(), ".openclaw");
|
|
768
|
+
const configPath = join(stateDir, "openclaw.json");
|
|
769
|
+
if (!existsSync(configPath)) return;
|
|
770
|
+
|
|
771
|
+
let raw = readFileSync(configPath, "utf-8");
|
|
772
|
+
// Strip UTF-8 BOM if present
|
|
773
|
+
if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1);
|
|
774
|
+
const config = JSON.parse(raw);
|
|
775
|
+
const currentUrl = config?.models?.providers?.abacusai?.baseUrl;
|
|
776
|
+
|
|
777
|
+
if (currentUrl === newBaseUrl) {
|
|
778
|
+
// Already up to date
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Update the baseUrl
|
|
783
|
+
if (config.models?.providers?.abacusai) {
|
|
784
|
+
config.models.providers.abacusai.baseUrl = newBaseUrl;
|
|
785
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
786
|
+
console.log(`[abacusai] Updated config baseUrl: ${currentUrl} → ${newBaseUrl}`);
|
|
787
|
+
}
|
|
788
|
+
} catch (err) {
|
|
789
|
+
console.error("[abacusai] Failed to update baseUrl in config:", err);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
723
793
|
// ---------------------------------------------------------------------------
|
|
724
794
|
// Plugin
|
|
725
795
|
// ---------------------------------------------------------------------------
|
|
@@ -741,7 +811,7 @@ interface PluginAuthContext {
|
|
|
741
811
|
}
|
|
742
812
|
|
|
743
813
|
const abacusaiPlugin = {
|
|
744
|
-
id: "abacusai-auth",
|
|
814
|
+
id: "openclaw-abacusai-auth",
|
|
745
815
|
name: "AbacusAI Auth",
|
|
746
816
|
description: "AbacusAI RouteLLM provider plugin with direct connection and schema normalization",
|
|
747
817
|
configSchema: emptyPluginConfigSchema(),
|
|
@@ -760,9 +830,16 @@ const abacusaiPlugin = {
|
|
|
760
830
|
// Auto-start proxy if we have a saved API key
|
|
761
831
|
const savedKey = tryRecoverApiKey();
|
|
762
832
|
if (savedKey) {
|
|
763
|
-
startProxy(savedKey)
|
|
764
|
-
|
|
765
|
-
|
|
833
|
+
startProxy(savedKey)
|
|
834
|
+
.then(() => {
|
|
835
|
+
// Update baseUrl in config to match the new proxy port
|
|
836
|
+
// (The proxy gets a new random port each time the gateway starts,
|
|
837
|
+
// but the config still has the port from when auth was first run)
|
|
838
|
+
updateBaseUrlInConfig();
|
|
839
|
+
})
|
|
840
|
+
.catch((err) => {
|
|
841
|
+
console.error("[abacusai] Failed to auto-start proxy:", err);
|
|
842
|
+
});
|
|
766
843
|
}
|
|
767
844
|
|
|
768
845
|
pluginApi.registerProvider({
|
|
@@ -885,7 +962,7 @@ const abacusaiPlugin = {
|
|
|
885
962
|
// and adding `additionalProperties: false`
|
|
886
963
|
baseUrl: `http://${PROXY_HOST}:${proxyPort}`,
|
|
887
964
|
api: "openai-completions",
|
|
888
|
-
|
|
965
|
+
apiKey: "abacusai-proxy",
|
|
889
966
|
models: modelIds.map((id) => buildModelDefinition(id)),
|
|
890
967
|
},
|
|
891
968
|
},
|
|
@@ -916,3 +993,4 @@ const abacusaiPlugin = {
|
|
|
916
993
|
};
|
|
917
994
|
|
|
918
995
|
export default abacusaiPlugin;
|
|
996
|
+
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-abacusai-auth",
|
|
3
|
-
"version": "1.2.
|
|
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
|
+
}
|