@qwickapps/qwickbrain-proxy 1.0.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/.github/workflows/publish.yml +92 -0
- package/CHANGELOG.md +47 -0
- package/LICENSE +45 -0
- package/README.md +165 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +142 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/db/client.d.ts +10 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +23 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/schema.d.ts +551 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +65 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/__tests__/cache-manager.test.d.ts +2 -0
- package/dist/lib/__tests__/cache-manager.test.d.ts.map +1 -0
- package/dist/lib/__tests__/cache-manager.test.js +202 -0
- package/dist/lib/__tests__/cache-manager.test.js.map +1 -0
- package/dist/lib/__tests__/connection-manager.test.d.ts +2 -0
- package/dist/lib/__tests__/connection-manager.test.d.ts.map +1 -0
- package/dist/lib/__tests__/connection-manager.test.js +188 -0
- package/dist/lib/__tests__/connection-manager.test.js.map +1 -0
- package/dist/lib/__tests__/proxy-server.test.d.ts +2 -0
- package/dist/lib/__tests__/proxy-server.test.d.ts.map +1 -0
- package/dist/lib/__tests__/proxy-server.test.js +205 -0
- package/dist/lib/__tests__/proxy-server.test.js.map +1 -0
- package/dist/lib/__tests__/qwickbrain-client.test.d.ts +2 -0
- package/dist/lib/__tests__/qwickbrain-client.test.d.ts.map +1 -0
- package/dist/lib/__tests__/qwickbrain-client.test.js +233 -0
- package/dist/lib/__tests__/qwickbrain-client.test.js.map +1 -0
- package/dist/lib/cache-manager.d.ts +25 -0
- package/dist/lib/cache-manager.d.ts.map +1 -0
- package/dist/lib/cache-manager.js +149 -0
- package/dist/lib/cache-manager.js.map +1 -0
- package/dist/lib/connection-manager.d.ts +26 -0
- package/dist/lib/connection-manager.d.ts.map +1 -0
- package/dist/lib/connection-manager.js +130 -0
- package/dist/lib/connection-manager.js.map +1 -0
- package/dist/lib/proxy-server.d.ts +19 -0
- package/dist/lib/proxy-server.d.ts.map +1 -0
- package/dist/lib/proxy-server.js +258 -0
- package/dist/lib/proxy-server.js.map +1 -0
- package/dist/lib/qwickbrain-client.d.ts +24 -0
- package/dist/lib/qwickbrain-client.d.ts.map +1 -0
- package/dist/lib/qwickbrain-client.js +197 -0
- package/dist/lib/qwickbrain-client.js.map +1 -0
- package/dist/types/config.d.ts +186 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +42 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/mcp.d.ts +223 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +78 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/drizzle/0000_fat_rafael_vega.sql +41 -0
- package/drizzle/0001_goofy_invisible_woman.sql +2 -0
- package/drizzle/meta/0000_snapshot.json +276 -0
- package/drizzle/meta/0001_snapshot.json +295 -0
- package/drizzle/meta/_journal.json +20 -0
- package/drizzle.config.ts +12 -0
- package/package.json +65 -0
- package/src/bin/cli.ts +158 -0
- package/src/db/client.ts +34 -0
- package/src/db/schema.ts +68 -0
- package/src/index.ts +6 -0
- package/src/lib/__tests__/cache-manager.test.ts +264 -0
- package/src/lib/__tests__/connection-manager.test.ts +255 -0
- package/src/lib/__tests__/proxy-server.test.ts +261 -0
- package/src/lib/__tests__/qwickbrain-client.test.ts +310 -0
- package/src/lib/cache-manager.ts +201 -0
- package/src/lib/connection-manager.ts +156 -0
- package/src/lib/proxy-server.ts +320 -0
- package/src/lib/qwickbrain-client.ts +260 -0
- package/src/types/config.ts +47 -0
- package/src/types/mcp.ts +97 -0
- package/src/version.ts +11 -0
- package/test/fixtures/test-mcp.json +5 -0
- package/test-mcp-client.js +67 -0
- package/test-proxy.sh +25 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# NPM Publish Workflow for Public Repos
|
|
2
|
+
# Copy this file to .github/workflows/publish.yml in each public repo
|
|
3
|
+
#
|
|
4
|
+
# Required secrets:
|
|
5
|
+
# NPM_TOKEN - npm automation token with publish permissions
|
|
6
|
+
|
|
7
|
+
name: Publish to npm
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
paths:
|
|
14
|
+
- 'package.json'
|
|
15
|
+
- 'src/**'
|
|
16
|
+
|
|
17
|
+
# Allow manual publish
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
publish:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
permissions:
|
|
24
|
+
contents: write
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Setup Node.js
|
|
29
|
+
uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: '20'
|
|
32
|
+
registry-url: 'https://registry.npmjs.org'
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: npm install --legacy-peer-deps
|
|
36
|
+
|
|
37
|
+
- name: Build
|
|
38
|
+
run: npm run build
|
|
39
|
+
|
|
40
|
+
- name: Run validation
|
|
41
|
+
run: |
|
|
42
|
+
if grep -q '"validate"' package.json; then
|
|
43
|
+
echo "Running validation..."
|
|
44
|
+
npm run validate
|
|
45
|
+
else
|
|
46
|
+
echo "No validation script found, skipping"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
- name: Check if version changed
|
|
50
|
+
id: version
|
|
51
|
+
run: |
|
|
52
|
+
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
53
|
+
LOCAL_VERSION=$(node -p "require('./package.json').version")
|
|
54
|
+
|
|
55
|
+
# Get published version (returns empty if not published)
|
|
56
|
+
PUBLISHED_VERSION=$(npm view "$PACKAGE_NAME" version 2>/dev/null || echo "")
|
|
57
|
+
|
|
58
|
+
echo "Package: $PACKAGE_NAME"
|
|
59
|
+
echo "Local version: $LOCAL_VERSION"
|
|
60
|
+
echo "Published version: $PUBLISHED_VERSION"
|
|
61
|
+
|
|
62
|
+
if [ "$LOCAL_VERSION" != "$PUBLISHED_VERSION" ]; then
|
|
63
|
+
echo "should_publish=true" >> $GITHUB_OUTPUT
|
|
64
|
+
echo "version=$LOCAL_VERSION" >> $GITHUB_OUTPUT
|
|
65
|
+
else
|
|
66
|
+
echo "should_publish=false" >> $GITHUB_OUTPUT
|
|
67
|
+
echo "Version $LOCAL_VERSION already published"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
- name: Publish to npm
|
|
71
|
+
if: steps.version.outputs.should_publish == 'true'
|
|
72
|
+
run: npm publish --access public --ignore-scripts
|
|
73
|
+
env:
|
|
74
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
75
|
+
|
|
76
|
+
- name: Create GitHub Release
|
|
77
|
+
if: steps.version.outputs.should_publish == 'true'
|
|
78
|
+
uses: actions/create-release@v1
|
|
79
|
+
env:
|
|
80
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
81
|
+
with:
|
|
82
|
+
tag_name: v${{ steps.version.outputs.version }}
|
|
83
|
+
release_name: v${{ steps.version.outputs.version }}
|
|
84
|
+
body: |
|
|
85
|
+
Published to npm: https://www.npmjs.com/package/${{ github.repository }}
|
|
86
|
+
|
|
87
|
+
Install:
|
|
88
|
+
```bash
|
|
89
|
+
npm install @qwickapps/${{ github.event.repository.name }}@${{ steps.version.outputs.version }}
|
|
90
|
+
```
|
|
91
|
+
draft: false
|
|
92
|
+
prerelease: false
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the QwickBrain MCP Proxy will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-01-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **MCP Proxy Server**: Local MCP proxy server for QwickBrain with stdio communication
|
|
13
|
+
- **SQLite Cache**: Local caching for workflows, rules, documents, and memories with configurable TTL
|
|
14
|
+
- **Multi-Transport Support**: Three connection modes - SSE (Server-Sent Events), MCP (stdio), and HTTP (REST API)
|
|
15
|
+
- **Auto-Reconnect**: Automatic reconnection to QwickBrain server with exponential backoff
|
|
16
|
+
- **Graceful Degradation**: Serves stale cached data when QwickBrain server is unavailable
|
|
17
|
+
- **Write Queue**: Queues write operations when offline, syncs when reconnected
|
|
18
|
+
- **Health Monitoring**: Continuous health checks and connection state management
|
|
19
|
+
- **CLI Commands**:
|
|
20
|
+
- `init` - Initialize configuration
|
|
21
|
+
- `serve` - Start the proxy server
|
|
22
|
+
- `status` - Check current configuration and cache
|
|
23
|
+
- `config show/get/set` - Configuration management
|
|
24
|
+
- **Cache Strategy**: Intelligent caching with different TTL for different content types:
|
|
25
|
+
- Workflows: 24h (global, rarely changes)
|
|
26
|
+
- Rules: 24h (global, rarely changes)
|
|
27
|
+
- Documents: 6h (project-scoped FRDs, designs)
|
|
28
|
+
- Memories: 1h (project context, updated frequently)
|
|
29
|
+
- **Configuration Management**: JSON-based configuration at `~/.qwickbrain/config.json`
|
|
30
|
+
- **Database Migrations**: Drizzle ORM-based schema migrations
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **License**: Updated from MIT to PolyForm Shield 1.0.0
|
|
35
|
+
- **Package Location**: Moved to `packages/qwickbrain-proxy` for public publishing
|
|
36
|
+
|
|
37
|
+
### Technical Details
|
|
38
|
+
|
|
39
|
+
- TypeScript implementation with full type safety
|
|
40
|
+
- MCP SDK integration for Model Context Protocol compliance
|
|
41
|
+
- Better-SQLite3 for high-performance local caching
|
|
42
|
+
- Commander.js for CLI interface
|
|
43
|
+
- Drizzle ORM for database schema management
|
|
44
|
+
- Zod for configuration validation
|
|
45
|
+
- Comprehensive test suite with Vitest
|
|
46
|
+
|
|
47
|
+
[1.0.0]: https://github.com/qwickapps/qwickbrain-proxy/releases/tag/v1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# QwickBrain Proxy License
|
|
2
|
+
|
|
3
|
+
This software is licensed under the **PolyForm Shield License 1.0.0**.
|
|
4
|
+
|
|
5
|
+
## Summary
|
|
6
|
+
|
|
7
|
+
- **Free to use** for non-competitive purposes
|
|
8
|
+
- **Source code available** for learning and development
|
|
9
|
+
- **Cannot be used** to compete with QwickApps
|
|
10
|
+
- **Cannot be reverse engineered** for competitive purposes
|
|
11
|
+
- **Cannot be redistributed** in competitive products
|
|
12
|
+
|
|
13
|
+
## Full License Text
|
|
14
|
+
|
|
15
|
+
See https://polyformproject.org/licenses/shield/1.0.0/ for complete terms.
|
|
16
|
+
|
|
17
|
+
## What This Means
|
|
18
|
+
|
|
19
|
+
### Permitted Uses:
|
|
20
|
+
- Internal business applications
|
|
21
|
+
- Learning and educational projects
|
|
22
|
+
- Non-competitive commercial applications
|
|
23
|
+
- Academic research and teaching
|
|
24
|
+
- Contributing to this project
|
|
25
|
+
- Building applications that use this proxy
|
|
26
|
+
|
|
27
|
+
### Prohibited Uses:
|
|
28
|
+
- Creating competing MCP proxy solutions
|
|
29
|
+
- Building competing developer tools or AI infrastructure
|
|
30
|
+
- Reselling or redistributing as a competing product
|
|
31
|
+
- Reverse engineering to create competitive products
|
|
32
|
+
|
|
33
|
+
## Commercial Licensing
|
|
34
|
+
|
|
35
|
+
If your use case might be competitive or you need different terms, contact us at **legal@qwickapps.com** for commercial licensing options.
|
|
36
|
+
|
|
37
|
+
## Questions?
|
|
38
|
+
|
|
39
|
+
- Email: legal@qwickapps.com
|
|
40
|
+
- Website: https://qwickapps.com
|
|
41
|
+
- Repository: https://github.com/qwickapps/qwickbrain-proxy
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
Copyright (c) 2025 QwickApps. All rights reserved.
|
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# QwickBrain MCP Proxy
|
|
2
|
+
|
|
3
|
+
Local MCP proxy for QwickBrain with caching and resilience features.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Local caching**: SQLite-based cache for workflows, rules, documents, and memories
|
|
8
|
+
- **Auto-reconnect**: Automatic reconnection to QwickBrain server with exponential backoff
|
|
9
|
+
- **Graceful degradation**: Serves stale cached data when QwickBrain is unavailable
|
|
10
|
+
- **Write queue**: Queues write operations when offline, syncs when reconnected
|
|
11
|
+
- **Health monitoring**: Continuous health checks and connection state management
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @qwickapps/qwickbrain-proxy
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
### Initialize
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
qwickbrain-proxy init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This creates a default configuration at `~/.qwickbrain/config.json` with:
|
|
28
|
+
- QwickBrain URL: `http://macmini-devserver.local:3000`
|
|
29
|
+
- Connection mode: `sse` (Server-Sent Events)
|
|
30
|
+
- Cache directory: `~/.qwickbrain/cache`
|
|
31
|
+
|
|
32
|
+
### Connection Modes
|
|
33
|
+
|
|
34
|
+
The proxy supports three connection modes:
|
|
35
|
+
|
|
36
|
+
1. **SSE (Server-Sent Events)** - Default, for remote QwickBrain servers
|
|
37
|
+
2. **MCP (stdio)** - For local MCP server processes
|
|
38
|
+
3. **HTTP** - Direct HTTP REST API calls
|
|
39
|
+
|
|
40
|
+
Configure mode:
|
|
41
|
+
```bash
|
|
42
|
+
# SSE mode (default)
|
|
43
|
+
qwickbrain-proxy config set qwickbrain.mode sse
|
|
44
|
+
qwickbrain-proxy config set qwickbrain.url http://macmini-devserver.local:3000
|
|
45
|
+
|
|
46
|
+
# MCP stdio mode
|
|
47
|
+
qwickbrain-proxy config set qwickbrain.mode mcp
|
|
48
|
+
qwickbrain-proxy config set qwickbrain.command npx
|
|
49
|
+
qwickbrain-proxy config set qwickbrain.args '["@qwickapps/qwickbrain-server"]'
|
|
50
|
+
|
|
51
|
+
# HTTP mode
|
|
52
|
+
qwickbrain-proxy config set qwickbrain.mode http
|
|
53
|
+
qwickbrain-proxy config set qwickbrain.url http://qwickbrain.qwickapps.com
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### View Configuration
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Show all configuration
|
|
60
|
+
qwickbrain-proxy config show
|
|
61
|
+
|
|
62
|
+
# Get specific value
|
|
63
|
+
qwickbrain-proxy config get qwickbrain.url
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
### Start the Proxy
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Start the proxy server (runs in stdio mode for MCP)
|
|
72
|
+
qwickbrain-proxy serve
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The proxy runs in stdio mode, communicating via standard input/output with the MCP client (Claude Code).
|
|
76
|
+
|
|
77
|
+
### Check Status
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
qwickbrain-proxy status
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Shows current configuration and cache location.
|
|
84
|
+
|
|
85
|
+
## Claude Code Configuration
|
|
86
|
+
|
|
87
|
+
Add to your Claude Code MCP configuration:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"qwickbrain": {
|
|
93
|
+
"command": "qwickbrain-proxy",
|
|
94
|
+
"args": ["serve"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Cache Strategy
|
|
101
|
+
|
|
102
|
+
| Content Type | Default TTL | Offline | Notes |
|
|
103
|
+
|--------------|-------------|---------|-------|
|
|
104
|
+
| workflows | 24h (86400s) | ✓ | Global, rarely changes |
|
|
105
|
+
| rules | 24h (86400s) | ✓ | Global, rarely changes |
|
|
106
|
+
| documents | 6h (21600s) | ✓ | Project-scoped FRDs, designs |
|
|
107
|
+
| memories | 1h (3600s) | ✓ | Project context, updated frequently |
|
|
108
|
+
|
|
109
|
+
Cache is automatically populated on first access and refreshed when TTL expires.
|
|
110
|
+
|
|
111
|
+
## Development
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Install dependencies
|
|
115
|
+
npm install
|
|
116
|
+
|
|
117
|
+
# Run in development mode (with watch)
|
|
118
|
+
npm run dev
|
|
119
|
+
|
|
120
|
+
# Run tests
|
|
121
|
+
npm test
|
|
122
|
+
|
|
123
|
+
# Format code
|
|
124
|
+
npm run format
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Architecture
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Claude Code (MCP Client)
|
|
131
|
+
↓ (stdio)
|
|
132
|
+
qwickbrain-proxy (MCP Server)
|
|
133
|
+
├─ Local Cache (SQLite)
|
|
134
|
+
│ ├─ Documents (workflows, rules, FRDs, designs)
|
|
135
|
+
│ ├─ Memories (project context)
|
|
136
|
+
│ └─ Sync Queue (offline writes)
|
|
137
|
+
├─ Connection Manager
|
|
138
|
+
│ ├─ Health checks
|
|
139
|
+
│ ├─ Auto-reconnect with exponential backoff
|
|
140
|
+
│ └─ Graceful degradation
|
|
141
|
+
└─ QwickBrain Client (multi-mode)
|
|
142
|
+
├─ SSE (Server-Sent Events) → QwickBrain Server
|
|
143
|
+
├─ MCP (stdio) → Local QwickBrain Server
|
|
144
|
+
└─ HTTP (REST API) → Cloud QwickBrain Server
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Connection Flow:**
|
|
148
|
+
1. Claude Code requests document via MCP
|
|
149
|
+
2. Proxy checks local cache (SQLite)
|
|
150
|
+
3. If cached and fresh, returns immediately
|
|
151
|
+
4. If expired or missing, fetches from QwickBrain (if connected)
|
|
152
|
+
5. Updates cache and returns data
|
|
153
|
+
6. If offline, serves stale cache with metadata indicating age
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
This project is licensed under the [PolyForm Shield License 1.0.0](LICENSE).
|
|
158
|
+
|
|
159
|
+
**Summary:**
|
|
160
|
+
- Free to use for non-competitive purposes
|
|
161
|
+
- Source code available for learning and development
|
|
162
|
+
- Cannot be used to compete with QwickApps
|
|
163
|
+
- Commercial licensing available for competitive use cases
|
|
164
|
+
|
|
165
|
+
For questions about licensing, contact legal@qwickapps.com
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":""}
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { createDatabase, runMigrations } from '../db/client.js';
|
|
4
|
+
import { ProxyServer } from '../lib/proxy-server.js';
|
|
5
|
+
import { ConfigSchema } from '../types/config.js';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
9
|
+
import { VERSION } from '../version.js';
|
|
10
|
+
const DEFAULT_CONFIG_DIR = join(homedir(), '.qwickbrain');
|
|
11
|
+
const DEFAULT_CONFIG_PATH = join(DEFAULT_CONFIG_DIR, 'config.json');
|
|
12
|
+
function loadConfig() {
|
|
13
|
+
if (!existsSync(DEFAULT_CONFIG_PATH)) {
|
|
14
|
+
return ConfigSchema.parse({});
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const raw = readFileSync(DEFAULT_CONFIG_PATH, 'utf-8');
|
|
18
|
+
const parsed = JSON.parse(raw);
|
|
19
|
+
return ConfigSchema.parse(parsed);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error('Failed to load config:', error);
|
|
23
|
+
return ConfigSchema.parse({});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function saveConfig(config) {
|
|
27
|
+
mkdirSync(DEFAULT_CONFIG_DIR, { recursive: true });
|
|
28
|
+
writeFileSync(DEFAULT_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
const program = new Command();
|
|
31
|
+
program
|
|
32
|
+
.name('qwickbrain-proxy')
|
|
33
|
+
.description('Local MCP proxy for QwickBrain with caching and resilience')
|
|
34
|
+
.version(VERSION);
|
|
35
|
+
program
|
|
36
|
+
.command('serve')
|
|
37
|
+
.description('Start the MCP proxy server (stdio mode)')
|
|
38
|
+
.action(async () => {
|
|
39
|
+
try {
|
|
40
|
+
const config = loadConfig();
|
|
41
|
+
const { db } = createDatabase(config.cache.dir);
|
|
42
|
+
// Run migrations to ensure database schema is up to date
|
|
43
|
+
try {
|
|
44
|
+
runMigrations(db);
|
|
45
|
+
}
|
|
46
|
+
catch (migrationError) {
|
|
47
|
+
console.error('Failed to run database migrations:', migrationError);
|
|
48
|
+
console.error('Please ensure the database directory is writable and try again.');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const server = new ProxyServer(db, config);
|
|
52
|
+
process.on('SIGINT', async () => {
|
|
53
|
+
console.error('\nShutting down...');
|
|
54
|
+
await server.stop();
|
|
55
|
+
process.exit(0);
|
|
56
|
+
});
|
|
57
|
+
await server.start();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error('Failed to start server:', error);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
program
|
|
65
|
+
.command('init')
|
|
66
|
+
.description('Initialize configuration')
|
|
67
|
+
.action(async () => {
|
|
68
|
+
console.log('QwickBrain Proxy Configuration');
|
|
69
|
+
console.log('==============================\n');
|
|
70
|
+
// Simple init - create default config
|
|
71
|
+
const config = ConfigSchema.parse({
|
|
72
|
+
qwickbrain: {
|
|
73
|
+
url: 'http://macmini-devserver.local:3000',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
saveConfig(config);
|
|
77
|
+
console.log(`Configuration saved to: ${DEFAULT_CONFIG_PATH}`);
|
|
78
|
+
console.log('\nDefault settings:');
|
|
79
|
+
console.log(` QwickBrain URL: ${config.qwickbrain.url}`);
|
|
80
|
+
console.log(` Cache directory: ${join(homedir(), '.qwickbrain', 'cache')}`);
|
|
81
|
+
console.log('\nTo customize, edit the config file or use "qwickbrain-proxy config" commands.');
|
|
82
|
+
});
|
|
83
|
+
const configCmd = program.command('config').description('Manage configuration');
|
|
84
|
+
configCmd
|
|
85
|
+
.command('get <key>')
|
|
86
|
+
.description('Get a configuration value')
|
|
87
|
+
.action((key) => {
|
|
88
|
+
const config = loadConfig();
|
|
89
|
+
const value = key.split('.').reduce((obj, k) => obj?.[k], config);
|
|
90
|
+
console.log(value !== undefined ? value : 'Not set');
|
|
91
|
+
});
|
|
92
|
+
configCmd
|
|
93
|
+
.command('set <key> <value>')
|
|
94
|
+
.description('Set a configuration value')
|
|
95
|
+
.action((key, value) => {
|
|
96
|
+
const config = loadConfig();
|
|
97
|
+
const keys = key.split('.');
|
|
98
|
+
const lastKey = keys.pop();
|
|
99
|
+
const target = keys.reduce((obj, k) => {
|
|
100
|
+
if (!obj[k])
|
|
101
|
+
obj[k] = {};
|
|
102
|
+
return obj[k];
|
|
103
|
+
}, config);
|
|
104
|
+
// Try to parse as JSON, otherwise use as string
|
|
105
|
+
try {
|
|
106
|
+
target[lastKey] = JSON.parse(value);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
target[lastKey] = value;
|
|
110
|
+
}
|
|
111
|
+
// Validate against schema to prevent bypassing validation
|
|
112
|
+
try {
|
|
113
|
+
const validatedConfig = ConfigSchema.parse(config);
|
|
114
|
+
saveConfig(validatedConfig);
|
|
115
|
+
console.log(`Updated ${key} = ${value}`);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error('Configuration validation failed:', error instanceof Error ? error.message : String(error));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
configCmd
|
|
123
|
+
.command('show')
|
|
124
|
+
.description('Show current configuration')
|
|
125
|
+
.action(() => {
|
|
126
|
+
const config = loadConfig();
|
|
127
|
+
console.log(JSON.stringify(config, null, 2));
|
|
128
|
+
});
|
|
129
|
+
program
|
|
130
|
+
.command('status')
|
|
131
|
+
.description('Show proxy status')
|
|
132
|
+
.action(() => {
|
|
133
|
+
const config = loadConfig();
|
|
134
|
+
console.log('QwickBrain Proxy Status');
|
|
135
|
+
console.log('======================\n');
|
|
136
|
+
console.log(`Config file: ${DEFAULT_CONFIG_PATH}`);
|
|
137
|
+
console.log(`Config exists: ${existsSync(DEFAULT_CONFIG_PATH) ? 'Yes' : 'No'}`);
|
|
138
|
+
console.log(`QwickBrain URL: ${config.qwickbrain.url}`);
|
|
139
|
+
console.log(`Cache directory: ${config.cache.dir || join(homedir(), '.qwickbrain', 'cache')}`);
|
|
140
|
+
});
|
|
141
|
+
program.parse();
|
|
142
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
|
3
|
+
import * as schema from './schema.js';
|
|
4
|
+
export declare function createDatabase(cacheDir?: string): {
|
|
5
|
+
db: ReturnType<typeof drizzle<typeof schema>>;
|
|
6
|
+
sqlite: Database.Database;
|
|
7
|
+
};
|
|
8
|
+
export declare function runMigrations(db: ReturnType<typeof drizzle>): void;
|
|
9
|
+
export type DB = ReturnType<typeof createDatabase>['db'];
|
|
10
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAErD,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAOtC,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAA0B,GAAG;IACpE,EAAE,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAA;CAC1B,CAaA;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,QAG3D;AAED,MAAM,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
|
3
|
+
import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
|
|
4
|
+
import * as schema from './schema.js';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
import { mkdirSync } from 'fs';
|
|
8
|
+
const DEFAULT_CACHE_DIR = join(homedir(), '.qwickbrain', 'cache');
|
|
9
|
+
export function createDatabase(cacheDir = DEFAULT_CACHE_DIR) {
|
|
10
|
+
// Ensure cache directory exists
|
|
11
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
12
|
+
const dbPath = join(cacheDir, 'qwickbrain.db');
|
|
13
|
+
const sqlite = new Database(dbPath);
|
|
14
|
+
// Enable WAL mode for better concurrency
|
|
15
|
+
sqlite.pragma('journal_mode = WAL');
|
|
16
|
+
const db = drizzle(sqlite, { schema });
|
|
17
|
+
return { db, sqlite };
|
|
18
|
+
}
|
|
19
|
+
export function runMigrations(db) {
|
|
20
|
+
// Drizzle will look for migrations in drizzle/ directory
|
|
21
|
+
migrate(db, { migrationsFolder: './drizzle' });
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,qCAAqC,CAAC;AAC9D,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAE/B,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;AAElE,MAAM,UAAU,cAAc,CAAC,WAAmB,iBAAiB;IAIjE,gCAAgC;IAChC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEpC,yCAAyC;IACzC,MAAM,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAEpC,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAEvC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAA8B;IAC1D,yDAAyD;IACzD,OAAO,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC,CAAC;AACjD,CAAC"}
|