ima2-gen 1.1.8 → 1.1.9
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 +56 -27
- package/bin/commands/annotate.js +137 -0
- package/bin/commands/annotate.ts +118 -0
- package/bin/commands/cancel.js +37 -33
- package/bin/commands/cancel.ts +45 -0
- package/bin/commands/canvas-versions.js +91 -0
- package/bin/commands/canvas-versions.ts +80 -0
- package/bin/commands/cardnews.js +293 -0
- package/bin/commands/cardnews.ts +248 -0
- package/bin/commands/comfy.js +63 -0
- package/bin/commands/comfy.ts +54 -0
- package/bin/commands/config.js +270 -0
- package/bin/commands/config.ts +265 -0
- package/bin/commands/edit.js +97 -72
- package/bin/commands/edit.ts +116 -0
- package/bin/commands/gen.js +140 -118
- package/bin/commands/gen.ts +176 -0
- package/bin/commands/history.js +164 -0
- package/bin/commands/history.ts +145 -0
- package/bin/commands/ls.js +60 -42
- package/bin/commands/ls.ts +60 -0
- package/bin/commands/metadata.js +45 -0
- package/bin/commands/metadata.ts +36 -0
- package/bin/commands/multimode.js +159 -0
- package/bin/commands/multimode.ts +146 -0
- package/bin/commands/node.js +176 -0
- package/bin/commands/node.ts +157 -0
- package/bin/commands/observability.js +201 -0
- package/bin/commands/observability.ts +176 -0
- package/bin/commands/ping.js +26 -20
- package/bin/commands/ping.ts +29 -0
- package/bin/commands/prompt.js +506 -0
- package/bin/commands/prompt.ts +421 -0
- package/bin/commands/ps.js +78 -71
- package/bin/commands/ps.ts +78 -0
- package/bin/commands/session.js +308 -0
- package/bin/commands/session.ts +265 -0
- package/bin/commands/show.js +75 -40
- package/bin/commands/show.ts +69 -0
- package/bin/ima2.js +324 -310
- package/bin/ima2.ts +444 -0
- package/bin/lib/args.js +75 -66
- package/bin/lib/args.ts +73 -0
- package/bin/lib/browser-id.js +15 -0
- package/bin/lib/browser-id.ts +16 -0
- package/bin/lib/client.js +91 -83
- package/bin/lib/client.ts +109 -0
- package/bin/lib/error-hints.js +14 -17
- package/bin/lib/error-hints.ts +23 -0
- package/bin/lib/files.js +26 -28
- package/bin/lib/files.ts +39 -0
- package/bin/lib/output.js +44 -42
- package/bin/lib/output.ts +58 -0
- package/bin/lib/platform.js +60 -56
- package/bin/lib/platform.ts +97 -0
- package/bin/lib/sse.js +73 -0
- package/bin/lib/sse.ts +73 -0
- package/bin/lib/star-prompt.js +69 -76
- package/bin/lib/star-prompt.ts +97 -0
- package/bin/lib/storage-doctor.js +34 -35
- package/bin/lib/storage-doctor.ts +38 -0
- package/config.js +147 -243
- package/config.ts +331 -0
- package/docs/API.md +48 -8
- package/docs/CLI.md +190 -0
- package/docs/FAQ.ko.md +5 -5
- package/docs/FAQ.md +5 -5
- package/docs/README.ja.md +71 -25
- package/docs/README.ko.md +61 -24
- package/docs/README.zh-CN.md +73 -27
- package/lib/assetLifecycle.js +130 -130
- package/lib/assetLifecycle.ts +142 -0
- package/lib/canvasVersionStore.js +135 -153
- package/lib/canvasVersionStore.ts +181 -0
- package/lib/cardNewsGenerator.js +127 -142
- package/lib/cardNewsGenerator.ts +162 -0
- package/lib/cardNewsJobStore.js +78 -84
- package/lib/cardNewsJobStore.ts +107 -0
- package/lib/cardNewsManifestStore.js +88 -93
- package/lib/cardNewsManifestStore.ts +112 -0
- package/lib/cardNewsPlanner.js +157 -152
- package/lib/cardNewsPlanner.ts +180 -0
- package/lib/cardNewsPlannerClient.js +101 -98
- package/lib/cardNewsPlannerClient.ts +114 -0
- package/lib/cardNewsPlannerPrompt.js +56 -56
- package/lib/cardNewsPlannerPrompt.ts +60 -0
- package/lib/cardNewsPlannerSchema.js +231 -223
- package/lib/cardNewsPlannerSchema.ts +259 -0
- package/lib/cardNewsRoleTemplateStore.js +39 -41
- package/lib/cardNewsRoleTemplateStore.ts +47 -0
- package/lib/cardNewsTemplateStore.js +171 -175
- package/lib/cardNewsTemplateStore.ts +210 -0
- package/lib/codexDetect.js +44 -47
- package/lib/codexDetect.ts +69 -0
- package/lib/comfyBridge.js +164 -184
- package/lib/comfyBridge.ts +214 -0
- package/lib/db.js +41 -51
- package/lib/db.ts +166 -0
- package/lib/errorClassify.js +62 -78
- package/lib/errorClassify.ts +100 -0
- package/lib/generationErrors.js +140 -103
- package/lib/generationErrors.ts +125 -0
- package/lib/historyList.js +149 -147
- package/lib/historyList.ts +164 -0
- package/lib/imageMetadata.js +86 -89
- package/lib/imageMetadata.ts +111 -0
- package/lib/imageMetadataStore.js +46 -51
- package/lib/imageMetadataStore.ts +67 -0
- package/lib/imageModels.js +38 -45
- package/lib/imageModels.ts +52 -0
- package/lib/inflight.js +131 -150
- package/lib/inflight.ts +204 -0
- package/lib/localImportStore.js +87 -93
- package/lib/localImportStore.ts +111 -0
- package/lib/logger.js +105 -112
- package/lib/logger.ts +150 -0
- package/lib/nodeStore.js +65 -64
- package/lib/nodeStore.ts +81 -0
- package/lib/oauthLauncher.js +61 -59
- package/lib/oauthLauncher.ts +64 -0
- package/lib/oauthNormalize.js +15 -19
- package/lib/oauthNormalize.ts +30 -0
- package/lib/oauthProxy.js +834 -832
- package/lib/oauthProxy.ts +995 -0
- package/lib/openDirectory.js +41 -40
- package/lib/openDirectory.ts +45 -0
- package/lib/pngInfo.js +18 -20
- package/lib/pngInfo.ts +26 -0
- package/lib/promptImport/curatedSources.js +125 -129
- package/lib/promptImport/curatedSources.ts +139 -0
- package/lib/promptImport/discoveryRegistry.js +185 -203
- package/lib/promptImport/discoveryRegistry.ts +236 -0
- package/lib/promptImport/errors.js +10 -10
- package/lib/promptImport/errors.ts +18 -0
- package/lib/promptImport/githubDiscovery.js +209 -219
- package/lib/promptImport/githubDiscovery.ts +248 -0
- package/lib/promptImport/githubFolder.js +253 -259
- package/lib/promptImport/githubFolder.ts +308 -0
- package/lib/promptImport/githubSource.js +189 -200
- package/lib/promptImport/githubSource.ts +239 -0
- package/lib/promptImport/gptImageHints.js +49 -56
- package/lib/promptImport/gptImageHints.ts +68 -0
- package/lib/promptImport/parsePromptCandidates.js +108 -123
- package/lib/promptImport/parsePromptCandidates.ts +153 -0
- package/lib/promptImport/promptIndex.js +190 -208
- package/lib/promptImport/promptIndex.ts +248 -0
- package/lib/promptImport/rankPromptCandidates.js +46 -43
- package/lib/promptImport/rankPromptCandidates.ts +49 -0
- package/lib/providerOptions.js +31 -0
- package/lib/providerOptions.ts +41 -0
- package/lib/referenceImageCompress.js +51 -62
- package/lib/referenceImageCompress.ts +75 -0
- package/lib/refs.js +93 -81
- package/lib/refs.ts +117 -0
- package/lib/requestLogger.js +32 -38
- package/lib/requestLogger.ts +48 -0
- package/lib/responsesImageAdapter.js +351 -0
- package/lib/responsesImageAdapter.ts +352 -0
- package/lib/runtimePorts.js +71 -73
- package/lib/runtimePorts.ts +93 -0
- package/lib/sessionStore.js +179 -230
- package/lib/sessionStore.ts +272 -0
- package/lib/storageMigration.js +247 -245
- package/lib/storageMigration.ts +284 -0
- package/lib/styleSheet.js +86 -90
- package/lib/styleSheet.ts +128 -0
- package/lib/systemTrash.js +18 -0
- package/lib/systemTrash.ts +20 -0
- package/package.json +26 -10
- package/routes/annotations.js +76 -79
- package/routes/annotations.ts +95 -0
- package/routes/canvasVersions.js +50 -54
- package/routes/canvasVersions.ts +64 -0
- package/routes/cardNews.js +158 -171
- package/routes/cardNews.ts +183 -0
- package/routes/comfy.js +23 -31
- package/routes/comfy.ts +39 -0
- package/routes/edit.js +183 -214
- package/routes/edit.ts +230 -0
- package/routes/generate.js +269 -291
- package/routes/generate.ts +309 -0
- package/routes/health.js +102 -107
- package/routes/health.ts +114 -0
- package/routes/history.js +136 -144
- package/routes/history.ts +153 -0
- package/routes/imageImport.js +27 -27
- package/routes/imageImport.ts +33 -0
- package/routes/index.js +17 -17
- package/routes/index.ts +35 -0
- package/routes/metadata.js +60 -64
- package/routes/metadata.ts +71 -0
- package/routes/multimode.js +228 -263
- package/routes/multimode.ts +280 -0
- package/routes/nodes.js +378 -424
- package/routes/nodes.ts +455 -0
- package/routes/promptImport.js +284 -324
- package/routes/promptImport.ts +354 -0
- package/routes/prompts.js +333 -360
- package/routes/prompts.ts +379 -0
- package/routes/sessions.js +277 -285
- package/routes/sessions.ts +292 -0
- package/routes/storage.js +29 -31
- package/routes/storage.ts +39 -0
- package/server.js +189 -196
- package/server.ts +235 -0
- package/ui/dist/.vite/manifest.json +101 -0
- package/ui/dist/assets/CardNewsWorkspace-BJOCey7Z.js +2 -0
- package/ui/dist/assets/NodeCanvas-BZV40eAE.css +1 -0
- package/ui/dist/assets/NodeCanvas-C3dzYNsk.js +7 -0
- package/ui/dist/assets/PromptImportDialog-Dqu1VpUh.js +2 -0
- package/ui/dist/assets/PromptImportDiscoverySection-Dg8T9X0L.js +1 -0
- package/ui/dist/assets/PromptImportFolderSection-DBaqsFO4.js +1 -0
- package/ui/dist/assets/PromptLibraryPanel-p5QqR97M.js +2 -0
- package/ui/dist/assets/SettingsWorkspace-B5bSAZ6u.js +1 -0
- package/ui/dist/assets/index-C9cXwiWE.js +25 -0
- package/ui/dist/assets/index-CGMIkZXn.css +1 -0
- package/ui/dist/assets/index-Cvld7dUZ.js +1 -0
- package/ui/dist/index.html +2 -2
- package/assets/phase-a-bg-cleanup-test.png +0 -0
- package/assets/screenshot.png +0 -0
- package/assets/screenshots/classic-generate-light.png +0 -0
- package/assets/screenshots/node-graph-branching.png +0 -0
- package/assets/screenshots/settings-oauth-generation.png +0 -0
- package/assets/screenshots/settings-workspace.png +0 -0
- package/assets/screenshots/style-sheet-editor.png +0 -0
- package/integrations/comfyui/ima2_gen_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/integrations/comfyui/ima2_gen_bridge/__pycache__/nodes.cpython-313.pyc +0 -0
- package/ui/dist/assets/index-BDffwmLs.css +0 -1
- package/ui/dist/assets/index-D0fdHLkJ.js +0 -31
- package/ui/dist/assets/index-D0fdHLkJ.js.map +0 -1
package/bin/ima2.js
CHANGED
|
@@ -5,13 +5,12 @@ import { join, dirname } from "path";
|
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { createRequire } from "module";
|
|
7
7
|
import { spawn, execSync } from "child_process";
|
|
8
|
-
import {
|
|
8
|
+
import { homedir } from "os";
|
|
9
9
|
import { openUrl, resolveBin } from "./lib/platform.js";
|
|
10
10
|
import { maybePromptGithubStar } from "./lib/star-prompt.js";
|
|
11
11
|
import { buildStorageDoctorLines } from "./lib/storage-doctor.js";
|
|
12
12
|
import { detectCodexAuth } from "../lib/codexDetect.js";
|
|
13
13
|
import { config as runtimeConfig } from "../config.js";
|
|
14
|
-
|
|
15
14
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
15
|
const ROOT = join(__dirname, "..");
|
|
17
16
|
const HOME = homedir();
|
|
@@ -22,295 +21,275 @@ const requireFromRoot = createRequire(join(ROOT, "package.json"));
|
|
|
22
21
|
const CONFIG_DIR = runtimeConfig.storage.configDir;
|
|
23
22
|
const CONFIG_FILE = runtimeConfig.storage.configFile;
|
|
24
23
|
const LEGACY_CONFIG_FILE = join(ROOT, ".ima2", "config.json");
|
|
25
|
-
|
|
26
24
|
// Load package.json for version
|
|
27
25
|
let pkg = { version: "?", name: "ima2-gen" };
|
|
28
26
|
try {
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
27
|
+
pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf-8"));
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
32
30
|
function loadConfig() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
if (existsSync(CONFIG_FILE)) {
|
|
32
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
33
|
+
}
|
|
34
|
+
// One-time read from legacy location so users who set up on <1.0.4 don't lose auth.
|
|
35
|
+
if (existsSync(LEGACY_CONFIG_FILE)) {
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(readFileSync(LEGACY_CONFIG_FILE, "utf-8"));
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
}
|
|
41
|
+
return {};
|
|
41
42
|
}
|
|
42
|
-
|
|
43
43
|
function saveConfig(config) {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
45
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
46
46
|
}
|
|
47
|
-
|
|
48
47
|
function loadAdvertisement() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
const p = runtimeConfig.storage.advertiseFile;
|
|
49
|
+
if (!existsSync(p))
|
|
50
|
+
return null;
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
56
57
|
}
|
|
57
|
-
|
|
58
58
|
function advertisedServerUrl() {
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
const adv = loadAdvertisement();
|
|
60
|
+
return adv?.backend?.url || adv?.url || (adv?.port ? `http://localhost:${adv.port}` : null);
|
|
61
61
|
}
|
|
62
|
-
|
|
63
62
|
function missingRuntimeDeps() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
const deps = ["express", "better-sqlite3", "openai", "openai-oauth"];
|
|
64
|
+
return deps.filter((dep) => {
|
|
65
|
+
try {
|
|
66
|
+
requireFromRoot.resolve(dep);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
73
|
}
|
|
74
|
-
|
|
75
74
|
async function setup() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
75
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
76
|
+
console.log("\n ima2-gen — GPT Image 2 Generator\n");
|
|
77
|
+
console.log(" Choose authentication method:\n");
|
|
78
|
+
console.log(" 1) API Key — paste your OpenAI API key (paid)");
|
|
79
|
+
console.log(" 2) OAuth — login with ChatGPT account (free)\n");
|
|
80
|
+
const choice = await rl.question(" Enter 1 or 2: ");
|
|
81
|
+
const config = loadConfig();
|
|
82
|
+
if (choice.trim() === "1") {
|
|
83
|
+
const key = await rl.question(" OpenAI API Key: ");
|
|
84
|
+
if (!key.startsWith("sk-")) {
|
|
85
|
+
console.log(" Invalid API key format. Expected sk-...");
|
|
86
|
+
rl.close();
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
config.provider = "api";
|
|
90
|
+
config.apiKey = key.trim();
|
|
91
|
+
saveConfig(config);
|
|
92
|
+
console.log("\n API key saved. Starting server...\n");
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} else {
|
|
122
|
-
const how = auth.probe === "authed" ? "codex CLI" : "auth file";
|
|
123
|
-
console.log(` Existing OAuth session found (${how}).\n`);
|
|
94
|
+
else {
|
|
95
|
+
config.provider = "oauth";
|
|
96
|
+
delete config.apiKey;
|
|
97
|
+
saveConfig(config);
|
|
98
|
+
console.log("\n Starting OAuth login...\n");
|
|
99
|
+
// Check if codex auth exists (file OR keyring via `codex login status`)
|
|
100
|
+
const auth = detectCodexAuth();
|
|
101
|
+
const hasAuth = auth.authed;
|
|
102
|
+
if (!hasAuth) {
|
|
103
|
+
if (auth.platform === "win32") {
|
|
104
|
+
console.log(" Windows note: OpenAI Codex has no documented native installer. Use WSL2 for best results.\n");
|
|
105
|
+
}
|
|
106
|
+
console.log(" Running 'codex login' — follow the browser prompt.\n");
|
|
107
|
+
try {
|
|
108
|
+
execSync(`${resolveBin("npx")} @openai/codex login`, { stdio: "inherit" });
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
console.log("\n Login failed or cancelled. You can retry with 'ima2 serve'.\n");
|
|
112
|
+
rl.close();
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const how = auth.probe === "authed" ? "codex CLI" : "auth file";
|
|
118
|
+
console.log(` Existing OAuth session found (${how}).\n`);
|
|
119
|
+
}
|
|
120
|
+
saveConfig(config);
|
|
121
|
+
console.log(" OAuth configured. Starting server...\n");
|
|
124
122
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
console.log(" OAuth configured. Starting server...\n");
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
rl.close();
|
|
131
|
-
return config;
|
|
123
|
+
rl.close();
|
|
124
|
+
return config;
|
|
132
125
|
}
|
|
133
|
-
|
|
134
126
|
async function serve(serveArgs = []) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
} catch (err) {
|
|
138
|
-
console.error(`[ima2] Star prompt skipped: ${err?.message || err}`);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
let config = loadConfig();
|
|
142
|
-
|
|
143
|
-
if (!config.provider) {
|
|
144
|
-
config = await setup();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Ensure ui/dist exists — if missing, auto-build (dev) or error (installed pkg)
|
|
148
|
-
const distIndex = join(ROOT, "ui", "dist", "index.html");
|
|
149
|
-
if (!existsSync(distIndex)) {
|
|
150
|
-
const hasUiSrc = existsSync(join(ROOT, "ui", "package.json"));
|
|
151
|
-
if (hasUiSrc) {
|
|
152
|
-
console.log("\n ui/dist missing — running 'npm run build' first...\n");
|
|
153
|
-
try {
|
|
154
|
-
execSync(`${resolveBin("npm")} run build`, { stdio: "inherit", cwd: ROOT });
|
|
155
|
-
} catch {
|
|
156
|
-
console.log("\n Build failed. Try: cd ui && npm install && npm run build\n");
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
console.log("\n ui/dist not found and ui/ source is missing.");
|
|
161
|
-
console.log(" This installation appears broken. Reinstall: npm i -g ima2-gen\n");
|
|
162
|
-
process.exit(1);
|
|
127
|
+
try {
|
|
128
|
+
await maybePromptGithubStar();
|
|
163
129
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.error(`[ima2] Star prompt skipped: ${err?.message || err}`);
|
|
132
|
+
}
|
|
133
|
+
let config = loadConfig();
|
|
134
|
+
if (!config.provider) {
|
|
135
|
+
config = await setup();
|
|
136
|
+
}
|
|
137
|
+
// Ensure ui/dist exists — if missing, auto-build (dev) or error (installed pkg)
|
|
138
|
+
const distIndex = join(ROOT, "ui", "dist", "index.html");
|
|
139
|
+
if (!existsSync(distIndex)) {
|
|
140
|
+
const hasUiSrc = existsSync(join(ROOT, "ui", "package.json"));
|
|
141
|
+
if (hasUiSrc) {
|
|
142
|
+
console.log("\n ui/dist missing — running 'npm run build' first...\n");
|
|
143
|
+
try {
|
|
144
|
+
execSync(`${resolveBin("npm")} run build`, { stdio: "inherit", cwd: ROOT });
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
console.log("\n Build failed. Try: cd ui && npm install && npm run build\n");
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.log("\n ui/dist not found and ui/ source is missing.");
|
|
153
|
+
console.log(" This installation appears broken. Reinstall: npm i -g ima2-gen\n");
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const env = { ...process.env };
|
|
158
|
+
const serveDev = serveArgs.includes("--dev");
|
|
159
|
+
if (serveDev) {
|
|
160
|
+
env.IMA2_DEV = "1";
|
|
161
|
+
env.IMA2_LOG_LEVEL = env.IMA2_LOG_LEVEL || "debug";
|
|
162
|
+
}
|
|
163
|
+
if (config.provider === "api" && config.apiKey) {
|
|
164
|
+
env.OPENAI_API_KEY = config.apiKey;
|
|
165
|
+
}
|
|
166
|
+
const serverPath = join(ROOT, "server.js");
|
|
167
|
+
const child = spawn("node", [serverPath], {
|
|
168
|
+
stdio: "inherit",
|
|
169
|
+
env,
|
|
170
|
+
cwd: ROOT,
|
|
171
|
+
});
|
|
172
|
+
child.on("exit", (code) => process.exit(code));
|
|
173
|
+
process.on("SIGINT", () => child.kill("SIGINT"));
|
|
174
|
+
process.on("SIGTERM", () => child.kill("SIGTERM"));
|
|
188
175
|
}
|
|
189
|
-
|
|
190
176
|
async function showStatus() {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
177
|
+
const config = loadConfig();
|
|
178
|
+
console.log(`\n ${pkg.name} v${pkg.version}\n`);
|
|
179
|
+
console.log(` Config file: ${CONFIG_FILE}`);
|
|
180
|
+
console.log(` Exists: ${existsSync(CONFIG_FILE) ? "yes" : "no"}\n`);
|
|
181
|
+
if (config.provider) {
|
|
182
|
+
console.log(` Provider: ${config.provider}`);
|
|
183
|
+
if (config.provider === "api") {
|
|
184
|
+
const key = config.apiKey || "";
|
|
185
|
+
console.log(` API Key: ${key ? key.slice(0, 8) + "..." + key.slice(-4) : "not set"}`);
|
|
186
|
+
}
|
|
187
|
+
console.log("");
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
console.log(" Status: not configured");
|
|
191
|
+
console.log(" Run 'ima2 setup' to configure.\n");
|
|
192
|
+
}
|
|
193
|
+
// Check OAuth auth files + codex CLI probe
|
|
194
|
+
const auth = detectCodexAuth();
|
|
195
|
+
console.log(` OAuth sessions:`);
|
|
196
|
+
console.log(` ${auth.files.codex} ${auth.fileHits.codex ? "✓" : "✗"}`);
|
|
197
|
+
console.log(` ${auth.files.chatgpt} ${auth.fileHits.chatgpt ? "✓" : "✗"}`);
|
|
198
|
+
if (auth.fileHits.xdgCodex) {
|
|
199
|
+
console.log(` ${auth.files.xdgCodex} ✓`);
|
|
200
|
+
}
|
|
201
|
+
const probeLabel = auth.probe === "authed" ? "✓ authed"
|
|
202
|
+
: auth.probe === "unauthed" ? "✗ not logged in"
|
|
203
|
+
: "– codex CLI not found";
|
|
204
|
+
console.log(` codex login status ${probeLabel}`);
|
|
205
|
+
if (auth.platform === "win32") {
|
|
206
|
+
console.log(" (Windows: no native codex installer — use WSL2)");
|
|
201
207
|
}
|
|
202
208
|
console.log("");
|
|
203
|
-
} else {
|
|
204
|
-
console.log(" Status: not configured");
|
|
205
|
-
console.log(" Run 'ima2 setup' to configure.\n");
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Check OAuth auth files + codex CLI probe
|
|
209
|
-
const auth = detectCodexAuth();
|
|
210
|
-
console.log(` OAuth sessions:`);
|
|
211
|
-
console.log(` ${auth.files.codex} ${auth.fileHits.codex ? "✓" : "✗"}`);
|
|
212
|
-
console.log(` ${auth.files.chatgpt} ${auth.fileHits.chatgpt ? "✓" : "✗"}`);
|
|
213
|
-
if (auth.fileHits.xdgCodex) {
|
|
214
|
-
console.log(` ${auth.files.xdgCodex} ✓`);
|
|
215
|
-
}
|
|
216
|
-
const probeLabel =
|
|
217
|
-
auth.probe === "authed" ? "✓ authed"
|
|
218
|
-
: auth.probe === "unauthed" ? "✗ not logged in"
|
|
219
|
-
: "– codex CLI not found";
|
|
220
|
-
console.log(` codex login status ${probeLabel}`);
|
|
221
|
-
if (auth.platform === "win32") {
|
|
222
|
-
console.log(" (Windows: no native codex installer — use WSL2)");
|
|
223
|
-
}
|
|
224
|
-
console.log("");
|
|
225
209
|
}
|
|
226
|
-
|
|
227
210
|
async function doctor() {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
console.log(` ✓ Node.js ${nodeVersion} (>= 20)`);
|
|
238
|
-
ok++;
|
|
239
|
-
} else {
|
|
240
|
-
console.log(` ✗ Node.js ${nodeVersion} (requires >= 20)`);
|
|
241
|
-
fail++;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// package.json exists
|
|
245
|
-
if (existsSync(join(ROOT, "package.json"))) {
|
|
246
|
-
console.log(" ✓ package.json found");
|
|
247
|
-
ok++;
|
|
248
|
-
} else {
|
|
249
|
-
console.log(" ✗ package.json missing");
|
|
250
|
-
fail++;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Runtime dependencies may be hoisted by npm, pnpm, or Yarn. Resolve the
|
|
254
|
-
// packages instead of requiring a package-local node_modules folder.
|
|
255
|
-
const missingDeps = missingRuntimeDeps();
|
|
256
|
-
if (missingDeps.length === 0) {
|
|
257
|
-
console.log(" ✓ runtime dependencies resolvable");
|
|
258
|
-
ok++;
|
|
259
|
-
} else {
|
|
260
|
-
console.log(` ✗ missing runtime dependencies: ${missingDeps.join(", ")}`);
|
|
261
|
-
fail++;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// .env
|
|
265
|
-
if (existsSync(join(ROOT, ".env"))) {
|
|
266
|
-
console.log(" ✓ .env file exists");
|
|
267
|
-
ok++;
|
|
268
|
-
} else {
|
|
269
|
-
console.log(" ⚠ .env file not found (optional — copy from .env.example)");
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Config
|
|
273
|
-
const config = loadConfig();
|
|
274
|
-
if (config.provider) {
|
|
275
|
-
console.log(` ✓ Configured: ${config.provider}`);
|
|
276
|
-
ok++;
|
|
277
|
-
} else {
|
|
278
|
-
console.log(" ⚠ Not configured — run 'ima2 setup'");
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Port availability (simple check)
|
|
282
|
-
const adv = loadAdvertisement();
|
|
283
|
-
console.log(` ℹ Preferred backend port: ${runtimeConfig.server.port}`);
|
|
284
|
-
if (adv?.backend || adv?.port) {
|
|
285
|
-
console.log(` ℹ Backend actual URL: ${adv?.backend?.url || adv?.url || `http://localhost:${adv.port}`}`);
|
|
286
|
-
if (adv?.oauth) {
|
|
287
|
-
console.log(` ℹ OAuth actual URL: ${adv.oauth.url} (${adv.oauth.status || "unknown"})`);
|
|
211
|
+
console.log(`\n ${pkg.name} v${pkg.version} — Doctor\n`);
|
|
212
|
+
let ok = 0;
|
|
213
|
+
let fail = 0;
|
|
214
|
+
// Node version
|
|
215
|
+
const nodeVersion = process.version;
|
|
216
|
+
const nodeMajor = parseInt(nodeVersion.slice(1).split(".")[0]);
|
|
217
|
+
if (nodeMajor >= 20) {
|
|
218
|
+
console.log(` ✓ Node.js ${nodeVersion} (>= 20)`);
|
|
219
|
+
ok++;
|
|
288
220
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
221
|
+
else {
|
|
222
|
+
console.log(` ✗ Node.js ${nodeVersion} (requires >= 20)`);
|
|
223
|
+
fail++;
|
|
224
|
+
}
|
|
225
|
+
// package.json exists
|
|
226
|
+
if (existsSync(join(ROOT, "package.json"))) {
|
|
227
|
+
console.log(" ✓ package.json found");
|
|
228
|
+
ok++;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
console.log(" ✗ package.json missing");
|
|
232
|
+
fail++;
|
|
233
|
+
}
|
|
234
|
+
// Runtime dependencies may be hoisted by npm, pnpm, or Yarn. Resolve the
|
|
235
|
+
// packages instead of requiring a package-local node_modules folder.
|
|
236
|
+
const missingDeps = missingRuntimeDeps();
|
|
237
|
+
if (missingDeps.length === 0) {
|
|
238
|
+
console.log(" ✓ runtime dependencies resolvable");
|
|
239
|
+
ok++;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
console.log(` ✗ missing runtime dependencies: ${missingDeps.join(", ")}`);
|
|
243
|
+
fail++;
|
|
244
|
+
}
|
|
245
|
+
// .env
|
|
246
|
+
if (existsSync(join(ROOT, ".env"))) {
|
|
247
|
+
console.log(" ✓ .env file exists");
|
|
248
|
+
ok++;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
console.log(" ⚠ .env file not found (optional — copy from .env.example)");
|
|
252
|
+
}
|
|
253
|
+
// Config
|
|
254
|
+
const config = loadConfig();
|
|
255
|
+
if (config.provider) {
|
|
256
|
+
console.log(` ✓ Configured: ${config.provider}`);
|
|
257
|
+
ok++;
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
console.log(" ⚠ Not configured — run 'ima2 setup'");
|
|
261
|
+
}
|
|
262
|
+
// Port availability (simple check)
|
|
263
|
+
const adv = loadAdvertisement();
|
|
264
|
+
console.log(` ℹ Preferred backend port: ${runtimeConfig.server.port}`);
|
|
265
|
+
if (adv?.backend || adv?.port) {
|
|
266
|
+
console.log(` ℹ Backend actual URL: ${adv?.backend?.url || adv?.url || `http://localhost:${adv.port}`}`);
|
|
267
|
+
if (adv?.oauth) {
|
|
268
|
+
console.log(` ℹ OAuth actual URL: ${adv.oauth.url} (${adv.oauth.status || "unknown"})`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const storageLines = await buildStorageDoctorLines({
|
|
272
|
+
rootDir: ROOT,
|
|
273
|
+
config: runtimeConfig,
|
|
274
|
+
});
|
|
275
|
+
console.log("");
|
|
276
|
+
for (const line of storageLines)
|
|
277
|
+
console.log(line);
|
|
278
|
+
console.log(`\n ${ok} passed, ${fail} failed\n`);
|
|
279
|
+
process.exit(fail > 0 ? 1 : 0);
|
|
300
280
|
}
|
|
301
|
-
|
|
302
281
|
function openBrowser() {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}
|
|
282
|
+
const url = advertisedServerUrl() || `http://localhost:${runtimeConfig.server.port}`;
|
|
283
|
+
const res = openUrl(url);
|
|
284
|
+
if (res.ok) {
|
|
285
|
+
console.log(`\n Opening ${url} ...\n`);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
console.log(`\n Could not open browser. Visit: ${url}\n`);
|
|
289
|
+
}
|
|
310
290
|
}
|
|
311
|
-
|
|
312
291
|
function showHelp() {
|
|
313
|
-
|
|
292
|
+
console.log(`
|
|
314
293
|
${pkg.name} v${pkg.version} — GPT Image 2 Generator
|
|
315
294
|
|
|
316
295
|
Usage: ima2 <command> [options]
|
|
@@ -328,8 +307,24 @@ function showHelp() {
|
|
|
328
307
|
edit <file> Edit an existing image (ima2 edit --help)
|
|
329
308
|
ls List recent history (ima2 ls --help)
|
|
330
309
|
show <name> Show one history item (ima2 show --help)
|
|
310
|
+
session <sub> Session/graph CRUD (ima2 session --help)
|
|
311
|
+
history <sub> History write-ops (ima2 history --help)
|
|
312
|
+
prompt <sub> Prompt library + folders + import (ima2 prompt --help)
|
|
313
|
+
multimode <prompt> Multi-image SSE generation (ima2 multimode --help)
|
|
314
|
+
node <sub> Node-mode generate/show (ima2 node --help)
|
|
315
|
+
annotate <sub> Image annotations CRUD (ima2 annotate --help)
|
|
316
|
+
canvas-versions <sub> Canvas version save/update (ima2 canvas-versions --help)
|
|
317
|
+
metadata <file> Read embedded metadata
|
|
318
|
+
comfy <sub> ComfyUI workflow export (ima2 comfy --help)
|
|
319
|
+
cardnews <sub> Card News templates/jobs/export (ima2 cardnews --help)
|
|
331
320
|
ps List active jobs (ima2 ps --help)
|
|
332
321
|
cancel <id> Mark an in-flight job canceled (ima2 cancel --help)
|
|
322
|
+
inflight <sub> Inflight jobs (ls / rm) (ima2 inflight --help)
|
|
323
|
+
storage <sub> Storage status / open-dir (ima2 storage --help)
|
|
324
|
+
billing API usage / quota
|
|
325
|
+
providers Configured providers
|
|
326
|
+
oauth <sub> OAuth proxy status (ima2 oauth --help)
|
|
327
|
+
config <sub> Config get/set/ls/path/rm (ima2 config --help)
|
|
333
328
|
ping Ping running server / check health
|
|
334
329
|
|
|
335
330
|
Options:
|
|
@@ -345,62 +340,81 @@ function showHelp() {
|
|
|
345
340
|
ima2 ping Health check
|
|
346
341
|
`);
|
|
347
342
|
}
|
|
348
|
-
|
|
349
343
|
// ── CLI ──
|
|
350
344
|
const args = process.argv.slice(2);
|
|
351
345
|
const command = args[0];
|
|
352
|
-
|
|
353
346
|
if (args.includes("-v") || args.includes("--version")) {
|
|
354
|
-
|
|
355
|
-
|
|
347
|
+
console.log(pkg.version);
|
|
348
|
+
process.exit(0);
|
|
356
349
|
}
|
|
357
|
-
|
|
358
350
|
if ((!command || args.includes("-h") || args.includes("--help"))
|
|
359
|
-
&& !["gen", "edit", "ls", "show", "ps", "cancel", "ping"].includes(command)) {
|
|
360
|
-
|
|
361
|
-
|
|
351
|
+
&& !["gen", "edit", "ls", "show", "ps", "cancel", "session", "history", "prompt", "multimode", "node", "annotate", "canvas-versions", "metadata", "comfy", "cardnews", "inflight", "storage", "billing", "providers", "oauth", "config", "ping"].includes(command)) {
|
|
352
|
+
showHelp();
|
|
353
|
+
process.exit(command ? 0 : 1);
|
|
362
354
|
}
|
|
363
|
-
|
|
364
355
|
switch (command) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
356
|
+
case "serve":
|
|
357
|
+
serve(args.slice(1));
|
|
358
|
+
break;
|
|
359
|
+
case "setup":
|
|
360
|
+
case "login":
|
|
361
|
+
setup().then(() => console.log(" Done. Run 'ima2 serve' to start."));
|
|
362
|
+
break;
|
|
363
|
+
case "status":
|
|
364
|
+
showStatus();
|
|
365
|
+
break;
|
|
366
|
+
case "doctor":
|
|
367
|
+
await doctor();
|
|
368
|
+
break;
|
|
369
|
+
case "open":
|
|
370
|
+
openBrowser();
|
|
371
|
+
break;
|
|
372
|
+
case "reset":
|
|
373
|
+
if (existsSync(CONFIG_FILE)) {
|
|
374
|
+
writeFileSync(CONFIG_FILE, "{}");
|
|
375
|
+
console.log(" Config reset. Run 'ima2 serve' to reconfigure.");
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
console.log(" No config to reset.");
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
case "gen":
|
|
382
|
+
case "edit":
|
|
383
|
+
case "ls":
|
|
384
|
+
case "show":
|
|
385
|
+
case "ps":
|
|
386
|
+
case "cancel":
|
|
387
|
+
case "session":
|
|
388
|
+
case "history":
|
|
389
|
+
case "prompt":
|
|
390
|
+
case "multimode":
|
|
391
|
+
case "node":
|
|
392
|
+
case "annotate":
|
|
393
|
+
case "canvas-versions":
|
|
394
|
+
case "metadata":
|
|
395
|
+
case "comfy":
|
|
396
|
+
case "cardnews":
|
|
397
|
+
case "config":
|
|
398
|
+
case "ping": {
|
|
399
|
+
const { setCliVersion } = await import("./lib/client.js");
|
|
400
|
+
setCliVersion(pkg.version);
|
|
401
|
+
const mod = await import(`./commands/${command}.js`);
|
|
402
|
+
await mod.default(args.slice(1));
|
|
403
|
+
break;
|
|
387
404
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
console.log(` Unknown command: "${command}"`);
|
|
404
|
-
console.log(" Run 'ima2 --help' for usage.\n");
|
|
405
|
-
process.exit(1);
|
|
405
|
+
case "storage":
|
|
406
|
+
case "billing":
|
|
407
|
+
case "providers":
|
|
408
|
+
case "oauth":
|
|
409
|
+
case "inflight": {
|
|
410
|
+
const { setCliVersion } = await import("./lib/client.js");
|
|
411
|
+
setCliVersion(pkg.version);
|
|
412
|
+
const mod = await import("./commands/observability.js");
|
|
413
|
+
await mod.default([command, ...args.slice(1)]);
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
default:
|
|
417
|
+
console.log(` Unknown command: "${command}"`);
|
|
418
|
+
console.log(" Run 'ima2 --help' for usage.\n");
|
|
419
|
+
process.exit(1);
|
|
406
420
|
}
|