@yuiseki/gyazocli 0.5.1 → 0.5.2
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/dist/api.js +5 -1
- package/dist/index.js +1 -1
- package/dist/mcp.js +30 -8
- package/dist/services/memory.js +10 -13
- package/docs/ADR/003-cli-structure.md +1 -1
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -119,7 +119,11 @@ async function listCollectionImages(collectionId, page = 1, per = 100) {
|
|
|
119
119
|
*/
|
|
120
120
|
async function fetchImageRendition(imageId, width, format = 'webp') {
|
|
121
121
|
const extension = format === 'jpeg' ? 'jpg' : 'webp';
|
|
122
|
-
|
|
122
|
+
// The `-jpg` before the extension is a source-type marker, and it has to be
|
|
123
|
+
// there: without it the route answers 404 for any capture whose derivative
|
|
124
|
+
// is not already stored, which is most of them. Its value is ignored, and
|
|
125
|
+
// the extension alone decides what comes back, so a constant will do.
|
|
126
|
+
const url = `${imageOrigin()}/thumb/${width}_w/${imageId}-jpg.${extension}`;
|
|
123
127
|
const response = await axios_1.default.get(url, { responseType: 'arraybuffer' });
|
|
124
128
|
const data = Buffer.from(response.data);
|
|
125
129
|
const contentType = String(response.headers['content-type'] || '').split(';')[0].trim();
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ program
|
|
|
26
26
|
.name('gyazo')
|
|
27
27
|
.description('Gyazo Memory CLI for AI Secretary')
|
|
28
28
|
.option('--mcp-server', 'run as a Model Context Protocol server over stdio')
|
|
29
|
-
.version('0.5.
|
|
29
|
+
.version('0.5.2');
|
|
30
30
|
(0, config_1.registerConfigCommand)(program);
|
|
31
31
|
(0, list_1.registerListCommand)(program);
|
|
32
32
|
(0, get_1.registerGetCommand)(program);
|
package/dist/mcp.js
CHANGED
|
@@ -193,16 +193,35 @@ const INCLUDE_LOCATION = zod_1.z
|
|
|
193
193
|
.boolean()
|
|
194
194
|
.default(true)
|
|
195
195
|
.describe('Fill in the location, which the listing and search endpoints leave out. Costs one ' +
|
|
196
|
-
'extra lookup per capture the local cache does not already hold
|
|
197
|
-
'
|
|
196
|
+
'extra lookup per capture the local cache does not already hold, so a page of 40 ' +
|
|
197
|
+
'uncached captures adds a few seconds; the next look at the same captures is free. ' +
|
|
198
|
+
'Set false when the coordinates do not matter');
|
|
199
|
+
/**
|
|
200
|
+
* Fill in the locations, and say so when the per-call limit stopped it part
|
|
201
|
+
* way: past that point a capture without a location is indistinguishable from
|
|
202
|
+
* one that has none, which is exactly the sort of silence worth breaking.
|
|
203
|
+
*/
|
|
198
204
|
async function withLocations(images, includeLocation) {
|
|
199
|
-
|
|
205
|
+
if (!includeLocation)
|
|
206
|
+
return { images };
|
|
207
|
+
const result = await (0, memory_1.enrichImageLocations)(images);
|
|
208
|
+
if (result.skipped === 0)
|
|
209
|
+
return { images: result.images };
|
|
210
|
+
return {
|
|
211
|
+
images: result.images,
|
|
212
|
+
note: `The location was filled in for ${result.considered} captures; ${result.skipped} were ` +
|
|
213
|
+
'left as the listing returned them, so their missing location means "not looked up" ' +
|
|
214
|
+
'rather than "no location". Ask for fewer at a time to cover them all.',
|
|
215
|
+
};
|
|
200
216
|
}
|
|
201
|
-
function asMetadataListResult(images) {
|
|
217
|
+
function asMetadataListResult(images, note) {
|
|
202
218
|
if (!images || images.length === 0) {
|
|
203
219
|
return NO_IMAGES;
|
|
204
220
|
}
|
|
205
|
-
|
|
221
|
+
const json = asJsonResult(images.map(toMetadata));
|
|
222
|
+
if (!note)
|
|
223
|
+
return json;
|
|
224
|
+
return { content: [...json.content, { type: 'text', text: note }] };
|
|
206
225
|
}
|
|
207
226
|
/**
|
|
208
227
|
* A date argument, refused by throwing. The CLI reports and exits here, which
|
|
@@ -249,7 +268,8 @@ function createMcpServer() {
|
|
|
249
268
|
if (!images || images.length === 0) {
|
|
250
269
|
return NO_IMAGES;
|
|
251
270
|
}
|
|
252
|
-
|
|
271
|
+
const withLocation = await withLocations(images, includeLocation);
|
|
272
|
+
return asMetadataListResult(withLocation.images, withLocation.note);
|
|
253
273
|
}));
|
|
254
274
|
server.registerTool('gyazo_image', {
|
|
255
275
|
title: 'Describe one Gyazo capture',
|
|
@@ -365,7 +385,8 @@ function createMcpServer() {
|
|
|
365
385
|
hour: hour || undefined,
|
|
366
386
|
alias,
|
|
367
387
|
});
|
|
368
|
-
|
|
388
|
+
const withLocation = await withLocations(images, args.include_location);
|
|
389
|
+
return asMetadataListResult(withLocation.images, withLocation.note);
|
|
369
390
|
}));
|
|
370
391
|
server.registerTool('gyazo_summary', {
|
|
371
392
|
title: 'Summarise a stretch of Gyazo captures',
|
|
@@ -535,7 +556,8 @@ function createMcpServer() {
|
|
|
535
556
|
'recent pages of captures. It may be older than that, or belong to another ' +
|
|
536
557
|
'account. Ask for a window in minutes instead, or raise max_pages.');
|
|
537
558
|
}
|
|
538
|
-
|
|
559
|
+
const withLocation = await withLocations(result.images, args.include_location);
|
|
560
|
+
return asMetadataListResult(withLocation.images, withLocation.note);
|
|
539
561
|
}));
|
|
540
562
|
server.registerTool('gyazo_collections', {
|
|
541
563
|
title: 'Find a Gyazo collection by name',
|
package/dist/services/memory.js
CHANGED
|
@@ -416,26 +416,23 @@ async function listCapturesSince(options) {
|
|
|
416
416
|
}
|
|
417
417
|
return { images: collected.slice(0, limit), pagesWalked };
|
|
418
418
|
}
|
|
419
|
-
/**
|
|
420
|
-
* Fill in what the lean endpoints leave out.
|
|
421
|
-
*
|
|
422
|
-
* The listing and the search endpoints return an image without its
|
|
423
|
-
* coordinates or its address, whatever the capture actually carries; only the
|
|
424
|
-
* detail endpoint has them. So a caller that needs a location has to ask again
|
|
425
|
-
* per image, which is what this does: the cache first, the API for the rest,
|
|
426
|
-
* a few at a time, writing what it fetches back to the cache so the next look
|
|
427
|
-
* is free.
|
|
428
|
-
*/
|
|
429
419
|
async function enrichImageLocations(images, options = {}) {
|
|
430
420
|
const useCache = options.useCache !== false;
|
|
431
421
|
const limit = options.limit ?? 40;
|
|
432
|
-
const concurrency = Math.max(1, options.concurrency ??
|
|
422
|
+
const concurrency = Math.max(1, options.concurrency ?? 10);
|
|
433
423
|
const enriched = [...images];
|
|
434
424
|
const pending = [];
|
|
435
|
-
|
|
425
|
+
let considered = 0;
|
|
426
|
+
let skipped = 0;
|
|
427
|
+
for (let index = 0; index < enriched.length; index++) {
|
|
436
428
|
const image = enriched[index];
|
|
437
429
|
if (image?.metadata?.exif_normalized || image?.metadata?.exif_address)
|
|
438
430
|
continue;
|
|
431
|
+
if (considered >= limit) {
|
|
432
|
+
skipped++;
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
considered++;
|
|
439
436
|
if (useCache) {
|
|
440
437
|
const cached = (0, storage_1.loadImageCache)(image?.image_id);
|
|
441
438
|
if (cached) {
|
|
@@ -459,5 +456,5 @@ async function enrichImageLocations(images, options = {}) {
|
|
|
459
456
|
}
|
|
460
457
|
}));
|
|
461
458
|
}
|
|
462
|
-
return enriched;
|
|
459
|
+
return { images: enriched, considered, skipped };
|
|
463
460
|
}
|