kobako 0.3.0 → 0.5.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/.release-please-manifest.json +1 -0
- data/CHANGELOG.md +29 -0
- data/Cargo.lock +1 -1
- data/README.md +85 -6
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +1 -1
- data/ext/kobako/src/lib.rs +4 -2
- data/ext/kobako/src/{wasm → runtime}/cache.rs +22 -18
- data/ext/kobako/src/runtime/capture.rs +91 -0
- data/ext/kobako/src/runtime/config.rs +26 -0
- data/ext/kobako/src/runtime/dispatch.rs +211 -0
- data/ext/kobako/src/runtime/exports.rs +51 -0
- data/ext/kobako/src/runtime/guest_mem.rs +228 -0
- data/ext/kobako/src/{wasm/host_state.rs → runtime/invocation.rs} +195 -81
- data/ext/kobako/src/runtime/trap.rs +134 -0
- data/ext/kobako/src/runtime.rs +782 -0
- data/ext/kobako/src/snapshot.rs +110 -0
- data/lib/kobako/capture.rb +11 -16
- data/lib/kobako/catalog/handles.rb +107 -0
- data/lib/kobako/catalog/namespaces.rb +99 -0
- data/lib/kobako/{snippet/table.rb → catalog/snippets.rb} +37 -62
- data/lib/kobako/catalog.rb +18 -0
- data/lib/kobako/codec/decoder.rb +13 -7
- data/lib/kobako/codec/factory.rb +21 -18
- data/lib/kobako/codec/utils.rb +118 -29
- data/lib/kobako/codec.rb +6 -3
- data/lib/kobako/errors.rb +45 -28
- data/lib/kobako/fault.rb +40 -0
- data/lib/kobako/handle.rb +60 -0
- data/lib/kobako/namespace.rb +67 -0
- data/lib/kobako/outcome.rb +55 -29
- data/lib/kobako/runtime.rb +30 -0
- data/lib/kobako/sandbox.rb +131 -67
- data/lib/kobako/sandbox_options.rb +6 -9
- data/lib/kobako/snapshot.rb +40 -0
- data/lib/kobako/snippet/binary.rb +6 -7
- data/lib/kobako/snippet/source.rb +8 -8
- data/lib/kobako/snippet.rb +7 -9
- data/lib/kobako/transport/dispatcher.rb +195 -0
- data/lib/kobako/transport/error.rb +24 -0
- data/lib/kobako/transport/request.rb +78 -0
- data/lib/kobako/transport/response.rb +69 -0
- data/lib/kobako/transport/run.rb +141 -0
- data/lib/kobako/transport/yield.rb +91 -0
- data/lib/kobako/transport/yielder.rb +89 -0
- data/lib/kobako/transport.rb +24 -0
- data/lib/kobako/usage.rb +41 -0
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +4 -3
- data/release-please-config.json +24 -0
- data/sig/kobako/capture.rbs +0 -2
- data/sig/kobako/{rpc/handle_table.rbs → catalog/handles.rbs} +3 -9
- data/sig/kobako/catalog/namespaces.rbs +17 -0
- data/sig/kobako/{snippet/table.rbs → catalog/snippets.rbs} +2 -11
- data/sig/kobako/{rpc.rbs → catalog.rbs} +1 -1
- data/sig/kobako/codec/decoder.rbs +2 -1
- data/sig/kobako/codec/factory.rbs +3 -3
- data/sig/kobako/codec/utils.rbs +11 -1
- data/sig/kobako/errors.rbs +7 -7
- data/sig/kobako/fault.rbs +19 -0
- data/sig/kobako/handle.rbs +18 -0
- data/sig/kobako/namespace.rbs +19 -0
- data/sig/kobako/outcome.rbs +2 -2
- data/sig/kobako/runtime.rbs +23 -0
- data/sig/kobako/sandbox.rbs +10 -7
- data/sig/kobako/snapshot.rbs +15 -0
- data/sig/kobako/transport/dispatcher.rbs +34 -0
- data/sig/kobako/transport/error.rbs +6 -0
- data/sig/kobako/transport/request.rbs +32 -0
- data/sig/kobako/transport/response.rbs +30 -0
- data/sig/kobako/transport/run.rbs +27 -0
- data/sig/kobako/transport/yield.rbs +34 -0
- data/sig/kobako/transport/yielder.rbs +21 -0
- data/sig/kobako/transport.rbs +4 -0
- data/sig/kobako/usage.rbs +11 -0
- metadata +52 -30
- data/ext/kobako/src/wasm/dispatch.rs +0 -161
- data/ext/kobako/src/wasm/instance.rs +0 -771
- data/ext/kobako/src/wasm.rs +0 -125
- data/lib/kobako/invocation.rb +0 -112
- data/lib/kobako/rpc/dispatcher.rb +0 -169
- data/lib/kobako/rpc/envelope.rb +0 -118
- data/lib/kobako/rpc/fault.rb +0 -41
- data/lib/kobako/rpc/handle.rb +0 -39
- data/lib/kobako/rpc/handle_table.rb +0 -107
- data/lib/kobako/rpc/namespace.rb +0 -74
- data/lib/kobako/rpc/server.rb +0 -158
- data/lib/kobako/rpc.rb +0 -11
- data/lib/kobako/wasm.rb +0 -25
- data/sig/kobako/invocation.rbs +0 -23
- data/sig/kobako/rpc/dispatcher.rbs +0 -33
- data/sig/kobako/rpc/envelope.rbs +0 -51
- data/sig/kobako/rpc/fault.rbs +0 -20
- data/sig/kobako/rpc/handle.rbs +0 -19
- data/sig/kobako/rpc/namespace.rbs +0 -24
- data/sig/kobako/rpc/server.rbs +0 -37
- data/sig/kobako/wasm.rbs +0 -39
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "handle"
|
|
4
|
-
|
|
5
|
-
module Kobako
|
|
6
|
-
module RPC
|
|
7
|
-
# Host-side mapping from opaque integer Handle IDs to Ruby objects
|
|
8
|
-
# (capability proxies). One table is owned per +Kobako::RPC::Server+
|
|
9
|
-
# instance (and therefore per +Kobako::Sandbox+ instance). See
|
|
10
|
-
# {docs/behavior.md B-15}[link:../../../docs/behavior.md].
|
|
11
|
-
#
|
|
12
|
-
# Lifecycle invariants ({docs/behavior.md}[link:../../../docs/behavior.md]):
|
|
13
|
-
#
|
|
14
|
-
# - {docs/behavior.md B-15}[link:../../../docs/behavior.md] — Handle IDs are allocated by
|
|
15
|
-
# a monotonically increasing counter scoped to a single `#run`. The
|
|
16
|
-
# first ID issued in a run is 1; ID 0 is reserved as the invalid
|
|
17
|
-
# sentinel and is never returned by #alloc.
|
|
18
|
-
#
|
|
19
|
-
# - {docs/behavior.md B-19}[link:../../../docs/behavior.md] — When between `#run`
|
|
20
|
-
# invocations (via `#reset!`), every Handle issued under the old state
|
|
21
|
-
# becomes invalid.
|
|
22
|
-
#
|
|
23
|
-
# - {docs/behavior.md B-21}[link:../../../docs/behavior.md] — The cap is `0x7fff_ffff`
|
|
24
|
-
# (2³¹ − 1). Allocation beyond the cap raises immediately — no silent
|
|
25
|
-
# truncation, no wrap, no ID reuse.
|
|
26
|
-
class HandleTable
|
|
27
|
-
# Build a fresh, empty HandleTable. +next_id+ is an internal seam that
|
|
28
|
-
# sets the starting value of the monotonic counter (defaults to 1 per
|
|
29
|
-
# B-15); tests pass a value near +RPC::Handle::MAX_ID+ to exercise
|
|
30
|
-
# the cap-exhaustion path without 2³¹ allocations.
|
|
31
|
-
def initialize(next_id: 1)
|
|
32
|
-
@entries = {} # : Hash[Integer, untyped]
|
|
33
|
-
@next_id = next_id
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Bind +object+ in the table and return its newly-allocated Handle ID.
|
|
37
|
-
# +object+ is any host-side Ruby object to bind. Returns a freshly-
|
|
38
|
-
# allocated Handle ID in +[1, RPC::Handle::MAX_ID]+. Raises
|
|
39
|
-
# +Kobako::HandleTableExhausted+ if the next ID would exceed the cap.
|
|
40
|
-
# The cap is anchored on +RPC::Handle+ — the wire codec and the
|
|
41
|
-
# allocator share the same invariant ({docs/behavior.md B-21}[link:../../../docs/behavior.md]).
|
|
42
|
-
def alloc(object)
|
|
43
|
-
id = @next_id
|
|
44
|
-
cap = RPC::Handle::MAX_ID
|
|
45
|
-
raise HandleTableExhausted, "HandleTable exhausted: id #{id} exceeds MAX_ID #{cap}" if id > cap
|
|
46
|
-
|
|
47
|
-
@entries[id] = object
|
|
48
|
-
@next_id = id + 1
|
|
49
|
-
id
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Resolve a Handle ID to its bound object. +id+ is a Handle ID previously
|
|
53
|
-
# returned by +#alloc+. Returns the bound object. Raises
|
|
54
|
-
# +Kobako::HandleTableError+ if +id+ is not currently bound.
|
|
55
|
-
def fetch(id)
|
|
56
|
-
require_bound!(id)
|
|
57
|
-
@entries[id]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Remove and return the binding for +id+. +id+ is the Handle ID to
|
|
61
|
-
# release. Returns the previously-bound object. Raises
|
|
62
|
-
# +Kobako::HandleTableError+ if +id+ is not currently bound.
|
|
63
|
-
def release(id)
|
|
64
|
-
require_bound!(id)
|
|
65
|
-
@entries.delete(id)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Clear all entries AND reset the counter to 1. Called at the per-run
|
|
69
|
-
# boundary — see {docs/behavior.md B-19}[link:../../../docs/behavior.md].
|
|
70
|
-
# Returns +self+.
|
|
71
|
-
def reset!
|
|
72
|
-
@entries.clear
|
|
73
|
-
@next_id = 1
|
|
74
|
-
self
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Mark the entry at +id+ as disconnected (ABA protection). +id+ is the
|
|
78
|
-
# Handle ID to poison; silently ignored if +id+ is not currently bound.
|
|
79
|
-
# Returns +self+ for chainability, matching the convention of +#reset!+.
|
|
80
|
-
def mark_disconnected(id)
|
|
81
|
-
@entries[id] = :disconnected if @entries.key?(id)
|
|
82
|
-
self
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Returns the number of currently-bound entries.
|
|
86
|
-
def size
|
|
87
|
-
@entries.size
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Returns +true+ when +id+ is currently bound, +false+ otherwise.
|
|
91
|
-
def include?(id)
|
|
92
|
-
@entries.key?(id)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
private
|
|
96
|
-
|
|
97
|
-
# Single source of truth for the "unknown Handle id" raise shared by
|
|
98
|
-
# {#fetch} and {#release}. Returns +nil+ on success; raises
|
|
99
|
-
# +Kobako::HandleTableError+ when +id+ is not currently bound.
|
|
100
|
-
def require_bound!(id)
|
|
101
|
-
return if @entries.key?(id)
|
|
102
|
-
|
|
103
|
-
raise HandleTableError, "unknown Handle id: #{id.inspect}"
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
data/lib/kobako/rpc/namespace.rb
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Kobako
|
|
4
|
-
module RPC
|
|
5
|
-
# A named grouping of Members for one Sandbox
|
|
6
|
-
# ({docs/behavior.md B-07..B-11}[link:../../../docs/behavior.md]). Returned by
|
|
7
|
-
# +Sandbox#define+. Each instance owns a flat name→object table of
|
|
8
|
-
# Members; member binding is validated against {NAME_PATTERN}.
|
|
9
|
-
class Namespace
|
|
10
|
-
# Ruby constant-name pattern shared by Namespace and Member names
|
|
11
|
-
# ({docs/behavior.md B-07/B-08 Notes}[link:../../../docs/behavior.md]).
|
|
12
|
-
NAME_PATTERN = /\A[A-Z]\w*\z/
|
|
13
|
-
|
|
14
|
-
attr_reader :name, :members
|
|
15
|
-
|
|
16
|
-
# Build a new Namespace. +name+ is an already-validated Namespace
|
|
17
|
-
# name (must satisfy {NAME_PATTERN}; validation is the caller's
|
|
18
|
-
# responsibility).
|
|
19
|
-
def initialize(name)
|
|
20
|
-
@name = name
|
|
21
|
-
@members = {}
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Bind +object+ under +member+ inside this Namespace. +member+ is a
|
|
25
|
-
# constant-form name as a +Symbol+ or +String+. +object+ is any Ruby
|
|
26
|
-
# object that responds to the methods guest code will invoke. Returns
|
|
27
|
-
# +self+ for chaining. Raises +ArgumentError+ when +member+ does not
|
|
28
|
-
# match the constant pattern, or a Member of the same name is already
|
|
29
|
-
# bound ({docs/behavior.md B-11}[link:../../../docs/behavior.md]).
|
|
30
|
-
def bind(member, object)
|
|
31
|
-
member_str = validate_member_name!(member)
|
|
32
|
-
raise ArgumentError, "Member #{@name}::#{member_str} is already bound" if @members.key?(member_str)
|
|
33
|
-
|
|
34
|
-
@members[member_str] = object
|
|
35
|
-
self
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Member lookup. Returns the bound object or +nil+ when missing.
|
|
39
|
-
def [](member)
|
|
40
|
-
@members[member.to_s]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Strict variant of {#[]}; raises +KeyError+ when no Member is
|
|
44
|
-
# registered under +member+.
|
|
45
|
-
def fetch(member)
|
|
46
|
-
member_str = member.to_s
|
|
47
|
-
unless @members.key?(member_str)
|
|
48
|
-
raise KeyError,
|
|
49
|
-
"no member named #{member_str.inspect} in namespace #{@name.inspect}"
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
@members[member_str]
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Structured description for the guest preamble (Frame 1). Returns a
|
|
56
|
-
# two-element array +[name, member_keys]+ suitable for msgpack encoding.
|
|
57
|
-
def to_preamble
|
|
58
|
-
[@name, @members.keys]
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
private
|
|
62
|
-
|
|
63
|
-
def validate_member_name!(member)
|
|
64
|
-
member_str = member.to_s
|
|
65
|
-
unless NAME_PATTERN.match?(member_str)
|
|
66
|
-
raise ArgumentError,
|
|
67
|
-
"MemberName must match #{NAME_PATTERN.inspect} (got #{member.inspect})"
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
member_str
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
data/lib/kobako/rpc/server.rb
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "msgpack"
|
|
4
|
-
require_relative "../errors"
|
|
5
|
-
require_relative "envelope"
|
|
6
|
-
require_relative "namespace"
|
|
7
|
-
require_relative "handle_table"
|
|
8
|
-
require_relative "dispatcher"
|
|
9
|
-
|
|
10
|
-
module Kobako
|
|
11
|
-
module RPC
|
|
12
|
-
# Kobako::RPC::Server — per-Sandbox host-side RPC coordinator. Maintains
|
|
13
|
-
# the Namespace / Member registry, owns the HandleTable, and routes
|
|
14
|
-
# incoming Requests to the resolved Service object
|
|
15
|
-
# ({docs/behavior.md B-07..B-21}[link:../../../docs/behavior.md]).
|
|
16
|
-
#
|
|
17
|
-
# Public API:
|
|
18
|
-
#
|
|
19
|
-
# server = Kobako::RPC::Server.new
|
|
20
|
-
# namespace = server.define(:MyService) # => Kobako::RPC::Namespace
|
|
21
|
-
# namespace.bind(:KV, kv_object) # => namespace (chainable)
|
|
22
|
-
# server.to_preamble # => array for Frame 1
|
|
23
|
-
# server.dispatch(request_bytes) # => msgpack bytes (delegated to Dispatcher)
|
|
24
|
-
#
|
|
25
|
-
# Namespaces live at +Kobako::RPC::Namespace+
|
|
26
|
-
# (lib/kobako/rpc/namespace.rb). The opaque Handle allocator lives at
|
|
27
|
-
# +Kobako::RPC::HandleTable+
|
|
28
|
-
# (lib/kobako/rpc/handle_table.rb). Dispatch helpers live at
|
|
29
|
-
# +Kobako::RPC::Dispatcher+
|
|
30
|
-
# (lib/kobako/rpc/dispatcher.rb).
|
|
31
|
-
class Server
|
|
32
|
-
# Build a fresh Server. +handle_table+ is an internal seam that
|
|
33
|
-
# injects a pre-configured +HandleTable+; tests pass one whose +next_id+
|
|
34
|
-
# is pinned near +MAX_ID+ to exercise the B-21 cap-exhaustion path
|
|
35
|
-
# without 2³¹ allocations. Production callers leave it at the default.
|
|
36
|
-
def initialize(handle_table: HandleTable.new)
|
|
37
|
-
@namespaces = {} # : Hash[String, Kobako::RPC::Namespace]
|
|
38
|
-
@handle_table = handle_table
|
|
39
|
-
@sealed = false
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Declare or retrieve the Namespace named +name+ (idempotent — docs/behavior.md B-10).
|
|
43
|
-
# +name+ is a constant-form name as a +Symbol+ or +String+ (must satisfy
|
|
44
|
-
# +Namespace::NAME_PATTERN+). Returns the +Kobako::RPC::Namespace+ for
|
|
45
|
-
# that name, creating it if it does not exist. Raises +ArgumentError+
|
|
46
|
-
# when +name+ is malformed, or when called after the owning Sandbox has
|
|
47
|
-
# been sealed by its first invocation ({docs/behavior.md B-07}[link:../../../docs/behavior.md]).
|
|
48
|
-
def define(name)
|
|
49
|
-
raise ArgumentError, "cannot define after first Sandbox invocation" if @sealed
|
|
50
|
-
|
|
51
|
-
name_str = name.to_s
|
|
52
|
-
unless Namespace::NAME_PATTERN.match?(name_str)
|
|
53
|
-
raise ArgumentError,
|
|
54
|
-
"Namespace name must match #{Namespace::NAME_PATTERN.inspect} (got #{name.inspect})"
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
@namespaces[name_str] ||= Namespace.new(name_str)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Resolve a +target+ path of the form +"Namespace::Member"+ to the
|
|
61
|
-
# bound Host object. +target+ is a two-level path using the +::+
|
|
62
|
-
# separator. Returns the bound Host object. Raises +KeyError+ when the
|
|
63
|
-
# namespace or the member is not bound.
|
|
64
|
-
def lookup(target)
|
|
65
|
-
namespace, member_name, namespace_name = parse_target(target)
|
|
66
|
-
raise KeyError, "no namespace named #{namespace_name.inspect}" if namespace.nil?
|
|
67
|
-
raise KeyError, "no member #{target.inspect} bound on server" unless member_name
|
|
68
|
-
|
|
69
|
-
namespace.fetch(member_name)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Returns +true+ when +target+ (a +"Namespace::Member"+ path) resolves
|
|
73
|
-
# to a bound member, +false+ otherwise.
|
|
74
|
-
def bound?(target)
|
|
75
|
-
namespace, member_name, = parse_target(target)
|
|
76
|
-
!namespace.nil? && !member_name.nil? && !namespace[member_name].nil?
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Returns all declared +Kobako::RPC::Namespace+ instances as an +Array+.
|
|
80
|
-
def namespaces
|
|
81
|
-
@namespaces.values
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Returns the number of declared namespaces as an +Integer+.
|
|
85
|
-
def size
|
|
86
|
-
@namespaces.size
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Returns +true+ when no namespaces have been declared, +false+ otherwise.
|
|
90
|
-
def empty?
|
|
91
|
-
@namespaces.empty?
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Structured Frame 1 description. Called by +Sandbox#eval+ when
|
|
95
|
-
# assembling stdin Frame 1
|
|
96
|
-
# ({docs/behavior.md B-02}[link:../../../docs/behavior.md]). Returns an
|
|
97
|
-
# unencoded preamble array — an +Array+ of two-element +[name, members]+
|
|
98
|
-
# arrays, one per declared namespace.
|
|
99
|
-
def to_preamble
|
|
100
|
-
@namespaces.values.map(&:to_preamble)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# Encode the preamble as msgpack bytes for stdin Frame 1 delivery
|
|
104
|
-
# ({docs/behavior.md B-02}[link:../../../docs/behavior.md]). Uses plain MessagePack (no
|
|
105
|
-
# kobako ext types) because the preamble contains only strings — no
|
|
106
|
-
# Handles or Fault envelopes. Structure:
|
|
107
|
-
# +[["Namespace", ["MemberA", "MemberB"]], ...]+. Returns a binary
|
|
108
|
-
# +String+ of msgpack bytes.
|
|
109
|
-
def encoded_preamble
|
|
110
|
-
MessagePack.pack(to_preamble)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# Mark the Server as sealed. Called by +Sandbox+ on the first
|
|
114
|
-
# invocation. After sealing, #define raises ArgumentError. Idempotent.
|
|
115
|
-
def seal!
|
|
116
|
-
@sealed = true
|
|
117
|
-
self
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Returns +true+ when {#seal!} has been called, +false+ otherwise.
|
|
121
|
-
def sealed?
|
|
122
|
-
@sealed
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Reset the HandleTable for a new invocation boundary. Called by
|
|
126
|
-
# +Sandbox+ before each invocation
|
|
127
|
-
# ({docs/behavior.md B-19}[link:../../../docs/behavior.md]).
|
|
128
|
-
def reset_handles!
|
|
129
|
-
@handle_table.reset!
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Dispatch a single RPC request and return the encoded response bytes
|
|
133
|
-
# ({docs/behavior.md B-12}[link:../../../docs/behavior.md]). +request_bytes+ is a
|
|
134
|
-
# msgpack-encoded Request envelope. Called by the Rust ext from inside
|
|
135
|
-
# +__kobako_dispatch+. Always returns a binary +String+ — never raises.
|
|
136
|
-
# Delegates to +Dispatcher.dispatch+ which reifies any failure as a
|
|
137
|
-
# +Response.error+ envelope so the guest sees the failure as a normal RPC
|
|
138
|
-
# error rather than a wasm trap.
|
|
139
|
-
def dispatch(request_bytes)
|
|
140
|
-
Dispatcher.dispatch(request_bytes, self)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Expose the +HandleTable+ for tests and wire-layer Handle wrapping.
|
|
144
|
-
attr_reader :handle_table
|
|
145
|
-
|
|
146
|
-
private
|
|
147
|
-
|
|
148
|
-
# Split +target+ on the +::+ separator and resolve the namespace half.
|
|
149
|
-
# Returns +[namespace_or_nil, member_str_or_nil, namespace_name_str]+ so
|
|
150
|
-
# each public method ({#lookup} / {#bound?}) only owns its boundary
|
|
151
|
-
# semantics (raise vs predicate).
|
|
152
|
-
def parse_target(target)
|
|
153
|
-
namespace_name, member_name = target.to_s.split("::", 2)
|
|
154
|
-
[@namespaces[namespace_name], member_name, namespace_name]
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
data/lib/kobako/rpc.rb
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Kobako
|
|
4
|
-
# Kobako::RPC — protocol namespace for host↔guest RPC. Houses the value
|
|
5
|
-
# objects that travel on the wire (+Handle+, +Request+, +Response+,
|
|
6
|
-
# +Fault+) and the host-side Server coordinator. See
|
|
7
|
-
# {SPEC.md Refinement → Internal Concepts}[link:../../SPEC.md] for the
|
|
8
|
-
# RPC role split.
|
|
9
|
-
module RPC
|
|
10
|
-
end
|
|
11
|
-
end
|
data/lib/kobako/wasm.rb
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Kobako
|
|
4
|
-
# Host-side wasmtime wrapper, surfaced as Ruby classes by the native ext
|
|
5
|
-
# (see ext/kobako/src/wasm.rs). This module is the foundational binding
|
|
6
|
-
# layer for Sandbox (#14), the run path (#16) and RPC dispatch (#18).
|
|
7
|
-
#
|
|
8
|
-
# The classes themselves (Instance) and the error hierarchy (Error /
|
|
9
|
-
# ModuleNotBuiltError) are defined from Rust at ext load time; this file
|
|
10
|
-
# only adds the pure-Ruby helpers that have no reason to live in Rust.
|
|
11
|
-
module Wasm
|
|
12
|
-
# Absolute path to the gem-bundled `data/kobako.wasm` artifact. Computed
|
|
13
|
-
# from this file's location so it works for both `bundle exec` (running
|
|
14
|
-
# from the repo) and an installed gem (running from the gem dir).
|
|
15
|
-
#
|
|
16
|
-
# Returns a String regardless of whether the file currently exists —
|
|
17
|
-
# call sites that need the file to be present should pass this through
|
|
18
|
-
# +Kobako::Wasm::Instance.from_path+, which raises +ModuleNotBuiltError+
|
|
19
|
-
# with a clear remediation message.
|
|
20
|
-
def self.default_path
|
|
21
|
-
dir = __dir__ or raise Error, "Kobako::Wasm.default_path requires __dir__"
|
|
22
|
-
File.expand_path("../../data/kobako.wasm", dir)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
data/sig/kobako/invocation.rbs
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
class Invocation < Data
|
|
3
|
-
NAME_PATTERN: Regexp
|
|
4
|
-
|
|
5
|
-
attr_reader entrypoint: Symbol
|
|
6
|
-
attr_reader args: Array[untyped]
|
|
7
|
-
attr_reader kwargs: Hash[Symbol, untyped]
|
|
8
|
-
|
|
9
|
-
def self.new: (entrypoint: Symbol | String, ?args: Array[untyped], ?kwargs: Hash[untyped, untyped]) -> Invocation
|
|
10
|
-
|
|
11
|
-
def initialize: (entrypoint: Symbol | String, ?args: Array[untyped], ?kwargs: Hash[untyped, untyped]) -> void
|
|
12
|
-
|
|
13
|
-
def encode: () -> String
|
|
14
|
-
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
def normalize_entrypoint: (Symbol | String target) -> Symbol
|
|
18
|
-
|
|
19
|
-
def validate_args!: (Array[untyped] args) -> Array[untyped]
|
|
20
|
-
|
|
21
|
-
def validate_kwargs!: (Hash[untyped, untyped] kwargs) -> Hash[Symbol, untyped]
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module RPC
|
|
3
|
-
module Dispatcher
|
|
4
|
-
class UndefinedTargetError < StandardError
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
class DisconnectedTargetError < StandardError
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def self?.dispatch: (String request_bytes, Server server) -> String
|
|
11
|
-
|
|
12
|
-
def self?.encode_caught_error: (StandardError error) -> String
|
|
13
|
-
|
|
14
|
-
def self?.invoke: (untyped target, String method, Array[untyped] args, Hash[Symbol, untyped] kwargs) -> untyped
|
|
15
|
-
|
|
16
|
-
def self?.resolve_arg: (untyped value, HandleTable handle_table) -> untyped
|
|
17
|
-
|
|
18
|
-
def self?.resolve_target: (String | Kobako::RPC::Handle target, Server server, HandleTable handle_table) -> untyped
|
|
19
|
-
|
|
20
|
-
def self?.resolve_path: (String path, Server server) -> untyped
|
|
21
|
-
|
|
22
|
-
def self?.resolve_handle: (Kobako::RPC::Handle handle, HandleTable handle_table) -> untyped
|
|
23
|
-
|
|
24
|
-
def self?.require_live_object!: (Integer id, HandleTable handle_table) -> untyped
|
|
25
|
-
|
|
26
|
-
def self?.encode_ok: (untyped value, Server server) -> String
|
|
27
|
-
|
|
28
|
-
def self?.wrap_as_handle: (untyped value, Server server) -> Kobako::RPC::Handle
|
|
29
|
-
|
|
30
|
-
def self?.encode_error: (String type, String message) -> String
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
data/sig/kobako/rpc/envelope.rbs
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module RPC
|
|
3
|
-
STATUS_OK: Integer
|
|
4
|
-
STATUS_ERROR: Integer
|
|
5
|
-
|
|
6
|
-
class Request
|
|
7
|
-
attr_reader target: String | Kobako::RPC::Handle
|
|
8
|
-
attr_reader method_name: String
|
|
9
|
-
attr_reader args: Array[untyped]
|
|
10
|
-
attr_reader kwargs: Hash[Symbol, untyped]
|
|
11
|
-
|
|
12
|
-
def initialize: (target: String | Kobako::RPC::Handle, method: String, ?args: Array[untyped], ?kwargs: Hash[Symbol, untyped]) -> void
|
|
13
|
-
|
|
14
|
-
def with: (?target: String | Kobako::RPC::Handle, ?method: String, ?args: Array[untyped], ?kwargs: Hash[Symbol, untyped]) -> Request
|
|
15
|
-
|
|
16
|
-
def ==: (untyped other) -> bool
|
|
17
|
-
|
|
18
|
-
def hash: () -> Integer
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def self.encode_request: (Request request) -> String
|
|
22
|
-
|
|
23
|
-
def self.decode_request: (String bytes) -> Request
|
|
24
|
-
|
|
25
|
-
class Response
|
|
26
|
-
attr_reader status: Integer
|
|
27
|
-
attr_reader payload: untyped
|
|
28
|
-
|
|
29
|
-
def self.ok: (untyped value) -> Response
|
|
30
|
-
|
|
31
|
-
def self.error: (Kobako::RPC::Fault fault) -> Response
|
|
32
|
-
|
|
33
|
-
def initialize: (Integer status, untyped payload) -> void
|
|
34
|
-
| (status: Integer, payload: untyped) -> void
|
|
35
|
-
|
|
36
|
-
def with: (?status: Integer, ?payload: untyped) -> Response
|
|
37
|
-
|
|
38
|
-
def ok?: () -> bool
|
|
39
|
-
|
|
40
|
-
def error?: () -> bool
|
|
41
|
-
|
|
42
|
-
def ==: (untyped other) -> bool
|
|
43
|
-
|
|
44
|
-
def hash: () -> Integer
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def self.encode_response: (Response response) -> String
|
|
48
|
-
|
|
49
|
-
def self.decode_response: (String bytes) -> Response
|
|
50
|
-
end
|
|
51
|
-
end
|
data/sig/kobako/rpc/fault.rbs
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module RPC
|
|
3
|
-
class Fault
|
|
4
|
-
VALID_TYPES: Array[String]
|
|
5
|
-
|
|
6
|
-
attr_reader type: String
|
|
7
|
-
attr_reader message: String
|
|
8
|
-
attr_reader details: untyped
|
|
9
|
-
|
|
10
|
-
def initialize: (String type, String message, untyped details) -> void
|
|
11
|
-
| (type: String, message: String, ?details: untyped) -> void
|
|
12
|
-
|
|
13
|
-
def with: (?type: String, ?message: String, ?details: untyped) -> Fault
|
|
14
|
-
|
|
15
|
-
def ==: (untyped other) -> bool
|
|
16
|
-
|
|
17
|
-
def hash: () -> Integer
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
data/sig/kobako/rpc/handle.rbs
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module RPC
|
|
3
|
-
class Handle
|
|
4
|
-
MIN_ID: Integer
|
|
5
|
-
MAX_ID: Integer
|
|
6
|
-
|
|
7
|
-
attr_reader id: Integer
|
|
8
|
-
|
|
9
|
-
def initialize: (Integer id) -> void
|
|
10
|
-
| (id: Integer) -> void
|
|
11
|
-
|
|
12
|
-
def with: (?id: Integer) -> Handle
|
|
13
|
-
|
|
14
|
-
def ==: (untyped other) -> bool
|
|
15
|
-
|
|
16
|
-
def hash: () -> Integer
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module RPC
|
|
3
|
-
class Namespace
|
|
4
|
-
NAME_PATTERN: Regexp
|
|
5
|
-
|
|
6
|
-
attr_reader name: String
|
|
7
|
-
attr_reader members: Hash[String, untyped]
|
|
8
|
-
|
|
9
|
-
def initialize: (String name) -> void
|
|
10
|
-
|
|
11
|
-
def bind: (Symbol | String member, untyped object) -> self
|
|
12
|
-
|
|
13
|
-
def []: (Symbol | String member) -> untyped
|
|
14
|
-
|
|
15
|
-
def fetch: (Symbol | String member) -> untyped
|
|
16
|
-
|
|
17
|
-
def to_preamble: () -> [String, Array[String]]
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def validate_member_name!: (Symbol | String member) -> String
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
data/sig/kobako/rpc/server.rbs
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module RPC
|
|
3
|
-
class Server
|
|
4
|
-
attr_reader handle_table: HandleTable
|
|
5
|
-
|
|
6
|
-
def initialize: (?handle_table: HandleTable) -> void
|
|
7
|
-
|
|
8
|
-
def define: (Symbol | String name) -> Kobako::RPC::Namespace
|
|
9
|
-
|
|
10
|
-
def lookup: (String target) -> untyped
|
|
11
|
-
|
|
12
|
-
def bound?: (String target) -> bool
|
|
13
|
-
|
|
14
|
-
def namespaces: () -> Array[Kobako::RPC::Namespace]
|
|
15
|
-
|
|
16
|
-
def size: () -> Integer
|
|
17
|
-
|
|
18
|
-
def empty?: () -> bool
|
|
19
|
-
|
|
20
|
-
def to_preamble: () -> Array[[String, Array[String]]]
|
|
21
|
-
|
|
22
|
-
def encoded_preamble: () -> String
|
|
23
|
-
|
|
24
|
-
def seal!: () -> self
|
|
25
|
-
|
|
26
|
-
def sealed?: () -> bool
|
|
27
|
-
|
|
28
|
-
def reset_handles!: () -> HandleTable
|
|
29
|
-
|
|
30
|
-
def dispatch: (String request_bytes) -> String
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
def parse_target: (String target) -> [Kobako::RPC::Namespace?, String?, String?]
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
data/sig/kobako/wasm.rbs
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
module Kobako
|
|
2
|
-
module Wasm
|
|
3
|
-
class Error < StandardError
|
|
4
|
-
end
|
|
5
|
-
|
|
6
|
-
class ModuleNotBuiltError < Error
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def self.default_path: () -> String
|
|
10
|
-
|
|
11
|
-
class TimeoutError < Error
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
class MemoryLimitError < Error
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
class Instance
|
|
18
|
-
def self.from_path: (
|
|
19
|
-
String path,
|
|
20
|
-
(Float | Integer)? timeout_seconds,
|
|
21
|
-
Integer? memory_limit,
|
|
22
|
-
Integer? stdout_limit_bytes,
|
|
23
|
-
Integer? stderr_limit_bytes
|
|
24
|
-
) -> Instance
|
|
25
|
-
|
|
26
|
-
def server=: (Kobako::RPC::Server server) -> void
|
|
27
|
-
|
|
28
|
-
def eval: (String preamble, String source, String snippets) -> void
|
|
29
|
-
|
|
30
|
-
def run: (String preamble, String snippets, String envelope) -> void
|
|
31
|
-
|
|
32
|
-
def stdout: () -> [String, bool]
|
|
33
|
-
|
|
34
|
-
def stderr: () -> [String, bool]
|
|
35
|
-
|
|
36
|
-
def outcome!: () -> String
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|