@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.
- package/README.md +42 -0
- package/dist/api.js +67 -0
- package/dist/config.js +25 -0
- package/dist/credentials.js +60 -0
- package/dist/index.js +1903 -0
- package/dist/storage.js +107 -0
- package/docs/ADR/001-credentials.md +38 -0
- package/docs/ADR/002-caching-strategy.md +77 -0
- package/docs/ADR/003-cli-structure.md +112 -0
- package/docs/API/gyazo_api.md +66 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @yuiseki/gyazocli
|
|
2
|
+
|
|
3
|
+
Gyazo Memory CLI for AI Secretary.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g @yuiseki/gyazocli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
gyazo config set token your_personal_gyazo_access_token_here
|
|
15
|
+
gyazo sync --days 10
|
|
16
|
+
gyazo --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Development
|
|
20
|
+
|
|
21
|
+
### Build
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install
|
|
25
|
+
npm run build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Link local CLI with npm link
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# from this repository root
|
|
32
|
+
npm link
|
|
33
|
+
|
|
34
|
+
# verify linked command
|
|
35
|
+
gyazo --version
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Unlink when finished:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm unlink -g @yuiseki/gyazocli
|
|
42
|
+
```
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
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.listImages = listImages;
|
|
7
|
+
exports.getImageDetail = getImageDetail;
|
|
8
|
+
exports.searchImages = searchImages;
|
|
9
|
+
exports.getCurrentUser = getCurrentUser;
|
|
10
|
+
exports.uploadImage = uploadImage;
|
|
11
|
+
const axios_1 = __importDefault(require("axios"));
|
|
12
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const API_BASE_URL = 'https://api.gyazo.com/api/images';
|
|
15
|
+
const API_SEARCH_URL = 'https://api.gyazo.com/api/search';
|
|
16
|
+
const API_USERS_ME_URL = 'https://api.gyazo.com/api/users/me';
|
|
17
|
+
const API_UPLOAD_URL = 'https://upload.gyazo.com/api/upload';
|
|
18
|
+
async function requestWithRetry(url, params = {}) {
|
|
19
|
+
const headers = { Authorization: `Bearer ${config_1.config.GYAZO_ACCESS_TOKEN}` };
|
|
20
|
+
try {
|
|
21
|
+
const response = await axios_1.default.get(url, { headers, params });
|
|
22
|
+
return response.data;
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error.response && error.response.status === 429) {
|
|
26
|
+
const retryAfter = parseInt(error.response.headers['retry-after'] || '5', 10);
|
|
27
|
+
console.warn(`Rate limited. Retrying after ${retryAfter} seconds...`);
|
|
28
|
+
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
|
|
29
|
+
return requestWithRetry(url, params);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function listImages(page = 1, perPage = 20) {
|
|
35
|
+
return requestWithRetry(API_BASE_URL, { page, per_page: perPage });
|
|
36
|
+
}
|
|
37
|
+
async function getImageDetail(imageId) {
|
|
38
|
+
return requestWithRetry(`${API_BASE_URL}/${imageId}`);
|
|
39
|
+
}
|
|
40
|
+
async function searchImages(query, page = 1, perPage = 20) {
|
|
41
|
+
return requestWithRetry(API_SEARCH_URL, { query, page, per: perPage });
|
|
42
|
+
}
|
|
43
|
+
async function getCurrentUser() {
|
|
44
|
+
return requestWithRetry(API_USERS_ME_URL);
|
|
45
|
+
}
|
|
46
|
+
async function uploadImage(options) {
|
|
47
|
+
const form = new form_data_1.default();
|
|
48
|
+
form.append('access_token', config_1.config.GYAZO_ACCESS_TOKEN || '');
|
|
49
|
+
form.append('imagedata', options.imageData, {
|
|
50
|
+
filename: options.filename || 'upload.bin',
|
|
51
|
+
});
|
|
52
|
+
if (options.title)
|
|
53
|
+
form.append('title', options.title);
|
|
54
|
+
if (options.app)
|
|
55
|
+
form.append('app', options.app);
|
|
56
|
+
if (options.refererUrl)
|
|
57
|
+
form.append('referer_url', options.refererUrl);
|
|
58
|
+
if (options.desc)
|
|
59
|
+
form.append('desc', options.desc);
|
|
60
|
+
if (typeof options.timestamp === 'number') {
|
|
61
|
+
form.append('created_at', String(options.timestamp));
|
|
62
|
+
}
|
|
63
|
+
const response = await axios_1.default.post(API_UPLOAD_URL, form, {
|
|
64
|
+
headers: form.getHeaders(),
|
|
65
|
+
});
|
|
66
|
+
return response.data;
|
|
67
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
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.config = void 0;
|
|
7
|
+
exports.setAccessToken = setAccessToken;
|
|
8
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
+
const zod_1 = require("zod");
|
|
10
|
+
dotenv_1.default.config({ quiet: true });
|
|
11
|
+
const configSchema = zod_1.z.object({
|
|
12
|
+
GYAZO_ACCESS_TOKEN: zod_1.z.string().optional(),
|
|
13
|
+
GYAZO_CLIENT_ID: zod_1.z.string().optional(),
|
|
14
|
+
GYAZO_CLIENT_SECRET: zod_1.z.string().optional(),
|
|
15
|
+
GYAZO_CACHE_DIR: zod_1.z.string().optional(),
|
|
16
|
+
});
|
|
17
|
+
exports.config = configSchema.parse({
|
|
18
|
+
GYAZO_ACCESS_TOKEN: process.env.GYAZO_ACCESS_TOKEN,
|
|
19
|
+
GYAZO_CLIENT_ID: process.env.GYAZO_CLIENT_ID,
|
|
20
|
+
GYAZO_CLIENT_SECRET: process.env.GYAZO_CLIENT_SECRET,
|
|
21
|
+
GYAZO_CACHE_DIR: process.env.GYAZO_CACHE_DIR,
|
|
22
|
+
});
|
|
23
|
+
function setAccessToken(token) {
|
|
24
|
+
exports.config.GYAZO_ACCESS_TOKEN = token;
|
|
25
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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.getStoredConfig = getStoredConfig;
|
|
7
|
+
exports.setStoredConfig = setStoredConfig;
|
|
8
|
+
exports.ensureAccessToken = ensureAccessToken;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const config_1 = require("./config");
|
|
13
|
+
const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.config', 'gyazo');
|
|
14
|
+
const CREDENTIALS_FILE = path_1.default.join(CONFIG_DIR, 'credentials.json');
|
|
15
|
+
function loadStoredCredentials() {
|
|
16
|
+
if (fs_1.default.existsSync(CREDENTIALS_FILE)) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(fs_1.default.readFileSync(CREDENTIALS_FILE, 'utf-8'));
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
function getStoredConfig(key) {
|
|
27
|
+
const stored = loadStoredCredentials();
|
|
28
|
+
if (key === 'token')
|
|
29
|
+
return stored.GYAZO_ACCESS_TOKEN;
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function setStoredConfig(key, value) {
|
|
33
|
+
const stored = loadStoredCredentials();
|
|
34
|
+
if (key === 'token') {
|
|
35
|
+
stored.GYAZO_ACCESS_TOKEN = value;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.error(`Unknown config key: ${key}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
if (!fs_1.default.existsSync(CONFIG_DIR)) {
|
|
42
|
+
fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
fs_1.default.writeFileSync(CREDENTIALS_FILE, JSON.stringify(stored, null, 2));
|
|
45
|
+
console.error(`Config set: ${key}=${value}`);
|
|
46
|
+
}
|
|
47
|
+
async function ensureAccessToken() {
|
|
48
|
+
if (config_1.config.GYAZO_ACCESS_TOKEN) {
|
|
49
|
+
return config_1.config.GYAZO_ACCESS_TOKEN;
|
|
50
|
+
}
|
|
51
|
+
const storedToken = getStoredConfig('token');
|
|
52
|
+
if (storedToken) {
|
|
53
|
+
(0, config_1.setAccessToken)(storedToken);
|
|
54
|
+
return storedToken;
|
|
55
|
+
}
|
|
56
|
+
console.error('Error: Gyazo Access Token is not set.');
|
|
57
|
+
console.error('Please run the following command to set your access token:');
|
|
58
|
+
console.error(' gyazo config set token <your_access_token>');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|