iterm2_ruby 0.3.1 → 0.4.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/CHANGELOG.md +11 -0
- data/README.md +6 -3
- data/docs/api.md +33 -0
- data/lib/iterm2/browser.rb +95 -0
- data/lib/iterm2/client.rb +8 -0
- data/lib/iterm2/version.rb +1 -1
- data/lib/iterm2.rb +13 -0
- data/llms.txt +12 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '005262752689643c56c4a97896c3b923f422c4ff49dd5c310664dcefbe7f4c2a'
|
|
4
|
+
data.tar.gz: 332e89ff558b000f3f69d35102587a051b48db11818bafeba03c34621feab9f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2f9294eb24b0e60b62954296a1ee675a9107b9caea7e6cf75fb1c36429740c0219cae2c0dfa1710d303007f024b713ab5fb5b7f0a8ada62915b6a5fcac4668a8
|
|
7
|
+
data.tar.gz: fdb38625258e956ae0fef7be027a099739fda95c256a7b6bd2be5e7034bc13e16c885fbed94b26a230a5efded8334a6e4e907be58ed978d0bf0c2f7bfd4a804b
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [0.4.0] - 2026-10-23
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- `client.split_browser_pane(session_id, url, vertical: true)` -- split a pane that shows a URL as a web view.
|
|
10
|
+
Uses the `"Custom Command" => "Browser"` profile override, so no "Web Browser" profile is needed.
|
|
11
|
+
- `ITerm2::Browser` -- connection-free probes (`plugin_installed?`, `profiles_enabled?`, `available?`) and
|
|
12
|
+
`pane_properties(url)`. The plugin check looks in the usual install folders first and asks Spotlight only
|
|
13
|
+
as a fallback, so a plugin in those folders never depends on a Spotlight lookup. Probe commands run without a
|
|
14
|
+
shell and are terminated on timeout.
|
|
15
|
+
- `ITerm2.current_session_id` -- the calling pane's session ID from `ITERM_SESSION_ID`, `nil` outside iTerm2.
|
|
16
|
+
|
|
6
17
|
## [0.3.1] - 2026-09-04
|
|
7
18
|
|
|
8
19
|
### Fixed
|
data/README.md
CHANGED
|
@@ -35,12 +35,14 @@ This feeds a real-time "mission control" view over a whole fleet of sessions.
|
|
|
35
35
|
**Split a pane and pop a live browser preview next to your code:**
|
|
36
36
|
|
|
37
37
|
```ruby
|
|
38
|
-
ITerm2.
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
here = ITerm2.current_session_id # nil outside iTerm2
|
|
39
|
+
if here && ITerm2::Browser.available? # Browser Plugin installed, browser-style profiles on
|
|
40
|
+
ITerm2.connect { |c| c.split_browser_pane(here, url, vertical: true) }
|
|
41
41
|
end
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
No "Web Browser" profile needs to exist; the pane's profile is overridden to show the URL.
|
|
45
|
+
|
|
44
46
|
**Spawn a background task in its own window, no permission dialogs:**
|
|
45
47
|
|
|
46
48
|
```bash
|
|
@@ -214,6 +216,7 @@ Most commands accept `--session ID`, `--tab ID`, or `--window ID` to target a sp
|
|
|
214
216
|
| `raise_by_cwd(pattern)` | Raise first session matching cwd |
|
|
215
217
|
| `create_tab(window_id:, profile_name:)` | Create a new tab |
|
|
216
218
|
| `split_pane(session_id, vertical:)` | Split a pane |
|
|
219
|
+
| `split_browser_pane(session_id, url, vertical:)` | Split a pane that shows a URL as a web view (check `ITerm2::Browser.available?`) |
|
|
217
220
|
| `close_session(session_id)` | Close a session |
|
|
218
221
|
| `close_tab(tab_id)` | Close a tab |
|
|
219
222
|
| `reorder_tabs(window_id => [tab_ids])` | Move/reorder tabs across windows |
|
data/docs/api.md
CHANGED
|
@@ -28,6 +28,12 @@ Creates a persistent client with an open connection. Equivalent to `ITerm2.conne
|
|
|
28
28
|
|
|
29
29
|
Closes the connection. Unsubscribes all notification listeners and stops the dispatch thread if running.
|
|
30
30
|
|
|
31
|
+
### `ITerm2.current_session_id(env = ENV)` -> String | nil
|
|
32
|
+
|
|
33
|
+
The session ID of the pane the calling process runs in: the GUID after the colon of `ITERM_SESSION_ID`
|
|
34
|
+
(`"w0t0p0:<GUID>"`). `nil` outside iTerm2 or when the value is not that shape. This is the ID every
|
|
35
|
+
session-targeted call takes.
|
|
36
|
+
|
|
31
37
|
---
|
|
32
38
|
|
|
33
39
|
## Topology
|
|
@@ -188,6 +194,33 @@ Splits a pane. Returns the new session ID.
|
|
|
188
194
|
|
|
189
195
|
**Raises:** `RPCError` on failure.
|
|
190
196
|
|
|
197
|
+
### `client.split_browser_pane(session_id, url, vertical: true)` -> String
|
|
198
|
+
|
|
199
|
+
Splits a pane that shows `url` as a web view (a browser pane), beside `session_id`. Returns the new session ID.
|
|
200
|
+
It is `split_pane` with the profile overrides `"Custom Command" => "Browser"` and `"Initial URL" => url`, so no
|
|
201
|
+
"Web Browser" profile has to be installed. iTerm2 still needs its separately installed Browser Plugin and the
|
|
202
|
+
"Enable browser-style profiles" Advanced setting (on by default); check with `ITerm2::Browser.available?`.
|
|
203
|
+
|
|
204
|
+
- **session_id** (String) -- session to split
|
|
205
|
+
- **url** (String) -- page the pane loads
|
|
206
|
+
- **vertical** (Boolean) -- `true` for a vertical split, `false` for horizontal
|
|
207
|
+
|
|
208
|
+
**Raises:** `RPCError` on failure.
|
|
209
|
+
|
|
210
|
+
Pass a real session ID: with `nil`, `split_pane` sends no session and iTerm2 chooses which pane to split.
|
|
211
|
+
When calling with `ITerm2.current_session_id`, guard against `nil` (outside iTerm2).
|
|
212
|
+
|
|
213
|
+
### `ITerm2::Browser`
|
|
214
|
+
|
|
215
|
+
Connection-free checks for browser panes. Off macOS, `plugin_installed?`, `profiles_enabled?` and `available?` return `false`.
|
|
216
|
+
When a check cannot tell (a failed or timed-out probe), the two differ on purpose: `plugin_installed?` returns `false`
|
|
217
|
+
(a pane that cannot work should not be attempted) and `profiles_enabled?` returns `true` (the setting ships enabled).
|
|
218
|
+
|
|
219
|
+
- `ITerm2::Browser.plugin_installed?` -- the Browser Plugin is in `/Applications` or `~/Applications`, or Spotlight finds it by bundle identifier
|
|
220
|
+
- `ITerm2::Browser.profiles_enabled?` -- the "Enable browser-style profiles" setting is not turned off (unset counts as enabled)
|
|
221
|
+
- `ITerm2::Browser.available?` -- both of the above
|
|
222
|
+
- `ITerm2::Browser.pane_properties(url)` -- the profile overrides `split_browser_pane` applies; returns a Hash on every platform
|
|
223
|
+
|
|
191
224
|
### `client.close_session(session_id, force: false)` -> true | false
|
|
192
225
|
|
|
193
226
|
Closes a session. With `force: true`, skips the confirmation prompt.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
require "timeout"
|
|
5
|
+
|
|
6
|
+
module ITerm2
|
|
7
|
+
# iTerm2 draws a pane as a web view instead of a shell when its profile's
|
|
8
|
+
# "Custom Command" is the literal string "Browser" -- exactly how iTerm2's own
|
|
9
|
+
# built-in "Web Browser" profile is defined. Overriding that property (plus
|
|
10
|
+
# "Initial URL") on a split produces a browser pane from ANY base profile, so
|
|
11
|
+
# no "Web Browser" profile needs to be installed.
|
|
12
|
+
#
|
|
13
|
+
# iTerm2's own browser feature still needs two things this override cannot
|
|
14
|
+
# bypass: the separately downloaded Browser Plugin, and the "Enable
|
|
15
|
+
# browser-style profiles" Advanced setting (on by default). The probes below
|
|
16
|
+
# check both without opening an API connection.
|
|
17
|
+
module Browser
|
|
18
|
+
PROFILE_PROPERTIES = { "Custom Command" => "Browser" }.freeze
|
|
19
|
+
|
|
20
|
+
# The plugin's own CFBundleIdentifier (iterm2.com/browser-plugin.html).
|
|
21
|
+
PLUGIN_BUNDLE_ID = "com.googlecode.iterm2.iTermBrowserPlugin"
|
|
22
|
+
|
|
23
|
+
# Persisted key of Settings > Advanced > "Enable browser-style profiles".
|
|
24
|
+
# Unset on a machine that never touched it; iTerm2 treats that as enabled.
|
|
25
|
+
PROFILES_PREF_KEY = "browserProfiles"
|
|
26
|
+
|
|
27
|
+
# Where the plugin is normally unzipped. Checked first: a directory test is
|
|
28
|
+
# instant and deterministic, where a cold Spotlight (mdfind) lookup can take
|
|
29
|
+
# seconds and would read as "not installed" if it hit the timeout.
|
|
30
|
+
PLUGIN_PATHS = [
|
|
31
|
+
"/Applications/iTermBrowserPlugin.app",
|
|
32
|
+
File.join(Dir.home, "Applications", "iTermBrowserPlugin.app")
|
|
33
|
+
].freeze
|
|
34
|
+
|
|
35
|
+
PROBE_TIMEOUT = 5
|
|
36
|
+
SPOTLIGHT_TIMEOUT = 15
|
|
37
|
+
|
|
38
|
+
# The profile properties that turn a split into a browser pane showing `url`.
|
|
39
|
+
def self.pane_properties(url)
|
|
40
|
+
PROFILE_PROPERTIES.merge("Initial URL" => url)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# True when the Browser Plugin is installed: in a usual location, or found
|
|
44
|
+
# by bundle identifier wherever it was unzipped. False on any doubt: not
|
|
45
|
+
# macOS, mdfind missing or erroring, timeout.
|
|
46
|
+
def self.plugin_installed?
|
|
47
|
+
return false unless darwin?
|
|
48
|
+
return true if PLUGIN_PATHS.any? { |path| File.directory?(path) }
|
|
49
|
+
|
|
50
|
+
found = capture("mdfind", "kMDItemCFBundleIdentifier == '#{PLUGIN_BUNDLE_ID}'", timeout: SPOTLIGHT_TIMEOUT)
|
|
51
|
+
!found.strip.empty?
|
|
52
|
+
rescue StandardError
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# True unless the user explicitly turned off browser-style profiles. An
|
|
57
|
+
# unset preference reads as enabled. Not macOS: false. Any probe failure:
|
|
58
|
+
# true (the setting ships enabled, so doubt should not block).
|
|
59
|
+
def self.profiles_enabled?
|
|
60
|
+
return false unless darwin?
|
|
61
|
+
|
|
62
|
+
capture("defaults", "read", "com.googlecode.iterm2", PROFILES_PREF_KEY, timeout: PROBE_TIMEOUT).strip != "0"
|
|
63
|
+
rescue StandardError
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Whether a browser pane can be expected to work on this machine.
|
|
68
|
+
def self.available?
|
|
69
|
+
plugin_installed? && profiles_enabled?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.darwin?
|
|
73
|
+
RbConfig::CONFIG["host_os"].match?(/darwin/)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Runs `command` without a shell and returns its stdout. On timeout the
|
|
77
|
+
# child is terminated, not left running, and Timeout::Error is raised.
|
|
78
|
+
def self.capture(*command, timeout:)
|
|
79
|
+
IO.popen(command, err: File::NULL) do |io|
|
|
80
|
+
Timeout.timeout(timeout) { io.read }
|
|
81
|
+
rescue Timeout::Error
|
|
82
|
+
terminate(io.pid)
|
|
83
|
+
raise
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.terminate(pid)
|
|
88
|
+
Process.kill("TERM", pid)
|
|
89
|
+
rescue Errno::ESRCH
|
|
90
|
+
nil # already gone
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private_class_method :darwin?, :capture, :terminate
|
|
94
|
+
end
|
|
95
|
+
end
|
data/lib/iterm2/client.rb
CHANGED
|
@@ -159,6 +159,14 @@ module ITerm2
|
|
|
159
159
|
resp.session_id.first
|
|
160
160
|
end
|
|
161
161
|
|
|
162
|
+
# --- Browser panes ---
|
|
163
|
+
|
|
164
|
+
# Splits `session_id` with a pane that shows `url` as a web view. Returns the
|
|
165
|
+
# new session ID. See ITerm2::Browser for what iTerm2 needs for this to work.
|
|
166
|
+
def split_browser_pane(session_id, url, vertical: true)
|
|
167
|
+
split_pane(session_id, vertical: vertical, profile_customizations: Browser.pane_properties(url))
|
|
168
|
+
end
|
|
169
|
+
|
|
162
170
|
# --- ReorderTabs ---
|
|
163
171
|
|
|
164
172
|
def reorder_tabs(assignments)
|
data/lib/iterm2/version.rb
CHANGED
data/lib/iterm2.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "iterm2/version"
|
|
4
4
|
require_relative "iterm2/proto/api_pb"
|
|
5
5
|
require_relative "iterm2/connection"
|
|
6
|
+
require_relative "iterm2/browser"
|
|
6
7
|
require_relative "iterm2/client"
|
|
7
8
|
require_relative "iterm2/window"
|
|
8
9
|
require_relative "iterm2/tab"
|
|
@@ -19,6 +20,18 @@ module ITerm2
|
|
|
19
20
|
class NotFoundError < RPCError; end
|
|
20
21
|
class SubscriptionError < Error; end
|
|
21
22
|
|
|
23
|
+
# The session ID of the pane the calling process runs in, or nil outside
|
|
24
|
+
# iTerm2. ITERM_SESSION_ID looks like "w0t0p0:<GUID>"; the GUID after the
|
|
25
|
+
# colon is the ID every API call takes. A value with no colon is not that
|
|
26
|
+
# shape, so it returns nil rather than a meaningless string.
|
|
27
|
+
def self.current_session_id(env = ENV)
|
|
28
|
+
raw = env["ITERM_SESSION_ID"]
|
|
29
|
+
return nil unless raw&.include?(":")
|
|
30
|
+
|
|
31
|
+
id = raw.split(":", 2).last
|
|
32
|
+
id.empty? ? nil : id
|
|
33
|
+
end
|
|
34
|
+
|
|
22
35
|
# Convenience: open connection, yield client, close
|
|
23
36
|
def self.connect(app_name: "iterm2_ruby")
|
|
24
37
|
client = Client.new(app_name: app_name)
|
data/llms.txt
CHANGED
|
@@ -64,11 +64,22 @@ States: `:editing`, `:running`, `:at_prompt`, `:unavailable`.
|
|
|
64
64
|
## Other Methods
|
|
65
65
|
|
|
66
66
|
`topology_enriched`, `list_sessions`, `activate_tab`, `activate_window`,
|
|
67
|
-
`raise_by_cwd`, `create_tab`, `split_pane`, `close_session`, `close_tab`,
|
|
67
|
+
`raise_by_cwd`, `create_tab`, `split_pane`, `split_browser_pane`, `close_session`, `close_tab`,
|
|
68
68
|
`set_profile_property`, `get_profile_property`, `list_profiles`, `get_property`,
|
|
69
69
|
`set_property`, `window_frame`, `set_window_frame`, `set_session_grid_size`,
|
|
70
70
|
`get_variables`, `set_variables`, `inject`, `reorder_tabs`, `topology_for_aggregator`.
|
|
71
71
|
|
|
72
|
+
## Browser panes (split a web view beside a session)
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
here = ITerm2.current_session_id # the calling pane's id from ENV["ITERM_SESSION_ID"]; nil outside iTerm2
|
|
76
|
+
if here && ITerm2::Browser.available? # plugin installed AND browser-style profiles enabled (macOS only; no API connection)
|
|
77
|
+
ITerm2.connect { |c| c.split_browser_pane(here, "http://localhost:4579/ambient", vertical: true) } # => new session id
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
No "Web Browser" profile is needed: the method overrides the new pane's profile ("Custom Command" => "Browser", "Initial URL" => url). iTerm2's separately installed Browser Plugin must be present; `ITerm2::Browser.available?` checks that. Always pass a real session id (with nil, iTerm2 picks which pane to split). Callers keep their own platform guard and fallback (e.g. system browser off iTerm2). Never use the Python `iterm2` package.
|
|
82
|
+
|
|
72
83
|
## Notifications
|
|
73
84
|
|
|
74
85
|
```ruby
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: iterm2_ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Forrest Chang
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: google-protobuf
|
|
@@ -43,6 +43,7 @@ files:
|
|
|
43
43
|
- docs/cli.md
|
|
44
44
|
- iterm2_ruby.gemspec
|
|
45
45
|
- lib/iterm2.rb
|
|
46
|
+
- lib/iterm2/browser.rb
|
|
46
47
|
- lib/iterm2/client.rb
|
|
47
48
|
- lib/iterm2/connection.rb
|
|
48
49
|
- lib/iterm2/proto/api_pb.rb
|