@simplellm/opencode-provider 0.5.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/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # @simplellm/opencode-provider
2
+
3
+ Set up [SimpleLLM](https://simplellm.eu) as an LLM provider for [OpenCode](https://github.com/sst/opencode) in one command.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx @simplellm/opencode-provider
9
+ ```
10
+
11
+ This will:
12
+ 1. Fetch available models from SimpleLLM
13
+ 2. Ask for your API key (get one at [app.simplellm.eu](https://app.simplellm.eu))
14
+ 3. Configure OpenCode with SimpleLLM as provider
15
+ 4. Store your API key in OpenCode's auth store
16
+
17
+ ## Plugin
18
+
19
+ The package also exports an OpenCode plugin that shows your credit balance on session start:
20
+
21
+ ```json
22
+ {
23
+ "plugins": ["@simplellm/opencode-provider"]
24
+ }
25
+ ```
26
+
27
+ ## Programmatic Usage
28
+
29
+ ```typescript
30
+ import { discoverModels, SimpleLLMPlugin } from '@simplellm/opencode-provider'
31
+
32
+ // Fetch available models
33
+ const models = await discoverModels()
34
+
35
+ // Use the plugin
36
+ const plugin = await SimpleLLMPlugin()
37
+ ```
38
+
39
+ ## Environment Variables
40
+
41
+ | Variable | Description |
42
+ |---|---|
43
+ | `SIMPLELLM_BASE_URL` | Override API endpoint (default: `https://api.simplellm.eu`) |
44
+ | `SIMPLELLM_API_KEY` | API key (also stored in OpenCode auth) |
45
+
46
+ ## What is SimpleLLM?
47
+
48
+ EU-hosted LLM inference. GDPR-compliant, no data leaves Europe. Self-hosted models, no third-party API proxying.
49
+
50
+ ## License
51
+
52
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ import { createInterface } from 'node:readline';
3
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
4
+ import { homedir } from 'node:os';
5
+ import { join, dirname } from 'node:path';
6
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
7
+ const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
8
+ const DEFAULT_BASE_URL = 'https://api.simplellm.eu';
9
+ const OPENCODE_CONFIG = join(homedir(), '.config', 'opencode', 'opencode.json');
10
+ const OPENCODE_AUTH = join(homedir(), '.local', 'share', 'opencode', 'auth.json');
11
+ async function readJSON(path) {
12
+ try {
13
+ return JSON.parse(await readFile(path, 'utf-8'));
14
+ }
15
+ catch {
16
+ return {};
17
+ }
18
+ }
19
+ async function writeJSON(path, data) {
20
+ await mkdir(dirname(path), { recursive: true });
21
+ await writeFile(path, JSON.stringify(data, null, 2) + '\n');
22
+ }
23
+ async function main() {
24
+ const baseURL = process.env.SIMPLELLM_BASE_URL ?? DEFAULT_BASE_URL;
25
+ const apiURL = `${baseURL}/v1`;
26
+ console.log();
27
+ console.log(' SimpleLLM + OpenCode Setup');
28
+ console.log(' ─────────────────────────');
29
+ console.log();
30
+ console.log(` Endpoint: ${baseURL}`);
31
+ console.log();
32
+ // Fetch models
33
+ console.log(' Fetching models...');
34
+ let models;
35
+ try {
36
+ const res = await fetch(`${baseURL}/api/models`);
37
+ if (!res.ok)
38
+ throw new Error(`HTTP ${res.status}`);
39
+ models = await res.json();
40
+ }
41
+ catch {
42
+ console.error(` Could not reach ${baseURL}`);
43
+ console.error(' Check your connection or set SIMPLELLM_BASE_URL.');
44
+ process.exit(1);
45
+ }
46
+ const enabled = models.filter((m) => m.enabled);
47
+ const available = enabled.filter((m) => m.tier !== 'premium');
48
+ const premium = enabled.filter((m) => m.tier === 'premium');
49
+ console.log(` ${available.length} model${available.length !== 1 ? 's' : ''} available:`);
50
+ for (const m of available) {
51
+ const label = m.display_name ?? m.id;
52
+ const tag = m.is_free ? ' (free)' : ` (SC ${m.price_sc_input}/${m.price_sc_output})`;
53
+ console.log(` - ${label}${tag}`);
54
+ }
55
+ if (premium.length > 0) {
56
+ console.log(` ${premium.length} coming soon:`);
57
+ for (const m of premium) {
58
+ console.log(` - ${m.display_name ?? m.id}`);
59
+ }
60
+ }
61
+ console.log();
62
+ // API Key
63
+ const apiKey = (await ask(' API Key: ')).trim();
64
+ if (!apiKey) {
65
+ console.error(' API key is required.');
66
+ process.exit(1);
67
+ }
68
+ // Validate
69
+ try {
70
+ const res = await fetch(`${apiURL}/usage`, {
71
+ headers: { Authorization: `Bearer ${apiKey}` },
72
+ });
73
+ if (!res.ok)
74
+ throw new Error(`HTTP ${res.status}`);
75
+ const data = await res.json();
76
+ if (data.balance != null) {
77
+ console.log(` Balance: ${data.balance.toLocaleString()} SC`);
78
+ }
79
+ }
80
+ catch {
81
+ console.error(' Warning: Could not validate key. Saving anyway.');
82
+ }
83
+ console.log();
84
+ // Build model config
85
+ const modelConfig = {};
86
+ for (const m of available) {
87
+ const ctx = m.context_length ?? 32768;
88
+ modelConfig[m.id] = {
89
+ name: m.display_name ?? m.id,
90
+ limit: {
91
+ context: ctx,
92
+ output: Math.floor(ctx / 2),
93
+ },
94
+ };
95
+ }
96
+ // Write opencode config
97
+ const existingConfig = await readJSON(OPENCODE_CONFIG);
98
+ const defaultModelId = available.find((m) => m.id === 'Qwen3-Coder-30B-A3B-Instruct')?.id
99
+ ?? available[0]?.id
100
+ ?? 'Qwen3-Coder-30B-A3B-Instruct';
101
+ const newConfig = {
102
+ ...existingConfig,
103
+ $schema: 'https://opencode.ai/config.json',
104
+ model: `simplellm/${defaultModelId}`,
105
+ provider: {
106
+ ...(existingConfig.provider ?? {}),
107
+ simplellm: {
108
+ npm: '@ai-sdk/openai-compatible',
109
+ name: 'SimpleLLM',
110
+ options: { baseURL: apiURL },
111
+ models: modelConfig,
112
+ },
113
+ },
114
+ };
115
+ await writeJSON(OPENCODE_CONFIG, newConfig);
116
+ console.log(` Config: ${OPENCODE_CONFIG}`);
117
+ // Write auth
118
+ const existingAuth = await readJSON(OPENCODE_AUTH);
119
+ existingAuth.simplellm = apiKey;
120
+ await writeJSON(OPENCODE_AUTH, existingAuth);
121
+ console.log(` Auth: ${OPENCODE_AUTH}`);
122
+ console.log();
123
+ console.log(' Done! Start OpenCode:');
124
+ console.log();
125
+ console.log(` opencode --model simplellm/${defaultModelId}`);
126
+ console.log();
127
+ rl.close();
128
+ }
129
+ main().catch((err) => {
130
+ console.error(err);
131
+ process.exit(1);
132
+ });
133
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { SimpleLLMPlugin } from './plugin';
2
+ export { discoverModels } from './models';
3
+ export type { SimpleLLMConfig, SimpleLLMModelInfo, ModelConfig } from './types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { SimpleLLMPlugin } from './plugin';
2
+ export { discoverModels } from './models';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,7 @@
1
+ import type { ModelConfig } from './types';
2
+ /**
3
+ * Fetches enabled models from SimpleLLM's public /api/models endpoint
4
+ * and maps them to OpenCode model configs.
5
+ */
6
+ export declare function discoverModels(baseURL?: string): Promise<Record<string, ModelConfig>>;
7
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,WAAW,EAAE,MAAM,SAAS,CAAA;AAI9D;;;GAGG;AACH,wBAAsB,cAAc,CAClC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA4BtC"}
package/dist/models.js ADDED
@@ -0,0 +1,31 @@
1
+ const DEFAULT_BASE_URL = 'https://api.simplellm.eu';
2
+ /**
3
+ * Fetches enabled models from SimpleLLM's public /api/models endpoint
4
+ * and maps them to OpenCode model configs.
5
+ */
6
+ export async function discoverModels(baseURL) {
7
+ const base = baseURL ?? process.env.SIMPLELLM_BASE_URL ?? DEFAULT_BASE_URL;
8
+ const apiBase = base.replace(/\/v1\/?$/, '');
9
+ const res = await fetch(`${apiBase}/api/models`);
10
+ if (!res.ok) {
11
+ throw new Error(`Failed to fetch models: ${res.status} ${res.statusText}`);
12
+ }
13
+ const models = await res.json();
14
+ const config = {};
15
+ for (const m of models) {
16
+ if (!m.enabled)
17
+ continue;
18
+ const contextLength = m.context_length ?? 32768;
19
+ config[m.id] = {
20
+ name: m.id,
21
+ contextLength,
22
+ maxOutputTokens: Math.floor(contextLength / 2),
23
+ displayName: m.display_name ?? undefined,
24
+ isFree: m.is_free,
25
+ priceSCInput: m.price_sc_input,
26
+ priceSCOutput: m.price_sc_output,
27
+ };
28
+ }
29
+ return config;
30
+ }
31
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,0BAA0B,CAAA;AAEnD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAgB;IAEhB,MAAM,IAAI,GAAG,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,gBAAgB,CAAA;IAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAE5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,aAAa,CAAC,CAAA;IAChD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAA;IAC5E,CAAC;IAED,MAAM,MAAM,GAAyB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IACrD,MAAM,MAAM,GAAgC,EAAE,CAAA;IAE9C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,CAAC,OAAO;YAAE,SAAQ;QAExB,MAAM,aAAa,GAAG,CAAC,CAAC,cAAc,IAAI,KAAK,CAAA;QAC/C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG;YACb,IAAI,EAAE,CAAC,CAAC,EAAE;YACV,aAAa;YACb,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;YAC9C,WAAW,EAAE,CAAC,CAAC,YAAY,IAAI,SAAS;YACxC,MAAM,EAAE,CAAC,CAAC,OAAO;YACjB,YAAY,EAAE,CAAC,CAAC,cAAc;YAC9B,aAAa,EAAE,CAAC,CAAC,eAAe;SACjC,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * OpenCode plugin for SimpleLLM.
3
+ * Shows credit balance on session start, guides setup if not configured.
4
+ */
5
+ export declare const SimpleLLMPlugin: () => Promise<{
6
+ name: string;
7
+ hooks: {
8
+ 'session.created': () => Promise<{
9
+ toast: {
10
+ message: string;
11
+ variant: string;
12
+ };
13
+ } | undefined>;
14
+ };
15
+ }>;
16
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAiCA;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;EA2C3B,CAAA"}
package/dist/plugin.js ADDED
@@ -0,0 +1,78 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ const DEFAULT_BASE_URL = 'https://api.simplellm.eu';
5
+ async function getApiKey() {
6
+ if (process.env.SIMPLELLM_API_KEY)
7
+ return process.env.SIMPLELLM_API_KEY;
8
+ try {
9
+ const authPath = join(homedir(), '.local', 'share', 'opencode', 'auth.json');
10
+ const auth = JSON.parse(await readFile(authPath, 'utf-8'));
11
+ if (auth.simplellm)
12
+ return auth.simplellm;
13
+ }
14
+ catch { }
15
+ return undefined;
16
+ }
17
+ async function getBaseURL() {
18
+ if (process.env.SIMPLELLM_BASE_URL) {
19
+ return process.env.SIMPLELLM_BASE_URL.replace(/\/v1\/?$/, '');
20
+ }
21
+ try {
22
+ const configPath = join(homedir(), '.config', 'opencode', 'opencode.json');
23
+ const config = JSON.parse(await readFile(configPath, 'utf-8'));
24
+ const url = config?.provider?.simplellm?.options?.baseURL;
25
+ if (url)
26
+ return url.replace(/\/v1\/?$/, '');
27
+ }
28
+ catch { }
29
+ return DEFAULT_BASE_URL;
30
+ }
31
+ /**
32
+ * OpenCode plugin for SimpleLLM.
33
+ * Shows credit balance on session start, guides setup if not configured.
34
+ */
35
+ export const SimpleLLMPlugin = async () => {
36
+ const baseURL = await getBaseURL();
37
+ const apiKey = await getApiKey();
38
+ return {
39
+ name: 'simplellm',
40
+ hooks: {
41
+ 'session.created': async () => {
42
+ if (!apiKey) {
43
+ return {
44
+ toast: {
45
+ message: 'SimpleLLM: Not configured. Run: npx @simplellm/opencode-provider',
46
+ variant: 'warning',
47
+ },
48
+ };
49
+ }
50
+ try {
51
+ const res = await fetch(`${baseURL}/v1/usage`, {
52
+ headers: { Authorization: `Bearer ${apiKey}` },
53
+ });
54
+ if (!res.ok)
55
+ return;
56
+ const data = await res.json();
57
+ if (data.balance == null)
58
+ return;
59
+ return {
60
+ toast: {
61
+ message: `SimpleLLM: ${data.balance.toLocaleString()} SC`,
62
+ variant: data.balance < 100 ? 'warning' : 'info',
63
+ },
64
+ };
65
+ }
66
+ catch {
67
+ return {
68
+ toast: {
69
+ message: `SimpleLLM: Could not reach ${baseURL}`,
70
+ variant: 'warning',
71
+ },
72
+ };
73
+ }
74
+ },
75
+ },
76
+ };
77
+ };
78
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /** Model info as returned by SimpleLLM's /api/models endpoint */
2
+ export interface SimpleLLMModelInfo {
3
+ id: string;
4
+ display_name: string | null;
5
+ description: string | null;
6
+ context_length: number | null;
7
+ price_sc_input: number;
8
+ price_sc_output: number;
9
+ tier: 'free' | 'standard' | 'premium';
10
+ is_free: boolean;
11
+ enabled: boolean;
12
+ }
13
+ /** Resolved model config for OpenCode */
14
+ export interface ModelConfig {
15
+ name: string;
16
+ contextLength: number;
17
+ maxOutputTokens: number;
18
+ displayName?: string;
19
+ isFree: boolean;
20
+ priceSCInput: number;
21
+ priceSCOutput: number;
22
+ }
23
+ /** Options for creating the SimpleLLM provider */
24
+ export interface SimpleLLMConfig {
25
+ /** Base URL for the SimpleLLM API (default: SIMPLELLM_BASE_URL or https://api.simplellm.eu) */
26
+ baseURL?: string;
27
+ /** API key for authentication (default: SIMPLELLM_API_KEY) */
28
+ apiKey?: string;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;IACrC,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@simplellm/opencode-provider",
3
+ "version": "0.5.0",
4
+ "description": "SimpleLLM provider setup for OpenCode — one command, EU-hosted LLM inference",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "simplellm-opencode": "dist/cli.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "files": [
19
+ "dist/"
20
+ ],
21
+ "keywords": [
22
+ "opencode",
23
+ "opencode-plugin",
24
+ "simplellm",
25
+ "ai-sdk",
26
+ "llm",
27
+ "eu-hosted",
28
+ "dsgvo",
29
+ "gdpr",
30
+ "ai"
31
+ ],
32
+ "author": "Stage One Solutions UG <info@simplellm.eu>",
33
+ "homepage": "https://simplellm.eu",
34
+ "license": "MIT",
35
+ "scripts": {
36
+ "build": "tsc",
37
+ "prepublishOnly": "npm run build"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.0.0",
41
+ "typescript": "^5.9.3"
42
+ }
43
+ }