aven 0.0.3

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 (159) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +35 -0
  4. data/Rakefile +19 -0
  5. data/app/assets/stylesheets/aven/application.css +14 -0
  6. data/app/assets/stylesheets/aven/application.tailwind.css +7 -0
  7. data/app/assets/stylesheets/aven/tailwind.css +224 -0
  8. data/app/channels/aven/chat/thread_channel.rb +39 -0
  9. data/app/components/aven/application_view_component.rb +15 -0
  10. data/app/components/aven/views/admin/dashboard/index/component.html.erb +1 -0
  11. data/app/components/aven/views/admin/dashboard/index/component.rb +5 -0
  12. data/app/components/aven/views/articles/edit/component.html.erb +14 -0
  13. data/app/components/aven/views/articles/edit/component.rb +14 -0
  14. data/app/components/aven/views/articles/form/component.html.erb +45 -0
  15. data/app/components/aven/views/articles/form/component.rb +27 -0
  16. data/app/components/aven/views/articles/index/component.html.erb +93 -0
  17. data/app/components/aven/views/articles/index/component.rb +29 -0
  18. data/app/components/aven/views/articles/new/component.html.erb +13 -0
  19. data/app/components/aven/views/articles/new/component.rb +14 -0
  20. data/app/components/aven/views/articles/show/component.html.erb +110 -0
  21. data/app/components/aven/views/articles/show/component.rb +34 -0
  22. data/app/components/aven/views/oauth/error/component.html.erb +44 -0
  23. data/app/components/aven/views/oauth/error/component.rb +30 -0
  24. data/app/components/aven/views/static/index/component.html.erb +17 -0
  25. data/app/components/aven/views/static/index/component.rb +16 -0
  26. data/app/components/aven/views/static/index/controller.js +7 -0
  27. data/app/controllers/aven/admin/base.rb +16 -0
  28. data/app/controllers/aven/admin/dashboard_controller.rb +9 -0
  29. data/app/controllers/aven/agentic/agents_controller.rb +56 -0
  30. data/app/controllers/aven/agentic/documents_controller.rb +51 -0
  31. data/app/controllers/aven/agentic/mcp_controller.rb +124 -0
  32. data/app/controllers/aven/agentic/tools_controller.rb +37 -0
  33. data/app/controllers/aven/ai/text_controller.rb +41 -0
  34. data/app/controllers/aven/application_controller.rb +27 -0
  35. data/app/controllers/aven/articles_controller.rb +114 -0
  36. data/app/controllers/aven/auth_controller.rb +12 -0
  37. data/app/controllers/aven/chat/threads_controller.rb +67 -0
  38. data/app/controllers/aven/oauth/auth0_controller.rb +84 -0
  39. data/app/controllers/aven/oauth/base_controller.rb +183 -0
  40. data/app/controllers/aven/oauth/documentation/auth0.md +387 -0
  41. data/app/controllers/aven/oauth/documentation/entra_id.md +608 -0
  42. data/app/controllers/aven/oauth/documentation/github.md +329 -0
  43. data/app/controllers/aven/oauth/documentation/google.md +253 -0
  44. data/app/controllers/aven/oauth/entra_id_controller.rb +92 -0
  45. data/app/controllers/aven/oauth/github_controller.rb +91 -0
  46. data/app/controllers/aven/oauth/google_controller.rb +64 -0
  47. data/app/controllers/aven/static_controller.rb +7 -0
  48. data/app/controllers/aven/tags_controller.rb +44 -0
  49. data/app/controllers/aven/workspaces_controller.rb +20 -0
  50. data/app/controllers/concerns/aven/authentication.rb +49 -0
  51. data/app/controllers/concerns/aven/controller_helpers.rb +38 -0
  52. data/app/helpers/aven/application_helper.rb +16 -0
  53. data/app/javascript/aven/application.js +3 -0
  54. data/app/javascript/aven/controllers/application.js +5 -0
  55. data/app/javascript/aven/controllers/index.js +11 -0
  56. data/app/jobs/aven/agentic/document_embedding_job.rb +28 -0
  57. data/app/jobs/aven/agentic/document_ocr_job.rb +28 -0
  58. data/app/jobs/aven/application_job.rb +4 -0
  59. data/app/jobs/aven/chat/calculate_cost_job.rb +26 -0
  60. data/app/jobs/aven/chat/run_job.rb +27 -0
  61. data/app/mailers/aven/application_mailer.rb +6 -0
  62. data/app/models/aven/agentic/agent.rb +76 -0
  63. data/app/models/aven/agentic/agent_document.rb +37 -0
  64. data/app/models/aven/agentic/agent_tool.rb +37 -0
  65. data/app/models/aven/agentic/document.rb +162 -0
  66. data/app/models/aven/agentic/document_embedding.rb +39 -0
  67. data/app/models/aven/agentic/tool.rb +106 -0
  68. data/app/models/aven/agentic/tool_parameter.rb +56 -0
  69. data/app/models/aven/application_record.rb +5 -0
  70. data/app/models/aven/article.rb +86 -0
  71. data/app/models/aven/article_attachment.rb +18 -0
  72. data/app/models/aven/article_relationship.rb +26 -0
  73. data/app/models/aven/chat/message.rb +135 -0
  74. data/app/models/aven/chat/thread.rb +159 -0
  75. data/app/models/aven/import/entry.rb +45 -0
  76. data/app/models/aven/import/item_link.rb +36 -0
  77. data/app/models/aven/import/processor.rb +123 -0
  78. data/app/models/aven/import.rb +102 -0
  79. data/app/models/aven/item/embed.rb +54 -0
  80. data/app/models/aven/item/embeddable.rb +141 -0
  81. data/app/models/aven/item/linkable.rb +212 -0
  82. data/app/models/aven/item/schema/builder.rb +139 -0
  83. data/app/models/aven/item/schemaed.rb +252 -0
  84. data/app/models/aven/item/schemas/base.rb +108 -0
  85. data/app/models/aven/item.rb +128 -0
  86. data/app/models/aven/item_link.rb +43 -0
  87. data/app/models/aven/item_schema.rb +87 -0
  88. data/app/models/aven/log.rb +66 -0
  89. data/app/models/aven/loggable.rb +20 -0
  90. data/app/models/aven/user.rb +40 -0
  91. data/app/models/aven/workspace.rb +93 -0
  92. data/app/models/aven/workspace_role.rb +46 -0
  93. data/app/models/aven/workspace_user.rb +54 -0
  94. data/app/models/aven/workspace_user_role.rb +38 -0
  95. data/app/models/concerns/aven/agentic/document_embeddable.rb +58 -0
  96. data/app/models/concerns/aven/searchable.rb +61 -0
  97. data/app/services/aven/agentic/dynamic_tool_builder.rb +81 -0
  98. data/app/services/aven/agentic/mcp/adapter.rb +77 -0
  99. data/app/services/aven/agentic/mcp/result_formatter.rb +57 -0
  100. data/app/services/aven/agentic/mcp/server_factory.rb +43 -0
  101. data/app/services/aven/agentic/ocr/base_extractor.rb +39 -0
  102. data/app/services/aven/agentic/ocr/excel_extractor.rb +43 -0
  103. data/app/services/aven/agentic/ocr/image_extractor.rb +22 -0
  104. data/app/services/aven/agentic/ocr/pdf_extractor.rb +48 -0
  105. data/app/services/aven/agentic/ocr/processor.rb +36 -0
  106. data/app/services/aven/agentic/ocr/textract_client.rb +131 -0
  107. data/app/services/aven/agentic/ocr/word_extractor.rb +34 -0
  108. data/app/services/aven/agentic/tool_result_formatter.rb +76 -0
  109. data/app/services/aven/agentic/tools/base.rb +55 -0
  110. data/app/services/aven/agentic/tools/concerns/boolean_filtering.rb +40 -0
  111. data/app/services/aven/agentic/tools/concerns/enum_filtering.rb +47 -0
  112. data/app/services/aven/agentic/tools/concerns/geo_filtering.rb +56 -0
  113. data/app/services/aven/agentic/tools/concerns/range_filtering.rb +51 -0
  114. data/app/services/aven/chat/broadcaster.rb +59 -0
  115. data/app/services/aven/chat/config.rb +93 -0
  116. data/app/services/aven/chat/message_builder.rb +42 -0
  117. data/app/services/aven/chat/orchestrator.rb +69 -0
  118. data/app/services/aven/chat/runner.rb +105 -0
  119. data/app/services/aven/chat/title_generator.rb +61 -0
  120. data/app/services/aven/external/gmail_client.rb +173 -0
  121. data/app/services/aven/external/google_contacts_client.rb +95 -0
  122. data/app/views/layouts/aven/admin.html.erb +16 -0
  123. data/app/views/layouts/aven/application.html.erb +18 -0
  124. data/config/importmap.rb +16 -0
  125. data/config/routes.rb +63 -0
  126. data/db/migrate/20200101000001_create_aven_users.rb +19 -0
  127. data/db/migrate/20200101000002_create_aven_workspaces.rb +14 -0
  128. data/db/migrate/20200101000003_create_aven_workspace_users.rb +12 -0
  129. data/db/migrate/20200101000004_create_aven_workspace_roles.rb +13 -0
  130. data/db/migrate/20200101000005_create_aven_workspace_user_roles.rb +12 -0
  131. data/db/migrate/20200101000006_create_aven_logs.rb +21 -0
  132. data/db/migrate/20200101000009_create_aven_items.rb +17 -0
  133. data/db/migrate/20200101000010_create_aven_item_links.rb +17 -0
  134. data/db/migrate/20200101000011_create_aven_agentic_tools.rb +19 -0
  135. data/db/migrate/20200101000012_create_aven_agentic_tool_parameters.rb +20 -0
  136. data/db/migrate/20200101000013_create_aven_agentic_documents.rb +22 -0
  137. data/db/migrate/20200101000014_create_aven_agentic_document_embeddings.rb +18 -0
  138. data/db/migrate/20200101000015_create_aven_agentic_agents.rb +18 -0
  139. data/db/migrate/20200101000016_create_aven_agentic_agent_tools.rb +13 -0
  140. data/db/migrate/20200101000017_create_aven_agentic_agent_documents.rb +13 -0
  141. data/db/migrate/20200101000018_create_aven_chat_threads.rb +19 -0
  142. data/db/migrate/20200101000019_create_aven_chat_messages.rb +26 -0
  143. data/db/migrate/20200101000020_add_pg_search_support.rb +21 -0
  144. data/db/migrate/20200101000021_create_aven_item_schemas.rb +18 -0
  145. data/db/migrate/20200101000022_create_aven_imports.rb +23 -0
  146. data/db/migrate/20200101000023_create_aven_import_entries.rb +13 -0
  147. data/db/migrate/20200101000024_create_aven_import_item_links.rb +13 -0
  148. data/db/migrate/20200101000025_create_aven_articles.rb +19 -0
  149. data/db/migrate/20200101000026_create_aven_article_attachments.rb +13 -0
  150. data/db/migrate/20200101000027_create_aven_article_relationships.rb +15 -0
  151. data/lib/aven/configuration.rb +87 -0
  152. data/lib/aven/engine.rb +43 -0
  153. data/lib/aven/model/tenant_model.rb +91 -0
  154. data/lib/aven/model.rb +6 -0
  155. data/lib/aven/version.rb +3 -0
  156. data/lib/aven.rb +8 -0
  157. data/lib/tasks/annotate_rb.rake +10 -0
  158. data/lib/tasks/aven_tasks.rake +21 -0
  159. metadata +426 -0
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenAgenticAgents < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_agentic_agents do |t|
6
+ t.references :workspace, null: false, foreign_key: { to_table: :aven_workspaces }
7
+ t.string :label, null: false
8
+ t.string :slug
9
+ t.text :system_prompt
10
+ t.text :user_facing_question
11
+ t.boolean :enabled, default: true, null: false
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :aven_agentic_agents, [:workspace_id, :slug], unique: true
16
+ add_index :aven_agentic_agents, :enabled
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenAgenticAgentTools < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_agentic_agent_tools do |t|
6
+ t.references :agent, null: false, foreign_key: { to_table: :aven_agentic_agents }
7
+ t.references :tool, null: false, foreign_key: { to_table: :aven_agentic_tools }
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :aven_agentic_agent_tools, [:agent_id, :tool_id], unique: true
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenAgenticAgentDocuments < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_agentic_agent_documents do |t|
6
+ t.references :agent, null: false, foreign_key: { to_table: :aven_agentic_agents }
7
+ t.references :document, null: false, foreign_key: { to_table: :aven_agentic_documents }
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :aven_agentic_agent_documents, [:agent_id, :document_id], unique: true
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenChatThreads < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_chat_threads do |t|
6
+ t.references :workspace, null: false, foreign_key: { to_table: :aven_workspaces }
7
+ t.references :user, null: false, foreign_key: { to_table: :aven_users }
8
+ t.references :agent, foreign_key: { to_table: :aven_agentic_agents }
9
+ t.string :title
10
+ t.jsonb :tools, default: nil # Locked tool names array
11
+ t.jsonb :documents, default: nil # Locked document IDs array
12
+ t.text :context_markdown
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :aven_chat_threads, [:workspace_id, :user_id]
17
+ add_index :aven_chat_threads, :created_at
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenChatMessages < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_chat_messages do |t|
6
+ t.references :thread, null: false, foreign_key: { to_table: :aven_chat_threads }
7
+ t.references :parent, foreign_key: { to_table: :aven_chat_messages }
8
+ t.string :role, null: false # user, assistant, tool, system
9
+ t.string :status, default: "pending" # pending, streaming, success, error
10
+ t.text :content
11
+ t.string :model
12
+ t.integer :input_tokens, default: 0
13
+ t.integer :output_tokens, default: 0
14
+ t.integer :total_tokens, default: 0
15
+ t.decimal :cost_usd, precision: 10, scale: 6, default: 0.0
16
+ t.datetime :started_at
17
+ t.datetime :completed_at
18
+ t.jsonb :tool_call, default: nil # Tool call details
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :aven_chat_messages, :role
23
+ add_index :aven_chat_messages, :status
24
+ add_index :aven_chat_messages, [:thread_id, :created_at]
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddPgSearchSupport < ActiveRecord::Migration[8.0]
4
+ def up
5
+ # Enable pg_trgm for fuzzy matching
6
+ enable_extension "pg_trgm"
7
+
8
+ # Create pg_search_documents table for multi-search
9
+ create_table :pg_search_documents do |t|
10
+ t.text :content
11
+ t.belongs_to :searchable, polymorphic: true, index: true
12
+ t.belongs_to :workspace, foreign_key: { to_table: :aven_workspaces }, index: true
13
+ t.timestamps null: false
14
+ end
15
+ end
16
+
17
+ def down
18
+ drop_table :pg_search_documents
19
+ disable_extension "pg_trgm"
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenItemSchemas < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_item_schemas do |t|
6
+ t.references :workspace, null: false, foreign_key: { to_table: :aven_workspaces }
7
+ t.string :slug, null: false
8
+ t.jsonb :schema, null: false, default: {}
9
+ t.jsonb :fields, null: false, default: {}
10
+ t.jsonb :embeds, null: false, default: {}
11
+ t.jsonb :links, null: false, default: {}
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :aven_item_schemas, [:workspace_id, :slug], unique: true
16
+ add_index :aven_item_schemas, :slug
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenImports < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_imports do |t|
6
+ t.references :workspace, null: false, foreign_key: { to_table: :aven_workspaces }
7
+ t.string :source, null: false
8
+ t.string :status, null: false, default: "pending"
9
+ t.integer :total_count, default: 0
10
+ t.integer :processed_count, default: 0
11
+ t.integer :imported_count, default: 0
12
+ t.integer :skipped_count, default: 0
13
+ t.text :error_message
14
+ t.jsonb :errors_log, default: []
15
+ t.datetime :started_at
16
+ t.datetime :completed_at
17
+ t.timestamps
18
+ end
19
+
20
+ add_index :aven_imports, :source
21
+ add_index :aven_imports, :status
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenImportEntries < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_import_entries do |t|
6
+ t.references :import, null: false, foreign_key: { to_table: :aven_imports }
7
+ t.jsonb :data, null: false, default: {}
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :aven_import_entries, :data, using: :gin
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenImportItemLinks < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_import_item_links do |t|
6
+ t.references :entry, null: false, foreign_key: { to_table: :aven_import_entries }
7
+ t.references :item, null: false, foreign_key: { to_table: :aven_items }
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :aven_import_item_links, [:entry_id, :item_id], unique: true
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenArticles < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_articles do |t|
6
+ t.references :workspace, null: false, foreign_key: { to_table: :aven_workspaces }
7
+ t.references :author, null: true, foreign_key: { to_table: :aven_users }
8
+ t.string :title, null: false
9
+ t.string :slug
10
+ t.text :intro
11
+ t.text :description
12
+ t.datetime :published_at
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :aven_articles, [:workspace_id, :slug], unique: true
17
+ add_index :aven_articles, :published_at
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenArticleAttachments < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_article_attachments do |t|
6
+ t.references :article, null: false, foreign_key: { to_table: :aven_articles }
7
+ t.integer :position, default: 0, null: false
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :aven_article_attachments, [:article_id, :position]
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateAvenArticleRelationships < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :aven_article_relationships do |t|
6
+ t.references :article, null: false, foreign_key: { to_table: :aven_articles }
7
+ t.references :related_article, null: false, foreign_key: { to_table: :aven_articles }
8
+ t.integer :position, default: 0
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :aven_article_relationships, [:article_id, :related_article_id], unique: true, name: "idx_article_relationships_unique"
13
+ add_index :aven_article_relationships, [:article_id, :position]
14
+ end
15
+ end
@@ -0,0 +1,87 @@
1
+ module Aven
2
+ class Configuration
3
+ attr_accessor :authenticated_root_path
4
+ attr_accessor :oauth_providers
5
+ attr_accessor :agentic
6
+ attr_accessor :ocr
7
+
8
+ def initialize
9
+ @authenticated_root_path = nil
10
+ @oauth_providers = {}
11
+ @agentic = AgenticConfiguration.new
12
+ @ocr = OcrConfiguration.new
13
+ end
14
+
15
+ # Configure OAuth providers
16
+ #
17
+ # @param provider [Symbol] The OAuth provider name (:github, :google, etc.)
18
+ # @param credentials [Hash] Configuration hash with:
19
+ # - :client_id [String] OAuth client ID
20
+ # - :client_secret [String] OAuth client secret
21
+ # - :scope [String] Optional. OAuth scopes to request
22
+ # - Any other provider-specific options
23
+ #
24
+ # @example
25
+ # config.configure_oauth(:github, {
26
+ # client_id: "abc123",
27
+ # client_secret: "secret",
28
+ # scope: "user:email,repo,workflow"
29
+ # })
30
+ def configure_oauth(provider, credentials = {})
31
+ @oauth_providers[provider.to_sym] = credentials
32
+ end
33
+
34
+ # Resolves authenticated_root_path, calling it if it's a lambda/proc
35
+ #
36
+ # @return [String] The resolved path
37
+ def resolve_authenticated_root_path
38
+ return nil if @authenticated_root_path.nil?
39
+
40
+ @authenticated_root_path.respond_to?(:call) ? @authenticated_root_path.call : @authenticated_root_path
41
+ end
42
+ end
43
+
44
+ # Agentic configuration (LLM, tools, chat)
45
+ class AgenticConfiguration
46
+ attr_accessor :default_model
47
+ attr_accessor :system_prompt
48
+ attr_accessor :tools_enabled
49
+ attr_accessor :mcp_enabled
50
+ attr_accessor :mcp_api_token
51
+
52
+ def initialize
53
+ @default_model = "claude-sonnet-4-5-20250929"
54
+ @system_prompt = "You are a helpful assistant."
55
+ @tools_enabled = true
56
+ @mcp_enabled = false
57
+ @mcp_api_token = nil
58
+ end
59
+ end
60
+
61
+ # OCR configuration
62
+ class OcrConfiguration
63
+ attr_accessor :provider
64
+ attr_accessor :aws_region
65
+ attr_accessor :aws_access_key_id
66
+ attr_accessor :aws_secret_access_key
67
+
68
+ def initialize
69
+ @provider = nil # :textract, :google_vision, etc.
70
+ @aws_region = nil
71
+ @aws_access_key_id = nil
72
+ @aws_secret_access_key = nil
73
+ end
74
+ end
75
+
76
+ class << self
77
+ attr_writer :configuration
78
+
79
+ def configuration
80
+ @configuration ||= Configuration.new
81
+ end
82
+
83
+ def configure
84
+ yield(configuration)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,43 @@
1
+ require "importmap-rails"
2
+ require "view_component-contrib"
3
+ require "dry-effects"
4
+ require "json_skooma"
5
+ require "aeno"
6
+ require "friendly_id"
7
+ require "acts-as-taggable-on"
8
+ require "aven/model"
9
+
10
+ module Aven
11
+ class << self
12
+ attr_accessor :importmap
13
+ end
14
+
15
+ class Engine < ::Rails::Engine
16
+ isolate_namespace Aven
17
+
18
+ Aeno::EngineHelpers.setup_assets(self, namespace: Aven)
19
+ Aeno::EngineHelpers.setup_importmap(self, namespace: Aven)
20
+
21
+ # Append engine migrations to the main app
22
+ initializer :append_migrations do |app|
23
+ unless app.root.to_s.include?("test/dummy")
24
+ config.paths["db/migrate"].expanded.each do |expanded_path|
25
+ app.config.paths["db/migrate"] << expanded_path
26
+ end
27
+ end
28
+ end
29
+
30
+ # Include engine route helpers, authentication, and controller helpers in controllers and views
31
+ initializer "aven.helpers" do
32
+ ActiveSupport.on_load(:action_controller) do
33
+ include Aven::Engine.routes.url_helpers
34
+ include Aven::Authentication
35
+ include Aven::ControllerHelpers
36
+ end
37
+
38
+ ActiveSupport.on_load(:action_view) do
39
+ include Aven::Engine.routes.url_helpers
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,91 @@
1
+ module Aven
2
+ module Model
3
+ # TenantModel provides workspace multi-tenancy support for ActiveRecord models.
4
+ #
5
+ # Usage:
6
+ # class Server < ApplicationRecord
7
+ # include Aven::TenantModel
8
+ # end
9
+ #
10
+ # This will:
11
+ # - Add belongs_to :workspace association
12
+ # - Validate workspace_id column exists
13
+ # - Register model with Aven::Workspace
14
+ # - Add workspace scoping helpers
15
+ #
16
+ # Inspired by Flipper's Model::ActiveRecord pattern.
17
+ module TenantModel
18
+ extend ActiveSupport::Concern
19
+
20
+ included do
21
+ # Validate workspace_id column exists at include time (skip during migrations)
22
+ if table_exists? && !column_names.include?("workspace_id")
23
+ raise ArgumentError,
24
+ "#{name} includes Aven::TenantModel but does not have a workspace_id column. " \
25
+ "Add a workspace_id column to #{table_name} first."
26
+ end
27
+
28
+ # Add belongs_to association if not already defined
29
+ unless reflect_on_association(:workspace)
30
+ belongs_to :workspace, class_name: "Aven::Workspace"
31
+ end
32
+
33
+ # Register this model as a tenant model
34
+ Aven::Workspace.register_tenant_model(self)
35
+
36
+ # Add scopes for workspace querying
37
+ scope :in_workspace, ->(workspace) { where(workspace_id: workspace.id) }
38
+ scope :for_workspace, ->(workspace) { where(workspace_id: workspace.id) }
39
+ end
40
+
41
+ # Returns a unique identifier combining class name and workspace_id
42
+ # Useful for caching keys, logging, permissions, etc.
43
+ #
44
+ # Example:
45
+ # server.workspace_tenant_id #=> "Server;123"
46
+ def workspace_tenant_id
47
+ "#{self.class.base_class.name};#{workspace_id}"
48
+ end
49
+
50
+ # Check if this model is workspace-scoped
51
+ def workspace_scoped?
52
+ true
53
+ end
54
+
55
+ # Returns workspace association name for this model type
56
+ def workspace_association_name
57
+ self.class.workspace_association_name
58
+ end
59
+
60
+ module ClassMethods
61
+ # Make workspace association optional
62
+ # Call this in your model if workspace can be nil
63
+ #
64
+ # Example:
65
+ # class DnsCredential < ApplicationRecord
66
+ # include Aven::TenantModel
67
+ # workspace_optional!
68
+ # end
69
+ def workspace_optional!
70
+ _reflect_on_association(:workspace).options[:optional] = true
71
+ end
72
+
73
+ # Returns the association name that Workspace will use for this model
74
+ # Example: Server => :servers, DnsCredential => :dns_credentials
75
+ def workspace_association_name
76
+ name.underscore.pluralize.to_sym
77
+ end
78
+
79
+ # Check if this model has a unique workspace constraint
80
+ # Used to determine if Workspace should use has_one vs has_many
81
+ def unique_per_workspace?
82
+ return false unless table_exists?
83
+
84
+ connection.indexes(table_name).any? { |idx|
85
+ idx.unique && idx.columns == [ "workspace_id" ]
86
+ }
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
data/lib/aven/model.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "aven/model/tenant_model"
2
+
3
+ module Aven
4
+ module Model
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Aven
2
+ VERSION = "0.0.3"
3
+ end
data/lib/aven.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "aven/version"
2
+ require "aven/configuration"
3
+ require "aven/engine"
4
+ require "neighbor"
5
+ require "ruby_llm"
6
+
7
+ module Aven
8
+ end
@@ -0,0 +1,10 @@
1
+ # This rake task was added by annotate_rb gem.
2
+
3
+ # Can set `ANNOTATERB_SKIP_ON_DB_TASKS` to be anything to skip this
4
+ is_dummy = Rails.application.class.module_parent_name == "Dummy"
5
+
6
+ if Rails.env.development? && ENV["ANNOTATERB_SKIP_ON_DB_TASKS"].nil? && is_dummy
7
+ require "annotate_rb"
8
+
9
+ AnnotateRb::Core.load_rake_tasks
10
+ end
@@ -0,0 +1,21 @@
1
+ # desc "Explaining what the task does"
2
+ namespace :aven do
3
+ desc "run tailwind"
4
+ task :tailwind_engine_watch do
5
+ require "tailwindcss-rails"
6
+
7
+ command = [
8
+ Tailwindcss::Commands.compile_command.first,
9
+ "-i", Aven::Engine.root.join("app/assets/stylesheets/aven/application.tailwind.css").to_s,
10
+ "-o", Aven::Engine.root.join("app/assets/stylesheets/aven/tailwind.css").to_s,
11
+ "-w",
12
+ "--content", [
13
+ Aven::Engine.root.join("app/components/**/*.rb").to_s,
14
+ Aven::Engine.root.join("app/components/**/*.erb").to_s
15
+ ].join(",")
16
+ ]
17
+
18
+ p command
19
+ system(*command)
20
+ end
21
+ end