@character-foundry/character-foundry 0.4.2 → 0.4.3-dev.1766103111
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/dist/charx.cjs +87 -54
- package/dist/charx.cjs.map +1 -1
- package/dist/charx.d.cts +40 -31
- package/dist/charx.d.ts +40 -31
- package/dist/charx.js +87 -54
- package/dist/charx.js.map +1 -1
- package/dist/exporter.cjs +106 -56
- package/dist/exporter.cjs.map +1 -1
- package/dist/exporter.d.cts +37 -28
- package/dist/exporter.d.ts +37 -28
- package/dist/exporter.js +106 -56
- package/dist/exporter.js.map +1 -1
- package/dist/federation.cjs +104 -36
- package/dist/federation.cjs.map +1 -1
- package/dist/federation.d.cts +72 -28
- package/dist/federation.d.ts +72 -28
- package/dist/federation.js +104 -36
- package/dist/federation.js.map +1 -1
- package/dist/index.cjs +106 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -50
- package/dist/index.d.ts +71 -50
- package/dist/index.js +106 -56
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +173 -33
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.d.cts +65 -37
- package/dist/loader.d.ts +65 -37
- package/dist/loader.js +173 -33
- package/dist/loader.js.map +1 -1
- package/dist/lorebook.d.cts +57 -40
- package/dist/lorebook.d.ts +57 -40
- package/dist/normalizer.cjs +74 -20
- package/dist/normalizer.cjs.map +1 -1
- package/dist/normalizer.d.cts +97 -67
- package/dist/normalizer.d.ts +97 -67
- package/dist/normalizer.js +74 -20
- package/dist/normalizer.js.map +1 -1
- package/dist/png.cjs +74 -20
- package/dist/png.cjs.map +1 -1
- package/dist/png.d.cts +57 -41
- package/dist/png.d.ts +57 -41
- package/dist/png.js +74 -20
- package/dist/png.js.map +1 -1
- package/dist/schemas.cjs +82 -25
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +181 -115
- package/dist/schemas.d.ts +181 -115
- package/dist/schemas.js +82 -25
- package/dist/schemas.js.map +1 -1
- package/dist/voxta.cjs +93 -22
- package/dist/voxta.cjs.map +1 -1
- package/dist/voxta.d.cts +51 -37
- package/dist/voxta.d.ts +51 -37
- package/dist/voxta.js +93 -22
- package/dist/voxta.js.map +1 -1
- package/package.json +5 -5
package/dist/exporter.cjs
CHANGED
|
@@ -216,6 +216,58 @@ var import_zod = require("zod");
|
|
|
216
216
|
var import_zod2 = require("zod");
|
|
217
217
|
var import_zod3 = require("zod");
|
|
218
218
|
var import_zod4 = require("zod");
|
|
219
|
+
function preprocessTimestamp(val) {
|
|
220
|
+
if (val === null || val === void 0) return void 0;
|
|
221
|
+
let num;
|
|
222
|
+
if (typeof val === "number") {
|
|
223
|
+
num = val;
|
|
224
|
+
} else if (typeof val === "string") {
|
|
225
|
+
const trimmed = val.trim();
|
|
226
|
+
if (!trimmed) return void 0;
|
|
227
|
+
const parsed = Number(trimmed);
|
|
228
|
+
if (!isNaN(parsed)) {
|
|
229
|
+
num = parsed;
|
|
230
|
+
} else {
|
|
231
|
+
const date = new Date(trimmed);
|
|
232
|
+
if (isNaN(date.getTime())) return void 0;
|
|
233
|
+
num = Math.floor(date.getTime() / 1e3);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
if (num > 1e10) {
|
|
239
|
+
num = Math.floor(num / 1e3);
|
|
240
|
+
}
|
|
241
|
+
if (num < 0) return void 0;
|
|
242
|
+
return num;
|
|
243
|
+
}
|
|
244
|
+
function preprocessNumeric(val) {
|
|
245
|
+
if (val === null || val === void 0) return void 0;
|
|
246
|
+
if (typeof val === "number") {
|
|
247
|
+
return isNaN(val) ? void 0 : val;
|
|
248
|
+
}
|
|
249
|
+
if (typeof val === "string") {
|
|
250
|
+
const trimmed = val.trim();
|
|
251
|
+
if (!trimmed) return void 0;
|
|
252
|
+
const parsed = Number(trimmed);
|
|
253
|
+
return isNaN(parsed) ? void 0 : parsed;
|
|
254
|
+
}
|
|
255
|
+
return void 0;
|
|
256
|
+
}
|
|
257
|
+
var KNOWN_ASSET_TYPES = /* @__PURE__ */ new Set([
|
|
258
|
+
"icon",
|
|
259
|
+
"background",
|
|
260
|
+
"emotion",
|
|
261
|
+
"user_icon",
|
|
262
|
+
"sound",
|
|
263
|
+
"video",
|
|
264
|
+
"custom",
|
|
265
|
+
"x-risu-asset"
|
|
266
|
+
]);
|
|
267
|
+
function preprocessAssetType(val) {
|
|
268
|
+
if (typeof val !== "string") return "custom";
|
|
269
|
+
return KNOWN_ASSET_TYPES.has(val) ? val : "custom";
|
|
270
|
+
}
|
|
219
271
|
var ISO8601Schema = import_zod.z.string().datetime();
|
|
220
272
|
var UUIDSchema = import_zod.z.string().uuid();
|
|
221
273
|
var SpecSchema = import_zod.z.enum(["v2", "v3"]);
|
|
@@ -238,16 +290,19 @@ var SourceFormatSchema = import_zod.z.enum([
|
|
|
238
290
|
// VoxPkg format
|
|
239
291
|
]);
|
|
240
292
|
var OriginalShapeSchema = import_zod.z.enum(["wrapped", "unwrapped", "legacy"]);
|
|
241
|
-
var AssetTypeSchema = import_zod.z.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
293
|
+
var AssetTypeSchema = import_zod.z.preprocess(
|
|
294
|
+
preprocessAssetType,
|
|
295
|
+
import_zod.z.enum([
|
|
296
|
+
"icon",
|
|
297
|
+
"background",
|
|
298
|
+
"emotion",
|
|
299
|
+
"user_icon",
|
|
300
|
+
"sound",
|
|
301
|
+
"video",
|
|
302
|
+
"custom",
|
|
303
|
+
"x-risu-asset"
|
|
304
|
+
])
|
|
305
|
+
);
|
|
251
306
|
var AssetDescriptorSchema = import_zod.z.object({
|
|
252
307
|
type: AssetTypeSchema,
|
|
253
308
|
uri: import_zod.z.string(),
|
|
@@ -276,13 +331,13 @@ var CCv2LorebookEntrySchema = import_zod2.z.object({
|
|
|
276
331
|
selective: import_zod2.z.boolean().nullable().optional(),
|
|
277
332
|
secondary_keys: import_zod2.z.array(import_zod2.z.string()).nullable().optional(),
|
|
278
333
|
constant: import_zod2.z.boolean().nullable().optional(),
|
|
279
|
-
position: import_zod2.z.union([import_zod2.z.enum(["before_char", "after_char"]), import_zod2.z.number().int(), import_zod2.z.literal("")]).nullable().optional()
|
|
334
|
+
position: import_zod2.z.union([import_zod2.z.enum(["before_char", "after_char", "in_chat"]), import_zod2.z.number().int(), import_zod2.z.literal("")]).nullable().optional()
|
|
280
335
|
}).passthrough();
|
|
281
336
|
var CCv2CharacterBookSchema = import_zod2.z.object({
|
|
282
337
|
name: import_zod2.z.string().optional(),
|
|
283
338
|
description: import_zod2.z.string().optional(),
|
|
284
|
-
scan_depth: import_zod2.z.number().int().nonnegative().optional(),
|
|
285
|
-
token_budget: import_zod2.z.number().int().nonnegative().optional(),
|
|
339
|
+
scan_depth: import_zod2.z.preprocess(preprocessNumeric, import_zod2.z.number().int().nonnegative().optional()),
|
|
340
|
+
token_budget: import_zod2.z.preprocess(preprocessNumeric, import_zod2.z.number().int().nonnegative().optional()),
|
|
286
341
|
recursive_scanning: import_zod2.z.boolean().optional(),
|
|
287
342
|
extensions: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
288
343
|
entries: import_zod2.z.array(CCv2LorebookEntrySchema)
|
|
@@ -329,7 +384,7 @@ var CCv3LorebookEntrySchema = import_zod3.z.object({
|
|
|
329
384
|
selective: import_zod3.z.boolean().nullable().optional(),
|
|
330
385
|
secondary_keys: import_zod3.z.array(import_zod3.z.string()).nullable().optional(),
|
|
331
386
|
constant: import_zod3.z.boolean().nullable().optional(),
|
|
332
|
-
position: import_zod3.z.union([import_zod3.z.enum(["before_char", "after_char"]), import_zod3.z.number().int(), import_zod3.z.literal("")]).nullable().optional(),
|
|
387
|
+
position: import_zod3.z.union([import_zod3.z.enum(["before_char", "after_char", "in_chat"]), import_zod3.z.number().int(), import_zod3.z.literal("")]).nullable().optional(),
|
|
333
388
|
extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
|
|
334
389
|
// v3 specific - also lenient with types since SillyTavern uses numbers for enums
|
|
335
390
|
automation_id: import_zod3.z.string().optional(),
|
|
@@ -345,8 +400,8 @@ var CCv3LorebookEntrySchema = import_zod3.z.object({
|
|
|
345
400
|
var CCv3CharacterBookSchema = import_zod3.z.object({
|
|
346
401
|
name: import_zod3.z.string().optional(),
|
|
347
402
|
description: import_zod3.z.string().optional(),
|
|
348
|
-
scan_depth: import_zod3.z.number().int().nonnegative().optional(),
|
|
349
|
-
token_budget: import_zod3.z.number().int().nonnegative().optional(),
|
|
403
|
+
scan_depth: import_zod3.z.preprocess(preprocessNumeric, import_zod3.z.number().int().nonnegative().optional()),
|
|
404
|
+
token_budget: import_zod3.z.preprocess(preprocessNumeric, import_zod3.z.number().int().nonnegative().optional()),
|
|
350
405
|
recursive_scanning: import_zod3.z.boolean().optional(),
|
|
351
406
|
extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
|
|
352
407
|
entries: import_zod3.z.array(CCv3LorebookEntrySchema)
|
|
@@ -378,10 +433,9 @@ var CCv3DataInnerSchema = import_zod3.z.object({
|
|
|
378
433
|
nickname: import_zod3.z.string().optional(),
|
|
379
434
|
creator_notes_multilingual: import_zod3.z.record(import_zod3.z.string()).optional(),
|
|
380
435
|
source: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
modification_date: import_zod3.z.number().int().nonnegative().optional()
|
|
384
|
-
// Unix timestamp in seconds
|
|
436
|
+
// Unix timestamps - preprocess to handle ISO strings, numeric strings, milliseconds
|
|
437
|
+
creation_date: import_zod3.z.preprocess(preprocessTimestamp, import_zod3.z.number().int().nonnegative().optional()),
|
|
438
|
+
modification_date: import_zod3.z.preprocess(preprocessTimestamp, import_zod3.z.number().int().nonnegative().optional())
|
|
385
439
|
});
|
|
386
440
|
var CCv3DataSchema = import_zod3.z.object({
|
|
387
441
|
spec: import_zod3.z.literal("chara_card_v3"),
|
|
@@ -546,36 +600,6 @@ var SAFE_ASSET_TYPES = /* @__PURE__ */ new Set([
|
|
|
546
600
|
"data",
|
|
547
601
|
"unknown"
|
|
548
602
|
]);
|
|
549
|
-
var SAFE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
550
|
-
// Images
|
|
551
|
-
"png",
|
|
552
|
-
"jpg",
|
|
553
|
-
"jpeg",
|
|
554
|
-
"webp",
|
|
555
|
-
"gif",
|
|
556
|
-
"avif",
|
|
557
|
-
"svg",
|
|
558
|
-
"bmp",
|
|
559
|
-
"ico",
|
|
560
|
-
// Audio
|
|
561
|
-
"mp3",
|
|
562
|
-
"wav",
|
|
563
|
-
"ogg",
|
|
564
|
-
"flac",
|
|
565
|
-
"m4a",
|
|
566
|
-
"aac",
|
|
567
|
-
"opus",
|
|
568
|
-
// Video
|
|
569
|
-
"mp4",
|
|
570
|
-
"webm",
|
|
571
|
-
"avi",
|
|
572
|
-
"mov",
|
|
573
|
-
"mkv",
|
|
574
|
-
// Data
|
|
575
|
-
"json",
|
|
576
|
-
"txt",
|
|
577
|
-
"bin"
|
|
578
|
-
]);
|
|
579
603
|
function getCharxCategory(mimetype) {
|
|
580
604
|
if (mimetype.startsWith("image/")) return "images";
|
|
581
605
|
if (mimetype.startsWith("audio/")) return "audio";
|
|
@@ -591,11 +615,20 @@ function sanitizeAssetType(type) {
|
|
|
591
615
|
return sanitized || "custom";
|
|
592
616
|
}
|
|
593
617
|
function sanitizeExtension(ext) {
|
|
594
|
-
const normalized = ext.replace(/^\./, "").toLowerCase()
|
|
595
|
-
if (
|
|
596
|
-
|
|
618
|
+
const normalized = ext.trim().replace(/^\./, "").toLowerCase();
|
|
619
|
+
if (!normalized) {
|
|
620
|
+
throw new Error("Invalid asset extension: empty extension");
|
|
621
|
+
}
|
|
622
|
+
if (normalized.length > 64) {
|
|
623
|
+
throw new Error(`Invalid asset extension: too long (${normalized.length} chars)`);
|
|
624
|
+
}
|
|
625
|
+
if (normalized.includes("/") || normalized.includes("\\") || normalized.includes("\0")) {
|
|
626
|
+
throw new Error("Invalid asset extension: path separators are not allowed");
|
|
597
627
|
}
|
|
598
|
-
|
|
628
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
|
|
629
|
+
throw new Error(`Invalid asset extension: "${ext}"`);
|
|
630
|
+
}
|
|
631
|
+
return normalized;
|
|
599
632
|
}
|
|
600
633
|
function sanitizeName(name, ext) {
|
|
601
634
|
let safeName = name;
|
|
@@ -7046,6 +7079,22 @@ function sanitizeName2(name, ext) {
|
|
|
7046
7079
|
if (!safeName) safeName = "asset";
|
|
7047
7080
|
return safeName;
|
|
7048
7081
|
}
|
|
7082
|
+
function sanitizeExtension2(ext) {
|
|
7083
|
+
const normalized = ext.trim().replace(/^\./, "").toLowerCase();
|
|
7084
|
+
if (!normalized) {
|
|
7085
|
+
throw new Error("Invalid asset extension: empty extension");
|
|
7086
|
+
}
|
|
7087
|
+
if (normalized.length > 64) {
|
|
7088
|
+
throw new Error(`Invalid asset extension: too long (${normalized.length} chars)`);
|
|
7089
|
+
}
|
|
7090
|
+
if (normalized.includes("/") || normalized.includes("\\") || normalized.includes("\0")) {
|
|
7091
|
+
throw new Error("Invalid asset extension: path separators are not allowed");
|
|
7092
|
+
}
|
|
7093
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
|
|
7094
|
+
throw new Error(`Invalid asset extension: "${ext}"`);
|
|
7095
|
+
}
|
|
7096
|
+
return normalized;
|
|
7097
|
+
}
|
|
7049
7098
|
function writeVoxta(card, assets, options = {}) {
|
|
7050
7099
|
const { compressionLevel = 6, includePackageJson = false } = options;
|
|
7051
7100
|
const cardData = card.data;
|
|
@@ -7134,8 +7183,9 @@ function writeVoxta(card, assets, options = {}) {
|
|
|
7134
7183
|
let assetCount = 0;
|
|
7135
7184
|
let mainThumbnail;
|
|
7136
7185
|
for (const asset of assets) {
|
|
7137
|
-
const
|
|
7138
|
-
const
|
|
7186
|
+
const safeExt = sanitizeExtension2(asset.ext);
|
|
7187
|
+
const safeName = sanitizeName2(asset.name, safeExt);
|
|
7188
|
+
const finalFilename = `${safeName}.${safeExt}`;
|
|
7139
7189
|
let voxtaPath = "";
|
|
7140
7190
|
const tags = asset.tags || [];
|
|
7141
7191
|
const isMainIcon = asset.type === "icon" && (tags.includes("portrait-override") || asset.name === "main" || asset.isMain);
|