@lunacode/opencode-plugin-intelligence 0.1.0 → 0.1.2
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 +22 -4
- package/package.json +8 -2
- package/src/cache-status.js +11 -0
- package/src/index.js +25 -13
- package/src/leaderboard-cache.js +11 -12
- package/src/leaderboard-parser.js +1 -3
- package/src/model-annotations.js +0 -68
- package/src/tui.js +26 -0
- package/tui.js +1 -0
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
OpenCode v2 plugin that annotates model display names with the Artificial Analysis Intelligence Index.
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
## What it does
|
|
6
8
|
|
|
7
9
|
Matched models appear as `Model Name (AIQ: 60)` in the `/models` picker. Scores retain up to two decimal places, for example `(AIQ: 53.97)`. AIQ is the Artificial Analysis Intelligence Index score, not a leaderboard rank or a human IQ score.
|
|
@@ -12,7 +14,7 @@ It fetches the leaderboard once and writes a local cache file. It reuses that ca
|
|
|
12
14
|
|
|
13
15
|
## Install
|
|
14
16
|
|
|
15
|
-
This plugin is **OpenCode v2 only** and requires **v2.0.28 or newer v2**. It is **not compatible with OpenCode v1**.
|
|
17
|
+
This plugin is **OpenCode v2 only** and requires **v2.0.28 or newer v2** and **Node.js 20 or newer**. It is **not compatible with OpenCode v1**.
|
|
16
18
|
|
|
17
19
|
Add the package to `opencode.json` or `opencode.jsonc`:
|
|
18
20
|
|
|
@@ -37,16 +39,18 @@ Default cache path (shared with the previous version):
|
|
|
37
39
|
|
|
38
40
|
Environment variables (set on the OpenCode server):
|
|
39
41
|
|
|
40
|
-
- `OPENCODE_INTELLIGENCE_FORCE_REFRESH=1` forces a re-fetch instead of reading the cache when the plugin loads.
|
|
42
|
+
- `OPENCODE_INTELLIGENCE_FORCE_REFRESH=1` forces a re-fetch instead of reading the cache when the plugin loads. If it fails, annotations are skipped; this mode does not fall back to stale scores.
|
|
41
43
|
- `OPENCODE_INTELLIGENCE_CACHE_PATH=/path/to/leaderboard.json` overrides the cache location.
|
|
42
44
|
|
|
43
|
-
Manual refresh:
|
|
45
|
+
Manual refresh from a local checkout (uses `OPENCODE_INTELLIGENCE_CACHE_PATH` if set):
|
|
44
46
|
|
|
45
47
|
```bash
|
|
46
48
|
npm run refresh-cache
|
|
47
49
|
```
|
|
48
50
|
|
|
49
|
-
Reload the plugin or restart OpenCode after manually refreshing to load the new scores. There is no background polling timer. An expired cache refreshes on the next load; if that refresh fails, the plugin warns and uses the stale cache. Separate OpenCode server processes may each issue a refresh; explicit refreshes bypass the 24-hour expiry. If no leaderboard can be loaded, the plugin warns and leaves model names unchanged.
|
|
51
|
+
Reload the plugin or restart OpenCode after manually refreshing to load the new scores. There is no background polling timer. An expired cache refreshes on the next load; if that refresh fails, the plugin warns and uses the stale cache. Separate OpenCode server processes may each issue a refresh; explicit refreshes bypass the 24-hour expiry. If no leaderboard can be loaded, the plugin warns and leaves model names unchanged. Malformed or unreadable cache files also skip annotations; they are not automatically replaced. Use a manual or forced refresh to replace them.
|
|
52
|
+
|
|
53
|
+
Cache loading and refresh failures also show an error toast in the OpenCode TUI. The toast says whether saved scores are still in use or scores are unavailable. Console warnings remain available on headless servers.
|
|
50
54
|
|
|
51
55
|
## Matching and limitations
|
|
52
56
|
|
|
@@ -64,6 +68,20 @@ node --test
|
|
|
64
68
|
|
|
65
69
|
When changing leaderboard fetching or parsing, also run `npm run refresh-cache`.
|
|
66
70
|
|
|
71
|
+
## Publishing
|
|
72
|
+
|
|
73
|
+
GitHub Actions runs Biome and tests on branch pushes and pull requests. Pushing a `v<version>` tag runs the same checks and publishes to npm using trusted publishing. The tag must match `package.json`, for example `v0.1.0` for version `0.1.0`.
|
|
74
|
+
|
|
75
|
+
Configure a GitHub Actions trusted publisher in the npm package's settings:
|
|
76
|
+
|
|
77
|
+
- Organization or user: `SharkyRawr`
|
|
78
|
+
- Repository: `opencode-plugin-intelligence`
|
|
79
|
+
- Workflow filename: `publish.yml`
|
|
80
|
+
- Environment name: leave blank
|
|
81
|
+
- Allow direct publishing with `npm publish`
|
|
82
|
+
|
|
83
|
+
No npm token secret is required. If the package does not exist yet, publish its first version manually before configuring the trusted publisher. Use a new version for the first tag-triggered publish; npm cannot overwrite an existing version. See [npm's trusted publishing documentation](https://docs.npmjs.com/trusted-publishers) for setup details.
|
|
84
|
+
|
|
67
85
|
## License
|
|
68
86
|
|
|
69
87
|
Licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/): attribution, noncommercial use, and share-alike are required. See [LICENSE](LICENSE) for the full terms.
|
package/package.json
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunacode/opencode-plugin-intelligence",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "CC-BY-NC-SA-4.0",
|
|
5
5
|
"description": "Annotate OpenCode model names with the Artificial Analysis Intelligence Index.",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/SharkyRawr/opencode-plugin-intelligence.git"
|
|
10
|
+
},
|
|
7
11
|
"publishConfig": {
|
|
8
12
|
"access": "public"
|
|
9
13
|
},
|
|
10
14
|
"exports": {
|
|
11
|
-
".": "./index.js"
|
|
15
|
+
".": "./index.js",
|
|
16
|
+
"./tui": "./tui.js"
|
|
12
17
|
},
|
|
13
18
|
"files": [
|
|
14
19
|
"index.js",
|
|
20
|
+
"tui.js",
|
|
15
21
|
"src",
|
|
16
22
|
"scripts/refresh-cache.mjs",
|
|
17
23
|
"README.md",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Read the startup result so a TUI attaching after setup still sees failures.
|
|
2
|
+
export const cacheStatusRpc = {
|
|
3
|
+
id: "opencode-plugin-intelligence",
|
|
4
|
+
methods: {
|
|
5
|
+
status: {
|
|
6
|
+
input: { type: "null" },
|
|
7
|
+
output: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
events: {},
|
|
11
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cacheStatusRpc } from "./cache-status.js";
|
|
1
2
|
import {
|
|
2
3
|
readLeaderboardCache,
|
|
3
4
|
refreshLeaderboardCache,
|
|
@@ -9,9 +10,13 @@ export const IntelligencePlugin = {
|
|
|
9
10
|
id: "opencode-plugin-intelligence",
|
|
10
11
|
async setup(ctx) {
|
|
11
12
|
let leaderboard;
|
|
13
|
+
let errorMessage = null;
|
|
12
14
|
try {
|
|
13
15
|
const options = {
|
|
14
16
|
cachePath: process.env.OPENCODE_INTELLIGENCE_CACHE_PATH,
|
|
17
|
+
onRefreshError(error) {
|
|
18
|
+
errorMessage = `Leaderboard refresh failed; using saved scores. ${error.message ?? error}`;
|
|
19
|
+
},
|
|
15
20
|
};
|
|
16
21
|
leaderboard =
|
|
17
22
|
process.env.OPENCODE_INTELLIGENCE_FORCE_REFRESH === "1"
|
|
@@ -22,24 +27,31 @@ export const IntelligencePlugin = {
|
|
|
22
27
|
"opencode-plugin-intelligence: Failed to load Artificial Analysis leaderboard cache",
|
|
23
28
|
error,
|
|
24
29
|
);
|
|
25
|
-
|
|
30
|
+
errorMessage = `Leaderboard could not be loaded; scores are unavailable. ${error.message ?? error}`;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
let registration;
|
|
34
|
+
if (leaderboard?.models?.length) {
|
|
35
|
+
const annotateName = createModelNameAnnotator(leaderboard);
|
|
36
|
+
registration = await ctx.model.transform((editor) => {
|
|
37
|
+
for (const model of editor.list()) {
|
|
38
|
+
const name = annotateName(model.id, model.name);
|
|
39
|
+
if (name !== model.name) {
|
|
40
|
+
editor.update(model.providerID, model.id, (draft) => {
|
|
41
|
+
draft.name = name;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
|
-
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const statusRegistration = await ctx.rpc.register(cacheStatusRpc, {
|
|
48
|
+
status: async () => errorMessage,
|
|
40
49
|
});
|
|
41
50
|
|
|
42
|
-
return () =>
|
|
51
|
+
return async () => {
|
|
52
|
+
await statusRegistration.dispose();
|
|
53
|
+
await registration?.dispose();
|
|
54
|
+
};
|
|
43
55
|
},
|
|
44
56
|
};
|
|
45
57
|
|
package/src/leaderboard-cache.js
CHANGED
|
@@ -26,26 +26,23 @@ async function fetchLeaderboardHtml() {
|
|
|
26
26
|
const controller = new AbortController();
|
|
27
27
|
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
28
28
|
|
|
29
|
-
let response;
|
|
30
|
-
|
|
31
29
|
try {
|
|
32
|
-
response = await fetch(LEADERBOARD_URL, {
|
|
30
|
+
const response = await fetch(LEADERBOARD_URL, {
|
|
33
31
|
signal: controller.signal,
|
|
34
32
|
headers: {
|
|
35
|
-
"user-agent": "opencode-plugin-intelligence/0.1.
|
|
33
|
+
"user-agent": "opencode-plugin-intelligence/0.1.1",
|
|
36
34
|
},
|
|
37
35
|
});
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Artificial Analysis request failed with ${response.status}`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return await response.text();
|
|
38
43
|
} finally {
|
|
39
44
|
clearTimeout(timeout);
|
|
40
45
|
}
|
|
41
|
-
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`Artificial Analysis request failed with ${response.status}`,
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return response.text();
|
|
49
46
|
}
|
|
50
47
|
|
|
51
48
|
async function fetchLeaderboardSnapshot() {
|
|
@@ -92,6 +89,7 @@ export async function readLeaderboardCache({
|
|
|
92
89
|
directory,
|
|
93
90
|
worktree,
|
|
94
91
|
cachePath,
|
|
92
|
+
onRefreshError,
|
|
95
93
|
} = {}) {
|
|
96
94
|
const resolvedCachePath = resolveCachePath({
|
|
97
95
|
directory,
|
|
@@ -128,6 +126,7 @@ export async function readLeaderboardCache({
|
|
|
128
126
|
"opencode-plugin-intelligence: Refresh failed; using stale leaderboard cache",
|
|
129
127
|
error,
|
|
130
128
|
);
|
|
129
|
+
onRefreshError?.(error);
|
|
131
130
|
return cached;
|
|
132
131
|
}
|
|
133
132
|
}
|
|
@@ -15,9 +15,7 @@ function visit(value, predicate) {
|
|
|
15
15
|
return result;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (value && typeof value === "object") {
|
|
18
|
+
} else if (value && typeof value === "object") {
|
|
21
19
|
for (const item of Object.values(value)) {
|
|
22
20
|
const result = visit(item, predicate);
|
|
23
21
|
|
package/src/model-annotations.js
CHANGED
|
@@ -35,12 +35,6 @@ function scoreTag(score) {
|
|
|
35
35
|
return `(AIQ: ${Number(score.toFixed(2)).toString()})`;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function setModelOverride(providerConfig, modelId, displayName) {
|
|
39
|
-
providerConfig.models ||= {};
|
|
40
|
-
providerConfig.models[modelId] ||= {};
|
|
41
|
-
providerConfig.models[modelId].name = displayName;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
38
|
function baseDisplayName(name) {
|
|
45
39
|
return name.replace(INTELLIGENCE_TAG_PATTERN, "");
|
|
46
40
|
}
|
|
@@ -162,65 +156,3 @@ export function createModelNameAnnotator(leaderboard) {
|
|
|
162
156
|
return `${baseName} ${scoreTag(matchedModel.intelligenceIndex)}`;
|
|
163
157
|
};
|
|
164
158
|
}
|
|
165
|
-
|
|
166
|
-
export function applyIntelligenceAnnotations(config, leaderboard) {
|
|
167
|
-
if (!leaderboard?.models?.length) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
config.provider ||= {};
|
|
172
|
-
const annotateName = createModelNameAnnotator(leaderboard);
|
|
173
|
-
|
|
174
|
-
for (const providerConfig of Object.values(config.provider)) {
|
|
175
|
-
providerConfig.models ||= {};
|
|
176
|
-
|
|
177
|
-
for (const [modelId, modelConfig] of Object.entries(
|
|
178
|
-
providerConfig.models,
|
|
179
|
-
)) {
|
|
180
|
-
const currentName = modelConfig.name || modelId;
|
|
181
|
-
const name = annotateName(modelId, currentName);
|
|
182
|
-
if (name !== currentName) setModelOverride(providerConfig, modelId, name);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function buildProviderNamePatch(providers, leaderboard) {
|
|
188
|
-
const config = { provider: {} };
|
|
189
|
-
const candidateConfig = { provider: {} };
|
|
190
|
-
|
|
191
|
-
for (const provider of providers) {
|
|
192
|
-
if (!provider?.id || !provider?.models) {
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
candidateConfig.provider[provider.id] = { models: {} };
|
|
197
|
-
|
|
198
|
-
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
199
|
-
candidateConfig.provider[provider.id].models[modelId] = {
|
|
200
|
-
name: model.name,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
applyIntelligenceAnnotations(candidateConfig, leaderboard);
|
|
206
|
-
|
|
207
|
-
for (const provider of providers) {
|
|
208
|
-
if (!provider?.id || !provider?.models) {
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
213
|
-
const nextName =
|
|
214
|
-
candidateConfig.provider?.[provider.id]?.models?.[modelId]?.name;
|
|
215
|
-
|
|
216
|
-
if (!nextName || nextName === model.name) {
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
config.provider[provider.id] ||= { models: {} };
|
|
221
|
-
config.provider[provider.id].models[modelId] = { name: nextName };
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return config;
|
|
226
|
-
}
|
package/src/tui.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { cacheStatusRpc } from "./cache-status.js";
|
|
2
|
+
|
|
3
|
+
/** @type {import("@opencode/plugin/tui").Plugin.Definition} */
|
|
4
|
+
export default {
|
|
5
|
+
id: "opencode-plugin-intelligence",
|
|
6
|
+
async setup(ctx) {
|
|
7
|
+
try {
|
|
8
|
+
const message = await ctx.client.rpc(cacheStatusRpc).status(null, {
|
|
9
|
+
location: ctx.location,
|
|
10
|
+
});
|
|
11
|
+
if (message) {
|
|
12
|
+
ctx.ui.toast.show({
|
|
13
|
+
title: "Artificial Analysis",
|
|
14
|
+
message,
|
|
15
|
+
variant: "error",
|
|
16
|
+
duration: 10000,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.warn(
|
|
21
|
+
"opencode-plugin-intelligence: Failed to read leaderboard cache status",
|
|
22
|
+
error,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
package/tui.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./src/tui.js";
|