ask-app-server 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 +15 -0
- data/LICENSE +21 -0
- data/README.md +108 -0
- data/bin/ask-app-server +6 -0
- data/lib/ask/app_server/agent_adapter.rb +206 -0
- data/lib/ask/app_server/cli.rb +173 -0
- data/lib/ask/app_server/config.rb +258 -0
- data/lib/ask/app_server/event_translator.rb +194 -0
- data/lib/ask/app_server/permission_handler.rb +167 -0
- data/lib/ask/app_server/server.rb +384 -0
- data/lib/ask/app_server/session_manager.rb +237 -0
- data/lib/ask/app_server/session_store.rb +138 -0
- data/lib/ask/app_server/version.rb +7 -0
- data/lib/ask/app_server.rb +13 -0
- data/lib/ask-app-server.rb +15 -0
- metadata +201 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Ask
|
|
6
|
+
module AppServer
|
|
7
|
+
# Session store backed by Ask::State::Adapter.
|
|
8
|
+
#
|
|
9
|
+
# Stores session metadata and subscriptions in the state backend.
|
|
10
|
+
# AgentAdapter instances (which hold in-memory agent sessions) are
|
|
11
|
+
# kept separately and not persisted.
|
|
12
|
+
#
|
|
13
|
+
# Default backend is Ask::State::Memory (in-process, ephemeral).
|
|
14
|
+
# Pass Ask::State::Providers::SQLite for persistent storage:
|
|
15
|
+
#
|
|
16
|
+
# store = SessionStore.new(
|
|
17
|
+
# state: Ask::State::Providers::SQLite.new(path: "~/.ask-app-server/state.db")
|
|
18
|
+
# )
|
|
19
|
+
class SessionStore
|
|
20
|
+
SESSION_PREFIX = "session:"
|
|
21
|
+
SUBSCRIPTION_PREFIX = "sub:"
|
|
22
|
+
SESSION_LIST_KEY = "sessions"
|
|
23
|
+
EVENT_PREFIX = "events:"
|
|
24
|
+
|
|
25
|
+
def initialize(state: nil)
|
|
26
|
+
@state = state || Ask::State::Memory.new
|
|
27
|
+
@adapters = {} # session_id => AgentAdapter (in-memory only)
|
|
28
|
+
@mutex = Mutex.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Register a new session.
|
|
32
|
+
def add(session_id, adapter)
|
|
33
|
+
model = adapter.respond_to?(:instance_variable_get) ? adapter.instance_variable_get(:@model) : nil
|
|
34
|
+
metadata = {
|
|
35
|
+
sessionId: session_id,
|
|
36
|
+
model: model,
|
|
37
|
+
createdAt: adapter.respond_to?(:created_at) ? adapter.created_at.iso8601 : Time.now.iso8601
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
existing = @state.get("#{SESSION_PREFIX}#{session_id}")
|
|
42
|
+
raise Ask::AppServer::SessionAlreadyExists, "Session #{session_id} already exists" if existing
|
|
43
|
+
|
|
44
|
+
@state.set("#{SESSION_PREFIX}#{session_id}", metadata)
|
|
45
|
+
@state.list_append(SESSION_LIST_KEY, session_id, max_length: 200)
|
|
46
|
+
@adapters[session_id] = adapter
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Remove a session.
|
|
51
|
+
def remove(session_id)
|
|
52
|
+
@mutex.synchronize do
|
|
53
|
+
_remove(session_id)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Internal remove (no locking — caller must hold @mutex).
|
|
58
|
+
def _remove(session_id)
|
|
59
|
+
@state.delete("#{SESSION_PREFIX}#{session_id}")
|
|
60
|
+
@state.delete("#{SUBSCRIPTION_PREFIX}#{session_id}")
|
|
61
|
+
@state.delete("#{EVENT_PREFIX}#{session_id}")
|
|
62
|
+
@adapters.delete(session_id)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get a session adapter by ID.
|
|
66
|
+
def get(session_id)
|
|
67
|
+
@mutex.synchronize { @adapters[session_id] }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# List all sessions (summaries).
|
|
71
|
+
def list(limit: 20)
|
|
72
|
+
session_ids = @state.list_range(SESSION_LIST_KEY, 0, limit - 1)
|
|
73
|
+
session_ids.map do |sid|
|
|
74
|
+
metadata = @state.get("#{SESSION_PREFIX}#{sid}")
|
|
75
|
+
if metadata
|
|
76
|
+
adapter = @adapters[sid]
|
|
77
|
+
metadata.merge(
|
|
78
|
+
running: adapter&.running || false,
|
|
79
|
+
idle: adapter&.idle? || true
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
end.compact
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Subscribe to a session's events.
|
|
86
|
+
def subscribe(session_id, delivery_kind:)
|
|
87
|
+
@mutex.synchronize do
|
|
88
|
+
raise Ask::AppServer::SessionNotFound, "Session #{session_id} not found" unless @adapters.key?(session_id)
|
|
89
|
+
@state.set("#{SUBSCRIPTION_PREFIX}#{session_id}", { subscribed: true, deliveryKind: delivery_kind })
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Unsubscribe from a session.
|
|
94
|
+
def unsubscribe(session_id)
|
|
95
|
+
@mutex.synchronize do
|
|
96
|
+
@state.delete("#{SUBSCRIPTION_PREFIX}#{session_id}")
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Is the session subscribed?
|
|
101
|
+
def subscribed?(session_id)
|
|
102
|
+
sub = @state.get("#{SUBSCRIPTION_PREFIX}#{session_id}")
|
|
103
|
+
sub && sub[:subscribed]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Count of active sessions.
|
|
107
|
+
def count
|
|
108
|
+
@adapters.size
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Clear all sessions.
|
|
112
|
+
def clear
|
|
113
|
+
@mutex.synchronize do
|
|
114
|
+
@adapters.each_key { |sid| _remove(sid) }
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Iterate over all session adapters.
|
|
119
|
+
def each(&block)
|
|
120
|
+
# Iterate over a snapshot to avoid holding the mutex during yield
|
|
121
|
+
snapshot = @mutex.synchronize { @adapters.values.dup }
|
|
122
|
+
snapshot.each(&block)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Append an event to a session's event log.
|
|
126
|
+
def append_event(session_id, event)
|
|
127
|
+
@state.list_append("#{EVENT_PREFIX}#{session_id}", event, max_length: 1000)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Get events for a session after a given sequence number.
|
|
131
|
+
def events_after(session_id, after_seq, limit: nil)
|
|
132
|
+
events = @state.list_range("#{EVENT_PREFIX}#{session_id}") || []
|
|
133
|
+
filtered = events.select { |e| e[:seq].to_i > after_seq.to_i }
|
|
134
|
+
limit ? filtered.first(limit) : filtered
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ask
|
|
4
|
+
module AppServer
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
class ProtocolError < Error; end
|
|
7
|
+
class SessionNotFound < Error; end
|
|
8
|
+
class SessionAlreadyExists < Error; end
|
|
9
|
+
class SessionNotSubscribed < Error; end
|
|
10
|
+
class InvalidRequest < Error; end
|
|
11
|
+
class TimeoutError < Error; end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ask/agent"
|
|
4
|
+
require "ask-tools-shell"
|
|
5
|
+
|
|
6
|
+
require_relative "ask/app_server/version"
|
|
7
|
+
require_relative "ask/app_server"
|
|
8
|
+
require_relative "ask/app_server/config"
|
|
9
|
+
require_relative "ask/app_server/event_translator"
|
|
10
|
+
require_relative "ask/app_server/agent_adapter"
|
|
11
|
+
require_relative "ask/app_server/permission_handler"
|
|
12
|
+
require_relative "ask/app_server/session_store"
|
|
13
|
+
require_relative "ask/app_server/session_manager"
|
|
14
|
+
require_relative "ask/app_server/server"
|
|
15
|
+
require_relative "ask/app_server/cli"
|
metadata
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ask-app-server
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Kaka Ruto
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ask-agent
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ask-core
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: ask-tools
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.1'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.1'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: ask-tools-shell
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.1'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.1'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: ask-state-providers
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0.1'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0.1'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: minitest
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '5.25'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '5.25'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: mocha
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '3.1'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '3.1'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: rake
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '13.0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '13.0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: simplecov
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0.22'
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0.22'
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: ostruct
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0'
|
|
145
|
+
type: :development
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
152
|
+
description: Exposes Ask::Agent::Session behind the standard app-server JSON-RPC/stdio
|
|
153
|
+
protocol. Drop-in compatible with clients that speak the ZCode/Codex app-server
|
|
154
|
+
protocol — Telegram bots, AI SDK providers, IDE extensions, and headless automation.
|
|
155
|
+
email:
|
|
156
|
+
- kaka@myrrlabs.com
|
|
157
|
+
executables:
|
|
158
|
+
- ask-app-server
|
|
159
|
+
extensions: []
|
|
160
|
+
extra_rdoc_files: []
|
|
161
|
+
files:
|
|
162
|
+
- CHANGELOG.md
|
|
163
|
+
- LICENSE
|
|
164
|
+
- README.md
|
|
165
|
+
- bin/ask-app-server
|
|
166
|
+
- lib/ask-app-server.rb
|
|
167
|
+
- lib/ask/app_server.rb
|
|
168
|
+
- lib/ask/app_server/agent_adapter.rb
|
|
169
|
+
- lib/ask/app_server/cli.rb
|
|
170
|
+
- lib/ask/app_server/config.rb
|
|
171
|
+
- lib/ask/app_server/event_translator.rb
|
|
172
|
+
- lib/ask/app_server/permission_handler.rb
|
|
173
|
+
- lib/ask/app_server/server.rb
|
|
174
|
+
- lib/ask/app_server/session_manager.rb
|
|
175
|
+
- lib/ask/app_server/session_store.rb
|
|
176
|
+
- lib/ask/app_server/version.rb
|
|
177
|
+
homepage: https://github.com/ask-rb/ask-app-server
|
|
178
|
+
licenses:
|
|
179
|
+
- MIT
|
|
180
|
+
metadata:
|
|
181
|
+
homepage_uri: https://github.com/ask-rb/ask-app-server
|
|
182
|
+
source_code_uri: https://github.com/ask-rb/ask-app-server
|
|
183
|
+
changelog_uri: https://github.com/ask-rb/ask-app-server/blob/master/CHANGELOG.md
|
|
184
|
+
rdoc_options: []
|
|
185
|
+
require_paths:
|
|
186
|
+
- lib
|
|
187
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
188
|
+
requirements:
|
|
189
|
+
- - ">="
|
|
190
|
+
- !ruby/object:Gem::Version
|
|
191
|
+
version: '3.2'
|
|
192
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
|
+
requirements:
|
|
194
|
+
- - ">="
|
|
195
|
+
- !ruby/object:Gem::Version
|
|
196
|
+
version: '0'
|
|
197
|
+
requirements: []
|
|
198
|
+
rubygems_version: 4.0.3
|
|
199
|
+
specification_version: 4
|
|
200
|
+
summary: JSON-RPC app-server for ask-rb agents
|
|
201
|
+
test_files: []
|