Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions clis/yollomi/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand All @@ -25,7 +25,7 @@ cli({
const imageUrl = kwargs.image as string;
const prompt = kwargs.prompt as string;

process.stderr.write(chalk.dim('Generating background...\n'));
log.status('Generating background...');
const data = await yollomiPost(page, '/api/ai/ai-background-generator', {
images: [imageUrl],
prompt: prompt || undefined,
Expand Down
6 changes: 3 additions & 3 deletions clis/yollomi/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand Down Expand Up @@ -36,7 +36,7 @@ cli({
}

const apiPath = modelId === 'qwen-image-edit-plus' ? '/api/ai/qwen-image-edit-plus' : '/api/ai/qwen-image-edit';
process.stderr.write(chalk.dim(`Editing with ${modelId}...\n`));
log.status(`Editing with ${modelId}...`);
const data = await yollomiPost(page, apiPath, body);

const images: string[] = data.images || (data.image ? [data.image] : []);
Expand All @@ -49,7 +49,7 @@ cli({
try {
const filename = `yollomi_edit_${Date.now()}.png`;
const { path: fp, size } = await downloadOutput(url, kwargs.output as string, filename);
if (credits !== undefined) process.stderr.write(chalk.dim(`Credits remaining: ${credits}\n`));
if (credits !== undefined) log.status(`Credits remaining: ${credits}`);
return [{ status: 'saved', file: path.relative('.', fp), size: fmtBytes(size), credits: credits ?? '-', url }];
} catch {
return [{ status: 'download-failed', file: '-', size: '-', credits: credits ?? '-', url }];
Expand Down
4 changes: 2 additions & 2 deletions clis/yollomi/face-swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand All @@ -23,7 +23,7 @@ cli({
],
columns: ['status', 'file', 'size', 'url'],
func: async (page, kwargs) => {
process.stderr.write(chalk.dim('Swapping faces...\n'));
log.status('Swapping faces...');
const data = await yollomiPost(page, '/api/ai/face-swap', {
swap_image: kwargs.source as string,
input_image: kwargs.target as string,
Expand Down
8 changes: 4 additions & 4 deletions clis/yollomi/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { YOLLOMI_DOMAIN, yollomiPost, resolveImageInput, downloadOutput, fmtBytes, MODEL_ROUTES } from './utils.js';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes, MODEL_ROUTES } from './utils.js';

function getDimensions(ratio: string): { width: number; height: number } {
const map: Record<string, [number, number]> = {
Expand Down Expand Up @@ -62,7 +62,7 @@ cli({
if (kwargs.image) body.imageUrl = kwargs.image as string;
}

process.stderr.write(chalk.dim(`Generating with ${modelId}...\n`));
log.status(`Generating with ${modelId}...`);
const data = await yollomiPost(page, apiPath, body);

const images: string[] = data.images || (data.image ? [data.image] : []);
Expand All @@ -89,7 +89,7 @@ cli({
}
}

if (data.remainingCredits !== undefined) process.stderr.write(chalk.dim(`Credits remaining: ${data.remainingCredits}\n`));
if (data.remainingCredits !== undefined) log.status(`Credits remaining: ${data.remainingCredits}`);
return results;
},
});
4 changes: 2 additions & 2 deletions clis/yollomi/object-remover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand All @@ -22,7 +22,7 @@ cli({
],
columns: ['status', 'file', 'size', 'url'],
func: async (page, kwargs) => {
process.stderr.write(chalk.dim('Removing object...\n'));
log.status('Removing object...');
const data = await yollomiPost(page, '/api/ai/object-remover', {
image: kwargs.image as string,
mask: kwargs.mask as string,
Expand Down
4 changes: 2 additions & 2 deletions clis/yollomi/remove-bg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand All @@ -21,7 +21,7 @@ cli({
],
columns: ['status', 'file', 'size', 'url'],
func: async (page, kwargs) => {
process.stderr.write(chalk.dim('Removing background...\n'));
log.status('Removing background...');
const data = await yollomiPost(page, '/api/ai/remove-bg', { imageUrl: kwargs.image as string });

const url = data.image || (data.images?.[0]);
Expand Down
4 changes: 2 additions & 2 deletions clis/yollomi/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand All @@ -21,7 +21,7 @@ cli({
],
columns: ['status', 'file', 'size', 'url'],
func: async (page, kwargs) => {
process.stderr.write(chalk.dim('Restoring photo...\n'));
log.status('Restoring photo...');
const data = await yollomiPost(page, '/api/ai/photo-restoration', { imageUrl: kwargs.image as string });

const url = data.image || (data.images?.[0]);
Expand Down
4 changes: 2 additions & 2 deletions clis/yollomi/try-on.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand All @@ -23,7 +23,7 @@ cli({
],
columns: ['status', 'file', 'size', 'url'],
func: async (page, kwargs) => {
process.stderr.write(chalk.dim('Processing virtual try-on...\n'));
log.status('Processing virtual try-on...');
const data = await yollomiPost(page, '/api/ai/virtual-try-on', {
person_image: kwargs.person as string,
cloth_image: kwargs.cloth as string,
Expand Down
6 changes: 3 additions & 3 deletions clis/yollomi/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import * as fs from 'node:fs';
import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, ensureOnYollomi, fmtBytes } from './utils.js';

const MIME_MAP: Record<string, string> = {
Expand Down Expand Up @@ -46,7 +46,7 @@ cli({
const b64 = data.toString('base64');
const fileName = path.basename(filePath);

process.stderr.write(chalk.dim(`Uploading ${fileName} (${fmtBytes(data.length)})...\n`));
log.status(`Uploading ${fileName} (${fmtBytes(data.length)})...`);
await ensureOnYollomi(page);

const result = await page.evaluate(`
Expand All @@ -72,7 +72,7 @@ cli({
}

const url = result.data.url;
process.stderr.write(chalk.green(`Uploaded! Use this URL as input for other commands.\n`));
log.success('Uploaded! Use this URL as input for other commands.');
return [{ status: 'uploaded', file: fileName, size: fmtBytes(data.length), url }];
},
});
6 changes: 3 additions & 3 deletions clis/yollomi/upscale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand All @@ -23,7 +23,7 @@ cli({
columns: ['status', 'file', 'size', 'scale', 'url'],
func: async (page, kwargs) => {
const scale = parseInt(kwargs.scale as string, 10);
process.stderr.write(chalk.dim(`Upscaling ${scale}x...\n`));
log.status(`Upscaling ${scale}x...`);
const data = await yollomiPost(page, '/api/ai/image-upscaler', {
imageUrl: kwargs.image as string,
scale,
Expand All @@ -40,7 +40,7 @@ cli({
const ext = urlPath.endsWith('.png') || urlPath.endsWith('.webp') ? urlPath.slice(urlPath.lastIndexOf('.')) : '.jpg';
const filename = `yollomi_upscale_${scale}x_${Date.now()}${ext}`;
const { path: fp, size } = await downloadOutput(url, kwargs.output as string, filename);
if (data.remainingCredits !== undefined) process.stderr.write(chalk.dim(`Credits remaining: ${data.remainingCredits}\n`));
if (data.remainingCredits !== undefined) log.status(`Credits remaining: ${data.remainingCredits}`);
return [{ status: 'saved', file: path.relative('.', fp), size: fmtBytes(size), scale: `${scale}x`, url }];
} catch {
return [{ status: 'download-failed', file: '-', size: '-', scale: `${scale}x`, url }];
Expand Down
6 changes: 3 additions & 3 deletions clis/yollomi/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
*/

import * as path from 'node:path';
import chalk from 'chalk';
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
import { log } from '@jackwener/opencli/logger';
import { YOLLOMI_DOMAIN, yollomiPost, downloadOutput, fmtBytes } from './utils.js';

cli({
Expand Down Expand Up @@ -35,7 +35,7 @@ cli({

const body = { modelId, prompt, inputs };

process.stderr.write(chalk.dim(`Generating video with ${modelId} (may take a while)...\n`));
log.status(`Generating video with ${modelId} (may take a while)...`);
const data = await yollomiPost(page, '/api/ai/video', body);

const videoUrl: string = data.video || '';
Expand All @@ -52,7 +52,7 @@ cli({
try {
const filename = `yollomi_${modelId}_${Date.now()}.mp4`;
const { path: fp, size } = await downloadOutput(videoUrl, outputDir, filename);
if (credits !== undefined) process.stderr.write(chalk.dim(`Credits remaining: ${credits}\n`));
if (credits !== undefined) log.status(`Credits remaining: ${credits}`);
return [{ status: 'saved', file: path.relative('.', fp), size: fmtBytes(size), credits: credits ?? '-', url: videoUrl }];
} catch {
return [{ status: 'download-failed', file: '-', size: '-', credits: credits ?? '-', url: videoUrl }];
Expand Down
98 changes: 38 additions & 60 deletions clis/yuanbao/ask.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cli, Strategy } from '@jackwener/opencli/registry';
import type { IPage } from '@jackwener/opencli/types';
import TurndownService from 'turndown';
import { htmlToMarkdown } from '@jackwener/opencli/utils';
import { CommandExecutionError, TimeoutError } from '@jackwener/opencli/errors';
import { YUANBAO_DOMAIN, YUANBAO_URL, IS_VISIBLE_JS, authRequired, isOnYuanbao, ensureYuanbaoPage, hasLoginGate } from './shared.js';

Expand Down Expand Up @@ -40,66 +40,44 @@ function normalizeBooleanFlag(value: unknown, fallback: boolean): boolean {
return normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on';
}

function createYuanbaoTurndown(): TurndownService {
const td = new TurndownService({
headingStyle: 'atx',
codeBlockStyle: 'fenced',
bulletListMarker: '-',
});

td.addRule('linebreak', {
filter: 'br',
replacement: () => '\n',
});

td.addRule('table', {
filter: 'table',
replacement: (content) => `\n\n${content}\n\n`,
});

td.addRule('tableSection', {
filter: ['thead', 'tbody', 'tfoot'],
replacement: (content) => content,
});

td.addRule('tableRow', {
filter: 'tr',
replacement: (content, node) => {
const element = node as Element;
const cells = Array.from(element.children);
const isHeaderRow = element.parentElement?.tagName === 'THEAD'
|| (cells.length > 0 && cells.every((cell) => cell.tagName === 'TH'));

const row = `${content}\n`;
if (!isHeaderRow) return row;

const separator = `| ${cells.map(() => '---').join(' | ')} |\n`;
return `${row}${separator}`;
},
});

td.addRule('tableCell', {
filter: ['th', 'td'],
replacement: (content, node) => {
const element = node as Element;
const index = element.parentElement ? Array.from(element.parentElement.children).indexOf(element) : 0;
const prefix = index === 0 ? '| ' : ' ';
return `${prefix}${content.trim()} |`;
},
});

return td;
}

const yuanbaoTurndown = createYuanbaoTurndown();

export function convertYuanbaoHtmlToMarkdown(value: string): string {
const markdown = yuanbaoTurndown.turndown(value || '');
return markdown
.replace(/\u00a0/g, ' ')
.replace(/\n{4,}/g, '\n\n\n')
.replace(/[ \t]+$/gm, '')
.trim();
return htmlToMarkdown(value, (td) => {
td.addRule('table', {
filter: 'table',
replacement: (content) => `\n\n${content}\n\n`,
});

td.addRule('tableSection', {
filter: ['thead', 'tbody', 'tfoot'],
replacement: (content) => content,
});

td.addRule('tableRow', {
filter: 'tr',
replacement: (content, node) => {
const element = node as Element;
const cells = Array.from(element.children);
const isHeaderRow = element.parentElement?.tagName === 'THEAD'
|| (cells.length > 0 && cells.every((cell) => cell.tagName === 'TH'));

const row = `${content}\n`;
if (!isHeaderRow) return row;

const separator = `| ${cells.map(() => '---').join(' | ')} |\n`;
return `${row}${separator}`;
},
});

td.addRule('tableCell', {
filter: ['th', 'td'],
replacement: (content, node) => {
const element = node as Element;
const index = element.parentElement ? Array.from(element.parentElement.children).indexOf(element) : 0;
const prefix = index === 0 ? '| ' : ' ';
return `${prefix}${content.trim()} |`;
},
});
});
}

export function sanitizeYuanbaoResponseText(value: string, promptText: string): string {
Expand Down
2 changes: 0 additions & 2 deletions src/discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ export async function ensureUserCliCompatShims(baseDir: string = USER_OPENCLI_DI
} catch { /* doesn't exist */ }
if (needsUpdate) {
await fs.promises.mkdir(symlinkDir, { recursive: true });
// Use rm instead of unlink — handles both symlinks and stale directories
try { await fs.promises.rm(symlinkPath, { recursive: true, force: true }); } catch { /* doesn't exist */ }
// Use 'junction' on Windows — doesn't require admin/Developer Mode privileges
const symlinkType = process.platform === 'win32' ? 'junction' : 'dir';
await fs.promises.symlink(opencliRoot, symlinkPath, symlinkType);
}
Expand Down
Loading