@stackmemoryai/stackmemory 0.5.31 → 0.5.33

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 (63) hide show
  1. package/dist/cli/claude-sm.js +199 -16
  2. package/dist/cli/claude-sm.js.map +2 -2
  3. package/dist/cli/commands/context.js +0 -11
  4. package/dist/cli/commands/context.js.map +2 -2
  5. package/dist/cli/commands/linear.js +1 -14
  6. package/dist/cli/commands/linear.js.map +2 -2
  7. package/dist/cli/commands/login.js +32 -10
  8. package/dist/cli/commands/login.js.map +2 -2
  9. package/dist/cli/commands/migrate.js +80 -22
  10. package/dist/cli/commands/migrate.js.map +2 -2
  11. package/dist/cli/commands/model.js +533 -0
  12. package/dist/cli/commands/model.js.map +7 -0
  13. package/dist/cli/commands/ralph.js +93 -28
  14. package/dist/cli/commands/ralph.js.map +2 -2
  15. package/dist/cli/commands/service.js +10 -3
  16. package/dist/cli/commands/service.js.map +2 -2
  17. package/dist/cli/commands/skills.js +60 -10
  18. package/dist/cli/commands/skills.js.map +2 -2
  19. package/dist/cli/commands/sms-notify.js +342 -22
  20. package/dist/cli/commands/sms-notify.js.map +3 -3
  21. package/dist/cli/index.js +2 -0
  22. package/dist/cli/index.js.map +2 -2
  23. package/dist/core/context/dual-stack-manager.js +23 -7
  24. package/dist/core/context/dual-stack-manager.js.map +2 -2
  25. package/dist/core/context/frame-database.js +33 -5
  26. package/dist/core/context/frame-database.js.map +2 -2
  27. package/dist/core/context/frame-digest.js +6 -1
  28. package/dist/core/context/frame-digest.js.map +2 -2
  29. package/dist/core/context/frame-manager.js +56 -9
  30. package/dist/core/context/frame-manager.js.map +2 -2
  31. package/dist/core/context/permission-manager.js +0 -11
  32. package/dist/core/context/permission-manager.js.map +2 -2
  33. package/dist/core/context/recursive-context-manager.js +15 -9
  34. package/dist/core/context/recursive-context-manager.js.map +2 -2
  35. package/dist/core/context/shared-context-layer.js +0 -11
  36. package/dist/core/context/shared-context-layer.js.map +2 -2
  37. package/dist/core/context/validation.js +6 -1
  38. package/dist/core/context/validation.js.map +2 -2
  39. package/dist/core/models/fallback-monitor.js +229 -0
  40. package/dist/core/models/fallback-monitor.js.map +7 -0
  41. package/dist/core/models/model-router.js +331 -0
  42. package/dist/core/models/model-router.js.map +7 -0
  43. package/dist/hooks/claude-code-whatsapp-hook.js +197 -0
  44. package/dist/hooks/claude-code-whatsapp-hook.js.map +7 -0
  45. package/dist/hooks/linear-task-picker.js +1 -1
  46. package/dist/hooks/linear-task-picker.js.map +2 -2
  47. package/dist/hooks/schemas.js +55 -1
  48. package/dist/hooks/schemas.js.map +2 -2
  49. package/dist/hooks/session-summary.js +5 -1
  50. package/dist/hooks/session-summary.js.map +2 -2
  51. package/dist/hooks/sms-action-runner.js +12 -1
  52. package/dist/hooks/sms-action-runner.js.map +2 -2
  53. package/dist/hooks/sms-notify.js +4 -2
  54. package/dist/hooks/sms-notify.js.map +2 -2
  55. package/dist/hooks/sms-webhook.js +23 -2
  56. package/dist/hooks/sms-webhook.js.map +2 -2
  57. package/dist/hooks/whatsapp-commands.js +376 -0
  58. package/dist/hooks/whatsapp-commands.js.map +7 -0
  59. package/dist/hooks/whatsapp-scheduler.js +317 -0
  60. package/dist/hooks/whatsapp-scheduler.js.map +7 -0
  61. package/dist/hooks/whatsapp-sync.js +375 -0
  62. package/dist/hooks/whatsapp-sync.js.map +7 -0
  63. package/package.json +2 -3
