unlimit-keys 0.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 ADDED
@@ -0,0 +1,64 @@
1
+ # unlimit-keys
2
+
3
+ A generic, distributed API key rotator that uses Redis to select the least recently used (LRU) key from a pool. Ideal for distributing load across multiple API keys to avoid rate limits, regardless of the provider (Google, OpenAI, Anthropic, etc.).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install unlimit-keys
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ### 1. Environment Variables
14
+
15
+ Create a `.env.local` file with your Redis credentials and API keys:
16
+
17
+ ```env
18
+ # Upstash Redis credentials
19
+ REDIS_URL=https://your-redis-url.upstash.io
20
+ REDIS_TOKEN=your-token
21
+
22
+ # Comma-separated or newline-separated API keys
23
+ RU_API_KEYS="key1,key2,key3"
24
+ ```
25
+
26
+ ### 2. Sync Keys to Redis
27
+
28
+ Run the sync script to load your API keys into the global Redis pool:
29
+
30
+ ```bash
31
+ npx unlimit-keys sync
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Programmatic API
37
+
38
+ ```typescript
39
+ import { getLeastUsedKey } from 'unlimit-keys';
40
+
41
+ // Get the least recently used key
42
+ const apiKey = await getLeastUsedKey();
43
+ console.log(apiKey);
44
+ ```
45
+
46
+ ### CLI
47
+
48
+ ```bash
49
+ # Get the least recently used key via CLI
50
+ npx unlimit-keys get-key
51
+ ```
52
+
53
+ ## How It Works
54
+
55
+ 1. **Key Pool**: Keys are stored in a single Redis Sorted Set (`ru_api_keys`).
56
+ 2. **LRU Rotation**: When you request a key, the library atomically:
57
+ * Finds the key with the lowest score (oldest usage timestamp).
58
+ * Updates that key's score to the current timestamp.
59
+ * Returns the key.
60
+ 3. **Concurrency**: Atomic Lua scripts ensure no race conditions, even with high concurrency across serverless functions.
61
+
62
+ ## License
63
+
64
+ ISC
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const commander_1 = require("commander");
14
+ const getLeastUsedKey_1 = require("./schema/getLeastUsedKey");
15
+ const program = new commander_1.Command();
16
+ program
17
+ .name('unlimit-keys')
18
+ .description('Distributed LRU API key rotation using Redis')
19
+ .version('0.1.0');
20
+ program
21
+ .command('get-key')
22
+ .description('Get the least recently used API key')
23
+ .action(() => __awaiter(void 0, void 0, void 0, function* () {
24
+ try {
25
+ const key = yield (0, getLeastUsedKey_1.getLeastUsedKey)();
26
+ console.log(key);
27
+ }
28
+ catch (error) {
29
+ console.error('Error:', error instanceof Error ? error.message : error);
30
+ process.exit(1);
31
+ }
32
+ }));
33
+ program
34
+ .command('sync')
35
+ .description('Sync API keys from RU_API_KEYS env var to Redis')
36
+ .action(() => {
37
+ console.log('Please run: npx unlimit-keys sync');
38
+ console.log('Or: node dist/scripts/sync-api-keys.js');
39
+ });
40
+ program.parse();
41
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,yCAAoC;AACpC,8DAA2D;AAE3D,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,cAAc,CAAC;KACpB,WAAW,CAAC,8CAA8C,CAAC;KAC3D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,qCAAqC,CAAC;KAClD,MAAM,CAAC,GAAS,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAA,iCAAe,GAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAA,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,iDAAiD,CAAC;KAC9D,MAAM,CAAC,GAAG,EAAE;IACX,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { getLeastUsedKey } from './schema/getLeastUsedKey';
2
+ export { redis } from './schema/redis';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.redis = exports.getLeastUsedKey = void 0;
4
+ var getLeastUsedKey_1 = require("./schema/getLeastUsedKey");
5
+ Object.defineProperty(exports, "getLeastUsedKey", { enumerable: true, get: function () { return getLeastUsedKey_1.getLeastUsedKey; } });
6
+ var redis_1 = require("./schema/redis");
7
+ Object.defineProperty(exports, "redis", { enumerable: true, get: function () { return redis_1.redis; } });
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,4DAA2D;AAAlD,kHAAA,eAAe,OAAA;AACxB,wCAAuC;AAA9B,8FAAA,KAAK,OAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Fetches the least recently used API key from the global pool.
3
+ * Automatically updates the key's usage timestamp.
4
+ *
5
+ * @returns The API key to be used.
6
+ * @throws Will throw an error if no keys are found.
7
+ */
8
+ export declare function getLeastUsedKey(): Promise<string>;
9
+ //# sourceMappingURL=getLeastUsedKey.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getLeastUsedKey.d.ts","sourceRoot":"","sources":["../../src/schema/getLeastUsedKey.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAwBvD"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.getLeastUsedKey = getLeastUsedKey;
13
+ const redis_1 = require("./redis");
14
+ const KEY_SET = 'ru_api_keys';
15
+ /**
16
+ * Fetches the least recently used API key from the global pool.
17
+ * Automatically updates the key's usage timestamp.
18
+ *
19
+ * @returns The API key to be used.
20
+ * @throws Will throw an error if no keys are found.
21
+ */
22
+ function getLeastUsedKey() {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ const currentTime = Date.now() / 1000;
25
+ // Atomic Lua script:
26
+ // 1. Get the key with the lowest score (least recently used)
27
+ // 2. If a key exists, update its score to current time
28
+ // 3. Return the key
29
+ const script = `
30
+ local key = redis.call('zrange', KEYS[1], 0, 0)[1]
31
+ if key then
32
+ redis.call('zadd', KEYS[1], ARGV[1], key)
33
+ return key
34
+ else
35
+ return nil
36
+ end
37
+ `;
38
+ const key = yield redis_1.redis.eval(script, [KEY_SET], [currentTime]);
39
+ if (!key) {
40
+ throw new Error('No API keys found in Redis. Please check your RU_API_KEYS environment variable and run the sync script.');
41
+ }
42
+ return key;
43
+ });
44
+ }
45
+ //# sourceMappingURL=getLeastUsedKey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getLeastUsedKey.js","sourceRoot":"","sources":["../../src/schema/getLeastUsedKey.ts"],"names":[],"mappings":";;;;;;;;;;;AAWA,0CAwBC;AAnCD,mCAAgC;AAEhC,MAAM,OAAO,GAAG,aAAa,CAAC;AAE9B;;;;;;GAMG;AACH,SAAsB,eAAe;;QACjC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAEtC,qBAAqB;QACrB,6DAA6D;QAC7D,uDAAuD;QACvD,oBAAoB;QACpB,MAAM,MAAM,GAAG;;;;;;;;KAQd,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,aAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,CAAkB,CAAC;QAEhF,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,yGAAyG,CAAC,CAAC;QAC/H,CAAC;QAED,OAAO,GAAG,CAAC;IACf,CAAC;CAAA"}
@@ -0,0 +1,3 @@
1
+ import { Redis } from '@upstash/redis';
2
+ export declare const redis: Redis;
3
+ //# sourceMappingURL=redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/schema/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAMvC,eAAO,MAAM,KAAK,OAGhB,CAAC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.redis = void 0;
4
+ const redis_1 = require("@upstash/redis");
5
+ if (!process.env.REDIS_URL || !process.env.REDIS_TOKEN) {
6
+ throw new Error('Missing Redis credentials. Please set REDIS_URL and REDIS_TOKEN.');
7
+ }
8
+ exports.redis = new redis_1.Redis({
9
+ url: process.env.REDIS_URL,
10
+ token: process.env.REDIS_TOKEN,
11
+ });
12
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/schema/redis.ts"],"names":[],"mappings":";;;AAAA,0CAAuC;AAEvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;IACvD,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;AACtF,CAAC;AAEY,QAAA,KAAK,GAAG,IAAI,aAAK,CAAC;IAC7B,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;IAC1B,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;CAC/B,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import 'dotenv/config';
2
+ //# sourceMappingURL=sync-api-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-api-keys.d.ts","sourceRoot":"","sources":["../../src/scripts/sync-api-keys.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require("dotenv/config");
13
+ const redis_1 = require("@upstash/redis");
14
+ // Note: checking for properties on process.env needs type assertions or simply checking existence
15
+ if (!process.env.REDIS_URL || !process.env.REDIS_TOKEN) {
16
+ throw new Error('Missing Upstash Redis credentials in .env file');
17
+ }
18
+ const redis = new redis_1.Redis({
19
+ url: process.env.REDIS_URL,
20
+ token: process.env.REDIS_TOKEN,
21
+ });
22
+ const KEY_SET = 'ru_api_keys';
23
+ function syncApiKeys() {
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ console.log('Syncing API keys from RU_API_KEYS...');
26
+ const keysString = process.env.RU_API_KEYS || '';
27
+ const keysFromEnv = keysString.split(/[\n,]/).map(k => k.trim()).filter(Boolean);
28
+ if (keysFromEnv.length === 0) {
29
+ console.warn('No RU_API_KEYS found in environment variables.');
30
+ return;
31
+ }
32
+ const pipeline = redis.pipeline();
33
+ // Get existing keys
34
+ const keysInRedis = yield redis.zrange(KEY_SET, 0, -1);
35
+ // Determine additions and removals
36
+ const keysToAdd = keysFromEnv.filter(k => !keysInRedis.includes(k));
37
+ const keysToRemove = keysInRedis.filter(k => !keysFromEnv.includes(k));
38
+ if (keysToAdd.length > 0) {
39
+ console.log(`Adding ${keysToAdd.length} new keys.`);
40
+ for (const key of keysToAdd) {
41
+ // Add with score 0 (least used)
42
+ pipeline.zadd(KEY_SET, { score: 0, member: key });
43
+ }
44
+ }
45
+ if (keysToRemove.length > 0) {
46
+ console.log(`Removing ${keysToRemove.length} stale keys.`);
47
+ pipeline.zrem(KEY_SET, ...keysToRemove);
48
+ }
49
+ if (keysToAdd.length === 0 && keysToRemove.length === 0) {
50
+ console.log('Keys are already in sync.');
51
+ }
52
+ else {
53
+ yield pipeline.exec();
54
+ console.log('Sync complete.');
55
+ }
56
+ });
57
+ }
58
+ syncApiKeys().catch((err) => {
59
+ console.error('Error syncing keys:', err);
60
+ process.exit(1);
61
+ });
62
+ //# sourceMappingURL=sync-api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-api-keys.js","sourceRoot":"","sources":["../../src/scripts/sync-api-keys.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,yBAAuB;AACvB,0CAAuC;AAEvC,kGAAkG;AAClG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;IACrD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,aAAK,CAAC;IACpB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;IAC1B,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;CACjC,CAAC,CAAC;AAEH,MAAM,OAAO,GAAG,aAAa,CAAC;AAE9B,SAAe,WAAW;;QACtB,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;QAEpD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;QACjD,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEjF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;YAC/D,OAAO;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAElC,oBAAoB;QACpB,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAa,CAAC;QAEnE,mCAAmC;QACnC,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAEvE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,UAAU,SAAS,CAAC,MAAM,YAAY,CAAC,CAAC;YACpD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;gBAC1B,gCAAgC;gBAChC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACtD,CAAC;QACL,CAAC;QAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,YAAY,YAAY,CAAC,MAAM,cAAc,CAAC,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACJ,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAClC,CAAC;IACL,CAAC;CAAA;AAED,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACxB,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "unlimit-keys",
3
+ "version": "0.1.0",
4
+ "description": "An API key rotation system using Redis",
5
+ "author": "Anirban Majumder <anirbanmajumder.404@gmail.com>",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/Anirban-Majumder/unlimit-keys.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/Anirban-Majumder/unlimit-keys/issues"
12
+ },
13
+ "homepage": "https://github.com/Anirban-Majumder/unlimit-keys#readme",
14
+ "license": "MIT",
15
+ "keywords": [
16
+ "rate-limit",
17
+ "api-key",
18
+ "redis",
19
+ "upstash",
20
+ "key-rotation",
21
+ "lru",
22
+ "keys"
23
+ ],
24
+ "main": "dist/index.js",
25
+ "types": "dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "require": "./dist/index.js",
30
+ "import": "./dist/index.js"
31
+ }
32
+ },
33
+ "bin": {
34
+ "unlimit-keys": "./dist/cli.js"
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc",
41
+ "prepublishOnly": "npm run build",
42
+ "sync": "node dist/scripts/sync-api-keys.js",
43
+ "test": "vitest run",
44
+ "publish-pkg": "npm publish --access public"
45
+ },
46
+ "type": "commonjs",
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "dependencies": {
51
+ "@upstash/redis": ">=1.0.0",
52
+ "commander": "^14.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^24.0.15",
56
+ "ts-node": "^10.9.2",
57
+ "typescript": "^5.8.3",
58
+ "vitest": "^4.0.17"
59
+ },
60
+ "peerDependencies": {
61
+ "@upstash/redis": ">=1.0.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "@upstash/redis": {
65
+ "optional": true
66
+ }
67
+ }
68
+ }