pocketrb 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +32 -0
- data/LICENSE.txt +21 -0
- data/README.md +456 -0
- data/exe/pocketrb +6 -0
- data/lib/pocketrb/agent/compaction.rb +187 -0
- data/lib/pocketrb/agent/context.rb +171 -0
- data/lib/pocketrb/agent/loop.rb +276 -0
- data/lib/pocketrb/agent/spawn_tool.rb +72 -0
- data/lib/pocketrb/agent/subagent_manager.rb +196 -0
- data/lib/pocketrb/bus/events.rb +99 -0
- data/lib/pocketrb/bus/message_bus.rb +148 -0
- data/lib/pocketrb/channels/base.rb +69 -0
- data/lib/pocketrb/channels/cli.rb +109 -0
- data/lib/pocketrb/channels/telegram.rb +607 -0
- data/lib/pocketrb/channels/whatsapp.rb +242 -0
- data/lib/pocketrb/cli/base.rb +119 -0
- data/lib/pocketrb/cli/chat.rb +67 -0
- data/lib/pocketrb/cli/config.rb +52 -0
- data/lib/pocketrb/cli/cron.rb +144 -0
- data/lib/pocketrb/cli/gateway.rb +132 -0
- data/lib/pocketrb/cli/init.rb +39 -0
- data/lib/pocketrb/cli/plans.rb +28 -0
- data/lib/pocketrb/cli/skills.rb +34 -0
- data/lib/pocketrb/cli/start.rb +55 -0
- data/lib/pocketrb/cli/telegram.rb +93 -0
- data/lib/pocketrb/cli/version.rb +18 -0
- data/lib/pocketrb/cli/whatsapp.rb +60 -0
- data/lib/pocketrb/cli.rb +124 -0
- data/lib/pocketrb/config.rb +190 -0
- data/lib/pocketrb/cron/job.rb +155 -0
- data/lib/pocketrb/cron/service.rb +395 -0
- data/lib/pocketrb/heartbeat/service.rb +175 -0
- data/lib/pocketrb/mcp/client.rb +172 -0
- data/lib/pocketrb/mcp/memory_tool.rb +133 -0
- data/lib/pocketrb/media/processor.rb +258 -0
- data/lib/pocketrb/memory.rb +283 -0
- data/lib/pocketrb/planning/manager.rb +159 -0
- data/lib/pocketrb/planning/plan.rb +223 -0
- data/lib/pocketrb/planning/tool.rb +176 -0
- data/lib/pocketrb/providers/anthropic.rb +333 -0
- data/lib/pocketrb/providers/base.rb +98 -0
- data/lib/pocketrb/providers/claude_cli.rb +412 -0
- data/lib/pocketrb/providers/claude_max_proxy.rb +347 -0
- data/lib/pocketrb/providers/openrouter.rb +205 -0
- data/lib/pocketrb/providers/registry.rb +59 -0
- data/lib/pocketrb/providers/ruby_llm_provider.rb +136 -0
- data/lib/pocketrb/providers/types.rb +111 -0
- data/lib/pocketrb/session/manager.rb +192 -0
- data/lib/pocketrb/session/session.rb +204 -0
- data/lib/pocketrb/skills/builtin/github/SKILL.md +113 -0
- data/lib/pocketrb/skills/builtin/proactive/SKILL.md +101 -0
- data/lib/pocketrb/skills/builtin/reflection/SKILL.md +109 -0
- data/lib/pocketrb/skills/builtin/tmux/SKILL.md +130 -0
- data/lib/pocketrb/skills/builtin/weather/SKILL.md +130 -0
- data/lib/pocketrb/skills/create_tool.rb +115 -0
- data/lib/pocketrb/skills/loader.rb +164 -0
- data/lib/pocketrb/skills/modify_tool.rb +123 -0
- data/lib/pocketrb/skills/skill.rb +75 -0
- data/lib/pocketrb/tools/background_job_manager.rb +261 -0
- data/lib/pocketrb/tools/base.rb +118 -0
- data/lib/pocketrb/tools/browser.rb +152 -0
- data/lib/pocketrb/tools/browser_advanced.rb +470 -0
- data/lib/pocketrb/tools/browser_session.rb +167 -0
- data/lib/pocketrb/tools/cron.rb +222 -0
- data/lib/pocketrb/tools/edit_file.rb +101 -0
- data/lib/pocketrb/tools/exec.rb +194 -0
- data/lib/pocketrb/tools/jobs.rb +127 -0
- data/lib/pocketrb/tools/list_dir.rb +102 -0
- data/lib/pocketrb/tools/memory.rb +167 -0
- data/lib/pocketrb/tools/message.rb +70 -0
- data/lib/pocketrb/tools/para_memory.rb +264 -0
- data/lib/pocketrb/tools/read_file.rb +65 -0
- data/lib/pocketrb/tools/registry.rb +160 -0
- data/lib/pocketrb/tools/send_file.rb +158 -0
- data/lib/pocketrb/tools/think.rb +35 -0
- data/lib/pocketrb/tools/web_fetch.rb +150 -0
- data/lib/pocketrb/tools/web_search.rb +102 -0
- data/lib/pocketrb/tools/write_file.rb +55 -0
- data/lib/pocketrb/version.rb +5 -0
- data/lib/pocketrb.rb +75 -0
- data/pocketrb.gemspec +60 -0
- metadata +327 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# Gateway command - starts all services together
|
|
6
|
+
class Gateway < Base
|
|
7
|
+
desc "gateway", "Start the gateway with all services"
|
|
8
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
9
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider"
|
|
10
|
+
option :telegram_token, type: :string, desc: "Telegram bot token"
|
|
11
|
+
option :telegram_users, type: :array, desc: "Allowed Telegram users"
|
|
12
|
+
option :whatsapp_bridge, type: :string, default: "ws://localhost:3001", desc: "WhatsApp bridge URL"
|
|
13
|
+
option :whatsapp_users, type: :array, desc: "Allowed WhatsApp numbers"
|
|
14
|
+
option :heartbeat_interval, type: :numeric, default: 1800, desc: "Heartbeat interval in seconds"
|
|
15
|
+
option :enable_cron, type: :boolean, default: true, desc: "Enable cron service"
|
|
16
|
+
option :enable_heartbeat, type: :boolean, default: true, desc: "Enable heartbeat service"
|
|
17
|
+
option :autonomous, type: :boolean, default: false, desc: "Skip permission prompts (for sandboxed environments)"
|
|
18
|
+
|
|
19
|
+
# Starts the gateway with all configured services (Telegram, WhatsApp, cron, heartbeat)
|
|
20
|
+
# @return [void]
|
|
21
|
+
def call
|
|
22
|
+
setup_logging
|
|
23
|
+
workspace = resolve_workspace
|
|
24
|
+
memory_dir = resolve_memory_dir
|
|
25
|
+
|
|
26
|
+
config = Pocketrb::Config.load(memory_dir)
|
|
27
|
+
config[:model] = options[:model] if options[:model]
|
|
28
|
+
config[:provider] = options[:provider] if options[:provider]
|
|
29
|
+
config[:autonomous] = options[:autonomous] if options[:autonomous]
|
|
30
|
+
|
|
31
|
+
provider = create_provider(config)
|
|
32
|
+
bus = Pocketrb::Bus::MessageBus.new
|
|
33
|
+
|
|
34
|
+
agent_loop = Pocketrb::Agent::Loop.new(
|
|
35
|
+
bus: bus,
|
|
36
|
+
provider: provider,
|
|
37
|
+
workspace: workspace,
|
|
38
|
+
memory_dir: memory_dir,
|
|
39
|
+
model: config[:model],
|
|
40
|
+
max_iterations: config[:max_iterations]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
say "Starting Pocketrb Gateway", :green
|
|
44
|
+
say "Provider: #{config[:provider]}/#{config[:model]}"
|
|
45
|
+
say "Workspace: #{workspace}"
|
|
46
|
+
say "Memory: #{memory_dir}" if memory_dir != workspace
|
|
47
|
+
|
|
48
|
+
channels = []
|
|
49
|
+
services = []
|
|
50
|
+
cron_service = nil
|
|
51
|
+
|
|
52
|
+
# Start Telegram if token provided
|
|
53
|
+
telegram_token = options[:telegram_token] || ENV.fetch("TELEGRAM_BOT_TOKEN", nil)
|
|
54
|
+
if telegram_token
|
|
55
|
+
channels << Pocketrb::Channels::Telegram.new(
|
|
56
|
+
bus: bus,
|
|
57
|
+
token: telegram_token,
|
|
58
|
+
allowed_users: options[:telegram_users]
|
|
59
|
+
)
|
|
60
|
+
say " - Telegram: enabled"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Start WhatsApp if bridge available
|
|
64
|
+
if options[:whatsapp_bridge]
|
|
65
|
+
channels << Pocketrb::Channels::WhatsApp.new(
|
|
66
|
+
bus: bus,
|
|
67
|
+
bridge_url: options[:whatsapp_bridge],
|
|
68
|
+
allowed_users: options[:whatsapp_users]
|
|
69
|
+
)
|
|
70
|
+
say " - WhatsApp: enabled (#{options[:whatsapp_bridge]})"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Start Cron service
|
|
74
|
+
if options[:enable_cron]
|
|
75
|
+
cron_store = memory_dir.join(".pocketrb", "data", "cron", "jobs.json")
|
|
76
|
+
cron_service = Pocketrb::Cron::Service.new(
|
|
77
|
+
store_path: cron_store,
|
|
78
|
+
on_job: ->(job) { handle_cron_job(agent_loop, bus, job) }
|
|
79
|
+
)
|
|
80
|
+
services << cron_service
|
|
81
|
+
# Pass cron_service to tools so agent can manage jobs
|
|
82
|
+
agent_loop.tools.update_context(cron_service: cron_service)
|
|
83
|
+
say " - Cron: enabled"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Start Heartbeat service
|
|
87
|
+
if options[:enable_heartbeat]
|
|
88
|
+
heartbeat_service = Pocketrb::Heartbeat::Service.new(
|
|
89
|
+
workspace: workspace,
|
|
90
|
+
interval: options[:heartbeat_interval],
|
|
91
|
+
on_heartbeat: ->(prompt) { process_heartbeat(agent_loop, prompt) }
|
|
92
|
+
)
|
|
93
|
+
services << heartbeat_service
|
|
94
|
+
say " - Heartbeat: enabled (#{options[:heartbeat_interval]}s)"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Set up status context for /status command on Telegram channels
|
|
98
|
+
status_context = {
|
|
99
|
+
provider: provider,
|
|
100
|
+
model: config[:model],
|
|
101
|
+
sessions: agent_loop.sessions,
|
|
102
|
+
memory_dir: memory_dir,
|
|
103
|
+
cron_service: cron_service
|
|
104
|
+
}
|
|
105
|
+
channels.each do |ch|
|
|
106
|
+
ch.status_context = status_context if ch.respond_to?(:status_context=)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
say "\nPress Ctrl+C to stop\n"
|
|
110
|
+
|
|
111
|
+
Async do
|
|
112
|
+
# Start agent loop
|
|
113
|
+
agent_loop.run
|
|
114
|
+
|
|
115
|
+
# Start all channels
|
|
116
|
+
channels.each(&:run)
|
|
117
|
+
|
|
118
|
+
# Start all services
|
|
119
|
+
services.each(&:start)
|
|
120
|
+
|
|
121
|
+
# Keep running
|
|
122
|
+
sleep
|
|
123
|
+
end
|
|
124
|
+
rescue Interrupt
|
|
125
|
+
say "\nShutting down gateway...", :yellow
|
|
126
|
+
services.each(&:stop)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
default_task :call
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# Init command - initializes a new Pocketrb workspace
|
|
6
|
+
class Init < Base
|
|
7
|
+
desc "init", "Initialize a new Pocketrb workspace"
|
|
8
|
+
# Initializes a new Pocketrb workspace with config and default structure
|
|
9
|
+
# @return [void]
|
|
10
|
+
def call
|
|
11
|
+
workspace = resolve_workspace
|
|
12
|
+
config_dir = workspace.join(".pocketrb")
|
|
13
|
+
|
|
14
|
+
if config_dir.exist?
|
|
15
|
+
say "Workspace already initialized at #{workspace}", :yellow
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
FileUtils.mkdir_p(config_dir)
|
|
20
|
+
FileUtils.mkdir_p(workspace.join("skills"))
|
|
21
|
+
|
|
22
|
+
Pocketrb::Config.new(workspace: workspace).save!
|
|
23
|
+
|
|
24
|
+
# Create default TOOLS.md
|
|
25
|
+
tools_file = workspace.join("TOOLS.md")
|
|
26
|
+
File.write(tools_file, default_tools_content) unless tools_file.exist?
|
|
27
|
+
|
|
28
|
+
say "Initialized Pocketrb workspace at #{workspace}", :green
|
|
29
|
+
say " - Created .pocketrb/config.yml"
|
|
30
|
+
say " - Created skills/"
|
|
31
|
+
say "\nNext steps:"
|
|
32
|
+
say " 1. Set your API key: export ANTHROPIC_API_KEY=your-key"
|
|
33
|
+
say " 2. Start chatting: pocketrb chat"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
default_task :call
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# Plans command - lists active execution plans
|
|
6
|
+
class Plans < Base
|
|
7
|
+
desc "plans", "List active plans"
|
|
8
|
+
# Lists all active execution plans in the workspace
|
|
9
|
+
# @return [void]
|
|
10
|
+
def call
|
|
11
|
+
workspace = resolve_workspace
|
|
12
|
+
manager = Pocketrb::Planning::Manager.new(workspace: workspace)
|
|
13
|
+
|
|
14
|
+
plans = manager.list_plans
|
|
15
|
+
if plans.empty?
|
|
16
|
+
say "No plans found", :yellow
|
|
17
|
+
return
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
plans.each do |plan|
|
|
21
|
+
say "\n#{plan.to_markdown}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
default_task :call
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# Skills command - lists available skills from workspace
|
|
6
|
+
class Skills < Base
|
|
7
|
+
desc "skills", "List available skills"
|
|
8
|
+
# Lists all available skills from the workspace skills directory
|
|
9
|
+
# @return [void]
|
|
10
|
+
def call
|
|
11
|
+
workspace = resolve_workspace
|
|
12
|
+
loader = Pocketrb::Skills::Loader.new(workspace: workspace)
|
|
13
|
+
|
|
14
|
+
skills = loader.list_skills
|
|
15
|
+
if skills.empty?
|
|
16
|
+
say "No skills found in #{workspace}/skills/", :yellow
|
|
17
|
+
return
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
say "Available skills:"
|
|
21
|
+
skills.each do |skill|
|
|
22
|
+
flags = []
|
|
23
|
+
flags << "always" if skill.always?
|
|
24
|
+
flags << "triggers: #{skill.triggers.join(", ")}" if skill.triggers.any?
|
|
25
|
+
|
|
26
|
+
flag_str = flags.any? ? " (#{flags.join(", ")})" : ""
|
|
27
|
+
say " - #{skill.name}: #{skill.description}#{flag_str}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
default_task :call
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# Start command - continuous mode with CLI channel
|
|
6
|
+
class Start < Base
|
|
7
|
+
desc "start", "Start the agent in continuous mode"
|
|
8
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
9
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider (anthropic, openrouter)"
|
|
10
|
+
option :channel, type: :string, aliases: "-c", default: "cli", desc: "Channel to connect to"
|
|
11
|
+
|
|
12
|
+
# Starts the agent in continuous mode with CLI channel
|
|
13
|
+
# @return [void]
|
|
14
|
+
def call
|
|
15
|
+
setup_logging
|
|
16
|
+
workspace = resolve_workspace
|
|
17
|
+
memory_dir = resolve_memory_dir
|
|
18
|
+
|
|
19
|
+
config = Pocketrb::Config.load(memory_dir)
|
|
20
|
+
config[:model] = options[:model] if options[:model]
|
|
21
|
+
config[:provider] = options[:provider] if options[:provider]
|
|
22
|
+
|
|
23
|
+
provider = create_provider(config)
|
|
24
|
+
bus = Pocketrb::Bus::MessageBus.new
|
|
25
|
+
|
|
26
|
+
agent_loop = Pocketrb::Agent::Loop.new(
|
|
27
|
+
bus: bus,
|
|
28
|
+
provider: provider,
|
|
29
|
+
workspace: workspace,
|
|
30
|
+
memory_dir: memory_dir,
|
|
31
|
+
model: config[:model],
|
|
32
|
+
max_iterations: config[:max_iterations],
|
|
33
|
+
mcp_endpoint: config[:mcp_endpoint]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
say "Pocketrb started with #{config[:provider]}/#{config[:model]}", :green
|
|
37
|
+
say "Workspace: #{workspace}"
|
|
38
|
+
say "Memory: #{memory_dir}" if memory_dir != workspace
|
|
39
|
+
say "Press Ctrl+C to stop\n"
|
|
40
|
+
|
|
41
|
+
# Start CLI channel
|
|
42
|
+
channel = Pocketrb::Channels::CLI.new(bus: bus)
|
|
43
|
+
|
|
44
|
+
Async do
|
|
45
|
+
agent_loop.run
|
|
46
|
+
channel.run
|
|
47
|
+
end
|
|
48
|
+
rescue Interrupt
|
|
49
|
+
say "\nShutting down...", :yellow
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
default_task :call
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# Telegram command - runs agent as a Telegram bot
|
|
6
|
+
class Telegram < Base
|
|
7
|
+
desc "telegram", "Run as a Telegram bot"
|
|
8
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
9
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider"
|
|
10
|
+
option :token, type: :string, aliases: "-t", desc: "Telegram bot token (or TELEGRAM_BOT_TOKEN env)"
|
|
11
|
+
option :allowed_users, type: :array, aliases: "-u", desc: "Allowed usernames or user IDs"
|
|
12
|
+
option :enable_cron, type: :boolean, default: true, desc: "Enable cron/scheduling service"
|
|
13
|
+
option :autonomous, type: :boolean, default: false, desc: "Skip permission prompts (for sandboxed environments)"
|
|
14
|
+
|
|
15
|
+
# Runs the agent as a Telegram bot
|
|
16
|
+
# @return [void]
|
|
17
|
+
def call
|
|
18
|
+
setup_logging
|
|
19
|
+
workspace = resolve_workspace
|
|
20
|
+
memory_dir = resolve_memory_dir
|
|
21
|
+
|
|
22
|
+
token = options[:token] || ENV.fetch("TELEGRAM_BOT_TOKEN", nil)
|
|
23
|
+
unless token
|
|
24
|
+
say "Error: Telegram bot token required", :red
|
|
25
|
+
say "Set TELEGRAM_BOT_TOKEN env var or use --token", :yellow
|
|
26
|
+
exit 1
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
config = Pocketrb::Config.load(memory_dir)
|
|
30
|
+
config[:model] = options[:model] if options[:model]
|
|
31
|
+
config[:provider] = options[:provider] if options[:provider]
|
|
32
|
+
config[:autonomous] = options[:autonomous] if options[:autonomous]
|
|
33
|
+
|
|
34
|
+
provider = create_provider(config)
|
|
35
|
+
bus = Pocketrb::Bus::MessageBus.new
|
|
36
|
+
|
|
37
|
+
agent_loop = Pocketrb::Agent::Loop.new(
|
|
38
|
+
bus: bus,
|
|
39
|
+
provider: provider,
|
|
40
|
+
workspace: workspace,
|
|
41
|
+
memory_dir: memory_dir,
|
|
42
|
+
model: config[:model],
|
|
43
|
+
max_iterations: config[:max_iterations]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Enable cron service for proactive scheduling
|
|
47
|
+
cron_service = nil
|
|
48
|
+
if options[:enable_cron]
|
|
49
|
+
cron_store = memory_dir.join(".pocketrb", "data", "cron", "jobs.json")
|
|
50
|
+
cron_service = Pocketrb::Cron::Service.new(
|
|
51
|
+
store_path: cron_store,
|
|
52
|
+
on_job: ->(job) { handle_cron_job(agent_loop, bus, job) }
|
|
53
|
+
)
|
|
54
|
+
agent_loop.tools.update_context(cron_service: cron_service)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
say "Starting Pocketrb Telegram Bot", :green
|
|
58
|
+
say "Provider: #{config[:provider]}/#{config[:model]}"
|
|
59
|
+
say "Workspace: #{workspace}"
|
|
60
|
+
say "Memory: #{memory_dir}" if memory_dir != workspace
|
|
61
|
+
say "Cron: #{options[:enable_cron] ? "enabled" : "disabled"}"
|
|
62
|
+
say "Autonomous: #{options[:autonomous] ? "yes (claude_cli only)" : "no"}" if options[:autonomous]
|
|
63
|
+
say "Press Ctrl+C to stop\n"
|
|
64
|
+
|
|
65
|
+
channel = Pocketrb::Channels::Telegram.new(
|
|
66
|
+
bus: bus,
|
|
67
|
+
token: token,
|
|
68
|
+
allowed_users: options[:allowed_users]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Set up status context for /status command
|
|
72
|
+
channel.status_context = {
|
|
73
|
+
provider: provider,
|
|
74
|
+
model: config[:model],
|
|
75
|
+
sessions: agent_loop.sessions,
|
|
76
|
+
memory_dir: memory_dir,
|
|
77
|
+
cron_service: cron_service
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Async do
|
|
81
|
+
agent_loop.run
|
|
82
|
+
channel.run
|
|
83
|
+
cron_service&.start
|
|
84
|
+
end
|
|
85
|
+
rescue Interrupt
|
|
86
|
+
say "\nShutting down Telegram bot...", :yellow
|
|
87
|
+
cron_service&.stop
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
default_task :call
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# Version command - displays current Pocketrb version
|
|
6
|
+
class Version < Base
|
|
7
|
+
desc "version", "Show version"
|
|
8
|
+
# Displays the current Pocketrb version
|
|
9
|
+
# @return [void]
|
|
10
|
+
def call
|
|
11
|
+
say "Pocketrb #{Pocketrb::VERSION}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Thor doesn't support default task name, so alias it
|
|
15
|
+
default_task :call
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pocketrb
|
|
4
|
+
class CLI
|
|
5
|
+
# WhatsApp command - runs agent as a WhatsApp bot
|
|
6
|
+
class WhatsApp < Base
|
|
7
|
+
desc "whatsapp", "Run as a WhatsApp bot (requires Node.js bridge)"
|
|
8
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
9
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider"
|
|
10
|
+
option :bridge_url, type: :string, default: "ws://localhost:3001", desc: "WhatsApp bridge WebSocket URL"
|
|
11
|
+
option :allowed_users, type: :array, aliases: "-u", desc: "Allowed phone numbers"
|
|
12
|
+
|
|
13
|
+
# Runs the agent as a WhatsApp bot using a WebSocket bridge
|
|
14
|
+
# @return [void]
|
|
15
|
+
def call
|
|
16
|
+
setup_logging
|
|
17
|
+
workspace = resolve_workspace
|
|
18
|
+
memory_dir = resolve_memory_dir
|
|
19
|
+
|
|
20
|
+
config = Pocketrb::Config.load(memory_dir)
|
|
21
|
+
config[:model] = options[:model] if options[:model]
|
|
22
|
+
config[:provider] = options[:provider] if options[:provider]
|
|
23
|
+
|
|
24
|
+
provider = create_provider(config)
|
|
25
|
+
bus = Pocketrb::Bus::MessageBus.new
|
|
26
|
+
|
|
27
|
+
agent_loop = Pocketrb::Agent::Loop.new(
|
|
28
|
+
bus: bus,
|
|
29
|
+
provider: provider,
|
|
30
|
+
workspace: workspace,
|
|
31
|
+
memory_dir: memory_dir,
|
|
32
|
+
model: config[:model],
|
|
33
|
+
max_iterations: config[:max_iterations]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
say "Starting Pocketrb WhatsApp Bot", :green
|
|
37
|
+
say "Provider: #{config[:provider]}/#{config[:model]}"
|
|
38
|
+
say "Bridge: #{options[:bridge_url]}"
|
|
39
|
+
say "Workspace: #{workspace}"
|
|
40
|
+
say "Memory: #{memory_dir}" if memory_dir != workspace
|
|
41
|
+
say "Press Ctrl+C to stop\n"
|
|
42
|
+
|
|
43
|
+
channel = Pocketrb::Channels::WhatsApp.new(
|
|
44
|
+
bus: bus,
|
|
45
|
+
bridge_url: options[:bridge_url],
|
|
46
|
+
allowed_users: options[:allowed_users]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
Async do
|
|
50
|
+
agent_loop.run
|
|
51
|
+
channel.run
|
|
52
|
+
end
|
|
53
|
+
rescue Interrupt
|
|
54
|
+
say "\nShutting down WhatsApp bot...", :yellow
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
default_task :call
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/pocketrb/cli.rb
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
# Main namespace for Pocketrb gem
|
|
6
|
+
module Pocketrb
|
|
7
|
+
# Command-line interface
|
|
8
|
+
class CLI < Thor
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
|
|
11
|
+
# Configures Thor to exit with error status on command failure
|
|
12
|
+
# @return [Boolean] true to exit on failure
|
|
13
|
+
def self.exit_on_failure?
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Global options available to all commands
|
|
18
|
+
class_option :workspace, type: :string, aliases: "-w",
|
|
19
|
+
desc: "Workspace directory for file access (default: current directory)"
|
|
20
|
+
class_option :memory_dir, type: :string, aliases: "-M",
|
|
21
|
+
desc: "Memory/persona directory (default: same as workspace)"
|
|
22
|
+
class_option :verbose, type: :boolean, aliases: "-v",
|
|
23
|
+
desc: "Enable verbose output"
|
|
24
|
+
class_option :quiet, type: :boolean, aliases: "-q",
|
|
25
|
+
desc: "Suppress non-essential output"
|
|
26
|
+
|
|
27
|
+
# Register subcommands
|
|
28
|
+
desc "config SUBCOMMAND", "Manage configuration"
|
|
29
|
+
subcommand "config", CLI::Config
|
|
30
|
+
|
|
31
|
+
desc "cron SUBCOMMAND", "Manage scheduled jobs"
|
|
32
|
+
subcommand "cron", CLI::Cron
|
|
33
|
+
|
|
34
|
+
# Register individual commands by delegating to their classes
|
|
35
|
+
desc "version", "Show version"
|
|
36
|
+
# Displays the current Pocketrb version
|
|
37
|
+
# @return [void]
|
|
38
|
+
def version
|
|
39
|
+
invoke CLI::Version, :call, [], options
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc "skills", "List available skills"
|
|
43
|
+
# Lists available skills from the workspace
|
|
44
|
+
# @return [void]
|
|
45
|
+
def skills
|
|
46
|
+
invoke CLI::Skills, :call, [], options
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
desc "plans", "List active plans"
|
|
50
|
+
# Lists all active execution plans
|
|
51
|
+
# @return [void]
|
|
52
|
+
def plans
|
|
53
|
+
invoke CLI::Plans, :call, [], options
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
desc "init", "Initialize a new Pocketrb workspace"
|
|
57
|
+
# Initializes a new Pocketrb workspace with configuration
|
|
58
|
+
# @return [void]
|
|
59
|
+
def init
|
|
60
|
+
invoke CLI::Init, :call, [], options
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
desc "chat", "Interactive chat mode (single session)"
|
|
64
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
65
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider"
|
|
66
|
+
option :system_prompt, type: :string, aliases: "-s", desc: "Custom system prompt"
|
|
67
|
+
# Starts an interactive chat session
|
|
68
|
+
# @return [void]
|
|
69
|
+
def chat
|
|
70
|
+
invoke CLI::Chat, :call, [], options
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
desc "start", "Start the agent in continuous mode"
|
|
74
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
75
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider (anthropic, openrouter)"
|
|
76
|
+
option :channel, type: :string, aliases: "-c", default: "cli", desc: "Channel to connect to"
|
|
77
|
+
# Starts the agent in continuous mode with CLI channel
|
|
78
|
+
# @return [void]
|
|
79
|
+
def start
|
|
80
|
+
invoke CLI::Start, :call, [], options
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
desc "telegram", "Run as a Telegram bot"
|
|
84
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
85
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider"
|
|
86
|
+
option :token, type: :string, aliases: "-t", desc: "Telegram bot token (or TELEGRAM_BOT_TOKEN env)"
|
|
87
|
+
option :allowed_users, type: :array, aliases: "-u", desc: "Allowed usernames or user IDs"
|
|
88
|
+
option :enable_cron, type: :boolean, default: true, desc: "Enable cron/scheduling service"
|
|
89
|
+
option :autonomous, type: :boolean, default: false, desc: "Skip permission prompts (for sandboxed environments)"
|
|
90
|
+
# Runs the agent as a Telegram bot
|
|
91
|
+
# @return [void]
|
|
92
|
+
def telegram
|
|
93
|
+
invoke CLI::Telegram, :call, [], options
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
desc "whatsapp", "Run as a WhatsApp bot (requires Node.js bridge)"
|
|
97
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
98
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider"
|
|
99
|
+
option :bridge_url, type: :string, default: "ws://localhost:3001", desc: "WhatsApp bridge WebSocket URL"
|
|
100
|
+
option :allowed_users, type: :array, aliases: "-u", desc: "Allowed phone numbers"
|
|
101
|
+
# Runs the agent as a WhatsApp bot
|
|
102
|
+
# @return [void]
|
|
103
|
+
def whatsapp
|
|
104
|
+
invoke CLI::WhatsApp, :call, [], options
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
desc "gateway", "Start the gateway with all services"
|
|
108
|
+
option :model, type: :string, aliases: "-m", desc: "Model to use"
|
|
109
|
+
option :provider, type: :string, aliases: "-p", desc: "LLM provider"
|
|
110
|
+
option :telegram_token, type: :string, desc: "Telegram bot token"
|
|
111
|
+
option :telegram_users, type: :array, desc: "Allowed Telegram users"
|
|
112
|
+
option :whatsapp_bridge, type: :string, default: "ws://localhost:3001", desc: "WhatsApp bridge URL"
|
|
113
|
+
option :whatsapp_users, type: :array, desc: "Allowed WhatsApp numbers"
|
|
114
|
+
option :heartbeat_interval, type: :numeric, default: 1800, desc: "Heartbeat interval in seconds"
|
|
115
|
+
option :enable_cron, type: :boolean, default: true, desc: "Enable cron service"
|
|
116
|
+
option :enable_heartbeat, type: :boolean, default: true, desc: "Enable heartbeat service"
|
|
117
|
+
option :autonomous, type: :boolean, default: false, desc: "Skip permission prompts (for sandboxed environments)"
|
|
118
|
+
# Starts the gateway with all configured services
|
|
119
|
+
# @return [void]
|
|
120
|
+
def gateway
|
|
121
|
+
invoke CLI::Gateway, :call, [], options
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|