chatroom-cli 1.137.1 → 1.137.3
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/index.js +524 -142
- package/dist/index.js.map +21 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2140,7 +2140,241 @@ var require_commander = __commonJS((exports) => {
|
|
|
2140
2140
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2141
2141
|
});
|
|
2142
2142
|
|
|
2143
|
-
// src/commands/
|
|
2143
|
+
// src/commands/image-shared/image-preflight.ts
|
|
2144
|
+
import { createHash } from "node:crypto";
|
|
2145
|
+
function isRecord(value) {
|
|
2146
|
+
return typeof value === "object" && value !== null;
|
|
2147
|
+
}
|
|
2148
|
+
function sha256(bytes) {
|
|
2149
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
2150
|
+
}
|
|
2151
|
+
function errorMessage(err) {
|
|
2152
|
+
return err instanceof Error ? err.message : String(err);
|
|
2153
|
+
}
|
|
2154
|
+
function median(values) {
|
|
2155
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
2156
|
+
const middle = Math.floor(sorted.length / 2);
|
|
2157
|
+
if (sorted.length % 2 === 1)
|
|
2158
|
+
return sorted[middle] ?? 0;
|
|
2159
|
+
return Math.round(((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2);
|
|
2160
|
+
}
|
|
2161
|
+
function hasPngMagic(bytes) {
|
|
2162
|
+
if (bytes.length < PNG_MAGIC.length)
|
|
2163
|
+
return false;
|
|
2164
|
+
return PNG_MAGIC.every((byte, index) => bytes[index] === byte);
|
|
2165
|
+
}
|
|
2166
|
+
function readPngDimensions(bytes) {
|
|
2167
|
+
if (bytes.length < 24)
|
|
2168
|
+
return null;
|
|
2169
|
+
if (bytes[12] !== 73 || bytes[13] !== 72 || bytes[14] !== 68 || bytes[15] !== 82) {
|
|
2170
|
+
return null;
|
|
2171
|
+
}
|
|
2172
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
2173
|
+
return { width: view.getUint32(16), height: view.getUint32(20) };
|
|
2174
|
+
}
|
|
2175
|
+
function formatConflicts(conflicts) {
|
|
2176
|
+
const shown = conflicts.slice(0, 10);
|
|
2177
|
+
const lines = shown.map((conflict) => `- ${conflict.path}: ${conflict.reason}`);
|
|
2178
|
+
if (conflicts.length > shown.length) {
|
|
2179
|
+
lines.push(`- and ${conflicts.length - shown.length} more`);
|
|
2180
|
+
}
|
|
2181
|
+
return `Refusing to write ${conflicts.length} output file(s):
|
|
2182
|
+
${lines.join(`
|
|
2183
|
+
`)}
|
|
2184
|
+
Pass --force to overwrite.`;
|
|
2185
|
+
}
|
|
2186
|
+
async function findOutputConflicts(deps, paths, sourcePaths, sourceIdentities, force) {
|
|
2187
|
+
const matchesSourceIdentity = (identity) => sourceIdentities.some((source) => source !== null && source.dev === identity.dev && source.ino === identity.ino);
|
|
2188
|
+
const conflicts = [];
|
|
2189
|
+
for (const path of paths) {
|
|
2190
|
+
if (sourcePaths.has(path)) {
|
|
2191
|
+
conflicts.push({ path, reason: "this is the source sheet or its manifest" });
|
|
2192
|
+
continue;
|
|
2193
|
+
}
|
|
2194
|
+
if (await deps.isSymlink(path)) {
|
|
2195
|
+
conflicts.push({ path, reason: "this is a symbolic link" });
|
|
2196
|
+
continue;
|
|
2197
|
+
}
|
|
2198
|
+
const status = await deps.pathStatus(path);
|
|
2199
|
+
if (status === "directory") {
|
|
2200
|
+
conflicts.push({ path, reason: "a directory is in the way" });
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2203
|
+
const identity = await deps.fileIdentity(path);
|
|
2204
|
+
if (identity !== null && matchesSourceIdentity(identity)) {
|
|
2205
|
+
conflicts.push({ path, reason: "this resolves to the source sheet or its manifest" });
|
|
2206
|
+
continue;
|
|
2207
|
+
}
|
|
2208
|
+
if (status === "file" && !force) {
|
|
2209
|
+
conflicts.push({ path, reason: "already exists" });
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
return conflicts;
|
|
2213
|
+
}
|
|
2214
|
+
var SAFE_NAME, MAX_SOURCE_PIXELS = 1e8, MAX_SOURCE_BYTES, MAX_SIDECAR_BYTES, PNG_MAGIC;
|
|
2215
|
+
var init_image_preflight = __esm(() => {
|
|
2216
|
+
SAFE_NAME = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,63}$/;
|
|
2217
|
+
MAX_SOURCE_BYTES = 64 * 1024 * 1024;
|
|
2218
|
+
MAX_SIDECAR_BYTES = 1024 * 1024;
|
|
2219
|
+
PNG_MAGIC = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
2220
|
+
});
|
|
2221
|
+
|
|
2222
|
+
// src/commands/generate-image/fal-pricing.ts
|
|
2223
|
+
function listUnitPrices(endpointIds) {
|
|
2224
|
+
const prices = new Map;
|
|
2225
|
+
for (const endpointId of endpointIds) {
|
|
2226
|
+
const fallback = LIST_PRICES[endpointId];
|
|
2227
|
+
if (fallback !== undefined) {
|
|
2228
|
+
prices.set(endpointId, { endpointId, ...fallback, rateSource: "list" });
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
return prices;
|
|
2232
|
+
}
|
|
2233
|
+
function parseLivePrice(entry) {
|
|
2234
|
+
if (!isRecord(entry))
|
|
2235
|
+
return;
|
|
2236
|
+
const { endpoint_id, unit_price, unit, currency } = entry;
|
|
2237
|
+
if (typeof endpoint_id !== "string" || endpoint_id.length === 0)
|
|
2238
|
+
return;
|
|
2239
|
+
if (typeof unit_price !== "number" || !Number.isFinite(unit_price) || unit_price < 0) {
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
if (typeof unit !== "string" || unit.length === 0)
|
|
2243
|
+
return;
|
|
2244
|
+
if (currency !== undefined && currency !== "USD")
|
|
2245
|
+
return;
|
|
2246
|
+
return { endpointId: endpoint_id, unitPrice: unit_price, unit, rateSource: "live" };
|
|
2247
|
+
}
|
|
2248
|
+
function livePricesByEndpoint(body) {
|
|
2249
|
+
const prices = new Map;
|
|
2250
|
+
if (!isRecord(body) || !Array.isArray(body.prices))
|
|
2251
|
+
return prices;
|
|
2252
|
+
for (const entry of body.prices) {
|
|
2253
|
+
const price = parseLivePrice(entry);
|
|
2254
|
+
if (price !== undefined)
|
|
2255
|
+
prices.set(price.endpointId, price);
|
|
2256
|
+
}
|
|
2257
|
+
return prices;
|
|
2258
|
+
}
|
|
2259
|
+
async function lookupFalUnitPrices(endpointIds, deps) {
|
|
2260
|
+
const ids = [...new Set(endpointIds)];
|
|
2261
|
+
const key = deps.env.FAL_API_KEY?.trim();
|
|
2262
|
+
if (!key || ids.length === 0)
|
|
2263
|
+
return listUnitPrices(ids);
|
|
2264
|
+
try {
|
|
2265
|
+
const url = new URL(PRICING_URL);
|
|
2266
|
+
for (const endpointId of ids)
|
|
2267
|
+
url.searchParams.append("endpoint_id", endpointId);
|
|
2268
|
+
const body = await deps.getJson(url.toString(), { Authorization: `Key ${key}` }, PRICING_TIMEOUT_MS);
|
|
2269
|
+
const live = livePricesByEndpoint(body);
|
|
2270
|
+
const list = listUnitPrices(ids);
|
|
2271
|
+
const prices = new Map;
|
|
2272
|
+
for (const endpointId of ids) {
|
|
2273
|
+
const price = live.get(endpointId) ?? list.get(endpointId);
|
|
2274
|
+
if (price !== undefined)
|
|
2275
|
+
prices.set(endpointId, price);
|
|
2276
|
+
}
|
|
2277
|
+
return prices;
|
|
2278
|
+
} catch {
|
|
2279
|
+
return listUnitPrices(ids);
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
async function lookupUnitPrice(endpointId, lookupPrices) {
|
|
2283
|
+
try {
|
|
2284
|
+
const price = (await lookupPrices([endpointId])).get(endpointId);
|
|
2285
|
+
if (price !== undefined)
|
|
2286
|
+
return price;
|
|
2287
|
+
} catch {}
|
|
2288
|
+
return listUnitPrices([endpointId]).get(endpointId);
|
|
2289
|
+
}
|
|
2290
|
+
function estimateUsd(price, quantity) {
|
|
2291
|
+
return quantity * price.unitPrice;
|
|
2292
|
+
}
|
|
2293
|
+
function formatUsd(value) {
|
|
2294
|
+
if (!Number.isFinite(value))
|
|
2295
|
+
return "unknown";
|
|
2296
|
+
if (value === 0)
|
|
2297
|
+
return "$0";
|
|
2298
|
+
const magnitude = Math.floor(Math.log10(Math.abs(value)));
|
|
2299
|
+
const decimals = Math.min(12, Math.max(4, 3 - magnitude));
|
|
2300
|
+
return `$${value.toFixed(decimals)}`;
|
|
2301
|
+
}
|
|
2302
|
+
function imageSizeMegapixels(imageSize) {
|
|
2303
|
+
if (typeof imageSize === "string") {
|
|
2304
|
+
const preset = IMAGE_SIZE_PRESETS[imageSize];
|
|
2305
|
+
if (preset === undefined)
|
|
2306
|
+
return;
|
|
2307
|
+
return preset.width * preset.height / 1e6;
|
|
2308
|
+
}
|
|
2309
|
+
if (!Number.isFinite(imageSize.width) || !Number.isFinite(imageSize.height) || imageSize.width <= 0 || imageSize.height <= 0) {
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
return imageSize.width * imageSize.height / 1e6;
|
|
2313
|
+
}
|
|
2314
|
+
function displayUnit(unit) {
|
|
2315
|
+
return DISPLAY_UNITS[unit] ?? unit;
|
|
2316
|
+
}
|
|
2317
|
+
function formatRate(price) {
|
|
2318
|
+
return `${formatUsd(price.unitPrice)} per ${displayUnit(price.unit)}`;
|
|
2319
|
+
}
|
|
2320
|
+
function formatRateSource(price) {
|
|
2321
|
+
return price.rateSource === "live" ? "live rate for this account" : `list price as of ${LIST_PRICES_AS_OF}; live lookup unavailable`;
|
|
2322
|
+
}
|
|
2323
|
+
function formatCostEstimate(price, quantity, quantityLabel) {
|
|
2324
|
+
return `${formatRate(price)} (${formatRateSource(price)}) × ${quantityLabel} ≈ ${formatUsd(estimateUsd(price, quantity))}`;
|
|
2325
|
+
}
|
|
2326
|
+
function formatCostDisclosure(disclosure, label = "Cost") {
|
|
2327
|
+
if (disclosure.kind === "unknown") {
|
|
2328
|
+
return `${label}: unknown for ${disclosure.model}; no rate data.`;
|
|
2329
|
+
}
|
|
2330
|
+
if (disclosure.kind === "rate-only") {
|
|
2331
|
+
return `${label} (rate only): ${formatRate(disclosure.price)} (${formatRateSource(disclosure.price)}). ${disclosure.reason}`;
|
|
2332
|
+
}
|
|
2333
|
+
const estimateLabel = label === "Estimated cost" ? label : `${label} (estimate)`;
|
|
2334
|
+
return `${estimateLabel}: ${formatCostEstimate(disclosure.price, disclosure.quantity, disclosure.quantityLabel)}`;
|
|
2335
|
+
}
|
|
2336
|
+
function createDefaultFalPricingDeps(env = process.env) {
|
|
2337
|
+
return {
|
|
2338
|
+
env,
|
|
2339
|
+
getJson: async (url, headers, timeoutMs) => {
|
|
2340
|
+
const response = await fetch(url, {
|
|
2341
|
+
headers: { Accept: "application/json", ...headers },
|
|
2342
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
2343
|
+
redirect: "error"
|
|
2344
|
+
});
|
|
2345
|
+
if (!response.ok) {
|
|
2346
|
+
throw new Error(`fal pricing lookup failed (HTTP ${response.status})`);
|
|
2347
|
+
}
|
|
2348
|
+
return await response.json();
|
|
2349
|
+
}
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
var LIST_PRICES_AS_OF = "2026-10-25", LIST_PRICES, IMAGE_SIZE_PRESETS, PRICING_TIMEOUT_MS = 3000, PRICING_URL = "https://api.fal.ai/v1/models/pricing", DISPLAY_UNITS;
|
|
2353
|
+
var init_fal_pricing = __esm(() => {
|
|
2354
|
+
init_image_preflight();
|
|
2355
|
+
LIST_PRICES = {
|
|
2356
|
+
"fal-ai/flux-2/flash": { unitPrice: 0.005, unit: "megapixels" },
|
|
2357
|
+
"fal-ai/flux-2/flash/edit": { unitPrice: 0.005, unit: "processed megapixels" },
|
|
2358
|
+
"fal-ai/bria/background/remove": { unitPrice: 0.018, unit: "generations" },
|
|
2359
|
+
"fal-ai/birefnet/v2": { unitPrice: 0.000575, unit: "compute seconds" }
|
|
2360
|
+
};
|
|
2361
|
+
IMAGE_SIZE_PRESETS = {
|
|
2362
|
+
square_hd: { width: 1024, height: 1024 },
|
|
2363
|
+
square: { width: 512, height: 512 },
|
|
2364
|
+
portrait_4_3: { width: 768, height: 1024 },
|
|
2365
|
+
portrait_16_9: { width: 576, height: 1024 },
|
|
2366
|
+
landscape_4_3: { width: 1024, height: 768 },
|
|
2367
|
+
landscape_16_9: { width: 1024, height: 576 }
|
|
2368
|
+
};
|
|
2369
|
+
DISPLAY_UNITS = {
|
|
2370
|
+
megapixels: "megapixel",
|
|
2371
|
+
"processed megapixels": "processed megapixel",
|
|
2372
|
+
generations: "generation",
|
|
2373
|
+
"compute seconds": "compute second"
|
|
2374
|
+
};
|
|
2375
|
+
});
|
|
2376
|
+
|
|
2377
|
+
// src/commands/generate-image/model-capabilities.ts
|
|
2144
2378
|
function getModelCapability(id) {
|
|
2145
2379
|
return Object.prototype.hasOwnProperty.call(MODEL_CAPABILITIES, id) ? MODEL_CAPABILITIES[id] : undefined;
|
|
2146
2380
|
}
|
|
@@ -2278,7 +2512,7 @@ var init_model_capabilities = __esm(() => {
|
|
|
2278
2512
|
};
|
|
2279
2513
|
});
|
|
2280
2514
|
|
|
2281
|
-
// src/commands/generate/image.ts
|
|
2515
|
+
// src/commands/generate-image/image.ts
|
|
2282
2516
|
var exports_image = {};
|
|
2283
2517
|
__export(exports_image, {
|
|
2284
2518
|
planGenerateImage: () => planGenerateImage,
|
|
@@ -2290,7 +2524,7 @@ __export(exports_image, {
|
|
|
2290
2524
|
FAL_API_KEY_MISSING_MESSAGE: () => FAL_API_KEY_MISSING_MESSAGE,
|
|
2291
2525
|
DEFAULT_GENERATE_IMAGE_PLATFORM: () => DEFAULT_GENERATE_IMAGE_PLATFORM
|
|
2292
2526
|
});
|
|
2293
|
-
import { createHash } from "node:crypto";
|
|
2527
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2294
2528
|
import { extname, resolve } from "node:path";
|
|
2295
2529
|
function resolvePlatform(platform) {
|
|
2296
2530
|
return platform ?? DEFAULT_GENERATE_IMAGE_PLATFORM;
|
|
@@ -2494,6 +2728,155 @@ async function uploadInputImage(deps, target) {
|
|
|
2494
2728
|
const url = await deps.uploadFile(target.path, target.contentType);
|
|
2495
2729
|
return { url, record: { source: target.path, url } };
|
|
2496
2730
|
}
|
|
2731
|
+
function formatMegapixels(megapixels) {
|
|
2732
|
+
return `${megapixels.toFixed(2)} MP`;
|
|
2733
|
+
}
|
|
2734
|
+
function readImageSizeValue(value) {
|
|
2735
|
+
if (typeof value === "string")
|
|
2736
|
+
return value;
|
|
2737
|
+
if (isPlainObject(value) && typeof value.width === "number" && typeof value.height === "number") {
|
|
2738
|
+
return { width: value.width, height: value.height };
|
|
2739
|
+
}
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
function plannedOutputSize(model, input) {
|
|
2743
|
+
const capability = getModelCapability(model);
|
|
2744
|
+
const support = capability?.params.image_size;
|
|
2745
|
+
const value = input.image_size !== undefined ? input.image_size : support?.support === "supported" ? support.default : undefined;
|
|
2746
|
+
if (value === undefined) {
|
|
2747
|
+
return {
|
|
2748
|
+
megapixels: undefined,
|
|
2749
|
+
reason: "The image_size is unset and the model has no known default; output size not estimated."
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
const size = readImageSizeValue(value);
|
|
2753
|
+
if (size === undefined) {
|
|
2754
|
+
return {
|
|
2755
|
+
megapixels: undefined,
|
|
2756
|
+
reason: `The image_size ${JSON.stringify(value)} is not a preset name or {width,height}; output size not estimated.`
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
const megapixels = imageSizeMegapixels(size);
|
|
2760
|
+
if (megapixels === undefined) {
|
|
2761
|
+
return {
|
|
2762
|
+
megapixels: undefined,
|
|
2763
|
+
reason: `The image_size ${JSON.stringify(value)} is not a known preset; output size not estimated.`
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
return { megapixels, reason: undefined };
|
|
2767
|
+
}
|
|
2768
|
+
async function inputSizeNote(deps, target) {
|
|
2769
|
+
if (target === undefined)
|
|
2770
|
+
return EDIT_INPUT_NOTE;
|
|
2771
|
+
if (target.kind === "url") {
|
|
2772
|
+
return "Input is a URL; fal resizes it to 1 MP, so the estimate counts 1 MP of input.";
|
|
2773
|
+
}
|
|
2774
|
+
if (target.contentType !== "image/png") {
|
|
2775
|
+
return "Input is not a PNG; fal resizes it to 1 MP, so the estimate counts 1 MP of input.";
|
|
2776
|
+
}
|
|
2777
|
+
try {
|
|
2778
|
+
const dimensions = readPngDimensions(await deps.readFileBytes(target.path));
|
|
2779
|
+
if (dimensions === null || dimensions.width === 0 || dimensions.height === 0) {
|
|
2780
|
+
return EDIT_INPUT_NOTE;
|
|
2781
|
+
}
|
|
2782
|
+
const megapixels = dimensions.width * dimensions.height / 1e6;
|
|
2783
|
+
return `Input is ${dimensions.width}x${dimensions.height} (${formatMegapixels(megapixels)}); fal resizes it to 1 MP, so the estimate counts 1 MP of input.`;
|
|
2784
|
+
} catch {
|
|
2785
|
+
return EDIT_INPUT_NOTE;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
async function lookupPrice(deps, model) {
|
|
2789
|
+
return lookupUnitPrice(model, (ids) => deps.lookupPrices(ids));
|
|
2790
|
+
}
|
|
2791
|
+
async function buildGenerateImageCost(args) {
|
|
2792
|
+
const price = await lookupPrice(args.deps, args.model);
|
|
2793
|
+
const output = plannedOutputSize(args.model, args.input);
|
|
2794
|
+
const capability = getModelCapability(args.model);
|
|
2795
|
+
if (capability?.task === "image-edit") {
|
|
2796
|
+
const notes = [
|
|
2797
|
+
"Formula (unverified): (1 MP input + output MP) × rate.",
|
|
2798
|
+
await inputSizeNote(args.deps, args.inputTarget)
|
|
2799
|
+
];
|
|
2800
|
+
if (output.megapixels === undefined) {
|
|
2801
|
+
return {
|
|
2802
|
+
price,
|
|
2803
|
+
quantity: undefined,
|
|
2804
|
+
quantityLabel: undefined,
|
|
2805
|
+
inputMegapixels: EDIT_INPUT_MEGAPIXELS,
|
|
2806
|
+
reason: output.reason,
|
|
2807
|
+
notes
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
const quantity = EDIT_INPUT_MEGAPIXELS + output.megapixels;
|
|
2811
|
+
return {
|
|
2812
|
+
price,
|
|
2813
|
+
quantity,
|
|
2814
|
+
quantityLabel: `${formatMegapixels(quantity)} (${formatMegapixels(EDIT_INPUT_MEGAPIXELS)} input + ${formatMegapixels(output.megapixels)} output)`,
|
|
2815
|
+
inputMegapixels: EDIT_INPUT_MEGAPIXELS,
|
|
2816
|
+
reason: undefined,
|
|
2817
|
+
notes
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
if (output.megapixels === undefined) {
|
|
2821
|
+
return {
|
|
2822
|
+
price,
|
|
2823
|
+
quantity: undefined,
|
|
2824
|
+
quantityLabel: undefined,
|
|
2825
|
+
inputMegapixels: undefined,
|
|
2826
|
+
reason: output.reason,
|
|
2827
|
+
notes: []
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
return {
|
|
2831
|
+
price,
|
|
2832
|
+
quantity: output.megapixels,
|
|
2833
|
+
quantityLabel: `${formatMegapixels(output.megapixels)} (planned output)`,
|
|
2834
|
+
inputMegapixels: undefined,
|
|
2835
|
+
reason: undefined,
|
|
2836
|
+
notes: []
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
function formatGenerateImageCost(cost, model, label = "Cost") {
|
|
2840
|
+
const disclosure = cost.price === undefined ? { kind: "unknown", model } : cost.quantity === undefined ? {
|
|
2841
|
+
kind: "rate-only",
|
|
2842
|
+
model,
|
|
2843
|
+
price: cost.price,
|
|
2844
|
+
reason: cost.reason ?? "Quantity not estimated."
|
|
2845
|
+
} : {
|
|
2846
|
+
kind: "estimate",
|
|
2847
|
+
model,
|
|
2848
|
+
price: cost.price,
|
|
2849
|
+
quantity: cost.quantity,
|
|
2850
|
+
quantityLabel: cost.quantityLabel ?? formatMegapixels(cost.quantity)
|
|
2851
|
+
};
|
|
2852
|
+
const note = cost.price === undefined ? [] : [cost.quantity === undefined ? ACTUAL_COST_NOTE : PLANNED_COST_NOTE];
|
|
2853
|
+
return [formatCostDisclosure(disclosure, label), ...note, ...cost.notes];
|
|
2854
|
+
}
|
|
2855
|
+
function actualCostLines(plan, image) {
|
|
2856
|
+
const { price, inputMegapixels } = plan.cost;
|
|
2857
|
+
if (price === undefined) {
|
|
2858
|
+
return formatGenerateImageCost(plan.cost, plan.model, "Estimated cost");
|
|
2859
|
+
}
|
|
2860
|
+
const width = image.width;
|
|
2861
|
+
const height = image.height;
|
|
2862
|
+
const hasDimensions = width !== undefined && height !== undefined && Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0;
|
|
2863
|
+
if (!hasDimensions) {
|
|
2864
|
+
if (plan.cost.quantity === undefined) {
|
|
2865
|
+
return formatGenerateImageCost(plan.cost, plan.model, "Estimated cost");
|
|
2866
|
+
}
|
|
2867
|
+
return [
|
|
2868
|
+
`Estimated cost (planned size): ${formatCostEstimate(price, plan.cost.quantity, plan.cost.quantityLabel ?? formatMegapixels(plan.cost.quantity))}`,
|
|
2869
|
+
ACTUAL_COST_NOTE
|
|
2870
|
+
];
|
|
2871
|
+
}
|
|
2872
|
+
const outputMegapixels = width * height / 1e6;
|
|
2873
|
+
const quantity = inputMegapixels === undefined ? outputMegapixels : inputMegapixels + outputMegapixels;
|
|
2874
|
+
const quantityLabel = inputMegapixels === undefined ? `${formatMegapixels(outputMegapixels)} (actual ${width}x${height})` : `${formatMegapixels(quantity)} (actual ${width}x${height}; ${formatMegapixels(inputMegapixels)} input + ${formatMegapixels(outputMegapixels)} output)`;
|
|
2875
|
+
return [
|
|
2876
|
+
`Estimated cost: ${formatCostEstimate(price, quantity, quantityLabel)}`,
|
|
2877
|
+
ACTUAL_COST_NOTE
|
|
2878
|
+
];
|
|
2879
|
+
}
|
|
2497
2880
|
async function planGenerateImage(opts, deps) {
|
|
2498
2881
|
const platform = resolvePlatform(opts.platform);
|
|
2499
2882
|
requireSupportedPlatform(platform);
|
|
@@ -2511,6 +2894,7 @@ async function planGenerateImage(opts, deps) {
|
|
|
2511
2894
|
const manifestPath = `${outPath}.json`;
|
|
2512
2895
|
await requireWritableOutput(deps, outPath, manifestPath, opts.force === true);
|
|
2513
2896
|
const input = buildInput(prompt, opts, planInputImageUrl(inputTarget));
|
|
2897
|
+
const cost = await buildGenerateImageCost({ model, input, inputTarget, deps });
|
|
2514
2898
|
return {
|
|
2515
2899
|
platform,
|
|
2516
2900
|
model,
|
|
@@ -2518,7 +2902,8 @@ async function planGenerateImage(opts, deps) {
|
|
|
2518
2902
|
manifestPath,
|
|
2519
2903
|
input,
|
|
2520
2904
|
inputTarget,
|
|
2521
|
-
disclosures: describeConstraints(model, input, { aspectRatio: opts.aspectRatio })
|
|
2905
|
+
disclosures: describeConstraints(model, input, { aspectRatio: opts.aspectRatio }),
|
|
2906
|
+
cost
|
|
2522
2907
|
};
|
|
2523
2908
|
}
|
|
2524
2909
|
function disclosureFlag(key) {
|
|
@@ -2548,7 +2933,8 @@ function formatGenerateImagePlan(plan) {
|
|
|
2548
2933
|
lines.push("Request:", JSON.stringify(plan.input, null, 2), "Constraints:");
|
|
2549
2934
|
for (const disclosure of plan.disclosures)
|
|
2550
2935
|
lines.push(formatDisclosure(disclosure));
|
|
2551
|
-
lines.push(
|
|
2936
|
+
lines.push(...formatGenerateImageCost(plan.cost, plan.model));
|
|
2937
|
+
lines.push("No upload or fal generation call was made.");
|
|
2552
2938
|
return lines.join(`
|
|
2553
2939
|
`);
|
|
2554
2940
|
}
|
|
@@ -2563,7 +2949,7 @@ async function generateAndSave(deps, model, input, outPath) {
|
|
|
2563
2949
|
return {
|
|
2564
2950
|
image,
|
|
2565
2951
|
bytes,
|
|
2566
|
-
sha256:
|
|
2952
|
+
sha256: createHash2("sha256").update(bytes).digest("hex"),
|
|
2567
2953
|
requestId: result.requestId,
|
|
2568
2954
|
seed: result.data.seed
|
|
2569
2955
|
};
|
|
@@ -2576,10 +2962,14 @@ async function generateImage(opts, deps) {
|
|
|
2576
2962
|
deps.warn(`⚠️ ${disclosure.key}: ${disclosure.message}`);
|
|
2577
2963
|
}
|
|
2578
2964
|
}
|
|
2965
|
+
for (const line of formatGenerateImageCost(plan.cost, plan.model)) {
|
|
2966
|
+
deps.log(line);
|
|
2967
|
+
}
|
|
2579
2968
|
await deps.ensureParentDir(plan.outPath);
|
|
2580
2969
|
const preparedInput = await uploadInputImage(deps, plan.inputTarget);
|
|
2581
2970
|
const input = preparedInput.url === undefined ? plan.input : { ...plan.input, image_urls: [preparedInput.url] };
|
|
2582
2971
|
const saved = await generateAndSave(deps, plan.model, input, plan.outPath);
|
|
2972
|
+
const costLines = actualCostLines(plan, saved.image);
|
|
2583
2973
|
await deps.writeJson(plan.manifestPath, buildManifest({
|
|
2584
2974
|
platform: plan.platform,
|
|
2585
2975
|
model: plan.model,
|
|
@@ -2595,11 +2985,18 @@ async function generateImage(opts, deps) {
|
|
|
2595
2985
|
generatedAt: deps.now().toISOString(),
|
|
2596
2986
|
sdkVersion: deps.sdkVersion
|
|
2597
2987
|
}));
|
|
2598
|
-
return {
|
|
2988
|
+
return {
|
|
2989
|
+
outPath: plan.outPath,
|
|
2990
|
+
manifestPath: plan.manifestPath,
|
|
2991
|
+
sha256: saved.sha256,
|
|
2992
|
+
costLines
|
|
2993
|
+
};
|
|
2599
2994
|
}
|
|
2600
|
-
var GENERATE_IMAGE_PLATFORMS, DEFAULT_GENERATE_IMAGE_PLATFORM = "fal", GenerateImageError, LICENSING = "unverified", FAL_API_KEY_MISSING_MESSAGE = "FAL_API_KEY is not set. Set FAL_API_KEY in your environment to generate images with fal. (FAL_KEY_ID/FAL_KEY_SECRET are not supported by this command.)", EXTENSION_FORMATS, UNSUPPORTED_OUTPUT_EXTENSIONS, INPUT_DEDICATED_FLAGS, INPUT_IMAGE_CONTENT_TYPES, HTTP_URL_PATTERN;
|
|
2995
|
+
var GENERATE_IMAGE_PLATFORMS, DEFAULT_GENERATE_IMAGE_PLATFORM = "fal", GenerateImageError, LICENSING = "unverified", FAL_API_KEY_MISSING_MESSAGE = "FAL_API_KEY is not set. Set FAL_API_KEY in your environment to generate images with fal. (FAL_KEY_ID/FAL_KEY_SECRET are not supported by this command.)", EXTENSION_FORMATS, UNSUPPORTED_OUTPUT_EXTENSIONS, INPUT_DEDICATED_FLAGS, INPUT_IMAGE_CONTENT_TYPES, HTTP_URL_PATTERN, PLANNED_COST_NOTE = "fal returns no billed cost; this is computed from the planned image size.", ACTUAL_COST_NOTE = "fal returns no billed cost; check the fal dashboard for the actual bill.", EDIT_INPUT_MEGAPIXELS = 1, EDIT_INPUT_NOTE = "fal resizes the input image to 1 MP; the estimate counts 1 MP of input regardless of its dimensions.";
|
|
2601
2996
|
var init_image = __esm(() => {
|
|
2997
|
+
init_fal_pricing();
|
|
2602
2998
|
init_model_capabilities();
|
|
2999
|
+
init_image_preflight();
|
|
2603
3000
|
GENERATE_IMAGE_PLATFORMS = ["fal"];
|
|
2604
3001
|
GenerateImageError = class GenerateImageError extends Error {
|
|
2605
3002
|
constructor(message) {
|
|
@@ -2629,85 +3026,6 @@ var init_image = __esm(() => {
|
|
|
2629
3026
|
HTTP_URL_PATTERN = /^https?:\/\//i;
|
|
2630
3027
|
});
|
|
2631
3028
|
|
|
2632
|
-
// src/commands/generate/local-image-common.ts
|
|
2633
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
2634
|
-
function isRecord(value) {
|
|
2635
|
-
return typeof value === "object" && value !== null;
|
|
2636
|
-
}
|
|
2637
|
-
function sha256(bytes) {
|
|
2638
|
-
return createHash2("sha256").update(bytes).digest("hex");
|
|
2639
|
-
}
|
|
2640
|
-
function errorMessage(err) {
|
|
2641
|
-
return err instanceof Error ? err.message : String(err);
|
|
2642
|
-
}
|
|
2643
|
-
function median(values) {
|
|
2644
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
2645
|
-
const middle = Math.floor(sorted.length / 2);
|
|
2646
|
-
if (sorted.length % 2 === 1)
|
|
2647
|
-
return sorted[middle] ?? 0;
|
|
2648
|
-
return Math.round(((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2);
|
|
2649
|
-
}
|
|
2650
|
-
function hasPngMagic(bytes) {
|
|
2651
|
-
if (bytes.length < PNG_MAGIC.length)
|
|
2652
|
-
return false;
|
|
2653
|
-
return PNG_MAGIC.every((byte, index) => bytes[index] === byte);
|
|
2654
|
-
}
|
|
2655
|
-
function readPngDimensions(bytes) {
|
|
2656
|
-
if (bytes.length < 24)
|
|
2657
|
-
return null;
|
|
2658
|
-
if (bytes[12] !== 73 || bytes[13] !== 72 || bytes[14] !== 68 || bytes[15] !== 82) {
|
|
2659
|
-
return null;
|
|
2660
|
-
}
|
|
2661
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
2662
|
-
return { width: view.getUint32(16), height: view.getUint32(20) };
|
|
2663
|
-
}
|
|
2664
|
-
function formatConflicts(conflicts) {
|
|
2665
|
-
const shown = conflicts.slice(0, 10);
|
|
2666
|
-
const lines = shown.map((conflict) => `- ${conflict.path}: ${conflict.reason}`);
|
|
2667
|
-
if (conflicts.length > shown.length) {
|
|
2668
|
-
lines.push(`- and ${conflicts.length - shown.length} more`);
|
|
2669
|
-
}
|
|
2670
|
-
return `Refusing to write ${conflicts.length} output file(s):
|
|
2671
|
-
${lines.join(`
|
|
2672
|
-
`)}
|
|
2673
|
-
Pass --force to overwrite.`;
|
|
2674
|
-
}
|
|
2675
|
-
async function findOutputConflicts(deps, paths, sourcePaths, sourceIdentities, force) {
|
|
2676
|
-
const matchesSourceIdentity = (identity) => sourceIdentities.some((source) => source !== null && source.dev === identity.dev && source.ino === identity.ino);
|
|
2677
|
-
const conflicts = [];
|
|
2678
|
-
for (const path of paths) {
|
|
2679
|
-
if (sourcePaths.has(path)) {
|
|
2680
|
-
conflicts.push({ path, reason: "this is the source sheet or its manifest" });
|
|
2681
|
-
continue;
|
|
2682
|
-
}
|
|
2683
|
-
if (await deps.isSymlink(path)) {
|
|
2684
|
-
conflicts.push({ path, reason: "this is a symbolic link" });
|
|
2685
|
-
continue;
|
|
2686
|
-
}
|
|
2687
|
-
const status = await deps.pathStatus(path);
|
|
2688
|
-
if (status === "directory") {
|
|
2689
|
-
conflicts.push({ path, reason: "a directory is in the way" });
|
|
2690
|
-
continue;
|
|
2691
|
-
}
|
|
2692
|
-
const identity = await deps.fileIdentity(path);
|
|
2693
|
-
if (identity !== null && matchesSourceIdentity(identity)) {
|
|
2694
|
-
conflicts.push({ path, reason: "this resolves to the source sheet or its manifest" });
|
|
2695
|
-
continue;
|
|
2696
|
-
}
|
|
2697
|
-
if (status === "file" && !force) {
|
|
2698
|
-
conflicts.push({ path, reason: "already exists" });
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
|
-
return conflicts;
|
|
2702
|
-
}
|
|
2703
|
-
var SAFE_NAME, MAX_SOURCE_PIXELS = 1e8, MAX_SOURCE_BYTES, MAX_SIDECAR_BYTES, PNG_MAGIC;
|
|
2704
|
-
var init_local_image_common = __esm(() => {
|
|
2705
|
-
SAFE_NAME = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,63}$/;
|
|
2706
|
-
MAX_SOURCE_BYTES = 64 * 1024 * 1024;
|
|
2707
|
-
MAX_SIDECAR_BYTES = 1024 * 1024;
|
|
2708
|
-
PNG_MAGIC = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
2709
|
-
});
|
|
2710
|
-
|
|
2711
3029
|
// ../../node_modules/.pnpm/@fal-ai+client@1.10.1/node_modules/@fal-ai/client/src/middleware.js
|
|
2712
3030
|
var require_middleware = __commonJS((exports) => {
|
|
2713
3031
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
@@ -6736,7 +7054,7 @@ var require_src = __commonJS((exports) => {
|
|
|
6736
7054
|
}();
|
|
6737
7055
|
});
|
|
6738
7056
|
|
|
6739
|
-
// src/commands/
|
|
7057
|
+
// src/commands/image-shared/output-files.ts
|
|
6740
7058
|
import { stat, writeFile } from "node:fs/promises";
|
|
6741
7059
|
async function pathStatus(path) {
|
|
6742
7060
|
try {
|
|
@@ -6765,9 +7083,9 @@ async function writeJsonFile(path, value, options = {}) {
|
|
|
6765
7083
|
}
|
|
6766
7084
|
await writeFile(path, text, "utf8");
|
|
6767
7085
|
}
|
|
6768
|
-
var
|
|
7086
|
+
var init_output_files = () => {};
|
|
6769
7087
|
|
|
6770
|
-
// src/commands/
|
|
7088
|
+
// src/commands/image-shared/package-version.ts
|
|
6771
7089
|
import { readFileSync } from "node:fs";
|
|
6772
7090
|
import { createRequire as createRequire2 } from "node:module";
|
|
6773
7091
|
import { dirname, join } from "node:path";
|
|
@@ -6785,10 +7103,9 @@ function readInstalledPackageVersion(specifier) {
|
|
|
6785
7103
|
var UNKNOWN_VERSION = "unknown";
|
|
6786
7104
|
var init_package_version = () => {};
|
|
6787
7105
|
|
|
6788
|
-
// src/commands/generate/deps.ts
|
|
7106
|
+
// src/commands/generate-image/deps.ts
|
|
6789
7107
|
var exports_deps = {};
|
|
6790
7108
|
__export(exports_deps, {
|
|
6791
|
-
fetchBytes: () => fetchBytes,
|
|
6792
7109
|
createFalRawSubscribe: () => createFalRawSubscribe,
|
|
6793
7110
|
createDefaultGenerateImageDeps: () => createDefaultGenerateImageDeps
|
|
6794
7111
|
});
|
|
@@ -6828,16 +7145,23 @@ function requireSeed(value) {
|
|
|
6828
7145
|
}
|
|
6829
7146
|
return value;
|
|
6830
7147
|
}
|
|
7148
|
+
function optionalDimension(value) {
|
|
7149
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
7150
|
+
}
|
|
6831
7151
|
function validateImageFile(value) {
|
|
6832
7152
|
if (!isRecord2(value)) {
|
|
6833
7153
|
throw invalidResult("image entry is not an object");
|
|
6834
7154
|
}
|
|
6835
7155
|
const content_type = optionalString(value.content_type, "image content_type");
|
|
6836
7156
|
const file_name = optionalString(value.file_name, "image file_name");
|
|
7157
|
+
const width = optionalDimension(value.width);
|
|
7158
|
+
const height = optionalDimension(value.height);
|
|
6837
7159
|
return {
|
|
6838
7160
|
url: requireImageUrl(value.url),
|
|
6839
7161
|
...content_type !== undefined ? { content_type } : {},
|
|
6840
|
-
...file_name !== undefined ? { file_name } : {}
|
|
7162
|
+
...file_name !== undefined ? { file_name } : {},
|
|
7163
|
+
...width !== undefined ? { width } : {},
|
|
7164
|
+
...height !== undefined ? { height } : {}
|
|
6841
7165
|
};
|
|
6842
7166
|
}
|
|
6843
7167
|
function requireData(value) {
|
|
@@ -6951,24 +7275,29 @@ async function fetchBytes(url) {
|
|
|
6951
7275
|
}
|
|
6952
7276
|
function createDefaultGenerateImageDeps() {
|
|
6953
7277
|
const env = process.env;
|
|
7278
|
+
const pricingDeps = createDefaultFalPricingDeps(env);
|
|
6954
7279
|
return {
|
|
6955
7280
|
env,
|
|
6956
7281
|
falSubscribe: createFalSubscribe(env),
|
|
6957
7282
|
uploadFile: createFalUploadFile(env),
|
|
6958
7283
|
fetchBytes,
|
|
7284
|
+
readFileBytes: async (path) => new Uint8Array(await readFile(path)),
|
|
7285
|
+
lookupPrices: (ids) => lookupFalUnitPrices(ids, pricingDeps),
|
|
6959
7286
|
writeFile: writeFileBytes,
|
|
6960
7287
|
writeJson: writeJsonFile,
|
|
6961
7288
|
pathStatus,
|
|
6962
7289
|
ensureParentDir,
|
|
6963
7290
|
now: () => new Date,
|
|
6964
7291
|
sdkVersion: readInstalledPackageVersion("@fal-ai/client"),
|
|
6965
|
-
warn: (message) => console.error(message)
|
|
7292
|
+
warn: (message) => console.error(message),
|
|
7293
|
+
log: (message) => console.log(message)
|
|
6966
7294
|
};
|
|
6967
7295
|
}
|
|
6968
7296
|
var import_client, SUBSCRIBE_TIMEOUT_SECONDS = 300;
|
|
6969
7297
|
var init_deps = __esm(() => {
|
|
6970
|
-
|
|
7298
|
+
init_fal_pricing();
|
|
6971
7299
|
init_image();
|
|
7300
|
+
init_output_files();
|
|
6972
7301
|
init_package_version();
|
|
6973
7302
|
import_client = __toESM(require_src(), 1);
|
|
6974
7303
|
});
|
|
@@ -8860,7 +9189,7 @@ var require_png = __commonJS((exports) => {
|
|
|
8860
9189
|
};
|
|
8861
9190
|
});
|
|
8862
9191
|
|
|
8863
|
-
// src/commands/
|
|
9192
|
+
// src/commands/slice-image/slice-deps.ts
|
|
8864
9193
|
var exports_slice_deps = {};
|
|
8865
9194
|
__export(exports_slice_deps, {
|
|
8866
9195
|
createDefaultSliceImageDeps: () => createDefaultSliceImageDeps
|
|
@@ -8947,15 +9276,15 @@ function createDefaultSliceImageDeps() {
|
|
|
8947
9276
|
}
|
|
8948
9277
|
var import_pngjs, PNG;
|
|
8949
9278
|
var init_slice_deps = __esm(() => {
|
|
8950
|
-
|
|
9279
|
+
init_output_files();
|
|
8951
9280
|
init_package_version();
|
|
8952
9281
|
import_pngjs = __toESM(require_png(), 1);
|
|
8953
9282
|
({ PNG } = import_pngjs.default);
|
|
8954
9283
|
});
|
|
8955
9284
|
|
|
8956
|
-
// src/commands/
|
|
8957
|
-
var
|
|
8958
|
-
__export(
|
|
9285
|
+
// src/commands/remove-image-background/remove-image-background-deps.ts
|
|
9286
|
+
var exports_remove_image_background_deps = {};
|
|
9287
|
+
__export(exports_remove_image_background_deps, {
|
|
8959
9288
|
createDefaultRemoveImageBackgroundDeps: () => createDefaultRemoveImageBackgroundDeps
|
|
8960
9289
|
});
|
|
8961
9290
|
import { readdir } from "node:fs/promises";
|
|
@@ -8965,11 +9294,11 @@ function createDefaultRemoveImageBackgroundDeps() {
|
|
|
8965
9294
|
listDir: (dir) => readdir(dir)
|
|
8966
9295
|
};
|
|
8967
9296
|
}
|
|
8968
|
-
var
|
|
9297
|
+
var init_remove_image_background_deps = __esm(() => {
|
|
8969
9298
|
init_slice_deps();
|
|
8970
9299
|
});
|
|
8971
9300
|
|
|
8972
|
-
// src/commands/
|
|
9301
|
+
// src/commands/remove-image-background/remove-image-background-fal.ts
|
|
8973
9302
|
import { basename as basename2, resolve as resolve3 } from "node:path";
|
|
8974
9303
|
function validateBackgroundResult(raw, model) {
|
|
8975
9304
|
const unexpected = new RemoveBackgroundFalError(`fal returned no image for ${model}`);
|
|
@@ -9046,6 +9375,32 @@ function recoveryMessage2(action, err, requestId, url) {
|
|
|
9046
9375
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9047
9376
|
return `Failed to ${action} image for request ${requestId} (${url}): ${detail}. The result can still be recovered from that URL.`;
|
|
9048
9377
|
}
|
|
9378
|
+
async function findBackgroundPrice(deps, model) {
|
|
9379
|
+
return lookupUnitPrice(model, (ids) => deps.lookupPrices(ids));
|
|
9380
|
+
}
|
|
9381
|
+
function backgroundCostDisclosure(cost) {
|
|
9382
|
+
if (cost.price === undefined)
|
|
9383
|
+
return { kind: "unknown", model: cost.model };
|
|
9384
|
+
if (cost.engine === "birefnet") {
|
|
9385
|
+
return {
|
|
9386
|
+
kind: "rate-only",
|
|
9387
|
+
model: cost.model,
|
|
9388
|
+
price: cost.price,
|
|
9389
|
+
reason: BIREFNET_RATE_REASON
|
|
9390
|
+
};
|
|
9391
|
+
}
|
|
9392
|
+
return {
|
|
9393
|
+
kind: "estimate",
|
|
9394
|
+
model: cost.model,
|
|
9395
|
+
price: cost.price,
|
|
9396
|
+
quantity: 1,
|
|
9397
|
+
quantityLabel: "1 generation"
|
|
9398
|
+
};
|
|
9399
|
+
}
|
|
9400
|
+
function formatBackgroundCost(cost, label) {
|
|
9401
|
+
const line = formatCostDisclosure(backgroundCostDisclosure(cost), label);
|
|
9402
|
+
return cost.price === undefined ? [line] : [line, FAL_BILL_NOTE];
|
|
9403
|
+
}
|
|
9049
9404
|
async function removeBackgroundFal(options, deps) {
|
|
9050
9405
|
if (!deps.env.FAL_API_KEY) {
|
|
9051
9406
|
throw new RemoveBackgroundFalError(FAL_API_KEY_MISSING_MESSAGE);
|
|
@@ -9060,6 +9415,13 @@ async function removeBackgroundFal(options, deps) {
|
|
|
9060
9415
|
}
|
|
9061
9416
|
const source = await requireSource(inPath, deps);
|
|
9062
9417
|
await requireWritableOutputs(outPath, manifestPath, options.force === true, deps);
|
|
9418
|
+
const cost = {
|
|
9419
|
+
model: spec.model,
|
|
9420
|
+
engine,
|
|
9421
|
+
price: await findBackgroundPrice(deps, spec.model)
|
|
9422
|
+
};
|
|
9423
|
+
for (const line of formatBackgroundCost(cost, "Cost"))
|
|
9424
|
+
deps.log(line);
|
|
9063
9425
|
const dataUri = `data:image/png;base64,${Buffer.from(source.bytes).toString("base64")}`;
|
|
9064
9426
|
const raw = await deps.falSubscribe(spec.model, spec.buildInput(dataUri));
|
|
9065
9427
|
const result = validateBackgroundResult(raw, spec.model);
|
|
@@ -9121,6 +9483,8 @@ async function removeBackgroundFal(options, deps) {
|
|
|
9121
9483
|
} catch (err) {
|
|
9122
9484
|
throw new RemoveBackgroundFalError(manifestSaveMessage(err, result.requestId, outPath, manifestPath));
|
|
9123
9485
|
}
|
|
9486
|
+
for (const line of formatBackgroundCost(cost, "Estimated cost"))
|
|
9487
|
+
deps.log(line);
|
|
9124
9488
|
return {
|
|
9125
9489
|
outPath,
|
|
9126
9490
|
manifestPath,
|
|
@@ -9129,10 +9493,11 @@ async function removeBackgroundFal(options, deps) {
|
|
|
9129
9493
|
sha256: outputSha256
|
|
9130
9494
|
};
|
|
9131
9495
|
}
|
|
9132
|
-
var DEFAULT_BACKGROUND_ENGINE = "bria", BACKGROUND_ENGINES, MAX_FAL_INPUT_BYTES, MAX_FAL_RESULT_BYTES, TOOL2 = "chatroom remove-image-background-fal", LICENSING2 = "unverified", RemoveBackgroundFalError;
|
|
9133
|
-
var
|
|
9496
|
+
var DEFAULT_BACKGROUND_ENGINE = "bria", BACKGROUND_ENGINES, MAX_FAL_INPUT_BYTES, MAX_FAL_RESULT_BYTES, TOOL2 = "chatroom remove-image-background-fal", LICENSING2 = "unverified", RemoveBackgroundFalError, FAL_BILL_NOTE = "fal returns no billed cost; check the fal dashboard for the actual bill.", BIREFNET_RATE_REASON = "Total depends on runtime; not estimated.";
|
|
9497
|
+
var init_remove_image_background_fal = __esm(() => {
|
|
9498
|
+
init_fal_pricing();
|
|
9134
9499
|
init_image();
|
|
9135
|
-
|
|
9500
|
+
init_image_preflight();
|
|
9136
9501
|
BACKGROUND_ENGINES = {
|
|
9137
9502
|
bria: {
|
|
9138
9503
|
model: "fal-ai/bria/background/remove",
|
|
@@ -9153,9 +9518,9 @@ var init_remove_background_fal = __esm(() => {
|
|
|
9153
9518
|
};
|
|
9154
9519
|
});
|
|
9155
9520
|
|
|
9156
|
-
// src/commands/
|
|
9157
|
-
var
|
|
9158
|
-
__export(
|
|
9521
|
+
// src/commands/remove-image-background/remove-image-background-fal-deps.ts
|
|
9522
|
+
var exports_remove_image_background_fal_deps = {};
|
|
9523
|
+
__export(exports_remove_image_background_fal_deps, {
|
|
9159
9524
|
createDefaultRemoveBackgroundFalDeps: () => createDefaultRemoveBackgroundFalDeps
|
|
9160
9525
|
});
|
|
9161
9526
|
import { dirname as dirname4 } from "node:path";
|
|
@@ -9187,9 +9552,11 @@ async function fetchCappedBytes(url, maxBytes) {
|
|
|
9187
9552
|
}
|
|
9188
9553
|
function createDefaultRemoveBackgroundFalDeps() {
|
|
9189
9554
|
const slice = createDefaultSliceImageDeps();
|
|
9555
|
+
const pricingDeps = createDefaultFalPricingDeps(process.env);
|
|
9190
9556
|
return {
|
|
9191
9557
|
env: process.env,
|
|
9192
9558
|
falSubscribe: createFalRawSubscribe(process.env),
|
|
9559
|
+
lookupPrices: (ids) => lookupFalUnitPrices(ids, pricingDeps),
|
|
9193
9560
|
fetchBytes: (url) => fetchCappedBytes(url, MAX_FAL_RESULT_BYTES),
|
|
9194
9561
|
readInput: slice.readFile,
|
|
9195
9562
|
statInput: slice.statInput,
|
|
@@ -9198,13 +9565,15 @@ function createDefaultRemoveBackgroundFalDeps() {
|
|
|
9198
9565
|
writeJson: slice.writeJson,
|
|
9199
9566
|
ensureParentDir: (path) => slice.ensureDir(dirname4(path)),
|
|
9200
9567
|
now: () => new Date,
|
|
9201
|
-
sdkVersion: readInstalledPackageVersion("@fal-ai/client")
|
|
9568
|
+
sdkVersion: readInstalledPackageVersion("@fal-ai/client"),
|
|
9569
|
+
log: (message) => console.log(message)
|
|
9202
9570
|
};
|
|
9203
9571
|
}
|
|
9204
|
-
var
|
|
9572
|
+
var init_remove_image_background_fal_deps = __esm(() => {
|
|
9573
|
+
init_remove_image_background_fal();
|
|
9205
9574
|
init_deps();
|
|
9575
|
+
init_fal_pricing();
|
|
9206
9576
|
init_package_version();
|
|
9207
|
-
init_remove_background_fal();
|
|
9208
9577
|
init_slice_deps();
|
|
9209
9578
|
});
|
|
9210
9579
|
|
|
@@ -108251,7 +108620,7 @@ var init_game_asset_designer_workflow = __esm(() => {
|
|
|
108251
108620
|
// ../../services/backend/prompts/teams/game-asset-designer-handoff-templates.ts
|
|
108252
108621
|
function getEntryPointToGameAssetDesignerHandoffTemplate(query) {
|
|
108253
108622
|
const entryPoint = query.fromRole;
|
|
108254
|
-
return `Instructions (not part of the message): Pass the asset request as a brief the designer can act on without guessing. Provide every field below; write \`Not specified.\` for a field you cannot fill and the designer will ask or apply a stated default. Give each asset a stable ID (\`A01\`, \`A02\`…). Use \`spec only\` only when you want a specification with no files; the default is \`spec and generate\`. The \`## Allowed tools\` value is fixed: \`chatroom generate-image --platform=fal --model <Model> (dry run first), chatroom slice-image, chatroom remove-image-background\`. Do not name any other generator.
|
|
108623
|
+
return `Instructions (not part of the message): Pass the asset request as a brief the designer can act on without guessing. Provide every field below; write \`Not specified.\` for a field you cannot fill and the designer will ask or apply a stated default. Give each asset a stable ID (\`A01\`, \`A02\`…). Use \`spec only\` only when you want a specification with no files; the default is \`spec and generate\`. The \`## Allowed tools\` value is fixed: \`chatroom generate-image --platform=fal --model <Model> (dry run first), chatroom slice-image, chatroom remove-image-background\`. Do not name any other generator. Before generating anything, the designer evaluates each \`## Free asset candidates\` entry and records a \`use\`, \`adapt\`, or \`reject\` decision; it generates only the assets that no candidate covers.
|
|
108255
108624
|
|
|
108256
108625
|
## Asset request
|
|
108257
108626
|
<one to three sentences: what the assets are for and the user-visible goal>
|
|
@@ -108283,6 +108652,9 @@ function getEntryPointToGameAssetDesignerHandoffTemplate(query) {
|
|
|
108283
108652
|
## Existing assets
|
|
108284
108653
|
<repository paths the designer must reuse or must not overwrite; or \`Not specified.\`>
|
|
108285
108654
|
|
|
108655
|
+
## Free asset candidates
|
|
108656
|
+
<one entry per third-party pack to evaluate before generating: pack name, source URL, expected licence (for example CC0), formats, and the asset IDs it could cover; or \`None.\`. The designer checks each pack's own licence file and file sizes and gives each candidate a \`use\`, \`adapt\`, or \`reject\` decision. It fetches only the URLs named here, inspects the pack as static files, and never runs its code.>
|
|
108657
|
+
|
|
108286
108658
|
## Deliverable mode
|
|
108287
108659
|
<\`spec and generate\` (default when you name an output location and allowed tools) or \`spec only\`>
|
|
108288
108660
|
|
|
@@ -108342,10 +108714,14 @@ ${PROOF_OF_PRINCIPLES_REPORT}
|
|
|
108342
108714
|
- [ ] Every asset ID has a file or \`Specified only.\`
|
|
108343
108715
|
- [ ] Every sliced asset was isolated with \`remove-image-background\` and \`inspected\`.
|
|
108344
108716
|
- [ ] Manifests are listed in the asset manifest.
|
|
108717
|
+
- [ ] Every candidate in the brief has a decision with its licence and file sizes labelled \`inspected\`.
|
|
108345
108718
|
|
|
108346
108719
|
## Target and assumptions
|
|
108347
108720
|
<each assumption you made and why; each deviation from the planner's target or budgets; or \`None.\`>
|
|
108348
108721
|
|
|
108722
|
+
## Free asset decisions
|
|
108723
|
+
<one entry per candidate in the brief: pack name, licence quoted from the pack's own licence file, file sizes checked, decision (\`use\`, \`adapt\`, or \`reject\`), asset IDs covered, and the reason; for \`adapt\`, the edits made; write exactly \`None.\` if the brief named no candidates>
|
|
108724
|
+
|
|
108349
108725
|
## Asset manifest
|
|
108350
108726
|
<one entry per asset, keyed by the planner's stable ID (\`A01\`…): name, category, file format, path (or \`Specified only.\`), dimensions/units/scale, pivot, naming, LOD list with triangle budgets, texture set with resolution, color space and channel packing, atlas/pixel grid/frame data, audio format and loudness target, import settings, dependencies and variants; for each budget, the acceptance check (exact command and expected value); for each generated file, the generator command line (\`chatroom generate-image\` with its \`--platform\`, \`--model\`, \`--seed\`, \`--input\`, and \`--input-image\` values), the sheet and tile manifests, and the isolated asset manifest path>
|
|
108351
108727
|
|
|
@@ -146435,11 +146811,11 @@ var {
|
|
|
146435
146811
|
Help
|
|
146436
146812
|
} = import__.default;
|
|
146437
146813
|
|
|
146438
|
-
// src/commands/generate/index.ts
|
|
146814
|
+
// src/commands/generate-image/index.ts
|
|
146439
146815
|
import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
146440
146816
|
init_image();
|
|
146441
|
-
init_local_image_common();
|
|
146442
146817
|
init_model_capabilities();
|
|
146818
|
+
init_image_preflight();
|
|
146443
146819
|
var OUTPUT_FORMATS = ["png", "webp"];
|
|
146444
146820
|
function parsePlatform(value) {
|
|
146445
146821
|
if (!GENERATE_IMAGE_PLATFORMS.includes(value)) {
|
|
@@ -146479,6 +146855,9 @@ Examples:
|
|
|
146479
146855
|
chatroom generate-image --platform=fal --model ${FLUX_2_FLASH_MODEL} --prompt "a hero" --out hero.png
|
|
146480
146856
|
chatroom generate-image --platform=fal --model ${FLUX_2_FLASH_MODEL} --prompt 'pixel-art potion, transparent-friendly flat background' --output-format png --out potion.png
|
|
146481
146857
|
chatroom generate-image --platform=fal --model ${FLUX_2_FLASH_EDIT_MODEL} --prompt "make the sky stormy" --input-image ./hero.png --out hero-stormy.png
|
|
146858
|
+
|
|
146859
|
+
Cost lines are estimates: fal returns no billed cost. The rate is live when
|
|
146860
|
+
available, otherwise the list price; check the fal dashboard for the actual bill.
|
|
146482
146861
|
`;
|
|
146483
146862
|
async function runGenerateImage(options) {
|
|
146484
146863
|
try {
|
|
@@ -146505,20 +146884,30 @@ async function runGenerateImage(options) {
|
|
|
146505
146884
|
const result = await generateImage(generateOptions, deps);
|
|
146506
146885
|
console.log(`Generated ${result.outPath}`);
|
|
146507
146886
|
console.log(`Manifest ${result.manifestPath}`);
|
|
146887
|
+
for (const line of result.costLines)
|
|
146888
|
+
console.log(line);
|
|
146508
146889
|
} catch (err) {
|
|
146509
146890
|
console.error(`❌ ${err instanceof Error ? err.message : String(err)}`);
|
|
146510
146891
|
process.exitCode = 1;
|
|
146511
146892
|
}
|
|
146512
146893
|
}
|
|
146513
146894
|
function registerGenerateImageCommand(program2) {
|
|
146514
|
-
program2.command("generate-image").description("Generate one image with a fal model and write a JSON manifest next to it").requiredOption("--prompt <text>", "Text prompt describing the image").requiredOption("--out <path>", "Output image path (manifest is written to <path>.json)").option("--platform <name>", "Generation platform: fal", parsePlatform, DEFAULT_GENERATE_IMAGE_PLATFORM).requiredOption("--model <id>", `fal model id (required), for example ${FLUX_2_FLASH_MODEL}, or ${FLUX_2_FLASH_EDIT_MODEL} with --input-image`).option("--aspect-ratio <ratio>", "Aspect ratio for models that take one, e.g. 1:1, 16:9 (omitted = model default). Rejected for models the CLI knows do not support it").option("--output-format <format>", "Output format: png or webp (omitted = model default)", parseOutputFormat).option("--seed <n>", "Seed for the random number generator", parseSeed).option("--input <json|@file>", "Model-specific JSON input object merged into the fal request; @file reads JSON from a file").option("--input-image <path-or-url>", `Image to edit: a local .png, .jpg, .jpeg, .webp, or .gif (uploaded to fal) or an http(s) URL. Requires an edit model such as ${FLUX_2_FLASH_EDIT_MODEL}`).option("--force", "Overwrite existing output and manifest files").option("--dry-run", "Validate and print the request
|
|
146895
|
+
program2.command("generate-image").description("Generate one image with a fal model and write a JSON manifest next to it").requiredOption("--prompt <text>", "Text prompt describing the image").requiredOption("--out <path>", "Output image path (manifest is written to <path>.json)").option("--platform <name>", "Generation platform: fal", parsePlatform, DEFAULT_GENERATE_IMAGE_PLATFORM).requiredOption("--model <id>", `fal model id (required), for example ${FLUX_2_FLASH_MODEL}, or ${FLUX_2_FLASH_EDIT_MODEL} with --input-image`).option("--aspect-ratio <ratio>", "Aspect ratio for models that take one, e.g. 1:1, 16:9 (omitted = model default). Rejected for models the CLI knows do not support it").option("--output-format <format>", "Output format: png or webp (omitted = model default)", parseOutputFormat).option("--seed <n>", "Seed for the random number generator", parseSeed).option("--input <json|@file>", "Model-specific JSON input object merged into the fal request; @file reads JSON from a file").option("--input-image <path-or-url>", `Image to edit: a local .png, .jpg, .jpeg, .webp, or .gif (uploaded to fal) or an http(s) URL. Requires an edit model such as ${FLUX_2_FLASH_EDIT_MODEL}`).option("--force", "Overwrite existing output and manifest files").option("--dry-run", "Validate and print the request, the model constraints the CLI knows, and the estimated fal cost. No upload or fal generation call is made; one read-only pricing request may be made when FAL_API_KEY is set (the key is not required)").addHelpText("after", HELP_AFTER).action(runGenerateImage);
|
|
146515
146896
|
}
|
|
146516
146897
|
|
|
146517
|
-
// src/commands/
|
|
146898
|
+
// src/commands/register-chatroom-role-command.ts
|
|
146899
|
+
function registerChatroomRoleCommand(program2, config) {
|
|
146900
|
+
program2.command(config.name).description(config.description).requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role (e.g., planner, builder)").action(async (options) => {
|
|
146901
|
+
await config.maybeRequireAuth();
|
|
146902
|
+
await config.run(options.chatroomId, { role: options.role });
|
|
146903
|
+
});
|
|
146904
|
+
}
|
|
146905
|
+
|
|
146906
|
+
// src/commands/remove-image-background/remove-image-background.ts
|
|
146518
146907
|
import { basename, dirname as dirname3, extname as extname2, join as join2, resolve as resolve2 } from "node:path";
|
|
146519
146908
|
|
|
146520
|
-
// src/commands/
|
|
146521
|
-
|
|
146909
|
+
// src/commands/remove-image-background/background-key.ts
|
|
146910
|
+
init_image_preflight();
|
|
146522
146911
|
var DEFAULT_TOLERANCE = 24;
|
|
146523
146912
|
var DEFAULT_MIN_AREA_PERCENT = 0.5;
|
|
146524
146913
|
var BORDER_NOISE_REFUSAL_RATIO = 0.02;
|
|
@@ -146833,10 +147222,7 @@ function keyBackground(image, opts) {
|
|
|
146833
147222
|
});
|
|
146834
147223
|
}
|
|
146835
147224
|
|
|
146836
|
-
// src/commands/
|
|
146837
|
-
init_local_image_common();
|
|
146838
|
-
|
|
146839
|
-
// src/commands/generate/trim-crop.ts
|
|
147225
|
+
// src/commands/remove-image-background/trim-crop.ts
|
|
146840
147226
|
var MAX_TRIM_PADDING = 64;
|
|
146841
147227
|
function trimRect(contentBox, canvasWidth, canvasHeight, padding) {
|
|
146842
147228
|
const x = Math.max(0, contentBox.x - padding);
|
|
@@ -146855,7 +147241,9 @@ function cropRgba(data, canvasWidth, rect) {
|
|
|
146855
147241
|
return cropped;
|
|
146856
147242
|
}
|
|
146857
147243
|
|
|
146858
|
-
// src/commands/
|
|
147244
|
+
// src/commands/remove-image-background/remove-image-background.ts
|
|
147245
|
+
init_image_preflight();
|
|
147246
|
+
|
|
146859
147247
|
class RemoveBackgroundError extends Error {
|
|
146860
147248
|
constructor(message) {
|
|
146861
147249
|
super(message);
|
|
@@ -147293,7 +147681,7 @@ async function removeImageBackground(opts, deps) {
|
|
|
147293
147681
|
return result;
|
|
147294
147682
|
}
|
|
147295
147683
|
|
|
147296
|
-
// src/commands/
|
|
147684
|
+
// src/commands/remove-image-background/remove-image-background-command.ts
|
|
147297
147685
|
var DECIMAL_INTEGER = /^\d+$/;
|
|
147298
147686
|
var DECIMAL_PERCENT = /^\d+(?:\.\d+)?$/;
|
|
147299
147687
|
function parseTolerance(value) {
|
|
@@ -147395,7 +147783,7 @@ function printResult(result) {
|
|
|
147395
147783
|
function registerRemoveImageBackgroundCommand(program2) {
|
|
147396
147784
|
program2.command("remove-image-background").description("Key a tile background to transparency and write an isolated PNG asset").option("--in <png>", "Single input tile PNG").option("--out <png>", "Single output PNG path (must end in .png)").option("--in-dir <dir>", "Directory of input tile PNGs").option("--out-dir <dir>", "Directory for output PNGs and manifests").option("--map <tile=name,...>", "Directory-mode tile stem to output name mapping", parseMap).option("--tolerance <1-128>", "Background colour tolerance", parseTolerance, 24).option("--min-area <percent>", "Smallest kept region as a percent of the tile", parseMinArea, 0.5).option("--allow-edge-content", "Keep artwork that touches the tile border").option("--trim", "Crop each asset to its opaque content").option("--trim-padding <px>", "Pixels kept around trimmed content (0-64, requires --trim)", parseTrimPadding).option("--force", "Overwrite existing outputs").option("--dry-run", "Report the assets without writing anything").addHelpText("after", HELP_AFTER2).action(async (options) => {
|
|
147397
147785
|
try {
|
|
147398
|
-
const { createDefaultRemoveImageBackgroundDeps: createDefaultRemoveImageBackgroundDeps2 } = await Promise.resolve().then(() => (
|
|
147786
|
+
const { createDefaultRemoveImageBackgroundDeps: createDefaultRemoveImageBackgroundDeps2 } = await Promise.resolve().then(() => (init_remove_image_background_deps(), exports_remove_image_background_deps));
|
|
147399
147787
|
const removeOptions = {
|
|
147400
147788
|
in: options.in,
|
|
147401
147789
|
out: options.out,
|
|
@@ -147419,8 +147807,8 @@ function registerRemoveImageBackgroundCommand(program2) {
|
|
|
147419
147807
|
});
|
|
147420
147808
|
}
|
|
147421
147809
|
|
|
147422
|
-
// src/commands/
|
|
147423
|
-
|
|
147810
|
+
// src/commands/remove-image-background/remove-image-background-fal-command.ts
|
|
147811
|
+
init_remove_image_background_fal();
|
|
147424
147812
|
var ENGINES = ["bria", "birefnet"];
|
|
147425
147813
|
function parseEngine(value) {
|
|
147426
147814
|
if (!ENGINES.includes(value)) {
|
|
@@ -147444,8 +147832,10 @@ Model:
|
|
|
147444
147832
|
a PNG with alpha.
|
|
147445
147833
|
|
|
147446
147834
|
Billing:
|
|
147447
|
-
bria is
|
|
147448
|
-
|
|
147835
|
+
bria is estimated per generation at the live rate, or at the list price of
|
|
147836
|
+
$0.018 (as of 2026-10-25) when the live lookup is unavailable. birefnet is
|
|
147837
|
+
billed per compute second; the rate is shown but the total is not estimated.
|
|
147838
|
+
fal returns no billed cost; check the fal dashboard for the actual bill.
|
|
147449
147839
|
|
|
147450
147840
|
Refusals:
|
|
147451
147841
|
A missing FAL_API_KEY, a missing or non-PNG input, an input over 8 MiB, an
|
|
@@ -147465,7 +147855,7 @@ Examples:
|
|
|
147465
147855
|
function registerRemoveImageBackgroundFalCommand(program2) {
|
|
147466
147856
|
program2.command("remove-image-background-fal").description("Remove an image background with fal.ai and write a transparent PNG").requiredOption("--in <png>", "Input PNG to cut out").requiredOption("--out <png>", "Output PNG path for the image with alpha").option("--engine <engine>", "Background model: bria (default, fal-ai/bria/background/remove) or birefnet (previous BiRefNet Matting backend, fal-ai/birefnet/v2)", parseEngine, DEFAULT_BACKGROUND_ENGINE).option("--force", "Overwrite an existing output and manifest").addHelpText("after", HELP_AFTER3).action(async (options) => {
|
|
147467
147857
|
try {
|
|
147468
|
-
const { createDefaultRemoveBackgroundFalDeps: createDefaultRemoveBackgroundFalDeps2 } = await Promise.resolve().then(() => (
|
|
147858
|
+
const { createDefaultRemoveBackgroundFalDeps: createDefaultRemoveBackgroundFalDeps2 } = await Promise.resolve().then(() => (init_remove_image_background_fal_deps(), exports_remove_image_background_fal_deps));
|
|
147469
147859
|
const result = await removeBackgroundFal({ in: options.in, out: options.out, engine: options.engine, force: options.force }, createDefaultRemoveBackgroundFalDeps2());
|
|
147470
147860
|
console.log(`Removed background ${result.outPath} (${result.bytes} bytes)`);
|
|
147471
147861
|
console.log(`Manifest ${result.manifestPath}`);
|
|
@@ -147477,8 +147867,8 @@ function registerRemoveImageBackgroundFalCommand(program2) {
|
|
|
147477
147867
|
});
|
|
147478
147868
|
}
|
|
147479
147869
|
|
|
147480
|
-
// src/commands/
|
|
147481
|
-
|
|
147870
|
+
// src/commands/slice-image/slice.ts
|
|
147871
|
+
init_image_preflight();
|
|
147482
147872
|
import { basename as basename3, extname as extname3, join as join3, resolve as resolve4 } from "node:path";
|
|
147483
147873
|
var SLICE_TOOL = "chatroom slice-image";
|
|
147484
147874
|
var MAX_TILES = 256;
|
|
@@ -147974,7 +148364,7 @@ async function sliceImage(opts, deps) {
|
|
|
147974
148364
|
return result;
|
|
147975
148365
|
}
|
|
147976
148366
|
|
|
147977
|
-
// src/commands/
|
|
148367
|
+
// src/commands/slice-image/slice-command.ts
|
|
147978
148368
|
var DECIMAL_INTEGER2 = /^\d+$/;
|
|
147979
148369
|
var HELP_AFTER4 = `
|
|
147980
148370
|
Usage:
|
|
@@ -148074,14 +148464,6 @@ function registerSliceImageCommand(program2) {
|
|
|
148074
148464
|
});
|
|
148075
148465
|
}
|
|
148076
148466
|
|
|
148077
|
-
// src/commands/register-chatroom-role-command.ts
|
|
148078
|
-
function registerChatroomRoleCommand(program2, config) {
|
|
148079
|
-
program2.command(config.name).description(config.description).requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role (e.g., planner, builder)").action(async (options) => {
|
|
148080
|
-
await config.maybeRequireAuth();
|
|
148081
|
-
await config.run(options.chatroomId, { role: options.role });
|
|
148082
|
-
});
|
|
148083
|
-
}
|
|
148084
|
-
|
|
148085
148467
|
// src/utils/multi-value-option.ts
|
|
148086
148468
|
function collectMultiValueOption(value, previous) {
|
|
148087
148469
|
return previous ? [...previous, value] : [value];
|
|
@@ -148717,4 +149099,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
148717
149099
|
});
|
|
148718
149100
|
program2.parse();
|
|
148719
149101
|
|
|
148720
|
-
//# debugId=
|
|
149102
|
+
//# debugId=DB5E1205262551F864756E2164756E21
|