pocketshell 0.5.5__tar.gz → 0.5.7__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.
- {pocketshell-0.5.5 → pocketshell-0.5.7}/PKG-INFO +1 -1
- {pocketshell-0.5.5 → pocketshell-0.5.7}/pyproject.toml +1 -1
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/sessions.py +28 -1
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_sessions_create_routing.py +31 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/uv.lock +1 -1
- {pocketshell-0.5.5 → pocketshell-0.5.7}/.gitignore +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/LICENSE +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/README.md +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scheduler/README.md +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scheduler/pocketshell-usage-capture.service +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scheduler/pocketshell-usage-capture.timer +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scripts/check-cgroup-cap-proof.sh +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scripts/check-product-tmux-absent.sh +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scripts/check-python-ci-lock.sh +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scripts/sync-readme-exit-codes.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/scripts/test-usage-missing-window-mutation.sh +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/__init__.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/__main__.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/agent_card_push.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/agent_log.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/agents.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/agents_kind.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/aplexer.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/cards.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/cgroup_agents.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/cli.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/daemon.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/engines.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/env.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/github.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/hooks.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/logs.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/memcap.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/profiles.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/prune_attachments.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/push.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/repos.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/serve.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/session_enum.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/tree.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/usage.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/usage_capture.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/usage_reset.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/src/pocketshell/workspaces.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/__init__.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/conftest.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/quse-0.0.11-usage.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/quse-0.0.13-usage.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/quse-0.0.15-usage.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/quse-0.0.15-usage.ndjson +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/quse-0.0.15-usage.provenance.txt +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/quse-0.0.9-usage.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/sshd_anchor/Dockerfile.from-unanchored-base +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot-agent.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot-crashed-start.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot-liveness-mix.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot-mid-create.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot-post-kill-window.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot-reported-state.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot.json +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_agent_card_push.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_agent_log.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_agents.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_agents_kind.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_aplexer_contract.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_aplexer_resolution.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_cards.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_cards_push_notify.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_cgroup_agents.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_cli.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_daemon.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_daemon_fallback.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_engines.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_env.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_github.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_hooks.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_logs.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_profiles.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_prune_attachments.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_push.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_repos.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_serve.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_session_cgroup_kernel_proof.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_session_enum.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_sessions.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_sessions_attach.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_sessions_kill.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_sessions_mem_cap.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_tree.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_usage.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_usage_capture.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_usage_reset.py +0 -0
- {pocketshell-0.5.5 → pocketshell-0.5.7}/tests/test_workspaces.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pocketshell
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.7
|
|
4
4
|
Summary: Unified server-side Python utility for the PocketShell Android and desktop clients.
|
|
5
5
|
Project-URL: Homepage, https://github.com/PocketShell-io/pocketshell-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/PocketShell-io/pocketshell-cli/issues
|
|
@@ -11,7 +11,7 @@ name = "pocketshell"
|
|
|
11
11
|
# since the extraction (PocketShell-io/pocketshell#2643): the first
|
|
12
12
|
# self-governed release is 0.5.5, continuing from the last app-coupled
|
|
13
13
|
# release 0.5.4 so `uv tool upgrade pocketshell` stays monotonic.
|
|
14
|
-
version = "0.5.
|
|
14
|
+
version = "0.5.7"
|
|
15
15
|
description = "Unified server-side Python utility for the PocketShell Android and desktop clients."
|
|
16
16
|
readme = "README.md"
|
|
17
17
|
requires-python = ">=3.11"
|
|
@@ -9,6 +9,7 @@ import sys
|
|
|
9
9
|
import time
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from typing import Any, Mapping, Optional, Sequence
|
|
12
|
+
from uuid import uuid4
|
|
12
13
|
|
|
13
14
|
import click
|
|
14
15
|
|
|
@@ -181,7 +182,28 @@ def aplexer_start_argv(
|
|
|
181
182
|
profile: Optional[str],
|
|
182
183
|
memory_bytes: Optional[int],
|
|
183
184
|
) -> list[str]:
|
|
184
|
-
"""Build the detached ``a --json start`` invocation.
|
|
185
|
+
"""Build the detached ``a --json start`` invocation.
|
|
186
|
+
|
|
187
|
+
A capped start is wrapped in a transient per-user systemd scope
|
|
188
|
+
(``systemd-run --user --scope``). aplexer's worker moves the workload
|
|
189
|
+
into its delegated scope by writing its own pgid into that scope's
|
|
190
|
+
``cgroup.procs`` from a pre_exec hook, and cgroup-v2 grants that write
|
|
191
|
+
only on the common ancestor of the writer's cgroup and the target.
|
|
192
|
+
Spawned straight from an SSH session, the worker lives in
|
|
193
|
+
``session-*.scope``, the workload scope lands under
|
|
194
|
+
``user@<uid>.service/app.slice``, and their common ancestor
|
|
195
|
+
(``user-<uid>.slice``) is root-owned — so every capped create died with
|
|
196
|
+
``spawn workload: Permission denied (os error 13)`` (#2625; the same
|
|
197
|
+
runner gap PR #2590 documented from the CI side). Inside a user-manager
|
|
198
|
+
scope the common ancestor is the user-owned ``app.slice`` — the
|
|
199
|
+
placement every session had before #2561 removed the previous session
|
|
200
|
+
backend. The scope becomes the worker's home for the
|
|
201
|
+
session's whole life (aplexer's record discloses it as
|
|
202
|
+
``worker_cgroup``), and ``--collect`` reaps the unit once the session
|
|
203
|
+
is gone. Only capped starts wrap: an uncapped ``a start`` (the Docker
|
|
204
|
+
fixture's ``--mem none``) performs no cgroup migration and must keep
|
|
205
|
+
working on hosts with no user manager at all.
|
|
206
|
+
"""
|
|
185
207
|
argv = [aplexer_path, "--json", "start", "--workspace", workspace, "--tag", tag]
|
|
186
208
|
if engine:
|
|
187
209
|
argv.extend(["--engine", engine])
|
|
@@ -189,6 +211,11 @@ def aplexer_start_argv(
|
|
|
189
211
|
argv.extend(["--profile", profile])
|
|
190
212
|
if memory_bytes is not None:
|
|
191
213
|
argv.extend(["--memory", str(memory_bytes)])
|
|
214
|
+
argv = [
|
|
215
|
+
"systemd-run", "--user", "--scope",
|
|
216
|
+
"--unit", f"aplexer-launch-{uuid4().hex}",
|
|
217
|
+
"--collect", "--",
|
|
218
|
+
] + argv
|
|
192
219
|
return argv
|
|
193
220
|
|
|
194
221
|
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
from pathlib import Path
|
|
7
|
+
from types import SimpleNamespace
|
|
7
8
|
|
|
8
9
|
from click.testing import CliRunner
|
|
9
10
|
|
|
@@ -35,6 +36,7 @@ def test_create_uses_only_aplexer_and_emits_schema_three(monkeypatch, tmp_path:
|
|
|
35
36
|
monkeypatch.setattr(sessions, "_resolve_aplexer", lambda: _resolution("/fake/a"))
|
|
36
37
|
monkeypatch.setattr(sessions, "_aplexer_snapshot", lambda: [])
|
|
37
38
|
monkeypatch.setattr(sessions._memcap, "resolve_session_mem_bytes", lambda **_: 123)
|
|
39
|
+
monkeypatch.setattr(sessions, "uuid4", lambda: SimpleNamespace(hex="deadbeefcafe"))
|
|
38
40
|
|
|
39
41
|
def start(argv):
|
|
40
42
|
calls.append(list(argv))
|
|
@@ -55,12 +57,41 @@ def test_create_uses_only_aplexer_and_emits_schema_three(monkeypatch, tmp_path:
|
|
|
55
57
|
"created": True,
|
|
56
58
|
}
|
|
57
59
|
assert calls == [[
|
|
60
|
+
"systemd-run", "--user", "--scope",
|
|
61
|
+
"--unit", "aplexer-launch-deadbeefcafe", "--collect", "--",
|
|
58
62
|
"/fake/a", "--json", "start", "--workspace", str(tmp_path),
|
|
59
63
|
"--tag", "shell", "--engine", "codex", "--memory", "123",
|
|
60
64
|
]]
|
|
61
65
|
assert "backend" not in result.output.lower()
|
|
62
66
|
|
|
63
67
|
|
|
68
|
+
def test_capped_start_wraps_the_worker_in_a_unique_user_scope() -> None:
|
|
69
|
+
first = sessions.aplexer_start_argv(
|
|
70
|
+
aplexer_path="/fake/a", workspace="/work/project", tag="shell",
|
|
71
|
+
engine=None, profile=None, memory_bytes=123,
|
|
72
|
+
)
|
|
73
|
+
second = sessions.aplexer_start_argv(
|
|
74
|
+
aplexer_path="/fake/a", workspace="/work/project", tag="shell",
|
|
75
|
+
engine=None, profile=None, memory_bytes=123,
|
|
76
|
+
)
|
|
77
|
+
for argv in (first, second):
|
|
78
|
+
assert argv[:7] == [
|
|
79
|
+
"systemd-run", "--user", "--scope", "--unit", argv[4], "--collect", "--",
|
|
80
|
+
]
|
|
81
|
+
assert argv[4].startswith("aplexer-launch-")
|
|
82
|
+
assert "--memory" in argv[7:]
|
|
83
|
+
assert first[4] != second[4]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_uncapped_start_is_not_wrapped() -> None:
|
|
87
|
+
argv = sessions.aplexer_start_argv(
|
|
88
|
+
aplexer_path="/fake/a", workspace="/work/project", tag="shell",
|
|
89
|
+
engine=None, profile=None, memory_bytes=None,
|
|
90
|
+
)
|
|
91
|
+
assert argv == ["/fake/a", "--json", "start", "--workspace", "/work/project", "--tag", "shell"]
|
|
92
|
+
assert not any("systemd-run" in part for part in argv)
|
|
93
|
+
|
|
94
|
+
|
|
64
95
|
def test_create_reuses_a_live_record_without_starting_again(monkeypatch, tmp_path: Path) -> None:
|
|
65
96
|
starts: list[list[str]] = []
|
|
66
97
|
monkeypatch.setattr(sessions, "_resolve_aplexer", lambda: _resolution("/fake/a"))
|
|
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
|
|
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
|
{pocketshell-0.5.5 → pocketshell-0.5.7}/tests/data/sshd_anchor/Dockerfile.from-unanchored-base
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pocketshell-0.5.5 → pocketshell-0.5.7}/tests/fixtures/aplexer/snapshot-post-kill-window.json
RENAMED
|
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
|