openclacky 1.0.0 → 1.0.2

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/README.md +87 -53
  4. data/lib/clacky/agent/cost_tracker.rb +19 -2
  5. data/lib/clacky/agent/llm_caller.rb +218 -0
  6. data/lib/clacky/agent/message_compressor_helper.rb +32 -2
  7. data/lib/clacky/agent.rb +54 -22
  8. data/lib/clacky/client.rb +44 -5
  9. data/lib/clacky/default_parsers/pdf_parser.rb +58 -17
  10. data/lib/clacky/default_parsers/pdf_parser_ocr.py +103 -0
  11. data/lib/clacky/default_parsers/pdf_parser_plumber.py +62 -0
  12. data/lib/clacky/default_skills/deploy/SKILL.md +201 -77
  13. data/lib/clacky/default_skills/new/SKILL.md +3 -114
  14. data/lib/clacky/default_skills/onboard/SKILL.md +349 -133
  15. data/lib/clacky/default_skills/onboard/scripts/import_external_skills.rb +371 -0
  16. data/lib/clacky/default_skills/onboard/scripts/install_builtin_skills.rb +175 -0
  17. data/lib/clacky/default_skills/skill-add/scripts/install_from_zip.rb +59 -26
  18. data/lib/clacky/message_format/anthropic.rb +72 -8
  19. data/lib/clacky/message_format/bedrock.rb +6 -3
  20. data/lib/clacky/providers.rb +146 -3
  21. data/lib/clacky/server/channel/adapters/feishu/adapter.rb +14 -0
  22. data/lib/clacky/server/channel/adapters/feishu/bot.rb +10 -0
  23. data/lib/clacky/server/channel/adapters/feishu/message_parser.rb +1 -0
  24. data/lib/clacky/server/channel/channel_manager.rb +12 -4
  25. data/lib/clacky/server/channel/channel_ui_controller.rb +8 -2
  26. data/lib/clacky/server/http_server.rb +746 -13
  27. data/lib/clacky/server/session_registry.rb +55 -24
  28. data/lib/clacky/skill.rb +10 -9
  29. data/lib/clacky/skill_loader.rb +23 -11
  30. data/lib/clacky/tools/file_reader.rb +232 -127
  31. data/lib/clacky/tools/security.rb +42 -64
  32. data/lib/clacky/tools/terminal/persistent_session.rb +15 -4
  33. data/lib/clacky/tools/terminal/safe_rm.sh +106 -0
  34. data/lib/clacky/tools/terminal/session_manager.rb +8 -3
  35. data/lib/clacky/tools/terminal.rb +263 -16
  36. data/lib/clacky/ui2/layout_manager.rb +8 -1
  37. data/lib/clacky/ui2/output_buffer.rb +83 -23
  38. data/lib/clacky/ui2/ui_controller.rb +74 -7
  39. data/lib/clacky/utils/file_processor.rb +14 -40
  40. data/lib/clacky/utils/model_pricing.rb +215 -0
  41. data/lib/clacky/utils/parser_manager.rb +70 -6
  42. data/lib/clacky/utils/string_matcher.rb +23 -1
  43. data/lib/clacky/version.rb +1 -1
  44. data/lib/clacky/web/app.css +673 -9
  45. data/lib/clacky/web/app.js +40 -1608
  46. data/lib/clacky/web/i18n.js +209 -0
  47. data/lib/clacky/web/index.html +166 -2
  48. data/lib/clacky/web/onboard.js +77 -1
  49. data/lib/clacky/web/profile.js +442 -0
  50. data/lib/clacky/web/sessions.js +1034 -2
  51. data/lib/clacky/web/settings.js +127 -6
  52. data/lib/clacky/web/sidebar.js +39 -0
  53. data/lib/clacky/web/skills.js +460 -0
  54. data/lib/clacky/web/trash.js +343 -0
  55. data/lib/clacky/web/ws-dispatcher.js +255 -0
  56. data/lib/clacky.rb +5 -3
  57. metadata +16 -17
  58. data/lib/clacky/clacky_auth_client.rb +0 -152
  59. data/lib/clacky/clacky_cloud_config.rb +0 -123
  60. data/lib/clacky/cloud_project_client.rb +0 -169
  61. data/lib/clacky/default_skills/deploy/scripts/rails_deploy.rb +0 -1377
  62. data/lib/clacky/default_skills/deploy/tools/check_health.rb +0 -116
  63. data/lib/clacky/default_skills/deploy/tools/create_database_service.rb +0 -341
  64. data/lib/clacky/default_skills/deploy/tools/execute_deployment.rb +0 -99
  65. data/lib/clacky/default_skills/deploy/tools/fetch_runtime_logs.rb +0 -77
  66. data/lib/clacky/default_skills/deploy/tools/list_services.rb +0 -67
  67. data/lib/clacky/default_skills/deploy/tools/report_deploy_status.rb +0 -67
  68. data/lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb +0 -189
  69. data/lib/clacky/default_skills/new/scripts/cloud_project_init.sh +0 -74
  70. data/lib/clacky/deploy_api_client.rb +0 -484
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Clacky
4
- module DeployTools
5
- # Report deployment status to user with formatted output
6
- class ReportDeployStatus
7
- VALID_STATUSES = %w[analyzing deploying checking success failed].freeze
8
-
9
- STATUS_ICONS = {
10
- 'analyzing' => '🔍',
11
- 'deploying' => '🚀',
12
- 'checking' => '✅',
13
- 'success' => '🎉',
14
- 'failed' => '❌'
15
- }.freeze
16
-
17
- STATUS_COLORS = {
18
- 'analyzing' => :cyan,
19
- 'deploying' => :yellow,
20
- 'checking' => :blue,
21
- 'success' => :green,
22
- 'failed' => :red
23
- }.freeze
24
-
25
- # Execute the report_deploy_status command
26
- #
27
- # @param status [String] Deployment status (analyzing, deploying, checking, success, failed)
28
- # @param message [String] Status message to display
29
- # @return [Hash] Result of the report operation
30
- def self.execute(status:, message:)
31
- unless VALID_STATUSES.include?(status)
32
- return {
33
- error: "Invalid status",
34
- details: "Status must be one of: #{VALID_STATUSES.join(', ')}",
35
- provided: status
36
- }
37
- end
38
-
39
- icon = STATUS_ICONS[status]
40
- formatted_message = format_message(status, message, icon)
41
-
42
- # Output to stdout
43
- puts formatted_message
44
-
45
- {
46
- success: true,
47
- status: status,
48
- message: message,
49
- timestamp: Time.now.iso8601
50
- }
51
- end
52
-
53
- # Format the status message with icon and styling
54
- #
55
- # @param status [String] Deployment status
56
- # @param message [String] Status message
57
- # @param icon [String] Emoji icon for status
58
- # @return [String] Formatted message
59
- def self.format_message(status, message, icon)
60
- timestamp = Time.now.strftime("%H:%M:%S")
61
- status_label = status.upcase.ljust(10)
62
-
63
- "#{icon} [#{timestamp}] #{status_label} #{message}"
64
- end
65
- end
66
- end
67
- end
@@ -1,189 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "open3"
4
-
5
- module Clacky
6
- module DeployTools
7
- # Set environment variables on a Railway service via `railway variables --set`.
8
- # Uses RAILWAY_TOKEN passed through environment — no clackycli wrapper needed.
9
- #
10
- # Supports both normal key=value pairs and Railway inter-service references
11
- # like ${{postgres.DATABASE_PUBLIC_URL}} (pass raw_value: true to skip escaping).
12
- class SetDeployVariables
13
-
14
- SENSITIVE_PATTERNS = [
15
- /password/i, /secret/i, /api_key/i,
16
- /token/i, /credential/i, /private_key/i
17
- ].freeze
18
-
19
- # Maximum number of variables to set in a single batch call
20
- BATCH_SIZE = 20
21
-
22
- # Retry config for transient failures
23
- MAX_RETRIES = 3
24
- RETRY_DELAY = 2 # seconds
25
-
26
- # Set one or more environment variables on a Railway service.
27
- # Batches all variables into a single `railway variables` call to minimize
28
- # network connections and avoid SSL reset issues.
29
- #
30
- # @param service_name [String] Railway service name
31
- # @param variables [Hash] KEY => VALUE pairs
32
- # @param platform_token [String] RAILWAY_TOKEN for this deploy task
33
- # @param raw_value [Boolean] when true, values are passed unquoted
34
- # (for Railway ${{...}} references)
35
- # @return [Hash] {
36
- # success: Boolean,
37
- # set_variables: Array<String>,
38
- # errors: Array<Hash>
39
- # }
40
- def self.execute(service_name:, variables:, platform_token:, raw_value: false)
41
- if service_name.nil? || service_name.strip.empty?
42
- return { success: false, error: "service_name is required" }
43
- end
44
-
45
- env = ENV.to_h.merge("RAILWAY_TOKEN" => platform_token)
46
-
47
- # Log all variables being set
48
- variables.each do |key, value|
49
- log_value = sensitive?(key) ? "******" : value
50
- puts " Setting #{key}=#{log_value}"
51
- end
52
-
53
- # Split into batches to avoid command line length limits
54
- set_vars = []
55
- error_list = []
56
- var_pairs = variables.map { |k, v| [k.to_s, v.to_s] }
57
-
58
- var_pairs.each_slice(BATCH_SIZE) do |batch|
59
- result = set_batch(env, service_name, batch, raw_value: raw_value)
60
- if result[:success]
61
- set_vars.concat(batch.map(&:first))
62
- else
63
- # Retry logic: attempt individual vars if batch fails
64
- batch.each do |key, value|
65
- individual = set_one_with_retry(env, service_name, key, value, raw_value: raw_value)
66
- if individual[:success]
67
- set_vars << key
68
- else
69
- error_list << { key: key, error: individual[:error] }
70
- end
71
- end
72
- end
73
- end
74
-
75
- {
76
- success: error_list.empty?,
77
- set_variables: set_vars,
78
- errors: error_list
79
- }
80
- end
81
-
82
- # Set a batch of variables in a single railway command call.
83
- #
84
- # @param env [Hash] environment variables
85
- # @param service_name [String] Railway service name
86
- # @param pairs [Array<Array>] [[key, value], ...]
87
- # @return [Hash] { success: true } or { success: false, error: String }
88
- def self.set_batch(env, service_name, pairs, raw_value: false)
89
- # Each --set argument is passed as a separate array element
90
- set_flags = pairs.flat_map { |key, value| ["--set", "#{key}=#{value}"] }
91
- cmd = ["railway", "variables", "--service", service_name, "--skip-deploys"] + set_flags
92
-
93
- # Debug: print the full command being executed
94
- puts " [DEBUG] Executing Railway CLI command:"
95
- puts " [DEBUG] Array form: #{cmd.inspect}"
96
- puts " [DEBUG] Shell form: #{cmd.join(' ')}"
97
- puts " [DEBUG] with RAILWAY_TOKEN=#{env['RAILWAY_TOKEN']}" if env['RAILWAY_TOKEN']
98
- $stdout.flush
99
-
100
- # Use system() instead of Open3.capture3 to avoid stdin/stdout blocking issues
101
- # system() inherits the current process's stdin/stdout/stderr directly
102
- require 'timeout'
103
-
104
- begin
105
- success = Timeout.timeout(30) do
106
- # Close stdin, suppress stdout, but keep stderr visible
107
- system(env, *cmd, in: :close, out: File::NULL)
108
- end
109
- rescue Timeout::Error
110
- return { success: false, error: "Railway CLI command timed out after 30 seconds" }
111
- end
112
-
113
- if success
114
- { success: true }
115
- else
116
- { success: false, error: "railway variables command failed (exit code: #{$?.exitstatus})" }
117
- end
118
- end
119
-
120
- # Set a single variable with retry logic for transient network errors.
121
- #
122
- # @return [Hash] { success: true } or { success: false, error: String }
123
- def self.set_one_with_retry(env, service_name, key, value, raw_value: false)
124
- last_error = nil
125
-
126
- MAX_RETRIES.times do |attempt|
127
- result = set_one(env, service_name, key, value, raw_value: raw_value)
128
- return result if result[:success]
129
-
130
- last_error = result[:error]
131
- # Only retry on connection/SSL errors
132
- break unless last_error.to_s =~ /connection|ssl|reset|timeout|network/i
133
-
134
- puts " ⚠️ Retrying #{key} (attempt #{attempt + 2}/#{MAX_RETRIES})..." if attempt < MAX_RETRIES - 1
135
- sleep RETRY_DELAY
136
- end
137
-
138
- { success: false, error: last_error }
139
- end
140
-
141
- # Set a single variable. Builds the `railway variables --set` command.
142
- #
143
- # @return [Hash] { success: true } or { success: false, error: String }
144
- def self.set_one(env, service_name, key, value, raw_value: false)
145
- assignment = "#{key}=#{value}"
146
-
147
- cmd = [
148
- "railway", "variables",
149
- "--service", service_name,
150
- "--skip-deploys",
151
- "--set", assignment
152
- ]
153
-
154
- # Debug: print the full command being executed
155
- puts " [DEBUG] Executing single var Railway CLI command:"
156
- puts " [DEBUG] Array form: #{cmd.inspect}"
157
- puts " [DEBUG] Shell form: #{cmd.join(' ')}"
158
- puts " [DEBUG] with RAILWAY_TOKEN=#{env['RAILWAY_TOKEN']}" if env['RAILWAY_TOKEN']
159
- $stdout.flush
160
-
161
- # Use system() instead of Open3.capture3 to avoid stdin/stdout blocking issues
162
- require 'timeout'
163
-
164
- begin
165
- success = Timeout.timeout(30) do
166
- # Close stdin, suppress stdout, but keep stderr visible
167
- system(env, *cmd, in: :close, out: File::NULL)
168
- end
169
- rescue Timeout::Error
170
- return { success: false, error: "Railway CLI command timed out after 30 seconds" }
171
- end
172
-
173
- if success
174
- { success: true }
175
- else
176
- { success: false, error: "railway variables command failed (exit code: #{$?.exitstatus})" }
177
- end
178
- end
179
-
180
- private_class_method def self.sensitive?(key)
181
- SENSITIVE_PATTERNS.any? { |pat| key.match?(pat) }
182
- end
183
-
184
- private_class_method def self.shell_escape(str)
185
- "'#{str.to_s.gsub("'", "'\\\\''")}'"
186
- end
187
- end
188
- end
189
- end
@@ -1,74 +0,0 @@
1
- #!/bin/bash
2
- # Cloud Project Init Script
3
- # Connects the local Rails project to the Clacky cloud platform.
4
- #
5
- # Usage: cloud_project_init.sh [project_name] [workspace_key] [base_url]
6
- # - project_name: defaults to current directory name
7
- # - workspace_key: defaults to value in ~/.clacky/clacky_cloud.yml
8
- # - base_url: defaults to https://api.clacky.ai
9
- #
10
- # Outputs a JSON result on stdout:
11
- # { "success": true, "project_id": "...", "project_name": "..." }
12
- # { "success": false, "error": "..." }
13
-
14
- set -e
15
-
16
- SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
17
- GEM_LIB_DIR="$( cd "$SCRIPT_DIR/../../../.." && pwd )"
18
-
19
- PROJECT_NAME="${1:-$(basename "$PWD")}"
20
- WORKSPACE_KEY="${2:-}"
21
- BASE_URL="${3:-}"
22
-
23
- # --- Load workspace_key from clacky_cloud.yml if not provided ---
24
- if [ -z "$WORKSPACE_KEY" ]; then
25
- PLATFORM_YML="$HOME/.clacky/clacky_cloud.yml"
26
- if [ -f "$PLATFORM_YML" ]; then
27
- WORKSPACE_KEY=$(ruby -e "require 'yaml'; y = YAML.safe_load(File.read('$PLATFORM_YML')); print y['workspace_key'].to_s.strip" 2>/dev/null || true)
28
- fi
29
- fi
30
-
31
- if [ -z "$BASE_URL" ]; then
32
- PLATFORM_YML="$HOME/.clacky/clacky_cloud.yml"
33
- if [ -f "$PLATFORM_YML" ]; then
34
- BASE_URL=$(ruby -e "require 'yaml'; y = YAML.safe_load(File.read('$PLATFORM_YML')); print y['base_url'].to_s.strip" 2>/dev/null || true)
35
- fi
36
- BASE_URL="${BASE_URL:-https://api.clacky.ai}"
37
- fi
38
-
39
- if [ -z "$WORKSPACE_KEY" ]; then
40
- echo '{"success":false,"error":"No workspace_key found. Please set it in ~/.clacky/clacky_cloud.yml or pass as argument."}'
41
- exit 0
42
- fi
43
-
44
- # --- Call the API via Ruby one-liner using the gem's CloudProjectClient ---
45
- RUBY_SCRIPT=$(cat <<'RUBY'
46
- require_relative ENV['GEM_LIB_DIR'] + '/clacky/cloud_project_client'
47
- require 'json'
48
-
49
- workspace_key = ENV['WORKSPACE_KEY']
50
- base_url = ENV['BASE_URL']
51
- project_name = ENV['PROJECT_NAME']
52
-
53
- client = Clacky::CloudProjectClient.new(workspace_key, base_url: base_url)
54
- result = client.create_project(name: project_name)
55
-
56
- if result[:success]
57
- project = result[:project]
58
- puts JSON.generate({
59
- success: true,
60
- project_id: project['id'],
61
- project_name: project['name'],
62
- categorized_config: project['categorized_config'] || {}
63
- })
64
- else
65
- puts JSON.generate({ success: false, error: result[:error] })
66
- end
67
- RUBY
68
- )
69
-
70
- GEM_LIB_DIR="$GEM_LIB_DIR" \
71
- WORKSPACE_KEY="$WORKSPACE_KEY" \
72
- BASE_URL="$BASE_URL" \
73
- PROJECT_NAME="$PROJECT_NAME" \
74
- ruby -e "$RUBY_SCRIPT"