@plurnk/plurnk-aliases 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/README.md +50 -0
- package/SPEC.md +47 -0
- package/dist/aliases.d.ts +4 -0
- package/dist/aliases.d.ts.map +1 -0
- package/dist/aliases.js +67 -0
- package/dist/aliases.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +7 -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 +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @plurnk/plurnk-aliases
|
|
2
|
+
|
|
3
|
+
Zero-dependency parser for the plurnk model-alias cascade. Vendor-agnostic, MIT.
|
|
4
|
+
|
|
5
|
+
Resolves `PLURNK_MODEL_<alias>=<provider>/<model>` env vars (plus per-alias
|
|
6
|
+
`PLURNK_BASEURL_<alias>` endpoint overrides) into structured `ProviderAlias`
|
|
7
|
+
records. Extracted from `@plurnk/plurnk-providers` so a **thin client** can
|
|
8
|
+
resolve aliases from its own always-fresh env — and send the daemon a resolved
|
|
9
|
+
`{ provider, model, baseUrl? }` — without pulling the provider-instantiation or
|
|
10
|
+
tokenizer machinery. `@plurnk/plurnk-providers` depends on this and re-exports
|
|
11
|
+
the same surface, so it stays the single source of truth.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
npm install @plurnk/plurnk-aliases
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Node ≥26, ESM.
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { parseAliasesFromEnv, resolveActiveAlias, type ProviderAlias } from "@plurnk/plurnk-aliases";
|
|
25
|
+
|
|
26
|
+
parseAliasesFromEnv(env = process.env): ProviderAlias[] // every declared alias
|
|
27
|
+
resolveActiveAlias(env = process.env): ProviderAlias | null // the PLURNK_MODEL-selected one, or null
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
interface ProviderAlias {
|
|
32
|
+
readonly alias: string; // lowercase, .env key suffix downcased
|
|
33
|
+
readonly provider: string; // "openai", "openrouter", "ollama", …
|
|
34
|
+
readonly model: string; // provider-native id; may contain "/"
|
|
35
|
+
readonly baseUrl?: string; // PLURNK_BASEURL_<alias> override, when set
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To resolve a named alias (e.g. from `loop.run({ alias })`), parse and find:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const alias = parseAliasesFromEnv().find((a) => a.alias === name.toLowerCase());
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Contract
|
|
46
|
+
|
|
47
|
+
The provider segment is the **first** `/`-delimited field; the model id is the
|
|
48
|
+
remainder (it may itself contain `/`). Aliases are case-folded. Fail-hard on a
|
|
49
|
+
case-folding collision (two keys → one alias) and on a `PLURNK_BASEURL_*`
|
|
50
|
+
override with no matching alias (a typo, never a silent no-op). See `SPEC.md`.
|
package/SPEC.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @plurnk/plurnk-aliases — contract
|
|
2
|
+
|
|
3
|
+
The canonical parser for the plurnk model-alias cascade. Zero runtime
|
|
4
|
+
dependencies. `@plurnk/plurnk-providers` depends on this package and re-exports
|
|
5
|
+
its surface unchanged; thin clients depend on it directly to resolve aliases
|
|
6
|
+
without the provider/tokenizer machinery.
|
|
7
|
+
|
|
8
|
+
## The cascade
|
|
9
|
+
|
|
10
|
+
- **`PLURNK_MODEL_<alias>=<provider>/<model>`** declares an alias. The provider
|
|
11
|
+
segment is the **first** `/`-delimited field; the model id is everything after
|
|
12
|
+
that first `/` (it MAY contain further `/`, e.g. `openrouter/anthropic/claude-…`).
|
|
13
|
+
- **`PLURNK_MODEL=<alias>`** selects the active alias at boot.
|
|
14
|
+
- **`PLURNK_BASEURL_<alias>`** attaches a per-alias endpoint override — the one
|
|
15
|
+
thing a per-provider base-URL var can't express (two aliases on the same
|
|
16
|
+
provider name pointing at different self-hosted boxes).
|
|
17
|
+
|
|
18
|
+
Alias keys are **case-folded** (the suffix after `PLURNK_MODEL_` / `PLURNK_BASEURL_`
|
|
19
|
+
is downcased), so `PLURNK_MODEL_opus` and `PLURNK_MODEL_OPUS` name the same alias.
|
|
20
|
+
|
|
21
|
+
## `ProviderAlias`
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
interface ProviderAlias {
|
|
25
|
+
readonly alias: string;
|
|
26
|
+
readonly provider: string;
|
|
27
|
+
readonly model: string;
|
|
28
|
+
readonly baseUrl?: string; // present only when PLURNK_BASEURL_<alias> is set
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Functions
|
|
33
|
+
|
|
34
|
+
- **`parseAliasesFromEnv(env = process.env): ProviderAlias[]`** — every declared
|
|
35
|
+
alias. Skips entries with an empty value or no `/` in the value.
|
|
36
|
+
- **`resolveActiveAlias(env = process.env): ProviderAlias | null`** — the alias
|
|
37
|
+
named by `PLURNK_MODEL` (case-insensitive), or `null` when `PLURNK_MODEL` is
|
|
38
|
+
unset or names no declared alias.
|
|
39
|
+
|
|
40
|
+
## Fail-hard rules
|
|
41
|
+
|
|
42
|
+
- **Case-folding collision** — two `PLURNK_MODEL_*` keys that downcase to the
|
|
43
|
+
same alias throw (`Duplicate provider alias "<alias>"`). No silent pick.
|
|
44
|
+
- **Dangling override** — a `PLURNK_BASEURL_*` whose alias has no matching
|
|
45
|
+
`PLURNK_MODEL_*` throws (a typo, not a silent no-op).
|
|
46
|
+
|
|
47
|
+
Both are contract violations surfaced loudly, never recovered.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aliases.d.ts","sourceRoot":"","sources":["../src/aliases.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAkBhD,eAAO,MAAM,mBAAmB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,aAAa,EAuBvF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,aAAa,GAAG,IAKzF,CAAC"}
|
package/dist/aliases.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// The plurnk model-alias cascade — pure env parsing, zero runtime deps.
|
|
2
|
+
//
|
|
3
|
+
// PLURNK_MODEL_<alias>=<provider>/<model> declares an alias; PLURNK_MODEL=<alias>
|
|
4
|
+
// selects the active one at boot. The provider segment is the first "/"-delimited
|
|
5
|
+
// field; the model id is the remainder (it may itself contain "/"). Aliases are
|
|
6
|
+
// case-folded (the .env key suffix downcased). PLURNK_BASEURL_<alias> attaches a
|
|
7
|
+
// per-alias endpoint override — the one thing a per-provider base-URL var can't
|
|
8
|
+
// express (two aliases on the same provider name pointing at different boxes).
|
|
9
|
+
//
|
|
10
|
+
// Extracted from @plurnk/plurnk-providers so a thin consumer can resolve aliases
|
|
11
|
+
// from its own (always-fresh) env without pulling the provider/tokenizer machinery.
|
|
12
|
+
// PLURNK_BASEURL_<alias>: per-alias endpoint override, case-folded on the alias
|
|
13
|
+
// to match PLURNK_MODEL_<alias>. Lets two aliases on the same provider name target
|
|
14
|
+
// different self-hosted boxes (openai/ollama), the one thing a per-name base-URL
|
|
15
|
+
// var can't express.
|
|
16
|
+
const parseBaseUrls = (env) => {
|
|
17
|
+
const out = new Map();
|
|
18
|
+
for (const [key, value] of Object.entries(env)) {
|
|
19
|
+
if (value === undefined || value.length === 0)
|
|
20
|
+
continue;
|
|
21
|
+
if (!key.startsWith("PLURNK_BASEURL_"))
|
|
22
|
+
continue;
|
|
23
|
+
const aliasRaw = key.slice("PLURNK_BASEURL_".length);
|
|
24
|
+
if (aliasRaw.length === 0)
|
|
25
|
+
continue;
|
|
26
|
+
out.set(aliasRaw.toLowerCase(), value);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
};
|
|
30
|
+
export const parseAliasesFromEnv = (env = process.env) => {
|
|
31
|
+
const out = [];
|
|
32
|
+
const seen = new Set();
|
|
33
|
+
const baseUrls = parseBaseUrls(env);
|
|
34
|
+
for (const [key, value] of Object.entries(env)) {
|
|
35
|
+
if (value === undefined || value.length === 0)
|
|
36
|
+
continue;
|
|
37
|
+
if (!key.startsWith("PLURNK_MODEL_"))
|
|
38
|
+
continue;
|
|
39
|
+
const aliasRaw = key.slice("PLURNK_MODEL_".length);
|
|
40
|
+
if (aliasRaw.length === 0)
|
|
41
|
+
continue;
|
|
42
|
+
const slash = value.indexOf("/");
|
|
43
|
+
if (slash <= 0)
|
|
44
|
+
continue;
|
|
45
|
+
const alias = aliasRaw.toLowerCase();
|
|
46
|
+
// Aliases are case-folded, so PLURNK_MODEL_opus and PLURNK_MODEL_OPUS
|
|
47
|
+
// collide. Surface the ambiguity rather than silently picking one.
|
|
48
|
+
if (seen.has(alias))
|
|
49
|
+
throw new Error(`Duplicate provider alias "${alias}": multiple PLURNK_MODEL_* keys case-fold to the same alias. Rename one.`);
|
|
50
|
+
seen.add(alias);
|
|
51
|
+
const baseUrl = baseUrls.get(alias);
|
|
52
|
+
out.push({ alias, provider: value.slice(0, slash), model: value.slice(slash + 1), ...(baseUrl !== undefined ? { baseUrl } : {}) });
|
|
53
|
+
}
|
|
54
|
+
// A base-URL override with no matching alias is a typo, not a silent no-op.
|
|
55
|
+
const unmatched = [...baseUrls.keys()].filter((a) => !seen.has(a));
|
|
56
|
+
if (unmatched.length > 0)
|
|
57
|
+
throw new Error(`PLURNK_BASEURL_* override(s) with no matching PLURNK_MODEL_* alias: ${unmatched.join(", ")}. Declare the alias or remove the override.`);
|
|
58
|
+
return out;
|
|
59
|
+
};
|
|
60
|
+
export const resolveActiveAlias = (env = process.env) => {
|
|
61
|
+
const selected = env.PLURNK_MODEL;
|
|
62
|
+
if (selected === undefined || selected.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
const aliases = parseAliasesFromEnv(env);
|
|
65
|
+
return aliases.find((a) => a.alias === selected.toLowerCase()) ?? null;
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=aliases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aliases.js","sourceRoot":"","sources":["../src/aliases.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,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC7B"}
|
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,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@plurnk/plurnk-aliases",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Zero-dependency parser for the plurnk model-alias cascade (PLURNK_MODEL_<alias>=<provider>/<model>, PLURNK_BASEURL_<alias>). Shared by @plurnk/plurnk-providers and thin plurnk clients that resolve aliases without pulling the provider/tokenizer machinery.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"plurnk",
|
|
7
|
+
"alias",
|
|
8
|
+
"provider",
|
|
9
|
+
"env"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/plurnk/plurnk-aliases#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/plurnk/plurnk-aliases/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/plurnk/plurnk-aliases.git"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=26"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "@wikitopian",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/**/*",
|
|
37
|
+
"README.md",
|
|
38
|
+
"SPEC.md"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test:lint": "tsc --noEmit",
|
|
42
|
+
"test:unit": "node --test src/**/*.test.ts",
|
|
43
|
+
"test": "npm run test:lint && npm run test:unit",
|
|
44
|
+
"build:dist": "tsc -p tsconfig.build.json",
|
|
45
|
+
"build": "npm run build:dist",
|
|
46
|
+
"prepare": "npm run build"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^26.0.0",
|
|
50
|
+
"typescript": "^6.0.3"
|
|
51
|
+
}
|
|
52
|
+
}
|