@gokiteam/goki-dev 0.2.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 +478 -0
- package/bin/goki-dev.js +452 -0
- package/bin/mcp-server.js +16 -0
- package/bin/secrets-cli.js +302 -0
- package/cli/ComposeOverrideGenerator.js +226 -0
- package/cli/ComposeParser.js +73 -0
- package/cli/ConfigGenerator.js +304 -0
- package/cli/ConfigManager.js +46 -0
- package/cli/DatabaseManager.js +94 -0
- package/cli/DevToolsChecker.js +21 -0
- package/cli/DevToolsDir.js +66 -0
- package/cli/DevToolsManager.js +451 -0
- package/cli/DockerManager.js +138 -0
- package/cli/FunctionManager.js +95 -0
- package/cli/HttpProxyRewriter.js +91 -0
- package/cli/Logger.js +10 -0
- package/cli/McpConfigManager.js +123 -0
- package/cli/NgrokManager.js +431 -0
- package/cli/ProjectCLI.js +2322 -0
- package/cli/PubSubManager.js +129 -0
- package/cli/SnapshotManager.js +88 -0
- package/cli/UiFormatter.js +292 -0
- package/cli/WebhookUrlRewriter.js +32 -0
- package/cli/secrets/BiometricAuth.js +125 -0
- package/cli/secrets/SecretInjector.js +47 -0
- package/cli/secrets/SecretsConfig.js +141 -0
- package/cli/secrets/SecretsDoctor.js +384 -0
- package/cli/secrets/SecretsManager.js +255 -0
- package/client/dist/client.d.ts +332 -0
- package/client/dist/client.js +507 -0
- package/client/dist/helpers.d.ts +62 -0
- package/client/dist/helpers.js +122 -0
- package/client/dist/index.d.ts +59 -0
- package/client/dist/index.js +78 -0
- package/client/dist/package.json +1 -0
- package/client/dist/types.d.ts +280 -0
- package/client/dist/types.js +7 -0
- package/config.development +46 -0
- package/config.test +18 -0
- package/guidelines/CodingStyleGuideline.md +148 -0
- package/guidelines/CommentingGuideline.md +10 -0
- package/guidelines/HttpApiImplementationGuideline.md +137 -0
- package/guidelines/NamingGuideline.md +182 -0
- package/package.json +138 -0
- package/patterns/api/[collectionName]/Controllers.md +62 -0
- package/patterns/api/[collectionName]/Logic.md +154 -0
- package/patterns/api/[collectionName]/Permissions.md +81 -0
- package/patterns/api/[collectionName]/Router.md +83 -0
- package/patterns/api/[collectionName]/Schemas.md +197 -0
- package/patterns/configs/Patterns.md +7 -0
- package/patterns/enums/Patterns.md +24 -0
- package/patterns/errorHandling/Patterns.md +185 -0
- package/patterns/testing/Patterns.md +232 -0
- package/src/Server.js +238 -0
- package/src/api/dashboard/Controllers.js +9 -0
- package/src/api/dashboard/Logic.js +76 -0
- package/src/api/dashboard/Router.js +11 -0
- package/src/api/dashboard/Schemas.js +47 -0
- package/src/api/data/Controllers.js +26 -0
- package/src/api/data/Logic.js +188 -0
- package/src/api/data/Router.js +16 -0
- package/src/api/docker/Controllers.js +33 -0
- package/src/api/docker/Logic.js +268 -0
- package/src/api/docker/Router.js +15 -0
- package/src/api/docker/Schemas.js +80 -0
- package/src/api/docs/Controllers.js +15 -0
- package/src/api/docs/Logic.js +85 -0
- package/src/api/docs/Router.js +12 -0
- package/src/api/export/Controllers.js +30 -0
- package/src/api/export/Logic.js +143 -0
- package/src/api/export/Router.js +18 -0
- package/src/api/export/Schemas.js +104 -0
- package/src/api/firestore/Controllers.js +152 -0
- package/src/api/firestore/Logic.js +474 -0
- package/src/api/firestore/Router.js +23 -0
- package/src/api/functions/Controllers.js +261 -0
- package/src/api/functions/Logic.js +710 -0
- package/src/api/functions/Router.js +50 -0
- package/src/api/functions/Schemas.js +193 -0
- package/src/api/gateway/Controllers.js +72 -0
- package/src/api/gateway/Logic.js +74 -0
- package/src/api/gateway/Router.js +10 -0
- package/src/api/gateway/Schemas.js +19 -0
- package/src/api/health/Controllers.js +14 -0
- package/src/api/health/Logic.js +24 -0
- package/src/api/health/Router.js +12 -0
- package/src/api/httpTraffic/Controllers.js +29 -0
- package/src/api/httpTraffic/Logic.js +33 -0
- package/src/api/httpTraffic/Router.js +9 -0
- package/src/api/httpTraffic/Schemas.js +23 -0
- package/src/api/logging/Controllers.js +80 -0
- package/src/api/logging/Logic.js +461 -0
- package/src/api/logging/Router.js +24 -0
- package/src/api/logging/Schemas.js +43 -0
- package/src/api/mqtt/Controllers.js +17 -0
- package/src/api/mqtt/Logic.js +66 -0
- package/src/api/mqtt/Router.js +12 -0
- package/src/api/postgres/Controllers.js +97 -0
- package/src/api/postgres/Logic.js +221 -0
- package/src/api/postgres/Router.js +21 -0
- package/src/api/pubsub/Controllers.js +236 -0
- package/src/api/pubsub/Logic.js +732 -0
- package/src/api/pubsub/Router.js +41 -0
- package/src/api/pubsub/Schemas.js +355 -0
- package/src/api/redis/Controllers.js +63 -0
- package/src/api/redis/Logic.js +239 -0
- package/src/api/redis/Router.js +21 -0
- package/src/api/scheduler/Controllers.js +27 -0
- package/src/api/scheduler/Logic.js +49 -0
- package/src/api/scheduler/Router.js +16 -0
- package/src/api/services/Controllers.js +26 -0
- package/src/api/services/Logic.js +205 -0
- package/src/api/services/Router.js +14 -0
- package/src/api/services/Schemas.js +66 -0
- package/src/api/snapshots/Controllers.js +37 -0
- package/src/api/snapshots/Logic.js +797 -0
- package/src/api/snapshots/Router.js +15 -0
- package/src/api/snapshots/Schemas.js +23 -0
- package/src/api/webhooks/Controllers.js +49 -0
- package/src/api/webhooks/Logic.js +137 -0
- package/src/api/webhooks/Router.js +12 -0
- package/src/api/webhooks/Schemas.js +31 -0
- package/src/configs/Application.js +147 -0
- package/src/configs/Default.js +13 -0
- package/src/consumers/BlackboxLogsConsumer.js +235 -0
- package/src/consumers/DockerLogsConsumer.js +687 -0
- package/src/db/Tables.js +66 -0
- package/src/db/schemas/firestore.js +18 -0
- package/src/db/schemas/functions.js +65 -0
- package/src/db/schemas/httpTraffic.js +43 -0
- package/src/db/schemas/logging.js +74 -0
- package/src/db/schemas/migrations.js +64 -0
- package/src/db/schemas/mqtt.js +56 -0
- package/src/db/schemas/pubsub.js +90 -0
- package/src/db/schemas/pubsubRegistry.js +22 -0
- package/src/db/schemas/webhooks.js +28 -0
- package/src/emulation/awsiot/Controllers.js +91 -0
- package/src/emulation/awsiot/Logic.js +70 -0
- package/src/emulation/awsiot/Router.js +19 -0
- package/src/emulation/awsiot/Server.js +100 -0
- package/src/emulation/firestore/Server.js +136 -0
- package/src/emulation/logging/Controllers.js +212 -0
- package/src/emulation/logging/Logic.js +416 -0
- package/src/emulation/logging/Router.js +36 -0
- package/src/emulation/logging/Schemas.js +82 -0
- package/src/emulation/logging/Server.js +108 -0
- package/src/emulation/pubsub/Controllers.js +279 -0
- package/src/emulation/pubsub/DefaultTopics.js +162 -0
- package/src/emulation/pubsub/Logic.js +427 -0
- package/src/emulation/pubsub/README.md +309 -0
- package/src/emulation/pubsub/Router.js +33 -0
- package/src/emulation/pubsub/Server.js +104 -0
- package/src/emulation/pubsub/ShadowPoller.js +276 -0
- package/src/emulation/pubsub/ShadowSubscriptionManager.js +199 -0
- package/src/enums/ContainerNames.js +106 -0
- package/src/enums/ErrorReason.js +28 -0
- package/src/enums/FunctionStatuses.js +15 -0
- package/src/enums/FunctionTriggerTypes.js +15 -0
- package/src/enums/GatewayState.js +7 -0
- package/src/enums/ServiceNames.js +68 -0
- package/src/jobs/DatabaseMaintenance.js +184 -0
- package/src/jobs/MessageHistoryCleanup.js +152 -0
- package/src/mcp/ApiClient.js +25 -0
- package/src/mcp/Server.js +52 -0
- package/src/mcp/prompts/debugging.js +104 -0
- package/src/mcp/resources/platform.js +118 -0
- package/src/mcp/tools/data.js +84 -0
- package/src/mcp/tools/docker.js +166 -0
- package/src/mcp/tools/firestore.js +162 -0
- package/src/mcp/tools/functions.js +380 -0
- package/src/mcp/tools/httpTraffic.js +69 -0
- package/src/mcp/tools/logging.js +174 -0
- package/src/mcp/tools/mqtt.js +37 -0
- package/src/mcp/tools/postgres.js +130 -0
- package/src/mcp/tools/pubsub.js +316 -0
- package/src/mcp/tools/redis.js +146 -0
- package/src/mcp/tools/services.js +169 -0
- package/src/mcp/tools/snapshots.js +88 -0
- package/src/mcp/tools/webhooks.js +115 -0
- package/src/middleware/DevProxy.js +67 -0
- package/src/middleware/ErrorCatcher.js +35 -0
- package/src/middleware/HttpProxy.js +215 -0
- package/src/middleware/Reply.js +24 -0
- package/src/middleware/TraceId.js +9 -0
- package/src/middleware/WebhookProxy.js +234 -0
- package/src/protocols/mqtt/Broker.js +92 -0
- package/src/protocols/mqtt/Handlers.js +175 -0
- package/src/protocols/mqtt/PubSubBridge.js +162 -0
- package/src/protocols/mqtt/Server.js +116 -0
- package/src/runtime/FunctionRunner.js +179 -0
- package/src/services/AppGatewayService.js +582 -0
- package/src/singletons/FirestoreBroadcaster.js +367 -0
- package/src/singletons/FunctionTriggerDispatcher.js +456 -0
- package/src/singletons/FunctionsService.js +418 -0
- package/src/singletons/HttpProxy.js +224 -0
- package/src/singletons/LogBroadcaster.js +159 -0
- package/src/singletons/Logger.js +49 -0
- package/src/singletons/MemoryJsonStore.js +175 -0
- package/src/singletons/MessageBroadcaster.js +190 -0
- package/src/singletons/PostgresBroadcaster.js +367 -0
- package/src/singletons/PostgresClient.js +180 -0
- package/src/singletons/RedisClient.js +184 -0
- package/src/singletons/SqliteStore.js +480 -0
- package/src/singletons/TickService.js +151 -0
- package/src/singletons/WebhookProxy.js +223 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// LRU cleanup job for message history
|
|
2
|
+
// Runs periodically to maintain size limits while keeping frequently viewed messages
|
|
3
|
+
|
|
4
|
+
import { SqliteStore } from '../singletons/SqliteStore.js'
|
|
5
|
+
import { Application } from '../configs/Application.js'
|
|
6
|
+
import { Logger } from '../singletons/Logger.js'
|
|
7
|
+
import { PUBSUB_MESSAGE_HISTORY } from '../db/Tables.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Run the LRU cleanup process
|
|
11
|
+
* Deletes least viewed messages when approaching size limit
|
|
12
|
+
* @returns {Object} - { cleaned: boolean, deletedCount?: number, reason?: string }
|
|
13
|
+
*/
|
|
14
|
+
export const runMessageHistoryCleanup = () => {
|
|
15
|
+
const { maxMessages, minMessages, cleanupThreshold } = Application.messageHistory
|
|
16
|
+
|
|
17
|
+
// Get current count
|
|
18
|
+
const countResult = SqliteStore.db.prepare(`SELECT COUNT(*) as count FROM ${PUBSUB_MESSAGE_HISTORY}`).get()
|
|
19
|
+
const count = countResult.count
|
|
20
|
+
|
|
21
|
+
// Only cleanup if above threshold
|
|
22
|
+
if (count < maxMessages * cleanupThreshold) {
|
|
23
|
+
Logger.log({
|
|
24
|
+
level: 'debug',
|
|
25
|
+
message: 'Message history cleanup skipped - below threshold',
|
|
26
|
+
data: { currentCount: count, threshold: maxMessages * cleanupThreshold }
|
|
27
|
+
})
|
|
28
|
+
return { cleaned: false, reason: 'below threshold' }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Calculate how many to delete (target 80% capacity)
|
|
32
|
+
const targetCount = Math.floor(maxMessages * 0.8)
|
|
33
|
+
const deleteCount = Math.max(0, count - targetCount)
|
|
34
|
+
|
|
35
|
+
// Safety check: ensure we don't delete below minimum
|
|
36
|
+
if (deleteCount === 0 || count - deleteCount < minMessages) {
|
|
37
|
+
Logger.log({
|
|
38
|
+
level: 'debug',
|
|
39
|
+
message: 'Message history cleanup skipped - minimum threshold',
|
|
40
|
+
data: { currentCount: count, minMessages, targetCount }
|
|
41
|
+
})
|
|
42
|
+
return { cleaned: false, reason: 'minimum threshold' }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Delete least viewed messages (LRU strategy)
|
|
46
|
+
// Order by: view_count ASC (least viewed first), created_at ASC (oldest first)
|
|
47
|
+
const deleteSql = `
|
|
48
|
+
DELETE FROM ${PUBSUB_MESSAGE_HISTORY}
|
|
49
|
+
WHERE id IN (
|
|
50
|
+
SELECT id FROM ${PUBSUB_MESSAGE_HISTORY}
|
|
51
|
+
ORDER BY view_count ASC, created_at ASC
|
|
52
|
+
LIMIT ?
|
|
53
|
+
)
|
|
54
|
+
`
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const deleted = SqliteStore.db.prepare(deleteSql).run(deleteCount)
|
|
58
|
+
|
|
59
|
+
Logger.log({
|
|
60
|
+
level: 'info',
|
|
61
|
+
message: 'Message history LRU cleanup completed',
|
|
62
|
+
data: {
|
|
63
|
+
totalCount: count,
|
|
64
|
+
deletedCount: deleted.changes,
|
|
65
|
+
remainingCount: count - deleted.changes,
|
|
66
|
+
targetCount,
|
|
67
|
+
maxMessages,
|
|
68
|
+
minMessages
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return { cleaned: true, deletedCount: deleted.changes }
|
|
73
|
+
} catch (error) {
|
|
74
|
+
Logger.log({
|
|
75
|
+
level: 'error',
|
|
76
|
+
message: 'Message history cleanup failed',
|
|
77
|
+
data: { error: error.message, count, deleteCount }
|
|
78
|
+
})
|
|
79
|
+
throw error
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if cleanup is needed and run if necessary
|
|
85
|
+
* Used for on-demand cleanup (e.g., after publish)
|
|
86
|
+
* @returns {Object} - Cleanup result
|
|
87
|
+
*/
|
|
88
|
+
export const checkAndCleanup = () => {
|
|
89
|
+
const { maxMessages, cleanupThreshold } = Application.messageHistory
|
|
90
|
+
|
|
91
|
+
const countResult = SqliteStore.db.prepare(`SELECT COUNT(*) as count FROM ${PUBSUB_MESSAGE_HISTORY}`).get()
|
|
92
|
+
const count = countResult.count
|
|
93
|
+
|
|
94
|
+
if (count >= maxMessages * cleanupThreshold) {
|
|
95
|
+
return runMessageHistoryCleanup()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { cleaned: false, reason: 'below threshold' }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Start the scheduled cleanup job
|
|
103
|
+
* @returns {NodeJS.Timer} - Interval timer
|
|
104
|
+
*/
|
|
105
|
+
export const startCleanupJob = () => {
|
|
106
|
+
const { cleanupIntervalHours } = Application.messageHistory
|
|
107
|
+
const intervalMs = cleanupIntervalHours * 60 * 60 * 1000
|
|
108
|
+
|
|
109
|
+
Logger.log({
|
|
110
|
+
level: 'info',
|
|
111
|
+
message: 'Starting message history cleanup job',
|
|
112
|
+
data: { intervalHours: cleanupIntervalHours }
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// Run immediately on start
|
|
116
|
+
try {
|
|
117
|
+
runMessageHistoryCleanup()
|
|
118
|
+
} catch (error) {
|
|
119
|
+
Logger.log({
|
|
120
|
+
level: 'error',
|
|
121
|
+
message: 'Initial cleanup failed',
|
|
122
|
+
data: { error: error.message }
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Schedule periodic cleanup
|
|
127
|
+
return setInterval(() => {
|
|
128
|
+
try {
|
|
129
|
+
runMessageHistoryCleanup()
|
|
130
|
+
} catch (error) {
|
|
131
|
+
Logger.log({
|
|
132
|
+
level: 'error',
|
|
133
|
+
message: 'Scheduled cleanup failed',
|
|
134
|
+
data: { error: error.message }
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}, intervalMs)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Stop the cleanup job
|
|
142
|
+
* @param {NodeJS.Timer} timer - The interval timer to clear
|
|
143
|
+
*/
|
|
144
|
+
export const stopCleanupJob = (timer) => {
|
|
145
|
+
if (timer) {
|
|
146
|
+
clearInterval(timer)
|
|
147
|
+
Logger.log({
|
|
148
|
+
level: 'info',
|
|
149
|
+
message: 'Message history cleanup job stopped'
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class ApiClient {
|
|
2
|
+
constructor (baseUrl) {
|
|
3
|
+
this.baseUrl = baseUrl
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async post (path, body = {}) {
|
|
7
|
+
const url = `${this.baseUrl}${path}`
|
|
8
|
+
const response = await fetch(url, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: { 'Content-Type': 'application/json' },
|
|
11
|
+
body: JSON.stringify(body)
|
|
12
|
+
})
|
|
13
|
+
const result = await response.json()
|
|
14
|
+
if (!result.success) {
|
|
15
|
+
throw new Error(result.data?.message || result.message || 'API call failed')
|
|
16
|
+
}
|
|
17
|
+
return result.data
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async get (path) {
|
|
21
|
+
const url = `${this.baseUrl}${path}`
|
|
22
|
+
const response = await fetch(url)
|
|
23
|
+
return response.json()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
|
+
import { ApiClient } from './ApiClient.js'
|
|
4
|
+
import { registerPubsubTools } from './tools/pubsub.js'
|
|
5
|
+
import { registerLoggingTools } from './tools/logging.js'
|
|
6
|
+
import { registerFirestoreTools } from './tools/firestore.js'
|
|
7
|
+
import { registerPostgresTools } from './tools/postgres.js'
|
|
8
|
+
import { registerRedisTools } from './tools/redis.js'
|
|
9
|
+
import { registerDockerTools } from './tools/docker.js'
|
|
10
|
+
import { registerMqttTools } from './tools/mqtt.js'
|
|
11
|
+
import { registerServicesTools } from './tools/services.js'
|
|
12
|
+
import { registerHttpTrafficTools } from './tools/httpTraffic.js'
|
|
13
|
+
import { registerDataTools } from './tools/data.js'
|
|
14
|
+
import { registerWebhooksTools } from './tools/webhooks.js'
|
|
15
|
+
import { registerSnapshotTools } from './tools/snapshots.js'
|
|
16
|
+
import { registerFunctionsTools } from './tools/functions.js'
|
|
17
|
+
import { registerPlatformResources } from './resources/platform.js'
|
|
18
|
+
import { registerDebuggingPrompts } from './prompts/debugging.js'
|
|
19
|
+
|
|
20
|
+
export async function startMcpServer ({ devToolsUrl }) {
|
|
21
|
+
const server = new McpServer({
|
|
22
|
+
name: 'goki-dev-tools',
|
|
23
|
+
version: '1.0.0'
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const apiClient = new ApiClient(devToolsUrl)
|
|
27
|
+
|
|
28
|
+
// Register tool groups
|
|
29
|
+
registerPubsubTools(server, apiClient)
|
|
30
|
+
registerLoggingTools(server, apiClient)
|
|
31
|
+
registerFirestoreTools(server, apiClient)
|
|
32
|
+
registerPostgresTools(server, apiClient)
|
|
33
|
+
registerRedisTools(server, apiClient)
|
|
34
|
+
registerDockerTools(server, apiClient)
|
|
35
|
+
registerMqttTools(server, apiClient)
|
|
36
|
+
registerServicesTools(server, apiClient)
|
|
37
|
+
registerHttpTrafficTools(server, apiClient)
|
|
38
|
+
registerDataTools(server, apiClient)
|
|
39
|
+
registerWebhooksTools(server, apiClient)
|
|
40
|
+
registerSnapshotTools(server, apiClient)
|
|
41
|
+
registerFunctionsTools(server, apiClient)
|
|
42
|
+
|
|
43
|
+
// Register resources and prompts
|
|
44
|
+
registerPlatformResources(server, apiClient)
|
|
45
|
+
registerDebuggingPrompts(server)
|
|
46
|
+
|
|
47
|
+
// Connect via stdio transport
|
|
48
|
+
const transport = new StdioServerTransport()
|
|
49
|
+
await server.connect(transport)
|
|
50
|
+
console.error('Goki Dev Tools MCP server running on stdio')
|
|
51
|
+
console.error(`Backend URL: ${devToolsUrl}`)
|
|
52
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export function registerDebuggingPrompts (server) {
|
|
2
|
+
server.prompt(
|
|
3
|
+
'debug-pubsub',
|
|
4
|
+
'Debug Pub/Sub message flow — checks topics, subscriptions, and recent messages',
|
|
5
|
+
{ topicName: { type: 'string', description: 'Optional topic name to focus on' } },
|
|
6
|
+
async ({ topicName }) => ({
|
|
7
|
+
messages: [{
|
|
8
|
+
role: 'user',
|
|
9
|
+
content: {
|
|
10
|
+
type: 'text',
|
|
11
|
+
text: [
|
|
12
|
+
'Debug Pub/Sub message flow in the Goki dev-tools platform.',
|
|
13
|
+
topicName ? `Focus on topic: ${topicName}` : '',
|
|
14
|
+
'',
|
|
15
|
+
'Steps:',
|
|
16
|
+
'1. List all Pub/Sub topics and subscriptions',
|
|
17
|
+
'2. Check message history stats for overall volume',
|
|
18
|
+
topicName
|
|
19
|
+
? `3. Search recent messages on topic "${topicName}"`
|
|
20
|
+
: '3. Show recent messages across all topics',
|
|
21
|
+
'4. Identify any issues (missing subscriptions, undelivered messages)',
|
|
22
|
+
'5. Suggest fixes if problems are found'
|
|
23
|
+
].filter(Boolean).join('\n')
|
|
24
|
+
}
|
|
25
|
+
}]
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
server.prompt(
|
|
30
|
+
'monitor-services',
|
|
31
|
+
'Check health of all platform services and Docker containers',
|
|
32
|
+
{},
|
|
33
|
+
async () => ({
|
|
34
|
+
messages: [{
|
|
35
|
+
role: 'user',
|
|
36
|
+
content: {
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: [
|
|
39
|
+
'Check the health of all Goki dev-tools platform services:',
|
|
40
|
+
'',
|
|
41
|
+
'1. Get platform dashboard stats',
|
|
42
|
+
'2. List all Docker containers and their status',
|
|
43
|
+
'3. Check the gateway status',
|
|
44
|
+
'4. Test Redis connection',
|
|
45
|
+
'5. Test PostgreSQL connection',
|
|
46
|
+
'6. Check scheduler tick status',
|
|
47
|
+
'',
|
|
48
|
+
'Report any services that are down or unhealthy, and suggest remediation steps.'
|
|
49
|
+
].join('\n')
|
|
50
|
+
}
|
|
51
|
+
}]
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
server.prompt(
|
|
56
|
+
'trace-request',
|
|
57
|
+
'Trace a request through multiple services using a traceId',
|
|
58
|
+
{ traceId: { type: 'string', description: 'The trace ID to follow across services' } },
|
|
59
|
+
async ({ traceId }) => ({
|
|
60
|
+
messages: [{
|
|
61
|
+
role: 'user',
|
|
62
|
+
content: {
|
|
63
|
+
type: 'text',
|
|
64
|
+
text: [
|
|
65
|
+
`Trace request with traceId "${traceId}" through the Goki platform:`,
|
|
66
|
+
'',
|
|
67
|
+
'1. Search log entries for this traceId',
|
|
68
|
+
'2. Search HTTP traffic records for this traceId',
|
|
69
|
+
'3. Search Pub/Sub message history for this traceId in attributes',
|
|
70
|
+
'4. Build a timeline of events across services',
|
|
71
|
+
'5. Identify any errors, delays, or gaps in the flow'
|
|
72
|
+
].join('\n')
|
|
73
|
+
}
|
|
74
|
+
}]
|
|
75
|
+
})
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
server.prompt(
|
|
79
|
+
'setup-test-environment',
|
|
80
|
+
'Prepare the dev-tools platform for a fresh test run',
|
|
81
|
+
{ services: { type: 'string', description: 'Comma-separated service names to clear, or "all"' } },
|
|
82
|
+
async ({ services }) => ({
|
|
83
|
+
messages: [{
|
|
84
|
+
role: 'user',
|
|
85
|
+
content: {
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: [
|
|
88
|
+
'Prepare the Goki dev-tools platform for a fresh test run:',
|
|
89
|
+
'',
|
|
90
|
+
services && services !== 'all'
|
|
91
|
+
? `1. Clear data for these services: ${services}`
|
|
92
|
+
: '1. Clear all platform data',
|
|
93
|
+
'2. Verify all Docker containers are running',
|
|
94
|
+
'3. Confirm Pub/Sub emulator is ready (list topics)',
|
|
95
|
+
'4. Confirm logging is ready (list services)',
|
|
96
|
+
'5. Trigger a scheduler tick to initialize periodic tasks',
|
|
97
|
+
'',
|
|
98
|
+
'Report readiness status when done.'
|
|
99
|
+
].join('\n')
|
|
100
|
+
}
|
|
101
|
+
}]
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises'
|
|
2
|
+
import { join, dirname } from 'path'
|
|
3
|
+
import { fileURLToPath } from 'url'
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
const projectRoot = join(__dirname, '..', '..', '..')
|
|
7
|
+
|
|
8
|
+
async function readProjectFile (relativePath) {
|
|
9
|
+
try {
|
|
10
|
+
return await readFile(join(projectRoot, relativePath), 'utf-8')
|
|
11
|
+
} catch {
|
|
12
|
+
return `File not found: ${relativePath}`
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function registerPlatformResources (server, apiClient) {
|
|
17
|
+
server.resource(
|
|
18
|
+
'platform-stats',
|
|
19
|
+
'devtools://stats',
|
|
20
|
+
async (uri) => {
|
|
21
|
+
try {
|
|
22
|
+
const data = await apiClient.post('/v1/dashboard/stats')
|
|
23
|
+
return {
|
|
24
|
+
contents: [{
|
|
25
|
+
uri: uri.href,
|
|
26
|
+
mimeType: 'application/json',
|
|
27
|
+
text: JSON.stringify(data, null, 2)
|
|
28
|
+
}]
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return {
|
|
32
|
+
contents: [{
|
|
33
|
+
uri: uri.href,
|
|
34
|
+
mimeType: 'text/plain',
|
|
35
|
+
text: `Error fetching stats: ${error.message}`
|
|
36
|
+
}]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
server.resource(
|
|
43
|
+
'architecture',
|
|
44
|
+
'devtools://docs/architecture',
|
|
45
|
+
async (uri) => {
|
|
46
|
+
const text = await readProjectFile('CLAUDE.md')
|
|
47
|
+
return {
|
|
48
|
+
contents: [{
|
|
49
|
+
uri: uri.href,
|
|
50
|
+
mimeType: 'text/markdown',
|
|
51
|
+
text
|
|
52
|
+
}]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
server.resource(
|
|
58
|
+
'port-registry',
|
|
59
|
+
'devtools://config/ports',
|
|
60
|
+
async (uri) => {
|
|
61
|
+
return {
|
|
62
|
+
contents: [{
|
|
63
|
+
uri: uri.href,
|
|
64
|
+
mimeType: 'application/json',
|
|
65
|
+
text: JSON.stringify({
|
|
66
|
+
management: { port: 9000, description: 'Dev Tools API + Web UI' },
|
|
67
|
+
pubsub: { port: 8085, description: 'GCP Pub/Sub emulator' },
|
|
68
|
+
awsIot: { port: 8086, description: 'AWS IoT Core HTTPS API' },
|
|
69
|
+
logging: { port: 8087, description: 'Google Cloud Logging emulator' },
|
|
70
|
+
mqtt: { port: 8883, description: 'MQTT broker (Aedes)' },
|
|
71
|
+
firestore: { port: 8080, description: 'GCP Firestore emulator' },
|
|
72
|
+
postgres: { port: 5432, description: 'PostgreSQL database' },
|
|
73
|
+
redis: { port: 6379, description: 'Redis cache' },
|
|
74
|
+
redisLogs: { port: 6380, description: 'Redis for logs (LRU)' },
|
|
75
|
+
elasticsearch: { port: 9200, description: 'Elasticsearch' },
|
|
76
|
+
kibana: { port: 5601, description: 'Kibana dashboard' }
|
|
77
|
+
}, null, 2)
|
|
78
|
+
}]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
server.resource(
|
|
84
|
+
'topic-conventions',
|
|
85
|
+
'devtools://docs/topic-conventions',
|
|
86
|
+
async (uri) => {
|
|
87
|
+
return {
|
|
88
|
+
contents: [{
|
|
89
|
+
uri: uri.href,
|
|
90
|
+
mimeType: 'text/markdown',
|
|
91
|
+
text: [
|
|
92
|
+
'# Goki MQTT & Pub/Sub Topic Conventions',
|
|
93
|
+
'',
|
|
94
|
+
'## MQTT Topic Patterns',
|
|
95
|
+
'- **Request:** `rq/{action}/{clientId}`',
|
|
96
|
+
'- **Response:** `rs/{action}/{clientId}`',
|
|
97
|
+
'- **Notification:** `nf/{action}/{clientId}`',
|
|
98
|
+
'- **Will:** `w/{action}/{clientId}`',
|
|
99
|
+
'- **Lifecycle:** `simulator/connected`, `simulator/disconnected`',
|
|
100
|
+
'',
|
|
101
|
+
'## Pub/Sub Bridge',
|
|
102
|
+
'- MQTT messages are bridged to Pub/Sub topic `mqttMessageReceived`',
|
|
103
|
+
'- Format: `{ clientId, topicName, packet (base64), receivedAtMilliseconds }`',
|
|
104
|
+
'',
|
|
105
|
+
'## Common Pub/Sub Topics',
|
|
106
|
+
'- `mqttMessageReceived` — MQTT messages bridged to Pub/Sub',
|
|
107
|
+
'- `systemOneMinuteTick` — Scheduler tick every 60 seconds',
|
|
108
|
+
'',
|
|
109
|
+
'## Inter-Service Communication',
|
|
110
|
+
'- Services publish/subscribe via the Pub/Sub emulator on port 8085',
|
|
111
|
+
'- Backend-to-device messages go through AWS IoT HTTPS API on port 8086',
|
|
112
|
+
'- Device-to-backend messages go through MQTT broker on port 8883'
|
|
113
|
+
].join('\n')
|
|
114
|
+
}]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export function registerDataTools (server, apiClient) {
|
|
4
|
+
server.tool(
|
|
5
|
+
'platform_stats',
|
|
6
|
+
'Get platform dashboard statistics (topic count, message count, log entries, MQTT clients, etc.)',
|
|
7
|
+
{},
|
|
8
|
+
async () => {
|
|
9
|
+
try {
|
|
10
|
+
const data = await apiClient.post('/v1/dashboard/stats')
|
|
11
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return { content: [{ type: 'text', text: `Failed to get platform stats: ${error.message}` }], isError: true }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
server.tool(
|
|
19
|
+
'platform_export',
|
|
20
|
+
'Export all platform data (topics, subscriptions, messages, logs, etc.)',
|
|
21
|
+
{},
|
|
22
|
+
async () => {
|
|
23
|
+
try {
|
|
24
|
+
const data = await apiClient.post('/v1/data/export')
|
|
25
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return { content: [{ type: 'text', text: `Failed to export platform data: ${error.message}` }], isError: true }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
server.tool(
|
|
33
|
+
'platform_import',
|
|
34
|
+
'Import platform data (optionally clearing existing data first)',
|
|
35
|
+
{
|
|
36
|
+
data: z.record(z.any()).describe('Platform data object to import'),
|
|
37
|
+
clearExisting: z.boolean().optional().describe('Whether to clear existing data before importing')
|
|
38
|
+
},
|
|
39
|
+
async ({ data, clearExisting }) => {
|
|
40
|
+
try {
|
|
41
|
+
const body = { data }
|
|
42
|
+
if (clearExisting !== undefined) body.clearExisting = clearExisting
|
|
43
|
+
const result = await apiClient.post('/v1/data/import', body)
|
|
44
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return { content: [{ type: 'text', text: `Failed to import platform data: ${error.message}` }], isError: true }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
server.tool(
|
|
52
|
+
'platform_clear',
|
|
53
|
+
'Clear all platform data (topics, messages, logs, traffic records)',
|
|
54
|
+
{},
|
|
55
|
+
async () => {
|
|
56
|
+
try {
|
|
57
|
+
const data = await apiClient.post('/v1/data/clear')
|
|
58
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return { content: [{ type: 'text', text: `Failed to clear platform data: ${error.message}` }], isError: true }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
server.tool(
|
|
66
|
+
'platform_clear_services',
|
|
67
|
+
'Clear data for specific services. Useful for resetting test state without clearing everything.',
|
|
68
|
+
{
|
|
69
|
+
services: z.array(z.string()).optional().describe('Optional list of service names to clear'),
|
|
70
|
+
keepSystemData: z.boolean().optional().describe('Whether to keep system/infrastructure data')
|
|
71
|
+
},
|
|
72
|
+
async ({ services, keepSystemData }) => {
|
|
73
|
+
try {
|
|
74
|
+
const body = {}
|
|
75
|
+
if (services !== undefined) body.services = services
|
|
76
|
+
if (keepSystemData !== undefined) body.keepSystemData = keepSystemData
|
|
77
|
+
const data = await apiClient.post('/v1/data/clear-services', body)
|
|
78
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] }
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return { content: [{ type: 'text', text: `Failed to clear service data: ${error.message}` }], isError: true }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
}
|