openclaw-nexos-provider 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 openclaw-nexos-provider contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # openclaw-nexos-provider
2
+
3
+ An [OpenClaw](https://openclaw.ai) provider plugin for [Nexos AI](https://nexos.ai) —
4
+ a unified AI gateway that exposes Claude, GPT, Gemini, Grok, and 60+ other models
5
+ through a single API endpoint and one API key.
6
+
7
+ The plugin registers two providers backed by the same `NEXOS_API_KEY`:
8
+
9
+ | Provider | Transport | Used for |
10
+ | ----------------- | ------------------------------- | ----------------------------------------------------- |
11
+ | `nexos` | `openai-completions` (`/v1/chat/completions`) | Every model that does not advertise the Anthropic `messages` endpoint |
12
+ | `nexos-anthropic` | `anthropic-messages` (`/v1/messages`) | Claude models that advertise `messages` (native tool_use, caching) |
13
+
14
+ Models are **discovered dynamically** from the Nexos `/v1/models` API at runtime,
15
+ so the catalog always reflects what your account can access — there is no
16
+ hard-coded model list to keep in sync.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ openclaw plugins install clawhub:openclaw-nexos-provider
22
+ # or from npm during the launch cutover:
23
+ openclaw plugins install openclaw-nexos-provider
24
+ ```
25
+
26
+ ## Configure
27
+
28
+ Set your Nexos API key (either works):
29
+
30
+ ```bash
31
+ export NEXOS_API_KEY="your-nexos-key"
32
+ # or, interactively:
33
+ openclaw onboard --nexos-api-key "your-nexos-key"
34
+ ```
35
+
36
+ Then pick a model:
37
+
38
+ ```bash
39
+ openclaw models list --provider nexos
40
+ openclaw models list --provider nexos-anthropic
41
+ openclaw agent --model "nexos/<model-id>" --message "hello"
42
+ ```
43
+
44
+ Model definitions (context window, max output tokens, transport) are supplied by
45
+ the plugin; OpenClaw's catalog machinery marks the models available and shows
46
+ them in the model picker like any first-class provider.
47
+
48
+ ## Develop
49
+
50
+ ```bash
51
+ npm install
52
+ npm run typecheck # requires the openclaw peer dependency
53
+ npm run test # pure catalog/discovery unit tests (no OpenClaw runtime)
54
+ npm run build # bundles src/index.ts -> dist/index.js (openclaw externalized)
55
+ ```
56
+
57
+ - `src/nexos-models.ts` — pure, dependency-free catalog logic (fetch, project,
58
+ split by endpoint, build provider config). Fully unit-tested.
59
+ - `src/index.ts` — the plugin entry: registers the two providers and their model
60
+ catalogs against the OpenClaw Plugin SDK.
61
+
62
+ ## Status
63
+
64
+ Community/open-source plugin. Built to follow the OpenClaw maintainers' guidance
65
+ that optional gateway/provider integrations ship as ClawHub/npm plugins rather
66
+ than in core (see openclaw/openclaw#44963). Intended to be transferable to Nexos
67
+ AI for long-term maintenance.
68
+
69
+ ## License
70
+
71
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,218 @@
1
+ // src/index.ts
2
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
+ import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
4
+
5
+ // src/nexos-models.ts
6
+ var NEXOS_BASE_URL = "https://api.nexos.ai/v1";
7
+ var NEXOS_PROVIDER_COMPLETIONS = "nexos";
8
+ var NEXOS_PROVIDER_ANTHROPIC = "nexos-anthropic";
9
+ var DEFAULT_MAX_TOKENS = 8192;
10
+ var DEFAULT_CONTEXT_WINDOW = 2e5;
11
+ var MODEL_REGEX_PRIORITIES = {
12
+ "\\bgpt[\\s-_]5(.\\d+)?": 100
13
+ };
14
+ var MODEL_PREFIX_PRIORITIES = {
15
+ gpt: 200,
16
+ gemini: 300,
17
+ claude: 400,
18
+ "anthropic.claude": 400,
19
+ grok: 600
20
+ };
21
+ function beautifyName(name) {
22
+ return name.replace(/-/g, " ").replace(/\./g, " ").replace(/@/g, " @ ").replace(/\b\w/g, (l) => l.toUpperCase());
23
+ }
24
+ function prioritizeModel(name) {
25
+ for (const [pattern, priority] of Object.entries(MODEL_REGEX_PRIORITIES)) {
26
+ if (new RegExp(pattern, "i").test(name)) {
27
+ return priority;
28
+ }
29
+ }
30
+ const prefix = (name.replace(" ", "-").split("-")[0] ?? "").toLowerCase();
31
+ return MODEL_PREFIX_PRIORITIES[prefix] ?? 200;
32
+ }
33
+ function buildNexosCatalog(apiData) {
34
+ const data = apiData?.data;
35
+ if (!Array.isArray(data)) {
36
+ return [];
37
+ }
38
+ const models = [];
39
+ for (const model of data) {
40
+ const nexosModelId = model.nexos_model_id;
41
+ const rawName = model.name;
42
+ if (!nexosModelId || !rawName) {
43
+ continue;
44
+ }
45
+ const alias = `Nexos ${beautifyName(rawName)}`;
46
+ const endpoints = Array.isArray(model.endpoints) ? model.endpoints : ["chat_completions"];
47
+ models.push({
48
+ alias,
49
+ nexosModelId,
50
+ priority: prioritizeModel(rawName),
51
+ endpoints,
52
+ definition: {
53
+ id: nexosModelId,
54
+ name: alias,
55
+ reasoning: true,
56
+ input: ["text", "image"],
57
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
58
+ maxTokens: DEFAULT_MAX_TOKENS,
59
+ contextWindow: DEFAULT_CONTEXT_WINDOW,
60
+ compat: {
61
+ supportsStore: true,
62
+ supportsDeveloperRole: true,
63
+ supportsReasoningEffort: false
64
+ }
65
+ }
66
+ });
67
+ }
68
+ models.sort((a, b) => a.priority - b.priority);
69
+ return models;
70
+ }
71
+ function splitByEndpoint(models) {
72
+ const completions = [];
73
+ const anthropic = [];
74
+ for (const model of models) {
75
+ if (model.endpoints.includes("messages")) {
76
+ anthropic.push(model);
77
+ } else {
78
+ completions.push(model);
79
+ }
80
+ }
81
+ return { completions, anthropic };
82
+ }
83
+ function buildProviderConfig(params) {
84
+ const models = params.models.map((model) => model.definition);
85
+ if (params.kind === "anthropic") {
86
+ return {
87
+ baseUrl: NEXOS_BASE_URL,
88
+ apiKey: params.apiKey,
89
+ api: "anthropic-messages",
90
+ auth: "token",
91
+ authHeader: true,
92
+ models
93
+ };
94
+ }
95
+ return {
96
+ baseUrl: NEXOS_BASE_URL,
97
+ apiKey: params.apiKey,
98
+ api: "openai-completions",
99
+ auth: "api-key",
100
+ models
101
+ };
102
+ }
103
+ async function fetchNexosModels(apiKey, fetchImpl = fetch) {
104
+ const response = await fetchImpl(`${NEXOS_BASE_URL}/models`, {
105
+ headers: { Authorization: `Bearer ${apiKey}` }
106
+ });
107
+ if (!response.ok) {
108
+ throw new Error(
109
+ `Nexos model list request failed: ${response.status} ${response.statusText}`
110
+ );
111
+ }
112
+ return response.json();
113
+ }
114
+
115
+ // src/index.ts
116
+ var CATALOG_TTL_MS = 6e4;
117
+ var catalogCache = /* @__PURE__ */ new Map();
118
+ async function getNexosCatalog(apiKey) {
119
+ const cached = catalogCache.get(apiKey);
120
+ if (cached && Date.now() - cached.fetchedAt < CATALOG_TTL_MS) {
121
+ return cached.models;
122
+ }
123
+ const models = buildNexosCatalog(await fetchNexosModels(apiKey));
124
+ catalogCache.set(apiKey, { fetchedAt: Date.now(), models });
125
+ return models;
126
+ }
127
+ function resolveNexosApiKey(ctx, providerId) {
128
+ const tryResolve = (id) => {
129
+ try {
130
+ return ctx.resolveProviderApiKey?.(id)?.apiKey;
131
+ } catch {
132
+ return void 0;
133
+ }
134
+ };
135
+ return tryResolve(providerId) || tryResolve(NEXOS_PROVIDER_COMPLETIONS) || process.env.NEXOS_API_KEY || void 0;
136
+ }
137
+ function selectModels(models, providerId) {
138
+ const { completions, anthropic } = splitByEndpoint(models);
139
+ return providerId === NEXOS_PROVIDER_ANTHROPIC ? anthropic : completions;
140
+ }
141
+ function registerNexosProvider(api, providerId, label) {
142
+ const kind = providerId === NEXOS_PROVIDER_ANTHROPIC ? "anthropic" : "completions";
143
+ api.registerProvider({
144
+ id: providerId,
145
+ label,
146
+ docsPath: "/providers/nexos",
147
+ envVars: ["NEXOS_API_KEY"],
148
+ auth: [
149
+ createProviderApiKeyAuthMethod({
150
+ providerId,
151
+ methodId: "api-key",
152
+ label: "Nexos AI API key",
153
+ hint: "API key from your Nexos AI dashboard",
154
+ optionKey: "nexosApiKey",
155
+ flagName: "--nexos-api-key",
156
+ envVar: "NEXOS_API_KEY",
157
+ promptMessage: "Enter your Nexos AI API key"
158
+ })
159
+ ],
160
+ catalog: {
161
+ order: "simple",
162
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
+ run: async (ctx) => {
164
+ const apiKey = resolveNexosApiKey(ctx, providerId);
165
+ if (!apiKey) {
166
+ return null;
167
+ }
168
+ let models;
169
+ try {
170
+ models = await getNexosCatalog(apiKey);
171
+ } catch {
172
+ return null;
173
+ }
174
+ const selected = selectModels(models, providerId);
175
+ if (selected.length === 0) {
176
+ return null;
177
+ }
178
+ return { provider: buildProviderConfig({ models: selected, apiKey, kind }) };
179
+ }
180
+ }
181
+ });
182
+ api.registerModelCatalogProvider({
183
+ provider: providerId,
184
+ kinds: ["text"],
185
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
186
+ liveCatalog: async (ctx) => {
187
+ const apiKey = resolveNexosApiKey(ctx, providerId);
188
+ if (!apiKey) {
189
+ return null;
190
+ }
191
+ let models;
192
+ try {
193
+ models = await getNexosCatalog(apiKey);
194
+ } catch {
195
+ return null;
196
+ }
197
+ return selectModels(models, providerId).map((model) => ({
198
+ kind: "text",
199
+ provider: providerId,
200
+ model: model.nexosModelId,
201
+ label: model.alias,
202
+ source: "live"
203
+ }));
204
+ }
205
+ });
206
+ }
207
+ var index_default = definePluginEntry({
208
+ id: "nexos",
209
+ name: "Nexos AI",
210
+ description: "Nexos AI unified gateway \u2014 access Claude, GPT, Gemini, Grok and 60+ models through one API key.",
211
+ register(api) {
212
+ registerNexosProvider(api, NEXOS_PROVIDER_COMPLETIONS, "Nexos AI");
213
+ registerNexosProvider(api, NEXOS_PROVIDER_ANTHROPIC, "Nexos AI (Claude)");
214
+ }
215
+ });
216
+ export {
217
+ index_default as default
218
+ };
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: "Nexos AI"
3
+ summary: "Use Nexos AI's unified gateway (Claude, GPT, Gemini, Grok, and more) in OpenClaw via one API key"
4
+ ---
5
+
6
+ Nexos AI is a unified AI gateway that provides OpenAI-compatible access to models
7
+ from many providers — Anthropic, OpenAI, Google, xAI, Mistral, and more — through
8
+ a single API endpoint and API key.
9
+
10
+ This plugin registers two OpenClaw providers, both authenticated with
11
+ `NEXOS_API_KEY`:
12
+
13
+ - **`nexos`** — OpenAI-compatible `/v1/chat/completions` transport for all
14
+ general models.
15
+ - **`nexos-anthropic`** — Anthropic-style `/v1/messages` transport for Claude
16
+ models that advertise the `messages` endpoint (native tool_use and caching).
17
+
18
+ ## Getting started
19
+
20
+ 1. Get an API key from your Nexos AI dashboard.
21
+ 2. Provide it to OpenClaw:
22
+
23
+ ```bash
24
+ openclaw onboard --nexos-api-key "$NEXOS_API_KEY"
25
+ # or set NEXOS_API_KEY in the environment
26
+ ```
27
+
28
+ 3. Verify the models are available:
29
+
30
+ ```bash
31
+ openclaw models list --provider nexos
32
+ openclaw models list --provider nexos-anthropic
33
+ ```
34
+
35
+ ## Model discovery
36
+
37
+ The plugin fetches the Nexos `/v1/models` list at runtime and projects each model
38
+ into an OpenClaw model definition, so the catalog reflects exactly what your
39
+ account can access. Models advertising the `messages` endpoint are routed through
40
+ `nexos-anthropic`; the rest through `nexos`.
41
+
42
+ ## Notes
43
+
44
+ - One API key, many providers — no separate accounts/keys per model vendor.
45
+ - Cost is tracked centrally by Nexos; the plugin reports zeroed per-token cost.
46
+ - A conservative `maxTokens`/`contextWindow` default is applied because Nexos
47
+ does not advertise per-model output limits.
@@ -0,0 +1,38 @@
1
+ {
2
+ "id": "nexos",
3
+ "name": "Nexos AI",
4
+ "description": "Nexos AI unified gateway — access Claude, GPT, Gemini, Grok and 60+ models through one API key.",
5
+ "providers": ["nexos", "nexos-anthropic"],
6
+ "modelSupport": {
7
+ "modelPrefixes": ["nexos/", "nexos-anthropic/"]
8
+ },
9
+ "setup": {
10
+ "providers": [
11
+ { "id": "nexos", "envVars": ["NEXOS_API_KEY"] },
12
+ { "id": "nexos-anthropic", "envVars": ["NEXOS_API_KEY"] }
13
+ ]
14
+ },
15
+ "providerAuthAliases": {
16
+ "nexos-anthropic": "nexos"
17
+ },
18
+ "providerAuthChoices": [
19
+ {
20
+ "provider": "nexos",
21
+ "method": "api-key",
22
+ "choiceId": "nexos-api-key",
23
+ "choiceLabel": "Nexos AI API key",
24
+ "groupId": "nexos",
25
+ "groupLabel": "Nexos AI",
26
+ "cliFlag": "--nexos-api-key",
27
+ "cliOption": "--nexos-api-key <key>",
28
+ "cliDescription": "Nexos AI API key"
29
+ }
30
+ ],
31
+ "activation": {
32
+ "onStartup": true
33
+ },
34
+ "configSchema": {
35
+ "type": "object",
36
+ "additionalProperties": false
37
+ }
38
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "openclaw-nexos-provider",
3
+ "version": "0.1.0",
4
+ "description": "Nexos AI model provider plugin for OpenClaw — access Claude, GPT, Gemini, Grok and 60+ models through one API key.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "fizikiukas",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/fizikiukas/openclaw-nexos-provider.git"
11
+ },
12
+ "homepage": "https://github.com/fizikiukas/openclaw-nexos-provider#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/fizikiukas/openclaw-nexos-provider/issues"
15
+ },
16
+ "keywords": [
17
+ "openclaw",
18
+ "openclaw-plugin",
19
+ "clawhub",
20
+ "nexos",
21
+ "nexos-ai",
22
+ "llm",
23
+ "ai",
24
+ "model-provider"
25
+ ],
26
+ "main": "./dist/index.js",
27
+ "exports": {
28
+ ".": "./dist/index.js"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "openclaw.plugin.json",
33
+ "docs",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsup src/index.ts --format esm --out-dir dist --target node22 --clean",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "prepublishOnly": "npm run build"
43
+ },
44
+ "peerDependencies": {
45
+ "openclaw": ">=2026.7.1-2"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.0.0",
49
+ "openclaw": "2026.7.1-2",
50
+ "tsup": "^8.5.1",
51
+ "typescript": "^5.6.0",
52
+ "vitest": "^2.1.9"
53
+ },
54
+ "openclaw": {
55
+ "extensions": [
56
+ "./dist/index.js"
57
+ ],
58
+ "providers": [
59
+ "nexos",
60
+ "nexos-anthropic"
61
+ ],
62
+ "compat": {
63
+ "pluginApi": ">=2026.7.1-2",
64
+ "minGatewayVersion": "2026.7.1-2"
65
+ },
66
+ "build": {
67
+ "openclawVersion": "2026.7.1-2",
68
+ "pluginSdkVersion": "2026.7.1-2"
69
+ }
70
+ }
71
+ }