labtab 0.2.1__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.
Files changed (88) hide show
  1. {labtab-0.2.1/src/labtab.egg-info → labtab-0.2.2}/PKG-INFO +5 -2
  2. {labtab-0.2.1 → labtab-0.2.2}/README.md +4 -1
  3. {labtab-0.2.1 → labtab-0.2.2}/pyproject.toml +1 -1
  4. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/__init__.py +1 -1
  5. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/auth.py +23 -0
  6. {labtab-0.2.1 → labtab-0.2.2/src/labtab.egg-info}/PKG-INFO +5 -2
  7. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_auth.py +64 -0
  8. {labtab-0.2.1 → labtab-0.2.2}/.gitignore +0 -0
  9. {labtab-0.2.1 → labtab-0.2.2}/LICENSE +0 -0
  10. {labtab-0.2.1 → labtab-0.2.2}/RELEASING.md +0 -0
  11. {labtab-0.2.1 → labtab-0.2.2}/setup.cfg +0 -0
  12. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/auth.py +0 -0
  13. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/cli.py +0 -0
  14. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/client.py +0 -0
  15. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/__init__.py +0 -0
  16. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/_common.py +0 -0
  17. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/account.py +0 -0
  18. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/ai.py +0 -0
  19. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/api_keys.py +0 -0
  20. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/audit_logs.py +0 -0
  21. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/deadlines.py +0 -0
  22. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/export.py +0 -0
  23. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/grants.py +0 -0
  24. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/home.py +0 -0
  25. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/labs.py +0 -0
  26. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/letters.py +0 -0
  27. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/literature.py +0 -0
  28. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/meetings.py +0 -0
  29. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/notes.py +0 -0
  30. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/notifications.py +0 -0
  31. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/papers.py +0 -0
  32. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/people.py +0 -0
  33. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/posters.py +0 -0
  34. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/projects.py +0 -0
  35. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/results.py +0 -0
  36. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/search.py +0 -0
  37. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/shares.py +0 -0
  38. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/talks.py +0 -0
  39. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/tasks.py +0 -0
  40. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/commands/wiki.py +0 -0
  41. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/config.py +0 -0
  42. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/datetimes.py +0 -0
  43. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/errors.py +0 -0
  44. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/models/__init__.py +0 -0
  45. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/models/api.py +0 -0
  46. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/models/generated.py +0 -0
  47. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/output.py +0 -0
  48. {labtab-0.2.1 → labtab-0.2.2}/src/labtab/py.typed +0 -0
  49. {labtab-0.2.1 → labtab-0.2.2}/src/labtab.egg-info/SOURCES.txt +0 -0
  50. {labtab-0.2.1 → labtab-0.2.2}/src/labtab.egg-info/dependency_links.txt +0 -0
  51. {labtab-0.2.1 → labtab-0.2.2}/src/labtab.egg-info/entry_points.txt +0 -0
  52. {labtab-0.2.1 → labtab-0.2.2}/src/labtab.egg-info/requires.txt +0 -0
  53. {labtab-0.2.1 → labtab-0.2.2}/src/labtab.egg-info/top_level.txt +0 -0
  54. {labtab-0.2.1 → labtab-0.2.2}/tests/__init__.py +0 -0
  55. {labtab-0.2.1 → labtab-0.2.2}/tests/conftest.py +0 -0
  56. {labtab-0.2.1 → labtab-0.2.2}/tests/test_auth.py +0 -0
  57. {labtab-0.2.1 → labtab-0.2.2}/tests/test_client.py +0 -0
  58. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_account.py +0 -0
  59. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_ai.py +0 -0
  60. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_api_keys.py +0 -0
  61. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_audit_logs.py +0 -0
  62. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_deadlines.py +0 -0
  63. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_export.py +0 -0
  64. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_grants.py +0 -0
  65. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_home.py +0 -0
  66. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_labs.py +0 -0
  67. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_letters.py +0 -0
  68. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_literature.py +0 -0
  69. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_meetings.py +0 -0
  70. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_meetings_create.py +0 -0
  71. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_notes.py +0 -0
  72. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_notifications.py +0 -0
  73. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_papers.py +0 -0
  74. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_papers_import.py +0 -0
  75. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_people.py +0 -0
  76. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_posters.py +0 -0
  77. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_projects.py +0 -0
  78. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_results.py +0 -0
  79. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_search.py +0 -0
  80. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_shares.py +0 -0
  81. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_talks.py +0 -0
  82. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_tasks.py +0 -0
  83. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_tasks_complete.py +0 -0
  84. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_tasks_create.py +0 -0
  85. {labtab-0.2.1 → labtab-0.2.2}/tests/test_commands_wiki.py +0 -0
  86. {labtab-0.2.1 → labtab-0.2.2}/tests/test_config.py +0 -0
  87. {labtab-0.2.1 → labtab-0.2.2}/tests/test_datetimes.py +0 -0
  88. {labtab-0.2.1 → labtab-0.2.2}/tests/test_models_generated.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labtab
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Command-line interface for LabTab (Wetware Ltd). Manage your lab from the terminal.
5
5
  Author-email: Wetware Ltd <support@wetware.ai>
