kobako 0.6.1 → 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/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +24 -0
- data/Cargo.lock +1 -1
- data/README.md +81 -51
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +1 -1
- data/ext/kobako/src/runtime/trap.rs +48 -4
- data/lib/kobako/version.rb +1 -1
- data/rust-toolchain.toml +10 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac6c01cd421449a5b87d4a21777b257a204018747d468481ff309382d08f8a89
|
|
4
|
+
data.tar.gz: 2c172f0ffb8d0cb600bc8837102e584ca4492f1f59ce8d77989169d078976bb0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88fb3c50f8e631dbe6139ef9f8ab5e5ad50f4ee08a2aec0b8cdbba5e70c9670fb78c24e80a59916daa23ce6e9663f73ba82ad26c78e69588f59e91c72a4b1cf9
|
|
7
|
+
data.tar.gz: 1eacade67c1ddc6de4fa8a75e5975272157ea2e08c55dcb5c790eb3528941b3b96091f3695b60389d5c93d75599b04ff99a1af530530971f2765a94b101c3f57
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{".":"0.
|
|
1
|
+
{".":"0.7.0"}
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0](https://github.com/elct9620/kobako/compare/v0.6.2...v0.7.0) (2026-06-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **examples/async-io:** demo single-thread I/O overlap across Sandboxes ([858a0f7](https://github.com/elct9620/kobako/commit/858a0f70bb0730e5e3ad3f49a5caadf948f6ed7d))
|
|
9
|
+
* **guest:** reject construction of Handle proxies (B-39) ([bda5e2b](https://github.com/elct9620/kobako/commit/bda5e2b5e48fe25adeefac19c47f1b93585091bf))
|
|
10
|
+
* **guest:** reject construction of Member proxies (B-38) ([885c281](https://github.com/elct9620/kobako/commit/885c2812da8627e4ec7c185d9e04e4056c94a7fd))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **ext:** surface the buried root cause on non-cap traps ([dbd1ce5](https://github.com/elct9620/kobako/commit/dbd1ce51f25ab8c83d39daee64278192d763d5ef))
|
|
16
|
+
* **guest:** bound the encoder walk so cycles and deep nesting fail cleanly ([90880ff](https://github.com/elct9620/kobako/commit/90880ffe6f0ee911b3c7c076ef6c86b9e08c62e1))
|
|
17
|
+
* **guest:** stop an embedded NUL in a returned value from hard-trapping ([14fbb97](https://github.com/elct9620/kobako/commit/14fbb97ecb47b4263585602754e92237bb951d46))
|
|
18
|
+
* **guest:** stop named-capture regexes from hard-trapping the sandbox ([a279ea1](https://github.com/elct9620/kobako/commit/a279ea1e2f196580b396342851e4e75ff9ea5cfa))
|
|
19
|
+
|
|
20
|
+
## [0.6.2](https://github.com/elct9620/kobako/compare/v0.6.1...v0.6.2) (2026-05-31)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* **guest:** enable mruby-sprintf for printf and String#% ([1179227](https://github.com/elct9620/kobako/commit/1179227b85b861bccc82d2a258481769a13ed4d5))
|
|
26
|
+
|
|
3
27
|
## [0.6.1](https://github.com/elct9620/kobako/compare/v0.6.0...v0.6.1) (2026-05-28)
|
|
4
28
|
|
|
5
29
|
|
data/Cargo.lock
CHANGED
data/README.md
CHANGED
|
@@ -53,9 +53,21 @@ result # => 3
|
|
|
53
53
|
|
|
54
54
|
The script executes inside the Wasm guest. It cannot read your filesystem, open sockets, or touch your `ENV`.
|
|
55
55
|
|
|
56
|
+
## Glossary
|
|
57
|
+
|
|
58
|
+
| Term | Meaning |
|
|
59
|
+
|------|---------|
|
|
60
|
+
| Sandbox | The runtime unit (`Kobako::Sandbox`) that runs guest code and returns a result or raises a typed error. |
|
|
61
|
+
| Service | A host Ruby object injected under `<Namespace>::<Member>` — the guest's only path to host resources. |
|
|
62
|
+
| Namespace / Member | A guest-visible Ruby module, and a named binding (a module constant) within it. |
|
|
63
|
+
| Invocation | One `#eval` or `#run`; capability state resets between invocations. |
|
|
64
|
+
| Snippet | Named mruby code (source or bytecode) replayed into a fresh state before every invocation. |
|
|
65
|
+
| Handle | An opaque token the guest holds for a host object the wire cannot transmit directly. |
|
|
66
|
+
| Block | A guest mruby block passed to a Service; each `yield` is a synchronous round-trip into the guest. |
|
|
67
|
+
|
|
56
68
|
## Usage
|
|
57
69
|
|
|
58
|
-
###
|
|
70
|
+
### Services
|
|
59
71
|
|
|
60
72
|
Declare a Namespace, then `bind` any Ruby object as a Member; the guest reaches it as a `<Namespace>::<Member>` proxy and invokes its public methods through the Transport wire. See [`docs/behavior.md`](docs/behavior.md) B-07..B-12.
|
|
61
73
|
|
|
@@ -77,42 +89,9 @@ sandbox.eval(<<~RUBY)
|
|
|
77
89
|
RUBY
|
|
78
90
|
```
|
|
79
91
|
|
|
80
|
-
Names must match `/\A[A-Z]\w*\z/`. Symbol kwargs travel transparently to the host method's keyword arguments. The registry seals at the first invocation; later `#define` raises `ArgumentError`.
|
|
81
|
-
|
|
82
|
-
### Yielding to guest blocks
|
|
83
|
-
|
|
84
|
-
A Service method can accept a guest-supplied block via `&blk` and `yield` into it. The block body runs inside the Wasm guest; `break` / `next` / exceptions follow normal Ruby semantics, scoped to the single dispatch. See [`docs/behavior.md`](docs/behavior.md) B-23..B-30.
|
|
85
|
-
|
|
86
|
-
```ruby
|
|
87
|
-
sandbox.define(:Seq).bind(:Map, ->(items, &blk) { items.map(&blk) })
|
|
88
|
-
|
|
89
|
-
sandbox.eval('Seq::Map.call([1, 2, 3]) { |x| x * 2 }')
|
|
90
|
-
# => [2, 4, 6]
|
|
91
|
-
```
|
|
92
|
+
Names must match `/\A[A-Z]\w*\z/`. Symbol kwargs travel transparently to the host method's keyword arguments. The registry seals at the first invocation (see [Invocation Lifecycle](#invocation-lifecycle)); later `#define` raises `ArgumentError`.
|
|
92
93
|
|
|
93
|
-
###
|
|
94
|
-
|
|
95
|
-
Each invocation enforces a wall-clock `timeout` and a per-invocation linear-memory `memory_limit`; exhaustion raises a `TrapError` subclass. Pass `nil` to `timeout` / `memory_limit` to disable that cap. Read [`Sandbox#usage`](lib/kobako/sandbox.rb) after the call — populated on every outcome including traps — for actual consumption ([`docs/behavior.md`](docs/behavior.md) B-35).
|
|
96
|
-
|
|
97
|
-
```ruby
|
|
98
|
-
sandbox = Kobako::Sandbox.new(
|
|
99
|
-
timeout: 5.0, # seconds, default 60.0
|
|
100
|
-
memory_limit: 10 * 1024 * 1024, # bytes, default 1 MiB
|
|
101
|
-
stdout_limit: 64 * 1024, # bytes, default 1 MiB
|
|
102
|
-
stderr_limit: 64 * 1024
|
|
103
|
-
)
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
| Cap | Raises | Default |
|
|
107
|
-
|----------------|----------------------------|---------|
|
|
108
|
-
| `timeout` | `Kobako::TimeoutError` | 60.0 s |
|
|
109
|
-
| `memory_limit` | `Kobako::MemoryLimitError` | 1 MiB |
|
|
110
|
-
| `stdout_limit` | output clipped (no raise) | 1 MiB |
|
|
111
|
-
| `stderr_limit` | output clipped (no raise) | 1 MiB |
|
|
112
|
-
|
|
113
|
-
`memory_limit` covers the per-invocation `memory.grow` delta from the entry baseline, so a Sandbox reused across invocations does not silently accumulate against a global budget.
|
|
114
|
-
|
|
115
|
-
### Capturing stdout / stderr
|
|
94
|
+
### Output Capture
|
|
116
95
|
|
|
117
96
|
Guest writes through `puts` / `print` / `p` / `$stdout` / `$stderr` are buffered per-channel and exposed independently of the return value ([`docs/behavior.md`](docs/behavior.md) B-04). Buffers clear at the start of each invocation; overflow is clipped at the cap and flagged by `#stdout_truncated?` / `#stderr_truncated?`.
|
|
118
97
|
|
|
@@ -128,7 +107,7 @@ sandbox.stdout # => "hello\n"
|
|
|
128
107
|
sandbox.stderr # => "be careful\n"
|
|
129
108
|
```
|
|
130
109
|
|
|
131
|
-
### Error
|
|
110
|
+
### Error Handling
|
|
132
111
|
|
|
133
112
|
Every invocation either returns a value or raises exactly one of three classes, so you can route faults without inspecting messages. The full taxonomy lives in [`lib/kobako/errors.rb`](lib/kobako/errors.rb).
|
|
134
113
|
|
|
@@ -153,25 +132,29 @@ end
|
|
|
153
132
|
|
|
154
133
|
`SandboxError` and `ServiceError` carry structured `origin` / `klass` / `backtrace_lines` / `details` fields when the guest produced a panic envelope.
|
|
155
134
|
|
|
156
|
-
###
|
|
135
|
+
### Resource Limits
|
|
157
136
|
|
|
158
|
-
|
|
137
|
+
Each invocation enforces a wall-clock `timeout` and a per-invocation linear-memory `memory_limit`; exhaustion raises a `TrapError` subclass. Pass `nil` to `timeout` / `memory_limit` to disable that cap. Read [`Sandbox#usage`](lib/kobako/sandbox.rb) after the call — populated on every outcome including traps — for actual consumption ([`docs/behavior.md`](docs/behavior.md) B-35).
|
|
159
138
|
|
|
160
139
|
```ruby
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
sandbox.eval('Factory::Make.call("Bob").greet') # => "hi, Bob" (Handle round-trip inside guest)
|
|
169
|
-
sandbox.eval('Factory::Make.call("Bob")') # => #<Greeter @name="Bob"> (B-37 restoration)
|
|
140
|
+
sandbox = Kobako::Sandbox.new(
|
|
141
|
+
timeout: 5.0, # seconds, default 60.0
|
|
142
|
+
memory_limit: 10 * 1024 * 1024, # bytes, default 1 MiB
|
|
143
|
+
stdout_limit: 64 * 1024, # bytes, default 1 MiB
|
|
144
|
+
stderr_limit: 64 * 1024
|
|
145
|
+
)
|
|
170
146
|
```
|
|
171
147
|
|
|
172
|
-
|
|
148
|
+
| Cap | Raises | Default |
|
|
149
|
+
|----------------|----------------------------|---------|
|
|
150
|
+
| `timeout` | `Kobako::TimeoutError` | 60.0 s |
|
|
151
|
+
| `memory_limit` | `Kobako::MemoryLimitError` | 1 MiB |
|
|
152
|
+
| `stdout_limit` | output clipped (no raise) | 1 MiB |
|
|
153
|
+
| `stderr_limit` | output clipped (no raise) | 1 MiB |
|
|
173
154
|
|
|
174
|
-
|
|
155
|
+
`memory_limit` covers the per-invocation `memory.grow` delta from the entry baseline, so a Sandbox reused across invocations does not silently accumulate against a global budget.
|
|
156
|
+
|
|
157
|
+
### Invocation Lifecycle
|
|
175
158
|
|
|
176
159
|
One Sandbox serves many invocations. Service bindings and preloaded snippets persist across calls; capability state (Handles, stdout, stderr, memory delta) resets between them.
|
|
177
160
|
|
|
@@ -216,7 +199,54 @@ One Sandbox serves many invocations. Service bindings and preloaded snippets per
|
|
|
216
199
|
|
|
217
200
|
For workloads that must be isolated from each other (one Sandbox per tenant, per student submission, per agent session), construct a fresh `Kobako::Sandbox` per scope — wasmtime's Engine and the compiled Module are cached at process scope, so additional Sandboxes amortize cold-start cost automatically.
|
|
218
201
|
|
|
219
|
-
###
|
|
202
|
+
### Service Blocks
|
|
203
|
+
|
|
204
|
+
A Service method can accept a guest-supplied block via `&blk` and `yield` into it. The block body runs inside the Wasm guest; `break` / `next` / exceptions follow normal Ruby semantics, scoped to the single dispatch. See [`docs/behavior.md`](docs/behavior.md) B-23..B-30.
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
sandbox.define(:Seq).bind(:Map, ->(items, &blk) { items.map(&blk) })
|
|
208
|
+
|
|
209
|
+
sandbox.eval('Seq::Map.call([1, 2, 3]) { |x| x * 2 }')
|
|
210
|
+
# => [2, 4, 6]
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Handle Management
|
|
214
|
+
|
|
215
|
+
A non-wire-representable host object — returned from a Service (B-14), passed to `#run` (B-34), or handed back from the guest (B-37) — crosses the boundary as an opaque `Kobako::Handle` proxy and is restored to the original object before host code sees it; any other unrepresentable value raises `Kobako::SandboxError`. Handles are scoped to a single invocation ([`docs/behavior.md`](docs/behavior.md) B-13..B-21, B-34, B-37).
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
class Greeter
|
|
219
|
+
def initialize(name) = @name = name
|
|
220
|
+
def greet = "hi, #{@name}"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
sandbox.define(:Factory).bind(:Make, ->(name) { Greeter.new(name) })
|
|
224
|
+
|
|
225
|
+
sandbox.eval('Factory::Make.call("Bob").greet') # => "hi, Bob" (Handle round-trip inside guest)
|
|
226
|
+
sandbox.eval('Factory::Make.call("Bob")') # => #<Greeter @name="Bob"> (B-37 restoration)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
A `break` value from a guest block is the one exception: it unwinds back to the guest Member call rather than to host code, so a Handle in it stays a Handle — restoring would just re-wrap the same object into a new id on the return trip.
|
|
230
|
+
|
|
231
|
+
Each dispatch that hands back a non-wire-representable object allocates a *new* Handle — kobako never deduplicates by object identity (B-15, B-17). This is most visible with fluent / builder APIs. An `ActiveRecord::Relation` chain `spawn`s a fresh relation at each step, so every hop is an independent dispatch that binds its own Handle:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
guest chain host (Catalog::Handles, one invocation)
|
|
235
|
+
─────────── ─────────────────────────────────────────
|
|
236
|
+
User.where(active: true) ─call──▶ Relation #1 (fresh clone) bound ▶ Handle 1
|
|
237
|
+
◀─Handle 1
|
|
238
|
+
.order(:created_at) ─call──▶ Relation #2 (fresh clone) bound ▶ Handle 2
|
|
239
|
+
◀─Handle 2
|
|
240
|
+
.limit(10) ─call──▶ Relation #3 (fresh clone) bound ▶ Handle 3
|
|
241
|
+
◀─Handle 3
|
|
242
|
+
|
|
243
|
+
3 hops ─▶ 3 dispatches ─▶ 3 distinct relations ─▶ 3 Handles
|
|
244
|
+
all stay live until the invocation ends, then reset together
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
This is deliberate, not a leak. Handle IDs run to 2³¹ − 1 per invocation and reset between invocations, so even deep chains stay far inside the range. Two consequences are worth keeping in mind: the same host object handed back twice yields two *different* Handles — the guest cannot tell they alias — and every intermediate Handle stays live until the invocation ends, since there is no per-Handle release (B-19).
|
|
248
|
+
|
|
249
|
+
### Snippets & Entrypoints
|
|
220
250
|
|
|
221
251
|
`Sandbox#preload` registers named mruby snippets that replay against the fresh `mrb_state` before every invocation; `Sandbox#run(:Target, *args, **kwargs)` dispatches into a top-level `Object` constant defined by those snippets ([`docs/behavior.md`](docs/behavior.md) B-31..B-33).
|
|
222
252
|
|
data/data/kobako.wasm
CHANGED
|
Binary file
|
data/ext/kobako/Cargo.toml
CHANGED
|
@@ -76,8 +76,9 @@ fn classify_trap(err: &wasmtime::Error) -> TrapClass {
|
|
|
76
76
|
/// `"linear memory growth exceeded memory_limit: ..."`). The wasmtime
|
|
77
77
|
/// outer wrapper at `format!("{}", err)` would otherwise surface only
|
|
78
78
|
/// the `"error while executing at wasm backtrace: ..."` framing, which
|
|
79
|
-
/// is operator noise on a cap trap. For `TrapClass::Other` the
|
|
80
|
-
///
|
|
79
|
+
/// is operator noise on a cap trap. For `TrapClass::Other` the framing
|
|
80
|
+
/// is kept but the chain's root cause is appended (see
|
|
81
|
+
/// `other_trap_message`) so the real trap reason survives.
|
|
81
82
|
pub(super) fn call_err(ruby: &Ruby, err: wasmtime::Error) -> MagnusError {
|
|
82
83
|
match classify_trap(&err) {
|
|
83
84
|
TrapClass::Timeout => {
|
|
@@ -94,7 +95,24 @@ pub(super) fn call_err(ruby: &Ruby, err: wasmtime::Error) -> MagnusError {
|
|
|
94
95
|
.unwrap_or_else(|| format!("{}", err));
|
|
95
96
|
memory_limit_err(ruby, msg)
|
|
96
97
|
}
|
|
97
|
-
TrapClass::Other => trap_err(ruby,
|
|
98
|
+
TrapClass::Other => trap_err(ruby, other_trap_message(&err)),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Compose the message for a non-cap trap. wasmtime's `Display` surfaces only
|
|
103
|
+
/// the `"error while executing at wasm backtrace: ..."` framing; the actual
|
|
104
|
+
/// trap reason (e.g. `"wasm trap: indirect call type mismatch"`) is the
|
|
105
|
+
/// chain's root cause and would otherwise be dropped, making real guest
|
|
106
|
+
/// faults undiagnosable. Append the root cause unless the framing already
|
|
107
|
+
/// carries it. Pure so it can be exercised from `cargo test` without the
|
|
108
|
+
/// magnus surface.
|
|
109
|
+
fn other_trap_message(err: &wasmtime::Error) -> String {
|
|
110
|
+
let display = format!("{}", err);
|
|
111
|
+
let root = err.root_cause().to_string();
|
|
112
|
+
if display.contains(&root) {
|
|
113
|
+
display
|
|
114
|
+
} else {
|
|
115
|
+
format!("{display}\n\n{root}")
|
|
98
116
|
}
|
|
99
117
|
}
|
|
100
118
|
|
|
@@ -111,7 +129,7 @@ pub(super) fn instantiate_err(ruby: &Ruby, err: wasmtime::Error) -> MagnusError
|
|
|
111
129
|
|
|
112
130
|
#[cfg(test)]
|
|
113
131
|
mod tests {
|
|
114
|
-
use super::{classify_trap, TrapClass};
|
|
132
|
+
use super::{classify_trap, other_trap_message, TrapClass};
|
|
115
133
|
use crate::runtime::invocation::{MemoryLimitTrap, TimeoutTrap};
|
|
116
134
|
|
|
117
135
|
#[test]
|
|
@@ -131,4 +149,30 @@ mod tests {
|
|
|
131
149
|
let err = wasmtime::Error::msg("some other wasmtime fault");
|
|
132
150
|
assert_eq!(classify_trap(&err), TrapClass::Other);
|
|
133
151
|
}
|
|
152
|
+
|
|
153
|
+
// A guest hard trap reaches the host as a wasmtime error whose Display is
|
|
154
|
+
// only the backtrace framing, with the trap reason buried as the chain's
|
|
155
|
+
// root cause. The named-capture regex bug surfaced as exactly this shape.
|
|
156
|
+
#[test]
|
|
157
|
+
fn other_trap_message_surfaces_buried_trap_reason() {
|
|
158
|
+
let err = wasmtime::Error::msg("wasm trap: indirect call type mismatch")
|
|
159
|
+
.context("error while executing at wasm backtrace:\n 0: 0x1 - <unknown>");
|
|
160
|
+
let msg = other_trap_message(&err);
|
|
161
|
+
assert!(
|
|
162
|
+
msg.contains("indirect call type mismatch"),
|
|
163
|
+
"a non-cap trap surfaced through Kobako::TrapError must carry the root trap reason, not only the backtrace framing; got: {msg}"
|
|
164
|
+
);
|
|
165
|
+
assert!(
|
|
166
|
+
msg.contains("error while executing"),
|
|
167
|
+
"a non-cap trap surfaced through Kobako::TrapError must keep the wasm backtrace framing; got: {msg}"
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// A flat error (no cause chain) is its own root_cause; appending it would
|
|
172
|
+
// duplicate the whole message.
|
|
173
|
+
#[test]
|
|
174
|
+
fn other_trap_message_does_not_duplicate_a_flat_error() {
|
|
175
|
+
let err = wasmtime::Error::msg("plain fault");
|
|
176
|
+
assert_eq!(other_trap_message(&err), "plain fault");
|
|
177
|
+
}
|
|
134
178
|
}
|
data/lib/kobako/version.rb
CHANGED
data/rust-toolchain.toml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Pin the Rust toolchain so local builds and CI stay byte-identical.
|
|
2
|
+
# The wasm32-wasip1 crt1-command.o references __wasi_init_tp from 1.96 onward;
|
|
3
|
+
# vendored wasi-sdk 33's libc.a supplies that symbol, so the two move together.
|
|
4
|
+
# Bump this in lockstep with WASI_SDK_VERSION in tasks/support/kobako_vendor.rb.
|
|
5
|
+
# This file is the single source of the channel; the CI workflows read it.
|
|
6
|
+
[toolchain]
|
|
7
|
+
channel = "1.96.0"
|
|
8
|
+
components = ["clippy", "rustfmt"]
|
|
9
|
+
targets = ["wasm32-wasip1"]
|
|
10
|
+
profile = "minimal"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kobako
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aotokitsuruya
|
|
@@ -103,6 +103,7 @@ files:
|
|
|
103
103
|
- lib/kobako/usage.rb
|
|
104
104
|
- lib/kobako/version.rb
|
|
105
105
|
- release-please-config.json
|
|
106
|
+
- rust-toolchain.toml
|
|
106
107
|
- sig/kobako.rbs
|
|
107
108
|
- sig/kobako/capture.rbs
|
|
108
109
|
- sig/kobako/catalog.rbs
|