it2 0.2.2__tar.gz → 0.2.3__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: it2
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A powerful command-line interface for controlling iTerm2
5
5
  Project-URL: Homepage, https://github.com/mkusaka/it2
6
6
  Project-URL: Bug Tracker, https://github.com/mkusaka/it2/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "it2"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "A powerful command-line interface for controlling iTerm2"
9
9
  readme = "README.md"
10
10
  license = {file = "LICENSE"}
@@ -1,3 +1,3 @@
1
1
  """iTerm2 CLI - A powerful command-line interface for controlling iTerm2."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.2.3"
@@ -5,6 +5,7 @@ import iterm2
5
5
 
6
6
  from ..core.connection import run_command
7
7
  from ..core.errors import handle_error
8
+ from ..core.session_handler import get_session_by_id
8
9
 
9
10
  # Theme value mapping for PreferenceKey.THEME
10
11
  _THEME_MAP = {
@@ -105,7 +106,7 @@ async def broadcast_add(
105
106
  # Verify all sessions exist
106
107
  sessions = []
107
108
  for sid in session_ids:
108
- session = app.get_session_by_id(sid)
109
+ session = get_session_by_id(app, sid)
109
110
  if not session:
110
111
  handle_error(f"Session '{sid}' not found", 3)
111
112
  sessions.append(session)
@@ -10,6 +10,7 @@ from rich.table import Table
10
10
 
11
11
  from ..core.connection import run_command
12
12
  from ..core.errors import handle_error
13
+ from ..core.session_handler import get_session_by_id
13
14
 
14
15
  console = Console()
15
16
 
@@ -142,7 +143,7 @@ async def apply(
142
143
 
143
144
  # Get target session
144
145
  if session:
145
- target_session = app.get_session_by_id(session)
146
+ target_session = get_session_by_id(app, session)
146
147
  if not target_session:
147
148
  handle_error(f"Session '{session}' not found", 3)
148
149
  else:
@@ -9,7 +9,7 @@ from rich.table import Table
9
9
 
10
10
  from ..core.connection import run_command
11
11
  from ..core.errors import handle_error
12
- from ..core.session_handler import get_session_info, get_target_sessions
12
+ from ..core.session_handler import get_session_by_id, get_session_info, get_target_sessions
13
13
 
14
14
  console = Console()
15
15
 
@@ -157,7 +157,7 @@ async def restart(session: str | None, connection: iterm2.Connection, app: iterm
157
157
  @run_command
158
158
  async def focus(session_id: str, connection: iterm2.Connection, app: iterm2.App) -> None:
159
159
  """Focus a specific session."""
160
- target_session = app.get_session_by_id(session_id)
160
+ target_session = get_session_by_id(app, session_id)
161
161
  if not target_session:
162
162
  handle_error(f"Session '{session_id}' not found", 3)
163
163
 
@@ -1,9 +1,74 @@
1
1
  """Session handling utilities for iTerm2 CLI."""
2
2
 
3
+ import re
3
4
  import sys
4
5
 
5
6
  from iterm2 import App, Session
6
7
 
8
+ _UUID_PATTERN = r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}"
9
+ # iTerm2 commonly exposes these aliases:
10
+ # - ITERM_SESSION_ID / TERM_SESSION_ID: w<window>t<tab>p<pane>:<GUID>
11
+ # - session.termid variable: w<window>t<tab>p<pane>.<GUID>
12
+ # Only the GUID portion is the canonical API session identifier.
13
+ _ITERM_SESSION_ID_RE = re.compile(rf"^w\d+t\d+p\d+:(?P<uuid>{_UUID_PATTERN})$")
14
+ _TERMID_RE = re.compile(rf"^w\d+t\d+p\d+\.(?P<uuid>{_UUID_PATTERN})$")
15
+
16
+
17
+ def normalize_session_id(session_id: str | None) -> str | None:
18
+ """Normalize session ID aliases to a canonical UUID when possible.
19
+
20
+ Why this exists:
21
+ iTerm2 core builds ``ITERM_SESSION_ID`` in the form
22
+ ``w<window>t<tab>p<pane>:<guid>`` (see PTYSession.sessionId), and also
23
+ publishes ``session.termid`` as ``w<window>t<tab>p<pane>.<guid>``.
24
+ However, iTerm2's Python API stores ``Session.session_id`` as only the
25
+ GUID and ``App.get_session_by_id`` does an exact string match against that
26
+ GUID. Passing prefixed aliases directly therefore fails lookup.
27
+
28
+ When this is needed:
29
+ Any time we accept user-provided session identifiers (CLI args, env vars,
30
+ script output), because those inputs often come from shell integration
31
+ variables instead of the bare API GUID.
32
+ """
33
+ if session_id is None:
34
+ return None
35
+
36
+ if session_id in {"active", "all"}:
37
+ return session_id
38
+
39
+ match = _ITERM_SESSION_ID_RE.match(session_id)
40
+ if match:
41
+ return match.group("uuid")
42
+
43
+ match = _TERMID_RE.match(session_id)
44
+ if match:
45
+ return match.group("uuid")
46
+
47
+ return session_id
48
+
49
+
50
+ def get_session_by_id(app: App, session_id: str | None) -> Session | None:
51
+ """Get a session by ID while accepting common iTerm2 alias formats.
52
+
53
+ This centralizes the ID-shape mismatch between shell-facing identifiers and
54
+ API-facing identifiers so command handlers can call one safe lookup path.
55
+ """
56
+ if session_id is None:
57
+ return None
58
+
59
+ normalized = normalize_session_id(session_id)
60
+ if normalized is None:
61
+ return None
62
+
63
+ session = app.get_session_by_id(normalized)
64
+ if session:
65
+ return session
66
+
67
+ # Fallback for unexpected future formats where normalization is too strict.
68
+ if normalized != session_id:
69
+ return app.get_session_by_id(session_id)
70
+ return None
71
+
7
72
 
8
73
  async def get_target_sessions(
9
74
  app: App, session_id: str | None = None, all_sessions: bool = False
@@ -28,7 +93,7 @@ async def get_target_sessions(
28
93
 
29
94
  if session_id and session_id != "active":
30
95
  # Get specific session by ID
31
- session = app.get_session_by_id(session_id)
96
+ session = get_session_by_id(app, session_id)
32
97
  if not session:
33
98
  print(f"Error: Session '{session_id}' not found", file=sys.stderr)
34
99
  sys.exit(3)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes