openpets 1.0.5 → 1.0.6

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.
Files changed (96) hide show
  1. package/dist/data/api.json +3172 -0
  2. package/dist/src/core/ai-client-base/index.d.ts +47 -0
  3. package/dist/src/core/ai-client-base/index.d.ts.map +1 -0
  4. package/dist/src/core/ai-client-base/index.js +168 -0
  5. package/dist/src/core/ai-client-base/index.js.map +1 -0
  6. package/dist/src/core/browser.d.ts +10 -0
  7. package/dist/src/core/browser.d.ts.map +1 -0
  8. package/{browser.ts → dist/src/core/browser.js} +4 -4
  9. package/dist/src/core/browser.js.map +1 -0
  10. package/dist/src/core/build-pet.d.ts +2 -0
  11. package/dist/src/core/build-pet.d.ts.map +1 -0
  12. package/dist/src/core/build-pet.js +364 -0
  13. package/dist/src/core/build-pet.js.map +1 -0
  14. package/dist/src/core/cli.d.ts +3 -0
  15. package/dist/src/core/cli.d.ts.map +1 -0
  16. package/dist/src/core/cli.js +244 -0
  17. package/dist/src/core/cli.js.map +1 -0
  18. package/dist/src/core/config-manager.d.ts +13 -0
  19. package/dist/src/core/config-manager.d.ts.map +1 -0
  20. package/dist/src/core/config-manager.js +59 -0
  21. package/dist/src/core/config-manager.js.map +1 -0
  22. package/dist/src/core/deploy-pet.d.ts +2 -0
  23. package/dist/src/core/deploy-pet.d.ts.map +1 -0
  24. package/dist/src/core/deploy-pet.js +66 -0
  25. package/dist/src/core/deploy-pet.js.map +1 -0
  26. package/dist/src/core/index.d.ts +11 -0
  27. package/dist/src/core/index.d.ts.map +1 -0
  28. package/dist/src/core/index.js +11 -0
  29. package/dist/src/core/index.js.map +1 -0
  30. package/dist/src/core/local-cache.d.ts +69 -0
  31. package/dist/src/core/local-cache.d.ts.map +1 -0
  32. package/dist/src/core/local-cache.js +212 -0
  33. package/dist/src/core/local-cache.js.map +1 -0
  34. package/dist/src/core/logger.d.ts.map +1 -0
  35. package/{logger.js → dist/src/core/logger.js} +8 -9
  36. package/dist/src/core/logger.js.map +1 -0
  37. package/dist/src/core/mcp-factory.d.ts +12 -0
  38. package/dist/src/core/mcp-factory.d.ts.map +1 -0
  39. package/dist/src/core/mcp-factory.js +143 -0
  40. package/dist/src/core/mcp-factory.js.map +1 -0
  41. package/dist/src/core/mcp-server.d.ts +3 -0
  42. package/dist/src/core/mcp-server.d.ts.map +1 -0
  43. package/dist/src/core/mcp-server.js +55 -0
  44. package/dist/src/core/mcp-server.js.map +1 -0
  45. package/dist/src/core/migrate-plugin.d.ts +15 -0
  46. package/dist/src/core/migrate-plugin.d.ts.map +1 -0
  47. package/dist/src/core/migrate-plugin.js +181 -0
  48. package/dist/src/core/migrate-plugin.js.map +1 -0
  49. package/dist/src/core/pets-registry.d.ts +47 -0
  50. package/dist/src/core/pets-registry.d.ts.map +1 -0
  51. package/dist/src/core/pets-registry.js +109 -0
  52. package/dist/src/core/pets-registry.js.map +1 -0
  53. package/dist/src/core/plugin-factory.d.ts +58 -0
  54. package/dist/src/core/plugin-factory.d.ts.map +1 -0
  55. package/dist/src/core/plugin-factory.js +212 -0
  56. package/dist/src/core/plugin-factory.js.map +1 -0
  57. package/dist/src/core/prompt-utils.d.ts +14 -0
  58. package/dist/src/core/prompt-utils.d.ts.map +1 -0
  59. package/dist/src/core/prompt-utils.js +106 -0
  60. package/dist/src/core/prompt-utils.js.map +1 -0
  61. package/dist/src/core/schema-helpers.d.ts +33 -0
  62. package/dist/src/core/schema-helpers.d.ts.map +1 -0
  63. package/dist/src/core/schema-helpers.js +46 -0
  64. package/dist/src/core/schema-helpers.js.map +1 -0
  65. package/dist/src/core/search-pets.d.ts +29 -0
  66. package/dist/src/core/search-pets.d.ts.map +1 -0
  67. package/dist/src/core/search-pets.js +196 -0
  68. package/dist/src/core/search-pets.js.map +1 -0
  69. package/dist/src/core/types.d.ts +63 -0
  70. package/dist/src/core/types.d.ts.map +1 -0
  71. package/dist/src/core/types.js +2 -0
  72. package/dist/src/core/types.js.map +1 -0
  73. package/dist/src/core/validate-pet.d.ts +40 -0
  74. package/dist/src/core/validate-pet.d.ts.map +1 -0
  75. package/dist/src/core/validate-pet.js +650 -0
  76. package/dist/src/core/validate-pet.js.map +1 -0
  77. package/package.json +8 -21
  78. package/ai-client-base/index.ts +0 -229
  79. package/build-pet.ts +0 -429
  80. package/cli.ts +0 -268
  81. package/config-manager.ts +0 -82
  82. package/deploy-pet.ts +0 -91
  83. package/index.ts +0 -10
  84. package/local-cache.ts +0 -280
  85. package/logger.ts +0 -143
  86. package/mcp-factory.ts +0 -180
  87. package/mcp-server.ts +0 -69
  88. package/migrate-plugin.ts +0 -220
  89. package/pets-registry.ts +0 -160
  90. package/plugin-factory.ts +0 -300
  91. package/prompt-utils.ts +0 -130
  92. package/schema-helpers.ts +0 -59
  93. package/search-pets.ts +0 -267
  94. package/types.ts +0 -68
  95. package/validate-pet.ts +0 -749
  96. /package/{logger.d.ts → dist/src/core/logger.d.ts} +0 -0
