@character-foundry/character-foundry 0.1.3 → 0.1.6

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.
Files changed (100) hide show
  1. package/README.md +70 -0
  2. package/dist/app-framework.cjs +1859 -0
  3. package/dist/app-framework.cjs.map +1 -0
  4. package/dist/app-framework.d.cts +896 -0
  5. package/dist/app-framework.d.ts +896 -2
  6. package/dist/app-framework.js +1835 -1
  7. package/dist/app-framework.js.map +1 -1
  8. package/dist/charx.cjs +979 -0
  9. package/dist/charx.cjs.map +1 -0
  10. package/dist/charx.d.cts +640 -0
  11. package/dist/charx.d.ts +640 -2
  12. package/dist/charx.js +955 -1
  13. package/dist/charx.js.map +1 -1
  14. package/dist/core.cjs +755 -0
  15. package/dist/core.cjs.map +1 -0
  16. package/dist/core.d.cts +404 -0
  17. package/dist/core.d.ts +404 -2
  18. package/dist/core.js +731 -1
  19. package/dist/core.js.map +1 -1
  20. package/dist/exporter.cjs +7619 -0
  21. package/dist/exporter.cjs.map +1 -0
  22. package/dist/exporter.d.cts +681 -0
  23. package/dist/exporter.d.ts +681 -2
  24. package/dist/exporter.js +7602 -1
  25. package/dist/exporter.js.map +1 -1
  26. package/dist/federation.cjs +3916 -0
  27. package/dist/federation.cjs.map +1 -0
  28. package/dist/federation.d.cts +2951 -0
  29. package/dist/federation.d.ts +2951 -2
  30. package/dist/federation.js +3892 -1
  31. package/dist/federation.js.map +1 -1
  32. package/dist/index.cjs +9213 -0
  33. package/dist/index.cjs.map +1 -0
  34. package/dist/index.d.cts +1119 -0
  35. package/dist/index.d.ts +1113 -20
  36. package/dist/index.js +9196 -26
  37. package/dist/index.js.map +1 -1
  38. package/dist/loader.cjs +8951 -0
  39. package/dist/loader.cjs.map +1 -0
  40. package/dist/loader.d.cts +1037 -0
  41. package/dist/loader.d.ts +1037 -2
  42. package/dist/loader.js +8934 -1
  43. package/dist/loader.js.map +1 -1
  44. package/dist/lorebook.cjs +866 -0
  45. package/dist/lorebook.cjs.map +1 -0
  46. package/dist/lorebook.d.cts +1008 -0
  47. package/dist/lorebook.d.ts +1008 -2
  48. package/dist/lorebook.js +842 -1
  49. package/dist/lorebook.js.map +1 -1
  50. package/dist/media.cjs +6661 -0
  51. package/dist/media.cjs.map +1 -0
  52. package/dist/media.d.cts +87 -0
  53. package/dist/media.d.ts +87 -2
  54. package/dist/media.js +6644 -1
  55. package/dist/media.js.map +1 -1
  56. package/dist/normalizer.cjs +503 -0
  57. package/dist/normalizer.cjs.map +1 -0
  58. package/dist/normalizer.d.cts +1217 -0
  59. package/dist/normalizer.d.ts +1217 -2
  60. package/dist/normalizer.js +479 -1
  61. package/dist/normalizer.js.map +1 -1
  62. package/dist/png.cjs +797 -0
  63. package/dist/png.cjs.map +1 -0
  64. package/dist/png.d.cts +786 -0
  65. package/dist/png.d.ts +786 -2
  66. package/dist/png.js +773 -1
  67. package/dist/png.js.map +1 -1
  68. package/dist/schemas.cjs +879 -0
  69. package/dist/schemas.cjs.map +1 -0
  70. package/dist/schemas.d.cts +2208 -0
  71. package/dist/schemas.d.ts +2208 -2
  72. package/dist/schemas.js +855 -1
  73. package/dist/schemas.js.map +1 -1
  74. package/dist/tokenizers.cjs +153 -0
  75. package/dist/tokenizers.cjs.map +1 -0
  76. package/dist/tokenizers.d.cts +155 -0
  77. package/dist/tokenizers.d.ts +155 -2
  78. package/dist/tokenizers.js +129 -1
  79. package/dist/tokenizers.js.map +1 -1
  80. package/dist/voxta.cjs +7907 -0
  81. package/dist/voxta.cjs.map +1 -0
  82. package/dist/voxta.d.cts +1349 -0
  83. package/dist/voxta.d.ts +1349 -2
  84. package/dist/voxta.js +7890 -1
  85. package/dist/voxta.js.map +1 -1
  86. package/package.json +177 -45
  87. package/dist/app-framework.d.ts.map +0 -1
  88. package/dist/charx.d.ts.map +0 -1
  89. package/dist/core.d.ts.map +0 -1
  90. package/dist/exporter.d.ts.map +0 -1
  91. package/dist/federation.d.ts.map +0 -1
  92. package/dist/index.d.ts.map +0 -1
  93. package/dist/loader.d.ts.map +0 -1
  94. package/dist/lorebook.d.ts.map +0 -1
  95. package/dist/media.d.ts.map +0 -1
  96. package/dist/normalizer.d.ts.map +0 -1
  97. package/dist/png.d.ts.map +0 -1
  98. package/dist/schemas.d.ts.map +0 -1
  99. package/dist/tokenizers.d.ts.map +0 -1
  100. package/dist/voxta.d.ts.map +0 -1
