coaia-visualizer 1.4.2 → 1.5.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/.dockerignore +9 -0
- package/Dockerfile.app +50 -0
- package/Dockerfile.test +24 -0
- package/LIVE_MODE_DESIGN.md +435 -0
- package/MCP_TESTING_COMPLETE.md +302 -0
- package/MCP_TESTING_IMPLEMENTATION_SUMMARY.md +317 -0
- package/MCP_TESTING_SETUP.md +268 -0
- package/NAMING.md +218 -0
- package/QUICK_START_MCP_TESTING.md +236 -0
- package/WS__issue_8__coaia-visualizer__260207.code-workspace +45 -0
- package/app/api/audio/[filename]/route.ts +37 -0
- package/app/api/charts/[id]/route.ts +48 -35
- package/app/api/watch/route.ts +42 -0
- package/app/page.tsx +103 -53
- package/cli.ts +56 -3
- package/components/add-master-chart.tsx +230 -0
- package/components/chart-detail-editable.tsx +27 -16
- package/components/chart-list.tsx +13 -1
- package/components/create-chart-form.tsx +248 -0
- package/components/data-stats.tsx +9 -7
- package/components/live-indicator.tsx +14 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/label.tsx +24 -0
- package/direct-test.sh +180 -0
- package/dist/cli.js +52 -3
- package/docker-compose.test.yml +69 -0
- package/hooks/use-live-polling.ts +45 -0
- package/jgwill.coaia-visualizer-8--496dca71-d476-4ac9-ba9f-376add118dd8--260208.txt +2612 -0
- package/lib/chart-editor.ts +281 -68
- package/mcp/Dockerfile +21 -0
- package/mcp/README.md +25 -6
- package/mcp/src/api-client.ts +15 -3
- package/mcp/src/index.ts +17 -2
- package/mcp/src/tools/index.ts +21 -1
- package/mcp/test_mcp/.gemini/settings.json +18 -0
- package/mcp-config.json +14 -0
- package/package.json +2 -2
- package/run-mcp-tests.sh +99 -0
- package/samples/tradingchart.jsonl +31 -0
- package/test-data/test-master.jsonl +11 -0
- package/test-run.log +101 -0
- package/test-scripts/README.md +239 -0
- package/test-scripts/run-all-tests.sh +38 -0
- package/test-scripts/test-01-basic-operations.sh +87 -0
- package/test-scripts/test-02-telescope-creation.sh +91 -0
- package/test-scripts/test-03-navigation.sh +87 -0
- package/validate-mcp.sh +136 -0
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,11 @@ function loadConfig(args) {
|
|
|
17
17
|
let config = {
|
|
18
18
|
memoryPath: path.join(process.cwd(), 'memory.jsonl'),
|
|
19
19
|
port: 3000,
|
|
20
|
-
noOpen: false
|
|
20
|
+
noOpen: false,
|
|
21
|
+
live: false,
|
|
22
|
+
pollInterval: 2000,
|
|
23
|
+
autoPlay: false,
|
|
24
|
+
audioDir: path.join(process.cwd(), 'audio')
|
|
21
25
|
};
|
|
22
26
|
// Load .env files
|
|
23
27
|
const localEnvPath = path.join(process.cwd(), '.env');
|
|
@@ -38,6 +42,18 @@ function loadConfig(args) {
|
|
|
38
42
|
if (process.env.COAIAV_PORT) {
|
|
39
43
|
config.port = parseInt(process.env.COAIAV_PORT, 10);
|
|
40
44
|
}
|
|
45
|
+
if (process.env.COAIAV_LIVE === 'true') {
|
|
46
|
+
config.live = true;
|
|
47
|
+
}
|
|
48
|
+
if (process.env.COAIAV_POLL_INTERVAL) {
|
|
49
|
+
config.pollInterval = parseInt(process.env.COAIAV_POLL_INTERVAL, 10);
|
|
50
|
+
}
|
|
51
|
+
if (process.env.COAIAV_AUTO_PLAY === 'true') {
|
|
52
|
+
config.autoPlay = true;
|
|
53
|
+
}
|
|
54
|
+
if (process.env.COAIAV_AUDIO_DIR) {
|
|
55
|
+
config.audioDir = process.env.COAIAV_AUDIO_DIR;
|
|
56
|
+
}
|
|
41
57
|
// Command-line flags override everything
|
|
42
58
|
if (args['memory-path'] || args['M']) {
|
|
43
59
|
config.memoryPath = args['memory-path'] || args['M'];
|
|
@@ -48,6 +64,18 @@ function loadConfig(args) {
|
|
|
48
64
|
if (args['no-open']) {
|
|
49
65
|
config.noOpen = true;
|
|
50
66
|
}
|
|
67
|
+
if (args['live']) {
|
|
68
|
+
config.live = true;
|
|
69
|
+
}
|
|
70
|
+
if (args['poll-interval']) {
|
|
71
|
+
config.pollInterval = parseInt(args['poll-interval'], 10);
|
|
72
|
+
}
|
|
73
|
+
if (args['auto-play']) {
|
|
74
|
+
config.autoPlay = true;
|
|
75
|
+
}
|
|
76
|
+
if (args['audio-dir']) {
|
|
77
|
+
config.audioDir = args['audio-dir'];
|
|
78
|
+
}
|
|
51
79
|
return config;
|
|
52
80
|
}
|
|
53
81
|
async function main() {
|
|
@@ -67,12 +95,20 @@ USAGE:
|
|
|
67
95
|
OPTIONS:
|
|
68
96
|
--memory-path PATH, -M PATH Path to memory.jsonl file (default: ./memory.jsonl)
|
|
69
97
|
--port PORT, -p PORT Server port (default: 3000)
|
|
98
|
+
--live Enable live monitoring mode
|
|
99
|
+
--poll-interval MS Polling interval in ms (default: 2000)
|
|
100
|
+
--auto-play Auto-play audio for new beats
|
|
101
|
+
--audio-dir PATH Audio directory path (default: ./audio)
|
|
70
102
|
--no-open Don't auto-open browser
|
|
71
103
|
--help, -h Show this help message
|
|
72
104
|
|
|
73
105
|
ENVIRONMENT VARIABLES:
|
|
74
106
|
COAIAN_MF Default memory file path
|
|
75
107
|
COAIAV_PORT Default server port
|
|
108
|
+
COAIAV_LIVE Enable live mode (true/false)
|
|
109
|
+
COAIAV_POLL_INTERVAL Polling interval in ms
|
|
110
|
+
COAIAV_AUTO_PLAY Enable auto-play (true/false)
|
|
111
|
+
COAIAV_AUDIO_DIR Audio directory path
|
|
76
112
|
|
|
77
113
|
EXAMPLES:
|
|
78
114
|
# Launch with default memory.jsonl
|
|
@@ -99,13 +135,22 @@ EXAMPLES:
|
|
|
99
135
|
console.error(` Create a memory file or specify a different path with --memory-path`);
|
|
100
136
|
process.exit(1);
|
|
101
137
|
}
|
|
102
|
-
console.log(`🎨 COAIA Visualizer`);
|
|
138
|
+
console.log(`🎨 COAIA Visualizer${config.live ? ' [LIVE MODE]' : ''}`);
|
|
103
139
|
console.log(`📁 Memory file: ${config.memoryPath}`);
|
|
104
140
|
console.log(`🌐 Port: ${config.port}`);
|
|
141
|
+
if (config.live) {
|
|
142
|
+
console.log(`🔄 Polling: ${config.pollInterval}ms`);
|
|
143
|
+
console.log(`🎵 Audio: ${config.audioDir}`);
|
|
144
|
+
console.log(`🔊 Auto-play: ${config.autoPlay ? 'enabled' : 'disabled'}`);
|
|
145
|
+
}
|
|
105
146
|
console.log();
|
|
106
147
|
// Set environment variables for Next.js
|
|
107
148
|
process.env.COAIAV_MEMORY_PATH = path.resolve(config.memoryPath);
|
|
108
149
|
process.env.PORT = config.port.toString();
|
|
150
|
+
process.env.NEXT_PUBLIC_LIVE_MODE = config.live.toString();
|
|
151
|
+
process.env.NEXT_PUBLIC_POLL_INTERVAL = config.pollInterval.toString();
|
|
152
|
+
process.env.NEXT_PUBLIC_AUTO_PLAY = config.autoPlay.toString();
|
|
153
|
+
process.env.COAIAV_AUDIO_DIR = path.resolve(config.audioDir);
|
|
109
154
|
// Navigate to visualizer root
|
|
110
155
|
const visualizerRoot = path.resolve(__dirname, '..');
|
|
111
156
|
// Launch Next.js dev server with explicit port flag
|
|
@@ -115,7 +160,11 @@ EXAMPLES:
|
|
|
115
160
|
env: {
|
|
116
161
|
...process.env,
|
|
117
162
|
COAIAV_MEMORY_PATH: path.resolve(config.memoryPath),
|
|
118
|
-
PORT: config.port.toString()
|
|
163
|
+
PORT: config.port.toString(),
|
|
164
|
+
NEXT_PUBLIC_LIVE_MODE: config.live.toString(),
|
|
165
|
+
NEXT_PUBLIC_POLL_INTERVAL: config.pollInterval.toString(),
|
|
166
|
+
NEXT_PUBLIC_AUTO_PLAY: config.autoPlay.toString(),
|
|
167
|
+
COAIAV_AUDIO_DIR: path.resolve(config.audioDir)
|
|
119
168
|
}
|
|
120
169
|
});
|
|
121
170
|
// Open browser if not disabled
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
# Next.js application server
|
|
5
|
+
visualizer-app:
|
|
6
|
+
build:
|
|
7
|
+
context: .
|
|
8
|
+
dockerfile: Dockerfile.app
|
|
9
|
+
ports:
|
|
10
|
+
- "4321:4321"
|
|
11
|
+
volumes:
|
|
12
|
+
- ./samples:/app/samples:ro
|
|
13
|
+
- ./test-data:/app/test-data
|
|
14
|
+
environment:
|
|
15
|
+
- NODE_ENV=production
|
|
16
|
+
- PORT=4321
|
|
17
|
+
networks:
|
|
18
|
+
- test-network
|
|
19
|
+
healthcheck:
|
|
20
|
+
test: ["CMD", "curl", "-f", "http://localhost:4321/api/charts"]
|
|
21
|
+
interval: 10s
|
|
22
|
+
timeout: 5s
|
|
23
|
+
retries: 5
|
|
24
|
+
|
|
25
|
+
# MCP Server
|
|
26
|
+
mcp-server:
|
|
27
|
+
build:
|
|
28
|
+
context: ./mcp
|
|
29
|
+
dockerfile: Dockerfile
|
|
30
|
+
environment:
|
|
31
|
+
- VISUALIZER_API_URL=http://visualizer-app:4321
|
|
32
|
+
- VISUALIZER_API_KEY=${VISUALIZER_API_KEY:-test-api-key}
|
|
33
|
+
- NODE_ENV=production
|
|
34
|
+
volumes:
|
|
35
|
+
- ./test-data:/data
|
|
36
|
+
networks:
|
|
37
|
+
- test-network
|
|
38
|
+
depends_on:
|
|
39
|
+
visualizer-app:
|
|
40
|
+
condition: service_healthy
|
|
41
|
+
stdin_open: true
|
|
42
|
+
tty: true
|
|
43
|
+
|
|
44
|
+
# Test runner using claude-code CLI or gemini-cli
|
|
45
|
+
test-runner:
|
|
46
|
+
build:
|
|
47
|
+
context: .
|
|
48
|
+
dockerfile: Dockerfile.test
|
|
49
|
+
environment:
|
|
50
|
+
- MCP_SERVER_HOST=mcp-server
|
|
51
|
+
- VISUALIZER_API_URL=http://visualizer-app:4321
|
|
52
|
+
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
|
53
|
+
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
|
54
|
+
volumes:
|
|
55
|
+
- ./test-scripts:/test-scripts:ro
|
|
56
|
+
- ./test-results:/test-results
|
|
57
|
+
- ./test-data:/test-data
|
|
58
|
+
networks:
|
|
59
|
+
- test-network
|
|
60
|
+
depends_on:
|
|
61
|
+
visualizer-app:
|
|
62
|
+
condition: service_healthy
|
|
63
|
+
mcp-server:
|
|
64
|
+
condition: service_started
|
|
65
|
+
command: ["/test-scripts/run-all-tests.sh"]
|
|
66
|
+
|
|
67
|
+
networks:
|
|
68
|
+
test-network:
|
|
69
|
+
driver: bridge
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export function useLivePolling(options: {
|
|
4
|
+
enabled: boolean
|
|
5
|
+
interval: number
|
|
6
|
+
onReload: () => void
|
|
7
|
+
}) {
|
|
8
|
+
const [lastBeatCount, setLastBeatCount] = useState(0)
|
|
9
|
+
const [isLive, setIsLive] = useState(false)
|
|
10
|
+
const intervalRef = useRef<NodeJS.Timeout>()
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!options.enabled) {
|
|
14
|
+
setIsLive(false)
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const poll = async () => {
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch('/api/watch')
|
|
21
|
+
if (response.ok) {
|
|
22
|
+
const { beatCount } = await response.json()
|
|
23
|
+
|
|
24
|
+
if (beatCount > lastBeatCount) {
|
|
25
|
+
setIsLive(true)
|
|
26
|
+
setLastBeatCount(beatCount)
|
|
27
|
+
options.onReload()
|
|
28
|
+
|
|
29
|
+
// Flash live indicator
|
|
30
|
+
setTimeout(() => setIsLive(false), 2000)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Polling error:', error)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
intervalRef.current = setInterval(poll, options.interval)
|
|
39
|
+
return () => {
|
|
40
|
+
if (intervalRef.current) clearInterval(intervalRef.current)
|
|
41
|
+
}
|
|
42
|
+
}, [options.enabled, options.interval, lastBeatCount])
|
|
43
|
+
|
|
44
|
+
return { isLive }
|
|
45
|
+
}
|