spine-framework 0.3.17 → 0.3.19

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 (270) hide show
  1. package/.framework/cli/bin.mjs +16 -0
  2. package/.framework/cli/commands/create-app.ts +1 -1
  3. package/.framework/cli/commands/dev.ts +215 -33
  4. package/.framework/cli/index.ts +37 -75
  5. package/.framework/functions/_shared/app-manifest.ts +8 -15
  6. package/.framework/functions/_shared/db.ts +11 -15
  7. package/.framework/functions/_shared/index.ts +7 -16
  8. package/.framework/functions/_shared/principal.ts +4 -62
  9. package/.framework/functions/_shared/resolve-ids.ts +6 -6
  10. package/.framework/functions/_shared/webhook-registry.ts +43 -1
  11. package/.framework/functions/admin-funnel.ts +104 -0
  12. package/.framework/functions/admin-install-funnel.ts +225 -0
  13. package/.framework/functions/admin-signals.ts +241 -0
  14. package/.framework/functions/integration-routes.ts +4 -8
  15. package/.framework/migrations/001_schema.sql +406 -0
  16. package/.framework/migrations/002_rls_and_functions.sql +710 -0
  17. package/.framework/migrations/003_system_data.sql +520 -0
  18. package/.framework/migrations/004_enhancements.sql +43 -0
  19. package/.framework/public/_redirects +3 -0
  20. package/.framework/public/spine-logo.jpg +0 -0
  21. package/.framework/public/spine-logo.svg +3 -0
  22. package/{scripts → .framework/scripts}/assemble-frontend.sh +8 -5
  23. package/{scripts → .framework/scripts}/assemble-functions.sh +4 -47
  24. package/{scripts → .framework/scripts}/assemble.sh +2 -8
  25. package/{scripts → .framework/scripts}/boundary-check.sh +5 -9
  26. package/{scripts → .framework/scripts}/check-core-integrity.sh +0 -0
  27. package/.framework/seeds/link_types/account_cli_instances.json +9 -0
  28. package/.framework/seeds/types/funnel_signal_use.json +98 -0
  29. package/.framework/seeds/types/spine_instance.json +109 -0
  30. package/.framework/src/App.tsx +11 -21
  31. package/.framework/src/components/CustomAppLoader.tsx +2 -19
  32. package/.framework/src/components/ui/accordion.tsx +1 -1
  33. package/.framework/src/components/ui/alert-dialog.tsx +1 -1
  34. package/.framework/src/components/ui/aspect-ratio.tsx +1 -1
  35. package/.framework/src/components/ui/avatar.tsx +1 -1
  36. package/.framework/src/components/ui/badge.tsx +2 -2
  37. package/.framework/src/components/ui/breadcrumb.tsx +2 -2
  38. package/.framework/src/components/ui/button-group.tsx +2 -2
  39. package/.framework/src/components/ui/button.tsx +2 -2
  40. package/.framework/src/components/ui/chart.tsx +6 -1
  41. package/.framework/src/components/ui/checkbox.tsx +1 -1
  42. package/.framework/src/components/ui/collapsible.tsx +1 -1
  43. package/.framework/src/components/ui/context-menu.tsx +1 -1
  44. package/.framework/src/components/ui/dialog.tsx +1 -1
  45. package/.framework/src/components/ui/direction.tsx +17 -1
  46. package/.framework/src/components/ui/dropdown-menu.tsx +1 -1
  47. package/.framework/src/components/ui/hover-card.tsx +1 -1
  48. package/.framework/src/components/ui/item.tsx +2 -2
  49. package/.framework/src/components/ui/label.tsx +1 -1
  50. package/.framework/src/components/ui/menubar.tsx +1 -1
  51. package/.framework/src/components/ui/navigation-menu.tsx +1 -1
  52. package/.framework/src/components/ui/popover.tsx +1 -1
  53. package/.framework/src/components/ui/progress.tsx +1 -1
  54. package/.framework/src/components/ui/radio-group.tsx +1 -1
  55. package/.framework/src/components/ui/scroll-area.tsx +1 -1
  56. package/.framework/src/components/ui/select.tsx +1 -1
  57. package/.framework/src/components/ui/separator.tsx +1 -1
  58. package/.framework/src/components/ui/sheet.tsx +1 -1
  59. package/.framework/src/components/ui/sidebar.tsx +6 -6
  60. package/.framework/src/components/ui/slider.tsx +1 -1
  61. package/.framework/src/components/ui/switch.tsx +1 -1
  62. package/.framework/src/components/ui/tabs.tsx +1 -1
  63. package/.framework/src/components/ui/toggle-group.tsx +1 -1
  64. package/.framework/src/components/ui/toggle.tsx +1 -1
  65. package/.framework/src/components/ui/tooltip.tsx +1 -1
  66. package/.framework/src/pages/auth/LoginPage.tsx +10 -9
  67. package/LICENSE.md +63 -93
  68. package/README.md +254 -129
  69. package/custom/.devin/skills/install-app/SKILL.md +39 -0
  70. package/custom/.devin/workflows/app-install.yaml +83 -0
  71. package/custom/README.md +79 -0
  72. package/docs/APPS.md +232 -0
  73. package/docs/ARCHITECTURE.md +246 -0
  74. package/docs/INSTALL.md +155 -0
  75. package/docs/README.md +170 -0
  76. package/docs/design-schema-spec.md +1837 -0
  77. package/docs/dev-guides/01-create-an-app.md +183 -0
  78. package/docs/dev-guides/02-create-a-function.md +178 -0
  79. package/docs/dev-guides/03-create-a-component.md +178 -0
  80. package/docs/dev-guides/04-webhook-handlers.md +129 -0
  81. package/docs/dev-guides/05-testing.md +168 -0
  82. package/docs/dev-guides/README.md +40 -0
  83. package/docs/npm-packages-plan.md +288 -0
  84. package/docs/spine-core--api-reference.md +822 -0
  85. package/package.json +79 -150
  86. package/.framework/cli/bin.cjs +0 -14
  87. package/.framework/cli/commands/claim.ts +0 -430
  88. package/.framework/cli/commands/init.ts +0 -219
  89. package/.framework/cli/commands/install-app.ts +0 -685
  90. package/.framework/cli/commands/migrate.ts +0 -178
  91. package/.framework/cli/commands/publish.ts +0 -191
  92. package/.framework/cli/commands/status.ts +0 -268
  93. package/.framework/cli/commands/uninstall-app.ts +0 -166
  94. package/.framework/cli/commands/update-app.ts +0 -397
  95. package/.framework/cli/welcome.cjs +0 -45
  96. package/.framework/config/signal-client.ts +0 -147
  97. package/.framework/config/signals.ts +0 -70
  98. package/.framework/docs/STABILITY.md +0 -52
  99. package/.framework/docs/examples/custom-app/README.md +0 -77
  100. package/.framework/docs/examples/custom-function/README.md +0 -27
  101. package/.framework/docs/examples/custom-function/handler.ts +0 -48
  102. package/.framework/docs/examples/custom-webhook/README.md +0 -68
  103. package/.framework/functions/handler-registry.ts +0 -22
  104. package/.framework/functions/signal.ts +0 -168
  105. package/.framework/migrations/000_foundation.sql +0 -1302
  106. package/.framework/migrations/001_seed.sql +0 -163
  107. package/.framework/migrations/002_seed_constraints.sql +0 -19
  108. package/.framework/migrations/003_auth_user_trigger.sql +0 -67
  109. package/.framework/migrations/004_app_version_tracking.sql +0 -8
  110. package/.framework/migrations/005_pipelines_slug_stages.sql +0 -21
  111. package/.framework/src/pages/auth/RegisterPage.tsx +0 -163
  112. package/CHANGELOG.md +0 -71
  113. package/STRUCTURE.md +0 -150
  114. package/bin/spine-framework.cjs +0 -62
  115. package/bin/welcome.cjs +0 -58
  116. package/bin/ws-shim.ts +0 -10
  117. package/config/components.json +0 -25
  118. package/config/deno.lock +0 -108
  119. package/config/package-lock.json +0 -17183
  120. package/config/postcss.config.cjs +0 -10
  121. package/config/tailwind.config.cjs +0 -78
  122. package/config/tsconfig.build.json +0 -32
  123. package/config/tsconfig.cli.json +0 -18
  124. package/config/tsconfig.json +0 -41
  125. package/config/tsconfig.node.json +0 -17
  126. package/config/tsconfig.node.tsbuildinfo +0 -1
  127. package/config/tsconfig.tsbuildinfo +0 -1
  128. package/config/typedoc.json +0 -16
  129. package/config/vite.config.d.ts +0 -2
  130. package/config/vite.config.ts +0 -71
  131. package/dist/cli/commands/agents.d.ts +0 -39
  132. package/dist/cli/commands/agents.d.ts.map +0 -1
  133. package/dist/cli/commands/auth.d.ts +0 -36
  134. package/dist/cli/commands/auth.d.ts.map +0 -1
  135. package/dist/cli/commands/claim.d.ts +0 -38
  136. package/dist/cli/commands/claim.d.ts.map +0 -1
  137. package/dist/cli/commands/create-app.d.ts +0 -23
  138. package/dist/cli/commands/create-app.d.ts.map +0 -1
  139. package/dist/cli/commands/dev.d.ts +0 -24
  140. package/dist/cli/commands/dev.d.ts.map +0 -1
  141. package/dist/cli/commands/doctor.d.ts +0 -42
  142. package/dist/cli/commands/doctor.d.ts.map +0 -1
  143. package/dist/cli/commands/generate.d.ts +0 -36
  144. package/dist/cli/commands/generate.d.ts.map +0 -1
  145. package/dist/cli/commands/init.d.ts +0 -20
  146. package/dist/cli/commands/init.d.ts.map +0 -1
  147. package/dist/cli/commands/install-app.d.ts +0 -30
  148. package/dist/cli/commands/install-app.d.ts.map +0 -1
  149. package/dist/cli/commands/items.d.ts +0 -45
  150. package/dist/cli/commands/items.d.ts.map +0 -1
  151. package/dist/cli/commands/migrate.d.ts +0 -21
  152. package/dist/cli/commands/migrate.d.ts.map +0 -1
  153. package/dist/cli/commands/migrations.d.ts +0 -41
  154. package/dist/cli/commands/migrations.d.ts.map +0 -1
  155. package/dist/cli/commands/pipelines.d.ts +0 -40
  156. package/dist/cli/commands/pipelines.d.ts.map +0 -1
  157. package/dist/cli/commands/publish.d.ts +0 -23
  158. package/dist/cli/commands/publish.d.ts.map +0 -1
  159. package/dist/cli/commands/status.d.ts +0 -23
  160. package/dist/cli/commands/status.d.ts.map +0 -1
  161. package/dist/cli/commands/system.d.ts +0 -29
  162. package/dist/cli/commands/system.d.ts.map +0 -1
  163. package/dist/cli/commands/test.d.ts +0 -46
  164. package/dist/cli/commands/test.d.ts.map +0 -1
  165. package/dist/cli/commands/uninstall-app.d.ts +0 -23
  166. package/dist/cli/commands/uninstall-app.d.ts.map +0 -1
  167. package/dist/cli/commands/update-app.d.ts +0 -24
  168. package/dist/cli/commands/update-app.d.ts.map +0 -1
  169. package/dist/cli/context.d.ts +0 -88
  170. package/dist/cli/context.d.ts.map +0 -1
  171. package/dist/cli/env-loader.d.ts +0 -14
  172. package/dist/cli/env-loader.d.ts.map +0 -1
  173. package/dist/cli/index.d.ts +0 -44
  174. package/dist/cli/index.d.ts.map +0 -1
  175. package/dist/config/signals.d.ts +0 -82
  176. package/dist/config/signals.d.ts.map +0 -1
  177. package/dist/functions/_shared/agent-runner.d.ts +0 -156
  178. package/dist/functions/_shared/agent-runner.d.ts.map +0 -1
  179. package/dist/functions/_shared/app-manifest.d.ts +0 -68
  180. package/dist/functions/_shared/app-manifest.d.ts.map +0 -1
  181. package/dist/functions/_shared/audit.d.ts +0 -91
  182. package/dist/functions/_shared/audit.d.ts.map +0 -1
  183. package/dist/functions/_shared/db.d.ts +0 -125
  184. package/dist/functions/_shared/db.d.ts.map +0 -1
  185. package/dist/functions/_shared/index.d.ts +0 -299
  186. package/dist/functions/_shared/index.d.ts.map +0 -1
  187. package/dist/functions/_shared/middleware.d.ts +0 -315
  188. package/dist/functions/_shared/middleware.d.ts.map +0 -1
  189. package/dist/functions/_shared/permissions.d.ts +0 -626
  190. package/dist/functions/_shared/permissions.d.ts.map +0 -1
  191. package/dist/functions/_shared/pipeline-runner.d.ts +0 -124
  192. package/dist/functions/_shared/pipeline-runner.d.ts.map +0 -1
  193. package/dist/functions/_shared/principal.d.ts +0 -284
  194. package/dist/functions/_shared/principal.d.ts.map +0 -1
  195. package/dist/functions/_shared/resolve-ids.d.ts +0 -10
  196. package/dist/functions/_shared/resolve-ids.d.ts.map +0 -1
  197. package/dist/functions/_shared/schema-utils.d.ts +0 -181
  198. package/dist/functions/_shared/schema-utils.d.ts.map +0 -1
  199. package/dist/functions/_shared/testing.d.ts +0 -172
  200. package/dist/functions/_shared/testing.d.ts.map +0 -1
  201. package/dist/functions/_shared/trigger-engine.d.ts +0 -140
  202. package/dist/functions/_shared/trigger-engine.d.ts.map +0 -1
  203. package/dist/functions/_shared/webhook-registration.d.ts +0 -81
  204. package/dist/functions/_shared/webhook-registration.d.ts.map +0 -1
  205. package/dist/functions/_shared/webhook-registry.d.ts +0 -41
  206. package/dist/functions/_shared/webhook-registry.d.ts.map +0 -1
  207. package/dist/functions/account-nodes.d.ts +0 -48
  208. package/dist/functions/account-nodes.d.ts.map +0 -1
  209. package/dist/functions/admin-data.d.ts +0 -178
  210. package/dist/functions/admin-data.d.ts.map +0 -1
  211. package/dist/functions/ai-agents.d.ts +0 -125
  212. package/dist/functions/ai-agents.d.ts.map +0 -1
  213. package/dist/functions/api-keys.d.ts +0 -140
  214. package/dist/functions/api-keys.d.ts.map +0 -1
  215. package/dist/functions/apps.d.ts +0 -163
  216. package/dist/functions/apps.d.ts.map +0 -1
  217. package/dist/functions/auth.d.ts +0 -74
  218. package/dist/functions/auth.d.ts.map +0 -1
  219. package/dist/functions/debug-auth.d.ts +0 -33
  220. package/dist/functions/debug-auth.d.ts.map +0 -1
  221. package/dist/functions/embeddings.d.ts +0 -205
  222. package/dist/functions/embeddings.d.ts.map +0 -1
  223. package/dist/functions/handler-registry.d.ts +0 -22
  224. package/dist/functions/handler-registry.d.ts.map +0 -1
  225. package/dist/functions/integration-routes.d.ts +0 -45
  226. package/dist/functions/integration-routes.d.ts.map +0 -1
  227. package/dist/functions/integrations.d.ts +0 -124
  228. package/dist/functions/integrations.d.ts.map +0 -1
  229. package/dist/functions/item-progress.d.ts +0 -41
  230. package/dist/functions/item-progress.d.ts.map +0 -1
  231. package/dist/functions/logs.d.ts +0 -162
  232. package/dist/functions/logs.d.ts.map +0 -1
  233. package/dist/functions/observability.d.ts +0 -123
  234. package/dist/functions/observability.d.ts.map +0 -1
  235. package/dist/functions/pipeline-executions.d.ts +0 -190
  236. package/dist/functions/pipeline-executions.d.ts.map +0 -1
  237. package/dist/functions/pipelines.d.ts +0 -171
  238. package/dist/functions/pipelines.d.ts.map +0 -1
  239. package/dist/functions/prompt-configs.d.ts +0 -125
  240. package/dist/functions/prompt-configs.d.ts.map +0 -1
  241. package/dist/functions/roles.d.ts +0 -118
  242. package/dist/functions/roles.d.ts.map +0 -1
  243. package/dist/functions/signal.d.ts +0 -73
  244. package/dist/functions/signal.d.ts.map +0 -1
  245. package/dist/functions/system-cron.d.ts +0 -65
  246. package/dist/functions/system-cron.d.ts.map +0 -1
  247. package/dist/functions/system.d.ts +0 -29
  248. package/dist/functions/system.d.ts.map +0 -1
  249. package/dist/functions/tests.d.ts +0 -28
  250. package/dist/functions/tests.d.ts.map +0 -1
  251. package/dist/functions/timers.d.ts +0 -139
  252. package/dist/functions/timers.d.ts.map +0 -1
  253. package/dist/functions/triggers.d.ts +0 -203
  254. package/dist/functions/triggers.d.ts.map +0 -1
  255. package/dist/functions/types.d.ts +0 -151
  256. package/dist/functions/types.d.ts.map +0 -1
  257. package/dist/src/types/types.d.ts +0 -364
  258. package/dist/src/types/types.d.ts.map +0 -1
  259. package/index.html +0 -13
  260. package/netlify.toml +0 -36
  261. package/package-project.json +0 -71
  262. package/scripts/ingest-chunks.cjs +0 -202
  263. package/scripts/kb-chunk-parser.cjs +0 -312
  264. package/scripts/kb-chunk-parser.ts +0 -330
  265. package/scripts/load-test-app-install.ts +0 -484
  266. package/scripts/netlify-dev-wrapper.sh +0 -22
  267. package/vitest.config.ts +0 -45
  268. /package/{scripts → .framework/scripts}/app-install-cli.ts +0 -0
  269. /package/{scripts → .framework/scripts}/build-manifest.sh +0 -0
  270. /package/{scripts → .framework/scripts}/verify-integrity.sh +0 -0
