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
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module'
3
+ import { fileURLToPath } from 'url'
4
+ import { dirname, resolve } from 'path'
5
+ import { spawnSync } from 'child_process'
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url))
8
+ const tsEntry = resolve(__dirname, 'index.ts')
9
+
10
+ // Find tsx relative to this package or globally
11
+ const result = spawnSync('npx', ['--yes', 'tsx', tsEntry, ...process.argv.slice(2)], {
12
+ stdio: 'inherit',
13
+ shell: true,
14
+ })
15
+
16
+ process.exit(result.status ?? 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,13 +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
- * | update-app | commands/update-app.ts |
25
- * | publish | commands/publish.ts |
26
- * | status | commands/status.ts |
27
- * | claim | commands/claim.ts |
28
- * | uninstall | commands/uninstall-app.ts |
29
22
  *
30
23
  * **Usage:**
31
24
  * ```bash
@@ -42,6 +35,7 @@
42
35
  * @seeAlso functions/_shared/index.ts (core functions exposed to CLI)
43
36
  */
44
37
 
38
+ import './env-loader.ts'
45
39
  import { Command } from 'commander'
46
40
 
47
41
  const program = new Command()
@@ -52,75 +46,43 @@ program
52
46
  .version('2.0.0')
53
47
  .option('--account <id>', 'Override the account ID for this command')
54
48
 
55
- // Commands that do NOT need DB/env — safe to run on a fresh install
56
- const { registerInitCommands } = await import('./commands/init.ts')
57
- const { registerMigrateCommands } = await import('./commands/migrate.ts')
58
- registerInitCommands(program)
59
- 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
+ ])
60
74
 
61
- // All other commands require SUPABASE_URL to be set — only load if not init/migrate
62
- const subcommand = process.argv[2]
63
- const noDbCommands = ['init', 'migrate', '--help', '-h', '--version', '-V', undefined]
64
-
65
- if (!noDbCommands.includes(subcommand)) {
66
- await import('./env-loader.ts')
67
-
68
- const [
69
- { registerAuthCommands },
70
- { registerPipelineCommands },
71
- { registerItemCommands },
72
- { registerAgentCommands },
73
- { registerMigrationCommands },
74
- { registerDoctorCommands },
75
- { registerDevCommands },
76
- { registerTestCommands },
77
- { registerSystemCommands },
78
- { registerGenerateCommands },
79
- { registerCreateAppCommands },
80
- { registerInstallAppCommands },
81
- { registerUpdateAppCommands },
82
- { registerPublishCommands },
83
- { registerStatusCommands },
84
- { registerClaimCommands },
85
- { registerUninstallAppCommands },
86
- ] = await Promise.all([
87
- import('./commands/auth.ts'),
88
- import('./commands/pipelines.ts'),
89
- import('./commands/items.ts'),
90
- import('./commands/agents.ts'),
91
- import('./commands/migrations.ts'),
92
- import('./commands/doctor.ts'),
93
- import('./commands/dev.ts'),
94
- import('./commands/test.ts'),
95
- import('./commands/system.ts'),
96
- import('./commands/generate.ts'),
97
- import('./commands/create-app.ts'),
98
- import('./commands/install-app.ts'),
99
- import('./commands/update-app.ts'),
100
- import('./commands/publish.ts'),
101
- import('./commands/status.ts'),
102
- import('./commands/claim.ts'),
103
- import('./commands/uninstall-app.ts'),
104
- ])
105
-
106
- registerAuthCommands(program)
107
- registerPipelineCommands(program)
108
- registerItemCommands(program)
109
- registerAgentCommands(program)
110
- registerMigrationCommands(program)
111
- registerDoctorCommands(program)
112
- registerDevCommands(program)
113
- registerTestCommands(program)
114
- registerSystemCommands(program)
115
- registerGenerateCommands(program)
116
- registerCreateAppCommands(program)
117
- registerInstallAppCommands(program)
118
- registerUpdateAppCommands(program)
119
- registerPublishCommands(program)
120
- registerStatusCommands(program)
121
- registerClaimCommands(program)
122
- registerUninstallAppCommands(program)
123
- }
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)
124
86
 
125
87
  program.parseAsync(process.argv).catch((err) => {
126
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}`
@@ -285,21 +285,21 @@ export { emitAudit } from './audit'
285
285
  // ============================================
286
286
 
287
287
  /**
288
- * lookupHandler — look up webhook handler metadata by name.
288
+ * resolveHandler — dynamically load a webhook handler by name.
289
289
  *
290
- * Queries the webhook_handlers table for handler registration info.
291
- * Actual handler loading is done via HANDLER_REGISTRY static imports.
290
+ * Used by integration-routes.ts to resolve handlers at runtime
291
+ * without static imports, enabling custom handlers to self-register.
292
292
  *
293
293
  * @param handlerName — The handler identifier from webhook_handlers table
294
- * @returns Handler metadata or null if not found
294
+ * @returns Handler function or null if not found
295
295
  *
296
296
  * @example
297
297
  * ```ts
298
- * const meta = await lookupHandler('cortex-webhook')
299
- * if (meta) console.log(meta.functionName)
298
+ * const handler = await resolveHandler('cortex-webhook')
299
+ * if (handler) await handler(event, context)
300
300
  * ```
301
301
  */
302
- export { lookupHandler } from './webhook-registry'
302
+ export { resolveHandler, lookupHandler, loadHandler } from './webhook-registry'
303
303
 
304
304
  /**
305
305
  * registerWebhookHandler — self-register a custom webhook handler.
@@ -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'