openpets 1.0.4

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/deploy-pet.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { join, dirname } from 'path'
2
+ import { fileURLToPath } from 'url'
3
+ import { existsSync, readdirSync, readFileSync, writeFileSync, statSync, mkdirSync } from 'fs'
4
+ import { buildPet } from './build-pet.js'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = dirname(__filename)
8
+ const projectRoot = dirname(dirname(__dirname))
9
+
10
+ interface PetData {
11
+ name: string
12
+ version: string
13
+ description: string
14
+ source_code_url: string
15
+ [key: string]: any
16
+ }
17
+
18
+ export async function deployPet(petName?: string): Promise<void> {
19
+ if (!petName) {
20
+ const cwd = process.cwd()
21
+ const petsDir = join(projectRoot, 'pets')
22
+
23
+ if (cwd.includes('/pets/')) {
24
+ petName = cwd.split('/pets/')[1].split('/')[0]
25
+ console.log(`📦 Auto-detected pet: ${petName}`)
26
+ }
27
+
28
+ if (!petName) {
29
+ console.error('Usage: pets deploy <pet-name>')
30
+ console.error('Example: pets deploy postgres')
31
+ console.error('')
32
+ console.error('Available pets:')
33
+ if (existsSync(petsDir)) {
34
+ const pets = readdirSync(petsDir).filter(dir => {
35
+ const petPath = join(petsDir, dir)
36
+ return statSync(petPath).isDirectory() && dir !== '_TEMPLATE_'
37
+ })
38
+ pets.forEach(pet => console.error(` - ${pet}`))
39
+ }
40
+ process.exit(1)
41
+ }
42
+ }
43
+
44
+ console.log(`🚀 Deploying ${petName}...`)
45
+
46
+ await buildPet(petName)
47
+
48
+ const petDir = join(projectRoot, 'pets', petName)
49
+ const packageJsonPath = join(petDir, 'package.json')
50
+
51
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
52
+
53
+ const sourceCodeUrl = `https://github.com/raggle-ai/pets/tree/main/pets/${petName}`
54
+
55
+ const fieldsToExclude = ['$schema', 'version', 'main', 'types', 'scripts', 'license', 'repository', 'dependencies', 'author', 'utils', 'devDependencies']
56
+
57
+ const filteredData = Object.keys(packageJson).reduce((acc, key) => {
58
+ if (!fieldsToExclude.includes(key)) {
59
+ acc[key] = packageJson[key]
60
+ }
61
+ return acc
62
+ }, {} as Record<string, any>)
63
+
64
+ const petData = {
65
+ ...filteredData,
66
+ name: packageJson.name,
67
+ version: packageJson.version,
68
+ description: packageJson.description,
69
+ source_code_url: sourceCodeUrl,
70
+ } as PetData
71
+
72
+ const dataDir = join(projectRoot, 'data')
73
+ if (!existsSync(dataDir)) {
74
+ mkdirSync(dataDir, { recursive: true })
75
+ }
76
+
77
+ const petsJsonPath = join(dataDir, 'pets.json')
78
+ let petsData: Record<string, PetData> = {}
79
+
80
+ if (existsSync(petsJsonPath)) {
81
+ petsData = JSON.parse(readFileSync(petsJsonPath, 'utf8'))
82
+ }
83
+
84
+ petsData[packageJson.name] = petData
85
+
86
+ writeFileSync(petsJsonPath, JSON.stringify(petsData, null, 2), 'utf8')
87
+
88
+ console.log(`✅ ${petName} deployed successfully!`)
89
+ console.log(` Source URL: ${sourceCodeUrl}`)
90
+ console.log(` Data saved to: ${petsJsonPath}`)
91
+ }
package/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from './plugin-factory'
2
+ export * from './schema-helpers'
3
+ export * from './validate-pet'
4
+ export * from './pets-registry'
5
+ export * from './build-pet'
6
+ export * from './types'
7
+ export * from './ai-client-base'
8
+ export * from './search-pets'
9
+ export * from './logger'
10
+ export * from './local-cache'
package/local-cache.ts ADDED
@@ -0,0 +1,280 @@
1
+ export interface CacheEntry<T> {
2
+ data: T
3
+ timestamp: number
4
+ hits: number
5
+ }
6
+
7
+ export interface CacheStats {
8
+ hits: number
9
+ misses: number
10
+ size: number
11
+ oldestEntry?: number
12
+ newestEntry?: number
13
+ }
14
+
15
+ export interface CacheConfig {
16
+ ttl?: number
17
+ maxSize?: number
18
+ cleanupInterval?: number
19
+ }
20
+
21
+ export class LocalCache<T = any> {
22
+ private cache: Map<string, CacheEntry<T>> = new Map()
23
+ private stats = { hits: 0, misses: 0 }
24
+ private ttl: number
25
+ private maxSize: number
26
+ private cleanupInterval: number | null
27
+ private cleanupTimer: NodeJS.Timeout | null = null
28
+
29
+ constructor(config: CacheConfig = {}) {
30
+ this.ttl = config.ttl || 3600000
31
+ this.maxSize = config.maxSize || 1000
32
+ this.cleanupInterval = config.cleanupInterval || null
33
+
34
+ if (this.cleanupInterval) {
35
+ this.startCleanup()
36
+ }
37
+ }
38
+
39
+ private startCleanup(): void {
40
+ if (this.cleanupInterval) {
41
+ this.cleanupTimer = setInterval(() => {
42
+ this.cleanup()
43
+ }, this.cleanupInterval)
44
+ }
45
+ }
46
+
47
+ private stopCleanup(): void {
48
+ if (this.cleanupTimer) {
49
+ clearInterval(this.cleanupTimer)
50
+ this.cleanupTimer = null
51
+ }
52
+ }
53
+
54
+ get(key: string): T | null {
55
+ const entry = this.cache.get(key)
56
+
57
+ if (!entry) {
58
+ this.stats.misses++
59
+ return null
60
+ }
61
+
62
+ const now = Date.now()
63
+ if (now - entry.timestamp > this.ttl) {
64
+ this.cache.delete(key)
65
+ this.stats.misses++
66
+ return null
67
+ }
68
+
69
+ entry.hits++
70
+ this.stats.hits++
71
+ return entry.data
72
+ }
73
+
74
+ set(key: string, data: T): void {
75
+ if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
76
+ this.evictOldest()
77
+ }
78
+
79
+ this.cache.set(key, {
80
+ data,
81
+ timestamp: Date.now(),
82
+ hits: 0
83
+ })
84
+ }
85
+
86
+ has(key: string): boolean {
87
+ const entry = this.cache.get(key)
88
+ if (!entry) return false
89
+
90
+ const now = Date.now()
91
+ if (now - entry.timestamp > this.ttl) {
92
+ this.cache.delete(key)
93
+ return false
94
+ }
95
+
96
+ return true
97
+ }
98
+
99
+ delete(key: string): boolean {
100
+ return this.cache.delete(key)
101
+ }
102
+
103
+ clear(): void {
104
+ this.cache.clear()
105
+ this.stats = { hits: 0, misses: 0 }
106
+ }
107
+
108
+ cleanup(): number {
109
+ const now = Date.now()
110
+ let removed = 0
111
+
112
+ for (const [key, entry] of this.cache.entries()) {
113
+ if (now - entry.timestamp > this.ttl) {
114
+ this.cache.delete(key)
115
+ removed++
116
+ }
117
+ }
118
+
119
+ return removed
120
+ }
121
+
122
+ private evictOldest(): void {
123
+ let oldestKey: string | null = null
124
+ let oldestTime = Infinity
125
+
126
+ for (const [key, entry] of this.cache.entries()) {
127
+ if (entry.timestamp < oldestTime) {
128
+ oldestTime = entry.timestamp
129
+ oldestKey = key
130
+ }
131
+ }
132
+
133
+ if (oldestKey) {
134
+ this.cache.delete(oldestKey)
135
+ }
136
+ }
137
+
138
+ getStats(): CacheStats {
139
+ const entries = Array.from(this.cache.values())
140
+ const timestamps = entries.map(e => e.timestamp)
141
+
142
+ return {
143
+ hits: this.stats.hits,
144
+ misses: this.stats.misses,
145
+ size: this.cache.size,
146
+ oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : undefined,
147
+ newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : undefined
148
+ }
149
+ }
150
+
151
+ keys(): string[] {
152
+ return Array.from(this.cache.keys())
153
+ }
154
+
155
+ values(): T[] {
156
+ return Array.from(this.cache.values()).map(entry => entry.data)
157
+ }
158
+
159
+ entries(): Array<[string, T]> {
160
+ return Array.from(this.cache.entries()).map(([key, entry]) => [key, entry.data])
161
+ }
162
+
163
+ getHitRate(): number {
164
+ const total = this.stats.hits + this.stats.misses
165
+ return total === 0 ? 0 : this.stats.hits / total
166
+ }
167
+
168
+ destroy(): void {
169
+ this.stopCleanup()
170
+ this.clear()
171
+ }
172
+ }
173
+
174
+ export class SingleValueCache<T = any> {
175
+ private data: T | null = null
176
+ private timestamp: number = 0
177
+ private ttl: number
178
+ private hits: number = 0
179
+ private misses: number = 0
180
+
181
+ constructor(config: { ttl?: number } = {}) {
182
+ this.ttl = config.ttl || 3600000
183
+ }
184
+
185
+ get(): T | null {
186
+ if (!this.data) {
187
+ this.misses++
188
+ return null
189
+ }
190
+
191
+ const now = Date.now()
192
+ if (now - this.timestamp > this.ttl) {
193
+ this.data = null
194
+ this.misses++
195
+ return null
196
+ }
197
+
198
+ this.hits++
199
+ return this.data
200
+ }
201
+
202
+ set(data: T): void {
203
+ this.data = data
204
+ this.timestamp = Date.now()
205
+ }
206
+
207
+ has(): boolean {
208
+ if (!this.data) return false
209
+
210
+ const now = Date.now()
211
+ if (now - this.timestamp > this.ttl) {
212
+ this.data = null
213
+ return false
214
+ }
215
+
216
+ return true
217
+ }
218
+
219
+ clear(): void {
220
+ this.data = null
221
+ this.timestamp = 0
222
+ }
223
+
224
+ getAge(): number {
225
+ if (!this.data) return -1
226
+ return Date.now() - this.timestamp
227
+ }
228
+
229
+ getStats(): { hits: number; misses: number; hasData: boolean; age: number } {
230
+ return {
231
+ hits: this.hits,
232
+ misses: this.misses,
233
+ hasData: this.has(),
234
+ age: this.getAge()
235
+ }
236
+ }
237
+
238
+ getHitRate(): number {
239
+ const total = this.hits + this.misses
240
+ return total === 0 ? 0 : this.hits / total
241
+ }
242
+ }
243
+
244
+ export async function withCache<T>(
245
+ cache: LocalCache<T> | SingleValueCache<T>,
246
+ key: string | null,
247
+ fetcher: () => Promise<T>
248
+ ): Promise<T> {
249
+ if (cache instanceof SingleValueCache) {
250
+ const cached = cache.get()
251
+ if (cached !== null) {
252
+ return cached
253
+ }
254
+
255
+ const data = await fetcher()
256
+ cache.set(data)
257
+ return data
258
+ }
259
+
260
+ if (key === null) {
261
+ throw new Error('Key is required for LocalCache')
262
+ }
263
+
264
+ const cached = cache.get(key)
265
+ if (cached !== null) {
266
+ return cached
267
+ }
268
+
269
+ const data = await fetcher()
270
+ cache.set(key, data)
271
+ return data
272
+ }
273
+
274
+ export function createCache<T = any>(config: CacheConfig = {}): LocalCache<T> {
275
+ return new LocalCache<T>(config)
276
+ }
277
+
278
+ export function createSingleValueCache<T = any>(config: { ttl?: number } = {}): SingleValueCache<T> {
279
+ return new SingleValueCache<T>(config)
280
+ }
package/logger.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ export declare enum LogLevel {
2
+ DEBUG = 0,
3
+ INFO = 1,
4
+ WARN = 2,
5
+ ERROR = 3,
6
+ NONE = 4
7
+ }
8
+ interface LoggerOptions {
9
+ namespace: string;
10
+ level?: LogLevel;
11
+ enabled?: boolean;
12
+ }
13
+ export declare class Logger {
14
+ private namespace;
15
+ private level;
16
+ private enabled;
17
+ constructor(options: LoggerOptions);
18
+ private detectLoggingEnabled;
19
+ private detectLogLevel;
20
+ private getArgLogLevel;
21
+ private shouldLog;
22
+ private formatTimestamp;
23
+ private formatMessage;
24
+ debug(message: string, ...args: any[]): void;
25
+ info(message: string, ...args: any[]): void;
26
+ warn(message: string, ...args: any[]): void;
27
+ error(message: string, ...args: any[]): void;
28
+ child(subNamespace: string): Logger;
29
+ }
30
+ export declare function createLogger(namespace: string, options?: Partial<LoggerOptions>): Logger;
31
+ export {};
32
+ //# sourceMappingURL=logger.d.ts.map
package/logger.js ADDED
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Logger = exports.LogLevel = void 0;
4
+ exports.createLogger = createLogger;
5
+ var LogLevel;
6
+ (function (LogLevel) {
7
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
8
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
9
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
10
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
11
+ LogLevel[LogLevel["NONE"] = 4] = "NONE";
12
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
13
+ const LOG_LEVEL_NAMES = {
14
+ [LogLevel.DEBUG]: 'DEBUG',
15
+ [LogLevel.INFO]: 'INFO',
16
+ [LogLevel.WARN]: 'WARN',
17
+ [LogLevel.ERROR]: 'ERROR',
18
+ [LogLevel.NONE]: 'NONE'
19
+ };
20
+ class Logger {
21
+ constructor(options) {
22
+ this.namespace = options.namespace;
23
+ this.enabled = options.enabled ?? this.detectLoggingEnabled();
24
+ this.level = options.level ?? this.detectLogLevel();
25
+ }
26
+ detectLoggingEnabled() {
27
+ return process.env.ENABLE_LOGGING === 'true' ||
28
+ process.env.DEBUG === 'true' ||
29
+ process.argv.includes('--print-logs');
30
+ }
31
+ detectLogLevel() {
32
+ const envLevel = process.env.LOG_LEVEL?.toUpperCase();
33
+ const argLevel = this.getArgLogLevel();
34
+ const levelStr = argLevel || envLevel || 'INFO';
35
+ switch (levelStr) {
36
+ case 'DEBUG':
37
+ return LogLevel.DEBUG;
38
+ case 'INFO':
39
+ return LogLevel.INFO;
40
+ case 'WARN':
41
+ return LogLevel.WARN;
42
+ case 'ERROR':
43
+ return LogLevel.ERROR;
44
+ case 'NONE':
45
+ return LogLevel.NONE;
46
+ default:
47
+ return LogLevel.INFO;
48
+ }
49
+ }
50
+ getArgLogLevel() {
51
+ const logLevelIndex = process.argv.indexOf('--log-level');
52
+ if (logLevelIndex !== -1 && process.argv[logLevelIndex + 1]) {
53
+ return process.argv[logLevelIndex + 1].toUpperCase();
54
+ }
55
+ return undefined;
56
+ }
57
+ shouldLog(level) {
58
+ return this.enabled && level >= this.level;
59
+ }
60
+ formatTimestamp() {
61
+ const now = new Date();
62
+ const year = now.getFullYear();
63
+ const month = String(now.getMonth() + 1).padStart(2, '0');
64
+ const day = String(now.getDate()).padStart(2, '0');
65
+ const hours = String(now.getHours()).padStart(2, '0');
66
+ const minutes = String(now.getMinutes()).padStart(2, '0');
67
+ const seconds = String(now.getSeconds()).padStart(2, '0');
68
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
69
+ }
70
+ formatMessage(level, message, ...args) {
71
+ const timestamp = this.formatTimestamp();
72
+ const levelName = LOG_LEVEL_NAMES[level].padEnd(5, ' ');
73
+ const prefix = `${levelName} ${timestamp} service=${this.namespace}`;
74
+ if (args.length === 0) {
75
+ return `${prefix} ${message}`;
76
+ }
77
+ const formattedArgs = args.map(arg => {
78
+ if (typeof arg === 'object') {
79
+ return JSON.stringify(arg, null, 2);
80
+ }
81
+ return String(arg);
82
+ }).join(' ');
83
+ return `${prefix} ${message} ${formattedArgs}`;
84
+ }
85
+ debug(message, ...args) {
86
+ if (this.shouldLog(LogLevel.DEBUG)) {
87
+ console.log(this.formatMessage(LogLevel.DEBUG, message, ...args));
88
+ }
89
+ }
90
+ info(message, ...args) {
91
+ if (this.shouldLog(LogLevel.INFO)) {
92
+ console.log(this.formatMessage(LogLevel.INFO, message, ...args));
93
+ }
94
+ }
95
+ warn(message, ...args) {
96
+ if (this.shouldLog(LogLevel.WARN)) {
97
+ console.warn(this.formatMessage(LogLevel.WARN, message, ...args));
98
+ }
99
+ }
100
+ error(message, ...args) {
101
+ if (this.shouldLog(LogLevel.ERROR)) {
102
+ console.error(this.formatMessage(LogLevel.ERROR, message, ...args));
103
+ }
104
+ }
105
+ child(subNamespace) {
106
+ return new Logger({
107
+ namespace: `${this.namespace}:${subNamespace}`,
108
+ level: this.level,
109
+ enabled: this.enabled
110
+ });
111
+ }
112
+ }
113
+ exports.Logger = Logger;
114
+ function createLogger(namespace, options) {
115
+ return new Logger({
116
+ namespace,
117
+ level: options?.level,
118
+ enabled: options?.enabled
119
+ });
120
+ }
package/logger.ts ADDED
@@ -0,0 +1,143 @@
1
+ export enum LogLevel {
2
+ DEBUG = 0,
3
+ INFO = 1,
4
+ WARN = 2,
5
+ ERROR = 3,
6
+ NONE = 4
7
+ }
8
+
9
+ const LOG_LEVEL_NAMES: Record<LogLevel, string> = {
10
+ [LogLevel.DEBUG]: 'DEBUG',
11
+ [LogLevel.INFO]: 'INFO',
12
+ [LogLevel.WARN]: 'WARN',
13
+ [LogLevel.ERROR]: 'ERROR',
14
+ [LogLevel.NONE]: 'NONE'
15
+ }
16
+
17
+ interface LoggerOptions {
18
+ namespace: string
19
+ level?: LogLevel
20
+ enabled?: boolean
21
+ }
22
+
23
+ export class Logger {
24
+ private namespace: string
25
+ private level: LogLevel
26
+ private enabled: boolean
27
+
28
+ constructor(options: LoggerOptions) {
29
+ this.namespace = options.namespace
30
+ this.enabled = options.enabled ?? this.detectLoggingEnabled()
31
+ this.level = options.level ?? this.detectLogLevel()
32
+ }
33
+
34
+ private detectLoggingEnabled(): boolean {
35
+ return process.env.ENABLE_LOGGING === 'true' ||
36
+ process.env.DEBUG === 'true' ||
37
+ process.argv.includes('--print-logs')
38
+ }
39
+
40
+ private detectLogLevel(): LogLevel {
41
+ const envLevel = process.env.LOG_LEVEL?.toUpperCase()
42
+ const argLevel = this.getArgLogLevel()
43
+
44
+ const levelStr = argLevel || envLevel || 'INFO'
45
+
46
+ switch (levelStr) {
47
+ case 'DEBUG':
48
+ return LogLevel.DEBUG
49
+ case 'INFO':
50
+ return LogLevel.INFO
51
+ case 'WARN':
52
+ return LogLevel.WARN
53
+ case 'ERROR':
54
+ return LogLevel.ERROR
55
+ case 'NONE':
56
+ return LogLevel.NONE
57
+ default:
58
+ return LogLevel.INFO
59
+ }
60
+ }
61
+
62
+ private getArgLogLevel(): string | undefined {
63
+ const logLevelIndex = process.argv.indexOf('--log-level')
64
+ if (logLevelIndex !== -1 && process.argv[logLevelIndex + 1]) {
65
+ return process.argv[logLevelIndex + 1].toUpperCase()
66
+ }
67
+ return undefined
68
+ }
69
+
70
+ private shouldLog(level: LogLevel): boolean {
71
+ return this.enabled && level >= this.level
72
+ }
73
+
74
+ private formatTimestamp(): string {
75
+ const now = new Date()
76
+ const year = now.getFullYear()
77
+ const month = String(now.getMonth() + 1).padStart(2, '0')
78
+ const day = String(now.getDate()).padStart(2, '0')
79
+ const hours = String(now.getHours()).padStart(2, '0')
80
+ const minutes = String(now.getMinutes()).padStart(2, '0')
81
+ const seconds = String(now.getSeconds()).padStart(2, '0')
82
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`
83
+ }
84
+
85
+ private formatMessage(level: LogLevel, message: string, ...args: any[]): string {
86
+ const timestamp = this.formatTimestamp()
87
+ const levelName = LOG_LEVEL_NAMES[level].padEnd(5, ' ')
88
+ const prefix = `${levelName} ${timestamp} service=${this.namespace}`
89
+
90
+ if (args.length === 0) {
91
+ return `${prefix} ${message}`
92
+ }
93
+
94
+ const formattedArgs = args.map(arg => {
95
+ if (typeof arg === 'object') {
96
+ return JSON.stringify(arg, null, 2)
97
+ }
98
+ return String(arg)
99
+ }).join(' ')
100
+
101
+ return `${prefix} ${message} ${formattedArgs}`
102
+ }
103
+
104
+ debug(message: string, ...args: any[]): void {
105
+ if (this.shouldLog(LogLevel.DEBUG)) {
106
+ console.log(this.formatMessage(LogLevel.DEBUG, message, ...args))
107
+ }
108
+ }
109
+
110
+ info(message: string, ...args: any[]): void {
111
+ if (this.shouldLog(LogLevel.INFO)) {
112
+ console.log(this.formatMessage(LogLevel.INFO, message, ...args))
113
+ }
114
+ }
115
+
116
+ warn(message: string, ...args: any[]): void {
117
+ if (this.shouldLog(LogLevel.WARN)) {
118
+ console.warn(this.formatMessage(LogLevel.WARN, message, ...args))
119
+ }
120
+ }
121
+
122
+ error(message: string, ...args: any[]): void {
123
+ if (this.shouldLog(LogLevel.ERROR)) {
124
+ console.error(this.formatMessage(LogLevel.ERROR, message, ...args))
125
+ }
126
+ }
127
+
128
+ child(subNamespace: string): Logger {
129
+ return new Logger({
130
+ namespace: `${this.namespace}:${subNamespace}`,
131
+ level: this.level,
132
+ enabled: this.enabled
133
+ })
134
+ }
135
+ }
136
+
137
+ export function createLogger(namespace: string, options?: Partial<LoggerOptions>): Logger {
138
+ return new Logger({
139
+ namespace,
140
+ level: options?.level,
141
+ enabled: options?.enabled
142
+ })
143
+ }