@twelvehart/supermemory-runtime 1.0.0-next.1 → 1.0.0-next.3
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/Dockerfile +120 -0
- package/README.md +3 -3
- package/package.json +2 -1
- package/scripts/check-runtime-pack.ts +1 -0
- package/scripts/install.sh +8 -0
- package/scripts/mcp-setup.ts +23 -4
- package/scripts/migrations/run_migrations.sh +48 -1
package/Dockerfile
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# SuperMemory Clone API - Multi-stage Dockerfile
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# This Dockerfile uses a multi-stage build for optimal image size:
|
|
5
|
+
# - Stage 1 (deps): Install all dependencies
|
|
6
|
+
# - Stage 2 (builder): Build TypeScript to JavaScript
|
|
7
|
+
# - Stage 3 (runner): Production runtime with minimal footprint
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
# -----------------------------------------------------------------------------
|
|
11
|
+
# Stage 1: Dependencies
|
|
12
|
+
# -----------------------------------------------------------------------------
|
|
13
|
+
# Install all dependencies including devDependencies for building
|
|
14
|
+
FROM node:20-alpine AS deps
|
|
15
|
+
|
|
16
|
+
# Install build dependencies required for native modules (better-sqlite3)
|
|
17
|
+
# - python3 and make are needed for node-gyp
|
|
18
|
+
# - g++ is the C++ compiler
|
|
19
|
+
# - libc6-compat ensures glibc compatibility
|
|
20
|
+
RUN apk add --no-cache python3 make g++ libc6-compat
|
|
21
|
+
|
|
22
|
+
WORKDIR /app
|
|
23
|
+
|
|
24
|
+
# Copy package files for dependency installation
|
|
25
|
+
# This layer is cached unless package*.json changes
|
|
26
|
+
COPY package.json package-lock.json ./
|
|
27
|
+
|
|
28
|
+
# Install all dependencies (including dev for TypeScript compilation)
|
|
29
|
+
# Use --legacy-peer-deps if you encounter peer dependency issues
|
|
30
|
+
RUN npm ci
|
|
31
|
+
|
|
32
|
+
# -----------------------------------------------------------------------------
|
|
33
|
+
# Stage 2: Builder
|
|
34
|
+
# -----------------------------------------------------------------------------
|
|
35
|
+
# Compile TypeScript to JavaScript
|
|
36
|
+
FROM node:20-alpine AS builder
|
|
37
|
+
|
|
38
|
+
WORKDIR /app
|
|
39
|
+
|
|
40
|
+
# Copy dependencies from deps stage
|
|
41
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
42
|
+
|
|
43
|
+
# Copy source code and configuration files
|
|
44
|
+
COPY package.json package-lock.json tsconfig.json ./
|
|
45
|
+
COPY src ./src
|
|
46
|
+
COPY drizzle.config.ts ./
|
|
47
|
+
|
|
48
|
+
# Build TypeScript
|
|
49
|
+
RUN npm run build
|
|
50
|
+
|
|
51
|
+
# Generate drizzle migrations if schema exists
|
|
52
|
+
# This ensures migrations are available in production
|
|
53
|
+
RUN npm run db:generate || true
|
|
54
|
+
|
|
55
|
+
# Prune dev dependencies for production
|
|
56
|
+
# This removes TypeScript, test frameworks, etc.
|
|
57
|
+
RUN npm prune --production
|
|
58
|
+
|
|
59
|
+
# -----------------------------------------------------------------------------
|
|
60
|
+
# Stage 3: Production Runner
|
|
61
|
+
# -----------------------------------------------------------------------------
|
|
62
|
+
# Minimal production image
|
|
63
|
+
FROM node:20-alpine AS runner
|
|
64
|
+
|
|
65
|
+
# Add labels for container metadata
|
|
66
|
+
LABEL org.opencontainers.image.title="SuperMemory Clone API"
|
|
67
|
+
LABEL org.opencontainers.image.description="Personal AI memory assistant with semantic search"
|
|
68
|
+
LABEL org.opencontainers.image.version="1.0.0"
|
|
69
|
+
LABEL org.opencontainers.image.vendor="SuperMemory Clone"
|
|
70
|
+
|
|
71
|
+
# Install runtime dependencies only
|
|
72
|
+
# - libc6-compat: Required for better-sqlite3 native bindings
|
|
73
|
+
# - tini: Proper init system for signal handling in containers
|
|
74
|
+
RUN apk add --no-cache libc6-compat tini
|
|
75
|
+
|
|
76
|
+
# Create non-root user for security
|
|
77
|
+
# Running as root in containers is a security risk
|
|
78
|
+
RUN addgroup --system --gid 1001 nodejs && \
|
|
79
|
+
adduser --system --uid 1001 supermemory
|
|
80
|
+
|
|
81
|
+
WORKDIR /app
|
|
82
|
+
|
|
83
|
+
# Create data directory for SQLite database
|
|
84
|
+
# This directory should be mounted as a volume in production
|
|
85
|
+
RUN mkdir -p /app/data && chown -R supermemory:nodejs /app/data
|
|
86
|
+
|
|
87
|
+
# Copy production files from builder
|
|
88
|
+
COPY --from=builder --chown=supermemory:nodejs /app/node_modules ./node_modules
|
|
89
|
+
COPY --from=builder --chown=supermemory:nodejs /app/dist ./dist
|
|
90
|
+
COPY --from=builder --chown=supermemory:nodejs /app/package.json ./
|
|
91
|
+
COPY --from=builder --chown=supermemory:nodejs /app/drizzle ./drizzle
|
|
92
|
+
|
|
93
|
+
# Copy entrypoint script
|
|
94
|
+
COPY --chown=supermemory:nodejs scripts/docker-entrypoint.sh /app/docker-entrypoint.sh
|
|
95
|
+
RUN chmod +x /app/docker-entrypoint.sh
|
|
96
|
+
|
|
97
|
+
# Switch to non-root user
|
|
98
|
+
USER supermemory
|
|
99
|
+
|
|
100
|
+
# Set environment variables
|
|
101
|
+
# NODE_ENV=production enables production optimizations
|
|
102
|
+
ENV NODE_ENV=production
|
|
103
|
+
ENV API_HOST=0.0.0.0
|
|
104
|
+
ENV API_PORT=3000
|
|
105
|
+
|
|
106
|
+
# Expose API port
|
|
107
|
+
EXPOSE 3000
|
|
108
|
+
|
|
109
|
+
# Health check to verify the container is running properly
|
|
110
|
+
# Checks the /health endpoint every 30 seconds
|
|
111
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
112
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
|
|
113
|
+
|
|
114
|
+
# Use tini as init system for proper signal handling
|
|
115
|
+
# This ensures SIGTERM is properly forwarded to Node.js
|
|
116
|
+
ENTRYPOINT ["/sbin/tini", "--"]
|
|
117
|
+
|
|
118
|
+
# Run with node directly (not npm) for proper signal handling
|
|
119
|
+
# npm doesn't forward signals properly to child processes
|
|
120
|
+
CMD ["/app/docker-entrypoint.sh"]
|
package/README.md
CHANGED
|
@@ -33,12 +33,12 @@ It stores documents, extracts memories, indexes embeddings, and serves search/pr
|
|
|
33
33
|
Primary path for first-time users:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
npx -y @twelvehart/supermemory@latest full --
|
|
37
|
-
cd
|
|
36
|
+
npx -y @twelvehart/supermemory@latest full --mcp project
|
|
37
|
+
cd ~/.supermemory
|
|
38
38
|
claude
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
That command unpacks the runtime into
|
|
41
|
+
That command unpacks the runtime into `~/.supermemory`, runs the canonical `scripts/install.sh` from the final directory, registers Claude MCP against that final path, and prints only the next steps a new user can actually take.
|
|
42
42
|
|
|
43
43
|
The npx installer supports:
|
|
44
44
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twelvehart/supermemory-runtime",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.3",
|
|
4
4
|
"description": "A personal AI memory assistant - supermemory.ai clone",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"README.md",
|
|
10
10
|
".env.example",
|
|
11
|
+
"Dockerfile",
|
|
11
12
|
"package-lock.json",
|
|
12
13
|
"src",
|
|
13
14
|
"scripts",
|
package/scripts/install.sh
CHANGED
|
@@ -5,6 +5,14 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
5
5
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
6
6
|
cd "$REPO_ROOT"
|
|
7
7
|
|
|
8
|
+
if [[ -d "$HOME/.local/bin" ]]; then
|
|
9
|
+
PATH="$HOME/.local/bin:$PATH"
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
if [[ -d "/usr/local/bin" ]]; then
|
|
13
|
+
PATH="/usr/local/bin:$PATH"
|
|
14
|
+
fi
|
|
15
|
+
|
|
8
16
|
ACTION="install"
|
|
9
17
|
INSTALL_MODE="agent"
|
|
10
18
|
ENV_FILE=""
|
package/scripts/mcp-setup.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { existsSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { createInterface } from 'node:readline';
|
|
5
|
-
import {
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
import { delimiter, resolve } from 'node:path';
|
|
6
7
|
import pkg from 'pg';
|
|
7
8
|
import { loadEnvFile } from '../src/config/env.js';
|
|
8
9
|
import {
|
|
@@ -52,9 +53,26 @@ function formatRemovalCommand(scope: ClaudeMcpScope): string {
|
|
|
52
53
|
return `claude mcp remove --scope ${scope} supermemory`;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
function createCommandEnv(): NodeJS.ProcessEnv {
|
|
57
|
+
const preferredPaths = [`${homedir()}/.local/bin`, '/usr/local/bin'];
|
|
58
|
+
const currentPath = process.env.PATH ?? '';
|
|
59
|
+
const mergedPath = [...preferredPaths, currentPath]
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(delimiter);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
...process.env,
|
|
65
|
+
PATH: mergedPath,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
55
69
|
function commandExists(name: string): boolean {
|
|
56
70
|
try {
|
|
57
|
-
execSync(`command -v ${name}`, {
|
|
71
|
+
execSync(`command -v ${name}`, {
|
|
72
|
+
env: createCommandEnv(),
|
|
73
|
+
stdio: 'ignore',
|
|
74
|
+
shell: '/bin/zsh',
|
|
75
|
+
});
|
|
58
76
|
return true;
|
|
59
77
|
} catch {
|
|
60
78
|
return false;
|
|
@@ -162,7 +180,7 @@ async function run(): Promise<void> {
|
|
|
162
180
|
if (answer === '' || answer.toLowerCase() === 'y') {
|
|
163
181
|
console.log('Building...');
|
|
164
182
|
try {
|
|
165
|
-
execSync('npm run build', { stdio: 'inherit' });
|
|
183
|
+
execSync('npm run build', { env: createCommandEnv(), stdio: 'inherit' });
|
|
166
184
|
} catch {
|
|
167
185
|
console.error('Build failed. Fix errors and try again.');
|
|
168
186
|
process.exit(1);
|
|
@@ -238,7 +256,7 @@ async function run(): Promise<void> {
|
|
|
238
256
|
const removeCmd = formatRemovalCommand(selectedScope);
|
|
239
257
|
console.log(`[INFO] Existing ${selectedScope} scope registration does not match the current build output; repairing with: ${removeCmd} && ${cmd}`);
|
|
240
258
|
try {
|
|
241
|
-
execSync(removeCmd, { stdio: 'inherit', shell: '/bin/zsh' });
|
|
259
|
+
execSync(removeCmd, { env: createCommandEnv(), stdio: 'inherit', shell: '/bin/zsh' });
|
|
242
260
|
} catch (error) {
|
|
243
261
|
const msg = error instanceof Error ? error.message : String(error);
|
|
244
262
|
console.error(`\nCould not remove the existing ${selectedScope} scope registration: ${msg}`);
|
|
@@ -250,6 +268,7 @@ async function run(): Promise<void> {
|
|
|
250
268
|
|
|
251
269
|
try {
|
|
252
270
|
execSync(`claude mcp add supermemory --scope ${selectedScope} -- node ${JSON.stringify(entryPoint)}`, {
|
|
271
|
+
env: createCommandEnv(),
|
|
253
272
|
stdio: 'inherit',
|
|
254
273
|
shell: '/bin/zsh',
|
|
255
274
|
});
|
|
@@ -31,6 +31,25 @@ fi
|
|
|
31
31
|
# Database URL from environment or default
|
|
32
32
|
DATABASE_URL="${DATABASE_URL:-postgresql://supermemory:supermemory_secret@localhost:15432/supermemory}"
|
|
33
33
|
|
|
34
|
+
run_drizzle_migrations() {
|
|
35
|
+
print_info "Applying Drizzle schema migrations..."
|
|
36
|
+
|
|
37
|
+
if ! command -v npm >/dev/null 2>&1; then
|
|
38
|
+
print_error "npm is required to run Drizzle migrations"
|
|
39
|
+
return 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if ! (cd "$PROJECT_ROOT" && npm run db:migrate > /dev/null 2>&1); then
|
|
43
|
+
print_error "Drizzle schema migration failed"
|
|
44
|
+
print_info "Run with verbose mode from the project root:"
|
|
45
|
+
print_info " npm run db:migrate"
|
|
46
|
+
return 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
print_success "Drizzle schema migration completed"
|
|
50
|
+
return 0
|
|
51
|
+
}
|
|
52
|
+
|
|
34
53
|
# Check if DATABASE_URL is set
|
|
35
54
|
if [ -z "$DATABASE_URL" ]; then
|
|
36
55
|
echo -e "${RED}Error: DATABASE_URL not set${NC}"
|
|
@@ -122,7 +141,6 @@ run_migration() {
|
|
|
122
141
|
run_all_migrations() {
|
|
123
142
|
local migrations=(
|
|
124
143
|
"001_create_pgvector_extension.sql"
|
|
125
|
-
"003_create_hnsw_index.sql"
|
|
126
144
|
)
|
|
127
145
|
|
|
128
146
|
print_info "Starting migration process..."
|
|
@@ -144,6 +162,35 @@ run_all_migrations() {
|
|
|
144
162
|
echo ""
|
|
145
163
|
done
|
|
146
164
|
|
|
165
|
+
if ! run_drizzle_migrations; then
|
|
166
|
+
print_error "Migration process aborted"
|
|
167
|
+
return 1
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
echo ""
|
|
171
|
+
|
|
172
|
+
local optional_migrations=(
|
|
173
|
+
"003_create_hnsw_index.sql"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
for migration in "${optional_migrations[@]}"; do
|
|
177
|
+
local migration_path="$MIGRATIONS_DIR/$migration"
|
|
178
|
+
|
|
179
|
+
if [ ! -f "$migration_path" ]; then
|
|
180
|
+
print_warning "Optional migration file not found: $migration"
|
|
181
|
+
continue
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
if ! run_migration "$migration_path"; then
|
|
185
|
+
print_warning "Optional migration failed: $migration"
|
|
186
|
+
print_warning "Continuing because the core schema is already installed"
|
|
187
|
+
echo ""
|
|
188
|
+
continue
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
echo ""
|
|
192
|
+
done
|
|
193
|
+
|
|
147
194
|
print_success "All migrations completed successfully!"
|
|
148
195
|
return 0
|
|
149
196
|
}
|