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,526 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import { getDatabase } from "../db/connection.js";
|
|
3
|
+
import { safeJsonParse } from "../utils/json.js";
|
|
4
|
+
/**
|
|
5
|
+
* Validates that a game exists and returns it, or throws an error if not found.
|
|
6
|
+
* Use this in create operations to prevent orphaned records.
|
|
7
|
+
*/
|
|
8
|
+
export function validateGameExists(gameId) {
|
|
9
|
+
const db = getDatabase();
|
|
10
|
+
const row = db.prepare(`SELECT * FROM games WHERE id = ?`).get(gameId);
|
|
11
|
+
if (!row) {
|
|
12
|
+
throw new Error(`Game '${gameId}' not found. Cannot create entities for a non-existent game.`);
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
id: row.id,
|
|
16
|
+
name: row.name,
|
|
17
|
+
setting: row.setting,
|
|
18
|
+
style: row.style,
|
|
19
|
+
rules: row.rules ? safeJsonParse(row.rules, null) : null,
|
|
20
|
+
preferences: row.preferences ? safeJsonParse(row.preferences, null) : null,
|
|
21
|
+
currentLocationId: row.current_location_id,
|
|
22
|
+
titleImageId: row.title_image_id,
|
|
23
|
+
faviconImageId: row.favicon_image_id,
|
|
24
|
+
createdAt: row.created_at,
|
|
25
|
+
updatedAt: row.updated_at,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function createGame(params) {
|
|
29
|
+
const db = getDatabase();
|
|
30
|
+
const id = uuidv4();
|
|
31
|
+
const now = new Date().toISOString();
|
|
32
|
+
const stmt = db.prepare(`
|
|
33
|
+
INSERT INTO games (id, name, setting, style, rules, preferences, current_location_id, created_at, updated_at)
|
|
34
|
+
VALUES (?, ?, ?, ?, NULL, ?, NULL, ?, ?)
|
|
35
|
+
`);
|
|
36
|
+
stmt.run(id, params.name, params.setting, params.style, params.preferences ? JSON.stringify(params.preferences) : null, now, now);
|
|
37
|
+
return {
|
|
38
|
+
id,
|
|
39
|
+
name: params.name,
|
|
40
|
+
setting: params.setting,
|
|
41
|
+
style: params.style,
|
|
42
|
+
rules: null,
|
|
43
|
+
preferences: params.preferences || null,
|
|
44
|
+
currentLocationId: null,
|
|
45
|
+
titleImageId: null,
|
|
46
|
+
faviconImageId: null,
|
|
47
|
+
createdAt: now,
|
|
48
|
+
updatedAt: now,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function loadGame(id) {
|
|
52
|
+
const db = getDatabase();
|
|
53
|
+
const stmt = db.prepare(`SELECT * FROM games WHERE id = ?`);
|
|
54
|
+
const row = stmt.get(id);
|
|
55
|
+
if (!row)
|
|
56
|
+
return null;
|
|
57
|
+
return {
|
|
58
|
+
id: row.id,
|
|
59
|
+
name: row.name,
|
|
60
|
+
setting: row.setting,
|
|
61
|
+
style: row.style,
|
|
62
|
+
rules: row.rules ? safeJsonParse(row.rules, null) : null,
|
|
63
|
+
preferences: row.preferences ? safeJsonParse(row.preferences, null) : null,
|
|
64
|
+
currentLocationId: row.current_location_id,
|
|
65
|
+
titleImageId: row.title_image_id,
|
|
66
|
+
faviconImageId: row.favicon_image_id,
|
|
67
|
+
createdAt: row.created_at,
|
|
68
|
+
updatedAt: row.updated_at,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function listGames() {
|
|
72
|
+
const db = getDatabase();
|
|
73
|
+
const stmt = db.prepare(`SELECT * FROM games ORDER BY updated_at DESC`);
|
|
74
|
+
const rows = stmt.all();
|
|
75
|
+
return rows.map((row) => ({
|
|
76
|
+
id: row.id,
|
|
77
|
+
name: row.name,
|
|
78
|
+
setting: row.setting,
|
|
79
|
+
style: row.style,
|
|
80
|
+
rules: row.rules ? safeJsonParse(row.rules, null) : null,
|
|
81
|
+
preferences: row.preferences ? safeJsonParse(row.preferences, null) : null,
|
|
82
|
+
currentLocationId: row.current_location_id,
|
|
83
|
+
titleImageId: row.title_image_id,
|
|
84
|
+
faviconImageId: row.favicon_image_id,
|
|
85
|
+
createdAt: row.created_at,
|
|
86
|
+
updatedAt: row.updated_at,
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
// Lightweight version that omits rules and preferences for listing
|
|
90
|
+
export function listGameSummaries() {
|
|
91
|
+
const db = getDatabase();
|
|
92
|
+
const stmt = db.prepare(`SELECT id, name, setting, style, title_image_id, favicon_image_id, created_at, updated_at FROM games ORDER BY updated_at DESC`);
|
|
93
|
+
const rows = stmt.all();
|
|
94
|
+
return rows.map((row) => ({
|
|
95
|
+
id: row.id,
|
|
96
|
+
name: row.name,
|
|
97
|
+
setting: row.setting,
|
|
98
|
+
style: row.style,
|
|
99
|
+
titleImageId: row.title_image_id,
|
|
100
|
+
faviconImageId: row.favicon_image_id,
|
|
101
|
+
createdAt: row.created_at,
|
|
102
|
+
updatedAt: row.updated_at,
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
export function updateGamePreferences(gameId, preferences) {
|
|
106
|
+
const db = getDatabase();
|
|
107
|
+
const now = new Date().toISOString();
|
|
108
|
+
const stmt = db.prepare(`
|
|
109
|
+
UPDATE games SET preferences = ?, updated_at = ? WHERE id = ?
|
|
110
|
+
`);
|
|
111
|
+
const result = stmt.run(JSON.stringify(preferences), now, gameId);
|
|
112
|
+
return result.changes > 0;
|
|
113
|
+
}
|
|
114
|
+
export function getGamePreferences(gameId) {
|
|
115
|
+
const game = loadGame(gameId);
|
|
116
|
+
return game?.preferences || null;
|
|
117
|
+
}
|
|
118
|
+
export function deleteGame(id) {
|
|
119
|
+
const db = getDatabase();
|
|
120
|
+
const stmt = db.prepare(`DELETE FROM games WHERE id = ?`);
|
|
121
|
+
const result = stmt.run(id);
|
|
122
|
+
return result.changes > 0;
|
|
123
|
+
}
|
|
124
|
+
export function updateGameLocation(gameId, locationId) {
|
|
125
|
+
const db = getDatabase();
|
|
126
|
+
const now = new Date().toISOString();
|
|
127
|
+
const stmt = db.prepare(`
|
|
128
|
+
UPDATE games SET current_location_id = ?, updated_at = ? WHERE id = ?
|
|
129
|
+
`);
|
|
130
|
+
const result = stmt.run(locationId, now, gameId);
|
|
131
|
+
return result.changes > 0;
|
|
132
|
+
}
|
|
133
|
+
export function updateGame(gameId, updates) {
|
|
134
|
+
const db = getDatabase();
|
|
135
|
+
const game = loadGame(gameId);
|
|
136
|
+
if (!game)
|
|
137
|
+
return null;
|
|
138
|
+
const now = new Date().toISOString();
|
|
139
|
+
const newName = updates.name ?? game.name;
|
|
140
|
+
const newSetting = updates.setting ?? game.setting;
|
|
141
|
+
const newStyle = updates.style ?? game.style;
|
|
142
|
+
const stmt = db.prepare(`
|
|
143
|
+
UPDATE games SET name = ?, setting = ?, style = ?, updated_at = ? WHERE id = ?
|
|
144
|
+
`);
|
|
145
|
+
stmt.run(newName, newSetting, newStyle, now, gameId);
|
|
146
|
+
return {
|
|
147
|
+
...game,
|
|
148
|
+
name: newName,
|
|
149
|
+
setting: newSetting,
|
|
150
|
+
style: newStyle,
|
|
151
|
+
updatedAt: now,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function setGameTitleImage(gameId, imageId) {
|
|
155
|
+
const db = getDatabase();
|
|
156
|
+
const game = loadGame(gameId);
|
|
157
|
+
if (!game)
|
|
158
|
+
return null;
|
|
159
|
+
const now = new Date().toISOString();
|
|
160
|
+
const stmt = db.prepare(`
|
|
161
|
+
UPDATE games SET title_image_id = ?, updated_at = ? WHERE id = ?
|
|
162
|
+
`);
|
|
163
|
+
stmt.run(imageId, now, gameId);
|
|
164
|
+
return {
|
|
165
|
+
...game,
|
|
166
|
+
titleImageId: imageId,
|
|
167
|
+
updatedAt: now,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
export function setGameFavicon(gameId, imageId) {
|
|
171
|
+
const db = getDatabase();
|
|
172
|
+
const game = loadGame(gameId);
|
|
173
|
+
if (!game)
|
|
174
|
+
return null;
|
|
175
|
+
const now = new Date().toISOString();
|
|
176
|
+
const stmt = db.prepare(`
|
|
177
|
+
UPDATE games SET favicon_image_id = ?, updated_at = ? WHERE id = ?
|
|
178
|
+
`);
|
|
179
|
+
stmt.run(imageId, now, gameId);
|
|
180
|
+
return {
|
|
181
|
+
...game,
|
|
182
|
+
faviconImageId: imageId,
|
|
183
|
+
updatedAt: now,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
export function getGameMenu() {
|
|
187
|
+
const db = getDatabase();
|
|
188
|
+
const games = listGames();
|
|
189
|
+
if (games.length === 0) {
|
|
190
|
+
return {
|
|
191
|
+
hasExistingGames: false,
|
|
192
|
+
games: [],
|
|
193
|
+
instruction: "No existing games found. Start the new game interview process directly using get_interview_template.",
|
|
194
|
+
visualizationTip: "Enhance the experience with visuals: use render_map for world maps, and use image generation for character portraits, items, and scene illustrations.",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// Get additional info for each game
|
|
198
|
+
const gamesWithInfo = games.map((game) => {
|
|
199
|
+
const charCount = db
|
|
200
|
+
.prepare(`SELECT COUNT(*) as count FROM characters WHERE game_id = ?`)
|
|
201
|
+
.get(game.id);
|
|
202
|
+
const locCount = db
|
|
203
|
+
.prepare(`SELECT COUNT(*) as count FROM locations WHERE game_id = ?`)
|
|
204
|
+
.get(game.id);
|
|
205
|
+
return {
|
|
206
|
+
id: game.id,
|
|
207
|
+
name: game.name,
|
|
208
|
+
setting: game.setting,
|
|
209
|
+
style: game.style,
|
|
210
|
+
lastPlayed: game.updatedAt,
|
|
211
|
+
characterCount: charCount.count,
|
|
212
|
+
locationCount: locCount.count,
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
hasExistingGames: true,
|
|
217
|
+
games: gamesWithInfo,
|
|
218
|
+
instruction: "Present these existing games as choices (most recent first), with an option to start a new game. Use present_choices or ask the player which game to continue.",
|
|
219
|
+
visualizationTip: "Enhance the experience with visuals: use render_map for world maps, and create ASCII art for character portraits, items, scene illustrations, and combat layouts when image generation isn't available.",
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export function getGameState(gameId) {
|
|
223
|
+
const db = getDatabase();
|
|
224
|
+
const game = loadGame(gameId);
|
|
225
|
+
if (!game)
|
|
226
|
+
return null;
|
|
227
|
+
const charCount = db
|
|
228
|
+
.prepare(`SELECT COUNT(*) as count FROM characters WHERE game_id = ?`)
|
|
229
|
+
.get(gameId);
|
|
230
|
+
const locCount = db
|
|
231
|
+
.prepare(`SELECT COUNT(*) as count FROM locations WHERE game_id = ?`)
|
|
232
|
+
.get(gameId);
|
|
233
|
+
const questCount = db
|
|
234
|
+
.prepare(`SELECT COUNT(*) as count FROM quests WHERE game_id = ? AND status = 'active'`)
|
|
235
|
+
.get(gameId);
|
|
236
|
+
const combat = db
|
|
237
|
+
.prepare(`SELECT COUNT(*) as count FROM combats WHERE game_id = ? AND status = 'active'`)
|
|
238
|
+
.get(gameId);
|
|
239
|
+
return {
|
|
240
|
+
game,
|
|
241
|
+
characterCount: charCount.count,
|
|
242
|
+
locationCount: locCount.count,
|
|
243
|
+
activeQuests: questCount.count,
|
|
244
|
+
activeCombat: combat.count > 0,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
// ============================================================================
|
|
248
|
+
// Image Generation Presets - Collection Management
|
|
249
|
+
// ============================================================================
|
|
250
|
+
export function listImageGenerationPresets(gameId) {
|
|
251
|
+
const preferences = getGamePreferences(gameId);
|
|
252
|
+
return preferences?.imageGenerationPresets || [];
|
|
253
|
+
}
|
|
254
|
+
export function getImageGenerationPreset(gameId, presetId) {
|
|
255
|
+
const presets = listImageGenerationPresets(gameId);
|
|
256
|
+
return presets.find(p => p.id === presetId) || null;
|
|
257
|
+
}
|
|
258
|
+
export function getDefaultImagePreset(gameId) {
|
|
259
|
+
const preferences = getGamePreferences(gameId);
|
|
260
|
+
const presets = preferences?.imageGenerationPresets || [];
|
|
261
|
+
// First try to find by defaultImagePresetId
|
|
262
|
+
if (preferences?.defaultImagePresetId) {
|
|
263
|
+
const preset = presets.find(p => p.id === preferences.defaultImagePresetId);
|
|
264
|
+
if (preset)
|
|
265
|
+
return preset;
|
|
266
|
+
}
|
|
267
|
+
// Then try to find one marked as default
|
|
268
|
+
const defaultPreset = presets.find(p => p.isDefault);
|
|
269
|
+
if (defaultPreset)
|
|
270
|
+
return defaultPreset;
|
|
271
|
+
// Fall back to first preset
|
|
272
|
+
return presets[0] || null;
|
|
273
|
+
}
|
|
274
|
+
export function createImageGenerationPreset(gameId, params) {
|
|
275
|
+
const currentPrefs = getGamePreferences(gameId) || {};
|
|
276
|
+
const presets = currentPrefs.imageGenerationPresets || [];
|
|
277
|
+
const now = new Date().toISOString();
|
|
278
|
+
const newPreset = {
|
|
279
|
+
id: uuidv4(),
|
|
280
|
+
name: params.name,
|
|
281
|
+
description: params.description,
|
|
282
|
+
entityTypes: params.entityTypes,
|
|
283
|
+
isDefault: params.isDefault,
|
|
284
|
+
config: params.config,
|
|
285
|
+
createdAt: now,
|
|
286
|
+
updatedAt: now,
|
|
287
|
+
};
|
|
288
|
+
// If this is marked as default, unmark others
|
|
289
|
+
let updatedPresets = presets;
|
|
290
|
+
if (params.isDefault) {
|
|
291
|
+
updatedPresets = presets.map(p => ({ ...p, isDefault: false }));
|
|
292
|
+
}
|
|
293
|
+
const updatedPrefs = {
|
|
294
|
+
...currentPrefs,
|
|
295
|
+
imageGenerationPresets: [...updatedPresets, newPreset],
|
|
296
|
+
...(params.isDefault ? { defaultImagePresetId: newPreset.id } : {}),
|
|
297
|
+
};
|
|
298
|
+
updateGamePreferences(gameId, updatedPrefs);
|
|
299
|
+
return newPreset;
|
|
300
|
+
}
|
|
301
|
+
export function updateImageGenerationPreset(gameId, presetId, updates) {
|
|
302
|
+
const currentPrefs = getGamePreferences(gameId) || {};
|
|
303
|
+
const presets = currentPrefs.imageGenerationPresets || [];
|
|
304
|
+
const presetIndex = presets.findIndex(p => p.id === presetId);
|
|
305
|
+
if (presetIndex === -1)
|
|
306
|
+
return null;
|
|
307
|
+
const currentPreset = presets[presetIndex];
|
|
308
|
+
const now = new Date().toISOString();
|
|
309
|
+
// Deep merge config if provided
|
|
310
|
+
let updatedConfig = currentPreset.config;
|
|
311
|
+
if (updates.config) {
|
|
312
|
+
updatedConfig = deepMergePreferences(currentPreset.config, updates.config);
|
|
313
|
+
}
|
|
314
|
+
const updatedPreset = {
|
|
315
|
+
...currentPreset,
|
|
316
|
+
...(updates.name !== undefined ? { name: updates.name } : {}),
|
|
317
|
+
...(updates.description !== undefined ? { description: updates.description } : {}),
|
|
318
|
+
...(updates.entityTypes !== undefined ? { entityTypes: updates.entityTypes } : {}),
|
|
319
|
+
...(updates.isDefault !== undefined ? { isDefault: updates.isDefault } : {}),
|
|
320
|
+
config: updatedConfig,
|
|
321
|
+
updatedAt: now,
|
|
322
|
+
};
|
|
323
|
+
// If this is marked as default, unmark others
|
|
324
|
+
let updatedPresets = [...presets];
|
|
325
|
+
if (updates.isDefault) {
|
|
326
|
+
updatedPresets = presets.map(p => ({ ...p, isDefault: false }));
|
|
327
|
+
}
|
|
328
|
+
updatedPresets[presetIndex] = updatedPreset;
|
|
329
|
+
const updatedPrefs = {
|
|
330
|
+
...currentPrefs,
|
|
331
|
+
imageGenerationPresets: updatedPresets,
|
|
332
|
+
...(updates.isDefault ? { defaultImagePresetId: presetId } : {}),
|
|
333
|
+
};
|
|
334
|
+
updateGamePreferences(gameId, updatedPrefs);
|
|
335
|
+
return updatedPreset;
|
|
336
|
+
}
|
|
337
|
+
export function deleteImageGenerationPreset(gameId, presetId) {
|
|
338
|
+
const currentPrefs = getGamePreferences(gameId) || {};
|
|
339
|
+
const presets = currentPrefs.imageGenerationPresets || [];
|
|
340
|
+
const filteredPresets = presets.filter(p => p.id !== presetId);
|
|
341
|
+
if (filteredPresets.length === presets.length)
|
|
342
|
+
return false; // Not found
|
|
343
|
+
const updatedPrefs = {
|
|
344
|
+
...currentPrefs,
|
|
345
|
+
imageGenerationPresets: filteredPresets,
|
|
346
|
+
// Clear default if we deleted the default preset
|
|
347
|
+
...(currentPrefs.defaultImagePresetId === presetId ? { defaultImagePresetId: undefined } : {}),
|
|
348
|
+
};
|
|
349
|
+
updateGamePreferences(gameId, updatedPrefs);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
export function setDefaultImagePreset(gameId, presetId) {
|
|
353
|
+
const currentPrefs = getGamePreferences(gameId) || {};
|
|
354
|
+
const presets = currentPrefs.imageGenerationPresets || [];
|
|
355
|
+
// Verify preset exists
|
|
356
|
+
const preset = presets.find(p => p.id === presetId);
|
|
357
|
+
if (!preset)
|
|
358
|
+
return false;
|
|
359
|
+
// Update all presets to unmark default, then mark the selected one
|
|
360
|
+
const updatedPresets = presets.map(p => ({
|
|
361
|
+
...p,
|
|
362
|
+
isDefault: p.id === presetId,
|
|
363
|
+
}));
|
|
364
|
+
const updatedPrefs = {
|
|
365
|
+
...currentPrefs,
|
|
366
|
+
imageGenerationPresets: updatedPresets,
|
|
367
|
+
defaultImagePresetId: presetId,
|
|
368
|
+
};
|
|
369
|
+
updateGamePreferences(gameId, updatedPrefs);
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
// Helper function to deep merge preferences
|
|
373
|
+
function deepMergePreferences(current, updates) {
|
|
374
|
+
return {
|
|
375
|
+
...current,
|
|
376
|
+
...updates,
|
|
377
|
+
// Deep merge for nested objects
|
|
378
|
+
defaultStyle: updates.defaultStyle !== undefined ? {
|
|
379
|
+
...current.defaultStyle,
|
|
380
|
+
...updates.defaultStyle,
|
|
381
|
+
} : current.defaultStyle,
|
|
382
|
+
comfyui: updates.comfyui !== undefined ? {
|
|
383
|
+
...current.comfyui,
|
|
384
|
+
...updates.comfyui,
|
|
385
|
+
samplerSettings: {
|
|
386
|
+
...current.comfyui?.samplerSettings,
|
|
387
|
+
...updates.comfyui?.samplerSettings,
|
|
388
|
+
},
|
|
389
|
+
workflows: {
|
|
390
|
+
...current.comfyui?.workflows,
|
|
391
|
+
...updates.comfyui?.workflows,
|
|
392
|
+
},
|
|
393
|
+
} : current.comfyui,
|
|
394
|
+
dalle: updates.dalle !== undefined ? {
|
|
395
|
+
...current.dalle,
|
|
396
|
+
...updates.dalle,
|
|
397
|
+
} : current.dalle,
|
|
398
|
+
midjourney: updates.midjourney !== undefined ? {
|
|
399
|
+
...current.midjourney,
|
|
400
|
+
...updates.midjourney,
|
|
401
|
+
} : current.midjourney,
|
|
402
|
+
sdxl: updates.sdxl !== undefined ? {
|
|
403
|
+
...current.sdxl,
|
|
404
|
+
...updates.sdxl,
|
|
405
|
+
} : current.sdxl,
|
|
406
|
+
flux: updates.flux !== undefined ? {
|
|
407
|
+
...current.flux,
|
|
408
|
+
...updates.flux,
|
|
409
|
+
} : current.flux,
|
|
410
|
+
defaults: updates.defaults !== undefined ? {
|
|
411
|
+
...current.defaults,
|
|
412
|
+
...updates.defaults,
|
|
413
|
+
framing: {
|
|
414
|
+
...current.defaults?.framing,
|
|
415
|
+
...updates.defaults?.framing,
|
|
416
|
+
},
|
|
417
|
+
} : current.defaults,
|
|
418
|
+
consistency: updates.consistency !== undefined ? {
|
|
419
|
+
...current.consistency,
|
|
420
|
+
...updates.consistency,
|
|
421
|
+
} : current.consistency,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
// ============================================================================
|
|
425
|
+
// Image Prompt Templates - Template-based prompt building
|
|
426
|
+
// ============================================================================
|
|
427
|
+
export function listImagePromptTemplates(gameId) {
|
|
428
|
+
const preferences = getGamePreferences(gameId);
|
|
429
|
+
return preferences?.imagePromptTemplates || [];
|
|
430
|
+
}
|
|
431
|
+
export function getImagePromptTemplate(gameId, templateId) {
|
|
432
|
+
const templates = listImagePromptTemplates(gameId);
|
|
433
|
+
return templates.find(t => t.id === templateId) || null;
|
|
434
|
+
}
|
|
435
|
+
export function getDefaultImagePromptTemplate(gameId, entityType) {
|
|
436
|
+
const templates = listImagePromptTemplates(gameId);
|
|
437
|
+
const entityTemplates = templates.filter(t => t.entityType === entityType);
|
|
438
|
+
if (entityTemplates.length === 0)
|
|
439
|
+
return null;
|
|
440
|
+
// First try to find one marked as default
|
|
441
|
+
const defaultTemplate = entityTemplates.find(t => t.isDefault);
|
|
442
|
+
if (defaultTemplate)
|
|
443
|
+
return defaultTemplate;
|
|
444
|
+
// Otherwise return highest priority
|
|
445
|
+
return entityTemplates.sort((a, b) => (b.priority || 0) - (a.priority || 0))[0];
|
|
446
|
+
}
|
|
447
|
+
export function createImagePromptTemplate(gameId, params) {
|
|
448
|
+
const currentPrefs = getGamePreferences(gameId) || {};
|
|
449
|
+
const templates = currentPrefs.imagePromptTemplates || [];
|
|
450
|
+
const now = new Date().toISOString();
|
|
451
|
+
const newTemplate = {
|
|
452
|
+
id: uuidv4(),
|
|
453
|
+
name: params.name,
|
|
454
|
+
description: params.description,
|
|
455
|
+
entityType: params.entityType,
|
|
456
|
+
promptTemplate: params.promptTemplate,
|
|
457
|
+
negativePromptTemplate: params.negativePromptTemplate,
|
|
458
|
+
promptPrefix: params.promptPrefix,
|
|
459
|
+
promptSuffix: params.promptSuffix,
|
|
460
|
+
fieldAliases: params.fieldAliases,
|
|
461
|
+
defaults: params.defaults,
|
|
462
|
+
priority: params.priority,
|
|
463
|
+
isDefault: params.isDefault,
|
|
464
|
+
createdAt: now,
|
|
465
|
+
updatedAt: now,
|
|
466
|
+
};
|
|
467
|
+
// If this is marked as default, unmark others of same entity type
|
|
468
|
+
let updatedTemplates = templates;
|
|
469
|
+
if (params.isDefault) {
|
|
470
|
+
updatedTemplates = templates.map(t => t.entityType === params.entityType ? { ...t, isDefault: false } : t);
|
|
471
|
+
}
|
|
472
|
+
const updatedPrefs = {
|
|
473
|
+
...currentPrefs,
|
|
474
|
+
imagePromptTemplates: [...updatedTemplates, newTemplate],
|
|
475
|
+
};
|
|
476
|
+
updateGamePreferences(gameId, updatedPrefs);
|
|
477
|
+
return newTemplate;
|
|
478
|
+
}
|
|
479
|
+
export function updateImagePromptTemplate(gameId, templateId, updates) {
|
|
480
|
+
const currentPrefs = getGamePreferences(gameId) || {};
|
|
481
|
+
const templates = currentPrefs.imagePromptTemplates || [];
|
|
482
|
+
const templateIndex = templates.findIndex(t => t.id === templateId);
|
|
483
|
+
if (templateIndex === -1)
|
|
484
|
+
return null;
|
|
485
|
+
const currentTemplate = templates[templateIndex];
|
|
486
|
+
const now = new Date().toISOString();
|
|
487
|
+
const updatedTemplate = {
|
|
488
|
+
...currentTemplate,
|
|
489
|
+
...(updates.name !== undefined ? { name: updates.name } : {}),
|
|
490
|
+
...(updates.description !== undefined ? { description: updates.description } : {}),
|
|
491
|
+
...(updates.promptTemplate !== undefined ? { promptTemplate: updates.promptTemplate } : {}),
|
|
492
|
+
...(updates.negativePromptTemplate !== undefined ? { negativePromptTemplate: updates.negativePromptTemplate } : {}),
|
|
493
|
+
...(updates.promptPrefix !== undefined ? { promptPrefix: updates.promptPrefix } : {}),
|
|
494
|
+
...(updates.promptSuffix !== undefined ? { promptSuffix: updates.promptSuffix } : {}),
|
|
495
|
+
...(updates.fieldAliases !== undefined ? { fieldAliases: updates.fieldAliases } : {}),
|
|
496
|
+
...(updates.defaults !== undefined ? { defaults: updates.defaults } : {}),
|
|
497
|
+
...(updates.priority !== undefined ? { priority: updates.priority } : {}),
|
|
498
|
+
...(updates.isDefault !== undefined ? { isDefault: updates.isDefault } : {}),
|
|
499
|
+
updatedAt: now,
|
|
500
|
+
};
|
|
501
|
+
// If this is marked as default, unmark others of same entity type
|
|
502
|
+
let updatedTemplates = [...templates];
|
|
503
|
+
if (updates.isDefault) {
|
|
504
|
+
updatedTemplates = templates.map(t => t.entityType === currentTemplate.entityType ? { ...t, isDefault: false } : t);
|
|
505
|
+
}
|
|
506
|
+
updatedTemplates[templateIndex] = updatedTemplate;
|
|
507
|
+
const updatedPrefs = {
|
|
508
|
+
...currentPrefs,
|
|
509
|
+
imagePromptTemplates: updatedTemplates,
|
|
510
|
+
};
|
|
511
|
+
updateGamePreferences(gameId, updatedPrefs);
|
|
512
|
+
return updatedTemplate;
|
|
513
|
+
}
|
|
514
|
+
export function deleteImagePromptTemplate(gameId, templateId) {
|
|
515
|
+
const currentPrefs = getGamePreferences(gameId) || {};
|
|
516
|
+
const templates = currentPrefs.imagePromptTemplates || [];
|
|
517
|
+
const filteredTemplates = templates.filter(t => t.id !== templateId);
|
|
518
|
+
if (filteredTemplates.length === templates.length)
|
|
519
|
+
return false; // Not found
|
|
520
|
+
const updatedPrefs = {
|
|
521
|
+
...currentPrefs,
|
|
522
|
+
imagePromptTemplates: filteredTemplates,
|
|
523
|
+
};
|
|
524
|
+
updateGamePreferences(gameId, updatedPrefs);
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ImageGeneration, ImagePromptTemplate, Character, Location, Item, Faction } from "../types/index.js";
|
|
2
|
+
export interface PromptBuilderResult {
|
|
3
|
+
entityId: string;
|
|
4
|
+
entityType: string;
|
|
5
|
+
entityName: string;
|
|
6
|
+
prompt: string;
|
|
7
|
+
negativePrompt: string;
|
|
8
|
+
summary: string;
|
|
9
|
+
source: {
|
|
10
|
+
fromImageGen: boolean;
|
|
11
|
+
fromNotes: boolean;
|
|
12
|
+
fromPreset: boolean;
|
|
13
|
+
fromTemplate: boolean;
|
|
14
|
+
};
|
|
15
|
+
templateUsed?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fill a template string with values from data object.
|
|
19
|
+
* Supports:
|
|
20
|
+
* - {{field.path}} - simple value substitution
|
|
21
|
+
* - {{field.path|"default"}} - value with default if undefined/null
|
|
22
|
+
* - {{field.path|}} - value or empty string if undefined
|
|
23
|
+
*/
|
|
24
|
+
export declare function fillTemplate(template: string, data: Record<string, unknown>, defaults?: Record<string, string>, aliases?: Record<string, string>): string;
|
|
25
|
+
/**
|
|
26
|
+
* Build a prompt using a template.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildPromptFromTemplate(template: ImagePromptTemplate, entity: Character | Location | Item | Faction, entityType: string, imageGen: ImageGeneration | null, notes: string): {
|
|
29
|
+
prompt: string;
|
|
30
|
+
negativePrompt: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Build an image generation prompt from an entity's structured data.
|
|
34
|
+
* If a template exists for the entity type, uses that. Otherwise uses default logic.
|
|
35
|
+
* Combines imageGen schema, notes, and game preset to create a ready-to-use prompt.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildImagePrompt(entityId: string, entityType: string, gameId?: string, templateId?: string): PromptBuilderResult | null;
|
|
38
|
+
/**
|
|
39
|
+
* List characters with summary info for quick identification.
|
|
40
|
+
*/
|
|
41
|
+
export interface CharacterSummary {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
isPlayer: boolean;
|
|
45
|
+
summary: string;
|
|
46
|
+
hasImageGen: boolean;
|
|
47
|
+
hasImages: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare function listCharacterSummaries(gameId: string): CharacterSummary[];
|