@ktmcp-cli/aiception 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENT.md ADDED
@@ -0,0 +1,44 @@
1
+ # AIception CLI - AI Agent Guide
2
+
3
+ This CLI provides programmatic access to the AIception Image Recognition API.
4
+
5
+ ## Quick Start for AI Agents
6
+
7
+ ```bash
8
+ aiception config set --username YOUR_USER --password YOUR_PASS
9
+ aiception images analyze https://example.com/image.jpg
10
+ aiception tasks get TASK_ID
11
+ ```
12
+
13
+ ## Available Commands
14
+
15
+ ### config
16
+ - `aiception config set --username <user> --password <pass>` - Set credentials
17
+ - `aiception config get <key>` - Get config value
18
+ - `aiception config list` - Show all config
19
+
20
+ ### images
21
+ - `aiception images analyze <url>` - Analyze image content
22
+ - `aiception images classify <url>` - Classify image into categories
23
+ - `aiception images detect-objects <url>` - Detect objects in image
24
+
25
+ ### tasks
26
+ - `aiception tasks get <task-id>` - Get async task result
27
+ - `aiception tasks list` - List recent tasks
28
+
29
+ ### nudity
30
+ - `aiception nudity detect <url>` - Detect nudity in image
31
+
32
+ ## Async Processing Workflow
33
+
34
+ AIception uses async processing. The typical workflow:
35
+
36
+ 1. Submit image: `aiception images analyze <url> --json | jq -r '.task_id'`
37
+ 2. Wait for processing
38
+ 3. Get result: `aiception tasks get <task-id> --json`
39
+
40
+ ## Tips for Agents
41
+
42
+ - All commands support `--json` for machine-readable output
43
+ - Image commands return a task_id for async polling
44
+ - Use `--json` and `jq` to extract task IDs automatically
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 KTMCP
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ > "Six months ago, everyone was talking about MCPs. And I was like, screw MCPs. Every MCP would be better as a CLI."
2
+ >
3
+ > — [Peter Steinberger](https://twitter.com/steipete), Founder of OpenClaw
4
+ > [Watch on YouTube (~2:39:00)](https://www.youtube.com/@lexfridman) | [Lex Fridman Podcast #491](https://lexfridman.com/peter-steinberger/)
5
+
6
+ # AIception CLI
7
+
8
+ Production-ready CLI for the [AIception](https://aiception.com) Image Recognition API. Analyze images, detect objects, classify content, and detect nudity directly from your terminal.
9
+
10
+ > **Disclaimer**: This is an unofficial CLI tool and is not affiliated with, endorsed by, or supported by AIception.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install -g @ktmcp-cli/aiception
16
+ ```
17
+
18
+ ## Configuration
19
+
20
+ ```bash
21
+ aiception config set --username YOUR_USERNAME --password YOUR_PASSWORD
22
+ ```
23
+
24
+ Get your credentials at [aiception.com](https://aiception.com).
25
+
26
+ ## Usage
27
+
28
+ ### Configuration
29
+
30
+ ```bash
31
+ # Set credentials
32
+ aiception config set --username YOUR_USERNAME --password YOUR_PASSWORD
33
+
34
+ # Show configuration
35
+ aiception config list
36
+
37
+ # Get a specific config value
38
+ aiception config get username
39
+ ```
40
+
41
+ ### Image Analysis
42
+
43
+ ```bash
44
+ # Analyze an image and get a description
45
+ aiception images analyze https://example.com/image.jpg
46
+
47
+ # Classify an image into categories
48
+ aiception images classify https://example.com/photo.png
49
+
50
+ # Detect objects in an image
51
+ aiception images detect-objects https://example.com/scene.jpg
52
+ ```
53
+
54
+ ### Task Management
55
+
56
+ AIception processes images asynchronously. Use task commands to retrieve results:
57
+
58
+ ```bash
59
+ # Get the result of a processing task
60
+ aiception tasks get TASK_ID
61
+
62
+ # List all recent tasks
63
+ aiception tasks list
64
+ ```
65
+
66
+ ### Nudity Detection
67
+
68
+ ```bash
69
+ # Detect nudity in an image
70
+ aiception nudity detect https://example.com/image.jpg
71
+ ```
72
+
73
+ ### JSON Output
74
+
75
+ All commands support `--json` for machine-readable output:
76
+
77
+ ```bash
78
+ # Analyze image and get task ID as JSON
79
+ aiception images analyze https://example.com/image.jpg --json
80
+
81
+ # Get task result as JSON
82
+ aiception tasks get TASK_ID --json | jq '.result'
83
+
84
+ # Check nudity detection result
85
+ aiception nudity detect https://example.com/image.jpg --json | jq '{nude: .nude, confidence: .confidence}'
86
+ ```
87
+
88
+ ## Workflow Example
89
+
90
+ AIception uses asynchronous processing. Here's a typical workflow:
91
+
92
+ ```bash
93
+ # 1. Submit an image for analysis
94
+ aiception images analyze https://example.com/photo.jpg
95
+ # Note the Task ID from the output
96
+
97
+ # 2. Poll for the result
98
+ aiception tasks get TASK_ID
99
+
100
+ # 3. Or get it as JSON for scripting
101
+ aiception tasks get TASK_ID --json | jq '.result'
102
+ ```
103
+
104
+ ## Examples
105
+
106
+ ```bash
107
+ # Classify a product image
108
+ aiception images classify https://shop.example.com/product.jpg
109
+
110
+ # Detect objects in a scene
111
+ aiception images detect-objects https://example.com/street.jpg --json | jq '.objects'
112
+
113
+ # Moderate user-uploaded content
114
+ aiception nudity detect https://upload.example.com/user123/photo.jpg
115
+
116
+ # Batch analyze images
117
+ for url in url1 url2 url3; do
118
+ aiception images analyze $url --json | jq -r '.task_id'
119
+ done
120
+ ```
121
+
122
+ ## License
123
+
124
+ MIT
125
+
126
+ ---
127
+
128
+ Part of the [KTMCP CLI](https://killthemcp.com) project — replacing MCPs with simple, composable CLIs.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../src/index.js';
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@ktmcp-cli/aiception",
3
+ "version": "1.0.0",
4
+ "description": "Production-ready CLI for AIception Image Recognition API - Kill The MCP",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "aiception": "bin/aiception.js"
9
+ },
10
+ "keywords": ["aiception", "image-recognition", "ai", "cli", "api", "ktmcp"],
11
+ "author": "KTMCP",
12
+ "license": "MIT",
13
+ "dependencies": {
14
+ "commander": "^12.0.0",
15
+ "axios": "^1.6.7",
16
+ "chalk": "^5.3.0",
17
+ "ora": "^8.0.1",
18
+ "conf": "^12.0.0"
19
+ },
20
+ "engines": { "node": ">=18.0.0" },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/ktmcp-cli/aiception.git"
24
+ },
25
+ "homepage": "https://killthemcp.com/aiception-cli",
26
+ "bugs": { "url": "https://github.com/ktmcp-cli/aiception/issues" }
27
+ }
package/src/api.js ADDED
@@ -0,0 +1,90 @@
1
+ import axios from 'axios';
2
+ import { getConfig } from './config.js';
3
+
4
+ const BASE_URL = 'https://aiception.com/api/v2.1';
5
+
6
+ function getAuth() {
7
+ const username = getConfig('username');
8
+ const password = getConfig('password');
9
+ if (!username || !password) {
10
+ throw new Error('Credentials not configured. Run: aiception config set username YOUR_USER and aiception config set password YOUR_PASS');
11
+ }
12
+ return { username, password };
13
+ }
14
+
15
+ function handleApiError(error) {
16
+ if (error.response) {
17
+ const status = error.response.status;
18
+ const data = error.response.data;
19
+ if (status === 401 || status === 403) {
20
+ throw new Error('Authentication failed. Check your username/password credentials.');
21
+ } else if (status === 429) {
22
+ throw new Error('Rate limit exceeded. Please wait before retrying.');
23
+ } else if (status === 404) {
24
+ throw new Error('Resource not found.');
25
+ } else {
26
+ const message = data?.error || data?.message || JSON.stringify(data);
27
+ throw new Error(`API Error (${status}): ${message}`);
28
+ }
29
+ } else if (error.request) {
30
+ throw new Error('No response from AIception API. Check your internet connection.');
31
+ } else {
32
+ throw error;
33
+ }
34
+ }
35
+
36
+ async function apiGet(path) {
37
+ const auth = getAuth();
38
+ try {
39
+ const response = await axios.get(`${BASE_URL}${path}`, { auth });
40
+ return response.data;
41
+ } catch (error) {
42
+ handleApiError(error);
43
+ }
44
+ }
45
+
46
+ async function apiPost(path, data = {}) {
47
+ const auth = getAuth();
48
+ try {
49
+ const response = await axios.post(`${BASE_URL}${path}`, data, { auth });
50
+ return response.data;
51
+ } catch (error) {
52
+ handleApiError(error);
53
+ }
54
+ }
55
+
56
+ // ============================================================
57
+ // IMAGES
58
+ // ============================================================
59
+
60
+ export async function analyzeImage(imageUrl) {
61
+ return await apiPost('/image_understanding', { image_url: imageUrl });
62
+ }
63
+
64
+ export async function classifyImage(imageUrl) {
65
+ return await apiPost('/classify', { image_url: imageUrl });
66
+ }
67
+
68
+ export async function detectObjects(imageUrl) {
69
+ return await apiPost('/detect_objects', { image_url: imageUrl });
70
+ }
71
+
72
+ // ============================================================
73
+ // NUDITY DETECTION
74
+ // ============================================================
75
+
76
+ export async function detectNudity(imageUrl) {
77
+ return await apiPost('/detect_nudity', { image_url: imageUrl });
78
+ }
79
+
80
+ // ============================================================
81
+ // TASKS
82
+ // ============================================================
83
+
84
+ export async function getTask(taskId) {
85
+ return await apiGet(`/task/${taskId}`);
86
+ }
87
+
88
+ export async function listTasks() {
89
+ return await apiGet('/tasks');
90
+ }
package/src/config.js ADDED
@@ -0,0 +1,19 @@
1
+ import Conf from 'conf';
2
+
3
+ const config = new Conf({ projectName: '@ktmcp-cli/aiception' });
4
+
5
+ export function getConfig(key) {
6
+ return config.get(key);
7
+ }
8
+
9
+ export function setConfig(key, value) {
10
+ config.set(key, value);
11
+ }
12
+
13
+ export function isConfigured() {
14
+ return !!config.get('username') && !!config.get('password');
15
+ }
16
+
17
+ export function getAllConfig() {
18
+ return config.store;
19
+ }
package/src/index.js ADDED
@@ -0,0 +1,333 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { getConfig, setConfig, isConfigured, getAllConfig } from './config.js';
5
+ import {
6
+ analyzeImage,
7
+ classifyImage,
8
+ detectObjects,
9
+ detectNudity,
10
+ getTask,
11
+ listTasks
12
+ } from './api.js';
13
+
14
+ const program = new Command();
15
+
16
+ // ============================================================
17
+ // Helpers
18
+ // ============================================================
19
+
20
+ function printSuccess(message) {
21
+ console.log(chalk.green('✓') + ' ' + message);
22
+ }
23
+
24
+ function printError(message) {
25
+ console.error(chalk.red('✗') + ' ' + message);
26
+ }
27
+
28
+ function printTable(data, columns) {
29
+ if (!data || data.length === 0) {
30
+ console.log(chalk.yellow('No results found.'));
31
+ return;
32
+ }
33
+ const widths = {};
34
+ columns.forEach(col => {
35
+ widths[col.key] = col.label.length;
36
+ data.forEach(row => {
37
+ const val = String(col.format ? col.format(row[col.key], row) : (row[col.key] ?? ''));
38
+ if (val.length > widths[col.key]) widths[col.key] = val.length;
39
+ });
40
+ widths[col.key] = Math.min(widths[col.key], 40);
41
+ });
42
+ const header = columns.map(col => col.label.padEnd(widths[col.key])).join(' ');
43
+ console.log(chalk.bold(chalk.cyan(header)));
44
+ console.log(chalk.dim('─'.repeat(header.length)));
45
+ data.forEach(row => {
46
+ const line = columns.map(col => {
47
+ const val = String(col.format ? col.format(row[col.key], row) : (row[col.key] ?? ''));
48
+ return val.substring(0, widths[col.key]).padEnd(widths[col.key]);
49
+ }).join(' ');
50
+ console.log(line);
51
+ });
52
+ console.log(chalk.dim(`\n${data.length} result(s)`));
53
+ }
54
+
55
+ function printJson(data) {
56
+ console.log(JSON.stringify(data, null, 2));
57
+ }
58
+
59
+ async function withSpinner(message, fn) {
60
+ const spinner = ora(message).start();
61
+ try {
62
+ const result = await fn();
63
+ spinner.stop();
64
+ return result;
65
+ } catch (error) {
66
+ spinner.stop();
67
+ throw error;
68
+ }
69
+ }
70
+
71
+ function requireAuth() {
72
+ if (!isConfigured()) {
73
+ printError('AIception credentials not configured.');
74
+ console.log('\nRun the following to configure:');
75
+ console.log(chalk.cyan(' aiception config set --username YOUR_USERNAME --password YOUR_PASSWORD'));
76
+ process.exit(1);
77
+ }
78
+ }
79
+
80
+ // ============================================================
81
+ // Program metadata
82
+ // ============================================================
83
+
84
+ program
85
+ .name('aiception')
86
+ .description(chalk.bold('AIception CLI') + ' - AI image recognition from your terminal')
87
+ .version('1.0.0');
88
+
89
+ // ============================================================
90
+ // CONFIG
91
+ // ============================================================
92
+
93
+ const configCmd = program.command('config').description('Manage CLI configuration');
94
+
95
+ configCmd
96
+ .command('set')
97
+ .description('Set configuration values')
98
+ .option('--username <user>', 'AIception API username')
99
+ .option('--password <pass>', 'AIception API password')
100
+ .action((options) => {
101
+ let changed = false;
102
+ if (options.username) {
103
+ setConfig('username', options.username);
104
+ printSuccess('Username set');
105
+ changed = true;
106
+ }
107
+ if (options.password) {
108
+ setConfig('password', options.password);
109
+ printSuccess('Password set');
110
+ changed = true;
111
+ }
112
+ if (!changed) {
113
+ printError('No options provided. Use --username and/or --password');
114
+ }
115
+ });
116
+
117
+ configCmd
118
+ .command('get')
119
+ .description('Get a configuration value')
120
+ .argument('<key>', 'Config key to retrieve')
121
+ .action((key) => {
122
+ const value = getConfig(key);
123
+ if (value === undefined || value === null || value === '') {
124
+ console.log(chalk.yellow(`${key}: not set`));
125
+ } else {
126
+ const masked = key.toLowerCase().includes('pass') ? '*'.repeat(8) : value;
127
+ console.log(`${key}: ${chalk.green(masked)}`);
128
+ }
129
+ });
130
+
131
+ configCmd
132
+ .command('list')
133
+ .description('List all configuration values')
134
+ .action(() => {
135
+ const all = getAllConfig();
136
+ console.log(chalk.bold('\nAIception CLI Configuration\n'));
137
+ console.log('Username:', all.username ? chalk.green(all.username) : chalk.red('not set'));
138
+ console.log('Password:', all.password ? chalk.green('*'.repeat(8)) : chalk.red('not set'));
139
+ console.log('');
140
+ });
141
+
142
+ // ============================================================
143
+ // IMAGES
144
+ // ============================================================
145
+
146
+ const imagesCmd = program.command('images').description('Image analysis and recognition');
147
+
148
+ imagesCmd
149
+ .command('analyze <url>')
150
+ .description('Analyze an image and get a description of its content')
151
+ .option('--json', 'Output as JSON')
152
+ .action(async (url, options) => {
153
+ requireAuth();
154
+ try {
155
+ const result = await withSpinner('Analyzing image...', () => analyzeImage(url));
156
+ if (options.json) { printJson(result); return; }
157
+ console.log(chalk.bold('\nImage Analysis\n'));
158
+ console.log('Image URL:', chalk.cyan(url));
159
+ if (result.task_id || result.id) {
160
+ console.log('Task ID: ', chalk.yellow(result.task_id || result.id));
161
+ console.log('Status: ', result.status || 'processing');
162
+ console.log('\nNote: Use', chalk.cyan(`aiception tasks get ${result.task_id || result.id}`), 'to retrieve results');
163
+ } else if (result.result || result.description) {
164
+ console.log('Result: ', result.result || result.description);
165
+ } else {
166
+ printJson(result);
167
+ }
168
+ console.log('');
169
+ } catch (error) {
170
+ printError(error.message);
171
+ process.exit(1);
172
+ }
173
+ });
174
+
175
+ imagesCmd
176
+ .command('classify <url>')
177
+ .description('Classify an image into categories')
178
+ .option('--json', 'Output as JSON')
179
+ .action(async (url, options) => {
180
+ requireAuth();
181
+ try {
182
+ const result = await withSpinner('Classifying image...', () => classifyImage(url));
183
+ if (options.json) { printJson(result); return; }
184
+ console.log(chalk.bold('\nImage Classification\n'));
185
+ console.log('Image URL:', chalk.cyan(url));
186
+ if (result.task_id || result.id) {
187
+ console.log('Task ID: ', chalk.yellow(result.task_id || result.id));
188
+ console.log('Status: ', result.status || 'processing');
189
+ console.log('\nNote: Use', chalk.cyan(`aiception tasks get ${result.task_id || result.id}`), 'to retrieve results');
190
+ } else if (result.predictions || result.classes) {
191
+ const preds = result.predictions || result.classes || [];
192
+ const rows = Array.isArray(preds) ? preds : Object.entries(preds).map(([k, v]) => ({ label: k, confidence: v }));
193
+ printTable(rows, [
194
+ { key: 'label', label: 'Class' },
195
+ { key: 'confidence', label: 'Confidence', format: (v) => v !== undefined ? `${(parseFloat(v) * 100).toFixed(1)}%` : 'N/A' }
196
+ ]);
197
+ } else {
198
+ printJson(result);
199
+ }
200
+ console.log('');
201
+ } catch (error) {
202
+ printError(error.message);
203
+ process.exit(1);
204
+ }
205
+ });
206
+
207
+ imagesCmd
208
+ .command('detect-objects <url>')
209
+ .description('Detect and locate objects within an image')
210
+ .option('--json', 'Output as JSON')
211
+ .action(async (url, options) => {
212
+ requireAuth();
213
+ try {
214
+ const result = await withSpinner('Detecting objects...', () => detectObjects(url));
215
+ if (options.json) { printJson(result); return; }
216
+ console.log(chalk.bold('\nObject Detection\n'));
217
+ console.log('Image URL:', chalk.cyan(url));
218
+ if (result.task_id || result.id) {
219
+ console.log('Task ID: ', chalk.yellow(result.task_id || result.id));
220
+ console.log('Status: ', result.status || 'processing');
221
+ console.log('\nNote: Use', chalk.cyan(`aiception tasks get ${result.task_id || result.id}`), 'to retrieve results');
222
+ } else if (result.objects || result.detections) {
223
+ const objects = result.objects || result.detections || [];
224
+ printTable(objects, [
225
+ { key: 'label', label: 'Object' },
226
+ { key: 'confidence', label: 'Confidence', format: (v) => v !== undefined ? `${(parseFloat(v) * 100).toFixed(1)}%` : 'N/A' },
227
+ { key: 'bbox', label: 'Bounding Box', format: (v) => v ? JSON.stringify(v) : 'N/A' }
228
+ ]);
229
+ } else {
230
+ printJson(result);
231
+ }
232
+ console.log('');
233
+ } catch (error) {
234
+ printError(error.message);
235
+ process.exit(1);
236
+ }
237
+ });
238
+
239
+ // ============================================================
240
+ // TASKS
241
+ // ============================================================
242
+
243
+ const tasksCmd = program.command('tasks').description('Manage async processing tasks');
244
+
245
+ tasksCmd
246
+ .command('get <task-id>')
247
+ .description('Get the result of an async processing task')
248
+ .option('--json', 'Output as JSON')
249
+ .action(async (taskId, options) => {
250
+ requireAuth();
251
+ try {
252
+ const task = await withSpinner(`Fetching task ${taskId}...`, () => getTask(taskId));
253
+ if (options.json) { printJson(task); return; }
254
+ console.log(chalk.bold('\nTask Details\n'));
255
+ console.log('Task ID: ', chalk.cyan(taskId));
256
+ console.log('Status: ', task.status === 'completed' ? chalk.green(task.status) : chalk.yellow(task.status || 'unknown'));
257
+ if (task.result) {
258
+ console.log('Result: ', JSON.stringify(task.result, null, 2));
259
+ }
260
+ if (task.created_at) console.log('Created: ', task.created_at);
261
+ console.log('');
262
+ } catch (error) {
263
+ printError(error.message);
264
+ process.exit(1);
265
+ }
266
+ });
267
+
268
+ tasksCmd
269
+ .command('list')
270
+ .description('List recent processing tasks')
271
+ .option('--json', 'Output as JSON')
272
+ .action(async (options) => {
273
+ requireAuth();
274
+ try {
275
+ const tasks = await withSpinner('Fetching tasks...', () => listTasks());
276
+ if (options.json) { printJson(tasks); return; }
277
+ const list = Array.isArray(tasks) ? tasks : (tasks?.tasks || tasks?.data || []);
278
+ printTable(list, [
279
+ { key: 'id', label: 'Task ID' },
280
+ { key: 'status', label: 'Status' },
281
+ { key: 'type', label: 'Type' },
282
+ { key: 'created_at', label: 'Created' }
283
+ ]);
284
+ } catch (error) {
285
+ printError(error.message);
286
+ process.exit(1);
287
+ }
288
+ });
289
+
290
+ // ============================================================
291
+ // NUDITY
292
+ // ============================================================
293
+
294
+ const nudityCmd = program.command('nudity').description('Nudity detection in images');
295
+
296
+ nudityCmd
297
+ .command('detect <url>')
298
+ .description('Detect nudity content in an image')
299
+ .option('--json', 'Output as JSON')
300
+ .action(async (url, options) => {
301
+ requireAuth();
302
+ try {
303
+ const result = await withSpinner('Analyzing image for nudity...', () => detectNudity(url));
304
+ if (options.json) { printJson(result); return; }
305
+ console.log(chalk.bold('\nNudity Detection Result\n'));
306
+ console.log('Image URL:', chalk.cyan(url));
307
+ if (result.task_id || result.id) {
308
+ console.log('Task ID: ', chalk.yellow(result.task_id || result.id));
309
+ console.log('Status: ', result.status || 'processing');
310
+ console.log('\nNote: Use', chalk.cyan(`aiception tasks get ${result.task_id || result.id}`), 'to retrieve results');
311
+ } else {
312
+ const hasNudity = result.nude ?? result.has_nudity ?? false;
313
+ const confidence = result.confidence ?? result.score ?? 'N/A';
314
+ console.log('Contains Nudity:', hasNudity ? chalk.red('Yes') : chalk.green('No'));
315
+ console.log('Confidence: ', confidence !== 'N/A' ? `${(parseFloat(confidence) * 100).toFixed(1)}%` : 'N/A');
316
+ if (result.raw) console.log('Raw Score: ', result.raw);
317
+ }
318
+ console.log('');
319
+ } catch (error) {
320
+ printError(error.message);
321
+ process.exit(1);
322
+ }
323
+ });
324
+
325
+ // ============================================================
326
+ // Parse
327
+ // ============================================================
328
+
329
+ program.parse(process.argv);
330
+
331
+ if (process.argv.length <= 2) {
332
+ program.help();
333
+ }