spine-framework 0.1.61 → 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 -565
  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 -1266
  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
@@ -28,7 +28,7 @@ import { adminDb } from '../../functions/_shared/index.ts'
28
28
 
29
29
  const __filename = fileURLToPath(import.meta.url)
30
30
  const __dirname = dirname(__filename)
31
- const PROJECT_ROOT = process.cwd()
31
+ const PROJECT_ROOT = resolve(__dirname, '../../../')
32
32
 
33
33
  function toTitleCase(slug: string): string {
34
34
  return slug.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ')
@@ -5,81 +5,177 @@
5
5
  * @layer cli
6
6
  * @stability stable
7
7
  *
8
- * `spine-framework dev` command — assemble and start the Netlify dev server.
8
+ * `spine dev` command — unified development server for Spine.
9
+ * Handles assembly, integrity verification, and Vite dev server startup.
10
+ *
11
+ * **Commands:**
12
+ * | Subcommand | Description |
13
+ * |-------------------|-------------------------------------------------------|
14
+ * | `dev` | Start dev server with assembly and integrity check |
15
+ * | `dev --no-verify` | Skip integrity verification (faster) |
16
+ * | `dev --port 3002` | Use custom port (default: 3001) |
17
+ * | `dev --watch` | Watch v2-custom for changes and auto-reassemble |
18
+ * | `dev --no-watch` | Disable file watching (manual rebuild only) |
9
19
  *
10
20
  * **What happens:**
11
- * 1. Runs `npm run assemble` to merge framework + custom apps
12
- * 2. Starts `netlify dev` (which runs Vite + Netlify Functions)
21
+ * 1. Runs `assemble:v2` to merge v2-core + v2-custom
22
+ * 2. Runs `verify` to check core integrity (unless --no-verify)
23
+ * 3. Starts Vite dev server on port 3001 (or --port)
24
+ * 4. Watches v2-custom/ for changes (--watch by default)
25
+ * 5. Fast incremental reassembly on custom code changes
13
26
  *
14
27
  * **Usage:**
15
28
  * ```bash
16
- * spine-framework dev
17
- * spine-framework dev --no-assemble # skip assembly step
29
+ * spine dev
30
+ * spine dev --port 3002
31
+ * spine dev --no-verify
18
32
  * ```
19
33
  *
20
- * @seeAlso scripts/assemble.sh (assembly script)
21
- * @seeAlso netlify.toml (Netlify dev configuration)
34
+ * @seeAlso scripts/assemble-v2.sh (assembly script)
35
+ * @seeAlso scripts/verify-integrity.sh (verification script)
36
+ * @seeAlso vite.config.ts (Vite configuration)
22
37
  */
23
38
 
24
39
  import type { Command } from 'commander'
25
- import { spawn } from 'child_process'
40
+ import { spawn, execSync } from 'child_process'
26
41
  import { resolve, dirname } from 'path'
27
42
  import { fileURLToPath } from 'url'
28
- import { existsSync } from 'fs'
43
+ import { watch, existsSync, statSync, readdirSync } from 'fs'
29
44
 
30
45
  const __filename = fileURLToPath(import.meta.url)
31
46
  const __dirname = dirname(__filename)
32
- const PROJECT_ROOT = process.cwd()
47
+ const PROJECT_ROOT = resolve(__dirname, '../../../')
33
48
 
34
49
  // ─── COMMAND REGISTRATION ──────────────────────────────────────────────────
35
50
 
36
51
  export function registerDevCommands(program: Command) {
37
52
  program
38
53
  .command('dev')
39
- .description('Assemble and start the Netlify dev server')
40
- .option('--no-assemble', 'Skip the assembly step')
54
+ .description('Start Spine development server')
55
+ .option('--no-verify', 'Skip integrity verification')
56
+ .option('--port <port>', 'Dev server port', '3001')
57
+ .option('--host', 'Expose to network', false)
58
+ .option('--no-watch', 'Disable file watching')
59
+ .option('--watch-delay <ms>', 'Debounce delay for reassembly', '500')
41
60
  .action(async (opts) => {
42
61
  console.log('\n🚀 Starting Spine dev server...\n')
43
62
 
44
63
  try {
45
64
  // Step 1: Assembly
46
- if (opts.assemble !== false) {
47
- console.log('📦 Step 1: Assembling...')
48
- const assemblyExitCode = await runCommand('npm', ['run', 'assemble'], PROJECT_ROOT)
65
+ console.log('📦 Step 1: Assembling v2-core + v2-custom...')
66
+ const assemblyExitCode = await runCommand(
67
+ 'bash',
68
+ [resolve(PROJECT_ROOT, 'scripts/assemble-v2.sh')],
69
+ PROJECT_ROOT
70
+ )
71
+
72
+ if (assemblyExitCode !== 0) {
73
+ console.error('\n❌ Assembly failed. Please check the errors above.')
74
+ process.exit(1)
75
+ }
76
+ console.log('✓ Assembly complete\n')
49
77
 
50
- if (assemblyExitCode !== 0) {
51
- console.error('\n❌ Assembly failed. Please check the errors above.')
52
- process.exit(1)
78
+ // Step 2: Integrity verification (unless skipped)
79
+ if (opts.verify !== false) {
80
+ console.log('🔒 Step 2: Verifying core integrity...')
81
+ const verifyExitCode = await runCommand(
82
+ 'bash',
83
+ [resolve(PROJECT_ROOT, 'scripts/verify-integrity.sh')],
84
+ PROJECT_ROOT
85
+ )
86
+
87
+ if (verifyExitCode !== 0) {
88
+ console.warn('\n⚠️ Integrity check failed. Continuing anyway (--no-verify to skip)\n')
89
+ } else {
90
+ console.log('✓ Integrity verified\n')
53
91
  }
54
- console.log('✓ Assembly complete\n')
55
92
  } else {
56
- console.log('⏭️ Step 1: Skipping assembly (--no-assemble)\n')
93
+ console.log('⏭️ Step 2: Skipping integrity verification (--no-verify)\n')
57
94
  }
58
95
 
59
- // Step 2: Start netlify dev
60
- console.log('🌐 Step 2: Starting netlify dev...')
96
+ // Step 3: Start Vite
97
+ console.log(`🌐 Step 3: Starting Vite dev server on port ${opts.port}...`)
61
98
  console.log(' (Press Ctrl+C to stop)\n')
62
99
 
63
- const netlifyBin = resolve(PROJECT_ROOT, 'node_modules/.bin/netlify')
64
- const cmd = existsSync(netlifyBin) ? netlifyBin : 'netlify'
100
+ const vite = spawn(
101
+ resolve(PROJECT_ROOT, 'node_modules/.bin/vite'),
102
+ [
103
+ '--config', resolve(PROJECT_ROOT, 'vite.config.ts'),
104
+ '--port', opts.port,
105
+ ...(opts.host ? ['--host'] : [])
106
+ ],
107
+ {
108
+ cwd: PROJECT_ROOT,
109
+ stdio: 'inherit',
110
+ env: { ...process.env, SPINE_DEV: 'true' }
111
+ }
112
+ )
113
+
114
+ // Step 4: Watch v2-custom for changes (unless disabled)
115
+ let watcher: ReturnType<typeof watch> | null = null
116
+ if (opts.watch !== false) {
117
+ const watchDelay = parseInt(opts.watchDelay) || 500
118
+ const customDir = resolve(PROJECT_ROOT, 'v2-custom')
65
119
 
66
- const netlify = spawn(cmd, ['dev'], {
67
- cwd: PROJECT_ROOT,
68
- stdio: 'inherit',
69
- env: { ...process.env }
70
- })
120
+ if (existsSync(customDir)) {
121
+ console.log(`👁️ Step 4: Watching v2-custom/ for changes...`)
122
+ console.log(` (Changes trigger fast reassembly after ${watchDelay}ms debounce)\n`)
123
+
124
+ let reassemblyTimeout: NodeJS.Timeout | null = null
125
+
126
+ watcher = watch(customDir, { recursive: true }, (eventType, filename) => {
127
+ // Ignore node_modules, .git, and temp files
128
+ if (!filename || filename.includes('node_modules') || filename.startsWith('.')) {
129
+ return
130
+ }
131
+
132
+ console.log(`\n📝 Change detected in v2-custom/${filename}`)
71
133
 
72
- netlify.on('exit', (code) => {
134
+ // Debounce reassembly
135
+ if (reassemblyTimeout) {
136
+ clearTimeout(reassemblyTimeout)
137
+ }
138
+
139
+ reassemblyTimeout = setTimeout(async () => {
140
+ console.log('🔧 Running fast reassembly...')
141
+ try {
142
+ const start = Date.now()
143
+ await fastReassemble(PROJECT_ROOT)
144
+ const duration = Date.now() - start
145
+ console.log(`✓ Reassembly complete (${duration}ms)\n`)
146
+ } catch (err: any) {
147
+ console.error(`✗ Reassembly failed: ${err.message}\n`)
148
+ }
149
+ }, watchDelay)
150
+ })
151
+ } else {
152
+ console.log('⏭️ Step 4: v2-custom/ not found, skipping watch mode\n')
153
+ }
154
+ } else {
155
+ console.log('⏭️ Step 4: File watching disabled (--no-watch)\n')
156
+ }
157
+
158
+ vite.on('exit', (code) => {
159
+ if (watcher) {
160
+ watcher.close()
161
+ }
73
162
  process.exit(code || 0)
74
163
  })
75
164
 
165
+ // Handle Ctrl+C gracefully
76
166
  process.on('SIGINT', () => {
77
- console.log('\n\n🛑 Shutting down...')
78
- netlify.kill('SIGTERM')
167
+ console.log('\n\n🛑 Shutting down dev server...')
168
+ if (watcher) {
169
+ watcher.close()
170
+ }
171
+ vite.kill('SIGTERM')
79
172
  })
80
173
 
81
174
  process.on('SIGTERM', () => {
82
- netlify.kill('SIGTERM')
175
+ if (watcher) {
176
+ watcher.close()
177
+ }
178
+ vite.kill('SIGTERM')
83
179
  })
84
180
 
85
181
  } catch (err: any) {
@@ -111,3 +207,89 @@ function runCommand(command: string, args: string[], cwd: string): Promise<numbe
111
207
  })
112
208
  })
113
209
  }
210
+
211
+ /**
212
+ * Fast incremental reassembly of v2-custom files only.
213
+ * Uses rsync if available, falls back to selective copy.
214
+ */
215
+ async function fastReassemble(projectRoot: string): Promise<void> {
216
+ const customDir = resolve(projectRoot, 'v2-custom')
217
+ const srcDest = resolve(projectRoot, 'src/v2-assembled')
218
+ const funcsDest = resolve(projectRoot, 'functions')
219
+
220
+ // Check if rsync is available
221
+ let hasRsync = false
222
+ try {
223
+ execSync('which rsync', { stdio: 'pipe' })
224
+ hasRsync = true
225
+ } catch {
226
+ hasRsync = false
227
+ }
228
+
229
+ // Reassemble custom frontend files
230
+ const customSrcDir = resolve(customDir, 'src')
231
+ if (existsSync(customSrcDir)) {
232
+ const destDir = srcDest
233
+
234
+ if (hasRsync) {
235
+ // Use rsync for efficient incremental sync
236
+ execSync(
237
+ `rsync -av --delete "${customSrcDir}/" "${destDir}/"`,
238
+ { stdio: 'pipe', cwd: projectRoot }
239
+ )
240
+ } else {
241
+ // Fallback: copy files that have changed
242
+ copyChangedFiles(customSrcDir, destDir)
243
+ }
244
+ }
245
+
246
+ // Reassemble custom function files
247
+ const customFuncsDir = resolve(customDir, 'functions')
248
+ if (existsSync(customFuncsDir)) {
249
+ if (hasRsync) {
250
+ execSync(
251
+ `rsync -av --delete "${customFuncsDir}/" "${funcsDest}/"`,
252
+ { stdio: 'pipe', cwd: projectRoot }
253
+ )
254
+ } else {
255
+ copyChangedFiles(customFuncsDir, funcsDest)
256
+ }
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Copy only files that have changed (mtime comparison).
262
+ * Fallback when rsync is not available.
263
+ */
264
+ function copyChangedFiles(src: string, dest: string): void {
265
+ const files = readdirSync(src, { withFileTypes: true })
266
+
267
+ for (const entry of files) {
268
+ const srcPath = resolve(src, entry.name)
269
+ const destPath = resolve(dest, entry.name)
270
+
271
+ if (entry.isDirectory()) {
272
+ if (!existsSync(destPath)) {
273
+ // Create directory
274
+ const { mkdirSync } = require('fs')
275
+ mkdirSync(destPath, { recursive: true })
276
+ }
277
+ // Recurse
278
+ copyChangedFiles(srcPath, destPath)
279
+ } else {
280
+ // Check if file needs copying
281
+ const srcStat = statSync(srcPath)
282
+ let needsCopy = true
283
+
284
+ if (existsSync(destPath)) {
285
+ const destStat = statSync(destPath)
286
+ needsCopy = srcStat.mtime > destStat.mtime
287
+ }
288
+
289
+ if (needsCopy) {
290
+ const { copyFileSync } = require('fs')
291
+ copyFileSync(srcPath, destPath)
292
+ }
293
+ }
294
+ }
295
+ }
@@ -19,10 +19,6 @@
19
19
  * | items | commands/items.ts |
20
20
  * | agents | commands/agents.ts |
21
21
  * | migrations | commands/migrations.ts |
22
- * | init | commands/init.ts |
23
- * | install-app | commands/install-app.ts |
24
- * | status | commands/status.ts |
25
- * | uninstall | commands/uninstall-app.ts |
26
22
  *
27
23
  * **Usage:**
28
24
  * ```bash
@@ -39,6 +35,7 @@
39
35
  * @seeAlso functions/_shared/index.ts (core functions exposed to CLI)
40
36
  */
41
37
 
38
+ import './env-loader.ts'
42
39
  import { Command } from 'commander'
43
40
 
44
41
  const program = new Command()
@@ -49,66 +46,43 @@ program
49
46
  .version('2.0.0')
50
47
  .option('--account <id>', 'Override the account ID for this command')
51
48
 
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')
55
- registerInitCommands(program)
56
- registerMigrateCommands(program)
49
+ const [
50
+ { registerAuthCommands },
51
+ { registerPipelineCommands },
52
+ { registerItemCommands },
53
+ { registerAgentCommands },
54
+ { registerMigrationCommands },
55
+ { registerDoctorCommands },
56
+ { registerDevCommands },
57
+ { registerTestCommands },
58
+ { registerSystemCommands },
59
+ { registerGenerateCommands },
60
+ { registerCreateAppCommands },
61
+ ] = await Promise.all([
62
+ import('./commands/auth.ts'),
63
+ import('./commands/pipelines.ts'),
64
+ import('./commands/items.ts'),
65
+ import('./commands/agents.ts'),
66
+ import('./commands/migrations.ts'),
67
+ import('./commands/doctor.ts'),
68
+ import('./commands/dev.ts'),
69
+ import('./commands/test.ts'),
70
+ import('./commands/system.ts'),
71
+ import('./commands/generate.ts'),
72
+ import('./commands/create-app.ts'),
73
+ ])
57
74
 
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
- }
75
+ registerAuthCommands(program)
76
+ registerPipelineCommands(program)
77
+ registerItemCommands(program)
78
+ registerAgentCommands(program)
79
+ registerMigrationCommands(program)
80
+ registerDoctorCommands(program)
81
+ registerDevCommands(program)
82
+ registerTestCommands(program)
83
+ registerSystemCommands(program)
84
+ registerGenerateCommands(program)
85
+ registerCreateAppCommands(program)
112
86
 
113
87
  program.parseAsync(process.argv).catch((err) => {
114
88
  console.error('Error:', err.message)
@@ -16,8 +16,8 @@
16
16
  * @seeAlso 015_simplify_apps_table.sql (database structure)
17
17
  */
18
18
 
19
- import { readFileSync, readdirSync, existsSync } from 'fs'
20
- import { resolve, join } from 'path'
19
+ import { readFileSync } from 'fs'
20
+ import { resolve } from 'path'
21
21
 
22
22
  export interface AppManifest {
23
23
  name: string
@@ -168,17 +168,10 @@ export function mergeWithManifest(dbRecord: any): any {
168
168
  * @returns Array of discovered app slugs and their paths
169
169
  */
170
170
  export function discoverManifests(): Array<{slug: string, path: string}> {
171
- const appsDir = resolve(process.cwd(), 'custom/apps')
172
- if (!existsSync(appsDir)) return []
173
-
174
- const results: Array<{slug: string, path: string}> = []
175
- for (const entry of readdirSync(appsDir, { withFileTypes: true })) {
176
- if (!entry.isDirectory()) continue
177
- if (entry.name === 'lib' || entry.name.startsWith('.')) continue
178
- const manifestPath = join('custom/apps', entry.name, 'manifest.json')
179
- if (existsSync(resolve(process.cwd(), manifestPath))) {
180
- results.push({ slug: entry.name, path: manifestPath })
181
- }
182
- }
183
- return results
171
+ // In production, this would scan the filesystem
172
+ // For now, return known manifests based on the plan
173
+ return [
174
+ { slug: 'cortex', path: 'custom/apps/cortex/manifest.json' },
175
+ { slug: 'customer-portal', path: 'custom/apps/customer-portal/manifest.json' }
176
+ ]
184
177
  }
@@ -15,14 +15,14 @@
15
15
  */
16
16
 
17
17
  import { createClient } from '@supabase/supabase-js'
18
- import WebSocket from 'ws'
19
-
20
- // WebSocket constructor for Supabase Realtime in Node.js environment
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
- const RealtimeWebSocket = WebSocket as any
23
18
 
24
19
  // ─── ENVIRONMENT RESOLUTION ──────────────────────────────────────────────────
25
20
 
21
+ const _env = (globalThis as any).process?.env || {}
22
+ const supabaseUrl: string = _env.SUPABASE_URL!
23
+ const supabaseServiceKey: string = _env.SUPABASE_SERVICE_ROLE_KEY!
24
+ const supabaseAnonKey: string = _env.SUPABASE_ANON_KEY!
25
+
26
26
  /**
27
27
  * Active database schema name, read from `DB_SCHEMA` env var.
28
28
  *
@@ -35,10 +35,6 @@ const RealtimeWebSocket = WebSocket as any
35
35
  * @sideEffects none
36
36
  * @calledBy adminDb, getUserDb (applied at client construction time)
37
37
  */
38
- const _env = (globalThis as any).process?.env || {}
39
- const supabaseUrl: string = _env.SUPABASE_URL || 'https://placeholder.supabase.co'
40
- const supabaseServiceKey: string = _env.SUPABASE_SERVICE_ROLE_KEY || ''
41
- const supabaseAnonKey: string = _env.SUPABASE_ANON_KEY || ''
42
38
  const dbSchema: string = _env.DB_SCHEMA || 'public'
43
39
 
44
40
  // ─── CLIENTS ─────────────────────────────────────────────────────────────────
@@ -78,9 +74,9 @@ const dbSchema: string = _env.DB_SCHEMA || 'public'
78
74
  * ```
79
75
  */
80
76
  export const adminDb = createClient(supabaseUrl, supabaseServiceKey, {
81
- db: { schema: dbSchema },
82
- auth: { autoRefreshToken: false, persistSession: false },
83
- realtime: { transport: RealtimeWebSocket }
77
+ db: {
78
+ schema: dbSchema
79
+ }
84
80
  })
85
81
 
86
82
  // ─── CHUNK_START: SHARED_DB_GET_USER_DB ──────────────────────────────────────────────
@@ -99,9 +95,9 @@ export const adminDb = createClient(supabaseUrl, supabaseServiceKey, {
99
95
  */
100
96
  export function getUserDb(jwt: string) {
101
97
  return createClient(supabaseUrl, supabaseAnonKey, {
102
- db: { schema: dbSchema },
103
- auth: { autoRefreshToken: false, persistSession: false },
104
- realtime: { transport: RealtimeWebSocket }, // WebSocket for Node.js 20+
98
+ db: {
99
+ schema: dbSchema
100
+ },
105
101
  global: {
106
102
  headers: {
107
103
  Authorization: `Bearer ${jwt}`
@@ -380,12 +380,3 @@ export {
380
380
  expectErrorResponse
381
381
  } from './testing'
382
382
  export type { TestContext, TestPrincipal, TestLogger } from './testing'
383
-
384
- export {
385
- resolveTypeIds,
386
- resolveTypeId,
387
- resolveLinkTypeIds,
388
- resolveAccountId,
389
- resolveAgentId,
390
- resolvePromptConfigId
391
- } from './resolve-ids'
@@ -32,11 +32,6 @@
32
32
 
33
33
  import { getUserDb, adminDb } from './db'
34
34
  import { createClient } from '@supabase/supabase-js'
35
- import WebSocket from 'ws'
36
-
37
- // WebSocket constructor for Supabase Realtime in Node.js environment
38
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- const RealtimeWebSocket = WebSocket as any
40
35
 
41
36
  // ─── AUTH CLIENT ─────────────────────────────────────────────────────────────
42
37
 
@@ -51,8 +46,7 @@ const supabase = createClient(
51
46
  auth: {
52
47
  autoRefreshToken: false,
53
48
  persistSession: false
54
- },
55
- realtime: { transport: RealtimeWebSocket } // WebSocket for Node.js 20+
49
+ }
56
50
  }
57
51
  )
58
52
 
@@ -501,7 +495,7 @@ async function resolveHumanPrincipal(token: string, event: any): Promise<Princip
501
495
  type: 'human',
502
496
  accountId: person.account_id || null,
503
497
  roles: roleSlugs,
504
- displayName: person.full_name || person.email,
498
+ displayName: person.display_name || person.email,
505
499
  email: person.email,
506
500
  provenance: {
507
501
  sourceType: 'jwt',
@@ -567,60 +561,8 @@ async function resolveInternalPersonId(authUserId: string, email?: string): Prom
567
561
 
568
562
  if (byAuthId) return byAuthId.id
569
563
 
570
- // Not found — auto-create the person (handles cases where auth user pre-existed DB reset
571
- // or trigger fired before seed data was in place)
572
- const { data: account } = await adminDb
573
- .from('accounts')
574
- .select('id')
575
- .eq('slug', 'spine-system')
576
- .single()
577
-
578
- const { data: type } = await adminDb
579
- .from('types')
580
- .select('id')
581
- .eq('kind', 'person')
582
- .eq('slug', 'person')
583
- .single()
584
-
585
- if (!account || !type) {
586
- throw new Error('Cannot auto-create person: spine-system account or person type not found in DB')
587
- }
588
-
589
- // First person ever gets system_admin role
590
- const { count } = await adminDb
591
- .from('people')
592
- .select('id', { count: 'exact', head: true })
593
-
594
- let roleId: string | null = null
595
- if ((count ?? 0) === 0) {
596
- const { data: adminRole } = await adminDb
597
- .from('roles')
598
- .select('id')
599
- .eq('slug', 'system_admin')
600
- .single()
601
- roleId = adminRole?.id ?? null
602
- }
603
-
604
- const { data: newPerson, error: createError } = await adminDb
605
- .from('people')
606
- .upsert({
607
- id: authUserId,
608
- auth_uid: authUserId,
609
- email: email ?? '',
610
- full_name: email ? email.split('@')[0] : 'Unknown',
611
- account_id: account.id,
612
- type_id: type.id,
613
- role_id: roleId,
614
- is_active: true
615
- }, { onConflict: 'id' })
616
- .select('id')
617
- .single()
618
-
619
- if (createError || !newPerson) {
620
- throw new Error('Failed to auto-create person for auth user ' + authUserId + ': ' + createError?.message)
621
- }
622
-
623
- return newPerson.id
564
+ // Not found - return the auth ID as fallback
565
+ return authUserId
624
566
  }
625
567
 
626
568
  // ─── HELPER FUNCTIONS ────────────────────────────────────────────────────────
@@ -4,12 +4,6 @@
4
4
 
5
5
  import { adminDb } from './db'
6
6
 
7
- interface ResolvedIds {
8
- types: Record<string, string>
9
- linkTypes: Record<string, string>
10
- accounts: Record<string, string>
11
- }
12
-
13
7
  // Resolve type IDs by (kind, slug) pairs
14
8
  export async function resolveTypeIds(
15
9
  pairs: Array<{ kind: string; slug: string }>
@@ -67,6 +61,12 @@ export async function resolveLinkTypeIds(slugs: string[]): Promise<Record<string
67
61
  return result
68
62
  }
69
63
 
64
+ // Resolve a single link type ID — throws if not found
65
+ export async function resolveLinkTypeId(slug: string): Promise<string> {
66
+ const ids = await resolveLinkTypeIds([slug])
67
+ return ids[slug]
68
+ }
69
+
70
70
  // Resolve account ID by slug
71
71
  export async function resolveAccountId(slug: string): Promise<string> {
72
72
  const { data, error } = await adminDb
@@ -18,7 +18,7 @@
18
18
  * - Multi-tenancy: different tenants can have different handlers
19
19
  *
20
20
  * @seeAlso integration-routes.ts (uses this registry)
21
- * @seeAlso webhook-registration.ts (self-registration helper for custom handlers)
21
+ * @seeAlso custom_webhook-handlers.ts (legacy static registry - being phased out)
22
22
  */
23
23
 
24
24
  import { adminDb } from './db'