ima2-gen 1.1.21 → 1.1.23
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/README.md +44 -7
- package/bin/commands/video.js +14 -0
- package/bin/ima2.js +14 -4
- package/bin/lib/platform.js +34 -5
- package/docs/README.ko.md +43 -2
- package/lib/agentQueueWorker.js +6 -0
- package/lib/agentRuntime.js +3 -2
- package/lib/atomicWrite.js +14 -0
- package/lib/grokImageAdapter.js +6 -0
- package/lib/grokProxyLauncher.js +5 -3
- package/lib/grokVideoAdapter.js +1 -1
- package/lib/grokVideoPlannerPrompt.js +10 -0
- package/lib/inflight.js +1 -1
- package/lib/oauthLauncher.js +5 -0
- package/lib/videoFrameExtract.js +3 -3
- package/package.json +5 -7
- package/routes/capabilities.js +13 -0
- package/routes/edit.js +2 -1
- package/routes/generate.js +32 -6
- package/routes/health.js +4 -3
- package/routes/multimode.js +2 -1
- package/routes/video.js +35 -3
- package/server.js +29 -2
- package/skills/ima2/SKILL.md +48 -6
- package/ui/dist/.vite/manifest.json +12 -12
- package/ui/dist/assets/{AgentWorkspace-B_hq9CLg.js → AgentWorkspace-C21zqdTZ.js} +1 -1
- package/ui/dist/assets/{CardNewsWorkspace-wD12J7qk.js → CardNewsWorkspace-BN-ga1lG.js} +1 -1
- package/ui/dist/assets/{NodeCanvas-CI_wuPMf.js → NodeCanvas-BbMa4IhI.js} +1 -1
- package/ui/dist/assets/{PromptBuilderPanel-CUTujJUV.js → PromptBuilderPanel-DRwBJRDQ.js} +1 -1
- package/ui/dist/assets/{PromptImportDialog-CUi66jPK.js → PromptImportDialog-Dp85kHCq.js} +2 -2
- package/ui/dist/assets/{PromptImportDiscoverySection-Cm3vrjY4.js → PromptImportDiscoverySection-BE8Q8MLD.js} +1 -1
- package/ui/dist/assets/{PromptImportFolderSection-DOtWTD9n.js → PromptImportFolderSection-PtH5x0sc.js} +1 -1
- package/ui/dist/assets/{PromptLibraryPanel-BMjQegRa.js → PromptLibraryPanel-FnM9tHI9.js} +2 -2
- package/ui/dist/assets/SettingsWorkspace-MARPGyBL.js +1 -0
- package/ui/dist/assets/index-BAFI6htx.js +42 -0
- package/ui/dist/assets/{index-31uVIdt4.js → index-BSXxr_Bt.js} +1 -1
- package/ui/dist/assets/index-DS-ADE7U.css +1 -0
- package/ui/dist/index.html +2 -2
- package/bin/commands/annotate.ts +0 -119
- package/bin/commands/cancel.ts +0 -48
- package/bin/commands/canvas-versions.ts +0 -80
- package/bin/commands/capabilities.ts +0 -110
- package/bin/commands/cardnews.ts +0 -249
- package/bin/commands/comfy.ts +0 -54
- package/bin/commands/config.ts +0 -186
- package/bin/commands/defaults.ts +0 -192
- package/bin/commands/doctor.ts +0 -202
- package/bin/commands/edit.ts +0 -150
- package/bin/commands/gen.ts +0 -214
- package/bin/commands/grok.ts +0 -90
- package/bin/commands/history.ts +0 -146
- package/bin/commands/ls.ts +0 -64
- package/bin/commands/metadata.ts +0 -39
- package/bin/commands/multimode.ts +0 -196
- package/bin/commands/node.ts +0 -166
- package/bin/commands/observability.ts +0 -176
- package/bin/commands/ping.ts +0 -31
- package/bin/commands/prompt-sub/build.ts +0 -101
- package/bin/commands/prompt.ts +0 -492
- package/bin/commands/ps.ts +0 -81
- package/bin/commands/session.ts +0 -266
- package/bin/commands/show.ts +0 -72
- package/bin/commands/skill.ts +0 -70
- package/bin/commands/video.ts +0 -442
- package/bin/ima2.ts +0 -430
- package/bin/lib/args.ts +0 -92
- package/bin/lib/browser-id.ts +0 -16
- package/bin/lib/client.ts +0 -122
- package/bin/lib/config-store.ts +0 -120
- package/bin/lib/destructive-confirm.ts +0 -19
- package/bin/lib/doctor-checks.ts +0 -91
- package/bin/lib/error-hints.ts +0 -23
- package/bin/lib/files.ts +0 -39
- package/bin/lib/output.ts +0 -73
- package/bin/lib/platform.ts +0 -99
- package/bin/lib/recover-output.ts +0 -139
- package/bin/lib/sse.ts +0 -73
- package/bin/lib/star-prompt.ts +0 -97
- package/bin/lib/storage-doctor.ts +0 -39
- package/bin/lib/ui-build.ts +0 -85
- package/config.ts +0 -354
- package/lib/agentCommandParser.ts +0 -69
- package/lib/agentGenerationPlanner.ts +0 -273
- package/lib/agentQuestionResponder.ts +0 -266
- package/lib/agentQueueStore.ts +0 -270
- package/lib/agentQueueWorker.ts +0 -89
- package/lib/agentRuntime.ts +0 -604
- package/lib/agentSettings.ts +0 -72
- package/lib/agentStore.ts +0 -422
- package/lib/agentStoreRows.ts +0 -136
- package/lib/agentTypes.ts +0 -154
- package/lib/apiCachePolicy.ts +0 -11
- package/lib/assetLifecycle.ts +0 -146
- package/lib/canvasVersionStore.ts +0 -223
- package/lib/capabilities.ts +0 -126
- package/lib/cardNewsGenerator.ts +0 -271
- package/lib/cardNewsJobStore.ts +0 -142
- package/lib/cardNewsManifestStore.ts +0 -154
- package/lib/cardNewsPlanner.ts +0 -236
- package/lib/cardNewsPlannerClient.ts +0 -155
- package/lib/cardNewsPlannerPrompt.ts +0 -62
- package/lib/cardNewsPlannerSchema.ts +0 -321
- package/lib/cardNewsRoleTemplateStore.ts +0 -47
- package/lib/cardNewsTemplateStore.ts +0 -252
- package/lib/codexDetect.ts +0 -71
- package/lib/comfyBridge.ts +0 -235
- package/lib/composerSnapshot.ts +0 -33
- package/lib/configKeys.ts +0 -62
- package/lib/db.ts +0 -295
- package/lib/errInfo.ts +0 -43
- package/lib/errorClassify.ts +0 -100
- package/lib/generationCancel.ts +0 -28
- package/lib/generationErrors.ts +0 -238
- package/lib/grokImageAdapter.ts +0 -513
- package/lib/grokMultimodeAdapter.ts +0 -84
- package/lib/grokProxyLauncher.ts +0 -153
- package/lib/grokRuntime.ts +0 -23
- package/lib/grokSizeMapper.ts +0 -71
- package/lib/grokVideoAdapter.ts +0 -458
- package/lib/grokVideoCanvas.ts +0 -26
- package/lib/grokVideoDownload.ts +0 -59
- package/lib/grokVideoPlannerPrompt.ts +0 -67
- package/lib/historyIndex.ts +0 -51
- package/lib/historyList.ts +0 -181
- package/lib/imageMetadata.ts +0 -113
- package/lib/imageMetadataStore.ts +0 -67
- package/lib/imageModels.ts +0 -165
- package/lib/inflight.ts +0 -281
- package/lib/localImportStore.ts +0 -114
- package/lib/logger.ts +0 -161
- package/lib/nodeStore.ts +0 -91
- package/lib/oauthLauncher.ts +0 -94
- package/lib/oauthNormalize.ts +0 -30
- package/lib/oauthProxy/errors.ts +0 -128
- package/lib/oauthProxy/generators.ts +0 -494
- package/lib/oauthProxy/index.ts +0 -28
- package/lib/oauthProxy/prompts.ts +0 -123
- package/lib/oauthProxy/references.ts +0 -45
- package/lib/oauthProxy/runtime.ts +0 -115
- package/lib/oauthProxy/streams.ts +0 -232
- package/lib/oauthProxy/types.ts +0 -9
- package/lib/oauthProxy.ts +0 -3
- package/lib/openDirectory.ts +0 -47
- package/lib/pngInfo.ts +0 -26
- package/lib/promptBuilder/attachments.ts +0 -74
- package/lib/promptBuilder/client.ts +0 -130
- package/lib/promptBuilder/constants.ts +0 -9
- package/lib/promptBuilder/context.ts +0 -36
- package/lib/promptBuilder/errors.ts +0 -12
- package/lib/promptBuilder/requestSchema.ts +0 -56
- package/lib/promptBuilder/responseParser.ts +0 -219
- package/lib/promptBuilder/systemPrompt.ts +0 -135
- package/lib/promptBuilder/transport.ts +0 -94
- package/lib/promptBuilder/types.ts +0 -109
- package/lib/promptImport/curatedSources.ts +0 -141
- package/lib/promptImport/discoveryRegistry.ts +0 -329
- package/lib/promptImport/errors.ts +0 -18
- package/lib/promptImport/githubDiscovery.ts +0 -309
- package/lib/promptImport/githubFolder.ts +0 -397
- package/lib/promptImport/githubSource.ts +0 -257
- package/lib/promptImport/gptImageHints.ts +0 -70
- package/lib/promptImport/parsePromptCandidates.ts +0 -179
- package/lib/promptImport/promptIndex.ts +0 -326
- package/lib/promptImport/rankPromptCandidates.ts +0 -65
- package/lib/promptImport/types.ts +0 -103
- package/lib/promptSafetyPolicy.ts +0 -5
- package/lib/providerOptions.ts +0 -56
- package/lib/referenceImageCompress.ts +0 -84
- package/lib/refs.ts +0 -133
- package/lib/requestLogger.ts +0 -49
- package/lib/responsesDoctor.ts +0 -456
- package/lib/responsesErrors.ts +0 -83
- package/lib/responsesFallback.ts +0 -114
- package/lib/responsesImageAdapter.ts +0 -466
- package/lib/responsesParse.ts +0 -452
- package/lib/responsesTools.ts +0 -28
- package/lib/runtimeContext.ts +0 -146
- package/lib/runtimePorts.ts +0 -105
- package/lib/sessionStore.ts +0 -308
- package/lib/storageMigration.ts +0 -310
- package/lib/styleSheet.ts +0 -139
- package/lib/systemTrash.ts +0 -20
- package/lib/videoContinuity.ts +0 -180
- package/lib/videoFrameExtract.ts +0 -78
- package/lib/videoSeriesChain.ts +0 -29
- package/lib/visibleTextLanguagePolicy.ts +0 -7
- package/routes/agent.ts +0 -308
- package/routes/annotations.ts +0 -118
- package/routes/canvasVersions.ts +0 -69
- package/routes/capabilities.ts +0 -18
- package/routes/cardNews.ts +0 -211
- package/routes/comfy.ts +0 -43
- package/routes/edit.ts +0 -352
- package/routes/generate.ts +0 -492
- package/routes/grok.ts +0 -24
- package/routes/health.ts +0 -123
- package/routes/history.ts +0 -221
- package/routes/imageImport.ts +0 -37
- package/routes/index.ts +0 -52
- package/routes/metadata.ts +0 -77
- package/routes/multimode.ts +0 -499
- package/routes/nodes.ts +0 -578
- package/routes/promptBuilder.ts +0 -37
- package/routes/promptImport.ts +0 -379
- package/routes/prompts.ts +0 -428
- package/routes/quota.ts +0 -89
- package/routes/sessions.ts +0 -317
- package/routes/storage.ts +0 -47
- package/routes/video.ts +0 -300
- package/routes/videoExtended.ts +0 -284
- package/server.ts +0 -293
- package/ui/dist/assets/SettingsWorkspace-PiaVnsdA.js +0 -1
- package/ui/dist/assets/index-CjgnNtgt.css +0 -1
- package/ui/dist/assets/index-Da2s4_-5.js +0 -36
package/lib/comfyBridge.ts
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
import { constants as fsConstants } from "node:fs";
|
|
2
|
-
import { access, readFile, realpath, stat } from "node:fs/promises";
|
|
3
|
-
import { basename, extname, isAbsolute, join, relative } from "node:path";
|
|
4
|
-
|
|
5
|
-
export const COMFY_ERROR = {
|
|
6
|
-
URL_NOT_LOCAL: "COMFY_URL_NOT_LOCAL",
|
|
7
|
-
IMAGE_INVALID: "COMFY_IMAGE_INVALID",
|
|
8
|
-
IMAGE_NOT_FOUND: "COMFY_IMAGE_NOT_FOUND",
|
|
9
|
-
UPLOAD_FAILED: "COMFY_UPLOAD_FAILED",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const LOCAL_HOSTS = new Set(["127.0.0.1", "localhost", "[::1]"]);
|
|
13
|
-
|
|
14
|
-
class ComfyBridgeError extends Error {
|
|
15
|
-
code: string;
|
|
16
|
-
status: number;
|
|
17
|
-
constructor(code: string, message: string, status: number) {
|
|
18
|
-
super(message);
|
|
19
|
-
this.name = "ComfyBridgeError";
|
|
20
|
-
this.code = code;
|
|
21
|
-
this.status = status;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function bridgeError(code: string, message: string, status: number) {
|
|
26
|
-
return new ComfyBridgeError(code, message, status);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function isComfyBridgeError(error: unknown): error is ComfyBridgeError {
|
|
30
|
-
return error instanceof ComfyBridgeError;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function normalizeComfyOrigin(rawUrl: unknown): string {
|
|
34
|
-
if (typeof rawUrl !== "string" || !rawUrl.trim()) {
|
|
35
|
-
throw bridgeError(COMFY_ERROR.URL_NOT_LOCAL, "ComfyUI URL is not configured.", 400);
|
|
36
|
-
}
|
|
37
|
-
const trimmed = rawUrl.trim();
|
|
38
|
-
const rawHost = trimmed.match(/^http:\/\/(?:[^@/]+@)?(\[[^\]]+\]|[^/:?#]+)/i)?.[1] ?? "";
|
|
39
|
-
if (
|
|
40
|
-
rawHost === "localhost." ||
|
|
41
|
-
/^\d+$/.test(rawHost) ||
|
|
42
|
-
/^0x/i.test(rawHost) ||
|
|
43
|
-
/^0[0-9]+/.test(rawHost) ||
|
|
44
|
-
(/^[0-9.]+$/.test(rawHost) && rawHost.split(".").length !== 4) ||
|
|
45
|
-
rawHost.split(".").some((part) => part.length > 1 && part.startsWith("0"))
|
|
46
|
-
) {
|
|
47
|
-
throw bridgeError(COMFY_ERROR.URL_NOT_LOCAL, "ComfyUI URL is not local.", 400);
|
|
48
|
-
}
|
|
49
|
-
let url;
|
|
50
|
-
try {
|
|
51
|
-
url = new URL(trimmed);
|
|
52
|
-
} catch {
|
|
53
|
-
throw bridgeError(COMFY_ERROR.URL_NOT_LOCAL, "ComfyUI URL is invalid.", 400);
|
|
54
|
-
}
|
|
55
|
-
if (url.protocol !== "http:") {
|
|
56
|
-
throw bridgeError(COMFY_ERROR.URL_NOT_LOCAL, "ComfyUI URL must use HTTP.", 400);
|
|
57
|
-
}
|
|
58
|
-
if (url.username || url.password || !url.port) {
|
|
59
|
-
throw bridgeError(COMFY_ERROR.URL_NOT_LOCAL, "ComfyUI URL is not local.", 400);
|
|
60
|
-
}
|
|
61
|
-
if (!LOCAL_HOSTS.has(url.hostname)) {
|
|
62
|
-
throw bridgeError(COMFY_ERROR.URL_NOT_LOCAL, "ComfyUI URL is not local.", 400);
|
|
63
|
-
}
|
|
64
|
-
if (url.pathname !== "/" || url.search || url.hash) {
|
|
65
|
-
throw bridgeError(COMFY_ERROR.URL_NOT_LOCAL, "ComfyUI URL must be an origin.", 400);
|
|
66
|
-
}
|
|
67
|
-
return url.origin;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function hasEncodedSeparator(filename: string): boolean {
|
|
71
|
-
try {
|
|
72
|
-
const decoded = decodeURIComponent(filename);
|
|
73
|
-
return decoded.includes("/") || decoded.includes("\\");
|
|
74
|
-
} catch {
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function validateFilename(filename: unknown): string {
|
|
80
|
-
if (typeof filename !== "string" || !filename.trim()) {
|
|
81
|
-
throw bridgeError(COMFY_ERROR.IMAGE_INVALID, "A generated filename is required.", 400);
|
|
82
|
-
}
|
|
83
|
-
if (
|
|
84
|
-
isAbsolute(filename) ||
|
|
85
|
-
filename !== basename(filename) ||
|
|
86
|
-
filename.includes("/") ||
|
|
87
|
-
filename.includes("\\") ||
|
|
88
|
-
filename.includes("..") ||
|
|
89
|
-
/^[a-z][a-z0-9+.-]*:/i.test(filename) ||
|
|
90
|
-
hasEncodedSeparator(filename)
|
|
91
|
-
) {
|
|
92
|
-
throw bridgeError(COMFY_ERROR.IMAGE_INVALID, "Generated filename is invalid.", 400);
|
|
93
|
-
}
|
|
94
|
-
return filename;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function isInsideDirectory(parent: string, candidate: string): boolean {
|
|
98
|
-
const rel = relative(parent, candidate);
|
|
99
|
-
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
interface ImageType { ext: string; mime: string; }
|
|
103
|
-
|
|
104
|
-
function sniffImage(buffer: Buffer): ImageType {
|
|
105
|
-
if (
|
|
106
|
-
buffer.length >= 8 &&
|
|
107
|
-
buffer[0] === 0x89 &&
|
|
108
|
-
buffer[1] === 0x50 &&
|
|
109
|
-
buffer[2] === 0x4e &&
|
|
110
|
-
buffer[3] === 0x47 &&
|
|
111
|
-
buffer[4] === 0x0d &&
|
|
112
|
-
buffer[5] === 0x0a &&
|
|
113
|
-
buffer[6] === 0x1a &&
|
|
114
|
-
buffer[7] === 0x0a
|
|
115
|
-
) {
|
|
116
|
-
return { ext: "png", mime: "image/png" };
|
|
117
|
-
}
|
|
118
|
-
if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
|
|
119
|
-
return { ext: "jpg", mime: "image/jpeg" };
|
|
120
|
-
}
|
|
121
|
-
if (
|
|
122
|
-
buffer.length >= 12 &&
|
|
123
|
-
buffer.subarray(0, 4).toString("ascii") === "RIFF" &&
|
|
124
|
-
buffer.subarray(8, 12).toString("ascii") === "WEBP"
|
|
125
|
-
) {
|
|
126
|
-
return { ext: "webp", mime: "image/webp" };
|
|
127
|
-
}
|
|
128
|
-
throw bridgeError(COMFY_ERROR.IMAGE_INVALID, "Generated file is not a supported image.", 400);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function sanitizeBaseName(filename: string): string {
|
|
132
|
-
const raw = basename(filename, extname(filename));
|
|
133
|
-
const safe = raw.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
|
|
134
|
-
return safe || "image";
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
interface ComfyCtx {
|
|
138
|
-
config: {
|
|
139
|
-
storage: { generatedDir: string };
|
|
140
|
-
comfy: { defaultUrl: string; uploadTimeoutMs: number; maxUploadBytes: number };
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
interface GeneratedImage {
|
|
145
|
-
buffer: Buffer;
|
|
146
|
-
imageType: ImageType;
|
|
147
|
-
sourceFilename: string;
|
|
148
|
-
uploadFilename: string;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async function readGeneratedImage(ctx: ComfyCtx, filename: unknown): Promise<GeneratedImage> {
|
|
152
|
-
const safeFilename = validateFilename(filename);
|
|
153
|
-
const generatedDir = await realpath(ctx.config.storage.generatedDir);
|
|
154
|
-
const candidatePath = join(ctx.config.storage.generatedDir, safeFilename);
|
|
155
|
-
try {
|
|
156
|
-
await access(candidatePath, fsConstants.F_OK);
|
|
157
|
-
} catch {
|
|
158
|
-
throw bridgeError(COMFY_ERROR.IMAGE_NOT_FOUND, "Generated image was not found.", 404);
|
|
159
|
-
}
|
|
160
|
-
let candidateReal;
|
|
161
|
-
try {
|
|
162
|
-
candidateReal = await realpath(candidatePath);
|
|
163
|
-
} catch {
|
|
164
|
-
throw bridgeError(COMFY_ERROR.IMAGE_NOT_FOUND, "Generated image was not found.", 404);
|
|
165
|
-
}
|
|
166
|
-
if (!isInsideDirectory(generatedDir, candidateReal)) {
|
|
167
|
-
throw bridgeError(COMFY_ERROR.IMAGE_INVALID, "Generated filename is invalid.", 400);
|
|
168
|
-
}
|
|
169
|
-
const info = await stat(candidateReal);
|
|
170
|
-
if (!info.isFile()) {
|
|
171
|
-
throw bridgeError(COMFY_ERROR.IMAGE_INVALID, "Generated filename is invalid.", 400);
|
|
172
|
-
}
|
|
173
|
-
if (info.size > ctx.config.comfy.maxUploadBytes) {
|
|
174
|
-
throw bridgeError(COMFY_ERROR.IMAGE_INVALID, "Generated image is too large.", 400);
|
|
175
|
-
}
|
|
176
|
-
const buffer = await readFile(candidateReal);
|
|
177
|
-
const imageType = sniffImage(buffer);
|
|
178
|
-
return {
|
|
179
|
-
buffer,
|
|
180
|
-
imageType,
|
|
181
|
-
sourceFilename: safeFilename,
|
|
182
|
-
uploadFilename: `ima2_${Date.now()}_${sanitizeBaseName(safeFilename)}.${imageType.ext}`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async function postToComfy(origin: string, image: GeneratedImage, timeoutMs: number, fetchImpl: typeof fetch = fetch): Promise<string> {
|
|
187
|
-
const controller = new AbortController();
|
|
188
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
189
|
-
try {
|
|
190
|
-
const form = new FormData();
|
|
191
|
-
form.append("image", new Blob([new Uint8Array(image.buffer)], { type: image.imageType.mime }), image.uploadFilename);
|
|
192
|
-
form.append("type", "input");
|
|
193
|
-
const res = await fetchImpl(`${origin}/upload/image`, {
|
|
194
|
-
method: "POST",
|
|
195
|
-
body: form,
|
|
196
|
-
redirect: "manual",
|
|
197
|
-
signal: controller.signal,
|
|
198
|
-
});
|
|
199
|
-
if (res.status >= 300 && res.status < 400) {
|
|
200
|
-
throw bridgeError(COMFY_ERROR.UPLOAD_FAILED, "Could not upload image to ComfyUI.", 502);
|
|
201
|
-
}
|
|
202
|
-
if (!res.ok) {
|
|
203
|
-
throw bridgeError(COMFY_ERROR.UPLOAD_FAILED, "Could not upload image to ComfyUI.", 502);
|
|
204
|
-
}
|
|
205
|
-
const data = await res.json().catch(() => null) as { name?: unknown } | null;
|
|
206
|
-
if (!data || typeof data.name !== "string" || !data.name.trim()) {
|
|
207
|
-
throw bridgeError(COMFY_ERROR.UPLOAD_FAILED, "Could not upload image to ComfyUI.", 502);
|
|
208
|
-
}
|
|
209
|
-
return data.name;
|
|
210
|
-
} catch (error) {
|
|
211
|
-
if (isComfyBridgeError(error)) throw error;
|
|
212
|
-
throw bridgeError(COMFY_ERROR.UPLOAD_FAILED, "Could not upload image to ComfyUI.", 502);
|
|
213
|
-
} finally {
|
|
214
|
-
clearTimeout(timeout);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
interface ExportInput { filename: unknown; }
|
|
219
|
-
interface ExportOptions { comfyUrl?: string; fetchImpl?: (input: any, init?: any) => Promise<any>; }
|
|
220
|
-
|
|
221
|
-
export async function exportImageToComfy(ctx: ComfyCtx, input: ExportInput, options: ExportOptions = {}) {
|
|
222
|
-
const origin = normalizeComfyOrigin(options.comfyUrl ?? ctx.config.comfy.defaultUrl);
|
|
223
|
-
const image = await readGeneratedImage(ctx, input.filename);
|
|
224
|
-
const uploadedFilename = await postToComfy(
|
|
225
|
-
origin,
|
|
226
|
-
image,
|
|
227
|
-
ctx.config.comfy.uploadTimeoutMs,
|
|
228
|
-
options.fetchImpl,
|
|
229
|
-
);
|
|
230
|
-
return {
|
|
231
|
-
ok: true,
|
|
232
|
-
sourceFilename: image.sourceFilename,
|
|
233
|
-
uploadedFilename,
|
|
234
|
-
};
|
|
235
|
-
}
|
package/lib/composerSnapshot.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export type ComposerInsertedPromptSnapshot = {
|
|
2
|
-
id: string;
|
|
3
|
-
name: string;
|
|
4
|
-
text: string;
|
|
5
|
-
placement: "before" | "after";
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export function normalizeComposerPrompt(value: unknown): string | null {
|
|
9
|
-
return typeof value === "string" ? value : null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function normalizeComposerInsertedPrompts(
|
|
13
|
-
value: unknown,
|
|
14
|
-
): ComposerInsertedPromptSnapshot[] {
|
|
15
|
-
if (!Array.isArray(value)) return [];
|
|
16
|
-
return value.flatMap((entry) => {
|
|
17
|
-
if (!entry || typeof entry !== "object") return [];
|
|
18
|
-
const item = entry as Record<string, unknown>;
|
|
19
|
-
if (
|
|
20
|
-
typeof item.id !== "string" ||
|
|
21
|
-
typeof item.name !== "string" ||
|
|
22
|
-
typeof item.text !== "string"
|
|
23
|
-
) {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
return [{
|
|
27
|
-
id: item.id,
|
|
28
|
-
name: item.name,
|
|
29
|
-
text: item.text,
|
|
30
|
-
placement: item.placement === "after" ? "after" : "before",
|
|
31
|
-
}];
|
|
32
|
-
});
|
|
33
|
-
}
|
package/lib/configKeys.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
export const AUTH_CONFIG_KEYS = new Set(["provider", "apiKey"]);
|
|
2
|
-
|
|
3
|
-
export const WRITABLE_CONFIG_KEYS = new Set([
|
|
4
|
-
"imageModels.default",
|
|
5
|
-
"imageModels.reasoningEffort",
|
|
6
|
-
"apiProvider.defaultImageModel",
|
|
7
|
-
"apiProvider.defaultReasoningEffort",
|
|
8
|
-
"log.level",
|
|
9
|
-
"features.cardNews",
|
|
10
|
-
"cardNewsPlanner.enabled",
|
|
11
|
-
"cardNewsPlanner.model",
|
|
12
|
-
"cardNewsPlanner.timeoutMs",
|
|
13
|
-
"cardNewsPlanner.deterministicFallback",
|
|
14
|
-
"grokProvider.plannerModel",
|
|
15
|
-
"grokProvider.plannerTimeoutMs",
|
|
16
|
-
"grokProvider.defaultImageModel",
|
|
17
|
-
"comfy.defaultUrl",
|
|
18
|
-
"comfy.uploadTimeoutMs",
|
|
19
|
-
"comfy.maxUploadBytes",
|
|
20
|
-
"storage.generatedDir",
|
|
21
|
-
"storage.generatedDirName",
|
|
22
|
-
"server.port",
|
|
23
|
-
"server.host",
|
|
24
|
-
"server.bodyLimit",
|
|
25
|
-
"oauth.proxyPort",
|
|
26
|
-
"oauth.statusTimeoutMs",
|
|
27
|
-
"oauth.restartDelayMs",
|
|
28
|
-
"limits.maxRefCount",
|
|
29
|
-
"limits.maxParallel",
|
|
30
|
-
"history.defaultPageSize",
|
|
31
|
-
"history.maxPageCap",
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
|
-
export const KEY_TO_ENV: Record<string, string> = {
|
|
35
|
-
"imageModels.default": "IMA2_IMAGE_MODEL_DEFAULT",
|
|
36
|
-
"imageModels.reasoningEffort": "IMA2_REASONING_EFFORT",
|
|
37
|
-
"apiProvider.defaultImageModel": "IMA2_API_IMAGE_MODEL_DEFAULT",
|
|
38
|
-
"apiProvider.defaultReasoningEffort": "IMA2_API_REASONING_EFFORT",
|
|
39
|
-
"log.level": "IMA2_LOG_LEVEL",
|
|
40
|
-
"features.cardNews": "IMA2_CARD_NEWS",
|
|
41
|
-
"server.port": "IMA2_PORT",
|
|
42
|
-
"server.host": "IMA2_HOST",
|
|
43
|
-
"server.bodyLimit": "IMA2_BODY_LIMIT",
|
|
44
|
-
"oauth.proxyPort": "IMA2_OAUTH_PROXY_PORT",
|
|
45
|
-
"storage.generatedDir": "IMA2_GENERATED_DIR",
|
|
46
|
-
"cardNewsPlanner.enabled": "IMA2_CARD_NEWS_PLANNER",
|
|
47
|
-
"cardNewsPlanner.model": "IMA2_CARD_NEWS_PLANNER_MODEL",
|
|
48
|
-
"cardNewsPlanner.timeoutMs": "IMA2_CARD_NEWS_PLANNER_TIMEOUT_MS",
|
|
49
|
-
"grokProvider.plannerModel": "IMA2_GROK_PLANNER_MODEL",
|
|
50
|
-
"grokProvider.plannerTimeoutMs": "IMA2_GROK_PLANNER_TIMEOUT_MS",
|
|
51
|
-
"grokProvider.defaultImageModel": "IMA2_GROK_IMAGE_MODEL_DEFAULT",
|
|
52
|
-
"limits.maxParallel": "IMA2_MAX_PARALLEL",
|
|
53
|
-
"limits.maxRefCount": "IMA2_MAX_REF_COUNT",
|
|
54
|
-
"history.defaultPageSize": "IMA2_HISTORY_PAGE_SIZE",
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const REDACT_PATTERN = /token|secret|apikey|password/i;
|
|
58
|
-
const ALWAYS_REDACT = new Set(["provider", "apiKey", "oauth.token", "oauth.refreshToken"]);
|
|
59
|
-
|
|
60
|
-
export function isSensitiveConfigKey(key: string): boolean {
|
|
61
|
-
return ALWAYS_REDACT.has(key) || REDACT_PATTERN.test(key);
|
|
62
|
-
}
|
package/lib/db.ts
DELETED
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3";
|
|
2
|
-
import { mkdirSync, existsSync } from "fs";
|
|
3
|
-
import { dirname } from "path";
|
|
4
|
-
import { config } from "../config.js";
|
|
5
|
-
|
|
6
|
-
let db: Database.Database | null = null;
|
|
7
|
-
|
|
8
|
-
export function getDbPath() {
|
|
9
|
-
return config.storage.dbPath;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function getDb() {
|
|
13
|
-
if (db) return db;
|
|
14
|
-
const dbPath = getDbPath();
|
|
15
|
-
const dir = dirname(dbPath);
|
|
16
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
17
|
-
|
|
18
|
-
db = new Database(dbPath);
|
|
19
|
-
db.pragma("journal_mode = WAL");
|
|
20
|
-
db.pragma("foreign_keys = ON");
|
|
21
|
-
migrate(db);
|
|
22
|
-
return db;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function migrate(database: Database.Database) {
|
|
26
|
-
database.exec(`
|
|
27
|
-
CREATE TABLE IF NOT EXISTS _meta (
|
|
28
|
-
key TEXT PRIMARY KEY,
|
|
29
|
-
value TEXT NOT NULL
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
33
|
-
id TEXT PRIMARY KEY,
|
|
34
|
-
title TEXT NOT NULL DEFAULT 'Untitled',
|
|
35
|
-
created_at INTEGER NOT NULL,
|
|
36
|
-
updated_at INTEGER NOT NULL,
|
|
37
|
-
graph_version INTEGER NOT NULL DEFAULT 0
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
CREATE TABLE IF NOT EXISTS nodes (
|
|
41
|
-
session_id TEXT NOT NULL,
|
|
42
|
-
id TEXT NOT NULL,
|
|
43
|
-
x REAL NOT NULL DEFAULT 0,
|
|
44
|
-
y REAL NOT NULL DEFAULT 0,
|
|
45
|
-
data TEXT NOT NULL DEFAULT '{}',
|
|
46
|
-
PRIMARY KEY (session_id, id),
|
|
47
|
-
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
CREATE TABLE IF NOT EXISTS edges (
|
|
51
|
-
session_id TEXT NOT NULL,
|
|
52
|
-
id TEXT NOT NULL,
|
|
53
|
-
source TEXT NOT NULL,
|
|
54
|
-
target TEXT NOT NULL,
|
|
55
|
-
data TEXT NOT NULL DEFAULT '{}',
|
|
56
|
-
PRIMARY KEY (session_id, id),
|
|
57
|
-
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
CREATE INDEX IF NOT EXISTS idx_nodes_session ON nodes(session_id);
|
|
61
|
-
CREATE INDEX IF NOT EXISTS idx_edges_session ON edges(session_id);
|
|
62
|
-
|
|
63
|
-
CREATE TABLE IF NOT EXISTS inflight (
|
|
64
|
-
request_id TEXT PRIMARY KEY,
|
|
65
|
-
kind TEXT NOT NULL,
|
|
66
|
-
prompt TEXT NOT NULL DEFAULT '',
|
|
67
|
-
meta TEXT NOT NULL DEFAULT '{}',
|
|
68
|
-
session_id TEXT,
|
|
69
|
-
parent_node_id TEXT,
|
|
70
|
-
client_node_id TEXT,
|
|
71
|
-
started_at INTEGER NOT NULL,
|
|
72
|
-
phase TEXT NOT NULL DEFAULT 'queued',
|
|
73
|
-
phase_at INTEGER NOT NULL
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
CREATE INDEX IF NOT EXISTS idx_inflight_started ON inflight(started_at);
|
|
77
|
-
CREATE INDEX IF NOT EXISTS idx_inflight_kind ON inflight(kind);
|
|
78
|
-
CREATE INDEX IF NOT EXISTS idx_inflight_session ON inflight(session_id);
|
|
79
|
-
|
|
80
|
-
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
81
|
-
id TEXT PRIMARY KEY,
|
|
82
|
-
title TEXT NOT NULL DEFAULT 'New Agent',
|
|
83
|
-
codex_thread_id TEXT,
|
|
84
|
-
last_turn_id TEXT,
|
|
85
|
-
current_image_id TEXT,
|
|
86
|
-
compacted INTEGER NOT NULL DEFAULT 0,
|
|
87
|
-
web_search_enabled INTEGER NOT NULL DEFAULT 1,
|
|
88
|
-
generation_settings TEXT NOT NULL DEFAULT '{}',
|
|
89
|
-
style_locks TEXT NOT NULL DEFAULT '[]',
|
|
90
|
-
subject_locks TEXT NOT NULL DEFAULT '[]',
|
|
91
|
-
created_at INTEGER NOT NULL,
|
|
92
|
-
updated_at INTEGER NOT NULL
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
CREATE TABLE IF NOT EXISTS agent_turns (
|
|
96
|
-
id TEXT PRIMARY KEY,
|
|
97
|
-
session_id TEXT NOT NULL,
|
|
98
|
-
role TEXT NOT NULL,
|
|
99
|
-
text TEXT NOT NULL DEFAULT '',
|
|
100
|
-
status TEXT NOT NULL DEFAULT 'complete',
|
|
101
|
-
image_ids TEXT NOT NULL DEFAULT '[]',
|
|
102
|
-
web_finding_ids TEXT NOT NULL DEFAULT '[]',
|
|
103
|
-
raw TEXT NOT NULL DEFAULT '{}',
|
|
104
|
-
created_at INTEGER NOT NULL,
|
|
105
|
-
FOREIGN KEY (session_id) REFERENCES agent_sessions(id) ON DELETE CASCADE
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
CREATE TABLE IF NOT EXISTS agent_images (
|
|
109
|
-
id TEXT PRIMARY KEY,
|
|
110
|
-
session_id TEXT NOT NULL,
|
|
111
|
-
filename TEXT NOT NULL,
|
|
112
|
-
url TEXT NOT NULL,
|
|
113
|
-
thumb_url TEXT,
|
|
114
|
-
prompt TEXT,
|
|
115
|
-
revised_prompt TEXT,
|
|
116
|
-
width INTEGER,
|
|
117
|
-
height INTEGER,
|
|
118
|
-
created_at INTEGER NOT NULL,
|
|
119
|
-
FOREIGN KEY (session_id) REFERENCES agent_sessions(id) ON DELETE CASCADE
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
CREATE TABLE IF NOT EXISTS agent_references (
|
|
123
|
-
id TEXT PRIMARY KEY,
|
|
124
|
-
session_id TEXT NOT NULL,
|
|
125
|
-
role TEXT NOT NULL DEFAULT 'source',
|
|
126
|
-
image_id TEXT,
|
|
127
|
-
filename TEXT,
|
|
128
|
-
url TEXT,
|
|
129
|
-
prompt TEXT,
|
|
130
|
-
created_at INTEGER NOT NULL,
|
|
131
|
-
FOREIGN KEY (session_id) REFERENCES agent_sessions(id) ON DELETE CASCADE
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
CREATE TABLE IF NOT EXISTS agent_web_findings (
|
|
135
|
-
id TEXT PRIMARY KEY,
|
|
136
|
-
session_id TEXT NOT NULL,
|
|
137
|
-
query TEXT NOT NULL DEFAULT '',
|
|
138
|
-
url TEXT,
|
|
139
|
-
title TEXT,
|
|
140
|
-
snippet TEXT,
|
|
141
|
-
created_at INTEGER NOT NULL,
|
|
142
|
-
FOREIGN KEY (session_id) REFERENCES agent_sessions(id) ON DELETE CASCADE
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
CREATE TABLE IF NOT EXISTS agent_queue_items (
|
|
146
|
-
id TEXT PRIMARY KEY,
|
|
147
|
-
session_id TEXT NOT NULL,
|
|
148
|
-
request_id TEXT NOT NULL,
|
|
149
|
-
prompt TEXT NOT NULL DEFAULT '',
|
|
150
|
-
options TEXT NOT NULL DEFAULT '{}',
|
|
151
|
-
tool_plan TEXT NOT NULL DEFAULT '{}',
|
|
152
|
-
status TEXT NOT NULL DEFAULT 'queued',
|
|
153
|
-
position INTEGER NOT NULL DEFAULT 0,
|
|
154
|
-
result_image_ids TEXT NOT NULL DEFAULT '[]',
|
|
155
|
-
error_code TEXT,
|
|
156
|
-
error_message TEXT,
|
|
157
|
-
created_at INTEGER NOT NULL,
|
|
158
|
-
started_at INTEGER,
|
|
159
|
-
finished_at INTEGER,
|
|
160
|
-
FOREIGN KEY (session_id) REFERENCES agent_sessions(id) ON DELETE CASCADE
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
CREATE INDEX IF NOT EXISTS idx_agent_sessions_updated
|
|
164
|
-
ON agent_sessions(updated_at);
|
|
165
|
-
CREATE INDEX IF NOT EXISTS idx_agent_turns_session
|
|
166
|
-
ON agent_turns(session_id, created_at);
|
|
167
|
-
CREATE INDEX IF NOT EXISTS idx_agent_images_session
|
|
168
|
-
ON agent_images(session_id, created_at);
|
|
169
|
-
CREATE INDEX IF NOT EXISTS idx_agent_web_findings_session
|
|
170
|
-
ON agent_web_findings(session_id, created_at);
|
|
171
|
-
CREATE INDEX IF NOT EXISTS idx_agent_queue_session
|
|
172
|
-
ON agent_queue_items(session_id, status, created_at);
|
|
173
|
-
CREATE INDEX IF NOT EXISTS idx_agent_queue_status
|
|
174
|
-
ON agent_queue_items(status, created_at);
|
|
175
|
-
`);
|
|
176
|
-
|
|
177
|
-
const sessionColumns = (database
|
|
178
|
-
.prepare("PRAGMA table_info(sessions)")
|
|
179
|
-
.all() as Array<{ name: string }>)
|
|
180
|
-
.map((row) => row.name);
|
|
181
|
-
if (!sessionColumns.includes("graph_version")) {
|
|
182
|
-
database.exec(
|
|
183
|
-
"ALTER TABLE sessions ADD COLUMN graph_version INTEGER NOT NULL DEFAULT 0",
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
if (!sessionColumns.includes("style_sheet")) {
|
|
187
|
-
database.exec("ALTER TABLE sessions ADD COLUMN style_sheet TEXT");
|
|
188
|
-
}
|
|
189
|
-
if (!sessionColumns.includes("style_sheet_enabled")) {
|
|
190
|
-
database.exec(
|
|
191
|
-
"ALTER TABLE sessions ADD COLUMN style_sheet_enabled INTEGER NOT NULL DEFAULT 0",
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const agentSessionColumns = (database
|
|
196
|
-
.prepare("PRAGMA table_info(agent_sessions)")
|
|
197
|
-
.all() as Array<{ name: string }>)
|
|
198
|
-
.map((row) => row.name);
|
|
199
|
-
if (!agentSessionColumns.includes("generation_settings")) {
|
|
200
|
-
database.exec("ALTER TABLE agent_sessions ADD COLUMN generation_settings TEXT NOT NULL DEFAULT '{}'");
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const agentQueueColumns = (database
|
|
204
|
-
.prepare("PRAGMA table_info(agent_queue_items)")
|
|
205
|
-
.all() as Array<{ name: string }>)
|
|
206
|
-
.map((row) => row.name);
|
|
207
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "request_id", "TEXT NOT NULL DEFAULT ''");
|
|
208
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "options", "TEXT NOT NULL DEFAULT '{}'");
|
|
209
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "tool_plan", "TEXT NOT NULL DEFAULT '{}'");
|
|
210
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "position", "INTEGER NOT NULL DEFAULT 0");
|
|
211
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "result_image_ids", "TEXT NOT NULL DEFAULT '[]'");
|
|
212
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "error_code", "TEXT");
|
|
213
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "error_message", "TEXT");
|
|
214
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "started_at", "INTEGER");
|
|
215
|
-
addColumnIfMissing(database, agentQueueColumns, "agent_queue_items", "finished_at", "INTEGER");
|
|
216
|
-
|
|
217
|
-
// ── Prompt Library (schema v4) ──
|
|
218
|
-
database.exec(`
|
|
219
|
-
CREATE TABLE IF NOT EXISTS prompt_folders (
|
|
220
|
-
id TEXT PRIMARY KEY,
|
|
221
|
-
parent_id TEXT NOT NULL,
|
|
222
|
-
name TEXT NOT NULL COLLATE NOCASE,
|
|
223
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
224
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
225
|
-
UNIQUE(parent_id, name)
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
CREATE TABLE IF NOT EXISTS prompts (
|
|
229
|
-
id TEXT PRIMARY KEY,
|
|
230
|
-
folder_id TEXT NOT NULL DEFAULT '__root__',
|
|
231
|
-
name TEXT NOT NULL,
|
|
232
|
-
text TEXT NOT NULL,
|
|
233
|
-
tags TEXT,
|
|
234
|
-
mode TEXT,
|
|
235
|
-
is_favorite INTEGER NOT NULL DEFAULT 0,
|
|
236
|
-
favorited_at INTEGER,
|
|
237
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
238
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
239
|
-
FOREIGN KEY (folder_id) REFERENCES prompt_folders(id) ON DELETE SET DEFAULT
|
|
240
|
-
);
|
|
241
|
-
|
|
242
|
-
CREATE TABLE IF NOT EXISTS gallery_favorites (
|
|
243
|
-
id TEXT PRIMARY KEY,
|
|
244
|
-
browser_id TEXT NOT NULL,
|
|
245
|
-
filename TEXT NOT NULL,
|
|
246
|
-
favorited_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
247
|
-
UNIQUE(browser_id, filename)
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
CREATE TABLE IF NOT EXISTS image_annotations (
|
|
251
|
-
id TEXT PRIMARY KEY,
|
|
252
|
-
browser_id TEXT NOT NULL,
|
|
253
|
-
filename TEXT NOT NULL,
|
|
254
|
-
payload TEXT NOT NULL,
|
|
255
|
-
schema_version INTEGER NOT NULL DEFAULT 1,
|
|
256
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
257
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
258
|
-
UNIQUE(browser_id, filename)
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
CREATE INDEX IF NOT EXISTS idx_image_annotations_filename
|
|
262
|
-
ON image_annotations(filename);
|
|
263
|
-
|
|
264
|
-
INSERT OR IGNORE INTO prompt_folders (id, parent_id, name) VALUES
|
|
265
|
-
('__root__', '__root__', '__root__'),
|
|
266
|
-
('__trash__', '__root__', '__trash__');
|
|
267
|
-
`);
|
|
268
|
-
|
|
269
|
-
const row = database.prepare("SELECT value FROM _meta WHERE key = 'schema_version'").get() as { value?: string } | undefined;
|
|
270
|
-
if (!row) {
|
|
271
|
-
database.prepare("INSERT INTO _meta (key, value) VALUES ('schema_version', '5')").run();
|
|
272
|
-
} else if (row.value !== "5") {
|
|
273
|
-
database
|
|
274
|
-
.prepare("UPDATE _meta SET value = '5' WHERE key = 'schema_version'")
|
|
275
|
-
.run();
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function addColumnIfMissing(
|
|
280
|
-
database: Database.Database,
|
|
281
|
-
columns: readonly string[],
|
|
282
|
-
table: string,
|
|
283
|
-
name: string,
|
|
284
|
-
definition: string,
|
|
285
|
-
) {
|
|
286
|
-
if (columns.includes(name)) return;
|
|
287
|
-
database.exec(`ALTER TABLE ${table} ADD COLUMN ${name} ${definition}`);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export function closeDb() {
|
|
291
|
-
if (db) {
|
|
292
|
-
db.close();
|
|
293
|
-
db = null;
|
|
294
|
-
}
|
|
295
|
-
}
|
package/lib/errInfo.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export interface ErrInfo {
|
|
2
|
-
message: string;
|
|
3
|
-
code: string | undefined;
|
|
4
|
-
status: number | undefined;
|
|
5
|
-
name: string | undefined;
|
|
6
|
-
cause: unknown;
|
|
7
|
-
stack: string | undefined;
|
|
8
|
-
raw: unknown;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Narrow an unknown thrown value to a stable info shape. */
|
|
12
|
-
export function errInfo(e: unknown): ErrInfo {
|
|
13
|
-
if (e instanceof Error) {
|
|
14
|
-
const anyE = e as Error & { code?: unknown; status?: unknown; cause?: unknown };
|
|
15
|
-
return {
|
|
16
|
-
message: e.message,
|
|
17
|
-
code: typeof anyE.code === "string" ? anyE.code : undefined,
|
|
18
|
-
status: typeof anyE.status === "number" ? anyE.status : undefined,
|
|
19
|
-
name: e.name,
|
|
20
|
-
cause: anyE.cause,
|
|
21
|
-
stack: e.stack,
|
|
22
|
-
raw: e,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
if (e && typeof e === "object") {
|
|
26
|
-
const o = e as Record<string, unknown>;
|
|
27
|
-
return {
|
|
28
|
-
message: typeof o.message === "string" ? o.message : String(e),
|
|
29
|
-
code: typeof o.code === "string" ? o.code : undefined,
|
|
30
|
-
status: typeof o.status === "number" ? o.status : undefined,
|
|
31
|
-
name: typeof o.name === "string" ? o.name : undefined,
|
|
32
|
-
cause: o.cause,
|
|
33
|
-
stack: typeof o.stack === "string" ? o.stack : undefined,
|
|
34
|
-
raw: e,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
return { message: String(e), code: undefined, status: undefined, name: undefined, cause: undefined, stack: undefined, raw: e };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Handy for `throw e instanceof Error ? e : asError(e)`. */
|
|
41
|
-
export function asError(e: unknown): Error {
|
|
42
|
-
return e instanceof Error ? e : new Error(typeof e === "string" ? e : JSON.stringify(e));
|
|
43
|
-
}
|