pulse-rb 1.2.24

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.
Files changed (54) hide show
  1. package/README.md +225 -0
  2. package/adapters/linoria.lua +233 -0
  3. package/adapters/windui.llms.txt +366 -0
  4. package/adapters/windui.lua +505 -0
  5. package/bin/rb.js +2 -0
  6. package/dist/index.js +3285 -0
  7. package/package.json +59 -0
  8. package/pulse/dev/debuggui.lua +1206 -0
  9. package/pulse/dev/devconfig.lua +81 -0
  10. package/pulse/dev/ui/9_DevPanel.lua +384 -0
  11. package/pulse/helpers/aim.lua +193 -0
  12. package/pulse/helpers/cache.lua +68 -0
  13. package/pulse/helpers/cleaner.lua +110 -0
  14. package/pulse/helpers/conn.lua +33 -0
  15. package/pulse/helpers/cooldown.lua +47 -0
  16. package/pulse/helpers/draw.lua +122 -0
  17. package/pulse/helpers/hitbox.lua +63 -0
  18. package/pulse/helpers/input.lua +24 -0
  19. package/pulse/helpers/log.lua +228 -0
  20. package/pulse/helpers/loop.lua +58 -0
  21. package/pulse/helpers/memory.lua +48 -0
  22. package/pulse/helpers/narrate.lua +160 -0
  23. package/pulse/helpers/notify.lua +51 -0
  24. package/pulse/helpers/perf.lua +48 -0
  25. package/pulse/helpers/remote.lua +128 -0
  26. package/pulse/helpers/restore.lua +59 -0
  27. package/pulse/helpers/store.lua +39 -0
  28. package/pulse/helpers/team.lua +83 -0
  29. package/pulse/helpers/testmode.lua +80 -0
  30. package/pulse/helpers/trace.lua +111 -0
  31. package/pulse/helpers/track.lua +85 -0
  32. package/pulse/helpers/vec.lua +52 -0
  33. package/pulse/helpers/world.lua +51 -0
  34. package/pulse/runtime.lua +343 -0
  35. package/pulse/ui/linoria_settings.lua +55 -0
  36. package/pulse/ui/windui_settings.lua +87 -0
  37. package/templates/AGENTS.md +177 -0
  38. package/templates/CLAUDE.md +424 -0
  39. package/templates/deploy_config.example +17 -0
  40. package/templates/example_esp.rblua +69 -0
  41. package/templates/example_fov.rblua +20 -0
  42. package/templates/example_speed.rblua +25 -0
  43. package/templates/gitignore +4 -0
  44. package/templates/globals.lua +66 -0
  45. package/templates/layout.rblua +28 -0
  46. package/templates/module.lua +7 -0
  47. package/templates/module.rblua +32 -0
  48. package/templates/page_home.rblua +9 -0
  49. package/templates/remote.lua +6 -0
  50. package/templates/remotes.lua +14 -0
  51. package/vscode/language-configuration.json +35 -0
  52. package/vscode/package.json +35 -0
  53. package/vscode/src/extension.js +397 -0
  54. package/vscode/syntaxes/rblua.tmLanguage.json +126 -0
