smallm 0.5.0 → 0.6.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 +12 -6
- package/dist/cache-file.d.ts +38 -4
- package/dist/cache-file.d.ts.map +1 -1
- package/dist/cache-file.js +314 -25
- package/dist/cache-file.js.map +1 -1
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +37 -8
- package/dist/cli.js.map +1 -1
- package/dist/http.d.ts +19 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +57 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/params.d.ts +6 -1
- package/dist/params.d.ts.map +1 -1
- package/dist/params.js +15 -2
- package/dist/params.js.map +1 -1
- package/dist/query.d.ts +9 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +73 -7
- package/dist/query.js.map +1 -1
- package/dist/registry/huggingface.d.ts +2 -0
- package/dist/registry/huggingface.d.ts.map +1 -1
- package/dist/registry/huggingface.js +81 -11
- package/dist/registry/huggingface.js.map +1 -1
- package/dist/registry/ollama.d.ts +2 -0
- package/dist/registry/ollama.d.ts.map +1 -1
- package/dist/registry/ollama.js +42 -12
- package/dist/registry/ollama.js.map +1 -1
- package/dist/scorer.d.ts.map +1 -1
- package/dist/scorer.js +2 -1
- package/dist/scorer.js.map +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts +22 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +114 -0
- package/dist/validate.js.map +1 -0
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@ Find the right small language model (SLM) for a task, based on given context.
|
|
|
10
10
|
npm install smallm
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
Requires Node.js 18 or newer.
|
|
14
|
+
|
|
15
|
+
**Upgrading from 0.5?** v0.6.0 is a security release with a few breaking changes (no more imports from inside `dist/`, two stricter CLI flag checks, length limits on `task`/`domain`, a new default cache location). See [Upgrading from 0.5](./CHANGELOG.md#upgrading-from-05).
|
|
16
|
+
|
|
13
17
|
## Usage
|
|
14
18
|
|
|
15
19
|
```ts
|
|
@@ -53,7 +57,7 @@ await findModels({ task: "chat", contextLength: 4096, hardware: { type: "gpu", v
|
|
|
53
57
|
await findModels({ task: "chat", contextLength: 4096, hardware: "gpu-low" }); // still works
|
|
54
58
|
```
|
|
55
59
|
|
|
56
|
-
**Prerequisite for `providers: ["ollama"]`:** Ollama must be running locally (`ollama serve`, or the desktop app) and reachable at `http://
|
|
60
|
+
**Prerequisite for `providers: ["ollama"]`:** Ollama must be running locally (`ollama serve`, or the desktop app) and reachable at `http://127.0.0.1:11434` (IPv4 loopback, Ollama's default bind address). If it isn't, `smallm` doesn't throw or crash the whole call — it logs a warning and simply returns no Ollama candidates, so a mixed `["huggingface", "ollama"]` query still returns your HuggingFace results.
|
|
57
61
|
|
|
58
62
|
**Important:** `HardwareSpec.vramGB` filtering uses an **estimated** footprint (~2GB VRAM per 1B params, a common fp16 rule of thumb) — not a measured or authoritative figure. It's meant to rule out obviously-too-large models, not to guarantee something fits.
|
|
59
63
|
|
|
@@ -92,7 +96,7 @@ await findModels({
|
|
|
92
96
|
await findModels({ task: "chat", contextLength: 4096, hardware: "gpu-low" });
|
|
93
97
|
```
|
|
94
98
|
|
|
95
|
-
**Ollama prerequisite:** `providers: ["ollama"]` (or including `"ollama"` in a multi-provider list) expects a local Ollama installation running at `http://
|
|
99
|
+
**Ollama prerequisite:** `providers: ["ollama"]` (or including `"ollama"` in a multi-provider list) expects a local Ollama installation running at `http://127.0.0.1:11434`. If it's not running or not installed, `smallm` doesn't throw or crash the whole query — it logs a warning and simply contributes zero Ollama candidates, so a mixed `["huggingface", "ollama"]` query still returns HuggingFace results normally. Ollama itself is never installed, started, or asked to download models by this package — it only queries what's already available locally.
|
|
96
100
|
|
|
97
101
|
**`vramGB` filtering is a heuristic, not a measured constraint.** There's no standard "how much VRAM does model X need" figure available from either provider's listing API, so `smallm` estimates it from `paramsB` using a commonly-cited rule of thumb (~2GB VRAM per 1B parameters, roughly fp16 inference). A model quantized to 4-bit would actually need much less — this estimate is conservative/pessimistic in that case. Treat `vramGB` filtering as a rough guide, not a guarantee.
|
|
98
102
|
|
|
@@ -124,16 +128,16 @@ The single public entry point. Validates the query, fetches live candidates from
|
|
|
124
128
|
|
|
125
129
|
| Field | Type | Required | Notes |
|
|
126
130
|
|---|---|---|---|
|
|
127
|
-
| `task` | `"summarize" \| "classify" \| "extract" \| "chat" \| "code" \| "translate" \| string` | yes | |
|
|
131
|
+
| `task` | `"summarize" \| "classify" \| "extract" \| "chat" \| "code" \| "translate" \| string` | yes | Max 512 characters |
|
|
128
132
|
| `contextLength` | `number` | yes | Hard filter (tokens) |
|
|
129
133
|
| `hardware` | `"cpu" \| "gpu-low" \| "gpu-high" \| { type: "cpu" \| "gpu"; vramGB?: number }` | no | String enum: required alongside `maxLatencyMs` to enforce latency filtering (v0.2). Object form (v0.4): `vramGB` enables the estimated-footprint hard filter. |
|
|
130
|
-
| `domain` | `string` | no | Used in scoring, not filtering |
|
|
134
|
+
| `domain` | `string` | no | Used in scoring, not filtering. Max 128 characters |
|
|
131
135
|
| `maxParamsB` | `number` | no | Hard filter, e.g. `7` = "under 7B" |
|
|
132
136
|
| `maxLatencyMs` | `number` | no | **v0.2:** real hard filter when a benchmark entry exists for the (model, string-enum `hardware`) pair. No entry = not enforced. |
|
|
133
137
|
| `scoringMode` | `"rule" \| "embedding" \| "hybrid"` | no | **v0.3:** which scoring strategy to use. Defaults to `"rule"`. |
|
|
134
|
-
| `providers` | `("huggingface" \| "ollama")[]` | no | **v0.4:** which registries to query. Defaults to `["huggingface"]`. |
|
|
138
|
+
| `providers` | `("huggingface" \| "ollama")[]` | no | **v0.4:** which registries to query. Defaults to `["huggingface"]`. Duplicates are ignored. |
|
|
135
139
|
| `limit` | `number` | no | Defaults to `5` |
|
|
136
|
-
| `cacheOptions` | `{ dir?: string; ttlMs?: number }` | no | **v0.2:** configure the file-based cache. Defaults:
|
|
140
|
+
| `cacheOptions` | `{ dir?: string; ttlMs?: number }` | no | **v0.2:** configure the file-based cache. Defaults: a per-user cache dir (`$XDG_CACHE_HOME/smallm`, `~/.cache/smallm`, `~/Library/Caches/smallm`, or `%LOCALAPPDATA%\smallm\cache`), 5-minute TTL. On Linux/macOS the dir must be owned by you and not world-writable. Windows UNC/network paths are rejected; ACLs of a custom Windows dir are not checked. TTLs above 7 days are clamped. If the default dir is unusable, an in-memory cache is used. Never pass untrusted input as `dir`. |
|
|
137
141
|
|
|
138
142
|
### `ModelMatch`
|
|
139
143
|
|
|
@@ -185,6 +189,8 @@ Unknown metadata (e.g. undetectable param count, no benchmark entry, unreachable
|
|
|
185
189
|
- Ollama candidates never have a `pipeline_tag`, so the task hard filter always lets them through (same "unknown, don't exclude" rule as everywhere else) — task relevance for Ollama results relies entirely on scoring, not filtering.
|
|
186
190
|
- Sub-billion-parameter models (e.g. `125M`) aren't detected by the size regex and report `paramsB: null`.
|
|
187
191
|
- File-based cache only — no database engine. Cache entries are per-process-agnostic (survive restarts) but there's no cross-machine sharing.
|
|
192
|
+
- **On Windows, a custom cache directory's permissions aren't checked** (Node has no ACL API). Don't point `cacheOptions.dir` / `--cache-dir` at a folder other users can write to; the default under `%LOCALAPPDATA%` is per-user. On Linux/macOS, unsafe directories are refused.
|
|
193
|
+
- **Don't pass untrusted input as `cacheOptions`.** If you expose `findModels()` to end users (e.g. in a web service), build `cacheOptions` yourself rather than taking it from the request.
|
|
188
194
|
|
|
189
195
|
## CLI (v0.5, optional)
|
|
190
196
|
|
package/dist/cache-file.d.ts
CHANGED
|
@@ -1,23 +1,56 @@
|
|
|
1
1
|
import { CacheOptions, HFModel } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Per-user cache directory. Deliberately NOT os.tmpdir(): on Linux/macOS
|
|
4
|
+
* that is shared and world-writable (/tmp), which let any local user
|
|
5
|
+
* pre-create the cache dir and plant poisoned entries or symlinks.
|
|
6
|
+
*/
|
|
7
|
+
export declare function defaultCacheDir(): string;
|
|
2
8
|
/**
|
|
3
9
|
* File-based TTL cache — same conceptual interface as MVP's in-memory
|
|
4
10
|
* TTLCache (get/set/clear), but each entry is a JSON file on disk, so it
|
|
5
11
|
* survives process restarts. No database engine, per the v0.2 guide's
|
|
6
12
|
* explicit "don't introduce SQLite/Postgres/etc." rule — this is still a
|
|
7
13
|
* small library, not a service.
|
|
14
|
+
*
|
|
15
|
+
* Hardening:
|
|
16
|
+
* - the directory is re-verified before every get/set (not just once), so
|
|
17
|
+
* a directory deleted and re-created by someone else is noticed;
|
|
18
|
+
* - entries are written atomically (exclusive-create temp file, mode
|
|
19
|
+
* 0600, then rename — never follows a planted symlink);
|
|
20
|
+
* - reads open with O_NOFOLLOW|O_NONBLOCK and check the open descriptor,
|
|
21
|
+
* so a swap to a symlink/FIFO/device after the check doesn't matter,
|
|
22
|
+
* and read at most MAX_CACHE_FILE_BYTES;
|
|
23
|
+
* - entries whose expiry exceeds the TTL or whose value fails the
|
|
24
|
+
* validator are discarded;
|
|
25
|
+
* - expired files are swept periodically, with a file-count cap.
|
|
8
26
|
*/
|
|
9
27
|
export declare class FileTTLCache<T> {
|
|
10
28
|
private dir;
|
|
11
29
|
private ttlMs;
|
|
12
|
-
|
|
30
|
+
private isDefaultDir;
|
|
31
|
+
private validate?;
|
|
32
|
+
private lastSweep;
|
|
33
|
+
constructor(options?: CacheOptions, validate?: (value: unknown) => value is T);
|
|
34
|
+
/** Re-checks the directory; a failure makes get() miss and set() skip, never throw. */
|
|
35
|
+
private dirIsUsable;
|
|
13
36
|
/** Maps a cache key to a safe filename (keys can contain arbitrary characters). */
|
|
14
37
|
private filePathFor;
|
|
38
|
+
/** Reads an entry file safely, or returns undefined (and removes junk) if it isn't a small regular file. */
|
|
39
|
+
private readEntryFile;
|
|
15
40
|
get(key: string): T | undefined;
|
|
41
|
+
/** Best-effort: a failed cache write never fails the caller. */
|
|
16
42
|
set(key: string, value: T): void;
|
|
43
|
+
/**
|
|
44
|
+
* Removes expired entries (by mtime) and stale temp files, then trims to
|
|
45
|
+
* MAX_CACHE_FILES. Without this, an entry is only deleted if its exact key
|
|
46
|
+
* is read again, so one-off keys accumulate forever.
|
|
47
|
+
*/
|
|
48
|
+
private sweep;
|
|
49
|
+
/** Removes only files this cache created (hash-named entries and leftover temp files) — never anything else in the directory. */
|
|
17
50
|
clear(): void;
|
|
18
51
|
private deleteFile;
|
|
19
52
|
}
|
|
20
|
-
/** Sets (or resets) the cache options used
|
|
53
|
+
/** Sets (or resets) the default cache options used when a call passes no cacheOptions. Throws if the directory is unusable. */
|
|
21
54
|
export declare function configureCandidateModelsCache(options?: CacheOptions): void;
|
|
22
55
|
/**
|
|
23
56
|
* (v0.2) Cached wrapper around fetchCandidateModels, backed by the
|
|
@@ -32,9 +65,10 @@ export declare function fetchCandidateModelsCached(task: string): Promise<HFMode
|
|
|
32
65
|
* requested provider's candidate list independently (keyed by
|
|
33
66
|
* `${providerName}:${task}`, so HuggingFace and Ollama results for the
|
|
34
67
|
* same task never collide). Not provider-specific itself — any async
|
|
35
|
-
* producer of RawModel[] can use this.
|
|
68
|
+
* producer of RawModel[] can use this. Pass `options` to use a cache
|
|
69
|
+
* scoped to those options instead of the module-level default.
|
|
36
70
|
*/
|
|
37
|
-
export declare function getOrFetchCached(key: string, fetchFn: () => Promise<HFModel[]
|
|
71
|
+
export declare function getOrFetchCached(key: string, fetchFn: () => Promise<HFModel[]>, options?: CacheOptions): Promise<HFModel[]>;
|
|
38
72
|
/** Exposed for tests that need to reset cache state between runs. */
|
|
39
73
|
export declare function clearCandidateModelsCache(): void;
|
|
40
74
|
//# sourceMappingURL=cache-file.d.ts.map
|
package/dist/cache-file.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-file.d.ts","sourceRoot":"","sources":["../src/cache-file.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"cache-file.d.ts","sourceRoot":"","sources":["../src/cache-file.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AA+BhD;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAwBxC;AA2DD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,YAAY,CAAC,CAAC;IACzB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,QAAQ,CAAC,CAAiC;IAClD,OAAO,CAAC,SAAS,CAAK;IAEtB,YAAY,OAAO,GAAE,YAAiB,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,EAQhF;IAED,uFAAuF;IACvF,OAAO,CAAC,WAAW;IASnB,mFAAmF;IACnF,OAAO,CAAC,WAAW;IAKnB,4GAA4G;IAC5G,OAAO,CAAC,aAAa;IAqCrB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAqC9B;IAED,gEAAgE;IAChE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAgB/B;IAED;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAyCb,iIAAiI;IACjI,KAAK,IAAI,IAAI,CAYZ;IAED,OAAO,CAAC,UAAU;CAOnB;AA2BD,+HAA+H;AAC/H,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,YAAiB,GAAG,IAAI,CAE9E;AAiCD;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAEjF;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,EACjC,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,EAAE,CAAC,CAgBpB;AAED,qEAAqE;AACrE,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
package/dist/cache-file.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.FileTTLCache = void 0;
|
|
37
|
+
exports.defaultCacheDir = defaultCacheDir;
|
|
37
38
|
exports.configureCandidateModelsCache = configureCandidateModelsCache;
|
|
38
39
|
exports.fetchCandidateModelsCached = fetchCandidateModelsCached;
|
|
39
40
|
exports.getOrFetchCached = getOrFetchCached;
|
|
@@ -42,36 +43,205 @@ const fs = __importStar(require("fs"));
|
|
|
42
43
|
const path = __importStar(require("path"));
|
|
43
44
|
const os = __importStar(require("os"));
|
|
44
45
|
const crypto = __importStar(require("crypto"));
|
|
46
|
+
const errors_1 = require("./errors");
|
|
45
47
|
const huggingface_1 = require("./registry/huggingface");
|
|
48
|
+
const validate_1 = require("./validate");
|
|
49
|
+
const cache_1 = require("./cache");
|
|
46
50
|
const DEFAULT_TTL_MS = 5 * 60 * 1000; // 5 minutes — same default as MVP's in-memory cache
|
|
47
|
-
|
|
51
|
+
/** Cache files larger than this are ignored — a legit candidate list is a few KB. */
|
|
52
|
+
const MAX_CACHE_FILE_BYTES = 5 * 1024 * 1024;
|
|
53
|
+
/** Expired files are swept at most this often per cache instance (on set). */
|
|
54
|
+
const SWEEP_INTERVAL_MS = 60 * 1000;
|
|
55
|
+
/** After sweeping expired files, keep at most this many (oldest removed first). */
|
|
56
|
+
const MAX_CACHE_FILES = 1000;
|
|
57
|
+
/** Leftover temp files (from a crash mid-write) older than this are removed by the sweep. */
|
|
58
|
+
const STALE_TEMP_MS = 60 * 1000;
|
|
59
|
+
/** Only files matching these are ever read or deleted by the cache — see clear()/sweep(). */
|
|
60
|
+
const CACHE_FILE_PATTERN = /^[0-9a-f]{64}\.json$/;
|
|
61
|
+
const TEMP_FILE_PATTERN = /^[0-9a-f]{64}\.json\.\d+\.[0-9a-f]{16}\.tmp$/;
|
|
62
|
+
/**
|
|
63
|
+
* Open flags for reading an entry: never follow a symlink (ELOOP instead)
|
|
64
|
+
* and never block on a FIFO. Both constants are POSIX-only; on Windows they
|
|
65
|
+
* are undefined and the lstat pre-check in readEntryFile() is what applies.
|
|
66
|
+
*/
|
|
67
|
+
const READ_FLAGS = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0);
|
|
68
|
+
const isPosix = process.platform !== "win32" && typeof process.getuid === "function";
|
|
69
|
+
/**
|
|
70
|
+
* Per-user cache directory. Deliberately NOT os.tmpdir(): on Linux/macOS
|
|
71
|
+
* that is shared and world-writable (/tmp), which let any local user
|
|
72
|
+
* pre-create the cache dir and plant poisoned entries or symlinks.
|
|
73
|
+
*/
|
|
74
|
+
function defaultCacheDir() {
|
|
75
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
76
|
+
if (xdg && path.isAbsolute(xdg))
|
|
77
|
+
return path.join(xdg, "smallm");
|
|
78
|
+
let home = "";
|
|
79
|
+
try {
|
|
80
|
+
home = os.homedir();
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Some sandboxes have no passwd entry; fall through to the temp fallback.
|
|
84
|
+
}
|
|
85
|
+
if (process.platform === "win32") {
|
|
86
|
+
const localAppData = process.env.LOCALAPPDATA || (home ? path.join(home, "AppData", "Local") : "");
|
|
87
|
+
if (localAppData)
|
|
88
|
+
return path.join(localAppData, "smallm", "cache");
|
|
89
|
+
}
|
|
90
|
+
else if (process.platform === "darwin" && home) {
|
|
91
|
+
return path.join(home, "Library", "Caches", "smallm");
|
|
92
|
+
}
|
|
93
|
+
else if (home) {
|
|
94
|
+
return path.join(home, ".cache", "smallm");
|
|
95
|
+
}
|
|
96
|
+
// No home directory at all (rare: some containers). Fall back to temp, but
|
|
97
|
+
// namespaced per user; ensureSafeDir() still verifies ownership.
|
|
98
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : "user";
|
|
99
|
+
return path.join(os.tmpdir(), `smallm-cache-${uid}`);
|
|
100
|
+
}
|
|
101
|
+
function unsafeDir(reason) {
|
|
102
|
+
return new errors_1.SmallmError(`Refusing to use the cache directory: it ${reason}. Pass a different cacheOptions.dir / --cache-dir.`);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Creates the directory (0700) if needed and refuses one another user could
|
|
106
|
+
* tamper with. On POSIX:
|
|
107
|
+
* - a symlink at the path itself must be owned by the current user
|
|
108
|
+
* (blocks an attacker pre-creating the path as a link to elsewhere);
|
|
109
|
+
* - the target must be a directory owned by the current user;
|
|
110
|
+
* - the default directory is forced to 0700; a caller-chosen directory
|
|
111
|
+
* must not be world-writable (group-writable is allowed so that
|
|
112
|
+
* user-private-group setups with umask 002 keep working).
|
|
113
|
+
* Windows: the default location (%LOCALAPPDATA%) is protected by per-user
|
|
114
|
+
* ACLs. Node has no ACL API, so caller-chosen Windows paths are not checked
|
|
115
|
+
* — documented in CacheOptions.dir.
|
|
116
|
+
* Every failure is a SmallmError whose message does not include the path.
|
|
117
|
+
*/
|
|
118
|
+
function ensureSafeDir(dir, isDefault) {
|
|
119
|
+
try {
|
|
120
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
121
|
+
const linkStat = fs.lstatSync(dir);
|
|
122
|
+
if (linkStat.isSymbolicLink() && isPosix && linkStat.uid !== process.getuid()) {
|
|
123
|
+
throw unsafeDir("is a symlink owned by another user");
|
|
124
|
+
}
|
|
125
|
+
const stat = linkStat.isSymbolicLink() ? fs.statSync(dir) : linkStat;
|
|
126
|
+
if (!stat.isDirectory()) {
|
|
127
|
+
throw unsafeDir("is not a directory");
|
|
128
|
+
}
|
|
129
|
+
if (isPosix) {
|
|
130
|
+
if (stat.uid !== process.getuid()) {
|
|
131
|
+
throw unsafeDir("is owned by another user");
|
|
132
|
+
}
|
|
133
|
+
if (isDefault && (stat.mode & 0o077) !== 0) {
|
|
134
|
+
fs.chmodSync(dir, 0o700);
|
|
135
|
+
}
|
|
136
|
+
else if (!isDefault && (stat.mode & 0o002) !== 0) {
|
|
137
|
+
throw unsafeDir("is world-writable");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
if (err instanceof errors_1.SmallmError)
|
|
143
|
+
throw err;
|
|
144
|
+
const code = err?.code ?? "unknown error";
|
|
145
|
+
const wrapped = new errors_1.SmallmError(`Cannot use the cache directory (${code})`);
|
|
146
|
+
wrapped.cause = err;
|
|
147
|
+
throw wrapped;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
48
150
|
/**
|
|
49
151
|
* File-based TTL cache — same conceptual interface as MVP's in-memory
|
|
50
152
|
* TTLCache (get/set/clear), but each entry is a JSON file on disk, so it
|
|
51
153
|
* survives process restarts. No database engine, per the v0.2 guide's
|
|
52
154
|
* explicit "don't introduce SQLite/Postgres/etc." rule — this is still a
|
|
53
155
|
* small library, not a service.
|
|
156
|
+
*
|
|
157
|
+
* Hardening:
|
|
158
|
+
* - the directory is re-verified before every get/set (not just once), so
|
|
159
|
+
* a directory deleted and re-created by someone else is noticed;
|
|
160
|
+
* - entries are written atomically (exclusive-create temp file, mode
|
|
161
|
+
* 0600, then rename — never follows a planted symlink);
|
|
162
|
+
* - reads open with O_NOFOLLOW|O_NONBLOCK and check the open descriptor,
|
|
163
|
+
* so a swap to a symlink/FIFO/device after the check doesn't matter,
|
|
164
|
+
* and read at most MAX_CACHE_FILE_BYTES;
|
|
165
|
+
* - entries whose expiry exceeds the TTL or whose value fails the
|
|
166
|
+
* validator are discarded;
|
|
167
|
+
* - expired files are swept periodically, with a file-count cap.
|
|
54
168
|
*/
|
|
55
169
|
class FileTTLCache {
|
|
56
|
-
constructor(options = {}) {
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
170
|
+
constructor(options = {}, validate) {
|
|
171
|
+
this.lastSweep = 0;
|
|
172
|
+
(0, validate_1.validateCacheOptions)(options);
|
|
173
|
+
this.isDefaultDir = options.dir === undefined;
|
|
174
|
+
this.dir = path.resolve(options.dir ?? defaultCacheDir());
|
|
175
|
+
// Clamp rather than reject, so existing callers with long TTLs keep working.
|
|
176
|
+
this.ttlMs = Math.min(options.ttlMs ?? DEFAULT_TTL_MS, validate_1.MAX_CACHE_TTL_MS);
|
|
177
|
+
this.validate = validate;
|
|
178
|
+
ensureSafeDir(this.dir, this.isDefaultDir);
|
|
179
|
+
}
|
|
180
|
+
/** Re-checks the directory; a failure makes get() miss and set() skip, never throw. */
|
|
181
|
+
dirIsUsable() {
|
|
182
|
+
try {
|
|
183
|
+
ensureSafeDir(this.dir, this.isDefaultDir);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
60
189
|
}
|
|
61
190
|
/** Maps a cache key to a safe filename (keys can contain arbitrary characters). */
|
|
62
191
|
filePathFor(key) {
|
|
63
192
|
const hash = crypto.createHash("sha256").update(key).digest("hex");
|
|
64
193
|
return path.join(this.dir, `${hash}.json`);
|
|
65
194
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
let
|
|
195
|
+
/** Reads an entry file safely, or returns undefined (and removes junk) if it isn't a small regular file. */
|
|
196
|
+
readEntryFile(filePath) {
|
|
197
|
+
let linkStat;
|
|
69
198
|
try {
|
|
70
|
-
|
|
199
|
+
linkStat = fs.lstatSync(filePath);
|
|
71
200
|
}
|
|
72
201
|
catch {
|
|
73
|
-
return undefined; //
|
|
202
|
+
return undefined; // not cached
|
|
203
|
+
}
|
|
204
|
+
if (!linkStat.isFile() || linkStat.size > MAX_CACHE_FILE_BYTES) {
|
|
205
|
+
this.deleteFile(filePath);
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
let fd;
|
|
209
|
+
try {
|
|
210
|
+
fd = fs.openSync(filePath, READ_FLAGS);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return undefined; // vanished, or swapped for a symlink (ELOOP)
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const stat = fs.fstatSync(fd);
|
|
217
|
+
if (!stat.isFile() || stat.size > MAX_CACHE_FILE_BYTES)
|
|
218
|
+
return undefined;
|
|
219
|
+
const buffer = Buffer.alloc(stat.size + 1);
|
|
220
|
+
let offset = 0;
|
|
221
|
+
while (offset < buffer.length) {
|
|
222
|
+
const n = fs.readSync(fd, buffer, offset, buffer.length - offset, null);
|
|
223
|
+
if (n === 0)
|
|
224
|
+
break;
|
|
225
|
+
offset += n;
|
|
226
|
+
}
|
|
227
|
+
if (offset > stat.size)
|
|
228
|
+
return undefined; // grew while reading — not ours
|
|
229
|
+
return buffer.subarray(0, offset).toString("utf-8");
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
fs.closeSync(fd);
|
|
74
236
|
}
|
|
237
|
+
}
|
|
238
|
+
get(key) {
|
|
239
|
+
if (!this.dirIsUsable())
|
|
240
|
+
return undefined;
|
|
241
|
+
const filePath = this.filePathFor(key);
|
|
242
|
+
const raw = this.readEntryFile(filePath);
|
|
243
|
+
if (raw === undefined)
|
|
244
|
+
return undefined;
|
|
75
245
|
let entry;
|
|
76
246
|
try {
|
|
77
247
|
entry = JSON.parse(raw);
|
|
@@ -81,17 +251,92 @@ class FileTTLCache {
|
|
|
81
251
|
this.deleteFile(filePath);
|
|
82
252
|
return undefined;
|
|
83
253
|
}
|
|
84
|
-
if (
|
|
254
|
+
if (typeof entry !== "object" || entry === null) {
|
|
85
255
|
this.deleteFile(filePath);
|
|
86
256
|
return undefined;
|
|
87
257
|
}
|
|
88
|
-
|
|
258
|
+
const { expiresAt, value } = entry;
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
const valid = typeof expiresAt === "number" &&
|
|
261
|
+
Number.isFinite(expiresAt) &&
|
|
262
|
+
now <= expiresAt &&
|
|
263
|
+
// An entry claiming to live longer than our TTL wasn't written by us — reject.
|
|
264
|
+
expiresAt <= now + this.ttlMs &&
|
|
265
|
+
(this.validate ? this.validate(value) : true);
|
|
266
|
+
if (!valid) {
|
|
267
|
+
this.deleteFile(filePath);
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
return value;
|
|
89
271
|
}
|
|
272
|
+
/** Best-effort: a failed cache write never fails the caller. */
|
|
90
273
|
set(key, value) {
|
|
274
|
+
if (!this.dirIsUsable())
|
|
275
|
+
return;
|
|
91
276
|
const filePath = this.filePathFor(key);
|
|
92
277
|
const entry = { value, expiresAt: Date.now() + this.ttlMs };
|
|
93
|
-
|
|
278
|
+
const tempPath = `${filePath}.${process.pid}.${crypto.randomBytes(8).toString("hex")}.tmp`;
|
|
279
|
+
try {
|
|
280
|
+
// "wx" = O_CREAT|O_EXCL: fails if anything (including a symlink) already exists at tempPath.
|
|
281
|
+
fs.writeFileSync(tempPath, JSON.stringify(entry), { encoding: "utf-8", flag: "wx", mode: 0o600 });
|
|
282
|
+
// rename replaces the destination entry itself — a symlink there is replaced, not followed.
|
|
283
|
+
fs.renameSync(tempPath, filePath);
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
this.deleteFile(tempPath);
|
|
287
|
+
}
|
|
288
|
+
this.sweep();
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Removes expired entries (by mtime) and stale temp files, then trims to
|
|
292
|
+
* MAX_CACHE_FILES. Without this, an entry is only deleted if its exact key
|
|
293
|
+
* is read again, so one-off keys accumulate forever.
|
|
294
|
+
*/
|
|
295
|
+
sweep() {
|
|
296
|
+
const now = Date.now();
|
|
297
|
+
if (now - this.lastSweep < SWEEP_INTERVAL_MS)
|
|
298
|
+
return;
|
|
299
|
+
this.lastSweep = now;
|
|
300
|
+
let files;
|
|
301
|
+
try {
|
|
302
|
+
files = fs.readdirSync(this.dir);
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const live = [];
|
|
308
|
+
for (const file of files) {
|
|
309
|
+
const isEntry = CACHE_FILE_PATTERN.test(file);
|
|
310
|
+
const isTemp = !isEntry && TEMP_FILE_PATTERN.test(file);
|
|
311
|
+
if (!isEntry && !isTemp)
|
|
312
|
+
continue;
|
|
313
|
+
const filePath = path.join(this.dir, file);
|
|
314
|
+
let stat;
|
|
315
|
+
try {
|
|
316
|
+
stat = fs.lstatSync(filePath);
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const age = now - stat.mtimeMs;
|
|
322
|
+
if (isTemp) {
|
|
323
|
+
if (age > STALE_TEMP_MS)
|
|
324
|
+
this.deleteFile(filePath);
|
|
325
|
+
}
|
|
326
|
+
else if (age > this.ttlMs) {
|
|
327
|
+
this.deleteFile(filePath);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
live.push({ filePath, mtimeMs: stat.mtimeMs });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (live.length > MAX_CACHE_FILES) {
|
|
334
|
+
live.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
335
|
+
for (const { filePath } of live.slice(0, live.length - MAX_CACHE_FILES))
|
|
336
|
+
this.deleteFile(filePath);
|
|
337
|
+
}
|
|
94
338
|
}
|
|
339
|
+
/** Removes only files this cache created (hash-named entries and leftover temp files) — never anything else in the directory. */
|
|
95
340
|
clear() {
|
|
96
341
|
let files;
|
|
97
342
|
try {
|
|
@@ -101,7 +346,7 @@ class FileTTLCache {
|
|
|
101
346
|
return;
|
|
102
347
|
}
|
|
103
348
|
for (const file of files) {
|
|
104
|
-
if (file.
|
|
349
|
+
if (CACHE_FILE_PATTERN.test(file) || TEMP_FILE_PATTERN.test(file)) {
|
|
105
350
|
this.deleteFile(path.join(this.dir, file));
|
|
106
351
|
}
|
|
107
352
|
}
|
|
@@ -116,17 +361,53 @@ class FileTTLCache {
|
|
|
116
361
|
}
|
|
117
362
|
}
|
|
118
363
|
exports.FileTTLCache = FileTTLCache;
|
|
119
|
-
// Module-level
|
|
120
|
-
//
|
|
121
|
-
// first findModels() call if you need non-default dir/ttl.
|
|
364
|
+
// Module-level default cache, matching MVP's cache.ts pattern. Created
|
|
365
|
+
// lazily on first use; configureCandidateModelsCache() replaces it.
|
|
122
366
|
let candidateModelsCache;
|
|
123
|
-
/**
|
|
367
|
+
/**
|
|
368
|
+
* Caches for explicit per-call cacheOptions, keyed by (dir, ttl). Per-call
|
|
369
|
+
* options never overwrite the module-level default, so one caller's
|
|
370
|
+
* options can't leak into later/concurrent calls that didn't pass any.
|
|
371
|
+
*/
|
|
372
|
+
const MAX_SCOPED_CACHES = 16;
|
|
373
|
+
const scopedCaches = new Map();
|
|
374
|
+
/**
|
|
375
|
+
* In-flight fetches keyed by cache key: concurrent identical requests share
|
|
376
|
+
* one upstream call instead of each missing the cache and fetching.
|
|
377
|
+
*/
|
|
378
|
+
const inflight = new Map();
|
|
379
|
+
/** Sets (or resets) the default cache options used when a call passes no cacheOptions. Throws if the directory is unusable. */
|
|
124
380
|
function configureCandidateModelsCache(options = {}) {
|
|
125
|
-
candidateModelsCache = new FileTTLCache(options);
|
|
381
|
+
candidateModelsCache = new FileTTLCache(options, validate_1.isRawModelArray);
|
|
126
382
|
}
|
|
127
|
-
function getCache() {
|
|
383
|
+
function getCache(options) {
|
|
384
|
+
if (options) {
|
|
385
|
+
(0, validate_1.validateCacheOptions)(options);
|
|
386
|
+
const ttl = Math.min(options.ttlMs ?? DEFAULT_TTL_MS, validate_1.MAX_CACHE_TTL_MS);
|
|
387
|
+
const key = `${path.resolve(options.dir ?? defaultCacheDir())}\0${ttl}`;
|
|
388
|
+
let cache = scopedCaches.get(key);
|
|
389
|
+
if (!cache) {
|
|
390
|
+
// Explicitly requested directory: an unusable one is an error the caller should see.
|
|
391
|
+
cache = new FileTTLCache(options, validate_1.isRawModelArray);
|
|
392
|
+
if (scopedCaches.size >= MAX_SCOPED_CACHES) {
|
|
393
|
+
const oldest = scopedCaches.keys().next().value;
|
|
394
|
+
if (oldest !== undefined)
|
|
395
|
+
scopedCaches.delete(oldest);
|
|
396
|
+
}
|
|
397
|
+
scopedCaches.set(key, cache);
|
|
398
|
+
}
|
|
399
|
+
return cache;
|
|
400
|
+
}
|
|
128
401
|
if (!candidateModelsCache) {
|
|
129
|
-
|
|
402
|
+
try {
|
|
403
|
+
candidateModelsCache = new FileTTLCache({}, validate_1.isRawModelArray);
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
// The default directory is unusable (e.g. pre-created by another user,
|
|
407
|
+
// read-only home). Don't fail the whole lookup over a cache: degrade to
|
|
408
|
+
// the in-memory cache for this process.
|
|
409
|
+
candidateModelsCache = new cache_1.TTLCache(DEFAULT_TTL_MS);
|
|
410
|
+
}
|
|
130
411
|
}
|
|
131
412
|
return candidateModelsCache;
|
|
132
413
|
}
|
|
@@ -145,14 +426,22 @@ async function fetchCandidateModelsCached(task) {
|
|
|
145
426
|
* requested provider's candidate list independently (keyed by
|
|
146
427
|
* `${providerName}:${task}`, so HuggingFace and Ollama results for the
|
|
147
428
|
* same task never collide). Not provider-specific itself — any async
|
|
148
|
-
* producer of RawModel[] can use this.
|
|
429
|
+
* producer of RawModel[] can use this. Pass `options` to use a cache
|
|
430
|
+
* scoped to those options instead of the module-level default.
|
|
149
431
|
*/
|
|
150
|
-
async function getOrFetchCached(key, fetchFn) {
|
|
151
|
-
const cache = getCache();
|
|
432
|
+
async function getOrFetchCached(key, fetchFn, options) {
|
|
433
|
+
const cache = getCache(options);
|
|
152
434
|
const cached = cache.get(key);
|
|
153
435
|
if (cached)
|
|
154
436
|
return cached;
|
|
155
|
-
|
|
437
|
+
let pending = inflight.get(key);
|
|
438
|
+
if (!pending) {
|
|
439
|
+
pending = Promise.resolve()
|
|
440
|
+
.then(fetchFn)
|
|
441
|
+
.finally(() => inflight.delete(key));
|
|
442
|
+
inflight.set(key, pending);
|
|
443
|
+
}
|
|
444
|
+
const result = await pending;
|
|
156
445
|
cache.set(key, result);
|
|
157
446
|
return result;
|
|
158
447
|
}
|
package/dist/cache-file.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-file.js","sourceRoot":"","sources":["../src/cache-file.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache-file.js","sourceRoot":"","sources":["../src/cache-file.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,MAAY,EAAE,+BAAW;AACzB,MAAY,IAAI,iCAAa;AAC7B,MAAY,EAAE,+BAAW;AACzB,MAAY,MAAM,mCAAe;AAEjC,qCAAuC;AACvC,wDAA8D;AAC9D,yCAAqF;AACrF,mCAAmC;AAEnC,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,oDAAoD;AAE1F,qFAAqF;AACrF,MAAM,oBAAoB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AACpC,mFAAmF;AACnF,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,6FAA6F;AAC7F,MAAM,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhC,6FAA6F;AAC7F,MAAM,kBAAkB,GAAG,sBAAsB,CAAC;AAClD,MAAM,iBAAiB,GAAG,8CAA8C,CAAC;AAEzE;;;;GAIG;AACH,MAAM,UAAU,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;AAE3G,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC;AAErF;;;;GAIG;AACH;IACE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACvC,IAAI,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAEjE,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,CAAC;QACH,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;IAC5E,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACnG,IAAI,YAAY;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtE,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACxD,CAAC;SAAM,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAED,2EAA2E;IAC3E,iEAAiE;IACjE,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7E,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,gBAAgB,GAAG,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,SAAS,SAAS,CAAC,MAAc;IAC/B,OAAO,IAAI,oBAAW,CACpB,2CAA2C,MAAM,oDAAoD,CACtG,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,aAAa,CAAC,GAAW,EAAE,SAAkB;IACpD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAEpD,MAAM,QAAQ,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,QAAQ,CAAC,cAAc,EAAE,IAAI,OAAO,IAAI,QAAQ,CAAC,GAAG,KAAK,OAAO,CAAC,MAAO,EAAE,EAAE,CAAC;YAC/E,MAAM,SAAS,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACrE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACxC,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,CAAC,MAAO,EAAE,EAAE,CAAC;gBACnC,MAAM,SAAS,CAAC,0BAA0B,CAAC,CAAC;YAC9C,CAAC;YACD,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3C,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC3B,CAAC;iBAAM,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnD,MAAM,SAAS,CAAC,mBAAmB,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,oBAAW;YAAE,MAAM,GAAG,CAAC;QAC1C,MAAM,IAAI,GAAI,GAAoC,EAAE,IAAI,IAAI,eAAe,CAAC;QAC5E,MAAM,OAAO,GAAG,IAAI,oBAAW,CAAC,mCAAmC,IAAI,GAAG,CAAC,CAAC;QAC3E,OAA+B,CAAC,KAAK,GAAG,GAAG,CAAC;QAC7C,MAAM,OAAO,CAAC;IAChB,CAAC;AACH,CAAC;AAOD;;;;;;;;;;;;;;;;;;GAkBG;AACH;IAOE,YAAY,OAAO,GAAiB,EAAE,EAAE,QAAyC;QAFzE,cAAS,GAAG,CAAC,CAAC;QAGpB,IAAA,+BAAoB,EAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC;QAC9C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,eAAe,EAAE,CAAC,CAAC;QAC1D,6EAA6E;QAC7E,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,cAAc,EAAE,2BAAgB,CAAC,CAAC;QACzE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;IAED,uFAAuF;IAC/E,WAAW;QACjB,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,mFAAmF;IAC3E,WAAW,CAAC,GAAW;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,4GAA4G;IACpG,aAAa,CAAC,QAAgB;QACpC,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC,CAAC,aAAa;QACjC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,QAAQ,CAAC,IAAI,GAAG,oBAAoB,EAAE,CAAC;YAC/D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC1B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,EAAU,CAAC;QACf,IAAI,CAAC;YACH,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC,CAAC,6CAA6C;QACjE,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,oBAAoB;gBAAE,OAAO,SAAS,CAAC;YACzE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;YAC3C,IAAI,MAAM,GAAG,CAAC,CAAC;YACf,OAAO,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC;gBACxE,IAAI,CAAC,KAAK,CAAC;oBAAE,MAAM;gBACnB,MAAM,IAAI,CAAC,CAAC;YACd,CAAC;YACD,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI;gBAAE,OAAO,SAAS,CAAC,CAAC,gCAAgC;YAC1E,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,GAAG,CAAC,GAAW;QACb,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,SAAS,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAEvC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAExC,IAAI,KAAc,CAAC;QACnB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;YAC1E,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC1B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAChD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC1B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAyC,CAAC;QAEvE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GACT,OAAO,SAAS,KAAK,QAAQ;YAC7B,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;YAC1B,GAAG,IAAI,SAAS;YAChB,+EAA+E;YAC/E,SAAS,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK;YAC7B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAEhD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC1B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,KAAU,CAAC;IACpB,CAAC;IAED,gEAAgE;IAChE,GAAG,CAAC,GAAW,EAAE,KAAQ;QACvB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,KAAK,GAAsB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/E,MAAM,QAAQ,GAAG,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;QAE3F,IAAI,CAAC;YACH,6FAA6F;YAC7F,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAClG,4FAA4F;YAC5F,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED;;;;OAIG;IACK,KAAK;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,GAAG,iBAAiB;YAAE,OAAO;QACrD,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QAErB,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAiD,EAAE,CAAC;QAC9D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,CAAC,OAAO,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxD,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM;gBAAE,SAAS;YAElC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3C,IAAI,IAAc,CAAC;YACnB,IAAI,CAAC;gBACH,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;YAC/B,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,GAAG,GAAG,aAAa;oBAAE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrD,CAAC;iBAAM,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC5B,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;YAC3C,KAAK,MAAM,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC;gBAAE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACrG,CAAC;IACH,CAAC;IAED,iIAAiI;IACjI,KAAK;QACH,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,QAAgB;QACjC,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;CACF;;AASD,uEAAuE;AACvE,oEAAoE;AACpE,IAAI,oBAAgD,CAAC;AAErD;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAmC,CAAC;AAEhE;;;GAGG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA8B,CAAC;AAEvD,+HAA+H;AAC/H,uCAA8C,OAAO,GAAiB,EAAE;IACtE,oBAAoB,GAAG,IAAI,YAAY,CAAY,OAAO,EAAE,0BAAe,CAAC,CAAC;AAC/E,CAAC;AAED,SAAS,QAAQ,CAAC,OAAsB;IACtC,IAAI,OAAO,EAAE,CAAC;QACZ,IAAA,+BAAoB,EAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,cAAc,EAAE,2BAAgB,CAAC,CAAC;QACxE,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,eAAe,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC;QACxE,IAAI,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,qFAAqF;YACrF,KAAK,GAAG,IAAI,YAAY,CAAY,OAAO,EAAE,0BAAe,CAAC,CAAC;YAC9D,IAAI,YAAY,CAAC,IAAI,IAAI,iBAAiB,EAAE,CAAC;gBAC3C,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;gBAChD,IAAI,MAAM,KAAK,SAAS;oBAAE,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACxD,CAAC;YACD,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,oBAAoB,GAAG,IAAI,YAAY,CAAY,EAAE,EAAE,0BAAe,CAAC,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,wEAAwE;YACxE,wCAAwC;YACxC,oBAAoB,GAAG,IAAI,gBAAQ,CAAY,cAAc,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,OAAO,oBAAoB,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACI,KAAK,qCAAqC,IAAY;IAC3D,OAAO,gBAAgB,CAAC,eAAe,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,IAAA,kCAAoB,EAAC,IAAI,CAAC,CAAC,CAAC;AACnF,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,2BACV,GAAW,EACX,OAAiC,EACjC,OAAsB;IAEtB,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,IAAI,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE;aACxB,IAAI,CAAC,OAAO,CAAC;aACb,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACvC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;IAC7B,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,qEAAqE;AACrE;IACE,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -17,10 +17,14 @@ export interface ParsedCli {
|
|
|
17
17
|
query: ModelQuery;
|
|
18
18
|
json: boolean;
|
|
19
19
|
showHelp: boolean;
|
|
20
|
+
/** Non-fatal notices (e.g. a flag that was overridden), printed to stderr by main(). */
|
|
21
|
+
warnings: string[];
|
|
20
22
|
}
|
|
21
23
|
export declare const USAGE = "Usage: smallm find [options]\n\nRequired:\n --task <task> Task type (chat, summarize, classify, extract, code, translate, or any custom string)\n --context <tokens> Required context window, in tokens\n\nOptional (map 1:1 to ModelQuery fields):\n --max-params <billions> Hard filter, e.g. 7 = \"under 7B\"\n --domain <domain> Preferred domain tag, used in scoring only\n --hardware <cpu|gpu-low|gpu-high> Hardware hint (original string form)\n --hardware-type <cpu|gpu> Hardware type (structured form \u2014 use with --hardware-vram)\n --hardware-vram <gb> VRAM budget in GB (structured form only)\n --max-latency <ms> Enforced only when benchmark data exists for the (model, hardware) pair\n --scoring-mode <rule|embedding|hybrid> Scoring strategy (default: rule)\n --providers <list> Comma-separated, e.g. huggingface,ollama (default: huggingface)\n --limit <n> Max results (default: 5)\n --cache-dir <path> File cache directory\n --cache-ttl <ms> File cache TTL in ms\n\nOutput:\n --json Print raw ModelMatch[] JSON instead of a table\n\nExamples:\n npx smallm find --task summarize --context 4096\n npx smallm find --task chat --context 8192 --max-params 7 --json\n npx smallm find --task chat --context 4096 --providers huggingface,ollama --hardware-type gpu --hardware-vram 8\n";
|
|
22
24
|
/** Pure parser: argv (already sliced past "find") -> a ModelQuery + output mode. Never touches process.exit or the network. */
|
|
23
25
|
export declare function parseArgs(argv: string[]): ParsedCli;
|
|
26
|
+
/** Removes control/invisible characters so text can't inject terminal escapes or visually disguise itself. */
|
|
27
|
+
export declare function stripControlChars(str: string): string;
|
|
24
28
|
/** Pure formatter: ModelMatch[] -> a human-readable table string. */
|
|
25
29
|
export declare function formatTable(results: ModelMatch[]): string;
|
|
26
30
|
/**
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGjD;;;;;;;GAOG;AAEH,8LAA8L;AAC9L,qBAAa,aAAc,SAAQ,KAAK;aACO,QAAQ,EAAE,MAAM;IAA7D,YAAY,OAAO,EAAE,MAAM,EAAkB,QAAQ,GAAE,MAAU,EAGhE;CACF;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,wFAAwF;IACxF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,eAAO,MAAM,KAAK,i6CA0BjB,CAAC;AAEF,+HAA+H;AAC/H,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAoJnD;AAKD,8GAA8G;AAC9G,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAOD,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAmBzD;AAED;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkD1D"}
|