@@ -1,178 +0,0 @@
1
- /// <reference types="node" />
2
- /**
3
- * @module cli/commands/migrate
4
- * @audience installer
5
- * @layer cli
6
- * @stability stable
7
- *
8
- * `spine-framework migrate` — Apply SQL migrations to a Supabase project
9
- * via a direct Postgres connection.
10
- *
11
- * Requires --db-password (the Supabase database password).
12
- * Get it from: https://supabase.com/dashboard/project/_/settings/database
13
- *
14
- * **Usage:**
15
- * ```bash
16
- * spine-framework migrate --db-password <password>
17
- * spine-framework migrate --db-password <password> --dry-run
18
- * ```
19
- */
20
-
21
- import type { Command } from 'commander'
22
- import { existsSync, readdirSync, readFileSync } from 'fs'
23
- import { resolve, dirname } from 'path'
24
- import { fileURLToPath } from 'url'
25
-
26
- const __filename = fileURLToPath(import.meta.url)
27
- const __dirname = dirname(__filename)
28
- const MIGRATIONS_DIR = resolve(__dirname, '../../migrations')
29
-
30
- interface MigrateOptions {
31
- dbPassword?: string
32
- dryRun: boolean
33
- }
34
-
35
- async function migrateCommand(options: MigrateOptions): Promise<void> {
36
- console.log('\n🗄️ Spine Framework — Migrate\n')
37
-
38
- const supabaseUrl = process.env.SUPABASE_URL
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')
49
- process.exit(1)
50
- }
51
-
52
- const projectRef = supabaseUrl.replace('https://', '').split('.')[0]
53
- const connectionString = `postgresql://postgres:${dbPassword}@db.${projectRef}.supabase.co:5432/postgres`
54
-
55
- if (!existsSync(MIGRATIONS_DIR)) {
56
- console.error(`❌ Migrations directory not found: ${MIGRATIONS_DIR}`)
57
- process.exit(1)
58
- }
59
-
60
- const files = readdirSync(MIGRATIONS_DIR)
61
- .filter(f => f.endsWith('.sql'))
62
- .sort()
63
-
64
- if (files.length === 0) {
65
- console.log(' No migration files found.')
66
- return
67
- }
68
-
69
- console.log(` Found ${files.length} migration file(s)\n`)
70
-
71
- if (options.dryRun) {
72
- for (const file of files) {
73
- console.log(` [dry-run] Would apply: ${file}`)
74
- }
75
- return
76
- }
77
-
78
- // Dynamically import pg to avoid loading it at module init time
79
- const { default: pg } = await import('pg')
80
- const client = new pg.Client({ connectionString })
81
-
82
- try {
83
- await client.connect()
84
- console.log(' ✓ Connected to database\n')
85
- } catch (err: any) {
86
- console.error(`❌ Could not connect to database: ${err.message}`)
87
- console.error(' Check your --db-password and that the project is active.')
88
- process.exit(1)
89
- }
90
-
91
- let applied = 0
92
- let failed = 0
93
-
94
- for (const file of files) {
95
- const sql = readFileSync(resolve(MIGRATIONS_DIR, file), 'utf8')
96
- process.stdout.write(` Applying ${file}... `)
97
-
98
- // Split SQL into statements, being careful to preserve dollar-quoted blocks.
99
- // Dollar-quoted strings ($$ ... $$ or $tag$ ... $tag$) can contain semicolons
100
- // that are NOT statement terminators.
101
- function splitSqlStatements(sql: string): string[] {
102
- const dollarBlocks: string[] = []
103
- let blockIndex = 0
104
-
105
- // Step 1: Protect dollar-quoted blocks with placeholders (preserves internal semicolons)
106
- const protectedSql = sql.replace(/\$[A-Za-z_]*\$[\s\S]*?\$[A-Za-z_]*\$/g, (match) => {
107
- const placeholder = `__DOLLAR_BLOCK_${blockIndex}__`
108
- dollarBlocks[blockIndex] = match
109
- blockIndex++
110
- return placeholder
111
- })
112
-
113
- // Step 2: Strip single-line comments (prevents semicolons inside -- comments
114
- // from being treated as statement terminators)
115
- const noComments = protectedSql.replace(/--[^\n]*/g, '')
116
-
117
- // Step 3: Split by semicolons, restore dollar blocks, and filter empty statements
118
- return noComments
119
- .split(';')
120
- .map(s => {
121
- let restored = s.trim()
122
- // Use function form of replace — prevents JS from interpreting $$ as $
123
- dollarBlocks.forEach((block, idx) => {
124
- restored = restored.replace(`__DOLLAR_BLOCK_${idx}__`, () => block)
125
- })
126
- return restored
127
- })
128
- .filter(s => s.length > 0)
129
- .map(s => s + ';')
130
- }
131
-
132
- const statements = splitSqlStatements(sql)
133
-
134
- try {
135
- for (const stmt of statements) {
136
- await client.query(stmt)
137
- }
138
- console.log('✓')
139
- applied++
140
- } catch (err: any) {
141
- console.log('❌')
142
- console.error(` ${err.message}`)
143
- failed++
144
- // Continue applying remaining migrations
145
- }
146
- }
147
-
148
- await client.end()
149
-
150
- console.log(`\n ${applied} applied, ${failed} failed`)
151
-
152
- if (failed > 0) {
153
- console.error('\n❌ Some migrations failed. Check errors above.')
154
- process.exit(1)
155
- }
156
-
157
- console.log('\n✅ All migrations applied successfully!')
158
- console.log('\n Next steps:')
159
- console.log(' 1. spine-framework install-app <app-slug>')
160
- console.log(' 2. npm run assemble && netlify dev')
161
- }
162
-
163
- export function registerMigrateCommands(program: Command) {
164
- program
165
- .command('migrate')
166
- .description('Apply SQL migrations via direct Postgres connection')
167
- .option('--db-password <password>', 'DB password override (default: reads SUPABASE_DB_PASSWORD from .env)')
168
- .option('--dry-run', 'Show what would happen without making changes', false)
169
- .action(async (opts) => {
170
- try {
171
- await migrateCommand(opts)
172
- } catch (err: any) {
173
- console.error('Error:', err.message)
174
- if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
175
- process.exit(1)
176
- }
177
- })
178
- }
@@ -1,191 +0,0 @@
1
- /// <reference types="node" />
2
- /**
3
- * @module cli/commands/publish
4
- * @audience developer
5
- * @layer cli
6
- * @stability stable
7
- *
8
- * `spine-framework publish` — Bump version, write changelog, publish to npm, commit.
9
- *
10
- * Run from the spine-ia coding repo. Handles both the framework package and
11
- * individual app packages (cortex, portal, etc.).
12
- *
13
- * **Usage:**
14
- * ```bash
15
- * spine-framework publish # publish framework (patch bump)
16
- * spine-framework publish --app cortex # publish cortex app (patch bump)
17
- * spine-framework publish --app cortex --bump minor
18
- * spine-framework publish --app cortex --bump patch --notes "Fix sidebar nav"
19
- * spine-framework publish --app cortex --ci --notes "Fix sidebar nav" --bump patch
20
- * ```
21
- */
22
-
23
- import type { Command } from 'commander'
24
- import { existsSync, readFileSync, writeFileSync } from 'fs'
25
- import { resolve, dirname } from 'path'
26
- import { fileURLToPath } from 'url'
27
- import { execSync } from 'child_process'
28
- import * as readline from 'readline'
29
-
30
- const __filename = fileURLToPath(import.meta.url)
31
- const __dirname = dirname(__filename)
32
- const PROJECT_ROOT = process.cwd()
33
-
34
- interface PublishOptions {
35
- app?: string
36
- bump: 'patch' | 'minor' | 'major'
37
- notes?: string
38
- ci: boolean
39
- dryRun: boolean
40
- }
41
-
42
- // ─── SEMVER BUMP ──────────────────────────────────────────────────────────────
43
-
44
- function bumpVersion(current: string, bump: 'patch' | 'minor' | 'major'): string {
45
- const [major, minor, patch] = current.replace(/^v/, '').split('.').map(Number)
46
- if (bump === 'major') return `${major + 1}.0.0`
47
- if (bump === 'minor') return `${major}.${minor + 1}.0`
48
- return `${major}.${minor}.${patch + 1}`
49
- }
50
-
51
- // ─── INTERACTIVE PROMPT ───────────────────────────────────────────────────────
52
-
53
- function promptNotes(): Promise<string> {
54
- return new Promise(resolve => {
55
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
56
- rl.question(' Changelog notes (comma-separated): ', answer => {
57
- rl.close()
58
- resolve(answer.trim())
59
- })
60
- })
61
- }
62
-
63
- // ─── PUBLISH LOGIC ────────────────────────────────────────────────────────────
64
-
65
- async function publishTarget(options: PublishOptions): Promise<void> {
66
- const isApp = !!options.app
67
- const slug = options.app
68
-
69
- let pkgJsonPath: string
70
- let manifestPath: string | null
71
- let publishDir: string
72
- let displayName: string
73
-
74
- if (isApp && slug) {
75
- pkgJsonPath = resolve(PROJECT_ROOT, `custom/apps/${slug}/package.json`)
76
- manifestPath = resolve(PROJECT_ROOT, `custom/apps/${slug}/manifest.json`)
77
- publishDir = resolve(PROJECT_ROOT, `custom/apps/${slug}`)
78
- displayName = `spine-framework-${slug}`
79
- } else {
80
- pkgJsonPath = resolve(PROJECT_ROOT, 'package.json')
81
- manifestPath = null
82
- publishDir = PROJECT_ROOT
83
- displayName = 'spine-framework'
84
- }
85
-
86
- if (!existsSync(pkgJsonPath)) {
87
- console.error(` ❌ package.json not found: ${pkgJsonPath}`)
88
- process.exit(1)
89
- }
90
-
91
- // 1. Read current version
92
- const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
93
- const currentVersion = pkgJson.version as string
94
- const newVersion = bumpVersion(currentVersion, options.bump)
95
-
96
- console.log(`\n📦 Publishing ${displayName}`)
97
- console.log(` ${currentVersion} → ${newVersion} (${options.bump} bump)\n`)
98
-
99
- // 2. Collect changelog notes
100
- let notesInput = options.notes || ''
101
- if (!notesInput) {
102
- if (options.ci) {
103
- console.error(` ❌ --ci mode requires --notes "..."`)
104
- process.exit(1)
105
- }
106
- notesInput = await promptNotes()
107
- }
108
- const notes = notesInput
109
- .split(',')
110
- .map(s => s.trim())
111
- .filter(Boolean)
112
-
113
- if (notes.length === 0) {
114
- console.error(` ❌ At least one changelog note is required.`)
115
- process.exit(1)
116
- }
117
-
118
- if (options.dryRun) {
119
- console.log(` [dry-run] Would bump ${displayName} ${currentVersion} → ${newVersion}`)
120
- console.log(` [dry-run] Changelog entry:`)
121
- console.log(` { "version": "${newVersion}", "notes": ${JSON.stringify(notes)} }`)
122
- console.log(` [dry-run] Would run: npm publish (from ${publishDir})`)
123
- console.log(` [dry-run] Would commit: chore: publish ${displayName}@${newVersion}`)
124
- return
125
- }
126
-
127
- // 3. Bump version in package.json
128
- pkgJson.version = newVersion
129
- writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n', 'utf8')
130
- console.log(` ✓ package.json version → ${newVersion}`)
131
-
132
- // 4. Prepend changelog entry to manifest.json (apps only)
133
- if (manifestPath && existsSync(manifestPath)) {
134
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
135
- const changelog: Array<{ version: string; notes: string[] }> = manifest.changelog || []
136
- changelog.unshift({ version: newVersion, notes })
137
- manifest.version = newVersion
138
- manifest.changelog = changelog
139
- writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf8')
140
- console.log(` ✓ manifest.json changelog prepended, version → ${newVersion}`)
141
- }
142
-
143
- // 5. npm publish
144
- console.log(`\n🚀 Running npm publish...`)
145
- try {
146
- execSync('npm publish --access public', { cwd: publishDir, stdio: 'inherit' })
147
- console.log(` ✓ Published ${displayName}@${newVersion}`)
148
- } catch (err: any) {
149
- console.error(` ❌ npm publish failed: ${err.message}`)
150
- console.error(` Version files have already been updated — commit them manually.`)
151
- process.exit(1)
152
- }
153
-
154
- // 6. Git commit
155
- console.log(`\n📝 Committing version bump...`)
156
- try {
157
- const filesToStage = [pkgJsonPath]
158
- if (manifestPath && existsSync(manifestPath)) filesToStage.push(manifestPath)
159
- execSync(`git add ${filesToStage.map(f => `"${f}"`).join(' ')}`, { cwd: PROJECT_ROOT })
160
- execSync(
161
- `git commit -m "chore: publish ${displayName}@${newVersion}"`,
162
- { cwd: PROJECT_ROOT, stdio: 'inherit' },
163
- )
164
- console.log(` ✓ Committed`)
165
- } catch (err: any) {
166
- console.error(` ⚠️ Git commit failed: ${err.message}`)
167
- console.error(` Published successfully — commit the version files manually.`)
168
- }
169
-
170
- console.log(`\n✅ ${displayName}@${newVersion} published successfully!\n`)
171
- }
172
-
173
- export function registerPublishCommands(program: Command) {
174
- program
175
- .command('publish')
176
- .description('Bump version, write changelog, publish to npm, commit (run from spine-ia)')
177
- .option('--app <slug>', 'App slug to publish (e.g. cortex). Omit to publish the framework itself.')
178
- .option('--bump <level>', 'Version bump level: patch | minor | major', 'patch')
179
- .option('--notes <text>', 'Comma-separated changelog notes (required in --ci mode)')
180
- .option('--ci', 'Non-interactive mode: skip prompts, requires --notes', false)
181
- .option('--dry-run', 'Show what would happen without making changes', false)
182
- .action(async (opts) => {
183
- try {
184
- await publishTarget(opts as PublishOptions)
185
- } catch (err: any) {
186
- console.error('Error:', err.message)
187
- if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
188
- process.exit(1)
189
- }
190
- })
191
- }
@@ -1,268 +0,0 @@
1
- /// <reference types="node" />
2
- /**
3
- * @module cli/commands/status
4
- * @audience installer
5
- * @layer cli
6
- * @stability stable
7
- *
8
- * `spine-framework status` — Show the current state of the Spine installation.
9
- *
10
- * Displays:
11
- * - Database connection status
12
- * - Installed apps and their seed state
13
- * - Migration history
14
- * - Webhook handler registrations
15
- *
16
- * **Usage:**
17
- * ```bash
18
- * spine-framework status
19
- * spine-framework status --json
20
- * ```
21
- */
22
-
23
- import type { Command } from 'commander'
24
- import { existsSync, readdirSync } from 'fs'
25
- import { resolve, dirname } from 'path'
26
- import { fileURLToPath } from 'url'
27
- import { adminDb } from '../../functions/_shared/index.ts'
28
- import { discoverManifests } from '../../functions/_shared/app-manifest.ts'
29
- import { readEngagementData, ensureSerialData, shouldOfferClaim } from './claim.ts'
30
-
31
- const __filename = fileURLToPath(import.meta.url)
32
- const __dirname = dirname(__filename)
33
- const PROJECT_ROOT = resolve(__dirname, '../../../')
34
-
35
- interface StatusOptions {
36
- json: boolean
37
- }
38
-
39
- interface StatusReport {
40
- database: { connected: boolean; error?: string }
41
- installation: {
42
- frameworkInstallSerial: string | null
43
- engagementScore: number
44
- claimStatus: 'anonymous' | 'claimed' | 'pending' | null
45
- claimPrompt: { timing: string; message: string } | null
46
- }
47
- apps: Array<{
48
- slug: string
49
- name: string
50
- hasManifest: boolean
51
- hasSeed: boolean
52
- installed: boolean
53
- installedVersion: string | null
54
- packageName: string | null
55
- appInstallSerial: string | null
56
- installTimestamp: string | null
57
- }>
58
- webhookHandlers: Array<{ name: string; functionName: string; isActive: boolean }>
59
- typeCounts: { total: number; byApp: Record<string, number> }
60
- }
61
-
62
- async function gatherStatus(): Promise<StatusReport> {
63
- // Get installation/engagement data
64
- let frameworkSerial: string | null = null
65
- let engagementScore = 0
66
- let claimStatus: 'anonymous' | 'claimed' | 'pending' | null = null
67
- let claimPrompt: { timing: string; message: string } | null = null
68
-
69
- try {
70
- const serialData = ensureSerialData()
71
- frameworkSerial = serialData.serial_number
72
- const engagement = readEngagementData()
73
- if (engagement) {
74
- engagementScore = engagement.engagement_score
75
- claimStatus = engagement.claim_status
76
- const prompt = shouldOfferClaim()
77
- if (prompt) {
78
- claimPrompt = { timing: prompt.timing, message: prompt.message }
79
- }
80
- }
81
- } catch {
82
- // Silent fail - engagement tracking is optional
83
- }
84
-
85
- const report: StatusReport = {
86
- database: { connected: false },
87
- installation: {
88
- frameworkInstallSerial: frameworkSerial,
89
- engagementScore,
90
- claimStatus,
91
- claimPrompt,
92
- },
93
- apps: [],
94
- webhookHandlers: [],
95
- typeCounts: { total: 0, byApp: {} },
96
- }
97
-
98
- // 1. Database connection
99
- try {
100
- const { data, error } = await adminDb
101
- .from('apps')
102
- .select('slug')
103
- .limit(1)
104
-
105
- if (error) {
106
- report.database = { connected: false, error: error.message }
107
- } else {
108
- report.database = { connected: true }
109
- }
110
- } catch (err: any) {
111
- report.database = { connected: false, error: err.message }
112
- return report
113
- }
114
-
115
- // 2. Apps — merge filesystem + database
116
- const manifests = discoverManifests()
117
- const { data: dbApps } = await adminDb
118
- .from('apps')
119
- .select('slug, name, is_active')
120
- .eq('is_active', true)
121
- .order('slug')
122
-
123
- const { data: installations } = await adminDb
124
- .from('app_installations')
125
- .select('app_slug, is_enabled, installed_version, package_name')
126
-
127
- const installedSlugs = new Set(
128
- (installations || []).filter(i => i.is_enabled).map(i => i.app_slug)
129
- )
130
-
131
- const installationMap = new Map(
132
- (installations || []).map(i => [i.app_slug, i])
133
- )
134
-
135
- const allSlugs = new Set([
136
- ...manifests.map(m => m.slug),
137
- ...(dbApps || []).map(a => a.slug).filter(s => s !== 'spine-core'),
138
- ])
139
-
140
- for (const slug of allSlugs) {
141
- const manifest = manifests.find(m => m.slug === slug)
142
- const dbApp = dbApps?.find(a => a.slug === slug)
143
- const seedDir = resolve(PROJECT_ROOT, `custom/apps/${slug}/seed`)
144
-
145
- const installation = installationMap.get(slug)
146
- report.apps.push({
147
- slug,
148
- name: dbApp?.name || slug,
149
- hasManifest: !!manifest,
150
- hasSeed: existsSync(seedDir) && readdirSync(seedDir).some(f => f.endsWith('.json')),
151
- installed: installedSlugs.has(slug),
152
- installedVersion: installation?.installed_version || null,
153
- packageName: installation?.package_name || null,
154
- appInstallSerial: null, // Populated via signal system
155
- installTimestamp: null, // Populated via signal system
156
- })
157
- }
158
-
159
- // 3. Webhook handlers
160
- const { data: handlers } = await adminDb
161
- .from('webhook_handlers')
162
- .select('name, function_name, is_active')
163
- .order('name')
164
-
165
- report.webhookHandlers = (handlers || []).map(h => ({
166
- name: h.name,
167
- functionName: h.function_name,
168
- isActive: h.is_active,
169
- }))
170
-
171
- // 4. Type counts by app
172
- const { data: types } = await adminDb
173
- .from('types')
174
- .select('slug, app_id, apps!inner(slug)')
175
- .eq('is_active', true)
176
-
177
- report.typeCounts.total = types?.length || 0
178
- for (const t of types || []) {
179
- const appSlug = (t as any).apps?.slug || 'unassigned'
180
- report.typeCounts.byApp[appSlug] = (report.typeCounts.byApp[appSlug] || 0) + 1
181
- }
182
-
183
- return report
184
- }
185
-
186
- function printStatus(report: StatusReport): void {
187
- console.log('\n🔍 Spine Framework Status\n')
188
-
189
- // Installation / Claim Status
190
- if (report.installation.frameworkInstallSerial) {
191
- console.log(` 🔐 Installation: ${report.installation.frameworkInstallSerial}`)
192
- if (report.installation.claimStatus === 'claimed') {
193
- console.log(' ✅ Claimed: Yes')
194
- } else {
195
- console.log(` ⏳ Claim Status: ${report.installation.claimStatus || 'anonymous'}`)
196
- console.log(` 📊 Engagement Score: ${report.installation.engagementScore}`)
197
- if (report.installation.claimPrompt) {
198
- console.log(` 💡 ${report.installation.claimPrompt.message}`)
199
- console.log(` Run: spine-framework claim`)
200
- }
201
- }
202
- console.log('')
203
- }
204
-
205
- // Database
206
- if (report.database.connected) {
207
- console.log(' 📡 Database: ✅ Connected')
208
- } else {
209
- console.log(` 📡 Database: ❌ ${report.database.error || 'Not connected'}`)
210
- return
211
- }
212
-
213
- // Apps
214
- console.log(`\n 📱 Apps (${report.apps.length}):`)
215
- if (report.apps.length === 0) {
216
- console.log(' (none)')
217
- } else {
218
- for (const app of report.apps) {
219
- const versionStr = app.installedVersion ? `v${app.installedVersion}` : ''
220
- const status = [
221
- app.hasManifest ? '📋 manifest' : '',
222
- app.hasSeed ? '🌱 seed' : '',
223
- app.installed ? `✅ installed${versionStr ? ' ' + versionStr : ''}` : '⬜ not installed',
224
- ].filter(Boolean).join(' ')
225
- console.log(` ${app.slug.padEnd(24)} ${status}`)
226
- }
227
- }
228
-
229
- // Webhook handlers
230
- console.log(`\n 🔗 Webhook Handlers (${report.webhookHandlers.length}):`)
231
- if (report.webhookHandlers.length === 0) {
232
- console.log(' (none)')
233
- } else {
234
- for (const h of report.webhookHandlers) {
235
- const status = h.isActive ? '✅' : '❌'
236
- console.log(` ${status} ${h.name.padEnd(24)} → ${h.functionName}`)
237
- }
238
- }
239
-
240
- // Types
241
- console.log(`\n 📊 Types (${report.typeCounts.total} total):`)
242
- for (const [app, count] of Object.entries(report.typeCounts.byApp).sort()) {
243
- console.log(` ${app.padEnd(24)} ${count} types`)
244
- }
245
-
246
- console.log('')
247
- }
248
-
249
- export function registerStatusCommands(program: Command) {
250
- program
251
- .command('status')
252
- .description('Show the current state of the Spine installation')
253
- .option('--json', 'Output as JSON', false)
254
- .action(async (opts: StatusOptions) => {
255
- try {
256
- const report = await gatherStatus()
257
- if (opts.json) {
258
- console.log(JSON.stringify(report, null, 2))
259
- } else {
260
- printStatus(report)
261
- }
262
- } catch (err: any) {
263
- console.error('Error:', err.message)
264
- if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
265
- process.exit(1)
266
- }
267
- })
268
- }