@nospt/plugin-dev-ai-hub-backend 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.
Files changed (36) hide show
  1. package/.eslintrc.js +1 -0
  2. package/dev/index.ts +8 -0
  3. package/dist/database/AiAssetStore.cjs.js +224 -0
  4. package/dist/database/AiAssetStore.cjs.js.map +1 -0
  5. package/dist/index.cjs.js +11 -0
  6. package/dist/index.cjs.js.map +1 -0
  7. package/dist/index.d.ts +5 -0
  8. package/dist/plugin.cjs.js +70 -0
  9. package/dist/plugin.cjs.js.map +1 -0
  10. package/dist/router.cjs.js +247 -0
  11. package/dist/router.cjs.js.map +1 -0
  12. package/dist/service/AiAssetSyncService.cjs.js +210 -0
  13. package/dist/service/AiAssetSyncService.cjs.js.map +1 -0
  14. package/dist/service/AssetParser.cjs.js +91 -0
  15. package/dist/service/AssetParser.cjs.js.map +1 -0
  16. package/dist/service/McpServerService.cjs.js +366 -0
  17. package/dist/service/McpServerService.cjs.js.map +1 -0
  18. package/migrations/001_init.js +47 -0
  19. package/migrations/002_install_count.js +17 -0
  20. package/migrations/003_install_paths.js +19 -0
  21. package/migrations/004_label.js +17 -0
  22. package/migrations/005_remove_unused_fields.js +22 -0
  23. package/migrations/006_resources_content.js +16 -0
  24. package/package.json +72 -0
  25. package/package.json-prepack +64 -0
  26. package/src/database/AiAssetStore.test.ts +294 -0
  27. package/src/database/AiAssetStore.ts +256 -0
  28. package/src/index.ts +2 -0
  29. package/src/plugin.ts +84 -0
  30. package/src/router.ts +338 -0
  31. package/src/service/AiAssetSyncService.ts +267 -0
  32. package/src/service/AssetParser.test.ts +261 -0
  33. package/src/service/AssetParser.ts +117 -0
  34. package/src/service/McpServerService.ts +464 -0
  35. package/src/types.ts +31 -0
  36. package/tsconfig.json +8 -0
