@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 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 --dir ./supermemory --mcp project
37
- cd ./supermemory
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 `./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.
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.1",
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",
@@ -14,6 +14,7 @@ interface PackResult {
14
14
 
15
15
  const REQUIRED_PATHS = [
16
16
  '.env.example',
17
+ 'Dockerfile',
17
18
  'docker-compose.prod.yml',
18
19
  'docker-compose.yml',
19
20
  'package.json',
@@ -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=""
@@ -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 { resolve } from 'node:path';
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}`, { stdio: 'ignore', shell: '/bin/zsh' });
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
  }