spine-framework 0.1.60 → 0.2.0

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 (246) hide show
  1. package/.framework/cli/commands/create-app.ts +1 -1
  2. package/.framework/cli/commands/dev.ts +215 -33
  3. package/.framework/cli/index.ts +37 -63
  4. package/.framework/functions/_shared/app-manifest.ts +8 -15
  5. package/.framework/functions/_shared/db.ts +11 -15
  6. package/.framework/functions/_shared/index.ts +0 -9
  7. package/.framework/functions/_shared/principal.ts +4 -62
  8. package/.framework/functions/_shared/resolve-ids.ts +6 -6
  9. package/.framework/functions/_shared/webhook-registry.ts +1 -1
  10. package/.framework/functions/admin-funnel.ts +104 -0
  11. package/.framework/functions/admin-install-funnel.ts +225 -0
  12. package/.framework/functions/admin-signals.ts +241 -0
  13. package/.framework/migrations/001_schema.sql +406 -0
  14. package/.framework/migrations/002_rls_and_functions.sql +710 -0
  15. package/.framework/migrations/003_system_data.sql +520 -0
  16. package/.framework/migrations/004_enhancements.sql +43 -0
  17. package/.framework/public/_redirects +3 -0
  18. package/.framework/public/spine-logo.jpg +0 -0
  19. package/.framework/public/spine-logo.svg +3 -0
  20. package/{scripts → .framework/scripts}/assemble-frontend.sh +1 -1
  21. package/{scripts → .framework/scripts}/assemble-functions.sh +1 -1
  22. package/{scripts → .framework/scripts}/assemble.sh +2 -8
  23. package/{scripts → .framework/scripts}/check-core-integrity.sh +0 -0
  24. package/.framework/seeds/link_types/account_cli_instances.json +9 -0
  25. package/.framework/seeds/types/funnel_signal_use.json +98 -0
  26. package/.framework/seeds/types/spine_instance.json +109 -0
  27. package/.framework/src/App.tsx +6 -4
  28. package/.framework/src/components/ui/accordion.tsx +1 -1
  29. package/.framework/src/components/ui/alert-dialog.tsx +1 -1
  30. package/.framework/src/components/ui/aspect-ratio.tsx +1 -1
  31. package/.framework/src/components/ui/avatar.tsx +1 -1
  32. package/.framework/src/components/ui/badge.tsx +2 -2
  33. package/.framework/src/components/ui/breadcrumb.tsx +2 -2
  34. package/.framework/src/components/ui/button-group.tsx +2 -2
  35. package/.framework/src/components/ui/button.tsx +2 -2
  36. package/.framework/src/components/ui/chart.tsx +6 -1
  37. package/.framework/src/components/ui/checkbox.tsx +1 -1
  38. package/.framework/src/components/ui/collapsible.tsx +1 -1
  39. package/.framework/src/components/ui/context-menu.tsx +1 -1
  40. package/.framework/src/components/ui/dialog.tsx +1 -1
  41. package/.framework/src/components/ui/direction.tsx +17 -1
  42. package/.framework/src/components/ui/dropdown-menu.tsx +1 -1
  43. package/.framework/src/components/ui/hover-card.tsx +1 -1
  44. package/.framework/src/components/ui/item.tsx +2 -2
  45. package/.framework/src/components/ui/label.tsx +1 -1
  46. package/.framework/src/components/ui/menubar.tsx +1 -1
  47. package/.framework/src/components/ui/navigation-menu.tsx +1 -1
  48. package/.framework/src/components/ui/popover.tsx +1 -1
  49. package/.framework/src/components/ui/progress.tsx +1 -1
  50. package/.framework/src/components/ui/radio-group.tsx +1 -1
  51. package/.framework/src/components/ui/scroll-area.tsx +1 -1
  52. package/.framework/src/components/ui/select.tsx +1 -1
  53. package/.framework/src/components/ui/separator.tsx +1 -1
  54. package/.framework/src/components/ui/sheet.tsx +1 -1
  55. package/.framework/src/components/ui/sidebar.tsx +6 -6
  56. package/.framework/src/components/ui/slider.tsx +1 -1
  57. package/.framework/src/components/ui/switch.tsx +1 -1
  58. package/.framework/src/components/ui/tabs.tsx +1 -1
  59. package/.framework/src/components/ui/toggle-group.tsx +1 -1
  60. package/.framework/src/components/ui/toggle.tsx +1 -1
  61. package/.framework/src/components/ui/tooltip.tsx +1 -1
  62. package/.framework/src/pages/auth/LoginPage.tsx +10 -9
  63. package/LICENSE.md +193 -0
  64. package/README.md +254 -0
  65. package/custom/.devin/skills/install-app/SKILL.md +39 -0
  66. package/custom/.devin/workflows/app-install.yaml +83 -0
  67. package/custom/README.md +79 -0
  68. package/docs/APPS.md +232 -0
  69. package/docs/ARCHITECTURE.md +246 -0
  70. package/docs/INSTALL.md +155 -0
  71. package/docs/README.md +170 -0
  72. package/docs/design-schema-spec.md +1837 -0
  73. package/docs/dev-guides/01-create-an-app.md +183 -0
  74. package/docs/dev-guides/02-create-a-function.md +178 -0
  75. package/docs/dev-guides/03-create-a-component.md +178 -0
  76. package/docs/dev-guides/04-webhook-handlers.md +129 -0
  77. package/docs/dev-guides/05-testing.md +168 -0
  78. package/docs/dev-guides/README.md +40 -0
  79. package/docs/npm-packages-plan.md +288 -0
  80. package/docs/spine-core--api-reference.md +822 -0
  81. package/package.json +78 -144
  82. package/.framework/cli/bin.cjs +0 -14
  83. package/.framework/cli/commands/init.ts +0 -186
  84. package/.framework/cli/commands/install-app.ts +0 -560
  85. package/.framework/cli/commands/migrate.ts +0 -139
  86. package/.framework/cli/commands/status.ts +0 -197
  87. package/.framework/cli/commands/uninstall-app.ts +0 -166
  88. package/.framework/cli/welcome.cjs +0 -45
  89. package/.framework/docs/STABILITY.md +0 -52
  90. package/.framework/docs/examples/custom-app/README.md +0 -77
  91. package/.framework/docs/examples/custom-function/README.md +0 -27
  92. package/.framework/docs/examples/custom-function/handler.ts +0 -48
  93. package/.framework/docs/examples/custom-webhook/README.md +0 -68
  94. package/.framework/migrations/000_foundation.sql +0 -1265
  95. package/.framework/migrations/001_seed.sql +0 -163
  96. package/.framework/migrations/002_seed_constraints.sql +0 -19
  97. package/.framework/migrations/003_auth_user_trigger.sql +0 -67
  98. package/.framework/src/pages/auth/RegisterPage.tsx +0 -163
  99. package/STRUCTURE.md +0 -150
  100. package/bin/spine-framework.cjs +0 -62
  101. package/bin/welcome.cjs +0 -45
  102. package/bin/ws-shim.cjs +0 -8
  103. package/bin/ws-shim.ts +0 -10
  104. package/config/components.json +0 -25
  105. package/config/deno.lock +0 -108
  106. package/config/package-lock.json +0 -17183
  107. package/config/postcss.config.cjs +0 -10
  108. package/config/tailwind.config.cjs +0 -78
  109. package/config/tsconfig.build.json +0 -32
  110. package/config/tsconfig.cli.json +0 -18
  111. package/config/tsconfig.json +0 -41
  112. package/config/tsconfig.node.json +0 -17
  113. package/config/tsconfig.node.tsbuildinfo +0 -1
  114. package/config/tsconfig.tsbuildinfo +0 -1
  115. package/config/typedoc.json +0 -16
  116. package/config/vite.config.d.ts +0 -2
  117. package/config/vite.config.ts +0 -72
  118. package/dist/cli/commands/agents.d.ts +0 -39
  119. package/dist/cli/commands/agents.d.ts.map +0 -1
  120. package/dist/cli/commands/auth.d.ts +0 -36
  121. package/dist/cli/commands/auth.d.ts.map +0 -1
  122. package/dist/cli/commands/create-app.d.ts +0 -23
  123. package/dist/cli/commands/create-app.d.ts.map +0 -1
  124. package/dist/cli/commands/dev.d.ts +0 -24
  125. package/dist/cli/commands/dev.d.ts.map +0 -1
  126. package/dist/cli/commands/doctor.d.ts +0 -42
  127. package/dist/cli/commands/doctor.d.ts.map +0 -1
  128. package/dist/cli/commands/generate.d.ts +0 -36
  129. package/dist/cli/commands/generate.d.ts.map +0 -1
  130. package/dist/cli/commands/init.d.ts +0 -20
  131. package/dist/cli/commands/init.d.ts.map +0 -1
  132. package/dist/cli/commands/install-app.d.ts +0 -30
  133. package/dist/cli/commands/install-app.d.ts.map +0 -1
  134. package/dist/cli/commands/items.d.ts +0 -45
  135. package/dist/cli/commands/items.d.ts.map +0 -1
  136. package/dist/cli/commands/migrate.d.ts +0 -21
  137. package/dist/cli/commands/migrate.d.ts.map +0 -1
  138. package/dist/cli/commands/migrations.d.ts +0 -41
  139. package/dist/cli/commands/migrations.d.ts.map +0 -1
  140. package/dist/cli/commands/pipelines.d.ts +0 -40
  141. package/dist/cli/commands/pipelines.d.ts.map +0 -1
  142. package/dist/cli/commands/status.d.ts +0 -23
  143. package/dist/cli/commands/status.d.ts.map +0 -1
  144. package/dist/cli/commands/system.d.ts +0 -29
  145. package/dist/cli/commands/system.d.ts.map +0 -1
  146. package/dist/cli/commands/test.d.ts +0 -46
  147. package/dist/cli/commands/test.d.ts.map +0 -1
  148. package/dist/cli/commands/uninstall-app.d.ts +0 -23
  149. package/dist/cli/commands/uninstall-app.d.ts.map +0 -1
  150. package/dist/cli/context.d.ts +0 -88
  151. package/dist/cli/context.d.ts.map +0 -1
  152. package/dist/cli/env-loader.d.ts +0 -14
  153. package/dist/cli/env-loader.d.ts.map +0 -1
  154. package/dist/cli/index.d.ts +0 -41
  155. package/dist/cli/index.d.ts.map +0 -1
  156. package/dist/functions/_shared/agent-runner.d.ts +0 -156
  157. package/dist/functions/_shared/agent-runner.d.ts.map +0 -1
  158. package/dist/functions/_shared/app-manifest.d.ts +0 -68
  159. package/dist/functions/_shared/app-manifest.d.ts.map +0 -1
  160. package/dist/functions/_shared/audit.d.ts +0 -91
  161. package/dist/functions/_shared/audit.d.ts.map +0 -1
  162. package/dist/functions/_shared/db.d.ts +0 -125
  163. package/dist/functions/_shared/db.d.ts.map +0 -1
  164. package/dist/functions/_shared/index.d.ts +0 -299
  165. package/dist/functions/_shared/index.d.ts.map +0 -1
  166. package/dist/functions/_shared/middleware.d.ts +0 -315
  167. package/dist/functions/_shared/middleware.d.ts.map +0 -1
  168. package/dist/functions/_shared/permissions.d.ts +0 -626
  169. package/dist/functions/_shared/permissions.d.ts.map +0 -1
  170. package/dist/functions/_shared/pipeline-runner.d.ts +0 -124
  171. package/dist/functions/_shared/pipeline-runner.d.ts.map +0 -1
  172. package/dist/functions/_shared/principal.d.ts +0 -284
  173. package/dist/functions/_shared/principal.d.ts.map +0 -1
  174. package/dist/functions/_shared/resolve-ids.d.ts +0 -10
  175. package/dist/functions/_shared/resolve-ids.d.ts.map +0 -1
  176. package/dist/functions/_shared/schema-utils.d.ts +0 -181
  177. package/dist/functions/_shared/schema-utils.d.ts.map +0 -1
  178. package/dist/functions/_shared/testing.d.ts +0 -172
  179. package/dist/functions/_shared/testing.d.ts.map +0 -1
  180. package/dist/functions/_shared/trigger-engine.d.ts +0 -140
  181. package/dist/functions/_shared/trigger-engine.d.ts.map +0 -1
  182. package/dist/functions/_shared/webhook-registration.d.ts +0 -81
  183. package/dist/functions/_shared/webhook-registration.d.ts.map +0 -1
  184. package/dist/functions/_shared/webhook-registry.d.ts +0 -57
  185. package/dist/functions/_shared/webhook-registry.d.ts.map +0 -1
  186. package/dist/functions/account-nodes.d.ts +0 -48
  187. package/dist/functions/account-nodes.d.ts.map +0 -1
  188. package/dist/functions/admin-data.d.ts +0 -178
  189. package/dist/functions/admin-data.d.ts.map +0 -1
  190. package/dist/functions/ai-agents.d.ts +0 -125
  191. package/dist/functions/ai-agents.d.ts.map +0 -1
  192. package/dist/functions/api-keys.d.ts +0 -140
  193. package/dist/functions/api-keys.d.ts.map +0 -1
  194. package/dist/functions/apps.d.ts +0 -163
  195. package/dist/functions/apps.d.ts.map +0 -1
  196. package/dist/functions/auth.d.ts +0 -74
  197. package/dist/functions/auth.d.ts.map +0 -1
  198. package/dist/functions/debug-auth.d.ts +0 -33
  199. package/dist/functions/debug-auth.d.ts.map +0 -1
  200. package/dist/functions/embeddings.d.ts +0 -205
  201. package/dist/functions/embeddings.d.ts.map +0 -1
  202. package/dist/functions/integration-routes.d.ts +0 -45
  203. package/dist/functions/integration-routes.d.ts.map +0 -1
  204. package/dist/functions/integrations.d.ts +0 -124
  205. package/dist/functions/integrations.d.ts.map +0 -1
  206. package/dist/functions/item-progress.d.ts +0 -41
  207. package/dist/functions/item-progress.d.ts.map +0 -1
  208. package/dist/functions/logs.d.ts +0 -162
  209. package/dist/functions/logs.d.ts.map +0 -1
  210. package/dist/functions/observability.d.ts +0 -123
  211. package/dist/functions/observability.d.ts.map +0 -1
  212. package/dist/functions/pipeline-executions.d.ts +0 -190
  213. package/dist/functions/pipeline-executions.d.ts.map +0 -1
  214. package/dist/functions/pipelines.d.ts +0 -171
  215. package/dist/functions/pipelines.d.ts.map +0 -1
  216. package/dist/functions/prompt-configs.d.ts +0 -125
  217. package/dist/functions/prompt-configs.d.ts.map +0 -1
  218. package/dist/functions/roles.d.ts +0 -118
  219. package/dist/functions/roles.d.ts.map +0 -1
  220. package/dist/functions/system-cron.d.ts +0 -65
  221. package/dist/functions/system-cron.d.ts.map +0 -1
  222. package/dist/functions/system.d.ts +0 -29
  223. package/dist/functions/system.d.ts.map +0 -1
  224. package/dist/functions/tests.d.ts +0 -28
  225. package/dist/functions/tests.d.ts.map +0 -1
  226. package/dist/functions/timers.d.ts +0 -139
  227. package/dist/functions/timers.d.ts.map +0 -1
  228. package/dist/functions/triggers.d.ts +0 -203
  229. package/dist/functions/triggers.d.ts.map +0 -1
  230. package/dist/functions/types.d.ts +0 -151
  231. package/dist/functions/types.d.ts.map +0 -1
  232. package/dist/src/types/types.d.ts +0 -364
  233. package/dist/src/types/types.d.ts.map +0 -1
  234. package/index.html +0 -13
  235. package/netlify.toml +0 -36
  236. package/package-project.json +0 -64
  237. package/scripts/ingest-chunks.cjs +0 -202
  238. package/scripts/kb-chunk-parser.cjs +0 -312
  239. package/scripts/kb-chunk-parser.ts +0 -330
  240. package/scripts/load-test-app-install.ts +0 -484
  241. package/scripts/netlify-dev-wrapper.sh +0 -22
  242. package/vitest.config.ts +0 -45
  243. /package/{scripts → .framework/scripts}/app-install-cli.ts +0 -0
  244. /package/{scripts → .framework/scripts}/boundary-check.sh +0 -0
  245. /package/{scripts → .framework/scripts}/build-manifest.sh +0 -0
  246. /package/{scripts → .framework/scripts}/verify-integrity.sh +0 -0
