ruby_native 0.19.0 → 0.19.1
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/lib/ruby_native/cli/link.rb +21 -2
- data/lib/ruby_native/cli/mcp/prompts.rb +16 -11
- data/lib/ruby_native/cli/mcp.rb +61 -2
- data/lib/ruby_native/cli/preview.rb +63 -6
- data/lib/ruby_native/cli/status.rb +2 -2
- data/lib/ruby_native/cli.rb +1 -0
- data/lib/ruby_native/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 781df25f5a04d4f1d797f8004248c71e5e9f6b73d18965b4036e51bd8990f021
|
|
4
|
+
data.tar.gz: b232c5cdfd1f2cda62214936b27ae08e840f1666c0feecdd4e92bd7fd7b70c1c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5afe36bca245ee30724d854b2d11b8684090ae7fd4d441f1d5d8f6267113c3abbc18533fedfe0654ee3800d8e78509b0322b41675d282483ae3dc4a6de247695
|
|
7
|
+
data.tar.gz: d2334ac64c9a9283ddacbd4230fb6e965ddf9fec9f909bb91b6c0675e73c0e15ebcb064d77e93c8aa15c31733cec9ae47adc7531cefdeb8d9386880cfe4d1715
|
data/lib/ruby_native/cli/link.rb
CHANGED
|
@@ -10,7 +10,26 @@ module RubyNative
|
|
|
10
10
|
class Link < Command
|
|
11
11
|
def run
|
|
12
12
|
ensure_authenticated!
|
|
13
|
-
finish(self.class.link(positional.first || flag("app")))
|
|
13
|
+
return finish(self.class.link(positional.first || flag("app"))) unless @argv.include?("--new")
|
|
14
|
+
|
|
15
|
+
name, url = flag("name"), flag("url")
|
|
16
|
+
if name.to_s.strip.empty? || url.to_s.strip.empty?
|
|
17
|
+
puts "Usage: ruby_native link --new --name=\"App name\" --url=https://example.com"
|
|
18
|
+
exit 1
|
|
19
|
+
end
|
|
20
|
+
finish(self.class.create(name: name, url: url))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Creates the app on the account and links it, so an agent never sends
|
|
24
|
+
# the person to Create your app.
|
|
25
|
+
def self.create(name:, url:)
|
|
26
|
+
result = Mcp::Platform.post("/api/v1/apps", { name: name, url: url })
|
|
27
|
+
return failure(result) unless result.ok?
|
|
28
|
+
|
|
29
|
+
app = result.value
|
|
30
|
+
Mcp::Platform.write_app_id(app["public_id"])
|
|
31
|
+
success("Created #{app["name"]} (#{app["public_id"]}) for #{app["url"]} and linked it. Wrote ruby_native.app_id to #{Mcp::Platform::CONFIG_PATH}.",
|
|
32
|
+
{ "app_id" => app["public_id"], "name" => app["name"], "url" => app["url"] })
|
|
14
33
|
end
|
|
15
34
|
|
|
16
35
|
def self.link(app_id = nil)
|
|
@@ -18,7 +37,7 @@ module RubyNative
|
|
|
18
37
|
return failure(result) unless result.ok?
|
|
19
38
|
|
|
20
39
|
apps = Array(result.value)
|
|
21
|
-
return Outcome.new(ok: false, text: "No apps on this account yet. Create one
|
|
40
|
+
return Outcome.new(ok: false, text: "No apps on this account yet. Create one with `ruby_native link --new --name=\"App name\" --url=https://example.com`.", data: { "apps" => [] }) if apps.empty?
|
|
22
41
|
|
|
23
42
|
app = if app_id.to_s.strip.empty?
|
|
24
43
|
apps.one? ? apps.first : nil
|
|
@@ -81,22 +81,27 @@ module RubyNative
|
|
|
81
81
|
pass.
|
|
82
82
|
|
|
83
83
|
When you are done, run `validate_config` and `check_views` and fix what they report. Then
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
get the preview ready so the person only has to scan: call `preview_check`. If cloudflared
|
|
85
|
+
is missing, run the install command it gives you, or hand over the download link when
|
|
86
|
+
there is no Homebrew. If the Rails server is not running, ask the person to start it in
|
|
87
|
+
its own terminal; if it answers with an error, fix what the check reports. Call
|
|
88
|
+
`preview_check` again until it says ready.
|
|
89
|
+
|
|
90
|
+
Then tell the person to run `bundle exec ruby_native preview`, long-press the Ruby Native
|
|
91
|
+
app on their phone, tap Switch website, and scan the code it prints. No Ruby Native app
|
|
92
|
+
yet? It is at https://rubynative.com/try. Do not run preview yourself: it opens a tunnel
|
|
93
|
+
and waits for a phone. List what you changed and what they should look at on the device,
|
|
87
94
|
since you cannot see the app running in the native shell.
|
|
88
95
|
|
|
89
96
|
## 3. The account and the app
|
|
90
97
|
|
|
91
|
-
If `status` says there is no login
|
|
92
|
-
|
|
98
|
+
If `status` says there is no login, run `bundle exec ruby_native login`. It opens the
|
|
99
|
+
browser, where the person signs in, or creates an account and enters the code we email
|
|
100
|
+
them, then clicks Approve. Wait for them. No password goes through the terminal.
|
|
93
101
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Approve. No password goes through the terminal.
|
|
98
|
-
|
|
99
|
-
Then call `link_app`.
|
|
102
|
+
Then, with no linked app, call `link_app`. If the account has no app for this project yet,
|
|
103
|
+
call `app_create` with the app's name and its production URL. Take both from the codebase
|
|
104
|
+
when it says, and ask the person when it does not; never use localhost or a staging host.
|
|
100
105
|
|
|
101
106
|
## 4. The stores
|
|
102
107
|
|
data/lib/ruby_native/cli/mcp.rb
CHANGED
|
@@ -9,6 +9,7 @@ require "ruby_native/cli/demo_account"
|
|
|
9
9
|
require "ruby_native/cli/icon"
|
|
10
10
|
require "ruby_native/cli/listing"
|
|
11
11
|
require "ruby_native/cli/permissions"
|
|
12
|
+
require "ruby_native/cli/preview"
|
|
12
13
|
require "ruby_native/cli/screenshots"
|
|
13
14
|
require "ruby_native/cli/link"
|
|
14
15
|
require "ruby_native/cli/status"
|
|
@@ -114,6 +115,22 @@ module RubyNative
|
|
|
114
115
|
},
|
|
115
116
|
annotations: { title: "Validate config", readOnlyHint: true, openWorldHint: false }
|
|
116
117
|
},
|
|
118
|
+
{
|
|
119
|
+
name: "preview_check",
|
|
120
|
+
description: "Check what `bundle exec ruby_native preview` needs before handing it to the person: whether " \
|
|
121
|
+
"cloudflared is installed, with the command that installs it, and whether the Rails server " \
|
|
122
|
+
"answers on its port with a Ruby Native config. Opens no tunnel and reaches only the local " \
|
|
123
|
+
"server. Run it, fix what it reports, and run it again until it says ready.",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
port: { type: "integer", description: "The Rails server's port. Defaults to PORT, then 3000, like preview." },
|
|
128
|
+
url: { type: "string", description: "The Rails server's URL, for a server not on localhost. Overrides port." }
|
|
129
|
+
},
|
|
130
|
+
additionalProperties: false
|
|
131
|
+
},
|
|
132
|
+
annotations: { title: "Check preview", readOnlyHint: true, openWorldHint: false }
|
|
133
|
+
},
|
|
117
134
|
{
|
|
118
135
|
name: "config_errors",
|
|
119
136
|
description: "Report what the app your users are running is failing on: the config errors real devices " \
|
|
@@ -184,6 +201,23 @@ module RubyNative
|
|
|
184
201
|
},
|
|
185
202
|
annotations: { title: "Link app", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
186
203
|
},
|
|
204
|
+
{
|
|
205
|
+
name: "app_create",
|
|
206
|
+
description: "Create the app on the person's rubynative.com account and link this project to it, in place of " \
|
|
207
|
+
"sending them to Create your app. Use the app's name and its production URL, never localhost " \
|
|
208
|
+
"or a staging host; ask the person when the codebase doesn't say. Call `link_app` instead when " \
|
|
209
|
+
"the account already has this app. Reaches rubynative.com and needs `ruby_native login`.",
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
name: { type: "string", description: "What the person's customers see under the icon on their Home Screen." },
|
|
214
|
+
url: { type: "string", description: "The production Rails root URL, like https://example.com." }
|
|
215
|
+
},
|
|
216
|
+
required: [ "name", "url" ],
|
|
217
|
+
additionalProperties: false
|
|
218
|
+
},
|
|
219
|
+
annotations: { title: "Create app", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
220
|
+
},
|
|
187
221
|
{
|
|
188
222
|
name: "status",
|
|
189
223
|
description: "Report where the app is on the way to the App Store and Google Play and what is in the way: " \
|
|
@@ -532,8 +566,8 @@ module RubyNative
|
|
|
532
566
|
(2) In the layout <head>, add `<%= stylesheet_link_tag :ruby_native %>` and put
|
|
533
567
|
`viewport-fit=cover` in the viewport meta tag. (3) In the layout <body>, add
|
|
534
568
|
`<%= native_tabs_tag %>` and `<%= native_identity_tag(current_user&.id) %>`.
|
|
535
|
-
(4) Run `validate_config` and `check_views`, then `
|
|
536
|
-
on a phone. Full docs: https://rubynative.com/llms.txt (index) and llms-full.txt (everything).
|
|
569
|
+
(4) Run `validate_config` and `check_views`, then `preview_check` until it says ready, and
|
|
570
|
+
hand the person `bundle exec ruby_native preview` to see it on a phone. Full docs: https://rubynative.com/llms.txt (index) and llms-full.txt (everything).
|
|
537
571
|
|
|
538
572
|
Signals fail silently: an attribute the app does not know is ignored with no error anywhere.
|
|
539
573
|
Call `lookup_signals` before writing one and `check_views` after editing any view.
|
|
@@ -564,10 +598,14 @@ module RubyNative
|
|
|
564
598
|
when "check_views" then check_views(arguments)
|
|
565
599
|
when "lookup_signals" then lookup_signals(arguments)
|
|
566
600
|
when "validate_config" then validate_config(arguments)
|
|
601
|
+
when "preview_check" then preview_check(arguments)
|
|
567
602
|
when "config_errors" then config_errors(arguments)
|
|
568
603
|
when "deployed_builds" then deployed_builds(arguments)
|
|
569
604
|
when "build_status" then build_status(arguments)
|
|
570
605
|
when "status" then status(arguments)
|
|
606
|
+
when "app_create"
|
|
607
|
+
outcome = Link.create(name: string!(arguments, "name"), url: string!(arguments, "url"))
|
|
608
|
+
tool_result(outcome.text, outcome.data, is_error: !outcome.ok)
|
|
571
609
|
when "link_app"
|
|
572
610
|
outcome = Link.link(arguments["app_id"].to_s.strip.empty? ? nil : arguments["app_id"].to_s.strip)
|
|
573
611
|
tool_result(outcome.text, outcome.data, is_error: !outcome.ok)
|
|
@@ -677,6 +715,27 @@ module RubyNative
|
|
|
677
715
|
})
|
|
678
716
|
end
|
|
679
717
|
|
|
718
|
+
def preview_check(arguments)
|
|
719
|
+
argv = []
|
|
720
|
+
argv += [ "--port", arguments["port"].to_s ] if arguments["port"]
|
|
721
|
+
argv += [ "--url", arguments["url"].to_s ] if arguments["url"].is_a?(String) && !arguments["url"].strip.empty?
|
|
722
|
+
ready = Preview.new(argv).readiness
|
|
723
|
+
|
|
724
|
+
lines = []
|
|
725
|
+
if ready[:cloudflared]
|
|
726
|
+
lines << "cloudflared is installed."
|
|
727
|
+
else
|
|
728
|
+
lines << "cloudflared is not installed. Install it with: #{ready[:cloudflared_install]}"
|
|
729
|
+
end
|
|
730
|
+
lines << (ready[:server_ready] ? "The Rails server answers at #{ready[:upstream]} with a Ruby Native config." : ready[:server_problem])
|
|
731
|
+
if ready[:cloudflared] && ready[:server_ready]
|
|
732
|
+
lines << "Ready. Tell the person to run `bundle exec ruby_native preview`, long-press the Ruby Native app " \
|
|
733
|
+
"on their phone, tap Switch website, and scan the code it prints."
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
tool_result(lines.join("\n\n"), ready.merge(ready: ready[:cloudflared] && ready[:server_ready]))
|
|
737
|
+
end
|
|
738
|
+
|
|
680
739
|
# --- Tools that read the API ---
|
|
681
740
|
|
|
682
741
|
def config_errors(arguments)
|
|
@@ -2,6 +2,7 @@ require "open3"
|
|
|
2
2
|
require "net/http"
|
|
3
3
|
require "uri"
|
|
4
4
|
require "resolv"
|
|
5
|
+
require "stringio"
|
|
5
6
|
require "ruby_native/cli/credentials"
|
|
6
7
|
|
|
7
8
|
module RubyNative
|
|
@@ -17,6 +18,8 @@ module RubyNative
|
|
|
17
18
|
OUTPUT_TAIL_LINES = 5
|
|
18
19
|
DEPLOY_URL = "https://rubynative.com/deploy"
|
|
19
20
|
QR_QUIET_ZONE = 4
|
|
21
|
+
BREW_INSTALL = "brew install cloudflare/cloudflare/cloudflared".freeze
|
|
22
|
+
CLOUDFLARED_DOWNLOADS = "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/".freeze
|
|
20
23
|
|
|
21
24
|
def initialize(argv)
|
|
22
25
|
@url = parse_option(argv, "--url")
|
|
@@ -30,6 +33,19 @@ module RubyNative
|
|
|
30
33
|
start_tunnel
|
|
31
34
|
end
|
|
32
35
|
|
|
36
|
+
# What `run` would stop on, without opening a tunnel. The MCP's
|
|
37
|
+
# preview_check reads this so an agent can fix it before handing over.
|
|
38
|
+
def readiness
|
|
39
|
+
problem = upstream_problem
|
|
40
|
+
{
|
|
41
|
+
cloudflared: cloudflared_installed?,
|
|
42
|
+
cloudflared_install: homebrew_installed? ? BREW_INSTALL : CLOUDFLARED_DOWNLOADS,
|
|
43
|
+
upstream: @upstream,
|
|
44
|
+
server_ready: problem.nil?,
|
|
45
|
+
server_problem: problem
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
33
49
|
private
|
|
34
50
|
|
|
35
51
|
def check_upstream!
|
|
@@ -189,17 +205,58 @@ module RubyNative
|
|
|
189
205
|
end
|
|
190
206
|
|
|
191
207
|
def check_cloudflared!
|
|
192
|
-
|
|
193
|
-
|
|
208
|
+
return if cloudflared_installed?
|
|
209
|
+
|
|
210
|
+
puts "cloudflared is not installed."
|
|
211
|
+
puts ""
|
|
212
|
+
return if offer_to_install_cloudflared
|
|
213
|
+
|
|
214
|
+
puts "Install it with Homebrew:"
|
|
215
|
+
puts " #{BREW_INSTALL}"
|
|
216
|
+
puts ""
|
|
217
|
+
puts "Or see: #{CLOUDFLARED_DOWNLOADS}"
|
|
218
|
+
exit 1
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Only someone at a terminal can say yes; an agent or CI gets the command instead.
|
|
222
|
+
def offer_to_install_cloudflared
|
|
223
|
+
return false unless homebrew_installed? && $stdin.tty?
|
|
224
|
+
|
|
225
|
+
print "Install it now with Homebrew? [Y/n] "
|
|
226
|
+
answer = $stdin.gets.to_s.strip.downcase
|
|
227
|
+
return false unless answer.empty? || answer.start_with?("y")
|
|
228
|
+
|
|
229
|
+
if system(BREW_INSTALL) && cloudflared_installed?
|
|
230
|
+
puts ""
|
|
231
|
+
true
|
|
232
|
+
else
|
|
194
233
|
puts ""
|
|
195
|
-
puts "
|
|
196
|
-
puts " brew install cloudflare/cloudflare/cloudflared"
|
|
234
|
+
puts "Homebrew could not install cloudflared."
|
|
197
235
|
puts ""
|
|
198
|
-
|
|
199
|
-
exit 1
|
|
236
|
+
false
|
|
200
237
|
end
|
|
201
238
|
end
|
|
202
239
|
|
|
240
|
+
def cloudflared_installed?
|
|
241
|
+
system("which cloudflared > /dev/null 2>&1")
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def homebrew_installed?
|
|
245
|
+
system("which brew > /dev/null 2>&1")
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# check_upstream! prints its advice and exits; this keeps the advice as text.
|
|
249
|
+
def upstream_problem
|
|
250
|
+
original = $stdout
|
|
251
|
+
$stdout = StringIO.new
|
|
252
|
+
check_upstream!
|
|
253
|
+
nil
|
|
254
|
+
rescue SystemExit
|
|
255
|
+
$stdout.string.strip
|
|
256
|
+
ensure
|
|
257
|
+
$stdout = original
|
|
258
|
+
end
|
|
259
|
+
|
|
203
260
|
def start_tunnel
|
|
204
261
|
puts "Starting tunnel to #{@upstream}..."
|
|
205
262
|
if @url
|
|
@@ -93,8 +93,8 @@ module RubyNative
|
|
|
93
93
|
"the first native pass, the seven changes in the `ship` prompt (`ruby_native ship` prints it). " \
|
|
94
94
|
"Then `bundle exec ruby_native preview` to see it on a phone."
|
|
95
95
|
elsif !local["logged_in"]
|
|
96
|
-
"
|
|
97
|
-
"then `ruby_native
|
|
96
|
+
"`ruby_native login` (it opens the browser to sign in, or to create an account and confirm the email), " \
|
|
97
|
+
"then `ruby_native link --new --name=NAME --url=URL` to create the app, or `ruby_native link` if it already exists."
|
|
98
98
|
else
|
|
99
99
|
"run `ruby_native link` to pick the app on your account."
|
|
100
100
|
end
|
data/lib/ruby_native/cli.rb
CHANGED
|
@@ -83,6 +83,7 @@ module RubyNative
|
|
|
83
83
|
puts "Commands:"
|
|
84
84
|
puts " Every command that talks to rubynative.com reads the app from config/ruby_native.yml, or takes --app=ID."
|
|
85
85
|
puts " link [APP_ID] Link this project to an app on your account (writes ruby_native.app_id)"
|
|
86
|
+
puts " --new --name=NAME --url=URL Create the app on your account first, then link it"
|
|
86
87
|
puts " apple key PATH --key-id=KEY --issuer-id=ISSUER --team-id=TEAM"
|
|
87
88
|
puts " Hand over the App Store Connect key (.p8) and wait for Apple to accept it"
|
|
88
89
|
puts " check Validate your views against the signal vocabulary"
|
data/lib/ruby_native/version.rb
CHANGED