package/mcp-factory.ts DELETED
@@ -1,180 +0,0 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import {
4
- CallToolRequestSchema,
5
- ListToolsRequestSchema,
6
- Tool,
7
- } from "@modelcontextprotocol/sdk/types.js";
8
- import type { ToolDefinition, ToolRecord } from "./plugin-factory";
9
- import { createLogger } from "./logger";
10
-
11
- const logger = createLogger("mcp-factory");
12
-
13
- export interface MCPServerConfig {
14
- name: string;
15
- version: string;
16
- description?: string;
17
- }
18
-
19
- export function extractToolsFromPlugin(pluginModule: any): ToolDefinition[] {
20
- const tools: ToolDefinition[] = [];
21
-
22
- if (pluginModule.tool && typeof pluginModule.tool === "object") {
23
- const toolRecord = pluginModule.tool as ToolRecord;
24
-
25
- for (const [name, toolDef] of Object.entries(toolRecord)) {
26
- tools.push({
27
- name: name,
28
- description: toolDef.description,
29
- schema: toolDef.args,
30
- execute: toolDef.execute,
31
- });
32
- }
33
- }
34
-
35
- return tools;
36
- }
37
-
38
- function convertToolToMCPSchema(tool: ToolDefinition): Tool {
39
- const schema = tool.schema as any;
40
- const shape = schema.shape || schema._def?.shape?.();
41
-
42
- const properties: Record<string, any> = {};
43
- const required: string[] = [];
44
-
45
- if (shape) {
46
- for (const [key, value] of Object.entries(shape)) {
47
- const fieldSchema = value as any;
48
- const fieldType = fieldSchema._def?.typeName;
49
-
50
- if (fieldType === "ZodString") {
51
- properties[key] = {
52
- type: "string",
53
- description: fieldSchema._def?.description || "",
54
- };
55
- } else if (fieldType === "ZodNumber") {
56
- properties[key] = {
57
- type: "number",
58
- description: fieldSchema._def?.description || "",
59
- };
60
- } else if (fieldType === "ZodBoolean") {
61
- properties[key] = {
62
- type: "boolean",
63
- description: fieldSchema._def?.description || "",
64
- };
65
- } else if (fieldType === "ZodArray") {
66
- const elementType = fieldSchema._def?.type?._def?.typeName;
67
- properties[key] = {
68
- type: "array",
69
- items: {
70
- type: elementType === "ZodString" ? "string" : elementType === "ZodNumber" ? "number" : "string",
71
- },
72
- description: fieldSchema._def?.description || "",
73
- };
74
- } else {
75
- properties[key] = {
76
- type: "string",
77
- description: fieldSchema._def?.description || "",
78
- };
79
- }
80
-
81
- if (!fieldSchema._def?.checks?.some((check: any) => check.kind === "optional")) {
82
- if (fieldSchema._def?.typeName !== "ZodOptional") {
83
- required.push(key);
84
- }
85
- }
86
- }
87
- }
88
-
89
- return {
90
- name: tool.name.replace(/-/g, "_"),
91
- description: tool.description,
92
- inputSchema: {
93
- type: "object",
94
- properties,
95
- ...(required.length > 0 ? { required } : {}),
96
- },
97
- };
98
- }
99
-
100
- export function createMCPServer(
101
- config: MCPServerConfig,
102
- tools: ToolDefinition[]
103
- ): Server {
104
- const server = new Server(
105
- {
106
- name: config.name,
107
- version: config.version,
108
- },
109
- {
110
- capabilities: {
111
- tools: {},
112
- },
113
- }
114
- );
115
-
116
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
117
- tools: tools.map((tool) => convertToolToMCPSchema(tool)),
118
- }));
119
-
120
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
121
- const { name, arguments: args } = request.params as {
122
- name: string;
123
- arguments: any;
124
- };
125
-
126
- const tool = tools.find((t) => t.name.replace(/-/g, "_") === name);
127
-
128
- if (!tool) {
129
- return {
130
- content: [
131
- {
132
- type: "text",
133
- text: `Error: Unknown tool: ${name}`,
134
- },
135
- ],
136
- isError: true,
137
- };
138
- }
139
-
140
- try {
141
- const result = await tool.execute(args);
142
- return {
143
- content: [
144
- {
145
- type: "text",
146
- text: result,
147
- },
148
- ],
149
- };
150
- } catch (error) {
151
- return {
152
- content: [
153
- {
154
- type: "text",
155
- text: `Error executing ${name}: ${error}`,
156
- },
157
- ],
158
- isError: true,
159
- };
160
- }
161
- });
162
-
163
- return server;
164
- }
165
-
166
- export async function startMCPServer(server: Server): Promise<void> {
167
- const transport = new StdioServerTransport();
168
- await server.connect(transport);
169
- console.error(`MCP server running on stdio`);
170
- }
171
-
172
- export function createMCPServerFromTools(
173
- config: MCPServerConfig,
174
- tools: ToolDefinition[]
175
- ): () => Promise<void> {
176
- return async () => {
177
- const server = createMCPServer(config, tools);
178
- await startMCPServer(server);
179
- };
180
- }
package/mcp-server.ts DELETED
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { createMCPServer, startMCPServer, extractToolsFromPlugin } from "./mcp-factory";
4
- import { createLogger } from "./logger";
5
- import { resolve } from "path";
6
- import { readFileSync } from "fs";
7
-
8
- const logger = createLogger("mcp-server");
9
-
10
- async function main() {
11
- const petName = process.argv[2] || process.env.PET_NAME;
12
-
13
- if (!petName) {
14
- logger.error("Pet name is required. Usage: node mcp-server.js <pet-name>");
15
- logger.error("Example: node mcp-server.js polar");
16
- logger.error("Or set PET_NAME environment variable");
17
- process.exit(1);
18
- }
19
-
20
- logger.info("Starting MCP server for pet", { petName });
21
-
22
- try {
23
- const petPath = resolve(__dirname, `../../pets/${petName}`);
24
- const packageJsonPath = resolve(petPath, "package.json");
25
-
26
- let packageJson: any;
27
- try {
28
- packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
29
- } catch (error) {
30
- logger.error("Failed to read package.json", { petPath: packageJsonPath, error: String(error) });
31
- process.exit(1);
32
- }
33
-
34
- const pluginModule = await import(`../../pets/${petName}/index.js`);
35
-
36
- let plugin;
37
- if (typeof pluginModule.default === "function") {
38
- plugin = await pluginModule.default();
39
- } else {
40
- plugin = pluginModule.default;
41
- }
42
-
43
- const tools = extractToolsFromPlugin(plugin);
44
-
45
- if (tools.length === 0) {
46
- logger.error("No tools found in plugin", { petName });
47
- process.exit(1);
48
- }
49
-
50
- logger.info("Extracted tools from plugin", { petName, toolCount: tools.length });
51
-
52
- const server = createMCPServer(
53
- {
54
- name: packageJson.name || `openpets/${petName}`,
55
- version: packageJson.version || "1.0.0",
56
- description: packageJson.description || `${petName} MCP server`,
57
- },
58
- tools
59
- );
60
-
61
- await startMCPServer(server);
62
- } catch (error) {
63
- logger.error("Fatal error starting MCP server", { petName, error: String(error) });
64
- console.error(error);
65
- process.exit(1);
66
- }
67
- }
68
-
69
- main();
package/migrate-plugin.ts DELETED
@@ -1,220 +0,0 @@
1
- import { readFileSync, writeFileSync, existsSync } from 'fs'
2
- import { join } from 'path'
3
-
4
- export interface MigrationResult {
5
- success: boolean
6
- changes: string[]
7
- errors: string[]
8
- }
9
-
10
- export class PluginMigrator {
11
- migratePlugin(pluginPath: string): MigrationResult {
12
- const indexPath = join(pluginPath, 'index.ts')
13
- const changes: string[] = []
14
- const errors: string[] = []
15
-
16
- if (!existsSync(indexPath)) {
17
- return {
18
- success: false,
19
- changes,
20
- errors: ['index.ts not found']
21
- }
22
- }
23
-
24
- try {
25
- let code = readFileSync(indexPath, 'utf-8')
26
- const original = code
27
-
28
- code = this.addImports(code, changes)
29
- code = this.convertInputSchemaToToolBuilder(code, changes)
30
-
31
- if (code !== original) {
32
- writeFileSync(indexPath, code, 'utf-8')
33
- return { success: true, changes, errors }
34
- } else {
35
- return { success: true, changes: ['No changes needed'], errors }
36
- }
37
- } catch (error) {
38
- errors.push(`Migration failed: ${error instanceof Error ? error.message : String(error)}`)
39
- return { success: false, changes, errors }
40
- }
41
- }
42
-
43
- private addImports(code: string, changes: string[]): string {
44
- const hasToolImport = /import\s+{\s*tool\s*}\s+from\s+['"]@opencode-ai\/plugin['"]/.test(code)
45
- const hasZodImport = /import.*from\s+['"]zod['"]/.test(code)
46
- const hasPluginImport = /import.*from\s+['"]@opencode-ai\/plugin['"]/.test(code)
47
-
48
- if (!hasPluginImport) {
49
- code = `import type { Plugin } from "@opencode-ai/plugin"\n${code}`
50
- changes.push('Added Plugin type import')
51
- }
52
-
53
- if (!hasToolImport) {
54
- if (hasPluginImport) {
55
- code = code.replace(
56
- /import\s+(type\s+)?\{([^}]+)\}\s+from\s+['"]@opencode-ai\/plugin['"]/,
57
- (match, typeKeyword, imports) => {
58
- if (typeKeyword) {
59
- return match.replace('}', ', tool }').replace('type ', '')
60
- }
61
- const cleanImports = imports.split(',').map((s: string) => s.trim()).filter(Boolean)
62
- if (!cleanImports.includes('tool')) {
63
- cleanImports.push('tool')
64
- }
65
- return `import { ${cleanImports.join(', ')} } from "@opencode-ai/plugin"`
66
- }
67
- )
68
- } else {
69
- code = `import { tool } from "@opencode-ai/plugin"\n${code}`
70
- }
71
- changes.push('Added tool import from @opencode-ai/plugin')
72
- }
73
-
74
- if (!hasZodImport) {
75
- const pluginImportIndex = code.indexOf('import')
76
- const firstNewline = code.indexOf('\n', pluginImportIndex)
77
- code = code.slice(0, firstNewline + 1) + `import { z } from "zod"\n` + code.slice(firstNewline + 1)
78
- changes.push('Added Zod import')
79
- }
80
-
81
- return code
82
- }
83
-
84
- private convertInputSchemaToToolBuilder(code: string, changes: string[]): string {
85
- const toolPattern = /"([^"]+)":\s*{\s*description:\s*"([^"]+)",\s*inputSchema:\s*(\{[\s\S]*?\}),\s*async\s+execute\s*\(([^)]*)\)\s*(\{[\s\S]*?\n\s*\})\s*\}/g
86
-
87
- let matchCount = 0
88
- code = code.replace(toolPattern, (match, toolName, description, inputSchemaStr, argsParam, executeBody) => {
89
- matchCount++
90
-
91
- try {
92
- const inputSchema = this.parseInputSchema(inputSchemaStr)
93
- const zodArgs = this.convertToZodSchema(inputSchema)
94
-
95
- const converted = `"${toolName}": tool({
96
- description: "${description}",
97
- args: ${zodArgs},
98
- async execute(${argsParam}) ${executeBody}
99
- })`
100
-
101
- return converted
102
- } catch (error) {
103
- console.warn(`Failed to convert tool "${toolName}": ${error}`)
104
- return match
105
- }
106
- })
107
-
108
- if (matchCount > 0) {
109
- changes.push(`Converted ${matchCount} tool(s) from inputSchema to tool() builder`)
110
- }
111
-
112
- return code
113
- }
114
-
115
- private parseInputSchema(schemaStr: string): any {
116
- const cleaned = schemaStr
117
- .replace(/\/\/.*$/gm, '')
118
- .replace(/\/\*[\s\S]*?\*\//g, '')
119
- .trim()
120
-
121
- try {
122
- const jsonStr = cleaned
123
- .replace(/(\w+):/g, '"$1":')
124
- .replace(/'/g, '"')
125
-
126
- return JSON.parse(jsonStr)
127
- } catch (error) {
128
- return eval(`(${cleaned})`)
129
- }
130
- }
131
-
132
- private convertToZodSchema(inputSchema: any): string {
133
- if (!inputSchema.properties) {
134
- return '{}'
135
- }
136
-
137
- const required = new Set(inputSchema.required || [])
138
- const properties = inputSchema.properties
139
-
140
- const zodFields: string[] = []
141
-
142
- for (const [key, propDef] of Object.entries<any>(properties)) {
143
- const isRequired = required.has(key)
144
- let zodType = this.getZodType(propDef, isRequired)
145
-
146
- const description = propDef.description
147
- if (description) {
148
- zodType += `.describe("${description.replace(/"/g, '\\"')}")`
149
- }
150
-
151
- zodFields.push(`${key}: ${zodType}`)
152
- }
153
-
154
- return `{\n ${zodFields.join(',\n ')}\n }`
155
- }
156
-
157
- private getZodType(propDef: any, isRequired: boolean): string {
158
- let zodType: string
159
-
160
- switch (propDef.type) {
161
- case 'string':
162
- if (propDef.enum && Array.isArray(propDef.enum)) {
163
- zodType = `z.enum([${propDef.enum.map((v: string) => `"${v}"`).join(', ')}])`
164
- } else {
165
- zodType = 'z.string()'
166
- }
167
- break
168
-
169
- case 'number':
170
- zodType = 'z.number()'
171
- break
172
-
173
- case 'boolean':
174
- zodType = 'z.boolean()'
175
- break
176
-
177
- case 'array':
178
- if (propDef.items) {
179
- const itemType = this.getZodType(propDef.items, true)
180
- zodType = `z.array(${itemType})`
181
- } else {
182
- zodType = 'z.array(z.any())'
183
- }
184
- break
185
-
186
- case 'object':
187
- if (propDef.additionalProperties) {
188
- const valueType = this.getZodType(propDef.additionalProperties, true)
189
- zodType = `z.record(${valueType})`
190
- } else {
191
- zodType = 'z.object({})'
192
- }
193
- break
194
-
195
- default:
196
- zodType = 'z.any()'
197
- }
198
-
199
- if (!isRequired) {
200
- zodType += '.optional()'
201
- }
202
-
203
- return zodType
204
- }
205
-
206
- printMigrationResult(pluginName: string, result: MigrationResult): void {
207
- const status = result.success ? '✅' : '❌'
208
- console.log(`\n${status} ${pluginName}`)
209
-
210
- if (result.changes.length > 0) {
211
- console.log(' Changes:')
212
- result.changes.forEach(change => console.log(` • ${change}`))
213
- }
214
-
215
- if (result.errors.length > 0) {
216
- console.log(' Errors:')
217
- result.errors.forEach(error => console.log(` ❌ ${error}`))
218
- }
219
- }
220
- }
package/pets-registry.ts DELETED
@@ -1,160 +0,0 @@
1
- import { readdirSync, existsSync, readFileSync } from "fs"
2
- import { join, resolve } from "path"
3
- import { createLogger } from "./logger"
4
-
5
- const logger = createLogger("pets-registry")
6
-
7
- export interface PetMetadata {
8
- id: string
9
- name: string
10
- description: string
11
- version: string
12
- keywords: string[]
13
- author?: string
14
- repository?: {
15
- type: string
16
- url: string
17
- directory?: string
18
- }
19
- queries?: string[]
20
- scenarios?: Record<string, string[]>
21
- envVariables?: {
22
- required?: Array<{ name: string; description: string; provider?: string }>
23
- optional?: Array<{ name: string; description: string; provider?: string }>
24
- }
25
- utils?: {
26
- dependencies?: string[]
27
- paths?: string[]
28
- }
29
- }
30
-
31
- export class PetsRegistry {
32
- private pets: Map<string, PetMetadata> = new Map()
33
- private petsDir: string
34
-
35
- constructor(petsDir?: string) {
36
- this.petsDir = petsDir || resolve(__dirname, "../../pets")
37
- this.loadPets()
38
- }
39
-
40
- private loadPets(): void {
41
- try {
42
- if (!existsSync(this.petsDir)) {
43
- logger.warn(`Pets directory not found: ${this.petsDir}`)
44
- return
45
- }
46
-
47
- const entries = readdirSync(this.petsDir, { withFileTypes: true })
48
-
49
- for (const entry of entries) {
50
- if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') {
51
- continue
52
- }
53
-
54
- const petDir = join(this.petsDir, entry.name)
55
- const packageJsonPath = join(petDir, 'package.json')
56
-
57
- if (!existsSync(packageJsonPath)) {
58
- continue
59
- }
60
-
61
- try {
62
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
63
-
64
- const metadata: PetMetadata = {
65
- id: entry.name,
66
- name: packageJson.name || entry.name,
67
- description: packageJson.description || '',
68
- version: packageJson.version || '1.0.0',
69
- keywords: packageJson.keywords || [],
70
- author: packageJson.author,
71
- repository: packageJson.repository,
72
- queries: packageJson.queries,
73
- scenarios: packageJson.scenarios,
74
- envVariables: packageJson.envVariables,
75
- utils: packageJson.utils
76
- }
77
-
78
- this.pets.set(entry.name, metadata)
79
- logger.debug(`Loaded pet: ${entry.name}`)
80
- } catch (error: any) {
81
- logger.error(`Failed to load pet ${entry.name}: ${error.message}`)
82
- }
83
- }
84
-
85
- logger.info(`Loaded ${this.pets.size} pets`)
86
- } catch (error: any) {
87
- logger.error(`Failed to load pets: ${error.message}`)
88
- }
89
- }
90
-
91
- getAllPets(): PetMetadata[] {
92
- return Array.from(this.pets.values())
93
- }
94
-
95
- getPet(id: string): PetMetadata | undefined {
96
- return this.pets.get(id)
97
- }
98
-
99
- searchPets(query: string): PetMetadata[] {
100
- if (!query || query.trim() === '') {
101
- return this.getAllPets()
102
- }
103
-
104
- const normalizedQuery = query.toLowerCase().trim()
105
- const results: PetMetadata[] = []
106
-
107
- for (const pet of this.pets.values()) {
108
- const searchableText = [
109
- pet.id,
110
- pet.name,
111
- pet.description,
112
- ...(pet.keywords || []),
113
- pet.author || '',
114
- ].join(' ').toLowerCase()
115
-
116
- if (searchableText.includes(normalizedQuery)) {
117
- results.push(pet)
118
- }
119
- }
120
-
121
- return results
122
- }
123
-
124
- getPetsByCategory(keywords: string[]): PetMetadata[] {
125
- return this.getAllPets().filter(pet =>
126
- keywords.some(keyword =>
127
- pet.keywords?.some(k => k.toLowerCase().includes(keyword.toLowerCase()))
128
- )
129
- )
130
- }
131
-
132
- reload(): void {
133
- this.pets.clear()
134
- this.loadPets()
135
- }
136
- }
137
-
138
- let defaultRegistry: PetsRegistry | null = null
139
-
140
- export function getPetsRegistry(petsDir?: string): PetsRegistry {
141
- if (!defaultRegistry) {
142
- defaultRegistry = new PetsRegistry(petsDir)
143
- }
144
- return defaultRegistry
145
- }
146
-
147
- export function searchPetsInRegistry(query: string, petsDir?: string): PetMetadata[] {
148
- const registry = getPetsRegistry(petsDir)
149
- return registry.searchPets(query)
150
- }
151
-
152
- export function getAllPetsFromRegistry(petsDir?: string): PetMetadata[] {
153
- const registry = getPetsRegistry(petsDir)
154
- return registry.getAllPets()
155
- }
156
-
157
- export function getPetFromRegistry(id: string, petsDir?: string): PetMetadata | undefined {
158
- const registry = getPetsRegistry(petsDir)
159
- return registry.getPet(id)
160
- }