session-trail 0.2.0__tar.gz → 0.2.1__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.
Files changed (43) hide show
  1. {session_trail-0.2.0/src/session_trail.egg-info → session_trail-0.2.1}/PKG-INFO +2 -2
  2. {session_trail-0.2.0 → session_trail-0.2.1}/README.en.md +1 -1
  3. {session_trail-0.2.0 → session_trail-0.2.1}/README.md +1 -1
  4. {session_trail-0.2.0 → session_trail-0.2.1}/pyproject.toml +1 -1
  5. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/cli.py +1 -0
  6. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/app.py +56 -4
  7. {session_trail-0.2.0 → session_trail-0.2.1/src/session_trail.egg-info}/PKG-INFO +2 -2
  8. {session_trail-0.2.0 → session_trail-0.2.1}/LICENSE +0 -0
  9. {session_trail-0.2.0 → session_trail-0.2.1}/MANIFEST.in +0 -0
  10. {session_trail-0.2.0 → session_trail-0.2.1}/setup.cfg +0 -0
  11. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/__init__.py +0 -0
  12. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/__main__.py +0 -0
  13. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/__init__.py +0 -0
  14. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/base.py +0 -0
  15. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/claude_code.py +0 -0
  16. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/codebuddy.py +0 -0
  17. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/codex.py +0 -0
  18. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/deepseek_harness.py +0 -0
  19. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/jsonl_utils.py +0 -0
  20. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/kimi_code.py +0 -0
  21. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/minimax_code.py +0 -0
  22. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/opencode.py +0 -0
  23. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/pi.py +0 -0
  24. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/sqlite_utils.py +0 -0
  25. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/zcode.py +0 -0
  26. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/launcher.py +0 -0
  27. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/models.py +0 -0
  28. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/scanner.py +0 -0
  29. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/server.py +0 -0
  30. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/app.js +0 -0
  31. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/i18n.js +0 -0
  32. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/index.html +0 -0
  33. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/styles.css +0 -0
  34. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/store.py +0 -0
  35. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/__init__.py +0 -0
  36. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/desktop.template +0 -0
  37. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/icon.png +0 -0
  38. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/sni.py +0 -0
  39. {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/watcher.py +0 -0
  40. {session_trail-0.2.0 → session_trail-0.2.1}/src/session_trail.egg-info/SOURCES.txt +0 -0
  41. {session_trail-0.2.0 → session_trail-0.2.1}/src/session_trail.egg-info/dependency_links.txt +0 -0
  42. {session_trail-0.2.0 → session_trail-0.2.1}/src/session_trail.egg-info/entry_points.txt +0 -0
  43. {session_trail-0.2.0 → session_trail-0.2.1}/src/session_trail.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: session-trail
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Local cross-agent work timeline: discover, browse and resume coding-agent sessions on your machine
5
5
  Author: fwt11
6
6
  License-Expression: MIT
@@ -111,7 +111,7 @@ agent-trail tray --install # 写入应用菜单入口;--uninstall 反向清
111
111
  sudo apt install python3-gi gir1.2-webkit2-4.1
112
112
  ```
113
113
 
114
- 托盘以子进程方式管理 `agent-trail serve`(可用环境变量 `AGENT_TRAIL_BIN` 覆盖可执行文件路径),服务异常退出自动重启(退避重试,连续失败后停在可手动启动的状态)。窗口直接关闭只是隐藏,退出走托盘菜单;flock 单实例锁防止重复启动。
114
+ `agent-trail tray` 默认脱离终端后台运行:命令立即返回,日志追加在 `~/.agent-trail/tray.log`,关闭终端不影响托盘;`--foreground` 可前台运行调试。托盘以子进程方式管理 `agent-trail serve`——**启动托盘即自动拉起服务**(菜单里可停止/启动,异常退出自动重启)。窗口直接关闭只是隐藏,退出走托盘菜单,或 `kill $(cat ~/.agent-trail/tray.lock)`;flock 单实例锁防止重复启动(重复执行会提示"已在运行")。
115
115
 
116
116
  托盘图标走原生 StatusNotifierItem/DBusMenu 协议(`agent_trail/tray/sni.py`,Gio 实现——libayatana 在 Deepin 23 的托盘守护上不工作,详见源码注释)。内嵌 WebView 默认使用软件合成(`WEBKIT_DISABLE_DMABUF_RENDERER` / `WEBKIT_DISABLE_COMPOSITING_MODE`,可用同名环境变量覆盖),避免在无 DRM 权限的会话里出现空白窗口。`serve` 的所有命令行形态保持不变,无托盘环境(SSH/无桌面)不受影响。
117
117
 
@@ -84,7 +84,7 @@ The tray needs GTK GI bindings (preinstalled on most distros as the system Pytho
84
84
  sudo apt install python3-gi gir1.2-webkit2-4.1
85
85
  ```
86
86
 
87
- The tray manages `agent-trail serve` as a subprocess (override with `AGENT_TRAIL_BIN`) and restarts it automatically with backoff if it dies. Closing the window only hides it; quit lives in the tray menu. A flock single-instance lock prevents duplicates.
87
+ `agent-trail tray` detaches from the terminal by default: the command returns immediately, logs append to `~/.agent-trail/tray.log`, and closing the terminal does not affect the tray; use `--foreground` to debug in the foreground. The tray manages `agent-trail serve` as a subprocess — **starting the tray starts the service** (stop/start from the menu; automatic restart with backoff if it dies). Closing the window only hides it; quit lives in the tray menu, or `kill $(cat ~/.agent-trail/tray.lock)`. A flock single-instance lock prevents duplicates (a second invocation prints "already running").
88
88
 
89
89
  The tray icon speaks the native StatusNotifierItem/DBusMenu protocol (`agent_trail/tray/sni.py`, implemented with Gio — libayatana does not work against Deepin 23's tray daemon; see source comments). The embedded WebView defaults to software compositing (`WEBKIT_DISABLE_DMABUF_RENDERER` / `WEBKIT_DISABLE_COMPOSITING_MODE`, overridable via env), avoiding blank windows on sessions without DRM access. All `serve`/CLI forms keep working without a desktop.
90
90
 
@@ -85,7 +85,7 @@ agent-trail tray --install # 写入应用菜单入口;--uninstall 反向清
85
85
  sudo apt install python3-gi gir1.2-webkit2-4.1
86
86
  ```
87
87
 
88
- 托盘以子进程方式管理 `agent-trail serve`(可用环境变量 `AGENT_TRAIL_BIN` 覆盖可执行文件路径),服务异常退出自动重启(退避重试,连续失败后停在可手动启动的状态)。窗口直接关闭只是隐藏,退出走托盘菜单;flock 单实例锁防止重复启动。
88
+ `agent-trail tray` 默认脱离终端后台运行:命令立即返回,日志追加在 `~/.agent-trail/tray.log`,关闭终端不影响托盘;`--foreground` 可前台运行调试。托盘以子进程方式管理 `agent-trail serve`——**启动托盘即自动拉起服务**(菜单里可停止/启动,异常退出自动重启)。窗口直接关闭只是隐藏,退出走托盘菜单,或 `kill $(cat ~/.agent-trail/tray.lock)`;flock 单实例锁防止重复启动(重复执行会提示"已在运行")。
89
89
 
90
90
  托盘图标走原生 StatusNotifierItem/DBusMenu 协议(`agent_trail/tray/sni.py`,Gio 实现——libayatana 在 Deepin 23 的托盘守护上不工作,详见源码注释)。内嵌 WebView 默认使用软件合成(`WEBKIT_DISABLE_DMABUF_RENDERER` / `WEBKIT_DISABLE_COMPOSITING_MODE`,可用同名环境变量覆盖),避免在无 DRM 权限的会话里出现空白窗口。`serve` 的所有命令行形态保持不变,无托盘环境(SSH/无桌面)不受影响。
91
91
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "session-trail"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Local cross-agent work timeline: discover, browse and resume coding-agent sessions on your machine"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -31,6 +31,7 @@ def build_parser() -> argparse.ArgumentParser:
31
31
  serve.add_argument("--no-watch", action="store_true")
32
32
  tray = commands.add_parser("tray", help="system tray app (Linux)")
33
33
  tray.add_argument("--show", action="store_true", help="open the console window at startup")
34
+ tray.add_argument("--foreground", action="store_true", help="run in the foreground (default: detach with logs in ~/.agent-trail/tray.log)")
34
35
  tray.add_argument("--install", action="store_true", help="add an applications-menu entry")
35
36
  tray.add_argument("--uninstall", action="store_true", help="remove the menu entry and autostart")
36
37
  return parser
@@ -96,6 +96,7 @@ def tr(key: str, **params) -> str:
96
96
 
97
97
 
98
98
  LOCK_PATH = Path.home() / ".agent-trail" / "tray.lock"
99
+ LOG_PATH = Path.home() / ".agent-trail" / "tray.log"
99
100
  AUTOSTART_PATH = Path.home() / ".config" / "autostart" / "agent-trail-tray.desktop"
100
101
  APP_MENU_PATH = Path.home() / ".local" / "share" / "applications" / "agent-trail-tray.desktop"
101
102
 
@@ -111,6 +112,11 @@ def emit(event: dict) -> None:
111
112
  print(json.dumps(event, ensure_ascii=False), flush=True)
112
113
 
113
114
 
115
+ def _looks_executable(path: str) -> bool:
116
+ candidate = Path(path)
117
+ return candidate.is_file() and os.access(candidate, os.X_OK)
118
+
119
+
114
120
  class TrailCore:
115
121
  """平台无关的服务子进程管理器;不含任何 GUI 依赖。
116
122
 
@@ -128,9 +134,15 @@ class TrailCore:
128
134
  restart_delay: float = 3.0,
129
135
  stability_seconds: float = 300.0,
130
136
  ) -> None:
137
+ # serve 的拉起命令不依赖 PATH:executable 未显式给出(或给出的路径
138
+ # 无效)时,退到 [sys.executable, -m, agent_trail]——托盘进程自己能
139
+ # 跑,serve 就能跑(重执行场景的 PYTHONPATH 会被子进程继承)。
131
140
  self.executable = executable
132
- if self.executable is None:
133
- self.executable = os.environ.get("AGENT_TRAIL_BIN") or shutil.which("agent-trail")
141
+ if self.executable is not None and not _looks_executable(self.executable):
142
+ self.executable = None
143
+ self._env_bin = os.environ.get("AGENT_TRAIL_BIN")
144
+ if self._env_bin is not None and not _looks_executable(self._env_bin):
145
+ self._env_bin = None
134
146
  self.on_event = on_event or (lambda event: None)
135
147
  self.max_restarts = max_restarts
136
148
  self.restart_delay = restart_delay
@@ -149,7 +161,13 @@ class TrailCore:
149
161
 
150
162
  @property
151
163
  def available(self) -> bool:
152
- return bool(self.executable)
164
+ return True # 至少有 [sys.executable, -m agent_trail] 兜底
165
+
166
+ def _serve_command(self) -> list[str]:
167
+ for candidate in (self.executable, self._env_bin):
168
+ if candidate and _looks_executable(candidate):
169
+ return [candidate, "serve", "--no-browser"]
170
+ return [sys.executable, "-m", "agent_trail", "serve", "--no-browser"]
153
171
 
154
172
  def start(self, *, reset_attempts: bool = True) -> bool:
155
173
  with self._lock:
@@ -166,7 +184,7 @@ class TrailCore:
166
184
  self.url = self.port = self.key = None
167
185
  self._spawned_at = time.monotonic()
168
186
  self.process = subprocess.Popen(
169
- [self.executable, "serve", "--no-browser"],
187
+ self._serve_command(),
170
188
  stdout=subprocess.PIPE,
171
189
  stderr=subprocess.STDOUT,
172
190
  text=True,
@@ -327,6 +345,13 @@ class SingleInstance:
327
345
  os.write(self._fd, f"{os.getpid()}\n".encode())
328
346
  return True
329
347
 
348
+ def rewrite_pid(self) -> None:
349
+ """守护化后由最终进程调用:锁文件里写自己的 pid。"""
350
+ if self._fd is not None:
351
+ os.ftruncate(self._fd, 0)
352
+ os.lseek(self._fd, 0, os.SEEK_SET) # ftruncate 不回卷写偏移,必须显式归零
353
+ os.write(self._fd, f"{os.getpid()}\n".encode())
354
+
330
355
  def release(self) -> None:
331
356
  if self._fd is not None:
332
357
  try:
@@ -497,6 +522,28 @@ def run_gtk(icon_path: Path, show: bool = False) -> int:
497
522
  # -- 入口 --------------------------------------------------------------------
498
523
 
499
524
 
525
+ def _daemonize(log_path: Path) -> bool:
526
+ """双 fork 脱离终端;返回 False 表示当前在父进程(调用方应立即退出)。
527
+
528
+ 守护进程的 stdout/stderr 追加到日志文件,stdin 接到 /dev/null。
529
+ 单实例锁的 fd 随 fork 继承,由守护进程继续持有。
530
+ """
531
+ if os.fork() > 0:
532
+ return False
533
+ os.setsid()
534
+ if os.fork() > 0:
535
+ os._exit(0)
536
+ log_fd = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
537
+ null_fd = os.open(os.devnull, os.O_RDONLY)
538
+ os.dup2(null_fd, 0)
539
+ os.dup2(log_fd, 1)
540
+ os.dup2(log_fd, 2)
541
+ for fd in (log_fd, null_fd):
542
+ if fd > 2:
543
+ os.close(fd)
544
+ return True
545
+
546
+
500
547
  def _reexec_with_system_python(argv: list[str], guard: "SingleInstance | None" = None) -> int | None:
501
548
  """当前解释器缺 GI 绑定时,改用带 gi 的系统 Python 重执行自己。
502
549
 
@@ -540,6 +587,7 @@ def main(argv: list[str] | None = None) -> int:
540
587
  parser.add_argument("--install", action="store_true", help="写入应用菜单入口")
541
588
  parser.add_argument("--uninstall", action="store_true", help="移除应用菜单入口与开机自启")
542
589
  parser.add_argument("--show", action="store_true", help="启动时同时打开控制台窗口")
590
+ parser.add_argument("--foreground", action="store_true", help="前台运行(默认脱离终端后台运行,日志见 ~/.agent-trail/tray.log)")
543
591
  args = parser.parse_args(argv)
544
592
 
545
593
  if args.uninstall:
@@ -558,10 +606,14 @@ def main(argv: list[str] | None = None) -> int:
558
606
  if not ICON_PATH.is_file():
559
607
  print(tr("error.no_icon", path=ICON_PATH), file=sys.stderr)
560
608
  return 1
609
+ # 锁在前台获取:重复启动时提示直接出现在用户终端;fd 由守护进程继承
561
610
  guard = SingleInstance()
562
611
  if not guard.acquire():
563
612
  print(tr("error.already_running"), flush=True)
564
613
  return 0
614
+ if not args.foreground and not _daemonize(LOG_PATH):
615
+ return 0 # 父进程:立即把终端还给用户;托盘在后台继续
616
+ guard.rewrite_pid() # 锁文件写守护进程的 pid,kill $(cat 锁文件) 才有效
565
617
  try:
566
618
  return run_gtk(ICON_PATH, show=args.show)
567
619
  except (ImportError, ValueError) as error:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: session-trail
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Local cross-agent work timeline: discover, browse and resume coding-agent sessions on your machine
5
5
  Author: fwt11
6
6
  License-Expression: MIT
@@ -111,7 +111,7 @@ agent-trail tray --install # 写入应用菜单入口;--uninstall 反向清
111
111
  sudo apt install python3-gi gir1.2-webkit2-4.1
112
112
  ```
113
113
 
114
- 托盘以子进程方式管理 `agent-trail serve`(可用环境变量 `AGENT_TRAIL_BIN` 覆盖可执行文件路径),服务异常退出自动重启(退避重试,连续失败后停在可手动启动的状态)。窗口直接关闭只是隐藏,退出走托盘菜单;flock 单实例锁防止重复启动。
114
+ `agent-trail tray` 默认脱离终端后台运行:命令立即返回,日志追加在 `~/.agent-trail/tray.log`,关闭终端不影响托盘;`--foreground` 可前台运行调试。托盘以子进程方式管理 `agent-trail serve`——**启动托盘即自动拉起服务**(菜单里可停止/启动,异常退出自动重启)。窗口直接关闭只是隐藏,退出走托盘菜单,或 `kill $(cat ~/.agent-trail/tray.lock)`;flock 单实例锁防止重复启动(重复执行会提示"已在运行")。
115
115
 
116
116
  托盘图标走原生 StatusNotifierItem/DBusMenu 协议(`agent_trail/tray/sni.py`,Gio 实现——libayatana 在 Deepin 23 的托盘守护上不工作,详见源码注释)。内嵌 WebView 默认使用软件合成(`WEBKIT_DISABLE_DMABUF_RENDERER` / `WEBKIT_DISABLE_COMPOSITING_MODE`,可用同名环境变量覆盖),避免在无 DRM 权限的会话里出现空白窗口。`serve` 的所有命令行形态保持不变,无托盘环境(SSH/无桌面)不受影响。
117
117
 
File without changes
File without changes
File without changes