@yuiseki/gyazocli 0.0.1

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.
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getCacheDir = getCacheDir;
7
+ exports.getImagePath = getImagePath;
8
+ exports.getSearchImagePath = getSearchImagePath;
9
+ exports.getHourlyPath = getHourlyPath;
10
+ exports.saveImageCache = saveImageCache;
11
+ exports.loadImageCache = loadImageCache;
12
+ exports.saveSearchImageCache = saveSearchImageCache;
13
+ exports.loadSearchImageCache = loadSearchImageCache;
14
+ exports.saveHourlyCache = saveHourlyCache;
15
+ exports.loadHourlyCache = loadHourlyCache;
16
+ exports.saveHourlyMetadataCache = saveHourlyMetadataCache;
17
+ exports.loadHourlyMetadataCache = loadHourlyMetadataCache;
18
+ const fs_1 = __importDefault(require("fs"));
19
+ const path_1 = __importDefault(require("path"));
20
+ const os_1 = __importDefault(require("os"));
21
+ function getCacheDir() {
22
+ if (process.env.GYAZO_CACHE_DIR) {
23
+ return process.env.GYAZO_CACHE_DIR;
24
+ }
25
+ const cacheBase = process.env.XDG_CACHE_HOME || path_1.default.join(os_1.default.homedir(), '.cache');
26
+ const dir = path_1.default.join(cacheBase, 'gyazocli');
27
+ if (!fs_1.default.existsSync(dir)) {
28
+ fs_1.default.mkdirSync(dir, { recursive: true });
29
+ }
30
+ return dir;
31
+ }
32
+ function getImagePath(imageId) {
33
+ const prefix1 = imageId[0] || '_';
34
+ const prefix2 = imageId[1] || '_';
35
+ const dir = path_1.default.join(getCacheDir(), 'images', prefix1, prefix2);
36
+ if (!fs_1.default.existsSync(dir)) {
37
+ fs_1.default.mkdirSync(dir, { recursive: true });
38
+ }
39
+ return path_1.default.join(dir, `${imageId}.json`);
40
+ }
41
+ function getSearchImagePath(imageId) {
42
+ const prefix1 = imageId[0] || '_';
43
+ const prefix2 = imageId[1] || '_';
44
+ const dir = path_1.default.join(getCacheDir(), 'search_images', prefix1, prefix2);
45
+ if (!fs_1.default.existsSync(dir)) {
46
+ fs_1.default.mkdirSync(dir, { recursive: true });
47
+ }
48
+ return path_1.default.join(dir, `${imageId}.json`);
49
+ }
50
+ function getHourlyPath(year, month, day, hour) {
51
+ const dir = path_1.default.join(getCacheDir(), 'hourly', year, month, day);
52
+ if (!fs_1.default.existsSync(dir)) {
53
+ fs_1.default.mkdirSync(dir, { recursive: true });
54
+ }
55
+ return path_1.default.join(dir, `${hour}.json`);
56
+ }
57
+ function getHourlyMetadataPath(kind, year, month, day, hour) {
58
+ const dir = path_1.default.join(getCacheDir(), 'hourly', year, month, day);
59
+ if (!fs_1.default.existsSync(dir)) {
60
+ fs_1.default.mkdirSync(dir, { recursive: true });
61
+ }
62
+ return path_1.default.join(dir, `${hour}-${kind}.json`);
63
+ }
64
+ function saveImageCache(imageId, data) {
65
+ const filePath = getImagePath(imageId);
66
+ fs_1.default.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
67
+ }
68
+ function loadImageCache(imageId) {
69
+ const filePath = getImagePath(imageId);
70
+ if (fs_1.default.existsSync(filePath)) {
71
+ return JSON.parse(fs_1.default.readFileSync(filePath, 'utf-8'));
72
+ }
73
+ return null;
74
+ }
75
+ function saveSearchImageCache(imageId, data) {
76
+ const filePath = getSearchImagePath(imageId);
77
+ fs_1.default.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
78
+ }
79
+ function loadSearchImageCache(imageId) {
80
+ const filePath = getSearchImagePath(imageId);
81
+ if (fs_1.default.existsSync(filePath)) {
82
+ return JSON.parse(fs_1.default.readFileSync(filePath, 'utf-8'));
83
+ }
84
+ return null;
85
+ }
86
+ function saveHourlyCache(year, month, day, hour, imageIds) {
87
+ const filePath = getHourlyPath(year, month, day, hour);
88
+ fs_1.default.writeFileSync(filePath, JSON.stringify(imageIds, null, 2), 'utf-8');
89
+ }
90
+ function loadHourlyCache(year, month, day, hour) {
91
+ const filePath = getHourlyPath(year, month, day, hour);
92
+ if (fs_1.default.existsSync(filePath)) {
93
+ return JSON.parse(fs_1.default.readFileSync(filePath, 'utf-8'));
94
+ }
95
+ return null;
96
+ }
97
+ function saveHourlyMetadataCache(kind, year, month, day, hour, valuesByImageId) {
98
+ const filePath = getHourlyMetadataPath(kind, year, month, day, hour);
99
+ fs_1.default.writeFileSync(filePath, JSON.stringify(valuesByImageId, null, 2), 'utf-8');
100
+ }
101
+ function loadHourlyMetadataCache(kind, year, month, day, hour) {
102
+ const filePath = getHourlyMetadataPath(kind, year, month, day, hour);
103
+ if (fs_1.default.existsSync(filePath)) {
104
+ return JSON.parse(fs_1.default.readFileSync(filePath, 'utf-8'));
105
+ }
106
+ return null;
107
+ }
@@ -0,0 +1,38 @@
1
+ # ADR 001: Credential Management and Configuration
2
+
3
+ ## Status
4
+ Accepted
5
+
6
+ ## Context
7
+ `gyazocli` needs a credential flow that works in both local shell usage and automation. The implementation currently supports loading values from environment variables and persisting an access token in a local config file.
8
+
9
+ ## Decision
10
+ Use `GYAZO_ACCESS_TOKEN` as the primary runtime credential, with a local fallback file for convenience.
11
+
12
+ ### 1. Supported Environment Variables
13
+ - `GYAZO_ACCESS_TOKEN`: Personal access token used for API requests.
14
+ - `GYAZO_CLIENT_ID`: Parsed from env for future OAuth use (not used by current commands).
15
+ - `GYAZO_CLIENT_SECRET`: Parsed from env for future OAuth use (not used by current commands).
16
+ - `GYAZO_CACHE_DIR`: Optional cache directory override (used by storage logic).
17
+
18
+ ### 2. `.env` Loading Behavior
19
+ - `dotenv` is used via `dotenv.config()`.
20
+ - `.env` is loaded from the current working directory by default.
21
+ - If the same key is already set in process environment, that value takes precedence.
22
+
23
+ ### 3. Local Stored Token
24
+ - File path: `~/.config/gyazo/credentials.json`
25
+ - Supported key for CLI config commands: `token`
26
+ - `gyazo config set token <value>` stores `GYAZO_ACCESS_TOKEN` in the file.
27
+ - `gyazo config get token` prints a masked token value.
28
+
29
+ ### 4. Access Token Resolution Order
30
+ When an authenticated command runs:
31
+ 1. `GYAZO_ACCESS_TOKEN` from environment/config object
32
+ 2. Stored token from `~/.config/gyazo/credentials.json`
33
+ 3. Exit with an error and instructions
34
+
35
+ ## Consequences
36
+ - Token management works without requiring shell profile edits.
37
+ - CI and non-interactive usage can rely on environment variables only.
38
+ - OAuth client fields are documented but currently unused by CLI commands.
@@ -0,0 +1,77 @@
1
+ # ADR 002: Caching Strategy and Storage Structure
2
+
3
+ ## Status
4
+ Accepted
5
+
6
+ ## Context
7
+ `gyazocli` caches image detail and hourly indices on local disk so repeated lookups avoid unnecessary API calls and historical lookups can be served quickly.
8
+
9
+ ## Decision
10
+ Store cache files under XDG-style user cache location by default, with an environment override.
11
+
12
+ ### 1. Cache Root
13
+ `getCacheDir()` resolves cache root as:
14
+ 1. `GYAZO_CACHE_DIR` (if set)
15
+ 2. `${XDG_CACHE_HOME}/gyazocli` (if `XDG_CACHE_HOME` is set)
16
+ 3. `~/.cache/gyazocli`
17
+
18
+ ### 2. Image Cache Layout
19
+ - Path pattern: `<cacheRoot>/images/<firstChar>/<secondChar>/<imageId>.json`
20
+ - Example: `~/.cache/gyazocli/images/a/1/a1b2c3d4.json`
21
+ - Purpose: Avoid a single large flat directory.
22
+
23
+ ### 3. Hourly Index Layout
24
+ - Path pattern: `<cacheRoot>/hourly/<YYYY>/<MM>/<DD>/<HH>.json`
25
+ - File content: JSON array of image IDs for that hour.
26
+ - Used by `gyazo list --hour <yyyy-mm-dd-hh>`.
27
+
28
+ ### 4. Hourly Metadata Extract Cache Layout
29
+ - Path patterns:
30
+ - `<cacheRoot>/hourly/<YYYY>/<MM>/<DD>/<HH>-apps.json`
31
+ - `<cacheRoot>/hourly/<YYYY>/<MM>/<DD>/<HH>-domains.json`
32
+ - `<cacheRoot>/hourly/<YYYY>/<MM>/<DD>/<HH>-tags.json`
33
+ - `<cacheRoot>/hourly/<YYYY>/<MM>/<DD>/<HH>-locations.json`
34
+ - File content:
35
+ - JSON object keyed by `image_id`.
36
+ - Value is string array of extracted metadata tokens for that command.
37
+ - Example:
38
+ - `apps`: `{ "<imageId>": ["Chrome"] }`
39
+ - `domains`: `{ "<imageId>": ["x.com"] }`
40
+ - `tags`: `{ "<imageId>": ["ゆいせきのコーデ", "ゆいせきの自撮り"] }`
41
+ - `locations`: `{ "<imageId>": ["東京都台東区竜泉"] }`
42
+ - Purpose:
43
+ - Avoid repeated full image-cache scans when computing rankings.
44
+ - Preserve negative results (`[]`) per image so commands do not re-fetch detail repeatedly.
45
+
46
+ ### 5. Cache Update Behavior in Commands
47
+ - `gyazo get <image_id>`:
48
+ - Uses cached detail by default.
49
+ - `--no-cache` forces API fetch and rewrites cache.
50
+ - `gyazo sync`:
51
+ - Fetches list pages (`per_page=100`) for a bounded date range.
52
+ - Skips detail fetch when cached record already has `ocr`.
53
+ - Writes/merges hourly index files.
54
+ - `gyazo apps`, `gyazo domains`, `gyazo tags`, `gyazo locations`:
55
+ - Default target range is from 8 days ago to yesterday.
56
+ - `--today` targets only today.
57
+ - `--date` can target a specific day/month/year.
58
+ - With default cache mode:
59
+ - Read ranking from hourly metadata extract caches for fast aggregation.
60
+ - If `<HH>-*.json` is missing but `<HH>.json` exists, build it once from image cache and persist.
61
+ - If the target range has no hourly cache data, run API-based warming once, then read from cache.
62
+ - With `--no-cache`:
63
+ - Force API-based warming by scanning list pages (`per_page=100`) in the target date range.
64
+ - Update both hourly image index (`<HH>.json`) and hourly metadata extract caches (`<HH>-*.json`).
65
+ - Ranking is computed from warmed image cache data for that run (no historical cache merge).
66
+ - `gyazo stats`:
67
+ - Default window is from 8 days ago to yesterday (`7` days), output is Markdown.
68
+ - Section rows are rendered as bullet lists (`- <label>: <count>`), not tables.
69
+ - Aggregates upload time bands and rankings of apps/domains/tags from hourly caches.
70
+ - Uses the same hourly metadata extract cache mechanism as ranking commands.
71
+ - If cache data for the target window is absent, warming is triggered once, then summary is built from cache.
72
+
73
+ ## Consequences
74
+ - Cache is portable and independent from the repository working tree.
75
+ - Large historical datasets remain manageable on filesystem.
76
+ - Ranking commands become significantly faster on repeated runs due to hourly extracted metadata cache.
77
+ - Cache freshness depends on command behavior (`get --no-cache`, `sync`, and ranking command warming).
@@ -0,0 +1,112 @@
1
+ # ADR 003: CLI Command Structure and Interface
2
+
3
+ ## Status
4
+ Accepted
5
+
6
+ ## Context
7
+ The current `gyazocli` implementation uses flat top-level commands (not nested under `images`). Documentation should reflect the command tree implemented in `src/index.ts`.
8
+
9
+ ## Decision
10
+ Adopt and document the existing top-level command structure.
11
+
12
+ ### 1. Program Metadata
13
+ - Binary name: `gyazo`
14
+ - Version: `1.0.0`
15
+ - Description: `Gyazo Memory CLI for AI Secretary`
16
+
17
+ ### 2. Commands
18
+ - `gyazo config set <key> <value>`
19
+ - Currently supported key: `token`
20
+ - `gyazo config get <key>`
21
+ - `token` is masked
22
+ - `me` fetches `/api/users/me`
23
+ - Options:
24
+ - `-j, --json`
25
+ - `gyazo list` (alias: `gyazo ls`)
26
+ - Options:
27
+ - `-p, --page <number>` (default: `1`)
28
+ - `-l, --limit <number>` (default: `20`)
29
+ - `-j, --json`
30
+ - `-H, --hour <yyyy-mm-dd-hh>` (reads hourly cache only)
31
+ - `--photos` (alias of `search has:location`)
32
+ - `--uploaded` (alias of `search gyazocli_uploads`)
33
+ - `--no-cache`
34
+ - `gyazo get <image_id>`
35
+ - Options:
36
+ - `-j, --json`
37
+ - `--ocr`
38
+ - `--objects`
39
+ - `--no-cache`
40
+ - `gyazo search [query]`
41
+ - Options:
42
+ - `-j, --json`
43
+ - `--no-cache`
44
+ - `gyazo apps`
45
+ - Default range: from 8 days ago to yesterday
46
+ - Options:
47
+ - `--date <yyyy|yyyy-mm|yyyy-mm-dd>`
48
+ - `--today` (target today only)
49
+ - `-l, --limit <number>` (default: `10`, max: `10`)
50
+ - `--max-pages <number>` (default: `10`)
51
+ - `-j, --json`
52
+ - `--no-cache`
53
+ - `gyazo domains`
54
+ - Default range: from 8 days ago to yesterday
55
+ - Options:
56
+ - `--date <yyyy|yyyy-mm|yyyy-mm-dd>`
57
+ - `--today` (target today only)
58
+ - `-l, --limit <number>` (default: `10`, max: `10`)
59
+ - `--max-pages <number>` (default: `10`)
60
+ - `-j, --json`
61
+ - `--no-cache`
62
+ - `gyazo tags`
63
+ - Default range: from 8 days ago to yesterday
64
+ - Options:
65
+ - `--date <yyyy|yyyy-mm|yyyy-mm-dd>`
66
+ - `--today` (target today only)
67
+ - `-l, --limit <number>` (default: `10`, max: `10`)
68
+ - `--max-pages <number>` (default: `10`)
69
+ - `-j, --json`
70
+ - `--no-cache`
71
+ - `gyazo locations`
72
+ - Default range: from 8 days ago to yesterday
73
+ - Options:
74
+ - `--date <yyyy|yyyy-mm|yyyy-mm-dd>`
75
+ - `--today` (target today only)
76
+ - `-l, --limit <number>` (default: `10`, max: `10`)
77
+ - `--max-pages <number>` (default: `10`)
78
+ - `-j, --json`
79
+ - `--no-cache`
80
+ - `gyazo stats`
81
+ - Default range: from 8 days ago to yesterday
82
+ - Default behavior: weekly Markdown summary.
83
+ - Section rows are rendered as bullet lists (`- <label>: <count>`) for terminal readability.
84
+ - Options:
85
+ - `--date <yyyy|yyyy-mm|yyyy-mm-dd>` (window end date anchor; default: yesterday)
86
+ - `--days <number>` (default: `7`)
87
+ - `--top <number>` (default: `10`)
88
+ - `--max-pages <number>` (default: `10`)
89
+ - `--no-cache`
90
+ - `gyazo upload [path]`
91
+ - Options:
92
+ - `--title <title>`
93
+ - `--app <app>` (default: `gyazocli`)
94
+ - `--url <url>`
95
+ - `--timestamp <unix_timestamp>`
96
+ - `--desc <desc>` (`#gyazocli_uploads` is always appended)
97
+ - `gyazo sync`
98
+ - Options:
99
+ - `--days <number>` (default: `1`, used when `--date` is omitted)
100
+ - `--date <yyyy|yyyy-mm|yyyy-mm-dd>`
101
+ - `--max-pages <number>` (default: `10`)
102
+ - `gyazo import <type> <dir>`
103
+ - Supported types: `json`, `hourly`
104
+
105
+ ### 3. Output and Behavior Notes
106
+ - `-j, --json` is available on `config get`, `list`, `get`, `search`, `apps`, `domains`, `tags`, and `locations`.
107
+ - There are no global `--plain` or `--verbose` flags in current implementation.
108
+ - Authenticated commands call token resolution before API access.
109
+
110
+ ## Consequences
111
+ - Docs now match the command UX shipped in code.
112
+ - Users can rely on CLI help output and docs without cross-project noise.
@@ -0,0 +1,66 @@
1
+ # Gyazo API
2
+
3
+ Gyazo provides a REST API to access user images and metadata including OCR and object recognition results.
4
+
5
+ ## Base URL
6
+
7
+ `https://api.gyazo.com/api`
8
+
9
+ ## Authentication
10
+
11
+ Authentication is performed using an OAuth2 Access Token.
12
+
13
+ **Header:** `Authorization: Bearer {access_token}`
14
+
15
+ ---
16
+
17
+ ## Endpoints
18
+
19
+ ### 1. List Images
20
+ `GET /images`
21
+
22
+ Fetch metadata for the user's images.
23
+
24
+ **Parameters:**
25
+ - `page` (int): Page number.
26
+ - `per_page` (int): Number of items per page (Max: 100).
27
+
28
+ ### 2. Search Images
29
+ `GET /search`
30
+
31
+ Search for images using queries.
32
+
33
+ **Parameters:**
34
+ - `query` (string): Search query (e.g., `date:YYYY-MM-DD`).
35
+ - `page` (int): Page number.
36
+ - `per` (int): Number of items per page.
37
+
38
+ ### 3. Get Image Detail
39
+ `GET /images/{image_id}`
40
+
41
+ Fetch detailed information for a specific image, including OCR and localized object annotations.
42
+
43
+ ---
44
+
45
+ ## Data Models
46
+
47
+ ### Image Object
48
+ - `image_id`: Unique identifier.
49
+ - `permalink_url`: Gyazo page URL.
50
+ - `url`: Direct image URL.
51
+ - `type`: Image type (e.g., `png`, `jpg`).
52
+ - `created_at`: Creation timestamp (ISO 8601).
53
+ - `ocr`: OCR results (description and locale).
54
+ - `metadata`:
55
+ - `title`: Window title at the time of capture.
56
+ - `url`: Source URL of the capture.
57
+ - `app`: Application name.
58
+ - `links`: Extracted link keywords (used as tags in this CLI).
59
+ - `exif_address`: Geolocation address from EXIF.
60
+
61
+ ---
62
+
63
+ ## Rate Limiting
64
+
65
+ If you exceed the rate limit, the API returns a `429 Too Many Requests` status code.
66
+ Check the `Retry-After` header for the number of seconds to wait before retrying.
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@yuiseki/gyazocli",
3
+ "version": "0.0.1",
4
+ "description": "Gyazo Memory CLI for AI Secretary",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "gyazo": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "docs"
13
+ ],
14
+ "directories": {
15
+ "doc": "docs"
16
+ },
17
+ "scripts": {
18
+ "build": "npx tsc",
19
+ "prepack": "npm run build",
20
+ "test": "npm run build && vitest run",
21
+ "test:watch": "vitest"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "keywords": [],
27
+ "author": "",
28
+ "license": "ISC",
29
+ "type": "commonjs",
30
+ "dependencies": {
31
+ "axios": "^1.13.5",
32
+ "commander": "^14.0.3",
33
+ "dotenv": "^17.3.1",
34
+ "zod": "^4.3.6"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^25.3.0",
38
+ "ts-node": "^10.9.2",
39
+ "typescript": "^5.9.3",
40
+ "vitest": "^4.0.18"
41
+ }
42
+ }