package/.eslintrc.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
package/dev/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { createBackend } from '@backstage/backend-defaults';
2
+
3
+ const backend = createBackend();
4
+
5
+ // The plugin registers its own routes, database migrations, and sync scheduler
6
+ backend.add(import('../src'));
7
+
8
+ backend.start();
@@ -0,0 +1,224 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+
5
+ class AiAssetStore {
6
+ constructor(db) {
7
+ this.db = db;
8
+ }
9
+ static async create(options) {
10
+ const db = await options.database.getClient();
11
+ await db.migrate.latest({
12
+ directory: backendPluginApi.resolvePackagePath("@nospt/plugin-dev-ai-hub-backend", "migrations"),
13
+ loadExtensions: [".js"]
14
+ });
15
+ return new AiAssetStore(db);
16
+ }
17
+ async upsertAsset(input) {
18
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19
+ const row = {
20
+ id: input.id,
21
+ provider_id: input.providerId,
22
+ name: input.name,
23
+ label: input.label ?? null,
24
+ description: input.description,
25
+ type: input.type,
26
+ tools: JSON.stringify(input.tools),
27
+ tags: JSON.stringify(input.tags),
28
+ author: input.author,
29
+ icon: input.icon ?? null,
30
+ version: input.version,
31
+ install_path: input.installPath ?? null,
32
+ install_paths: input.installPaths ? JSON.stringify(input.installPaths) : null,
33
+ content: input.content,
34
+ yaml_raw: input.yamlRaw,
35
+ metadata: input.metadata ? JSON.stringify(input.metadata) : null,
36
+ resources_content: input.resourcesContent ? JSON.stringify(input.resourcesContent) : null,
37
+ yaml_path: input.yamlPath,
38
+ md_path: input.mdPath,
39
+ repo_url: input.repoUrl,
40
+ branch: input.branch,
41
+ commit_sha: input.commitSha ?? null,
42
+ synced_at: now,
43
+ updated_at: now
44
+ };
45
+ await this.db("ai_assets").insert({ ...row, created_at: now }).onConflict("id").merge(Object.keys(row));
46
+ }
47
+ async listAssets(filter) {
48
+ let query = this.db("ai_assets");
49
+ if (filter.type) {
50
+ query = query.where("type", filter.type);
51
+ }
52
+ if (filter.tool) {
53
+ query = query.where(function toolFilter() {
54
+ this.where("tools", "like", `%"${filter.tool}"%`).orWhere("tools", "like", `%"all"%`);
55
+ });
56
+ }
57
+ if (filter.providerId) {
58
+ query = query.where("provider_id", filter.providerId);
59
+ }
60
+ if (filter.search) {
61
+ const words = filter.search.trim().split(/\s+/).filter(Boolean);
62
+ for (const word of words) {
63
+ const term = `%${word}%`;
64
+ query = query.where(function searchFilter() {
65
+ this.where("name", "like", term).orWhere("label", "like", term).orWhere("description", "like", term).orWhere("tags", "like", term).orWhere("content", "like", term);
66
+ });
67
+ }
68
+ }
69
+ if (filter.tags && filter.tags.length > 0) {
70
+ for (const tag of filter.tags) {
71
+ query = query.where("tags", "like", `%"${tag}"%`);
72
+ }
73
+ }
74
+ const countResult = await query.clone().count("id as count").first();
75
+ const totalCount = Number(countResult?.count ?? 0);
76
+ const page = Math.max(1, filter.page ?? 1);
77
+ const pageSize = Math.min(100, Math.max(1, filter.pageSize ?? 20));
78
+ const rows = await query.select(
79
+ "id",
80
+ "provider_id",
81
+ "name",
82
+ "label",
83
+ "description",
84
+ "type",
85
+ "tools",
86
+ "tags",
87
+ "author",
88
+ "icon",
89
+ "version",
90
+ "install_count",
91
+ "synced_at",
92
+ "created_at",
93
+ "updated_at"
94
+ ).orderBy("name", "asc").limit(pageSize).offset((page - 1) * pageSize);
95
+ return { items: rows.map((r) => this.rowToAssetSummary(r)), totalCount };
96
+ }
97
+ async getAsset(id) {
98
+ const row = await this.db("ai_assets").where("id", id).first();
99
+ return row ? this.rowToAsset(row) : null;
100
+ }
101
+ async incrementInstallCount(id) {
102
+ await this.db("ai_assets").where("id", id).increment("install_count", 1);
103
+ }
104
+ async deleteAssetsNotIn(providerId, ids) {
105
+ let query = this.db("ai_assets").where("provider_id", providerId);
106
+ if (ids.length > 0) {
107
+ query = query.whereNotIn("id", ids);
108
+ }
109
+ await query.delete();
110
+ }
111
+ async upsertSyncStatus(status) {
112
+ const row = {
113
+ provider_id: status.providerId,
114
+ last_sync: status.lastSync ?? null,
115
+ last_commit: status.lastCommit ?? null,
116
+ status: status.status,
117
+ error: status.error ?? null,
118
+ asset_count: status.assetCount
119
+ };
120
+ await this.db("ai_asset_sync_status").insert(row).onConflict("provider_id").merge(Object.keys(row));
121
+ }
122
+ async getSyncStatus(providerId) {
123
+ const row = await this.db("ai_asset_sync_status").where("provider_id", providerId).first();
124
+ if (!row) return null;
125
+ return {
126
+ providerId: row.provider_id,
127
+ lastSync: row.last_sync ?? void 0,
128
+ lastCommit: row.last_commit ?? void 0,
129
+ status: row.status,
130
+ error: row.error ?? void 0,
131
+ assetCount: row.asset_count ?? 0
132
+ };
133
+ }
134
+ async getAllSyncStatuses() {
135
+ const rows = await this.db("ai_asset_sync_status").select("*");
136
+ return rows.map((row) => ({
137
+ providerId: row.provider_id,
138
+ lastSync: row.last_sync ?? void 0,
139
+ lastCommit: row.last_commit ?? void 0,
140
+ status: row.status,
141
+ error: row.error ?? void 0,
142
+ assetCount: row.asset_count ?? 0
143
+ }));
144
+ }
145
+ async getStats() {
146
+ const rows = await this.db("ai_assets").select("type", "tools", "provider_id");
147
+ const byType = {
148
+ instruction: 0,
149
+ agent: 0,
150
+ skill: 0,
151
+ workflow: 0
152
+ };
153
+ const byTool = {};
154
+ const byProvider = {};
155
+ for (const row of rows) {
156
+ byType[row.type] = (byType[row.type] ?? 0) + 1;
157
+ byProvider[row.provider_id] = (byProvider[row.provider_id] ?? 0) + 1;
158
+ const tools = JSON.parse(row.tools || "[]");
159
+ for (const tool of tools) {
160
+ byTool[tool] = (byTool[tool] ?? 0) + 1;
161
+ }
162
+ }
163
+ const lastSyncRow = await this.db("ai_asset_sync_status").select(this.db.raw("max(last_sync) as last_sync")).first();
164
+ return {
165
+ totalAssets: rows.length,
166
+ byType,
167
+ byTool,
168
+ byProvider,
169
+ lastSync: lastSyncRow?.last_sync ?? void 0
170
+ };
171
+ }
172
+ rowToAssetSummary(row) {
173
+ return {
174
+ id: row.id,
175
+ providerId: row.provider_id,
176
+ name: row.name,
177
+ label: row.label ?? void 0,
178
+ description: row.description,
179
+ type: row.type,
180
+ tools: JSON.parse(row.tools || "[]"),
181
+ tags: JSON.parse(row.tags || "[]"),
182
+ author: row.author,
183
+ icon: row.icon ?? void 0,
184
+ version: row.version,
185
+ installCount: row.install_count ?? 0,
186
+ syncedAt: row.synced_at,
187
+ createdAt: row.created_at,
188
+ updatedAt: row.updated_at
189
+ };
190
+ }
191
+ rowToAsset(row) {
192
+ return {
193
+ id: row.id,
194
+ providerId: row.provider_id,
195
+ name: row.name,
196
+ label: row.label ?? void 0,
197
+ description: row.description,
198
+ type: row.type,
199
+ tools: JSON.parse(row.tools || "[]"),
200
+ tags: JSON.parse(row.tags || "[]"),
201
+ author: row.author,
202
+ icon: row.icon ?? void 0,
203
+ version: row.version,
204
+ installPath: row.install_path ?? void 0,
205
+ installPaths: row.install_paths ? JSON.parse(row.install_paths) : void 0,
206
+ content: row.content,
207
+ yamlRaw: row.yaml_raw,
208
+ metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
209
+ resourcesContent: row.resources_content ? JSON.parse(row.resources_content) : void 0,
210
+ yamlPath: row.yaml_path,
211
+ mdPath: row.md_path,
212
+ repoUrl: row.repo_url,
213
+ branch: row.branch,
214
+ commitSha: row.commit_sha ?? void 0,
215
+ installCount: row.install_count ?? 0,
216
+ syncedAt: row.synced_at,
217
+ createdAt: row.created_at,
218
+ updatedAt: row.updated_at
219
+ };
220
+ }
221
+ }
222
+
223
+ exports.AiAssetStore = AiAssetStore;
224
+ //# sourceMappingURL=AiAssetStore.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiAssetStore.cjs.js","sources":["../../src/database/AiAssetStore.ts"],"sourcesContent":["import type { Knex } from 'knex';\nimport { resolvePackagePath, type DatabaseService } from '@backstage/backend-plugin-api';\nimport type { AiAsset, AiAssetSummary, AiHubStats, AssetListFilter, AssetType } from '@nospt/plugin-dev-ai-hub-common';\nimport type { AiAssetInput, SyncStatus } from '../types';\n\nexport class AiAssetStore {\n private constructor(private readonly db: Knex) {}\n\n static async create(options: { database: DatabaseService }): Promise<AiAssetStore> {\n const db = await options.database.getClient();\n await db.migrate.latest({\n directory: resolvePackagePath('@nospt/plugin-dev-ai-hub-backend', 'migrations'),\n loadExtensions: ['.js'],\n });\n return new AiAssetStore(db);\n }\n\n async upsertAsset(input: AiAssetInput): Promise<void> {\n const now = new Date().toISOString();\n const row = {\n id: input.id,\n provider_id: input.providerId,\n name: input.name,\n label: input.label ?? null,\n description: input.description,\n type: input.type,\n tools: JSON.stringify(input.tools),\n tags: JSON.stringify(input.tags),\n author: input.author,\n icon: input.icon ?? null,\n version: input.version,\n install_path: input.installPath ?? null,\n install_paths: input.installPaths ? JSON.stringify(input.installPaths) : null,\n content: input.content,\n yaml_raw: input.yamlRaw,\n metadata: input.metadata ? JSON.stringify(input.metadata) : null,\n resources_content: input.resourcesContent ? JSON.stringify(input.resourcesContent) : null,\n yaml_path: input.yamlPath,\n md_path: input.mdPath,\n repo_url: input.repoUrl,\n branch: input.branch,\n commit_sha: input.commitSha ?? null,\n synced_at: now,\n updated_at: now,\n };\n\n await this.db('ai_assets')\n .insert({ ...row, created_at: now })\n .onConflict('id')\n .merge(Object.keys(row) as (keyof typeof row)[]);\n }\n\n async listAssets(\n filter: AssetListFilter,\n ): Promise<{ items: AiAssetSummary[]; totalCount: number }> {\n let query = this.db<Record<string, unknown>>('ai_assets');\n\n if (filter.type) {\n query = query.where('type', filter.type);\n }\n if (filter.tool) {\n query = query.where(function toolFilter() {\n this.where('tools', 'like', `%\"${filter.tool}\"%`)\n .orWhere('tools', 'like', `%\"all\"%`);\n });\n }\n if (filter.providerId) {\n query = query.where('provider_id', filter.providerId);\n }\n if (filter.search) {\n // Split into words so \"Python linting\" matches items containing both words\n const words = filter.search.trim().split(/\\s+/).filter(Boolean);\n for (const word of words) {\n const term = `%${word}%`;\n query = query.where(function searchFilter() {\n this.where('name', 'like', term)\n .orWhere('label', 'like', term)\n .orWhere('description', 'like', term)\n .orWhere('tags', 'like', term)\n .orWhere('content', 'like', term);\n });\n }\n }\n if (filter.tags && filter.tags.length > 0) {\n for (const tag of filter.tags) {\n query = query.where('tags', 'like', `%\"${tag}\"%`);\n }\n }\n\n const countResult = await query.clone().count('id as count').first();\n const totalCount = Number(countResult?.count ?? 0);\n\n const page = Math.max(1, filter.page ?? 1);\n const pageSize = Math.min(100, Math.max(1, filter.pageSize ?? 20));\n\n const rows = await query\n .select('id', 'provider_id', 'name', 'label', 'description', 'type', 'tools', 'tags',\n 'author', 'icon', 'version', 'install_count', 'synced_at', 'created_at', 'updated_at')\n .orderBy('name', 'asc')\n .limit(pageSize)\n .offset((page - 1) * pageSize);\n\n return { items: rows.map(r => this.rowToAssetSummary(r)), totalCount };\n }\n\n async getAsset(id: string): Promise<AiAsset | null> {\n const row = await this.db('ai_assets').where('id', id).first();\n return row ? this.rowToAsset(row) : null;\n }\n\n async incrementInstallCount(id: string): Promise<void> {\n await this.db('ai_assets').where('id', id).increment('install_count', 1);\n }\n\n async deleteAssetsNotIn(providerId: string, ids: string[]): Promise<void> {\n let query = this.db('ai_assets').where('provider_id', providerId);\n if (ids.length > 0) {\n query = query.whereNotIn('id', ids);\n }\n await query.delete();\n }\n\n async upsertSyncStatus(status: SyncStatus): Promise<void> {\n const row = {\n provider_id: status.providerId,\n last_sync: status.lastSync ?? null,\n last_commit: status.lastCommit ?? null,\n status: status.status,\n error: status.error ?? null,\n asset_count: status.assetCount,\n };\n await this.db('ai_asset_sync_status')\n .insert(row)\n .onConflict('provider_id')\n .merge(Object.keys(row) as (keyof typeof row)[]);\n }\n\n async getSyncStatus(providerId: string): Promise<SyncStatus | null> {\n const row = await this.db('ai_asset_sync_status')\n .where('provider_id', providerId)\n .first();\n if (!row) return null;\n return {\n providerId: row.provider_id,\n lastSync: row.last_sync ?? undefined,\n lastCommit: row.last_commit ?? undefined,\n status: row.status,\n error: row.error ?? undefined,\n assetCount: row.asset_count ?? 0,\n };\n }\n\n async getAllSyncStatuses(): Promise<SyncStatus[]> {\n const rows = await this.db('ai_asset_sync_status').select('*');\n return rows.map(row => ({\n providerId: row.provider_id,\n lastSync: row.last_sync ?? undefined,\n lastCommit: row.last_commit ?? undefined,\n status: row.status,\n error: row.error ?? undefined,\n assetCount: row.asset_count ?? 0,\n }));\n }\n\n async getStats(): Promise<AiHubStats> {\n const rows = await this.db('ai_assets').select('type', 'tools', 'provider_id');\n\n const byType: Record<AssetType, number> = {\n instruction: 0,\n agent: 0,\n skill: 0,\n workflow: 0,\n };\n const byTool: Record<string, number> = {};\n const byProvider: Record<string, number> = {};\n\n for (const row of rows) {\n byType[row.type as AssetType] = (byType[row.type as AssetType] ?? 0) + 1;\n byProvider[row.provider_id] = (byProvider[row.provider_id] ?? 0) + 1;\n\n const tools: string[] = JSON.parse(row.tools || '[]');\n for (const tool of tools) {\n byTool[tool] = (byTool[tool] ?? 0) + 1;\n }\n }\n\n const lastSyncRow = await this.db('ai_asset_sync_status')\n .select(this.db.raw('max(last_sync) as last_sync'))\n .first();\n\n return {\n totalAssets: rows.length,\n byType,\n byTool,\n byProvider,\n lastSync: (lastSyncRow?.last_sync as string | null) ?? undefined,\n };\n }\n\n private rowToAssetSummary(row: Record<string, unknown>): AiAssetSummary {\n return {\n id: row.id as string,\n providerId: row.provider_id as string,\n name: row.name as string,\n label: (row.label as string | null) ?? undefined,\n description: row.description as string,\n type: row.type as AssetType,\n tools: JSON.parse((row.tools as string) || '[]'),\n tags: JSON.parse((row.tags as string) || '[]'),\n author: row.author as string,\n icon: (row.icon as string | null) ?? undefined,\n version: row.version as string,\n installCount: (row.install_count as number) ?? 0,\n syncedAt: row.synced_at as string,\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n }\n\n private rowToAsset(row: Record<string, unknown>): AiAsset {\n return {\n id: row.id as string,\n providerId: row.provider_id as string,\n name: row.name as string,\n label: (row.label as string | null) ?? undefined,\n description: row.description as string,\n type: row.type as AssetType,\n tools: JSON.parse((row.tools as string) || '[]'),\n tags: JSON.parse((row.tags as string) || '[]'),\n author: row.author as string,\n icon: (row.icon as string | null) ?? undefined,\n version: row.version as string,\n installPath: (row.install_path as string | null) ?? undefined,\n installPaths: row.install_paths\n ? JSON.parse(row.install_paths as string)\n : undefined,\n content: row.content as string,\n yamlRaw: row.yaml_raw as string,\n metadata: row.metadata\n ? JSON.parse(row.metadata as string)\n : undefined,\n resourcesContent: row.resources_content\n ? JSON.parse(row.resources_content as string)\n : undefined,\n yamlPath: row.yaml_path as string,\n mdPath: row.md_path as string,\n repoUrl: row.repo_url as string,\n branch: row.branch as string,\n commitSha: (row.commit_sha as string | null) ?? undefined,\n installCount: (row.install_count as number) ?? 0,\n syncedAt: row.synced_at as string,\n createdAt: row.created_at as string,\n updatedAt: row.updated_at as string,\n };\n }\n}\n"],"names":["resolvePackagePath"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var plugin = require('./plugin.cjs.js');
6
+
7
+
8
+
9
+ exports.default = plugin.devAiHubPlugin;
10
+ exports.devAiHubPlugin = plugin.devAiHubPlugin;
11
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
+
3
+ declare const devAiHubPlugin: _backstage_backend_plugin_api.BackendFeature;
4
+
5
+ export { devAiHubPlugin as default, devAiHubPlugin };
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var pluginDevAiHubNode = require('@nospt/plugin-dev-ai-hub-node');
5
+ var AiAssetStore = require('./database/AiAssetStore.cjs.js');
6
+ var AiAssetSyncService = require('./service/AiAssetSyncService.cjs.js');
7
+ var router = require('./router.cjs.js');
8
+
9
+ const devAiHubPlugin = backendPluginApi.createBackendPlugin({
10
+ pluginId: "dev-ai-hub",
11
+ register(env) {
12
+ const externalProviders = [];
13
+ env.registerExtensionPoint(pluginDevAiHubNode.devAiHubProviderExtensionPoint, {
14
+ addProvider(provider) {
15
+ externalProviders.push(provider);
16
+ }
17
+ });
18
+ env.registerInit({
19
+ deps: {
20
+ config: backendPluginApi.coreServices.rootConfig,
21
+ logger: backendPluginApi.coreServices.logger,
22
+ database: backendPluginApi.coreServices.database,
23
+ scheduler: backendPluginApi.coreServices.scheduler,
24
+ httpRouter: backendPluginApi.coreServices.httpRouter,
25
+ urlReader: backendPluginApi.coreServices.urlReader
26
+ },
27
+ async init({ config, logger, database, scheduler, httpRouter, urlReader }) {
28
+ const store = await AiAssetStore.AiAssetStore.create({ database });
29
+ const providers = (config.getOptionalConfigArray("devAiHub.providers") ?? []).map((p) => ({
30
+ id: p.getString("id"),
31
+ type: p.getString("type"),
32
+ target: p.getString("target"),
33
+ branch: p.getOptionalString("branch") ?? "main",
34
+ schedule: {
35
+ frequency: {
36
+ minutes: p.getOptionalNumber("schedule.frequency.minutes"),
37
+ hours: p.getOptionalNumber("schedule.frequency.hours")
38
+ },
39
+ timeout: {
40
+ minutes: p.getOptionalNumber("schedule.timeout.minutes"),
41
+ hours: p.getOptionalNumber("schedule.timeout.hours")
42
+ }
43
+ },
44
+ filters: p.has("filters") ? {
45
+ tools: p.getOptionalStringArray("filters.tools"),
46
+ types: p.getOptionalStringArray("filters.types")
47
+ } : void 0
48
+ }));
49
+ const syncService = new AiAssetSyncService.AiAssetSyncService({
50
+ logger,
51
+ store,
52
+ scheduler,
53
+ urlReader,
54
+ providers,
55
+ externalProviders
56
+ });
57
+ await syncService.start();
58
+ const router$1 = router.createRouter({ logger, store, syncService, providers });
59
+ httpRouter.use(router$1);
60
+ httpRouter.addAuthPolicy({ path: "/assets", allow: "unauthenticated" });
61
+ httpRouter.addAuthPolicy({ path: "/providers", allow: "unauthenticated" });
62
+ httpRouter.addAuthPolicy({ path: "/stats", allow: "unauthenticated" });
63
+ httpRouter.addAuthPolicy({ path: "/mcp", allow: "unauthenticated" });
64
+ }
65
+ });
66
+ }
67
+ });
68
+
69
+ exports.devAiHubPlugin = devAiHubPlugin;
70
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n devAiHubProviderExtensionPoint,\n type AiAssetProvider,\n} from '@nospt/plugin-dev-ai-hub-node';\nimport { AiAssetStore } from './database/AiAssetStore';\nimport { AiAssetSyncService } from './service/AiAssetSyncService';\nimport { createRouter } from './router';\nimport type { ProviderConfig } from './types';\n\nexport const devAiHubPlugin = createBackendPlugin({\n pluginId: 'dev-ai-hub',\n register(env) {\n const externalProviders: AiAssetProvider[] = [];\n\n env.registerExtensionPoint(devAiHubProviderExtensionPoint, {\n addProvider(provider) {\n externalProviders.push(provider);\n },\n });\n\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n database: coreServices.database,\n scheduler: coreServices.scheduler,\n httpRouter: coreServices.httpRouter,\n urlReader: coreServices.urlReader,\n },\n async init({ config, logger, database, scheduler, httpRouter, urlReader }) {\n const store = await AiAssetStore.create({ database });\n\n const providers: ProviderConfig[] = (\n config.getOptionalConfigArray('devAiHub.providers') ?? []\n ).map(p => ({\n id: p.getString('id'),\n type: p.getString('type') as ProviderConfig['type'],\n target: p.getString('target'),\n branch: p.getOptionalString('branch') ?? 'main',\n schedule: {\n frequency: {\n minutes: p.getOptionalNumber('schedule.frequency.minutes'),\n hours: p.getOptionalNumber('schedule.frequency.hours'),\n },\n timeout: {\n minutes: p.getOptionalNumber('schedule.timeout.minutes'),\n hours: p.getOptionalNumber('schedule.timeout.hours'),\n },\n },\n filters: p.has('filters')\n ? {\n tools: p.getOptionalStringArray('filters.tools'),\n types: p.getOptionalStringArray('filters.types'),\n }\n : undefined,\n }));\n\n const syncService = new AiAssetSyncService({\n logger,\n store,\n scheduler,\n urlReader,\n providers,\n externalProviders,\n });\n\n await syncService.start();\n\n const router = createRouter({ logger, store, syncService, providers });\n\n httpRouter.use(router);\n\n httpRouter.addAuthPolicy({ path: '/assets', allow: 'unauthenticated' });\n httpRouter.addAuthPolicy({ path: '/providers', allow: 'unauthenticated' });\n httpRouter.addAuthPolicy({ path: '/stats', allow: 'unauthenticated' });\n httpRouter.addAuthPolicy({ path: '/mcp', allow: 'unauthenticated' });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","devAiHubProviderExtensionPoint","coreServices","AiAssetStore","AiAssetSyncService","router","createRouter"],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+ var express = require('express');
5
+ var archiver = require('archiver');
6
+ var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
7
+ var McpServerService = require('./service/McpServerService.cjs.js');
8
+
9
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
10
+
11
+ var express__default = /*#__PURE__*/_interopDefaultCompat(express);
12
+ var archiver__default = /*#__PURE__*/_interopDefaultCompat(archiver);
13
+
14
+ function createRouter(options) {
15
+ const { store, syncService, providers } = options;
16
+ const router = express__default.default.Router();
17
+ const mcpSessions = /* @__PURE__ */ new Map();
18
+ router.use(express__default.default.json());
19
+ router.get("/assets", async (req, res) => {
20
+ try {
21
+ const filter = {
22
+ type: req.query.type,
23
+ tool: req.query.tool,
24
+ providerId: req.query.provider,
25
+ search: req.query.search,
26
+ tags: req.query.tags ? req.query.tags.split(",").filter(Boolean) : void 0,
27
+ page: req.query.page ? Number(req.query.page) : 1,
28
+ pageSize: req.query.pageSize ? Number(req.query.pageSize) : 20
29
+ };
30
+ const { items, totalCount } = await store.listAssets(filter);
31
+ res.json({
32
+ items,
33
+ totalCount,
34
+ page: filter.page,
35
+ pageSize: filter.pageSize
36
+ });
37
+ } catch (err) {
38
+ options.logger.error("GET /assets failed", err);
39
+ res.status(500).json({ error: "Internal server error" });
40
+ }
41
+ });
42
+ router.get("/assets/:id", async (req, res) => {
43
+ try {
44
+ const asset = await store.getAsset(req.params.id);
45
+ if (!asset) return res.status(404).json({ error: "Asset not found" });
46
+ return res.json(asset);
47
+ } catch (err) {
48
+ options.logger.error("GET /assets/:id failed", err);
49
+ return res.status(500).json({ error: "Internal server error" });
50
+ }
51
+ });
52
+ router.get("/assets/:id/raw", async (req, res) => {
53
+ try {
54
+ const asset = await store.getAsset(req.params.id);
55
+ if (!asset) return res.status(404).json({ error: "Asset not found" });
56
+ res.setHeader("Content-Type", "text/markdown; charset=utf-8");
57
+ return res.send(asset.content);
58
+ } catch (err) {
59
+ options.logger.error("GET /assets/:id/raw failed", err);
60
+ return res.status(500).json({ error: "Internal server error" });
61
+ }
62
+ });
63
+ router.get("/assets/:id/download", async (req, res) => {
64
+ try {
65
+ const asset = await store.getAsset(req.params.id);
66
+ if (!asset) return res.status(404).json({ error: "Asset not found" });
67
+ const filename = `${asset.name.replace(/\s+/g, "-").toLowerCase()}`;
68
+ if (asset.type === "skill") {
69
+ res.setHeader("Content-Type", "application/zip");
70
+ res.setHeader(
71
+ "Content-Disposition",
72
+ `attachment; filename="${filename}.zip"`
73
+ );
74
+ const archive = archiver__default.default("zip");
75
+ archive.pipe(res);
76
+ archive.append(asset.content, { name: "SKILL.md" });
77
+ if (asset.resourcesContent) {
78
+ for (const [resourcePath, resourceContent] of Object.entries(asset.resourcesContent)) {
79
+ archive.append(resourceContent, { name: resourcePath });
80
+ }
81
+ }
82
+ await archive.finalize();
83
+ } else {
84
+ res.setHeader("Content-Type", "text/markdown; charset=utf-8");
85
+ res.setHeader(
86
+ "Content-Disposition",
87
+ `attachment; filename="${filename}.md"`
88
+ );
89
+ return res.send(asset.content);
90
+ }
91
+ return void 0;
92
+ } catch (err) {
93
+ options.logger.error("GET /assets/:id/download failed", err);
94
+ return res.status(500).json({ error: "Internal server error" });
95
+ }
96
+ });
97
+ router.post("/assets/:id/track-install", async (req, res) => {
98
+ try {
99
+ const asset = await store.getAsset(req.params.id);
100
+ if (!asset) return res.status(404).json({ error: "Asset not found" });
101
+ await store.incrementInstallCount(req.params.id);
102
+ return res.status(204).send();
103
+ } catch (err) {
104
+ options.logger.error("POST /assets/:id/track-install failed", err);
105
+ return res.status(500).json({ error: "Internal server error" });
106
+ }
107
+ });
108
+ router.get("/providers", async (_req, res) => {
109
+ try {
110
+ const statuses = await store.getAllSyncStatuses();
111
+ const statusMap = new Map(statuses.map((s) => [s.providerId, s]));
112
+ const result = providers.map((p) => {
113
+ const status = statusMap.get(p.id);
114
+ return {
115
+ id: p.id,
116
+ type: p.type,
117
+ target: p.target,
118
+ branch: p.branch,
119
+ lastSync: status?.lastSync,
120
+ lastCommit: status?.lastCommit,
121
+ status: status?.status ?? "idle",
122
+ error: status?.error,
123
+ assetCount: status?.assetCount ?? 0
124
+ };
125
+ });
126
+ res.json(result);
127
+ } catch (err) {
128
+ options.logger.error("GET /providers failed", err);
129
+ res.status(500).json({ error: "Internal server error" });
130
+ }
131
+ });
132
+ router.get("/providers/:id/status", async (req, res) => {
133
+ try {
134
+ const provider = providers.find((p) => p.id === req.params.id);
135
+ if (!provider) return res.status(404).json({ error: "Provider not found" });
136
+ const status = await store.getSyncStatus(req.params.id);
137
+ return res.json(
138
+ status ?? {
139
+ providerId: req.params.id,
140
+ status: "idle",
141
+ assetCount: 0
142
+ }
143
+ );
144
+ } catch (err) {
145
+ options.logger.error("GET /providers/:id/status failed", err);
146
+ return res.status(500).json({ error: "Internal server error" });
147
+ }
148
+ });
149
+ router.post("/providers/:id/sync", async (req, res) => {
150
+ try {
151
+ const provider = providers.find((p) => p.id === req.params.id);
152
+ if (!provider) return res.status(404).json({ error: "Provider not found" });
153
+ setImmediate(() => syncService.syncProvider(provider));
154
+ return res.json({ message: "Sync triggered", providerId: provider.id });
155
+ } catch (err) {
156
+ options.logger.error("POST /providers/:id/sync failed", err);
157
+ return res.status(500).json({ error: "Internal server error" });
158
+ }
159
+ });
160
+ router.get("/stats", async (_req, res) => {
161
+ try {
162
+ const stats = await store.getStats();
163
+ res.json(stats);
164
+ } catch (err) {
165
+ options.logger.error("GET /stats failed", err);
166
+ res.status(500).json({ error: "Internal server error" });
167
+ }
168
+ });
169
+ router.post("/mcp", async (req, res) => {
170
+ try {
171
+ const sessionId = req.headers["mcp-session-id"];
172
+ if (sessionId) {
173
+ const transport2 = mcpSessions.get(sessionId);
174
+ if (!transport2) {
175
+ res.status(404).json({ error: "MCP session not found" });
176
+ return;
177
+ }
178
+ await transport2.handleRequest(req, res, req.body);
179
+ return;
180
+ }
181
+ const toolFilter = req.query.tool ?? req.headers["x-ai-hub-tool"] ?? "";
182
+ const providerFilter = req.query.provider ?? req.headers["x-ai-hub-provider"] ?? "";
183
+ const proactiveEnabled = (req.query.proactive ?? req.headers["x-ai-hub-proactive"] ?? "false") === "true";
184
+ const newSessionId = crypto.randomUUID();
185
+ const transport = new streamableHttp_js.StreamableHTTPServerTransport({
186
+ sessionIdGenerator: () => newSessionId,
187
+ onsessioninitialized: (id) => {
188
+ mcpSessions.set(id, transport);
189
+ options.logger.debug(
190
+ `dev-ai-hub MCP: session ${id} opened (tool="${toolFilter || "all"}", provider="${providerFilter || "all"}", proactive=${proactiveEnabled})`
191
+ );
192
+ }
193
+ });
194
+ transport.onclose = () => {
195
+ mcpSessions.delete(newSessionId);
196
+ options.logger.debug(`dev-ai-hub MCP: session ${newSessionId} closed`);
197
+ };
198
+ const server = McpServerService.createMcpServer(store, toolFilter, providerFilter, providers, proactiveEnabled);
199
+ await server.connect(transport);
200
+ await transport.handleRequest(req, res, req.body);
201
+ } catch (err) {
202
+ options.logger.error("POST /mcp failed", err);
203
+ if (!res.headersSent) res.status(500).json({ error: "Internal server error" });
204
+ }
205
+ });
206
+ router.get("/mcp", async (req, res) => {
207
+ try {
208
+ const sessionId = req.headers["mcp-session-id"];
209
+ if (!sessionId) {
210
+ res.status(400).json({ error: "mcp-session-id header required" });
211
+ return;
212
+ }
213
+ const transport = mcpSessions.get(sessionId);
214
+ if (!transport) {
215
+ res.status(404).json({ error: "MCP session not found" });
216
+ return;
217
+ }
218
+ await transport.handleRequest(req, res);
219
+ } catch (err) {
220
+ options.logger.error("GET /mcp failed", err);
221
+ if (!res.headersSent) res.status(500).json({ error: "Internal server error" });
222
+ }
223
+ });
224
+ router.delete("/mcp", async (req, res) => {
225
+ try {
226
+ const sessionId = req.headers["mcp-session-id"];
227
+ if (!sessionId) {
228
+ res.status(400).json({ error: "mcp-session-id header required" });
229
+ return;
230
+ }
231
+ const transport = mcpSessions.get(sessionId);
232
+ if (!transport) {
233
+ res.status(404).json({ error: "MCP session not found" });
234
+ return;
235
+ }
236
+ await transport.handleRequest(req, res);
237
+ mcpSessions.delete(sessionId);
238
+ } catch (err) {
239
+ options.logger.error("DELETE /mcp failed", err);
240
+ if (!res.headersSent) res.status(500).json({ error: "Internal server error" });
241
+ }
242
+ });
243
+ return router;
244
+ }
245
+
246
+ exports.createRouter = createRouter;
247
+ //# sourceMappingURL=router.cjs.js.map