pocketshell 0.4.13__tar.gz → 0.4.14__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.4.13 → pocketshell-0.4.14}/PKG-INFO +1 -1
- {pocketshell-0.4.13 → pocketshell-0.4.14}/pyproject.toml +1 -1
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/agents.py +25 -5
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_agents.py +108 -17
- {pocketshell-0.4.13 → pocketshell-0.4.14}/uv.lock +1 -1
- {pocketshell-0.4.13 → pocketshell-0.4.14}/.gitignore +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/README.md +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/scheduler/README.md +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/scheduler/pocketshell-usage-capture.service +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/scheduler/pocketshell-usage-capture.timer +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/__init__.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/__main__.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/agent_log.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/agents_kind.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/cards.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/cgroup_agents.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/cli.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/daemon.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/env.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/github.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/hooks.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/jobs.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/logs.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/profiles.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/prune_attachments.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/push.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/qr_share.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/repos.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/resume.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/sessions.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/tree.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/usage.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/usage_capture.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/src/pocketshell/usage_reset.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/__init__.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_agent_log.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_agents_kind.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_cards.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_cgroup_agents.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_cli.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_daemon.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_env.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_github.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_hooks.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_jobs.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_logs.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_profiles.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_prune_attachments.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_push.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_qr_share.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_repos.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_resume.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_sessions.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_tree.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_usage.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_usage_capture.py +0 -0
- {pocketshell-0.4.13 → pocketshell-0.4.14}/tests/test_usage_reset.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pocketshell
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.14
|
|
4
4
|
Summary: Unified server-side Python utility for the PocketShell Android client.
|
|
5
5
|
Project-URL: Homepage, https://github.com/alexeygrigorev/pocketshell
|
|
6
6
|
Project-URL: Issues, https://github.com/alexeygrigorev/pocketshell/issues
|
|
@@ -8,7 +8,7 @@ name = "pocketshell"
|
|
|
8
8
|
# scripts/check-pypi-version.sh enforces this; .github/workflows/build.yml
|
|
9
9
|
# runs that check before publishing to PyPI. See
|
|
10
10
|
# tools/pocketshell/README.md ("Release flow") for the bump procedure.
|
|
11
|
-
version = "0.4.
|
|
11
|
+
version = "0.4.14"
|
|
12
12
|
description = "Unified server-side Python utility for the PocketShell Android client."
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
requires-python = ">=3.11"
|
|
@@ -379,8 +379,13 @@ def record_agent_kind(
|
|
|
379
379
|
profile name is known here, before ``os.execvpe``; #826 record-at-start
|
|
380
380
|
hard-cut — no detection/parse path). When set, it is written as the
|
|
381
381
|
per-session ``@ps_agent_profile`` user option alongside ``@ps_agent_kind``.
|
|
382
|
-
A default / no-profile launch passes ``None`` and
|
|
383
|
-
|
|
382
|
+
A default / no-profile launch passes ``None`` and the option is
|
|
383
|
+
RECONCILED to the current launch by UNSETTING it
|
|
384
|
+
(``tmux set-option -uq @ps_agent_profile``), so a session previously
|
|
385
|
+
launched with a non-default profile and then relaunched as a default
|
|
386
|
+
agent in the SAME tmux session does not keep the stale profile label
|
|
387
|
+
(issue #889). ``@ps_agent_kind`` is always overwritten on every launch
|
|
388
|
+
so it has no equivalent stale hazard.
|
|
384
389
|
|
|
385
390
|
The options are session-scoped (not global): ``tmux set-option`` without
|
|
386
391
|
``-g`` sets it on the current session, which is the session the agent
|
|
@@ -411,12 +416,26 @@ def record_agent_kind(
|
|
|
411
416
|
check=False,
|
|
412
417
|
)
|
|
413
418
|
if profile:
|
|
414
|
-
#
|
|
415
|
-
# launch writes no option so the tree shows the plain kind.
|
|
419
|
+
# A non-default profile is recorded so the tree shows its label.
|
|
416
420
|
runner(
|
|
417
421
|
["tmux", "set-option", "@ps_agent_profile", profile],
|
|
418
422
|
check=False,
|
|
419
423
|
)
|
|
424
|
+
else:
|
|
425
|
+
# A default / no-profile launch must RECONCILE the option to the
|
|
426
|
+
# current launch by UNSETTING it (issue #889). tmux session
|
|
427
|
+
# options persist for the life of the session, so a session
|
|
428
|
+
# launched once with a non-default profile (e.g. z.ai) and then
|
|
429
|
+
# relaunched as a default agent in the SAME session would keep the
|
|
430
|
+
# stale @ps_agent_profile and be mislabelled in the tree. The
|
|
431
|
+
# ``-u`` unsets the session option; ``-q`` makes unsetting an
|
|
432
|
+
# already-absent option a no-op (a fresh default session stays
|
|
433
|
+
# clean). The kind itself (set above) is always overwritten, so it
|
|
434
|
+
# has no equivalent stale hazard.
|
|
435
|
+
runner(
|
|
436
|
+
["tmux", "set-option", "-uq", "@ps_agent_profile"],
|
|
437
|
+
check=False,
|
|
438
|
+
)
|
|
420
439
|
except Exception:
|
|
421
440
|
# Recording the kind is best-effort; never block the launch on it.
|
|
422
441
|
return False
|
|
@@ -522,7 +541,8 @@ def _resolve_config_dir(
|
|
|
522
541
|
tree can tell a z.ai Claude apart from a default Claude. The engine's
|
|
523
542
|
default profile, ``--config-dir`` (which carries no named profile), and
|
|
524
543
|
omitting both flags all resolve ``profile_label`` to ``None`` — so a
|
|
525
|
-
default
|
|
544
|
+
default launch clears any stale ``@ps_agent_profile`` option (issue
|
|
545
|
+
#889) rather than leaving a profile label behind.
|
|
526
546
|
"""
|
|
527
547
|
if config_dir is not None and profile is not None:
|
|
528
548
|
click.echo(
|
|
@@ -470,11 +470,11 @@ def test_record_agent_kind_sets_session_option_inside_tmux(kind):
|
|
|
470
470
|
runner=lambda argv, **kw: calls.append((argv, kw)),
|
|
471
471
|
)
|
|
472
472
|
assert ok is True
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
assert
|
|
473
|
+
# No profile passed -> the kind is set (session-scoped, no -g), and the
|
|
474
|
+
# @ps_agent_profile option is reconciled away by an unset (issue #889).
|
|
475
|
+
argvs = [argv for argv, _kw in calls]
|
|
476
|
+
assert ["tmux", "set-option", "@ps_agent_kind", kind] in argvs
|
|
477
|
+
assert ["tmux", "set-option", "-uq", "@ps_agent_profile"] in argvs
|
|
478
478
|
|
|
479
479
|
|
|
480
480
|
def test_record_agent_kind_noop_when_not_in_tmux():
|
|
@@ -594,9 +594,12 @@ def test_record_agent_kind_writes_profile_option_when_profile_set():
|
|
|
594
594
|
]
|
|
595
595
|
|
|
596
596
|
|
|
597
|
-
def
|
|
598
|
-
# A default / no-profile launch records the kind
|
|
599
|
-
#
|
|
597
|
+
def test_record_agent_kind_no_profile_clears_profile_option():
|
|
598
|
+
# A default / no-profile launch records the kind AND reconciles the
|
|
599
|
+
# @ps_agent_profile option by UNSETTING it (issue #889), so a session
|
|
600
|
+
# previously launched with a non-default profile cannot keep a stale
|
|
601
|
+
# label. ``-uq`` makes unsetting an already-absent option a no-op, so a
|
|
602
|
+
# fresh default session stays clean.
|
|
600
603
|
calls = []
|
|
601
604
|
ok = agents.record_agent_kind(
|
|
602
605
|
"claude",
|
|
@@ -605,11 +608,15 @@ def test_record_agent_kind_no_profile_writes_only_kind():
|
|
|
605
608
|
profile=None,
|
|
606
609
|
)
|
|
607
610
|
assert ok is True
|
|
608
|
-
assert calls == [
|
|
611
|
+
assert calls == [
|
|
612
|
+
["tmux", "set-option", "@ps_agent_kind", "claude"],
|
|
613
|
+
["tmux", "set-option", "-uq", "@ps_agent_profile"],
|
|
614
|
+
]
|
|
609
615
|
|
|
610
616
|
|
|
611
|
-
def
|
|
612
|
-
# An empty-string profile is treated like no profile
|
|
617
|
+
def test_record_agent_kind_empty_profile_clears_profile_option():
|
|
618
|
+
# An empty-string profile is treated like no profile: kind recorded, the
|
|
619
|
+
# @ps_agent_profile option unset (issue #889).
|
|
613
620
|
calls = []
|
|
614
621
|
agents.record_agent_kind(
|
|
615
622
|
"codex",
|
|
@@ -617,7 +624,45 @@ def test_record_agent_kind_empty_profile_writes_only_kind():
|
|
|
617
624
|
runner=lambda argv, **kw: calls.append(argv),
|
|
618
625
|
profile="",
|
|
619
626
|
)
|
|
620
|
-
assert calls == [
|
|
627
|
+
assert calls == [
|
|
628
|
+
["tmux", "set-option", "@ps_agent_kind", "codex"],
|
|
629
|
+
["tmux", "set-option", "-uq", "@ps_agent_profile"],
|
|
630
|
+
]
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def test_record_agent_kind_default_relaunch_clears_stale_profile():
|
|
634
|
+
# REPRODUCE-FIRST (issue #889): the reported false-z.ai-label bug. A tmux
|
|
635
|
+
# session is launched once with the z.ai profile (sets @ps_agent_profile),
|
|
636
|
+
# then the agent is killed and RELAUNCHED as a regular default Claude in
|
|
637
|
+
# the SAME session. tmux session options persist, so without the #889 fix
|
|
638
|
+
# the stale "Claude (Z.AI)" lingers and the tree mislabels the session.
|
|
639
|
+
# The default relaunch must emit the unset that clears it.
|
|
640
|
+
#
|
|
641
|
+
# Launch 1: z.ai profile.
|
|
642
|
+
zai_calls = []
|
|
643
|
+
agents.record_agent_kind(
|
|
644
|
+
"claude",
|
|
645
|
+
env={"TMUX": "/tmp/tmux-1000/default,1234,0"},
|
|
646
|
+
runner=lambda argv, **kw: zai_calls.append(argv),
|
|
647
|
+
profile="Claude (Z.AI)",
|
|
648
|
+
)
|
|
649
|
+
assert ["tmux", "set-option", "@ps_agent_profile", "Claude (Z.AI)"] in zai_calls
|
|
650
|
+
|
|
651
|
+
# Launch 2: default relaunch in the same session. It MUST issue the unset
|
|
652
|
+
# so the previously-set @ps_agent_profile is reconciled away.
|
|
653
|
+
default_calls = []
|
|
654
|
+
agents.record_agent_kind(
|
|
655
|
+
"claude",
|
|
656
|
+
env={"TMUX": "/tmp/tmux-1000/default,1234,0"},
|
|
657
|
+
runner=lambda argv, **kw: default_calls.append(argv),
|
|
658
|
+
profile=None,
|
|
659
|
+
)
|
|
660
|
+
assert ["tmux", "set-option", "-uq", "@ps_agent_profile"] in default_calls
|
|
661
|
+
# And it must NOT re-set a profile value.
|
|
662
|
+
assert not any(
|
|
663
|
+
argv[:3] == ["tmux", "set-option", "@ps_agent_profile"]
|
|
664
|
+
for argv in default_calls
|
|
665
|
+
)
|
|
621
666
|
|
|
622
667
|
|
|
623
668
|
def test_launch_agent_records_profile_before_exec(tmp_path, monkeypatch):
|
|
@@ -669,9 +714,10 @@ def test_cli_agent_named_profile_records_profile_option(tmp_path, monkeypatch):
|
|
|
669
714
|
assert ["tmux", "set-option", "@ps_agent_profile", "Claude (Z.AI)"] in calls
|
|
670
715
|
|
|
671
716
|
|
|
672
|
-
def
|
|
673
|
-
"""A default Claude profile records the kind
|
|
674
|
-
|
|
717
|
+
def test_cli_agent_default_profile_clears_profile_option(tmp_path, monkeypatch):
|
|
718
|
+
"""A default Claude profile records the kind and reconciles the profile
|
|
719
|
+
option by UNSETTING it (issue #889), so a default launch never carries a
|
|
720
|
+
stale label and never sets a spurious one."""
|
|
675
721
|
_seed_zlaude_home(tmp_path, monkeypatch)
|
|
676
722
|
workdir = tmp_path / "work"
|
|
677
723
|
workdir.mkdir()
|
|
@@ -684,13 +730,15 @@ def test_cli_agent_default_profile_records_no_profile_option(tmp_path, monkeypat
|
|
|
684
730
|
)
|
|
685
731
|
assert rc == 0
|
|
686
732
|
assert ["tmux", "set-option", "@ps_agent_kind", "claude"] in calls
|
|
733
|
+
assert ["tmux", "set-option", "-uq", "@ps_agent_profile"] in calls
|
|
687
734
|
assert not any(
|
|
688
735
|
argv[:3] == ["tmux", "set-option", "@ps_agent_profile"] for argv in calls
|
|
689
736
|
)
|
|
690
737
|
|
|
691
738
|
|
|
692
|
-
def
|
|
693
|
-
"""A bare launch (no --profile/--config-dir) records the kind
|
|
739
|
+
def test_cli_agent_no_profile_clears_profile_option(tmp_path, monkeypatch):
|
|
740
|
+
"""A bare launch (no --profile/--config-dir) records the kind and unsets
|
|
741
|
+
@ps_agent_profile (issue #889)."""
|
|
694
742
|
workdir = tmp_path / "work"
|
|
695
743
|
workdir.mkdir()
|
|
696
744
|
monkeypatch.setenv("HOME", str(tmp_path))
|
|
@@ -701,11 +749,54 @@ def test_cli_agent_no_profile_records_no_profile_option(tmp_path, monkeypatch):
|
|
|
701
749
|
rc = main(["agent", "codex", "--dir", str(workdir)])
|
|
702
750
|
assert rc == 0
|
|
703
751
|
assert ["tmux", "set-option", "@ps_agent_kind", "codex"] in calls
|
|
752
|
+
assert ["tmux", "set-option", "-uq", "@ps_agent_profile"] in calls
|
|
704
753
|
assert not any(
|
|
705
754
|
argv[:3] == ["tmux", "set-option", "@ps_agent_profile"] for argv in calls
|
|
706
755
|
)
|
|
707
756
|
|
|
708
757
|
|
|
758
|
+
def test_cli_agent_default_then_zai_relaunch_sets_profile(tmp_path, monkeypatch):
|
|
759
|
+
"""Class coverage (issue #889): default-then-relaunched-z.ai. A default
|
|
760
|
+
launch in the session unsets the profile; relaunching the SAME session as
|
|
761
|
+
z.ai sets the correct label (the chip appears)."""
|
|
762
|
+
_seed_zlaude_home(tmp_path, monkeypatch)
|
|
763
|
+
workdir = tmp_path / "work"
|
|
764
|
+
workdir.mkdir()
|
|
765
|
+
monkeypatch.setenv("TMUX", "/tmp/tmux-1000/default,1234,0")
|
|
766
|
+
monkeypatch.setattr(agents.os, "execvpe", lambda f, a, e: None)
|
|
767
|
+
|
|
768
|
+
# Launch 1: default -> unset.
|
|
769
|
+
default_calls = []
|
|
770
|
+
monkeypatch.setattr(
|
|
771
|
+
agents.subprocess, "run", lambda argv, **kw: default_calls.append(argv)
|
|
772
|
+
)
|
|
773
|
+
assert main(["agent", "claude", "--dir", str(workdir), "--profile", "Claude"]) == 0
|
|
774
|
+
assert ["tmux", "set-option", "-uq", "@ps_agent_profile"] in default_calls
|
|
775
|
+
|
|
776
|
+
# Launch 2: z.ai relaunch in the same session -> sets the label.
|
|
777
|
+
zai_calls = []
|
|
778
|
+
monkeypatch.setattr(
|
|
779
|
+
agents.subprocess, "run", lambda argv, **kw: zai_calls.append(argv)
|
|
780
|
+
)
|
|
781
|
+
assert (
|
|
782
|
+
main(
|
|
783
|
+
[
|
|
784
|
+
"agent",
|
|
785
|
+
"claude",
|
|
786
|
+
"--dir",
|
|
787
|
+
str(workdir),
|
|
788
|
+
"--profile",
|
|
789
|
+
"Claude (Z.AI)",
|
|
790
|
+
]
|
|
791
|
+
)
|
|
792
|
+
== 0
|
|
793
|
+
)
|
|
794
|
+
assert ["tmux", "set-option", "@ps_agent_profile", "Claude (Z.AI)"] in zai_calls
|
|
795
|
+
assert not any(
|
|
796
|
+
argv[:3] == ["tmux", "set-option", "-uq"] for argv in zai_calls
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
|
|
709
800
|
def test_resolve_config_dir_default_profile_label_is_none(tmp_path, monkeypatch):
|
|
710
801
|
"""The default Claude profile resolves to a None profile label."""
|
|
711
802
|
_seed_zlaude_home(tmp_path, monkeypatch)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|