@@ -0,0 +1,879 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/schemas.ts
21
+ var schemas_exports = {};
22
+ __export(schemas_exports, {
23
+ AssetDescriptorSchema: () => AssetDescriptorSchema,
24
+ AssetTypeSchema: () => AssetTypeSchema,
25
+ CCv2CharacterBookSchema: () => CCv2CharacterBookSchema,
26
+ CCv2DataSchema: () => CCv2DataSchema,
27
+ CCv2LorebookEntrySchema: () => CCv2LorebookEntrySchema,
28
+ CCv2WrappedSchema: () => CCv2WrappedSchema,
29
+ CCv3CharacterBookSchema: () => CCv3CharacterBookSchema,
30
+ CCv3DataInnerSchema: () => CCv3DataInnerSchema,
31
+ CCv3DataSchema: () => CCv3DataSchema,
32
+ CCv3LorebookEntrySchema: () => CCv3LorebookEntrySchema,
33
+ CardNormalizer: () => CardNormalizer,
34
+ ExtractedAssetSchema: () => ExtractedAssetSchema,
35
+ ISO8601Schema: () => ISO8601Schema,
36
+ OriginalShapeSchema: () => OriginalShapeSchema,
37
+ SourceFormatSchema: () => SourceFormatSchema,
38
+ SpecSchema: () => SpecSchema,
39
+ UUIDSchema: () => UUIDSchema,
40
+ createEmptyFeatures: () => createEmptyFeatures,
41
+ createEmptyNormalizedCard: () => createEmptyNormalizedCard,
42
+ deriveFeatures: () => deriveFeatures,
43
+ detectSpec: () => detectSpec,
44
+ detectSpecDetailed: () => detectSpecDetailed,
45
+ getFirstErrorField: () => getFirstErrorField,
46
+ getV2Data: () => getV2Data,
47
+ getV3Data: () => getV3Data,
48
+ hasDepthPrompt: () => hasDepthPrompt,
49
+ hasLorebook: () => hasLorebook,
50
+ hasRisuExtensions: () => hasRisuExtensions,
51
+ hasRisuScripts: () => hasRisuScripts,
52
+ isV2CardData: () => isV2CardData,
53
+ isV3Card: () => isV3Card,
54
+ isWrappedV2: () => isWrappedV2,
55
+ looksLikeCard: () => looksLikeCard,
56
+ looksLikeV3Card: () => looksLikeV3Card,
57
+ looksLikeWrappedV2: () => looksLikeWrappedV2,
58
+ parseV2Data: () => parseV2Data,
59
+ parseV3Card: () => parseV3Card,
60
+ parseV3DataInner: () => parseV3DataInner,
61
+ parseWrappedV2: () => parseWrappedV2,
62
+ safeParse: () => safeParse,
63
+ zodErrorToMessage: () => zodErrorToMessage
64
+ });
65
+ module.exports = __toCommonJS(schemas_exports);
66
+
67
+ // ../schemas/dist/index.js
68
+ var import_zod = require("zod");
69
+ var import_zod2 = require("zod");
70
+ var import_zod3 = require("zod");
71
+ var import_zod4 = require("zod");
72
+ var ISO8601Schema = import_zod.z.string().datetime();
73
+ var UUIDSchema = import_zod.z.string().uuid();
74
+ var SpecSchema = import_zod.z.enum(["v2", "v3"]);
75
+ var SourceFormatSchema = import_zod.z.enum([
76
+ "png_v2",
77
+ // PNG with 'chara' chunk (v2)
78
+ "png_v3",
79
+ // PNG with 'ccv3' chunk (v3)
80
+ "json_v2",
81
+ // Raw JSON v2
82
+ "json_v3",
83
+ // Raw JSON v3
84
+ "charx",
85
+ // ZIP with card.json (v3 spec)
86
+ "charx_risu",
87
+ // ZIP with card.json + module.risum
88
+ "charx_jpeg",
89
+ // JPEG with appended ZIP (read-only)
90
+ "voxta"
91
+ // VoxPkg format
92
+ ]);
93
+ var OriginalShapeSchema = import_zod.z.enum(["wrapped", "unwrapped", "legacy"]);
94
+ var AssetTypeSchema = import_zod.z.enum([
95
+ "icon",
96
+ "background",
97
+ "emotion",
98
+ "user_icon",
99
+ "sound",
100
+ "video",
101
+ "custom",
102
+ "x-risu-asset"
103
+ ]);
104
+ var AssetDescriptorSchema = import_zod.z.object({
105
+ type: AssetTypeSchema,
106
+ uri: import_zod.z.string(),
107
+ name: import_zod.z.string(),
108
+ ext: import_zod.z.string()
109
+ });
110
+ var ExtractedAssetSchema = import_zod.z.object({
111
+ descriptor: AssetDescriptorSchema,
112
+ data: import_zod.z.instanceof(Uint8Array),
113
+ mimeType: import_zod.z.string()
114
+ });
115
+ var CCv2LorebookEntrySchema = import_zod2.z.object({
116
+ keys: import_zod2.z.array(import_zod2.z.string()),
117
+ content: import_zod2.z.string(),
118
+ enabled: import_zod2.z.boolean(),
119
+ insertion_order: import_zod2.z.number().int(),
120
+ // Optional fields
121
+ extensions: import_zod2.z.record(import_zod2.z.unknown()).optional(),
122
+ case_sensitive: import_zod2.z.boolean().optional(),
123
+ name: import_zod2.z.string().optional(),
124
+ priority: import_zod2.z.number().int().optional(),
125
+ id: import_zod2.z.number().int().optional(),
126
+ comment: import_zod2.z.string().optional(),
127
+ selective: import_zod2.z.boolean().optional(),
128
+ secondary_keys: import_zod2.z.array(import_zod2.z.string()).optional(),
129
+ constant: import_zod2.z.boolean().optional(),
130
+ position: import_zod2.z.enum(["before_char", "after_char"]).optional()
131
+ });
132
+ var CCv2CharacterBookSchema = import_zod2.z.object({
133
+ name: import_zod2.z.string().optional(),
134
+ description: import_zod2.z.string().optional(),
135
+ scan_depth: import_zod2.z.number().int().nonnegative().optional(),
136
+ token_budget: import_zod2.z.number().int().nonnegative().optional(),
137
+ recursive_scanning: import_zod2.z.boolean().optional(),
138
+ extensions: import_zod2.z.record(import_zod2.z.unknown()).optional(),
139
+ entries: import_zod2.z.array(CCv2LorebookEntrySchema)
140
+ });
141
+ var CCv2DataSchema = import_zod2.z.object({
142
+ // Core fields - use .default('') to handle missing fields in malformed cards
143
+ name: import_zod2.z.string().default(""),
144
+ description: import_zod2.z.string().default(""),
145
+ personality: import_zod2.z.string().nullable().default(""),
146
+ // Can be null in wild (141 cards)
147
+ scenario: import_zod2.z.string().default(""),
148
+ first_mes: import_zod2.z.string().default(""),
149
+ mes_example: import_zod2.z.string().nullable().default(""),
150
+ // Can be null in wild (186 cards)
151
+ // Optional fields
152
+ creator_notes: import_zod2.z.string().optional(),
153
+ system_prompt: import_zod2.z.string().optional(),
154
+ post_history_instructions: import_zod2.z.string().optional(),
155
+ alternate_greetings: import_zod2.z.array(import_zod2.z.string()).optional(),
156
+ character_book: CCv2CharacterBookSchema.optional().nullable(),
157
+ tags: import_zod2.z.array(import_zod2.z.string()).optional(),
158
+ creator: import_zod2.z.string().optional(),
159
+ character_version: import_zod2.z.string().optional(),
160
+ extensions: import_zod2.z.record(import_zod2.z.unknown()).optional()
161
+ });
162
+ var CCv2WrappedSchema = import_zod2.z.object({
163
+ spec: import_zod2.z.literal("chara_card_v2"),
164
+ spec_version: import_zod2.z.literal("2.0"),
165
+ data: CCv2DataSchema
166
+ });
167
+ function isWrappedV2(data) {
168
+ return CCv2WrappedSchema.safeParse(data).success;
169
+ }
170
+ function isV2CardData(data) {
171
+ return CCv2WrappedSchema.safeParse(data).success || CCv2DataSchema.safeParse(data).success;
172
+ }
173
+ function parseWrappedV2(data) {
174
+ return CCv2WrappedSchema.parse(data);
175
+ }
176
+ function parseV2Data(data) {
177
+ return CCv2DataSchema.parse(data);
178
+ }
179
+ function looksLikeWrappedV2(data) {
180
+ if (!data || typeof data !== "object") return false;
181
+ const obj = data;
182
+ return obj.spec === "chara_card_v2" && obj.data !== null && typeof obj.data === "object";
183
+ }
184
+ function getV2Data(card) {
185
+ if (looksLikeWrappedV2(card)) {
186
+ return card.data;
187
+ }
188
+ return card;
189
+ }
190
+ var CCv3LorebookEntrySchema = import_zod3.z.object({
191
+ keys: import_zod3.z.array(import_zod3.z.string()),
192
+ content: import_zod3.z.string(),
193
+ enabled: import_zod3.z.boolean(),
194
+ insertion_order: import_zod3.z.number().int(),
195
+ // Optional fields
196
+ case_sensitive: import_zod3.z.boolean().optional(),
197
+ name: import_zod3.z.string().optional(),
198
+ priority: import_zod3.z.number().int().optional(),
199
+ id: import_zod3.z.number().int().optional(),
200
+ comment: import_zod3.z.string().optional(),
201
+ selective: import_zod3.z.boolean().optional(),
202
+ secondary_keys: import_zod3.z.array(import_zod3.z.string()).optional(),
203
+ constant: import_zod3.z.boolean().optional(),
204
+ position: import_zod3.z.enum(["before_char", "after_char"]).optional(),
205
+ extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
206
+ // v3 specific
207
+ automation_id: import_zod3.z.string().optional(),
208
+ role: import_zod3.z.enum(["system", "user", "assistant"]).optional(),
209
+ group: import_zod3.z.string().optional(),
210
+ scan_frequency: import_zod3.z.number().int().nonnegative().optional(),
211
+ probability: import_zod3.z.number().min(0).max(1).optional(),
212
+ use_regex: import_zod3.z.boolean().optional(),
213
+ depth: import_zod3.z.number().int().nonnegative().optional(),
214
+ selective_logic: import_zod3.z.enum(["AND", "NOT"]).optional()
215
+ });
216
+ var CCv3CharacterBookSchema = import_zod3.z.object({
217
+ name: import_zod3.z.string().optional(),
218
+ description: import_zod3.z.string().optional(),
219
+ scan_depth: import_zod3.z.number().int().nonnegative().optional(),
220
+ token_budget: import_zod3.z.number().int().nonnegative().optional(),
221
+ recursive_scanning: import_zod3.z.boolean().optional(),
222
+ extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
223
+ entries: import_zod3.z.array(CCv3LorebookEntrySchema)
224
+ });
225
+ var CCv3DataInnerSchema = import_zod3.z.object({
226
+ // Core fields - use .default('') to handle missing fields in malformed cards
227
+ name: import_zod3.z.string().default(""),
228
+ description: import_zod3.z.string().default(""),
229
+ personality: import_zod3.z.string().nullable().default(""),
230
+ // Can be null in wild (141 cards)
231
+ scenario: import_zod3.z.string().default(""),
232
+ first_mes: import_zod3.z.string().default(""),
233
+ mes_example: import_zod3.z.string().nullable().default(""),
234
+ // Can be null in wild (186 cards)
235
+ // "Required" per spec but often missing in wild - use defaults for leniency
236
+ creator: import_zod3.z.string().default(""),
237
+ character_version: import_zod3.z.string().default(""),
238
+ tags: import_zod3.z.array(import_zod3.z.string()).default([]),
239
+ group_only_greetings: import_zod3.z.array(import_zod3.z.string()).default([]),
240
+ // Optional fields
241
+ creator_notes: import_zod3.z.string().optional(),
242
+ system_prompt: import_zod3.z.string().optional(),
243
+ post_history_instructions: import_zod3.z.string().optional(),
244
+ alternate_greetings: import_zod3.z.array(import_zod3.z.string()).optional(),
245
+ character_book: CCv3CharacterBookSchema.optional().nullable(),
246
+ extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
247
+ // v3 specific
248
+ assets: import_zod3.z.array(AssetDescriptorSchema).optional(),
249
+ nickname: import_zod3.z.string().optional(),
250
+ creator_notes_multilingual: import_zod3.z.record(import_zod3.z.string()).optional(),
251
+ source: import_zod3.z.array(import_zod3.z.string()).optional(),
252
+ creation_date: import_zod3.z.number().int().nonnegative().optional(),
253
+ // Unix timestamp in seconds
254
+ modification_date: import_zod3.z.number().int().nonnegative().optional()
255
+ // Unix timestamp in seconds
256
+ });
257
+ var CCv3DataSchema = import_zod3.z.object({
258
+ spec: import_zod3.z.literal("chara_card_v3"),
259
+ spec_version: import_zod3.z.literal("3.0"),
260
+ data: CCv3DataInnerSchema
261
+ });
262
+ function isV3Card(data) {
263
+ return CCv3DataSchema.safeParse(data).success;
264
+ }
265
+ function parseV3Card(data) {
266
+ return CCv3DataSchema.parse(data);
267
+ }
268
+ function parseV3DataInner(data) {
269
+ return CCv3DataInnerSchema.parse(data);
270
+ }
271
+ function getV3Data(card) {
272
+ return card.data;
273
+ }
274
+ function looksLikeV3Card(data) {
275
+ if (!data || typeof data !== "object") return false;
276
+ const obj = data;
277
+ return obj.spec === "chara_card_v3" && obj.data !== null && typeof obj.data === "object";
278
+ }
279
+ function hasRisuExtensions(extensions) {
280
+ if (!extensions) return false;
281
+ return "risuai" in extensions || "risu" in extensions;
282
+ }
283
+ function hasRisuScripts(extensions) {
284
+ if (!extensions) return false;
285
+ const risu = extensions.risuai;
286
+ if (!risu) return false;
287
+ return !!risu.triggerscript || !!risu.customScripts;
288
+ }
289
+ function hasDepthPrompt(extensions) {
290
+ if (!extensions) return false;
291
+ if ("depth_prompt" in extensions && extensions.depth_prompt) return true;
292
+ const risu = extensions.risuai;
293
+ return !!risu?.depth_prompt;
294
+ }
295
+ function createEmptyNormalizedCard() {
296
+ return {
297
+ name: "",
298
+ description: "",
299
+ personality: "",
300
+ scenario: "",
301
+ firstMes: "",
302
+ mesExample: "",
303
+ alternateGreetings: [],
304
+ groupOnlyGreetings: [],
305
+ tags: [],
306
+ extensions: {}
307
+ };
308
+ }
309
+ function createEmptyFeatures() {
310
+ return {
311
+ hasAlternateGreetings: false,
312
+ alternateGreetingsCount: 0,
313
+ totalGreetingsCount: 1,
314
+ // first_mes always counts as 1
315
+ hasLorebook: false,
316
+ lorebookEntriesCount: 0,
317
+ hasEmbeddedImages: false,
318
+ embeddedImagesCount: 0,
319
+ hasGallery: false,
320
+ hasRisuExtensions: false,
321
+ hasRisuScripts: false,
322
+ hasDepthPrompt: false,
323
+ hasVoxtaAppearance: false,
324
+ tokens: {
325
+ description: 0,
326
+ personality: 0,
327
+ scenario: 0,
328
+ firstMes: 0,
329
+ mesExample: 0,
330
+ systemPrompt: 0,
331
+ total: 0
332
+ }
333
+ };
334
+ }
335
+ function deriveFeatures(card) {
336
+ const isV3 = "assets" in card;
337
+ const altGreetings = card.alternate_greetings ?? [];
338
+ const hasAlternateGreetings = altGreetings.length > 0;
339
+ const alternateGreetingsCount = altGreetings.length;
340
+ const totalGreetingsCount = 1 + alternateGreetingsCount;
341
+ const characterBook = card.character_book;
342
+ const hasLorebook2 = !!characterBook && characterBook.entries.length > 0;
343
+ const lorebookEntriesCount = characterBook?.entries.length ?? 0;
344
+ const assets = isV3 ? card.assets ?? [] : [];
345
+ const imageAssetTypes = ["icon", "background", "emotion", "custom"];
346
+ const imageAssets = assets.filter(
347
+ (a) => imageAssetTypes.includes(a.type) || ["png", "jpg", "jpeg", "webp", "gif"].includes(a.ext.toLowerCase())
348
+ );
349
+ const hasGallery = imageAssets.length > 0;
350
+ const embeddedImageCount = countEmbeddedImages(card);
351
+ const hasEmbeddedImages = embeddedImageCount > 0;
352
+ const extensions = card.extensions ?? {};
353
+ const hasRisu = hasRisuExtensions(extensions);
354
+ const hasScripts = hasRisuScripts(extensions);
355
+ const hasDepth = hasDepthPrompt(extensions);
356
+ const hasVoxta = checkVoxtaAppearance(extensions);
357
+ const tokens = {
358
+ description: 0,
359
+ personality: 0,
360
+ scenario: 0,
361
+ firstMes: 0,
362
+ mesExample: 0,
363
+ systemPrompt: 0,
364
+ total: 0
365
+ };
366
+ return {
367
+ hasAlternateGreetings,
368
+ alternateGreetingsCount,
369
+ totalGreetingsCount,
370
+ hasLorebook: hasLorebook2,
371
+ lorebookEntriesCount,
372
+ hasEmbeddedImages,
373
+ embeddedImagesCount: embeddedImageCount,
374
+ hasGallery,
375
+ hasRisuExtensions: hasRisu,
376
+ hasRisuScripts: hasScripts,
377
+ hasDepthPrompt: hasDepth,
378
+ hasVoxtaAppearance: hasVoxta,
379
+ tokens
380
+ };
381
+ }
382
+ function countEmbeddedImages(card) {
383
+ const textFields = [
384
+ card.description,
385
+ card.personality,
386
+ card.scenario,
387
+ card.first_mes,
388
+ card.mes_example,
389
+ card.creator_notes,
390
+ card.system_prompt,
391
+ card.post_history_instructions,
392
+ ...card.alternate_greetings ?? []
393
+ ].filter((field) => typeof field === "string");
394
+ if ("group_only_greetings" in card) {
395
+ textFields.push(...card.group_only_greetings ?? []);
396
+ }
397
+ let count = 0;
398
+ const dataUrlPattern = /data:image\/[^;]+;base64,/g;
399
+ for (const text of textFields) {
400
+ const matches = text.match(dataUrlPattern);
401
+ if (matches) {
402
+ count += matches.length;
403
+ }
404
+ }
405
+ return count;
406
+ }
407
+ function checkVoxtaAppearance(extensions) {
408
+ if (!extensions.voxta) return false;
409
+ const voxta = extensions.voxta;
410
+ return !!voxta.appearance;
411
+ }
412
+ var V3_ONLY_FIELDS = ["group_only_greetings", "creation_date", "modification_date", "assets"];
413
+ function detectSpec(data) {
414
+ return detectSpecDetailed(data).spec;
415
+ }
416
+ function detectSpecDetailed(data) {
417
+ const result = {
418
+ spec: null,
419
+ confidence: "low",
420
+ indicators: [],
421
+ warnings: []
422
+ };
423
+ if (!data || typeof data !== "object") {
424
+ result.indicators.push("Input is not an object");
425
+ return result;
426
+ }
427
+ const obj = data;
428
+ const dataObj = obj.data && typeof obj.data === "object" ? obj.data : null;
429
+ if (obj.spec === "chara_card_v3") {
430
+ result.spec = "v3";
431
+ result.confidence = "high";
432
+ result.indicators.push('spec field is "chara_card_v3"');
433
+ if (obj.spec_version && obj.spec_version !== "3.0") {
434
+ result.warnings.push(`spec_version "${obj.spec_version}" inconsistent with v3 spec`);
435
+ }
436
+ return result;
437
+ }
438
+ if (obj.spec === "chara_card_v2") {
439
+ result.spec = "v2";
440
+ result.confidence = "high";
441
+ result.indicators.push('spec field is "chara_card_v2"');
442
+ if (dataObj) {
443
+ for (const field of V3_ONLY_FIELDS) {
444
+ if (field in dataObj) {
445
+ result.warnings.push(`V3-only field "${field}" found in V2 card`);
446
+ }
447
+ }
448
+ }
449
+ if (obj.spec_version && obj.spec_version !== "2.0") {
450
+ result.warnings.push(`spec_version "${obj.spec_version}" inconsistent with v2 spec`);
451
+ }
452
+ return result;
453
+ }
454
+ if (typeof obj.spec_version === "string") {
455
+ if (obj.spec_version.startsWith("3")) {
456
+ result.spec = "v3";
457
+ result.confidence = "high";
458
+ result.indicators.push(`spec_version "${obj.spec_version}" starts with "3"`);
459
+ return result;
460
+ }
461
+ if (obj.spec_version.startsWith("2")) {
462
+ result.spec = "v2";
463
+ result.confidence = "high";
464
+ result.indicators.push(`spec_version "${obj.spec_version}" starts with "2"`);
465
+ return result;
466
+ }
467
+ }
468
+ if (obj.spec_version === 2 || obj.spec_version === 2) {
469
+ result.spec = "v2";
470
+ result.confidence = "high";
471
+ result.indicators.push(`spec_version is numeric ${obj.spec_version}`);
472
+ return result;
473
+ }
474
+ if (dataObj) {
475
+ const v3Fields = [];
476
+ for (const field of V3_ONLY_FIELDS) {
477
+ if (field in dataObj) {
478
+ v3Fields.push(field);
479
+ }
480
+ }
481
+ if (v3Fields.length > 0) {
482
+ result.spec = "v3";
483
+ result.confidence = "medium";
484
+ result.indicators.push(`Has V3-only fields: ${v3Fields.join(", ")}`);
485
+ return result;
486
+ }
487
+ }
488
+ const rootV3Fields = [];
489
+ for (const field of V3_ONLY_FIELDS) {
490
+ if (field in obj) {
491
+ rootV3Fields.push(field);
492
+ }
493
+ }
494
+ if (rootV3Fields.length > 0) {
495
+ result.spec = "v3";
496
+ result.confidence = "medium";
497
+ result.indicators.push(`Has V3-only fields at root: ${rootV3Fields.join(", ")}`);
498
+ result.warnings.push("V3 fields found at root level instead of data object");
499
+ return result;
500
+ }
501
+ if (obj.spec && dataObj) {
502
+ const dataName = dataObj.name;
503
+ if (dataName && typeof dataName === "string") {
504
+ if (typeof obj.spec === "string") {
505
+ if (obj.spec.includes("v3") || obj.spec.includes("3")) {
506
+ result.spec = "v3";
507
+ result.confidence = "medium";
508
+ result.indicators.push(`spec field "${obj.spec}" contains "v3" or "3"`);
509
+ return result;
510
+ }
511
+ if (obj.spec.includes("v2") || obj.spec.includes("2")) {
512
+ result.spec = "v2";
513
+ result.confidence = "medium";
514
+ result.indicators.push(`spec field "${obj.spec}" contains "v2" or "2"`);
515
+ return result;
516
+ }
517
+ }
518
+ result.spec = "v3";
519
+ result.confidence = "medium";
520
+ result.indicators.push("Has wrapped format with spec and data.name");
521
+ return result;
522
+ }
523
+ }
524
+ if (obj.name && typeof obj.name === "string") {
525
+ if ("description" in obj || "personality" in obj || "scenario" in obj) {
526
+ result.spec = "v2";
527
+ result.confidence = "medium";
528
+ result.indicators.push("Unwrapped format with name, description/personality/scenario");
529
+ return result;
530
+ }
531
+ }
532
+ if (dataObj && typeof dataObj.name === "string") {
533
+ if ("description" in dataObj || "personality" in dataObj) {
534
+ result.spec = "v2";
535
+ result.confidence = "low";
536
+ result.indicators.push("Has data object with name and card fields, but no spec");
537
+ result.warnings.push("Missing spec field");
538
+ return result;
539
+ }
540
+ }
541
+ result.indicators.push("No card structure detected");
542
+ return result;
543
+ }
544
+ function hasLorebook(data) {
545
+ if (!data || typeof data !== "object") return false;
546
+ const obj = data;
547
+ const wrapped = obj.data;
548
+ if (wrapped?.character_book) {
549
+ const book = wrapped.character_book;
550
+ if (Array.isArray(book.entries) && book.entries.length > 0) return true;
551
+ }
552
+ if (obj.character_book) {
553
+ const book = obj.character_book;
554
+ if (Array.isArray(book.entries) && book.entries.length > 0) return true;
555
+ }
556
+ return false;
557
+ }
558
+ function looksLikeCard(data) {
559
+ if (!data || typeof data !== "object") return false;
560
+ const obj = data;
561
+ if (obj.spec === "chara_card_v2" || obj.spec === "chara_card_v3") {
562
+ return true;
563
+ }
564
+ if (obj.data && typeof obj.data === "object") {
565
+ const dataObj = obj.data;
566
+ if (typeof dataObj.name === "string" && dataObj.name.length > 0) {
567
+ return true;
568
+ }
569
+ }
570
+ if (typeof obj.name === "string" && obj.name.length > 0) {
571
+ if ("description" in obj || "personality" in obj || "first_mes" in obj) {
572
+ return true;
573
+ }
574
+ }
575
+ return false;
576
+ }
577
+ var POSITION_MAP = {
578
+ 0: "before_char",
579
+ 1: "after_char"
580
+ };
581
+ var V3_ONLY_ENTRY_FIELDS = [
582
+ "probability",
583
+ "depth",
584
+ "group",
585
+ "scan_frequency",
586
+ "use_regex",
587
+ "selective_logic",
588
+ "role",
589
+ "automation_id"
590
+ ];
591
+ var V2_REQUIRED_DEFAULTS = {
592
+ name: "",
593
+ description: "",
594
+ personality: "",
595
+ scenario: "",
596
+ first_mes: "",
597
+ mes_example: ""
598
+ };
599
+ var V3_REQUIRED_DEFAULTS = {
600
+ name: "",
601
+ description: "",
602
+ personality: "",
603
+ scenario: "",
604
+ first_mes: "",
605
+ mes_example: "",
606
+ creator: "",
607
+ character_version: "1.0",
608
+ tags: [],
609
+ group_only_greetings: []
610
+ };
611
+ var DATA_FIELDS = [
612
+ "name",
613
+ "description",
614
+ "personality",
615
+ "scenario",
616
+ "first_mes",
617
+ "mes_example",
618
+ "creator_notes",
619
+ "system_prompt",
620
+ "post_history_instructions",
621
+ "alternate_greetings",
622
+ "character_book",
623
+ "tags",
624
+ "creator",
625
+ "character_version",
626
+ "extensions",
627
+ "assets",
628
+ "nickname",
629
+ "creator_notes_multilingual",
630
+ "source",
631
+ "creation_date",
632
+ "modification_date",
633
+ "group_only_greetings"
634
+ ];
635
+ function deepClone(obj) {
636
+ if (obj === null || obj === void 0) {
637
+ return obj;
638
+ }
639
+ if (Array.isArray(obj)) {
640
+ return obj.map((item) => deepClone(item));
641
+ }
642
+ if (typeof obj === "object") {
643
+ const result = {};
644
+ for (const [key, value] of Object.entries(obj)) {
645
+ result[key] = deepClone(value);
646
+ }
647
+ return result;
648
+ }
649
+ return obj;
650
+ }
651
+ function isMilliseconds(timestamp) {
652
+ return timestamp > 1e10;
653
+ }
654
+ var CardNormalizer = {
655
+ /**
656
+ * Normalize card data to valid schema format.
657
+ *
658
+ * Handles:
659
+ * - Fixing spec/spec_version values
660
+ * - Moving misplaced fields to correct locations
661
+ * - Adding missing required fields with defaults
662
+ * - Handling hybrid formats (fields at root AND in data object)
663
+ *
664
+ * @param data - Raw card data (potentially malformed)
665
+ * @param spec - Target spec version
666
+ * @returns Normalized card data (does not mutate input)
667
+ */
668
+ normalize(data, spec) {
669
+ if (!data || typeof data !== "object") {
670
+ if (spec === "v3") {
671
+ return {
672
+ spec: "chara_card_v3",
673
+ spec_version: "3.0",
674
+ data: { ...V3_REQUIRED_DEFAULTS }
675
+ };
676
+ }
677
+ return {
678
+ spec: "chara_card_v2",
679
+ spec_version: "2.0",
680
+ data: { ...V2_REQUIRED_DEFAULTS }
681
+ };
682
+ }
683
+ const obj = data;
684
+ const result = {};
685
+ const existingData = obj.data && typeof obj.data === "object" ? obj.data : {};
686
+ const mergedData = {};
687
+ for (const [key, value] of Object.entries(existingData)) {
688
+ mergedData[key] = deepClone(value);
689
+ }
690
+ for (const field of DATA_FIELDS) {
691
+ if (field in obj && !(field in mergedData)) {
692
+ mergedData[field] = deepClone(obj[field]);
693
+ }
694
+ }
695
+ if (mergedData.character_book === null) {
696
+ delete mergedData.character_book;
697
+ }
698
+ if (mergedData.character_book && typeof mergedData.character_book === "object") {
699
+ mergedData.character_book = this.normalizeCharacterBook(
700
+ mergedData.character_book,
701
+ spec
702
+ );
703
+ }
704
+ const defaults = spec === "v3" ? V3_REQUIRED_DEFAULTS : V2_REQUIRED_DEFAULTS;
705
+ for (const [key, defaultValue] of Object.entries(defaults)) {
706
+ if (!(key in mergedData) || mergedData[key] === void 0) {
707
+ mergedData[key] = Array.isArray(defaultValue) ? [...defaultValue] : defaultValue;
708
+ }
709
+ }
710
+ if (mergedData.tags && !Array.isArray(mergedData.tags)) {
711
+ mergedData.tags = [];
712
+ }
713
+ if (mergedData.alternate_greetings && !Array.isArray(mergedData.alternate_greetings)) {
714
+ mergedData.alternate_greetings = [];
715
+ }
716
+ if (spec === "v3") {
717
+ if (mergedData.group_only_greetings && !Array.isArray(mergedData.group_only_greetings)) {
718
+ mergedData.group_only_greetings = [];
719
+ }
720
+ }
721
+ if (spec === "v3") {
722
+ result.spec = "chara_card_v3";
723
+ result.spec_version = "3.0";
724
+ result.data = this.fixTimestampsInner(mergedData);
725
+ } else {
726
+ result.spec = "chara_card_v2";
727
+ result.spec_version = "2.0";
728
+ result.data = mergedData;
729
+ }
730
+ return result;
731
+ },
732
+ /**
733
+ * Normalize a character book (lorebook).
734
+ *
735
+ * Handles:
736
+ * - Ensuring required fields exist
737
+ * - Converting numeric position values to string enums
738
+ * - Moving V3-only fields to extensions for V2 compatibility
739
+ *
740
+ * @param book - Raw character book data
741
+ * @param spec - Target spec version
742
+ * @returns Normalized character book
743
+ */
744
+ normalizeCharacterBook(book, spec) {
745
+ const result = {};
746
+ if (book.name !== void 0) result.name = book.name;
747
+ if (book.description !== void 0) result.description = book.description;
748
+ if (book.scan_depth !== void 0) result.scan_depth = book.scan_depth;
749
+ if (book.token_budget !== void 0) result.token_budget = book.token_budget;
750
+ if (book.recursive_scanning !== void 0)
751
+ result.recursive_scanning = book.recursive_scanning;
752
+ if (book.extensions !== void 0) result.extensions = deepClone(book.extensions);
753
+ const entries = Array.isArray(book.entries) ? book.entries : [];
754
+ result.entries = entries.map(
755
+ (entry) => this.normalizeEntry(entry, spec)
756
+ );
757
+ return result;
758
+ },
759
+ /**
760
+ * Normalize a single lorebook entry.
761
+ *
762
+ * Handles:
763
+ * - Converting numeric position to string enum
764
+ * - Moving V3-only fields to extensions for V2
765
+ * - Ensuring required fields exist
766
+ *
767
+ * @param entry - Raw entry data
768
+ * @param spec - Target spec version
769
+ * @returns Normalized entry
770
+ */
771
+ normalizeEntry(entry, spec) {
772
+ const result = {};
773
+ result.keys = Array.isArray(entry.keys) ? [...entry.keys] : [];
774
+ result.content = typeof entry.content === "string" ? entry.content : "";
775
+ result.enabled = entry.enabled !== false;
776
+ result.insertion_order = typeof entry.insertion_order === "number" ? entry.insertion_order : 0;
777
+ if (spec === "v2") {
778
+ result.extensions = entry.extensions && typeof entry.extensions === "object" ? deepClone(entry.extensions) : {};
779
+ }
780
+ if (entry.case_sensitive !== void 0) result.case_sensitive = entry.case_sensitive;
781
+ if (entry.name !== void 0) result.name = entry.name;
782
+ if (entry.priority !== void 0) result.priority = entry.priority;
783
+ if (entry.id !== void 0) result.id = entry.id;
784
+ if (entry.comment !== void 0) result.comment = entry.comment;
785
+ if (entry.selective !== void 0) result.selective = entry.selective;
786
+ if (entry.secondary_keys !== void 0) {
787
+ result.secondary_keys = Array.isArray(entry.secondary_keys) ? [...entry.secondary_keys] : [];
788
+ }
789
+ if (entry.constant !== void 0) result.constant = entry.constant;
790
+ if (entry.position !== void 0) {
791
+ if (typeof entry.position === "number") {
792
+ result.position = POSITION_MAP[entry.position] || "before_char";
793
+ } else if (entry.position === "before_char" || entry.position === "after_char") {
794
+ result.position = entry.position;
795
+ }
796
+ }
797
+ if (spec === "v3") {
798
+ if (entry.extensions !== void 0) result.extensions = deepClone(entry.extensions);
799
+ for (const field of V3_ONLY_ENTRY_FIELDS) {
800
+ if (entry[field] !== void 0) {
801
+ result[field] = entry[field];
802
+ }
803
+ }
804
+ } else {
805
+ const ext = result.extensions || {};
806
+ for (const field of V3_ONLY_ENTRY_FIELDS) {
807
+ if (entry[field] !== void 0) {
808
+ ext[field] = entry[field];
809
+ }
810
+ }
811
+ result.extensions = ext;
812
+ }
813
+ return result;
814
+ },
815
+ /**
816
+ * Fix CharacterTavern timestamp format (milliseconds -> seconds).
817
+ *
818
+ * CCv3 spec requires timestamps in seconds (Unix epoch).
819
+ * CharacterTavern exports timestamps in milliseconds.
820
+ *
821
+ * @param data - V3 card data
822
+ * @returns Card data with fixed timestamps (does not mutate input)
823
+ */
824
+ fixTimestamps(data) {
825
+ const result = deepClone(data);
826
+ result.data = this.fixTimestampsInner(
827
+ result.data
828
+ );
829
+ return result;
830
+ },
831
+ /**
832
+ * Internal: fix timestamps in data object
833
+ */
834
+ fixTimestampsInner(data) {
835
+ const result = { ...data };
836
+ if (typeof result.creation_date === "number" && isMilliseconds(result.creation_date)) {
837
+ result.creation_date = Math.floor(result.creation_date / 1e3);
838
+ }
839
+ if (typeof result.modification_date === "number" && isMilliseconds(result.modification_date)) {
840
+ result.modification_date = Math.floor(result.modification_date / 1e3);
841
+ }
842
+ return result;
843
+ },
844
+ /**
845
+ * Auto-detect spec and normalize.
846
+ *
847
+ * @param data - Raw card data
848
+ * @returns Normalized card data, or null if not a valid card
849
+ */
850
+ autoNormalize(data) {
851
+ const spec = detectSpec(data);
852
+ if (!spec) return null;
853
+ const targetSpec = spec === "v3" ? "v3" : "v2";
854
+ return this.normalize(data, targetSpec);
855
+ }
856
+ };
857
+ function zodErrorToMessage(zodError, context) {
858
+ const messages = zodError.errors.map((err) => {
859
+ const path = err.path.length > 0 ? `${err.path.join(".")}: ` : "";
860
+ return `${path}${err.message}`;
861
+ });
862
+ const message = messages.join("; ");
863
+ return context ? `${context} - ${message}` : message;
864
+ }
865
+ function getFirstErrorField(zodError) {
866
+ return zodError.errors[0]?.path[0]?.toString();
867
+ }
868
+ function safeParse(schema, data) {
869
+ const result = schema.safeParse(data);
870
+ if (result.success) {
871
+ return { success: true, data: result.data };
872
+ }
873
+ return {
874
+ success: false,
875
+ error: zodErrorToMessage(result.error),
876
+ field: getFirstErrorField(result.error)
877
+ };
878
+ }
879
+ //# sourceMappingURL=schemas.cjs.map