@kyoji2/raindrop-cli
Advanced tools
+1
-1
| { | ||
| "name": "@kyoji2/raindrop-cli", | ||
| "version": "0.1.2", | ||
| "version": "0.1.4", | ||
| "description": "AI-native CLI for Raindrop.io", | ||
@@ -5,0 +5,0 @@ "author": "kyoji2", |
+6
-1
| # raindrop-cli | ||
| AI-native CLI for Raindrop.io. Built with TypeScript and Bun. | ||
| AI-native CLI for Raindrop.io. Built with TypeScript, using Bun for tooling and Node standard APIs in the codebase. | ||
@@ -15,2 +15,7 @@ Designed for AI agents and automation scripts. **TOON** format for maximum token efficiency, with optional JSON output for standard integrations. | ||
| ## Runtime Notes | ||
| - Prefer Node standard APIs (`node:fs`, `node:timers/promises`, `node:child_process`) over `Bun.*` in source code. | ||
| - Tests still run with `bun test`. | ||
| ## Installation | ||
@@ -17,0 +22,0 @@ |
@@ -0,1 +1,5 @@ | ||
| import { Blob } from "node:buffer"; | ||
| import { readFile } from "node:fs/promises"; | ||
| import { basename } from "node:path"; | ||
| import { setTimeout as delay } from "node:timers/promises"; | ||
| import type { z } from "zod"; | ||
@@ -184,3 +188,3 @@ import { | ||
| ); | ||
| await Bun.sleep(backoff); | ||
| await delay(backoff); | ||
| continue; | ||
@@ -203,3 +207,3 @@ } | ||
| ); | ||
| await Bun.sleep(backoff); | ||
| await delay(backoff); | ||
| continue; | ||
@@ -250,3 +254,3 @@ } | ||
| ); | ||
| await Bun.sleep(backoff); | ||
| await delay(backoff); | ||
| } | ||
@@ -361,5 +365,5 @@ } | ||
| const file = Bun.file(filePath); | ||
| const fileBuffer = await readFile(filePath); | ||
| const formData = new FormData(); | ||
| formData.append("cover", file); | ||
| formData.append("cover", new Blob([fileBuffer]), basename(filePath)); | ||
@@ -366,0 +370,0 @@ const response = await fetch(`${RaindropAPI.BASE_URL}/collection/${id}/cover`, { |
@@ -0,1 +1,2 @@ | ||
| import { rm, writeFile } from "node:fs/promises"; | ||
| import type { CollectionCreate, CollectionUpdate } from "../api"; | ||
@@ -199,3 +200,3 @@ import { type GlobalOptions, output, outputError } from "../utils/output"; | ||
| filePath = getTempFilePath("raindrop_cover", ".png"); | ||
| await Bun.write(filePath, await response.arrayBuffer()); | ||
| await writeFile(filePath, Buffer.from(await response.arrayBuffer())); | ||
| stopSpinner(spinner, true, "Downloaded"); | ||
@@ -208,3 +209,3 @@ isTemp = true; | ||
| if (isTemp) { | ||
| await Bun.$`rm -f ${filePath}`; | ||
| await rm(filePath, { force: true }); | ||
| } | ||
@@ -251,3 +252,3 @@ | ||
| const filePath = getTempFilePath("raindrop_icon", ".png"); | ||
| await Bun.write(filePath, await response.arrayBuffer()); | ||
| await writeFile(filePath, Buffer.from(await response.arrayBuffer())); | ||
| stopSpinner(spinner, true, "Downloaded"); | ||
@@ -257,5 +258,5 @@ | ||
| await Bun.$`rm -f ${filePath}`; | ||
| await rm(filePath, { force: true }); | ||
| output(result, options.format); | ||
| } |
+15
-1
| #!/usr/bin/env bun | ||
| import { readFile } from "node:fs/promises"; | ||
| import { Command } from "commander"; | ||
@@ -38,4 +39,17 @@ import { RaindropError } from "./api"; | ||
| const VERSION = "0.1.0"; | ||
| async function getPackageVersion(): Promise<string> { | ||
| try { | ||
| const text = await readFile(new URL("../package.json", import.meta.url), "utf-8"); | ||
| const pkg = JSON.parse(text) as { version?: unknown }; | ||
| if (pkg && typeof pkg === "object" && typeof pkg.version === "string" && pkg.version.trim().length > 0) { | ||
| return pkg.version; | ||
| } | ||
| } catch {} | ||
| return "0.0.0"; | ||
| } | ||
| const VERSION = await getPackageVersion(); | ||
| function getGlobalOptions(cmd: Command): GlobalOptions { | ||
@@ -42,0 +56,0 @@ const opts = cmd.optsWithGlobals(); |
+18
-10
| import { existsSync, readFileSync } from "node:fs"; | ||
| import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; | ||
| import { homedir } from "node:os"; | ||
@@ -25,9 +26,19 @@ import { join } from "node:path"; | ||
| function isNotFoundError(error: unknown): error is { code?: string } { | ||
| if (typeof error !== "object" || error === null || !("code" in error)) { | ||
| return false; | ||
| } | ||
| return (error as { code?: string }).code === "ENOENT"; | ||
| } | ||
| export async function loadConfig(): Promise<Config | null> { | ||
| try { | ||
| const file = Bun.file(CONFIG_FILE); | ||
| const exists = await file.exists(); | ||
| if (!exists) return null; | ||
| let text = ""; | ||
| try { | ||
| text = await readFile(CONFIG_FILE, "utf-8"); | ||
| } catch (error) { | ||
| if (isNotFoundError(error)) return null; | ||
| throw error; | ||
| } | ||
| const text = await file.text(); | ||
| if (!text.trim()) return null; | ||
@@ -78,4 +89,4 @@ | ||
| await Bun.$`mkdir -p ${CONFIG_DIR}`; | ||
| await Bun.write(CONFIG_FILE, JSON.stringify(config, null, 2)); | ||
| await mkdir(CONFIG_DIR, { recursive: true }); | ||
| await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2)); | ||
| } | ||
@@ -85,6 +96,3 @@ | ||
| try { | ||
| const file = Bun.file(CONFIG_FILE); | ||
| if (await file.exists()) { | ||
| await Bun.$`rm -f ${CONFIG_FILE}`; | ||
| } | ||
| await rm(CONFIG_FILE, { force: true }); | ||
| } catch {} | ||
@@ -91,0 +99,0 @@ } |
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
817348
0.25%22235
0.18%231
2.21%43
10.26%