Skip to content
Closed
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.info('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.info(`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.info(`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.info('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
6 changes: 3 additions & 3 deletions clis/yollomi/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,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, resolveImageInput, downloadOutput, fmtBytes, MODEL_ROUTES } from './utils.js';

function getDimensions(ratio: string): { width: number; height: 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.info(`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.info(`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.info('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.info('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.info('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.info('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.info(`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.info('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.info(`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.info(`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.info(`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.info(`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
2 changes: 1 addition & 1 deletion 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 { TurndownService } 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
37 changes: 34 additions & 3 deletions src/package-exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const CLIS_DIR = path.join(ROOT, 'clis');

/** Recursively collect all .ts files in a directory. */
function collectTsFiles(dir: string): string[] {
/** Recursively collect .ts files in a directory, optionally excluding test files. */
function collectTsFiles(dir: string, opts?: { excludeTests?: boolean }): string[] {
const results: string[] = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...collectTsFiles(full));
results.push(...collectTsFiles(full, opts));
} else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {
if (opts?.excludeTests && entry.name.endsWith('.test.ts')) continue;
results.push(full);
}
}
Expand Down Expand Up @@ -61,6 +62,36 @@ describe('adapter imports use package exports', () => {
});
});

describe('adapters do not import third-party packages directly', () => {
const pkgJson = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8'));
const deps = Object.keys(pkgJson.dependencies ?? {});
// Build a pattern that matches: from 'chalk' / from "turndown" etc.
// Excludes node: builtins and relative/package imports.
const depPattern = new RegExp(
`(?:from|mock|importActual)\\s*\\(?['"](?:${deps.map(d => d.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})['"]`,
);

// Only check non-test adapter files (test files run inside the package tree)
const nonTestFiles = collectTsFiles(CLIS_DIR, { excludeTests: true });

it('found non-test adapter files to check', () => {
expect(nonTestFiles.length).toBeGreaterThan(100);
});

it('no adapter directly imports opencli runtime dependencies', () => {
const violations: string[] = [];
for (const file of nonTestFiles) {
const content = fs.readFileSync(file, 'utf-8');
if (depPattern.test(content)) {
const rel = path.relative(ROOT, file);
const match = content.match(depPattern)?.[0];
violations.push(`${rel}: ${match}`);
}
}
expect(violations).toEqual([]);
});
});

describe('package.json exports resolve to real files', () => {
const pkgJson = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf-8'));
const exports = pkgJson.exports as Record<string, string>;
Expand Down
7 changes: 7 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
/**
* Shared utility functions used across the codebase.
*
* Adapters should import from '@jackwener/opencli/utils' instead of
* depending on third-party packages directly, so they work correctly
* when loaded from ~/.opencli/clis/.
*/

import * as fs from 'node:fs';
import * as path from 'node:path';
import TurndownService from 'turndown';

export { TurndownService };

/** Type guard: checks if a value is a non-null, non-array object. */
export function isRecord(value: unknown): value is Record<string, unknown> {
Expand Down