run-dmcp 0.1.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/LICENSE +22 -0
- package/README.md +79 -0
- package/dist/__tests__/engineVocabulary.test.d.ts +1 -0
- package/dist/__tests__/engineVocabulary.test.js +147 -0
- package/dist/db/__tests__/connection.test.d.ts +1 -0
- package/dist/db/__tests__/connection.test.js +72 -0
- package/dist/db/__tests__/testDb.d.ts +33 -0
- package/dist/db/__tests__/testDb.js +41 -0
- package/dist/db/connection.d.ts +22 -0
- package/dist/db/connection.js +107 -0
- package/dist/db/schema.d.ts +1 -0
- package/dist/db/schema.js +725 -0
- package/dist/events/emitter.d.ts +22 -0
- package/dist/events/emitter.js +71 -0
- package/dist/http/server.d.ts +3 -0
- package/dist/http/server.js +649 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +92 -0
- package/dist/register/abilities.d.ts +2 -0
- package/dist/register/abilities.js +165 -0
- package/dist/register/audio.d.ts +2 -0
- package/dist/register/audio.js +326 -0
- package/dist/register/batch.d.ts +2 -0
- package/dist/register/batch.js +343 -0
- package/dist/register/character.d.ts +2 -0
- package/dist/register/character.js +324 -0
- package/dist/register/combat.d.ts +2 -0
- package/dist/register/combat.js +207 -0
- package/dist/register/core.d.ts +2 -0
- package/dist/register/core.js +1040 -0
- package/dist/register/display.d.ts +2 -0
- package/dist/register/display.js +263 -0
- package/dist/register/factions.d.ts +2 -0
- package/dist/register/factions.js +186 -0
- package/dist/register/images.d.ts +2 -0
- package/dist/register/images.js +400 -0
- package/dist/register/inventory.d.ts +2 -0
- package/dist/register/inventory.js +115 -0
- package/dist/register/mcp-prompts.d.ts +2 -0
- package/dist/register/mcp-prompts.js +684 -0
- package/dist/register/mcp-resources.d.ts +2 -0
- package/dist/register/mcp-resources.js +335 -0
- package/dist/register/narrative.d.ts +2 -0
- package/dist/register/narrative.js +242 -0
- package/dist/register/notes.d.ts +2 -0
- package/dist/register/notes.js +170 -0
- package/dist/register/pause.d.ts +2 -0
- package/dist/register/pause.js +580 -0
- package/dist/register/quests.d.ts +2 -0
- package/dist/register/quests.js +118 -0
- package/dist/register/relationships.d.ts +2 -0
- package/dist/register/relationships.js +147 -0
- package/dist/register/resources.d.ts +2 -0
- package/dist/register/resources.js +277 -0
- package/dist/register/secrets.d.ts +2 -0
- package/dist/register/secrets.js +192 -0
- package/dist/register/status.d.ts +2 -0
- package/dist/register/status.js +130 -0
- package/dist/register/tables.d.ts +2 -0
- package/dist/register/tables.js +146 -0
- package/dist/register/tags.d.ts +2 -0
- package/dist/register/tags.js +114 -0
- package/dist/register/time.d.ts +2 -0
- package/dist/register/time.js +281 -0
- package/dist/register/world.d.ts +2 -0
- package/dist/register/world.js +127 -0
- package/dist/schemas/index.d.ts +921 -0
- package/dist/schemas/index.js +121 -0
- package/dist/test-setup.d.ts +1 -0
- package/dist/test-setup.js +13 -0
- package/dist/tools/__tests__/audio.test.d.ts +1 -0
- package/dist/tools/__tests__/audio.test.js +59 -0
- package/dist/tools/__tests__/conserved.test.d.ts +1 -0
- package/dist/tools/__tests__/conserved.test.js +488 -0
- package/dist/tools/__tests__/constraint.test.d.ts +1 -0
- package/dist/tools/__tests__/constraint.test.js +212 -0
- package/dist/tools/__tests__/expiry-consequences.test.d.ts +1 -0
- package/dist/tools/__tests__/expiry-consequences.test.js +110 -0
- package/dist/tools/__tests__/images.test.d.ts +1 -0
- package/dist/tools/__tests__/images.test.js +59 -0
- package/dist/tools/__tests__/relationship.test.d.ts +1 -0
- package/dist/tools/__tests__/relationship.test.js +132 -0
- package/dist/tools/__tests__/resource-constraints.test.d.ts +1 -0
- package/dist/tools/__tests__/resource-constraints.test.js +131 -0
- package/dist/tools/__tests__/resource.test.d.ts +1 -0
- package/dist/tools/__tests__/resource.test.js +190 -0
- package/dist/tools/__tests__/time.test.d.ts +1 -0
- package/dist/tools/__tests__/time.test.js +404 -0
- package/dist/tools/__tests__/timers.test.d.ts +1 -0
- package/dist/tools/__tests__/timers.test.js +426 -0
- package/dist/tools/__tests__/world.test.d.ts +1 -0
- package/dist/tools/__tests__/world.test.js +70 -0
- package/dist/tools/ability.d.ts +48 -0
- package/dist/tools/ability.js +238 -0
- package/dist/tools/audio.d.ts +24 -0
- package/dist/tools/audio.js +365 -0
- package/dist/tools/character.d.ts +70 -0
- package/dist/tools/character.js +309 -0
- package/dist/tools/combat.d.ts +13 -0
- package/dist/tools/combat.js +195 -0
- package/dist/tools/constraint.d.ts +132 -0
- package/dist/tools/constraint.js +269 -0
- package/dist/tools/dice.d.ts +23 -0
- package/dist/tools/dice.js +111 -0
- package/dist/tools/display.d.ts +120 -0
- package/dist/tools/display.js +528 -0
- package/dist/tools/faction.d.ts +61 -0
- package/dist/tools/faction.js +269 -0
- package/dist/tools/game.d.ts +96 -0
- package/dist/tools/game.js +526 -0
- package/dist/tools/image-prompt.d.ts +49 -0
- package/dist/tools/image-prompt.js +479 -0
- package/dist/tools/images.d.ts +47 -0
- package/dist/tools/images.js +449 -0
- package/dist/tools/inventory.d.ts +20 -0
- package/dist/tools/inventory.js +145 -0
- package/dist/tools/narrative.d.ts +58 -0
- package/dist/tools/narrative.js +237 -0
- package/dist/tools/notes.d.ts +41 -0
- package/dist/tools/notes.js +220 -0
- package/dist/tools/pause.d.ts +110 -0
- package/dist/tools/pause.js +1254 -0
- package/dist/tools/quest.d.ts +34 -0
- package/dist/tools/quest.js +164 -0
- package/dist/tools/relationship.d.ts +74 -0
- package/dist/tools/relationship.js +324 -0
- package/dist/tools/resource.d.ts +93 -0
- package/dist/tools/resource.js +374 -0
- package/dist/tools/rules.d.ts +4 -0
- package/dist/tools/rules.js +30 -0
- package/dist/tools/secrets.d.ts +49 -0
- package/dist/tools/secrets.js +195 -0
- package/dist/tools/status.d.ts +36 -0
- package/dist/tools/status.js +218 -0
- package/dist/tools/tables.d.ts +33 -0
- package/dist/tools/tables.js +209 -0
- package/dist/tools/tags.d.ts +52 -0
- package/dist/tools/tags.js +176 -0
- package/dist/tools/time.d.ts +33 -0
- package/dist/tools/time.js +276 -0
- package/dist/tools/timers.d.ts +41 -0
- package/dist/tools/timers.js +215 -0
- package/dist/tools/world.d.ts +78 -0
- package/dist/tools/world.js +331 -0
- package/dist/types/index.d.ts +969 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/__tests__/json.test.d.ts +1 -0
- package/dist/utils/__tests__/json.test.js +55 -0
- package/dist/utils/__tests__/validation.test.d.ts +1 -0
- package/dist/utils/__tests__/validation.test.js +90 -0
- package/dist/utils/errors.d.ts +44 -0
- package/dist/utils/errors.js +121 -0
- package/dist/utils/json.d.ts +9 -0
- package/dist/utils/json.js +23 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +50 -0
- package/dist/utils/output-schemas.d.ts +594 -0
- package/dist/utils/output-schemas.js +331 -0
- package/dist/utils/tool-annotations.d.ts +147 -0
- package/dist/utils/tool-annotations.js +98 -0
- package/dist/utils/validation.d.ts +34 -0
- package/dist/utils/validation.js +52 -0
- package/dist/utils/verbosity.d.ts +57 -0
- package/dist/utils/verbosity.js +67 -0
- package/dist/utils/webui.d.ts +20 -0
- package/dist/utils/webui.js +35 -0
- package/package.json +75 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import { getCharacter, listCharacters } from "./character.js";
|
|
2
|
+
import { getLocation } from "./world.js";
|
|
3
|
+
import { getItem } from "./inventory.js";
|
|
4
|
+
import { getFaction } from "./faction.js";
|
|
5
|
+
import { getDefaultImagePreset, getDefaultImagePromptTemplate, getImagePromptTemplate } from "./game.js";
|
|
6
|
+
import { listEntityImages } from "./images.js";
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Template Processing
|
|
9
|
+
// ============================================================================
|
|
10
|
+
/**
|
|
11
|
+
* Get a nested value from an object using dot notation path.
|
|
12
|
+
* E.g., getNestedValue(obj, "subject.physicalTraits.gender")
|
|
13
|
+
*/
|
|
14
|
+
function getNestedValue(obj, path) {
|
|
15
|
+
const parts = path.split(".");
|
|
16
|
+
let current = obj;
|
|
17
|
+
for (const part of parts) {
|
|
18
|
+
if (current === null || current === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
if (typeof current !== "object")
|
|
21
|
+
return undefined;
|
|
22
|
+
current = current[part];
|
|
23
|
+
}
|
|
24
|
+
return current;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Fill a template string with values from data object.
|
|
28
|
+
* Supports:
|
|
29
|
+
* - {{field.path}} - simple value substitution
|
|
30
|
+
* - {{field.path|"default"}} - value with default if undefined/null
|
|
31
|
+
* - {{field.path|}} - value or empty string if undefined
|
|
32
|
+
*/
|
|
33
|
+
export function fillTemplate(template, data, defaults, aliases) {
|
|
34
|
+
// Pattern matches: {{path}} or {{path|"default"}} or {{path|}}
|
|
35
|
+
const pattern = /\{\{([^}|]+)(?:\|(?:"([^"]*)"|([^}]*)))?\}\}/g;
|
|
36
|
+
return template.replace(pattern, (match, path, quotedDefault, bareDefault) => {
|
|
37
|
+
const trimmedPath = path.trim();
|
|
38
|
+
// Check aliases first
|
|
39
|
+
const resolvedPath = aliases?.[trimmedPath] || trimmedPath;
|
|
40
|
+
// Get value from data
|
|
41
|
+
let value = getNestedValue(data, resolvedPath);
|
|
42
|
+
// Handle arrays by joining
|
|
43
|
+
if (Array.isArray(value)) {
|
|
44
|
+
value = value.join(", ");
|
|
45
|
+
}
|
|
46
|
+
// If value exists and is not empty, return it
|
|
47
|
+
if (value !== undefined && value !== null && value !== "") {
|
|
48
|
+
return String(value);
|
|
49
|
+
}
|
|
50
|
+
// Check template defaults
|
|
51
|
+
if (defaults?.[trimmedPath] !== undefined) {
|
|
52
|
+
return defaults[trimmedPath];
|
|
53
|
+
}
|
|
54
|
+
// Use inline default if provided
|
|
55
|
+
if (quotedDefault !== undefined) {
|
|
56
|
+
return quotedDefault;
|
|
57
|
+
}
|
|
58
|
+
if (bareDefault !== undefined) {
|
|
59
|
+
return bareDefault.trim();
|
|
60
|
+
}
|
|
61
|
+
// No value, no default - return empty string
|
|
62
|
+
return "";
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Clean up a filled template by removing extra commas, spaces, etc.
|
|
67
|
+
*/
|
|
68
|
+
function cleanFilledTemplate(text) {
|
|
69
|
+
return text
|
|
70
|
+
.replace(/,\s*,/g, ",") // Remove double commas
|
|
71
|
+
.replace(/,\s*$/g, "") // Remove trailing comma
|
|
72
|
+
.replace(/^\s*,/g, "") // Remove leading comma
|
|
73
|
+
.replace(/\s+/g, " ") // Normalize spaces
|
|
74
|
+
.replace(/\s+,/g, ",") // Remove space before comma
|
|
75
|
+
.trim();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build data context for template filling from entity and imageGen.
|
|
79
|
+
*/
|
|
80
|
+
function buildTemplateContext(entity, entityType, imageGen, notes) {
|
|
81
|
+
// Start with imageGen data (the primary source)
|
|
82
|
+
const context = {
|
|
83
|
+
entity: entity,
|
|
84
|
+
entityType,
|
|
85
|
+
name: entity.name,
|
|
86
|
+
notes,
|
|
87
|
+
...(imageGen || {}),
|
|
88
|
+
};
|
|
89
|
+
// Add convenience aliases at top level for common fields
|
|
90
|
+
if (imageGen?.subject) {
|
|
91
|
+
context.subject = imageGen.subject;
|
|
92
|
+
if (imageGen.subject.physicalTraits) {
|
|
93
|
+
context.physicalTraits = imageGen.subject.physicalTraits;
|
|
94
|
+
}
|
|
95
|
+
if (imageGen.subject.attire) {
|
|
96
|
+
context.attire = imageGen.subject.attire;
|
|
97
|
+
}
|
|
98
|
+
if (imageGen.subject.environment) {
|
|
99
|
+
context.environment = imageGen.subject.environment;
|
|
100
|
+
}
|
|
101
|
+
if (imageGen.subject.objectDetails) {
|
|
102
|
+
context.objectDetails = imageGen.subject.objectDetails;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (imageGen?.style) {
|
|
106
|
+
context.style = imageGen.style;
|
|
107
|
+
}
|
|
108
|
+
if (imageGen?.composition) {
|
|
109
|
+
context.composition = imageGen.composition;
|
|
110
|
+
}
|
|
111
|
+
return context;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build a prompt using a template.
|
|
115
|
+
*/
|
|
116
|
+
export function buildPromptFromTemplate(template, entity, entityType, imageGen, notes) {
|
|
117
|
+
const context = buildTemplateContext(entity, entityType, imageGen, notes);
|
|
118
|
+
// Fill the main template
|
|
119
|
+
let prompt = fillTemplate(template.promptTemplate, context, template.defaults, template.fieldAliases);
|
|
120
|
+
// Clean up the filled template
|
|
121
|
+
prompt = cleanFilledTemplate(prompt);
|
|
122
|
+
// Add prefix and suffix
|
|
123
|
+
if (template.promptPrefix) {
|
|
124
|
+
prompt = template.promptPrefix + " " + prompt;
|
|
125
|
+
}
|
|
126
|
+
if (template.promptSuffix) {
|
|
127
|
+
prompt = prompt + ", " + template.promptSuffix;
|
|
128
|
+
}
|
|
129
|
+
// Build negative prompt
|
|
130
|
+
let negativePrompt = "";
|
|
131
|
+
if (template.negativePromptTemplate) {
|
|
132
|
+
negativePrompt = fillTemplate(template.negativePromptTemplate, context, template.defaults, template.fieldAliases);
|
|
133
|
+
negativePrompt = cleanFilledTemplate(negativePrompt);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
prompt: cleanFilledTemplate(prompt),
|
|
137
|
+
negativePrompt: negativePrompt || "low quality, blurry, distorted, deformed",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Build an image generation prompt from an entity's structured data.
|
|
142
|
+
* If a template exists for the entity type, uses that. Otherwise uses default logic.
|
|
143
|
+
* Combines imageGen schema, notes, and game preset to create a ready-to-use prompt.
|
|
144
|
+
*/
|
|
145
|
+
export function buildImagePrompt(entityId, entityType, gameId, templateId) {
|
|
146
|
+
// Get the entity
|
|
147
|
+
let entity = null;
|
|
148
|
+
let entityName = "";
|
|
149
|
+
let imageGen = null;
|
|
150
|
+
let notes = "";
|
|
151
|
+
switch (entityType) {
|
|
152
|
+
case "character": {
|
|
153
|
+
const char = getCharacter(entityId);
|
|
154
|
+
if (!char)
|
|
155
|
+
return null;
|
|
156
|
+
entity = char;
|
|
157
|
+
entityName = char.name;
|
|
158
|
+
imageGen = char.imageGen || null;
|
|
159
|
+
notes = char.notes || "";
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case "location": {
|
|
163
|
+
const loc = getLocation(entityId);
|
|
164
|
+
if (!loc)
|
|
165
|
+
return null;
|
|
166
|
+
entity = loc;
|
|
167
|
+
entityName = loc.name;
|
|
168
|
+
imageGen = loc.imageGen || null;
|
|
169
|
+
notes = loc.description || "";
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "item": {
|
|
173
|
+
const item = getItem(entityId);
|
|
174
|
+
if (!item)
|
|
175
|
+
return null;
|
|
176
|
+
entity = item;
|
|
177
|
+
entityName = item.name;
|
|
178
|
+
imageGen = item.imageGen || null;
|
|
179
|
+
notes = item.properties?.description || "";
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case "faction": {
|
|
183
|
+
const faction = getFaction(entityId);
|
|
184
|
+
if (!faction)
|
|
185
|
+
return null;
|
|
186
|
+
entity = faction;
|
|
187
|
+
entityName = faction.name;
|
|
188
|
+
imageGen = faction.imageGen || null;
|
|
189
|
+
notes = faction.description || "";
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!entity)
|
|
194
|
+
return null;
|
|
195
|
+
// Get game ID for template/preset lookup
|
|
196
|
+
const effectiveGameId = gameId || entity.gameId;
|
|
197
|
+
// Check for a template to use
|
|
198
|
+
let template = null;
|
|
199
|
+
if (effectiveGameId) {
|
|
200
|
+
if (templateId) {
|
|
201
|
+
// Use specific template if provided
|
|
202
|
+
template = getImagePromptTemplate(effectiveGameId, templateId);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Try to find default template for this entity type
|
|
206
|
+
template = getDefaultImagePromptTemplate(effectiveGameId, entityType);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// If we have a template, use template-based building
|
|
210
|
+
if (template) {
|
|
211
|
+
const { prompt, negativePrompt } = buildPromptFromTemplate(template, entity, entityType, imageGen, notes);
|
|
212
|
+
// Build summary
|
|
213
|
+
const summaryParts = [
|
|
214
|
+
`Type: ${entityType}`,
|
|
215
|
+
`Name: ${entityName}`,
|
|
216
|
+
`Template: ${template.name}`,
|
|
217
|
+
];
|
|
218
|
+
if (imageGen?.subject?.physicalTraits?.gender) {
|
|
219
|
+
summaryParts.push(`Gender: ${imageGen.subject.physicalTraits.gender}`);
|
|
220
|
+
}
|
|
221
|
+
if (imageGen?.subject?.physicalTraits?.age) {
|
|
222
|
+
summaryParts.push(`Age: ${imageGen.subject.physicalTraits.age}`);
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
entityId,
|
|
226
|
+
entityType,
|
|
227
|
+
entityName,
|
|
228
|
+
prompt,
|
|
229
|
+
negativePrompt,
|
|
230
|
+
summary: summaryParts.join("\n"),
|
|
231
|
+
source: {
|
|
232
|
+
fromImageGen: !!imageGen,
|
|
233
|
+
fromNotes: !!notes && !imageGen,
|
|
234
|
+
fromPreset: false,
|
|
235
|
+
fromTemplate: true,
|
|
236
|
+
},
|
|
237
|
+
templateUsed: template.name,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// No template - use default logic
|
|
241
|
+
// Get game preset for style defaults
|
|
242
|
+
let preset = null;
|
|
243
|
+
if (effectiveGameId) {
|
|
244
|
+
preset = getDefaultImagePreset(effectiveGameId);
|
|
245
|
+
}
|
|
246
|
+
// Build the prompt
|
|
247
|
+
const promptParts = [];
|
|
248
|
+
const negativeParts = [];
|
|
249
|
+
const summaryParts = [];
|
|
250
|
+
const source = { fromImageGen: false, fromNotes: false, fromPreset: false, fromTemplate: false };
|
|
251
|
+
// 1. Start with imageGen structured data if available
|
|
252
|
+
if (imageGen) {
|
|
253
|
+
source.fromImageGen = true;
|
|
254
|
+
// Subject description
|
|
255
|
+
if (imageGen.subject) {
|
|
256
|
+
const subj = imageGen.subject;
|
|
257
|
+
// Primary description
|
|
258
|
+
if (subj.primaryDescription) {
|
|
259
|
+
promptParts.push(subj.primaryDescription);
|
|
260
|
+
}
|
|
261
|
+
// Physical traits (for characters)
|
|
262
|
+
if (subj.physicalTraits) {
|
|
263
|
+
const traits = subj.physicalTraits;
|
|
264
|
+
const traitParts = [];
|
|
265
|
+
if (traits.gender)
|
|
266
|
+
traitParts.push(traits.gender);
|
|
267
|
+
if (traits.age)
|
|
268
|
+
traitParts.push(traits.age);
|
|
269
|
+
if (traits.bodyType)
|
|
270
|
+
traitParts.push(traits.bodyType);
|
|
271
|
+
if (traits.skinTone)
|
|
272
|
+
traitParts.push(`${traits.skinTone} skin`);
|
|
273
|
+
if (traits.hairColor && traits.hairStyle) {
|
|
274
|
+
traitParts.push(`${traits.hairColor} ${traits.hairStyle} hair`);
|
|
275
|
+
}
|
|
276
|
+
else if (traits.hairColor) {
|
|
277
|
+
traitParts.push(`${traits.hairColor} hair`);
|
|
278
|
+
}
|
|
279
|
+
else if (traits.hairStyle) {
|
|
280
|
+
traitParts.push(`${traits.hairStyle} hair`);
|
|
281
|
+
}
|
|
282
|
+
if (traits.eyeColor)
|
|
283
|
+
traitParts.push(`${traits.eyeColor} eyes`);
|
|
284
|
+
if (traits.facialFeatures)
|
|
285
|
+
traitParts.push(traits.facialFeatures);
|
|
286
|
+
if (traits.distinguishingMarks?.length) {
|
|
287
|
+
traitParts.push(traits.distinguishingMarks.join(", "));
|
|
288
|
+
}
|
|
289
|
+
if (traitParts.length > 0) {
|
|
290
|
+
promptParts.push(traitParts.join(", "));
|
|
291
|
+
summaryParts.push(`Physical: ${traitParts.join(", ")}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Attire
|
|
295
|
+
if (subj.attire) {
|
|
296
|
+
const attireParts = [];
|
|
297
|
+
if (subj.attire.description)
|
|
298
|
+
attireParts.push(subj.attire.description);
|
|
299
|
+
if (subj.attire.colors?.length)
|
|
300
|
+
attireParts.push(subj.attire.colors.join(" and ") + " colors");
|
|
301
|
+
if (subj.attire.materials?.length)
|
|
302
|
+
attireParts.push(subj.attire.materials.join(" and "));
|
|
303
|
+
if (subj.attire.accessories?.length)
|
|
304
|
+
attireParts.push(`wearing ${subj.attire.accessories.join(", ")}`);
|
|
305
|
+
if (attireParts.length > 0) {
|
|
306
|
+
promptParts.push(attireParts.join(", "));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Environment (for locations)
|
|
310
|
+
if (subj.environment) {
|
|
311
|
+
const envParts = [];
|
|
312
|
+
if (subj.environment.setting)
|
|
313
|
+
envParts.push(subj.environment.setting);
|
|
314
|
+
if (subj.environment.timeOfDay)
|
|
315
|
+
envParts.push(subj.environment.timeOfDay);
|
|
316
|
+
if (subj.environment.weather)
|
|
317
|
+
envParts.push(subj.environment.weather);
|
|
318
|
+
if (subj.environment.lighting)
|
|
319
|
+
envParts.push(subj.environment.lighting);
|
|
320
|
+
if (subj.environment.architecture)
|
|
321
|
+
envParts.push(subj.environment.architecture);
|
|
322
|
+
if (subj.environment.notableFeatures?.length) {
|
|
323
|
+
envParts.push(subj.environment.notableFeatures.join(", "));
|
|
324
|
+
}
|
|
325
|
+
if (envParts.length > 0) {
|
|
326
|
+
promptParts.push(envParts.join(", "));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Object details (for items)
|
|
330
|
+
if (subj.objectDetails) {
|
|
331
|
+
const objParts = [];
|
|
332
|
+
if (subj.objectDetails.material)
|
|
333
|
+
objParts.push(subj.objectDetails.material);
|
|
334
|
+
if (subj.objectDetails.size)
|
|
335
|
+
objParts.push(subj.objectDetails.size);
|
|
336
|
+
if (subj.objectDetails.condition)
|
|
337
|
+
objParts.push(subj.objectDetails.condition);
|
|
338
|
+
if (subj.objectDetails.glowOrEffects)
|
|
339
|
+
objParts.push(subj.objectDetails.glowOrEffects);
|
|
340
|
+
if (objParts.length > 0) {
|
|
341
|
+
promptParts.push(objParts.join(", "));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Pose/expression (for characters)
|
|
345
|
+
if (subj.pose)
|
|
346
|
+
promptParts.push(subj.pose);
|
|
347
|
+
if (subj.expression)
|
|
348
|
+
promptParts.push(subj.expression);
|
|
349
|
+
if (subj.action)
|
|
350
|
+
promptParts.push(subj.action);
|
|
351
|
+
}
|
|
352
|
+
// Style
|
|
353
|
+
if (imageGen.style) {
|
|
354
|
+
const style = imageGen.style;
|
|
355
|
+
if (style.artisticStyle)
|
|
356
|
+
promptParts.push(style.artisticStyle);
|
|
357
|
+
if (style.mood)
|
|
358
|
+
promptParts.push(`${style.mood} mood`);
|
|
359
|
+
if (style.colorScheme)
|
|
360
|
+
promptParts.push(`${style.colorScheme} colors`);
|
|
361
|
+
if (style.qualityTags?.length)
|
|
362
|
+
promptParts.push(style.qualityTags.join(", "));
|
|
363
|
+
if (style.influences?.length)
|
|
364
|
+
promptParts.push(`in the style of ${style.influences.join(", ")}`);
|
|
365
|
+
if (style.negativeElements?.length)
|
|
366
|
+
negativeParts.push(...style.negativeElements);
|
|
367
|
+
}
|
|
368
|
+
// Composition
|
|
369
|
+
if (imageGen.composition) {
|
|
370
|
+
const comp = imageGen.composition;
|
|
371
|
+
if (comp.framing)
|
|
372
|
+
promptParts.push(comp.framing);
|
|
373
|
+
if (comp.cameraAngle)
|
|
374
|
+
promptParts.push(comp.cameraAngle);
|
|
375
|
+
if (comp.background)
|
|
376
|
+
promptParts.push(`${comp.background} background`);
|
|
377
|
+
if (comp.depth)
|
|
378
|
+
promptParts.push(comp.depth);
|
|
379
|
+
}
|
|
380
|
+
// Use cached prompts if available
|
|
381
|
+
if (imageGen.prompts?.generic && promptParts.length === 0) {
|
|
382
|
+
promptParts.push(imageGen.prompts.generic);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// 2. Fall back to notes if no imageGen or very sparse
|
|
386
|
+
if (promptParts.length < 3 && notes) {
|
|
387
|
+
source.fromNotes = true;
|
|
388
|
+
// Clean and truncate notes for use as prompt
|
|
389
|
+
const cleanNotes = notes
|
|
390
|
+
.replace(/\n/g, " ")
|
|
391
|
+
.replace(/\s+/g, " ")
|
|
392
|
+
.trim()
|
|
393
|
+
.slice(0, 500);
|
|
394
|
+
if (cleanNotes) {
|
|
395
|
+
promptParts.push(cleanNotes);
|
|
396
|
+
}
|
|
397
|
+
// Warn that we're using notes which may lack visual specificity
|
|
398
|
+
summaryParts.push("⚠️ WARNING: Using notes/description as prompt source");
|
|
399
|
+
summaryParts.push("Notes may contain biographical info rather than visual description.");
|
|
400
|
+
summaryParts.push("Consider populating imageGen schema or writing a custom prompt.");
|
|
401
|
+
}
|
|
402
|
+
// 3. Apply preset style defaults
|
|
403
|
+
if (preset?.config?.defaultStyle) {
|
|
404
|
+
source.fromPreset = true;
|
|
405
|
+
const style = preset.config.defaultStyle;
|
|
406
|
+
const artisticStyle = style.artisticStyle;
|
|
407
|
+
const mood = style.mood;
|
|
408
|
+
if (artisticStyle && !promptParts.some(p => p.includes(artisticStyle))) {
|
|
409
|
+
promptParts.push(artisticStyle);
|
|
410
|
+
}
|
|
411
|
+
if (mood && !promptParts.some(p => p.includes(mood))) {
|
|
412
|
+
promptParts.push(`${mood} mood`);
|
|
413
|
+
}
|
|
414
|
+
if (style.qualityTags?.length) {
|
|
415
|
+
const newTags = style.qualityTags.filter(t => !promptParts.some(p => p.includes(t)));
|
|
416
|
+
if (newTags.length)
|
|
417
|
+
promptParts.push(newTags.join(", "));
|
|
418
|
+
}
|
|
419
|
+
if (style.negativePrompts?.length) {
|
|
420
|
+
negativeParts.push(...style.negativePrompts.filter(n => !negativeParts.includes(n)));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Build summary for verification
|
|
424
|
+
summaryParts.unshift(`Name: ${entityName}`);
|
|
425
|
+
summaryParts.unshift(`Type: ${entityType}`);
|
|
426
|
+
if (imageGen?.subject?.physicalTraits?.gender) {
|
|
427
|
+
summaryParts.push(`Gender: ${imageGen.subject.physicalTraits.gender}`);
|
|
428
|
+
}
|
|
429
|
+
if (imageGen?.subject?.physicalTraits?.age) {
|
|
430
|
+
summaryParts.push(`Age: ${imageGen.subject.physicalTraits.age}`);
|
|
431
|
+
}
|
|
432
|
+
// Default negative prompt if none specified
|
|
433
|
+
if (negativeParts.length === 0) {
|
|
434
|
+
negativeParts.push("low quality", "blurry", "distorted", "deformed");
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
entityId,
|
|
438
|
+
entityType,
|
|
439
|
+
entityName,
|
|
440
|
+
prompt: promptParts.join(", "),
|
|
441
|
+
negativePrompt: negativeParts.join(", "),
|
|
442
|
+
summary: summaryParts.join("\n"),
|
|
443
|
+
source,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
export function listCharacterSummaries(gameId) {
|
|
447
|
+
const characters = listCharacters(gameId);
|
|
448
|
+
return characters.map(char => {
|
|
449
|
+
// Build summary from imageGen or notes
|
|
450
|
+
const summaryParts = [];
|
|
451
|
+
if (char.imageGen?.subject?.physicalTraits) {
|
|
452
|
+
const traits = char.imageGen.subject.physicalTraits;
|
|
453
|
+
if (traits.gender)
|
|
454
|
+
summaryParts.push(traits.gender);
|
|
455
|
+
if (traits.age)
|
|
456
|
+
summaryParts.push(traits.age);
|
|
457
|
+
if (traits.bodyType)
|
|
458
|
+
summaryParts.push(traits.bodyType);
|
|
459
|
+
}
|
|
460
|
+
// Extract role/occupation from notes if available
|
|
461
|
+
if (char.notes) {
|
|
462
|
+
// Look for common role patterns
|
|
463
|
+
const roleMatch = char.notes.match(/(?:is a|works as|serves as|the) ([a-z]+ ?[a-z]*)/i);
|
|
464
|
+
if (roleMatch && roleMatch[1]) {
|
|
465
|
+
summaryParts.push(roleMatch[1].trim());
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// Check if has images
|
|
469
|
+
const images = listEntityImages(char.id, "character");
|
|
470
|
+
return {
|
|
471
|
+
id: char.id,
|
|
472
|
+
name: char.name,
|
|
473
|
+
isPlayer: char.isPlayer,
|
|
474
|
+
summary: summaryParts.length > 0 ? summaryParts.join(", ") : char.isPlayer ? "Player Character" : "NPC",
|
|
475
|
+
hasImageGen: !!char.imageGen,
|
|
476
|
+
hasImages: images.images.length > 0,
|
|
477
|
+
};
|
|
478
|
+
});
|
|
479
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { StoredImage, StoreImageParams, ImageListResult } from "../types/index.js";
|
|
2
|
+
export declare function storeImage(params: StoreImageParams): Promise<StoredImage>;
|
|
3
|
+
export declare function getImage(imageId: string): StoredImage | null;
|
|
4
|
+
export interface GetImageDataOptions {
|
|
5
|
+
format?: "jpeg" | "webp" | "png";
|
|
6
|
+
width?: number;
|
|
7
|
+
height?: number;
|
|
8
|
+
quality?: number;
|
|
9
|
+
fit?: "cover" | "contain" | "fill" | "inside" | "outside";
|
|
10
|
+
}
|
|
11
|
+
export declare function getImageData(imageId: string, options?: GetImageDataOptions): Promise<{
|
|
12
|
+
image: StoredImage;
|
|
13
|
+
base64: string;
|
|
14
|
+
outputFormat: string;
|
|
15
|
+
outputSize: number;
|
|
16
|
+
} | null>;
|
|
17
|
+
export declare function listEntityImages(entityId: string, entityType: string): ImageListResult;
|
|
18
|
+
export interface EntityMissingImage {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
entityType: string;
|
|
22
|
+
hasImageGen: boolean;
|
|
23
|
+
isPlayer?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface EntitiesMissingImagesResult {
|
|
26
|
+
gameId: string;
|
|
27
|
+
entities: EntityMissingImage[];
|
|
28
|
+
totalMissing: number;
|
|
29
|
+
byType: {
|
|
30
|
+
characters: number;
|
|
31
|
+
locations: number;
|
|
32
|
+
items: number;
|
|
33
|
+
factions: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare function listEntitiesMissingImages(gameId: string, entityType?: string): EntitiesMissingImagesResult;
|
|
37
|
+
export declare function deleteImage(imageId: string): boolean;
|
|
38
|
+
export declare function setPrimaryImage(imageId: string): StoredImage | null;
|
|
39
|
+
export declare function updateImageMetadata(imageId: string, updates: {
|
|
40
|
+
label?: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
entityId?: string;
|
|
43
|
+
entityType?: string;
|
|
44
|
+
}): StoredImage | null;
|
|
45
|
+
export declare function deleteGameImages(gameId: string): number;
|
|
46
|
+
export declare function getPrimaryImage(entityId: string, entityType: string): StoredImage | null;
|
|
47
|
+
export declare function listGameImages(gameId: string): StoredImage[];
|