@@ -0,0 +1,505 @@
1
+ -- [adapters/windui.lua]
2
+ -- Wind UI adapter for Pulse components.
3
+ -- Injected by the compiler when ui_library = "windui" in layout.rblua.
4
+ -- Provides _UIAdapter, _PulseNotify, and Pulse.UI (dynamic element builder).
5
+ --
6
+ -- Activation: set ui_library = "windui" in src/ui/layout.rblua
7
+ -- Docs: footagesus.github.io/treehub-web/docs/windui
8
+ -- Version: 1.6.64-fix (pinned)
9
+
10
+ local _WINDUI_URL = "https://github.com/Footagesus/WindUI/releases/download/1.6.64-fix/main.lua"
11
+ local _PULSE_LOGO = "https://pulse-rb.vercel.app/img/logo.svg"
12
+
13
+ local _ok, _src = pcall(game.HttpGet, game, _WINDUI_URL)
14
+ if not _ok then error("[Pulse/WindUI] HTTP failed: " .. tostring(_src)) end
15
+ if type(_src) ~= "string" or #_src < 100 then
16
+ error("[Pulse/WindUI] Empty response from WindUI CDN")
17
+ end
18
+ local _loadFn, _loadErr = loadstring(_src)
19
+ if not _loadFn then error("[Pulse/WindUI] Compile error: " .. tostring(_loadErr)) end
20
+ local _ok2, WindUI = pcall(_loadFn)
21
+ if not _ok2 then error("[Pulse/WindUI] Init failed: " .. tostring(WindUI)) end
22
+
23
+ -- ── Shared state ──────────────────────────────────────────────────────────────
24
+
25
+ local _UIS = game:GetService("UserInputService")
26
+ local _widgets = {} -- [id] → wind element, for programmatic signal sync
27
+ local _mountedGbs = {} -- [component table ref] → groupbox proxy
28
+ local _windWindow = nil -- set in CreateWindow
29
+ local _windConfig = nil -- set in CreateWindow (ConfigManager handle)
30
+ local _folder = "Hub" -- overwritten in CreateWindow from layout opts
31
+ local _notifyTitle = "Hub" -- overwritten in CreateWindow from layout opts
32
+ local _toggleKeyName = "RightControl"
33
+ local _windowVisible = true
34
+
35
+ local _isMobile = _UIS.TouchEnabled and not _UIS.KeyboardEnabled
36
+
37
+ -- ── Adapter ───────────────────────────────────────────────────────────────────
38
+
39
+ local _UIAdapter = {}
40
+
41
+ function _UIAdapter:RegisterTheme(t)
42
+ pcall(function()
43
+ WindUI:AddTheme({
44
+ Name = t.name,
45
+ Accent = Color3.fromHex(t.accent or "#ffffff"),
46
+ Background = Color3.fromHex(t.background or "#1a1a2e"),
47
+ Outline = Color3.fromHex(t.outline or "#2a2a3e"),
48
+ Text = Color3.fromHex(t.text or "#ffffff"),
49
+ Placeholder = Color3.fromHex(t.placeholder or "#888888"),
50
+ Button = Color3.fromHex(t.button or "#2a2a3e"),
51
+ Icon = Color3.fromHex(t.icon or "#ffffff"),
52
+ })
53
+ end)
54
+ end
55
+
56
+ function _UIAdapter:GetThemeNames()
57
+ local names = {}
58
+ pcall(function()
59
+ for name, _ in pairs(WindUI:GetThemes()) do
60
+ table.insert(names, name)
61
+ end
62
+ table.sort(names)
63
+ end)
64
+ return next(names) ~= nil and names or nil
65
+ end
66
+
67
+ -- ── Helpers ───────────────────────────────────────────────────────────────────
68
+
69
+ local function _getViewportWidth()
70
+ local ok, vp = pcall(function() return workspace.CurrentCamera.ViewportSize.X end)
71
+ return (ok and type(vp) == "number") and vp or 1920
72
+ end
73
+
74
+ local function _mkColumns(windTab)
75
+ local leftCol, rightCol
76
+ local vw = _getViewportWidth()
77
+
78
+ if not _isMobile and vw >= 960 then
79
+ pcall(function()
80
+ local hs = windTab:HStack({ AutoSpace = true })
81
+ local lv = hs:VStack({})
82
+ local rv = hs:VStack({})
83
+ if lv and rv then
84
+ leftCol = lv
85
+ rightCol = rv
86
+ end
87
+ end)
88
+ else
89
+ pcall(function()
90
+ local col = windTab:VStack({})
91
+ if col then
92
+ leftCol = col
93
+ rightCol = col
94
+ end
95
+ end)
96
+ end
97
+
98
+ leftCol = leftCol or windTab
99
+ rightCol = rightCol or windTab
100
+ return leftCol, rightCol
101
+ end
102
+
103
+ -- ── Window proxy ──────────────────────────────────────────────────────────────
104
+
105
+ function _UIAdapter:CreateWindow(title, w, h, opts)
106
+ opts = opts or {}
107
+ _folder = opts.folder or _folder
108
+ _notifyTitle = opts.notify_title or _notifyTitle
109
+
110
+ local icon = (opts.icon and opts.icon ~= "") and opts.icon or _PULSE_LOGO
111
+ local author = (opts.author and opts.author ~= "") and opts.author or nil
112
+ local transparency = opts.transparency or 0
113
+ local acrylic = opts.acrylic == true
114
+ local openBtnMobileOnly = opts.open_btn_mobile_only ~= false
115
+ local openBtnIcon = (opts.open_btn_icon and opts.open_btn_icon ~= "") and opts.open_btn_icon or nil
116
+
117
+ _windWindow = WindUI:CreateWindow({
118
+ Title = title,
119
+ Icon = icon,
120
+ Author = author,
121
+ Folder = _folder,
122
+ Theme = opts.theme or "Dark",
123
+ Resizable = true,
124
+ Size = UDim2.fromOffset(w or 950, h or 600),
125
+ Transparent = acrylic or (transparency > 0),
126
+ BackgroundImageTransparency = acrylic and 0 or transparency,
127
+ Acrylic = acrylic,
128
+
129
+ OpenButton = {
130
+ Enabled = true,
131
+ Draggable = true,
132
+ OnlyMobile = openBtnMobileOnly,
133
+ Icon = openBtnIcon,
134
+ Scale = 0.5,
135
+ CornerRadius = UDim.new(1, 0),
136
+ StrokeThickness = 3,
137
+ },
138
+ })
139
+
140
+ pcall(function()
141
+ _windConfig = _windWindow.ConfigManager:Config(_folder)
142
+ end)
143
+
144
+ _UIAdapter.Window = _windWindow
145
+ _UIAdapter.Config = _windConfig
146
+
147
+ local winProxy = { _wind = _windWindow }
148
+
149
+ function winProxy:AddTab(name, icon)
150
+ local windTab = _windWindow:Tab({
151
+ Title = name,
152
+ Icon = (icon and icon ~= "") and icon or nil,
153
+ })
154
+ if not _UIAdapter._firstTab then
155
+ _UIAdapter._firstTab = windTab
156
+ end
157
+
158
+ local leftCol, rightCol = _mkColumns(windTab)
159
+
160
+ local function _mkSection(col, gbTitle, gbIcon)
161
+ local sect = col:Section({
162
+ Title = gbTitle,
163
+ Icon = (gbIcon and gbIcon ~= "") and gbIcon or nil,
164
+ Box = true,
165
+ BoxBorder = true,
166
+ Opened = true,
167
+ })
168
+ return { _container = sect }
169
+ end
170
+
171
+ local tabProxy = { _wind = windTab }
172
+ function tabProxy:AddLeftGroupbox(title, gbIcon) return _mkSection(leftCol, title, gbIcon) end
173
+ function tabProxy:AddRightGroupbox(title, gbIcon) return _mkSection(rightCol, title, gbIcon) end
174
+ return tabProxy
175
+ end
176
+
177
+ return winProxy
178
+ end
179
+
180
+ function _UIAdapter:ToggleWindow()
181
+ if not _windWindow then return end
182
+ if _windowVisible then
183
+ pcall(_PulseNotify, "Press " .. _toggleKeyName .. " to open", 4)
184
+ end
185
+ pcall(function() _windWindow:Toggle() end)
186
+ _windowVisible = not _windowVisible
187
+ end
188
+
189
+ function _UIAdapter:SetToggleKey(keyCode)
190
+ if keyCode then
191
+ pcall(function() _toggleKeyName = keyCode.Name end)
192
+ end
193
+ end
194
+
195
+ function _UIAdapter:SetTheme(name)
196
+ pcall(function() WindUI:SetTheme(name) end)
197
+ end
198
+
199
+ -- ── Widget helpers ────────────────────────────────────────────────────────────
200
+
201
+ function _UIAdapter:addToggle(gb, id, opts)
202
+ local elem = gb._container:Toggle({
203
+ Title = opts.label,
204
+ Desc = opts.tip or nil,
205
+ Value = opts.signal(),
206
+ Flag = id,
207
+ Callback = function(v) opts.signal(v) end,
208
+ })
209
+ _widgets[id] = elem
210
+ _G.Toggles = _G.Toggles or {}
211
+ _G.Toggles[id] = { Value = opts.signal(), SetValue = function(_, v) opts.signal(v) end }
212
+ opts.signal:onChange(function(v)
213
+ local w = _widgets[id]
214
+ if w then pcall(function() w:Set(v) end) end
215
+ if _G.Toggles and _G.Toggles[id] then _G.Toggles[id].Value = v end
216
+ end)
217
+ end
218
+
219
+ function _UIAdapter:addSlider(gb, id, opts)
220
+ local min = opts.min or 0
221
+ local max = opts.max or 100
222
+ local def = opts.signal()
223
+ local step
224
+ if opts.rounding and opts.rounding > 0 then
225
+ step = 1 / (10 ^ opts.rounding)
226
+ else
227
+ local isFloat = (min % 1 ~= 0) or (max % 1 ~= 0)
228
+ or (type(def) == "number" and def % 1 ~= 0)
229
+ step = isFloat and 0.01 or 1
230
+ end
231
+ local elem = gb._container:Slider({
232
+ Title = opts.label,
233
+ Desc = opts.tip or nil,
234
+ Step = step,
235
+ Value = { Min = min, Max = max, Default = def },
236
+ Flag = id,
237
+ Callback = function(v) opts.signal(v) end,
238
+ })
239
+ _widgets[id] = elem
240
+ _G.Options = _G.Options or {}
241
+ _G.Options[id] = { Value = opts.signal(), SetValue = function(_, v) opts.signal(v) end }
242
+ opts.signal:onChange(function(v)
243
+ local w = _widgets[id]
244
+ if w then pcall(function() w:Set(v) end) end
245
+ if _G.Options and _G.Options[id] then _G.Options[id].Value = v end
246
+ end)
247
+ end
248
+
249
+ function _UIAdapter:addDropdown(gb, id, opts)
250
+ local elem = gb._container:Dropdown({
251
+ Title = opts.label,
252
+ Desc = opts.tip or nil,
253
+ Values = opts.options,
254
+ Value = opts.signal(),
255
+ Flag = id,
256
+ Callback = function(v) opts.signal(v) end,
257
+ })
258
+ _widgets[id] = elem
259
+ _G.Options = _G.Options or {}
260
+ _G.Options[id] = { Value = opts.signal(), SetValue = function(_, v) opts.signal(v) end }
261
+ opts.signal:onChange(function(v)
262
+ local w = _widgets[id]
263
+ if w then pcall(function() w:Select(v) end) end
264
+ if _G.Options and _G.Options[id] then _G.Options[id].Value = v end
265
+ end)
266
+ end
267
+
268
+ function _UIAdapter:addMultiDropdown(gb, id, opts)
269
+ local current = opts.signal()
270
+ local elem = gb._container:Dropdown({
271
+ Title = opts.label,
272
+ Desc = opts.tip or nil,
273
+ Values = opts.options,
274
+ Value = (type(current) == "table" and next(current) ~= nil) and current or nil,
275
+ Multi = true,
276
+ AllowNone = true,
277
+ Flag = id,
278
+ Callback = function(v) opts.signal(v) end,
279
+ })
280
+ _widgets[id] = elem
281
+ opts.signal:onChange(function(v)
282
+ local w = _widgets[id]
283
+ if w then pcall(function() w:Select(v) end) end
284
+ end)
285
+ end
286
+
287
+ function _UIAdapter:addButton(gb, opts)
288
+ gb._container:Button({
289
+ Title = opts.label,
290
+ Desc = opts.tip or nil,
291
+ Callback = opts.action,
292
+ })
293
+ end
294
+
295
+ function _UIAdapter:addKeybind(gb, id, opts)
296
+ local elem = gb._container:Keybind({
297
+ Title = opts.label,
298
+ Value = opts.key,
299
+ Flag = id,
300
+ Callback = function(_v)
301
+ if _UIS:GetFocusedTextBox() then return end
302
+ opts.action()
303
+ end,
304
+ })
305
+ _widgets[id] = elem
306
+ end
307
+
308
+ function _UIAdapter:addLabel(gb, text)
309
+ if text == "" or text:match("^%s*$") then
310
+ gb._container:Space({ Columns = 1 })
311
+ else
312
+ gb._container:Paragraph({ Title = text, Desc = "" })
313
+ end
314
+ end
315
+
316
+ function _UIAdapter:addParagraph(gb, title, desc)
317
+ gb._container:Paragraph({ Title = title or "", Desc = desc or "" })
318
+ end
319
+
320
+ function _UIAdapter:addSeparator(gb)
321
+ gb._container:Divider()
322
+ end
323
+
324
+ -- ── Notify ────────────────────────────────────────────────────────────────────
325
+
326
+ _PulseNotify = function(msg, duration)
327
+ pcall(function()
328
+ WindUI:Notify({
329
+ Title = _notifyTitle,
330
+ Content = msg,
331
+ Duration = duration or 5,
332
+ })
333
+ end)
334
+ end
335
+
336
+ -- ── Destroy ───────────────────────────────────────────────────────────────────
337
+
338
+ _PulseOnDestroy(function()
339
+ pcall(function()
340
+ if _windWindow then _windWindow:Destroy() end
341
+ end)
342
+ end)
343
+
344
+ -- ── Mount ─────────────────────────────────────────────────────────────────────
345
+
346
+ function _UIAdapter:mount(component, gb)
347
+ if not component then return end
348
+ -- Store groupbox so Pulse.UI.gb(component) works in after {} blocks
349
+ _mountedGbs[component] = gb
350
+ local ui = component._ui
351
+ if not ui then return end
352
+ for _, w in ipairs(ui) do
353
+ local t = w.type
354
+ local sig = component[w.signal]
355
+ if t == "toggle" then
356
+ self:addToggle(gb, w.id, { label = w.label, signal = sig, tip = w.tip })
357
+ elseif t == "slider" then
358
+ self:addSlider(gb, w.id, { label = w.label, signal = sig,
359
+ min = w.min, max = w.max, tip = w.tip,
360
+ rounding = w.rounding })
361
+ elseif t == "dropdown" then
362
+ self:addDropdown(gb, w.id, { label = w.label, signal = sig,
363
+ options = w.options, tip = w.tip })
364
+ elseif t == "multidropdown" then
365
+ self:addMultiDropdown(gb, w.id, { label = w.label, signal = sig,
366
+ options = w.options, tip = w.tip })
367
+ elseif t == "button" then
368
+ self:addButton(gb, { label = w.label, action = w.action, tip = w.tip })
369
+ elseif t == "keybind" then
370
+ self:addKeybind(gb, w.id, { label = w.label, key = w.key, action = w.action })
371
+ elseif t == "label" then
372
+ self:addLabel(gb, w.label)
373
+ elseif t == "paragraph" then
374
+ self:addParagraph(gb, w.title, w.desc)
375
+ elseif t == "separator" then
376
+ self:addSeparator(gb)
377
+ end
378
+ end
379
+ end
380
+
381
+ -- ── Pulse.UI — imperative dynamic UI builder ──────────────────────────────────
382
+ -- Use in after {} blocks (runs after mount, so groupbox is available).
383
+ -- All functions return a handle with :set / :setTitle / :setDesc / :refresh /
384
+ -- :destroy methods. All mutations are nil-safe (pcall-wrapped).
385
+ --
386
+ -- Quick reference:
387
+ -- local gb = Pulse.UI.gb(MyComponent)
388
+ -- local par = Pulse.UI.paragraph(gb, "Title", "desc") par:setDesc("new")
389
+ -- local btn = Pulse.UI.button(gb, "Label", "desc", fn)
390
+ -- local tog = Pulse.UI.toggle(gb, "id", "Label", "desc", false, fn)
391
+ -- local sli = Pulse.UI.slider(gb, "id", "Label", 0, 100, 50, fn)
392
+ -- local drp = Pulse.UI.dropdown(gb, "id", "Label", {"A"}, nil, false, fn)
393
+ -- drp:refresh({"A","B"}) drp:set("B")
394
+ -- local sec = Pulse.UI.section(gb, "Title", "icon-name")
395
+ -- sec:paragraph(...) sec:button(...) etc.
396
+
397
+ Pulse.UI = {}
398
+
399
+ function Pulse.UI.gb(component)
400
+ return _mountedGbs[component]
401
+ end
402
+
403
+ function Pulse.UI.paragraph(container, title, desc)
404
+ local elem
405
+ pcall(function()
406
+ elem = container._container:Paragraph({ Title = title or "", Desc = desc or "" })
407
+ end)
408
+ local h = {}
409
+ function h:set(t, d)
410
+ pcall(function()
411
+ if t ~= nil then elem:SetTitle(t) end
412
+ if d ~= nil then elem:SetDesc(d) end
413
+ end)
414
+ end
415
+ function h:setTitle(v) pcall(function() elem:SetTitle(v) end) end
416
+ function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
417
+ function h:destroy() pcall(function() elem:Destroy() end) end
418
+ return h
419
+ end
420
+
421
+ function Pulse.UI.button(container, label, desc, fn)
422
+ local elem
423
+ pcall(function()
424
+ elem = container._container:Button({ Title = label, Desc = desc, Callback = fn })
425
+ end)
426
+ local h = {}
427
+ function h:setTitle(v) pcall(function() elem:SetTitle(v) end) end
428
+ function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
429
+ function h:destroy() pcall(function() elem:Destroy() end) end
430
+ return h
431
+ end
432
+
433
+ function Pulse.UI.toggle(container, id, label, desc, default, fn)
434
+ local elem
435
+ pcall(function()
436
+ elem = container._container:Toggle({
437
+ Title = label, Desc = desc, Value = default or false,
438
+ Flag = id, Callback = fn,
439
+ })
440
+ end)
441
+ local h = {}
442
+ function h:set(v) pcall(function() elem:Set(v) end) end
443
+ function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
444
+ function h:destroy() pcall(function() elem:Destroy() end) end
445
+ return h
446
+ end
447
+
448
+ function Pulse.UI.slider(container, id, label, min, max, default, fn)
449
+ local elem
450
+ pcall(function()
451
+ local mn, mx = min or 0, max or 100
452
+ elem = container._container:Slider({
453
+ Title = label, Step = 1,
454
+ Value = { Min = mn, Max = mx, Default = default or mn },
455
+ Flag = id, Callback = fn,
456
+ })
457
+ end)
458
+ local h = {}
459
+ function h:set(v) pcall(function() elem:Set(v) end) end
460
+ function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
461
+ function h:destroy() pcall(function() elem:Destroy() end) end
462
+ return h
463
+ end
464
+
465
+ function Pulse.UI.dropdown(container, id, label, values, default, multi, fn)
466
+ local elem
467
+ pcall(function()
468
+ elem = container._container:Dropdown({
469
+ Title = label, Values = values or {}, Value = default,
470
+ Multi = multi or false, AllowNone = multi or false,
471
+ Flag = id, Callback = fn,
472
+ })
473
+ end)
474
+ local h = {}
475
+ function h:set(v) pcall(function() elem:Select(v) end) end
476
+ function h:refresh(vs) pcall(function() elem:Refresh(vs) end) end
477
+ function h:setDesc(v) pcall(function() elem:SetDesc(v) end) end
478
+ function h:destroy() pcall(function() elem:Destroy() end) end
479
+ return h
480
+ end
481
+
482
+ function Pulse.UI.divider(container)
483
+ pcall(function() container._container:Divider() end)
484
+ end
485
+
486
+ -- Creates a nested section inside a groupbox. Returns a proxy with the same
487
+ -- methods as a groupbox so you can chain: sec:paragraph(), sec:button(), etc.
488
+ function Pulse.UI.section(container, title, icon, opened)
489
+ local sect
490
+ pcall(function()
491
+ sect = container._container:Section({
492
+ Title = title or "", Icon = (icon and icon ~= "") and icon or nil,
493
+ Box = true, BoxBorder = true, Opened = opened ~= false,
494
+ })
495
+ end)
496
+ local proxy = { _container = sect }
497
+ function proxy:paragraph(...) return Pulse.UI.paragraph(self, ...) end
498
+ function proxy:button(...) return Pulse.UI.button(self, ...) end
499
+ function proxy:toggle(...) return Pulse.UI.toggle(self, ...) end
500
+ function proxy:slider(...) return Pulse.UI.slider(self, ...) end
501
+ function proxy:dropdown(...) return Pulse.UI.dropdown(self, ...) end
502
+ function proxy:divider() Pulse.UI.divider(self) end
503
+ function proxy:section(...) return Pulse.UI.section(self, ...) end
504
+ return proxy
505
+ end
package/bin/rb.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../dist/index.js')