@harpiaresearch/vref 0.0.0-stage → 1.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 +28 -0
- package/README.md +405 -2
- package/dist/VREFReader.d.ts +22 -0
- package/dist/VREFReader.js +185 -0
- package/dist/VREFWriter.d.ts +46 -0
- package/dist/VREFWriter.js +225 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +216 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +16 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.js +18 -0
- package/package.json +46 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Harpia AI Research
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,406 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# VREF — Visual Reference Format
|
|
4
|
+
|
|
5
|
+
**A standardized format for curated visual references in AI image generation**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@harpiaresearch/vref)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
Harpia AI Research
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<div align="center">
|
|
17
|
+
|
|
18
|
+
## The Problem
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
You know exactly what you want your AI to generate, but describing it in words is **nearly impossible**.
|
|
23
|
+
|
|
24
|
+
- 🤔 "How do I describe this art style?"
|
|
25
|
+
- 📸 "I have 20 reference images, but uploading them one by one is tedious"
|
|
26
|
+
- 🔄 "I can't easily share or reuse my reference collections"
|
|
27
|
+
- 😤 "Text prompts alone just don't capture the vision"
|
|
28
|
+
|
|
29
|
+
**Visual concepts are hard to describe, but easy to show.**
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
|
|
35
|
+
## The Solution
|
|
36
|
+
|
|
37
|
+
**VREF** (Visual Reference Format) is a simple, open standard that packages your visual references into a single `.vref` file.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Your 50 reference images → one references.vref file
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
### How It Works
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
1. Package images → references.vref
|
|
49
|
+
2. Upload to AI platform
|
|
50
|
+
3. Platform samples N random images
|
|
51
|
+
4. You write text prompt
|
|
52
|
+
5. AI generates with visual context
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Instead of fighting with text descriptions, you **show the AI what you want**.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
<div align="center">
|
|
60
|
+
|
|
61
|
+
## Why You Should Use VREF
|
|
62
|
+
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
### ✅ For Creators
|
|
66
|
+
|
|
67
|
+
**Stop struggling with prompts.**
|
|
68
|
+
- Package your mood boards, style references, and inspirations
|
|
69
|
+
- Upload once, reuse forever
|
|
70
|
+
- Share with your team or community
|
|
71
|
+
|
|
72
|
+
**Example:** Download `roblox-banners.vref` → Upload → Describe what you want → Get perfect results
|
|
73
|
+
|
|
74
|
+
### ✅ For Platforms
|
|
75
|
+
|
|
76
|
+
**Better user experience.**
|
|
77
|
+
- Users get better results faster
|
|
78
|
+
- Reduces prompt engineering frustration
|
|
79
|
+
- Differentiates your platform
|
|
80
|
+
- Simple to implement (< 1 day)
|
|
81
|
+
|
|
82
|
+
### ✅ For Everyone
|
|
83
|
+
|
|
84
|
+
**It's an open standard.**
|
|
85
|
+
- No vendor lock-in
|
|
86
|
+
- Portable across platforms
|
|
87
|
+
- Community-driven
|
|
88
|
+
- Free forever
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
<div align="center">
|
|
93
|
+
|
|
94
|
+
## Real-World Use Cases
|
|
95
|
+
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
### 🎮 Game Development
|
|
99
|
+
```
|
|
100
|
+
rpg-sprites.vref → Character references
|
|
101
|
+
platformer-tiles.vref → Level design inspiration
|
|
102
|
+
ui-elements.vref → Interface styles
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 🎨 Creative Work
|
|
106
|
+
```
|
|
107
|
+
architecture.vref → Building styles
|
|
108
|
+
color-palettes.vref → Mood and tone
|
|
109
|
+
typography.vref → Font and layout ideas
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 🤖 AI Training
|
|
113
|
+
```
|
|
114
|
+
training-data.vref → Curated datasets
|
|
115
|
+
style-transfer.vref → Style references
|
|
116
|
+
fine-tuning.vref → Model-specific data
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
<div align="center">
|
|
122
|
+
|
|
123
|
+
## Installation
|
|
124
|
+
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm install @harpiaresearch/vref
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Or use directly:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx @harpiaresearch/vref
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
<div align="center">
|
|
140
|
+
|
|
141
|
+
## Quick Start
|
|
142
|
+
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
### Creating a .vref File
|
|
146
|
+
|
|
147
|
+
**CLI:**
|
|
148
|
+
```bash
|
|
149
|
+
# From a directory
|
|
150
|
+
npx @harpiaresearch/vref ./my-images -o references.vref
|
|
151
|
+
|
|
152
|
+
# With metadata
|
|
153
|
+
npx @harpiaresearch/vref ./images -o pack.vref \
|
|
154
|
+
-n "My Reference Pack" \
|
|
155
|
+
-d "Collection of art references" \
|
|
156
|
+
-t "art,style,inspiration"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Code:**
|
|
160
|
+
```typescript
|
|
161
|
+
import { VREFWriter } from '@harpiaresearch/vref';
|
|
162
|
+
|
|
163
|
+
const writer = new VREFWriter({
|
|
164
|
+
fileName: 'references.vref',
|
|
165
|
+
description: 'Art style references',
|
|
166
|
+
tags: ['art', 'style'],
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
writer.addImagesFromDirectory('./images');
|
|
170
|
+
writer.write('references.vref');
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Reading a .vref File
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { VREFReader } from '@harpiaresearch/vref';
|
|
177
|
+
|
|
178
|
+
// Read file
|
|
179
|
+
const vref = VREFReader.read('references.vref', { loadImages: true });
|
|
180
|
+
|
|
181
|
+
console.log(`Loaded ${vref.manifest.imageCount} images`);
|
|
182
|
+
|
|
183
|
+
// Sample 3 random images
|
|
184
|
+
const randomSamples = vref.sample(3);
|
|
185
|
+
|
|
186
|
+
// Get specific image
|
|
187
|
+
const image = vref.getImage('photo1.png');
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
<div align="center">
|
|
193
|
+
|
|
194
|
+
## For AI Platform Developers
|
|
195
|
+
|
|
196
|
+
Integrating VREF takes **< 1 day**:
|
|
197
|
+
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { VREFReader } from '@harpiaresearch/vref';
|
|
202
|
+
|
|
203
|
+
// 1. User uploads .vref
|
|
204
|
+
const vref = VREFReader.read(uploadedFile, { loadImages: true });
|
|
205
|
+
|
|
206
|
+
// 2. Show slider: "How many references? (1-{imageCount})"
|
|
207
|
+
const count = getUserSliderValue();
|
|
208
|
+
|
|
209
|
+
// 3. Sample random images
|
|
210
|
+
const references = vref.sample(count);
|
|
211
|
+
|
|
212
|
+
// 4. Get user prompt
|
|
213
|
+
const prompt = getUserPrompt();
|
|
214
|
+
|
|
215
|
+
// 5. Send to your AI
|
|
216
|
+
await generateImage({
|
|
217
|
+
prompt: prompt,
|
|
218
|
+
referenceImages: references,
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**That's it.** ✅
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## File Format
|
|
227
|
+
|
|
228
|
+
A `.vref` file is a ZIP container with:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
references.vref
|
|
232
|
+
├── manifest.json (metadata)
|
|
233
|
+
├── image1.png
|
|
234
|
+
├── image2.jpg
|
|
235
|
+
└── image3.webp
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Supported formats:** PNG, JPEG, WebP, GIF, AVIF
|
|
239
|
+
|
|
240
|
+
**Only images allowed.** Any other file type is rejected.
|
|
241
|
+
|
|
242
|
+
### manifest.json
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"version": "1.0.0",
|
|
247
|
+
"format": "VREF",
|
|
248
|
+
"fileName": "references.vref",
|
|
249
|
+
"imageCount": 24,
|
|
250
|
+
"images": [
|
|
251
|
+
{
|
|
252
|
+
"path": "image1.png",
|
|
253
|
+
"format": "png",
|
|
254
|
+
"size": 45678,
|
|
255
|
+
"width": 1920,
|
|
256
|
+
"height": 1080
|
|
257
|
+
}
|
|
258
|
+
],
|
|
259
|
+
"createdAt": "2024-10-12T10:30:00.000Z",
|
|
260
|
+
"metadata": {
|
|
261
|
+
"description": "Collection of references",
|
|
262
|
+
"tags": ["style", "mood"],
|
|
263
|
+
"author": "Your Name",
|
|
264
|
+
"license": "CC-BY-4.0"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
<div align="center">
|
|
272
|
+
|
|
273
|
+
## Why VREF Will Win
|
|
274
|
+
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
### 🎯 Solves a Real Problem
|
|
278
|
+
Everyone who uses AI image generation has struggled with describing visual concepts. VREF makes it trivial.
|
|
279
|
+
|
|
280
|
+
### 🚀 Simple to Implement
|
|
281
|
+
ZIP + JSON. No complex dependencies. Works everywhere.
|
|
282
|
+
|
|
283
|
+
### 🌍 Open Standard
|
|
284
|
+
No company owns it. Community-driven. Free forever.
|
|
285
|
+
|
|
286
|
+
### 📦 Already Shipping
|
|
287
|
+
- ✅ npm package published
|
|
288
|
+
- ✅ Full TypeScript support
|
|
289
|
+
- ✅ CLI tool ready
|
|
290
|
+
- ✅ Documentation complete
|
|
291
|
+
|
|
292
|
+
### 🇧🇷 Made in Brazil
|
|
293
|
+
Harpia AI Research is putting Brazil on the map of AI innovation.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
### VREFWriter
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
class VREFWriter {
|
|
301
|
+
constructor(options: VREFCreateOptions);
|
|
302
|
+
|
|
303
|
+
addImageFromFile(filePath: string): void;
|
|
304
|
+
addImage(fileName: string, buffer: Buffer): void;
|
|
305
|
+
addImagesFromDirectory(dirPath: string, recursive?: boolean): void;
|
|
306
|
+
|
|
307
|
+
write(outputPath: string): void;
|
|
308
|
+
getImageCount(): number;
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### VREFReader
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
class VREFReader {
|
|
316
|
+
static read(filePath: string, options?: VREFReadOptions): VREFReadResult;
|
|
317
|
+
static readManifest(filePath: string): VREFManifest;
|
|
318
|
+
static validate(filePath: string): { valid: boolean; errors: string[] };
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### VREFReadResult
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
interface VREFReadResult {
|
|
326
|
+
manifest: VREFManifest;
|
|
327
|
+
images?: Map<string, Buffer>;
|
|
328
|
+
sample: (count: number) => Buffer[];
|
|
329
|
+
getImage: (path: string) => Buffer | undefined;
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Full API documentation: [TypeScript definitions](src/types.ts)
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Roadmap
|
|
338
|
+
|
|
339
|
+
### Phase 1 ✅ (Done)
|
|
340
|
+
- Core library implementation
|
|
341
|
+
- CLI tool
|
|
342
|
+
- npm package published
|
|
343
|
+
- Full documentation
|
|
344
|
+
|
|
345
|
+
### Phase 2 (In Progress)
|
|
346
|
+
- Community-created .vref files
|
|
347
|
+
- Platform integrations
|
|
348
|
+
- Example collections
|
|
349
|
+
|
|
350
|
+
### Phase 3 (Future)
|
|
351
|
+
- Web-based .vref creator
|
|
352
|
+
- .vref marketplace
|
|
353
|
+
- Browser extensions
|
|
354
|
+
- Mobile support
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Join the Movement
|
|
359
|
+
|
|
360
|
+
VREF is just getting started. We need:
|
|
361
|
+
|
|
362
|
+
- **Platform developers**: Integrate VREF support
|
|
363
|
+
- **Creators**: Build and share .vref collections
|
|
364
|
+
- **Contributors**: Improve the library
|
|
365
|
+
- **Evangelists**: Spread the word
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Links
|
|
370
|
+
|
|
371
|
+
- **npm**: https://www.npmjs.com/package/@harpiaresearch/vref
|
|
372
|
+
- **GitHub**: https://github.com/Harpia-AI-Research/vref
|
|
373
|
+
- **Harpia AI Research**: https://harpiaresearch.cc
|
|
374
|
+
- **Issues**: https://github.com/Harpia-AI-Research/vref/issues
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## License
|
|
379
|
+
|
|
380
|
+
This project is licensed under the **BSD 3-Clause License** - see the [LICENSE](LICENSE) file for details.
|
|
381
|
+
|
|
382
|
+
Copyright (c) 2024, Harpia AI Research
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Acknowledgments
|
|
387
|
+
|
|
388
|
+
VREF is an open standard for visual references in AI systems. Built to solve the gap between what creators envision and what they can describe in text.
|
|
389
|
+
|
|
390
|
+
**Technologies**: TypeScript, Node.js, adm-zip
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
<div align="center">
|
|
395
|
+
|
|
396
|
+
**VREF** — Bridging vision and AI generation
|
|
397
|
+
|
|
398
|
+
<br><br>
|
|
399
|
+
|
|
400
|
+
[GitHub](https://github.com/Harpia-AI-Research/vref) •
|
|
401
|
+
[npm](https://www.npmjs.com/package/@harpiaresearch/vref) •
|
|
402
|
+
[Documentation](https://github.com/Harpia-AI-Research/vref#readme) •
|
|
403
|
+
[Issues](https://github.com/Harpia-AI-Research/vref/issues)
|
|
404
|
+
|
|
405
|
+
</div>
|
|
2
406
|
|
|
3
|
-
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VREFReader - Reads .vref files
|
|
3
|
+
* Harpia AI Research
|
|
4
|
+
*/
|
|
5
|
+
import { VREFManifest, VREFReadOptions, VREFReadResult } from './types';
|
|
6
|
+
export declare class VREFReader {
|
|
7
|
+
/**
|
|
8
|
+
* Read a .vref file
|
|
9
|
+
*/
|
|
10
|
+
static read(filePath: string, options?: VREFReadOptions): VREFReadResult;
|
|
11
|
+
/**
|
|
12
|
+
* Read only the manifest without loading images
|
|
13
|
+
*/
|
|
14
|
+
static readManifest(filePath: string): VREFManifest;
|
|
15
|
+
/**
|
|
16
|
+
* Validate a .vref file
|
|
17
|
+
*/
|
|
18
|
+
static validate(filePath: string): {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
errors: string[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VREFReader - Reads .vref files
|
|
4
|
+
* Harpia AI Research
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
|
+
};
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.VREFReader = void 0;
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
46
|
+
class VREFReader {
|
|
47
|
+
/**
|
|
48
|
+
* Read a .vref file
|
|
49
|
+
*/
|
|
50
|
+
static read(filePath, options = {}) {
|
|
51
|
+
if (!fs.existsSync(filePath)) {
|
|
52
|
+
throw new Error(`File not found: ${filePath}`);
|
|
53
|
+
}
|
|
54
|
+
const zip = new adm_zip_1.default(filePath);
|
|
55
|
+
const zipEntries = zip.getEntries();
|
|
56
|
+
// Find and parse manifest
|
|
57
|
+
const manifestEntry = zipEntries.find(entry => entry.entryName === 'manifest.json');
|
|
58
|
+
if (!manifestEntry) {
|
|
59
|
+
throw new Error('Invalid .vref file: manifest.json not found');
|
|
60
|
+
}
|
|
61
|
+
const manifestData = manifestEntry.getData().toString('utf8');
|
|
62
|
+
const manifest = JSON.parse(manifestData);
|
|
63
|
+
// Validate manifest
|
|
64
|
+
if (manifest.format !== 'VREF') {
|
|
65
|
+
throw new Error('Invalid .vref file: incorrect format identifier');
|
|
66
|
+
}
|
|
67
|
+
// Load images if requested
|
|
68
|
+
const images = new Map();
|
|
69
|
+
if (options.loadImages) {
|
|
70
|
+
for (const imageInfo of manifest.images) {
|
|
71
|
+
const entry = zipEntries.find(e => e.entryName === imageInfo.path);
|
|
72
|
+
if (entry) {
|
|
73
|
+
images.set(imageInfo.path, entry.getData());
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Helper function to get specific image
|
|
78
|
+
const getImage = (path) => {
|
|
79
|
+
if (images.has(path)) {
|
|
80
|
+
return images.get(path);
|
|
81
|
+
}
|
|
82
|
+
// Load on-demand if not already loaded
|
|
83
|
+
const entry = zipEntries.find(e => e.entryName === path);
|
|
84
|
+
if (entry) {
|
|
85
|
+
const buffer = entry.getData();
|
|
86
|
+
images.set(path, buffer);
|
|
87
|
+
return buffer;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
};
|
|
91
|
+
// Helper function to sample N random images
|
|
92
|
+
const sample = (count) => {
|
|
93
|
+
const imagePaths = manifest.images.map(img => img.path);
|
|
94
|
+
const sampledPaths = [];
|
|
95
|
+
// Fisher-Yates shuffle for random sampling
|
|
96
|
+
const shuffled = [...imagePaths];
|
|
97
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
98
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
99
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
100
|
+
}
|
|
101
|
+
// Take first N
|
|
102
|
+
const selected = shuffled.slice(0, Math.min(count, shuffled.length));
|
|
103
|
+
// Load and return buffers
|
|
104
|
+
return selected.map(path => {
|
|
105
|
+
const buffer = getImage(path);
|
|
106
|
+
if (!buffer) {
|
|
107
|
+
throw new Error(`Failed to load image: ${path}`);
|
|
108
|
+
}
|
|
109
|
+
return buffer;
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
return {
|
|
113
|
+
manifest,
|
|
114
|
+
images: options.loadImages ? images : undefined,
|
|
115
|
+
sample,
|
|
116
|
+
getImage,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Read only the manifest without loading images
|
|
121
|
+
*/
|
|
122
|
+
static readManifest(filePath) {
|
|
123
|
+
const result = VREFReader.read(filePath, { loadImages: false });
|
|
124
|
+
return result.manifest;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Validate a .vref file
|
|
128
|
+
*/
|
|
129
|
+
static validate(filePath) {
|
|
130
|
+
const errors = [];
|
|
131
|
+
try {
|
|
132
|
+
if (!fs.existsSync(filePath)) {
|
|
133
|
+
errors.push('File does not exist');
|
|
134
|
+
return { valid: false, errors };
|
|
135
|
+
}
|
|
136
|
+
const zip = new adm_zip_1.default(filePath);
|
|
137
|
+
const zipEntries = zip.getEntries();
|
|
138
|
+
// Check for manifest
|
|
139
|
+
const manifestEntry = zipEntries.find(entry => entry.entryName === 'manifest.json');
|
|
140
|
+
if (!manifestEntry) {
|
|
141
|
+
errors.push('manifest.json not found');
|
|
142
|
+
return { valid: false, errors };
|
|
143
|
+
}
|
|
144
|
+
// Parse manifest
|
|
145
|
+
let manifest;
|
|
146
|
+
try {
|
|
147
|
+
const manifestData = manifestEntry.getData().toString('utf8');
|
|
148
|
+
manifest = JSON.parse(manifestData);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
errors.push('Failed to parse manifest.json');
|
|
152
|
+
return { valid: false, errors };
|
|
153
|
+
}
|
|
154
|
+
// Validate format
|
|
155
|
+
if (manifest.format !== 'VREF') {
|
|
156
|
+
errors.push('Invalid format identifier (expected "VREF")');
|
|
157
|
+
}
|
|
158
|
+
// Validate version
|
|
159
|
+
if (!manifest.version) {
|
|
160
|
+
errors.push('Missing version field');
|
|
161
|
+
}
|
|
162
|
+
// Validate image count
|
|
163
|
+
const actualImageCount = zipEntries.filter(e => e.entryName !== 'manifest.json').length;
|
|
164
|
+
if (manifest.imageCount !== actualImageCount) {
|
|
165
|
+
errors.push(`Image count mismatch: manifest says ${manifest.imageCount}, found ${actualImageCount}`);
|
|
166
|
+
}
|
|
167
|
+
// Validate all images listed in manifest exist
|
|
168
|
+
for (const imageInfo of manifest.images) {
|
|
169
|
+
const entry = zipEntries.find(e => e.entryName === imageInfo.path);
|
|
170
|
+
if (!entry) {
|
|
171
|
+
errors.push(`Image listed in manifest not found: ${imageInfo.path}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
valid: errors.length === 0,
|
|
176
|
+
errors,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
errors.push(`Unexpected error: ${error}`);
|
|
181
|
+
return { valid: false, errors };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.VREFReader = VREFReader;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VREFWriter - Creates .vref files
|
|
3
|
+
* Harpia AI Research
|
|
4
|
+
*/
|
|
5
|
+
import { VREFCreateOptions } from './types';
|
|
6
|
+
export declare class VREFWriter {
|
|
7
|
+
private images;
|
|
8
|
+
private options;
|
|
9
|
+
constructor(options: VREFCreateOptions);
|
|
10
|
+
/**
|
|
11
|
+
* Add an image from a file path
|
|
12
|
+
*/
|
|
13
|
+
addImageFromFile(filePath: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Add an image from a buffer
|
|
16
|
+
*/
|
|
17
|
+
addImage(fileName: string, buffer: Buffer): void;
|
|
18
|
+
/**
|
|
19
|
+
* Add multiple images from a directory
|
|
20
|
+
*/
|
|
21
|
+
addImagesFromDirectory(dirPath: string, recursive?: boolean): void;
|
|
22
|
+
/**
|
|
23
|
+
* Write the .vref file
|
|
24
|
+
*/
|
|
25
|
+
write(outputPath: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Validate image format
|
|
28
|
+
*/
|
|
29
|
+
private isValidImageFormat;
|
|
30
|
+
/**
|
|
31
|
+
* Validate image buffer by checking magic bytes
|
|
32
|
+
*/
|
|
33
|
+
private isValidImageBuffer;
|
|
34
|
+
/**
|
|
35
|
+
* Get image dimensions (basic implementation for PNG and JPEG)
|
|
36
|
+
*/
|
|
37
|
+
private getImageDimensions;
|
|
38
|
+
/**
|
|
39
|
+
* Get current image count
|
|
40
|
+
*/
|
|
41
|
+
getImageCount(): number;
|
|
42
|
+
/**
|
|
43
|
+
* Clear all images
|
|
44
|
+
*/
|
|
45
|
+
clear(): void;
|
|
46
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VREFWriter - Creates .vref files
|
|
4
|
+
* Harpia AI Research
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
40
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
|
+
};
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.VREFWriter = void 0;
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
47
|
+
const types_1 = require("./types");
|
|
48
|
+
class VREFWriter {
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.images = new Map();
|
|
51
|
+
this.options = options;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Add an image from a file path
|
|
55
|
+
*/
|
|
56
|
+
addImageFromFile(filePath) {
|
|
57
|
+
if (!fs.existsSync(filePath)) {
|
|
58
|
+
throw new Error(`File not found: ${filePath}`);
|
|
59
|
+
}
|
|
60
|
+
const buffer = fs.readFileSync(filePath);
|
|
61
|
+
const fileName = path.basename(filePath);
|
|
62
|
+
this.addImage(fileName, buffer);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Add an image from a buffer
|
|
66
|
+
*/
|
|
67
|
+
addImage(fileName, buffer) {
|
|
68
|
+
const ext = path.extname(fileName).toLowerCase().slice(1);
|
|
69
|
+
if (!this.isValidImageFormat(ext)) {
|
|
70
|
+
throw new Error(`Unsupported format: ${ext}. Only images are allowed: ${types_1.SUPPORTED_IMAGE_FORMATS.join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
// Validate that it's actually an image by checking magic bytes
|
|
73
|
+
if (!this.isValidImageBuffer(buffer, ext)) {
|
|
74
|
+
throw new Error(`File ${fileName} does not appear to be a valid ${ext} image`);
|
|
75
|
+
}
|
|
76
|
+
this.images.set(fileName, buffer);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Add multiple images from a directory
|
|
80
|
+
*/
|
|
81
|
+
addImagesFromDirectory(dirPath, recursive = false) {
|
|
82
|
+
if (!fs.existsSync(dirPath)) {
|
|
83
|
+
throw new Error(`Directory not found: ${dirPath}`);
|
|
84
|
+
}
|
|
85
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
88
|
+
if (entry.isDirectory() && recursive) {
|
|
89
|
+
this.addImagesFromDirectory(fullPath, recursive);
|
|
90
|
+
}
|
|
91
|
+
else if (entry.isFile()) {
|
|
92
|
+
const ext = path.extname(entry.name).toLowerCase().slice(1);
|
|
93
|
+
if (this.isValidImageFormat(ext)) {
|
|
94
|
+
try {
|
|
95
|
+
this.addImageFromFile(fullPath);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.warn(`Skipping ${entry.name}: ${error}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Write the .vref file
|
|
106
|
+
*/
|
|
107
|
+
write(outputPath) {
|
|
108
|
+
if (this.images.size === 0) {
|
|
109
|
+
throw new Error('No images added. Cannot create empty .vref file.');
|
|
110
|
+
}
|
|
111
|
+
const zip = new adm_zip_1.default();
|
|
112
|
+
const imageMetadataList = [];
|
|
113
|
+
// Add all images to ZIP
|
|
114
|
+
for (const [fileName, buffer] of this.images) {
|
|
115
|
+
zip.addFile(fileName, buffer);
|
|
116
|
+
const ext = path.extname(fileName).toLowerCase().slice(1);
|
|
117
|
+
const dimensions = this.getImageDimensions(buffer, ext);
|
|
118
|
+
imageMetadataList.push({
|
|
119
|
+
path: fileName,
|
|
120
|
+
format: ext,
|
|
121
|
+
size: buffer.length,
|
|
122
|
+
width: dimensions?.width,
|
|
123
|
+
height: dimensions?.height,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// Create manifest
|
|
127
|
+
const manifest = {
|
|
128
|
+
version: '1.0.0',
|
|
129
|
+
format: 'VREF',
|
|
130
|
+
fileName: path.basename(outputPath),
|
|
131
|
+
imageCount: this.images.size,
|
|
132
|
+
images: imageMetadataList,
|
|
133
|
+
createdAt: new Date().toISOString(),
|
|
134
|
+
metadata: {
|
|
135
|
+
description: this.options.description,
|
|
136
|
+
tags: this.options.tags,
|
|
137
|
+
author: this.options.author,
|
|
138
|
+
license: this.options.license,
|
|
139
|
+
...this.options.metadata,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
// Add manifest as JSON
|
|
143
|
+
zip.addFile('manifest.json', Buffer.from(JSON.stringify(manifest, null, 2)));
|
|
144
|
+
// Write ZIP file
|
|
145
|
+
zip.writeZip(outputPath);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Validate image format
|
|
149
|
+
*/
|
|
150
|
+
isValidImageFormat(format) {
|
|
151
|
+
return types_1.SUPPORTED_IMAGE_FORMATS.includes(format);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Validate image buffer by checking magic bytes
|
|
155
|
+
*/
|
|
156
|
+
isValidImageBuffer(buffer, format) {
|
|
157
|
+
if (buffer.length < 8)
|
|
158
|
+
return false;
|
|
159
|
+
switch (format.toLowerCase()) {
|
|
160
|
+
case 'png':
|
|
161
|
+
return buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47;
|
|
162
|
+
case 'jpg':
|
|
163
|
+
case 'jpeg':
|
|
164
|
+
return buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff;
|
|
165
|
+
case 'gif':
|
|
166
|
+
return buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46;
|
|
167
|
+
case 'webp':
|
|
168
|
+
return buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50;
|
|
169
|
+
case 'avif':
|
|
170
|
+
// AVIF detection is more complex, but we can check for 'ftyp' box
|
|
171
|
+
const ftypCheck = buffer.toString('ascii', 4, 8);
|
|
172
|
+
return ftypCheck === 'ftyp';
|
|
173
|
+
default:
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get image dimensions (basic implementation for PNG and JPEG)
|
|
179
|
+
*/
|
|
180
|
+
getImageDimensions(buffer, format) {
|
|
181
|
+
try {
|
|
182
|
+
if (format === 'png' && buffer.length >= 24) {
|
|
183
|
+
const width = buffer.readUInt32BE(16);
|
|
184
|
+
const height = buffer.readUInt32BE(20);
|
|
185
|
+
return { width, height };
|
|
186
|
+
}
|
|
187
|
+
if ((format === 'jpg' || format === 'jpeg') && buffer.length >= 4) {
|
|
188
|
+
// Basic JPEG dimension extraction (simplified)
|
|
189
|
+
// This is a simplified version; production code might use a library
|
|
190
|
+
let offset = 2;
|
|
191
|
+
while (offset < buffer.length) {
|
|
192
|
+
if (buffer[offset] !== 0xff)
|
|
193
|
+
break;
|
|
194
|
+
const marker = buffer[offset + 1];
|
|
195
|
+
offset += 2;
|
|
196
|
+
if (marker === 0xc0 || marker === 0xc2) {
|
|
197
|
+
const height = buffer.readUInt16BE(offset + 3);
|
|
198
|
+
const width = buffer.readUInt16BE(offset + 5);
|
|
199
|
+
return { width, height };
|
|
200
|
+
}
|
|
201
|
+
const segmentLength = buffer.readUInt16BE(offset);
|
|
202
|
+
offset += segmentLength;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
// If dimension extraction fails, just return undefined
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get current image count
|
|
214
|
+
*/
|
|
215
|
+
getImageCount() {
|
|
216
|
+
return this.images.size;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Clear all images
|
|
220
|
+
*/
|
|
221
|
+
clear() {
|
|
222
|
+
this.images.clear();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
exports.VREFWriter = VREFWriter;
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* VREF CLI - Command line interface for creating .vref files
|
|
5
|
+
* Harpia AI Research
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const VREFWriter_1 = require("./VREFWriter");
|
|
44
|
+
function showHelp() {
|
|
45
|
+
console.log(`
|
|
46
|
+
┌─────────────────────────────────────────────────┐
|
|
47
|
+
│ VREF CLI v1.0.0 │
|
|
48
|
+
│ by Harpia AI Research │
|
|
49
|
+
│ https://harpiaresearch.cc │
|
|
50
|
+
└─────────────────────────────────────────────────┘
|
|
51
|
+
|
|
52
|
+
Create .vref files from images
|
|
53
|
+
|
|
54
|
+
USAGE:
|
|
55
|
+
npx create-vref <input> [options]
|
|
56
|
+
|
|
57
|
+
ARGUMENTS:
|
|
58
|
+
<input> Path to image file or directory
|
|
59
|
+
|
|
60
|
+
OPTIONS:
|
|
61
|
+
-o, --output <path> Output .vref file path (default: output.vref)
|
|
62
|
+
-n, --name <name> Name for the reference pack
|
|
63
|
+
-d, --desc <text> Description of the reference pack
|
|
64
|
+
-t, --tags <tags> Comma-separated tags
|
|
65
|
+
-a, --author <name> Author name
|
|
66
|
+
-l, --license <lic> License (e.g., CC-BY-4.0, MIT)
|
|
67
|
+
-r, --recursive Include subdirectories
|
|
68
|
+
-h, --help Show this help
|
|
69
|
+
|
|
70
|
+
EXAMPLES:
|
|
71
|
+
# Create from single image
|
|
72
|
+
npx create-vref image.png -o references.vref
|
|
73
|
+
|
|
74
|
+
# Create from directory
|
|
75
|
+
npx create-vref ./images -o pack.vref -n "My Pack"
|
|
76
|
+
|
|
77
|
+
# With metadata
|
|
78
|
+
npx create-vref ./sprites -o rpg-sprites.vref \\
|
|
79
|
+
-n "RPG Sprites" \\
|
|
80
|
+
-d "Character sprites for RPG games" \\
|
|
81
|
+
-t "rpg,sprites,pixelart" \\
|
|
82
|
+
-a "Your Name" \\
|
|
83
|
+
-l "CC-BY-4.0"
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
function parseArgs(args) {
|
|
87
|
+
const options = {
|
|
88
|
+
input: null,
|
|
89
|
+
output: 'output.vref',
|
|
90
|
+
name: null,
|
|
91
|
+
description: null,
|
|
92
|
+
tags: null,
|
|
93
|
+
author: null,
|
|
94
|
+
license: null,
|
|
95
|
+
recursive: false,
|
|
96
|
+
};
|
|
97
|
+
for (let i = 0; i < args.length; i++) {
|
|
98
|
+
const arg = args[i];
|
|
99
|
+
switch (arg) {
|
|
100
|
+
case '-h':
|
|
101
|
+
case '--help':
|
|
102
|
+
showHelp();
|
|
103
|
+
process.exit(0);
|
|
104
|
+
break;
|
|
105
|
+
case '-o':
|
|
106
|
+
case '--output':
|
|
107
|
+
options.output = args[++i];
|
|
108
|
+
break;
|
|
109
|
+
case '-n':
|
|
110
|
+
case '--name':
|
|
111
|
+
options.name = args[++i];
|
|
112
|
+
break;
|
|
113
|
+
case '-d':
|
|
114
|
+
case '--desc':
|
|
115
|
+
case '--description':
|
|
116
|
+
options.description = args[++i];
|
|
117
|
+
break;
|
|
118
|
+
case '-t':
|
|
119
|
+
case '--tags':
|
|
120
|
+
options.tags = args[++i].split(',').map((t) => t.trim());
|
|
121
|
+
break;
|
|
122
|
+
case '-a':
|
|
123
|
+
case '--author':
|
|
124
|
+
options.author = args[++i];
|
|
125
|
+
break;
|
|
126
|
+
case '-l':
|
|
127
|
+
case '--license':
|
|
128
|
+
options.license = args[++i];
|
|
129
|
+
break;
|
|
130
|
+
case '-r':
|
|
131
|
+
case '--recursive':
|
|
132
|
+
options.recursive = true;
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
if (!arg.startsWith('-')) {
|
|
136
|
+
options.input = arg;
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return options;
|
|
142
|
+
}
|
|
143
|
+
function main() {
|
|
144
|
+
const args = process.argv.slice(2);
|
|
145
|
+
if (args.length === 0) {
|
|
146
|
+
showHelp();
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
149
|
+
const options = parseArgs(args);
|
|
150
|
+
if (!options.input) {
|
|
151
|
+
console.error('❌ Error: No input specified');
|
|
152
|
+
console.log('Run "npx create-vref --help" for usage information');
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
if (!fs.existsSync(options.input)) {
|
|
156
|
+
console.error(`❌ Error: Input not found: ${options.input}`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
console.log('┌─────────────────────────────────────────────────┐');
|
|
160
|
+
console.log('│ VREF CLI v1.0.0 │');
|
|
161
|
+
console.log('│ by Harpia AI Research │');
|
|
162
|
+
console.log('└─────────────────────────────────────────────────┘\n');
|
|
163
|
+
try {
|
|
164
|
+
const fileName = options.name || path.basename(options.output, '.vref');
|
|
165
|
+
const writer = new VREFWriter_1.VREFWriter({
|
|
166
|
+
fileName: options.output,
|
|
167
|
+
description: options.description,
|
|
168
|
+
tags: options.tags,
|
|
169
|
+
author: options.author,
|
|
170
|
+
license: options.license,
|
|
171
|
+
});
|
|
172
|
+
console.log(`📂 Input: ${options.input}`);
|
|
173
|
+
const stat = fs.statSync(options.input);
|
|
174
|
+
if (stat.isDirectory()) {
|
|
175
|
+
console.log(`🔍 Scanning directory${options.recursive ? ' (recursive)' : ''}...`);
|
|
176
|
+
writer.addImagesFromDirectory(options.input, options.recursive);
|
|
177
|
+
}
|
|
178
|
+
else if (stat.isFile()) {
|
|
179
|
+
console.log('📄 Adding image...');
|
|
180
|
+
writer.addImageFromFile(options.input);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
throw new Error('Input must be a file or directory');
|
|
184
|
+
}
|
|
185
|
+
const imageCount = writer.getImageCount();
|
|
186
|
+
if (imageCount === 0) {
|
|
187
|
+
console.error('❌ Error: No valid images found');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
console.log(`✅ Found ${imageCount} valid image${imageCount !== 1 ? 's' : ''}`);
|
|
191
|
+
console.log(`📦 Creating ${options.output}...`);
|
|
192
|
+
writer.write(options.output);
|
|
193
|
+
const stats = fs.statSync(options.output);
|
|
194
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
195
|
+
console.log(`✅ Success! Created ${options.output} (${sizeMB} MB)`);
|
|
196
|
+
console.log('\n📋 Metadata:');
|
|
197
|
+
console.log(` Images: ${imageCount}`);
|
|
198
|
+
if (options.name)
|
|
199
|
+
console.log(` Name: ${options.name}`);
|
|
200
|
+
if (options.description)
|
|
201
|
+
console.log(` Description: ${options.description}`);
|
|
202
|
+
if (options.tags)
|
|
203
|
+
console.log(` Tags: ${options.tags.join(', ')}`);
|
|
204
|
+
if (options.author)
|
|
205
|
+
console.log(` Author: ${options.author}`);
|
|
206
|
+
if (options.license)
|
|
207
|
+
console.log(` License: ${options.license}`);
|
|
208
|
+
console.log('\n🚀 Share this file with AI platforms that support VREF!');
|
|
209
|
+
console.log(' Learn more: https://harpiaresearch.cc\n');
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.error(`\n❌ Error: ${error.message}`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
main();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VREF (Visual Reference Format)
|
|
3
|
+
* Standardized container for curated visual references in AI image generation
|
|
4
|
+
*
|
|
5
|
+
* Harpia AI Research
|
|
6
|
+
* https://harpiaresearch.cc
|
|
7
|
+
*/
|
|
8
|
+
export { VREFWriter } from './VREFWriter';
|
|
9
|
+
export { VREFReader } from './VREFReader';
|
|
10
|
+
export { VREFManifest, VREFCreateOptions, VREFReadOptions, VREFReadResult, ImageMetadata, SupportedImageFormat, SUPPORTED_IMAGE_FORMATS, } from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VREF (Visual Reference Format)
|
|
4
|
+
* Standardized container for curated visual references in AI image generation
|
|
5
|
+
*
|
|
6
|
+
* Harpia AI Research
|
|
7
|
+
* https://harpiaresearch.cc
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SUPPORTED_IMAGE_FORMATS = exports.VREFReader = exports.VREFWriter = void 0;
|
|
11
|
+
var VREFWriter_1 = require("./VREFWriter");
|
|
12
|
+
Object.defineProperty(exports, "VREFWriter", { enumerable: true, get: function () { return VREFWriter_1.VREFWriter; } });
|
|
13
|
+
var VREFReader_1 = require("./VREFReader");
|
|
14
|
+
Object.defineProperty(exports, "VREFReader", { enumerable: true, get: function () { return VREFReader_1.VREFReader; } });
|
|
15
|
+
var types_1 = require("./types");
|
|
16
|
+
Object.defineProperty(exports, "SUPPORTED_IMAGE_FORMATS", { enumerable: true, get: function () { return types_1.SUPPORTED_IMAGE_FORMATS; } });
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VREF (Visual Reference Format) Type Definitions
|
|
3
|
+
* Harpia AI Research
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* VREF Manifest structure
|
|
7
|
+
* Automatically generated and included in every .vref file
|
|
8
|
+
*/
|
|
9
|
+
export interface VREFManifest {
|
|
10
|
+
/** VREF format version (semver) */
|
|
11
|
+
version: string;
|
|
12
|
+
/** Format identifier */
|
|
13
|
+
format: 'VREF';
|
|
14
|
+
/** ZIP compression version (if applicable) */
|
|
15
|
+
zipVersion?: string;
|
|
16
|
+
/** Name of the .vref file */
|
|
17
|
+
fileName: string;
|
|
18
|
+
/** Total number of images in the container */
|
|
19
|
+
imageCount: number;
|
|
20
|
+
/** Array of image metadata */
|
|
21
|
+
images: ImageMetadata[];
|
|
22
|
+
/** ISO 8601 timestamp of creation */
|
|
23
|
+
createdAt: string;
|
|
24
|
+
/** Optional metadata */
|
|
25
|
+
metadata?: {
|
|
26
|
+
/** Description of the reference pack */
|
|
27
|
+
description?: string;
|
|
28
|
+
/** Tags for categorization */
|
|
29
|
+
tags?: string[];
|
|
30
|
+
/** Author information */
|
|
31
|
+
author?: string;
|
|
32
|
+
/** License information */
|
|
33
|
+
license?: string;
|
|
34
|
+
/** Any additional custom fields */
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Metadata for individual images within the VREF
|
|
40
|
+
*/
|
|
41
|
+
export interface ImageMetadata {
|
|
42
|
+
/** Relative path within the .vref archive */
|
|
43
|
+
path: string;
|
|
44
|
+
/** Image format (png, jpg, jpeg, webp, gif, avif) */
|
|
45
|
+
format: string;
|
|
46
|
+
/** File size in bytes */
|
|
47
|
+
size: number;
|
|
48
|
+
/** Image width in pixels */
|
|
49
|
+
width?: number;
|
|
50
|
+
/** Image height in pixels */
|
|
51
|
+
height?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Options for creating a VREF file
|
|
55
|
+
*/
|
|
56
|
+
export interface VREFCreateOptions {
|
|
57
|
+
/** Name for the output .vref file */
|
|
58
|
+
fileName: string;
|
|
59
|
+
/** Optional description */
|
|
60
|
+
description?: string;
|
|
61
|
+
/** Optional tags */
|
|
62
|
+
tags?: string[];
|
|
63
|
+
/** Optional author */
|
|
64
|
+
author?: string;
|
|
65
|
+
/** Optional license */
|
|
66
|
+
license?: string;
|
|
67
|
+
/** Additional metadata */
|
|
68
|
+
metadata?: Record<string, any>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Options for reading a VREF file
|
|
72
|
+
*/
|
|
73
|
+
export interface VREFReadOptions {
|
|
74
|
+
/** Whether to extract images to memory */
|
|
75
|
+
loadImages?: boolean;
|
|
76
|
+
/** Validate image formats */
|
|
77
|
+
validate?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Result of reading a VREF file
|
|
81
|
+
*/
|
|
82
|
+
export interface VREFReadResult {
|
|
83
|
+
/** Parsed manifest */
|
|
84
|
+
manifest: VREFManifest;
|
|
85
|
+
/** Image buffers (if loadImages: true) */
|
|
86
|
+
images?: Map<string, Buffer>;
|
|
87
|
+
/** Get a random sample of N images */
|
|
88
|
+
sample: (count: number) => Buffer[];
|
|
89
|
+
/** Get specific image by path */
|
|
90
|
+
getImage: (path: string) => Buffer | undefined;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Supported image formats
|
|
94
|
+
*/
|
|
95
|
+
export declare const SUPPORTED_IMAGE_FORMATS: readonly ["png", "jpg", "jpeg", "webp", "gif", "avif"];
|
|
96
|
+
export type SupportedImageFormat = typeof SUPPORTED_IMAGE_FORMATS[number];
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VREF (Visual Reference Format) Type Definitions
|
|
4
|
+
* Harpia AI Research
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SUPPORTED_IMAGE_FORMATS = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Supported image formats
|
|
10
|
+
*/
|
|
11
|
+
exports.SUPPORTED_IMAGE_FORMATS = [
|
|
12
|
+
'png',
|
|
13
|
+
'jpg',
|
|
14
|
+
'jpeg',
|
|
15
|
+
'webp',
|
|
16
|
+
'gif',
|
|
17
|
+
'avif'
|
|
18
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harpiaresearch/vref",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Visual Reference Format - Standardized container for curated visual references in AI image generation",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"bin": {
|
|
13
|
+
"create-vref": "./dist/cli.js",
|
|
14
|
+
"vref": "./dist/cli.js"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"test": "node --test",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"vref",
|
|
24
|
+
"visual-reference",
|
|
25
|
+
"ai",
|
|
26
|
+
"image-generation",
|
|
27
|
+
"midjourney",
|
|
28
|
+
"stable-diffusion",
|
|
29
|
+
"harpia"
|
|
30
|
+
],
|
|
31
|
+
"author": "Harpia AI Research",
|
|
32
|
+
"license": "BSD-3-Clause",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/Harpia-AI-Research/vref"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"adm-zip": "^0.5.10"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/adm-zip": "^0.5.5",
|
|
42
|
+
"@types/node": "^20.0.0",
|
|
43
|
+
"typescript": "^5.3.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
}
|
|
48
|
+
}
|