gienah 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.
data/lib/gienah.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open3"
5
+ require "rbconfig"
6
+ require "pathname"
7
+
8
+ require_relative "gienah/version"
9
+
10
+ module Gienah
11
+ Value = if defined?(Data)
12
+ Module.new do
13
+ module_function
14
+ def define(*members) = Data.define(*members)
15
+ end
16
+ else
17
+ Module.new do
18
+ module_function
19
+ def define(*members)
20
+ Struct.new(*members) do
21
+ define_method(:initialize) do |*values, **keywords|
22
+ values = members.map { |member| keywords.fetch(member) } if values.empty? && !keywords.empty?
23
+ super(*values)
24
+ freeze
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require_relative "gienah/protocol"
33
+ require_relative "gienah/future"
34
+ require_relative "gienah/manifest"
35
+ require_relative "gienah/sandbox"
36
+ require_relative "gienah/transport"
37
+ require_relative "gienah/host"
38
+ require_relative "gienah/plugin"
39
+ require_relative "gienah/testing/fake_plugin"
40
+
41
+ module Gienah
42
+ class Error < StandardError; end
43
+ class CapabilityDenied < Error; end
44
+ class Timeout < Error; end
45
+ class ProtocolError < Error; end
46
+ class LifecycleError < Error; end
47
+ end
data/sig/gienah.rbs ADDED
@@ -0,0 +1,117 @@
1
+ module Gienah
2
+ VERSION: String
3
+
4
+ type json = nil | bool | Integer | Float | String | Array[json] | Hash[String, json]
5
+
6
+ class Error < StandardError
7
+ end
8
+
9
+ class CapabilityDenied < Error
10
+ end
11
+
12
+ class Timeout < Error
13
+ end
14
+
15
+ class ProtocolError < Error
16
+ end
17
+
18
+ class LifecycleError < Error
19
+ end
20
+
21
+ class Manifest
22
+ attr_reader id: String
23
+ attr_reader name: String
24
+ attr_reader version: String
25
+ attr_reader api_version: Integer
26
+ attr_reader entry: String
27
+ attr_reader activation: Array[String]
28
+ attr_reader capabilities: Array[String]
29
+ attr_reader contributes: Hash[String, json]
30
+ attr_reader limits: Hash[String, Numeric]
31
+ attr_reader root: String
32
+ def self.load: (String path) -> Manifest
33
+ def self.from_hash: (Hash[String, untyped] value, ?root: String) -> Manifest
34
+ end
35
+
36
+ module Protocol
37
+ MAX_MESSAGE: Integer
38
+ MAX_HEADER: Integer
39
+ def self.frame: (Hash[String, untyped] message, ?max_size: Integer) -> String
40
+ def self.read: (IO io, ?max_size: Integer) -> Hash[String, untyped]?
41
+ def self.validate_message: (Hash[String, untyped] message) -> Hash[String, untyped]
42
+ def self.request: (Integer id, String method, ?json params) -> Hash[String, untyped]
43
+ def self.notification: (String method, ?json params) -> Hash[String, untyped]
44
+ def self.response: (Integer id, ?result: json, ?error: Hash[String, json]) -> Hash[String, untyped]
45
+ end
46
+
47
+ class Future
48
+ attr_reader id: Integer
49
+ def initialize: (Integer id, ?on_cancel: ^(Integer) -> untyped) -> void
50
+ def fulfill: (?untyped value, ?error: Exception?) -> Future
51
+ def await: (?timeout: Numeric?) -> untyped
52
+ def then: () { (untyped, Exception?) -> untyped } -> Future
53
+ def cancel: () -> bool
54
+ def done?: () -> bool
55
+ end
56
+
57
+ module Sandbox
58
+ def self.dangerous?: (Array[String] capabilities) -> bool
59
+ def self.policy_for: (Array[String] capabilities, root: String) -> untyped
60
+ def self.available?: () -> bool
61
+ def self.ensure_safe!: (Array[String] capabilities) -> void
62
+ end
63
+
64
+ class Transport
65
+ attr_reader pid: Integer
66
+ def self.open: (Array[String] command, ?cwd: String?, ?env: Hash[String, String], ?policy: untyped) { (Hash[String, untyped]?, Exception?) -> untyped } -> Transport
67
+ def write: (Hash[String, untyped] message, ?max_size: Integer) -> nil
68
+ def alive?: () -> bool
69
+ def close: (?grace: Numeric) -> bool
70
+ end
71
+
72
+ class Host
73
+ attr_reader api_version: Integer
74
+ def initialize: (api_version: Integer, ?dispatch: ^(Instance, String, json) -> untyped, ?sandbox: bool, ?restart: Hash[Symbol, untyped], ?limits: Hash[Symbol, Numeric], ?transport_factory: ^(Manifest) { (Manifest) -> Transport }) -> void
75
+ def expose: (String method, ?capability: String?) { (Instance, json) -> untyped } -> Host
76
+ def discover: (Array[String] directories) -> Array[Manifest]
77
+ def add: (Manifest manifest) -> Manifest
78
+ def activate: (String id, reason: String) -> Instance?
79
+ def deactivate: (String id) -> untyped
80
+ def instances: () -> Array[Instance]
81
+ def disabled?: (String id) -> bool
82
+ def on_contribution: () { (String, Hash[String, json]) -> untyped } -> Host
83
+ def on_error: () { (Exception, Instance?) -> untyped } -> Host
84
+ def shutdown: () -> untyped
85
+ end
86
+
87
+ class Instance
88
+ attr_reader id: String
89
+ attr_reader manifest: Manifest
90
+ attr_reader state: Symbol
91
+ def start: () -> Instance
92
+ def call: (String method, ?json params, ?timeout: Numeric?) -> Future
93
+ def notify: (String method, ?json params) -> nil
94
+ def granted?: (String capability) -> bool
95
+ def kill: () -> nil
96
+ def shutdown: () -> untyped
97
+ end
98
+
99
+ module Plugin
100
+ def self.export: (String name) { (json) -> untyped } -> nil
101
+ def self.on: (String event) { (json) -> untyped } -> nil
102
+ def self.call: (String method, ?json params) -> untyped
103
+ def self.notify: (String method, ?json params) -> nil
104
+ def self.capability?: (String name) -> bool
105
+ def self.run: (?input: IO, ?output: IO) -> nil
106
+ end
107
+
108
+ module Testing
109
+ class FakePlugin
110
+ attr_reader capabilities: Array[String]
111
+ def initialize: (?capabilities: Array[String]) -> void
112
+ def export: (String method) { (json) -> untyped } -> FakePlugin
113
+ def on_notification: () { (String, json) -> untyped } -> FakePlugin
114
+ def transport: (Manifest) { (Hash[String, untyped]?, Exception?) -> untyped } -> Transport
115
+ end
116
+ end
117
+ end
data/site.css ADDED
@@ -0,0 +1,247 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ --night: #060a13;
4
+ --deep: #0a1120;
5
+ --surface: #101a2d;
6
+ --surface-2: #152239;
7
+ --line: #283752;
8
+ --ink: #edf4ff;
9
+ --muted: #9aabc5;
10
+ --accent: #70dfc3;
11
+ --display: "Avenir Next Condensed", "Avenir Next", Avenir, "Segoe UI", sans-serif;
12
+ --body: "Avenir Next", Avenir, "Segoe UI", sans-serif;
13
+ --mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
14
+ }
15
+
16
+ * { box-sizing: border-box; }
17
+ html { scroll-behavior: smooth; }
18
+ body {
19
+ margin: 0;
20
+ background: var(--night);
21
+ color: var(--ink);
22
+ font-family: var(--body);
23
+ line-height: 1.6;
24
+ -webkit-font-smoothing: antialiased;
25
+ }
26
+
27
+ a { color: inherit; }
28
+ .skip-link {
29
+ position: fixed;
30
+ top: 0.75rem;
31
+ left: 0.75rem;
32
+ z-index: 10;
33
+ padding: 0.65rem 0.9rem;
34
+ background: var(--ink);
35
+ color: var(--night);
36
+ transform: translateY(-150%);
37
+ }
38
+ .skip-link:focus { transform: translateY(0); }
39
+ :focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }
40
+
41
+ .site-header,
42
+ main,
43
+ footer {
44
+ width: min(100% - 3rem, 76rem);
45
+ margin-inline: auto;
46
+ }
47
+ .site-header {
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: space-between;
51
+ min-height: 5rem;
52
+ border-bottom: 1px solid var(--line);
53
+ }
54
+ .brand {
55
+ display: inline-flex;
56
+ align-items: center;
57
+ gap: 0.7rem;
58
+ font-family: var(--display);
59
+ font-size: 1.05rem;
60
+ font-weight: 700;
61
+ text-decoration: none;
62
+ }
63
+ .star-mark {
64
+ width: 0.7rem;
65
+ aspect-ratio: 1;
66
+ background: var(--accent);
67
+ border-radius: 50%;
68
+ }
69
+ nav { display: flex; gap: clamp(1rem, 3vw, 2rem); }
70
+ nav a, footer a { color: var(--muted); font-size: 0.875rem; text-decoration: none; }
71
+ nav a:hover, footer a:hover { color: var(--ink); }
72
+
73
+ .hero {
74
+ display: grid;
75
+ grid-template-columns: minmax(0, 1fr) minmax(23rem, 0.9fr);
76
+ gap: clamp(3rem, 8vw, 7rem);
77
+ align-items: center;
78
+ min-height: calc(100svh - 5rem);
79
+ padding-block: 5rem;
80
+ }
81
+ .constellation {
82
+ margin: 0 0 1.4rem;
83
+ color: var(--accent);
84
+ font-size: 0.9rem;
85
+ font-weight: 650;
86
+ }
87
+ h1, h2, h3, p { margin-top: 0; }
88
+ h1, h2 { font-family: var(--display); text-wrap: balance; }
89
+ p { text-wrap: pretty; }
90
+ h1 {
91
+ max-width: 9ch;
92
+ margin-bottom: 1.5rem;
93
+ font-size: clamp(4.8rem, 11vw, 8.5rem);
94
+ font-weight: 650;
95
+ line-height: 0.82;
96
+ }
97
+ .lede {
98
+ max-width: 38rem;
99
+ margin-bottom: 2rem;
100
+ color: #c3cfe2;
101
+ font-size: clamp(1.05rem, 2vw, 1.28rem);
102
+ line-height: 1.65;
103
+ }
104
+ .actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }
105
+ .button {
106
+ display: inline-flex;
107
+ align-items: center;
108
+ min-height: 3rem;
109
+ padding: 0.65rem 1.1rem;
110
+ border: 1px solid var(--line);
111
+ color: var(--ink);
112
+ font-size: 0.9rem;
113
+ font-weight: 650;
114
+ text-decoration: none;
115
+ }
116
+ .button.primary { border-color: var(--accent); background: var(--accent); color: var(--night); }
117
+ .button:hover { border-color: var(--accent); }
118
+ .button.primary:hover { background: color-mix(in srgb, var(--accent) 80%, white); }
119
+ .facts {
120
+ display: flex;
121
+ flex-wrap: wrap;
122
+ gap: 1.5rem;
123
+ margin: 2.25rem 0 0;
124
+ padding: 0;
125
+ color: var(--muted);
126
+ font-size: 0.8rem;
127
+ list-style: none;
128
+ }
129
+ .facts strong { color: var(--ink); font-weight: 650; }
130
+
131
+ .instrument {
132
+ position: relative;
133
+ min-width: 0;
134
+ overflow: hidden;
135
+ border: 1px solid var(--line);
136
+ background: var(--surface);
137
+ box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
138
+ }
139
+ .instrument-head {
140
+ position: relative;
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: space-between;
144
+ min-height: 3rem;
145
+ padding-inline: 1rem;
146
+ border-bottom: 1px solid var(--line);
147
+ color: var(--muted);
148
+ font: 0.72rem var(--mono);
149
+ }
150
+ .signal { display: inline-flex; align-items: center; gap: 0.55rem; color: var(--accent); }
151
+ .signal::before { width: 0.45rem; aspect-ratio: 1; border-radius: 50%; background: currentColor; box-shadow: 0 0 0.8rem currentColor; content: ""; }
152
+ .instrument-body { position: relative; min-height: 25rem; padding: 1.5rem; font-family: var(--mono); font-variant-numeric: tabular-nums; }
153
+ .command { margin-bottom: 1.5rem; color: var(--muted); font-size: 0.78rem; }
154
+ .command b { color: var(--accent); font-weight: 500; }
155
+ .rows { border: 1px solid var(--line); background: rgb(6 10 19 / 0.58); }
156
+ .row {
157
+ display: grid;
158
+ grid-template-columns: minmax(0, 1fr) auto;
159
+ gap: 1rem;
160
+ align-items: center;
161
+ min-height: 3.25rem;
162
+ padding: 0.65rem 0.8rem;
163
+ color: #cbd7e8;
164
+ font-size: 0.74rem;
165
+ }
166
+ .row + .row { border-top: 1px solid var(--line); }
167
+ .row strong { color: var(--ink); font-weight: 550; }
168
+ .row small { color: var(--muted); font-size: 0.68rem; }
169
+ .path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
170
+ .hit { color: var(--night); background: var(--accent); }
171
+ .tag { padding: 0.18rem 0.4rem; border: 1px solid var(--line); color: var(--accent); font-size: 0.66rem; white-space: nowrap; }
172
+ .stream { display: grid; gap: 0.75rem; }
173
+ .message { max-width: 88%; padding: 0.8rem 0.9rem; border-left: 2px solid var(--accent); background: rgb(6 10 19 / 0.66); color: #ccd8e9; font-size: 0.74rem; }
174
+ .message:nth-child(even) { justify-self: end; border-color: var(--accent); }
175
+ .message small { display: block; margin-bottom: 0.3rem; color: var(--muted); }
176
+ .meter { display: grid; grid-template-columns: 5.5rem 1fr auto; gap: 0.75rem; align-items: center; color: var(--muted); font-size: 0.68rem; }
177
+ .meter + .meter { margin-top: 1rem; }
178
+ .track { height: 0.28rem; background: var(--line); }
179
+ .track i { display: block; width: var(--value); height: 100%; background: var(--accent); }
180
+ .diagram { position: relative; min-height: 20rem; }
181
+ .node { position: absolute; width: 7.5rem; padding: 0.7rem; border: 1px solid var(--line); background: var(--deep); color: var(--muted); font-size: 0.68rem; }
182
+ .node strong { display: block; margin-bottom: 0.25rem; color: var(--ink); font-size: 0.75rem; }
183
+ .node.core { inset: 38% auto auto 37%; border-color: var(--accent); }
184
+ .node.nw { inset: 4% auto auto 2%; }
185
+ .node.ne { inset: 2% 1% auto auto; }
186
+ .node.sw { inset: auto auto 3% 5%; }
187
+ .node.se { inset: auto 2% 5% auto; }
188
+ .orbit { position: absolute; inset: 19% 13%; border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); border-radius: 50%; transform: rotate(-9deg); }
189
+ .code {
190
+ min-height: 12rem;
191
+ margin: 0;
192
+ overflow-x: auto;
193
+ color: #cbd7e8;
194
+ font: 0.76rem/1.85 var(--mono);
195
+ white-space: pre;
196
+ }
197
+ .code .key { color: var(--accent); }
198
+ .code .value { color: var(--accent); }
199
+ .code .dim { color: var(--muted); }
200
+
201
+ .features { padding-block: 7rem; border-block: 1px solid var(--line); }
202
+ .section-heading { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: 4rem; align-items: end; }
203
+ h2 { margin-bottom: 0; font-size: clamp(2.6rem, 6vw, 5rem); line-height: 0.98; }
204
+ .section-heading p, .install-copy p { margin-bottom: 0; color: var(--muted); }
205
+ .feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 4rem; border-top: 1px solid var(--line); }
206
+ .feature-grid article { padding: 2rem 2rem 0 0; }
207
+ .feature-grid article + article { padding-left: 2rem; border-left: 1px solid var(--line); }
208
+ h3 { margin-bottom: 0.8rem; font-size: 1.05rem; }
209
+ .feature-grid p { margin-bottom: 0; color: var(--muted); font-size: 0.9rem; }
210
+
211
+ .install { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 5rem; align-items: center; padding-block: 7rem; }
212
+ .install h2 { margin-bottom: 1.5rem; }
213
+ .terminal { min-width: 0; border: 1px solid var(--line); background: var(--deep); }
214
+ .terminal-bar { padding: 0.7rem 1rem; border-bottom: 1px solid var(--line); color: var(--muted); font: 0.68rem var(--mono); }
215
+ .terminal pre { margin: 0; padding: 1.3rem; overflow-x: auto; color: #d5deeb; font: 0.77rem/1.8 var(--mono); }
216
+ .terminal .prompt { color: var(--accent); }
217
+
218
+ footer { display: flex; justify-content: space-between; gap: 2rem; padding-block: 2rem; color: var(--muted); font-size: 0.8rem; }
219
+ footer p { margin: 0; }
220
+ footer div { display: flex; gap: 1.5rem; }
221
+
222
+ @media (max-width: 880px) {
223
+ .hero, .section-heading, .install { grid-template-columns: 1fr; }
224
+ .hero { min-height: auto; }
225
+ .instrument { max-width: 38rem; }
226
+ .section-heading, .install { gap: 2.5rem; }
227
+ }
228
+
229
+ @media (max-width: 620px) {
230
+ .site-header, main, footer { width: min(100% - 2rem, 76rem); }
231
+ .site-header { min-height: 4.25rem; }
232
+ nav a:not(:last-child) { display: none; }
233
+ .hero { padding-block: 3.5rem 4.5rem; }
234
+ h1 { font-size: clamp(4.1rem, 23vw, 6.25rem); }
235
+ .instrument { margin-inline: -1rem; }
236
+ .instrument-body { min-height: 22rem; padding: 1rem; }
237
+ .facts { gap: 0.75rem 1rem; }
238
+ .features, .install { padding-block: 4.5rem; }
239
+ .feature-grid { grid-template-columns: 1fr; margin-top: 2.5rem; }
240
+ .feature-grid article { padding: 1.5rem 0; }
241
+ .feature-grid article + article { padding-left: 0; border-top: 1px solid var(--line); border-left: 0; }
242
+ footer { flex-direction: column; }
243
+ }
244
+
245
+ @media (prefers-reduced-motion: reduce) {
246
+ html { scroll-behavior: auto; }
247
+ }
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gienah
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: saiph
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 0.1.0
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.0
26
+ description: Content-Length RPC, capability gates, lifecycle management, and a small
27
+ plugin SDK.
28
+ email:
29
+ - t.yudai92@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - CHANGELOG.md
35
+ - LICENSE.txt
36
+ - README.md
37
+ - Rakefile
38
+ - docs/adr/000-template.md
39
+ - docs/adr/README.md
40
+ - index.html
41
+ - lib/gienah.rb
42
+ - lib/gienah/future.rb
43
+ - lib/gienah/host.rb
44
+ - lib/gienah/manifest.rb
45
+ - lib/gienah/plugin.rb
46
+ - lib/gienah/protocol.rb
47
+ - lib/gienah/sandbox.rb
48
+ - lib/gienah/testing/fake_plugin.rb
49
+ - lib/gienah/transport.rb
50
+ - lib/gienah/version.rb
51
+ - sig/gienah.rbs
52
+ - site.css
53
+ homepage: https://github.com/noxdea/gienah
54
+ licenses:
55
+ - MIT
56
+ metadata:
57
+ allowed_push_host: https://rubygems.org
58
+ source_code_uri: https://github.com/noxdea/gienah
59
+ changelog_uri: https://github.com/noxdea/gienah/blob/main/CHANGELOG.md
60
+ rubygems_mfa_required: 'true'
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.1.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubygems_version: 4.0.16
76
+ specification_version: 4
77
+ summary: Out-of-process Ruby plugin host
78
+ test_files: []