@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 +52 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +133 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +7 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +31 -0
- package/dist/models.js.map +1 -0
- package/dist/plugin.d.ts +16 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +78 -0
- package/dist/plugin.js.map +1 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
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 @@
|
|
|
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
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|
package/dist/models.d.ts
ADDED
|
@@ -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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -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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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
+
}
|