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.
Files changed (47) hide show
  1. package/.dockerignore +9 -0
  2. package/Dockerfile.app +50 -0
  3. package/Dockerfile.test +24 -0
  4. package/LIVE_MODE_DESIGN.md +435 -0
  5. package/MCP_TESTING_COMPLETE.md +302 -0
  6. package/MCP_TESTING_IMPLEMENTATION_SUMMARY.md +317 -0
  7. package/MCP_TESTING_SETUP.md +268 -0
  8. package/NAMING.md +218 -0
  9. package/QUICK_START_MCP_TESTING.md +236 -0
  10. package/WS__issue_8__coaia-visualizer__260207.code-workspace +45 -0
  11. package/app/api/audio/[filename]/route.ts +37 -0
  12. package/app/api/charts/[id]/route.ts +48 -35
  13. package/app/api/watch/route.ts +42 -0
  14. package/app/page.tsx +103 -53
  15. package/cli.ts +56 -3
  16. package/components/add-master-chart.tsx +230 -0
  17. package/components/chart-detail-editable.tsx +27 -16
  18. package/components/chart-list.tsx +13 -1
  19. package/components/create-chart-form.tsx +248 -0
  20. package/components/data-stats.tsx +9 -7
  21. package/components/live-indicator.tsx +14 -0
  22. package/components/ui/dialog.tsx +143 -0
  23. package/components/ui/label.tsx +24 -0
  24. package/direct-test.sh +180 -0
  25. package/dist/cli.js +52 -3
  26. package/docker-compose.test.yml +69 -0
  27. package/hooks/use-live-polling.ts +45 -0
  28. package/jgwill.coaia-visualizer-8--496dca71-d476-4ac9-ba9f-376add118dd8--260208.txt +2612 -0
  29. package/lib/chart-editor.ts +281 -68
  30. package/mcp/Dockerfile +21 -0
  31. package/mcp/README.md +25 -6
  32. package/mcp/src/api-client.ts +15 -3
  33. package/mcp/src/index.ts +17 -2
  34. package/mcp/src/tools/index.ts +21 -1
  35. package/mcp/test_mcp/.gemini/settings.json +18 -0
  36. package/mcp-config.json +14 -0
  37. package/package.json +2 -2
  38. package/run-mcp-tests.sh +99 -0
  39. package/samples/tradingchart.jsonl +31 -0
  40. package/test-data/test-master.jsonl +11 -0
  41. package/test-run.log +101 -0
  42. package/test-scripts/README.md +239 -0
  43. package/test-scripts/run-all-tests.sh +38 -0
  44. package/test-scripts/test-01-basic-operations.sh +87 -0
  45. package/test-scripts/test-02-telescope-creation.sh +91 -0
  46. package/test-scripts/test-03-navigation.sh +87 -0
  47. 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
+ }