@@ -1,197 +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
-
30
- const __filename = fileURLToPath(import.meta.url)
31
- const __dirname = dirname(__filename)
32
- const PROJECT_ROOT = resolve(__dirname, '../../../')
33
-
34
- interface StatusOptions {
35
- json: boolean
36
- }
37
-
38
- interface StatusReport {
39
- database: { connected: boolean; error?: string }
40
- apps: Array<{ slug: string; name: string; hasManifest: boolean; hasSeed: boolean; installed: boolean }>
41
- webhookHandlers: Array<{ name: string; functionName: string; isActive: boolean }>
42
- typeCounts: { total: number; byApp: Record<string, number> }
43
- }
44
-
45
- async function gatherStatus(): Promise<StatusReport> {
46
- const report: StatusReport = {
47
- database: { connected: false },
48
- apps: [],
49
- webhookHandlers: [],
50
- typeCounts: { total: 0, byApp: {} },
51
- }
52
-
53
- // 1. Database connection
54
- try {
55
- const { data, error } = await adminDb
56
- .from('apps')
57
- .select('slug')
58
- .limit(1)
59
-
60
- if (error) {
61
- report.database = { connected: false, error: error.message }
62
- } else {
63
- report.database = { connected: true }
64
- }
65
- } catch (err: any) {
66
- report.database = { connected: false, error: err.message }
67
- return report
68
- }
69
-
70
- // 2. Apps — merge filesystem + database
71
- const manifests = discoverManifests()
72
- const { data: dbApps } = await adminDb
73
- .from('apps')
74
- .select('slug, name, is_active')
75
- .eq('is_active', true)
76
- .order('slug')
77
-
78
- const { data: installations } = await adminDb
79
- .from('app_installations')
80
- .select('app_slug, is_enabled')
81
-
82
- const installedSlugs = new Set(
83
- (installations || []).filter(i => i.is_enabled).map(i => i.app_slug)
84
- )
85
-
86
- const allSlugs = new Set([
87
- ...manifests.map(m => m.slug),
88
- ...(dbApps || []).map(a => a.slug).filter(s => s !== 'spine-core'),
89
- ])
90
-
91
- for (const slug of allSlugs) {
92
- const manifest = manifests.find(m => m.slug === slug)
93
- const dbApp = dbApps?.find(a => a.slug === slug)
94
- const seedDir = resolve(PROJECT_ROOT, `custom/apps/${slug}/seed`)
95
-
96
- report.apps.push({
97
- slug,
98
- name: dbApp?.name || slug,
99
- hasManifest: !!manifest,
100
- hasSeed: existsSync(seedDir) && readdirSync(seedDir).some(f => f.endsWith('.json')),
101
- installed: installedSlugs.has(slug),
102
- })
103
- }
104
-
105
- // 3. Webhook handlers
106
- const { data: handlers } = await adminDb
107
- .from('webhook_handlers')
108
- .select('name, function_name, is_active')
109
- .order('name')
110
-
111
- report.webhookHandlers = (handlers || []).map(h => ({
112
- name: h.name,
113
- functionName: h.function_name,
114
- isActive: h.is_active,
115
- }))
116
-
117
- // 4. Type counts by app
118
- const { data: types } = await adminDb
119
- .from('types')
120
- .select('slug, app_id, apps!inner(slug)')
121
- .eq('is_active', true)
122
-
123
- report.typeCounts.total = types?.length || 0
124
- for (const t of types || []) {
125
- const appSlug = (t as any).apps?.slug || 'unassigned'
126
- report.typeCounts.byApp[appSlug] = (report.typeCounts.byApp[appSlug] || 0) + 1
127
- }
128
-
129
- return report
130
- }
131
-
132
- function printStatus(report: StatusReport): void {
133
- console.log('\n🔍 Spine Framework Status\n')
134
-
135
- // Database
136
- if (report.database.connected) {
137
- console.log(' 📡 Database: ✅ Connected')
138
- } else {
139
- console.log(` 📡 Database: ❌ ${report.database.error || 'Not connected'}`)
140
- return
141
- }
142
-
143
- // Apps
144
- console.log(`\n 📱 Apps (${report.apps.length}):`)
145
- if (report.apps.length === 0) {
146
- console.log(' (none)')
147
- } else {
148
- for (const app of report.apps) {
149
- const status = [
150
- app.hasManifest ? '📋 manifest' : '',
151
- app.hasSeed ? '🌱 seed' : '',
152
- app.installed ? '✅ installed' : '⬜ not installed',
153
- ].filter(Boolean).join(' ')
154
- console.log(` ${app.slug.padEnd(24)} ${status}`)
155
- }
156
- }
157
-
158
- // Webhook handlers
159
- console.log(`\n 🔗 Webhook Handlers (${report.webhookHandlers.length}):`)
160
- if (report.webhookHandlers.length === 0) {
161
- console.log(' (none)')
162
- } else {
163
- for (const h of report.webhookHandlers) {
164
- const status = h.isActive ? '✅' : '❌'
165
- console.log(` ${status} ${h.name.padEnd(24)} → ${h.functionName}`)
166
- }
167
- }
168
-
169
- // Types
170
- console.log(`\n 📊 Types (${report.typeCounts.total} total):`)
171
- for (const [app, count] of Object.entries(report.typeCounts.byApp).sort()) {
172
- console.log(` ${app.padEnd(24)} ${count} types`)
173
- }
174
-
175
- console.log('')
176
- }
177
-
178
- export function registerStatusCommands(program: Command) {
179
- program
180
- .command('status')
181
- .description('Show the current state of the Spine installation')
182
- .option('--json', 'Output as JSON', false)
183
- .action(async (opts: StatusOptions) => {
184
- try {
185
- const report = await gatherStatus()
186
- if (opts.json) {
187
- console.log(JSON.stringify(report, null, 2))
188
- } else {
189
- printStatus(report)
190
- }
191
- } catch (err: any) {
192
- console.error('Error:', err.message)
193
- if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
194
- process.exit(1)
195
- }
196
- })
197
- }
@@ -1,166 +0,0 @@
1
- /// <reference types="node" />
2
- /**
3
- * @module cli/commands/uninstall-app
4
- * @audience installer
5
- * @layer cli
6
- * @stability stable
7
- *
8
- * `spine-framework uninstall-app <slug>` — Soft-uninstall an app.
9
- *
10
- * Deactivates the app's types, link types, and triggers in the database.
11
- * Does NOT delete data (items, threads, etc.) — those remain for audit.
12
- * Does NOT remove filesystem files — the app code stays in custom/apps/.
13
- *
14
- * To fully remove: manually delete the app directory after uninstall.
15
- *
16
- * **Usage:**
17
- * ```bash
18
- * spine-framework uninstall-app cortex
19
- * spine-framework uninstall-app cortex --hard # Also deactivate items
20
- * ```
21
- */
22
-
23
- import type { Command } from 'commander'
24
- import { adminDb } from '../../functions/_shared/index.ts'
25
-
26
- interface UninstallOptions {
27
- hard: boolean
28
- dryRun: boolean
29
- }
30
-
31
- async function uninstallApp(slug: string, options: UninstallOptions): Promise<void> {
32
- console.log(`\n🗑️ Uninstalling app '${slug}'...\n`)
33
-
34
- // 1. Find the app
35
- const { data: app, error: appErr } = await adminDb
36
- .from('apps')
37
- .select('id, name')
38
- .eq('slug', slug)
39
- .single()
40
-
41
- if (appErr || !app) {
42
- console.error(` ❌ App '${slug}' not found in database`)
43
- process.exit(1)
44
- }
45
-
46
- const appId = app.id
47
-
48
- // 2. Deactivate types
49
- if (options.dryRun) {
50
- const { data: types } = await adminDb
51
- .from('types').select('slug').eq('app_id', appId).eq('is_active', true)
52
- console.log(` [dry-run] Would deactivate ${types?.length || 0} types`)
53
- } else {
54
- const { data: updated } = await adminDb
55
- .from('types')
56
- .update({ is_active: false })
57
- .eq('app_id', appId)
58
- .eq('is_active', true)
59
- .select('id')
60
-
61
- console.log(` ✓ Deactivated ${updated?.length || 0} types`)
62
- }
63
-
64
- // 3. Deactivate link types
65
- if (options.dryRun) {
66
- const { data: lt } = await adminDb
67
- .from('link_types').select('slug').eq('app_id', appId).eq('is_active', true)
68
- console.log(` [dry-run] Would deactivate ${lt?.length || 0} link types`)
69
- } else {
70
- const { data: updated } = await adminDb
71
- .from('link_types')
72
- .update({ is_active: false })
73
- .eq('app_id', appId)
74
- .eq('is_active', true)
75
- .select('id')
76
-
77
- console.log(` ✓ Deactivated ${updated?.length || 0} link types`)
78
- }
79
-
80
- // 4. Deactivate triggers
81
- if (options.dryRun) {
82
- const { data: triggers } = await adminDb
83
- .from('triggers').select('name').eq('app_id', appId).eq('is_active', true)
84
- console.log(` [dry-run] Would deactivate ${triggers?.length || 0} triggers`)
85
- } else {
86
- const { data: updated } = await adminDb
87
- .from('triggers')
88
- .update({ is_active: false })
89
- .eq('app_id', appId)
90
- .eq('is_active', true)
91
- .select('id')
92
-
93
- console.log(` ✓ Deactivated ${updated?.length || 0} triggers`)
94
- }
95
-
96
- // 5. Hard mode: deactivate items of this app's types
97
- if (options.hard) {
98
- if (options.dryRun) {
99
- const { data: types } = await adminDb
100
- .from('types').select('id').eq('app_id', appId)
101
- const typeIds = types?.map(t => t.id) || []
102
- if (typeIds.length > 0) {
103
- const { data: items } = await adminDb
104
- .from('items').select('id').in('type_id', typeIds).eq('is_active', true)
105
- console.log(` [dry-run] Would deactivate ${items?.length || 0} items (--hard)`)
106
- }
107
- } else {
108
- const { data: types } = await adminDb
109
- .from('types').select('id').eq('app_id', appId)
110
- const typeIds = types?.map(t => t.id) || []
111
-
112
- if (typeIds.length > 0) {
113
- const { data: updated } = await adminDb
114
- .from('items')
115
- .update({ is_active: false })
116
- .in('type_id', typeIds)
117
- .eq('is_active', true)
118
- .select('id')
119
-
120
- console.log(` ✓ Deactivated ${updated?.length || 0} items (--hard)`)
121
- }
122
- }
123
- }
124
-
125
- // 6. Mark installation as disabled
126
- if (!options.dryRun) {
127
- await adminDb
128
- .from('app_installations')
129
- .update({ is_enabled: false, updated_at: new Date().toISOString() })
130
- .eq('app_slug', slug)
131
- }
132
-
133
- // 7. Deactivate app record
134
- if (!options.dryRun) {
135
- await adminDb
136
- .from('apps')
137
- .update({ is_active: false })
138
- .eq('id', appId)
139
-
140
- console.log(` ✓ App '${slug}' deactivated`)
141
- }
142
-
143
- console.log(`\n✅ App '${app.name}' uninstalled (soft-delete)`)
144
- if (!options.hard) {
145
- console.log(` Items created by this app are preserved.`)
146
- console.log(` Use --hard to also deactivate items.`)
147
- }
148
- console.log(` App files remain in custom/apps/${slug}/ — delete manually if desired.`)
149
- }
150
-
151
- export function registerUninstallAppCommands(program: Command) {
152
- program
153
- .command('uninstall-app <slug>')
154
- .description('Soft-uninstall an app (deactivate types, triggers, link types)')
155
- .option('--hard', 'Also deactivate items created by this app', false)
156
- .option('--dry-run', 'Show what would happen without making changes', false)
157
- .action(async (slug, opts) => {
158
- try {
159
- await uninstallApp(slug, opts)
160
- } catch (err: any) {
161
- console.error('Error:', err.message)
162
- if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
163
- process.exit(1)
164
- }
165
- })
166
- }
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env node
2
- // Postinstall welcome message — runs automatically after npm install spine-framework.
3
- // Tells the agent (or developer) exactly what to do next.
4
- // Uses only Node built-ins — no dependencies.
5
-
6
- const lines = [
7
- '',
8
- '╭──────────────────────────────────────────────────────────────╮',
9
- '│ │',
10
- '│ ███████╗██████╗ ██╗███╗ ██╗███████╗ │',
11
- '│ ██╔════╝██╔══██╗██║████╗ ██║██╔════╝ │',
12
- '│ ███████╗██████╔╝██║██╔██╗ ██║█████╗ │',
13
- '│ ╚════██║██╔═══╝ ██║██║╚██╗██║██╔══╝ │',
14
- '│ ███████║██║ ██║██║ ╚████║███████╗ │',
15
- '│ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝ Framework v0.1 │',
16
- '│ │',
17
- '╰──────────────────────────────────────────────────────────────╯',
18
- '',
19
- ' Spine Framework installed successfully!',
20
- '',
21
- ' To complete setup you need a Supabase project.',
22
- ' ─────────────────────────────────────────────',
23
- ' If you have one already, run:',
24
- '',
25
- ' spine-framework init \\',
26
- ' --url https://<project>.supabase.co \\',
27
- ' --anon-key <anon-key> \\',
28
- ' --service-role-key <service-role-key>',
29
- '',
30
- ' Your credentials are at:',
31
- ' Supabase Dashboard → Your Project → Settings → API',
32
- '',
33
- ' ─────────────────────────────────────────────',
34
- ' If you need a Supabase project, create one at:',
35
- ' https://supabase.com/dashboard → New project',
36
- '',
37
- ' After init:',
38
- ' spine-framework install-app <app-slug> # optional apps',
39
- ' npm run assemble && netlify dev # start dev server',
40
- '',
41
- ' Docs: https://github.com/art-mojo-admin/spine',
42
- '',
43
- ]
44
-
45
- console.log(lines.join('\n'))
@@ -1,52 +0,0 @@
1
- # Stability Policy
2
-
3
- > Version target: **0.x** — breaking changes are allowed until 1.0.
4
-
5
- ## Stability Levels
6
-
7
- Every exported symbol in `_shared/index.ts` is annotated with one of these levels:
8
-
9
- | Level | Meaning | Breaking change policy |
10
- |-------|---------|----------------------|
11
- | **stable** | Safe for custom code to depend on | Minimum 1 minor version deprecation notice before removal |
12
- | **evolving** | API may change between minor versions | No deprecation period required, but changelog entry mandatory |
13
- | **internal** | Not exported; core-contributor only | May change without notice |
14
-
15
- ## Versioning
16
-
17
- Spine follows [semver](https://semver.org/) with these conventions:
18
-
19
- - **0.x.y** — Pre-stable. Breaking changes increment the minor version.
20
- - **1.0.0+** — Stable. Breaking changes increment the major version.
21
-
22
- ## Deprecation Process
23
-
24
- 1. Mark the symbol with `@deprecated` JSDoc tag and a migration hint
25
- 2. Add a console warning on first use (once per process)
26
- 3. Remove after the next minor version bump
27
-
28
- ## What Counts as Breaking
29
-
30
- - Removing or renaming an exported function/type from `_shared/index.ts`
31
- - Changing function signatures (required params added, return type changed)
32
- - Removing a CLI command or changing its required arguments
33
- - Changing database table/column names in migration files
34
- - Removing seed data keys from `seed/*.json` schemas
35
-
36
- ## What Does NOT Count as Breaking
37
-
38
- - Adding new optional parameters to existing functions
39
- - Adding new exports to `_shared/index.ts`
40
- - Adding new CLI commands
41
- - Adding new database tables or columns
42
- - Performance improvements to existing functions
43
- - Bug fixes (even if they change observable behavior that was clearly wrong)
44
-
45
- ## Release Checklist
46
-
47
- 1. `npm run test:boundary` — architectural boundaries intact
48
- 2. `npm run test:unit` — unit tests pass
49
- 3. Update `CHANGELOG.md` with breaking changes highlighted
50
- 4. Bump version in `package.json`
51
- 5. `npm run prepublishOnly` — build + test gate
52
- 6. `npm publish`
@@ -1,77 +0,0 @@
1
- # Example: Custom App
2
-
3
- A minimal React app with manifest and routing, installable via `spine-framework install-app`.
4
-
5
- ## Directory Structure
6
-
7
- ```
8
- custom/apps/my-app/
9
- ├── manifest.json # App metadata, routes, nav items
10
- ├── index.tsx # React entry point (default export)
11
- ├── seed/
12
- │ ├── types.json # Item types this app provides
13
- │ └── triggers.json # Automation triggers
14
- ├── components/
15
- │ └── Dashboard.tsx # App-specific components
16
- └── package.json # npm package metadata (optional)
17
- ```
18
-
19
- ## manifest.json
20
-
21
- ```json
22
- {
23
- "name": "My App",
24
- "slug": "my-app",
25
- "version": "0.1.0",
26
- "description": "A custom app for demonstration",
27
- "routes": [
28
- { "path": "/", "redirect": "/dashboard" },
29
- { "path": "/dashboard", "component": "Dashboard" }
30
- ],
31
- "nav_items": [
32
- { "label": "Dashboard", "path": "/dashboard", "icon": "LayoutDashboard" }
33
- ],
34
- "required_roles": ["member"]
35
- }
36
- ```
37
-
38
- ## index.tsx
39
-
40
- ```tsx
41
- import * as React from 'react'
42
- import { Routes, Route, Navigate } from 'react-router-dom'
43
-
44
- function Dashboard() {
45
- return (
46
- <div className="p-6">
47
- <h1 className="text-2xl font-bold">My App</h1>
48
- </div>
49
- )
50
- }
51
-
52
- export default function MyApp() {
53
- return (
54
- <Routes>
55
- <Route path="/" element={<Navigate to="dashboard" replace />} />
56
- <Route path="dashboard" element={<Dashboard />} />
57
- </Routes>
58
- )
59
- }
60
- ```
61
-
62
- ## Installation
63
-
64
- ```bash
65
- # Create the app scaffold
66
- spine-framework create-app my-app
67
-
68
- # Or install an existing app package
69
- spine-framework install-app my-app
70
- ```
71
-
72
- ## How It Works
73
-
74
- 1. `CustomAppLoader` discovers apps via `import.meta.glob('custom/apps/*/index.tsx')`
75
- 2. The app's `manifest.json` provides routing and navigation metadata
76
- 3. Seed data is applied via `spine-framework install-app` (idempotent upserts)
77
- 4. The app is isolated — it cannot import from other apps or core internals
@@ -1,27 +0,0 @@
1
- # Example: Custom Function
2
-
3
- A minimal Netlify Function using Spine's core context system.
4
-
5
- ## Setup
6
-
7
- 1. Create `custom/functions/custom_my-handler.ts`
8
- 2. Import `createHandler` and `CoreContext` from `spine-framework/_shared`
9
- 3. Your handler receives a fully-resolved `CoreContext` with:
10
- - `ctx.principal` — authenticated user/machine identity
11
- - `ctx.db` — Supabase client (scoped to principal's permissions)
12
- - `ctx.accountId` — resolved account UUID
13
- - `ctx.requestId` — unique request trace ID
14
-
15
- ## Key Points
16
-
17
- - **Naming convention:** Custom functions MUST be prefixed with `custom_`
18
- - **No static imports from core:** Use the `spine-framework` package exports
19
- - **Authentication is automatic:** `createHandler` validates the JWT/API key before your code runs
20
- - **Database access:** Use `ctx.db` for RLS-scoped queries, or import `adminDb` for service-role operations
21
-
22
- ## API
23
-
24
- ```
25
- GET/POST /.netlify/functions/custom_my-handler?action=list
26
- Authorization: Bearer <jwt>
27
- ```
@@ -1,48 +0,0 @@
1
- /**
2
- * Example: Custom Netlify Function using Spine core context
3
- *
4
- * This demonstrates the minimal pattern for creating a custom function
5
- * that leverages Spine's authentication, database, and principal system.
6
- *
7
- * File: custom/functions/custom_my-handler.ts
8
- */
9
-
10
- import { createHandler, adminDb } from 'spine-framework/_shared'
11
- import type { CoreContext } from 'spine-framework/_shared'
12
-
13
- /**
14
- * A simple handler that lists items for the current account.
15
- */
16
- async function listAccountItems(ctx: CoreContext) {
17
- const { data, error } = await ctx.db
18
- .from('items')
19
- .select('id, title, type_id, created_at')
20
- .eq('account_id', ctx.accountId)
21
- .eq('is_active', true)
22
- .order('created_at', { ascending: false })
23
- .limit(50)
24
-
25
- if (error) {
26
- return { statusCode: 500, body: JSON.stringify({ error: error.message }) }
27
- }
28
-
29
- return {
30
- statusCode: 200,
31
- body: JSON.stringify({ items: data }),
32
- }
33
- }
34
-
35
- /**
36
- * Netlify Function entry point.
37
- * `createHandler` resolves the principal, validates auth, and provides CoreContext.
38
- */
39
- export const handler = createHandler(async (ctx) => {
40
- const { action } = ctx.params
41
-
42
- switch (action) {
43
- case 'list':
44
- return listAccountItems(ctx)
45
- default:
46
- return { statusCode: 400, body: JSON.stringify({ error: 'Unknown action' }) }
47
- }
48
- })
@@ -1,68 +0,0 @@
1
- # Example: Custom Webhook Handler
2
-
3
- Demonstrates the self-registration pattern for webhook handlers.
4
-
5
- ## How It Works
6
-
7
- 1. Create a handler function in `custom/functions/custom_my-webhook.ts`
8
- 2. Register it by calling `registerWebhookHandler()` in `custom/functions/custom_webhook-handlers.ts`
9
- 3. Core resolves handlers at runtime via the `webhook_handlers` DB table — no static imports needed
10
-
11
- ## Handler Function
12
-
13
- ```ts
14
- // custom/functions/custom_my-webhook.ts
15
-
16
- import { createHandler } from 'spine-framework/_shared'
17
- import type { CoreContext } from 'spine-framework/_shared'
18
-
19
- export const handler = createHandler(async (ctx: CoreContext) => {
20
- const payload = ctx.body
21
-
22
- // Process the webhook payload
23
- console.log('Received webhook:', payload.event_type)
24
-
25
- // Do something with the data
26
- const { error } = await ctx.db
27
- .from('items')
28
- .insert({
29
- title: `Webhook: ${payload.event_type}`,
30
- type_id: await resolveTypeId('webhook_event'),
31
- account_id: ctx.accountId,
32
- data: payload,
33
- })
34
-
35
- if (error) {
36
- return { statusCode: 500, body: JSON.stringify({ error: error.message }) }
37
- }
38
-
39
- return { statusCode: 200, body: JSON.stringify({ ok: true }) }
40
- })
41
- ```
42
-
43
- ## Registration
44
-
45
- ```ts
46
- // In custom/functions/custom_webhook-handlers.ts
47
-
48
- import { registerWebhookHandler } from 'spine-framework/_shared/webhook-registration'
49
-
50
- registerWebhookHandler({
51
- name: 'my-webhook',
52
- functionName: 'custom_my-webhook',
53
- description: 'Handles incoming webhooks from MyService',
54
- events: ['integration.webhook'],
55
- }).catch(console.error)
56
- ```
57
-
58
- ## Integration Config
59
-
60
- When configuring an integration in the admin panel, set:
61
- - `handler.path` = `my-webhook` (matches the `name` in registration)
62
-
63
- ## Key Points
64
-
65
- - **Self-registration is idempotent** — safe to call on every module load
66
- - **No core changes needed** — just add your handler and register it
67
- - **Runtime resolution** — core looks up handlers from `webhook_handlers` table via `resolveHandler()`
68
- - **Events array** — currently `['integration.webhook']` is the standard event type