@telepat/ideon 0.1.18 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ideon.js +365 -1036
- package/package.json +2 -1
package/dist/ideon.js
CHANGED
|
@@ -111,7 +111,7 @@ var modelSettingsSchema = z.object({
|
|
|
111
111
|
topP: z.number().min(0).max(1).default(1)
|
|
112
112
|
});
|
|
113
113
|
var baseT2ISettingsSchema = z.object({
|
|
114
|
-
modelId: z.string().default("
|
|
114
|
+
modelId: z.string().default("flux"),
|
|
115
115
|
inputOverrides: z.record(z.string(), z.unknown()).default({})
|
|
116
116
|
});
|
|
117
117
|
var notificationsSettingsSchema = z.object({
|
|
@@ -1362,7 +1362,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
1362
1362
|
// package.json
|
|
1363
1363
|
var package_default = {
|
|
1364
1364
|
name: "@telepat/ideon",
|
|
1365
|
-
version: "0.1.
|
|
1365
|
+
version: "0.1.19",
|
|
1366
1366
|
description: "CLI for generating rich articles and images from ideas.",
|
|
1367
1367
|
type: "module",
|
|
1368
1368
|
repository: {
|
|
@@ -1420,6 +1420,7 @@ var package_default = {
|
|
|
1420
1420
|
dependencies: {
|
|
1421
1421
|
"@ant-design/icons": "^6.1.1",
|
|
1422
1422
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
1423
|
+
"@telepat/limn": "^0.1.5",
|
|
1423
1424
|
antd: "^6.3.4",
|
|
1424
1425
|
commander: "^14.0.1",
|
|
1425
1426
|
"env-paths": "^3.0.0",
|
|
@@ -2874,65 +2875,8 @@ function normalizeGeneratedSection(content, label2) {
|
|
|
2874
2875
|
return normalized.replace(/^```(?:markdown)?\s*/i, "").replace(/```\s*$/i, "").trim();
|
|
2875
2876
|
}
|
|
2876
2877
|
|
|
2877
|
-
// src/
|
|
2878
|
-
import
|
|
2879
|
-
var ReplicateClient = class {
|
|
2880
|
-
client;
|
|
2881
|
-
constructor(apiToken) {
|
|
2882
|
-
this.client = new Replicate({ auth: apiToken });
|
|
2883
|
-
}
|
|
2884
|
-
async runModel(model, input, options = {}) {
|
|
2885
|
-
let lastError = null;
|
|
2886
|
-
const startedAtMs = Date.now();
|
|
2887
|
-
let attempts = 0;
|
|
2888
|
-
let retries = 0;
|
|
2889
|
-
let retryBackoffMs = 0;
|
|
2890
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2891
|
-
attempts = attempt + 1;
|
|
2892
|
-
try {
|
|
2893
|
-
const output = await this.client.run(model, { input });
|
|
2894
|
-
options.onMetrics?.({
|
|
2895
|
-
durationMs: Date.now() - startedAtMs,
|
|
2896
|
-
attempts,
|
|
2897
|
-
retries,
|
|
2898
|
-
retryBackoffMs,
|
|
2899
|
-
modelId: model
|
|
2900
|
-
});
|
|
2901
|
-
return output;
|
|
2902
|
-
} catch (error) {
|
|
2903
|
-
lastError = error instanceof Error ? error : new Error("Unknown Replicate client error.");
|
|
2904
|
-
if (attempt < 2 && shouldRetryError(lastError)) {
|
|
2905
|
-
const backoff = backoffMs(attempt);
|
|
2906
|
-
retries += 1;
|
|
2907
|
-
retryBackoffMs += backoff;
|
|
2908
|
-
options.onRetry?.({
|
|
2909
|
-
attempts,
|
|
2910
|
-
retries,
|
|
2911
|
-
retryBackoffMs,
|
|
2912
|
-
backoffMs: backoff,
|
|
2913
|
-
errorMessage: lastError.message,
|
|
2914
|
-
modelId: model
|
|
2915
|
-
});
|
|
2916
|
-
await wait(backoff);
|
|
2917
|
-
continue;
|
|
2918
|
-
}
|
|
2919
|
-
break;
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
throw lastError ?? new Error("Replicate request failed.");
|
|
2923
|
-
}
|
|
2924
|
-
};
|
|
2925
|
-
function shouldRetryError(error) {
|
|
2926
|
-
return /timeout|network|fetch|temporarily|rate|429|500|502|503|504/i.test(error.message);
|
|
2927
|
-
}
|
|
2928
|
-
function backoffMs(attempt) {
|
|
2929
|
-
return 500 * (attempt + 1);
|
|
2930
|
-
}
|
|
2931
|
-
function wait(ms) {
|
|
2932
|
-
return new Promise((resolve) => {
|
|
2933
|
-
setTimeout(resolve, ms);
|
|
2934
|
-
});
|
|
2935
|
-
}
|
|
2878
|
+
// src/pipeline/runner.ts
|
|
2879
|
+
import { Limn } from "@telepat/limn";
|
|
2936
2880
|
|
|
2937
2881
|
// src/images/renderImages.ts
|
|
2938
2882
|
import { writeFile as writeFile4 } from "fs/promises";
|
|
@@ -2961,11 +2905,11 @@ function buildImagePromptMessages(plan, image, section) {
|
|
|
2961
2905
|
`Section excerpt: ${section.body.slice(0, 500)}`
|
|
2962
2906
|
);
|
|
2963
2907
|
}
|
|
2964
|
-
userLines.push("Write one strong prompt
|
|
2908
|
+
userLines.push("Write one strong visual prompt describing the image in natural language.");
|
|
2965
2909
|
return [
|
|
2966
2910
|
{
|
|
2967
2911
|
role: "system",
|
|
2968
|
-
content: "You write concise, high-signal prompts for text-to-image models. The prompt should be vivid
|
|
2912
|
+
content: "You write concise, high-signal prompts for text-to-image models. The prompt should be vivid and compositionally clear. Do not include any words, letters, numbers, logos, watermarks, or signage in the image, unless text in the image was explicitly requested. Return only the requested JSON."
|
|
2969
2913
|
},
|
|
2970
2914
|
{
|
|
2971
2915
|
role: "user",
|
|
@@ -2974,468 +2918,6 @@ function buildImagePromptMessages(plan, image, section) {
|
|
|
2974
2918
|
];
|
|
2975
2919
|
}
|
|
2976
2920
|
|
|
2977
|
-
// src/models/t2i/definitions/bytedance__seedream-4.json
|
|
2978
|
-
var bytedance_seedream_4_default = {
|
|
2979
|
-
modelId: "bytedance/seedream-4",
|
|
2980
|
-
provider: "replicate",
|
|
2981
|
-
displayName: "Seedream 4",
|
|
2982
|
-
category: "image-generation",
|
|
2983
|
-
pricingSourceUrl: "https://replicate.com/bytedance/seedream-4",
|
|
2984
|
-
pricingNotes: "Replicate pricing: $0.03 per output image.",
|
|
2985
|
-
pricing: {
|
|
2986
|
-
usdPerSecond: null,
|
|
2987
|
-
usdPer1kInputTokens: null,
|
|
2988
|
-
usdPer1kOutputTokens: null
|
|
2989
|
-
},
|
|
2990
|
-
pricingRules: {
|
|
2991
|
-
basis: "output_image_count",
|
|
2992
|
-
usdPerImage: 0.03
|
|
2993
|
-
},
|
|
2994
|
-
inputOptions: {
|
|
2995
|
-
userConfigurable: ["size", "sequential_image_generation", "max_images", "enhance_prompt"],
|
|
2996
|
-
pipelineManaged: ["prompt", "aspect_ratio"],
|
|
2997
|
-
fields: {
|
|
2998
|
-
size: {
|
|
2999
|
-
type: "string",
|
|
3000
|
-
default: "2K",
|
|
3001
|
-
enum: ["1K", "2K", "4K"]
|
|
3002
|
-
},
|
|
3003
|
-
sequential_image_generation: {
|
|
3004
|
-
type: "string",
|
|
3005
|
-
default: "disabled",
|
|
3006
|
-
enum: ["disabled", "auto"]
|
|
3007
|
-
},
|
|
3008
|
-
max_images: {
|
|
3009
|
-
type: "integer",
|
|
3010
|
-
default: 1,
|
|
3011
|
-
minimum: 1,
|
|
3012
|
-
maximum: 15
|
|
3013
|
-
},
|
|
3014
|
-
enhance_prompt: {
|
|
3015
|
-
type: "boolean",
|
|
3016
|
-
default: true
|
|
3017
|
-
},
|
|
3018
|
-
prompt: {
|
|
3019
|
-
type: "string",
|
|
3020
|
-
required: true
|
|
3021
|
-
},
|
|
3022
|
-
aspect_ratio: {
|
|
3023
|
-
type: "string",
|
|
3024
|
-
required: true,
|
|
3025
|
-
enum: ["1:1", "16:9", "9:16"]
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
}
|
|
3029
|
-
};
|
|
3030
|
-
|
|
3031
|
-
// src/models/t2i/definitions/black-forest-labs__flux-2-pro.json
|
|
3032
|
-
var black_forest_labs_flux_2_pro_default = {
|
|
3033
|
-
modelId: "black-forest-labs/flux-2-pro",
|
|
3034
|
-
provider: "replicate",
|
|
3035
|
-
displayName: "FLUX 2 Pro",
|
|
3036
|
-
category: "image-generation",
|
|
3037
|
-
pricingSourceUrl: "https://replicate.com/black-forest-labs/flux-2-pro",
|
|
3038
|
-
pricingNotes: "Replicate lists multi-property pricing for run and input/output megapixels; this model currently uses best-effort cost fallback when exact rule mapping is unavailable.",
|
|
3039
|
-
pricing: {
|
|
3040
|
-
usdPerSecond: null,
|
|
3041
|
-
usdPer1kInputTokens: null,
|
|
3042
|
-
usdPer1kOutputTokens: null
|
|
3043
|
-
},
|
|
3044
|
-
inputOptions: {
|
|
3045
|
-
userConfigurable: ["safety_tolerance", "seed", "output_format", "output_quality"],
|
|
3046
|
-
pipelineManaged: ["prompt", "aspect_ratio", "width", "height"],
|
|
3047
|
-
fields: {
|
|
3048
|
-
safety_tolerance: {
|
|
3049
|
-
type: "integer",
|
|
3050
|
-
default: 2,
|
|
3051
|
-
minimum: 1,
|
|
3052
|
-
maximum: 5
|
|
3053
|
-
},
|
|
3054
|
-
seed: {
|
|
3055
|
-
type: "integer",
|
|
3056
|
-
nullable: true
|
|
3057
|
-
},
|
|
3058
|
-
output_format: {
|
|
3059
|
-
type: "string",
|
|
3060
|
-
default: "webp",
|
|
3061
|
-
enum: ["webp", "png", "jpg", "jpeg"]
|
|
3062
|
-
},
|
|
3063
|
-
output_quality: {
|
|
3064
|
-
type: "integer",
|
|
3065
|
-
default: 80,
|
|
3066
|
-
minimum: 0,
|
|
3067
|
-
maximum: 100
|
|
3068
|
-
},
|
|
3069
|
-
prompt: {
|
|
3070
|
-
type: "string",
|
|
3071
|
-
required: true
|
|
3072
|
-
},
|
|
3073
|
-
aspect_ratio: {
|
|
3074
|
-
type: "string",
|
|
3075
|
-
required: true,
|
|
3076
|
-
enum: ["custom"]
|
|
3077
|
-
},
|
|
3078
|
-
width: {
|
|
3079
|
-
type: "integer",
|
|
3080
|
-
required: true,
|
|
3081
|
-
minimum: 256,
|
|
3082
|
-
maximum: 2048
|
|
3083
|
-
},
|
|
3084
|
-
height: {
|
|
3085
|
-
type: "integer",
|
|
3086
|
-
required: true,
|
|
3087
|
-
minimum: 256,
|
|
3088
|
-
maximum: 2048
|
|
3089
|
-
}
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
};
|
|
3093
|
-
|
|
3094
|
-
// src/models/t2i/definitions/black-forest-labs__flux-schnell.json
|
|
3095
|
-
var black_forest_labs_flux_schnell_default = {
|
|
3096
|
-
modelId: "black-forest-labs/flux-schnell",
|
|
3097
|
-
provider: "replicate",
|
|
3098
|
-
displayName: "FLUX Schnell",
|
|
3099
|
-
category: "image-generation",
|
|
3100
|
-
pricingSourceUrl: "https://replicate.com/black-forest-labs/flux-schnell",
|
|
3101
|
-
pricingNotes: "Replicate pricing: $3 per 1000 output images.",
|
|
3102
|
-
pricing: {
|
|
3103
|
-
usdPerSecond: null,
|
|
3104
|
-
usdPer1kInputTokens: null,
|
|
3105
|
-
usdPer1kOutputTokens: null
|
|
3106
|
-
},
|
|
3107
|
-
pricingRules: {
|
|
3108
|
-
basis: "output_image_count",
|
|
3109
|
-
usdPerImage: 3e-3
|
|
3110
|
-
},
|
|
3111
|
-
inputOptions: {
|
|
3112
|
-
userConfigurable: ["num_outputs", "num_inference_steps", "seed", "output_format", "output_quality", "disable_safety_checker", "go_fast", "megapixels"],
|
|
3113
|
-
pipelineManaged: ["prompt", "aspect_ratio"],
|
|
3114
|
-
fields: {
|
|
3115
|
-
num_outputs: {
|
|
3116
|
-
type: "integer",
|
|
3117
|
-
default: 1,
|
|
3118
|
-
minimum: 1,
|
|
3119
|
-
maximum: 4
|
|
3120
|
-
},
|
|
3121
|
-
num_inference_steps: {
|
|
3122
|
-
type: "integer",
|
|
3123
|
-
default: 4,
|
|
3124
|
-
minimum: 1,
|
|
3125
|
-
maximum: 4
|
|
3126
|
-
},
|
|
3127
|
-
seed: {
|
|
3128
|
-
type: "integer",
|
|
3129
|
-
nullable: true
|
|
3130
|
-
},
|
|
3131
|
-
output_format: {
|
|
3132
|
-
type: "string",
|
|
3133
|
-
default: "webp",
|
|
3134
|
-
enum: ["webp", "jpg", "png"]
|
|
3135
|
-
},
|
|
3136
|
-
output_quality: {
|
|
3137
|
-
type: "integer",
|
|
3138
|
-
default: 80,
|
|
3139
|
-
minimum: 0,
|
|
3140
|
-
maximum: 100
|
|
3141
|
-
},
|
|
3142
|
-
disable_safety_checker: {
|
|
3143
|
-
type: "boolean",
|
|
3144
|
-
default: false
|
|
3145
|
-
},
|
|
3146
|
-
go_fast: {
|
|
3147
|
-
type: "boolean",
|
|
3148
|
-
default: true
|
|
3149
|
-
},
|
|
3150
|
-
megapixels: {
|
|
3151
|
-
type: "string",
|
|
3152
|
-
default: "1",
|
|
3153
|
-
allowAnyString: true,
|
|
3154
|
-
recommendedValues: ["1"]
|
|
3155
|
-
},
|
|
3156
|
-
prompt: {
|
|
3157
|
-
type: "string",
|
|
3158
|
-
required: true
|
|
3159
|
-
},
|
|
3160
|
-
aspect_ratio: {
|
|
3161
|
-
type: "string",
|
|
3162
|
-
required: true,
|
|
3163
|
-
enum: ["1:1", "16:9", "9:16"]
|
|
3164
|
-
}
|
|
3165
|
-
}
|
|
3166
|
-
}
|
|
3167
|
-
};
|
|
3168
|
-
|
|
3169
|
-
// src/models/t2i/definitions/google__nano-banana-pro.json
|
|
3170
|
-
var google_nano_banana_pro_default = {
|
|
3171
|
-
modelId: "google/nano-banana-pro",
|
|
3172
|
-
provider: "replicate",
|
|
3173
|
-
displayName: "Nano Banana Pro",
|
|
3174
|
-
category: "image-generation",
|
|
3175
|
-
pricingSourceUrl: "https://replicate.com/google/nano-banana-pro",
|
|
3176
|
-
pricingNotes: "Replicate pricing by target resolution: 1K/2K=$0.15 per output image, 4K=$0.30 per output image, fallback tier=$0.035 per output image.",
|
|
3177
|
-
pricing: {
|
|
3178
|
-
usdPerSecond: null,
|
|
3179
|
-
usdPer1kInputTokens: null,
|
|
3180
|
-
usdPer1kOutputTokens: null
|
|
3181
|
-
},
|
|
3182
|
-
pricingRules: {
|
|
3183
|
-
basis: "output_image_resolution",
|
|
3184
|
-
tiers: [
|
|
3185
|
-
{
|
|
3186
|
-
resolution: "1K",
|
|
3187
|
-
usdPerImage: 0.15
|
|
3188
|
-
},
|
|
3189
|
-
{
|
|
3190
|
-
resolution: "2K",
|
|
3191
|
-
usdPerImage: 0.15
|
|
3192
|
-
},
|
|
3193
|
-
{
|
|
3194
|
-
resolution: "4K",
|
|
3195
|
-
usdPerImage: 0.3
|
|
3196
|
-
},
|
|
3197
|
-
{
|
|
3198
|
-
resolution: "fallback",
|
|
3199
|
-
usdPerImage: 0.035
|
|
3200
|
-
}
|
|
3201
|
-
]
|
|
3202
|
-
},
|
|
3203
|
-
inputOptions: {
|
|
3204
|
-
userConfigurable: ["resolution", "output_format", "safety_filter_level", "allow_fallback_model"],
|
|
3205
|
-
pipelineManaged: ["prompt", "aspect_ratio"],
|
|
3206
|
-
fields: {
|
|
3207
|
-
resolution: {
|
|
3208
|
-
type: "string",
|
|
3209
|
-
default: "2K",
|
|
3210
|
-
enum: ["1K", "2K", "4K"]
|
|
3211
|
-
},
|
|
3212
|
-
output_format: {
|
|
3213
|
-
type: "string",
|
|
3214
|
-
default: "jpg",
|
|
3215
|
-
enum: ["jpg", "png", "webp"]
|
|
3216
|
-
},
|
|
3217
|
-
safety_filter_level: {
|
|
3218
|
-
type: "string",
|
|
3219
|
-
default: "block_only_high",
|
|
3220
|
-
enum: ["block_low_and_above", "block_medium_and_above", "block_only_high"]
|
|
3221
|
-
},
|
|
3222
|
-
allow_fallback_model: {
|
|
3223
|
-
type: "boolean",
|
|
3224
|
-
default: false
|
|
3225
|
-
},
|
|
3226
|
-
prompt: {
|
|
3227
|
-
type: "string",
|
|
3228
|
-
required: true
|
|
3229
|
-
},
|
|
3230
|
-
aspect_ratio: {
|
|
3231
|
-
type: "string",
|
|
3232
|
-
required: true,
|
|
3233
|
-
enum: ["1:1", "16:9", "9:16"]
|
|
3234
|
-
}
|
|
3235
|
-
}
|
|
3236
|
-
}
|
|
3237
|
-
};
|
|
3238
|
-
|
|
3239
|
-
// src/models/t2i/definitions/prunaai__z-image-turbo.json
|
|
3240
|
-
var prunaai_z_image_turbo_default = {
|
|
3241
|
-
modelId: "prunaai/z-image-turbo",
|
|
3242
|
-
provider: "replicate",
|
|
3243
|
-
displayName: "Z Image Turbo",
|
|
3244
|
-
category: "image-generation",
|
|
3245
|
-
pricingSourceUrl: "https://replicate.com/prunaai/z-image-turbo",
|
|
3246
|
-
pricingNotes: "Replicate pricing is tiered by output image resolution (megapixels).",
|
|
3247
|
-
pricing: {
|
|
3248
|
-
usdPerSecond: null,
|
|
3249
|
-
usdPer1kInputTokens: null,
|
|
3250
|
-
usdPer1kOutputTokens: null
|
|
3251
|
-
},
|
|
3252
|
-
pricingRules: {
|
|
3253
|
-
basis: "output_image_megapixels",
|
|
3254
|
-
tiers: [
|
|
3255
|
-
{
|
|
3256
|
-
maxMegapixels: 0.5,
|
|
3257
|
-
usdPerImage: 25e-4
|
|
3258
|
-
},
|
|
3259
|
-
{
|
|
3260
|
-
maxMegapixels: 1,
|
|
3261
|
-
usdPerImage: 5e-3
|
|
3262
|
-
},
|
|
3263
|
-
{
|
|
3264
|
-
maxMegapixels: 2,
|
|
3265
|
-
usdPerImage: 0.01
|
|
3266
|
-
},
|
|
3267
|
-
{
|
|
3268
|
-
maxMegapixels: 3,
|
|
3269
|
-
usdPerImage: 0.015
|
|
3270
|
-
},
|
|
3271
|
-
{
|
|
3272
|
-
maxMegapixels: 4,
|
|
3273
|
-
usdPerImage: 0.02
|
|
3274
|
-
}
|
|
3275
|
-
]
|
|
3276
|
-
},
|
|
3277
|
-
inputOptions: {
|
|
3278
|
-
userConfigurable: ["num_inference_steps", "guidance_scale", "seed", "go_fast", "output_format", "output_quality"],
|
|
3279
|
-
pipelineManaged: ["prompt", "width", "height"],
|
|
3280
|
-
fields: {
|
|
3281
|
-
num_inference_steps: {
|
|
3282
|
-
type: "integer",
|
|
3283
|
-
default: 8,
|
|
3284
|
-
minimum: 1,
|
|
3285
|
-
maximum: 50
|
|
3286
|
-
},
|
|
3287
|
-
guidance_scale: {
|
|
3288
|
-
type: "number",
|
|
3289
|
-
default: 0,
|
|
3290
|
-
minimum: 0,
|
|
3291
|
-
maximum: 20
|
|
3292
|
-
},
|
|
3293
|
-
seed: {
|
|
3294
|
-
type: "integer",
|
|
3295
|
-
nullable: true
|
|
3296
|
-
},
|
|
3297
|
-
go_fast: {
|
|
3298
|
-
type: "boolean",
|
|
3299
|
-
default: false
|
|
3300
|
-
},
|
|
3301
|
-
output_format: {
|
|
3302
|
-
type: "string",
|
|
3303
|
-
default: "jpg",
|
|
3304
|
-
enum: ["jpg", "jpeg", "png", "webp"]
|
|
3305
|
-
},
|
|
3306
|
-
output_quality: {
|
|
3307
|
-
type: "integer",
|
|
3308
|
-
default: 80,
|
|
3309
|
-
minimum: 0,
|
|
3310
|
-
maximum: 100
|
|
3311
|
-
},
|
|
3312
|
-
prompt: {
|
|
3313
|
-
type: "string",
|
|
3314
|
-
required: true
|
|
3315
|
-
},
|
|
3316
|
-
width: {
|
|
3317
|
-
type: "integer",
|
|
3318
|
-
required: true,
|
|
3319
|
-
minimum: 64,
|
|
3320
|
-
maximum: 2048
|
|
3321
|
-
},
|
|
3322
|
-
height: {
|
|
3323
|
-
type: "integer",
|
|
3324
|
-
required: true,
|
|
3325
|
-
minimum: 64,
|
|
3326
|
-
maximum: 2048
|
|
3327
|
-
}
|
|
3328
|
-
}
|
|
3329
|
-
}
|
|
3330
|
-
};
|
|
3331
|
-
|
|
3332
|
-
// src/models/t2i/registry.ts
|
|
3333
|
-
var modelDefinitions = [
|
|
3334
|
-
black_forest_labs_flux_schnell_default,
|
|
3335
|
-
black_forest_labs_flux_2_pro_default,
|
|
3336
|
-
bytedance_seedream_4_default,
|
|
3337
|
-
google_nano_banana_pro_default,
|
|
3338
|
-
prunaai_z_image_turbo_default
|
|
3339
|
-
];
|
|
3340
|
-
function getSupportedT2IModels() {
|
|
3341
|
-
return modelDefinitions;
|
|
3342
|
-
}
|
|
3343
|
-
function getT2IModel(modelId) {
|
|
3344
|
-
const model = modelDefinitions.find((entry) => entry.modelId === modelId);
|
|
3345
|
-
if (!model) {
|
|
3346
|
-
throw new Error(`Unsupported T2I model: ${modelId}`);
|
|
3347
|
-
}
|
|
3348
|
-
return model;
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
|
-
// src/models/t2i/options.ts
|
|
3352
|
-
function getT2IFieldDefault(modelId, fieldName) {
|
|
3353
|
-
const field = getFieldDefinition(modelId, fieldName);
|
|
3354
|
-
return field?.default;
|
|
3355
|
-
}
|
|
3356
|
-
function sanitizeT2IOverrides(modelId, overrides) {
|
|
3357
|
-
const model = getT2IModel(modelId);
|
|
3358
|
-
return Object.fromEntries(
|
|
3359
|
-
Object.entries(overrides).flatMap(([fieldName, value2]) => {
|
|
3360
|
-
if (!model.inputOptions.userConfigurable.includes(fieldName)) {
|
|
3361
|
-
return [];
|
|
3362
|
-
}
|
|
3363
|
-
const sanitized = coerceT2IFieldValue(modelId, fieldName, value2);
|
|
3364
|
-
return sanitized === void 0 ? [] : [[fieldName, sanitized]];
|
|
3365
|
-
})
|
|
3366
|
-
);
|
|
3367
|
-
}
|
|
3368
|
-
function coerceT2IFieldValue(modelId, fieldName, value2) {
|
|
3369
|
-
const field = getFieldDefinition(modelId, fieldName);
|
|
3370
|
-
if (!field) {
|
|
3371
|
-
return void 0;
|
|
3372
|
-
}
|
|
3373
|
-
if (value2 === null) {
|
|
3374
|
-
return field.nullable ? null : void 0;
|
|
3375
|
-
}
|
|
3376
|
-
if (value2 === void 0) {
|
|
3377
|
-
return void 0;
|
|
3378
|
-
}
|
|
3379
|
-
if (field.type === "boolean") {
|
|
3380
|
-
return coerceBoolean(value2);
|
|
3381
|
-
}
|
|
3382
|
-
if (field.type === "integer") {
|
|
3383
|
-
return clampNumber(parseFiniteNumber(value2, true), field.minimum, field.maximum, true, field.nullable);
|
|
3384
|
-
}
|
|
3385
|
-
if (field.type === "number") {
|
|
3386
|
-
return clampNumber(parseFiniteNumber(value2, false), field.minimum, field.maximum, false, field.nullable);
|
|
3387
|
-
}
|
|
3388
|
-
if (field.type === "string" || typeof value2 === "string") {
|
|
3389
|
-
const normalized = String(value2).trim();
|
|
3390
|
-
if (!normalized) {
|
|
3391
|
-
return field.nullable ? null : void 0;
|
|
3392
|
-
}
|
|
3393
|
-
if (field.enum && field.enum.length > 0 && !field.allowAnyString && !field.enum.includes(normalized)) {
|
|
3394
|
-
return void 0;
|
|
3395
|
-
}
|
|
3396
|
-
return normalized;
|
|
3397
|
-
}
|
|
3398
|
-
return value2;
|
|
3399
|
-
}
|
|
3400
|
-
function getFieldDefinition(modelId, fieldName) {
|
|
3401
|
-
const model = getT2IModel(modelId);
|
|
3402
|
-
return model.inputOptions.fields[fieldName];
|
|
3403
|
-
}
|
|
3404
|
-
function coerceBoolean(value2) {
|
|
3405
|
-
if (typeof value2 === "boolean") {
|
|
3406
|
-
return value2;
|
|
3407
|
-
}
|
|
3408
|
-
if (typeof value2 === "string") {
|
|
3409
|
-
if (value2.trim() === "true") {
|
|
3410
|
-
return true;
|
|
3411
|
-
}
|
|
3412
|
-
if (value2.trim() === "false") {
|
|
3413
|
-
return false;
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
return void 0;
|
|
3417
|
-
}
|
|
3418
|
-
function parseFiniteNumber(value2, integer) {
|
|
3419
|
-
const parsed = typeof value2 === "number" ? value2 : Number(String(value2).trim());
|
|
3420
|
-
if (!Number.isFinite(parsed)) {
|
|
3421
|
-
return void 0;
|
|
3422
|
-
}
|
|
3423
|
-
return integer ? Math.round(parsed) : parsed;
|
|
3424
|
-
}
|
|
3425
|
-
function clampNumber(value2, minimum, maximum, integer, nullable) {
|
|
3426
|
-
if (value2 === void 0) {
|
|
3427
|
-
return nullable ? null : void 0;
|
|
3428
|
-
}
|
|
3429
|
-
let next = value2;
|
|
3430
|
-
if (minimum !== void 0) {
|
|
3431
|
-
next = Math.max(minimum, next);
|
|
3432
|
-
}
|
|
3433
|
-
if (maximum !== void 0) {
|
|
3434
|
-
next = Math.min(maximum, next);
|
|
3435
|
-
}
|
|
3436
|
-
return integer ? Math.round(next) : next;
|
|
3437
|
-
}
|
|
3438
|
-
|
|
3439
2921
|
// src/pipeline/analytics.ts
|
|
3440
2922
|
var LLM_USD_PER_1K_TOKENS = {
|
|
3441
2923
|
// AUTO-GENERATED:OPENROUTER_PRICING_START
|
|
@@ -3461,64 +2943,6 @@ function estimateLlmCostUsd(modelId, usage) {
|
|
|
3461
2943
|
const usd = promptTokens / 1e3 * pricing.input + completionTokens / 1e3 * pricing.output;
|
|
3462
2944
|
return { usd, source: "estimated" };
|
|
3463
2945
|
}
|
|
3464
|
-
function estimateImageCostUsd(modelId, input, imageCount) {
|
|
3465
|
-
const model = getT2IModel(modelId);
|
|
3466
|
-
const pricingRules = "pricingRules" in model ? model.pricingRules : void 0;
|
|
3467
|
-
if (!pricingRules) {
|
|
3468
|
-
return { usd: null, source: "unavailable" };
|
|
3469
|
-
}
|
|
3470
|
-
if (pricingRules.basis === "output_image_count" && "usdPerImage" in pricingRules && typeof pricingRules.usdPerImage === "number") {
|
|
3471
|
-
return {
|
|
3472
|
-
usd: pricingRules.usdPerImage * imageCount,
|
|
3473
|
-
source: "estimated"
|
|
3474
|
-
};
|
|
3475
|
-
}
|
|
3476
|
-
if (pricingRules.basis === "output_image_resolution" && "tiers" in pricingRules && Array.isArray(pricingRules.tiers)) {
|
|
3477
|
-
const resolution = typeof input.resolution === "string" ? input.resolution : "fallback";
|
|
3478
|
-
const tiers = pricingRules.tiers;
|
|
3479
|
-
const tier = tiers.find((entry) => entry.resolution === resolution) ?? tiers.find((entry) => entry.resolution === "fallback");
|
|
3480
|
-
if (tier?.usdPerImage !== void 0) {
|
|
3481
|
-
return {
|
|
3482
|
-
usd: tier.usdPerImage * imageCount,
|
|
3483
|
-
source: "estimated"
|
|
3484
|
-
};
|
|
3485
|
-
}
|
|
3486
|
-
}
|
|
3487
|
-
if (pricingRules.basis === "output_image_megapixels" && "tiers" in pricingRules && Array.isArray(pricingRules.tiers)) {
|
|
3488
|
-
const megapixels = resolveMegapixels(input);
|
|
3489
|
-
const tiers = pricingRules.tiers;
|
|
3490
|
-
const tier = tiers.find((entry) => megapixels <= entry.maxMegapixels);
|
|
3491
|
-
if (tier?.usdPerImage !== void 0) {
|
|
3492
|
-
return {
|
|
3493
|
-
usd: tier.usdPerImage * imageCount,
|
|
3494
|
-
source: "estimated"
|
|
3495
|
-
};
|
|
3496
|
-
}
|
|
3497
|
-
}
|
|
3498
|
-
return { usd: null, source: "unavailable" };
|
|
3499
|
-
}
|
|
3500
|
-
function resolveMegapixels(input) {
|
|
3501
|
-
const explicit = toNumber(input.megapixels);
|
|
3502
|
-
if (explicit !== null) {
|
|
3503
|
-
return explicit;
|
|
3504
|
-
}
|
|
3505
|
-
const width = toNumber(input.width);
|
|
3506
|
-
const height = toNumber(input.height);
|
|
3507
|
-
if (width !== null && height !== null && width > 0 && height > 0) {
|
|
3508
|
-
return width * height / 1e6;
|
|
3509
|
-
}
|
|
3510
|
-
return 1;
|
|
3511
|
-
}
|
|
3512
|
-
function toNumber(value2) {
|
|
3513
|
-
if (typeof value2 === "number" && Number.isFinite(value2)) {
|
|
3514
|
-
return value2;
|
|
3515
|
-
}
|
|
3516
|
-
if (typeof value2 === "string" && value2.trim() !== "") {
|
|
3517
|
-
const parsed = Number(value2);
|
|
3518
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
3519
|
-
}
|
|
3520
|
-
return null;
|
|
3521
|
-
}
|
|
3522
2946
|
function sumKnownCosts(values) {
|
|
3523
2947
|
const known = values.filter((value2) => typeof value2 === "number");
|
|
3524
2948
|
if (known.length !== values.length) {
|
|
@@ -3594,7 +3018,7 @@ async function expandImagePrompts({
|
|
|
3594
3018
|
const dryRunStartMs = Date.now();
|
|
3595
3019
|
prompts.push({
|
|
3596
3020
|
...image,
|
|
3597
|
-
prompt: `${image.description}
|
|
3021
|
+
prompt: `${image.description}`
|
|
3598
3022
|
});
|
|
3599
3023
|
onPromptComplete?.({
|
|
3600
3024
|
imageId: image.id,
|
|
@@ -3661,29 +3085,26 @@ async function expandImagePrompts({
|
|
|
3661
3085
|
async function renderExpandedImages({
|
|
3662
3086
|
prompts,
|
|
3663
3087
|
settings,
|
|
3664
|
-
|
|
3088
|
+
limn,
|
|
3665
3089
|
markdownPath,
|
|
3666
3090
|
assetDir,
|
|
3667
3091
|
dryRun,
|
|
3668
3092
|
onProgress,
|
|
3669
3093
|
onRenderComplete,
|
|
3670
|
-
onInteraction
|
|
3671
|
-
onRetry
|
|
3094
|
+
onInteraction
|
|
3672
3095
|
}) {
|
|
3673
3096
|
const renderedImages = [];
|
|
3674
3097
|
for (let index = 0; index < prompts.length; index += 1) {
|
|
3675
3098
|
const prompt = prompts[index];
|
|
3676
3099
|
onProgress?.(`Rendering image ${index + 1}/${prompts.length} with ${settings.t2i.modelId}`);
|
|
3677
|
-
const fileName = `${prompt.kind === "cover" ? "cover" : `inline-${prompt.anchorAfterSection}`}-${index + 1}
|
|
3100
|
+
const fileName = `${prompt.kind === "cover" ? "cover" : `inline-${prompt.anchorAfterSection}`}-${index + 1}.png`;
|
|
3678
3101
|
const outputPath = path6.join(assetDir, fileName);
|
|
3679
|
-
if (dryRun || !
|
|
3102
|
+
if (dryRun || !limn) {
|
|
3680
3103
|
const dryRunStartMs = Date.now();
|
|
3681
3104
|
await writeFile4(outputPath, `Placeholder image for: ${prompt.prompt}
|
|
3682
3105
|
`, "utf8");
|
|
3683
3106
|
const outputBytes = Buffer.byteLength(`Placeholder image for: ${prompt.prompt}
|
|
3684
3107
|
`, "utf8");
|
|
3685
|
-
const dryRunInput = createReplicateInput(settings, prompt.prompt, prompt.kind);
|
|
3686
|
-
const dryRunCost = estimateImageCostUsd(settings.t2i.modelId, dryRunInput, 1);
|
|
3687
3108
|
renderedImages.push({
|
|
3688
3109
|
...prompt,
|
|
3689
3110
|
outputPath,
|
|
@@ -3698,13 +3119,13 @@ async function renderExpandedImages({
|
|
|
3698
3119
|
retries: 0,
|
|
3699
3120
|
retryBackoffMs: 0,
|
|
3700
3121
|
outputBytes,
|
|
3701
|
-
costUsd:
|
|
3702
|
-
costSource:
|
|
3122
|
+
costUsd: null,
|
|
3123
|
+
costSource: "unavailable"
|
|
3703
3124
|
});
|
|
3704
3125
|
onInteraction?.({
|
|
3705
3126
|
stageId: "images",
|
|
3706
3127
|
operationId: `images:${prompt.id}`,
|
|
3707
|
-
provider: "
|
|
3128
|
+
provider: "limn-dry-run",
|
|
3708
3129
|
modelId: settings.t2i.modelId,
|
|
3709
3130
|
kind: prompt.kind,
|
|
3710
3131
|
startedAt: new Date(dryRunStartMs).toISOString(),
|
|
@@ -3715,92 +3136,79 @@ async function renderExpandedImages({
|
|
|
3715
3136
|
retryBackoffMs: 0,
|
|
3716
3137
|
status: "succeeded",
|
|
3717
3138
|
prompt: prompt.prompt,
|
|
3718
|
-
input:
|
|
3139
|
+
input: {},
|
|
3719
3140
|
errorMessage: null
|
|
3720
3141
|
});
|
|
3721
3142
|
continue;
|
|
3722
3143
|
}
|
|
3723
|
-
const
|
|
3144
|
+
const family = settings.t2i.modelId;
|
|
3724
3145
|
const renderStartedAtMs = Date.now();
|
|
3725
|
-
let runDurationMs = 0;
|
|
3726
|
-
let runAttempts = 1;
|
|
3727
|
-
let runRetries = 0;
|
|
3728
|
-
let runRetryBackoffMs = 0;
|
|
3729
3146
|
try {
|
|
3730
|
-
const
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
runAttempts = metrics.attempts;
|
|
3734
|
-
runRetries = metrics.retries;
|
|
3735
|
-
runRetryBackoffMs = metrics.retryBackoffMs;
|
|
3736
|
-
},
|
|
3737
|
-
onRetry(event) {
|
|
3738
|
-
onRetry?.({
|
|
3739
|
-
imageId: prompt.id,
|
|
3740
|
-
kind: prompt.kind,
|
|
3741
|
-
retries: event.retries,
|
|
3742
|
-
errorMessage: event.errorMessage
|
|
3743
|
-
});
|
|
3744
|
-
}
|
|
3147
|
+
const result = await limn.generate(prompt.prompt, family, {
|
|
3148
|
+
replicateModel: settings.t2i.modelId,
|
|
3149
|
+
aspectRatio: "16:9"
|
|
3745
3150
|
});
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3151
|
+
const ext = mimeTypeToExtension(result.mimeType);
|
|
3152
|
+
const liveFileName = `${prompt.kind === "cover" ? "cover" : `inline-${prompt.anchorAfterSection}`}-${index + 1}.${ext}`;
|
|
3153
|
+
const liveOutputPath = path6.join(assetDir, liveFileName);
|
|
3154
|
+
if (result.image.byteLength < MIN_IMAGE_BYTES) {
|
|
3748
3155
|
throw new Error(
|
|
3749
|
-
`Image ${index + 1} download appears corrupted: only ${
|
|
3156
|
+
`Image ${index + 1} download appears corrupted: only ${result.image.byteLength} bytes received.`
|
|
3750
3157
|
);
|
|
3751
3158
|
}
|
|
3752
|
-
await writeFile4(
|
|
3159
|
+
await writeFile4(liveOutputPath, result.image);
|
|
3753
3160
|
renderedImages.push({
|
|
3754
3161
|
...prompt,
|
|
3755
|
-
outputPath,
|
|
3756
|
-
relativePath: relativeAssetPath(markdownPath,
|
|
3162
|
+
outputPath: liveOutputPath,
|
|
3163
|
+
relativePath: relativeAssetPath(markdownPath, liveOutputPath)
|
|
3757
3164
|
});
|
|
3758
|
-
const
|
|
3165
|
+
const costSource = result.analytics.costSource === "unknown" ? "unavailable" : "estimated";
|
|
3759
3166
|
onRenderComplete?.({
|
|
3760
3167
|
imageId: prompt.id,
|
|
3761
3168
|
kind: prompt.kind,
|
|
3762
|
-
modelId:
|
|
3763
|
-
durationMs:
|
|
3764
|
-
attempts:
|
|
3765
|
-
retries:
|
|
3766
|
-
retryBackoffMs:
|
|
3767
|
-
outputBytes:
|
|
3768
|
-
costUsd:
|
|
3769
|
-
costSource
|
|
3169
|
+
modelId: result.modelSlug,
|
|
3170
|
+
durationMs: result.analytics.totalDurationMs,
|
|
3171
|
+
attempts: 1,
|
|
3172
|
+
retries: 0,
|
|
3173
|
+
retryBackoffMs: 0,
|
|
3174
|
+
outputBytes: result.image.byteLength,
|
|
3175
|
+
costUsd: result.analytics.totalEstimatedCostUsd,
|
|
3176
|
+
costSource
|
|
3770
3177
|
});
|
|
3771
3178
|
onInteraction?.({
|
|
3772
3179
|
stageId: "images",
|
|
3773
3180
|
operationId: `images:${prompt.id}`,
|
|
3774
|
-
provider: "
|
|
3775
|
-
modelId:
|
|
3181
|
+
provider: "limn",
|
|
3182
|
+
modelId: result.modelSlug,
|
|
3776
3183
|
kind: prompt.kind,
|
|
3777
3184
|
startedAt: new Date(renderStartedAtMs).toISOString(),
|
|
3778
3185
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3779
|
-
durationMs:
|
|
3780
|
-
attempts:
|
|
3781
|
-
retries:
|
|
3782
|
-
retryBackoffMs:
|
|
3186
|
+
durationMs: result.analytics.totalDurationMs,
|
|
3187
|
+
attempts: 1,
|
|
3188
|
+
retries: 0,
|
|
3189
|
+
retryBackoffMs: 0,
|
|
3783
3190
|
status: "succeeded",
|
|
3784
3191
|
prompt: prompt.prompt,
|
|
3785
|
-
input,
|
|
3192
|
+
input: {},
|
|
3786
3193
|
errorMessage: null
|
|
3787
3194
|
});
|
|
3788
3195
|
} catch (error) {
|
|
3196
|
+
const durationMs = Date.now() - renderStartedAtMs;
|
|
3789
3197
|
onInteraction?.({
|
|
3790
3198
|
stageId: "images",
|
|
3791
3199
|
operationId: `images:${prompt.id}`,
|
|
3792
|
-
provider: "
|
|
3200
|
+
provider: "limn",
|
|
3793
3201
|
modelId: settings.t2i.modelId,
|
|
3794
3202
|
kind: prompt.kind,
|
|
3795
3203
|
startedAt: new Date(renderStartedAtMs).toISOString(),
|
|
3796
3204
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3797
|
-
durationMs
|
|
3798
|
-
attempts:
|
|
3799
|
-
retries:
|
|
3800
|
-
retryBackoffMs:
|
|
3205
|
+
durationMs,
|
|
3206
|
+
attempts: 1,
|
|
3207
|
+
retries: 0,
|
|
3208
|
+
retryBackoffMs: 0,
|
|
3801
3209
|
status: "failed",
|
|
3802
3210
|
prompt: prompt.prompt,
|
|
3803
|
-
input,
|
|
3211
|
+
input: {},
|
|
3804
3212
|
errorMessage: error instanceof Error ? error.message : "Unknown image render error."
|
|
3805
3213
|
});
|
|
3806
3214
|
throw error;
|
|
@@ -3832,128 +3240,10 @@ function mergeLlmMetrics(left, right) {
|
|
|
3832
3240
|
}
|
|
3833
3241
|
};
|
|
3834
3242
|
}
|
|
3835
|
-
function
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
if (model.inputOptions.pipelineManaged.includes("aspect_ratio")) {
|
|
3840
|
-
input.aspect_ratio = kind === "cover" ? "16:9" : "16:9";
|
|
3841
|
-
}
|
|
3842
|
-
if (model.inputOptions.pipelineManaged.includes("width")) {
|
|
3843
|
-
input.width = 1536;
|
|
3844
|
-
}
|
|
3845
|
-
if (model.inputOptions.pipelineManaged.includes("height")) {
|
|
3846
|
-
input.height = 864;
|
|
3847
|
-
}
|
|
3848
|
-
if (!("output_format" in input)) {
|
|
3849
|
-
const fallback = getT2IFieldDefault(settings.t2i.modelId, "output_format");
|
|
3850
|
-
if (typeof fallback === "string") {
|
|
3851
|
-
input.output_format = fallback;
|
|
3852
|
-
}
|
|
3853
|
-
}
|
|
3854
|
-
if (!("num_outputs" in input) && "num_outputs" in model.inputOptions.fields) {
|
|
3855
|
-
input.num_outputs = coerceT2IFieldValue(settings.t2i.modelId, "num_outputs", getT2IFieldDefault(settings.t2i.modelId, "num_outputs")) ?? 1;
|
|
3856
|
-
}
|
|
3857
|
-
if (!("max_images" in input) && "max_images" in model.inputOptions.fields) {
|
|
3858
|
-
input.max_images = coerceT2IFieldValue(settings.t2i.modelId, "max_images", getT2IFieldDefault(settings.t2i.modelId, "max_images")) ?? 1;
|
|
3859
|
-
}
|
|
3860
|
-
return input;
|
|
3861
|
-
}
|
|
3862
|
-
function resolveOutputFormat(settings) {
|
|
3863
|
-
const outputFormat = coerceT2IFieldValue(settings.t2i.modelId, "output_format", settings.t2i.inputOverrides.output_format);
|
|
3864
|
-
if (typeof outputFormat === "string") {
|
|
3865
|
-
return outputFormat === "jpeg" ? "jpg" : outputFormat;
|
|
3866
|
-
}
|
|
3867
|
-
const fallback = getT2IFieldDefault(settings.t2i.modelId, "output_format");
|
|
3868
|
-
const normalizedFallback = typeof fallback === "string" ? fallback : "png";
|
|
3869
|
-
return normalizedFallback === "jpeg" ? "jpg" : normalizedFallback;
|
|
3870
|
-
}
|
|
3871
|
-
async function normalizeReplicateOutput(output) {
|
|
3872
|
-
const first = Array.isArray(output) ? output[0] : output;
|
|
3873
|
-
if (!first) {
|
|
3874
|
-
throw new Error("Replicate returned no image output.");
|
|
3875
|
-
}
|
|
3876
|
-
if (typeof first === "string") {
|
|
3877
|
-
return fetchBytes(first);
|
|
3878
|
-
}
|
|
3879
|
-
if (first instanceof URL) {
|
|
3880
|
-
return fetchBytes(first.toString());
|
|
3881
|
-
}
|
|
3882
|
-
if (first instanceof Uint8Array) {
|
|
3883
|
-
return first;
|
|
3884
|
-
}
|
|
3885
|
-
if (first instanceof ArrayBuffer) {
|
|
3886
|
-
return new Uint8Array(first);
|
|
3887
|
-
}
|
|
3888
|
-
if (typeof Blob !== "undefined" && first instanceof Blob) {
|
|
3889
|
-
return new Uint8Array(await first.arrayBuffer());
|
|
3890
|
-
}
|
|
3891
|
-
if (typeof ReadableStream !== "undefined" && first instanceof ReadableStream) {
|
|
3892
|
-
return new Uint8Array(await new Response(first).arrayBuffer());
|
|
3893
|
-
}
|
|
3894
|
-
const fromBlobMethod = await maybeBytesFromBlobMethod(first);
|
|
3895
|
-
if (fromBlobMethod) {
|
|
3896
|
-
return fromBlobMethod;
|
|
3897
|
-
}
|
|
3898
|
-
const fromUrlMethod = await maybeBytesFromUrlMethod(first);
|
|
3899
|
-
if (fromUrlMethod) {
|
|
3900
|
-
return fromUrlMethod;
|
|
3901
|
-
}
|
|
3902
|
-
const fromArrayBufferMethod = await maybeBytesFromArrayBufferMethod(first);
|
|
3903
|
-
if (fromArrayBufferMethod) {
|
|
3904
|
-
return fromArrayBufferMethod;
|
|
3905
|
-
}
|
|
3906
|
-
throw new Error("Unsupported Replicate output format.");
|
|
3907
|
-
}
|
|
3908
|
-
async function maybeBytesFromBlobMethod(value2) {
|
|
3909
|
-
if (!isRecord(value2) || typeof value2.blob !== "function") {
|
|
3910
|
-
return null;
|
|
3911
|
-
}
|
|
3912
|
-
const blobLike = await value2.blob();
|
|
3913
|
-
if (typeof Blob === "undefined" || !(blobLike instanceof Blob)) {
|
|
3914
|
-
return null;
|
|
3915
|
-
}
|
|
3916
|
-
return new Uint8Array(await blobLike.arrayBuffer());
|
|
3917
|
-
}
|
|
3918
|
-
async function maybeBytesFromUrlMethod(value2) {
|
|
3919
|
-
if (!isRecord(value2) || typeof value2.url !== "function") {
|
|
3920
|
-
return null;
|
|
3921
|
-
}
|
|
3922
|
-
const urlLike = value2.url();
|
|
3923
|
-
if (typeof urlLike === "string") {
|
|
3924
|
-
return fetchBytes(urlLike);
|
|
3925
|
-
}
|
|
3926
|
-
if (urlLike instanceof URL) {
|
|
3927
|
-
return fetchBytes(urlLike.toString());
|
|
3928
|
-
}
|
|
3929
|
-
return null;
|
|
3930
|
-
}
|
|
3931
|
-
async function maybeBytesFromArrayBufferMethod(value2) {
|
|
3932
|
-
if (!isRecord(value2) || typeof value2.arrayBuffer !== "function") {
|
|
3933
|
-
return null;
|
|
3934
|
-
}
|
|
3935
|
-
const data = await value2.arrayBuffer();
|
|
3936
|
-
if (data instanceof ArrayBuffer) {
|
|
3937
|
-
return new Uint8Array(data);
|
|
3938
|
-
}
|
|
3939
|
-
return null;
|
|
3940
|
-
}
|
|
3941
|
-
function isRecord(value2) {
|
|
3942
|
-
return typeof value2 === "object" && value2 !== null;
|
|
3943
|
-
}
|
|
3944
|
-
async function fetchBytes(url) {
|
|
3945
|
-
const controller = new AbortController();
|
|
3946
|
-
const timer = setTimeout(() => controller.abort(), 6e4);
|
|
3947
|
-
timer.unref();
|
|
3948
|
-
try {
|
|
3949
|
-
const response = await fetch(url, { signal: controller.signal });
|
|
3950
|
-
if (!response.ok) {
|
|
3951
|
-
throw new Error(`Failed to download generated asset from ${url}`);
|
|
3952
|
-
}
|
|
3953
|
-
return new Uint8Array(await response.arrayBuffer());
|
|
3954
|
-
} finally {
|
|
3955
|
-
clearTimeout(timer);
|
|
3956
|
-
}
|
|
3243
|
+
function mimeTypeToExtension(mimeType) {
|
|
3244
|
+
if (mimeType === "image/jpeg") return "jpg";
|
|
3245
|
+
if (mimeType === "image/webp") return "webp";
|
|
3246
|
+
return "png";
|
|
3957
3247
|
}
|
|
3958
3248
|
|
|
3959
3249
|
// src/llm/openRouterClient.ts
|
|
@@ -4002,9 +3292,9 @@ var OpenRouterClient = class {
|
|
|
4002
3292
|
return structured;
|
|
4003
3293
|
} catch (parseError) {
|
|
4004
3294
|
if (attempt < 2 && shouldRetryStructuredParseError(parseError)) {
|
|
4005
|
-
const backoff =
|
|
3295
|
+
const backoff = backoffMs(attempt);
|
|
4006
3296
|
aggregatedMetrics = recordParseRetryMetrics(aggregatedMetrics, backoff);
|
|
4007
|
-
await
|
|
3297
|
+
await wait(backoff);
|
|
4008
3298
|
continue;
|
|
4009
3299
|
}
|
|
4010
3300
|
throw parseError;
|
|
@@ -4116,7 +3406,7 @@ var OpenRouterClient = class {
|
|
|
4116
3406
|
if (!response.ok) {
|
|
4117
3407
|
const message = json?.error?.message ?? `OpenRouter request failed with status ${response.status}`;
|
|
4118
3408
|
if (shouldRetryStatus(response.status) && attempt < 2) {
|
|
4119
|
-
const backoff =
|
|
3409
|
+
const backoff = backoffMs(attempt);
|
|
4120
3410
|
retries += 1;
|
|
4121
3411
|
retryBackoffMs += backoff;
|
|
4122
3412
|
onInteraction?.({
|
|
@@ -4136,14 +3426,14 @@ var OpenRouterClient = class {
|
|
|
4136
3426
|
responseBody: responseBodyRaw,
|
|
4137
3427
|
errorMessage: message
|
|
4138
3428
|
});
|
|
4139
|
-
await
|
|
3429
|
+
await wait(backoff);
|
|
4140
3430
|
continue;
|
|
4141
3431
|
}
|
|
4142
3432
|
throw new Error(message);
|
|
4143
3433
|
}
|
|
4144
3434
|
const content = json.choices?.[0]?.message?.content;
|
|
4145
3435
|
if (!content && attempt < 2) {
|
|
4146
|
-
const backoff =
|
|
3436
|
+
const backoff = backoffMs(attempt);
|
|
4147
3437
|
retries += 1;
|
|
4148
3438
|
retryBackoffMs += backoff;
|
|
4149
3439
|
onInteraction?.({
|
|
@@ -4163,7 +3453,7 @@ var OpenRouterClient = class {
|
|
|
4163
3453
|
responseBody: responseBodyRaw,
|
|
4164
3454
|
errorMessage: "OpenRouter returned an empty response."
|
|
4165
3455
|
});
|
|
4166
|
-
await
|
|
3456
|
+
await wait(backoff);
|
|
4167
3457
|
continue;
|
|
4168
3458
|
}
|
|
4169
3459
|
onInteraction?.({
|
|
@@ -4218,11 +3508,11 @@ var OpenRouterClient = class {
|
|
|
4218
3508
|
responseBody: responseBodyRaw,
|
|
4219
3509
|
errorMessage: lastError.message
|
|
4220
3510
|
});
|
|
4221
|
-
if (attempt < 2 &&
|
|
4222
|
-
const backoff =
|
|
3511
|
+
if (attempt < 2 && shouldRetryError(lastError)) {
|
|
3512
|
+
const backoff = backoffMs(attempt);
|
|
4223
3513
|
retries += 1;
|
|
4224
3514
|
retryBackoffMs += backoff;
|
|
4225
|
-
await
|
|
3515
|
+
await wait(backoff);
|
|
4226
3516
|
continue;
|
|
4227
3517
|
}
|
|
4228
3518
|
} finally {
|
|
@@ -4318,7 +3608,7 @@ function isStructuredOutputCompatibilityError(message) {
|
|
|
4318
3608
|
function shouldRetryStatus(status) {
|
|
4319
3609
|
return status === 408 || status === 409 || status === 429 || status >= 500;
|
|
4320
3610
|
}
|
|
4321
|
-
function
|
|
3611
|
+
function shouldRetryError(error) {
|
|
4322
3612
|
return /timeout|network|fetch|temporarily|aborted/i.test(error.message);
|
|
4323
3613
|
}
|
|
4324
3614
|
function shouldRetryStructuredParseError(error) {
|
|
@@ -4342,7 +3632,7 @@ function normalizeClientError(error, timeoutMs) {
|
|
|
4342
3632
|
}
|
|
4343
3633
|
return new Error("Unknown OpenRouter client error.");
|
|
4344
3634
|
}
|
|
4345
|
-
function
|
|
3635
|
+
function backoffMs(attempt) {
|
|
4346
3636
|
return 500 * (attempt + 1);
|
|
4347
3637
|
}
|
|
4348
3638
|
function aggregateLlmMetrics(total, next) {
|
|
@@ -4376,7 +3666,7 @@ function sumNullableNumber(left, right) {
|
|
|
4376
3666
|
}
|
|
4377
3667
|
return left + right;
|
|
4378
3668
|
}
|
|
4379
|
-
function
|
|
3669
|
+
function wait(ms) {
|
|
4380
3670
|
return new Promise((resolve) => {
|
|
4381
3671
|
setTimeout(resolve, ms);
|
|
4382
3672
|
});
|
|
@@ -4663,7 +3953,6 @@ async function runPipelineShell(input, options = {}) {
|
|
|
4663
3953
|
const stageTracking = /* @__PURE__ */ new Map();
|
|
4664
3954
|
const stageRetryState = /* @__PURE__ */ new Map();
|
|
4665
3955
|
const llmOperationRetryState = /* @__PURE__ */ new Map();
|
|
4666
|
-
const imageOperationRetryState = /* @__PURE__ */ new Map();
|
|
4667
3956
|
stageTracking.set("shared-brief", {
|
|
4668
3957
|
startedAtMs: runStartedAtMs,
|
|
4669
3958
|
endedAtMs: null,
|
|
@@ -4739,7 +4028,11 @@ async function runPipelineShell(input, options = {}) {
|
|
|
4739
4028
|
const openRouter = dryRun ? null : new OpenRouterClient(requireSecret(input.config.secrets.openRouterApiKey, "OpenRouter API key"));
|
|
4740
4029
|
const canRenderImagesLive = Boolean(input.config.secrets.replicateApiToken);
|
|
4741
4030
|
const imageDryRun = dryRun || !canRenderImagesLive;
|
|
4742
|
-
const
|
|
4031
|
+
const limn = imageDryRun ? null : new Limn({
|
|
4032
|
+
openrouterApiKey: input.config.secrets.openRouterApiKey ?? void 0,
|
|
4033
|
+
replicateApiKey: requireSecret(input.config.secrets.replicateApiToken, "Replicate API token"),
|
|
4034
|
+
openrouterModel: input.config.settings.model
|
|
4035
|
+
});
|
|
4743
4036
|
let contentBrief = writeSession.contentBrief;
|
|
4744
4037
|
let plan = writeSession.plan;
|
|
4745
4038
|
let text = writeSession.text;
|
|
@@ -5172,7 +4465,7 @@ async function runPipelineShell(input, options = {}) {
|
|
|
5172
4465
|
const renderedImages = await renderExpandedImages({
|
|
5173
4466
|
prompts: imagePrompts,
|
|
5174
4467
|
settings: input.config.settings,
|
|
5175
|
-
|
|
4468
|
+
limn,
|
|
5176
4469
|
markdownPath: primaryMarkdownPath,
|
|
5177
4470
|
assetDir: sharedAssetDir,
|
|
5178
4471
|
dryRun: imageDryRun,
|
|
@@ -5195,15 +4488,6 @@ async function runPipelineShell(input, options = {}) {
|
|
|
5195
4488
|
recordStageCost(stageTracking, "images", metrics.costUsd, metrics.costSource);
|
|
5196
4489
|
addStageRetries(stageTracking, "images", metrics.retries);
|
|
5197
4490
|
},
|
|
5198
|
-
onRetry(event) {
|
|
5199
|
-
const operationKey = `images:${event.imageId}`;
|
|
5200
|
-
const previousRetries = imageOperationRetryState.get(operationKey) ?? 0;
|
|
5201
|
-
if (event.retries <= previousRetries) {
|
|
5202
|
-
return;
|
|
5203
|
-
}
|
|
5204
|
-
imageOperationRetryState.set(operationKey, event.retries);
|
|
5205
|
-
applyRetryUpdate("images", event.retries - previousRetries, event.errorMessage);
|
|
5206
|
-
},
|
|
5207
4491
|
onProgress(detail) {
|
|
5208
4492
|
stages[4] = {
|
|
5209
4493
|
...stages[4],
|
|
@@ -5265,7 +4549,7 @@ async function runPipelineShell(input, options = {}) {
|
|
|
5265
4549
|
const renderedImages = await renderExpandedImages({
|
|
5266
4550
|
prompts: imagePrompts,
|
|
5267
4551
|
settings: input.config.settings,
|
|
5268
|
-
|
|
4552
|
+
limn,
|
|
5269
4553
|
markdownPath: primaryMarkdownPath,
|
|
5270
4554
|
assetDir: sharedAssetDir,
|
|
5271
4555
|
dryRun: imageDryRun,
|
|
@@ -5288,15 +4572,6 @@ async function runPipelineShell(input, options = {}) {
|
|
|
5288
4572
|
recordStageCost(stageTracking, "images", metrics.costUsd, metrics.costSource);
|
|
5289
4573
|
addStageRetries(stageTracking, "images", metrics.retries);
|
|
5290
4574
|
},
|
|
5291
|
-
onRetry(event) {
|
|
5292
|
-
const operationKey = `images:${event.imageId}`;
|
|
5293
|
-
const previousRetries = imageOperationRetryState.get(operationKey) ?? 0;
|
|
5294
|
-
if (event.retries <= previousRetries) {
|
|
5295
|
-
return;
|
|
5296
|
-
}
|
|
5297
|
-
imageOperationRetryState.set(operationKey, event.retries);
|
|
5298
|
-
applyRetryUpdate("images", event.retries - previousRetries, event.errorMessage);
|
|
5299
|
-
},
|
|
5300
4575
|
onProgress(detail) {
|
|
5301
4576
|
stages[4] = {
|
|
5302
4577
|
...stages[4],
|
|
@@ -6011,13 +5286,13 @@ function buildPrimaryCoverPrompt(contentBrief, primaryContentType, primaryMarkdo
|
|
|
6011
5286
|
description: `Cover image for ${primaryContentType}`,
|
|
6012
5287
|
anchorAfterSection: null,
|
|
6013
5288
|
prompt: [
|
|
6014
|
-
`
|
|
5289
|
+
`Cover image for ${primaryContentType}.`,
|
|
6015
5290
|
`Core angle: ${contentBrief.description}`,
|
|
6016
5291
|
`Audience: ${contentBrief.targetAudience}`,
|
|
6017
5292
|
`Promise: ${contentBrief.corePromise}`,
|
|
6018
5293
|
`Voice: ${contentBrief.voiceNotes}`,
|
|
6019
5294
|
`Primary excerpt: ${markdownExcerpt}`,
|
|
6020
|
-
"
|
|
5295
|
+
"Do not include any words, letters, numbers, logos, watermarks, or signage in the image."
|
|
6021
5296
|
].join(" ")
|
|
6022
5297
|
};
|
|
6023
5298
|
}
|
|
@@ -7236,62 +6511,240 @@ async function startIdeonMcpServer() {
|
|
|
7236
6511
|
} catch (error) {
|
|
7237
6512
|
return formatToolError(error);
|
|
7238
6513
|
}
|
|
7239
|
-
}
|
|
7240
|
-
);
|
|
7241
|
-
server.registerTool(
|
|
7242
|
-
"ideon_config_unset",
|
|
7243
|
-
{
|
|
7244
|
-
title: "Ideon Config Unset",
|
|
7245
|
-
description: "Reset a setting to its default or delete a stored secret.",
|
|
7246
|
-
inputSchema: configUnsetToolInputSchema
|
|
7247
|
-
},
|
|
7248
|
-
async (input) => {
|
|
7249
|
-
try {
|
|
7250
|
-
if (!isConfigKey(input.key)) {
|
|
7251
|
-
throw new ReportedError(`Unsupported config key: ${input.key}`);
|
|
6514
|
+
}
|
|
6515
|
+
);
|
|
6516
|
+
server.registerTool(
|
|
6517
|
+
"ideon_config_unset",
|
|
6518
|
+
{
|
|
6519
|
+
title: "Ideon Config Unset",
|
|
6520
|
+
description: "Reset a setting to its default or delete a stored secret.",
|
|
6521
|
+
inputSchema: configUnsetToolInputSchema
|
|
6522
|
+
},
|
|
6523
|
+
async (input) => {
|
|
6524
|
+
try {
|
|
6525
|
+
if (!isConfigKey(input.key)) {
|
|
6526
|
+
throw new ReportedError(`Unsupported config key: ${input.key}`);
|
|
6527
|
+
}
|
|
6528
|
+
await configUnset(input.key);
|
|
6529
|
+
return {
|
|
6530
|
+
content: [
|
|
6531
|
+
{
|
|
6532
|
+
type: "text",
|
|
6533
|
+
text: `Unset ${input.key}.`
|
|
6534
|
+
}
|
|
6535
|
+
],
|
|
6536
|
+
structuredContent: {
|
|
6537
|
+
key: input.key,
|
|
6538
|
+
updated: true
|
|
6539
|
+
}
|
|
6540
|
+
};
|
|
6541
|
+
} catch (error) {
|
|
6542
|
+
return formatToolError(error);
|
|
6543
|
+
}
|
|
6544
|
+
}
|
|
6545
|
+
);
|
|
6546
|
+
const transport = new StdioServerTransport();
|
|
6547
|
+
await server.connect(transport);
|
|
6548
|
+
}
|
|
6549
|
+
function formatToolError(error) {
|
|
6550
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
6551
|
+
return {
|
|
6552
|
+
content: [{ type: "text", text: message }],
|
|
6553
|
+
isError: true
|
|
6554
|
+
};
|
|
6555
|
+
}
|
|
6556
|
+
|
|
6557
|
+
// src/cli/commands/mcp.ts
|
|
6558
|
+
async function runMcpServeCommand() {
|
|
6559
|
+
await startIdeonMcpServer();
|
|
6560
|
+
}
|
|
6561
|
+
|
|
6562
|
+
// src/cli/commands/settings.tsx
|
|
6563
|
+
import { render } from "ink";
|
|
6564
|
+
|
|
6565
|
+
// src/cli/flows/settingsFlow.tsx
|
|
6566
|
+
import { useEffect, useMemo, useState } from "react";
|
|
6567
|
+
import { Box, Text, useApp, useInput } from "ink";
|
|
6568
|
+
import SelectInput from "ink-select-input";
|
|
6569
|
+
import TextInput from "ink-text-input";
|
|
6570
|
+
|
|
6571
|
+
// src/images/limnModelCatalog.ts
|
|
6572
|
+
import { getSupportedModelCatalog } from "@telepat/limn";
|
|
6573
|
+
function getLimnGenerationModels() {
|
|
6574
|
+
return getSupportedModelCatalog().filter((entry) => entry.generationEnabled);
|
|
6575
|
+
}
|
|
6576
|
+
|
|
6577
|
+
// src/cli/flows/settingsFlowLogic.ts
|
|
6578
|
+
function handleMenuSelect(action, settings, secrets, setEditing, setShowModelSelect, setMenuMode, onDone, exit) {
|
|
6579
|
+
switch (action) {
|
|
6580
|
+
case "openrouter":
|
|
6581
|
+
setEditing({ key: action, label: "OpenRouter API key", value: secrets.openRouterApiKey ?? "" });
|
|
6582
|
+
return;
|
|
6583
|
+
case "replicate":
|
|
6584
|
+
setEditing({ key: action, label: "Replicate API token", value: secrets.replicateApiToken ?? "" });
|
|
6585
|
+
return;
|
|
6586
|
+
case "llm-model":
|
|
6587
|
+
setEditing({ key: action, label: "LLM model", value: settings.model });
|
|
6588
|
+
return;
|
|
6589
|
+
case "notifications-enabled":
|
|
6590
|
+
setEditing({ key: action, label: "Notifications > OS notifications enabled (true|false)", value: String(settings.notifications.enabled) });
|
|
6591
|
+
return;
|
|
6592
|
+
case "temperature":
|
|
6593
|
+
setEditing({ key: action, label: "Temperature", value: String(settings.modelSettings.temperature) });
|
|
6594
|
+
return;
|
|
6595
|
+
case "maxTokens":
|
|
6596
|
+
setEditing({ key: action, label: "Max tokens", value: String(settings.modelSettings.maxTokens) });
|
|
6597
|
+
return;
|
|
6598
|
+
case "topP":
|
|
6599
|
+
setEditing({ key: action, label: "Top p", value: String(settings.modelSettings.topP) });
|
|
6600
|
+
return;
|
|
6601
|
+
case "markdownOutputDir":
|
|
6602
|
+
setEditing({ key: action, label: "Markdown output directory", value: settings.markdownOutputDir });
|
|
6603
|
+
return;
|
|
6604
|
+
case "assetOutputDir":
|
|
6605
|
+
setEditing({ key: action, label: "Asset output directory", value: settings.assetOutputDir });
|
|
6606
|
+
return;
|
|
6607
|
+
case "t2i-settings":
|
|
6608
|
+
setMenuMode("t2i");
|
|
6609
|
+
return;
|
|
6610
|
+
case "t2i-model":
|
|
6611
|
+
setShowModelSelect(true);
|
|
6612
|
+
return;
|
|
6613
|
+
case "t2i-input-overrides":
|
|
6614
|
+
setEditing({
|
|
6615
|
+
key: action,
|
|
6616
|
+
label: "T2I input overrides (JSON)",
|
|
6617
|
+
value: JSON.stringify(settings.t2i.inputOverrides, null, 2)
|
|
6618
|
+
});
|
|
6619
|
+
return;
|
|
6620
|
+
case "t2i-back":
|
|
6621
|
+
setMenuMode("main");
|
|
6622
|
+
return;
|
|
6623
|
+
case "save":
|
|
6624
|
+
onDone({ settings, secrets });
|
|
6625
|
+
exit();
|
|
6626
|
+
return;
|
|
6627
|
+
case "cancel":
|
|
6628
|
+
onDone(null);
|
|
6629
|
+
exit();
|
|
6630
|
+
return;
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
function applyEdit(action, value2, settings, secrets, setSettings, setSecrets) {
|
|
6634
|
+
if (action === "openrouter") {
|
|
6635
|
+
setSecrets({ ...secrets, openRouterApiKey: value2.trim() || null });
|
|
6636
|
+
return true;
|
|
6637
|
+
}
|
|
6638
|
+
if (action === "replicate") {
|
|
6639
|
+
setSecrets({ ...secrets, replicateApiToken: value2.trim() || null });
|
|
6640
|
+
return true;
|
|
6641
|
+
}
|
|
6642
|
+
if (action === "llm-model") {
|
|
6643
|
+
setSettings({ ...settings, model: value2.trim() || settings.model });
|
|
6644
|
+
return true;
|
|
6645
|
+
}
|
|
6646
|
+
if (action === "notifications-enabled") {
|
|
6647
|
+
const parsed = parseBooleanOrFallback(value2, settings.notifications.enabled);
|
|
6648
|
+
setSettings({
|
|
6649
|
+
...settings,
|
|
6650
|
+
notifications: {
|
|
6651
|
+
...settings.notifications,
|
|
6652
|
+
enabled: parsed
|
|
6653
|
+
}
|
|
6654
|
+
});
|
|
6655
|
+
return true;
|
|
6656
|
+
}
|
|
6657
|
+
if (action === "temperature") {
|
|
6658
|
+
const nextTemperature = clampNumber(parseNumberOrFallback(value2, settings.modelSettings.temperature), 0, 2);
|
|
6659
|
+
setSettings({
|
|
6660
|
+
...settings,
|
|
6661
|
+
modelSettings: {
|
|
6662
|
+
...settings.modelSettings,
|
|
6663
|
+
temperature: nextTemperature
|
|
6664
|
+
}
|
|
6665
|
+
});
|
|
6666
|
+
return true;
|
|
6667
|
+
}
|
|
6668
|
+
if (action === "maxTokens") {
|
|
6669
|
+
const nextMaxTokens = Math.max(1, Math.round(parseNumberOrFallback(value2, settings.modelSettings.maxTokens)));
|
|
6670
|
+
setSettings({
|
|
6671
|
+
...settings,
|
|
6672
|
+
modelSettings: {
|
|
6673
|
+
...settings.modelSettings,
|
|
6674
|
+
maxTokens: nextMaxTokens
|
|
6675
|
+
}
|
|
6676
|
+
});
|
|
6677
|
+
return true;
|
|
6678
|
+
}
|
|
6679
|
+
if (action === "topP") {
|
|
6680
|
+
const nextTopP = clampNumber(parseNumberOrFallback(value2, settings.modelSettings.topP), 0, 1);
|
|
6681
|
+
setSettings({
|
|
6682
|
+
...settings,
|
|
6683
|
+
modelSettings: {
|
|
6684
|
+
...settings.modelSettings,
|
|
6685
|
+
topP: nextTopP
|
|
6686
|
+
}
|
|
6687
|
+
});
|
|
6688
|
+
return true;
|
|
6689
|
+
}
|
|
6690
|
+
if (action === "markdownOutputDir") {
|
|
6691
|
+
setSettings({ ...settings, markdownOutputDir: value2.trim() || settings.markdownOutputDir });
|
|
6692
|
+
return true;
|
|
6693
|
+
}
|
|
6694
|
+
if (action === "assetOutputDir") {
|
|
6695
|
+
setSettings({ ...settings, assetOutputDir: value2.trim() || settings.assetOutputDir });
|
|
6696
|
+
return true;
|
|
6697
|
+
}
|
|
6698
|
+
if (action === "t2i-input-overrides") {
|
|
6699
|
+
const trimmed = value2.trim();
|
|
6700
|
+
if (trimmed.length === 0) {
|
|
6701
|
+
setSettings({
|
|
6702
|
+
...settings,
|
|
6703
|
+
t2i: {
|
|
6704
|
+
...settings.t2i,
|
|
6705
|
+
inputOverrides: {}
|
|
7252
6706
|
}
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
],
|
|
7261
|
-
structuredContent: {
|
|
7262
|
-
key: input.key,
|
|
7263
|
-
updated: true
|
|
7264
|
-
}
|
|
7265
|
-
};
|
|
7266
|
-
} catch (error) {
|
|
7267
|
-
return formatToolError(error);
|
|
6707
|
+
});
|
|
6708
|
+
return true;
|
|
6709
|
+
}
|
|
6710
|
+
try {
|
|
6711
|
+
const parsed = JSON.parse(trimmed);
|
|
6712
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
6713
|
+
return false;
|
|
7268
6714
|
}
|
|
6715
|
+
setSettings({
|
|
6716
|
+
...settings,
|
|
6717
|
+
t2i: {
|
|
6718
|
+
...settings.t2i,
|
|
6719
|
+
inputOverrides: parsed
|
|
6720
|
+
}
|
|
6721
|
+
});
|
|
6722
|
+
return true;
|
|
6723
|
+
} catch {
|
|
6724
|
+
return false;
|
|
7269
6725
|
}
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
await server.connect(transport);
|
|
6726
|
+
}
|
|
6727
|
+
return false;
|
|
7273
6728
|
}
|
|
7274
|
-
function
|
|
7275
|
-
const
|
|
7276
|
-
return
|
|
7277
|
-
content: [{ type: "text", text: message }],
|
|
7278
|
-
isError: true
|
|
7279
|
-
};
|
|
6729
|
+
function parseNumberOrFallback(value2, fallback) {
|
|
6730
|
+
const parsed = Number(value2.trim());
|
|
6731
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7280
6732
|
}
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
6733
|
+
function clampNumber(value2, minimum, maximum) {
|
|
6734
|
+
return Math.min(maximum, Math.max(minimum, value2));
|
|
6735
|
+
}
|
|
6736
|
+
function parseBooleanOrFallback(value2, fallback) {
|
|
6737
|
+
const normalized = value2.trim().toLowerCase();
|
|
6738
|
+
if (normalized === "true") {
|
|
6739
|
+
return true;
|
|
6740
|
+
}
|
|
6741
|
+
if (normalized === "false") {
|
|
6742
|
+
return false;
|
|
6743
|
+
}
|
|
6744
|
+
return fallback;
|
|
7285
6745
|
}
|
|
7286
|
-
|
|
7287
|
-
// src/cli/commands/settings.tsx
|
|
7288
|
-
import { render } from "ink";
|
|
7289
6746
|
|
|
7290
6747
|
// src/cli/flows/settingsFlow.tsx
|
|
7291
|
-
import { useMemo, useState } from "react";
|
|
7292
|
-
import { Box, Text, useApp, useInput } from "ink";
|
|
7293
|
-
import SelectInput from "ink-select-input";
|
|
7294
|
-
import TextInput from "ink-text-input";
|
|
7295
6748
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7296
6749
|
function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
7297
6750
|
const { exit } = useApp();
|
|
@@ -7299,7 +6752,8 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7299
6752
|
const [secrets, setSecrets] = useState(initialSecrets);
|
|
7300
6753
|
const [editing, setEditing] = useState(null);
|
|
7301
6754
|
const [showModelSelect, setShowModelSelect] = useState(false);
|
|
7302
|
-
const
|
|
6755
|
+
const [menuMode, setMenuMode] = useState("main");
|
|
6756
|
+
const currentModelEntry = getLimnGenerationModels().find((m) => m.family === settings.t2i.modelId) ?? getLimnGenerationModels()[0];
|
|
7303
6757
|
useInput((input, key) => {
|
|
7304
6758
|
if (key.escape) {
|
|
7305
6759
|
if (editing) {
|
|
@@ -7308,6 +6762,11 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7308
6762
|
}
|
|
7309
6763
|
if (showModelSelect) {
|
|
7310
6764
|
setShowModelSelect(false);
|
|
6765
|
+
return;
|
|
6766
|
+
}
|
|
6767
|
+
if (menuMode === "t2i") {
|
|
6768
|
+
setMenuMode("main");
|
|
6769
|
+
return;
|
|
7311
6770
|
}
|
|
7312
6771
|
}
|
|
7313
6772
|
if (key.ctrl && input === "c") {
|
|
@@ -7315,11 +6774,28 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7315
6774
|
exit();
|
|
7316
6775
|
}
|
|
7317
6776
|
});
|
|
6777
|
+
const formatT2iOverridesSummary = (overrides) => {
|
|
6778
|
+
const count = Object.keys(overrides).length;
|
|
6779
|
+
return count === 0 ? "none" : `${count} override${count === 1 ? "" : "s"}`;
|
|
6780
|
+
};
|
|
7318
6781
|
const menuItems = useMemo(() => {
|
|
7319
|
-
const
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
6782
|
+
const t2iSubmenu = [
|
|
6783
|
+
{
|
|
6784
|
+
label: `T2I model: ${currentModelEntry?.displayName ?? settings.t2i.modelId}`,
|
|
6785
|
+
value: "t2i-model"
|
|
6786
|
+
},
|
|
6787
|
+
{
|
|
6788
|
+
label: `T2I input overrides: ${formatT2iOverridesSummary(settings.t2i.inputOverrides)}`,
|
|
6789
|
+
value: "t2i-input-overrides"
|
|
6790
|
+
},
|
|
6791
|
+
{
|
|
6792
|
+
label: "Back",
|
|
6793
|
+
value: "t2i-back"
|
|
6794
|
+
}
|
|
6795
|
+
];
|
|
6796
|
+
if (menuMode === "t2i") {
|
|
6797
|
+
return t2iSubmenu;
|
|
6798
|
+
}
|
|
7323
6799
|
return [
|
|
7324
6800
|
{
|
|
7325
6801
|
label: `OpenRouter API key: ${secrets.openRouterApiKey ? "stored in keychain" : "missing"}`,
|
|
@@ -7358,10 +6834,9 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7358
6834
|
value: "assetOutputDir"
|
|
7359
6835
|
},
|
|
7360
6836
|
{
|
|
7361
|
-
label: `T2I
|
|
7362
|
-
value: "t2i-
|
|
6837
|
+
label: `T2I settings: ${currentModelEntry?.displayName ?? settings.t2i.modelId}`,
|
|
6838
|
+
value: "t2i-settings"
|
|
7363
6839
|
},
|
|
7364
|
-
...t2iItems,
|
|
7365
6840
|
{
|
|
7366
6841
|
label: "Save and exit",
|
|
7367
6842
|
value: "save"
|
|
@@ -7371,11 +6846,11 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7371
6846
|
value: "cancel"
|
|
7372
6847
|
}
|
|
7373
6848
|
];
|
|
7374
|
-
}, [
|
|
6849
|
+
}, [currentModelEntry, menuMode, secrets.openRouterApiKey, secrets.replicateApiToken, settings]);
|
|
7375
6850
|
if (showModelSelect) {
|
|
7376
|
-
const items =
|
|
7377
|
-
label: `${model.displayName} (${model.
|
|
7378
|
-
value: model.
|
|
6851
|
+
const items = getLimnGenerationModels().map((model) => ({
|
|
6852
|
+
label: `${model.displayName} (${model.family})`,
|
|
6853
|
+
value: model.family
|
|
7379
6854
|
}));
|
|
7380
6855
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
7381
6856
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyanBright", children: "Choose T2I Model" }),
|
|
@@ -7389,7 +6864,7 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7389
6864
|
...current,
|
|
7390
6865
|
t2i: {
|
|
7391
6866
|
modelId: item.value,
|
|
7392
|
-
inputOverrides:
|
|
6867
|
+
inputOverrides: {}
|
|
7393
6868
|
}
|
|
7394
6869
|
}));
|
|
7395
6870
|
setShowModelSelect(false);
|
|
@@ -7404,8 +6879,11 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7404
6879
|
{
|
|
7405
6880
|
editing,
|
|
7406
6881
|
onSubmit: (value2) => {
|
|
7407
|
-
applyEdit(editing.key, value2, settings, secrets, setSettings, setSecrets);
|
|
7408
|
-
|
|
6882
|
+
const accepted = applyEdit(editing.key, value2, settings, secrets, setSettings, setSecrets);
|
|
6883
|
+
if (accepted) {
|
|
6884
|
+
setEditing(null);
|
|
6885
|
+
}
|
|
6886
|
+
return accepted;
|
|
7409
6887
|
},
|
|
7410
6888
|
onCancel: () => {
|
|
7411
6889
|
setEditing(null);
|
|
@@ -7416,7 +6894,13 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7416
6894
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
7417
6895
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyanBright", children: "Ideon Settings" }),
|
|
7418
6896
|
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Enter to edit. Esc backs out of nested menus. Ctrl+C cancels." }),
|
|
7419
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx(
|
|
6897
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx(
|
|
6898
|
+
SelectInput,
|
|
6899
|
+
{
|
|
6900
|
+
items: menuItems,
|
|
6901
|
+
onSelect: (item) => handleMenuSelect(item.value, settings, secrets, setEditing, setShowModelSelect, setMenuMode, onDone, exit)
|
|
6902
|
+
}
|
|
6903
|
+
) })
|
|
7420
6904
|
] });
|
|
7421
6905
|
}
|
|
7422
6906
|
function EditorView({
|
|
@@ -7425,6 +6909,11 @@ function EditorView({
|
|
|
7425
6909
|
onCancel
|
|
7426
6910
|
}) {
|
|
7427
6911
|
const [value2, setValue] = useState(editing.value);
|
|
6912
|
+
const [error, setError] = useState(null);
|
|
6913
|
+
useEffect(() => {
|
|
6914
|
+
setValue(editing.value);
|
|
6915
|
+
setError(null);
|
|
6916
|
+
}, [editing]);
|
|
7428
6917
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
7429
6918
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyanBright", children: editing.label }),
|
|
7430
6919
|
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Enter saves. Blank value clears nullable secrets and overrides. Esc cancels." }),
|
|
@@ -7436,11 +6925,15 @@ function EditorView({
|
|
|
7436
6925
|
value: value2,
|
|
7437
6926
|
onChange: setValue,
|
|
7438
6927
|
onSubmit: (nextValue) => {
|
|
7439
|
-
onSubmit(nextValue);
|
|
6928
|
+
const accepted = onSubmit(nextValue);
|
|
6929
|
+
if (!accepted) {
|
|
6930
|
+
setError("Invalid JSON. Please enter an object or leave blank to clear.");
|
|
6931
|
+
}
|
|
7440
6932
|
}
|
|
7441
6933
|
}
|
|
7442
6934
|
)
|
|
7443
6935
|
] }),
|
|
6936
|
+
error ? /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: "red", children: error }) }) : null,
|
|
7444
6937
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
7445
6938
|
"Current value: ",
|
|
7446
6939
|
editing.value || "(empty)"
|
|
@@ -7448,170 +6941,6 @@ function EditorView({
|
|
|
7448
6941
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "Press Esc to return without changes." }) })
|
|
7449
6942
|
] });
|
|
7450
6943
|
}
|
|
7451
|
-
function handleMenuSelect(action, settings, secrets, setEditing, setShowModelSelect, onDone, exit) {
|
|
7452
|
-
switch (action) {
|
|
7453
|
-
case "openrouter":
|
|
7454
|
-
setEditing({ key: action, label: "OpenRouter API key", value: secrets.openRouterApiKey ?? "" });
|
|
7455
|
-
return;
|
|
7456
|
-
case "replicate":
|
|
7457
|
-
setEditing({ key: action, label: "Replicate API token", value: secrets.replicateApiToken ?? "" });
|
|
7458
|
-
return;
|
|
7459
|
-
case "llm-model":
|
|
7460
|
-
setEditing({ key: action, label: "LLM model", value: settings.model });
|
|
7461
|
-
return;
|
|
7462
|
-
case "notifications-enabled":
|
|
7463
|
-
setEditing({ key: action, label: "Notifications > OS notifications enabled (true|false)", value: String(settings.notifications.enabled) });
|
|
7464
|
-
return;
|
|
7465
|
-
case "temperature":
|
|
7466
|
-
setEditing({ key: action, label: "Temperature", value: String(settings.modelSettings.temperature) });
|
|
7467
|
-
return;
|
|
7468
|
-
case "maxTokens":
|
|
7469
|
-
setEditing({ key: action, label: "Max tokens", value: String(settings.modelSettings.maxTokens) });
|
|
7470
|
-
return;
|
|
7471
|
-
case "topP":
|
|
7472
|
-
setEditing({ key: action, label: "Top p", value: String(settings.modelSettings.topP) });
|
|
7473
|
-
return;
|
|
7474
|
-
case "markdownOutputDir":
|
|
7475
|
-
setEditing({ key: action, label: "Markdown output directory", value: settings.markdownOutputDir });
|
|
7476
|
-
return;
|
|
7477
|
-
case "assetOutputDir":
|
|
7478
|
-
setEditing({ key: action, label: "Asset output directory", value: settings.assetOutputDir });
|
|
7479
|
-
return;
|
|
7480
|
-
case "t2i-model":
|
|
7481
|
-
setShowModelSelect(true);
|
|
7482
|
-
return;
|
|
7483
|
-
case "save":
|
|
7484
|
-
onDone({ settings, secrets });
|
|
7485
|
-
exit();
|
|
7486
|
-
return;
|
|
7487
|
-
case "cancel":
|
|
7488
|
-
onDone(null);
|
|
7489
|
-
exit();
|
|
7490
|
-
return;
|
|
7491
|
-
default:
|
|
7492
|
-
if (action.startsWith("t2i:")) {
|
|
7493
|
-
const fieldName = action.slice(4);
|
|
7494
|
-
const currentModel = getT2IModel(settings.t2i.modelId);
|
|
7495
|
-
setEditing({
|
|
7496
|
-
key: action,
|
|
7497
|
-
label: `${currentModel.displayName} \u2022 ${fieldName}`,
|
|
7498
|
-
value: formatEditorValue(settings.t2i.inputOverrides[fieldName] ?? getT2IFieldDefault(settings.t2i.modelId, fieldName))
|
|
7499
|
-
});
|
|
7500
|
-
}
|
|
7501
|
-
}
|
|
7502
|
-
}
|
|
7503
|
-
function applyEdit(action, value2, settings, secrets, setSettings, setSecrets) {
|
|
7504
|
-
if (action === "openrouter") {
|
|
7505
|
-
setSecrets({ ...secrets, openRouterApiKey: value2.trim() || null });
|
|
7506
|
-
return;
|
|
7507
|
-
}
|
|
7508
|
-
if (action === "replicate") {
|
|
7509
|
-
setSecrets({ ...secrets, replicateApiToken: value2.trim() || null });
|
|
7510
|
-
return;
|
|
7511
|
-
}
|
|
7512
|
-
if (action === "llm-model") {
|
|
7513
|
-
setSettings({ ...settings, model: value2.trim() || settings.model });
|
|
7514
|
-
return;
|
|
7515
|
-
}
|
|
7516
|
-
if (action === "notifications-enabled") {
|
|
7517
|
-
const parsed = parseBooleanOrFallback(value2, settings.notifications.enabled);
|
|
7518
|
-
setSettings({
|
|
7519
|
-
...settings,
|
|
7520
|
-
notifications: {
|
|
7521
|
-
...settings.notifications,
|
|
7522
|
-
enabled: parsed
|
|
7523
|
-
}
|
|
7524
|
-
});
|
|
7525
|
-
return;
|
|
7526
|
-
}
|
|
7527
|
-
if (action === "temperature") {
|
|
7528
|
-
const nextTemperature = clampNumber2(parseNumberOrFallback(value2, settings.modelSettings.temperature), 0, 2);
|
|
7529
|
-
setSettings({
|
|
7530
|
-
...settings,
|
|
7531
|
-
modelSettings: {
|
|
7532
|
-
...settings.modelSettings,
|
|
7533
|
-
temperature: nextTemperature
|
|
7534
|
-
}
|
|
7535
|
-
});
|
|
7536
|
-
return;
|
|
7537
|
-
}
|
|
7538
|
-
if (action === "maxTokens") {
|
|
7539
|
-
const nextMaxTokens = Math.max(1, Math.round(parseNumberOrFallback(value2, settings.modelSettings.maxTokens)));
|
|
7540
|
-
setSettings({
|
|
7541
|
-
...settings,
|
|
7542
|
-
modelSettings: {
|
|
7543
|
-
...settings.modelSettings,
|
|
7544
|
-
maxTokens: nextMaxTokens
|
|
7545
|
-
}
|
|
7546
|
-
});
|
|
7547
|
-
return;
|
|
7548
|
-
}
|
|
7549
|
-
if (action === "topP") {
|
|
7550
|
-
const nextTopP = clampNumber2(parseNumberOrFallback(value2, settings.modelSettings.topP), 0, 1);
|
|
7551
|
-
setSettings({
|
|
7552
|
-
...settings,
|
|
7553
|
-
modelSettings: {
|
|
7554
|
-
...settings.modelSettings,
|
|
7555
|
-
topP: nextTopP
|
|
7556
|
-
}
|
|
7557
|
-
});
|
|
7558
|
-
return;
|
|
7559
|
-
}
|
|
7560
|
-
if (action === "markdownOutputDir") {
|
|
7561
|
-
setSettings({ ...settings, markdownOutputDir: value2.trim() || settings.markdownOutputDir });
|
|
7562
|
-
return;
|
|
7563
|
-
}
|
|
7564
|
-
if (action === "assetOutputDir") {
|
|
7565
|
-
setSettings({ ...settings, assetOutputDir: value2.trim() || settings.assetOutputDir });
|
|
7566
|
-
return;
|
|
7567
|
-
}
|
|
7568
|
-
if (action.startsWith("t2i:")) {
|
|
7569
|
-
const fieldName = action.slice(4);
|
|
7570
|
-
const parsedValue = coerceT2IFieldValue(settings.t2i.modelId, fieldName, value2);
|
|
7571
|
-
const nextOverrides = { ...settings.t2i.inputOverrides };
|
|
7572
|
-
if (parsedValue === void 0) {
|
|
7573
|
-
delete nextOverrides[fieldName];
|
|
7574
|
-
} else {
|
|
7575
|
-
nextOverrides[fieldName] = parsedValue;
|
|
7576
|
-
}
|
|
7577
|
-
setSettings({
|
|
7578
|
-
...settings,
|
|
7579
|
-
t2i: {
|
|
7580
|
-
...settings.t2i,
|
|
7581
|
-
inputOverrides: nextOverrides
|
|
7582
|
-
}
|
|
7583
|
-
});
|
|
7584
|
-
}
|
|
7585
|
-
}
|
|
7586
|
-
function formatEditorValue(value2) {
|
|
7587
|
-
if (value2 === null || value2 === void 0) {
|
|
7588
|
-
return "";
|
|
7589
|
-
}
|
|
7590
|
-
return String(value2);
|
|
7591
|
-
}
|
|
7592
|
-
function formatValue(value2) {
|
|
7593
|
-
if (value2 === null || value2 === void 0 || value2 === "") {
|
|
7594
|
-
return "(default)";
|
|
7595
|
-
}
|
|
7596
|
-
return String(value2);
|
|
7597
|
-
}
|
|
7598
|
-
function parseNumberOrFallback(value2, fallback) {
|
|
7599
|
-
const parsed = Number(value2.trim());
|
|
7600
|
-
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7601
|
-
}
|
|
7602
|
-
function clampNumber2(value2, minimum, maximum) {
|
|
7603
|
-
return Math.min(maximum, Math.max(minimum, value2));
|
|
7604
|
-
}
|
|
7605
|
-
function parseBooleanOrFallback(value2, fallback) {
|
|
7606
|
-
const normalized = value2.trim().toLowerCase();
|
|
7607
|
-
if (normalized === "true") {
|
|
7608
|
-
return true;
|
|
7609
|
-
}
|
|
7610
|
-
if (normalized === "false") {
|
|
7611
|
-
return false;
|
|
7612
|
-
}
|
|
7613
|
-
return fallback;
|
|
7614
|
-
}
|
|
7615
6944
|
|
|
7616
6945
|
// src/cli/commands/settings.tsx
|
|
7617
6946
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
@@ -9565,7 +8894,7 @@ async function runServeCommand(options) {
|
|
|
9565
8894
|
}
|
|
9566
8895
|
|
|
9567
8896
|
// src/cli/commands/write.tsx
|
|
9568
|
-
import React4, { useEffect as
|
|
8897
|
+
import React4, { useEffect as useEffect3, useState as useState4 } from "react";
|
|
9569
8898
|
import { render as render2, useApp as useApp3 } from "ink";
|
|
9570
8899
|
import { createInterface } from "readline/promises";
|
|
9571
8900
|
|
|
@@ -9692,7 +9021,7 @@ function FinalSummary({
|
|
|
9692
9021
|
}
|
|
9693
9022
|
|
|
9694
9023
|
// src/cli/ui/stageRow.tsx
|
|
9695
|
-
import { useEffect, useState as useState2 } from "react";
|
|
9024
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
9696
9025
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
9697
9026
|
|
|
9698
9027
|
// src/cli/ui/progressVisibility.ts
|
|
@@ -9766,7 +9095,7 @@ function StageRow({
|
|
|
9766
9095
|
maxVisibleItems = 12
|
|
9767
9096
|
}) {
|
|
9768
9097
|
const [frameIndex, setFrameIndex] = useState2(0);
|
|
9769
|
-
|
|
9098
|
+
useEffect2(() => {
|
|
9770
9099
|
if (!isActive || stage.status !== "running") {
|
|
9771
9100
|
setFrameIndex(0);
|
|
9772
9101
|
return;
|
|
@@ -9827,7 +9156,7 @@ function ItemRows({
|
|
|
9827
9156
|
}
|
|
9828
9157
|
function ItemRow({ item, isActive }) {
|
|
9829
9158
|
const [frameIndex, setFrameIndex] = useState2(0);
|
|
9830
|
-
|
|
9159
|
+
useEffect2(() => {
|
|
9831
9160
|
if (!isActive || item.status !== "running") {
|
|
9832
9161
|
setFrameIndex(0);
|
|
9833
9162
|
return;
|
|
@@ -10430,7 +9759,7 @@ function WriteApp({
|
|
|
10430
9759
|
);
|
|
10431
9760
|
const [result, setResult] = useState4(null);
|
|
10432
9761
|
const [errorMessage, setErrorMessage] = useState4(null);
|
|
10433
|
-
|
|
9762
|
+
useEffect3(() => {
|
|
10434
9763
|
let mounted = true;
|
|
10435
9764
|
void (async () => {
|
|
10436
9765
|
try {
|
|
@@ -10480,7 +9809,7 @@ function WriteApp({
|
|
|
10480
9809
|
mounted = false;
|
|
10481
9810
|
};
|
|
10482
9811
|
}, [dryRun, enrichLinks2, input, links, unlinks, maxLinks, maxImages, onError, runMode]);
|
|
10483
|
-
|
|
9812
|
+
useEffect3(() => {
|
|
10484
9813
|
if (!result && !errorMessage) {
|
|
10485
9814
|
return;
|
|
10486
9815
|
}
|