@character-foundry/character-foundry 0.4.3 → 0.5.0
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/app-framework.cjs +12 -3
- package/dist/app-framework.cjs.map +1 -1
- package/dist/app-framework.d.cts +9 -1
- package/dist/app-framework.d.ts +9 -1
- package/dist/app-framework.js +12 -3
- package/dist/app-framework.js.map +1 -1
- package/dist/charx.cjs +85 -52
- package/dist/charx.cjs.map +1 -1
- package/dist/charx.d.cts +22 -22
- package/dist/charx.d.ts +22 -22
- package/dist/charx.js +85 -52
- package/dist/charx.js.map +1 -1
- package/dist/exporter.cjs +104 -54
- package/dist/exporter.cjs.map +1 -1
- package/dist/exporter.d.cts +19 -19
- package/dist/exporter.d.ts +19 -19
- package/dist/exporter.js +104 -54
- 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 +54 -19
- package/dist/federation.d.ts +54 -19
- package/dist/federation.js +104 -36
- package/dist/federation.js.map +1 -1
- package/dist/index.cjs +104 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -29
- package/dist/index.d.ts +29 -29
- package/dist/index.js +104 -54
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +171 -31
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.d.cts +37 -23
- package/dist/loader.d.ts +37 -23
- package/dist/loader.js +171 -31
- package/dist/loader.js.map +1 -1
- package/dist/lorebook.d.cts +23 -23
- package/dist/lorebook.d.ts +23 -23
- package/dist/normalizer.cjs +72 -18
- package/dist/normalizer.cjs.map +1 -1
- package/dist/normalizer.d.cts +37 -37
- package/dist/normalizer.d.ts +37 -37
- package/dist/normalizer.js +72 -18
- package/dist/normalizer.js.map +1 -1
- package/dist/png.cjs +72 -18
- package/dist/png.cjs.map +1 -1
- package/dist/png.d.cts +25 -25
- package/dist/png.d.ts +25 -25
- package/dist/png.js +72 -18
- package/dist/png.js.map +1 -1
- package/dist/schemas.cjs +80 -23
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +85 -67
- package/dist/schemas.d.ts +85 -67
- package/dist/schemas.js +80 -23
- package/dist/schemas.js.map +1 -1
- package/dist/voxta.cjs +91 -20
- package/dist/voxta.cjs.map +1 -1
- package/dist/voxta.d.cts +23 -23
- package/dist/voxta.d.ts +23 -23
- package/dist/voxta.js +91 -20
- package/dist/voxta.js.map +1 -1
- package/package.json +24 -24
package/dist/loader.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var loader_exports = {};
|
|
22
22
|
__export(loader_exports, {
|
|
23
23
|
computeContentHash: () => computeContentHash,
|
|
24
|
+
computeContentHashV2: () => computeContentHashV2,
|
|
24
25
|
detectFormat: () => detectFormat,
|
|
25
26
|
getContainerFormat: () => getContainerFormat,
|
|
26
27
|
mightBeCard: () => mightBeCard,
|
|
@@ -443,6 +444,58 @@ var import_zod = require("zod");
|
|
|
443
444
|
var import_zod2 = require("zod");
|
|
444
445
|
var import_zod3 = require("zod");
|
|
445
446
|
var import_zod4 = require("zod");
|
|
447
|
+
function preprocessTimestamp(val) {
|
|
448
|
+
if (val === null || val === void 0) return void 0;
|
|
449
|
+
let num;
|
|
450
|
+
if (typeof val === "number") {
|
|
451
|
+
num = val;
|
|
452
|
+
} else if (typeof val === "string") {
|
|
453
|
+
const trimmed = val.trim();
|
|
454
|
+
if (!trimmed) return void 0;
|
|
455
|
+
const parsed = Number(trimmed);
|
|
456
|
+
if (!isNaN(parsed)) {
|
|
457
|
+
num = parsed;
|
|
458
|
+
} else {
|
|
459
|
+
const date = new Date(trimmed);
|
|
460
|
+
if (isNaN(date.getTime())) return void 0;
|
|
461
|
+
num = Math.floor(date.getTime() / 1e3);
|
|
462
|
+
}
|
|
463
|
+
} else {
|
|
464
|
+
return void 0;
|
|
465
|
+
}
|
|
466
|
+
if (num > 1e10) {
|
|
467
|
+
num = Math.floor(num / 1e3);
|
|
468
|
+
}
|
|
469
|
+
if (num < 0) return void 0;
|
|
470
|
+
return num;
|
|
471
|
+
}
|
|
472
|
+
function preprocessNumeric(val) {
|
|
473
|
+
if (val === null || val === void 0) return void 0;
|
|
474
|
+
if (typeof val === "number") {
|
|
475
|
+
return isNaN(val) ? void 0 : val;
|
|
476
|
+
}
|
|
477
|
+
if (typeof val === "string") {
|
|
478
|
+
const trimmed = val.trim();
|
|
479
|
+
if (!trimmed) return void 0;
|
|
480
|
+
const parsed = Number(trimmed);
|
|
481
|
+
return isNaN(parsed) ? void 0 : parsed;
|
|
482
|
+
}
|
|
483
|
+
return void 0;
|
|
484
|
+
}
|
|
485
|
+
var KNOWN_ASSET_TYPES = /* @__PURE__ */ new Set([
|
|
486
|
+
"icon",
|
|
487
|
+
"background",
|
|
488
|
+
"emotion",
|
|
489
|
+
"user_icon",
|
|
490
|
+
"sound",
|
|
491
|
+
"video",
|
|
492
|
+
"custom",
|
|
493
|
+
"x-risu-asset"
|
|
494
|
+
]);
|
|
495
|
+
function preprocessAssetType(val) {
|
|
496
|
+
if (typeof val !== "string") return "custom";
|
|
497
|
+
return KNOWN_ASSET_TYPES.has(val) ? val : "custom";
|
|
498
|
+
}
|
|
446
499
|
var ISO8601Schema = import_zod.z.string().datetime();
|
|
447
500
|
var UUIDSchema = import_zod.z.string().uuid();
|
|
448
501
|
var SpecSchema = import_zod.z.enum(["v2", "v3"]);
|
|
@@ -465,16 +518,19 @@ var SourceFormatSchema = import_zod.z.enum([
|
|
|
465
518
|
// VoxPkg format
|
|
466
519
|
]);
|
|
467
520
|
var OriginalShapeSchema = import_zod.z.enum(["wrapped", "unwrapped", "legacy"]);
|
|
468
|
-
var AssetTypeSchema = import_zod.z.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
521
|
+
var AssetTypeSchema = import_zod.z.preprocess(
|
|
522
|
+
preprocessAssetType,
|
|
523
|
+
import_zod.z.enum([
|
|
524
|
+
"icon",
|
|
525
|
+
"background",
|
|
526
|
+
"emotion",
|
|
527
|
+
"user_icon",
|
|
528
|
+
"sound",
|
|
529
|
+
"video",
|
|
530
|
+
"custom",
|
|
531
|
+
"x-risu-asset"
|
|
532
|
+
])
|
|
533
|
+
);
|
|
478
534
|
var AssetDescriptorSchema = import_zod.z.object({
|
|
479
535
|
type: AssetTypeSchema,
|
|
480
536
|
uri: import_zod.z.string(),
|
|
@@ -508,8 +564,8 @@ var CCv2LorebookEntrySchema = import_zod2.z.object({
|
|
|
508
564
|
var CCv2CharacterBookSchema = import_zod2.z.object({
|
|
509
565
|
name: import_zod2.z.string().optional(),
|
|
510
566
|
description: import_zod2.z.string().optional(),
|
|
511
|
-
scan_depth: import_zod2.z.number().int().nonnegative().optional(),
|
|
512
|
-
token_budget: import_zod2.z.number().int().nonnegative().optional(),
|
|
567
|
+
scan_depth: import_zod2.z.preprocess(preprocessNumeric, import_zod2.z.number().int().nonnegative().optional()),
|
|
568
|
+
token_budget: import_zod2.z.preprocess(preprocessNumeric, import_zod2.z.number().int().nonnegative().optional()),
|
|
513
569
|
recursive_scanning: import_zod2.z.boolean().optional(),
|
|
514
570
|
extensions: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
515
571
|
entries: import_zod2.z.array(CCv2LorebookEntrySchema)
|
|
@@ -583,8 +639,8 @@ var CCv3LorebookEntrySchema = import_zod3.z.object({
|
|
|
583
639
|
var CCv3CharacterBookSchema = import_zod3.z.object({
|
|
584
640
|
name: import_zod3.z.string().optional(),
|
|
585
641
|
description: import_zod3.z.string().optional(),
|
|
586
|
-
scan_depth: import_zod3.z.number().int().nonnegative().optional(),
|
|
587
|
-
token_budget: import_zod3.z.number().int().nonnegative().optional(),
|
|
642
|
+
scan_depth: import_zod3.z.preprocess(preprocessNumeric, import_zod3.z.number().int().nonnegative().optional()),
|
|
643
|
+
token_budget: import_zod3.z.preprocess(preprocessNumeric, import_zod3.z.number().int().nonnegative().optional()),
|
|
588
644
|
recursive_scanning: import_zod3.z.boolean().optional(),
|
|
589
645
|
extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
|
|
590
646
|
entries: import_zod3.z.array(CCv3LorebookEntrySchema)
|
|
@@ -616,10 +672,9 @@ var CCv3DataInnerSchema = import_zod3.z.object({
|
|
|
616
672
|
nickname: import_zod3.z.string().optional(),
|
|
617
673
|
creator_notes_multilingual: import_zod3.z.record(import_zod3.z.string()).optional(),
|
|
618
674
|
source: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
modification_date: import_zod3.z.number().int().nonnegative().optional()
|
|
622
|
-
// Unix timestamp in seconds
|
|
675
|
+
// Unix timestamps - preprocess to handle ISO strings, numeric strings, milliseconds
|
|
676
|
+
creation_date: import_zod3.z.preprocess(preprocessTimestamp, import_zod3.z.number().int().nonnegative().optional()),
|
|
677
|
+
modification_date: import_zod3.z.preprocess(preprocessTimestamp, import_zod3.z.number().int().nonnegative().optional())
|
|
623
678
|
});
|
|
624
679
|
var CCv3DataSchema = import_zod3.z.object({
|
|
625
680
|
spec: import_zod3.z.literal("chara_card_v3"),
|
|
@@ -8727,7 +8782,7 @@ function defaultTokenCounter(_card) {
|
|
|
8727
8782
|
total: 0
|
|
8728
8783
|
};
|
|
8729
8784
|
}
|
|
8730
|
-
function
|
|
8785
|
+
function getCanonicalContentV1(card) {
|
|
8731
8786
|
const normalized = {
|
|
8732
8787
|
name: card.data.name,
|
|
8733
8788
|
description: card.data.description || "",
|
|
@@ -8749,6 +8804,69 @@ function getCanonicalContent(card) {
|
|
|
8749
8804
|
};
|
|
8750
8805
|
return JSON.stringify(normalized, Object.keys(normalized).sort());
|
|
8751
8806
|
}
|
|
8807
|
+
function stableStringify(value) {
|
|
8808
|
+
if (value === null) return "null";
|
|
8809
|
+
switch (typeof value) {
|
|
8810
|
+
case "string":
|
|
8811
|
+
return JSON.stringify(value);
|
|
8812
|
+
case "number":
|
|
8813
|
+
return Number.isFinite(value) ? String(value) : "null";
|
|
8814
|
+
case "boolean":
|
|
8815
|
+
return value ? "true" : "false";
|
|
8816
|
+
case "bigint":
|
|
8817
|
+
return JSON.stringify(value.toString());
|
|
8818
|
+
case "undefined":
|
|
8819
|
+
case "function":
|
|
8820
|
+
case "symbol":
|
|
8821
|
+
return "null";
|
|
8822
|
+
case "object": {
|
|
8823
|
+
if (Array.isArray(value)) {
|
|
8824
|
+
const parts2 = value.map((item) => {
|
|
8825
|
+
if (item === void 0 || typeof item === "function" || typeof item === "symbol") {
|
|
8826
|
+
return "null";
|
|
8827
|
+
}
|
|
8828
|
+
return stableStringify(item);
|
|
8829
|
+
});
|
|
8830
|
+
return `[${parts2.join(",")}]`;
|
|
8831
|
+
}
|
|
8832
|
+
const obj = value;
|
|
8833
|
+
const keys = Object.keys(obj).sort();
|
|
8834
|
+
const parts = [];
|
|
8835
|
+
for (const key of keys) {
|
|
8836
|
+
const v = obj[key];
|
|
8837
|
+
if (v === void 0 || typeof v === "function" || typeof v === "symbol") {
|
|
8838
|
+
continue;
|
|
8839
|
+
}
|
|
8840
|
+
parts.push(`${JSON.stringify(key)}:${stableStringify(v)}`);
|
|
8841
|
+
}
|
|
8842
|
+
return `{${parts.join(",")}}`;
|
|
8843
|
+
}
|
|
8844
|
+
default:
|
|
8845
|
+
return "null";
|
|
8846
|
+
}
|
|
8847
|
+
}
|
|
8848
|
+
function getCanonicalContentV2(card) {
|
|
8849
|
+
const normalized = {
|
|
8850
|
+
name: card.data.name,
|
|
8851
|
+
description: card.data.description || "",
|
|
8852
|
+
personality: card.data.personality || "",
|
|
8853
|
+
scenario: card.data.scenario || "",
|
|
8854
|
+
first_mes: card.data.first_mes || "",
|
|
8855
|
+
mes_example: card.data.mes_example || "",
|
|
8856
|
+
system_prompt: card.data.system_prompt || "",
|
|
8857
|
+
post_history_instructions: card.data.post_history_instructions || "",
|
|
8858
|
+
alternate_greetings: card.data.alternate_greetings || [],
|
|
8859
|
+
character_book: card.data.character_book ? {
|
|
8860
|
+
entries: (card.data.character_book.entries || []).map((e) => ({
|
|
8861
|
+
keys: e.keys,
|
|
8862
|
+
content: e.content,
|
|
8863
|
+
enabled: e.enabled
|
|
8864
|
+
}))
|
|
8865
|
+
} : null,
|
|
8866
|
+
creator_notes: card.data.creator_notes || ""
|
|
8867
|
+
};
|
|
8868
|
+
return stableStringify(normalized);
|
|
8869
|
+
}
|
|
8752
8870
|
function isWithinTolerance(clientValue, computedValue, tolerance) {
|
|
8753
8871
|
if (clientValue === void 0) return false;
|
|
8754
8872
|
if (computedValue === 0) return clientValue === 0;
|
|
@@ -8769,15 +8887,18 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
|
|
|
8769
8887
|
const warnings = [];
|
|
8770
8888
|
const errors = [];
|
|
8771
8889
|
const computedTokens = countTokens(card);
|
|
8772
|
-
const
|
|
8773
|
-
const
|
|
8890
|
+
const canonicalContentV1 = getCanonicalContentV1(card);
|
|
8891
|
+
const canonicalContentV2 = getCanonicalContentV2(card);
|
|
8892
|
+
const computedHashV1 = await computeHash(canonicalContentV1);
|
|
8893
|
+
const computedHashV2 = await computeHash(canonicalContentV2);
|
|
8774
8894
|
const entries = card.data.character_book?.entries || [];
|
|
8775
8895
|
const computedHasLorebook = entries.length > 0;
|
|
8776
8896
|
const computedLorebookCount = entries.length;
|
|
8777
8897
|
const authoritative = {
|
|
8778
8898
|
name: card.data.name,
|
|
8779
8899
|
tokens: computedTokens,
|
|
8780
|
-
contentHash:
|
|
8900
|
+
contentHash: computedHashV1,
|
|
8901
|
+
contentHashV2: computedHashV2,
|
|
8781
8902
|
hasLorebook: computedHasLorebook,
|
|
8782
8903
|
lorebookEntriesCount: computedLorebookCount
|
|
8783
8904
|
};
|
|
@@ -8789,21 +8910,27 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
|
|
|
8789
8910
|
withinTolerance: false
|
|
8790
8911
|
});
|
|
8791
8912
|
}
|
|
8792
|
-
|
|
8913
|
+
const matchesV1 = clientMetadata.contentHash === computedHashV1;
|
|
8914
|
+
const matchesV2 = clientMetadata.contentHash === computedHashV2;
|
|
8915
|
+
if (!matchesV1 && !matchesV2) {
|
|
8793
8916
|
const disc = {
|
|
8794
8917
|
field: "contentHash",
|
|
8795
8918
|
clientValue: clientMetadata.contentHash,
|
|
8796
|
-
computedValue:
|
|
8919
|
+
computedValue: computedHashV1,
|
|
8797
8920
|
withinTolerance: false
|
|
8798
8921
|
};
|
|
8799
8922
|
discrepancies.push(disc);
|
|
8800
8923
|
if (allowHashMismatch) {
|
|
8801
8924
|
warnings.push(
|
|
8802
|
-
`Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server=${
|
|
8925
|
+
`Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server(v1)=${computedHashV1.substring(0, 8)}..., server(v2)=${computedHashV2.substring(0, 8)}...`
|
|
8803
8926
|
);
|
|
8804
8927
|
} else {
|
|
8805
8928
|
errors.push("Content hash mismatch - possible tampering or encoding difference");
|
|
8806
8929
|
}
|
|
8930
|
+
} else if (matchesV1 && !matchesV2) {
|
|
8931
|
+
warnings.push(
|
|
8932
|
+
"Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
|
|
8933
|
+
);
|
|
8807
8934
|
}
|
|
8808
8935
|
const tokenFields = [
|
|
8809
8936
|
"description",
|
|
@@ -8884,7 +9011,11 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
|
|
|
8884
9011
|
};
|
|
8885
9012
|
}
|
|
8886
9013
|
async function computeContentHash(card) {
|
|
8887
|
-
const content =
|
|
9014
|
+
const content = getCanonicalContentV1(card);
|
|
9015
|
+
return sha256Hash(content);
|
|
9016
|
+
}
|
|
9017
|
+
async function computeContentHashV2(card) {
|
|
9018
|
+
const content = getCanonicalContentV2(card);
|
|
8888
9019
|
return sha256Hash(content);
|
|
8889
9020
|
}
|
|
8890
9021
|
function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
@@ -8900,15 +9031,18 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
|
8900
9031
|
const warnings = [];
|
|
8901
9032
|
const errors = [];
|
|
8902
9033
|
const computedTokens = countTokens(card);
|
|
8903
|
-
const
|
|
8904
|
-
const
|
|
9034
|
+
const canonicalContentV1 = getCanonicalContentV1(card);
|
|
9035
|
+
const canonicalContentV2 = getCanonicalContentV2(card);
|
|
9036
|
+
const computedHashV1 = computeHash(canonicalContentV1);
|
|
9037
|
+
const computedHashV2 = computeHash(canonicalContentV2);
|
|
8905
9038
|
const entries = card.data.character_book?.entries || [];
|
|
8906
9039
|
const computedHasLorebook = entries.length > 0;
|
|
8907
9040
|
const computedLorebookCount = entries.length;
|
|
8908
9041
|
const authoritative = {
|
|
8909
9042
|
name: card.data.name,
|
|
8910
9043
|
tokens: computedTokens,
|
|
8911
|
-
contentHash:
|
|
9044
|
+
contentHash: computedHashV1,
|
|
9045
|
+
contentHashV2: computedHashV2,
|
|
8912
9046
|
hasLorebook: computedHasLorebook,
|
|
8913
9047
|
lorebookEntriesCount: computedLorebookCount
|
|
8914
9048
|
};
|
|
@@ -8920,11 +9054,13 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
|
8920
9054
|
withinTolerance: false
|
|
8921
9055
|
});
|
|
8922
9056
|
}
|
|
8923
|
-
|
|
9057
|
+
const matchesV1 = clientMetadata.contentHash === computedHashV1;
|
|
9058
|
+
const matchesV2 = clientMetadata.contentHash === computedHashV2;
|
|
9059
|
+
if (!matchesV1 && !matchesV2) {
|
|
8924
9060
|
discrepancies.push({
|
|
8925
9061
|
field: "contentHash",
|
|
8926
9062
|
clientValue: clientMetadata.contentHash,
|
|
8927
|
-
computedValue:
|
|
9063
|
+
computedValue: computedHashV1,
|
|
8928
9064
|
withinTolerance: false
|
|
8929
9065
|
});
|
|
8930
9066
|
if (allowHashMismatch) {
|
|
@@ -8932,6 +9068,10 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
|
|
|
8932
9068
|
} else {
|
|
8933
9069
|
errors.push("Content hash mismatch");
|
|
8934
9070
|
}
|
|
9071
|
+
} else if (matchesV1 && !matchesV2) {
|
|
9072
|
+
warnings.push(
|
|
9073
|
+
"Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
|
|
9074
|
+
);
|
|
8935
9075
|
}
|
|
8936
9076
|
const tokenFields = [
|
|
8937
9077
|
"description",
|