@hasna/models 0.0.1
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/LICENSE +17 -0
- package/PLAN.md +359 -0
- package/README.md +67 -0
- package/dist/auth.d.ts +8 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +3738 -0
- package/dist/huggingface.d.ts +18 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +730 -0
- package/dist/paths.d.ts +5 -0
- package/dist/ref.d.ts +6 -0
- package/dist/storage.d.ts +21 -0
- package/dist/storage.js +255 -0
- package/dist/types.d.ts +89 -0
- package/dist/version.d.ts +1 -0
- package/docs/GOALS.md +49 -0
- package/package.json +71 -0
package/dist/paths.d.ts
ADDED
package/dist/ref.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { EntityKind, ProviderRef } from "./types.js";
|
|
2
|
+
export declare function parseEntityKind(value: string): EntityKind;
|
|
3
|
+
export declare function parseProviderRef(input: string, defaultKind?: EntityKind): ProviderRef;
|
|
4
|
+
export declare function formatProviderRef(ref: ProviderRef): string;
|
|
5
|
+
export declare function encodeRepoId(repoId: string): string;
|
|
6
|
+
export declare function safePathSegment(value: string): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import type { CatalogEntry, InstalledArtifact, RemoteFileEntry } from "./types.js";
|
|
3
|
+
export declare class ModelsStore {
|
|
4
|
+
readonly db: Database;
|
|
5
|
+
constructor(path?: string);
|
|
6
|
+
close(): void;
|
|
7
|
+
migrate(): void;
|
|
8
|
+
upsertCatalog(entries: CatalogEntry[]): number;
|
|
9
|
+
upsertFiles(files: RemoteFileEntry[]): number;
|
|
10
|
+
recordInstall(artifact: InstalledArtifact, metadata?: Record<string, unknown>): InstalledArtifact;
|
|
11
|
+
listInstalls(): InstalledArtifact[];
|
|
12
|
+
findInstall(repoIdOrId: string): InstalledArtifact | null;
|
|
13
|
+
deleteInstall(id: string): boolean;
|
|
14
|
+
catalogStats(): {
|
|
15
|
+
catalogEntries: number;
|
|
16
|
+
remoteFiles: number;
|
|
17
|
+
installs: number;
|
|
18
|
+
};
|
|
19
|
+
topCatalog(limit?: number): CatalogEntry[];
|
|
20
|
+
}
|
|
21
|
+
export declare function createStore(path?: string): ModelsStore;
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/storage.ts
|
|
3
|
+
import { mkdirSync } from "fs";
|
|
4
|
+
import { dirname } from "path";
|
|
5
|
+
import { Database } from "bun:sqlite";
|
|
6
|
+
|
|
7
|
+
// src/paths.ts
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
function getModelsHome() {
|
|
11
|
+
return process.env["HASNA_MODELS_HOME"] || join(homedir(), ".hasna", "models");
|
|
12
|
+
}
|
|
13
|
+
function getDbPath() {
|
|
14
|
+
return process.env["HASNA_MODELS_DB"] || join(getModelsHome(), "models.db");
|
|
15
|
+
}
|
|
16
|
+
function getAuthConfigPath() {
|
|
17
|
+
return join(getModelsHome(), "auth.json");
|
|
18
|
+
}
|
|
19
|
+
function getCacheRoot() {
|
|
20
|
+
return process.env["HASNA_MODELS_CACHE"] || join(getModelsHome(), "cache");
|
|
21
|
+
}
|
|
22
|
+
function getInstallRoot() {
|
|
23
|
+
return process.env["HASNA_MODELS_INSTALLS"] || join(getModelsHome(), "installs");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/storage.ts
|
|
27
|
+
var SCHEMA_VERSION = 1;
|
|
28
|
+
|
|
29
|
+
class ModelsStore {
|
|
30
|
+
db;
|
|
31
|
+
constructor(path = getDbPath()) {
|
|
32
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
33
|
+
this.db = new Database(path, { create: true });
|
|
34
|
+
this.db.run("PRAGMA busy_timeout = 5000");
|
|
35
|
+
this.db.run("PRAGMA journal_mode = WAL");
|
|
36
|
+
this.migrate();
|
|
37
|
+
}
|
|
38
|
+
close() {
|
|
39
|
+
this.db.close();
|
|
40
|
+
}
|
|
41
|
+
migrate() {
|
|
42
|
+
this.db.run(`
|
|
43
|
+
CREATE TABLE IF NOT EXISTS schema_meta (
|
|
44
|
+
key TEXT PRIMARY KEY,
|
|
45
|
+
value INTEGER NOT NULL
|
|
46
|
+
)
|
|
47
|
+
`);
|
|
48
|
+
const row = this.db.query("SELECT value FROM schema_meta WHERE key = 'schema_version' LIMIT 1").get();
|
|
49
|
+
const currentVersion = row ? Number(row.value) : 0;
|
|
50
|
+
if (currentVersion >= SCHEMA_VERSION)
|
|
51
|
+
return;
|
|
52
|
+
this.db.run(`
|
|
53
|
+
CREATE TABLE IF NOT EXISTS catalog_entries (
|
|
54
|
+
provider TEXT NOT NULL,
|
|
55
|
+
entity_kind TEXT NOT NULL,
|
|
56
|
+
repo_id TEXT NOT NULL,
|
|
57
|
+
revision TEXT NOT NULL,
|
|
58
|
+
title TEXT NOT NULL,
|
|
59
|
+
author TEXT,
|
|
60
|
+
task TEXT,
|
|
61
|
+
library_name TEXT,
|
|
62
|
+
license TEXT,
|
|
63
|
+
gated INTEGER NOT NULL,
|
|
64
|
+
private INTEGER NOT NULL,
|
|
65
|
+
downloads INTEGER,
|
|
66
|
+
likes INTEGER,
|
|
67
|
+
tags_json TEXT NOT NULL,
|
|
68
|
+
metadata_json TEXT NOT NULL,
|
|
69
|
+
canonical_url TEXT NOT NULL,
|
|
70
|
+
last_modified TEXT,
|
|
71
|
+
indexed_at TEXT NOT NULL,
|
|
72
|
+
PRIMARY KEY (provider, entity_kind, repo_id, revision)
|
|
73
|
+
)
|
|
74
|
+
`);
|
|
75
|
+
this.db.run(`
|
|
76
|
+
CREATE TABLE IF NOT EXISTS remote_files (
|
|
77
|
+
provider TEXT NOT NULL,
|
|
78
|
+
entity_kind TEXT NOT NULL,
|
|
79
|
+
repo_id TEXT NOT NULL,
|
|
80
|
+
revision TEXT NOT NULL,
|
|
81
|
+
path TEXT NOT NULL,
|
|
82
|
+
size INTEGER,
|
|
83
|
+
oid TEXT,
|
|
84
|
+
lfs_oid TEXT,
|
|
85
|
+
format TEXT,
|
|
86
|
+
download_url TEXT NOT NULL,
|
|
87
|
+
metadata_json TEXT NOT NULL,
|
|
88
|
+
indexed_at TEXT NOT NULL,
|
|
89
|
+
PRIMARY KEY (provider, entity_kind, repo_id, revision, path)
|
|
90
|
+
)
|
|
91
|
+
`);
|
|
92
|
+
this.db.run(`
|
|
93
|
+
CREATE TABLE IF NOT EXISTS installs (
|
|
94
|
+
id TEXT PRIMARY KEY,
|
|
95
|
+
provider TEXT NOT NULL,
|
|
96
|
+
entity_kind TEXT NOT NULL,
|
|
97
|
+
repo_id TEXT NOT NULL,
|
|
98
|
+
revision TEXT NOT NULL,
|
|
99
|
+
install_path TEXT NOT NULL,
|
|
100
|
+
bytes INTEGER NOT NULL,
|
|
101
|
+
files_json TEXT NOT NULL,
|
|
102
|
+
status TEXT NOT NULL,
|
|
103
|
+
runtime TEXT,
|
|
104
|
+
metadata_json TEXT NOT NULL,
|
|
105
|
+
created_at TEXT NOT NULL,
|
|
106
|
+
updated_at TEXT NOT NULL
|
|
107
|
+
)
|
|
108
|
+
`);
|
|
109
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_catalog_downloads ON catalog_entries(downloads DESC)");
|
|
110
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_catalog_task ON catalog_entries(task)");
|
|
111
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_files_repo ON remote_files(provider, entity_kind, repo_id)");
|
|
112
|
+
this.db.run("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', ?)", [SCHEMA_VERSION]);
|
|
113
|
+
}
|
|
114
|
+
upsertCatalog(entries) {
|
|
115
|
+
const now = new Date().toISOString();
|
|
116
|
+
const stmt = this.db.prepare(`
|
|
117
|
+
INSERT INTO catalog_entries (
|
|
118
|
+
provider, entity_kind, repo_id, revision, title, author, task, library_name,
|
|
119
|
+
license, gated, private, downloads, likes, tags_json, metadata_json,
|
|
120
|
+
canonical_url, last_modified, indexed_at
|
|
121
|
+
)
|
|
122
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
123
|
+
ON CONFLICT(provider, entity_kind, repo_id, revision) DO UPDATE SET
|
|
124
|
+
title=excluded.title,
|
|
125
|
+
author=excluded.author,
|
|
126
|
+
task=excluded.task,
|
|
127
|
+
library_name=excluded.library_name,
|
|
128
|
+
license=excluded.license,
|
|
129
|
+
gated=excluded.gated,
|
|
130
|
+
private=excluded.private,
|
|
131
|
+
downloads=excluded.downloads,
|
|
132
|
+
likes=excluded.likes,
|
|
133
|
+
tags_json=excluded.tags_json,
|
|
134
|
+
metadata_json=excluded.metadata_json,
|
|
135
|
+
canonical_url=excluded.canonical_url,
|
|
136
|
+
last_modified=excluded.last_modified,
|
|
137
|
+
indexed_at=excluded.indexed_at
|
|
138
|
+
`);
|
|
139
|
+
const tx = this.db.transaction((items) => {
|
|
140
|
+
for (const entry of items) {
|
|
141
|
+
stmt.run(entry.provider, entry.entityKind, entry.repoId, entry.revision, entry.title, entry.author ?? null, entry.task ?? null, entry.libraryName ?? null, entry.license ?? null, entry.gated ? 1 : 0, entry.private ? 1 : 0, entry.downloads ?? null, entry.likes ?? null, JSON.stringify(entry.tags), JSON.stringify(entry.metadata), entry.canonicalUrl, entry.lastModified ?? null, now);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
tx(entries);
|
|
145
|
+
return entries.length;
|
|
146
|
+
}
|
|
147
|
+
upsertFiles(files) {
|
|
148
|
+
const now = new Date().toISOString();
|
|
149
|
+
const stmt = this.db.prepare(`
|
|
150
|
+
INSERT INTO remote_files (
|
|
151
|
+
provider, entity_kind, repo_id, revision, path, size, oid, lfs_oid,
|
|
152
|
+
format, download_url, metadata_json, indexed_at
|
|
153
|
+
)
|
|
154
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
155
|
+
ON CONFLICT(provider, entity_kind, repo_id, revision, path) DO UPDATE SET
|
|
156
|
+
size=excluded.size,
|
|
157
|
+
oid=excluded.oid,
|
|
158
|
+
lfs_oid=excluded.lfs_oid,
|
|
159
|
+
format=excluded.format,
|
|
160
|
+
download_url=excluded.download_url,
|
|
161
|
+
metadata_json=excluded.metadata_json,
|
|
162
|
+
indexed_at=excluded.indexed_at
|
|
163
|
+
`);
|
|
164
|
+
const tx = this.db.transaction((items) => {
|
|
165
|
+
for (const file of items) {
|
|
166
|
+
stmt.run(file.provider, file.entityKind, file.repoId, file.revision, file.path, file.size ?? null, file.oid ?? null, file.lfsOid ?? null, file.format ?? null, file.downloadUrl, JSON.stringify(file.metadata), now);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
tx(files);
|
|
170
|
+
return files.length;
|
|
171
|
+
}
|
|
172
|
+
recordInstall(artifact, metadata = {}) {
|
|
173
|
+
this.db.prepare(`
|
|
174
|
+
INSERT INTO installs (
|
|
175
|
+
id, provider, entity_kind, repo_id, revision, install_path, bytes,
|
|
176
|
+
files_json, status, runtime, metadata_json, created_at, updated_at
|
|
177
|
+
)
|
|
178
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
179
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
180
|
+
bytes=excluded.bytes,
|
|
181
|
+
files_json=excluded.files_json,
|
|
182
|
+
status=excluded.status,
|
|
183
|
+
runtime=excluded.runtime,
|
|
184
|
+
metadata_json=excluded.metadata_json,
|
|
185
|
+
updated_at=excluded.updated_at
|
|
186
|
+
`).run(artifact.id, artifact.provider, artifact.entityKind, artifact.repoId, artifact.revision, artifact.installPath, artifact.bytes, JSON.stringify(artifact.files), artifact.status, null, JSON.stringify(metadata), artifact.createdAt, artifact.updatedAt);
|
|
187
|
+
return artifact;
|
|
188
|
+
}
|
|
189
|
+
listInstalls() {
|
|
190
|
+
const rows = this.db.query("SELECT * FROM installs ORDER BY updated_at DESC").all();
|
|
191
|
+
return rows.map((row) => ({
|
|
192
|
+
id: String(row.id),
|
|
193
|
+
provider: String(row.provider),
|
|
194
|
+
entityKind: String(row.entity_kind),
|
|
195
|
+
repoId: String(row.repo_id),
|
|
196
|
+
revision: String(row.revision),
|
|
197
|
+
installPath: String(row.install_path),
|
|
198
|
+
bytes: Number(row.bytes),
|
|
199
|
+
files: JSON.parse(String(row.files_json)),
|
|
200
|
+
status: String(row.status),
|
|
201
|
+
createdAt: String(row.created_at),
|
|
202
|
+
updatedAt: String(row.updated_at)
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
findInstall(repoIdOrId) {
|
|
206
|
+
const row = this.db.query("SELECT * FROM installs WHERE id = ? OR repo_id = ? ORDER BY updated_at DESC LIMIT 1").get(repoIdOrId, repoIdOrId);
|
|
207
|
+
if (!row)
|
|
208
|
+
return null;
|
|
209
|
+
return this.listInstalls().find((install) => install.id === row.id) ?? null;
|
|
210
|
+
}
|
|
211
|
+
deleteInstall(id) {
|
|
212
|
+
const result = this.db.prepare("DELETE FROM installs WHERE id = ?").run(id);
|
|
213
|
+
return result.changes > 0;
|
|
214
|
+
}
|
|
215
|
+
catalogStats() {
|
|
216
|
+
const one = (table) => {
|
|
217
|
+
const row = this.db.query(`SELECT COUNT(*) as count FROM ${table}`).get();
|
|
218
|
+
return Number(row?.count ?? 0);
|
|
219
|
+
};
|
|
220
|
+
return {
|
|
221
|
+
catalogEntries: one("catalog_entries"),
|
|
222
|
+
remoteFiles: one("remote_files"),
|
|
223
|
+
installs: one("installs")
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
topCatalog(limit = 20) {
|
|
227
|
+
const rows = this.db.query("SELECT * FROM catalog_entries ORDER BY COALESCE(downloads, 0) DESC, COALESCE(likes, 0) DESC LIMIT ?").all(limit);
|
|
228
|
+
return rows.map((row) => ({
|
|
229
|
+
provider: String(row.provider),
|
|
230
|
+
entityKind: String(row.entity_kind),
|
|
231
|
+
repoId: String(row.repo_id),
|
|
232
|
+
revision: String(row.revision),
|
|
233
|
+
canonicalUrl: String(row.canonical_url),
|
|
234
|
+
title: String(row.title),
|
|
235
|
+
author: row.author == null ? null : String(row.author),
|
|
236
|
+
task: row.task == null ? null : String(row.task),
|
|
237
|
+
libraryName: row.library_name == null ? null : String(row.library_name),
|
|
238
|
+
license: row.license == null ? null : String(row.license),
|
|
239
|
+
gated: Boolean(row.gated),
|
|
240
|
+
private: Boolean(row.private),
|
|
241
|
+
downloads: row.downloads == null ? null : Number(row.downloads),
|
|
242
|
+
likes: row.likes == null ? null : Number(row.likes),
|
|
243
|
+
tags: JSON.parse(String(row.tags_json)),
|
|
244
|
+
lastModified: row.last_modified == null ? null : String(row.last_modified),
|
|
245
|
+
metadata: JSON.parse(String(row.metadata_json))
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function createStore(path) {
|
|
250
|
+
return new ModelsStore(path);
|
|
251
|
+
}
|
|
252
|
+
export {
|
|
253
|
+
createStore,
|
|
254
|
+
ModelsStore
|
|
255
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export type ProviderId = "huggingface" | "github-release" | string;
|
|
2
|
+
export type EntityKind = "model" | "dataset" | "space" | "collection" | "artifact";
|
|
3
|
+
export interface ProviderRef {
|
|
4
|
+
provider: ProviderId;
|
|
5
|
+
entityKind: EntityKind;
|
|
6
|
+
repoId: string;
|
|
7
|
+
revision: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CatalogEntry {
|
|
10
|
+
provider: ProviderId;
|
|
11
|
+
entityKind: EntityKind;
|
|
12
|
+
repoId: string;
|
|
13
|
+
revision: string;
|
|
14
|
+
canonicalUrl: string;
|
|
15
|
+
title: string;
|
|
16
|
+
author?: string | null;
|
|
17
|
+
task?: string | null;
|
|
18
|
+
libraryName?: string | null;
|
|
19
|
+
license?: string | null;
|
|
20
|
+
gated: boolean;
|
|
21
|
+
private: boolean;
|
|
22
|
+
downloads?: number | null;
|
|
23
|
+
likes?: number | null;
|
|
24
|
+
tags: string[];
|
|
25
|
+
lastModified?: string | null;
|
|
26
|
+
metadata: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface RemoteFileEntry {
|
|
29
|
+
provider: ProviderId;
|
|
30
|
+
entityKind: EntityKind;
|
|
31
|
+
repoId: string;
|
|
32
|
+
revision: string;
|
|
33
|
+
path: string;
|
|
34
|
+
size: number | null;
|
|
35
|
+
oid?: string | null;
|
|
36
|
+
lfsOid?: string | null;
|
|
37
|
+
format?: string | null;
|
|
38
|
+
downloadUrl: string;
|
|
39
|
+
metadata: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
export interface SearchInput {
|
|
42
|
+
query?: string;
|
|
43
|
+
entityKind?: EntityKind;
|
|
44
|
+
task?: string;
|
|
45
|
+
license?: string;
|
|
46
|
+
tags?: string[];
|
|
47
|
+
limit?: number;
|
|
48
|
+
sort?: "downloads" | "likes" | "lastModified" | "createdAt" | "trendingScore";
|
|
49
|
+
direction?: "asc" | "desc";
|
|
50
|
+
}
|
|
51
|
+
export interface DownloadPlan {
|
|
52
|
+
ref: ProviderRef;
|
|
53
|
+
files: RemoteFileEntry[];
|
|
54
|
+
totalBytes: number | null;
|
|
55
|
+
unknownSizeFiles: string[];
|
|
56
|
+
destinationRoot: string;
|
|
57
|
+
exceedsMaxBytes: boolean;
|
|
58
|
+
maxBytes: number | null;
|
|
59
|
+
}
|
|
60
|
+
export interface InstalledArtifact {
|
|
61
|
+
id: string;
|
|
62
|
+
provider: ProviderId;
|
|
63
|
+
entityKind: EntityKind;
|
|
64
|
+
repoId: string;
|
|
65
|
+
revision: string;
|
|
66
|
+
installPath: string;
|
|
67
|
+
bytes: number;
|
|
68
|
+
files: string[];
|
|
69
|
+
status: "installed" | "planned" | "failed";
|
|
70
|
+
createdAt: string;
|
|
71
|
+
updatedAt: string;
|
|
72
|
+
}
|
|
73
|
+
export interface AuthStatus {
|
|
74
|
+
provider: ProviderId;
|
|
75
|
+
available: boolean;
|
|
76
|
+
source: "env" | "config" | "secrets" | "none";
|
|
77
|
+
secretKey?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface DoctorReport {
|
|
80
|
+
ok: boolean;
|
|
81
|
+
dataDir: string;
|
|
82
|
+
dbPath: string;
|
|
83
|
+
providers: AuthStatus[];
|
|
84
|
+
checks: Array<{
|
|
85
|
+
id: string;
|
|
86
|
+
status: "ok" | "warn" | "fail";
|
|
87
|
+
detail: string;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getPackageVersion(): string;
|
package/docs/GOALS.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Goal chain
|
|
2
|
+
|
|
3
|
+
## Goal 1: CLI foundation and Hugging Face catalog
|
|
4
|
+
|
|
5
|
+
Status: in progress in this repo.
|
|
6
|
+
|
|
7
|
+
- Publishable Bun/TypeScript package.
|
|
8
|
+
- `models` CLI with JSON contracts.
|
|
9
|
+
- Hugging Face auth discovery through env, local config, and `secrets`.
|
|
10
|
+
- SQLite catalog under `~/.hasna/models/models.db`.
|
|
11
|
+
- Search, info, files, best-model indexing, and selected-file installs.
|
|
12
|
+
- Tiny model download smoke tests.
|
|
13
|
+
|
|
14
|
+
## Goal 2: Dataset-first workflows
|
|
15
|
+
|
|
16
|
+
- Dataset search/info/files/install parity.
|
|
17
|
+
- Safe sample/stream commands before full materialization.
|
|
18
|
+
- Split-aware metadata and max-byte defaults.
|
|
19
|
+
- Dataset fixture tests with tiny public datasets.
|
|
20
|
+
|
|
21
|
+
## Goal 3: Runtime adapters
|
|
22
|
+
|
|
23
|
+
- `ollama` detection and pull/run helpers.
|
|
24
|
+
- `llama.cpp` GGUF plan/run/serve helpers.
|
|
25
|
+
- `vllm` server plan for GPU machines.
|
|
26
|
+
- `mlx-lm` plan for Apple Silicon.
|
|
27
|
+
- Runtime compatibility reports that explain memory, disk, and command gaps.
|
|
28
|
+
|
|
29
|
+
## Goal 4: Machine-aware installs
|
|
30
|
+
|
|
31
|
+
- Optional `@hasna/machines/consumer` adapter.
|
|
32
|
+
- CLI fallback to installed `machines`.
|
|
33
|
+
- Local fallback when machines is absent.
|
|
34
|
+
- `models machines preflight`, `models machines install`, and remote dry-run plans.
|
|
35
|
+
|
|
36
|
+
## Goal 5: Provider expansion
|
|
37
|
+
|
|
38
|
+
- GitHub release artifact provider.
|
|
39
|
+
- ModelScope model/dataset provider.
|
|
40
|
+
- Kaggle dataset/model provider.
|
|
41
|
+
- Civitai model/version artifact provider.
|
|
42
|
+
- Provider conformance fixtures so Hugging Face is not special-cased.
|
|
43
|
+
|
|
44
|
+
## Goal 6: Agent and release interfaces
|
|
45
|
+
|
|
46
|
+
- `models-mcp` with CLI/MCP parity manifest.
|
|
47
|
+
- SDK fixtures and JSON contract snapshots.
|
|
48
|
+
- Local REST/serve option if needed by dashboards.
|
|
49
|
+
- Release smoke: build, test, pack, local install, tiny download, best index.
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasna/models",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI-first local model and dataset lifecycle tool for open-source/open-weight catalogs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./storage": {
|
|
15
|
+
"import": "./dist/storage.js",
|
|
16
|
+
"types": "./dist/storage.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"models": "dist/cli/index.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"docs",
|
|
25
|
+
"README.md",
|
|
26
|
+
"PLAN.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "rm -rf dist && bun build src/cli/index.ts --outdir dist/cli --target bun && bun build src/index.ts src/storage.ts --outdir dist --target bun && tsc --emitDeclarationOnly --outDir dist",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"test": "bun test",
|
|
33
|
+
"dev": "bun run src/cli/index.ts",
|
|
34
|
+
"pack:local": "bun pm pack",
|
|
35
|
+
"prepublishOnly": "bun run build && bun test"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"models",
|
|
39
|
+
"huggingface",
|
|
40
|
+
"datasets",
|
|
41
|
+
"gguf",
|
|
42
|
+
"safetensors",
|
|
43
|
+
"local-ai",
|
|
44
|
+
"cli",
|
|
45
|
+
"sqlite"
|
|
46
|
+
],
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/hasna/models.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/hasna/models",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/hasna/models/issues"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"bun": ">=1.0.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"chalk": "^5.6.2",
|
|
60
|
+
"commander": "^13.1.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/bun": "latest",
|
|
64
|
+
"@types/node": "^20.14.0",
|
|
65
|
+
"typescript": "^5.7.3"
|
|
66
|
+
},
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"registry": "https://registry.npmjs.org",
|
|
69
|
+
"access": "public"
|
|
70
|
+
}
|
|
71
|
+
}
|