session-trail 0.2.0__tar.gz → 0.2.2__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.2}/PKG-INFO +5 -3
- {session_trail-0.2.0 → session_trail-0.2.2}/README.en.md +4 -2
- {session_trail-0.2.0 → session_trail-0.2.2}/README.md +4 -2
- {session_trail-0.2.0 → session_trail-0.2.2}/pyproject.toml +1 -1
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/cli.py +1 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/tray/app.py +89 -4
- {session_trail-0.2.0 → session_trail-0.2.2/src/session_trail.egg-info}/PKG-INFO +5 -3
- {session_trail-0.2.0 → session_trail-0.2.2}/LICENSE +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/MANIFEST.in +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/setup.cfg +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/__init__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/__main__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/__init__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/base.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/claude_code.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/codebuddy.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/codex.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/deepseek_harness.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/jsonl_utils.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/kimi_code.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/minimax_code.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/opencode.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/pi.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/sqlite_utils.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/adapters/zcode.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/launcher.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/models.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/scanner.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/server.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/static/app.js +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/static/i18n.js +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/static/index.html +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/static/styles.css +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/store.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/tray/__init__.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/tray/desktop.template +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/tray/icon.png +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/tray/sni.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/agent_trail/watcher.py +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/session_trail.egg-info/SOURCES.txt +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/session_trail.egg-info/dependency_links.txt +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/src/session_trail.egg-info/entry_points.txt +0 -0
- {session_trail-0.2.0 → session_trail-0.2.2}/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.2
|
|
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
|
|
@@ -101,9 +101,11 @@ agent-trail serve --interval 30 # 轮询间隔(watch 健康时自动降
|
|
|
101
101
|
```bash
|
|
102
102
|
agent-trail tray # 启动托盘(自动拉起 agent-trail serve)
|
|
103
103
|
agent-trail tray --show # 启动同时打开控制台窗口
|
|
104
|
-
agent-trail tray --install #
|
|
104
|
+
agent-trail tray --install # 手动写入应用菜单入口;--uninstall 反向清理
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
pip 安装后首次运行 `agent-trail tray` 会自动把"Agent 足迹"注册进系统应用菜单(应用启动器可直接搜索到),之后升级换路径也会自动刷新;无桌面会话(SSH/服务器)不写入。
|
|
108
|
+
|
|
107
109
|
托盘壳本身不依赖任何第三方 pip 包,但需要 GTK 的 GI 绑定(系统 Python 通常自带)。`agent-trail tray` 在当前解释器缺 GI 绑定时会自动改用带绑定的系统 Python 重执行(例如 pip 装在 venv 里、托盘跑在系统 Python),无需任何手动操作;实在找不到时按提示安装:
|
|
108
110
|
|
|
109
111
|
```bash
|
|
@@ -111,7 +113,7 @@ agent-trail tray --install # 写入应用菜单入口;--uninstall 反向清
|
|
|
111
113
|
sudo apt install python3-gi gir1.2-webkit2-4.1
|
|
112
114
|
```
|
|
113
115
|
|
|
114
|
-
|
|
116
|
+
`agent-trail tray` 默认脱离终端后台运行:命令立即返回,日志追加在 `~/.agent-trail/tray.log`,关闭终端不影响托盘;`--foreground` 可前台运行调试。托盘以子进程方式管理 `agent-trail serve`——**启动托盘即自动拉起服务**(菜单里可停止/启动,异常退出自动重启)。窗口直接关闭只是隐藏,退出走托盘菜单,或 `kill $(cat ~/.agent-trail/tray.lock)`;flock 单实例锁防止重复启动(重复执行会提示"已在运行")。
|
|
115
117
|
|
|
116
118
|
托盘图标走原生 StatusNotifierItem/DBusMenu 协议(`agent_trail/tray/sni.py`,Gio 实现——libayatana 在 Deepin 23 的托盘守护上不工作,详见源码注释)。内嵌 WebView 默认使用软件合成(`WEBKIT_DISABLE_DMABUF_RENDERER` / `WEBKIT_DISABLE_COMPOSITING_MODE`,可用同名环境变量覆盖),避免在无 DRM 权限的会话里出现空白窗口。`serve` 的所有命令行形态保持不变,无托盘环境(SSH/无桌面)不受影响。
|
|
117
119
|
|
|
@@ -74,9 +74,11 @@ Beyond `serve`, a tray application embeds the console in a native window and add
|
|
|
74
74
|
```bash
|
|
75
75
|
agent-trail tray # start the tray (spawns agent-trail serve)
|
|
76
76
|
agent-trail tray --show # also open the console window
|
|
77
|
-
agent-trail tray --install # add an applications-menu entry; --uninstall reverts
|
|
77
|
+
agent-trail tray --install # manually add an applications-menu entry; --uninstall reverts
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
The first `agent-trail tray` run after pip install automatically registers "Agent Trail" in the desktop applications menu (searchable from your launcher) and refreshes it on upgrades; headless sessions (SSH/servers) write nothing.
|
|
81
|
+
|
|
80
82
|
The tray needs GTK GI bindings (preinstalled on most distros as the system Python). If the interpreter running agent-trail lacks them — e.g. the package lives in a venv — `agent-trail tray` automatically re-executes itself with a system Python that has GI; no manual steps. If none is found, install:
|
|
81
83
|
|
|
82
84
|
```bash
|
|
@@ -84,7 +86,7 @@ The tray needs GTK GI bindings (preinstalled on most distros as the system Pytho
|
|
|
84
86
|
sudo apt install python3-gi gir1.2-webkit2-4.1
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
The tray manages `agent-trail serve` as a subprocess (
|
|
89
|
+
`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
90
|
|
|
89
91
|
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
92
|
|
|
@@ -75,9 +75,11 @@ agent-trail serve --interval 30 # 轮询间隔(watch 健康时自动降
|
|
|
75
75
|
```bash
|
|
76
76
|
agent-trail tray # 启动托盘(自动拉起 agent-trail serve)
|
|
77
77
|
agent-trail tray --show # 启动同时打开控制台窗口
|
|
78
|
-
agent-trail tray --install #
|
|
78
|
+
agent-trail tray --install # 手动写入应用菜单入口;--uninstall 反向清理
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
pip 安装后首次运行 `agent-trail tray` 会自动把"Agent 足迹"注册进系统应用菜单(应用启动器可直接搜索到),之后升级换路径也会自动刷新;无桌面会话(SSH/服务器)不写入。
|
|
82
|
+
|
|
81
83
|
托盘壳本身不依赖任何第三方 pip 包,但需要 GTK 的 GI 绑定(系统 Python 通常自带)。`agent-trail tray` 在当前解释器缺 GI 绑定时会自动改用带绑定的系统 Python 重执行(例如 pip 装在 venv 里、托盘跑在系统 Python),无需任何手动操作;实在找不到时按提示安装:
|
|
82
84
|
|
|
83
85
|
```bash
|
|
@@ -85,7 +87,7 @@ agent-trail tray --install # 写入应用菜单入口;--uninstall 反向清
|
|
|
85
87
|
sudo apt install python3-gi gir1.2-webkit2-4.1
|
|
86
88
|
```
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
`agent-trail tray` 默认脱离终端后台运行:命令立即返回,日志追加在 `~/.agent-trail/tray.log`,关闭终端不影响托盘;`--foreground` 可前台运行调试。托盘以子进程方式管理 `agent-trail serve`——**启动托盘即自动拉起服务**(菜单里可停止/启动,异常退出自动重启)。窗口直接关闭只是隐藏,退出走托盘菜单,或 `kill $(cat ~/.agent-trail/tray.lock)`;flock 单实例锁防止重复启动(重复执行会提示"已在运行")。
|
|
89
91
|
|
|
90
92
|
托盘图标走原生 StatusNotifierItem/DBusMenu 协议(`agent_trail/tray/sni.py`,Gio 实现——libayatana 在 Deepin 23 的托盘守护上不工作,详见源码注释)。内嵌 WebView 默认使用软件合成(`WEBKIT_DISABLE_DMABUF_RENDERER` / `WEBKIT_DISABLE_COMPOSITING_MODE`,可用同名环境变量覆盖),避免在无 DRM 权限的会话里出现空白窗口。`serve` 的所有命令行形态保持不变,无托盘环境(SSH/无桌面)不受影响。
|
|
91
93
|
|
|
@@ -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.2"
|
|
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,
|
|
@@ -287,6 +305,38 @@ class TrailCore:
|
|
|
287
305
|
# -- 桌面入口文件 -----------------------------------------------------------
|
|
288
306
|
|
|
289
307
|
|
|
308
|
+
def _desktop_session_present() -> bool:
|
|
309
|
+
return bool(
|
|
310
|
+
os.environ.get("DISPLAY")
|
|
311
|
+
or os.environ.get("WAYLAND_DISPLAY")
|
|
312
|
+
or os.environ.get("XDG_CURRENT_DESKTOP")
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def ensure_desktop_entry() -> None:
|
|
317
|
+
"""首次运行 tray 时自动注册应用菜单入口,并在路径变化后自愈刷新。
|
|
318
|
+
|
|
319
|
+
pip 的 wheel 安装没有装后钩子,因此采用运行时自注册:仅在有桌面
|
|
320
|
+
会话的环境执行,内容与当前渲染一致时跳过写入。开机自启已开启时
|
|
321
|
+
同步刷新其 Exec 路径,避免包位置变化后自启失效。
|
|
322
|
+
"""
|
|
323
|
+
if not _desktop_session_present():
|
|
324
|
+
return
|
|
325
|
+
executable = shutil.which("agent-trail") or sys.argv[0]
|
|
326
|
+
if not _looks_executable(executable):
|
|
327
|
+
return
|
|
328
|
+
entry = render_desktop_entry(executable, ICON_PATH)
|
|
329
|
+
try:
|
|
330
|
+
if not APP_MENU_PATH.exists() or APP_MENU_PATH.read_text(encoding="utf-8") != entry:
|
|
331
|
+
APP_MENU_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
332
|
+
APP_MENU_PATH.write_text(entry, encoding="utf-8")
|
|
333
|
+
if autostart_enabled():
|
|
334
|
+
if AUTOSTART_PATH.read_text(encoding="utf-8") != entry:
|
|
335
|
+
AUTOSTART_PATH.write_text(entry, encoding="utf-8")
|
|
336
|
+
except OSError:
|
|
337
|
+
pass # 桌面注册失败不影响托盘本身;--install/--uninstall 仍可手动操作
|
|
338
|
+
|
|
339
|
+
|
|
290
340
|
def render_desktop_entry(executable: str, icon: Path,
|
|
291
341
|
template: Path = DESKTOP_TEMPLATE) -> str:
|
|
292
342
|
text = template.read_text(encoding="utf-8")
|
|
@@ -327,6 +377,13 @@ class SingleInstance:
|
|
|
327
377
|
os.write(self._fd, f"{os.getpid()}\n".encode())
|
|
328
378
|
return True
|
|
329
379
|
|
|
380
|
+
def rewrite_pid(self) -> None:
|
|
381
|
+
"""守护化后由最终进程调用:锁文件里写自己的 pid。"""
|
|
382
|
+
if self._fd is not None:
|
|
383
|
+
os.ftruncate(self._fd, 0)
|
|
384
|
+
os.lseek(self._fd, 0, os.SEEK_SET) # ftruncate 不回卷写偏移,必须显式归零
|
|
385
|
+
os.write(self._fd, f"{os.getpid()}\n".encode())
|
|
386
|
+
|
|
330
387
|
def release(self) -> None:
|
|
331
388
|
if self._fd is not None:
|
|
332
389
|
try:
|
|
@@ -497,6 +554,28 @@ def run_gtk(icon_path: Path, show: bool = False) -> int:
|
|
|
497
554
|
# -- 入口 --------------------------------------------------------------------
|
|
498
555
|
|
|
499
556
|
|
|
557
|
+
def _daemonize(log_path: Path) -> bool:
|
|
558
|
+
"""双 fork 脱离终端;返回 False 表示当前在父进程(调用方应立即退出)。
|
|
559
|
+
|
|
560
|
+
守护进程的 stdout/stderr 追加到日志文件,stdin 接到 /dev/null。
|
|
561
|
+
单实例锁的 fd 随 fork 继承,由守护进程继续持有。
|
|
562
|
+
"""
|
|
563
|
+
if os.fork() > 0:
|
|
564
|
+
return False
|
|
565
|
+
os.setsid()
|
|
566
|
+
if os.fork() > 0:
|
|
567
|
+
os._exit(0)
|
|
568
|
+
log_fd = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
|
|
569
|
+
null_fd = os.open(os.devnull, os.O_RDONLY)
|
|
570
|
+
os.dup2(null_fd, 0)
|
|
571
|
+
os.dup2(log_fd, 1)
|
|
572
|
+
os.dup2(log_fd, 2)
|
|
573
|
+
for fd in (log_fd, null_fd):
|
|
574
|
+
if fd > 2:
|
|
575
|
+
os.close(fd)
|
|
576
|
+
return True
|
|
577
|
+
|
|
578
|
+
|
|
500
579
|
def _reexec_with_system_python(argv: list[str], guard: "SingleInstance | None" = None) -> int | None:
|
|
501
580
|
"""当前解释器缺 GI 绑定时,改用带 gi 的系统 Python 重执行自己。
|
|
502
581
|
|
|
@@ -540,6 +619,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
540
619
|
parser.add_argument("--install", action="store_true", help="写入应用菜单入口")
|
|
541
620
|
parser.add_argument("--uninstall", action="store_true", help="移除应用菜单入口与开机自启")
|
|
542
621
|
parser.add_argument("--show", action="store_true", help="启动时同时打开控制台窗口")
|
|
622
|
+
parser.add_argument("--foreground", action="store_true", help="前台运行(默认脱离终端后台运行,日志见 ~/.agent-trail/tray.log)")
|
|
543
623
|
args = parser.parse_args(argv)
|
|
544
624
|
|
|
545
625
|
if args.uninstall:
|
|
@@ -558,10 +638,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
558
638
|
if not ICON_PATH.is_file():
|
|
559
639
|
print(tr("error.no_icon", path=ICON_PATH), file=sys.stderr)
|
|
560
640
|
return 1
|
|
641
|
+
ensure_desktop_entry()
|
|
642
|
+
# 锁在前台获取:重复启动时提示直接出现在用户终端;fd 由守护进程继承
|
|
561
643
|
guard = SingleInstance()
|
|
562
644
|
if not guard.acquire():
|
|
563
645
|
print(tr("error.already_running"), flush=True)
|
|
564
646
|
return 0
|
|
647
|
+
if not args.foreground and not _daemonize(LOG_PATH):
|
|
648
|
+
return 0 # 父进程:立即把终端还给用户;托盘在后台继续
|
|
649
|
+
guard.rewrite_pid() # 锁文件写守护进程的 pid,kill $(cat 锁文件) 才有效
|
|
565
650
|
try:
|
|
566
651
|
return run_gtk(ICON_PATH, show=args.show)
|
|
567
652
|
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.2
|
|
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
|
|
@@ -101,9 +101,11 @@ agent-trail serve --interval 30 # 轮询间隔(watch 健康时自动降
|
|
|
101
101
|
```bash
|
|
102
102
|
agent-trail tray # 启动托盘(自动拉起 agent-trail serve)
|
|
103
103
|
agent-trail tray --show # 启动同时打开控制台窗口
|
|
104
|
-
agent-trail tray --install #
|
|
104
|
+
agent-trail tray --install # 手动写入应用菜单入口;--uninstall 反向清理
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
pip 安装后首次运行 `agent-trail tray` 会自动把"Agent 足迹"注册进系统应用菜单(应用启动器可直接搜索到),之后升级换路径也会自动刷新;无桌面会话(SSH/服务器)不写入。
|
|
108
|
+
|
|
107
109
|
托盘壳本身不依赖任何第三方 pip 包,但需要 GTK 的 GI 绑定(系统 Python 通常自带)。`agent-trail tray` 在当前解释器缺 GI 绑定时会自动改用带绑定的系统 Python 重执行(例如 pip 装在 venv 里、托盘跑在系统 Python),无需任何手动操作;实在找不到时按提示安装:
|
|
108
110
|
|
|
109
111
|
```bash
|
|
@@ -111,7 +113,7 @@ agent-trail tray --install # 写入应用菜单入口;--uninstall 反向清
|
|
|
111
113
|
sudo apt install python3-gi gir1.2-webkit2-4.1
|
|
112
114
|
```
|
|
113
115
|
|
|
114
|
-
|
|
116
|
+
`agent-trail tray` 默认脱离终端后台运行:命令立即返回,日志追加在 `~/.agent-trail/tray.log`,关闭终端不影响托盘;`--foreground` 可前台运行调试。托盘以子进程方式管理 `agent-trail serve`——**启动托盘即自动拉起服务**(菜单里可停止/启动,异常退出自动重启)。窗口直接关闭只是隐藏,退出走托盘菜单,或 `kill $(cat ~/.agent-trail/tray.lock)`;flock 单实例锁防止重复启动(重复执行会提示"已在运行")。
|
|
115
117
|
|
|
116
118
|
托盘图标走原生 StatusNotifierItem/DBusMenu 协议(`agent_trail/tray/sni.py`,Gio 实现——libayatana 在 Deepin 23 的托盘守护上不工作,详见源码注释)。内嵌 WebView 默认使用软件合成(`WEBKIT_DISABLE_DMABUF_RENDERER` / `WEBKIT_DISABLE_COMPOSITING_MODE`,可用同名环境变量覆盖),避免在无 DRM 权限的会话里出现空白窗口。`serve` 的所有命令行形态保持不变,无托盘环境(SSH/无桌面)不受影响。
|
|
117
119
|
|
|
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
|