prompt_objects 0.6.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +126 -0
- data/README.md +93 -22
- data/exe/poop +5 -0
- data/exe/prompt_objects +1 -1108
- data/exe/prompt_objects_mcp +5 -3
- data/lib/prompt_objects/capability.rb +33 -0
- data/lib/prompt_objects/cli/diagnostics_command.rb +69 -0
- data/lib/prompt_objects/cli/env_command.rb +435 -0
- data/lib/prompt_objects/cli/events_command.rb +145 -0
- data/lib/prompt_objects/cli/explore_command.rb +155 -0
- data/lib/prompt_objects/cli/helpers.rb +91 -0
- data/lib/prompt_objects/cli/message_command.rb +216 -0
- data/lib/prompt_objects/cli/repl_command.rb +366 -0
- data/lib/prompt_objects/cli/serve_command.rb +162 -0
- data/lib/prompt_objects/cli.rb +64 -422
- data/lib/prompt_objects/connectors/base.rb +16 -29
- data/lib/prompt_objects/connectors/mcp.rb +82 -98
- data/lib/prompt_objects/domain/records.rb +45 -0
- data/lib/prompt_objects/environment/exporter.rb +5 -4
- data/lib/prompt_objects/environment/git.rb +21 -1
- data/lib/prompt_objects/environment/importer.rb +5 -4
- data/lib/prompt_objects/environment/manager.rb +44 -6
- data/lib/prompt_objects/environment/manifest.rb +26 -4
- data/lib/prompt_objects/environment.rb +1337 -20
- data/lib/prompt_objects/execution/blocking_executor.rb +88 -0
- data/lib/prompt_objects/execution/context.rb +137 -0
- data/lib/prompt_objects/execution/contracts.rb +59 -0
- data/lib/prompt_objects/execution/permit_pool.rb +49 -0
- data/lib/prompt_objects/execution/policy.rb +47 -0
- data/lib/prompt_objects/execution/resource_lock_manager.rb +63 -0
- data/lib/prompt_objects/execution/runner.rb +498 -0
- data/lib/prompt_objects/execution/scheduler.rb +325 -0
- data/lib/prompt_objects/llm/factory.rb +4 -4
- data/lib/prompt_objects/llm/gemini_adapter.rb +25 -8
- data/lib/prompt_objects/llm/openai_adapter.rb +9 -1
- data/lib/prompt_objects/llm/pricing.rb +9 -2
- data/lib/prompt_objects/llm/response.rb +9 -4
- data/lib/prompt_objects/loader.rb +28 -1
- data/lib/prompt_objects/mcp/server.rb +6 -3
- data/lib/prompt_objects/mcp/tools/get_conversation.rb +15 -11
- data/lib/prompt_objects/mcp/tools/get_pending_requests.rb +2 -2
- data/lib/prompt_objects/mcp/tools/respond_to_request.rb +3 -3
- data/lib/prompt_objects/mcp/tools/send_message.rb +42 -32
- data/lib/prompt_objects/operations/diagnostics.rb +115 -0
- data/lib/prompt_objects/operations/retention.rb +75 -0
- data/lib/prompt_objects/operations/run_trace_builder.rb +152 -0
- data/lib/prompt_objects/primitives/http_get.rb +2 -0
- data/lib/prompt_objects/primitives/list_files.rb +2 -0
- data/lib/prompt_objects/primitives/read_file.rb +2 -0
- data/lib/prompt_objects/primitives/think.rb +36 -0
- data/lib/prompt_objects/primitives/write_file.rb +9 -0
- data/lib/prompt_objects/prompt_object.rb +171 -519
- data/lib/prompt_objects/repositories/artifact_repository.rb +109 -0
- data/lib/prompt_objects/repositories/base_repository.rb +37 -0
- data/lib/prompt_objects/repositories/definition_repository.rb +36 -0
- data/lib/prompt_objects/repositories/env_data_repository.rb +103 -0
- data/lib/prompt_objects/repositories/event_repository.rb +105 -0
- data/lib/prompt_objects/repositories/message_repository.rb +73 -0
- data/lib/prompt_objects/repositories/run_repository.rb +254 -0
- data/lib/prompt_objects/repositories/thread_repository.rb +87 -0
- data/lib/prompt_objects/repositories/tool_execution_repository.rb +88 -0
- data/lib/prompt_objects/repositories/workspace_session_repository.rb +61 -0
- data/lib/prompt_objects/server/api/routes.rb +549 -53
- data/lib/prompt_objects/server/app.rb +133 -14
- data/lib/prompt_objects/server/file_watcher.rb +9 -0
- data/lib/prompt_objects/server/public/assets/index-BjKlODDD.js +90 -0
- data/lib/prompt_objects/server/public/assets/index-w3fpIb5c.css +1 -0
- data/lib/prompt_objects/server/public/index.html +2 -2
- data/lib/prompt_objects/server/websocket_handler.rb +202 -227
- data/lib/prompt_objects/server.rb +170 -1
- data/lib/prompt_objects/service.rb +129 -0
- data/lib/prompt_objects/session/store.rb +600 -13
- data/lib/prompt_objects/universal/add_capability.rb +1 -1
- data/lib/prompt_objects/universal/create_capability.rb +4 -2
- data/lib/prompt_objects/universal/delete_env_data.rb +23 -15
- data/lib/prompt_objects/universal/delete_primitive.rb +1 -1
- data/lib/prompt_objects/universal/dispatch_po.rb +54 -0
- data/lib/prompt_objects/universal/get_env_data.rb +8 -14
- data/lib/prompt_objects/universal/get_run_status.rb +30 -0
- data/lib/prompt_objects/universal/join_runs.rb +35 -0
- data/lib/prompt_objects/universal/list_env_data.rb +10 -14
- data/lib/prompt_objects/universal/modify_primitive.rb +1 -1
- data/lib/prompt_objects/universal/render_artifact.rb +91 -0
- data/lib/prompt_objects/universal/request_primitive.rb +1 -1
- data/lib/prompt_objects/universal/{store_env_data.rb → set_env_data.rb} +34 -20
- data/lib/prompt_objects/version.rb +5 -0
- data/lib/prompt_objects.rb +33 -8
- data/objects/coordinator.md +8 -4
- data/templates/arc-agi-1/objects/data_manager.md +2 -2
- data/templates/arc-agi-1/objects/observer.md +2 -2
- data/templates/arc-agi-1/objects/solver.md +6 -5
- data/templates/arc-agi-1/objects/verifier.md +2 -2
- data/templates/arc-agi-3/manifest.yml +19 -0
- data/templates/arc-agi-3/objects/analyst.md +79 -0
- data/templates/arc-agi-3/objects/player.md +76 -0
- data/templates/arc-agi-3/objects/strategist.md +97 -0
- data/templates/arc-agi-3/primitives/action_log.rb +133 -0
- data/templates/arc-agi-3/primitives/arc3_api.rb +223 -0
- data/templates/arc-agi-3/primitives/arc3_scorecard.rb +131 -0
- data/templates/arc-agi-3/primitives/frame_diff.rb +143 -0
- data/templates/arc-agi-3/primitives/frame_info.rb +96 -0
- data/templates/arc-agi-3/primitives/render_frame.rb +140 -0
- data/templates/basic/objects/basic.md +11 -3
- data/templates/chiptune/extras/sonic_pi_boot.rb +220 -0
- data/templates/chiptune/manifest.yml +20 -0
- data/templates/chiptune/objects/conductor.md +184 -0
- data/templates/chiptune/objects/sonic_pi.md +65 -0
- data/templates/chiptune/objects/voice_bass.md +44 -0
- data/templates/chiptune/objects/voice_lead.md +44 -0
- data/templates/chiptune/objects/voice_pad.md +43 -0
- data/templates/chiptune/objects/voice_perc.md +45 -0
- data/templates/chiptune/primitives/play_note.rb +59 -0
- data/templates/chiptune/primitives/play_sequence.rb +55 -0
- data/templates/chiptune/primitives/query_sonic_pi.rb +141 -0
- data/templates/chiptune/primitives/set_synth.rb +57 -0
- data/templates/chiptune/primitives/set_tempo.rb +45 -0
- data/templates/chiptune/primitives/stop_voice.rb +46 -0
- data/templates/chiptune/primitives/support/osc_bridge.rb +33 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/chiptune/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/developer/objects/code_reviewer.md +1 -1
- data/templates/developer/objects/coordinator.md +16 -3
- data/templates/developer/objects/debugger.md +3 -3
- data/templates/empty/objects/assistant.md +10 -5
- data/templates/minimal/objects/assistant.md +10 -5
- data/templates/music/extras/sonic_pi_boot.rb +220 -0
- data/templates/music/manifest.yml +16 -0
- data/templates/music/objects/conductor.md +154 -0
- data/templates/music/objects/sonic_pi.md +65 -0
- data/templates/music/primitives/play_note.rb +55 -0
- data/templates/music/primitives/play_sequence.rb +51 -0
- data/templates/music/primitives/query_sonic_pi.rb +139 -0
- data/templates/music/primitives/set_synth.rb +53 -0
- data/templates/music/primitives/set_tempo.rb +43 -0
- data/templates/music/primitives/stop_voice.rb +44 -0
- data/templates/music/primitives/support/osc_bridge.rb +33 -0
- data/templates/music/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-beginner/README.md +100 -0
- data/templates/music-beginner/extras/README.md +27 -0
- data/templates/music-beginner/extras/sonic_pi_boot.rb +297 -0
- data/templates/music-beginner/manifest.yml +25 -0
- data/templates/music-beginner/objects/conductor.md +222 -0
- data/templates/music-beginner/objects/engine_setup.md +54 -0
- data/templates/music-beginner/objects/voice_bass.md +43 -0
- data/templates/music-beginner/objects/voice_keys.md +48 -0
- data/templates/music-beginner/objects/voice_pad.md +46 -0
- data/templates/music-beginner/objects/voice_percussion.md +45 -0
- data/templates/music-beginner/objects/voice_texture.md +37 -0
- data/templates/music-beginner/primitives/play_pattern.rb +118 -0
- data/templates/music-beginner/primitives/query_music_engine.rb +94 -0
- data/templates/music-beginner/primitives/set_instrument.rb +47 -0
- data/templates/music-beginner/primitives/set_tempo.rb +43 -0
- data/templates/music-beginner/primitives/set_voice_level.rb +46 -0
- data/templates/music-beginner/primitives/stop_voice.rb +37 -0
- data/templates/music-beginner/primitives/support/osc_bridge.rb +25 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-beginner/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/music-pro/extras/AUDITION.md +100 -0
- data/templates/music-pro/extras/sonic_pi_boot.rb +546 -0
- data/templates/music-pro/manifest.yml +19 -0
- data/templates/music-pro/objects/conductor.md +126 -0
- data/templates/music-pro/objects/sonic_pi.md +35 -0
- data/templates/music-pro/objects/voice_harmony.md +48 -0
- data/templates/music-pro/objects/voice_rhythm.md +43 -0
- data/templates/music-pro/objects/voice_texture.md +42 -0
- data/templates/music-pro/primitives/define_patch.rb +46 -0
- data/templates/music-pro/primitives/query_music_engine.rb +69 -0
- data/templates/music-pro/primitives/set_live_pattern.rb +70 -0
- data/templates/music-pro/primitives/set_tempo.rb +31 -0
- data/templates/music-pro/primitives/stop_voice.rb +31 -0
- data/templates/music-pro/primitives/support/music_protocol.rb +136 -0
- data/templates/music-pro/primitives/support/osc_bridge.rb +33 -0
- data/templates/music-pro/primitives/trigger_event.rb +44 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/LICENSE +19 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/address_pattern.rb +58 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/broadcast_client.rb +24 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/bundle.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/client.rb +13 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/core_ext/time.rb +6 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/em_server.rb +63 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/message.rb +46 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/network_packet.rb +42 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_argument.rb +26 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_packet.rb +140 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/osc_types.rb +53 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby/server.rb +91 -0
- data/templates/music-pro/vendor/osc-ruby-1.1.5/lib/osc-ruby.rb +18 -0
- data/templates/writer/objects/coordinator.md +14 -3
- data/templates/writer/objects/editor.md +1 -1
- data/templates/writer/objects/researcher.md +2 -2
- data/tools/thread-explorer.html +1 -1
- metadata +172 -65
- data/CLAUDE.md +0 -177
- data/Gemfile +0 -10
- data/Rakefile +0 -27
- data/frontend/.gitignore +0 -3
- data/frontend/index.html +0 -17
- data/frontend/package-lock.json +0 -4540
- data/frontend/package.json +0 -36
- data/frontend/postcss.config.js +0 -6
- data/frontend/src/App.tsx +0 -104
- data/frontend/src/canvas/CanvasView.tsx +0 -113
- data/frontend/src/canvas/ForceLayout.ts +0 -115
- data/frontend/src/canvas/SceneManager.ts +0 -587
- data/frontend/src/canvas/canvasStore.ts +0 -47
- data/frontend/src/canvas/constants.ts +0 -95
- data/frontend/src/canvas/controls/CameraControls.ts +0 -98
- data/frontend/src/canvas/edges/MessageArc.ts +0 -149
- data/frontend/src/canvas/inspector/InspectorPanel.tsx +0 -31
- data/frontend/src/canvas/inspector/POInspector.tsx +0 -262
- data/frontend/src/canvas/inspector/ToolCallInspector.tsx +0 -67
- data/frontend/src/canvas/nodes/PONode.ts +0 -249
- data/frontend/src/canvas/nodes/ToolCallNode.ts +0 -116
- data/frontend/src/canvas/types.ts +0 -24
- data/frontend/src/components/ContextMenu.tsx +0 -68
- data/frontend/src/components/EnvDataPane.tsx +0 -69
- data/frontend/src/components/Inspector.tsx +0 -263
- data/frontend/src/components/MarkdownMessage.tsx +0 -155
- data/frontend/src/components/MethodList.tsx +0 -90
- data/frontend/src/components/ModelSelector.tsx +0 -115
- data/frontend/src/components/NotificationPanel.tsx +0 -130
- data/frontend/src/components/ObjectList.tsx +0 -78
- data/frontend/src/components/PaneSlot.tsx +0 -30
- data/frontend/src/components/SourcePane.tsx +0 -202
- data/frontend/src/components/SystemBar.tsx +0 -74
- data/frontend/src/components/Transcript.tsx +0 -76
- data/frontend/src/components/UsagePanel.tsx +0 -105
- data/frontend/src/components/Workspace.tsx +0 -260
- data/frontend/src/components/index.ts +0 -12
- data/frontend/src/hooks/useResize.ts +0 -55
- data/frontend/src/hooks/useWebSocket.ts +0 -559
- data/frontend/src/index.css +0 -102
- data/frontend/src/main.tsx +0 -10
- data/frontend/src/store/index.ts +0 -311
- data/frontend/src/types/index.ts +0 -176
- data/frontend/tailwind.config.js +0 -44
- data/frontend/tsconfig.json +0 -30
- data/frontend/vite.config.ts +0 -29
- data/lib/prompt_objects/server/public/assets/index-DEPawnfZ.js +0 -4345
- data/lib/prompt_objects/server/public/assets/index-oMrRce1m.css +0 -1
- data/lib/prompt_objects/universal/add_primitive.rb +0 -118
- data/lib/prompt_objects/universal/ask_human.rb +0 -109
- data/lib/prompt_objects/universal/create_primitive.rb +0 -174
- data/lib/prompt_objects/universal/list_primitives.rb +0 -145
- data/lib/prompt_objects/universal/think.rb +0 -41
- data/lib/prompt_objects/universal/update_env_data.rb +0 -88
- data/prompt_objects.gemspec +0 -50
- data/templates/basic/.gitignore +0 -2
- data/templates/developer/.gitignore +0 -5
- data/templates/empty/.gitignore +0 -5
- data/templates/empty/objects/.gitkeep +0 -0
- data/templates/minimal/.gitignore +0 -5
- data/templates/writer/.gitignore +0 -5
data/exe/prompt_objects_mcp
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
# Add lib to the load path for development; installed gems already expose it.
|
|
5
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
6
|
+
|
|
7
|
+
require "prompt_objects"
|
|
8
|
+
require "prompt_objects/mcp/server"
|
|
7
9
|
|
|
8
10
|
# Parse command line options
|
|
9
11
|
objects_dir = ENV["PROMPT_OBJECTS_DIR"] || "objects"
|
|
@@ -11,6 +11,39 @@ module PromptObjects
|
|
|
11
11
|
@state = :idle
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
class << self
|
|
15
|
+
def execution_policy(parallel_safe: false, side_effect: :exclusive, resource_keys: nil, workload: :blocking)
|
|
16
|
+
@execution_policy = Execution::Policy.new(
|
|
17
|
+
parallel_safe: parallel_safe,
|
|
18
|
+
side_effect: side_effect,
|
|
19
|
+
resource_keys: resource_keys,
|
|
20
|
+
workload: workload
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def execution_policy_definition
|
|
25
|
+
return @execution_policy if defined?(@execution_policy) && @execution_policy
|
|
26
|
+
|
|
27
|
+
superclass.respond_to?(:execution_policy_definition) ? superclass.execution_policy_definition : nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def resolved_execution_policy(arguments:, context:)
|
|
32
|
+
policy = execution_policy_definition
|
|
33
|
+
policy.resolve(arguments: arguments, context: context, capability: self)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def execution_policy_signature
|
|
37
|
+
execution_policy_definition.signature(self)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def execution_policy_definition
|
|
41
|
+
self.class.execution_policy_definition || Execution::Policy.new(
|
|
42
|
+
parallel_safe: false,
|
|
43
|
+
side_effect: :exclusive
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
14
47
|
# Handle a message and return a response.
|
|
15
48
|
# @param message [String, Hash] The incoming message
|
|
16
49
|
# @param context [Context] Execution context with environment reference
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module PromptObjects
|
|
8
|
+
module CLI
|
|
9
|
+
class DiagnosticsCommand
|
|
10
|
+
def run(args)
|
|
11
|
+
json = args.delete("--json")
|
|
12
|
+
return help if args.include?("--help") || args.include?("-h")
|
|
13
|
+
|
|
14
|
+
env_name = args.first
|
|
15
|
+
raise Error, "environment name required" unless env_name
|
|
16
|
+
|
|
17
|
+
env_path = Helpers.resolve_environment(env_name)
|
|
18
|
+
raise Error, "environment '#{env_name}' not found" unless env_path
|
|
19
|
+
|
|
20
|
+
data = server_snapshot(env_path) || local_snapshot(env_path)
|
|
21
|
+
json ? puts(JSON.pretty_generate(data)) : print_summary(data)
|
|
22
|
+
rescue Error => e
|
|
23
|
+
$stderr.puts "Error: #{e.message}"
|
|
24
|
+
1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def help
|
|
28
|
+
puts <<~HELP
|
|
29
|
+
Usage: #{Helpers.program_name} diagnostics <environment> [--json]
|
|
30
|
+
|
|
31
|
+
Show content-free scheduler, timing, failure, usage, and database health metrics.
|
|
32
|
+
HELP
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def server_snapshot(env_path)
|
|
38
|
+
require "prompt_objects/server"
|
|
39
|
+
server = PromptObjects::Server.read_server_file(env_path)
|
|
40
|
+
return nil unless server
|
|
41
|
+
|
|
42
|
+
uri = URI("http://#{server[:host]}:#{server[:port]}/api/diagnostics")
|
|
43
|
+
JSON.parse(Net::HTTP.get(uri), symbolize_names: true)
|
|
44
|
+
rescue Errno::ECONNREFUSED
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def local_snapshot(env_path)
|
|
49
|
+
path = File.join(env_path, "sessions.db")
|
|
50
|
+
raise Error, "sessions.db not found" unless File.file?(path)
|
|
51
|
+
|
|
52
|
+
store = Session::Store.new(path)
|
|
53
|
+
Operations::Diagnostics.new(store).snapshot
|
|
54
|
+
ensure
|
|
55
|
+
store&.close
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def print_summary(data)
|
|
59
|
+
puts "Runtime diagnostics (#{data[:generated_at]})"
|
|
60
|
+
puts " database: #{data.dig(:database, :bytes)} bytes, #{data.dig(:database, :integrity)}"
|
|
61
|
+
puts " scheduler: #{data.dig(:scheduler, :running) || 0} running, #{data.dig(:scheduler, :queued) || 0} queued"
|
|
62
|
+
puts " runs: #{data.dig(:counts, :runs)} total, #{data.dig(:runs, :active)} active, #{data.dig(:runs, :failure_rate)} failure rate"
|
|
63
|
+
puts " queue wait: #{data.dig(:runs, :queue_wait_ms, :average)} ms avg / #{data.dig(:runs, :queue_wait_ms, :p95)} ms p95"
|
|
64
|
+
puts " run duration: #{data.dig(:runs, :duration_ms, :average)} ms avg / #{data.dig(:runs, :duration_ms, :p95)} ms p95"
|
|
65
|
+
puts " tool duration: #{data.dig(:tools, :duration_ms, :average)} ms avg / #{data.dig(:tools, :duration_ms, :p95)} ms p95"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PromptObjects
|
|
4
|
+
module CLI
|
|
5
|
+
# `env` command: manage environments (list, create, info, export, import,
|
|
6
|
+
# archive, restore, clone, delete, default).
|
|
7
|
+
class EnvCommand
|
|
8
|
+
def initialize(manager: nil)
|
|
9
|
+
@manager = manager || Env::Manager.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Run the env subcommand.
|
|
13
|
+
# @param args [Array<String>] Arguments after 'env'
|
|
14
|
+
def run(args)
|
|
15
|
+
subcommand = args.shift || "list"
|
|
16
|
+
|
|
17
|
+
case subcommand
|
|
18
|
+
when "list", "ls"
|
|
19
|
+
list(args)
|
|
20
|
+
when "create", "new"
|
|
21
|
+
create(args)
|
|
22
|
+
when "info"
|
|
23
|
+
info(args)
|
|
24
|
+
when "export"
|
|
25
|
+
export(args)
|
|
26
|
+
when "import"
|
|
27
|
+
import(args)
|
|
28
|
+
when "archive"
|
|
29
|
+
archive(args)
|
|
30
|
+
when "restore"
|
|
31
|
+
restore(args)
|
|
32
|
+
when "clone", "cp"
|
|
33
|
+
clone(args)
|
|
34
|
+
when "delete"
|
|
35
|
+
delete(args)
|
|
36
|
+
when "default"
|
|
37
|
+
set_default(args)
|
|
38
|
+
else
|
|
39
|
+
puts "Unknown env command: #{subcommand}"
|
|
40
|
+
puts
|
|
41
|
+
help
|
|
42
|
+
exit 1
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def list(args = [])
|
|
47
|
+
@manager.setup!
|
|
48
|
+
envs = @manager.list_with_manifests
|
|
49
|
+
|
|
50
|
+
if envs.empty?
|
|
51
|
+
puts "No environments found."
|
|
52
|
+
puts "Create one with: #{prog} env create <name>"
|
|
53
|
+
return
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
default = @manager.default_environment
|
|
57
|
+
|
|
58
|
+
if args.include?("-v") || args.include?("--verbose")
|
|
59
|
+
puts "Environments (#{envs.size}):"
|
|
60
|
+
puts
|
|
61
|
+
envs.each do |manifest|
|
|
62
|
+
default_marker = manifest.name == default ? " (default)" : ""
|
|
63
|
+
last_opened = manifest.last_opened&.strftime("%Y-%m-%d") || "never"
|
|
64
|
+
puts " #{manifest.icon} #{manifest.name}#{default_marker}"
|
|
65
|
+
puts " #{manifest.description}" if manifest.description
|
|
66
|
+
puts " Last opened: #{last_opened}, Objects: #{manifest.stats['po_count']}"
|
|
67
|
+
puts
|
|
68
|
+
end
|
|
69
|
+
return
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Concise: one line per environment, aligned for scanning.
|
|
73
|
+
width = [envs.map { |m| m.name.length }.max, 32].min
|
|
74
|
+
archived = @manager.list_archived.size
|
|
75
|
+
|
|
76
|
+
header = "Environments (#{envs.size})"
|
|
77
|
+
header += " · #{archived} archived" if archived.positive?
|
|
78
|
+
puts header
|
|
79
|
+
puts
|
|
80
|
+
envs.each do |m|
|
|
81
|
+
name = m.name.ljust(width)
|
|
82
|
+
last = (m.last_opened&.strftime("%Y-%m-%d") || "never").ljust(10)
|
|
83
|
+
pos = (m.stats["po_count"] || 0).to_s.rjust(2)
|
|
84
|
+
marker = m.name == default ? " (default)" : ""
|
|
85
|
+
puts " #{name} #{last} #{pos} POs#{marker}"
|
|
86
|
+
end
|
|
87
|
+
puts
|
|
88
|
+
puts "Details: env list -v · Delete: env delete <name>... --permanent"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def create(args)
|
|
92
|
+
name = args.shift
|
|
93
|
+
unless name
|
|
94
|
+
puts "Usage: #{prog} env create <name> [--template <template>]"
|
|
95
|
+
exit 1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
template = nil
|
|
99
|
+
args.each_with_index do |arg, i|
|
|
100
|
+
if arg == "--template" || arg == "-t"
|
|
101
|
+
template = args[i + 1]
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
@manager.setup!
|
|
106
|
+
|
|
107
|
+
path = @manager.create(name: name, template: template)
|
|
108
|
+
puts "Created environment: #{name}"
|
|
109
|
+
puts "Location: #{path}"
|
|
110
|
+
|
|
111
|
+
# Print dependency info if template declares gems
|
|
112
|
+
manifest = Env::Manifest.load_from_dir(path)
|
|
113
|
+
if manifest.dependencies&.dig("gems")&.any?
|
|
114
|
+
vendor_dir = File.join(path, "vendor")
|
|
115
|
+
if Dir.exist?(vendor_dir) && !Dir.empty?(vendor_dir)
|
|
116
|
+
puts "\nBundled dependencies (vendored):"
|
|
117
|
+
manifest.dependencies["gems"].each do |gem_info|
|
|
118
|
+
puts " #{gem_info['name']} — #{gem_info['purpose']}"
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
puts "\nRequired dependencies (not vendored — install manually):"
|
|
122
|
+
manifest.dependencies["gems"].each do |gem_info|
|
|
123
|
+
puts " gem install #{gem_info['name']} # #{gem_info['purpose']}"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
if @manager.list.size == 1
|
|
129
|
+
@manager.set_default_environment(name)
|
|
130
|
+
puts "Set as default environment."
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def info(args)
|
|
135
|
+
name = args.shift || @manager.default_environment
|
|
136
|
+
unless name
|
|
137
|
+
puts "Usage: #{prog} env info <name>"
|
|
138
|
+
exit 1
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
manifest = @manager.manifest_for(name)
|
|
142
|
+
unless manifest
|
|
143
|
+
puts "Environment '#{name}' not found."
|
|
144
|
+
exit 1
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
path = @manager.environment_path(name)
|
|
148
|
+
objects = Dir.glob(File.join(path, "objects", "*.md")).map { |f| File.basename(f, ".md") }
|
|
149
|
+
primitives = Dir.glob(File.join(path, "primitives", "*.rb")).map { |f| File.basename(f, ".rb") }
|
|
150
|
+
|
|
151
|
+
puts manifest.info
|
|
152
|
+
puts
|
|
153
|
+
puts " Location: #{path}"
|
|
154
|
+
puts " Objects: #{objects.join(', ')}" if objects.any?
|
|
155
|
+
puts " Custom primitives: #{primitives.join(', ')}" if primitives.any?
|
|
156
|
+
puts
|
|
157
|
+
|
|
158
|
+
if Env::Git.repo?(path)
|
|
159
|
+
commits = Env::Git.commit_count(path)
|
|
160
|
+
dirty = Env::Git.dirty?(path)
|
|
161
|
+
puts " Git: #{commits} commits#{dirty ? ' (uncommitted changes)' : ''}"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def export(args)
|
|
166
|
+
@manager.setup!
|
|
167
|
+
|
|
168
|
+
# Parse arguments
|
|
169
|
+
name = nil
|
|
170
|
+
output = nil
|
|
171
|
+
|
|
172
|
+
i = 0
|
|
173
|
+
while i < args.length
|
|
174
|
+
case args[i]
|
|
175
|
+
when "-o", "--output"
|
|
176
|
+
output = args[i + 1]
|
|
177
|
+
i += 1
|
|
178
|
+
else
|
|
179
|
+
name ||= args[i]
|
|
180
|
+
end
|
|
181
|
+
i += 1
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Default to current directory name if no output specified
|
|
185
|
+
name ||= @manager.default_environment
|
|
186
|
+
unless name
|
|
187
|
+
puts "Usage: #{prog} env export <name> [-o output.dump]"
|
|
188
|
+
exit 1
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
unless @manager.environment_exists?(name)
|
|
192
|
+
puts "Environment '#{name}' not found."
|
|
193
|
+
exit 1
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
output ||= "#{name}.dump"
|
|
197
|
+
env_path = @manager.environment_path(name)
|
|
198
|
+
|
|
199
|
+
exporter = Env::Exporter.new(env_path)
|
|
200
|
+
result = exporter.export(output)
|
|
201
|
+
|
|
202
|
+
if result[:success]
|
|
203
|
+
puts "Exported '#{name}' to: #{result[:path]}"
|
|
204
|
+
puts
|
|
205
|
+
puts "Stats:"
|
|
206
|
+
puts " Commits: #{result[:stats][:commits]}"
|
|
207
|
+
puts " Objects: #{result[:stats][:objects]}"
|
|
208
|
+
puts " Primitives: #{result[:stats][:primitives]}"
|
|
209
|
+
else
|
|
210
|
+
puts "Export failed: #{result[:error]}"
|
|
211
|
+
exit 1
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def import(args)
|
|
216
|
+
@manager.setup!
|
|
217
|
+
|
|
218
|
+
# Parse arguments
|
|
219
|
+
bundle_path = nil
|
|
220
|
+
import_as = nil
|
|
221
|
+
trust = false
|
|
222
|
+
|
|
223
|
+
i = 0
|
|
224
|
+
while i < args.length
|
|
225
|
+
case args[i]
|
|
226
|
+
when "--as"
|
|
227
|
+
import_as = args[i + 1]
|
|
228
|
+
i += 1
|
|
229
|
+
when "--trust"
|
|
230
|
+
trust = true
|
|
231
|
+
else
|
|
232
|
+
bundle_path ||= args[i]
|
|
233
|
+
end
|
|
234
|
+
i += 1
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
unless bundle_path
|
|
238
|
+
puts "Usage: #{prog} env import <bundle.dump> [--as <name>] [--trust]"
|
|
239
|
+
puts
|
|
240
|
+
puts "Options:"
|
|
241
|
+
puts " --as <name> Import with a different name"
|
|
242
|
+
puts " --trust Trust custom primitives (skip sandbox warnings)"
|
|
243
|
+
exit 1
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
importer = Env::Importer.new(bundle_path)
|
|
247
|
+
|
|
248
|
+
# First, inspect and show what's in the bundle
|
|
249
|
+
info = importer.inspect_bundle
|
|
250
|
+
unless info.valid
|
|
251
|
+
puts "Invalid bundle: #{info.error}"
|
|
252
|
+
exit 1
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
puts "Bundle contents:"
|
|
256
|
+
puts " Name: #{info.name}"
|
|
257
|
+
puts " Description: #{info.description}" if info.description
|
|
258
|
+
puts " Objects: #{info.objects.join(', ')}" if info.objects.any?
|
|
259
|
+
puts " Primitives: #{info.primitives.join(', ')}" if info.primitives.any?
|
|
260
|
+
puts " Commits: #{info.commits}"
|
|
261
|
+
puts
|
|
262
|
+
|
|
263
|
+
# Warn about primitives
|
|
264
|
+
if info.primitives.any? && !trust
|
|
265
|
+
puts "⚠️ WARNING: This bundle contains custom primitives."
|
|
266
|
+
puts "Custom primitives can execute arbitrary Ruby code."
|
|
267
|
+
puts "Review the code before running, or use --trust to skip this warning."
|
|
268
|
+
puts
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Import
|
|
272
|
+
import_name = import_as || info.name
|
|
273
|
+
result = importer.import(manager: @manager, name: import_name, trust_primitives: trust)
|
|
274
|
+
|
|
275
|
+
if result[:success]
|
|
276
|
+
puts "Imported as '#{result[:name]}'"
|
|
277
|
+
puts "Location: #{result[:path]}"
|
|
278
|
+
|
|
279
|
+
if result[:warnings].any?
|
|
280
|
+
puts
|
|
281
|
+
result[:warnings].each { |w| puts w }
|
|
282
|
+
end
|
|
283
|
+
else
|
|
284
|
+
puts "Import failed: #{result[:error]}"
|
|
285
|
+
exit 1
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def archive(args)
|
|
290
|
+
name = args.shift
|
|
291
|
+
unless name
|
|
292
|
+
puts "Usage: #{prog} env archive <name>"
|
|
293
|
+
exit 1
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
path = @manager.archive(name)
|
|
297
|
+
puts "Archived environment '#{name}' to:"
|
|
298
|
+
puts " #{path}"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def restore(args)
|
|
302
|
+
archived_name = args.shift
|
|
303
|
+
unless archived_name
|
|
304
|
+
# Show available archived environments
|
|
305
|
+
archived = @manager.list_archived
|
|
306
|
+
if archived.empty?
|
|
307
|
+
puts "No archived environments."
|
|
308
|
+
else
|
|
309
|
+
puts "Archived environments:"
|
|
310
|
+
archived.each { |name| puts " - #{name}" }
|
|
311
|
+
puts
|
|
312
|
+
puts "Usage: #{prog} env restore <archived_name> [--as <new_name>]"
|
|
313
|
+
end
|
|
314
|
+
return
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
restore_as = nil
|
|
318
|
+
args.each_with_index do |arg, i|
|
|
319
|
+
restore_as = args[i + 1] if arg == "--as"
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
path = @manager.restore(archived_name, restore_as: restore_as)
|
|
323
|
+
puts "Restored to: #{path}"
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def clone(args)
|
|
327
|
+
source = args.shift
|
|
328
|
+
target = args.shift
|
|
329
|
+
|
|
330
|
+
unless source && target
|
|
331
|
+
puts "Usage: #{prog} env clone <source> <target>"
|
|
332
|
+
exit 1
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
path = @manager.clone(source, target)
|
|
336
|
+
puts "Cloned '#{source}' to '#{target}'"
|
|
337
|
+
puts "Location: #{path}"
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def delete(args)
|
|
341
|
+
permanent = args.delete("--permanent")
|
|
342
|
+
names = args
|
|
343
|
+
|
|
344
|
+
if names.empty?
|
|
345
|
+
puts "Usage: #{prog} env delete <name> [<name>...] --permanent"
|
|
346
|
+
puts
|
|
347
|
+
puts "Permanently deletes environments (active or archived) and all their data."
|
|
348
|
+
puts "For a recoverable flow, use `env archive <name>` instead."
|
|
349
|
+
return
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
unless permanent
|
|
353
|
+
puts "This permanently deletes #{names.size} environment#{names.size == 1 ? '' : 's'} and cannot be undone:"
|
|
354
|
+
names.each { |n| puts " - #{n}" }
|
|
355
|
+
puts
|
|
356
|
+
puts "Re-run with --permanent to confirm."
|
|
357
|
+
return
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
names.each do |name|
|
|
361
|
+
if @manager.environment_exists?(name)
|
|
362
|
+
@manager.destroy(name)
|
|
363
|
+
puts "Deleted: #{name}"
|
|
364
|
+
elsif @manager.list_archived.include?(name)
|
|
365
|
+
@manager.delete_archived(name)
|
|
366
|
+
puts "Deleted (archived): #{name}"
|
|
367
|
+
else
|
|
368
|
+
puts "Not found: #{name}"
|
|
369
|
+
end
|
|
370
|
+
rescue => e
|
|
371
|
+
puts "Failed to delete #{name}: #{e.message}"
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def set_default(args)
|
|
376
|
+
name = args.shift
|
|
377
|
+
unless name
|
|
378
|
+
current = @manager.default_environment
|
|
379
|
+
if current
|
|
380
|
+
puts "Current default: #{current}"
|
|
381
|
+
else
|
|
382
|
+
puts "No default environment set."
|
|
383
|
+
end
|
|
384
|
+
puts
|
|
385
|
+
puts "Usage: #{prog} env default <name>"
|
|
386
|
+
return
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
@manager.set_default_environment(name)
|
|
390
|
+
puts "Default environment set to: #{name}"
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def help
|
|
394
|
+
puts <<~HELP
|
|
395
|
+
Environment management commands:
|
|
396
|
+
|
|
397
|
+
#{prog} env list List all environments (one line each)
|
|
398
|
+
-v, --verbose Show full details per environment
|
|
399
|
+
#{prog} env create <name> Create new environment
|
|
400
|
+
--template, -t <template> Use one of the templates listed below
|
|
401
|
+
#{prog} env info <name> Show environment details
|
|
402
|
+
#{prog} env export <name> Export environment as .dump bundle
|
|
403
|
+
-o, --output <file> Output file path
|
|
404
|
+
#{prog} env import <file> Import environment from .dump bundle
|
|
405
|
+
--as <name> Import with different name
|
|
406
|
+
--trust Trust custom primitives
|
|
407
|
+
#{prog} env archive <name> Archive (soft delete) environment
|
|
408
|
+
#{prog} env restore <name> Restore archived environment
|
|
409
|
+
--as <new_name> Restore with different name
|
|
410
|
+
#{prog} env clone <src> <dest> Clone environment
|
|
411
|
+
#{prog} env default <name> Set default environment
|
|
412
|
+
#{prog} env delete <name>... --permanent Permanently delete (active or archived; accepts multiple)
|
|
413
|
+
|
|
414
|
+
Available templates:
|
|
415
|
+
basic - No capabilities, learns as needed (great for demos!)
|
|
416
|
+
minimal - Basic assistant with file reading
|
|
417
|
+
developer - Code review, debugging, testing specialists
|
|
418
|
+
writer - Editor, researcher for content creation
|
|
419
|
+
empty - Bootstrap assistant only
|
|
420
|
+
arc-agi-1 - ARC-AGI-1 challenge solving with grid primitives
|
|
421
|
+
arc-agi-3 - Interactive ARC-AGI-3 environment with remote API
|
|
422
|
+
chiptune - Chiptune music composition via Sonic Pi (genre-specific)
|
|
423
|
+
music - Open-ended music composition via Sonic Pi (any genre)
|
|
424
|
+
music-pro - Expressive live-pattern composition via Sonic Pi
|
|
425
|
+
HELP
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
private
|
|
429
|
+
|
|
430
|
+
def prog
|
|
431
|
+
Helpers.program_name
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module PromptObjects
|
|
6
|
+
module CLI
|
|
7
|
+
# `events` command: show recent events from the message bus.
|
|
8
|
+
class EventsCommand
|
|
9
|
+
def run(args)
|
|
10
|
+
options = { count: 50, session_id: nil, json: false }
|
|
11
|
+
positional = []
|
|
12
|
+
skip_next = false
|
|
13
|
+
|
|
14
|
+
args.each_with_index do |arg, i|
|
|
15
|
+
if skip_next
|
|
16
|
+
skip_next = false
|
|
17
|
+
next
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
case arg
|
|
21
|
+
when "--session"
|
|
22
|
+
options[:session_id] = args[i + 1]
|
|
23
|
+
skip_next = true
|
|
24
|
+
when "--count"
|
|
25
|
+
options[:count] = args[i + 1].to_i
|
|
26
|
+
skip_next = true
|
|
27
|
+
when "--json"
|
|
28
|
+
options[:json] = true
|
|
29
|
+
when "--help", "-h"
|
|
30
|
+
help
|
|
31
|
+
exit 0
|
|
32
|
+
else
|
|
33
|
+
positional << arg
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
env_name = positional[0]
|
|
38
|
+
unless env_name
|
|
39
|
+
puts "Error: environment name required"
|
|
40
|
+
puts "Run '#{Helpers.program_name} events --help' for usage"
|
|
41
|
+
exit 1
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
env_path = Helpers.resolve_environment(env_name)
|
|
45
|
+
unless env_path
|
|
46
|
+
$stderr.puts "Error: environment '#{env_name}' not found"
|
|
47
|
+
exit 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Check for running server
|
|
51
|
+
require "prompt_objects/server"
|
|
52
|
+
server_info = PromptObjects::Server.read_server_file(env_path)
|
|
53
|
+
|
|
54
|
+
if server_info
|
|
55
|
+
show_via_http(server_info, options)
|
|
56
|
+
else
|
|
57
|
+
show_standalone(env_path, options)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def help
|
|
62
|
+
prog = Helpers.program_name
|
|
63
|
+
puts <<~HELP
|
|
64
|
+
Usage: #{prog} events [options] <environment>
|
|
65
|
+
|
|
66
|
+
Show recent events from the message bus.
|
|
67
|
+
|
|
68
|
+
Arguments:
|
|
69
|
+
environment Environment name or path
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
--session ID Show events for a specific session
|
|
73
|
+
--count N Number of events to show (default: 50)
|
|
74
|
+
--json Output as JSON
|
|
75
|
+
--help, -h Show this help
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
#{prog} events my-env
|
|
79
|
+
#{prog} events my-env --session abc-123
|
|
80
|
+
#{prog} events my-env --count 100 --json
|
|
81
|
+
HELP
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def show_via_http(server_info, options)
|
|
87
|
+
require "net/http"
|
|
88
|
+
require "uri"
|
|
89
|
+
|
|
90
|
+
if options[:session_id]
|
|
91
|
+
uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events/session/#{options[:session_id]}")
|
|
92
|
+
else
|
|
93
|
+
uri = URI("http://#{server_info[:host]}:#{server_info[:port]}/api/events?count=#{options[:count]}")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
response = Net::HTTP.get(uri)
|
|
97
|
+
data = JSON.parse(response)
|
|
98
|
+
|
|
99
|
+
if options[:json]
|
|
100
|
+
puts JSON.pretty_generate(data)
|
|
101
|
+
else
|
|
102
|
+
Helpers.print_events(data["events"] || [])
|
|
103
|
+
end
|
|
104
|
+
rescue Errno::ECONNREFUSED
|
|
105
|
+
$stderr.puts "Error: Server not responding"
|
|
106
|
+
exit 1
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def show_standalone(env_path, options)
|
|
110
|
+
db_path = File.join(env_path, "sessions.db")
|
|
111
|
+
unless File.exist?(db_path)
|
|
112
|
+
puts "No event history (sessions.db not found)"
|
|
113
|
+
exit 0
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
store = PromptObjects::Session::Store.new(db_path)
|
|
117
|
+
|
|
118
|
+
events = if options[:session_id]
|
|
119
|
+
store.get_events(session_id: options[:session_id])
|
|
120
|
+
else
|
|
121
|
+
store.get_recent_events(options[:count])
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if options[:json]
|
|
125
|
+
formatted = events.map do |e|
|
|
126
|
+
{
|
|
127
|
+
id: e[:id],
|
|
128
|
+
from: e[:from],
|
|
129
|
+
to: e[:to],
|
|
130
|
+
summary: e[:summary],
|
|
131
|
+
message: e[:message],
|
|
132
|
+
timestamp: e[:timestamp]&.iso8601,
|
|
133
|
+
session_id: e[:session_id]
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
puts JSON.pretty_generate(formatted)
|
|
137
|
+
else
|
|
138
|
+
Helpers.print_events(events)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
store.close
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|