@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/loader.js
CHANGED
|
@@ -413,6 +413,58 @@ import { z } from "zod";
|
|
|
413
413
|
import { z as z2 } from "zod";
|
|
414
414
|
import { z as z3 } from "zod";
|
|
415
415
|
import "zod";
|
|
416
|
+
function preprocessTimestamp(val) {
|
|
417
|
+
if (val === null || val === void 0) return void 0;
|
|
418
|
+
let num;
|
|
419
|
+
if (typeof val === "number") {
|
|
420
|
+
num = val;
|
|
421
|
+
} else if (typeof val === "string") {
|
|
422
|
+
const trimmed = val.trim();
|
|
423
|
+
if (!trimmed) return void 0;
|
|
424
|
+
const parsed = Number(trimmed);
|
|
425
|
+
if (!isNaN(parsed)) {
|
|
426
|
+
num = parsed;
|
|
427
|
+
} else {
|
|
428
|
+
const date = new Date(trimmed);
|
|
429
|
+
if (isNaN(date.getTime())) return void 0;
|
|
430
|
+
num = Math.floor(date.getTime() / 1e3);
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
return void 0;
|
|
434
|
+
}
|
|
435
|
+
if (num > 1e10) {
|
|
436
|
+
num = Math.floor(num / 1e3);
|
|
437
|
+
}
|
|
438
|
+
if (num < 0) return void 0;
|
|
439
|
+
return num;
|
|
440
|
+
}
|
|
441
|
+
function preprocessNumeric(val) {
|
|
442
|
+
if (val === null || val === void 0) return void 0;
|
|
443
|
+
if (typeof val === "number") {
|
|
444
|
+
return isNaN(val) ? void 0 : val;
|
|
445
|
+
}
|
|
446
|
+
if (typeof val === "string") {
|
|
447
|
+
const trimmed = val.trim();
|
|
448
|
+
if (!trimmed) return void 0;
|
|
449
|
+
const parsed = Number(trimmed);
|
|
450
|
+
return isNaN(parsed) ? void 0 : parsed;
|
|
451
|
+
}
|
|
452
|
+
return void 0;
|
|
453
|
+
}
|
|
454
|
+
var KNOWN_ASSET_TYPES = /* @__PURE__ */ new Set([
|
|
455
|
+
"icon",
|
|
456
|
+
"background",
|
|
457
|
+
"emotion",
|
|
458
|
+
"user_icon",
|
|
459
|
+
"sound",
|
|
460
|
+
"video",
|
|
461
|
+
"custom",
|
|
462
|
+
"x-risu-asset"
|
|
463
|
+
]);
|
|
464
|
+
function preprocessAssetType(val) {
|
|
465
|
+
if (typeof val !== "string") return "custom";
|
|
466
|
+
return KNOWN_ASSET_TYPES.has(val) ? val : "custom";
|
|
467
|
+
}
|
|
416
468
|
var ISO8601Schema = z.string().datetime();
|
|
417
469
|
var UUIDSchema = z.string().uuid();
|
|
418
470
|
var SpecSchema = z.enum(["v2", "v3"]);
|
|
@@ -435,16 +487,19 @@ var SourceFormatSchema = z.enum([
|
|
|
435
487
|
// VoxPkg format
|
|
436
488
|
]);
|
|
437
489
|
var OriginalShapeSchema = z.enum(["wrapped", "unwrapped", "legacy"]);
|
|
438
|
-
var AssetTypeSchema = z.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
490
|
+
var AssetTypeSchema = z.preprocess(
|
|
491
|
+
preprocessAssetType,
|
|
492
|
+
z.enum([
|
|
493
|
+
"icon",
|
|
494
|
+
"background",
|
|
495
|
+
"emotion",
|
|
496
|
+
"user_icon",
|
|
497
|
+
"sound",
|
|
498
|
+
"video",
|
|
499
|
+
"custom",
|
|
500
|
+
"x-risu-asset"
|
|
501
|
+
])
|
|
502
|
+
);
|
|
448
503
|
var AssetDescriptorSchema = z.object({
|
|
449
504
|
type: AssetTypeSchema,
|
|
450
505
|
uri: z.string(),
|
|
@@ -473,13 +528,13 @@ var CCv2LorebookEntrySchema = z2.object({
|
|
|
473
528
|
selective: z2.boolean().nullable().optional(),
|
|
474
529
|
secondary_keys: z2.array(z2.string()).nullable().optional(),
|
|
475
530
|
constant: z2.boolean().nullable().optional(),
|
|
476
|
-
position: z2.union([z2.enum(["before_char", "after_char"]), z2.number().int(), z2.literal("")]).nullable().optional()
|
|
531
|
+
position: z2.union([z2.enum(["before_char", "after_char", "in_chat"]), z2.number().int(), z2.literal("")]).nullable().optional()
|
|
477
532
|
}).passthrough();
|
|
478
533
|
var CCv2CharacterBookSchema = z2.object({
|
|
479
534
|
name: z2.string().optional(),
|
|
480
535
|
description: z2.string().optional(),
|
|
481
|
-
scan_depth: z2.number().int().nonnegative().optional(),
|
|
482
|
-
token_budget: z2.number().int().nonnegative().optional(),
|
|
536
|
+
scan_depth: z2.preprocess(preprocessNumeric, z2.number().int().nonnegative().optional()),
|
|
537
|
+
token_budget: z2.preprocess(preprocessNumeric, z2.number().int().nonnegative().optional()),
|
|
483
538
|
recursive_scanning: z2.boolean().optional(),
|
|
484
539
|
extensions: z2.record(z2.unknown()).optional(),
|
|
485
540
|
entries: z2.array(CCv2LorebookEntrySchema)
|
|
@@ -537,7 +592,7 @@ var CCv3LorebookEntrySchema = z3.object({
|
|
|
537
592
|
selective: z3.boolean().nullable().optional(),
|
|
538
593
|
secondary_keys: z3.array(z3.string()).nullable().optional(),
|
|
539
594
|
constant: z3.boolean().nullable().optional(),
|
|
540
|
-
position: z3.union([z3.enum(["before_char", "after_char"]), z3.number().int(), z3.literal("")]).nullable().optional(),
|
|
595
|
+
position: z3.union([z3.enum(["before_char", "after_char", "in_chat"]), z3.number().int(), z3.literal("")]).nullable().optional(),
|
|
541
596
|
extensions: z3.record(z3.unknown()).optional(),
|
|
542
597
|
// v3 specific - also lenient with types since SillyTavern uses numbers for enums
|
|
543
598
|
automation_id: z3.string().optional(),
|
|
@@ -553,8 +608,8 @@ var CCv3LorebookEntrySchema = z3.object({
|
|
|
553
608
|
var CCv3CharacterBookSchema = z3.object({
|
|
554
609
|
name: z3.string().optional(),
|
|
555
610
|
description: z3.string().optional(),
|
|
556
|
-
scan_depth: z3.number().int().nonnegative().optional(),
|
|
557
|
-
token_budget: z3.number().int().nonnegative().optional(),
|
|
611
|
+
scan_depth: z3.preprocess(preprocessNumeric, z3.number().int().nonnegative().optional()),
|
|
612
|
+
token_budget: z3.preprocess(preprocessNumeric, z3.number().int().nonnegative().optional()),
|
|
558
613
|
recursive_scanning: z3.boolean().optional(),
|
|
559
614
|
extensions: z3.record(z3.unknown()).optional(),
|
|
560
615
|
entries: z3.array(CCv3LorebookEntrySchema)
|
|
@@ -586,10 +641,9 @@ var CCv3DataInnerSchema = z3.object({
|
|
|
586
641
|
nickname: z3.string().optional(),
|
|
587
642
|
creator_notes_multilingual: z3.record(z3.string()).optional(),
|
|
588
643
|
source: z3.array(z3.string()).optional(),
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
modification_date: z3.number().int().nonnegative().optional()
|
|
592
|
-
// Unix timestamp in seconds
|
|
644
|
+
// Unix timestamps - preprocess to handle ISO strings, numeric strings, milliseconds
|
|
645
|
+
creation_date: z3.preprocess(preprocessTimestamp, z3.number().int().nonnegative().optional()),
|
|
646
|
+
modification_date: z3.preprocess(preprocessTimestamp, z3.number().int().nonnegative().optional())
|
|
593
647
|
});
|
|
594
648
|
var CCv3DataSchema = z3.object({
|
|
595
649
|
spec: z3.literal("chara_card_v3"),
|
|
@@ -8697,7 +8751,7 @@ function defaultTokenCounter(_card) {
|
|
|
8697
8751
|
total: 0
|
|
8698
8752
|
};
|
|
8699
8753
|
}
|
|
8700
|
-
function
|
|
8754
|
+
function getCanonicalContentV1(card) {
|
|
8701
8755
|
const normalized = {
|
|
8702
8756
|
name: card.data.name,
|
|
8703
8757
|
description: card.data.description || "",
|
|
@@ -8719,6 +8773,69 @@ function getCanonicalContent(card) {
|
|
|
8719
8773
|
};
|
|
8720
8774
|
return JSON.stringify(normalized, Object.keys(normalized).sort());
|
|
8721
8775
|
}
|
|
8776
|
+
function stableStringify(value) {
|
|
8777
|
+
if (value === null) return "null";
|
|
8778
|
+
switch (typeof value) {
|
|
8779
|
+
case "string":
|
|
8780
|
+
return JSON.stringify(value);
|
|
8781
|
+
case "number":
|
|
8782
|
+
return Number.isFinite(value) ? String(value) : "null";
|
|
8783
|
+
case "boolean":
|
|
8784
|
+
return value ? "true" : "false";
|
|
8785
|
+
case "bigint":
|
|
8786
|
+
return JSON.stringify(value.toString());
|
|
8787
|
+
case "undefined":
|
|
8788
|
+
case "function":
|
|
8789
|
+
case "symbol":
|
|
8790
|
+
return "null";
|
|
8791
|
+
case "object": {
|
|
8792
|
+
if (Array.isArray(value)) {
|
|
8793
|
+
const parts2 = value.map((item) => {
|
|
8794
|
+
if (item === void 0 || typeof item === "function" || typeof item === "symbol") {
|
|
8795
|
+
return "null";
|
|
8796
|
+
}
|
|
8797
|
+
return stableStringify(item);
|
|
8798
|
+
});
|
|
8799
|
+
return `[${parts2.join(",")}]`;
|
|
8800
|
+
}
|
|
8801
|
+
const obj = value;
|
|
8802
|
+
const keys = Object.keys(obj).sort();
|
|
8803
|
+
const parts = [];
|
|
8804
|
+
for (const key of keys) {
|
|
8805
|
+
const v = obj[key];
|
|
8806
|
+
if (v === void 0 || typeof v === "function" || typeof v === "symbol") {
|
|
8807
|
+
continue;
|
|
8808
|
+
}
|
|
8809
|
+
parts.push(`${JSON.stringify(key)}:${stableStringify(v)}`);
|
|
8810
|
+
}
|
|
8811
|
+
return `{${parts.join(",")}}`;
|
|
8812
|
+
}
|
|
8813
|
+
default:
|
|
8814
|
+
return "null";
|
|
8815
|
+
}
|
|
8816
|
+
}
|
|
8817
|
+
function getCanonicalContentV2(card) {
|
|
8818
|
+
const normalized = {
|
|
8819
|
+
name: card.data.name,
|
|
8820
|
+
description: card.data.description || "",
|
|
8821
|
+
personality: card.data.personality || "",
|
|
8822
|
+
scenario: card.data.scenario || "",
|
|
8823
|
+
first_mes: card.data.first_mes || "",
|
|
8824
|
+
mes_example: card.data.mes_example || "",
|
|
8825
|
+
system_prompt: card.data.system_prompt || "",
|
|
8826
|
+
post_history_instructions: card.data.post_history_instructions || "",
|
|
8827
|
+
alternate_greetings: card.data.alternate_greetings || [],
|
|
8828
|
+
character_book: card.data.character_book ? {
|
|
8829
|
+
entries: (card.data.character_book.entries || []).map((e) => ({
|
|
8830
|
+
keys: e.keys,
|
|
8831
|
+
content: e.content,
|
|
8832
|
+
enabled: e.enabled
|
|
8833
|
+
}))
|
|
8834
|
+
} : null,
|
|
8835
|
+
creator_notes: card.data.creator_notes || ""
|
|
8836
|
+
};
|
|
8837
|
+
return stableStringify(normalized);
|
|
8838
|
+
}
|
|
8722
8839
|
function isWithinTolerance(clientValue, computedValue, tolerance) {
|
|
8723
8840
|
if (clientValue === void 0) return false;
|
|
8724
8841
|
if (computedValue === 0) return clientValue === 0;
|
|
@@ -8739,15 +8856,18 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
|
|
|
8739
8856
|
const warnings = [];
|
|
8740
8857
|
const errors = [];
|
|
8741
8858
|
const computedTokens = countTokens(card);
|
|
8742
|
-
const
|
|
8743
|
-
const
|
|
8859
|
+
const canonicalContentV1 = getCanonicalContentV1(card);
|
|
8860
|
+
const canonicalContentV2 = getCanonicalContentV2(card);
|
|
8861
|
+
const computedHashV1 = await computeHash(canonicalContentV1);
|
|
8862
|
+
const computedHashV2 = await computeHash(canonicalContentV2);
|
|
8744
8863
|
const entries = card.data.character_book?.entries || [];
|
|
8745
8864
|
const computedHasLorebook = entries.length > 0;
|
|
8746
8865
|
const computedLorebookCount = entries.length;
|
|
8747
8866
|
const authoritative = {
|
|
8748
8867
|
name: card.data.name,
|
|
8749
8868
|
tokens: computedTokens,
|
|
8750
|
-
contentHash:
|
|
8869
|
+
contentHash: computedHashV1,
|
|
8870
|
+
contentHashV2: computedHashV2,
|
|
8751
8871
|
hasLorebook: computedHasLorebook,
|
|
8752
8872
|
lorebookEntriesCount: computedLorebookCount
|
|
8753
8873
|
};
|
|
@@ -8759,21 +8879,27 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
|
|
|
8759
8879
|
withinTolerance: false
|
|
8760
8880
|
});
|
|
8761
8881
|
}
|
|
8762
|
-
|
|
8882
|
+
const matchesV1 = clientMetadata.contentHash === computedHashV1;
|
|
8883
|
+
const matchesV2 = clientMetadata.contentHash === computedHashV2;
|
|
8884
|
+
if (!matchesV1 && !matchesV2) {
|
|
8763
8885
|
const disc = {
|
|
8764
8886
|
field: "contentHash",
|
|
8765
8887
|
clientValue: clientMetadata.contentHash,
|
|
8766
|
-
computedValue:
|
|
8888
|
+
computedValue: computedHashV1,
|
|
8767
8889
|
withinTolerance: false
|
|
8768
8890
|
};
|
|
8769
8891
|
discrepancies.push(disc);
|
|
8770
8892
|
if (allowHashMismatch) {
|
|
8771
8893
|
warnings.push(
|
|
8772
|
-
`Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server=${
|
|
8894
|
+
`Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server(v1)=${computedHashV1.substring(0, 8)}..., server(v2)=${computedHashV2.substring(0, 8)}...`
|
|
8773
8895
|
);
|
|
8774
8896
|
} else {
|
|
8775
8897
|
errors.push("Content hash mismatch - possible tampering or encoding difference");
|
|
8776
8898
|
}
|
|
8899
|
+
} else if (matchesV1 && !matchesV2) {
|
|
8900
|
+
warnings.push(
|
|
8901
|
+
"Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
|
|
8902
|
+
);
|
|
8777
8903
|
}
|
|
8778
8904
|
const tokenFields = [
|
|
8779
8905
|
"description",
|
|
@@ -8854,7 +8980,11 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
|
|
|
8854
8980
|
};
|
|
8855
8981
|
}
|
|
8856
8982
|
async function computeContentHash(card) {
|
|
8857
|
-
const content =
|
|
8983
|
+
const content = getCanonicalContentV1(card);
|
|
8984
|
+
return sha256Hash(content);
|
|
8985
|
+
}
|
|
8986
|
+
async function computeContentHashV2(card) {
|
|
8987
|
+
const content = getCanonicalContentV2(card);
|
|
8858
8988
|
return sha256Hash(content);
|
|
8859
8989
|
}
|
|
8860
8990
|
function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
@@ -8870,15 +9000,18 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
|
8870
9000
|
const warnings = [];
|
|
8871
9001
|
const errors = [];
|
|
8872
9002
|
const computedTokens = countTokens(card);
|
|
8873
|
-
const
|
|
8874
|
-
const
|
|
9003
|
+
const canonicalContentV1 = getCanonicalContentV1(card);
|
|
9004
|
+
const canonicalContentV2 = getCanonicalContentV2(card);
|
|
9005
|
+
const computedHashV1 = computeHash(canonicalContentV1);
|
|
9006
|
+
const computedHashV2 = computeHash(canonicalContentV2);
|
|
8875
9007
|
const entries = card.data.character_book?.entries || [];
|
|
8876
9008
|
const computedHasLorebook = entries.length > 0;
|
|
8877
9009
|
const computedLorebookCount = entries.length;
|
|
8878
9010
|
const authoritative = {
|
|
8879
9011
|
name: card.data.name,
|
|
8880
9012
|
tokens: computedTokens,
|
|
8881
|
-
contentHash:
|
|
9013
|
+
contentHash: computedHashV1,
|
|
9014
|
+
contentHashV2: computedHashV2,
|
|
8882
9015
|
hasLorebook: computedHasLorebook,
|
|
8883
9016
|
lorebookEntriesCount: computedLorebookCount
|
|
8884
9017
|
};
|
|
@@ -8890,11 +9023,13 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
|
8890
9023
|
withinTolerance: false
|
|
8891
9024
|
});
|
|
8892
9025
|
}
|
|
8893
|
-
|
|
9026
|
+
const matchesV1 = clientMetadata.contentHash === computedHashV1;
|
|
9027
|
+
const matchesV2 = clientMetadata.contentHash === computedHashV2;
|
|
9028
|
+
if (!matchesV1 && !matchesV2) {
|
|
8894
9029
|
discrepancies.push({
|
|
8895
9030
|
field: "contentHash",
|
|
8896
9031
|
clientValue: clientMetadata.contentHash,
|
|
8897
|
-
computedValue:
|
|
9032
|
+
computedValue: computedHashV1,
|
|
8898
9033
|
withinTolerance: false
|
|
8899
9034
|
});
|
|
8900
9035
|
if (allowHashMismatch) {
|
|
@@ -8902,6 +9037,10 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
|
8902
9037
|
} else {
|
|
8903
9038
|
errors.push("Content hash mismatch");
|
|
8904
9039
|
}
|
|
9040
|
+
} else if (matchesV1 && !matchesV2) {
|
|
9041
|
+
warnings.push(
|
|
9042
|
+
"Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
|
|
9043
|
+
);
|
|
8905
9044
|
}
|
|
8906
9045
|
const tokenFields = [
|
|
8907
9046
|
"description",
|
|
@@ -8970,6 +9109,7 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
|
8970
9109
|
}
|
|
8971
9110
|
export {
|
|
8972
9111
|
computeContentHash,
|
|
9112
|
+
computeContentHashV2,
|
|
8973
9113
|
detectFormat,
|
|
8974
9114
|
getContainerFormat,
|
|
8975
9115
|
mightBeCard,
|