riffer-rig 0.5.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 +58 -0
- data/README.md +77 -7
- data/exe/riffer +1 -1
- data/lib/riffer/rig/bundled/agents_md.rb +7 -0
- data/lib/riffer/rig/bundled/bash.rb +5 -0
- data/lib/riffer/rig/bundled/edit.rb +5 -0
- data/lib/riffer/rig/bundled/mcp.rb +9 -0
- data/lib/riffer/rig/bundled/read.rb +5 -0
- data/lib/riffer/rig/bundled/skills.rb +10 -0
- data/lib/riffer/rig/bundled/write.rb +5 -0
- data/lib/riffer/rig/bundled.rb +7 -0
- data/lib/riffer/rig/cli/flags.rb +77 -0
- data/lib/riffer/rig/cli.rb +70 -102
- data/lib/riffer/rig/command/context.rb +70 -0
- data/lib/riffer/rig/command.rb +29 -0
- data/lib/riffer/rig/commands/model.rb +54 -0
- data/lib/riffer/rig/commands/skill.rb +27 -0
- data/lib/riffer/rig/commands.rb +4 -0
- data/lib/riffer/rig/credentials/resolution.rb +16 -0
- data/lib/riffer/rig/credentials.rb +151 -60
- data/lib/riffer/rig/directories.rb +20 -0
- data/lib/riffer/rig/env/invalid.rb +13 -0
- data/lib/riffer/rig/env.rb +40 -0
- data/lib/riffer/rig/events/after_response.rb +25 -0
- data/lib/riffer/rig/events/after_tool_call.rb +31 -0
- data/lib/riffer/rig/events/before_prompt.rb +25 -0
- data/lib/riffer/rig/events/before_request.rb +25 -0
- data/lib/riffer/rig/events/before_tool_call.rb +28 -0
- data/lib/riffer/rig/events/command_output.rb +28 -0
- data/lib/riffer/rig/events/notify.rb +28 -0
- data/lib/riffer/rig/events/session_end.rb +25 -0
- data/lib/riffer/rig/events/session_start.rb +28 -0
- data/lib/riffer/rig/events/skill_activated.rb +25 -0
- data/lib/riffer/rig/events/turn_end.rb +33 -0
- data/lib/riffer/rig/events.rb +4 -0
- data/lib/riffer/rig/extension/failure.rb +16 -0
- data/lib/riffer/rig/extension.rb +16 -5
- data/lib/riffer/rig/hosts/mirror.rb +59 -0
- data/lib/riffer/rig/hosts/null.rb +36 -0
- data/lib/riffer/rig/loader.rb +163 -0
- data/lib/riffer/rig/mcp/declaration.rb +23 -0
- data/lib/riffer/rig/mcp/server.rb +16 -0
- data/lib/riffer/rig/mcp.rb +12 -0
- data/lib/riffer/rig/prompts/agents_md.rb +32 -0
- data/lib/riffer/rig/prompts.rb +4 -0
- data/lib/riffer/rig/provider_setup/field.rb +24 -0
- data/lib/riffer/rig/provider_setup.rb +84 -0
- data/lib/riffer/rig/registrar.rb +134 -7
- data/lib/riffer/rig/runtime/blocked.rb +13 -0
- data/lib/riffer/rig/runtime/cancel_flag.rb +27 -0
- data/lib/riffer/rig/runtime/hooks.rb +78 -0
- data/lib/riffer/rig/runtime/request_guardrail.rb +24 -0
- data/lib/riffer/rig/runtime/tool_runtime.rb +44 -0
- data/lib/riffer/rig/runtime.rb +582 -70
- data/lib/riffer/rig/settings/document.rb +28 -12
- data/lib/riffer/rig/settings/pricing.rb +16 -6
- data/lib/riffer/rig/settings.rb +108 -81
- data/lib/riffer/rig/skills/sources.rb +28 -0
- data/lib/riffer/rig/skills.rb +12 -0
- data/lib/riffer/rig/support/equatable.rb +21 -0
- data/lib/riffer/rig/{ui → terminal}/animator.rb +6 -13
- data/lib/riffer/rig/{ui → terminal}/banner.rb +17 -21
- data/lib/riffer/rig/{ui → terminal}/cursor.rb +4 -6
- data/lib/riffer/rig/terminal/host.rb +85 -0
- data/lib/riffer/rig/{ui → terminal}/palette.rb +1 -1
- data/lib/riffer/rig/{ui → terminal}/renderer.rb +121 -87
- data/lib/riffer/rig/{ui → terminal}/smoother.rb +15 -23
- data/lib/riffer/rig/{ui → terminal}/theme.rb +12 -14
- data/lib/riffer/rig/terminal.rb +207 -0
- data/lib/riffer/rig/tools/bash.rb +36 -13
- data/lib/riffer/rig/tools/edit.rb +3 -2
- data/lib/riffer/rig/tools/read.rb +3 -2
- data/lib/riffer/rig/tools/write.rb +3 -2
- data/lib/riffer/rig/version.rb +1 -1
- data/lib/riffer/rig.rb +10 -15
- data/sig/generated/riffer/rig/bundled/agents_md.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/bash.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/edit.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/mcp.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/read.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/skills.rbs +5 -0
- data/sig/generated/riffer/rig/bundled/write.rbs +5 -0
- data/sig/generated/riffer/rig/bundled.rbs +5 -0
- data/sig/generated/riffer/rig/cli/flags.rbs +40 -0
- data/sig/generated/riffer/rig/cli.rbs +30 -27
- data/sig/generated/riffer/rig/command/context.rbs +50 -0
- data/sig/generated/riffer/rig/command.rbs +23 -0
- data/sig/generated/riffer/rig/commands/model.rbs +27 -0
- data/sig/generated/riffer/rig/commands/skill.rbs +12 -0
- data/sig/generated/riffer/rig/commands.rbs +4 -0
- data/sig/generated/riffer/rig/credentials/resolution.rbs +13 -0
- data/sig/generated/riffer/rig/credentials.rbs +70 -41
- data/sig/generated/riffer/rig/directories.rbs +11 -0
- data/sig/generated/riffer/rig/env/invalid.rbs +10 -0
- data/sig/generated/riffer/rig/env.rbs +26 -0
- data/sig/generated/riffer/rig/events/after_response.rbs +18 -0
- data/sig/generated/riffer/rig/events/after_tool_call.rbs +24 -0
- data/sig/generated/riffer/rig/events/before_prompt.rbs +18 -0
- data/sig/generated/riffer/rig/events/before_request.rbs +18 -0
- data/sig/generated/riffer/rig/events/before_tool_call.rbs +21 -0
- data/sig/generated/riffer/rig/events/command_output.rbs +21 -0
- data/sig/generated/riffer/rig/events/notify.rbs +21 -0
- data/sig/generated/riffer/rig/events/session_end.rbs +18 -0
- data/sig/generated/riffer/rig/events/session_start.rbs +21 -0
- data/sig/generated/riffer/rig/events/skill_activated.rbs +18 -0
- data/sig/generated/riffer/rig/events/turn_end.rbs +24 -0
- data/sig/generated/riffer/rig/events.rbs +4 -0
- data/sig/generated/riffer/rig/extension/failure.rbs +13 -0
- data/sig/generated/riffer/rig/extension.rbs +14 -5
- data/sig/generated/riffer/rig/hosts/mirror.rbs +41 -0
- data/sig/generated/riffer/rig/hosts/null.rbs +26 -0
- data/sig/generated/riffer/rig/loader.rbs +77 -0
- data/sig/generated/riffer/rig/mcp/declaration.rbs +18 -0
- data/sig/generated/riffer/rig/mcp/server.rbs +13 -0
- data/sig/generated/riffer/rig/mcp.rbs +8 -0
- data/sig/generated/riffer/rig/prompts/agents_md.rbs +19 -0
- data/sig/generated/riffer/rig/prompts.rbs +4 -0
- data/sig/generated/riffer/rig/provider_setup/field.rbs +22 -0
- data/sig/generated/riffer/rig/provider_setup.rbs +30 -0
- data/sig/generated/riffer/rig/registrar.rbs +88 -6
- data/sig/generated/riffer/rig/runtime/blocked.rbs +10 -0
- data/sig/generated/riffer/rig/runtime/cancel_flag.rbs +19 -0
- data/sig/generated/riffer/rig/runtime/hooks.rbs +49 -0
- data/sig/generated/riffer/rig/runtime/request_guardrail.rbs +16 -0
- data/sig/generated/riffer/rig/runtime/tool_runtime.rbs +23 -0
- data/sig/generated/riffer/rig/runtime.rbs +262 -40
- data/sig/generated/riffer/rig/settings/document.rbs +13 -9
- data/sig/generated/riffer/rig/settings/pricing.rbs +7 -3
- data/sig/generated/riffer/rig/settings.rbs +56 -64
- data/sig/generated/riffer/rig/skills/sources.rbs +18 -0
- data/sig/generated/riffer/rig/skills.rbs +7 -0
- data/sig/generated/riffer/rig/support/equatable.rbs +15 -0
- data/sig/generated/riffer/rig/{ui → terminal}/animator.rbs +5 -13
- data/sig/generated/riffer/rig/terminal/banner.rbs +39 -0
- data/sig/generated/riffer/rig/terminal/cursor.rbs +31 -0
- data/sig/generated/riffer/rig/terminal/host.rbs +50 -0
- data/sig/generated/riffer/rig/{ui → terminal}/palette.rbs +2 -2
- data/sig/generated/riffer/rig/terminal/renderer.rbs +126 -0
- data/sig/generated/riffer/rig/terminal/smoother.rbs +59 -0
- data/sig/generated/riffer/rig/{ui → terminal}/theme.rbs +5 -7
- data/sig/generated/riffer/rig/terminal.rbs +85 -0
- data/sig/generated/riffer/rig/tools/bash.rbs +14 -1
- data/sig/generated/riffer/rig.rbs +4 -7
- data/sig/manual/riffer/rig/cli.rbs +2 -4
- data/sig/manual/riffer/rig/commands/model.rbs +6 -0
- data/sig/manual/riffer/rig/commands/skill.rbs +6 -0
- data/sig/manual/riffer/rig/credentials.rbs +2 -4
- data/sig/manual/riffer/rig/directories.rbs +6 -0
- data/sig/manual/riffer/rig/events.rbs +6 -0
- data/sig/manual/riffer/rig/hosts.rbs +12 -0
- data/sig/manual/riffer/rig/mcp.rbs +11 -0
- data/sig/manual/riffer/rig/prompts/agents_md.rbs +6 -0
- data/sig/manual/riffer/rig/settings.rbs +2 -4
- data/sig/manual/riffer/rig/skills.rbs +6 -0
- data/sig/manual/riffer/rig/support.rbs +8 -0
- data/sig/manual/riffer/rig/terminal/banner.rbs +6 -0
- metadata +173 -35
- data/lib/riffer/rig/coding_agent.rb +0 -53
- data/lib/riffer/rig/host.rb +0 -52
- data/lib/riffer/rig/repl.rb +0 -189
- data/lib/riffer/rig/token_tally.rb +0 -74
- data/sig/generated/riffer/rig/coding_agent.rbs +0 -9
- data/sig/generated/riffer/rig/host.rbs +0 -42
- data/sig/generated/riffer/rig/repl.rbs +0 -77
- data/sig/generated/riffer/rig/token_tally.rbs +0 -61
- data/sig/generated/riffer/rig/ui/banner.rbs +0 -42
- data/sig/generated/riffer/rig/ui/cursor.rbs +0 -33
- data/sig/generated/riffer/rig/ui/renderer.rbs +0 -118
- data/sig/generated/riffer/rig/ui/smoother.rbs +0 -72
- data/sig/manual/riffer/rig/ui/banner.rbs +0 -8
- data/sig/manual/riffer/rig/ui.rbs +0 -5
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Riffer::Rig::Commands::Model
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
HINT = 'Use /model provider/name, with a provider from: ' \
|
|
7
|
+
"#{Riffer::Rig::Settings::PROVIDERS.join(', ')}".freeze #: String
|
|
8
|
+
|
|
9
|
+
SAVE_UNAVAILABLE = '/model --save is not available in this host'
|
|
10
|
+
|
|
11
|
+
# @rbs return: Riffer::Rig::Command
|
|
12
|
+
def command
|
|
13
|
+
Riffer::Rig::Command.new(
|
|
14
|
+
'model',
|
|
15
|
+
description: 'Switch the model for this session: /model provider/name',
|
|
16
|
+
extension: 'core'
|
|
17
|
+
) { |ctx| run(ctx) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @rbs ctx: Riffer::Rig::Command::Context
|
|
21
|
+
# @rbs return: void
|
|
22
|
+
def run(ctx)
|
|
23
|
+
model = ctx.args.strip
|
|
24
|
+
return ctx.host.notify(SAVE_UNAVAILABLE, level: :error) if model.split.include?('--save')
|
|
25
|
+
return ctx.say("Model: #{ctx.runtime.model}") if model.empty?
|
|
26
|
+
|
|
27
|
+
provider = Riffer::Rig::Settings.provider_for(model)
|
|
28
|
+
return ctx.say(HINT) unless provider && Riffer::Providers::Repository.find(provider)
|
|
29
|
+
|
|
30
|
+
missing = missing_credentials(provider, ctx.runtime.credentials)
|
|
31
|
+
return ctx.host.notify(refusal(model, provider, missing), level: :error) unless missing.empty?
|
|
32
|
+
|
|
33
|
+
ctx.runtime.model = model
|
|
34
|
+
ctx.say("Model: #{model}")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# @rbs model: String
|
|
40
|
+
# @rbs provider: String
|
|
41
|
+
# @rbs missing: Array[Symbol]
|
|
42
|
+
# @rbs return: String
|
|
43
|
+
def refusal(model, provider, missing)
|
|
44
|
+
"Not switching to #{model}: #{provider} has no #{missing.join(', ')} in credentials"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @rbs provider: String
|
|
48
|
+
# @rbs credentials: Hash[Symbol, Hash[Symbol, String]]
|
|
49
|
+
# @rbs return: Array[Symbol]
|
|
50
|
+
def missing_credentials(provider, credentials)
|
|
51
|
+
required = Riffer::Rig::ProviderSetup[provider]&.fields&.select(&:required) || []
|
|
52
|
+
required.map(&:name).reject { |name| credentials.dig(provider.to_sym, name) }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Riffer::Rig::Commands::Skill
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
# @rbs skill: Riffer::Skills::Frontmatter
|
|
7
|
+
# @rbs return: Riffer::Rig::Command
|
|
8
|
+
def command(skill)
|
|
9
|
+
Riffer::Rig::Command.new("skill:#{skill.name}", description: skill.description, extension: 'core') do |ctx|
|
|
10
|
+
run(ctx, skill)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @rbs ctx: Riffer::Rig::Command::Context
|
|
15
|
+
# @rbs skill: Riffer::Skills::Frontmatter
|
|
16
|
+
# @rbs return: void
|
|
17
|
+
def run(ctx, skill)
|
|
18
|
+
skills = ctx.runtime.agent.context.skills
|
|
19
|
+
return ctx.host.notify("Skill #{skill.name} is no longer available", level: :error) unless skills
|
|
20
|
+
|
|
21
|
+
# Context#read, not #activate: a skill the user runs stays in the model's
|
|
22
|
+
# catalog, and only the model's own activations are recorded.
|
|
23
|
+
content = skills.adapter.render_activation(skill, skills.read(skill.name))
|
|
24
|
+
ctx.emit(Riffer::Rig::Events::SkillActivated.new(skill.name))
|
|
25
|
+
ctx.prompt([content, ctx.args.strip].reject(&:empty?).join("\n\n"))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Credentials::Resolution
|
|
4
|
+
# @dynamic values, missing
|
|
5
|
+
attr_reader :values #: Hash[Symbol, String]
|
|
6
|
+
attr_reader :missing #: Array[Symbol]
|
|
7
|
+
|
|
8
|
+
# @rbs values: Hash[Symbol, String]
|
|
9
|
+
# @rbs missing: Array[Symbol]
|
|
10
|
+
# @rbs return: void
|
|
11
|
+
def initialize(values:, missing:)
|
|
12
|
+
@values = values.freeze
|
|
13
|
+
@missing = missing.freeze
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -2,88 +2,179 @@
|
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
4
|
require 'fileutils'
|
|
5
|
+
require 'open3'
|
|
5
6
|
|
|
6
|
-
# Reads and writes API keys for each supported provider in
|
|
7
|
-
# <tt>~/.riffer/auth.json</tt>.
|
|
8
|
-
#
|
|
9
|
-
# Keys are stored under the provider name, e.g.:
|
|
10
|
-
#
|
|
11
|
-
# {
|
|
12
|
-
# "anthropic": "sk-ant-...",
|
|
13
|
-
# "openai": "sk-...",
|
|
14
|
-
# "gemini": "...",
|
|
15
|
-
# "openrouter": "sk-or-..."
|
|
16
|
-
# }
|
|
17
|
-
#
|
|
18
|
-
# The environment variable checked per provider:
|
|
19
|
-
#
|
|
20
|
-
# anthropic → ANTHROPIC_API_KEY
|
|
21
|
-
# openai → OPENAI_API_KEY
|
|
22
|
-
# gemini → GEMINI_API_KEY
|
|
23
|
-
# openrouter → OPENROUTER_API_KEY
|
|
24
|
-
#
|
|
25
7
|
module Riffer::Rig::Credentials
|
|
26
8
|
extend self
|
|
27
9
|
|
|
28
10
|
PATH = File.expand_path('~/.riffer/auth.json') #: String
|
|
29
11
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
12
|
+
ENTRY_TYPE = 'api_key'
|
|
13
|
+
|
|
14
|
+
# @rbs identifier: String | Symbol
|
|
15
|
+
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
16
|
+
# @rbs setup: Riffer::Rig::ProviderSetup
|
|
17
|
+
# @rbs env: Riffer::Rig::Env
|
|
18
|
+
# @rbs auth_path: String
|
|
19
|
+
# @rbs settings_path: String
|
|
20
|
+
# @rbs return: Resolution
|
|
21
|
+
def resolve(
|
|
22
|
+
identifier,
|
|
23
|
+
host:,
|
|
24
|
+
setup: Riffer::Rig::ProviderSetup.for(identifier),
|
|
25
|
+
env: Riffer::Rig::Env.new,
|
|
26
|
+
auth_path: PATH,
|
|
27
|
+
settings_path: Riffer::Rig::Settings::PATH
|
|
28
|
+
)
|
|
29
|
+
secrets = stored_secrets(identifier, auth_path)
|
|
30
|
+
plain = Riffer::Rig::Settings.provider_fields(identifier.to_s, path: settings_path)
|
|
31
|
+
found = setup.fields.to_h do |field|
|
|
32
|
+
[field.name, env.value_for(field) || stored_value(field, secrets, plain, env) || field.fallback&.call]
|
|
33
|
+
end.compact
|
|
34
|
+
required = setup.fields.select(&:required)
|
|
35
|
+
answers = ask_for(required.reject { |field| found.key?(field.name) }, identifier, host)
|
|
36
|
+
store(identifier, answers, setup:, auth_path:, settings_path:)
|
|
37
|
+
|
|
38
|
+
values = found.merge(answers)
|
|
39
|
+
Resolution.new(values:, missing: required.map(&:name) - values.keys)
|
|
47
40
|
end
|
|
48
41
|
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
# @rbs
|
|
52
|
-
# @rbs
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
# @rbs identifier: String | Symbol
|
|
43
|
+
# @rbs values: Hash[Symbol, String]
|
|
44
|
+
# @rbs config: Riffer::Config
|
|
45
|
+
# @rbs return: void
|
|
46
|
+
def apply(identifier, values, config: Riffer.config)
|
|
47
|
+
return unless Riffer::Rig::ProviderSetup[identifier]
|
|
48
|
+
|
|
49
|
+
provider_config = config.public_send(identifier)
|
|
50
|
+
values.each { |name, value| provider_config.public_send(:"#{name}=", value) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @rbs identifier: String | Symbol
|
|
54
|
+
# @rbs values: Hash[Symbol, String]
|
|
55
|
+
# @rbs setup: Riffer::Rig::ProviderSetup
|
|
56
|
+
# @rbs auth_path: String
|
|
57
|
+
# @rbs settings_path: String
|
|
58
|
+
# @rbs return: void
|
|
59
|
+
def store(
|
|
60
|
+
identifier,
|
|
61
|
+
values,
|
|
62
|
+
setup: Riffer::Rig::ProviderSetup.for(identifier),
|
|
63
|
+
auth_path: PATH,
|
|
64
|
+
settings_path: Riffer::Rig::Settings::PATH
|
|
65
|
+
)
|
|
66
|
+
secret_names = setup.fields.select(&:secret).map { |field| field.name.to_s }
|
|
67
|
+
fields = values.transform_keys(&:to_s)
|
|
68
|
+
secrets = fields.slice(*secret_names)
|
|
69
|
+
plain = fields.except(*secret_names)
|
|
70
|
+
|
|
71
|
+
unless secrets.empty?
|
|
72
|
+
entries = read_auth(auth_path).merge(identifier.to_s => secrets) do |_identifier, stored, given|
|
|
73
|
+
stored.merge(given)
|
|
74
|
+
end
|
|
75
|
+
write_auth(auth_path, entries)
|
|
60
76
|
end
|
|
61
|
-
|
|
77
|
+
Riffer::Rig::Settings.store_provider(identifier.to_s, plain, path: settings_path) unless plain.empty?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @rbs identifier: String | Symbol
|
|
81
|
+
# @rbs auth_path: String
|
|
82
|
+
# @rbs settings_path: String
|
|
83
|
+
# @rbs return: void
|
|
84
|
+
def remove(identifier, auth_path: PATH, settings_path: Riffer::Rig::Settings::PATH)
|
|
85
|
+
entries = read_auth(auth_path)
|
|
86
|
+
write_auth(auth_path, entries.except(identifier.to_s)) if entries.key?(identifier.to_s)
|
|
87
|
+
Riffer::Rig::Settings.remove_provider(identifier.to_s, path: settings_path)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @rbs identifier: String | Symbol
|
|
91
|
+
# @rbs setup: Riffer::Rig::ProviderSetup
|
|
92
|
+
# @rbs env: Riffer::Rig::Env
|
|
93
|
+
# @rbs auth_path: String
|
|
94
|
+
# @rbs return: :env | :stored | :chain | :missing
|
|
95
|
+
def status(identifier, setup: Riffer::Rig::ProviderSetup.for(identifier), env: Riffer::Rig::Env.new, auth_path: PATH)
|
|
96
|
+
secret_fields = setup.fields.select(&:secret)
|
|
97
|
+
return :env if secret_fields.any? { |field| env.value_for(field) }
|
|
98
|
+
|
|
99
|
+
stored = stored_secrets(identifier, auth_path)
|
|
100
|
+
return :stored if secret_fields.any? { |field| stored.key?(field.name.to_s) }
|
|
101
|
+
|
|
102
|
+
setup.chain ? :chain : :missing
|
|
62
103
|
end
|
|
63
104
|
|
|
64
105
|
private
|
|
65
106
|
|
|
66
|
-
# @rbs
|
|
67
|
-
# @rbs
|
|
107
|
+
# @rbs fields: Array[Riffer::Rig::ProviderSetup::Field]
|
|
108
|
+
# @rbs identifier: String | Symbol
|
|
109
|
+
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
110
|
+
# @rbs return: Hash[Symbol, String]
|
|
111
|
+
def ask_for(fields, identifier, host)
|
|
112
|
+
fields.to_h do |field|
|
|
113
|
+
[field.name, presence(host.ask("#{identifier} #{field.name}", secret: field.secret))]
|
|
114
|
+
end.compact
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @rbs field: Riffer::Rig::ProviderSetup::Field
|
|
118
|
+
# @rbs secrets: Hash[String, String]
|
|
119
|
+
# @rbs plain: Hash[String, String]
|
|
120
|
+
# @rbs env: Riffer::Rig::Env
|
|
121
|
+
# @rbs return: String?
|
|
122
|
+
def stored_value(field, secrets, plain, env)
|
|
123
|
+
field.secret ? expand(secrets[field.name.to_s], env) : plain[field.name.to_s]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @rbs value: String?
|
|
127
|
+
# @rbs env: Riffer::Rig::Env
|
|
128
|
+
# @rbs return: String?
|
|
129
|
+
def expand(value, env)
|
|
130
|
+
return nil if value.nil?
|
|
131
|
+
return env[value.delete_prefix('$')] if value.start_with?('$')
|
|
132
|
+
return presence(run(value.delete_prefix('!'))) if value.start_with?('!')
|
|
133
|
+
|
|
134
|
+
value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @rbs command: String
|
|
68
138
|
# @rbs return: String?
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
139
|
+
def run(command)
|
|
140
|
+
stdout, status = Open3.capture2(command)
|
|
141
|
+
stdout if status.success?
|
|
142
|
+
end
|
|
72
143
|
|
|
73
|
-
|
|
144
|
+
# @rbs value: String?
|
|
145
|
+
# @rbs return: String?
|
|
146
|
+
def presence(value)
|
|
147
|
+
stripped = value.to_s.strip
|
|
148
|
+
stripped unless stripped.empty?
|
|
74
149
|
end
|
|
75
150
|
|
|
76
|
-
#
|
|
77
|
-
# malformed. Non-string values can't be keys, so they're dropped here rather
|
|
78
|
-
# than re-checked by every reader.
|
|
79
|
-
#
|
|
151
|
+
# @rbs identifier: String | Symbol
|
|
80
152
|
# @rbs path: String
|
|
81
153
|
# @rbs return: Hash[String, String]
|
|
82
|
-
def
|
|
154
|
+
def stored_secrets(identifier, path)
|
|
155
|
+
read_auth(path)[identifier.to_s] || {}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# @rbs path: String
|
|
159
|
+
# @rbs return: Hash[String, Hash[String, String]]
|
|
160
|
+
def read_auth(path)
|
|
83
161
|
return {} unless File.file?(path)
|
|
84
162
|
|
|
85
|
-
JSON.parse(File.read(path))
|
|
163
|
+
JSON.parse(File.read(path))
|
|
164
|
+
.select { |_identifier, entry| entry.is_a?(Hash) && entry['type'] == ENTRY_TYPE }
|
|
165
|
+
.transform_values { |entry| entry.except('type').select { |_name, value| value.is_a?(String) } }
|
|
86
166
|
rescue JSON::ParserError
|
|
87
167
|
{}
|
|
88
168
|
end
|
|
169
|
+
|
|
170
|
+
# @rbs path: String
|
|
171
|
+
# @rbs entries: Hash[String, Hash[String, String]]
|
|
172
|
+
# @rbs return: void
|
|
173
|
+
def write_auth(path, entries)
|
|
174
|
+
FileUtils.mkdir_p(File.dirname(path), mode: 0o700)
|
|
175
|
+
data = entries.transform_values { |fields| { 'type' => ENTRY_TYPE }.merge(fields) }
|
|
176
|
+
File.open(path, File::WRONLY | File::CREAT | File::TRUNC, 0o600) do |file|
|
|
177
|
+
file.write(JSON.pretty_generate(data))
|
|
178
|
+
end
|
|
179
|
+
end
|
|
89
180
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Riffer::Rig::Directories
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
# @rbs dir: String
|
|
7
|
+
# @rbs return: Array[String]
|
|
8
|
+
def ancestors(dir)
|
|
9
|
+
parent = File.dirname(dir)
|
|
10
|
+
parent == dir ? [dir] : [dir, *ancestors(parent)]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @rbs cwd: String
|
|
14
|
+
# @rbs return: Array[String]
|
|
15
|
+
def up_to_repo_root(cwd)
|
|
16
|
+
dirs = ancestors(File.expand_path(cwd))
|
|
17
|
+
root = dirs.index { |dir| File.exist?(File.join(dir, '.git')) } || 0
|
|
18
|
+
dirs.take(root + 1)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Env
|
|
4
|
+
# @rbs @source: Hash[String, String]
|
|
5
|
+
|
|
6
|
+
# @dynamic no_color, model
|
|
7
|
+
attr_reader :no_color #: bool
|
|
8
|
+
attr_reader :model #: String?
|
|
9
|
+
|
|
10
|
+
# @rbs source: Hash[String, String]
|
|
11
|
+
# @rbs return: Riffer::Rig::Env | Riffer::Rig::Env::Invalid
|
|
12
|
+
def self.load(source = ENV.to_h)
|
|
13
|
+
env = new(source)
|
|
14
|
+
model = env.model
|
|
15
|
+
rejection = model && Riffer::Rig::Settings.rejection(model)
|
|
16
|
+
rejection ? Invalid.new("RIFFER_MODEL: #{rejection}") : env
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @rbs source: Hash[String, String]
|
|
20
|
+
# @rbs return: void
|
|
21
|
+
def initialize(source = ENV.to_h)
|
|
22
|
+
@source = source.freeze
|
|
23
|
+
@no_color = @source.key?('NO_COLOR')
|
|
24
|
+
@model = self['RIFFER_MODEL']
|
|
25
|
+
freeze
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @rbs name: String
|
|
29
|
+
# @rbs return: String?
|
|
30
|
+
def [](name)
|
|
31
|
+
stripped = @source[name].to_s.strip
|
|
32
|
+
stripped unless stripped.empty?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @rbs field: Riffer::Rig::ProviderSetup::Field
|
|
36
|
+
# @rbs return: String?
|
|
37
|
+
def value_for(field)
|
|
38
|
+
field.env.filter_map { |name| self[name] }.first
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::AfterResponse
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic message
|
|
7
|
+
attr_reader :message #: ::Riffer::Messages::Assistant
|
|
8
|
+
|
|
9
|
+
# @rbs message: ::Riffer::Messages::Assistant
|
|
10
|
+
# @rbs return: void
|
|
11
|
+
def initialize(message)
|
|
12
|
+
@message = message
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @rbs return: Symbol
|
|
17
|
+
def type
|
|
18
|
+
:after_response
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
22
|
+
def to_h
|
|
23
|
+
{ type: type, message: message }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::AfterToolCall
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic tool, args, result
|
|
7
|
+
attr_reader :tool #: String
|
|
8
|
+
attr_reader :args #: Hash[Symbol, untyped]
|
|
9
|
+
attr_reader :result #: ::Riffer::Tools::Response
|
|
10
|
+
|
|
11
|
+
# @rbs tool: String
|
|
12
|
+
# @rbs args: Hash[Symbol, untyped]
|
|
13
|
+
# @rbs result: ::Riffer::Tools::Response
|
|
14
|
+
# @rbs return: void
|
|
15
|
+
def initialize(tool, args, result)
|
|
16
|
+
@tool = tool
|
|
17
|
+
@args = args.dup.freeze
|
|
18
|
+
@result = result
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @rbs return: Symbol
|
|
23
|
+
def type
|
|
24
|
+
:after_tool_call
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
28
|
+
def to_h
|
|
29
|
+
{ type: type, tool: tool, args: args, result: result }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::BeforePrompt
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic text
|
|
7
|
+
attr_reader :text #: String
|
|
8
|
+
|
|
9
|
+
# @rbs text: String
|
|
10
|
+
# @rbs return: void
|
|
11
|
+
def initialize(text)
|
|
12
|
+
@text = text
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @rbs return: Symbol
|
|
17
|
+
def type
|
|
18
|
+
:before_prompt
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
22
|
+
def to_h
|
|
23
|
+
{ type: type, text: text }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::BeforeRequest
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic messages
|
|
7
|
+
attr_reader :messages #: Array[::Riffer::Messages::Base]
|
|
8
|
+
|
|
9
|
+
# @rbs messages: Array[::Riffer::Messages::Base]
|
|
10
|
+
# @rbs return: void
|
|
11
|
+
def initialize(messages)
|
|
12
|
+
@messages = messages.dup.freeze
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @rbs return: Symbol
|
|
17
|
+
def type
|
|
18
|
+
:before_request
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
22
|
+
def to_h
|
|
23
|
+
{ type: type, messages: messages }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::BeforeToolCall
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic tool, args
|
|
7
|
+
attr_reader :tool #: String
|
|
8
|
+
attr_reader :args #: Hash[Symbol, untyped]
|
|
9
|
+
|
|
10
|
+
# @rbs tool: String
|
|
11
|
+
# @rbs args: Hash[Symbol, untyped]
|
|
12
|
+
# @rbs return: void
|
|
13
|
+
def initialize(tool, args)
|
|
14
|
+
@tool = tool
|
|
15
|
+
@args = args.dup.freeze
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @rbs return: Symbol
|
|
20
|
+
def type
|
|
21
|
+
:before_tool_call
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
25
|
+
def to_h
|
|
26
|
+
{ type: type, tool: tool, args: args }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::CommandOutput
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic command, text
|
|
7
|
+
attr_reader :command #: String
|
|
8
|
+
attr_reader :text #: String
|
|
9
|
+
|
|
10
|
+
# @rbs command: String
|
|
11
|
+
# @rbs text: String
|
|
12
|
+
# @rbs return: void
|
|
13
|
+
def initialize(command, text)
|
|
14
|
+
@command = command
|
|
15
|
+
@text = text
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @rbs return: Symbol
|
|
20
|
+
def type
|
|
21
|
+
:command_output
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
25
|
+
def to_h
|
|
26
|
+
{ type: type, command: command, text: text }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::Notify
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic message, level
|
|
7
|
+
attr_reader :message #: String?
|
|
8
|
+
attr_reader :level #: Symbol
|
|
9
|
+
|
|
10
|
+
# @rbs message: String?
|
|
11
|
+
# @rbs level: Symbol
|
|
12
|
+
# @rbs return: void
|
|
13
|
+
def initialize(message, level)
|
|
14
|
+
@message = message
|
|
15
|
+
@level = level
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @rbs return: Symbol
|
|
20
|
+
def type
|
|
21
|
+
:notify
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
25
|
+
def to_h
|
|
26
|
+
{ type: type, message: message, level: level }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::SessionEnd
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic reason
|
|
7
|
+
attr_reader :reason #: Symbol
|
|
8
|
+
|
|
9
|
+
# @rbs reason: Symbol
|
|
10
|
+
# @rbs return: void
|
|
11
|
+
def initialize(reason)
|
|
12
|
+
@reason = reason
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @rbs return: Symbol
|
|
17
|
+
def type
|
|
18
|
+
:session_end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
22
|
+
def to_h
|
|
23
|
+
{ type: type, reason: reason }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Riffer::Rig::Events::SessionStart
|
|
4
|
+
include Riffer::Rig::Support::Equatable
|
|
5
|
+
|
|
6
|
+
# @dynamic id, reason
|
|
7
|
+
attr_reader :id #: String
|
|
8
|
+
attr_reader :reason #: Symbol
|
|
9
|
+
|
|
10
|
+
# @rbs id: String
|
|
11
|
+
# @rbs reason: Symbol
|
|
12
|
+
# @rbs return: void
|
|
13
|
+
def initialize(id, reason)
|
|
14
|
+
@id = id
|
|
15
|
+
@reason = reason
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @rbs return: Symbol
|
|
20
|
+
def type
|
|
21
|
+
:session_start
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
25
|
+
def to_h
|
|
26
|
+
{ type: type, id: id, reason: reason }
|
|
27
|
+
end
|
|
28
|
+
end
|