pm-media:background/watermark
3
packages/ai-tools/dist/lib/tools/fs copy.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const decode_base64: (base64: string) => string;
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
432
packages/ai-tools/dist/lib/tools/fs copy.js
vendored
Normal file
3
packages/ai-tools/dist/lib/tools/fs.js
vendored
5
packages/ai-tools/dist/lib/tools/keyv.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
import Keyv from 'keyv';
|
||||
export declare const store: (storePath: string, ns?: string, opts?: any) => Keyv<any>;
|
||||
export declare const get: (key: string, storePath: string, ns?: string, opts?: any) => Promise<any>;
|
||||
export declare const set: (key: string, value: any, storePath: string, ns?: string, opts?: any) => Promise<boolean>;
|
||||
export declare const list: (key: string, value: any, storePath: string, ns?: string, opts?: any) => Promise<boolean>;
|
||||
21
packages/ai-tools/dist/lib/tools/keyv.js
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import * as path from 'path';
|
||||
import Keyv from 'keyv';
|
||||
import KeyvSqlite from '@keyv/sqlite';
|
||||
import { resolve } from '@polymech/commons';
|
||||
export const store = (storePath, ns = 'ns-unknown', opts = {}) => {
|
||||
const keyvSqlite = new KeyvSqlite(path.resolve(resolve(storePath)));
|
||||
return new Keyv({ store: keyvSqlite, ttl: 5000, namespace: ns, ...opts });
|
||||
};
|
||||
export const get = async (key, storePath, ns = 'ns-unknown', opts = {}) => {
|
||||
const keyv = store(storePath, ns, opts);
|
||||
return await keyv.get(key);
|
||||
};
|
||||
export const set = async (key, value, storePath, ns = 'ns-unknown', opts = {}) => {
|
||||
const keyv = store(storePath, ns, opts);
|
||||
return await keyv.set(key, value);
|
||||
};
|
||||
export const list = async (key, value, storePath, ns = 'ns-unknown', opts = {}) => {
|
||||
const keyv = store(storePath, ns, opts);
|
||||
return await keyv.set(key, value);
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5di5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvdG9vbHMva2V5di50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQTtBQUM1QixPQUFPLElBQUksTUFBTSxNQUFNLENBQUE7QUFDdkIsT0FBTyxVQUFVLE1BQU0sY0FBYyxDQUFBO0FBRXJDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUUzQyxNQUFNLENBQUMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxTQUFpQixFQUFFLEtBQWEsWUFBWSxFQUFFLE9BQVksRUFBRSxFQUFFLEVBQUU7SUFDbEYsTUFBTSxVQUFVLEdBQUcsSUFBSSxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ25FLE9BQU8sSUFBSSxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxHQUFHLElBQUksRUFBRSxDQUFDLENBQUE7QUFDN0UsQ0FBQyxDQUFBO0FBQ0QsTUFBTSxDQUFDLE1BQU0sR0FBRyxHQUFHLEtBQUssRUFBRSxHQUFXLEVBQUUsU0FBaUIsRUFBRSxLQUFhLFlBQVksRUFBRSxPQUFZLEVBQUUsRUFBRSxFQUFFO0lBQ25HLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxTQUFTLEVBQUUsRUFBRSxFQUFFLElBQUksQ0FBQyxDQUFBO0lBQ3ZDLE9BQU8sTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0FBQzlCLENBQUMsQ0FBQTtBQUNELE1BQU0sQ0FBQyxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQUUsR0FBVyxFQUFFLEtBQVUsRUFBRSxTQUFpQixFQUFFLEtBQWEsWUFBWSxFQUFFLE9BQVksRUFBRSxFQUFFLEVBQUU7SUFDL0csTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsRUFBRSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUE7SUFDdkMsT0FBTyxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFBO0FBQ3JDLENBQUMsQ0FBQTtBQUNELE1BQU0sQ0FBQyxNQUFNLElBQUksR0FBRyxLQUFLLEVBQUUsR0FBVyxFQUFFLEtBQVUsRUFBRSxTQUFpQixFQUFFLEtBQWEsWUFBWSxFQUFFLE9BQVksRUFBRSxFQUFFLEVBQUU7SUFDaEgsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsRUFBRSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUE7SUFDdkMsT0FBTyxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFBO0FBQ3JDLENBQUMsQ0FBQSJ9
|
||||
2
packages/ai-tools/dist/lib/tools/memory.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { IKBotTask } from '../../types.js';
|
||||
export declare const tools: (target: string, options: IKBotTask) => Array<any>;
|
||||
354
packages/ai-tools/dist/lib/tools/memory.js
vendored
Normal file
1
packages/ai-tools/dist/lib/tools/tools.d.ts
vendored
@ -7,4 +7,5 @@ export declare const tools: {
|
||||
user: (target: string, options: import("../../types.js").IKBotTask) => any[];
|
||||
search: (target: string, options: import("../../types.js").IKBotTask) => any[];
|
||||
web: (target: string, options: import("../../types.js").IKBotTask) => any[];
|
||||
memory: (target: string, options: import("../../types.js").IKBotTask) => any[];
|
||||
};
|
||||
|
||||
4
packages/ai-tools/dist/lib/tools/tools.js
vendored
@ -6,6 +6,7 @@ import { tools as interactTools } from './interact.js';
|
||||
import { tools as userTools } from './user.js';
|
||||
import { tools as search } from './search.js';
|
||||
import { tools as webTools } from './web.js';
|
||||
import { tools as memoryTools } from './memory.js';
|
||||
//import { tools as emailTools } from './email'
|
||||
export const tools = {
|
||||
fs: fsTools,
|
||||
@ -16,6 +17,7 @@ export const tools = {
|
||||
user: userTools,
|
||||
search: search,
|
||||
web: webTools,
|
||||
memory: memoryTools,
|
||||
// email: emailTools
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Rvb2xzL3Rvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLElBQUksT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQzFDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLE9BQU8sRUFBRSxLQUFLLElBQUksYUFBYSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3RELE9BQU8sRUFBRSxLQUFLLElBQUksYUFBYSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3RELE9BQU8sRUFBRSxLQUFLLElBQUksU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQzlDLE9BQU8sRUFBRSxLQUFLLElBQUksTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQzdDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLCtDQUErQztBQUUvQyxNQUFNLENBQUMsTUFBTSxLQUFLLEdBQUc7SUFDakIsRUFBRSxFQUFFLE9BQU87SUFDWCxHQUFHLEVBQUUsUUFBUTtJQUNiLEdBQUcsRUFBRSxRQUFRO0lBQ2IsUUFBUSxFQUFFLGFBQWE7SUFDdkIsUUFBUSxFQUFFLGFBQWE7SUFDdkIsSUFBSSxFQUFFLFNBQVM7SUFDZixNQUFNLEVBQUUsTUFBTTtJQUNkLEdBQUcsRUFBRSxRQUFRO0lBQ2Isb0JBQW9CO0NBQ3ZCLENBQUEifQ==
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Rvb2xzL3Rvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLElBQUksT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQzFDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLE9BQU8sRUFBRSxLQUFLLElBQUksYUFBYSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3RELE9BQU8sRUFBRSxLQUFLLElBQUksYUFBYSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ3RELE9BQU8sRUFBRSxLQUFLLElBQUksU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQzlDLE9BQU8sRUFBRSxLQUFLLElBQUksTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQzdDLE9BQU8sRUFBRSxLQUFLLElBQUksUUFBUSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQzVDLE9BQU8sRUFBRSxLQUFLLElBQUksV0FBVyxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ2xELCtDQUErQztBQUUvQyxNQUFNLENBQUMsTUFBTSxLQUFLLEdBQUc7SUFDakIsRUFBRSxFQUFFLE9BQU87SUFDWCxHQUFHLEVBQUUsUUFBUTtJQUNiLEdBQUcsRUFBRSxRQUFRO0lBQ2IsUUFBUSxFQUFFLGFBQWE7SUFDdkIsUUFBUSxFQUFFLGFBQWE7SUFDdkIsSUFBSSxFQUFFLFNBQVM7SUFDZixNQUFNLEVBQUUsTUFBTTtJQUNkLEdBQUcsRUFBRSxRQUFRO0lBQ2IsTUFBTSxFQUFFLFdBQVc7SUFDbkIsb0JBQW9CO0NBQ3ZCLENBQUEifQ==
|
||||
56
packages/ai-tools/dist/types_kbot.d.ts
vendored
@ -8,7 +8,7 @@ export interface IKBotOptions {
|
||||
/** Optional destination path for the result, will substitute ${MODEL_NAME} and ${ROUTER} in the path. Optional, used for "completion" mode */
|
||||
dst?: string | undefined;
|
||||
/** How to handle output if --dst file already exists: "concat" (append) or "merge" (try to merge structures if possible, otherwise append). Only used if --dst is specified. */
|
||||
append?: ("concat" | "merge") | undefined;
|
||||
append?: ("concat" | "merge" | "replace") | undefined;
|
||||
/** Specify how to wrap the output, "meta (file name, absolute path, cwd)" or "none". */
|
||||
wrap?: "meta" | "none";
|
||||
/** Iterate over items, supported: GLOB | Path to JSON File | array of strings (comma separated). To test different models, use --each="gpt-3.5-turbo,gpt-4o", the actual string will exposed as variable `ITEM`, eg: --dst="${ITEM}-output.md" */
|
||||
@ -31,8 +31,6 @@ export interface IKBotOptions {
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenRouter models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
01-ai/yi-large | paid
|
||||
aetherwiing/mn-starcannon-12b | paid
|
||||
agentica-org/deepcoder-14b-preview | paid
|
||||
agentica-org/deepcoder-14b-preview:free | free
|
||||
ai21/jamba-1.6-large | paid
|
||||
@ -48,11 +46,8 @@ export interface IKBotOptions {
|
||||
anthropic/claude-3-haiku:beta | paid
|
||||
anthropic/claude-3-opus | paid
|
||||
anthropic/claude-3-opus:beta | paid
|
||||
anthropic/claude-3-sonnet | paid
|
||||
anthropic/claude-3-sonnet:beta | paid
|
||||
anthropic/claude-3.5-haiku | paid
|
||||
anthropic/claude-3.5-haiku-20241022 | paid
|
||||
anthropic/claude-3.5-haiku-20241022:beta | paid
|
||||
anthropic/claude-3.5-haiku:beta | paid
|
||||
anthropic/claude-3.5-sonnet | paid
|
||||
anthropic/claude-3.5-sonnet-20240620 | paid
|
||||
@ -62,23 +57,17 @@ export interface IKBotOptions {
|
||||
anthropic/claude-3.7-sonnet:beta | paid
|
||||
anthropic/claude-3.7-sonnet:thinking | paid
|
||||
anthropic/claude-opus-4 | paid
|
||||
anthropic/claude-opus-4.1 | paid
|
||||
anthropic/claude-sonnet-4 | paid
|
||||
anthropic/claude-2 | paid
|
||||
anthropic/claude-2:beta | paid
|
||||
anthropic/claude-2.0 | paid
|
||||
anthropic/claude-2.0:beta | paid
|
||||
anthropic/claude-2.1 | paid
|
||||
anthropic/claude-2.1:beta | paid
|
||||
arcee-ai/arcee-blitz | paid
|
||||
arcee-ai/caller-large | paid
|
||||
arcee-ai/coder-large | paid
|
||||
arcee-ai/maestro-reasoning | paid
|
||||
arcee-ai/spotlight | paid
|
||||
arcee-ai/virtuoso-large | paid
|
||||
arcee-ai/virtuoso-medium-v2 | paid
|
||||
arliai/qwq-32b-arliai-rpr-v1 | paid
|
||||
arliai/qwq-32b-arliai-rpr-v1:free | free
|
||||
openrouter/auto | paid
|
||||
baidu/ernie-4.5-300b-a47b | paid
|
||||
bytedance/ui-tars-1.5-7b | paid
|
||||
cohere/command | paid
|
||||
cohere/command-a | paid
|
||||
cohere/command-r | paid
|
||||
@ -92,10 +81,9 @@ export interface IKBotOptions {
|
||||
deepseek/deepseek-r1-0528-qwen3-8b | paid
|
||||
deepseek/deepseek-r1-0528-qwen3-8b:free | free
|
||||
deepseek/deepseek-chat | paid
|
||||
deepseek/deepseek-chat:free | free
|
||||
deepseek/deepseek-chat-v3-0324 | paid
|
||||
deepseek/deepseek-chat-v3-0324:free | free
|
||||
deepseek/deepseek-v3-base:free | free
|
||||
deepseek/deepseek-v3-base | paid
|
||||
deepseek/deepseek-r1 | paid
|
||||
deepseek/deepseek-r1:free | free
|
||||
deepseek/deepseek-r1-0528 | paid
|
||||
@ -109,12 +97,11 @@ export interface IKBotOptions {
|
||||
deepseek/deepseek-r1-distill-qwen-32b | paid
|
||||
deepseek/deepseek-r1-distill-qwen-7b | paid
|
||||
cognitivecomputations/dolphin-mixtral-8x22b | paid
|
||||
cognitivecomputations/dolphin3.0-mistral-24b | paid
|
||||
cognitivecomputations/dolphin3.0-mistral-24b:free | free
|
||||
cognitivecomputations/dolphin3.0-r1-mistral-24b | paid
|
||||
cognitivecomputations/dolphin3.0-r1-mistral-24b:free | free
|
||||
eleutherai/llemma_7b | paid
|
||||
eva-unit-01/eva-llama-3.33-70b | paid
|
||||
eva-unit-01/eva-qwen-2.5-72b | paid
|
||||
sao10k/fimbulvetr-11b-v2 | paid
|
||||
alpindale/goliath-120b | paid
|
||||
google/gemini-flash-1.5 | paid
|
||||
@ -124,6 +111,7 @@ export interface IKBotOptions {
|
||||
google/gemini-2.0-flash-exp:free | free
|
||||
google/gemini-2.0-flash-lite-001 | paid
|
||||
google/gemini-2.5-flash | paid
|
||||
google/gemini-2.5-flash-lite | paid
|
||||
google/gemini-2.5-flash-lite-preview-06-17 | paid
|
||||
google/gemini-2.5-pro | paid
|
||||
google/gemini-2.5-pro-exp-03-25 | paid
|
||||
@ -141,6 +129,7 @@ export interface IKBotOptions {
|
||||
google/gemma-3n-e2b-it:free | free
|
||||
google/gemma-3n-e4b-it | paid
|
||||
google/gemma-3n-e4b-it:free | free
|
||||
openrouter/horizon-beta | paid
|
||||
inception/mercury | paid
|
||||
inception/mercury-coder | paid
|
||||
infermatic/mn-inferor-12b | paid
|
||||
@ -151,7 +140,6 @@ export interface IKBotOptions {
|
||||
liquid/lfm-40b | paid
|
||||
liquid/lfm-7b | paid
|
||||
meta-llama/llama-guard-3-8b | paid
|
||||
alpindale/magnum-72b | paid
|
||||
anthracite-org/magnum-v2-72b | paid
|
||||
anthracite-org/magnum-v4-72b | paid
|
||||
mancer/weaver | paid
|
||||
@ -174,6 +162,7 @@ export interface IKBotOptions {
|
||||
meta-llama/llama-4-scout | paid
|
||||
meta-llama/llama-guard-4-12b | paid
|
||||
meta-llama/llama-guard-2-8b | paid
|
||||
microsoft/mai-ds-r1 | paid
|
||||
microsoft/mai-ds-r1:free | free
|
||||
microsoft/phi-4 | paid
|
||||
microsoft/phi-4-multimodal-instruct | paid
|
||||
@ -187,10 +176,10 @@ export interface IKBotOptions {
|
||||
mistralai/mistral-large | paid
|
||||
mistralai/mistral-large-2407 | paid
|
||||
mistralai/mistral-large-2411 | paid
|
||||
nothingiisreal/mn-celeste-12b | paid
|
||||
mistralai/mistral-small | paid
|
||||
mistralai/mistral-tiny | paid
|
||||
mistralai/codestral-2501 | paid
|
||||
mistralai/codestral-2508 | paid
|
||||
mistralai/devstral-medium | paid
|
||||
mistralai/devstral-small | paid
|
||||
mistralai/devstral-small-2505 | paid
|
||||
@ -219,10 +208,10 @@ export interface IKBotOptions {
|
||||
mistralai/pixtral-12b | paid
|
||||
mistralai/pixtral-large-2411 | paid
|
||||
mistralai/mistral-saba | paid
|
||||
moonshotai/kimi-vl-a3b-thinking | paid
|
||||
moonshotai/kimi-vl-a3b-thinking:free | free
|
||||
moonshotai/kimi-k2 | paid
|
||||
moonshotai/kimi-k2:free | free
|
||||
morph/morph-v2 | paid
|
||||
morph/morph-v3-fast | paid
|
||||
morph/morph-v3-large | paid
|
||||
gryphe/mythomax-l2-13b | paid
|
||||
@ -230,6 +219,7 @@ export interface IKBotOptions {
|
||||
neversleep/llama-3.1-lumimaid-8b | paid
|
||||
neversleep/noromaid-20b | paid
|
||||
nousresearch/deephermes-3-llama-3-8b-preview:free | free
|
||||
nousresearch/deephermes-3-mistral-24b-preview | paid
|
||||
nousresearch/nous-hermes-2-mixtral-8x7b-dpo | paid
|
||||
nousresearch/hermes-3-llama-3.1-405b | paid
|
||||
nousresearch/hermes-3-llama-3.1-70b | paid
|
||||
@ -240,6 +230,8 @@ export interface IKBotOptions {
|
||||
nvidia/llama-3.3-nemotron-super-49b-v1 | paid
|
||||
openai/chatgpt-4o-latest | paid
|
||||
openai/codex-mini | paid
|
||||
openai/gpt-oss-120b | paid
|
||||
openai/gpt-oss-20b | paid
|
||||
openai/gpt-3.5-turbo | paid
|
||||
openai/gpt-3.5-turbo-0613 | paid
|
||||
openai/gpt-3.5-turbo-16k | paid
|
||||
@ -264,8 +256,6 @@ export interface IKBotOptions {
|
||||
openai/o1 | paid
|
||||
openai/o1-mini | paid
|
||||
openai/o1-mini-2024-09-12 | paid
|
||||
openai/o1-preview | paid
|
||||
openai/o1-preview-2024-09-12 | paid
|
||||
openai/o1-pro | paid
|
||||
openai/o3 | paid
|
||||
openai/o3-mini | paid
|
||||
@ -281,6 +271,7 @@ export interface IKBotOptions {
|
||||
perplexity/sonar-reasoning | paid
|
||||
perplexity/sonar-reasoning-pro | paid
|
||||
pygmalionai/mythalion-13b | paid
|
||||
featherless/qwerky-72b:free | free
|
||||
qwen/qwen-2-72b-instruct | paid
|
||||
qwen/qwen-vl-max | paid
|
||||
qwen/qwen-vl-plus | paid
|
||||
@ -296,13 +287,16 @@ export interface IKBotOptions {
|
||||
qwen/qwen3-14b:free | free
|
||||
qwen/qwen3-235b-a22b | paid
|
||||
qwen/qwen3-235b-a22b:free | free
|
||||
qwen/qwen3-235b-a22b-2507 | paid
|
||||
qwen/qwen3-235b-a22b-thinking-2507 | paid
|
||||
qwen/qwen3-30b-a3b | paid
|
||||
qwen/qwen3-30b-a3b:free | free
|
||||
qwen/qwen3-30b-a3b-instruct-2507 | paid
|
||||
qwen/qwen3-32b | paid
|
||||
qwen/qwen3-32b:free | free
|
||||
qwen/qwen3-4b:free | free
|
||||
qwen/qwen3-8b | paid
|
||||
qwen/qwen3-8b:free | free
|
||||
qwen/qwen3-coder | paid
|
||||
qwen/qwq-32b | paid
|
||||
qwen/qwq-32b:free | free
|
||||
qwen/qwq-32b-preview | paid
|
||||
@ -311,16 +305,14 @@ export interface IKBotOptions {
|
||||
qwen/qwen-2.5-7b-instruct | paid
|
||||
qwen/qwen-2.5-coder-32b-instruct | paid
|
||||
qwen/qwen-2.5-coder-32b-instruct:free | free
|
||||
featherless/qwerky-72b:free | free
|
||||
rekaai/reka-flash-3 | paid
|
||||
rekaai/reka-flash-3:free | free
|
||||
undi95/remm-slerp-l2-13b | paid
|
||||
sao10k/l3-lunaris-8b | paid
|
||||
sao10k/l3-euryale-70b | paid
|
||||
sao10k/l3.1-euryale-70b | paid
|
||||
sao10k/l3.3-euryale-70b | paid
|
||||
sarvamai/sarvam-m | paid
|
||||
sarvamai/sarvam-m:free | free
|
||||
shisa-ai/shisa-v2-llama3.3-70b | paid
|
||||
shisa-ai/shisa-v2-llama3.3-70b:free | free
|
||||
raifle/sorcererlm-8x22b | paid
|
||||
switchpoint/router | paid
|
||||
@ -333,9 +325,9 @@ export interface IKBotOptions {
|
||||
thedrummer/unslopnemo-12b | paid
|
||||
thedrummer/valkyrie-49b-v1 | paid
|
||||
thudm/glm-4-32b | paid
|
||||
thudm/glm-4-32b:free | free
|
||||
thudm/glm-4.1v-9b-thinking | paid
|
||||
thudm/glm-z1-32b:free | free
|
||||
tngtech/deepseek-r1t-chimera | paid
|
||||
tngtech/deepseek-r1t-chimera:free | free
|
||||
tngtech/deepseek-r1t2-chimera:free | free
|
||||
undi95/toppy-m-7b | paid
|
||||
@ -350,6 +342,10 @@ export interface IKBotOptions {
|
||||
x-ai/grok-3-mini-beta | paid
|
||||
x-ai/grok-4 | paid
|
||||
x-ai/grok-vision-beta | paid
|
||||
z-ai/glm-4-32b | paid
|
||||
z-ai/glm-4.5 | paid
|
||||
z-ai/glm-4.5-air | paid
|
||||
z-ai/glm-4.5-air:free | free
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenAI models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
@ -408,8 +404,6 @@ export interface IKBotOptions {
|
||||
o1-2024-12-17
|
||||
o1-mini
|
||||
o1-mini-2024-09-12
|
||||
o1-preview
|
||||
o1-preview-2024-09-12
|
||||
o1-pro
|
||||
o1-pro-2025-03-19
|
||||
o3-mini
|
||||
|
||||
1525
packages/ai-tools/package-lock.json
generated
@ -30,6 +30,7 @@
|
||||
"dependencies": {
|
||||
"@datalust/winston-seq": "^2.0.0",
|
||||
"@inquirer/prompts": "^7.3.2",
|
||||
"@keyv/sqlite": "^4.0.5",
|
||||
"@polymech/commons": "file:../commons",
|
||||
"@polymech/core": "file:../core",
|
||||
"@polymech/fs": "file:../fs",
|
||||
@ -40,6 +41,8 @@
|
||||
"glob": "^11.0.1",
|
||||
"inquirer": "^12.2.0",
|
||||
"jsdom": "^25.0.1",
|
||||
"keyv": "^5.5.0",
|
||||
"keyv-file": "^5.1.3",
|
||||
"marked": "^15.0.4",
|
||||
"mime-types": "^2.1.35",
|
||||
"nodemailer": "^6.9.16",
|
||||
|
||||
@ -98,7 +98,7 @@ const decodeContentSmart = (content: string, logger: any, identifier: string): s
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('fs', options)
|
||||
const category = 'fs'
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
|
||||
22
packages/ai-tools/src/lib/tools/keyv.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import * as path from 'path'
|
||||
import Keyv from 'keyv'
|
||||
import KeyvSqlite from '@keyv/sqlite'
|
||||
|
||||
import { resolve } from '@polymech/commons'
|
||||
|
||||
export const store = (storePath: string, ns: string = 'ns-unknown', opts: any = {}) => {
|
||||
const keyvSqlite = new KeyvSqlite(path.resolve(resolve(storePath)))
|
||||
return new Keyv({ store: keyvSqlite, ttl: 5000, namespace: ns, ...opts })
|
||||
}
|
||||
export const get = async (key: string, storePath: string, ns: string = 'ns-unknown', opts: any = {}) => {
|
||||
const keyv = store(storePath, ns, opts)
|
||||
return await keyv.get(key)
|
||||
}
|
||||
export const set = async (key: string, value: any, storePath: string, ns: string = 'ns-unknown', opts: any = {}) => {
|
||||
const keyv = store(storePath, ns, opts)
|
||||
return await keyv.set(key, value)
|
||||
}
|
||||
export const list = async (key: string, value: any, storePath: string, ns: string = 'ns-unknown', opts: any = {}) => {
|
||||
const keyv = store(storePath, ns, opts)
|
||||
return await keyv.set(key, value)
|
||||
}
|
||||
379
packages/ai-tools/src/lib/tools/memory.ts
Normal file
@ -0,0 +1,379 @@
|
||||
import * as path from 'path'
|
||||
import { RunnableToolFunction } from 'openai/lib/RunnableFunction'
|
||||
import { isString } from '@polymech/core/primitives'
|
||||
|
||||
import { toolLogger } from '../../index.js'
|
||||
import { IKBotTask } from '../../types.js'
|
||||
|
||||
import { store, get, set } from './keyv.js'
|
||||
|
||||
// Helper function to get storage path
|
||||
const getStoragePath = (options: IKBotTask): string => {
|
||||
// For now, use default path. Later this can be configured via options
|
||||
return path.join(process.cwd(), 'memory.sqlite');
|
||||
};
|
||||
|
||||
// Default collection name when none provided
|
||||
const DEFAULT_COLLECTION = 'no-collection';
|
||||
|
||||
// Supported data formats
|
||||
type DataFormat = 'text' | 'json' | 'binary';
|
||||
|
||||
// Memory entry structure
|
||||
interface MemoryEntry {
|
||||
value: string;
|
||||
meta: {
|
||||
type: DataFormat;
|
||||
created: string;
|
||||
updated: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Helper function to process value based on format
|
||||
const processValueForStorage = (value: string, format: DataFormat): string => {
|
||||
switch (format) {
|
||||
case 'json':
|
||||
try {
|
||||
// Validate JSON by parsing and re-stringifying
|
||||
JSON.parse(value);
|
||||
return value;
|
||||
} catch (error) {
|
||||
throw new Error('Invalid JSON format provided');
|
||||
}
|
||||
case 'binary':
|
||||
// For binary, we expect base64 encoded data
|
||||
try {
|
||||
// Validate base64
|
||||
Buffer.from(value, 'base64');
|
||||
return value;
|
||||
} catch (error) {
|
||||
throw new Error('Invalid base64 format for binary data');
|
||||
}
|
||||
case 'text':
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to create memory entry
|
||||
const createMemoryEntry = (value: string, format: DataFormat): MemoryEntry => {
|
||||
const now = new Date().toISOString();
|
||||
return {
|
||||
value: processValueForStorage(value, format),
|
||||
meta: {
|
||||
type: format,
|
||||
created: now,
|
||||
updated: now
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const tools = (target: string, options: IKBotTask): Array<any> => {
|
||||
const logger = toolLogger('memory', options)
|
||||
const storagePath = getStoragePath(options);
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'memorize',
|
||||
description: `Store information in memory as a key-value collection with format support. Supports text, JSON, and binary (base64) formats.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
message: string,
|
||||
meta: {
|
||||
type: "text" | "json" | "binary",
|
||||
created: string (ISO timestamp),
|
||||
updated: string (ISO timestamp)
|
||||
}
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to organize related data (defaults to "no-collection" if not provided). Acts like a namespace.',
|
||||
optional: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'Unique identifier for the data within the collection. Must be a string.'
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
description: 'The data to store. For format="text": any string. For format="json": valid JSON string. For format="binary": base64 encoded data.'
|
||||
},
|
||||
format: {
|
||||
type: 'string',
|
||||
description: 'Data format type. "text" for plain text (default), "json" for JSON data (validates structure), "binary" for base64 encoded binary data.',
|
||||
enum: ['text', 'json', 'binary'],
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: ['key', 'value']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION, key, value, format = 'text' } = params;
|
||||
logger.debug(`Tool::Memorize Storing ${key} in collection ${collection} as ${format}`);
|
||||
|
||||
const memoryEntry = createMemoryEntry(value, format as DataFormat);
|
||||
await set(`${collection}:${key}`, memoryEntry, storagePath, collection);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Stored ${key} in collection ${collection} as ${format}`,
|
||||
meta: memoryEntry.meta
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error storing memory', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'recall',
|
||||
description: `Retrieve stored information from memory by collection and key, including format metadata.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
value?: string (the stored data),
|
||||
meta?: {
|
||||
type: "text" | "json" | "binary",
|
||||
created: string (ISO timestamp),
|
||||
updated: string (ISO timestamp)
|
||||
},
|
||||
key: string,
|
||||
collection: string,
|
||||
message?: string (error message if success=false)
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to retrieve from (defaults to "no-collection" if not provided). Must match the collection used when storing.',
|
||||
optional: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'The unique identifier of the data to retrieve. Must match the key used when storing.'
|
||||
}
|
||||
},
|
||||
required: ['key']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION, key } = params;
|
||||
logger.debug(`Tool::Recall Retrieving ${key} from collection ${collection}`);
|
||||
|
||||
const storedData = await get(`${collection}:${key}`, storagePath, collection);
|
||||
if (storedData === undefined) {
|
||||
return { success: false, message: `Key ${key} not found in collection ${collection}` };
|
||||
}
|
||||
|
||||
// Handle both old format (plain string) and new format (MemoryEntry)
|
||||
let memoryEntry: MemoryEntry;
|
||||
if (typeof storedData === 'string') {
|
||||
// Legacy format - convert to new format
|
||||
memoryEntry = {
|
||||
value: storedData,
|
||||
meta: {
|
||||
type: 'text',
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
} else {
|
||||
memoryEntry = storedData as MemoryEntry;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
value: memoryEntry.value,
|
||||
meta: memoryEntry.meta,
|
||||
key,
|
||||
collection
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error retrieving memory', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'forget',
|
||||
description: `Remove a specific key from memory collection.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
message: string (confirmation or error message)
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to remove from (defaults to "no-collection" if not provided). Must match the collection where the key was stored.',
|
||||
optional: true
|
||||
},
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'The unique identifier of the data to remove. Must match exactly the key used when storing.'
|
||||
}
|
||||
},
|
||||
required: ['key']
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION, key } = params;
|
||||
logger.debug(`Tool::Forget Removing ${key} from collection ${collection}`);
|
||||
|
||||
const keyv = store(storagePath, collection);
|
||||
const deleted = await keyv.delete(`${collection}:${key}`);
|
||||
return { success: deleted, message: deleted ? `Removed ${key} from ${collection}` : `Key ${key} not found in ${collection}` };
|
||||
} catch (error) {
|
||||
logger.error('Error removing from memory', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>,
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'list_memories',
|
||||
description: `List all keys in a specific collection using Keyv's iterator method.
|
||||
|
||||
Returns: {
|
||||
success: boolean,
|
||||
collection: string (the collection name),
|
||||
keys: string[] (array of key names in the collection),
|
||||
entries: Array<{
|
||||
key: string,
|
||||
meta?: {
|
||||
type: "text" | "json" | "binary",
|
||||
created: string (ISO timestamp),
|
||||
updated: string (ISO timestamp)
|
||||
}
|
||||
}>,
|
||||
count: number (total number of keys),
|
||||
message?: string (info or error message)
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
collection: {
|
||||
type: 'string',
|
||||
description: 'Collection name to list keys from (defaults to "no-collection" if not provided). Will return all keys stored in this collection namespace.',
|
||||
optional: true
|
||||
}
|
||||
},
|
||||
required: []
|
||||
},
|
||||
function: async (params: any) => {
|
||||
try {
|
||||
const { collection = DEFAULT_COLLECTION } = params;
|
||||
|
||||
logger.debug(`Tool::ListMemories Listing keys in collection ${collection}`);
|
||||
|
||||
// Create a Keyv instance for the specific collection to use iterator
|
||||
const keyv = store(storagePath, collection);
|
||||
const keys: string[] = [];
|
||||
const entries: { key: string; meta?: any }[] = [];
|
||||
|
||||
try {
|
||||
// Check if iterator method exists and use it
|
||||
if (typeof keyv.iterator === 'function') {
|
||||
try {
|
||||
// Try calling iterator without arguments first
|
||||
const iterator = (keyv as any).iterator();
|
||||
for await (const [key, value] of iterator) {
|
||||
// Remove the collection prefix from the key to get the clean key name
|
||||
const cleanKey = key.replace(`${collection}:`, '');
|
||||
keys.push(cleanKey);
|
||||
|
||||
// Try to extract metadata if it's a MemoryEntry
|
||||
let meta = undefined;
|
||||
if (value && typeof value === 'object' && value.meta) {
|
||||
meta = value.meta;
|
||||
}
|
||||
|
||||
entries.push({ key: cleanKey, meta });
|
||||
}
|
||||
} catch (iteratorCallError) {
|
||||
logger.warn(`Tool::ListMemories Iterator call failed:`, iteratorCallError);
|
||||
// Fall through to the not available case
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys: [],
|
||||
entries: [],
|
||||
count: 0,
|
||||
message: 'Iterator call failed. Unable to list keys.'
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Iterator not available, provide helpful message
|
||||
logger.warn(`Tool::ListMemories Iterator method not available for Keyv instance`);
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys: [],
|
||||
entries: [],
|
||||
count: 0,
|
||||
message: 'Iterator method not available in this Keyv version. Use individual key operations instead.'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys,
|
||||
entries,
|
||||
count: keys.length
|
||||
};
|
||||
} catch (iteratorError) {
|
||||
// If iterator fails, fall back to returning basic info
|
||||
logger.warn(`Tool::ListMemories Iterator failed for collection ${collection}:`, iteratorError);
|
||||
return {
|
||||
success: true,
|
||||
collection,
|
||||
keys: [],
|
||||
entries: [],
|
||||
count: 0,
|
||||
message: 'Iterator failed or collection is empty'
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error listing memories', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
};
|
||||
}
|
||||
},
|
||||
parse: JSON.parse
|
||||
}
|
||||
} as RunnableToolFunction<any>
|
||||
]
|
||||
};
|
||||
@ -6,6 +6,7 @@ import { tools as interactTools } from './interact.js'
|
||||
import { tools as userTools } from './user.js'
|
||||
import { tools as search } from './search.js'
|
||||
import { tools as webTools } from './web.js'
|
||||
import { tools as memoryTools } from './memory.js'
|
||||
//import { tools as emailTools } from './email'
|
||||
|
||||
export const tools = {
|
||||
@ -17,5 +18,6 @@ export const tools = {
|
||||
user: userTools,
|
||||
search: search,
|
||||
web: webTools,
|
||||
memory: memoryTools,
|
||||
// email: emailTools
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ export interface IKBotOptions {
|
||||
/** Optional destination path for the result, will substitute ${MODEL_NAME} and ${ROUTER} in the path. Optional, used for "completion" mode */
|
||||
dst?: string | undefined;
|
||||
/** How to handle output if --dst file already exists: "concat" (append) or "merge" (try to merge structures if possible, otherwise append). Only used if --dst is specified. */
|
||||
append?: ("concat" | "merge") | undefined;
|
||||
append?: ("concat" | "merge" | "replace") | undefined;
|
||||
/** Specify how to wrap the output, "meta (file name, absolute path, cwd)" or "none". */
|
||||
wrap?: "meta" | "none";
|
||||
/** Iterate over items, supported: GLOB | Path to JSON File | array of strings (comma separated). To test different models, use --each="gpt-3.5-turbo,gpt-4o", the actual string will exposed as variable `ITEM`, eg: --dst="${ITEM}-output.md" */
|
||||
@ -31,8 +31,6 @@ export interface IKBotOptions {
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenRouter models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
01-ai/yi-large | paid
|
||||
aetherwiing/mn-starcannon-12b | paid
|
||||
agentica-org/deepcoder-14b-preview | paid
|
||||
agentica-org/deepcoder-14b-preview:free | free
|
||||
ai21/jamba-1.6-large | paid
|
||||
@ -48,11 +46,8 @@ export interface IKBotOptions {
|
||||
anthropic/claude-3-haiku:beta | paid
|
||||
anthropic/claude-3-opus | paid
|
||||
anthropic/claude-3-opus:beta | paid
|
||||
anthropic/claude-3-sonnet | paid
|
||||
anthropic/claude-3-sonnet:beta | paid
|
||||
anthropic/claude-3.5-haiku | paid
|
||||
anthropic/claude-3.5-haiku-20241022 | paid
|
||||
anthropic/claude-3.5-haiku-20241022:beta | paid
|
||||
anthropic/claude-3.5-haiku:beta | paid
|
||||
anthropic/claude-3.5-sonnet | paid
|
||||
anthropic/claude-3.5-sonnet-20240620 | paid
|
||||
@ -62,23 +57,17 @@ export interface IKBotOptions {
|
||||
anthropic/claude-3.7-sonnet:beta | paid
|
||||
anthropic/claude-3.7-sonnet:thinking | paid
|
||||
anthropic/claude-opus-4 | paid
|
||||
anthropic/claude-opus-4.1 | paid
|
||||
anthropic/claude-sonnet-4 | paid
|
||||
anthropic/claude-2 | paid
|
||||
anthropic/claude-2:beta | paid
|
||||
anthropic/claude-2.0 | paid
|
||||
anthropic/claude-2.0:beta | paid
|
||||
anthropic/claude-2.1 | paid
|
||||
anthropic/claude-2.1:beta | paid
|
||||
arcee-ai/arcee-blitz | paid
|
||||
arcee-ai/caller-large | paid
|
||||
arcee-ai/coder-large | paid
|
||||
arcee-ai/maestro-reasoning | paid
|
||||
arcee-ai/spotlight | paid
|
||||
arcee-ai/virtuoso-large | paid
|
||||
arcee-ai/virtuoso-medium-v2 | paid
|
||||
arliai/qwq-32b-arliai-rpr-v1 | paid
|
||||
arliai/qwq-32b-arliai-rpr-v1:free | free
|
||||
openrouter/auto | paid
|
||||
baidu/ernie-4.5-300b-a47b | paid
|
||||
bytedance/ui-tars-1.5-7b | paid
|
||||
cohere/command | paid
|
||||
cohere/command-a | paid
|
||||
cohere/command-r | paid
|
||||
@ -92,10 +81,9 @@ export interface IKBotOptions {
|
||||
deepseek/deepseek-r1-0528-qwen3-8b | paid
|
||||
deepseek/deepseek-r1-0528-qwen3-8b:free | free
|
||||
deepseek/deepseek-chat | paid
|
||||
deepseek/deepseek-chat:free | free
|
||||
deepseek/deepseek-chat-v3-0324 | paid
|
||||
deepseek/deepseek-chat-v3-0324:free | free
|
||||
deepseek/deepseek-v3-base:free | free
|
||||
deepseek/deepseek-v3-base | paid
|
||||
deepseek/deepseek-r1 | paid
|
||||
deepseek/deepseek-r1:free | free
|
||||
deepseek/deepseek-r1-0528 | paid
|
||||
@ -109,12 +97,11 @@ export interface IKBotOptions {
|
||||
deepseek/deepseek-r1-distill-qwen-32b | paid
|
||||
deepseek/deepseek-r1-distill-qwen-7b | paid
|
||||
cognitivecomputations/dolphin-mixtral-8x22b | paid
|
||||
cognitivecomputations/dolphin3.0-mistral-24b | paid
|
||||
cognitivecomputations/dolphin3.0-mistral-24b:free | free
|
||||
cognitivecomputations/dolphin3.0-r1-mistral-24b | paid
|
||||
cognitivecomputations/dolphin3.0-r1-mistral-24b:free | free
|
||||
eleutherai/llemma_7b | paid
|
||||
eva-unit-01/eva-llama-3.33-70b | paid
|
||||
eva-unit-01/eva-qwen-2.5-72b | paid
|
||||
sao10k/fimbulvetr-11b-v2 | paid
|
||||
alpindale/goliath-120b | paid
|
||||
google/gemini-flash-1.5 | paid
|
||||
@ -124,6 +111,7 @@ export interface IKBotOptions {
|
||||
google/gemini-2.0-flash-exp:free | free
|
||||
google/gemini-2.0-flash-lite-001 | paid
|
||||
google/gemini-2.5-flash | paid
|
||||
google/gemini-2.5-flash-lite | paid
|
||||
google/gemini-2.5-flash-lite-preview-06-17 | paid
|
||||
google/gemini-2.5-pro | paid
|
||||
google/gemini-2.5-pro-exp-03-25 | paid
|
||||
@ -141,6 +129,7 @@ export interface IKBotOptions {
|
||||
google/gemma-3n-e2b-it:free | free
|
||||
google/gemma-3n-e4b-it | paid
|
||||
google/gemma-3n-e4b-it:free | free
|
||||
openrouter/horizon-beta | paid
|
||||
inception/mercury | paid
|
||||
inception/mercury-coder | paid
|
||||
infermatic/mn-inferor-12b | paid
|
||||
@ -151,7 +140,6 @@ export interface IKBotOptions {
|
||||
liquid/lfm-40b | paid
|
||||
liquid/lfm-7b | paid
|
||||
meta-llama/llama-guard-3-8b | paid
|
||||
alpindale/magnum-72b | paid
|
||||
anthracite-org/magnum-v2-72b | paid
|
||||
anthracite-org/magnum-v4-72b | paid
|
||||
mancer/weaver | paid
|
||||
@ -174,6 +162,7 @@ export interface IKBotOptions {
|
||||
meta-llama/llama-4-scout | paid
|
||||
meta-llama/llama-guard-4-12b | paid
|
||||
meta-llama/llama-guard-2-8b | paid
|
||||
microsoft/mai-ds-r1 | paid
|
||||
microsoft/mai-ds-r1:free | free
|
||||
microsoft/phi-4 | paid
|
||||
microsoft/phi-4-multimodal-instruct | paid
|
||||
@ -187,10 +176,10 @@ export interface IKBotOptions {
|
||||
mistralai/mistral-large | paid
|
||||
mistralai/mistral-large-2407 | paid
|
||||
mistralai/mistral-large-2411 | paid
|
||||
nothingiisreal/mn-celeste-12b | paid
|
||||
mistralai/mistral-small | paid
|
||||
mistralai/mistral-tiny | paid
|
||||
mistralai/codestral-2501 | paid
|
||||
mistralai/codestral-2508 | paid
|
||||
mistralai/devstral-medium | paid
|
||||
mistralai/devstral-small | paid
|
||||
mistralai/devstral-small-2505 | paid
|
||||
@ -219,10 +208,10 @@ export interface IKBotOptions {
|
||||
mistralai/pixtral-12b | paid
|
||||
mistralai/pixtral-large-2411 | paid
|
||||
mistralai/mistral-saba | paid
|
||||
moonshotai/kimi-vl-a3b-thinking | paid
|
||||
moonshotai/kimi-vl-a3b-thinking:free | free
|
||||
moonshotai/kimi-k2 | paid
|
||||
moonshotai/kimi-k2:free | free
|
||||
morph/morph-v2 | paid
|
||||
morph/morph-v3-fast | paid
|
||||
morph/morph-v3-large | paid
|
||||
gryphe/mythomax-l2-13b | paid
|
||||
@ -230,6 +219,7 @@ export interface IKBotOptions {
|
||||
neversleep/llama-3.1-lumimaid-8b | paid
|
||||
neversleep/noromaid-20b | paid
|
||||
nousresearch/deephermes-3-llama-3-8b-preview:free | free
|
||||
nousresearch/deephermes-3-mistral-24b-preview | paid
|
||||
nousresearch/nous-hermes-2-mixtral-8x7b-dpo | paid
|
||||
nousresearch/hermes-3-llama-3.1-405b | paid
|
||||
nousresearch/hermes-3-llama-3.1-70b | paid
|
||||
@ -240,6 +230,8 @@ export interface IKBotOptions {
|
||||
nvidia/llama-3.3-nemotron-super-49b-v1 | paid
|
||||
openai/chatgpt-4o-latest | paid
|
||||
openai/codex-mini | paid
|
||||
openai/gpt-oss-120b | paid
|
||||
openai/gpt-oss-20b | paid
|
||||
openai/gpt-3.5-turbo | paid
|
||||
openai/gpt-3.5-turbo-0613 | paid
|
||||
openai/gpt-3.5-turbo-16k | paid
|
||||
@ -264,8 +256,6 @@ export interface IKBotOptions {
|
||||
openai/o1 | paid
|
||||
openai/o1-mini | paid
|
||||
openai/o1-mini-2024-09-12 | paid
|
||||
openai/o1-preview | paid
|
||||
openai/o1-preview-2024-09-12 | paid
|
||||
openai/o1-pro | paid
|
||||
openai/o3 | paid
|
||||
openai/o3-mini | paid
|
||||
@ -281,6 +271,7 @@ export interface IKBotOptions {
|
||||
perplexity/sonar-reasoning | paid
|
||||
perplexity/sonar-reasoning-pro | paid
|
||||
pygmalionai/mythalion-13b | paid
|
||||
featherless/qwerky-72b:free | free
|
||||
qwen/qwen-2-72b-instruct | paid
|
||||
qwen/qwen-vl-max | paid
|
||||
qwen/qwen-vl-plus | paid
|
||||
@ -296,13 +287,16 @@ export interface IKBotOptions {
|
||||
qwen/qwen3-14b:free | free
|
||||
qwen/qwen3-235b-a22b | paid
|
||||
qwen/qwen3-235b-a22b:free | free
|
||||
qwen/qwen3-235b-a22b-2507 | paid
|
||||
qwen/qwen3-235b-a22b-thinking-2507 | paid
|
||||
qwen/qwen3-30b-a3b | paid
|
||||
qwen/qwen3-30b-a3b:free | free
|
||||
qwen/qwen3-30b-a3b-instruct-2507 | paid
|
||||
qwen/qwen3-32b | paid
|
||||
qwen/qwen3-32b:free | free
|
||||
qwen/qwen3-4b:free | free
|
||||
qwen/qwen3-8b | paid
|
||||
qwen/qwen3-8b:free | free
|
||||
qwen/qwen3-coder | paid
|
||||
qwen/qwq-32b | paid
|
||||
qwen/qwq-32b:free | free
|
||||
qwen/qwq-32b-preview | paid
|
||||
@ -311,16 +305,14 @@ export interface IKBotOptions {
|
||||
qwen/qwen-2.5-7b-instruct | paid
|
||||
qwen/qwen-2.5-coder-32b-instruct | paid
|
||||
qwen/qwen-2.5-coder-32b-instruct:free | free
|
||||
featherless/qwerky-72b:free | free
|
||||
rekaai/reka-flash-3 | paid
|
||||
rekaai/reka-flash-3:free | free
|
||||
undi95/remm-slerp-l2-13b | paid
|
||||
sao10k/l3-lunaris-8b | paid
|
||||
sao10k/l3-euryale-70b | paid
|
||||
sao10k/l3.1-euryale-70b | paid
|
||||
sao10k/l3.3-euryale-70b | paid
|
||||
sarvamai/sarvam-m | paid
|
||||
sarvamai/sarvam-m:free | free
|
||||
shisa-ai/shisa-v2-llama3.3-70b | paid
|
||||
shisa-ai/shisa-v2-llama3.3-70b:free | free
|
||||
raifle/sorcererlm-8x22b | paid
|
||||
switchpoint/router | paid
|
||||
@ -333,9 +325,9 @@ export interface IKBotOptions {
|
||||
thedrummer/unslopnemo-12b | paid
|
||||
thedrummer/valkyrie-49b-v1 | paid
|
||||
thudm/glm-4-32b | paid
|
||||
thudm/glm-4-32b:free | free
|
||||
thudm/glm-4.1v-9b-thinking | paid
|
||||
thudm/glm-z1-32b:free | free
|
||||
tngtech/deepseek-r1t-chimera | paid
|
||||
tngtech/deepseek-r1t-chimera:free | free
|
||||
tngtech/deepseek-r1t2-chimera:free | free
|
||||
undi95/toppy-m-7b | paid
|
||||
@ -350,6 +342,10 @@ export interface IKBotOptions {
|
||||
x-ai/grok-3-mini-beta | paid
|
||||
x-ai/grok-4 | paid
|
||||
x-ai/grok-vision-beta | paid
|
||||
z-ai/glm-4-32b | paid
|
||||
z-ai/glm-4.5 | paid
|
||||
z-ai/glm-4.5-air | paid
|
||||
z-ai/glm-4.5-air:free | free
|
||||
[35m[1m[22m[39m
|
||||
[35m[1m OpenAI models:[22m[39m
|
||||
[35m[1m[22m[39m
|
||||
@ -408,8 +404,6 @@ export interface IKBotOptions {
|
||||
o1-2024-12-17
|
||||
o1-mini
|
||||
o1-mini-2024-09-12
|
||||
o1-preview
|
||||
o1-preview-2024-09-12
|
||||
o1-pro
|
||||
o1-pro-2025-03-19
|
||||
o3-mini
|
||||
|
||||
2
packages/kbot/scripts/ollama/oai-20b.sh
Normal file
@ -0,0 +1,2 @@
|
||||
ollama pull gpt-oss:20b
|
||||
ollama run gpt-oss:20b
|
||||
11
packages/kbot/scripts/ollama/test-oai-20b.sh
Normal file
@ -0,0 +1,11 @@
|
||||
kbot-d.cmd --router=ollama \
|
||||
--model=gpt-oss:20b \
|
||||
--mode=completion \
|
||||
--baseURL=http://localhost:11434/v1 \
|
||||
--prompt=./tools.md \
|
||||
--preferences=none \
|
||||
--filters=code \
|
||||
--dst=./tools-test-ouput.md
|
||||
|
||||
|
||||
|
||||
1
packages/kbot/scripts/ollama/tools.md
Normal file
@ -0,0 +1 @@
|
||||
meaning of love, per continent - as markdown
|
||||
6
packages/media/dist-in/commands/background-remove.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const command = "background:remove";
|
||||
export declare const desc = "Remove background from images using AI";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
86
packages/media/dist-in/commands/background-remove.js
Normal file
@ -0,0 +1,86 @@
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons';
|
||||
import { logger } from '../index.js';
|
||||
import { cli } from '../cli.js';
|
||||
import { sanitize, defaults } from '../_cli.js';
|
||||
import { backgroundRemove } from '../lib/media/images/background-remove.js';
|
||||
export const defaultOptions = (yargs) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
demandOption: true
|
||||
}).option('dst', {
|
||||
describe: 'FILE|FOLDER|GLOB'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('alt', {
|
||||
default: false,
|
||||
describe: 'Use alternate tokenizer, & instead of $',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Run without conversion',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('apiKey', {
|
||||
describe: 'Replicate API key (or set REPLICATE_API_TOKEN env var)',
|
||||
type: 'string'
|
||||
}).option('model', {
|
||||
describe: 'Background removal model to use',
|
||||
choices: ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta'],
|
||||
default: 'u2net'
|
||||
}).option('alphaMattting', {
|
||||
describe: 'Enable alpha matting for better edge refinement',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('alphaMattingForegroundThreshold', {
|
||||
describe: 'Alpha matting foreground threshold',
|
||||
type: 'number',
|
||||
default: 270
|
||||
}).option('alphaMattingBackgroundThreshold', {
|
||||
describe: 'Alpha matting background threshold',
|
||||
type: 'number',
|
||||
default: 10
|
||||
}).option('alphaMattingErodeSize', {
|
||||
describe: 'Alpha matting erode size',
|
||||
type: 'number',
|
||||
default: 10
|
||||
});
|
||||
};
|
||||
export const command = 'background:remove';
|
||||
export const desc = 'Remove background from images using AI';
|
||||
export const builder = defaultOptions;
|
||||
export async function handler(argv) {
|
||||
defaults();
|
||||
const options = sanitize(argv);
|
||||
logger.settings.minLevel = options.logLevel;
|
||||
const config = CONFIG_DEFAULT();
|
||||
// Get API key from argument or environment variable
|
||||
options.apiKey = options.apiKey || process.env.REPLICATE_API_TOKEN || config?.replicate?.key;
|
||||
if (!options.apiKey) {
|
||||
logger.error('Replicate API key is required. Provide it via --apiKey argument or set REPLICATE_API_TOKEN environment variable');
|
||||
logger.info('Get your API key at: https://replicate.com/account/api-tokens');
|
||||
process.exit(1);
|
||||
}
|
||||
// Map CLI arguments to library options
|
||||
options.model = argv.model;
|
||||
options.alpha_matting = argv.alphaMattting;
|
||||
options.alpha_matting_foreground_threshold = argv.alphaMattingForegroundThreshold;
|
||||
options.alpha_matting_background_threshold = argv.alphaMattingBackgroundThreshold;
|
||||
options.alpha_matting_erode_size = argv.alphaMattingErodeSize;
|
||||
logger.info("Removing background with options:", {
|
||||
model: options.model,
|
||||
alpha_matting: options.alpha_matting,
|
||||
files: options.srcInfo?.FILES?.length || 0
|
||||
});
|
||||
await backgroundRemove(options);
|
||||
}
|
||||
cli.command(command, desc, builder, handler);
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1yZW1vdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29tbWFuZHMvYmFja2dyb3VuZC1yZW1vdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1CQUFtQixDQUFBO0FBQ2xELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDcEMsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUMvQixPQUFPLEVBQ0gsUUFBUSxFQUNSLFFBQVEsRUFDWCxNQUFNLFlBQVksQ0FBQTtBQUVuQixPQUFPLEVBQ0gsZ0JBQWdCLEVBRW5CLE1BQU0sMENBQTBDLENBQUE7QUFFakQsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBZSxFQUFFLEVBQUU7SUFDOUMsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUN2QixRQUFRLEVBQUUsa0JBQWtCO1FBQzVCLFlBQVksRUFBRSxJQUFJO0tBQ3JCLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ2IsUUFBUSxFQUFFLGtCQUFrQjtLQUMvQixDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRTtRQUNmLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLGdDQUFnQztRQUMxQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHlDQUF5QztRQUNuRCxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHdCQUF3QjtRQUNsQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNqQixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx3QkFBd0I7UUFDbEMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUU7UUFDbEIsUUFBUSxFQUFFLHNDQUFzQztRQUNoRCxJQUFJLEVBQUUsUUFBUTtRQUNkLE9BQU8sRUFBRSxNQUFNO0tBQ2xCLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFO1FBQ2hCLFFBQVEsRUFBRSx3REFBd0Q7UUFDbEUsSUFBSSxFQUFFLFFBQVE7S0FDakIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDZixRQUFRLEVBQUUsaUNBQWlDO1FBQzNDLE9BQU8sRUFBRSxDQUFDLE9BQU8sRUFBRSxRQUFRLEVBQUUsaUJBQWlCLEVBQUUsaUJBQWlCLEVBQUUsU0FBUyxDQUFDO1FBQzdFLE9BQU8sRUFBRSxPQUFPO0tBQ25CLENBQUMsQ0FBQyxNQUFNLENBQUMsZUFBZSxFQUFFO1FBQ3ZCLFFBQVEsRUFBRSxpREFBaUQ7UUFDM0QsSUFBSSxFQUFFLFNBQVM7UUFDZixPQUFPLEVBQUUsS0FBSztLQUNqQixDQUFDLENBQUMsTUFBTSxDQUFDLGlDQUFpQyxFQUFFO1FBQ3pDLFFBQVEsRUFBRSxvQ0FBb0M7UUFDOUMsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsR0FBRztLQUNmLENBQUMsQ0FBQyxNQUFNLENBQUMsaUNBQWlDLEVBQUU7UUFDekMsUUFBUSxFQUFFLG9DQUFvQztRQUM5QyxJQUFJLEVBQUUsUUFBUTtRQUNkLE9BQU8sRUFBRSxFQUFFO0tBQ2QsQ0FBQyxDQUFDLE1BQU0sQ0FBQyx1QkFBdUIsRUFBRTtRQUMvQixRQUFRLEVBQUUsMEJBQTBCO1FBQ3BDLElBQUksRUFBRSxRQUFRO1FBQ2QsT0FBTyxFQUFFLEVBQUU7S0FDZCxDQUFDLENBQUE7QUFDTixDQUFDLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsbUJBQW1CLENBQUM7QUFDM0MsTUFBTSxDQUFDLE1BQU0sSUFBSSxHQUFHLHdDQUF3QyxDQUFDO0FBQzdELE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRyxjQUFjLENBQUM7QUFFdEMsTUFBTSxDQUFDLEtBQUssVUFBVSxPQUFPLENBQUMsSUFBbUI7SUFDN0MsUUFBUSxFQUFFLENBQUE7SUFDVixNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsSUFBSSxDQUE0QixDQUFBO0lBQ3pELE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFlLENBQUE7SUFDbEQsTUFBTSxNQUFNLEdBQVEsY0FBYyxFQUFFLENBQUE7SUFFcEMsb0RBQW9EO0lBQ3BELE9BQU8sQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixJQUFJLE1BQU0sRUFBRSxTQUFTLEVBQUUsR0FBRyxDQUFDO0lBRTdGLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDbEIsTUFBTSxDQUFDLEtBQUssQ0FBQyxpSEFBaUgsQ0FBQyxDQUFDO1FBQ2hJLE1BQU0sQ0FBQyxJQUFJLENBQUMsK0RBQStELENBQUMsQ0FBQztRQUM3RSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BCLENBQUM7SUFFRCx1Q0FBdUM7SUFDdkMsT0FBTyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBZSxDQUFDO0lBQ3JDLE9BQU8sQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDLGFBQXdCLENBQUM7SUFDdEQsT0FBTyxDQUFDLGtDQUFrQyxHQUFHLElBQUksQ0FBQywrQkFBeUMsQ0FBQztJQUM1RixPQUFPLENBQUMsa0NBQWtDLEdBQUcsSUFBSSxDQUFDLCtCQUF5QyxDQUFDO0lBQzVGLE9BQU8sQ0FBQyx3QkFBd0IsR0FBRyxJQUFJLENBQUMscUJBQStCLENBQUM7SUFFeEUsTUFBTSxDQUFDLElBQUksQ0FBQyxtQ0FBbUMsRUFBRTtRQUM3QyxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7UUFDcEIsYUFBYSxFQUFFLE9BQU8sQ0FBQyxhQUFhO1FBQ3BDLEtBQUssRUFBRSxPQUFPLENBQUMsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztLQUM3QyxDQUFDLENBQUM7SUFFSCxNQUFNLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQ3BDLENBQUM7QUFFRCxHQUFHLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFBIn0=
|
||||
5
packages/media/dist-in/commands/resize.d.ts
vendored
@ -1,3 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const register: (cli: CLI.Argv) => any;
|
||||
export declare const command = "resize";
|
||||
export declare const desc = "Resizes files";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { logger } from '../index.js';
|
||||
import { resize } from '../lib/media/images/resize.js';
|
||||
import { cli } from '../cli.js';
|
||||
import { sanitize, defaults } from '../_cli.js';
|
||||
export const defaultOptions = (yargs) => {
|
||||
return yargs.option('src', {
|
||||
@ -51,14 +52,15 @@ export const defaultOptions = (yargs) => {
|
||||
default: 'info'
|
||||
});
|
||||
};
|
||||
let options = (yargs) => defaultOptions(yargs);
|
||||
export const register = (cli) => {
|
||||
return cli.command('resize', 'Resizes files', options, async (argv) => {
|
||||
export const command = 'resize';
|
||||
export const desc = 'Resizes files';
|
||||
export const builder = defaultOptions;
|
||||
export async function handler(argv) {
|
||||
defaults();
|
||||
const options = sanitize(argv);
|
||||
logger.settings.minLevel = options.logLevel;
|
||||
logger.info("options " + argv.dst, options);
|
||||
await resize(options);
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzaXplLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL3Jlc2l6ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ3BDLE9BQU8sRUFDSCxNQUFNLEVBQ1QsTUFBTSwrQkFBK0IsQ0FBQTtBQUV0QyxPQUFPLEVBQ0gsUUFBUSxFQUNSLFFBQVEsRUFDWCxNQUFNLFlBQVksQ0FBQTtBQU1uQixNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsQ0FBQyxLQUFlLEVBQUUsRUFBRTtJQUM5QyxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ3ZCLFFBQVEsRUFBRSxrQkFBa0I7UUFDNUIsWUFBWSxFQUFFLElBQUk7S0FDckIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixRQUFRLEVBQUUsa0JBQWtCO0tBQy9CLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFO1FBQ2YsT0FBTyxFQUFFLEtBQUs7UUFDZCxRQUFRLEVBQUUsZ0NBQWdDO1FBQzFDLElBQUksRUFBRSxTQUFTO0tBQ2xCLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ2IsT0FBTyxFQUFFLEtBQUs7UUFDZCxRQUFRLEVBQUUseUNBQXlDO1FBQ25ELElBQUksRUFBRSxTQUFTO0tBQ2xCLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ2IsT0FBTyxFQUFFLEtBQUs7UUFDZCxRQUFRLEVBQUUsd0JBQXdCO1FBQ2xDLElBQUksRUFBRSxTQUFTO0tBQ2xCLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFO1FBQ2pCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHdCQUF3QjtRQUNsQyxJQUFJLEVBQUUsU0FBUztLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNqQixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSwyQkFBMkI7UUFDckMsSUFBSSxFQUFFLFFBQVE7S0FDakIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDZixRQUFRLEVBQUUsbUJBQW1CO1FBQzdCLElBQUksRUFBRSxRQUFRO0tBQ2pCLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFO1FBQ2hCLFFBQVEsRUFBRSxxQkFBcUI7UUFDL0IsSUFBSSxFQUFFLFFBQVE7S0FDakIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUU7UUFDbkIsUUFBUSxFQUFFLDZCQUE2QjtRQUN2QyxJQUFJLEVBQUUsUUFBUTtLQUNqQixDQUFDLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRTtRQUNsQixRQUFRLEVBQUUsNEJBQTRCO1FBQ3RDLElBQUksRUFBRSxRQUFRO0tBQ2pCLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFO1FBQ2pCLFFBQVEsRUFBRSwyQkFBMkI7UUFDckMsSUFBSSxFQUFFLFFBQVE7S0FDakIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDakIsUUFBUSxFQUFFLGlDQUFpQztRQUMzQyxJQUFJLEVBQUUsUUFBUTtLQUNqQixDQUFDLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRTtRQUNsQixRQUFRLEVBQUUsc0NBQXNDO1FBQ2hELElBQUksRUFBRSxRQUFRO1FBQ2QsT0FBTyxFQUFFLE1BQU07S0FDbEIsQ0FBQyxDQUFBO0FBQ04sQ0FBQyxDQUFBO0FBRUQsSUFBSSxPQUFPLEdBQUcsQ0FBQyxLQUFlLEVBQUUsRUFBRSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQTtBQUV4RCxNQUFNLENBQUMsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFhLEVBQUUsRUFBRTtJQUN0QyxPQUFPLEdBQUcsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLGVBQWUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQW1CLEVBQUUsRUFBRTtRQUNqRixRQUFRLEVBQUUsQ0FBQTtRQUNWLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQWEsQ0FBQTtRQUMxQyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBZSxDQUFBO1FBQ2xELE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUE7UUFDM0MsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDekIsQ0FBQyxDQUFDLENBQUE7QUFDTixDQUFDLENBQUEifQ==
|
||||
}
|
||||
cli.command(command, desc, builder, handler);
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzaXplLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL3Jlc2l6ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ3BDLE9BQU8sRUFDSCxNQUFNLEVBQ1QsTUFBTSwrQkFBK0IsQ0FBQTtBQUN0QyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQy9CLE9BQU8sRUFDSCxRQUFRLEVBQ1IsUUFBUSxFQUNYLE1BQU0sWUFBWSxDQUFBO0FBTW5CLE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLEtBQWUsRUFBRSxFQUFFO0lBQzlDLE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDdkIsUUFBUSxFQUFFLGtCQUFrQjtRQUM1QixZQUFZLEVBQUUsSUFBSTtLQUNyQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLFFBQVEsRUFBRSxrQkFBa0I7S0FDL0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDZixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSxnQ0FBZ0M7UUFDMUMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx5Q0FBeUM7UUFDbkQsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx3QkFBd0I7UUFDbEMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDakIsT0FBTyxFQUFFLEtBQUs7UUFDZCxRQUFRLEVBQUUsd0JBQXdCO1FBQ2xDLElBQUksRUFBRSxTQUFTO0tBQ2xCLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFO1FBQ2pCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLDJCQUEyQjtRQUNyQyxJQUFJLEVBQUUsUUFBUTtLQUNqQixDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRTtRQUNmLFFBQVEsRUFBRSxtQkFBbUI7UUFDN0IsSUFBSSxFQUFFLFFBQVE7S0FDakIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUU7UUFDaEIsUUFBUSxFQUFFLHFCQUFxQjtRQUMvQixJQUFJLEVBQUUsUUFBUTtLQUNqQixDQUFDLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRTtRQUNuQixRQUFRLEVBQUUsNkJBQTZCO1FBQ3ZDLElBQUksRUFBRSxRQUFRO0tBQ2pCLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFO1FBQ2xCLFFBQVEsRUFBRSw0QkFBNEI7UUFDdEMsSUFBSSxFQUFFLFFBQVE7S0FDakIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDakIsUUFBUSxFQUFFLDJCQUEyQjtRQUNyQyxJQUFJLEVBQUUsUUFBUTtLQUNqQixDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNqQixRQUFRLEVBQUUsaUNBQWlDO1FBQzNDLElBQUksRUFBRSxRQUFRO0tBQ2pCLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFO1FBQ2xCLFFBQVEsRUFBRSxzQ0FBc0M7UUFDaEQsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsTUFBTTtLQUNsQixDQUFDLENBQUE7QUFDTixDQUFDLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDO0FBQ2hDLE1BQU0sQ0FBQyxNQUFNLElBQUksR0FBRyxlQUFlLENBQUM7QUFDcEMsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLGNBQWMsQ0FBQztBQUV0QyxNQUFNLENBQUMsS0FBSyxVQUFVLE9BQU8sQ0FBQyxJQUFtQjtJQUM3QyxRQUFRLEVBQUUsQ0FBQTtJQUNWLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQWEsQ0FBQTtJQUMxQyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBZSxDQUFBO0lBQ2xELE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUE7SUFDM0MsTUFBTSxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUE7QUFDekIsQ0FBQztBQUVELEdBQUcsQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUEifQ==
|
||||
@ -1,3 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const register: (cli: CLI.Argv) => any;
|
||||
export declare const command = "watermark";
|
||||
export declare const desc = "Adds watermarks to images";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons';
|
||||
import { logger } from '../index.js';
|
||||
import { watermark } from '../lib/media/images/watermark.js';
|
||||
import { cli } from '../cli.js';
|
||||
import { sanitize, defaults } from '../_cli.js';
|
||||
import { watermark } from '../lib/media/images/watermark.js';
|
||||
export const defaultOptions = (yargs) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
@ -24,47 +24,98 @@ export const defaultOptions = (yargs) => {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('percent', {
|
||||
default: false,
|
||||
describe: 'Resize image with percent',
|
||||
type: 'number'
|
||||
}).option('width', {
|
||||
default: false,
|
||||
describe: 'Resize image with',
|
||||
type: 'number'
|
||||
}).option('height', {
|
||||
default: false,
|
||||
describe: 'Resize image height',
|
||||
type: 'number'
|
||||
}).option('minHeight', {
|
||||
describe: 'Resize image minimum height',
|
||||
type: 'number'
|
||||
}).option('minWidth', {
|
||||
describe: 'Resize image minimum width',
|
||||
type: 'number'
|
||||
}).option('minSize', {
|
||||
describe: 'Resize image size (bytes)',
|
||||
type: 'number'
|
||||
}).option('percent', {
|
||||
describe: 'Resize image in percent (width)',
|
||||
type: 'number'
|
||||
}).option('key', {
|
||||
describe: 'API Key',
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('watermarkType', {
|
||||
describe: 'Type of watermark: text or image',
|
||||
choices: ['text', 'image'],
|
||||
demandOption: true
|
||||
}).option('text', {
|
||||
describe: 'Text to use for text watermark',
|
||||
type: 'string'
|
||||
}).option('logoPath', {
|
||||
describe: 'Path to logo image for image watermark',
|
||||
type: 'string'
|
||||
}).option('position', {
|
||||
describe: 'Position of watermark',
|
||||
choices: ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'center'],
|
||||
default: 'bottom-right'
|
||||
}).option('margin', {
|
||||
describe: 'Margin from edges in pixels',
|
||||
type: 'number',
|
||||
default: 24
|
||||
}).option('opacity', {
|
||||
describe: 'Opacity of watermark (0-1)',
|
||||
type: 'number',
|
||||
default: 0.85
|
||||
}).option('sizePct', {
|
||||
describe: 'Size of image watermark as percentage of base image width (0-1)',
|
||||
type: 'number',
|
||||
default: 0.2
|
||||
}).option('fontSize', {
|
||||
describe: 'Font size for text watermark in pixels',
|
||||
type: 'number',
|
||||
default: 48
|
||||
}).option('color', {
|
||||
describe: 'Text color (hex format, e.g., #ffffff)',
|
||||
type: 'string',
|
||||
default: '#ffffff'
|
||||
}).option('fontFamily', {
|
||||
describe: 'Font family for text watermark',
|
||||
type: 'string',
|
||||
default: 'Arial'
|
||||
}).option('strokeColor', {
|
||||
describe: 'Text stroke color (hex format, e.g., #000000)',
|
||||
type: 'string',
|
||||
default: '#000000'
|
||||
}).option('strokeWidth', {
|
||||
describe: 'Text stroke width in pixels',
|
||||
type: 'number',
|
||||
default: 2
|
||||
});
|
||||
};
|
||||
const options = (yargs) => defaultOptions(yargs);
|
||||
export const register = (cli) => {
|
||||
return cli.command('watermark', 'Remove watermark : FILE|GLOB', options, async (argv) => {
|
||||
export const command = 'watermark';
|
||||
export const desc = 'Adds watermarks to images';
|
||||
export const builder = defaultOptions;
|
||||
export async function handler(argv) {
|
||||
defaults();
|
||||
const options = sanitize(argv);
|
||||
const config = CONFIG_DEFAULT();
|
||||
if (!config.novita) {
|
||||
logger.error("Novita key not found");
|
||||
return;
|
||||
logger.settings.minLevel = options.logLevel;
|
||||
// Validate required options based on watermark type
|
||||
if (options.watermarkType === 'text' && !options.text) {
|
||||
logger.error('Text is required when using text watermark type');
|
||||
process.exit(1);
|
||||
}
|
||||
options.debug && logger.info("Watermark Options " + argv.dst, options);
|
||||
return watermark({ ...options, key: config.novita.key });
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0ZXJtYXJrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL3dhdGVybWFyay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFFbEQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUNwQyxPQUFPLEVBQ0wsU0FBUyxFQUNWLE1BQU0sa0NBQWtDLENBQUE7QUFFekMsT0FBTyxFQUNMLFFBQVEsRUFDUixRQUFRLEVBQ1QsTUFBTSxZQUFZLENBQUE7QUFNbkIsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBZSxFQUFFLEVBQUU7SUFDaEQsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUN6QixRQUFRLEVBQUUsa0JBQWtCO1FBQzVCLFlBQVksRUFBRSxJQUFJO0tBQ25CLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO1FBQ2YsUUFBUSxFQUFFLGtCQUFrQjtLQUM3QixDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRTtRQUNqQixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSxnQ0FBZ0M7UUFDMUMsSUFBSSxFQUFFLFNBQVM7S0FDaEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDZixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx5Q0FBeUM7UUFDbkQsSUFBSSxFQUFFLFNBQVM7S0FDaEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDZixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx3QkFBd0I7UUFDbEMsSUFBSSxFQUFFLFNBQVM7S0FDaEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDbkIsT0FBTyxFQUFFLEtBQUs7UUFDZCxRQUFRLEVBQUUsd0JBQXdCO1FBQ2xDLElBQUksRUFBRSxTQUFTO0tBQ2hCLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFO1FBQ25CLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLDJCQUEyQjtRQUNyQyxJQUFJLEVBQUUsUUFBUTtLQUNmLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFO1FBQ2pCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLG1CQUFtQjtRQUM3QixJQUFJLEVBQUUsUUFBUTtLQUNmLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFO1FBQ2xCLE9BQU8sRUFBRSxLQUFLO1FBQ2QsUUFBUSxFQUFFLHFCQUFxQjtRQUMvQixJQUFJLEVBQUUsUUFBUTtLQUNmLENBQUMsQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFO1FBQ3JCLFFBQVEsRUFBRSw2QkFBNkI7UUFDdkMsSUFBSSxFQUFFLFFBQVE7S0FDZixDQUFDLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRTtRQUNwQixRQUFRLEVBQUUsNEJBQTRCO1FBQ3RDLElBQUksRUFBRSxRQUFRO0tBQ2YsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDbkIsUUFBUSxFQUFFLDJCQUEyQjtRQUNyQyxJQUFJLEVBQUUsUUFBUTtLQUNmLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFO1FBQ25CLFFBQVEsRUFBRSxpQ0FBaUM7UUFDM0MsSUFBSSxFQUFFLFFBQVE7S0FDZixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNmLFFBQVEsRUFBRSxTQUFTO1FBQ25CLElBQUksRUFBRSxRQUFRO0tBQ2YsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFBO0FBRUQsTUFBTSxPQUFPLEdBQUcsQ0FBQyxLQUFlLEVBQUUsRUFBRSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQTtBQUUxRCxNQUFNLENBQUMsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFhLEVBQUUsRUFBRTtJQUN4QyxPQUFPLEdBQUcsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLDhCQUE4QixFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBbUIsRUFBRSxFQUFFO1FBQ3JHLFFBQVEsRUFBRSxDQUFBO1FBQ1YsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBYSxDQUFBO1FBQzFDLE1BQU0sTUFBTSxHQUFRLGNBQWMsRUFBRSxDQUFBO1FBQ3BDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDbkIsTUFBTSxDQUFDLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1lBQ3JDLE9BQU07UUFDUixDQUFDO1FBQ0QsT0FBTyxDQUFDLEtBQUssSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLG9CQUFvQixHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUE7UUFDdEUsT0FBTyxTQUFTLENBQUMsRUFBRSxHQUFHLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFBO0lBQzFELENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFBIn0=
|
||||
if (options.watermarkType === 'image' && !options.logoPath) {
|
||||
logger.error('Logo path is required when using image watermark type');
|
||||
process.exit(1);
|
||||
}
|
||||
// Set up watermark options based on type
|
||||
if (options.watermarkType === 'text') {
|
||||
options.textOptions = {
|
||||
position: argv.position,
|
||||
margin: argv.margin,
|
||||
fontSize: argv.fontSize,
|
||||
opacity: argv.opacity,
|
||||
color: argv.color,
|
||||
fontFamily: argv.fontFamily,
|
||||
strokeColor: argv.strokeColor,
|
||||
strokeWidth: argv.strokeWidth,
|
||||
};
|
||||
}
|
||||
else if (options.watermarkType === 'image') {
|
||||
options.imageOptions = {
|
||||
position: argv.position,
|
||||
margin: argv.margin,
|
||||
sizePct: argv.sizePct,
|
||||
opacity: argv.opacity,
|
||||
blend: 'over'
|
||||
};
|
||||
}
|
||||
logger.info("Adding watermark with options:", options);
|
||||
await watermark(options);
|
||||
}
|
||||
cli.command(command, desc, builder, handler);
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0ZXJtYXJrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL3dhdGVybWFyay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDL0IsT0FBTyxFQUNILFFBQVEsRUFDUixRQUFRLEVBQ1gsTUFBTSxZQUFZLENBQUE7QUFNbkIsT0FBTyxFQUNILFNBQVMsRUFFWixNQUFNLGtDQUFrQyxDQUFBO0FBRXpDLE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLEtBQWUsRUFBRSxFQUFFO0lBQzlDLE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDdkIsUUFBUSxFQUFFLGtCQUFrQjtRQUM1QixZQUFZLEVBQUUsSUFBSTtLQUNyQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLFFBQVEsRUFBRSxrQkFBa0I7S0FDL0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDZixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSxnQ0FBZ0M7UUFDMUMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx5Q0FBeUM7UUFDbkQsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx3QkFBd0I7UUFDbEMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDakIsT0FBTyxFQUFFLEtBQUs7UUFDZCxRQUFRLEVBQUUsd0JBQXdCO1FBQ2xDLElBQUksRUFBRSxTQUFTO0tBQ2xCLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFO1FBQ2xCLFFBQVEsRUFBRSxzQ0FBc0M7UUFDaEQsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsTUFBTTtLQUNsQixDQUFDLENBQUMsTUFBTSxDQUFDLGVBQWUsRUFBRTtRQUN2QixRQUFRLEVBQUUsa0NBQWtDO1FBQzVDLE9BQU8sRUFBRSxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUM7UUFDMUIsWUFBWSxFQUFFLElBQUk7S0FDckIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUU7UUFDZCxRQUFRLEVBQUUsZ0NBQWdDO1FBQzFDLElBQUksRUFBRSxRQUFRO0tBQ2pCLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFO1FBQ2xCLFFBQVEsRUFBRSx3Q0FBd0M7UUFDbEQsSUFBSSxFQUFFLFFBQVE7S0FDakIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUU7UUFDbEIsUUFBUSxFQUFFLHVCQUF1QjtRQUNqQyxPQUFPLEVBQUUsQ0FBQyxVQUFVLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxjQUFjLEVBQUUsUUFBUSxDQUFDO1FBQzNFLE9BQU8sRUFBRSxjQUFjO0tBQzFCLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFO1FBQ2hCLFFBQVEsRUFBRSw2QkFBNkI7UUFDdkMsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsRUFBRTtLQUNkLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFO1FBQ2pCLFFBQVEsRUFBRSw0QkFBNEI7UUFDdEMsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsSUFBSTtLQUNoQixDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRTtRQUNqQixRQUFRLEVBQUUsaUVBQWlFO1FBQzNFLElBQUksRUFBRSxRQUFRO1FBQ2QsT0FBTyxFQUFFLEdBQUc7S0FDZixDQUFDLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRTtRQUNsQixRQUFRLEVBQUUsd0NBQXdDO1FBQ2xELElBQUksRUFBRSxRQUFRO1FBQ2QsT0FBTyxFQUFFLEVBQUU7S0FDZCxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRTtRQUNmLFFBQVEsRUFBRSx3Q0FBd0M7UUFDbEQsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsU0FBUztLQUNyQixDQUFDLENBQUMsTUFBTSxDQUFDLFlBQVksRUFBRTtRQUNwQixRQUFRLEVBQUUsZ0NBQWdDO1FBQzFDLElBQUksRUFBRSxRQUFRO1FBQ2QsT0FBTyxFQUFFLE9BQU87S0FDbkIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUU7UUFDckIsUUFBUSxFQUFFLCtDQUErQztRQUN6RCxJQUFJLEVBQUUsUUFBUTtRQUNkLE9BQU8sRUFBRSxTQUFTO0tBQ3JCLENBQUMsQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFO1FBQ3JCLFFBQVEsRUFBRSw2QkFBNkI7UUFDdkMsSUFBSSxFQUFFLFFBQVE7UUFDZCxPQUFPLEVBQUUsQ0FBQztLQUNiLENBQUMsQ0FBQTtBQUNOLENBQUMsQ0FBQTtBQUlELE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRyxXQUFXLENBQUM7QUFDbkMsTUFBTSxDQUFDLE1BQU0sSUFBSSxHQUFHLDJCQUEyQixDQUFDO0FBQ2hELE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRyxjQUFjLENBQUM7QUFFdEMsTUFBTSxDQUFDLEtBQUssVUFBVSxPQUFPLENBQUMsSUFBbUI7SUFDN0MsUUFBUSxFQUFFLENBQUE7SUFDVixNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFxQixDQUFBO0lBQ2xELE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFlLENBQUE7SUFFbEQsb0RBQW9EO0lBQ3BELElBQUksT0FBTyxDQUFDLGFBQWEsS0FBSyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDcEQsTUFBTSxDQUFDLEtBQUssQ0FBQyxpREFBaUQsQ0FBQyxDQUFBO1FBQy9ELE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDbkIsQ0FBQztJQUVELElBQUksT0FBTyxDQUFDLGFBQWEsS0FBSyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDekQsTUFBTSxDQUFDLEtBQUssQ0FBQyx1REFBdUQsQ0FBQyxDQUFBO1FBQ3JFLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDbkIsQ0FBQztJQUVELHlDQUF5QztJQUN6QyxJQUFJLE9BQU8sQ0FBQyxhQUFhLEtBQUssTUFBTSxFQUFFLENBQUM7UUFDbkMsT0FBTyxDQUFDLFdBQVcsR0FBRztZQUNsQixRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQWU7WUFDOUIsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFnQjtZQUM3QixRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQWtCO1lBQ2pDLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBaUI7WUFDL0IsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFlO1lBQzNCLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBb0I7WUFDckMsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFxQjtZQUN2QyxXQUFXLEVBQUUsSUFBSSxDQUFDLFdBQXFCO1NBQzFDLENBQUE7SUFDTCxDQUFDO1NBQU0sSUFBSSxPQUFPLENBQUMsYUFBYSxLQUFLLE9BQU8sRUFBRSxDQUFDO1FBQzNDLE9BQU8sQ0FBQyxZQUFZLEdBQUc7WUFDbkIsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFlO1lBQzlCLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBZ0I7WUFDN0IsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFpQjtZQUMvQixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQWlCO1lBQy9CLEtBQUssRUFBRSxNQUFNO1NBQ2hCLENBQUE7SUFDTCxDQUFDO0lBRUQsTUFBTSxDQUFDLElBQUksQ0FBQyxnQ0FBZ0MsRUFBRSxPQUFPLENBQUMsQ0FBQTtJQUN0RCxNQUFNLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQTtBQUM1QixDQUFDO0FBRUQsR0FBRyxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQSJ9
|
||||
11
packages/media/dist-in/lib/media/images/background-remove.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
import { IOptions } from '../../../types.js';
|
||||
export interface BackgroundRemoveOptions extends IOptions {
|
||||
apiKey?: string;
|
||||
model?: string;
|
||||
alpha_matting?: boolean;
|
||||
alpha_matting_foreground_threshold?: number;
|
||||
alpha_matting_background_threshold?: number;
|
||||
alpha_matting_erode_size?: number;
|
||||
}
|
||||
export declare function removeBackgroundFile(inputPath: string, outputPath: string, options: BackgroundRemoveOptions): Promise<void>;
|
||||
export declare const backgroundRemove: (options: BackgroundRemoveOptions) => Promise<void[][]>;
|
||||
117
packages/media/dist-in/lib/media/images/background-remove.js
Normal file
@ -1,2 +1,31 @@
|
||||
import { OverlayOptions } from "sharp";
|
||||
import { IOptions } from '../../../types.js';
|
||||
export declare const watermark: (options: IOptions) => Promise<any>;
|
||||
type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
|
||||
export interface LogoWatermarkOptions {
|
||||
position?: Corner;
|
||||
margin?: number;
|
||||
sizePct?: number;
|
||||
opacity?: number;
|
||||
blend?: OverlayOptions["blend"];
|
||||
}
|
||||
export interface TextWatermarkOptions {
|
||||
position?: Corner;
|
||||
margin?: number;
|
||||
fontSize?: number;
|
||||
opacity?: number;
|
||||
color?: string;
|
||||
fontFamily?: string;
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
}
|
||||
export interface WatermarkOptions extends IOptions {
|
||||
watermarkType: 'text' | 'image';
|
||||
text?: string;
|
||||
textOptions?: TextWatermarkOptions;
|
||||
logoPath?: string;
|
||||
imageOptions?: LogoWatermarkOptions;
|
||||
}
|
||||
export declare function addLogoWatermark(inputPath: string, logoPath: string, outputPath: string, opts?: LogoWatermarkOptions): Promise<void>;
|
||||
export declare function addTextWatermark(inputPath: string, text: string, outputPath: string, opts?: TextWatermarkOptions): Promise<void>;
|
||||
export declare const watermark: (options: WatermarkOptions) => Promise<void[][]>;
|
||||
export {};
|
||||
|
||||
2
packages/media/dist-in/main.d.ts
vendored
@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import './commands/resize.js';
|
||||
import './commands/pdf2jpg.js';
|
||||
import './commands/watermark.js';
|
||||
import './commands/background-remove.js';
|
||||
|
||||
@ -4,6 +4,8 @@ defaults();
|
||||
import { cli } from './cli.js';
|
||||
import './commands/resize.js';
|
||||
import './commands/pdf2jpg.js';
|
||||
import './commands/watermark.js';
|
||||
import './commands/background-remove.js';
|
||||
const argv = cli.argv;
|
||||
if (argv.h || argv.help) {
|
||||
cli.showHelp();
|
||||
@ -12,4 +14,4 @@ if (argv.h || argv.help) {
|
||||
else if (argv.v || argv.version) {
|
||||
process.exit();
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9tYWluLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQUMsUUFBUSxFQUFFLENBQUE7QUFFaEQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUM5QixPQUFPLHNCQUFzQixDQUFBO0FBQzdCLE9BQU8sdUJBQXVCLENBQUE7QUFFOUIsTUFBTSxJQUFJLEdBQVEsR0FBRyxDQUFDLElBQUksQ0FBQztBQUUzQixJQUFJLElBQUksQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3RCLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUNmLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUNuQixDQUFDO0tBQU0sSUFBSSxJQUFJLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNoQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDbkIsQ0FBQyJ9
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9tYWluLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQUMsUUFBUSxFQUFFLENBQUE7QUFFaEQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUU5QixPQUFPLHNCQUFzQixDQUFBO0FBQzdCLE9BQU8sdUJBQXVCLENBQUE7QUFDOUIsT0FBTyx5QkFBeUIsQ0FBQTtBQUNoQyxPQUFPLGlDQUFpQyxDQUFBO0FBRXhDLE1BQU0sSUFBSSxHQUFRLEdBQUcsQ0FBQyxJQUFJLENBQUM7QUFFM0IsSUFBSSxJQUFJLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN0QixHQUFHLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDZixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDbkIsQ0FBQztLQUFNLElBQUksSUFBSSxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDaEMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ25CLENBQUMifQ==
|
||||
2
packages/media/dist-in/types.d.ts
vendored
@ -50,3 +50,5 @@ export type IConvertVideoOptions = IOptions & {
|
||||
verb: string;
|
||||
audio: string;
|
||||
};
|
||||
export type { WatermarkOptions, LogoWatermarkOptions, TextWatermarkOptions } from './lib/media/images/watermark.js';
|
||||
export type { BackgroundRemoveOptions } from './lib/media/images/background-remove.js';
|
||||
|
||||
638
packages/media/docs/cli-background-remove.md
Normal file
@ -0,0 +1,638 @@
|
||||
# Background Remove CLI Documentation
|
||||
|
||||
The `pm-media background:remove` command uses AI-powered background removal to automatically isolate subjects from their backgrounds. This tool leverages the [Replicate API](https://replicate.com/cjwbw/rembg) for high-quality results and supports batch processing with multiple AI models.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Setup](#setup)
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [AI Models](#ai-models)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [API Usage](#api-usage)
|
||||
- [Examples](#examples)
|
||||
- [Alpha Matting](#alpha-matting)
|
||||
- [Performance Tips](#performance-tips)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @polymech/media
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Get Replicate API Key
|
||||
|
||||
1. Visit [Replicate API Tokens](https://replicate.com/account/api-tokens)
|
||||
2. Sign up or log in to your account
|
||||
3. Create a new API token
|
||||
4. Copy your API token
|
||||
|
||||
### 2. Set API Key
|
||||
|
||||
You can provide your API key in two ways:
|
||||
|
||||
**Option A: Environment Variable (Recommended)**
|
||||
```bash
|
||||
export REPLICATE_API_TOKEN="your_api_token_here"
|
||||
```
|
||||
|
||||
**Option B: Command Line Argument**
|
||||
```bash
|
||||
pm-media background:remove --src input.jpg --apiKey "your_api_token_here"
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
pm-media background:remove --src <input> --dst <output> [options]
|
||||
```
|
||||
|
||||
### Required Parameters
|
||||
|
||||
- `--src`: Source files (FILE|FOLDER|GLOB pattern)
|
||||
|
||||
### Optional Parameters
|
||||
|
||||
- `--dst`: Destination path (defaults to source location with modified name)
|
||||
- `--apiKey`: Replicate API key (if not set via environment variable)
|
||||
|
||||
## AI Models
|
||||
|
||||
The tool supports multiple specialized AI models for different use cases:
|
||||
|
||||
### Available Models
|
||||
|
||||
| Model | Best For | Description |
|
||||
|-------|----------|-------------|
|
||||
| `u2net` (default) | General use | Universal background removal |
|
||||
| `u2netp` | Performance | Lighter version of U2Net |
|
||||
| `u2net_human_seg` | People | Optimized for human subjects |
|
||||
| `u2net_cloth_seg` | Clothing | Specialized for clothing items |
|
||||
| `silueta` | Portraits | High-quality person silhouettes |
|
||||
|
||||
### Model Selection
|
||||
|
||||
```bash
|
||||
# General purpose (default)
|
||||
pm-media background:remove --src photo.jpg --model u2net
|
||||
|
||||
# Optimized for people
|
||||
pm-media background:remove --src portrait.jpg --model u2net_human_seg
|
||||
|
||||
# Clothing/fashion
|
||||
pm-media background:remove --src product.jpg --model u2net_cloth_seg
|
||||
```
|
||||
|
||||
## Command Line Options
|
||||
|
||||
### Core Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--src` | string | required | Source files (FILE\|FOLDER\|GLOB) |
|
||||
| `--dst` | string | - | Destination path |
|
||||
| `--apiKey` | string | - | Replicate API key |
|
||||
| `--model` | choice | "u2net" | AI model to use |
|
||||
|
||||
### Alpha Matting Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--alphaMattting` | boolean | false | Enable alpha matting for better edges |
|
||||
| `--alphaMattingForegroundThreshold` | number | 270 | Foreground detection threshold |
|
||||
| `--alphaMattingBackgroundThreshold` | number | 10 | Background detection threshold |
|
||||
| `--alphaMattingErodeSize` | number | 10 | Edge erosion size |
|
||||
|
||||
### Utility Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--dry` | boolean | false | Preview mode (no API calls) |
|
||||
| `--verbose` | boolean | false | Show detailed processing info |
|
||||
| `--debug` | boolean | false | Enable debug messages |
|
||||
| `--logLevel` | string | "info" | Log level (warn\|info\|debug\|error) |
|
||||
|
||||
## API Usage
|
||||
|
||||
### Import the Library
|
||||
|
||||
```typescript
|
||||
import {
|
||||
backgroundRemove,
|
||||
removeBackgroundFile,
|
||||
BackgroundRemoveOptions
|
||||
} from '@polymech/media';
|
||||
```
|
||||
|
||||
### Single File Background Removal
|
||||
|
||||
```typescript
|
||||
import { removeBackgroundFile } from '@polymech/media';
|
||||
|
||||
await removeBackgroundFile(
|
||||
'input.jpg',
|
||||
'output.png',
|
||||
{
|
||||
apiKey: 'your_api_token',
|
||||
model: 'u2net',
|
||||
alpha_matting: false
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Batch Processing API
|
||||
|
||||
```typescript
|
||||
import { backgroundRemove } from '@polymech/media';
|
||||
|
||||
const options: BackgroundRemoveOptions = {
|
||||
src: 'photos/*.jpg',
|
||||
dst: 'no-bg/',
|
||||
apiKey: process.env.REPLICATE_API_TOKEN,
|
||||
model: 'u2net_human_seg',
|
||||
alpha_matting: true,
|
||||
// ... other IOptions
|
||||
};
|
||||
|
||||
await backgroundRemove(options);
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
```typescript
|
||||
const advancedOptions: BackgroundRemoveOptions = {
|
||||
src: 'portraits/*.jpg',
|
||||
dst: 'processed/',
|
||||
apiKey: 'your_token',
|
||||
model: 'u2net_human_seg',
|
||||
alpha_matting: true,
|
||||
alpha_matting_foreground_threshold: 280,
|
||||
alpha_matting_background_threshold: 15,
|
||||
alpha_matting_erode_size: 8,
|
||||
verbose: true
|
||||
};
|
||||
|
||||
await backgroundRemove(advancedOptions);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Basic Background Removal
|
||||
|
||||
```bash
|
||||
# Remove background from a single image
|
||||
pm-media background:remove \
|
||||
--src "photo.jpg" \
|
||||
--dst "no-background.png"
|
||||
```
|
||||
|
||||
### Example 2: Batch Processing
|
||||
|
||||
```bash
|
||||
# Process multiple images
|
||||
pm-media background:remove \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "processed/" \
|
||||
--model u2net
|
||||
```
|
||||
|
||||
### Example 3: Portrait Optimization
|
||||
|
||||
```bash
|
||||
# Optimized for people/portraits
|
||||
pm-media background:remove \
|
||||
--src "portraits/*.jpg" \
|
||||
--dst "headshots/" \
|
||||
--model u2net_human_seg \
|
||||
--alphaMattting
|
||||
```
|
||||
|
||||
### Example 4: Product Photography
|
||||
|
||||
```bash
|
||||
# E-commerce product images
|
||||
pm-media background:remove \
|
||||
--src "products/*.jpg" \
|
||||
--dst "catalog/" \
|
||||
--model u2net_cloth_seg \
|
||||
--alphaMattting \
|
||||
--alphaMattingForegroundThreshold 280
|
||||
```
|
||||
|
||||
### Example 5: High-Quality Processing
|
||||
|
||||
```bash
|
||||
# Maximum quality with alpha matting
|
||||
pm-media background:remove \
|
||||
--src "studio-photos/*.jpg" \
|
||||
--dst "final/" \
|
||||
--model silueta \
|
||||
--alphaMattting \
|
||||
--alphaMattingForegroundThreshold 300 \
|
||||
--alphaMattingBackgroundThreshold 5 \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Example 6: Fast Batch Processing
|
||||
|
||||
```bash
|
||||
# Quick processing for previews
|
||||
pm-media background:remove \
|
||||
--src "batch/*.jpg" \
|
||||
--dst "previews/" \
|
||||
--model u2netp
|
||||
```
|
||||
|
||||
### Example 7: Dry Run Preview
|
||||
|
||||
```bash
|
||||
# Preview what would be processed
|
||||
pm-media background:remove \
|
||||
--src "archive/**/*.jpg" \
|
||||
--dst "processed/" \
|
||||
--dry \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Example 8: Organized Output
|
||||
|
||||
```bash
|
||||
# Maintain folder structure
|
||||
pm-media background:remove \
|
||||
--src "organized/session-1/*.jpg" \
|
||||
--dst "no-bg/session-1/" \
|
||||
--model u2net_human_seg
|
||||
```
|
||||
|
||||
## Alpha Matting
|
||||
|
||||
Alpha matting provides superior edge quality for complex subjects with fine details like hair, fur, or transparent materials.
|
||||
|
||||
### When to Use Alpha Matting
|
||||
|
||||
✅ **Use alpha matting for:**
|
||||
- Portraits with detailed hair
|
||||
- Furry animals
|
||||
- Objects with fine edges
|
||||
- Semi-transparent materials
|
||||
- Professional/commercial work
|
||||
|
||||
❌ **Skip alpha matting for:**
|
||||
- Simple shapes
|
||||
- Hard edges
|
||||
- Quick previews
|
||||
- Batch processing (slower)
|
||||
|
||||
### Alpha Matting Parameters
|
||||
|
||||
```bash
|
||||
# Standard alpha matting
|
||||
pm-media background:remove \
|
||||
--src input.jpg \
|
||||
--dst output.png \
|
||||
--alphaMattting
|
||||
|
||||
# Fine-tuned alpha matting
|
||||
pm-media background:remove \
|
||||
--src portrait.jpg \
|
||||
--dst result.png \
|
||||
--alphaMattting \
|
||||
--alphaMattingForegroundThreshold 280 \
|
||||
--alphaMattingBackgroundThreshold 12 \
|
||||
--alphaMattingErodeSize 6
|
||||
```
|
||||
|
||||
### Parameter Guidelines
|
||||
|
||||
| Parameter | Low Value | High Value | Effect |
|
||||
|-----------|-----------|------------|--------|
|
||||
| Foreground Threshold | 200 | 350 | More/less foreground detection |
|
||||
| Background Threshold | 5 | 20 | More/less background detection |
|
||||
| Erode Size | 3 | 15 | Smaller/larger edge refinement |
|
||||
|
||||
## File Format Support
|
||||
|
||||
### Supported Input Formats
|
||||
- JPEG (.jpg, .jpeg)
|
||||
- PNG (.png)
|
||||
- WebP (.webp)
|
||||
- TIFF (.tiff, .tif)
|
||||
|
||||
### Output Format
|
||||
- **PNG with transparency** (recommended)
|
||||
- Automatically adds transparency channel
|
||||
- Preserves original quality
|
||||
|
||||
### Format Recommendations
|
||||
|
||||
```bash
|
||||
# For web use
|
||||
pm-media background:remove --src input.jpg --dst output.png
|
||||
|
||||
# For print/professional
|
||||
pm-media background:remove --src input.tiff --dst output.png --alphaMattting
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### 1. Choose Appropriate Models
|
||||
|
||||
```bash
|
||||
# Fast processing
|
||||
--model u2netp
|
||||
|
||||
# Balanced quality/speed
|
||||
--model u2net
|
||||
|
||||
# High quality (slower)
|
||||
--model silueta
|
||||
```
|
||||
|
||||
### 2. Use Alpha Matting Selectively
|
||||
|
||||
```bash
|
||||
# For most images (faster)
|
||||
pm-media background:remove --src batch/*.jpg --dst output/
|
||||
|
||||
# For detailed work only (slower)
|
||||
pm-media background:remove --src detailed/*.jpg --dst output/ --alphaMattting
|
||||
```
|
||||
|
||||
### 3. Batch Processing Strategies
|
||||
|
||||
```bash
|
||||
# Process in chunks for large batches
|
||||
pm-media background:remove --src "batch1/*.jpg" --dst "output1/"
|
||||
pm-media background:remove --src "batch2/*.jpg" --dst "output2/"
|
||||
```
|
||||
|
||||
### 4. API Rate Limiting
|
||||
|
||||
The Replicate API has rate limits. For large batches:
|
||||
- Process during off-peak hours
|
||||
- Consider parallel processing limits
|
||||
- Monitor API usage in your Replicate dashboard
|
||||
|
||||
## Cost Considerations
|
||||
|
||||
### Replicate Pricing
|
||||
|
||||
Background removal costs per prediction. Check current pricing at [Replicate Pricing](https://replicate.com/pricing).
|
||||
|
||||
### Cost Optimization
|
||||
|
||||
```bash
|
||||
# Test with dry run first
|
||||
pm-media background:remove --src "*.jpg" --dst "output/" --dry
|
||||
|
||||
# Use efficient models
|
||||
--model u2netp # Fastest/cheapest
|
||||
--model u2net # Balanced
|
||||
--model silueta # Highest quality/most expensive
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "API key is required"
|
||||
```bash
|
||||
# Set environment variable
|
||||
export REPLICATE_API_TOKEN="your_token"
|
||||
|
||||
# Or use command line
|
||||
pm-media background:remove --src input.jpg --apiKey "your_token"
|
||||
```
|
||||
|
||||
#### "Replicate package not found"
|
||||
```bash
|
||||
# Install missing dependency
|
||||
npm install replicate
|
||||
```
|
||||
|
||||
#### Poor edge quality
|
||||
```bash
|
||||
# Enable alpha matting
|
||||
pm-media background:remove \
|
||||
--src input.jpg \
|
||||
--dst output.png \
|
||||
--alphaMattting \
|
||||
--alphaMattingForegroundThreshold 280
|
||||
```
|
||||
|
||||
#### API rate limit errors
|
||||
- Reduce batch size
|
||||
- Add delays between requests
|
||||
- Check your Replicate dashboard for limits
|
||||
|
||||
#### Large file processing fails
|
||||
- Resize images before processing
|
||||
- Use appropriate model for image type
|
||||
- Check API payload limits
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable detailed logging:
|
||||
|
||||
```bash
|
||||
pm-media background:remove \
|
||||
--src "input/*" \
|
||||
--dst "output/" \
|
||||
--debug \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Quality Issues
|
||||
|
||||
```bash
|
||||
# For people/portraits
|
||||
--model u2net_human_seg
|
||||
|
||||
# For products/objects
|
||||
--model u2net_cloth_seg
|
||||
|
||||
# For complex edges
|
||||
--alphaMattting --alphaMattingForegroundThreshold 300
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### E-commerce Workflow
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Product photography pipeline
|
||||
|
||||
INPUT_DIR="$1"
|
||||
OUTPUT_DIR="$2"
|
||||
|
||||
# Remove backgrounds
|
||||
pm-media background:remove \
|
||||
--src "$INPUT_DIR/*.jpg" \
|
||||
--dst "$OUTPUT_DIR/no-bg/" \
|
||||
--model u2net_cloth_seg \
|
||||
--alphaMattting
|
||||
|
||||
# Create thumbnails
|
||||
pm-media resize \
|
||||
--src "$OUTPUT_DIR/no-bg/*.png" \
|
||||
--dst "$OUTPUT_DIR/thumbs/" \
|
||||
--width 300 \
|
||||
--height 300 \
|
||||
--fit contain \
|
||||
--background white
|
||||
```
|
||||
|
||||
### Portrait Studio Workflow
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Professional portrait processing
|
||||
|
||||
pm-media background:remove \
|
||||
--src "portraits/*.jpg" \
|
||||
--dst "processed/" \
|
||||
--model u2net_human_seg \
|
||||
--alphaMattting \
|
||||
--alphaMattingForegroundThreshold 290 \
|
||||
--verbose
|
||||
|
||||
echo "Background removal complete"
|
||||
```
|
||||
|
||||
### Batch Organization Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Organize processed images
|
||||
|
||||
for dir in */; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "Processing directory: $dir"
|
||||
pm-media background:remove \
|
||||
--src "$dir*.jpg" \
|
||||
--dst "no-bg/$dir" \
|
||||
--model u2net \
|
||||
--verbose
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Environment Configuration
|
||||
|
||||
```bash
|
||||
# .env file
|
||||
REPLICATE_API_TOKEN=your_token_here
|
||||
DEFAULT_BG_MODEL=u2net_human_seg
|
||||
ALPHA_MATTING=true
|
||||
|
||||
# Use in scripts
|
||||
pm-media background:remove \
|
||||
--src "*.jpg" \
|
||||
--dst "output/" \
|
||||
--model "$DEFAULT_BG_MODEL"
|
||||
```
|
||||
|
||||
### Quality Control
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Process with multiple quality levels
|
||||
|
||||
INPUT="$1"
|
||||
BASE_NAME=$(basename "$INPUT" .jpg)
|
||||
|
||||
# Quick preview
|
||||
pm-media background:remove \
|
||||
--src "$INPUT" \
|
||||
--dst "preview/${BASE_NAME}_preview.png" \
|
||||
--model u2netp
|
||||
|
||||
# High quality
|
||||
pm-media background:remove \
|
||||
--src "$INPUT" \
|
||||
--dst "final/${BASE_NAME}_final.png" \
|
||||
--model silueta \
|
||||
--alphaMattting
|
||||
```
|
||||
|
||||
### Conditional Processing
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Different models based on file type
|
||||
|
||||
for image in *.jpg; do
|
||||
if [[ $image == *"portrait"* ]]; then
|
||||
MODEL="u2net_human_seg"
|
||||
elif [[ $image == *"product"* ]]; then
|
||||
MODEL="u2net_cloth_seg"
|
||||
else
|
||||
MODEL="u2net"
|
||||
fi
|
||||
|
||||
pm-media background:remove \
|
||||
--src "$image" \
|
||||
--dst "processed/" \
|
||||
--model "$MODEL"
|
||||
done
|
||||
```
|
||||
|
||||
## TypeScript Definitions
|
||||
|
||||
```typescript
|
||||
interface BackgroundRemoveOptions extends IOptions {
|
||||
apiKey?: string;
|
||||
model?: 'u2net' | 'u2netp' | 'u2net_human_seg' | 'u2net_cloth_seg' | 'silueta';
|
||||
alpha_matting?: boolean;
|
||||
alpha_matting_foreground_threshold?: number;
|
||||
alpha_matting_background_threshold?: number;
|
||||
alpha_matting_erode_size?: number;
|
||||
}
|
||||
|
||||
declare function removeBackgroundFile(
|
||||
inputPath: string,
|
||||
outputPath: string,
|
||||
options: BackgroundRemoveOptions
|
||||
): Promise<void>;
|
||||
|
||||
declare function backgroundRemove(
|
||||
options: BackgroundRemoveOptions
|
||||
): Promise<any>;
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
### API Key Security
|
||||
|
||||
- Never commit API keys to version control
|
||||
- Use environment variables in production
|
||||
- Rotate keys regularly
|
||||
- Monitor usage in Replicate dashboard
|
||||
|
||||
### File Handling
|
||||
|
||||
- Validate input file types
|
||||
- Check file sizes before processing
|
||||
- Sanitize file paths
|
||||
- Handle temporary files securely
|
||||
|
||||
## Contributing
|
||||
|
||||
For bug reports, feature requests, or contributions, please visit the project repository.
|
||||
|
||||
## References
|
||||
|
||||
- [Replicate API Documentation](https://replicate.com/docs)
|
||||
- [rembg Model on Replicate](https://replicate.com/cjwbw/rembg)
|
||||
- [Replicate Pricing](https://replicate.com/pricing)
|
||||
- [API Rate Limits](https://replicate.com/docs/reference/http#rate-limits)
|
||||
|
||||
## License
|
||||
|
||||
This tool is part of the @polymech/media package. Please refer to the package license for usage terms.
|
||||
532
packages/media/docs/cli-pdf2jpg.md
Normal file
@ -0,0 +1,532 @@
|
||||
# PDF2JPG CLI Documentation
|
||||
|
||||
The `pm-media pdf2jpg` command converts PDF documents to high-quality images (JPG or PNG). This tool supports batch processing, custom DPI settings, page range selection, and advanced rendering options using MuPDF.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Output Formats](#output-formats)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [API Usage](#api-usage)
|
||||
- [Examples](#examples)
|
||||
- [Template Variables](#template-variables)
|
||||
- [Performance Optimization](#performance-optimization)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @polymech/media
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
pm-media pdf2jpg --input <pdf-file> [options]
|
||||
```
|
||||
|
||||
### Required Parameters
|
||||
|
||||
- `--input` (or `-i`): Path to the input PDF file
|
||||
|
||||
### Optional Parameters
|
||||
|
||||
- `--output` (or `-o`): Output path template
|
||||
- `--dpi`: Resolution for output images (default: 300)
|
||||
- `--format`: Output format - 'jpg' or 'png' (default: 'jpg')
|
||||
- `--scale`: Scaling factor (default: 2)
|
||||
- `--startPage`: First page to convert (1-based)
|
||||
- `--endPage`: Last page to convert (1-based)
|
||||
|
||||
## Output Formats
|
||||
|
||||
### JPEG Format
|
||||
- **Extension**: `.jpg`
|
||||
- **Use case**: Photographs, complex images
|
||||
- **Pros**: Smaller file sizes, good compression
|
||||
- **Cons**: Lossy compression, no transparency
|
||||
|
||||
### PNG Format
|
||||
- **Extension**: `.png`
|
||||
- **Use case**: Text, diagrams, images with transparency
|
||||
- **Pros**: Lossless compression, transparency support
|
||||
- **Cons**: Larger file sizes
|
||||
|
||||
## Command Line Options
|
||||
|
||||
### Core Options
|
||||
|
||||
| Option | Alias | Type | Default | Description |
|
||||
|--------|-------|------|---------|-------------|
|
||||
| `--input` | `-i` | string | required | Path to the input PDF file |
|
||||
| `--output` | `-o` | string | auto | Output path template |
|
||||
| `--dpi` | - | number | 300 | Resolution for output images |
|
||||
| `--format` | - | choice | 'jpg' | Output format: 'jpg' or 'png' |
|
||||
| `--scale` | - | number | 2 | Scaling factor for rendering |
|
||||
|
||||
### Page Range Options
|
||||
|
||||
| Option | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `--startPage` | number | First page to convert (1-based index) |
|
||||
| `--endPage` | number | Last page to convert (1-based index) |
|
||||
|
||||
### Quality & Performance
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--dpi` | number | 300 | Higher = better quality, larger files |
|
||||
| `--scale` | number | 2 | Rendering scale factor |
|
||||
|
||||
## API Usage
|
||||
|
||||
### Import the Library
|
||||
|
||||
```typescript
|
||||
import {
|
||||
runConversion,
|
||||
convertPdfToImages,
|
||||
ConvertCommandConfig,
|
||||
ImageFormat
|
||||
} from '@polymech/media';
|
||||
```
|
||||
|
||||
### Single PDF Conversion
|
||||
|
||||
```typescript
|
||||
import { runConversion } from '@polymech/media';
|
||||
import { logger } from 'tslog';
|
||||
|
||||
const config: ConvertCommandConfig = {
|
||||
input: 'document.pdf',
|
||||
output: 'output/page_{PAGE}.jpg',
|
||||
dpi: 300,
|
||||
format: 'jpg',
|
||||
scale: 2
|
||||
};
|
||||
|
||||
const outputFiles = await runConversion(config, logger);
|
||||
console.log(`Generated ${outputFiles.length} images`);
|
||||
```
|
||||
|
||||
### Advanced PDF Conversion
|
||||
|
||||
```typescript
|
||||
import { convertPdfToImages } from '@polymech/media';
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
const pdfBuffer = await readFile('document.pdf');
|
||||
|
||||
const options = {
|
||||
baseVariables: {
|
||||
SRC_NAME: 'document',
|
||||
SRC_DIR: '/output'
|
||||
},
|
||||
outputPathTemplate: '${SRC_DIR}/${SRC_NAME}_${PAGE}.${FORMAT}',
|
||||
dpi: 600,
|
||||
scale: 1.5,
|
||||
format: 'png' as ImageFormat,
|
||||
startPage: 1,
|
||||
endPage: 10
|
||||
};
|
||||
|
||||
const outputFiles = await convertPdfToImages(pdfBuffer, options);
|
||||
```
|
||||
|
||||
### Custom Page Range
|
||||
|
||||
```typescript
|
||||
const config: ConvertCommandConfig = {
|
||||
input: 'large-document.pdf',
|
||||
output: 'pages/chapter_{PAGE}.png',
|
||||
format: 'png',
|
||||
startPage: 10,
|
||||
endPage: 25,
|
||||
dpi: 150 // Lower DPI for faster processing
|
||||
};
|
||||
|
||||
await runConversion(config, logger);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Basic PDF to JPG
|
||||
|
||||
```bash
|
||||
# Convert entire PDF to JPG images
|
||||
pm-media pdf2jpg \
|
||||
--input "document.pdf" \
|
||||
--output "images/page_{PAGE}.jpg"
|
||||
```
|
||||
|
||||
### Example 2: High-Quality PNG Conversion
|
||||
|
||||
```bash
|
||||
# High-quality PNG conversion
|
||||
pm-media pdf2jpg \
|
||||
--input "technical-manual.pdf" \
|
||||
--output "manual/page_{PAGE}.png" \
|
||||
--format png \
|
||||
--dpi 600 \
|
||||
--scale 1
|
||||
```
|
||||
|
||||
### Example 3: Specific Page Range
|
||||
|
||||
```bash
|
||||
# Convert only pages 5-15
|
||||
pm-media pdf2jpg \
|
||||
--input "book.pdf" \
|
||||
--output "chapter/page_{PAGE}.jpg" \
|
||||
--startPage 5 \
|
||||
--endPage 15 \
|
||||
--dpi 300
|
||||
```
|
||||
|
||||
### Example 4: Fast Preview Generation
|
||||
|
||||
```bash
|
||||
# Low-quality previews for quick processing
|
||||
pm-media pdf2jpg \
|
||||
--input "presentation.pdf" \
|
||||
--output "previews/slide_{PAGE}.jpg" \
|
||||
--dpi 150 \
|
||||
--scale 1
|
||||
```
|
||||
|
||||
### Example 5: Print-Quality Images
|
||||
|
||||
```bash
|
||||
# Print-quality conversion
|
||||
pm-media pdf2jpg \
|
||||
--input "brochure.pdf" \
|
||||
--output "print-ready/page_{PAGE}.png" \
|
||||
--format png \
|
||||
--dpi 600 \
|
||||
--scale 2
|
||||
```
|
||||
|
||||
### Example 6: Organized Output Structure
|
||||
|
||||
```bash
|
||||
# Organized folder structure
|
||||
pm-media pdf2jpg \
|
||||
--input "reports/quarterly-report.pdf" \
|
||||
--output "output/quarterly/{PAGE:03d}_page.jpg" \
|
||||
--dpi 300
|
||||
```
|
||||
|
||||
### Example 7: Single Page Extraction
|
||||
|
||||
```bash
|
||||
# Extract just the first page
|
||||
pm-media pdf2jpg \
|
||||
--input "document.pdf" \
|
||||
--output "cover.jpg" \
|
||||
--startPage 1 \
|
||||
--endPage 1 \
|
||||
--dpi 300
|
||||
```
|
||||
|
||||
### Example 8: Batch Processing Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Process multiple PDFs
|
||||
|
||||
for pdf in *.pdf; do
|
||||
name=$(basename "$pdf" .pdf)
|
||||
pm-media pdf2jpg \
|
||||
--input "$pdf" \
|
||||
--output "converted/${name}/{PAGE}.jpg" \
|
||||
--dpi 300 \
|
||||
--format jpg
|
||||
done
|
||||
```
|
||||
|
||||
## Template Variables
|
||||
|
||||
The output path supports template variables for dynamic naming:
|
||||
|
||||
### Available Variables
|
||||
|
||||
| Variable | Description | Example |
|
||||
|----------|-------------|---------|
|
||||
| `{PAGE}` | Page number | `1`, `2`, `3` |
|
||||
| `{PAGE:03d}` | Zero-padded page | `001`, `002`, `003` |
|
||||
| `{SRC_NAME}` | PDF filename (no extension) | `document` |
|
||||
| `{SRC_DIR}` | Source directory | `/path/to/source` |
|
||||
| `{FORMAT}` | Output format | `jpg`, `png` |
|
||||
|
||||
### Template Examples
|
||||
|
||||
```bash
|
||||
# Basic template
|
||||
--output "pages/page_{PAGE}.jpg"
|
||||
# Result: pages/page_1.jpg, pages/page_2.jpg
|
||||
|
||||
# Zero-padded pages
|
||||
--output "output/{PAGE:03d}_page.{FORMAT}"
|
||||
# Result: output/001_page.jpg, output/002_page.jpg
|
||||
|
||||
# Include source name
|
||||
--output "{SRC_NAME}/page_{PAGE}.{FORMAT}"
|
||||
# Result: document/page_1.jpg, document/page_2.jpg
|
||||
|
||||
# Complex structure
|
||||
--output "converted/{SRC_NAME}/{PAGE:04d}_{SRC_NAME}.{FORMAT}"
|
||||
# Result: converted/manual/0001_manual.jpg
|
||||
```
|
||||
|
||||
## DPI and Quality Guidelines
|
||||
|
||||
### DPI Recommendations
|
||||
|
||||
| Use Case | DPI | File Size | Quality |
|
||||
|----------|-----|-----------|---------|
|
||||
| Web preview | 72-96 | Small | Basic |
|
||||
| Screen viewing | 150 | Medium | Good |
|
||||
| Standard print | 300 | Large | High |
|
||||
| Professional print | 600+ | Very large | Excellent |
|
||||
|
||||
### Quality vs Performance
|
||||
|
||||
```bash
|
||||
# Fast processing (previews)
|
||||
pm-media pdf2jpg --input doc.pdf --dpi 150 --scale 1
|
||||
|
||||
# Balanced (general use)
|
||||
pm-media pdf2jpg --input doc.pdf --dpi 300 --scale 2
|
||||
|
||||
# High quality (print/archive)
|
||||
pm-media pdf2jpg --input doc.pdf --dpi 600 --scale 1 --format png
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### 1. Adjust DPI for Use Case
|
||||
|
||||
```bash
|
||||
# Thumbnails/previews
|
||||
pm-media pdf2jpg --input large.pdf --dpi 96 --output "thumbs/{PAGE}.jpg"
|
||||
|
||||
# Print quality
|
||||
pm-media pdf2jpg --input doc.pdf --dpi 300 --output "print/{PAGE}.png"
|
||||
```
|
||||
|
||||
### 2. Use Appropriate Format
|
||||
|
||||
```bash
|
||||
# Photos/complex images → JPG
|
||||
pm-media pdf2jpg --input photo-catalog.pdf --format jpg --dpi 300
|
||||
|
||||
# Text/diagrams → PNG
|
||||
pm-media pdf2jpg --input technical-docs.pdf --format png --dpi 300
|
||||
```
|
||||
|
||||
### 3. Process Page Ranges
|
||||
|
||||
```bash
|
||||
# Process in chunks for large PDFs
|
||||
pm-media pdf2jpg --input huge.pdf --startPage 1 --endPage 50 --output "batch1/{PAGE}.jpg"
|
||||
pm-media pdf2jpg --input huge.pdf --startPage 51 --endPage 100 --output "batch2/{PAGE}.jpg"
|
||||
```
|
||||
|
||||
### 4. Scale Factor Optimization
|
||||
|
||||
```bash
|
||||
# Scale = 1 for faster processing
|
||||
pm-media pdf2jpg --input doc.pdf --scale 1 --dpi 300
|
||||
|
||||
# Scale = 2 for better quality (default)
|
||||
pm-media pdf2jpg --input doc.pdf --scale 2 --dpi 300
|
||||
```
|
||||
|
||||
## File Organization
|
||||
|
||||
### Automatic Directory Creation
|
||||
|
||||
The tool automatically creates output directories:
|
||||
|
||||
```bash
|
||||
# Creates nested directories as needed
|
||||
pm-media pdf2jpg \
|
||||
--input "doc.pdf" \
|
||||
--output "output/projects/client-a/pages/{PAGE}.jpg"
|
||||
```
|
||||
|
||||
### Organized Output Patterns
|
||||
|
||||
```bash
|
||||
# By document name
|
||||
--output "{SRC_NAME}/page_{PAGE:03d}.jpg"
|
||||
|
||||
# By date and document
|
||||
--output "$(date +%Y-%m-%d)/{SRC_NAME}/{PAGE}.jpg"
|
||||
|
||||
# By quality level
|
||||
--output "high-res/{SRC_NAME}/page_{PAGE}.png"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "Input file does not exist"
|
||||
```bash
|
||||
# Check file path and permissions
|
||||
ls -la path/to/file.pdf
|
||||
pm-media pdf2jpg --input "./document.pdf" --output "output/{PAGE}.jpg"
|
||||
```
|
||||
|
||||
#### "Unable to open PDF"
|
||||
- Ensure PDF is not corrupted
|
||||
- Check if PDF is password-protected
|
||||
- Verify file permissions
|
||||
|
||||
#### Poor image quality
|
||||
```bash
|
||||
# Increase DPI and use PNG for text
|
||||
pm-media pdf2jpg \
|
||||
--input doc.pdf \
|
||||
--format png \
|
||||
--dpi 600 \
|
||||
--scale 1
|
||||
```
|
||||
|
||||
#### Large file sizes
|
||||
```bash
|
||||
# Reduce DPI or use JPG format
|
||||
pm-media pdf2jpg \
|
||||
--input doc.pdf \
|
||||
--format jpg \
|
||||
--dpi 300 \
|
||||
--scale 1
|
||||
```
|
||||
|
||||
#### Memory issues with large PDFs
|
||||
```bash
|
||||
# Process in smaller page ranges
|
||||
pm-media pdf2jpg --input large.pdf --startPage 1 --endPage 10
|
||||
pm-media pdf2jpg --input large.pdf --startPage 11 --endPage 20
|
||||
```
|
||||
|
||||
### Debug Mode
|
||||
|
||||
No specific debug mode, but check output for errors:
|
||||
|
||||
```bash
|
||||
pm-media pdf2jpg --input doc.pdf --output "{PAGE}.jpg" 2>&1 | tee conversion.log
|
||||
```
|
||||
|
||||
### Page Range Validation
|
||||
|
||||
```bash
|
||||
# Check PDF page count first
|
||||
pm-media pdf2jpg --input doc.pdf --startPage 1 --endPage 1 # Test first page
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Integration with Other Tools
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Convert PDF and create thumbnails
|
||||
|
||||
PDF_FILE="$1"
|
||||
BASE_NAME=$(basename "$PDF_FILE" .pdf)
|
||||
|
||||
# Convert to high-res images
|
||||
pm-media pdf2jpg \
|
||||
--input "$PDF_FILE" \
|
||||
--output "images/${BASE_NAME}/{PAGE:03d}.png" \
|
||||
--format png \
|
||||
--dpi 300
|
||||
|
||||
# Create thumbnails
|
||||
pm-media resize \
|
||||
--src "images/${BASE_NAME}/*.png" \
|
||||
--dst "thumbnails/${BASE_NAME}/" \
|
||||
--width 200 \
|
||||
--height 200 \
|
||||
--fit cover
|
||||
```
|
||||
|
||||
### Automation Scripts
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Automated PDF processing pipeline
|
||||
|
||||
INPUT_DIR="$1"
|
||||
OUTPUT_DIR="$2"
|
||||
|
||||
find "$INPUT_DIR" -name "*.pdf" | while read pdf; do
|
||||
name=$(basename "$pdf" .pdf)
|
||||
echo "Processing: $pdf"
|
||||
|
||||
pm-media pdf2jpg \
|
||||
--input "$pdf" \
|
||||
--output "$OUTPUT_DIR/$name/{PAGE:03d}.jpg" \
|
||||
--dpi 300 \
|
||||
--format jpg
|
||||
|
||||
echo "Completed: $name"
|
||||
done
|
||||
```
|
||||
|
||||
### Quality Control
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Generate multiple quality versions
|
||||
|
||||
PDF="$1"
|
||||
NAME=$(basename "$PDF" .pdf)
|
||||
|
||||
# Preview quality
|
||||
pm-media pdf2jpg --input "$PDF" --output "preview/$NAME/{PAGE}.jpg" --dpi 150
|
||||
|
||||
# Standard quality
|
||||
pm-media pdf2jpg --input "$PDF" --output "standard/$NAME/{PAGE}.jpg" --dpi 300
|
||||
|
||||
# Print quality
|
||||
pm-media pdf2jpg --input "$PDF" --output "print/$NAME/{PAGE}.png" --dpi 600 --format png
|
||||
```
|
||||
|
||||
## TypeScript Definitions
|
||||
|
||||
```typescript
|
||||
interface ConvertCommandConfig {
|
||||
input: string;
|
||||
output?: string;
|
||||
dpi: number;
|
||||
scale?: number;
|
||||
format: 'png' | 'jpg';
|
||||
startPage?: number;
|
||||
endPage?: number;
|
||||
}
|
||||
|
||||
interface PdfToImageOptions {
|
||||
baseVariables: Record<string, any>;
|
||||
outputPathTemplate: string;
|
||||
dpi: number;
|
||||
scale?: number;
|
||||
format: ImageFormat;
|
||||
startPage?: number;
|
||||
endPage?: number;
|
||||
logger?: Logger<any>;
|
||||
}
|
||||
|
||||
type ImageFormat = 'png' | 'jpg';
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
For bug reports, feature requests, or contributions, please visit the project repository.
|
||||
|
||||
## License
|
||||
|
||||
This tool is part of the @polymech/media package. Please refer to the package license for usage terms.
|
||||
545
packages/media/docs/cli-resize.md
Normal file
@ -0,0 +1,545 @@
|
||||
# Resize CLI Documentation
|
||||
|
||||
The `pm-media resize` command allows you to resize images with precise control over dimensions, quality, and performance. This tool supports batch processing and offers extensive options for scaling, cropping, and format conversion.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Resize Methods](#resize-methods)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [API Usage](#api-usage)
|
||||
- [Examples](#examples)
|
||||
- [Performance Options](#performance-options)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @polymech/media
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
pm-media resize --src <input> --dst <output> [resize-options]
|
||||
```
|
||||
|
||||
### Required Parameters
|
||||
|
||||
- `--src`: Source files (FILE|FOLDER|GLOB pattern)
|
||||
|
||||
### Optional Parameters
|
||||
|
||||
- `--dst`: Destination path (defaults to source location)
|
||||
|
||||
## Resize Methods
|
||||
|
||||
### 1. Percentage-based Resizing
|
||||
|
||||
Resize images by a percentage of their original size:
|
||||
|
||||
```bash
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "thumbnails/" \
|
||||
--percent 50
|
||||
```
|
||||
|
||||
### 2. Fixed Width/Height
|
||||
|
||||
Resize to specific dimensions:
|
||||
|
||||
```bash
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "resized/" \
|
||||
--width 800 \
|
||||
--height 600
|
||||
```
|
||||
|
||||
### 3. Width-only or Height-only
|
||||
|
||||
Maintain aspect ratio with one dimension:
|
||||
|
||||
```bash
|
||||
# Resize to 1920px width, auto height
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "web/" \
|
||||
--width 1920
|
||||
|
||||
# Resize to 1080px height, auto width
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "web/" \
|
||||
--height 1080
|
||||
```
|
||||
|
||||
## Command Line Options
|
||||
|
||||
### Resize Dimensions
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--width` | number | - | Target width in pixels |
|
||||
| `--height` | number | - | Target height in pixels |
|
||||
| `--percent` | number | - | Resize by percentage (1-100) |
|
||||
|
||||
### Minimum Size Constraints
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--minWidth` | number | - | Don't resize if width below this value |
|
||||
| `--minHeight` | number | - | Don't resize if height below this value |
|
||||
| `--minSize` | number | - | Don't resize if file size below this (bytes) |
|
||||
|
||||
### Sharp-specific Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--fit` | string | 'cover' | How to fit: cover, contain, fill, inside, outside |
|
||||
| `--position` | string | 'centre' | Position when using fit: top, bottom, left, right, etc |
|
||||
| `--background` | string | 'white' | Background color for contain/letterbox |
|
||||
| `--withoutEnlargement` | boolean | false | Don't enlarge smaller images |
|
||||
| `--withoutReduction` | boolean | false | Don't reduce larger images |
|
||||
| `--fastShrinkOnLoad` | boolean | true | Use JPEG/WebP shrink-on-load |
|
||||
|
||||
### Common Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--src` | string | required | Source files (FILE\|FOLDER\|GLOB) |
|
||||
| `--dst` | string | - | Destination path |
|
||||
| `--dry` | boolean | false | Preview mode (no files written) |
|
||||
| `--verbose` | boolean | false | Show detailed processing info |
|
||||
| `--debug` | boolean | false | Enable debug messages |
|
||||
| `--logLevel` | string | "info" | Log level (warn\|info\|debug\|error) |
|
||||
|
||||
## API Usage
|
||||
|
||||
### Import the Library
|
||||
|
||||
```typescript
|
||||
import {
|
||||
resize,
|
||||
resizeFile,
|
||||
IResizeOptions
|
||||
} from '@polymech/media';
|
||||
```
|
||||
|
||||
### Single File Resize
|
||||
|
||||
```typescript
|
||||
import { resizeFile } from '@polymech/media';
|
||||
|
||||
// Resize by percentage
|
||||
await resizeFile(
|
||||
'input.jpg',
|
||||
'output.jpg',
|
||||
() => {}, // onNode callback
|
||||
{
|
||||
percent: 50,
|
||||
logLevel: 'info'
|
||||
}
|
||||
);
|
||||
|
||||
// Resize to specific dimensions
|
||||
await resizeFile(
|
||||
'input.jpg',
|
||||
'thumbnail.jpg',
|
||||
() => {},
|
||||
{
|
||||
width: 300,
|
||||
height: 300,
|
||||
fit: 'cover',
|
||||
withoutEnlargement: true
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Batch Resize API
|
||||
|
||||
```typescript
|
||||
import { resize } from '@polymech/media';
|
||||
|
||||
const options: IResizeOptions = {
|
||||
src: 'photos/*.jpg',
|
||||
dst: 'resized/',
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
fit: 'inside',
|
||||
withoutEnlargement: true,
|
||||
// ... other IOptions
|
||||
};
|
||||
|
||||
const results = await resize(options);
|
||||
```
|
||||
|
||||
### Advanced Resize Options
|
||||
|
||||
```typescript
|
||||
const advancedOptions: IResizeOptions = {
|
||||
src: 'raw/*.tiff',
|
||||
dst: 'processed/',
|
||||
width: 2048,
|
||||
height: 1536,
|
||||
fit: 'cover',
|
||||
position: 'center',
|
||||
background: { r: 255, g: 255, b: 255, alpha: 1 },
|
||||
withoutEnlargement: true,
|
||||
fastShrinkOnLoad: true,
|
||||
kernel: 'lanczos3'
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Create Thumbnails
|
||||
|
||||
```bash
|
||||
# Generate 150px square thumbnails
|
||||
pm-media resize \
|
||||
--src "gallery/*.jpg" \
|
||||
--dst "thumbs/" \
|
||||
--width 150 \
|
||||
--height 150 \
|
||||
--fit cover \
|
||||
--withoutEnlargement
|
||||
```
|
||||
|
||||
### Example 2: Web Optimization
|
||||
|
||||
```bash
|
||||
# Optimize for web (max 1920px width)
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "web-optimized/" \
|
||||
--width 1920 \
|
||||
--minWidth 800 \
|
||||
--fit inside \
|
||||
--withoutEnlargement
|
||||
```
|
||||
|
||||
### Example 3: Percentage-based Batch Resize
|
||||
|
||||
```bash
|
||||
# Reduce all images to 75% of original size
|
||||
pm-media resize \
|
||||
--src "archive/**/*.{jpg,png}" \
|
||||
--dst "compressed/" \
|
||||
--percent 75
|
||||
```
|
||||
|
||||
### Example 4: Social Media Formats
|
||||
|
||||
```bash
|
||||
# Instagram square format (1080x1080)
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "instagram/" \
|
||||
--width 1080 \
|
||||
--height 1080 \
|
||||
--fit cover \
|
||||
--position center
|
||||
|
||||
# Twitter header (1500x500)
|
||||
pm-media resize \
|
||||
--src "headers/*.jpg" \
|
||||
--dst "twitter/" \
|
||||
--width 1500 \
|
||||
--height 500 \
|
||||
--fit cover
|
||||
```
|
||||
|
||||
### Example 5: Preserve Small Images
|
||||
|
||||
```bash
|
||||
# Don't resize images smaller than 500px width
|
||||
pm-media resize \
|
||||
--src "mixed/*.jpg" \
|
||||
--dst "standardized/" \
|
||||
--width 1200 \
|
||||
--minWidth 500 \
|
||||
--withoutEnlargement
|
||||
```
|
||||
|
||||
### Example 6: File Size Based Resize
|
||||
|
||||
```bash
|
||||
# Only resize files larger than 2MB
|
||||
pm-media resize \
|
||||
--src "large/*.jpg" \
|
||||
--dst "optimized/" \
|
||||
--percent 60 \
|
||||
--minSize 2097152
|
||||
```
|
||||
|
||||
### Example 7: Format Conversion with Resize
|
||||
|
||||
```bash
|
||||
# Resize and convert to WebP
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "webp/{SRC_NAME}.webp" \
|
||||
--width 1920 \
|
||||
--fit inside
|
||||
```
|
||||
|
||||
## Fit Options Explained
|
||||
|
||||
### `cover` (default)
|
||||
- Crop to fill exact dimensions
|
||||
- Maintains aspect ratio
|
||||
- May crop parts of the image
|
||||
|
||||
```bash
|
||||
pm-media resize --src input.jpg --dst output.jpg --width 800 --height 600 --fit cover
|
||||
```
|
||||
|
||||
### `contain`
|
||||
- Letterbox to fit within dimensions
|
||||
- Maintains aspect ratio
|
||||
- No cropping, adds background
|
||||
|
||||
```bash
|
||||
pm-media resize --src input.jpg --dst output.jpg --width 800 --height 600 --fit contain --background white
|
||||
```
|
||||
|
||||
### `fill`
|
||||
- Stretch to exact dimensions
|
||||
- Does not maintain aspect ratio
|
||||
- No cropping
|
||||
|
||||
```bash
|
||||
pm-media resize --src input.jpg --dst output.jpg --width 800 --height 600 --fit fill
|
||||
```
|
||||
|
||||
### `inside`
|
||||
- Resize to fit within dimensions
|
||||
- Maintains aspect ratio
|
||||
- May result in smaller dimensions
|
||||
|
||||
```bash
|
||||
pm-media resize --src input.jpg --dst output.jpg --width 800 --height 600 --fit inside
|
||||
```
|
||||
|
||||
### `outside`
|
||||
- Resize to cover dimensions
|
||||
- Maintains aspect ratio
|
||||
- May result in larger dimensions
|
||||
|
||||
```bash
|
||||
pm-media resize --src input.jpg --dst output.jpg --width 800 --height 600 --fit outside
|
||||
```
|
||||
|
||||
## Position Options
|
||||
|
||||
When using `fit: cover` or `fit: contain`, control crop/letterbox position:
|
||||
|
||||
- `center` (default)
|
||||
- `top`, `bottom`, `left`, `right`
|
||||
- `top left`, `top right`, `bottom left`, `bottom right`
|
||||
- `north`, `south`, `east`, `west`
|
||||
- `northeast`, `northwest`, `southeast`, `southwest`
|
||||
|
||||
```bash
|
||||
pm-media resize \
|
||||
--src portrait.jpg \
|
||||
--dst cropped.jpg \
|
||||
--width 800 \
|
||||
--height 600 \
|
||||
--fit cover \
|
||||
--position "top center"
|
||||
```
|
||||
|
||||
## File Format Support
|
||||
|
||||
### Supported Input Formats
|
||||
- JPEG (.jpg, .jpeg)
|
||||
- PNG (.png)
|
||||
- WebP (.webp)
|
||||
- TIFF (.tiff, .tif)
|
||||
- AVIF (.avif)
|
||||
- SVG (.svg) - rasterized
|
||||
- GIF (.gif) - first frame
|
||||
|
||||
### Supported Output Formats
|
||||
Output format determined by destination file extension:
|
||||
- `.jpg`, `.jpeg` → JPEG
|
||||
- `.png` → PNG
|
||||
- `.webp` → WebP
|
||||
- `.tiff`, `.tif` → TIFF
|
||||
- `.avif` → AVIF
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### 1. Fast Shrink-on-Load
|
||||
```bash
|
||||
# Use JPEG/WebP built-in scaling (faster for large reductions)
|
||||
pm-media resize \
|
||||
--src "huge/*.jpg" \
|
||||
--dst "thumbnails/" \
|
||||
--width 300 \
|
||||
--fastShrinkOnLoad
|
||||
```
|
||||
|
||||
### 2. Batch Processing
|
||||
```bash
|
||||
# Process multiple files efficiently
|
||||
pm-media resize --src "batch/*.jpg" --dst "output/" --percent 50
|
||||
```
|
||||
|
||||
### 3. Prevent Unnecessary Enlargement
|
||||
```bash
|
||||
# Don't enlarge small images (saves processing time)
|
||||
pm-media resize \
|
||||
--src "mixed/*.jpg" \
|
||||
--dst "standardized/" \
|
||||
--width 1920 \
|
||||
--withoutEnlargement
|
||||
```
|
||||
|
||||
### 4. Size-based Processing
|
||||
```bash
|
||||
# Only process large files
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "compressed/" \
|
||||
--percent 70 \
|
||||
--minSize 1048576 # 1MB minimum
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Images appear distorted
|
||||
```bash
|
||||
# Maintain aspect ratio with 'inside' or 'cover'
|
||||
pm-media resize --src input.jpg --dst output.jpg --width 800 --height 600 --fit inside
|
||||
```
|
||||
|
||||
#### Small images getting enlarged
|
||||
```bash
|
||||
# Prevent enlargement
|
||||
pm-media resize --src input.jpg --dst output.jpg --width 1920 --withoutEnlargement
|
||||
```
|
||||
|
||||
#### Poor quality on large reductions
|
||||
```bash
|
||||
# Use better kernel for quality
|
||||
pm-media resize --src input.jpg --dst output.jpg --percent 25 --kernel lanczos3
|
||||
```
|
||||
|
||||
#### Memory issues with large files
|
||||
```bash
|
||||
# Use fast shrink for initial reduction
|
||||
pm-media resize --src huge.jpg --dst smaller.jpg --percent 50 --fastShrinkOnLoad
|
||||
```
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable detailed logging:
|
||||
|
||||
```bash
|
||||
pm-media resize \
|
||||
--src "input/*" \
|
||||
--dst "output/" \
|
||||
--width 800 \
|
||||
--debug \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### File Permissions
|
||||
|
||||
Ensure you have:
|
||||
- Read access to source files
|
||||
- Write access to destination directory
|
||||
- Sufficient disk space for output files
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Template Variables
|
||||
|
||||
Use template variables in destination paths:
|
||||
|
||||
```bash
|
||||
# Use source filename in destination
|
||||
pm-media resize \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "resized/{SRC_NAME}_resized.jpg" \
|
||||
--width 1200
|
||||
```
|
||||
|
||||
Available variables:
|
||||
- `{SRC_NAME}`: Source filename without extension
|
||||
- `{SRC_EXT}`: Source file extension
|
||||
- `{SRC_DIR}`: Source directory
|
||||
|
||||
### Integration Scripts
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Multi-size generation script
|
||||
|
||||
SIZES=(300 600 1200 1920)
|
||||
INPUT_DIR="$1"
|
||||
OUTPUT_BASE="$2"
|
||||
|
||||
for size in "${SIZES[@]}"; do
|
||||
pm-media resize \
|
||||
--src "$INPUT_DIR/*.jpg" \
|
||||
--dst "$OUTPUT_BASE/${size}px/" \
|
||||
--width $size \
|
||||
--withoutEnlargement \
|
||||
--fit inside
|
||||
done
|
||||
```
|
||||
|
||||
### Conditional Processing
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Only resize if source is larger than target
|
||||
|
||||
if [ -f "$1" ]; then
|
||||
pm-media resize \
|
||||
--src "$1" \
|
||||
--dst "$2" \
|
||||
--width 1920 \
|
||||
--minWidth 1920 \
|
||||
--withoutEnlargement
|
||||
fi
|
||||
```
|
||||
|
||||
## TypeScript Definitions
|
||||
|
||||
```typescript
|
||||
interface IResizeOptionsSharp {
|
||||
width?: number;
|
||||
height?: number;
|
||||
fit?: keyof sharp.FitEnum;
|
||||
position?: number | string;
|
||||
background?: sharp.Color;
|
||||
kernel?: keyof sharp.KernelEnum;
|
||||
withoutEnlargement?: boolean;
|
||||
withoutReduction?: boolean;
|
||||
fastShrinkOnLoad?: boolean;
|
||||
}
|
||||
|
||||
interface IResizeOptions extends IOptions, IResizeOptionsSharp {
|
||||
percent?: number;
|
||||
minWidth?: number;
|
||||
minHeight?: number;
|
||||
minSize?: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
For bug reports, feature requests, or contributions, please visit the project repository.
|
||||
|
||||
## License
|
||||
|
||||
This tool is part of the @polymech/media package. Please refer to the package license for usage terms.
|
||||
446
packages/media/docs/cli-watermark.md
Normal file
@ -0,0 +1,446 @@
|
||||
# Watermark CLI Documentation
|
||||
|
||||
The `pm-media watermark` command allows you to add watermarks to images using either text or image overlays. This tool supports batch processing and offers extensive customization options for positioning, styling, and opacity.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Text Watermarks](#text-watermarks)
|
||||
- [Image Watermarks](#image-watermarks)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [API Usage](#api-usage)
|
||||
- [Examples](#examples)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @polymech/media
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
pm-media watermark --src <input> --dst <output> --watermarkType <type> [options]
|
||||
```
|
||||
|
||||
### Required Parameters
|
||||
|
||||
- `--src`: Source files (FILE|FOLDER|GLOB pattern)
|
||||
- `--watermarkType`: Type of watermark (`text` or `image`)
|
||||
|
||||
### Conditional Requirements
|
||||
|
||||
- For text watermarks: `--text` is required
|
||||
- For image watermarks: `--logoPath` is required
|
||||
|
||||
## Text Watermarks
|
||||
|
||||
Add text overlays to your images with full typography control.
|
||||
|
||||
### Basic Text Watermark
|
||||
|
||||
```bash
|
||||
pm-media watermark \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "output/" \
|
||||
--watermarkType text \
|
||||
--text "© 2024 MyBrand"
|
||||
```
|
||||
|
||||
### Advanced Text Styling
|
||||
|
||||
```bash
|
||||
pm-media watermark \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "output/" \
|
||||
--watermarkType text \
|
||||
--text "© 2024 MyBrand" \
|
||||
--position bottom-right \
|
||||
--fontSize 42 \
|
||||
--color "#ffffff" \
|
||||
--fontFamily "Arial" \
|
||||
--strokeColor "#000000" \
|
||||
--strokeWidth 3 \
|
||||
--opacity 0.9 \
|
||||
--margin 30
|
||||
```
|
||||
|
||||
### Text Watermark Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--text` | string | required | Text to display as watermark |
|
||||
| `--fontSize` | number | 48 | Font size in pixels |
|
||||
| `--color` | string | "#ffffff" | Text color (hex format) |
|
||||
| `--fontFamily` | string | "Arial" | Font family name |
|
||||
| `--strokeColor` | string | "#000000" | Text outline color (hex format) |
|
||||
| `--strokeWidth` | number | 2 | Text outline width in pixels |
|
||||
|
||||
## Image Watermarks
|
||||
|
||||
Add logo or image overlays with automatic scaling and positioning.
|
||||
|
||||
### Basic Image Watermark
|
||||
|
||||
```bash
|
||||
pm-media watermark \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "output/" \
|
||||
--watermarkType image \
|
||||
--logoPath "logo.png"
|
||||
```
|
||||
|
||||
### Advanced Image Positioning
|
||||
|
||||
```bash
|
||||
pm-media watermark \
|
||||
--src "photos/*.jpg" \
|
||||
--dst "output/" \
|
||||
--watermarkType image \
|
||||
--logoPath "watermark.png" \
|
||||
--position top-left \
|
||||
--sizePct 0.15 \
|
||||
--opacity 0.7 \
|
||||
--margin 20
|
||||
```
|
||||
|
||||
### Image Watermark Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--logoPath` | string | required | Path to logo/watermark image file |
|
||||
| `--sizePct` | number | 0.2 | Size as percentage of base image width (0-1) |
|
||||
|
||||
## Command Line Options
|
||||
|
||||
### Common Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--src` | string | required | Source files (FILE\|FOLDER\|GLOB) |
|
||||
| `--dst` | string | - | Destination path |
|
||||
| `--watermarkType` | choice | required | "text" or "image" |
|
||||
| `--position` | choice | "bottom-right" | Watermark position |
|
||||
| `--margin` | number | 24 | Margin from edges in pixels |
|
||||
| `--opacity` | number | 0.85 | Opacity level (0-1) |
|
||||
|
||||
### Position Options
|
||||
|
||||
- `top-left`: Upper left corner
|
||||
- `top-right`: Upper right corner
|
||||
- `bottom-left`: Lower left corner
|
||||
- `bottom-right`: Lower right corner (default)
|
||||
- `center`: Center of image
|
||||
|
||||
### Utility Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `--dry` | boolean | false | Preview mode (no files written) |
|
||||
| `--verbose` | boolean | false | Show detailed processing info |
|
||||
| `--debug` | boolean | false | Enable debug messages |
|
||||
| `--logLevel` | string | "info" | Log level (warn\|info\|debug\|error) |
|
||||
|
||||
## API Usage
|
||||
|
||||
### Import the Library
|
||||
|
||||
```typescript
|
||||
import {
|
||||
watermark,
|
||||
addTextWatermark,
|
||||
addLogoWatermark,
|
||||
WatermarkOptions,
|
||||
TextWatermarkOptions,
|
||||
LogoWatermarkOptions
|
||||
} from '@polymech/media';
|
||||
```
|
||||
|
||||
### Text Watermark API
|
||||
|
||||
```typescript
|
||||
import { addTextWatermark } from '@polymech/media';
|
||||
|
||||
await addTextWatermark(
|
||||
'input.jpg',
|
||||
'© 2024 MyBrand',
|
||||
'output.jpg',
|
||||
{
|
||||
position: 'bottom-right',
|
||||
fontSize: 48,
|
||||
color: '#ffffff',
|
||||
opacity: 0.9,
|
||||
margin: 24,
|
||||
fontFamily: 'Arial',
|
||||
strokeColor: '#000000',
|
||||
strokeWidth: 2
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Image Watermark API
|
||||
|
||||
```typescript
|
||||
import { addLogoWatermark } from '@polymech/media';
|
||||
|
||||
await addLogoWatermark(
|
||||
'input.jpg',
|
||||
'logo.png',
|
||||
'output.jpg',
|
||||
{
|
||||
position: 'bottom-right',
|
||||
sizePct: 0.2,
|
||||
opacity: 0.85,
|
||||
margin: 24,
|
||||
blend: 'over'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Batch Processing API
|
||||
|
||||
```typescript
|
||||
import { watermark } from '@polymech/media';
|
||||
|
||||
const options: WatermarkOptions = {
|
||||
src: 'photos/*.jpg',
|
||||
dst: 'output/',
|
||||
watermarkType: 'text',
|
||||
text: '© 2024 MyBrand',
|
||||
textOptions: {
|
||||
position: 'bottom-right',
|
||||
fontSize: 48,
|
||||
color: '#ffffff',
|
||||
opacity: 0.9
|
||||
},
|
||||
// ... other IOptions
|
||||
};
|
||||
|
||||
await watermark(options);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Brand Photos with Logo
|
||||
|
||||
```bash
|
||||
# Add company logo to product photos
|
||||
pm-media watermark \
|
||||
--src "products/*.jpg" \
|
||||
--dst "branded/" \
|
||||
--watermarkType image \
|
||||
--logoPath "brand-logo.png" \
|
||||
--position bottom-right \
|
||||
--sizePct 0.12 \
|
||||
--opacity 0.8 \
|
||||
--margin 30
|
||||
```
|
||||
|
||||
### Example 2: Copyright Text on Multiple Formats
|
||||
|
||||
```bash
|
||||
# Add copyright to various image formats
|
||||
pm-media watermark \
|
||||
--src "gallery/*.{jpg,png,webp}" \
|
||||
--dst "copyrighted/" \
|
||||
--watermarkType text \
|
||||
--text "© 2024 Photography Studio" \
|
||||
--position bottom-left \
|
||||
--fontSize 28 \
|
||||
--color "#ffffff" \
|
||||
--strokeColor "#000000" \
|
||||
--strokeWidth 1 \
|
||||
--opacity 0.85
|
||||
```
|
||||
|
||||
### Example 3: Centered Watermark
|
||||
|
||||
```bash
|
||||
# Large central watermark for draft images
|
||||
pm-media watermark \
|
||||
--src "drafts/*.jpg" \
|
||||
--dst "review/" \
|
||||
--watermarkType text \
|
||||
--text "DRAFT - NOT FOR PUBLICATION" \
|
||||
--position center \
|
||||
--fontSize 72 \
|
||||
--color "#ff0000" \
|
||||
--opacity 0.3
|
||||
```
|
||||
|
||||
### Example 4: Batch Processing with Dry Run
|
||||
|
||||
```bash
|
||||
# Preview changes before applying
|
||||
pm-media watermark \
|
||||
--src "archive/**/*.jpg" \
|
||||
--dst "processed/" \
|
||||
--watermarkType image \
|
||||
--logoPath "watermark.png" \
|
||||
--dry \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### Example 5: Custom Logo Positioning
|
||||
|
||||
```bash
|
||||
# Small logo in top-right corner
|
||||
pm-media watermark \
|
||||
--src "social-media/*.png" \
|
||||
--dst "branded/" \
|
||||
--watermarkType image \
|
||||
--logoPath "social-logo.png" \
|
||||
--position top-right \
|
||||
--sizePct 0.08 \
|
||||
--margin 15 \
|
||||
--opacity 0.9
|
||||
```
|
||||
|
||||
## File Format Support
|
||||
|
||||
### Supported Input Formats
|
||||
- JPEG (.jpg, .jpeg)
|
||||
- PNG (.png)
|
||||
- WebP (.webp)
|
||||
- TIFF (.tiff, .tif)
|
||||
|
||||
### Supported Output Formats
|
||||
Output format is automatically determined by the destination file extension or input format.
|
||||
|
||||
## Performance Tips
|
||||
|
||||
1. **Batch Processing**: Process multiple files in one command for better performance
|
||||
2. **Concurrency**: The tool processes files with controlled concurrency (default: 1)
|
||||
3. **Logo Optimization**: Use PNG logos with transparency for best results
|
||||
4. **Size Optimization**: Keep logo files reasonably sized to improve processing speed
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "Logo file not found"
|
||||
```bash
|
||||
# Ensure the logo path is correct and file exists
|
||||
ls -la path/to/logo.png
|
||||
```
|
||||
|
||||
#### "Unable to open for write"
|
||||
```bash
|
||||
# Ensure output directory exists
|
||||
mkdir -p output/directory
|
||||
```
|
||||
|
||||
#### Text appears blurry
|
||||
- Increase `--fontSize` for better clarity
|
||||
- Adjust `--strokeWidth` for better contrast
|
||||
- Use appropriate `--color` contrast against background
|
||||
|
||||
#### Logo appears too large/small
|
||||
- Adjust `--sizePct` (0.1 = 10% of image width)
|
||||
- Consider image dimensions when setting size
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable detailed logging to troubleshoot issues:
|
||||
|
||||
```bash
|
||||
pm-media watermark \
|
||||
--src "input/*" \
|
||||
--dst "output/" \
|
||||
--watermarkType text \
|
||||
--text "Debug Test" \
|
||||
--debug \
|
||||
--verbose
|
||||
```
|
||||
|
||||
### File Permissions
|
||||
|
||||
Ensure you have:
|
||||
- Read access to source files
|
||||
- Write access to destination directory
|
||||
- Execute permissions on the tool
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Using Environment Variables
|
||||
|
||||
```bash
|
||||
export WATERMARK_TEXT="© 2024 MyCompany"
|
||||
export WATERMARK_LOGO="./assets/logo.png"
|
||||
|
||||
pm-media watermark \
|
||||
--src "batch/*.jpg" \
|
||||
--dst "output/" \
|
||||
--watermarkType text \
|
||||
--text "$WATERMARK_TEXT"
|
||||
```
|
||||
|
||||
### Integration with Scripts
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Automated watermarking script
|
||||
|
||||
INPUT_DIR="$1"
|
||||
OUTPUT_DIR="$2"
|
||||
WATERMARK_TYPE="$3"
|
||||
|
||||
if [ "$WATERMARK_TYPE" = "logo" ]; then
|
||||
pm-media watermark \
|
||||
--src "$INPUT_DIR/*.jpg" \
|
||||
--dst "$OUTPUT_DIR/" \
|
||||
--watermarkType image \
|
||||
--logoPath "./branding/logo.png" \
|
||||
--position bottom-right
|
||||
else
|
||||
pm-media watermark \
|
||||
--src "$INPUT_DIR/*.jpg" \
|
||||
--dst "$OUTPUT_DIR/" \
|
||||
--watermarkType text \
|
||||
--text "© $(date +%Y) MyBrand" \
|
||||
--position bottom-right
|
||||
fi
|
||||
```
|
||||
|
||||
## TypeScript Definitions
|
||||
|
||||
```typescript
|
||||
type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
|
||||
|
||||
interface LogoWatermarkOptions {
|
||||
position?: Corner;
|
||||
margin?: number;
|
||||
sizePct?: number;
|
||||
opacity?: number;
|
||||
blend?: OverlayOptions["blend"];
|
||||
}
|
||||
|
||||
interface TextWatermarkOptions {
|
||||
position?: Corner;
|
||||
margin?: number;
|
||||
fontSize?: number;
|
||||
opacity?: number;
|
||||
color?: string;
|
||||
fontFamily?: string;
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
}
|
||||
|
||||
interface WatermarkOptions extends IOptions {
|
||||
watermarkType: 'text' | 'image';
|
||||
text?: string;
|
||||
textOptions?: TextWatermarkOptions;
|
||||
logoPath?: string;
|
||||
imageOptions?: LogoWatermarkOptions;
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
For bug reports, feature requests, or contributions, please visit the project repository.
|
||||
|
||||
## License
|
||||
|
||||
This tool is part of the @polymech/media package. Please refer to the package license for usage terms.
|
||||
@ -1 +0,0 @@
|
||||
mock-jpg-data
|
||||
@ -1 +0,0 @@
|
||||
mock-jpg-data
|
||||
@ -1 +0,0 @@
|
||||
mock-jpg-data
|
||||
@ -1 +0,0 @@
|
||||
mock-png-data
|
||||
@ -1 +0,0 @@
|
||||
mock-png-data
|
||||
@ -1 +0,0 @@
|
||||
mock-png-data
|
||||
@ -1 +0,0 @@
|
||||
mock-png-data
|
||||
@ -1 +0,0 @@
|
||||
mock-png-data
|
||||
337
packages/media/package-lock.json
generated
@ -20,11 +20,11 @@
|
||||
"fast-glob": "^3.3.2",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
"glob": "^11.0.0",
|
||||
"glob-base": "^0.3.0",
|
||||
"js-beautify": "^1.14.6",
|
||||
"mupdf": "^1.3.3",
|
||||
"novita-sdk": "^1.0.37",
|
||||
"p-map": "^7.0.3",
|
||||
"replicate": "^1.0.1",
|
||||
"sharp": "^0.34.2",
|
||||
"tslog": "^4.9.3",
|
||||
"typescript": "^5.8.3",
|
||||
@ -32,7 +32,7 @@
|
||||
"zod": "^3.25.74"
|
||||
},
|
||||
"bin": {
|
||||
"pm-media": "main.js"
|
||||
"pm-media": "dist-in/main.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^8.1.0",
|
||||
@ -1691,6 +1691,19 @@
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||
},
|
||||
"node_modules/abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
@ -1751,6 +1764,27 @@
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@ -1768,6 +1802,31 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
@ -2051,6 +2110,26 @@
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz",
|
||||
@ -2237,49 +2316,6 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-base": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
|
||||
"integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"glob-parent": "^2.0.0",
|
||||
"is-glob": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-base/node_modules/glob-parent": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
|
||||
"integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-base/node_modules/is-extglob": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
||||
"integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-base/node_modules/is-glob": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
|
||||
"integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@ -2292,6 +2328,27 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@ -2722,6 +2779,16 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proto-list": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||
@ -2757,6 +2824,38 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"events": "^3.3.0",
|
||||
"process": "^0.11.10",
|
||||
"string_decoder": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/replicate": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/replicate/-/replicate-1.0.1.tgz",
|
||||
"integrity": "sha512-EY+rK1YR5bKHcM9pd6WyaIbv6m2aRIvHfHDh51j/LahlHTLKemTYXF6ptif2sLa+YospupAsIoxw8Ndt5nI3vg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"git": ">=2.11.0",
|
||||
"node": ">=18.0.0",
|
||||
"npm": ">=7.19.0",
|
||||
"yarn": ">=1.7.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"readable-stream": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||
@ -2830,6 +2929,27 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
@ -2969,6 +3089,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
@ -4323,6 +4453,15 @@
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||
},
|
||||
"abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
@ -4364,6 +4503,12 @@
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"optional": true
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@ -4377,6 +4522,16 @@
|
||||
"fill-range": "^7.1.1"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
@ -4586,6 +4741,18 @@
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"optional": true
|
||||
},
|
||||
"events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"optional": true
|
||||
},
|
||||
"expect-type": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz",
|
||||
@ -4695,38 +4862,6 @@
|
||||
"path-scurry": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"glob-base": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
|
||||
"integrity": "sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==",
|
||||
"requires": {
|
||||
"glob-parent": "^2.0.0",
|
||||
"is-glob": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"glob-parent": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
|
||||
"integrity": "sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==",
|
||||
"requires": {
|
||||
"is-glob": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
||||
"integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww=="
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
|
||||
"integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==",
|
||||
"requires": {
|
||||
"is-extglob": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@ -4735,6 +4870,12 @@
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"optional": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@ -5020,6 +5161,12 @@
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"optional": true
|
||||
},
|
||||
"proto-list": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||
@ -5040,6 +5187,27 @@
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"events": "^3.3.0",
|
||||
"process": "^0.11.10",
|
||||
"string_decoder": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"replicate": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/replicate/-/replicate-1.0.1.tgz",
|
||||
"integrity": "sha512-EY+rK1YR5bKHcM9pd6WyaIbv6m2aRIvHfHDh51j/LahlHTLKemTYXF6ptif2sLa+YospupAsIoxw8Ndt5nI3vg==",
|
||||
"requires": {
|
||||
"readable-stream": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"reusify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||
@ -5083,6 +5251,12 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"optional": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
@ -5181,6 +5355,15 @@
|
||||
"integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
|
||||
"dev": true
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"access": "public"
|
||||
},
|
||||
"bin": {
|
||||
"pm-media": "main.js"
|
||||
"pm-media": "dist-in/main.js"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
@ -28,6 +28,7 @@
|
||||
"mupdf": "^1.3.3",
|
||||
"novita-sdk": "^1.0.37",
|
||||
"p-map": "^7.0.3",
|
||||
"replicate": "^1.0.1",
|
||||
"sharp": "^0.34.2",
|
||||
"tslog": "^4.9.3",
|
||||
"typescript": "^5.8.3",
|
||||
|
||||
102
packages/media/src/commands/background-remove.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import * as CLI from 'yargs'
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import { logger } from '../index.js'
|
||||
import { cli } from '../cli.js'
|
||||
import {
|
||||
sanitize,
|
||||
defaults
|
||||
} from '../_cli.js'
|
||||
|
||||
import {
|
||||
backgroundRemove,
|
||||
BackgroundRemoveOptions
|
||||
} from '../lib/media/images/background-remove.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
demandOption: true
|
||||
}).option('dst', {
|
||||
describe: 'FILE|FOLDER|GLOB'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('alt', {
|
||||
default: false,
|
||||
describe: 'Use alternate tokenizer, & instead of $',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Run without conversion',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('apiKey', {
|
||||
describe: 'Replicate API key (or set REPLICATE_API_TOKEN env var)',
|
||||
type: 'string'
|
||||
}).option('model', {
|
||||
describe: 'Background removal model to use',
|
||||
choices: ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta'],
|
||||
default: 'u2net'
|
||||
}).option('alphaMattting', {
|
||||
describe: 'Enable alpha matting for better edge refinement',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('alphaMattingForegroundThreshold', {
|
||||
describe: 'Alpha matting foreground threshold',
|
||||
type: 'number',
|
||||
default: 270
|
||||
}).option('alphaMattingBackgroundThreshold', {
|
||||
describe: 'Alpha matting background threshold',
|
||||
type: 'number',
|
||||
default: 10
|
||||
}).option('alphaMattingErodeSize', {
|
||||
describe: 'Alpha matting erode size',
|
||||
type: 'number',
|
||||
default: 10
|
||||
})
|
||||
}
|
||||
|
||||
export const command = 'background:remove';
|
||||
export const desc = 'Remove background from images using AI';
|
||||
export const builder = defaultOptions;
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
const options = sanitize(argv) as BackgroundRemoveOptions
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
const config: any = CONFIG_DEFAULT()
|
||||
|
||||
// Get API key from argument or environment variable
|
||||
options.apiKey = options.apiKey || process.env.REPLICATE_API_TOKEN || config?.replicate?.key;
|
||||
|
||||
if (!options.apiKey) {
|
||||
logger.error('Replicate API key is required. Provide it via --apiKey argument or set REPLICATE_API_TOKEN environment variable');
|
||||
logger.info('Get your API key at: https://replicate.com/account/api-tokens');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Map CLI arguments to library options
|
||||
options.model = argv.model as string;
|
||||
options.alpha_matting = argv.alphaMattting as boolean;
|
||||
options.alpha_matting_foreground_threshold = argv.alphaMattingForegroundThreshold as number;
|
||||
options.alpha_matting_background_threshold = argv.alphaMattingBackgroundThreshold as number;
|
||||
options.alpha_matting_erode_size = argv.alphaMattingErodeSize as number;
|
||||
|
||||
logger.info("Removing background with options:", {
|
||||
model: options.model,
|
||||
alpha_matting: options.alpha_matting,
|
||||
files: options.srcInfo?.FILES?.length || 0
|
||||
});
|
||||
|
||||
await backgroundRemove(options);
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
@ -64,14 +64,16 @@ export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
})
|
||||
}
|
||||
|
||||
let options = (yargs: CLI.Argv) => defaultOptions(yargs)
|
||||
export const command = 'resize';
|
||||
export const desc = 'Resizes files';
|
||||
export const builder = defaultOptions;
|
||||
|
||||
export const register = (cli: CLI.Argv) => {
|
||||
return cli.command('resize', 'Resizes files', options, async (argv: CLI.Arguments) => {
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
const options = sanitize(argv) as IOptions
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
logger.info("options " + argv.dst, options)
|
||||
await resize(options)
|
||||
})
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
|
||||
83
packages/media/src/commands/watermark-rm.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import * as CLI from 'yargs'
|
||||
import { logger } from '../index.js'
|
||||
import {
|
||||
watermark
|
||||
} from '../lib/media/images/watermark.js'
|
||||
|
||||
import {
|
||||
sanitize,
|
||||
defaults
|
||||
} from '../_cli.js'
|
||||
|
||||
import {
|
||||
IOptions
|
||||
} from '../types.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
demandOption: true
|
||||
}).option('dst', {
|
||||
describe: 'FILE|FOLDER|GLOB'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('alt', {
|
||||
default: false,
|
||||
describe: 'Use alternate tokenizer, & instead of $',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Run without conversion',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('percent', {
|
||||
default: false,
|
||||
describe: 'Resize image with percent',
|
||||
type: 'number'
|
||||
}).option('width', {
|
||||
default: false,
|
||||
describe: 'Resize image with',
|
||||
type: 'number'
|
||||
}).option('height', {
|
||||
default: false,
|
||||
describe: 'Resize image height',
|
||||
type: 'number'
|
||||
}).option('minHeight', {
|
||||
describe: 'Resize image minimum height',
|
||||
type: 'number'
|
||||
}).option('minWidth', {
|
||||
describe: 'Resize image minimum width',
|
||||
type: 'number'
|
||||
}).option('minSize', {
|
||||
describe: 'Resize image size (bytes)',
|
||||
type: 'number'
|
||||
}).option('percent', {
|
||||
describe: 'Resize image in percent (width)',
|
||||
type: 'number'
|
||||
}).option('key', {
|
||||
describe: 'API Key',
|
||||
type: 'string'
|
||||
})
|
||||
}
|
||||
|
||||
const options = (yargs: CLI.Argv) => defaultOptions(yargs)
|
||||
|
||||
export const register = (cli: CLI.Argv) => {
|
||||
return cli.command('watermark', 'Remove watermark : FILE|GLOB', options, async (argv: CLI.Arguments) => {
|
||||
defaults()
|
||||
const options = sanitize(argv) as IOptions
|
||||
const config: any = CONFIG_DEFAULT()
|
||||
if (!config.novita) {
|
||||
logger.error("Novita key not found");
|
||||
return
|
||||
}
|
||||
options.debug && logger.info("Watermark Options " + argv.dst, options)
|
||||
return watermark({ ...options, key: config.novita.key })
|
||||
})
|
||||
}
|
||||
@ -1,10 +1,6 @@
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import * as CLI from 'yargs'
|
||||
import { logger } from '../index.js'
|
||||
import {
|
||||
watermark
|
||||
} from '../lib/media/images/watermark.js'
|
||||
|
||||
import { cli } from '../cli.js'
|
||||
import {
|
||||
sanitize,
|
||||
defaults
|
||||
@ -14,6 +10,11 @@ import {
|
||||
IOptions
|
||||
} from '../types.js'
|
||||
|
||||
import {
|
||||
watermark,
|
||||
WatermarkOptions
|
||||
} from '../lib/media/images/watermark.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('src', {
|
||||
describe: 'FILE|FOLDER|GLOB',
|
||||
@ -36,48 +37,105 @@ export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('percent', {
|
||||
default: false,
|
||||
describe: 'Resize image with percent',
|
||||
type: 'number'
|
||||
}).option('width', {
|
||||
default: false,
|
||||
describe: 'Resize image with',
|
||||
type: 'number'
|
||||
}).option('height', {
|
||||
default: false,
|
||||
describe: 'Resize image height',
|
||||
type: 'number'
|
||||
}).option('minHeight', {
|
||||
describe: 'Resize image minimum height',
|
||||
type: 'number'
|
||||
}).option('minWidth', {
|
||||
describe: 'Resize image minimum width',
|
||||
type: 'number'
|
||||
}).option('minSize', {
|
||||
describe: 'Resize image size (bytes)',
|
||||
type: 'number'
|
||||
}).option('percent', {
|
||||
describe: 'Resize image in percent (width)',
|
||||
type: 'number'
|
||||
}).option('key', {
|
||||
describe: 'API Key',
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('watermarkType', {
|
||||
describe: 'Type of watermark: text or image',
|
||||
choices: ['text', 'image'],
|
||||
demandOption: true
|
||||
}).option('text', {
|
||||
describe: 'Text to use for text watermark',
|
||||
type: 'string'
|
||||
}).option('logoPath', {
|
||||
describe: 'Path to logo image for image watermark',
|
||||
type: 'string'
|
||||
}).option('position', {
|
||||
describe: 'Position of watermark',
|
||||
choices: ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'center'],
|
||||
default: 'bottom-right'
|
||||
}).option('margin', {
|
||||
describe: 'Margin from edges in pixels',
|
||||
type: 'number',
|
||||
default: 24
|
||||
}).option('opacity', {
|
||||
describe: 'Opacity of watermark (0-1)',
|
||||
type: 'number',
|
||||
default: 0.85
|
||||
}).option('sizePct', {
|
||||
describe: 'Size of image watermark as percentage of base image width (0-1)',
|
||||
type: 'number',
|
||||
default: 0.2
|
||||
}).option('fontSize', {
|
||||
describe: 'Font size for text watermark in pixels',
|
||||
type: 'number',
|
||||
default: 48
|
||||
}).option('color', {
|
||||
describe: 'Text color (hex format, e.g., #ffffff)',
|
||||
type: 'string',
|
||||
default: '#ffffff'
|
||||
}).option('fontFamily', {
|
||||
describe: 'Font family for text watermark',
|
||||
type: 'string',
|
||||
default: 'Arial'
|
||||
}).option('strokeColor', {
|
||||
describe: 'Text stroke color (hex format, e.g., #000000)',
|
||||
type: 'string',
|
||||
default: '#000000'
|
||||
}).option('strokeWidth', {
|
||||
describe: 'Text stroke width in pixels',
|
||||
type: 'number',
|
||||
default: 2
|
||||
})
|
||||
}
|
||||
|
||||
const options = (yargs: CLI.Argv) => defaultOptions(yargs)
|
||||
|
||||
export const register = (cli: CLI.Argv) => {
|
||||
return cli.command('watermark', 'Remove watermark : FILE|GLOB', options, async (argv: CLI.Arguments) => {
|
||||
|
||||
export const command = 'watermark';
|
||||
export const desc = 'Adds watermarks to images';
|
||||
export const builder = defaultOptions;
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
const options = sanitize(argv) as IOptions
|
||||
const config: any = CONFIG_DEFAULT()
|
||||
if (!config.novita) {
|
||||
logger.error("Novita key not found");
|
||||
return
|
||||
const options = sanitize(argv) as WatermarkOptions
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
|
||||
// Validate required options based on watermark type
|
||||
if (options.watermarkType === 'text' && !options.text) {
|
||||
logger.error('Text is required when using text watermark type')
|
||||
process.exit(1)
|
||||
}
|
||||
options.debug && logger.info("Watermark Options " + argv.dst, options)
|
||||
return watermark({ ...options, key: config.novita.key })
|
||||
})
|
||||
|
||||
if (options.watermarkType === 'image' && !options.logoPath) {
|
||||
logger.error('Logo path is required when using image watermark type')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Set up watermark options based on type
|
||||
if (options.watermarkType === 'text') {
|
||||
options.textOptions = {
|
||||
position: argv.position as any,
|
||||
margin: argv.margin as number,
|
||||
fontSize: argv.fontSize as number,
|
||||
opacity: argv.opacity as number,
|
||||
color: argv.color as string,
|
||||
fontFamily: argv.fontFamily as string,
|
||||
strokeColor: argv.strokeColor as string,
|
||||
strokeWidth: argv.strokeWidth as number,
|
||||
}
|
||||
} else if (options.watermarkType === 'image') {
|
||||
options.imageOptions = {
|
||||
position: argv.position as any,
|
||||
margin: argv.margin as number,
|
||||
sizePct: argv.sizePct as number,
|
||||
opacity: argv.opacity as number,
|
||||
blend: 'over'
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Adding watermark with options:", options)
|
||||
await watermark(options)
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
146
packages/media/src/lib/media/images/background-remove.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import pMap from 'p-map';
|
||||
import { logger } from '../../../index.js';
|
||||
import { IOptions } from '../../../types.js';
|
||||
import { targets } from '../../index.js';
|
||||
import { sync as mkdir } from '@polymech/fs/dir';
|
||||
|
||||
// https://replicate.com/cjwbw/rembg
|
||||
export interface BackgroundRemoveOptions extends IOptions {
|
||||
apiKey?: string;
|
||||
model?: string;
|
||||
alpha_matting?: boolean;
|
||||
alpha_matting_foreground_threshold?: number;
|
||||
alpha_matting_background_threshold?: number;
|
||||
alpha_matting_erode_size?: number;
|
||||
}
|
||||
|
||||
// Base64 encode image file
|
||||
function encodeImageToBase64(filePath: string): string {
|
||||
const imageBuffer = fs.readFileSync(filePath);
|
||||
return `data:image/${path.extname(filePath).slice(1)};base64,${imageBuffer.toString('base64')}`;
|
||||
}
|
||||
|
||||
// Save FileOutput from Replicate API to file
|
||||
async function saveReplicateOutput(output: any, outputPath: string): Promise<void> {
|
||||
// Ensure output directory exists
|
||||
mkdir(path.dirname(outputPath));
|
||||
|
||||
// Handle FileOutput from Replicate API
|
||||
if (output && typeof output.url === 'function') {
|
||||
// This is a FileOutput object from Replicate
|
||||
logger.debug(`Saving FileOutput to: ${outputPath}`);
|
||||
await fs.promises.writeFile(outputPath, output);
|
||||
} else if (typeof output === 'string' && output.startsWith('http')) {
|
||||
// This is a URL, fetch and save the image
|
||||
logger.debug(`Downloading image from URL: ${output}`);
|
||||
const response = await fetch(output);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
fs.writeFileSync(outputPath, buffer);
|
||||
} else if (typeof output === 'string') {
|
||||
// This might be base64 data
|
||||
let base64String = output.replace(/^data:image\/[a-z]+;base64,/, '');
|
||||
const imageBuffer = Buffer.from(base64String, 'base64');
|
||||
fs.writeFileSync(outputPath, imageBuffer);
|
||||
} else if (Buffer.isBuffer(output)) {
|
||||
// Already a buffer
|
||||
fs.writeFileSync(outputPath, output);
|
||||
} else {
|
||||
throw new Error(`Unexpected output format: ${typeof output}, ${JSON.stringify(output).substring(0, 200)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeBackgroundFile(
|
||||
inputPath: string,
|
||||
outputPath: string,
|
||||
options: BackgroundRemoveOptions
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (!options.apiKey) {
|
||||
throw new Error('Replicate API key is required. Set it via --apiKey or REPLICATE_API_TOKEN environment variable');
|
||||
}
|
||||
|
||||
// Import Replicate dynamically to handle potential installation issues
|
||||
let Replicate;
|
||||
try {
|
||||
const replicateModule = await import('replicate');
|
||||
Replicate = replicateModule.default;
|
||||
} catch (error) {
|
||||
throw new Error('Replicate package not found. Please install it with: npm install replicate');
|
||||
}
|
||||
|
||||
const replicate = new Replicate({
|
||||
auth: options.apiKey,
|
||||
});
|
||||
|
||||
logger.debug(`Removing background from ${inputPath}`);
|
||||
|
||||
// Encode input image to base64 data URL
|
||||
const inputImageBase64 = encodeImageToBase64(inputPath);
|
||||
|
||||
// Prepare input for the model
|
||||
const input = {
|
||||
image: inputImageBase64,
|
||||
model: options.model || 'u2net',
|
||||
alpha_matting: options.alpha_matting || false,
|
||||
alpha_matting_foreground_threshold: options.alpha_matting_foreground_threshold || 270,
|
||||
alpha_matting_background_threshold: options.alpha_matting_background_threshold || 10,
|
||||
alpha_matting_erode_size: options.alpha_matting_erode_size || 10,
|
||||
};
|
||||
|
||||
// Run the background removal model
|
||||
const output = await replicate.run("cjwbw/rembg:fb8af171cfa1616ddcf1242c093f9c46bcada5ad4cf6f2fbe8b81b330ec5c003", {
|
||||
input
|
||||
});
|
||||
|
||||
logger.debug(`API response type: ${typeof output}`);
|
||||
logger.debug(`API response has url method: ${typeof output?.url}`);
|
||||
|
||||
if (output && typeof output.url === 'function') {
|
||||
logger.debug(`Output URL: ${output.url()}`);
|
||||
}
|
||||
|
||||
// Save the result
|
||||
await saveReplicateOutput(output, outputPath);
|
||||
|
||||
logger.info(`Background removed: ${inputPath} → ${outputPath}`);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to remove background from ${inputPath}:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const _backgroundRemove = async (
|
||||
file: string,
|
||||
targets: string[],
|
||||
onNode: (data: any) => void = () => {},
|
||||
options: BackgroundRemoveOptions
|
||||
) => {
|
||||
return pMap(targets, async (target) => {
|
||||
options.verbose && logger.debug(`Removing background ${file} to ${target}`);
|
||||
if (options.dry) {
|
||||
logger.info(`[DRY RUN] Would remove background: ${file} → ${target}`);
|
||||
return;
|
||||
}
|
||||
return await removeBackgroundFile(file, target, options);
|
||||
}, { concurrency: 1 });
|
||||
};
|
||||
|
||||
export const backgroundRemove = async (options: BackgroundRemoveOptions) => {
|
||||
// reporting, stub
|
||||
let reports: any = [];
|
||||
const onNode = (data: any) => reports.push(data);
|
||||
|
||||
if (options.srcInfo) {
|
||||
options.verbose && logger.info(`Removing background from ${options.srcInfo.FILES.length} files`);
|
||||
return await pMap(options.srcInfo.FILES, async (f) => {
|
||||
const outputs = targets(f, options);
|
||||
options.verbose && logger.info(`Removing background ${f} to`, outputs);
|
||||
return _backgroundRemove(f, outputs, onNode, options);
|
||||
}, { concurrency: 1 });
|
||||
} else {
|
||||
options.debug && logger.error(`Invalid source info`);
|
||||
}
|
||||
};
|
||||
@ -1,65 +1,247 @@
|
||||
// https://novita.ai/playground#remove-watermark
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
import BPromise from 'bluebird'
|
||||
import { async as write } from "@polymech/fs/write"
|
||||
import {
|
||||
logger
|
||||
} from '../../../index.js'
|
||||
import sharp, { OverlayOptions } from "sharp";
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { glob } from 'glob';
|
||||
import pMap from 'p-map';
|
||||
import { logger } from '../../../index.js';
|
||||
import { IOptions } from '../../../types.js';
|
||||
import { targets } from '../../index.js';
|
||||
import { sync as mkdir } from '@polymech/fs/dir'
|
||||
|
||||
import {
|
||||
IOptions,
|
||||
IResizeOptions
|
||||
} from '../../../types.js'
|
||||
type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
|
||||
|
||||
import {
|
||||
imageToBase64,
|
||||
base64ToBuffer
|
||||
} from './lib.js'
|
||||
export interface LogoWatermarkOptions {
|
||||
position?: Corner;
|
||||
margin?: number; // px
|
||||
sizePct?: number; // of base image width (0–1)
|
||||
opacity?: number; // 0–1
|
||||
blend?: OverlayOptions["blend"];
|
||||
}
|
||||
|
||||
export interface TextWatermarkOptions {
|
||||
position?: Corner;
|
||||
margin?: number; // px
|
||||
fontSize?: number; // px
|
||||
opacity?: number; // 0–1
|
||||
color?: string; // hex color like '#ffffff'
|
||||
fontFamily?: string; // font family
|
||||
strokeColor?: string; // outline color
|
||||
strokeWidth?: number; // outline width
|
||||
}
|
||||
|
||||
import { NovitaSDK } from "novita-sdk"
|
||||
import { targets, targetsNext } from '../../index.js'
|
||||
export interface WatermarkOptions extends IOptions {
|
||||
// Watermark type
|
||||
watermarkType: 'text' | 'image';
|
||||
|
||||
const removeWatermark = async (file, target, onNode: (data: any) => void = () => { }, options: IResizeOptions) => {
|
||||
const novitaClient = new NovitaSDK(options.key)
|
||||
const params = {
|
||||
image_file: await imageToBase64(file)
|
||||
//"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD",
|
||||
// Text watermark options
|
||||
text?: string;
|
||||
textOptions?: TextWatermarkOptions;
|
||||
|
||||
// Image watermark options
|
||||
logoPath?: string;
|
||||
imageOptions?: LogoWatermarkOptions;
|
||||
}
|
||||
|
||||
export async function addLogoWatermark(
|
||||
inputPath: string,
|
||||
logoPath: string,
|
||||
outputPath: string,
|
||||
opts: LogoWatermarkOptions = {}
|
||||
): Promise<void> {
|
||||
const {
|
||||
position = "bottom-right",
|
||||
margin = 24,
|
||||
sizePct = 0.2,
|
||||
opacity = 0.85,
|
||||
blend = "over",
|
||||
} = opts;
|
||||
|
||||
const base = sharp(inputPath);
|
||||
const { width: W = 0, height: H = 0 } = await base.metadata();
|
||||
if (!W || !H) throw new Error("Could not read base image size");
|
||||
|
||||
// Resize logo to sizePct of base width
|
||||
const logoWidth = Math.max(1, Math.round(W * sizePct));
|
||||
const logoBuf = await sharp(logoPath).resize({ width: logoWidth }).toBuffer();
|
||||
const { width: w = 0, height: h = 0 } = await sharp(logoBuf).metadata();
|
||||
|
||||
// Compute pixel placement
|
||||
const pos = (() => {
|
||||
switch (position) {
|
||||
case "top-left": return { left: margin, top: margin };
|
||||
case "top-right": return { left: W - w - margin, top: margin };
|
||||
case "bottom-left": return { left: margin, top: H - h - margin };
|
||||
case "center": return { left: Math.round((W - w) / 2), top: Math.round((H - h) / 2) };
|
||||
case "bottom-right":
|
||||
default: return { left: W - w - margin, top: H - h - margin };
|
||||
}
|
||||
try {
|
||||
const wMark = await novitaClient.removeWatermark(params)
|
||||
logger.info(`Watermark removed: ${file} to ${target}`)
|
||||
write(target,base64ToBuffer(wMark.image_file))
|
||||
} catch (error) {
|
||||
logger.error(`Failed to remove watermark: ${error.msg}`)
|
||||
})();
|
||||
|
||||
// Ensure output directory exists
|
||||
mkdir(path.dirname(outputPath));
|
||||
|
||||
await base
|
||||
.composite([{
|
||||
input: logoBuf,
|
||||
left: pos.left,
|
||||
top: pos.top,
|
||||
blend,
|
||||
...(opacity < 1 ? { blend: 'multiply' } : {}),
|
||||
}])
|
||||
.toFile(outputPath);
|
||||
}
|
||||
|
||||
export async function addTextWatermark(
|
||||
inputPath: string,
|
||||
text: string,
|
||||
outputPath: string,
|
||||
opts: TextWatermarkOptions = {}
|
||||
): Promise<void> {
|
||||
const {
|
||||
position = "bottom-right",
|
||||
margin = 24,
|
||||
fontSize = 48,
|
||||
opacity = 0.8,
|
||||
color = '#ffffff',
|
||||
fontFamily = 'Arial',
|
||||
strokeColor = '#000000',
|
||||
strokeWidth = 2,
|
||||
} = opts;
|
||||
|
||||
const base = sharp(inputPath);
|
||||
const { width: W = 0, height: H = 0 } = await base.metadata();
|
||||
if (!W || !H) throw new Error("Could not read base image size");
|
||||
|
||||
// Create SVG text element
|
||||
const svgText = `
|
||||
<svg width="${W}" height="${H}" xmlns="http://www.w3.org/2000/svg">
|
||||
<text
|
||||
x="${getTextX(position, W, margin)}"
|
||||
y="${getTextY(position, H, margin, fontSize)}"
|
||||
font-family="${fontFamily}"
|
||||
font-size="${fontSize}"
|
||||
fill="${color}"
|
||||
opacity="${opacity}"
|
||||
stroke="${strokeColor}"
|
||||
stroke-width="${strokeWidth}"
|
||||
text-anchor="${getTextAnchor(position)}"
|
||||
dominant-baseline="${getTextBaseline(position)}"
|
||||
>
|
||||
${text}
|
||||
</text>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
const textBuffer = Buffer.from(svgText);
|
||||
|
||||
// Ensure output directory exists
|
||||
mkdir(path.dirname(outputPath));
|
||||
|
||||
await base
|
||||
.composite([{
|
||||
input: textBuffer,
|
||||
left: 0,
|
||||
top: 0,
|
||||
blend: 'over',
|
||||
}])
|
||||
.toFile(outputPath);
|
||||
}
|
||||
|
||||
function getTextX(position: Corner, width: number, margin: number): number {
|
||||
switch (position) {
|
||||
case "top-left":
|
||||
case "bottom-left":
|
||||
return margin;
|
||||
case "top-right":
|
||||
case "bottom-right":
|
||||
return width - margin;
|
||||
case "center":
|
||||
default:
|
||||
return width / 2;
|
||||
}
|
||||
}
|
||||
|
||||
const _watermark = async (file, targets: string[], onNode: (data: any) => void = () => { }, options: IOptions) => {
|
||||
return BPromise.resolve(targets).map((target) => {
|
||||
options.verbose && logger.debug(`Removing Watermark ${file} to ${target}`)
|
||||
function getTextY(position: Corner, height: number, margin: number, fontSize: number): number {
|
||||
switch (position) {
|
||||
case "top-left":
|
||||
case "top-right":
|
||||
return margin + fontSize;
|
||||
case "bottom-left":
|
||||
case "bottom-right":
|
||||
return height - margin;
|
||||
case "center":
|
||||
default:
|
||||
return height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
function getTextAnchor(position: Corner): string {
|
||||
switch (position) {
|
||||
case "top-left":
|
||||
case "bottom-left":
|
||||
return "start";
|
||||
case "top-right":
|
||||
case "bottom-right":
|
||||
return "end";
|
||||
case "center":
|
||||
default:
|
||||
return "middle";
|
||||
}
|
||||
}
|
||||
|
||||
function getTextBaseline(position: Corner): string {
|
||||
switch (position) {
|
||||
case "top-left":
|
||||
case "top-right":
|
||||
return "hanging";
|
||||
case "bottom-left":
|
||||
case "bottom-right":
|
||||
return "auto";
|
||||
case "center":
|
||||
default:
|
||||
return "middle";
|
||||
}
|
||||
}
|
||||
|
||||
const _watermark = async (file: string, targets: string[], onNode: (data: any) => void = () => { }, options: WatermarkOptions) => {
|
||||
return pMap(targets, async (target) => {
|
||||
options.verbose && logger.debug(`Adding watermark ${file} to ${target}`);
|
||||
if (options.dry) {
|
||||
return BPromise.resolve()
|
||||
return;
|
||||
}
|
||||
return removeWatermark(file, target, onNode, options);
|
||||
}, { concurrency: 1 })
|
||||
}
|
||||
|
||||
export const watermark = async (options: IOptions) => {
|
||||
|
||||
// reporting, stub
|
||||
let reports: any = []
|
||||
const onNode = (data: any) => reports.push(data)
|
||||
if (options.srcInfo) {
|
||||
options.verbose && logger.info(`Convert ${options.srcInfo.FILES.length} files`)
|
||||
return await BPromise.resolve(options.srcInfo.FILES).map((f) => {
|
||||
const outputs = targets(f, options)
|
||||
options.verbose && logger.info(`Convert ${f} to `, outputs)
|
||||
return _watermark(f, outputs, onNode, options)
|
||||
}, { concurrency: 1 })
|
||||
if (options.watermarkType === 'text') {
|
||||
if (!options.text) {
|
||||
throw new Error('Text is required for text watermark');
|
||||
}
|
||||
return addTextWatermark(file, options.text, target, options.textOptions);
|
||||
} else if (options.watermarkType === 'image') {
|
||||
if (!options.logoPath) {
|
||||
throw new Error('Logo path is required for image watermark');
|
||||
}
|
||||
if (!fs.existsSync(options.logoPath)) {
|
||||
throw new Error(`Logo file not found: ${options.logoPath}`);
|
||||
}
|
||||
return addLogoWatermark(file, options.logoPath, target, options.imageOptions);
|
||||
} else {
|
||||
options.debug && logger.error(`Invalid source info`)
|
||||
throw new Error('Invalid watermark type. Must be "text" or "image"');
|
||||
}
|
||||
}
|
||||
}, { concurrency: 1 });
|
||||
};
|
||||
|
||||
export const watermark = async (options: WatermarkOptions) => {
|
||||
// reporting, stub
|
||||
let reports: any = [];
|
||||
const onNode = (data: any) => reports.push(data);
|
||||
|
||||
if (options.srcInfo) {
|
||||
options.verbose && logger.info(`Adding watermark to ${options.srcInfo.FILES.length} files`);
|
||||
return await pMap(options.srcInfo.FILES, async (f) => {
|
||||
const outputs = targets(f, options);
|
||||
options.verbose && logger.info(`Adding watermark ${f} to `, outputs);
|
||||
return _watermark(f, outputs, onNode, options);
|
||||
}, { concurrency: 1 });
|
||||
} else {
|
||||
options.debug && logger.error(`Invalid source info`);
|
||||
}
|
||||
};
|
||||
@ -2,8 +2,11 @@
|
||||
import { defaults } from './_cli.js'; defaults()
|
||||
|
||||
import { cli } from './cli.js'
|
||||
|
||||
import './commands/resize.js'
|
||||
import './commands/pdf2jpg.js'
|
||||
import './commands/watermark.js'
|
||||
import './commands/background-remove.js'
|
||||
|
||||
const argv: any = cli.argv;
|
||||
|
||||
|
||||
@ -55,3 +55,9 @@ export type IConvertVideoOptions = IOptions & {
|
||||
verb: string
|
||||
audio: string
|
||||
}
|
||||
|
||||
// Re-export watermark types from the watermark module
|
||||
export type { WatermarkOptions, LogoWatermarkOptions, TextWatermarkOptions } from './lib/media/images/watermark.js'
|
||||
|
||||
// Re-export background removal types
|
||||
export type { BackgroundRemoveOptions } from './lib/media/images/background-remove.js'
|
||||
BIN
packages/media/tests/images/watermark-add/DSC05572.JPG
Normal file
|
After Width: | Height: | Size: 676 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05575.JPG
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05620.JPG
Normal file
|
After Width: | Height: | Size: 252 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05627.JPG
Normal file
|
After Width: | Height: | Size: 237 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05630.JPG
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05633.JPG
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05634.JPG
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05639.JPG
Normal file
|
After Width: | Height: | Size: 336 KiB |
BIN
packages/media/tests/images/watermark-add/DSC05640.JPG
Normal file
|
After Width: | Height: | Size: 297 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05572.JPG
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05575.JPG
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05620.JPG
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05627.JPG
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05630.JPG
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05633.JPG
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05634.JPG
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05639.JPG
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
packages/media/tests/images/watermark-out/DSC05640.JPG
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05572.JPG
Normal file
|
After Width: | Height: | Size: 649 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05575.JPG
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05620.JPG
Normal file
|
After Width: | Height: | Size: 942 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05627.JPG
Normal file
|
After Width: | Height: | Size: 703 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05630.JPG
Normal file
|
After Width: | Height: | Size: 539 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05633.JPG
Normal file
|
After Width: | Height: | Size: 344 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05634.JPG
Normal file
|
After Width: | Height: | Size: 504 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05639.JPG
Normal file
|
After Width: | Height: | Size: 957 KiB |
BIN
packages/media/tests/images/watermark-rm-out/DSC05640.JPG
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
packages/media/tests/pdf/MS300-Manual-Compact.pdf
Normal file
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 259 KiB |
|
After Width: | Height: | Size: 254 KiB |
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 203 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 276 KiB |
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 219 KiB |