6
6
  License: MIT
@@ -59,6 +59,9 @@ labtab --show-completion
59
59
  # Pair your terminal with your LabTab account (opens a browser window)
60
60
  labtab auth login
61
61
 
62
+ # Later logins reuse the API key in ~/.config/labtab/config.toml; pair again only when needed
63
+ labtab auth login --force
64
+
62
65
  # Pair with a read-only key instead - mutating commands will be rejected
63
66
  labtab auth login --read-only
64
67
 
@@ -199,7 +202,7 @@ permission check.
199
202
  | `labtab notes create <type> <id>` | Create a note from `--text` or `--file` |
200
203
  | `labtab notes update <id>` | Replace note content from `--text` or `--file` |
201
204
  | `labtab notes export <type> <id>` | Export entity notes as Markdown or text |
202
- | `labtab auth login` | Pair this terminal with your account; `--read-only` requests a key that cannot modify data |
205
+ | `labtab auth login` | Pair once, then reuse the locally stored key; `--force` replaces it and `--read-only` requests a key that cannot modify data |
203
206
  | `labtab auth logout` | Forget the local API key |
204
207
  | `labtab auth whoami` | Show the signed-in user and default lab |
205
208
  | `labtab auth set-base-url` | Update the stored API base URL |
@@ -33,6 +33,9 @@ labtab --show-completion
33
33
  # Pair your terminal with your LabTab account (opens a browser window)
34
34
  labtab auth login
35
35
 
36
+ # Later logins reuse the API key in ~/.config/labtab/config.toml; pair again only when needed
37
+ labtab auth login --force
38
+
36
39
  # Pair with a read-only key instead - mutating commands will be rejected
37
40
  labtab auth login --read-only
38
41
 
@@ -173,7 +176,7 @@ permission check.
173
176
  | `labtab notes create <type> <id>` | Create a note from `--text` or `--file` |
174
177
  | `labtab notes update <id>` | Replace note content from `--text` or `--file` |
175
178
  | `labtab notes export <type> <id>` | Export entity notes as Markdown or text |
176
- | `labtab auth login` | Pair this terminal with your account; `--read-only` requests a key that cannot modify data |
179
+ | `labtab auth login` | Pair once, then reuse the locally stored key; `--force` replaces it and `--read-only` requests a key that cannot modify data |
177
180
  | `labtab auth logout` | Forget the local API key |
178
181
  | `labtab auth whoami` | Show the signed-in user and default lab |
179
182
  | `labtab auth set-base-url` | Update the stored API base URL |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "labtab"
3
- version = "0.2.1"
3
+ version = "0.2.2"
4
4
  description = "Command-line interface for LabTab (Wetware Ltd). Manage your lab from the terminal."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,5 +1,5 @@