@@ -6,6 +6,11 @@ const __dirname = __pathDirname(__filename);
6
6
  import { Command } from "commander";
7
7
  import { Pool } from "pg";
8
8
  import Database from "better-sqlite3";
9
+ import {
10
+ DatabaseError,
11
+ ValidationError,
12
+ ErrorCode
13
+ } from "../../core/errors/index.js";
9
14
  const MIGRATIONS = [
10
15
  {
11
16
  version: 1,
@@ -57,34 +62,54 @@ function isPg() {
57
62
  async function connect() {
58
63
  if (isPg()) {
59
64
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
60
- await pool.query(`CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at TIMESTAMPTZ DEFAULT NOW(), description TEXT)`);
65
+ await pool.query(
66
+ `CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at TIMESTAMPTZ DEFAULT NOW(), description TEXT)`
67
+ );
61
68
  return { kind: "pg", pg: pool };
62
69
  } else {
63
70
  const path = process.env.DATABASE_URL || ".stackmemory/railway.db";
64
71
  const db = new Database(path);
65
- db.exec(`CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP, description TEXT)`);
72
+ db.exec(
73
+ `CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP, description TEXT)`
74
+ );
66
75
  return { kind: "sqlite", sqlite: db };
67
76
  }
68
77
  }
69
78
  async function getCurrentVersion(m) {
70
79
  if (m.kind === "pg") {
71
- const r = await m.pg.query("SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version");
80
+ const r = await m.pg.query(
81
+ "SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version"
82
+ );
72
83
  return Number(r.rows[0]?.v || 0);
73
84
  }
74
- const row = m.sqlite.prepare("SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version").get();
85
+ const row = m.sqlite.prepare(
86
+ "SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version"
87
+ ).get();
75
88
  return Number(row?.v || 0);
76
89
  }
77
90
  async function listApplied(m) {
78
91
  if (m.kind === "pg") {
79
- const r = await m.pg.query("SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC");
80
- return r.rows.map((row) => ({ version: Number(row.version), description: row.description }));
92
+ const r = await m.pg.query(
93
+ "SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC"
94
+ );
95
+ return r.rows.map((row) => ({
96
+ version: Number(row.version),
97
+ description: row.description
98
+ }));
81
99
  }
82
- const rows = m.sqlite.prepare("SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC").all();
83
- return rows.map((row) => ({ version: Number(row.version), description: row.description }));
100
+ const rows = m.sqlite.prepare(
101
+ "SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC"
102
+ ).all();
103
+ return rows.map((row) => ({
104
+ version: Number(row.version),
105
+ description: row.description
106
+ }));
84
107
  }
85
108
  async function applyTo(m, target) {
86
109
  const current = await getCurrentVersion(m);
87
- const pending = MIGRATIONS.filter((mig) => mig.version > current && mig.version <= target);
110
+ const pending = MIGRATIONS.filter(
111
+ (mig) => mig.version > current && mig.version <= target
112
+ );
88
113
  for (const mig of pending) {
89
114
  if (m.kind === "pg") {
90
115
  for (const s of mig.statements) {
@@ -93,7 +118,10 @@ async function applyTo(m, target) {
93
118
  } catch {
94
119
  }
95
120
  }
96
- await m.pg.query("INSERT INTO railway_schema_version (version, description) VALUES ($1, $2) ON CONFLICT (version) DO NOTHING", [mig.version, mig.description]);
121
+ await m.pg.query(
122
+ "INSERT INTO railway_schema_version (version, description) VALUES ($1, $2) ON CONFLICT (version) DO NOTHING",
123
+ [mig.version, mig.description]
124
+ );
97
125
  } else {
98
126
  m.sqlite.exec("BEGIN");
99
127
  try {
@@ -103,11 +131,17 @@ async function applyTo(m, target) {
103
131
  } catch {
104
132
  }
105
133
  }
106
- m.sqlite.prepare("INSERT OR IGNORE INTO railway_schema_version (version, description) VALUES (?, ?)").run(mig.version, mig.description);
134
+ m.sqlite.prepare(
135
+ "INSERT OR IGNORE INTO railway_schema_version (version, description) VALUES (?, ?)"
136
+ ).run(mig.version, mig.description);
107
137
  m.sqlite.exec("COMMIT");
108
138
  } catch {
109
139
  m.sqlite.exec("ROLLBACK");
110
- throw new Error(`Migration ${mig.version} failed`);
140
+ throw new DatabaseError(
141
+ `Migration ${mig.version} failed`,
142
+ ErrorCode.DB_MIGRATION_FAILED,
143
+ { version: mig.version, description: mig.description }
144
+ );
111
145
  }
112
146
  }
113
147
  console.log(`Applied migration v${mig.version}: ${mig.description}`);
@@ -120,17 +154,24 @@ async function rollbackTo(m, target) {
120
154
  return;
121
155
  }
122
156
  if (m.kind === "pg") {
123
- await m.pg.query("DELETE FROM railway_schema_version WHERE version > $1", [target]);
157
+ await m.pg.query("DELETE FROM railway_schema_version WHERE version > $1", [
158
+ target
159
+ ]);
124
160
  } else {
125
- m.sqlite.prepare("DELETE FROM railway_schema_version WHERE version > ?").run(target);
161
+ m.sqlite.prepare(
162
+ "DELETE FROM railway_schema_version WHERE version > ?"
163
+ ).run(target);
126
164
  }
127
- console.log(`Rolled back schema version pointer from ${current} to ${target}`);
165
+ console.log(
166
+ `Rolled back schema version pointer from ${current} to ${target}`
167
+ );
128
168
  }
129
169
  async function main() {
130
170
  const program = new Command();
131
171
  program.name("railway-migrate").description("Manage Railway server schema migrations").option("-d, --database <url>", "DATABASE_URL override");
132
172
  program.command("list").description("List applied migrations").action(async () => {
133
- if (program.opts().database) process.env.DATABASE_URL = program.opts().database;
173
+ if (program.opts().database)
174
+ process.env.DATABASE_URL = program.opts().database;
134
175
  const m = await connect();
135
176
  const applied = await listApplied(m);
136
177
  const current = await getCurrentVersion(m);
@@ -140,7 +181,8 @@ async function main() {
140
181
  process.exit(0);
141
182
  });
142
183
  program.command("status").description("Show current version and pending migrations").action(async () => {
143
- if (program.opts().database) process.env.DATABASE_URL = program.opts().database;
184
+ if (program.opts().database)
185
+ process.env.DATABASE_URL = program.opts().database;
144
186
  const m = await connect();
145
187
  const current = await getCurrentVersion(m);
146
188
  const latest = Math.max(...MIGRATIONS.map((m2) => m2.version));
@@ -154,21 +196,37 @@ async function main() {
154
196
  }
155
197
  process.exit(0);
156
198
  });
157
- program.command("apply").description("Apply migrations up to a target").option("--to <version|latest>", 'Target version (number or "latest")', "latest").action(async (cmd) => {
158
- if (program.opts().database) process.env.DATABASE_URL = program.opts().database;
199
+ program.command("apply").description("Apply migrations up to a target").option(
200
+ "--to <version|latest>",
201
+ 'Target version (number or "latest")',
202
+ "latest"
203
+ ).action(async (cmd) => {
204
+ if (program.opts().database)
205
+ process.env.DATABASE_URL = program.opts().database;
159
206
  const m = await connect();
160
207
  const latest = Math.max(...MIGRATIONS.map((m2) => m2.version));
161
208
  const target = cmd.to === "latest" ? latest : parseInt(cmd.to, 10);
162
- if (!Number.isFinite(target)) throw new Error("Invalid target");
209
+ if (!Number.isFinite(target))
210
+ throw new ValidationError(
211
+ "Invalid target version",
212
+ ErrorCode.INVALID_INPUT,
213
+ { target: cmd.to }
214
+ );
163
215
  await applyTo(m, target);
164
216
  console.log("Done.");
165
217
  process.exit(0);
166
218
  });
167
219
  program.command("rollback").description("Rollback schema version pointer (non-destructive)").option("--to <version>", "Target version number", "0").action(async (cmd) => {
168
- if (program.opts().database) process.env.DATABASE_URL = program.opts().database;
220
+ if (program.opts().database)
221
+ process.env.DATABASE_URL = program.opts().database;
169
222
  const m = await connect();
170
223
  const target = parseInt(cmd.to, 10);
171
- if (!Number.isFinite(target)) throw new Error("Invalid target");
224
+ if (!Number.isFinite(target))
225
+ throw new ValidationError(
226
+ "Invalid target version",
227
+ ErrorCode.INVALID_INPUT,
228
+ { target: cmd.to }
229
+ );
172
230
  await rollbackTo(m, target);
173
231
  console.log("Done.");
174
232
  process.exit(0);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/cli/commands/migrate.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n/**\n * Railway schema migration CLI\n * Usage examples:\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts list\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts status\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts apply --to latest\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts rollback --to 2\n */\n\nimport { Command } from 'commander';\nimport { Pool } from 'pg';\nimport Database from 'better-sqlite3';\n\ntype DbKind = 'pg' | 'sqlite';\n\ninterface Migrator {\n kind: DbKind;\n pg?: Pool;\n sqlite?: Database.Database;\n}\n\nconst MIGRATIONS: Array<{ version: number; description: string; statements: string[] }> = [\n {\n version: 1,\n description: 'base schema',\n statements: [\n // contexts\n `CREATE TABLE IF NOT EXISTS contexts (id ${isPg() ? 'BIGSERIAL' : 'INTEGER PRIMARY KEY AUTOINCREMENT'} PRIMARY KEY, project_id TEXT NOT NULL, content TEXT NOT NULL, type TEXT DEFAULT 'general', ${isPg() ? \"metadata JSONB DEFAULT '{}'::jsonb\" : \"metadata TEXT DEFAULT '{}'\"}, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, updated_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'})`,\n // api_keys\n `CREATE TABLE IF NOT EXISTS api_keys (id ${isPg() ? 'BIGSERIAL' : 'INTEGER PRIMARY KEY AUTOINCREMENT'} PRIMARY KEY, key_hash TEXT UNIQUE NOT NULL, user_id TEXT NOT NULL, name TEXT, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, ${isPg() ? 'last_used TIMESTAMPTZ' : 'last_used DATETIME'}, revoked ${isPg() ? 'BOOLEAN' : 'BOOLEAN'} DEFAULT ${isPg() ? 'false' : '0'})`,\n // users with role\n `CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, email TEXT, name TEXT, tier TEXT DEFAULT 'free', role TEXT DEFAULT 'user', created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, updated_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'})`,\n // projects\n `CREATE TABLE IF NOT EXISTS projects (id TEXT PRIMARY KEY, name TEXT, is_public ${isPg() ? 'BOOLEAN' : 'BOOLEAN'} DEFAULT ${isPg() ? 'false' : '0'}, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, updated_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'})`,\n // project members\n `CREATE TABLE IF NOT EXISTS project_members (project_id TEXT NOT NULL, user_id TEXT NOT NULL, role TEXT NOT NULL ${isPg() ? '' : \"CHECK (role IN ('admin','owner','editor','viewer'))\"}, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, PRIMARY KEY (project_id, user_id))`,\n // indexes\n `CREATE INDEX IF NOT EXISTS idx_contexts_project ON contexts(project_id)`,\n `CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`,\n `CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)`,\n `CREATE INDEX IF NOT EXISTS idx_project_members_user ON project_members(user_id)`\n ],\n },\n {\n version: 2,\n description: 'admin sessions',\n statements: [\n `CREATE TABLE IF NOT EXISTS admin_sessions (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, expires_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} NOT NULL, user_agent TEXT, ip TEXT)`,\n `CREATE INDEX IF NOT EXISTS idx_admin_sessions_user ON admin_sessions(user_id)`\n ],\n },\n {\n version: 3,\n description: 'role enums & checks',\n statements: [\n // PG enum upgrades; for SQLite CHECK already present\n `CREATE TYPE user_role AS ENUM ('admin','user')`,\n `CREATE TYPE member_role AS ENUM ('admin','owner','editor','viewer')`,\n `ALTER TABLE users ALTER COLUMN role TYPE user_role USING role::user_role`,\n `ALTER TABLE project_members ALTER COLUMN role TYPE member_role USING role::member_role`,\n `ALTER TABLE project_members ADD CONSTRAINT project_members_role_check CHECK (role IN ('admin','owner','editor','viewer'))`,\n `ALTER TABLE users ADD CONSTRAINT users_role_check CHECK (role IN ('admin','user'))`\n ],\n },\n];\n\nfunction isPg(): boolean {\n const url = process.env.DATABASE_URL || '';\n return url.startsWith('postgres://') || url.startsWith('postgresql://');\n}\n\nasync function connect(): Promise<Migrator> {\n if (isPg()) {\n const pool = new Pool({ connectionString: process.env.DATABASE_URL });\n // ensure version table\n await pool.query(`CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at TIMESTAMPTZ DEFAULT NOW(), description TEXT)`);\n return { kind: 'pg', pg: pool };\n } else {\n const path = process.env.DATABASE_URL || '.stackmemory/railway.db';\n const db = new Database(path);\n db.exec(`CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP, description TEXT)`);\n return { kind: 'sqlite', sqlite: db };\n }\n}\n\nasync function getCurrentVersion(m: Migrator): Promise<number> {\n if (m.kind === 'pg') {\n const r = await m.pg!.query('SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version');\n return Number(r.rows[0]?.v || 0);\n }\n const row = m.sqlite!.prepare('SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version').get() as any;\n return Number(row?.v || 0);\n}\n\nasync function listApplied(m: Migrator): Promise<Array<{ version: number; description: string }>> {\n if (m.kind === 'pg') {\n const r = await m.pg!.query('SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC');\n return r.rows.map((row) => ({ version: Number(row.version), description: row.description }));\n }\n const rows = m.sqlite!.prepare('SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC').all() as any[];\n return rows.map((row) => ({ version: Number(row.version), description: row.description }));\n}\n\nasync function applyTo(m: Migrator, target: number): Promise<void> {\n const current = await getCurrentVersion(m);\n const pending = MIGRATIONS.filter((mig) => mig.version > current && mig.version <= target);\n for (const mig of pending) {\n if (m.kind === 'pg') {\n for (const s of mig.statements) {\n try { await m.pg!.query(s); } catch {}\n }\n await m.pg!.query('INSERT INTO railway_schema_version (version, description) VALUES ($1, $2) ON CONFLICT (version) DO NOTHING', [mig.version, mig.description]);\n } else {\n m.sqlite!.exec('BEGIN');\n try {\n for (const s of mig.statements) {\n try { m.sqlite!.exec(s); } catch {}\n }\n m.sqlite!.prepare('INSERT OR IGNORE INTO railway_schema_version (version, description) VALUES (?, ?)').run(mig.version, mig.description);\n m.sqlite!.exec('COMMIT');\n } catch {\n m.sqlite!.exec('ROLLBACK');\n throw new Error(`Migration ${mig.version} failed`);\n }\n }\n console.log(`Applied migration v${mig.version}: ${mig.description}`);\n }\n}\n\nasync function rollbackTo(m: Migrator, target: number): Promise<void> {\n const current = await getCurrentVersion(m);\n if (target >= current) {\n console.log('Nothing to rollback');\n return;\n }\n // Soft rollback: move version pointer back; does not drop objects\n if (m.kind === 'pg') {\n await m.pg!.query('DELETE FROM railway_schema_version WHERE version > $1', [target]);\n } else {\n m.sqlite!.prepare('DELETE FROM railway_schema_version WHERE version > ?').run(target);\n }\n console.log(`Rolled back schema version pointer from ${current} to ${target}`);\n}\n\nasync function main() {\n const program = new Command();\n program\n .name('railway-migrate')\n .description('Manage Railway server schema migrations')\n .option('-d, --database <url>', 'DATABASE_URL override');\n\n program\n .command('list')\n .description('List applied migrations')\n .action(async () => {\n if (program.opts().database) process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const applied = await listApplied(m);\n const current = await getCurrentVersion(m);\n console.log('Current version:', current);\n if (applied.length === 0) console.log('(no migrations applied)');\n applied.forEach((a) => console.log(`v${a.version} - ${a.description}`));\n process.exit(0);\n });\n\n program\n .command('status')\n .description('Show current version and pending migrations')\n .action(async () => {\n if (program.opts().database) process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const current = await getCurrentVersion(m);\n const latest = Math.max(...MIGRATIONS.map((m) => m.version));\n const pending = MIGRATIONS.filter((mig) => mig.version > current);\n console.log('Current version:', current);\n console.log('Latest available:', latest);\n if (pending.length === 0) console.log('No pending migrations.');\n else {\n console.log('Pending:');\n pending.forEach((p) => console.log(`- v${p.version} ${p.description}`));\n }\n process.exit(0);\n });\n\n program\n .command('apply')\n .description('Apply migrations up to a target')\n .option('--to <version|latest>', 'Target version (number or \"latest\")', 'latest')\n .action(async (cmd) => {\n if (program.opts().database) process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const latest = Math.max(...MIGRATIONS.map((m) => m.version));\n const target = cmd.to === 'latest' ? latest : parseInt(cmd.to, 10);\n if (!Number.isFinite(target)) throw new Error('Invalid target');\n await applyTo(m, target);\n console.log('Done.');\n process.exit(0);\n });\n\n program\n .command('rollback')\n .description('Rollback schema version pointer (non-destructive)')\n .option('--to <version>', 'Target version number', '0')\n .action(async (cmd) => {\n if (program.opts().database) process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const target = parseInt(cmd.to, 10);\n if (!Number.isFinite(target)) throw new Error('Invalid target');\n await rollbackTo(m, target);\n console.log('Done.');\n process.exit(0);\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exit(1);\n});\n\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n/**\n * Railway schema migration CLI\n * Usage examples:\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts list\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts status\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts apply --to latest\n * - DATABASE_URL=... tsx src/cli/commands/migrate.ts rollback --to 2\n */\n\nimport { Command } from 'commander';\nimport { Pool } from 'pg';\nimport Database from 'better-sqlite3';\nimport {\n DatabaseError,\n ValidationError,\n ErrorCode,\n} from '../../core/errors/index.js';\n\ntype DbKind = 'pg' | 'sqlite';\n\ninterface Migrator {\n kind: DbKind;\n pg?: Pool;\n sqlite?: Database.Database;\n}\n\nconst MIGRATIONS: Array<{\n version: number;\n description: string;\n statements: string[];\n}> = [\n {\n version: 1,\n description: 'base schema',\n statements: [\n // contexts\n `CREATE TABLE IF NOT EXISTS contexts (id ${isPg() ? 'BIGSERIAL' : 'INTEGER PRIMARY KEY AUTOINCREMENT'} PRIMARY KEY, project_id TEXT NOT NULL, content TEXT NOT NULL, type TEXT DEFAULT 'general', ${isPg() ? \"metadata JSONB DEFAULT '{}'::jsonb\" : \"metadata TEXT DEFAULT '{}'\"}, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, updated_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'})`,\n // api_keys\n `CREATE TABLE IF NOT EXISTS api_keys (id ${isPg() ? 'BIGSERIAL' : 'INTEGER PRIMARY KEY AUTOINCREMENT'} PRIMARY KEY, key_hash TEXT UNIQUE NOT NULL, user_id TEXT NOT NULL, name TEXT, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, ${isPg() ? 'last_used TIMESTAMPTZ' : 'last_used DATETIME'}, revoked ${isPg() ? 'BOOLEAN' : 'BOOLEAN'} DEFAULT ${isPg() ? 'false' : '0'})`,\n // users with role\n `CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, email TEXT, name TEXT, tier TEXT DEFAULT 'free', role TEXT DEFAULT 'user', created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, updated_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'})`,\n // projects\n `CREATE TABLE IF NOT EXISTS projects (id TEXT PRIMARY KEY, name TEXT, is_public ${isPg() ? 'BOOLEAN' : 'BOOLEAN'} DEFAULT ${isPg() ? 'false' : '0'}, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, updated_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'})`,\n // project members\n `CREATE TABLE IF NOT EXISTS project_members (project_id TEXT NOT NULL, user_id TEXT NOT NULL, role TEXT NOT NULL ${isPg() ? '' : \"CHECK (role IN ('admin','owner','editor','viewer'))\"}, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, PRIMARY KEY (project_id, user_id))`,\n // indexes\n `CREATE INDEX IF NOT EXISTS idx_contexts_project ON contexts(project_id)`,\n `CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`,\n `CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)`,\n `CREATE INDEX IF NOT EXISTS idx_project_members_user ON project_members(user_id)`,\n ],\n },\n {\n version: 2,\n description: 'admin sessions',\n statements: [\n `CREATE TABLE IF NOT EXISTS admin_sessions (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, created_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} DEFAULT ${isPg() ? 'NOW()' : 'CURRENT_TIMESTAMP'}, expires_at ${isPg() ? 'TIMESTAMPTZ' : 'DATETIME'} NOT NULL, user_agent TEXT, ip TEXT)`,\n `CREATE INDEX IF NOT EXISTS idx_admin_sessions_user ON admin_sessions(user_id)`,\n ],\n },\n {\n version: 3,\n description: 'role enums & checks',\n statements: [\n // PG enum upgrades; for SQLite CHECK already present\n `CREATE TYPE user_role AS ENUM ('admin','user')`,\n `CREATE TYPE member_role AS ENUM ('admin','owner','editor','viewer')`,\n `ALTER TABLE users ALTER COLUMN role TYPE user_role USING role::user_role`,\n `ALTER TABLE project_members ALTER COLUMN role TYPE member_role USING role::member_role`,\n `ALTER TABLE project_members ADD CONSTRAINT project_members_role_check CHECK (role IN ('admin','owner','editor','viewer'))`,\n `ALTER TABLE users ADD CONSTRAINT users_role_check CHECK (role IN ('admin','user'))`,\n ],\n },\n];\n\nfunction isPg(): boolean {\n const url = process.env.DATABASE_URL || '';\n return url.startsWith('postgres://') || url.startsWith('postgresql://');\n}\n\nasync function connect(): Promise<Migrator> {\n if (isPg()) {\n const pool = new Pool({ connectionString: process.env.DATABASE_URL });\n // ensure version table\n await pool.query(\n `CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at TIMESTAMPTZ DEFAULT NOW(), description TEXT)`\n );\n return { kind: 'pg', pg: pool };\n } else {\n const path = process.env.DATABASE_URL || '.stackmemory/railway.db';\n const db = new Database(path);\n db.exec(\n `CREATE TABLE IF NOT EXISTS railway_schema_version (version INTEGER PRIMARY KEY, applied_at DATETIME DEFAULT CURRENT_TIMESTAMP, description TEXT)`\n );\n return { kind: 'sqlite', sqlite: db };\n }\n}\n\nasync function getCurrentVersion(m: Migrator): Promise<number> {\n if (m.kind === 'pg') {\n const r = await m.pg!.query(\n 'SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version'\n );\n return Number(r.rows[0]?.v || 0);\n }\n const row = m\n .sqlite!.prepare(\n 'SELECT COALESCE(MAX(version), 0) AS v FROM railway_schema_version'\n )\n .get() as any;\n return Number(row?.v || 0);\n}\n\nasync function listApplied(\n m: Migrator\n): Promise<Array<{ version: number; description: string }>> {\n if (m.kind === 'pg') {\n const r = await m.pg!.query(\n 'SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC'\n );\n return r.rows.map((row) => ({\n version: Number(row.version),\n description: row.description,\n }));\n }\n const rows = m\n .sqlite!.prepare(\n 'SELECT version, description, applied_at FROM railway_schema_version ORDER BY version ASC'\n )\n .all() as any[];\n return rows.map((row) => ({\n version: Number(row.version),\n description: row.description,\n }));\n}\n\nasync function applyTo(m: Migrator, target: number): Promise<void> {\n const current = await getCurrentVersion(m);\n const pending = MIGRATIONS.filter(\n (mig) => mig.version > current && mig.version <= target\n );\n for (const mig of pending) {\n if (m.kind === 'pg') {\n for (const s of mig.statements) {\n try {\n await m.pg!.query(s);\n } catch {}\n }\n await m.pg!.query(\n 'INSERT INTO railway_schema_version (version, description) VALUES ($1, $2) ON CONFLICT (version) DO NOTHING',\n [mig.version, mig.description]\n );\n } else {\n m.sqlite!.exec('BEGIN');\n try {\n for (const s of mig.statements) {\n try {\n m.sqlite!.exec(s);\n } catch {}\n }\n m.sqlite!.prepare(\n 'INSERT OR IGNORE INTO railway_schema_version (version, description) VALUES (?, ?)'\n ).run(mig.version, mig.description);\n m.sqlite!.exec('COMMIT');\n } catch {\n m.sqlite!.exec('ROLLBACK');\n throw new DatabaseError(\n `Migration ${mig.version} failed`,\n ErrorCode.DB_MIGRATION_FAILED,\n { version: mig.version, description: mig.description }\n );\n }\n }\n console.log(`Applied migration v${mig.version}: ${mig.description}`);\n }\n}\n\nasync function rollbackTo(m: Migrator, target: number): Promise<void> {\n const current = await getCurrentVersion(m);\n if (target >= current) {\n console.log('Nothing to rollback');\n return;\n }\n // Soft rollback: move version pointer back; does not drop objects\n if (m.kind === 'pg') {\n await m.pg!.query('DELETE FROM railway_schema_version WHERE version > $1', [\n target,\n ]);\n } else {\n m.sqlite!.prepare(\n 'DELETE FROM railway_schema_version WHERE version > ?'\n ).run(target);\n }\n console.log(\n `Rolled back schema version pointer from ${current} to ${target}`\n );\n}\n\nasync function main() {\n const program = new Command();\n program\n .name('railway-migrate')\n .description('Manage Railway server schema migrations')\n .option('-d, --database <url>', 'DATABASE_URL override');\n\n program\n .command('list')\n .description('List applied migrations')\n .action(async () => {\n if (program.opts().database)\n process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const applied = await listApplied(m);\n const current = await getCurrentVersion(m);\n console.log('Current version:', current);\n if (applied.length === 0) console.log('(no migrations applied)');\n applied.forEach((a) => console.log(`v${a.version} - ${a.description}`));\n process.exit(0);\n });\n\n program\n .command('status')\n .description('Show current version and pending migrations')\n .action(async () => {\n if (program.opts().database)\n process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const current = await getCurrentVersion(m);\n const latest = Math.max(...MIGRATIONS.map((m) => m.version));\n const pending = MIGRATIONS.filter((mig) => mig.version > current);\n console.log('Current version:', current);\n console.log('Latest available:', latest);\n if (pending.length === 0) console.log('No pending migrations.');\n else {\n console.log('Pending:');\n pending.forEach((p) => console.log(`- v${p.version} ${p.description}`));\n }\n process.exit(0);\n });\n\n program\n .command('apply')\n .description('Apply migrations up to a target')\n .option(\n '--to <version|latest>',\n 'Target version (number or \"latest\")',\n 'latest'\n )\n .action(async (cmd) => {\n if (program.opts().database)\n process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const latest = Math.max(...MIGRATIONS.map((m) => m.version));\n const target = cmd.to === 'latest' ? latest : parseInt(cmd.to, 10);\n if (!Number.isFinite(target))\n throw new ValidationError(\n 'Invalid target version',\n ErrorCode.INVALID_INPUT,\n { target: cmd.to }\n );\n await applyTo(m, target);\n console.log('Done.');\n process.exit(0);\n });\n\n program\n .command('rollback')\n .description('Rollback schema version pointer (non-destructive)')\n .option('--to <version>', 'Target version number', '0')\n .action(async (cmd) => {\n if (program.opts().database)\n process.env.DATABASE_URL = program.opts().database;\n const m = await connect();\n const target = parseInt(cmd.to, 10);\n if (!Number.isFinite(target))\n throw new ValidationError(\n 'Invalid target version',\n ErrorCode.INVALID_INPUT,\n { target: cmd.to }\n );\n await rollbackTo(m, target);\n console.log('Done.');\n process.exit(0);\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((e) => {\n console.error(e);\n process.exit(1);\n});\n"],
5
+ "mappings": "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",
6
6
  "names": ["m"]
7
7
  }