spine-framework 0.1.20 → 0.1.22

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.
@@ -30,9 +30,6 @@ const PACKAGE_ROOT = resolve(__dirname, '../../..')
30
30
 
31
31
  interface InitOptions {
32
32
  dryRun: boolean
33
- url?: string
34
- anonKey?: string
35
- serviceRoleKey?: string
36
33
  }
37
34
 
38
35
  function scaffoldCustomWorkspace(dryRun: boolean): void {
@@ -58,38 +55,47 @@ function scaffoldCustomWorkspace(dryRun: boolean): void {
58
55
  }
59
56
  }
60
57
 
61
- function writeEnvFile(url: string, anonKey: string, serviceRoleKey: string, dryRun: boolean): void {
58
+ function writeEnvFile(dryRun: boolean): void {
62
59
  const envPath = resolve(PROJECT_ROOT, '.env')
60
+
61
+ if (existsSync(envPath)) {
62
+ console.log(' ⏭️ .env already exists, skipping')
63
+ return
64
+ }
65
+
63
66
  const envContent = [
64
- `VITE_SUPABASE_URL=${url}`,
65
- `VITE_SUPABASE_ANON_KEY=${anonKey}`,
66
- `SUPABASE_URL=${url}`,
67
- `SUPABASE_ANON_KEY=${anonKey}`,
68
- `SUPABASE_SERVICE_ROLE_KEY=${serviceRoleKey}`,
69
- `VITE_APP_NAME=Spine`,
70
- `DB_SCHEMA=public`,
67
+ '# ─── Supabase ────────────────────────────────────────────────────────────────',
68
+ '# Get these from: https://supabase.com/dashboard/project/_/settings/api',
69
+ 'SUPABASE_URL=https://your-project.supabase.co',
70
+ 'SUPABASE_ANON_KEY=your-anon-key',
71
+ 'SUPABASE_SERVICE_ROLE_KEY=your-service-role-key',
72
+ '',
73
+ '# DB password for running migrations (direct Postgres connection)',
74
+ '# Get from: https://supabase.com/dashboard/project/_/settings/database',
75
+ 'SUPABASE_DB_PASSWORD=your-db-password',
76
+ '',
77
+ '# ─── Vite / Frontend ─────────────────────────────────────────────────────────',
78
+ 'VITE_SUPABASE_URL=https://your-project.supabase.co',
79
+ 'VITE_SUPABASE_ANON_KEY=your-anon-key',
80
+ 'VITE_APP_NAME=Spine',
81
+ '',
82
+ '# ─── OpenAI ──────────────────────────────────────────────────────────────────',
83
+ 'OPENAI_API_KEY=your-openai-api-key',
84
+ '',
85
+ '# ─── App config ──────────────────────────────────────────────────────────────',
86
+ 'DB_SCHEMA=public',
71
87
  ].join('\n') + '\n'
72
88
 
73
89
  if (dryRun) {
74
- console.log(` [dry-run] Would write .env with SUPABASE_URL=${url}`)
90
+ console.log(' [dry-run] Would write .env with placeholder values')
75
91
  return
76
92
  }
77
93
 
78
- const existed = existsSync(envPath)
79
94
  writeFileSync(envPath, envContent, 'utf8')
80
- console.log(`${existed ? 'Updated' : 'Created'} .env`)
81
-
82
- // Also inject into process.env for the rest of this run
83
- process.env.SUPABASE_URL = url
84
- process.env.SUPABASE_ANON_KEY = anonKey
85
- process.env.SUPABASE_SERVICE_ROLE_KEY = serviceRoleKey
86
- process.env.VITE_SUPABASE_URL = url
87
- process.env.VITE_SUPABASE_ANON_KEY = anonKey
95
+ console.log(' ✓ Created .env')
88
96
  }
89
97
 
90
98
  function copyFrameworkFiles(dryRun: boolean): void {
91
- console.log(` package: ${PACKAGE_ROOT}`)
92
- console.log(` project: ${PROJECT_ROOT}`)
93
99
  // Directories and files to copy from the package root into the consumer project root
94
100
  const items = [
95
101
  { src: '.framework', dest: '.framework' },
@@ -129,40 +135,30 @@ function copyFrameworkFiles(dryRun: boolean): void {
129
135
  async function initCommand(options: InitOptions): Promise<void> {
130
136
  console.log('\n🚀 Spine Framework — Init\n')
131
137
 
132
- // Step 1: Write .env
133
- if (options.url && options.anonKey && options.serviceRoleKey) {
134
- console.log('🔑 Step 1: Writing environment configuration...')
135
- writeEnvFile(options.url, options.anonKey, options.serviceRoleKey, options.dryRun)
136
- } else if (!process.env.SUPABASE_URL) {
137
- console.error('❌ No Supabase credentials found.')
138
- console.error(' spine-framework init --url <url> --anon-key <key> --service-role-key <key>')
139
- process.exit(1)
140
- } else {
141
- console.log(' ✓ Using existing .env credentials')
142
- }
143
-
144
- // Step 2: Copy framework files to project root
145
- console.log('\n📦 Step 2: Installing framework files...')
138
+ // Step 1: Copy framework files to project root
139
+ console.log('📦 Step 1: Installing framework files...')
146
140
  copyFrameworkFiles(options.dryRun)
147
141
 
148
- // Step 3: Scaffold custom workspace
149
- console.log('\n📁 Step 3: Scaffolding custom workspace...')
142
+ // Step 2: Scaffold custom workspace
143
+ console.log('\n📁 Step 2: Scaffolding custom workspace...')
150
144
  scaffoldCustomWorkspace(options.dryRun)
151
145
 
146
+ // Step 3: Write .env with placeholders
147
+ console.log('\n🔑 Step 3: Writing environment configuration...')
148
+ writeEnvFile(options.dryRun)
149
+
152
150
  console.log('\n✅ Project initialized!')
153
- console.log('\n Next: apply database migrations:')
154
- console.log(' npx spine-framework migrate --db-password <your-db-password>')
155
- console.log('\n Find your DB password at:')
156
- console.log(' https://supabase.com/dashboard/project/_/settings/database')
151
+ console.log('\n Next steps:')
152
+ console.log(' 1. Fill in your Supabase credentials in .env')
153
+ console.log(' (get them from https://supabase.com/dashboard/project/_/settings/api)')
154
+ console.log(' 2. Run: npx spine-framework migrate --db-password <your-db-password>')
155
+ console.log(' (get DB password from https://supabase.com/dashboard/project/_/settings/database)')
157
156
  }
158
157
 
159
158
  export function registerInitCommands(program: Command) {
160
159
  program
161
160
  .command('init')
162
- .description('Write .env and scaffold the custom/ workspace (no DB changes)')
163
- .option('--url <url>', 'Supabase project URL')
164
- .option('--anon-key <key>', 'Supabase anon key')
165
- .option('--service-role-key <key>', 'Supabase service role key')
161
+ .description('Install framework files, scaffold custom/ workspace, and write .env template')
166
162
  .option('--dry-run', 'Show what would happen without making changes', false)
167
163
  .action(async (opts) => {
168
164
  try {
@@ -28,7 +28,7 @@ const __dirname = dirname(__filename)
28
28
  const MIGRATIONS_DIR = resolve(__dirname, '../../migrations')
29
29
 
30
30
  interface MigrateOptions {
31
- dbPassword: string
31
+ dbPassword?: string
32
32
  dryRun: boolean
33
33
  }
34
34
 
@@ -36,16 +36,21 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
36
36
  console.log('\n🗄️ Spine Framework — Migrate\n')
37
37
 
38
38
  const supabaseUrl = process.env.SUPABASE_URL
39
- if (!supabaseUrl) {
40
- console.error('❌ SUPABASE_URL not set. Run `spine-framework init` first.')
39
+ if (!supabaseUrl || supabaseUrl === 'https://your-project.supabase.co') {
40
+ console.error('❌ SUPABASE_URL not set. Fill in your .env file first.')
41
+ process.exit(1)
42
+ }
43
+
44
+ const dbPassword = options.dbPassword || process.env.SUPABASE_DB_PASSWORD
45
+ if (!dbPassword || dbPassword === 'your-db-password') {
46
+ console.error('❌ SUPABASE_DB_PASSWORD not set.')
47
+ console.error(' Add it to .env or pass --db-password <password>')
48
+ console.error(' Get it from: https://supabase.com/dashboard/project/_/settings/database')
41
49
  process.exit(1)
42
50
  }
43
51
 
44
- // Derive connection string from Supabase URL
45
- // Format: https://<ref>.supabase.co → postgres://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres
46
- // Use direct connection (port 5432) via the db.<ref>.supabase.co host
47
52
  const projectRef = supabaseUrl.replace('https://', '').split('.')[0]
48
- const connectionString = `postgresql://postgres:${options.dbPassword}@db.${projectRef}.supabase.co:5432/postgres`
53
+ const connectionString = `postgresql://postgres:${dbPassword}@db.${projectRef}.supabase.co:5432/postgres`
49
54
 
50
55
  if (!existsSync(MIGRATIONS_DIR)) {
51
56
  console.error(`❌ Migrations directory not found: ${MIGRATIONS_DIR}`)
@@ -120,7 +125,7 @@ export function registerMigrateCommands(program: Command) {
120
125
  program
121
126
  .command('migrate')
122
127
  .description('Apply SQL migrations via direct Postgres connection')
123
- .requiredOption('--db-password <password>', 'Supabase database password (from dashboard Settings → Database)')
128
+ .option('--db-password <password>', 'DB password override (default: reads SUPABASE_DB_PASSWORD from .env)')
124
129
  .option('--dry-run', 'Show what would happen without making changes', false)
125
130
  .action(async (opts) => {
126
131
  try {
@@ -39,7 +39,6 @@
39
39
  * @seeAlso functions/_shared/index.ts (core functions exposed to CLI)
40
40
  */
41
41
 
42
- import './env-loader.ts'
43
42
  import { Command } from 'commander'
44
43
 
45
44
  const program = new Command()
@@ -50,59 +49,67 @@ program
50
49
  .version('2.0.0')
51
50
  .option('--account <id>', 'Override the account ID for this command')
52
51
 
53
- const [
54
- { registerAuthCommands },
55
- { registerPipelineCommands },
56
- { registerItemCommands },
57
- { registerAgentCommands },
58
- { registerMigrationCommands },
59
- { registerDoctorCommands },
60
- { registerDevCommands },
61
- { registerTestCommands },
62
- { registerSystemCommands },
63
- { registerGenerateCommands },
64
- { registerCreateAppCommands },
65
- { registerInitCommands },
66
- { registerInstallAppCommands },
67
- { registerStatusCommands },
68
- { registerUninstallAppCommands },
69
- { registerMigrateCommands },
70
- ] = await Promise.all([
71
- import('./commands/auth.ts'),
72
- import('./commands/pipelines.ts'),
73
- import('./commands/items.ts'),
74
- import('./commands/agents.ts'),
75
- import('./commands/migrations.ts'),
76
- import('./commands/doctor.ts'),
77
- import('./commands/dev.ts'),
78
- import('./commands/test.ts'),
79
- import('./commands/system.ts'),
80
- import('./commands/generate.ts'),
81
- import('./commands/create-app.ts'),
82
- import('./commands/init.ts'),
83
- import('./commands/install-app.ts'),
84
- import('./commands/status.ts'),
85
- import('./commands/uninstall-app.ts'),
86
- import('./commands/migrate.ts'),
87
- ])
88
-
89
- registerAuthCommands(program)
90
- registerPipelineCommands(program)
91
- registerItemCommands(program)
92
- registerAgentCommands(program)
93
- registerMigrationCommands(program)
94
- registerDoctorCommands(program)
95
- registerDevCommands(program)
96
- registerTestCommands(program)
97
- registerSystemCommands(program)
98
- registerGenerateCommands(program)
99
- registerCreateAppCommands(program)
52
+ // Commands that do NOT need DB/env — safe to run on a fresh install
53
+ const { registerInitCommands } = await import('./commands/init.ts')
54
+ const { registerMigrateCommands } = await import('./commands/migrate.ts')
100
55
  registerInitCommands(program)
101
- registerInstallAppCommands(program)
102
- registerStatusCommands(program)
103
- registerUninstallAppCommands(program)
104
56
  registerMigrateCommands(program)
105
57
 
58
+ // All other commands require SUPABASE_URL to be set — only load if not init/migrate
59
+ const subcommand = process.argv[2]
60
+ const noDbCommands = ['init', 'migrate', '--help', '-h', '--version', '-V', undefined]
61
+
62
+ if (!noDbCommands.includes(subcommand)) {
63
+ await import('./env-loader.ts')
64
+
65
+ const [
66
+ { registerAuthCommands },
67
+ { registerPipelineCommands },
68
+ { registerItemCommands },
69
+ { registerAgentCommands },
70
+ { registerMigrationCommands },
71
+ { registerDoctorCommands },
72
+ { registerDevCommands },
73
+ { registerTestCommands },
74
+ { registerSystemCommands },
75
+ { registerGenerateCommands },
76
+ { registerCreateAppCommands },
77
+ { registerInstallAppCommands },
78
+ { registerStatusCommands },
79
+ { registerUninstallAppCommands },
80
+ ] = await Promise.all([
81
+ import('./commands/auth.ts'),
82
+ import('./commands/pipelines.ts'),
83
+ import('./commands/items.ts'),
84
+ import('./commands/agents.ts'),
85
+ import('./commands/migrations.ts'),
86
+ import('./commands/doctor.ts'),
87
+ import('./commands/dev.ts'),
88
+ import('./commands/test.ts'),
89
+ import('./commands/system.ts'),
90
+ import('./commands/generate.ts'),
91
+ import('./commands/create-app.ts'),
92
+ import('./commands/install-app.ts'),
93
+ import('./commands/status.ts'),
94
+ import('./commands/uninstall-app.ts'),
95
+ ])
96
+
97
+ registerAuthCommands(program)
98
+ registerPipelineCommands(program)
99
+ registerItemCommands(program)
100
+ registerAgentCommands(program)
101
+ registerMigrationCommands(program)
102
+ registerDoctorCommands(program)
103
+ registerDevCommands(program)
104
+ registerTestCommands(program)
105
+ registerSystemCommands(program)
106
+ registerGenerateCommands(program)
107
+ registerCreateAppCommands(program)
108
+ registerInstallAppCommands(program)
109
+ registerStatusCommands(program)
110
+ registerUninstallAppCommands(program)
111
+ }
112
+
106
113
  program.parseAsync(process.argv).catch((err) => {
107
114
  console.error('Error:', err.message)
108
115
  process.exit(1)
@@ -31,9 +31,9 @@ import { createClient } from '@supabase/supabase-js'
31
31
  * @calledBy adminDb, getUserDb (applied at client construction time)
32
32
  */
33
33
  const _env = (globalThis as any).process?.env || {}
34
- const supabaseUrl: string = _env.SUPABASE_URL!
35
- const supabaseServiceKey: string = _env.SUPABASE_SERVICE_ROLE_KEY!
36
- const supabaseAnonKey: string = _env.SUPABASE_ANON_KEY!
34
+ const supabaseUrl: string = _env.SUPABASE_URL || 'https://placeholder.supabase.co'
35
+ const supabaseServiceKey: string = _env.SUPABASE_SERVICE_ROLE_KEY || ''
36
+ const supabaseAnonKey: string = _env.SUPABASE_ANON_KEY || ''
37
37
  const dbSchema: string = _env.DB_SCHEMA || 'public'
38
38
 
39
39
  // ─── CLIENTS ─────────────────────────────────────────────────────────────────
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/init.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA2IxC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAiBpD"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/init.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA0IxC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAcpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/migrate.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkGxC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAevD"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/migrate.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAuGxC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAevD"}
@@ -37,5 +37,5 @@
37
37
  * @seeAlso cli/context.ts (buildCliContext — constructs CoreContext for every command)
38
38
  * @seeAlso functions/_shared/index.ts (core functions exposed to CLI)
39
39
  */
40
- import './env-loader.ts';
40
+ export {};
41
41
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../.framework/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../.framework/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "private": false,
5
5
  "description": "Spine — enterprise application framework built on Supabase + Netlify + React",
6
6
  "type": "module",