1
1
  """labtab - command-line interface for LabTab (Wetware Ltd)."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.2"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -92,11 +92,33 @@ def login(
92
92
  "--read-only",
93
93
  help="Request a read-only API key - mutating commands will be rejected.",
94
94
  ),
95
+ force: bool = typer.Option(
96
+ False,
97
+ "--force",
98
+ help="Pair again and replace the API key already stored on this machine.",
99
+ ),
95
100
  json_mode: bool = typer.Option(False, "--json", help="Output JSON."),
96
101
  ) -> None:
97
102
  """Pair this terminal with your LabTab account."""
98
103
 
99
104
  config = Config.load()
105
+ if config.api_key and not force:
106
+ if json_mode:
107
+ dump_json(
108
+ {
109
+ "ok": True,
110
+ "user_email": config.user_email,
111
+ "default_lab_slug": config.default_lab_slug,
112
+ "reused": True,
113
+ },
114
+ )
115
+ return
116
+
117
+ email = config.user_email or "your account"
118
+ print_info(f"Already signed in as {email}; using the stored API key.")
119
+ print_info("Run `labtab auth login --force` to pair again.")
120
+ return
121
+
100
122
  client = LabtabClient.from_config(config)
101
123
  console = Console()
102
124
  requested_scopes = ["read"] if read_only else None
@@ -150,6 +172,7 @@ def login(
150
172
  "user_email": result.user_email,
151
173
  "default_lab_slug": result.default_lab_slug,
152
174
  "read_only": read_only,
175
+ "reused": False,
153
176
  },
154
177
  )
155
178
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labtab
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Command-line interface for LabTab (Wetware Ltd). Manage your lab from the terminal.
5
5
  Author-email: Wetware Ltd <support@wetware.ai>
6
6
  License: MIT
@@ -59,6 +59,9 @@ labtab --show-completion
59
59
  # Pair your terminal with your LabTab account (opens a browser window)
60
60
  labtab auth login
61
61
 
62
+ # Later logins reuse the API key in ~/.config/labtab/config.toml; pair again only when needed
63
+ labtab auth login --force
64
+
62
65
  # Pair with a read-only key instead - mutating commands will be rejected
63
66
  labtab auth login --read-only
64
67
 
@@ -199,7 +202,7 @@ permission check.
199
202
  | `labtab notes create <type> <id>` | Create a note from `--text` or `--file` |
200
203
  | `labtab notes update <id>` | Replace note content from `--text` or `--file` |
201
204
  | `labtab notes export <type> <id>` | Export entity notes as Markdown or text |
202
- | `labtab auth login` | Pair this terminal with your account; `--read-only` requests a key that cannot modify data |
205
+ | `labtab auth login` | Pair once, then reuse the locally stored key; `--force` replaces it and `--read-only` requests a key that cannot modify data |
203
206
  | `labtab auth logout` | Forget the local API key |
204
207
  | `labtab auth whoami` | Show the signed-in user and default lab |
205
208
  | `labtab auth set-base-url` | Update the stored API base URL |
@@ -91,6 +91,70 @@ def test_login_happy_path(
91
91
  assert reloaded.default_lab_slug == "demo-lab"
92
92
 
93
93
 
94
+ def test_login_reuses_stored_key_without_starting_pairing(
95
+ invoke,
96
+ httpx_mock: HTTPXMock,
97
+ signed_in_config: Path,
98
+ ) -> None:
99
+ result = invoke("auth", "login")
100
+
101
+ assert result.exit_code == 0, result.output
102
+ assert "Already signed in as pi@example.com" in result.stdout
103
+ assert "using the stored API key" in result.stdout
104
+ assert "--force" in result.stdout
105
+ assert httpx_mock.get_requests() == []
106
+ assert Config.load().api_key == "lab_api_test"
107
+
108
+
109
+ def test_login_reuses_stored_key_in_json_mode(
110
+ invoke,
111
+ httpx_mock: HTTPXMock,
112
+ signed_in_config: Path,
113
+ ) -> None:
114
+ result = invoke("auth", "login", "--json")
115
+
116
+ assert result.exit_code == 0, result.output
117
+ assert json.loads(result.stdout) == {
118
+ "ok": True,
119
+ "user_email": "pi@example.com",
120
+ "default_lab_slug": "smith-neuro-lab",
121
+ "reused": True,
122
+ }
123
+ assert httpx_mock.get_requests() == []
124
+
125
+
126
+ def test_login_force_replaces_stored_key(
127
+ invoke,
128
+ httpx_mock: HTTPXMock,
129
+ signed_in_config: Path,
130
+ ) -> None:
131
+ httpx_mock.add_response(
132
+ url="https://api.test/v1/cli/pair/start/",
133
+ method="POST",
134
+ json={
135
+ "pairing_code": "NEWK-EY12",
136
+ "confirm_url": "https://labtab.app/cli/confirm?code=NEWK-EY12",
137
+ "expires_in": 600,
138
+ },
139
+ )
140
+ httpx_mock.add_response(
141
+ url="https://api.test/v1/cli/pair/poll/?code=NEWK-EY12",
142
+ json={
143
+ "status": "confirmed",
144
+ "api_key": "lab_api_replacement",
145
+ "user_email": "pi@example.com",
146
+ "default_lab_slug": "smith-neuro-lab",
147
+ },
148
+ )
149
+
150
+ result = invoke("auth", "login", "--force", "--no-browser", "--json")
151
+
152
+ assert result.exit_code == 0, result.output
153
+ assert json.loads(result.stdout)["reused"] is False
154
+ assert Config.load().api_key == "lab_api_replacement"
155
+ assert len(httpx_mock.get_requests(method="POST")) == 1
156
+
157
+
94
158
  def test_login_read_only_sends_requested_scopes(
95
159
  invoke,
96
160
  httpx_mock: HTTPXMock,
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