@rizal72/oc-models-pro 0.1.2 → 0.1.4
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/package.json +3 -3
- package/tags.ts +127 -0
- package/tui.js +1024 -0
- package/tui.tsx +0 -1016
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rizal72/oc-models-pro",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode v2 model picker with prices, context, intelligence, Go credit and the cost of a request",
|
|
6
6
|
"repository": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"files": [
|
|
12
12
|
"*.ts",
|
|
13
|
-
"
|
|
13
|
+
"tui.js",
|
|
14
14
|
"README.md"
|
|
15
15
|
],
|
|
16
16
|
"keywords": [
|
|
@@ -24,6 +24,6 @@
|
|
|
24
24
|
},
|
|
25
25
|
"exports": {
|
|
26
26
|
".": "./index.ts",
|
|
27
|
-
"./tui": "./tui.
|
|
27
|
+
"./tui": "./tui.js"
|
|
28
28
|
}
|
|
29
29
|
}
|
package/tags.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Two letters per provider, so a row says where its model comes from without spending a column.
|
|
2
|
+
//
|
|
3
|
+
// The host's own two providers keep a tag chosen by hand, and those two are reserved: they exist in
|
|
4
|
+
// every installation, so nobody else may take their letters. Everything else is derived from the
|
|
5
|
+
// provider's display name — the only place where a merged name still keeps its seams ("DeepSeek" is
|
|
6
|
+
// Deep·Seek; the id `deepseek` no longer says so).
|
|
7
|
+
//
|
|
8
|
+
// Runnable standalone for its self-check: node plugins/oc-models-pro/tags.ts --self-check
|
|
9
|
+
|
|
10
|
+
/** Tags that belong to the host's own providers, present or not. */
|
|
11
|
+
const RESERVED: Record<string, string> = {
|
|
12
|
+
opencode: "OC",
|
|
13
|
+
"opencode-go": "GO",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The words of a display name: `OpenCode Go` is two, `OpenRouter` is two as well — but a camelCase
|
|
18
|
+
* split only when the name has no separator, so a brand like `GitHub Copilot` keeps `GitHub` whole.
|
|
19
|
+
*/
|
|
20
|
+
export function words(name: string): string[] {
|
|
21
|
+
const split = name.split(/[^A-Za-z0-9]+/).filter(Boolean)
|
|
22
|
+
if (split.length > 1) return split
|
|
23
|
+
const camel = (split[0] ?? "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(" ").filter(Boolean)
|
|
24
|
+
return camel.length > 1 ? camel : split
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** What the name asks for: the initials of its words, or two letters of a single one. */
|
|
28
|
+
function preferred(name: string): string {
|
|
29
|
+
const parts = words(name)
|
|
30
|
+
const letters = parts.length > 1 ? parts.map((part) => part[0]).join("") : (parts[0] ?? "?")
|
|
31
|
+
return letters.toUpperCase().padEnd(2, "?").slice(0, 2)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A tag per provider id, unique and order-independent. Derived in alphabetical order of id, so the
|
|
36
|
+
* same catalogue always yields the same tags whatever order the providers arrive in.
|
|
37
|
+
*/
|
|
38
|
+
export function tagsFor(providers: Array<{ id: string; name: string }>): Map<string, string> {
|
|
39
|
+
const taken = new Set(Object.values(RESERVED))
|
|
40
|
+
const tags = new Map<string, string>()
|
|
41
|
+
const order = [...providers].sort((a, b) => a.id.localeCompare(b.id))
|
|
42
|
+
|
|
43
|
+
for (const provider of order) {
|
|
44
|
+
const fixed = RESERVED[provider.id]
|
|
45
|
+
if (fixed) tags.set(provider.id, fixed)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const provider of order) {
|
|
49
|
+
if (tags.has(provider.id)) continue
|
|
50
|
+
const letters = (words(provider.name).join("") || "?").toUpperCase()
|
|
51
|
+
const first = letters[0] ?? "?"
|
|
52
|
+
let tag = preferred(provider.name)
|
|
53
|
+
if (taken.has(tag)) {
|
|
54
|
+
// Walk the name for a free pair, so `Google` steps aside from the reserved GO.
|
|
55
|
+
tag = ""
|
|
56
|
+
for (const letter of letters.slice(1)) {
|
|
57
|
+
// A doubled letter reads as a typo ("GG"): prefer anything else.
|
|
58
|
+
if (letter === first) continue
|
|
59
|
+
const candidate = first + letter
|
|
60
|
+
if (!taken.has(candidate)) {
|
|
61
|
+
tag = candidate
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (taken.has(tag) || !tag) {
|
|
67
|
+
tag = ""
|
|
68
|
+
for (let digit = 2; digit <= 9; digit++) {
|
|
69
|
+
if (!taken.has(first + digit)) {
|
|
70
|
+
tag = first + digit
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!tag) tag = "?" + (tags.size % 10)
|
|
76
|
+
taken.add(tag)
|
|
77
|
+
tags.set(provider.id, tag)
|
|
78
|
+
}
|
|
79
|
+
return tags
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Self-check: node plugins/oc-models-pro/tags.ts --self-check
|
|
83
|
+
if (process.argv[2] === "--self-check") {
|
|
84
|
+
const fail = (message: string): never => {
|
|
85
|
+
throw new Error("self-check: " + message)
|
|
86
|
+
}
|
|
87
|
+
const at = (providers: Array<{ id: string; name: string }>, id: string): string =>
|
|
88
|
+
tagsFor(providers).get(id) ?? fail("no tag for " + id)
|
|
89
|
+
|
|
90
|
+
if (at([{ id: "opencode", name: "anything" }], "opencode") !== "OC") fail("opencode must stay OC")
|
|
91
|
+
if (at([{ id: "opencode-go", name: "whatever" }], "opencode-go") !== "GO") fail("opencode-go must stay GO")
|
|
92
|
+
if (at([{ id: "openrouter", name: "OpenRouter" }], "openrouter") !== "OR") fail("OpenRouter must be OR")
|
|
93
|
+
if (at([{ id: "deepseek", name: "DeepSeek" }], "deepseek") !== "DS") fail("DeepSeek must be DS")
|
|
94
|
+
if (at([{ id: "github-copilot", name: "GitHub Copilot" }], "github-copilot") !== "GC") {
|
|
95
|
+
fail("GitHub Copilot must be GC")
|
|
96
|
+
}
|
|
97
|
+
// Reserved letters are reserved even when the host's provider is not in the list.
|
|
98
|
+
if (at([{ id: "google", name: "Google" }], "google") !== "GL") fail("Google must step aside from GO")
|
|
99
|
+
|
|
100
|
+
const twins = tagsFor([
|
|
101
|
+
{ id: "acme-one", name: "Acme" },
|
|
102
|
+
{ id: "acme-two", name: "Acme" },
|
|
103
|
+
])
|
|
104
|
+
if (new Set(twins.values()).size !== 2) fail("two providers share a tag")
|
|
105
|
+
|
|
106
|
+
const forward = tagsFor([
|
|
107
|
+
{ id: "beta", name: "Beta" },
|
|
108
|
+
{ id: "alpha", name: "Alpha" },
|
|
109
|
+
{ id: "google", name: "Google" },
|
|
110
|
+
])
|
|
111
|
+
const backward = tagsFor([
|
|
112
|
+
{ id: "google", name: "Google" },
|
|
113
|
+
{ id: "alpha", name: "Alpha" },
|
|
114
|
+
{ id: "beta", name: "Beta" },
|
|
115
|
+
])
|
|
116
|
+
if ([...forward].map(String).sort().join() !== [...backward].map(String).sort().join()) {
|
|
117
|
+
fail("the input order changes the tags")
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log(
|
|
121
|
+
"self-check ok: " +
|
|
122
|
+
[...forward].map(([id, tag]) => id + "=" + tag).join(" ") +
|
|
123
|
+
" (" +
|
|
124
|
+
words("OpenCode Go").join("|") +
|
|
125
|
+
")",
|
|
126
|
+
)
|
|
127
|
+
}
|