compartment 3.4__tar.gz → 3.5__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.
- {compartment-3.4 → compartment-3.5}/PKG-INFO +23 -21
- {compartment-3.4 → compartment-3.5}/README.md +22 -20
- {compartment-3.4 → compartment-3.5}/pyproject.toml +8 -8
- {compartment-3.4 → compartment-3.5}/src/compartment/__init__.py +1 -1
- {compartment-3.4 → compartment-3.5}/src/compartment/cli.py +108 -37
- compartment-3.5/src/compartment/data/app.icns +0 -0
- compartment-3.5/src/compartment/data/app.png +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/menubar.py +321 -13
- {compartment-3.4 → compartment-3.5}/src/compartment/systray.py +238 -52
- {compartment-3.4 → compartment-3.5}/src/compartment.egg-info/PKG-INFO +23 -21
- {compartment-3.4 → compartment-3.5}/src/compartment.egg-info/SOURCES.txt +6 -0
- {compartment-3.4 → compartment-3.5}/tests/test_connect_buttons.py +7 -3
- compartment-3.5/tests/test_connect_environment.py +256 -0
- compartment-3.5/tests/test_linux_panel.py +229 -0
- compartment-3.5/tests/test_login_item_identity.py +145 -0
- {compartment-3.4 → compartment-3.5}/tests/test_menubar.py +2 -1
- compartment-3.5/tests/test_single_instance.py +152 -0
- {compartment-3.4 → compartment-3.5}/tests/test_status_bar_install.py +4 -3
- {compartment-3.4 → compartment-3.5}/tests/test_systray.py +8 -4
- {compartment-3.4 → compartment-3.5}/setup.cfg +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/acl.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/audit.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/bench.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/claude_hooks.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/claude_memory.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/crypto.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/dash.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/data/hermes-plugin/__init__.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/data/hermes-plugin/plugin.yaml +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/data/menubar.png +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/data/menubar@2x.png +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/data/starter.mpack +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/data/tray.ico +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/embed.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/home.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/longmemeval.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/models/bge-small-en-v1.5-int8/model_quantized.onnx +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/models/bge-small-en-v1.5-int8/tokenizer.json +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/offline_guard.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/packs.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/platforms.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/salience.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/selftest.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/server.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/session.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/store.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/vault.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/vaultfile.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/vindex.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment/wire.py +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment.egg-info/dependency_links.txt +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment.egg-info/entry_points.txt +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment.egg-info/requires.txt +0 -0
- {compartment-3.4 → compartment-3.5}/src/compartment.egg-info/top_level.txt +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_acl_audit_packs.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_acl_precedence.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_attempt_pacing.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_audit_chain.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_claude_hooks_and_integrate.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_claude_memory_and_recent.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_crypto.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_dash.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_instructions.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_journal_framing.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_macos_bundle.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_offline_guard_paths.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_offline_selftest_bench.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_ondisk_format.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_pack_trust.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_platforms.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_relations.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_salience.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_session_entropy.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_session_lock.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_starter_data_quality.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_starter_packs.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_starter_visibility.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_tamper.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_twofa.py +0 -0
- {compartment-3.4 → compartment-3.5}/tests/test_vault_ops.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: compartment
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.5
|
|
4
4
|
Summary: Compartment - high-security, fully offline, encrypted vector memory for AI agents (MCP)
|
|
5
5
|
Project-URL: Homepage, https://github.com/MaxFreedomPollard/Compartment
|
|
6
6
|
Project-URL: Source, https://github.com/MaxFreedomPollard/Compartment
|
|
@@ -96,8 +96,9 @@ configuration step.
|
|
|
96
96
|
key, no cloud account, no daemon.
|
|
97
97
|
- On the Mac, open one `.pkg` and you are done. Python, the embedding model
|
|
98
98
|
and every dependency are inside it.
|
|
99
|
-
-
|
|
100
|
-
unlock, lock, and the last five memories it saved.
|
|
99
|
+
- An app runs the whole thing without a terminal on all three systems: vault
|
|
100
|
+
state, unlock, lock, and the last five memories it saved. The macOS menu
|
|
101
|
+
bar, the Windows notification area, a window on Linux.
|
|
101
102
|
- Every feature toggles in that panel instead of a config file:
|
|
102
103
|
model-independent capture, starter facts in search, auto-lock.
|
|
103
104
|
- Your vault ships full. The 6,718 seeded facts are ordinary memories,
|
|
@@ -196,28 +197,29 @@ backed up first), it exits successfully no matter what - a memory tool must
|
|
|
196
197
|
never break your editor - and it stays quiet when the vault is locked.
|
|
197
198
|
`compartment hook status | install | uninstall`, or `integrate claude --no-hooks`.
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
alt="The Compartment panel in the Windows notification area.">
|
|
204
|
-
</p>
|
|
205
|
-
|
|
206
|
-
**A menu bar and system tray app, from the same one install.** `pip install
|
|
207
|
-
compartment && compartment init` sets it up on macOS and on Windows: the app
|
|
208
|
-
starts straight away and comes back at every login. There is no separate
|
|
209
|
-
package, no extra to remember and no second command. On the Mac you can
|
|
210
|
-
instead open **Compartment.pkg** from the
|
|
200
|
+
**An app on all three systems, from the same one install.** `pip install
|
|
201
|
+
compartment && compartment init` sets it up on macOS, Windows and Linux.
|
|
202
|
+
There is no separate package, no extra to remember and no second command. On
|
|
203
|
+
the Mac you can instead open **Compartment.pkg** from the
|
|
211
204
|
[latest release](https://github.com/MaxFreedomPollard/Compartment/releases/latest),
|
|
212
205
|
which carries Python and every dependency inside it, so there is nothing to
|
|
213
206
|
install first. `compartment init --no-app` skips the app for headless boxes
|
|
214
|
-
and CI.
|
|
215
|
-
|
|
207
|
+
and CI.
|
|
208
|
+
|
|
209
|
+
The same panel, in the place each system keeps things like this: the **menu
|
|
210
|
+
bar** on macOS, the **notification area** on Windows, and on **Linux** an
|
|
211
|
+
ordinary window, with Compartment in your applications menu. Linux gets a
|
|
212
|
+
window rather than an icon deliberately. Whether a tray icon appears there
|
|
213
|
+
depends on the desktop, and on GNOME or Wayland it can simply never show up
|
|
214
|
+
with nothing said, which is the worst way for the control that unlocks your
|
|
215
|
+
memories to fail.
|
|
216
|
+
|
|
217
|
+
The panel shows whether the vault is open, how much it has learned, the three
|
|
216
218
|
settings worth changing day to day (capture hook, whether starter facts join
|
|
217
|
-
searches, auto-lock),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
CLI, so an idle app costs nothing.
|
|
219
|
+
searches, auto-lock), which agents are connected and buttons to connect them,
|
|
220
|
+
and the last five things it remembered. Unlock, lock and change your
|
|
221
|
+
passphrase there too, without opening a terminal. It holds no vault in
|
|
222
|
+
memory - state comes from the CLI, so an idle app costs nothing.
|
|
221
223
|
|
|
222
224
|
**See what it just learned.** `compartment recent` lists the newest memories,
|
|
223
225
|
newest last, hiding the thousands of seeded starting facts so the handful
|
|
@@ -50,8 +50,9 @@ configuration step.
|
|
|
50
50
|
key, no cloud account, no daemon.
|
|
51
51
|
- On the Mac, open one `.pkg` and you are done. Python, the embedding model
|
|
52
52
|
and every dependency are inside it.
|
|
53
|
-
-
|
|
54
|
-
unlock, lock, and the last five memories it saved.
|
|
53
|
+
- An app runs the whole thing without a terminal on all three systems: vault
|
|
54
|
+
state, unlock, lock, and the last five memories it saved. The macOS menu
|
|
55
|
+
bar, the Windows notification area, a window on Linux.
|
|
55
56
|
- Every feature toggles in that panel instead of a config file:
|
|
56
57
|
model-independent capture, starter facts in search, auto-lock.
|
|
57
58
|
- Your vault ships full. The 6,718 seeded facts are ordinary memories,
|
|
@@ -150,28 +151,29 @@ backed up first), it exits successfully no matter what - a memory tool must
|
|
|
150
151
|
never break your editor - and it stays quiet when the vault is locked.
|
|
151
152
|
`compartment hook status | install | uninstall`, or `integrate claude --no-hooks`.
|
|
152
153
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
alt="The Compartment panel in the Windows notification area.">
|
|
158
|
-
</p>
|
|
159
|
-
|
|
160
|
-
**A menu bar and system tray app, from the same one install.** `pip install
|
|
161
|
-
compartment && compartment init` sets it up on macOS and on Windows: the app
|
|
162
|
-
starts straight away and comes back at every login. There is no separate
|
|
163
|
-
package, no extra to remember and no second command. On the Mac you can
|
|
164
|
-
instead open **Compartment.pkg** from the
|
|
154
|
+
**An app on all three systems, from the same one install.** `pip install
|
|
155
|
+
compartment && compartment init` sets it up on macOS, Windows and Linux.
|
|
156
|
+
There is no separate package, no extra to remember and no second command. On
|
|
157
|
+
the Mac you can instead open **Compartment.pkg** from the
|
|
165
158
|
[latest release](https://github.com/MaxFreedomPollard/Compartment/releases/latest),
|
|
166
159
|
which carries Python and every dependency inside it, so there is nothing to
|
|
167
160
|
install first. `compartment init --no-app` skips the app for headless boxes
|
|
168
|
-
and CI.
|
|
169
|
-
|
|
161
|
+
and CI.
|
|
162
|
+
|
|
163
|
+
The same panel, in the place each system keeps things like this: the **menu
|
|
164
|
+
bar** on macOS, the **notification area** on Windows, and on **Linux** an
|
|
165
|
+
ordinary window, with Compartment in your applications menu. Linux gets a
|
|
166
|
+
window rather than an icon deliberately. Whether a tray icon appears there
|
|
167
|
+
depends on the desktop, and on GNOME or Wayland it can simply never show up
|
|
168
|
+
with nothing said, which is the worst way for the control that unlocks your
|
|
169
|
+
memories to fail.
|
|
170
|
+
|
|
171
|
+
The panel shows whether the vault is open, how much it has learned, the three
|
|
170
172
|
settings worth changing day to day (capture hook, whether starter facts join
|
|
171
|
-
searches, auto-lock),
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
CLI, so an idle app costs nothing.
|
|
173
|
+
searches, auto-lock), which agents are connected and buttons to connect them,
|
|
174
|
+
and the last five things it remembered. Unlock, lock and change your
|
|
175
|
+
passphrase there too, without opening a terminal. It holds no vault in
|
|
176
|
+
memory - state comes from the CLI, so an idle app costs nothing.
|
|
175
177
|
|
|
176
178
|
**See what it just learned.** `compartment recent` lists the newest memories,
|
|
177
179
|
newest last, hiding the thousands of seeded starting facts so the handful
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "compartment"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.5"
|
|
8
8
|
description = "Compartment - high-security, fully offline, encrypted vector memory for AI agents (MCP)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -36,13 +36,13 @@ dependencies = [
|
|
|
36
36
|
"tokenizers>=0.15",
|
|
37
37
|
"numpy>=1.26",
|
|
38
38
|
"mcp>=1.0",
|
|
39
|
-
# The
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
39
|
+
# The app ships with every install. It is not an add-on: unlocking,
|
|
40
|
+
# locking and changing the passphrase are things people do through a
|
|
41
|
+
# panel, not through a terminal, so an install that only lays down a CLI
|
|
42
|
+
# is an incomplete install. The markers keep it honest - nothing
|
|
43
|
+
# macOS-only is fetched on Windows and nothing Windows-only on macOS.
|
|
44
|
+
# Linux needs neither: its panel is Tk, which is in the standard library,
|
|
45
|
+
# so nothing here is fetched and nothing is compiled there.
|
|
46
46
|
"pyobjc-framework-Cocoa>=10.0; sys_platform == 'darwin'",
|
|
47
47
|
"pystray>=0.19; sys_platform == 'win32'",
|
|
48
48
|
"pillow>=10; sys_platform == 'win32'",
|
|
@@ -167,10 +167,10 @@ def cmd_init(args) -> None:
|
|
|
167
167
|
st = v.status()
|
|
168
168
|
v.save()
|
|
169
169
|
if args.no_app:
|
|
170
|
-
print(" command-line only install (--no-app): no
|
|
170
|
+
print(" command-line only install (--no-app): no app")
|
|
171
171
|
elif _cli_only_requested():
|
|
172
|
-
print(" command-line only install: no
|
|
173
|
-
print(" add it later with `compartment
|
|
172
|
+
print(" command-line only install: no app")
|
|
173
|
+
print(" add it later with `compartment panel --login on`")
|
|
174
174
|
else:
|
|
175
175
|
_start_status_bar_app(path)
|
|
176
176
|
print(f"\nVault ready: {st['records']} records, projected RAM "
|
|
@@ -219,6 +219,32 @@ def _cli_only_requested(seconds: float = 5.0) -> bool:
|
|
|
219
219
|
return False # never let this block an install
|
|
220
220
|
|
|
221
221
|
|
|
222
|
+
def _linux_has_display() -> bool:
|
|
223
|
+
"""Is there a desktop to draw on?
|
|
224
|
+
|
|
225
|
+
Most Linux installs of a memory server are headless: a box over SSH, a
|
|
226
|
+
container, a CI runner. Starting a window there fails, and an
|
|
227
|
+
applications menu entry on a machine with no applications menu is
|
|
228
|
+
litter. Neither is a broken install - it is a server, and the CLI and
|
|
229
|
+
the MCP server are the whole product on one."""
|
|
230
|
+
return bool(os.environ.get("DISPLAY")
|
|
231
|
+
or os.environ.get("WAYLAND_DISPLAY"))
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _linux_gui_available() -> bool:
|
|
235
|
+
"""Can this Python draw the panel?
|
|
236
|
+
|
|
237
|
+
tkinter is in the standard library but not in every Linux distribution's
|
|
238
|
+
Python package, and pip cannot install it. Checked here so an install
|
|
239
|
+
says so plainly, rather than a launcher entry that opens nothing.
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
import tkinter # noqa: F401
|
|
243
|
+
return True
|
|
244
|
+
except Exception: # noqa: BLE001
|
|
245
|
+
return False
|
|
246
|
+
|
|
247
|
+
|
|
222
248
|
def _start_status_bar_app(vault: str) -> None:
|
|
223
249
|
"""Put the status bar app up now, and again at every login.
|
|
224
250
|
|
|
@@ -229,18 +255,36 @@ def _start_status_bar_app(vault: str) -> None:
|
|
|
229
255
|
platform with no status bar still gets a perfectly good CLI and MCP
|
|
230
256
|
server, so a failure here is reported and stepped over.
|
|
231
257
|
"""
|
|
232
|
-
if sys.platform not in ("darwin", "win32"):
|
|
233
|
-
return # no status bar app here
|
|
234
258
|
try:
|
|
235
259
|
app = _tray_app()
|
|
236
260
|
except Exception as exc: # noqa: BLE001
|
|
237
|
-
print(f"
|
|
261
|
+
print(f" app unavailable ({exc}); the CLI is unaffected")
|
|
262
|
+
return
|
|
263
|
+
linux = sys.platform not in ("darwin", "win32")
|
|
264
|
+
if linux and not _linux_has_display():
|
|
265
|
+
print(" headless machine (no DISPLAY): no panel and no menu entry.")
|
|
266
|
+
print(" The CLI and the MCP server are ready, and `compartment "
|
|
267
|
+
"dash` opens the vault in a browser.")
|
|
238
268
|
return
|
|
239
269
|
try:
|
|
240
270
|
state = app.set_login(True)
|
|
241
|
-
|
|
271
|
+
# On Linux this is the applications menu entry. Nothing starts at
|
|
272
|
+
# login there: without a tray icon to sit quietly in, an app that
|
|
273
|
+
# started itself would just be a window in your face at every
|
|
274
|
+
# sign-in.
|
|
275
|
+
print(f" {'applications menu entry' if linux else 'start at login'}"
|
|
276
|
+
f": {state}")
|
|
242
277
|
except Exception as exc: # noqa: BLE001
|
|
243
|
-
print(f" could not
|
|
278
|
+
print(f" could not register the app ({exc})")
|
|
279
|
+
if linux and not _linux_gui_available():
|
|
280
|
+
print(" the panel needs tkinter, which this Python does not have.")
|
|
281
|
+
print(" Quickest fix, a Python that includes it:")
|
|
282
|
+
print(" uv tool install compartment")
|
|
283
|
+
print(" or install your distribution's package (Debian/Ubuntu: "
|
|
284
|
+
"python3-tk, Fedora: python3-tkinter).")
|
|
285
|
+
print(" Everything else works now: the CLI, the MCP server, and "
|
|
286
|
+
"`compartment dash`.")
|
|
287
|
+
return
|
|
244
288
|
try:
|
|
245
289
|
exe = shutil.which("compartment")
|
|
246
290
|
argv = ([exe, "--vault", vault, "menubar"] if exe else
|
|
@@ -253,12 +297,17 @@ def _start_status_bar_app(vault: str) -> None:
|
|
|
253
297
|
else:
|
|
254
298
|
kwargs["start_new_session"] = True # outlive this shell
|
|
255
299
|
subprocess.Popen(argv, **kwargs)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
300
|
+
if sys.platform == "darwin":
|
|
301
|
+
print(" app started - look for the icon in your menu bar")
|
|
302
|
+
elif sys.platform == "win32":
|
|
303
|
+
print(" app started - look for the icon in your notification "
|
|
304
|
+
"area")
|
|
305
|
+
else:
|
|
306
|
+
print(" app started - its window is open, and Compartment is "
|
|
307
|
+
"in your applications menu")
|
|
259
308
|
except Exception as exc: # noqa: BLE001
|
|
260
|
-
print(f" could not start the
|
|
261
|
-
"`compartment
|
|
309
|
+
print(f" could not start the app ({exc}); run "
|
|
310
|
+
"`compartment panel` yourself")
|
|
262
311
|
|
|
263
312
|
|
|
264
313
|
def _install_kind() -> tuple[str, list[str]]:
|
|
@@ -314,17 +363,29 @@ def _restart_status_bar_app(vault: str) -> None:
|
|
|
314
363
|
def cmd_uninstall(args) -> None:
|
|
315
364
|
"""Remove Compartment from this machine. The vault is KEPT unless asked."""
|
|
316
365
|
print(f"Compartment {__version__} - uninstalling")
|
|
317
|
-
|
|
366
|
+
try:
|
|
367
|
+
app = _tray_app()
|
|
318
368
|
try:
|
|
319
|
-
app
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
369
|
+
app.quit_running()
|
|
370
|
+
print(" app stopped")
|
|
371
|
+
except Exception: # noqa: BLE001
|
|
372
|
+
pass
|
|
373
|
+
linux = sys.platform not in ("darwin", "win32")
|
|
374
|
+
print(f" {'applications menu entry' if linux else 'start at login'}"
|
|
375
|
+
f": {app.set_login(False)}")
|
|
376
|
+
except Exception as exc: # noqa: BLE001
|
|
377
|
+
print(f" app: nothing to remove ({exc})")
|
|
378
|
+
if sys.platform == "darwin":
|
|
379
|
+
# Only the small bundle a pip install writes for its own login item.
|
|
380
|
+
# A .pkg install is left alone: that one was not ours to create and
|
|
381
|
+
# its receipt is what removes it.
|
|
382
|
+
try:
|
|
383
|
+
from .menubar import USER_APP_BUNDLE, _is_generated
|
|
384
|
+
if _is_generated(USER_APP_BUNDLE):
|
|
385
|
+
shutil.rmtree(USER_APP_BUNDLE, ignore_errors=True)
|
|
386
|
+
print(f" removed {USER_APP_BUNDLE}")
|
|
326
387
|
except Exception as exc: # noqa: BLE001
|
|
327
|
-
print(f"
|
|
388
|
+
print(f" login bundle: {exc}")
|
|
328
389
|
try:
|
|
329
390
|
if claude_hooks.is_installed():
|
|
330
391
|
claude_hooks.uninstall()
|
|
@@ -486,14 +547,20 @@ def cmd_recent(args) -> None:
|
|
|
486
547
|
|
|
487
548
|
|
|
488
549
|
def _tray_app():
|
|
489
|
-
"""The platform's front end: the macOS menu bar item,
|
|
490
|
-
icon
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
550
|
+
"""The platform's front end: the macOS menu bar item, the Windows tray
|
|
551
|
+
icon, or on Linux the same panel as an ordinary window. All three are
|
|
552
|
+
thin shells over the same data layer in `menubar`, so the command is the
|
|
553
|
+
same everywhere and only the widgets differ.
|
|
554
|
+
|
|
555
|
+
Linux gets a window rather than an icon on purpose. Whether a tray icon
|
|
556
|
+
appears there depends on the desktop, and on GNOME or Wayland it can
|
|
557
|
+
simply never show up with nothing said - which is the worst way for the
|
|
558
|
+
control that unlocks your memories to fail."""
|
|
559
|
+
if sys.platform == "darwin":
|
|
560
|
+
from . import menubar
|
|
561
|
+
return menubar
|
|
562
|
+
from . import systray
|
|
563
|
+
return systray
|
|
497
564
|
|
|
498
565
|
|
|
499
566
|
def cmd_menubar(args) -> None:
|
|
@@ -1316,19 +1383,23 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
1316
1383
|
p.add_argument("--json", action="store_true")
|
|
1317
1384
|
p.set_defaults(fn=cmd_recent)
|
|
1318
1385
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1386
|
+
# One command, three front ends. "panel" is the name that is true on
|
|
1387
|
+
# every platform, and the older names keep working.
|
|
1388
|
+
p = sub.add_parser("menubar", aliases=["tray", "panel"],
|
|
1389
|
+
help="the app: menu bar on macOS, notification area "
|
|
1390
|
+
"on Windows, a window on Linux (unlock, "
|
|
1391
|
+
"settings, connect an agent, recent memories)")
|
|
1322
1392
|
p.add_argument("--show", action="store_true",
|
|
1323
|
-
help="open the
|
|
1393
|
+
help="open the panel immediately on launch")
|
|
1324
1394
|
p.add_argument("--self-check", action="store_true",
|
|
1325
|
-
help="print what the
|
|
1395
|
+
help="print what the panel would show, no window")
|
|
1326
1396
|
p.add_argument("--render", metavar="PNG",
|
|
1327
|
-
help="write the
|
|
1397
|
+
help="write the panel to a PNG and exit (macOS, UI check)")
|
|
1328
1398
|
p.add_argument("--login", nargs="?", const="status",
|
|
1329
1399
|
choices=["on", "off", "status"],
|
|
1330
1400
|
help="start Compartment at login or sign-in (on/off), or "
|
|
1331
|
-
"show the state"
|
|
1401
|
+
"show the state. On Linux this is the applications "
|
|
1402
|
+
"menu entry, since there is no icon to start")
|
|
1332
1403
|
p.set_defaults(fn=cmd_menubar)
|
|
1333
1404
|
|
|
1334
1405
|
ph = sub.add_parser("hook", help="Claude Code capture hook (deterministic "
|
|
Binary file
|
|
Binary file
|