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.
- {session_trail-0.2.0/src/session_trail.egg-info → session_trail-0.2.1}/PKG-INFO +2 -2
- {session_trail-0.2.0 → session_trail-0.2.1}/README.en.md +1 -1
- {session_trail-0.2.0 → session_trail-0.2.1}/README.md +1 -1
- {session_trail-0.2.0 → session_trail-0.2.1}/pyproject.toml +1 -1
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/cli.py +1 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/app.py +56 -4
- {session_trail-0.2.0 → session_trail-0.2.1/src/session_trail.egg-info}/PKG-INFO +2 -2
- {session_trail-0.2.0 → session_trail-0.2.1}/LICENSE +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/MANIFEST.in +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/setup.cfg +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/__init__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/__main__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/__init__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/base.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/claude_code.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/codebuddy.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/codex.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/deepseek_harness.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/jsonl_utils.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/kimi_code.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/minimax_code.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/opencode.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/pi.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/sqlite_utils.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/adapters/zcode.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/launcher.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/models.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/scanner.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/server.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/app.js +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/i18n.js +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/index.html +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/static/styles.css +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/store.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/__init__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/desktop.template +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/icon.png +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/tray/sni.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/agent_trail/watcher.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/session_trail.egg-info/SOURCES.txt +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/session_trail.egg-info/dependency_links.txt +0 -0
- {session_trail-0.2.0 → session_trail-0.2.1}/src/session_trail.egg-info/entry_points.txt +0 -0
- {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.
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|