@tinify-ai/mcp-server 1.0.4 → 1.1.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/README.md +155 -36
- package/dist/api/auth.d.ts +29 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +43 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/process.d.ts +1 -1
- package/dist/api/process.d.ts.map +1 -1
- package/dist/api/process.js +16 -6
- package/dist/api/process.js.map +1 -1
- package/dist/api/upload.d.ts +1 -1
- package/dist/api/upload.d.ts.map +1 -1
- package/dist/api/upload.js +2 -5
- package/dist/api/upload.js.map +1 -1
- package/dist/index.js +85 -24
- package/dist/index.js.map +1 -1
- package/dist/session/manager.d.ts +6 -0
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +42 -7
- package/dist/session/manager.js.map +1 -1
- package/dist/tools/login.d.ts +2 -0
- package/dist/tools/login.d.ts.map +1 -0
- package/dist/tools/login.js +64 -0
- package/dist/tools/login.js.map +1 -0
- package/dist/tools/logout.d.ts +2 -0
- package/dist/tools/logout.d.ts.map +1 -0
- package/dist/tools/logout.js +20 -0
- package/dist/tools/logout.js.map +1 -0
- package/dist/tools/optimize.d.ts +0 -1
- package/dist/tools/optimize.d.ts.map +1 -1
- package/dist/tools/optimize.js +14 -9
- package/dist/tools/optimize.js.map +1 -1
- package/dist/tools/status.d.ts +2 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +41 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/upgrade.d.ts +2 -0
- package/dist/tools/upgrade.d.ts.map +1 -0
- package/dist/tools/upgrade.js +10 -0
- package/dist/tools/upgrade.js.map +1 -0
- package/dist/utils/browser.d.ts +2 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +21 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/output.d.ts +1 -0
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +7 -4
- package/dist/utils/output.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,48 +86,86 @@ Edit `~/.codeium/windsurf/mcp_config.json`:
|
|
|
86
86
|
|
|
87
87
|
## Tool: `optimize_image`
|
|
88
88
|
|
|
89
|
-
Optimizes an image with smart
|
|
89
|
+
Optimizes an image with smart lossy compression (typically 60-80% size reduction), optional resize/upscale/format conversion, and AI-generated SEO metadata. Accepts absolute local file paths or remote URLs.
|
|
90
90
|
|
|
91
91
|
### Parameters
|
|
92
92
|
|
|
93
93
|
| Parameter | Type | Required | Default | Description |
|
|
94
94
|
|-----------|------|----------|---------|-------------|
|
|
95
|
-
| `input` | string | Yes | — |
|
|
96
|
-
| `output_path` | string | No | auto |
|
|
97
|
-
| `output_format` | string | No | original |
|
|
95
|
+
| `input` | string | Yes | — | Absolute local file path or remote URL |
|
|
96
|
+
| `output_path` | string | No | auto | File path or directory (ending in `/`). If omitted: saves next to original with SEO slug or `.tinified` suffix |
|
|
97
|
+
| `output_format` | string | No | original | `jpeg`, `png`, `webp`, or `original` |
|
|
98
98
|
| `output_width_px` | int | No | — | Target width in pixels |
|
|
99
99
|
| `output_height_px` | int | No | — | Target height in pixels |
|
|
100
|
-
| `output_upscale_factor` | float | No | — |
|
|
101
|
-
| `output_resize_mode` | string | No | pad | pad or crop |
|
|
102
|
-
| `
|
|
103
|
-
|
|
100
|
+
| `output_upscale_factor` | float | No | — | AI upscale factor (e.g. 2.0, 4.0) |
|
|
101
|
+
| `output_resize_mode` | string | No | pad | `pad` (white padding) or `crop` (smart crop). Only used when both width and height are set |
|
|
102
|
+
| `output_seo_tag_gen` | bool | No | true | Generate SEO metadata and rename file to SEO slug. Costs 1 extra credit |
|
|
103
|
+
|
|
104
|
+
### Resize Behavior
|
|
105
|
+
|
|
106
|
+
| Dimensions provided | Behavior | `output_resize_mode` |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| Width only | Proportional scale | N/A |
|
|
109
|
+
| Height only | Proportional scale | N/A |
|
|
110
|
+
| Width + Height | Exact dimensions, white padding | `pad` (default) |
|
|
111
|
+
| Width + Height | Exact dimensions, smart crop | `crop` |
|
|
104
112
|
|
|
105
113
|
### Examples
|
|
106
114
|
|
|
107
|
-
**Basic
|
|
108
|
-
|
|
115
|
+
**Basic compression** — just compress, keep format and dimensions:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{ "input": "/Users/me/photos/hero.png" }
|
|
119
|
+
```
|
|
109
120
|
|
|
110
121
|
**Convert to WebP:**
|
|
111
|
-
> "Optimize hero.png as webp"
|
|
112
122
|
|
|
113
|
-
|
|
114
|
-
|
|
123
|
+
```json
|
|
124
|
+
{ "input": "/Users/me/hero.png", "output_format": "webp" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Resize proportionally** — set one dimension, the other scales:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{ "input": "/Users/me/hero.png", "output_width_px": 1200 }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Exact dimensions with padding** — white bars fill the gap:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{ "input": "/Users/me/hero.png", "output_width_px": 1080, "output_height_px": 1080 }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Exact dimensions with smart crop:**
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{ "input": "/Users/me/hero.png", "output_width_px": 1080, "output_height_px": 1080, "output_resize_mode": "crop" }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**AI upscale 4x:**
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{ "input": "/Users/me/icon.png", "output_upscale_factor": 4 }
|
|
149
|
+
```
|
|
115
150
|
|
|
116
|
-
**
|
|
117
|
-
> "Upscale logo.png by 4x"
|
|
151
|
+
**From URL, save to directory:**
|
|
118
152
|
|
|
119
|
-
|
|
120
|
-
|
|
153
|
+
```json
|
|
154
|
+
{ "input": "https://example.com/photo.jpg", "output_path": "/Users/me/assets/" }
|
|
155
|
+
```
|
|
121
156
|
|
|
122
|
-
**
|
|
123
|
-
|
|
157
|
+
**Skip SEO to save 1 credit:**
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{ "input": "/Users/me/hero.png", "output_seo_tag_gen": false }
|
|
161
|
+
```
|
|
124
162
|
|
|
125
|
-
|
|
163
|
+
### Output
|
|
126
164
|
|
|
127
|
-
Returns
|
|
165
|
+
Returns a text summary and structured metadata:
|
|
128
166
|
|
|
129
167
|
```
|
|
130
|
-
Optimized:
|
|
168
|
+
Optimized: /Users/me/photos/modern-office-workspace.webp
|
|
131
169
|
Size: 142.3 KB
|
|
132
170
|
Compression: 73%
|
|
133
171
|
Format: webp
|
|
@@ -135,7 +173,21 @@ Dimensions: 1920x1080
|
|
|
135
173
|
Alt text: Modern office workspace with laptop and coffee cup on wooden desk
|
|
136
174
|
```
|
|
137
175
|
|
|
138
|
-
Structured
|
|
176
|
+
**Structured output fields:**
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"output_path": "/Users/me/photos/modern-office-workspace.webp",
|
|
181
|
+
"output_size_bytes": 145715,
|
|
182
|
+
"output_width_px": 1920,
|
|
183
|
+
"output_height_px": 1080,
|
|
184
|
+
"output_format": "webp",
|
|
185
|
+
"compression_ratio": 0.27,
|
|
186
|
+
"seo_alt_text": "Modern office workspace with laptop and coffee cup on wooden desk",
|
|
187
|
+
"seo_keywords": ["office", "workspace", "laptop", "desk", "modern"],
|
|
188
|
+
"seo_filename": "modern-office-workspace"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
139
191
|
|
|
140
192
|
## Supported Formats
|
|
141
193
|
|
|
@@ -158,27 +210,84 @@ Max file size: 50 MB.
|
|
|
158
210
|
```
|
|
159
211
|
Local file or URL
|
|
160
212
|
→ Upload to Tinify API
|
|
161
|
-
→ Smart compression (
|
|
213
|
+
→ Smart compression (lossy, typically 60-80% reduction)
|
|
162
214
|
→ AI SEO tag generation (alt text, keywords, filename)
|
|
163
215
|
→ Optional: resize, upscale, format conversion
|
|
164
216
|
→ Download optimized file
|
|
165
|
-
→ Save
|
|
217
|
+
→ Save with SEO filename slug (or .tinified suffix if SEO disabled)
|
|
166
218
|
```
|
|
167
219
|
|
|
168
220
|
All processing happens server-side via the [Tinify API](https://tinify.ai). The MCP server is a thin client that orchestrates the pipeline.
|
|
169
221
|
|
|
170
222
|
## Credits
|
|
171
223
|
|
|
172
|
-
| | Free
|
|
173
|
-
|
|
174
|
-
| Credits/day | 20 |
|
|
175
|
-
| Images/day | 5
|
|
176
|
-
| Cost per image |
|
|
177
|
-
| Signup required | No |
|
|
224
|
+
| | Guest | Free | Pro | Max |
|
|
225
|
+
|---|---|---|---|---|
|
|
226
|
+
| Credits/day or month | 20/day | 50/day | 3,000/month | 10,000/month |
|
|
227
|
+
| Images/day (default settings) | ~5 | ~12 | ~750 | ~2,500 |
|
|
228
|
+
| Cost per image | 3 credits + 1 SEO | same | same | same |
|
|
229
|
+
| Signup required | No | Free signup | Paid | Paid |
|
|
230
|
+
|
|
231
|
+
Session data is stored locally at `~/.tinify/session.json` and persists across invocations.
|
|
232
|
+
|
|
233
|
+
## Account & Credits
|
|
234
|
+
|
|
235
|
+
Log in to unlock more credits and share them across the web app and MCP server:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
Use the login tool to sign in.
|
|
239
|
+
Use the status tool to check your current credits.
|
|
240
|
+
Use the upgrade tool to open the pricing page.
|
|
241
|
+
Use the logout tool to sign out.
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### login
|
|
245
|
+
|
|
246
|
+
Opens a browser window to complete login (Google, Facebook, or email/magic link). After approval, your account is linked and credits are shared with the web app.
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
Login complete: user@example.com (Pro tier, 2,850 of 3,000 credits remaining)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### status
|
|
253
|
+
|
|
254
|
+
Check your current account status and credits before batch processing:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
Logged in as user@example.com (Pro tier)
|
|
258
|
+
Credits: 2,850 of 3,000 remaining
|
|
259
|
+
Resets: 03/01/2026, 12:00 AM PST
|
|
260
|
+
```
|
|
178
261
|
|
|
179
|
-
|
|
262
|
+
### logout
|
|
180
263
|
|
|
181
|
-
|
|
264
|
+
Revokes the session and reverts to guest mode (20 credits/day).
|
|
265
|
+
|
|
266
|
+
### upgrade
|
|
267
|
+
|
|
268
|
+
Opens [tinify.ai/pricing](https://tinify.ai/pricing) in your browser.
|
|
269
|
+
|
|
270
|
+
## Tips for AI Agents
|
|
271
|
+
|
|
272
|
+
Paste this into your `CLAUDE.md` or system prompt to help agents use the tool effectively:
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
## Tinify MCP
|
|
276
|
+
|
|
277
|
+
Tools: optimize_image, login, logout, status, upgrade
|
|
278
|
+
|
|
279
|
+
- Use status to check credits before batch processing
|
|
280
|
+
- Each optimize_image call costs 3 credits + 1 if SEO enabled (default)
|
|
281
|
+
- Guest: 20 credits/day. Free account: 50/day. Pro: 3,000/month.
|
|
282
|
+
- Always use absolute file paths, not relative.
|
|
283
|
+
- Set only width OR height for proportional resize. Set both for exact dimensions.
|
|
284
|
+
- When both dimensions are set, use output_resize_mode: "crop" for photos, "pad" for logos/icons.
|
|
285
|
+
- output_seo_tag_gen (default true) renames the file to an SEO slug and generates alt text + keywords.
|
|
286
|
+
- Set output_seo_tag_gen: false to save 1 credit when SEO metadata is not needed.
|
|
287
|
+
- HEIC, TIFF, BMP inputs are auto-converted to JPG.
|
|
288
|
+
- For batch processing, call optimize_image once per file.
|
|
289
|
+
- If credits run out, use login to sign in or upgrade to open pricing.
|
|
290
|
+
```
|
|
182
291
|
|
|
183
292
|
## Troubleshooting
|
|
184
293
|
|
|
@@ -188,9 +297,19 @@ Need more credits? See plans at [tinify.ai](https://tinify.ai/#pricing).
|
|
|
188
297
|
- Try running directly: `npx -y @tinify-ai/mcp-server@latest` (should start without errors)
|
|
189
298
|
|
|
190
299
|
**"Insufficient credits" error:**
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
-
|
|
300
|
+
- Use the `status` tool to check remaining credits
|
|
301
|
+
- Use the `login` tool to sign in for more credits (free accounts get 50/day)
|
|
302
|
+
- Use the `upgrade` tool to see paid plans (Pro: 3,000/month, Max: 10,000/month)
|
|
303
|
+
- Disable SEO tags (`output_seo_tag_gen: false`) to reduce cost to 3 credits/image
|
|
304
|
+
|
|
305
|
+
**Login browser window doesn't open:**
|
|
306
|
+
- Open this URL manually: `https://tinify.ai/mcp/authorize` and enter the code shown in the terminal
|
|
307
|
+
- Ensure a browser is installed and accessible
|
|
308
|
+
|
|
309
|
+
**Session token issues:**
|
|
310
|
+
- Session data is stored at `~/.tinify/session.json`
|
|
311
|
+
- Delete this file to reset and start fresh
|
|
312
|
+
- Use `logout` then `login` to re-authenticate
|
|
194
313
|
|
|
195
314
|
**File not found:**
|
|
196
315
|
- Use absolute paths for local files
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface DeviceCodeResponse {
|
|
2
|
+
device_code: string;
|
|
3
|
+
user_code: string;
|
|
4
|
+
verify_url: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TokenPollResponse {
|
|
7
|
+
status: "pending" | "approved" | "expired" | "denied";
|
|
8
|
+
mcp_token?: string;
|
|
9
|
+
user?: {
|
|
10
|
+
email: string;
|
|
11
|
+
tier: string;
|
|
12
|
+
credits_remaining: number;
|
|
13
|
+
credits_limit: number;
|
|
14
|
+
credits_reset_at: string | null;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface AccountStatus {
|
|
18
|
+
logged_in: boolean;
|
|
19
|
+
email?: string;
|
|
20
|
+
tier: string;
|
|
21
|
+
credits_remaining: number;
|
|
22
|
+
credits_limit: number;
|
|
23
|
+
credits_reset_at: string | null;
|
|
24
|
+
}
|
|
25
|
+
export declare function requestDeviceCode(baseUrl: string): Promise<DeviceCodeResponse>;
|
|
26
|
+
export declare function pollForToken(baseUrl: string, deviceCode: string): Promise<TokenPollResponse>;
|
|
27
|
+
export declare function revokeToken(baseUrl: string, mcpToken: string): Promise<void>;
|
|
28
|
+
export declare function getAccountStatus(baseUrl: string, mcpToken: string | null, sessionToken: string | null): Promise<AccountStatus>;
|
|
29
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,iBAAiB,EAAE,MAAM,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;KACjC,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAOpF;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAOlG;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASlF;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,OAAO,CAAC,aAAa,CAAC,CAaxB"}
|
package/dist/api/auth.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ApiError } from "./client.js";
|
|
2
|
+
export async function requestDeviceCode(baseUrl) {
|
|
3
|
+
const response = await fetch(`${baseUrl}/mcp/auth/device-code`, { method: "POST" });
|
|
4
|
+
if (!response.ok) {
|
|
5
|
+
const body = await response.json().catch(() => ({}));
|
|
6
|
+
throw new ApiError(body.detail || "Failed to create device code", response.status);
|
|
7
|
+
}
|
|
8
|
+
return response.json();
|
|
9
|
+
}
|
|
10
|
+
export async function pollForToken(baseUrl, deviceCode) {
|
|
11
|
+
const response = await fetch(`${baseUrl}/mcp/auth/token?device_code=${deviceCode}`);
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
const body = await response.json().catch(() => ({}));
|
|
14
|
+
throw new ApiError(body.detail || "Token poll failed", response.status);
|
|
15
|
+
}
|
|
16
|
+
return response.json();
|
|
17
|
+
}
|
|
18
|
+
export async function revokeToken(baseUrl, mcpToken) {
|
|
19
|
+
const response = await fetch(`${baseUrl}/mcp/auth/revoke`, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { Authorization: `Bearer ${mcpToken}` },
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
const body = await response.json().catch(() => ({}));
|
|
25
|
+
throw new ApiError(body.detail || "Token revocation failed", response.status);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function getAccountStatus(baseUrl, mcpToken, sessionToken) {
|
|
29
|
+
const headers = {};
|
|
30
|
+
if (mcpToken) {
|
|
31
|
+
headers.Authorization = `Bearer ${mcpToken}`;
|
|
32
|
+
}
|
|
33
|
+
else if (sessionToken) {
|
|
34
|
+
headers["X-Session-Token"] = sessionToken;
|
|
35
|
+
}
|
|
36
|
+
const response = await fetch(`${baseUrl}/mcp/auth/status`, { headers });
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
const body = await response.json().catch(() => ({}));
|
|
39
|
+
throw new ApiError(body.detail || "Status check failed", response.status);
|
|
40
|
+
}
|
|
41
|
+
return response.json();
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA6BvC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAe;IACrD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,uBAAuB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,8BAA8B,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,UAAkB;IACpE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,+BAA+B,UAAU,EAAE,CAAC,CAAC;IACpF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,QAAgB;IACjE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,kBAAkB,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,QAAQ,EAAE,EAAE;KACjD,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAe,EACf,QAAuB,EACvB,YAA2B;IAE3B,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,aAAa,GAAG,UAAU,QAAQ,EAAE,CAAC;IAC/C,CAAC;SAAM,IAAI,YAAY,EAAE,CAAC;QACxB,OAAO,CAAC,iBAAiB,CAAC,GAAG,YAAY,CAAC;IAC5C,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,kBAAkB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACxE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
package/dist/api/process.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/api/process.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/api/process.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7D,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAiDrF"}
|
package/dist/api/process.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { ApiError } from "./client.js";
|
|
2
2
|
export async function triggerProcessing(params) {
|
|
3
|
-
const { baseUrl, tempFileIds, settings,
|
|
3
|
+
const { baseUrl, tempFileIds, settings, authHeaders } = params;
|
|
4
4
|
const headers = {
|
|
5
5
|
"Content-Type": "application/json",
|
|
6
|
+
...authHeaders,
|
|
6
7
|
};
|
|
7
|
-
if (sessionToken) {
|
|
8
|
-
headers["X-Session-Token"] = sessionToken;
|
|
9
|
-
}
|
|
10
8
|
const response = await fetch(`${baseUrl}/auto`, {
|
|
11
9
|
method: "POST",
|
|
12
10
|
headers,
|
|
@@ -18,8 +16,20 @@ export async function triggerProcessing(params) {
|
|
|
18
16
|
if (!response.ok) {
|
|
19
17
|
const body = await response.json().catch(() => ({}));
|
|
20
18
|
if (response.status === 429) {
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
if (body.is_guest) {
|
|
20
|
+
throw new ApiError(`You've used all ${body.credits_limit || 20} free daily credits. ` +
|
|
21
|
+
`Log in for more credits (free = 50/day, Pro = 3,000/month). ` +
|
|
22
|
+
`Use the login tool to sign in, or wait until credits reset.`, 429, body.detail);
|
|
23
|
+
}
|
|
24
|
+
else if (body.tier) {
|
|
25
|
+
throw new ApiError(`You've used all ${body.credits_limit} credits for this period (${body.tier} tier). ` +
|
|
26
|
+
`Upgrade for more credits, or wait until they reset.`, 429, body.detail);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Fallback for old backend format
|
|
30
|
+
const remaining = response.headers.get("X-RateLimit-Remaining") ?? "0";
|
|
31
|
+
throw new ApiError(`Insufficient credits. ${remaining} credits remaining.`, 429, body.detail);
|
|
32
|
+
}
|
|
23
33
|
}
|
|
24
34
|
throw new ApiError(body.detail || "Processing failed", response.status, body.detail);
|
|
25
35
|
}
|
package/dist/api/process.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/api/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiCvC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAqB;IAC3D,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/api/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiCvC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAqB;IAC3D,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAE/D,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,GAAG,WAAW;KACf,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE;QAC9C,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,aAAa,EAAE,WAAW;YAC1B,QAAQ;SACT,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,MAAM,IAAI,QAAQ,CAChB,mBAAmB,IAAI,CAAC,aAAa,IAAI,EAAE,uBAAuB;oBAClE,8DAA8D;oBAC9D,6DAA6D,EAC7D,GAAG,EACH,IAAI,CAAC,MAAM,CACZ,CAAC;YACJ,CAAC;iBAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrB,MAAM,IAAI,QAAQ,CAChB,mBAAmB,IAAI,CAAC,aAAa,6BAA6B,IAAI,CAAC,IAAI,UAAU;oBACrF,qDAAqD,EACrD,GAAG,EACH,IAAI,CAAC,MAAM,CACZ,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,kCAAkC;gBAClC,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,GAAG,CAAC;gBACvE,MAAM,IAAI,QAAQ,CAChB,yBAAyB,SAAS,qBAAqB,EACvD,GAAG,EACH,IAAI,CAAC,MAAM,CACZ,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,mBAAmB,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACvF,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
package/dist/api/upload.d.ts
CHANGED
package/dist/api/upload.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED,UAAU,YAAY;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAsC5E"}
|
package/dist/api/upload.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { ApiError } from "./client.js";
|
|
2
2
|
export async function uploadFile(params) {
|
|
3
|
-
const { baseUrl, fileBuffer, filename,
|
|
3
|
+
const { baseUrl, fileBuffer, filename, authHeaders } = params;
|
|
4
4
|
const formData = new FormData();
|
|
5
5
|
formData.append("file", new Blob([fileBuffer.buffer.slice(fileBuffer.byteOffset, fileBuffer.byteOffset + fileBuffer.byteLength)]), filename);
|
|
6
|
-
const headers = {};
|
|
7
|
-
if (sessionToken) {
|
|
8
|
-
headers["X-Session-Token"] = sessionToken;
|
|
9
|
-
}
|
|
6
|
+
const headers = { ...authHeaders };
|
|
10
7
|
const response = await fetch(`${baseUrl}/upload`, {
|
|
11
8
|
method: "POST",
|
|
12
9
|
headers,
|
package/dist/api/upload.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiBvC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAoB;IACnD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiBvC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAoB;IACnD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAE9D,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,QAAQ,CAAC,MAAM,CACb,MAAM,EACN,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU,CAAgB,CAAC,CAAC,EACxH,QAAQ,CACT,CAAC;IAEF,MAAM,OAAO,GAA2B,EAAE,GAAG,WAAW,EAAE,CAAC;IAE3D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,SAAS,EAAE;QAChD,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,QAAQ;KACf,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC,kCAAkC,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,QAAQ,CAChB,6DAA6D,EAC7D,QAAQ,CAAC,MAAM,EACf,IAAI,CAAC,MAAM,CACZ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,QAAQ,CAChB,IAAI,CAAC,MAAM,IAAI,eAAe,EAC9B,QAAQ,CAAC,MAAM,EACf,IAAI,CAAC,MAAM,CACZ,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,24 +3,30 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import * as z from "zod";
|
|
5
5
|
import { optimizeImage } from "./tools/optimize.js";
|
|
6
|
+
import { loginTool } from "./tools/login.js";
|
|
7
|
+
import { logoutTool } from "./tools/logout.js";
|
|
8
|
+
import { statusTool } from "./tools/status.js";
|
|
9
|
+
import { upgradeTool } from "./tools/upgrade.js";
|
|
6
10
|
import { formatErrorForMcp } from "./errors.js";
|
|
7
11
|
const server = new McpServer({
|
|
8
12
|
name: "tinify",
|
|
9
|
-
version: "1.
|
|
13
|
+
version: "1.1.0",
|
|
10
14
|
});
|
|
11
15
|
server.registerTool("optimize_image", {
|
|
12
16
|
title: "Optimize Image",
|
|
13
|
-
description: "Optimize an image
|
|
14
|
-
"Accepts local file paths or remote URLs.
|
|
17
|
+
description: "Optimize an image: smart lossy compression (typically 60-80% size reduction), optional resize/upscale/format conversion, and AI-generated SEO metadata. " +
|
|
18
|
+
"Accepts absolute local file paths or remote URLs. Supported formats: JPG, PNG, WebP, AVIF, HEIC, TIFF, BMP (max 50 MB). " +
|
|
19
|
+
"Each call costs 3 credits + 1 if SEO tags enabled. Free tier: 20 credits/day, no signup. " +
|
|
20
|
+
"Log in with the login tool for more credits. Use status tool to check remaining credits before batch processing.",
|
|
15
21
|
inputSchema: {
|
|
16
22
|
input: z
|
|
17
23
|
.string()
|
|
18
|
-
.describe("
|
|
24
|
+
.describe("Absolute local file path or remote URL of the image to optimize. Supported: JPG, PNG, WebP, AVIF, HEIC, TIFF, BMP (max 50 MB)."),
|
|
19
25
|
output_path: z
|
|
20
26
|
.string()
|
|
21
27
|
.optional()
|
|
22
|
-
.describe("Where to save
|
|
23
|
-
"with
|
|
28
|
+
.describe("Where to save. Accepts a file path (/tmp/out.webp) or directory ending in / (/tmp/images/). " +
|
|
29
|
+
"If omitted: saves next to original, named with SEO slug when SEO is enabled or .tinified suffix otherwise. URLs save to current working directory."),
|
|
24
30
|
output_format: z
|
|
25
31
|
.enum(["original", "jpeg", "png", "webp"])
|
|
26
32
|
.optional()
|
|
@@ -30,13 +36,13 @@ server.registerTool("optimize_image", {
|
|
|
30
36
|
.int()
|
|
31
37
|
.positive()
|
|
32
38
|
.optional()
|
|
33
|
-
.describe("Target width in pixels.
|
|
39
|
+
.describe("Target width in pixels. Set only width for proportional resize. Set both width and height for exact output dimensions (see output_resize_mode)."),
|
|
34
40
|
output_height_px: z
|
|
35
41
|
.number()
|
|
36
42
|
.int()
|
|
37
43
|
.positive()
|
|
38
44
|
.optional()
|
|
39
|
-
.describe("Target height in pixels.
|
|
45
|
+
.describe("Target height in pixels. Set only height for proportional resize. Set both width and height for exact output dimensions (see output_resize_mode)."),
|
|
40
46
|
output_upscale_factor: z
|
|
41
47
|
.number()
|
|
42
48
|
.min(0.1)
|
|
@@ -46,26 +52,24 @@ server.registerTool("optimize_image", {
|
|
|
46
52
|
output_resize_mode: z
|
|
47
53
|
.enum(["pad", "crop"])
|
|
48
54
|
.optional()
|
|
49
|
-
.describe("
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
.optional()
|
|
53
|
-
.describe("Maintain aspect ratio during resize. Default: true."),
|
|
55
|
+
.describe("How to handle aspect ratio mismatch when both width and height are specified. " +
|
|
56
|
+
"'pad' adds white padding (default), 'crop' uses smart cropping. " +
|
|
57
|
+
"Only applies when both output_width_px and output_height_px are set."),
|
|
54
58
|
output_seo_tag_gen: z
|
|
55
59
|
.boolean()
|
|
56
60
|
.optional()
|
|
57
|
-
.describe("Generate SEO metadata (alt text, keywords, filename). Default: true."),
|
|
61
|
+
.describe("Generate SEO metadata (alt text, keywords, filename) and rename output file to SEO slug. Costs 1 extra credit. Default: true."),
|
|
58
62
|
},
|
|
59
63
|
outputSchema: {
|
|
60
|
-
output_path: z.string(),
|
|
61
|
-
output_size_bytes: z.number(),
|
|
62
|
-
output_width_px: z.number().nullable(),
|
|
63
|
-
output_height_px: z.number().nullable(),
|
|
64
|
-
output_format: z.string().nullable(),
|
|
65
|
-
compression_ratio: z.number().nullable(),
|
|
66
|
-
seo_alt_text: z.string().nullable(),
|
|
67
|
-
seo_keywords: z.array(z.string()).nullable(),
|
|
68
|
-
seo_filename: z.string().nullable(),
|
|
64
|
+
output_path: z.string().describe("Absolute path where the optimized file was saved"),
|
|
65
|
+
output_size_bytes: z.number().describe("File size of the optimized image in bytes"),
|
|
66
|
+
output_width_px: z.number().nullable().describe("Width of the output image in pixels"),
|
|
67
|
+
output_height_px: z.number().nullable().describe("Height of the output image in pixels"),
|
|
68
|
+
output_format: z.string().nullable().describe("Output format: jpg, png, webp, or avif"),
|
|
69
|
+
compression_ratio: z.number().nullable().describe("Output-to-input size ratio, e.g. 0.35 means 65% smaller"),
|
|
70
|
+
seo_alt_text: z.string().nullable().describe("AI-generated image alt text for accessibility and SEO"),
|
|
71
|
+
seo_keywords: z.array(z.string()).nullable().describe("AI-generated keywords describing the image"),
|
|
72
|
+
seo_filename: z.string().nullable().describe("AI-generated SEO filename slug without extension"),
|
|
69
73
|
},
|
|
70
74
|
}, async (params) => {
|
|
71
75
|
try {
|
|
@@ -77,7 +81,6 @@ server.registerTool("optimize_image", {
|
|
|
77
81
|
output_height_px: params.output_height_px,
|
|
78
82
|
output_upscale_factor: params.output_upscale_factor,
|
|
79
83
|
output_resize_mode: params.output_resize_mode,
|
|
80
|
-
output_aspect_lock: params.output_aspect_lock,
|
|
81
84
|
output_seo_tag_gen: params.output_seo_tag_gen,
|
|
82
85
|
});
|
|
83
86
|
const summary = [
|
|
@@ -103,6 +106,64 @@ server.registerTool("optimize_image", {
|
|
|
103
106
|
return formatErrorForMcp(error);
|
|
104
107
|
}
|
|
105
108
|
});
|
|
109
|
+
server.registerTool("login", {
|
|
110
|
+
title: "Log In",
|
|
111
|
+
description: "Log in to your Tinify account via browser to unlock more credits. " +
|
|
112
|
+
"Opens a browser window where you complete login (Google, Facebook, or email). " +
|
|
113
|
+
"After login, MCP automatically picks up your account with shared credits across web and MCP. " +
|
|
114
|
+
"Free: 50 credits/day. Pro: 3,000/month. Max: 10,000/month.",
|
|
115
|
+
inputSchema: {},
|
|
116
|
+
}, async () => {
|
|
117
|
+
try {
|
|
118
|
+
const message = await loginTool();
|
|
119
|
+
return { content: [{ type: "text", text: message }] };
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return formatErrorForMcp(error);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
server.registerTool("logout", {
|
|
126
|
+
title: "Log Out",
|
|
127
|
+
description: "Log out of your Tinify account. Reverts to guest session (20 free credits/day). " +
|
|
128
|
+
"Your web app account is not affected.",
|
|
129
|
+
inputSchema: {},
|
|
130
|
+
}, async () => {
|
|
131
|
+
try {
|
|
132
|
+
const message = await logoutTool();
|
|
133
|
+
return { content: [{ type: "text", text: message }] };
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return formatErrorForMcp(error);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
server.registerTool("status", {
|
|
140
|
+
title: "Account Status",
|
|
141
|
+
description: "Check your Tinify account status: login state, tier, credits remaining, and credit reset time. " +
|
|
142
|
+
"Use this before batch processing to verify sufficient credits.",
|
|
143
|
+
inputSchema: {},
|
|
144
|
+
}, async () => {
|
|
145
|
+
try {
|
|
146
|
+
const message = await statusTool();
|
|
147
|
+
return { content: [{ type: "text", text: message }] };
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return formatErrorForMcp(error);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
server.registerTool("upgrade", {
|
|
154
|
+
title: "Upgrade Plan",
|
|
155
|
+
description: "Open the Tinify pricing page in your browser to upgrade your plan for more credits. " +
|
|
156
|
+
"Plans: Free (50/day), Pro (3,000/month), Max (10,000/month).",
|
|
157
|
+
inputSchema: {},
|
|
158
|
+
}, async () => {
|
|
159
|
+
try {
|
|
160
|
+
const message = await upgradeTool();
|
|
161
|
+
return { content: [{ type: "text", text: message }] };
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return formatErrorForMcp(error);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
106
167
|
const transport = new StdioServerTransport();
|
|
107
168
|
await server.connect(transport);
|
|
108
169
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;IACE,KAAK,EAAE,gBAAgB;IACvB,WAAW,EACT,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,13 @@ export declare class SessionManager {
|
|
|
2
2
|
readonly sessionDir: string;
|
|
3
3
|
private readonly sessionFile;
|
|
4
4
|
constructor(sessionDir?: string);
|
|
5
|
+
private readData;
|
|
6
|
+
private writeData;
|
|
5
7
|
getToken(): string | null;
|
|
6
8
|
saveToken(token: string): void;
|
|
9
|
+
getMcpToken(): string | null;
|
|
10
|
+
saveMcpToken(token: string, email: string, tier: string): void;
|
|
11
|
+
clearMcpToken(): void;
|
|
12
|
+
getAuthHeaders(): Record<string, string>;
|
|
7
13
|
}
|
|
8
14
|
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"AAWA,qBAAa,cAAc;IACzB,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,UAAU,CAAC,EAAE,MAAM;IAK/B,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,SAAS;IAKjB,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK9B,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAW9D,aAAa,IAAI,IAAI;IAQrB,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAUzC"}
|
package/dist/session/manager.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
2
|
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
4
|
export class SessionManager {
|
|
5
5
|
sessionDir;
|
|
6
6
|
sessionFile;
|
|
@@ -8,20 +8,55 @@ export class SessionManager {
|
|
|
8
8
|
this.sessionDir = sessionDir ?? path.join(os.homedir(), ".tinify");
|
|
9
9
|
this.sessionFile = path.join(this.sessionDir, "session.json");
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
readData() {
|
|
12
12
|
try {
|
|
13
13
|
const raw = fs.readFileSync(this.sessionFile, "utf-8");
|
|
14
|
-
|
|
15
|
-
return data.session_token ?? null;
|
|
14
|
+
return JSON.parse(raw);
|
|
16
15
|
}
|
|
17
16
|
catch {
|
|
18
17
|
return null;
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
|
-
|
|
20
|
+
writeData(data) {
|
|
22
21
|
fs.mkdirSync(this.sessionDir, { recursive: true });
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
fs.writeFileSync(this.sessionFile, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
23
|
+
}
|
|
24
|
+
getToken() {
|
|
25
|
+
return this.readData()?.session_token ?? null;
|
|
26
|
+
}
|
|
27
|
+
saveToken(token) {
|
|
28
|
+
const existing = this.readData();
|
|
29
|
+
this.writeData({ ...existing, session_token: token });
|
|
30
|
+
}
|
|
31
|
+
getMcpToken() {
|
|
32
|
+
return this.readData()?.mcp_token ?? null;
|
|
33
|
+
}
|
|
34
|
+
saveMcpToken(token, email, tier) {
|
|
35
|
+
const existing = this.readData();
|
|
36
|
+
this.writeData({
|
|
37
|
+
...existing,
|
|
38
|
+
session_token: existing?.session_token ?? "",
|
|
39
|
+
mcp_token: token,
|
|
40
|
+
user_email: email,
|
|
41
|
+
user_tier: tier,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
clearMcpToken() {
|
|
45
|
+
const existing = this.readData();
|
|
46
|
+
if (existing) {
|
|
47
|
+
const { mcp_token, user_email, user_tier, ...rest } = existing;
|
|
48
|
+
this.writeData(rest);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
getAuthHeaders() {
|
|
52
|
+
const data = this.readData();
|
|
53
|
+
if (data?.mcp_token) {
|
|
54
|
+
return { Authorization: `Bearer ${data.mcp_token}` };
|
|
55
|
+
}
|
|
56
|
+
if (data?.session_token) {
|
|
57
|
+
return { "X-Session-Token": data.session_token };
|
|
58
|
+
}
|
|
59
|
+
return {};
|
|
25
60
|
}
|
|
26
61
|
}
|
|
27
62
|
//# sourceMappingURL=manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AASlC,MAAM,OAAO,cAAc;IACT,UAAU,CAAS;IAClB,WAAW,CAAS;IAErC,YAAY,UAAmB;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAChE,CAAC;IAEO,QAAQ;QACd,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACvD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,IAAiB;QACjC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,IAAI,IAAI,CAAC;IAChD,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAiB,CAAC,CAAC;IACvE,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,IAAI,IAAI,CAAC;IAC5C,CAAC;IAED,YAAY,CAAC,KAAa,EAAE,KAAa,EAAE,IAAY;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,CAAC,SAAS,CAAC;YACb,GAAG,QAAQ;YACX,aAAa,EAAE,QAAQ,EAAE,aAAa,IAAI,EAAE;YAC5C,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE,KAAK;YACjB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IACL,CAAC;IAED,aAAa;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC;YAC/D,IAAI,CAAC,SAAS,CAAC,IAAmB,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,cAAc;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,IAAI,EAAE,SAAS,EAAE,CAAC;YACpB,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;QACvD,CAAC;QACD,IAAI,IAAI,EAAE,aAAa,EAAE,CAAC;YACxB,OAAO,EAAE,iBAAiB,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnD,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/tools/login.ts"],"names":[],"mappings":"AAQA,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAkDjD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SessionManager } from "../session/manager.js";
|
|
2
|
+
import { DEFAULT_BASE_URL } from "../api/client.js";
|
|
3
|
+
import { requestDeviceCode, pollForToken, getAccountStatus } from "../api/auth.js";
|
|
4
|
+
import { openBrowser } from "../utils/browser.js";
|
|
5
|
+
const POLL_INTERVAL_MS = 3_000;
|
|
6
|
+
const POLL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
7
|
+
export async function loginTool() {
|
|
8
|
+
const sessionManager = new SessionManager();
|
|
9
|
+
const baseUrl = process.env.TINIFY_API_URL ?? DEFAULT_BASE_URL;
|
|
10
|
+
// If already logged in, return current status
|
|
11
|
+
const existingToken = sessionManager.getMcpToken();
|
|
12
|
+
if (existingToken) {
|
|
13
|
+
const status = await getAccountStatus(baseUrl, existingToken, null);
|
|
14
|
+
if (status.logged_in) {
|
|
15
|
+
return `Already logged in as ${status.email} (${status.tier} tier, ${status.credits_remaining.toLocaleString()} credits remaining).`;
|
|
16
|
+
}
|
|
17
|
+
// Token expired, clear it
|
|
18
|
+
sessionManager.clearMcpToken();
|
|
19
|
+
}
|
|
20
|
+
// Request device code
|
|
21
|
+
const { device_code, user_code, verify_url } = await requestDeviceCode(baseUrl);
|
|
22
|
+
const authorizeUrl = `${verify_url}?code=${user_code}`;
|
|
23
|
+
// Open browser
|
|
24
|
+
const opened = openBrowser(authorizeUrl);
|
|
25
|
+
const browserMsg = opened
|
|
26
|
+
? `Opening browser... Complete login at tinify.ai.`
|
|
27
|
+
: `Open this URL to log in: ${authorizeUrl}`;
|
|
28
|
+
// Poll for approval
|
|
29
|
+
const startTime = Date.now();
|
|
30
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
32
|
+
const result = await pollForToken(baseUrl, device_code);
|
|
33
|
+
if (result.status === "approved" && result.mcp_token && result.user) {
|
|
34
|
+
sessionManager.saveMcpToken(result.mcp_token, result.user.email, result.user.tier);
|
|
35
|
+
const resetInfo = result.user.credits_reset_at
|
|
36
|
+
? ` until ${formatResetTime(result.user.credits_reset_at)}`
|
|
37
|
+
: "";
|
|
38
|
+
return `Logged in as ${result.user.email} (${capitalize(result.user.tier)} tier, ${result.user.credits_remaining.toLocaleString()} of ${result.user.credits_limit.toLocaleString()} credits remaining${resetInfo})`;
|
|
39
|
+
}
|
|
40
|
+
if (result.status === "denied") {
|
|
41
|
+
return "Authorization denied.";
|
|
42
|
+
}
|
|
43
|
+
if (result.status === "expired") {
|
|
44
|
+
return "Login timed out. Try again.";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return "Login timed out after 5 minutes. Try again.";
|
|
48
|
+
}
|
|
49
|
+
function capitalize(s) {
|
|
50
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
51
|
+
}
|
|
52
|
+
function formatResetTime(isoString) {
|
|
53
|
+
const date = new Date(isoString);
|
|
54
|
+
return date.toLocaleString("en-US", {
|
|
55
|
+
hour: "numeric",
|
|
56
|
+
minute: "2-digit",
|
|
57
|
+
hour12: true,
|
|
58
|
+
timeZoneName: "short",
|
|
59
|
+
month: "2-digit",
|
|
60
|
+
day: "2-digit",
|
|
61
|
+
year: "numeric",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/tools/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/tools/logout.ts"],"names":[],"mappings":"AAIA,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAiBlD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SessionManager } from "../session/manager.js";
|
|
2
|
+
import { DEFAULT_BASE_URL } from "../api/client.js";
|
|
3
|
+
import { revokeToken } from "../api/auth.js";
|
|
4
|
+
export async function logoutTool() {
|
|
5
|
+
const sessionManager = new SessionManager();
|
|
6
|
+
const baseUrl = process.env.TINIFY_API_URL ?? DEFAULT_BASE_URL;
|
|
7
|
+
const mcpToken = sessionManager.getMcpToken();
|
|
8
|
+
if (!mcpToken) {
|
|
9
|
+
return "Not logged in. Already using guest session (20 free credits/day).";
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
await revokeToken(baseUrl, mcpToken);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Best-effort revocation — clear locally regardless
|
|
16
|
+
}
|
|
17
|
+
sessionManager.clearMcpToken();
|
|
18
|
+
return "Logged out. Using guest session (20 free credits/day).";
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/tools/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAE/D,MAAM,QAAQ,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,mEAAmE,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IAED,cAAc,CAAC,aAAa,EAAE,CAAC;IAC/B,OAAO,wDAAwD,CAAC;AAClE,CAAC"}
|
package/dist/tools/optimize.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"optimize.d.ts","sourceRoot":"","sources":["../../src/tools/optimize.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,
|
|
1
|
+
{"version":3,"file":"optimize.d.ts","sourceRoot":"","sources":["../../src/tools/optimize.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAkG9B"}
|
package/dist/tools/optimize.js
CHANGED
|
@@ -14,18 +14,17 @@ export async function optimizeImage(params) {
|
|
|
14
14
|
// 1. Resolve input (read file or fetch URL)
|
|
15
15
|
const input = await resolveInput(params.input);
|
|
16
16
|
// 2. Upload to backend
|
|
17
|
-
const
|
|
17
|
+
const authHeaders = sessionManager.getAuthHeaders();
|
|
18
18
|
const uploadResult = await uploadFile({
|
|
19
19
|
baseUrl,
|
|
20
20
|
fileBuffer: input.buffer,
|
|
21
21
|
filename: input.filename,
|
|
22
|
-
|
|
22
|
+
authHeaders,
|
|
23
23
|
});
|
|
24
|
-
// Persist new session token if returned
|
|
24
|
+
// Persist new session token if returned (for guest sessions)
|
|
25
25
|
if (uploadResult.session_token) {
|
|
26
26
|
sessionManager.saveToken(uploadResult.session_token);
|
|
27
27
|
}
|
|
28
|
-
const currentToken = uploadResult.session_token ?? sessionToken;
|
|
29
28
|
// 3. Build settings (smart defaults: compress always, SEO tags on)
|
|
30
29
|
const settings = {
|
|
31
30
|
output_format: params.output_format ?? "original",
|
|
@@ -41,18 +40,21 @@ export async function optimizeImage(params) {
|
|
|
41
40
|
if (params.output_upscale_factor !== undefined) {
|
|
42
41
|
settings.output_upscale_factor = params.output_upscale_factor;
|
|
43
42
|
}
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
// Derive aspect_lock: false only when both dimensions are specified (exact output requested)
|
|
44
|
+
// Otherwise omit (backend defaults to true = proportional scaling)
|
|
45
|
+
if (params.output_width_px !== undefined && params.output_height_px !== undefined) {
|
|
46
|
+
settings.output_aspect_lock = false;
|
|
47
|
+
settings.output_resize_mode = params.output_resize_mode ?? "pad";
|
|
46
48
|
}
|
|
47
|
-
if (params.
|
|
48
|
-
settings.
|
|
49
|
+
else if (params.output_resize_mode !== undefined) {
|
|
50
|
+
settings.output_resize_mode = params.output_resize_mode;
|
|
49
51
|
}
|
|
50
52
|
// 4. Trigger processing
|
|
51
53
|
const processResult = await triggerProcessing({
|
|
52
54
|
baseUrl,
|
|
53
55
|
tempFileIds: [uploadResult.temp_file_id],
|
|
54
56
|
settings,
|
|
55
|
-
|
|
57
|
+
authHeaders,
|
|
56
58
|
});
|
|
57
59
|
const job = processResult.jobs[0];
|
|
58
60
|
if (!job?.id) {
|
|
@@ -75,6 +77,9 @@ export async function optimizeImage(params) {
|
|
|
75
77
|
outputFormat: params.output_format && params.output_format !== "original"
|
|
76
78
|
? params.output_format
|
|
77
79
|
: completedJob.processed_format ?? undefined,
|
|
80
|
+
seoFilename: params.output_seo_tag_gen !== false
|
|
81
|
+
? (completedJob.seo_filename ?? undefined)
|
|
82
|
+
: undefined,
|
|
78
83
|
});
|
|
79
84
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
80
85
|
fs.writeFileSync(outputPath, downloadResult.buffer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"optimize.js","sourceRoot":"","sources":["../../src/tools/optimize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAA2B,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"optimize.js","sourceRoot":"","sources":["../../src/tools/optimize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAIA,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAyBlD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { SessionManager } from "../session/manager.js";
|
|
2
|
+
import { DEFAULT_BASE_URL } from "../api/client.js";
|
|
3
|
+
import { getAccountStatus } from "../api/auth.js";
|
|
4
|
+
export async function statusTool() {
|
|
5
|
+
const sessionManager = new SessionManager();
|
|
6
|
+
const baseUrl = process.env.TINIFY_API_URL ?? DEFAULT_BASE_URL;
|
|
7
|
+
const mcpToken = sessionManager.getMcpToken();
|
|
8
|
+
const sessionToken = sessionManager.getToken();
|
|
9
|
+
const status = await getAccountStatus(baseUrl, mcpToken, sessionToken);
|
|
10
|
+
if (status.logged_in) {
|
|
11
|
+
const resetInfo = status.credits_reset_at
|
|
12
|
+
? `Resets: ${formatResetTime(status.credits_reset_at)}`
|
|
13
|
+
: "";
|
|
14
|
+
return [
|
|
15
|
+
`Logged in as ${status.email} (${capitalize(status.tier)} tier)`,
|
|
16
|
+
`Credits: ${status.credits_remaining.toLocaleString()} of ${status.credits_limit.toLocaleString()} remaining`,
|
|
17
|
+
resetInfo,
|
|
18
|
+
].filter(Boolean).join("\n");
|
|
19
|
+
}
|
|
20
|
+
return [
|
|
21
|
+
"Not logged in. Using guest session.",
|
|
22
|
+
`Credits: ${status.credits_remaining} of ${status.credits_limit} remaining (resets daily)`,
|
|
23
|
+
"Tip: Log in for more credits \u2014 free accounts get 50/day.",
|
|
24
|
+
].join("\n");
|
|
25
|
+
}
|
|
26
|
+
function capitalize(s) {
|
|
27
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
28
|
+
}
|
|
29
|
+
function formatResetTime(isoString) {
|
|
30
|
+
const date = new Date(isoString);
|
|
31
|
+
return date.toLocaleString("en-US", {
|
|
32
|
+
hour: "numeric",
|
|
33
|
+
minute: "2-digit",
|
|
34
|
+
hour12: true,
|
|
35
|
+
timeZoneName: "short",
|
|
36
|
+
month: "2-digit",
|
|
37
|
+
day: "2-digit",
|
|
38
|
+
year: "numeric",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,gBAAgB,CAAC;IAE/D,MAAM,QAAQ,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;IAC9C,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;IAE/C,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IAEvE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,MAAM,SAAS,GAAG,MAAM,CAAC,gBAAgB;YACvC,CAAC,CAAC,WAAW,eAAe,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE;YACvD,CAAC,CAAC,EAAE,CAAC;QACP,OAAO;YACL,gBAAgB,MAAM,CAAC,KAAK,KAAK,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ;YAChE,YAAY,MAAM,CAAC,iBAAiB,CAAC,cAAc,EAAE,OAAO,MAAM,CAAC,aAAa,CAAC,cAAc,EAAE,YAAY;YAC7G,SAAS;SACV,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO;QACL,qCAAqC;QACrC,YAAY,MAAM,CAAC,iBAAiB,OAAO,MAAM,CAAC,aAAa,2BAA2B;QAC1F,+DAA+D;KAChE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,eAAe,CAAC,SAAiB;IACxC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QAClC,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,OAAO;QACrB,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;KAChB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/tools/upgrade.ts"],"names":[],"mappings":"AAIA,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAQnD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { openBrowser } from "../utils/browser.js";
|
|
2
|
+
const PRICING_URL = "https://tinify.ai/pricing";
|
|
3
|
+
export async function upgradeTool() {
|
|
4
|
+
const opened = openBrowser(PRICING_URL);
|
|
5
|
+
if (opened) {
|
|
6
|
+
return "Opened pricing page in browser. Plans: Free (50/day), Pro (3,000/month), Max (10,000/month).";
|
|
7
|
+
}
|
|
8
|
+
return `Open this URL to see pricing: ${PRICING_URL}\nPlans: Free (50/day), Pro (3,000/month), Max (10,000/month).`;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/tools/upgrade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,MAAM,WAAW,GAAG,2BAA2B,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IAExC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,8FAA8F,CAAC;IACxG,CAAC;IAED,OAAO,iCAAiC,WAAW,gEAAgE,CAAC;AACtH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/utils/browser.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAiBhD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
export function openBrowser(url) {
|
|
3
|
+
const platform = process.platform;
|
|
4
|
+
let command;
|
|
5
|
+
if (platform === "darwin") {
|
|
6
|
+
command = `open "${url}"`;
|
|
7
|
+
}
|
|
8
|
+
else if (platform === "win32") {
|
|
9
|
+
command = `start "" "${url}"`;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
command = `xdg-open "${url}"`;
|
|
13
|
+
}
|
|
14
|
+
let success = true;
|
|
15
|
+
exec(command, (error) => {
|
|
16
|
+
if (error)
|
|
17
|
+
success = false;
|
|
18
|
+
});
|
|
19
|
+
return success;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/utils/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,OAAe,CAAC;IAEpB,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC;IAC5B,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,GAAG,aAAa,GAAG,GAAG,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,aAAa,GAAG,GAAG,CAAC;IAChC,CAAC;IAED,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QACtB,IAAI,KAAK;YAAE,OAAO,GAAG,KAAK,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/utils/output.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAoBlE"}
|
package/dist/utils/output.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
function getTinifiedFilename(filename, outputFormat) {
|
|
2
|
+
function getTinifiedFilename(filename, outputFormat, seoFilename) {
|
|
3
3
|
const ext = path.extname(filename);
|
|
4
|
-
const name = path.basename(filename, ext);
|
|
5
4
|
let newExt;
|
|
6
5
|
if (!outputFormat || outputFormat === "original") {
|
|
7
6
|
newExt = ext || ".png";
|
|
@@ -9,17 +8,21 @@ function getTinifiedFilename(filename, outputFormat) {
|
|
|
9
8
|
else {
|
|
10
9
|
newExt = `.${outputFormat}`;
|
|
11
10
|
}
|
|
11
|
+
if (seoFilename) {
|
|
12
|
+
return `${seoFilename}${newExt}`;
|
|
13
|
+
}
|
|
14
|
+
const name = path.basename(filename, ext);
|
|
12
15
|
return `${name}.tinified${newExt}`;
|
|
13
16
|
}
|
|
14
17
|
function isDirectoryPath(p) {
|
|
15
18
|
return p.endsWith("/") || p.endsWith(path.sep);
|
|
16
19
|
}
|
|
17
20
|
export function resolveOutputPath(params) {
|
|
18
|
-
const { inputPath, isUrl, filename, outputPath, outputFormat, cwd } = params;
|
|
21
|
+
const { inputPath, isUrl, filename, outputPath, outputFormat, seoFilename, cwd } = params;
|
|
19
22
|
if (outputPath && !isDirectoryPath(outputPath)) {
|
|
20
23
|
return path.resolve(outputPath);
|
|
21
24
|
}
|
|
22
|
-
const tinifiedName = getTinifiedFilename(filename, outputFormat);
|
|
25
|
+
const tinifiedName = getTinifiedFilename(filename, outputFormat, seoFilename);
|
|
23
26
|
if (outputPath && isDirectoryPath(outputPath)) {
|
|
24
27
|
return path.join(path.resolve(outputPath), tinifiedName);
|
|
25
28
|
}
|
package/dist/utils/output.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAYlC,SAAS,mBAAmB,CAC1B,QAAgB,EAChB,YAAgC,EAChC,WAAoB;IAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEnC,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC,YAAY,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;IAC9B,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,GAAG,WAAW,GAAG,MAAM,EAAE,CAAC;IACnC,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1C,OAAO,GAAG,IAAI,YAAY,MAAM,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAwB;IACxD,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAE1F,IAAI,UAAU,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;IAE9E,IAAI,UAAU,IAAI,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,OAAO,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AAC3C,CAAC"}
|