Skip to content

Commit ffa86b7

Browse files
Enhance CLI credential handling and update documentation
- Updated `auralogger init`, `server-check`, and `client-check` commands to prompt for missing `AURALOGGER_PROJECT_TOKEN` and `AURALOGGER_USER_SECRET` values, improving user experience. - Modified `README.md` and other documentation files to reflect new credential handling behavior and added details on Node-parity features. - Introduced a new `auralog` function in `init.py` for structured logging with Pydantic models, enhancing type safety and clarity in log inputs.
1 parent 0307361 commit ffa86b7

9 files changed

Lines changed: 264 additions & 16 deletions

File tree

‎README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ auralogger init
3131
auralogger server-check
3232
```
3333

34+
`server-check` and `client-check` now follow the same credential UX as `init`: if token or user secret is missing after `.env` load, the CLI prompts for missing values before running checks.
35+
3436
The **`aura_log()`** library function reads **`os.environ` only**; it does not load `.env` files. In a web app, load env in your own startup code or rely on your host.
3537

3638
## Naming parity with Node
@@ -58,6 +60,11 @@ auralogger get-logs [filters...]
5860

5961
Filter syntax: **`user-docs/commands.md`**.
6062

63+
`auralogger init` now has two Node-parity UX branches:
64+
65+
- If token + user secret + session are already in env, it prints an "already configured" success path and Python server integration snippet without re-calling `proj_auth`.
66+
- Otherwise it fetches `proj_auth`, prints copy-paste env lines, and then prints the same integration snippet plus a frontend pointer (`auralogger-cli/client`).
67+
6168
## Requirements
6269

6370
Python 3.8+, `websocket-client`, and `python-dotenv` (declared in `pyproject.toml`).

‎auralogger/client/__init__.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""Client-facing CLI helper exports.
2+
3+
Python package does not currently expose a browser runtime logger API.
4+
"""
5+
6+
from auralogger.commands.client_check import run_client_check
7+
from auralogger.commands.test_clientlog import run_test_clientlog
8+
9+
__all__ = [
10+
"run_client_check",
11+
"run_test_clientlog",
12+
]

‎auralogger/commands/init.py‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,31 @@ def print_copy_paste_env_block(
100100
def _build_server_integration_snippet() -> str:
101101
return "\n".join(
102102
[
103+
"import os",
104+
"from typing import Any, Dict, Literal, Optional",
105+
"from pydantic import BaseModel, Field",
103106
"from auralogger import AuraServer",
104107
"",
108+
"class LogInputs(BaseModel):",
109+
" type: Literal['debug', 'info', 'warn', 'error'] = 'info'",
110+
" message: str = Field(..., min_length=1)",
111+
" location: Optional[str] = None",
112+
" data: Optional[Dict[str, Any]] = None",
113+
"",
105114
"def configure_auralogger() -> None:",
106115
" project_token = os.environ.get('AURALOGGER_PROJECT_TOKEN', '').strip()",
107116
" user_secret = os.environ.get('AURALOGGER_USER_SECRET', '').strip()",
108117
" if not project_token or not user_secret:",
109118
" raise RuntimeError('Missing Auralogger server env variables')",
110119
" AuraServer.sync_from_secret(project_token, user_secret)",
111120
"",
112-
"def log_checkout_error(order_id: str, error: str) -> None:",
113-
" AuraServer.log('error', 'checkout failed', 'payments/checkout', {",
114-
" 'order_id': order_id,",
115-
" 'error': error,",
116-
" })",
121+
"def auralog(loginputs: LogInputs) -> None:",
122+
" AuraServer.log(",
123+
" loginputs.type,",
124+
" loginputs.message,",
125+
" loginputs.location,",
126+
" loginputs.data,",
127+
" )",
117128
]
118129
)
119130

‎auralogger/server/__init__.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""Server-side runtime exports."""
2+
3+
from auralogger.aura_log import AuraServer, aura_log, close_aura_log_socket
4+
5+
__all__ = [
6+
"AuraServer",
7+
"aura_log",
8+
"close_aura_log_socket",
9+
]

‎auralogger/utils/__init__.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Utility exports for host integration and testing helpers."""
2+
3+
from auralogger.backend_origin import (
4+
build_create_browser_logs_url,
5+
build_proj_auth_url,
6+
build_project_logs_url,
7+
resolve_api_base_url,
8+
resolve_ws_base_url,
9+
)
10+
from auralogger.env_config import (
11+
ENV_NEXT_PUBLIC_PROJECT_TOKEN,
12+
ENV_PROJECT_ID,
13+
ENV_PROJECT_SESSION,
14+
ENV_PROJECT_STYLES,
15+
ENV_PROJECT_TOKEN,
16+
ENV_PROJECT_SECRET,
17+
ENV_USER_SECRET,
18+
ENV_VITE_PROJECT_TOKEN,
19+
)
20+
from auralogger.http_utils import parse_error_body
21+
22+
__all__ = [
23+
"build_proj_auth_url",
24+
"build_project_logs_url",
25+
"build_create_browser_logs_url",
26+
"resolve_api_base_url",
27+
"resolve_ws_base_url",
28+
"parse_error_body",
29+
"ENV_PROJECT_TOKEN",
30+
"ENV_NEXT_PUBLIC_PROJECT_TOKEN",
31+
"ENV_VITE_PROJECT_TOKEN",
32+
"ENV_USER_SECRET",
33+
"ENV_PROJECT_SECRET",
34+
"ENV_PROJECT_ID",
35+
"ENV_PROJECT_SESSION",
36+
"ENV_PROJECT_STYLES",
37+
]

‎dev-docs/file-map.md‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,30 +30,36 @@ Use these pairs when keeping Python terminology close to Node while preserving P
3030

3131
- `auralogger/commands/init.py`
3232
- `auralogger init`
33-
- Resolves project token + user secret, `POST /api/{project_token}/proj_auth` (token in path only), Node-aligned copy-paste env block
33+
- Early success path when token + user secret + session already exist; otherwise resolves token + secret, `POST /api/{project_token}/proj_auth` (token in path only), prints Node-aligned env block + Python server snippet
3434

3535
- `auralogger/commands/server_check.py`
3636
- `auralogger server-check`
37-
- `proj_auth` then `WS /{project_token}/create_log` with `Authorization: Bearer <user_secret>` and a test JSON frame
37+
- Uses shared CLI context resolver (env/prompt + `proj_auth` hydration), then `WS /{project_token}/create_log` with `Authorization: Bearer <user_secret>` and one test JSON frame
3838

3939
- `auralogger/commands/client_check.py`
4040
- `auralogger client-check`
41-
- `proj_auth` then `WS /{project_token}/create_browser_logs` with path-only auth and one test JSON frame
41+
- Uses the same shared CLI context resolver as `server-check`, then `WS /{project_token}/create_browser_logs` with path-only auth and one test JSON frame
4242

4343
- `auralogger/commands/test_serverlog.py`
4444
- `auralogger test-serverlog`
45-
- Sends 5 logs via `aura_log(...)`, then closes the cached server socket
45+
- Resolves token + secret (env/prompt), syncs runtime via `AuraServer.sync_from_secret(...)`, sends 5 logs via `aura_log(...)`, then closes the cached server socket
4646

4747
- `auralogger/commands/test_clientlog.py`
4848
- `auralogger test-clientlog`
49-
- `proj_auth`, single `create_browser_logs` socket, 5 JSON frames, close
49+
- Resolves project token (env/prompt), then `proj_auth`, single `create_browser_logs` socket, 5 JSON frames, close
5050

5151
- `auralogger/commands/get_logs_cmd.py`
5252
- Thin wrapper → `get_logs.run_get_logs`
5353

5454
## Shared CLI resolution
5555

56-
- `auralogger/cli_auth.py` — env or interactive resolution for project token and user secret
56+
- `auralogger/cli_auth.py` — env or interactive resolution for project token/user secret plus shared `resolve_project_context_for_cli_checks()`
57+
58+
## Namespaced compatibility exports
59+
60+
- `auralogger/server/__init__.py` — server runtime re-exports (`AuraServer`, `aura_log`, `close_aura_log_socket`)
61+
- `auralogger/client/__init__.py` — client-related CLI helper re-exports (`run_client_check`, `run_test_clientlog`)
62+
- `auralogger/utils/__init__.py` — URL/env/error utility re-exports for stable namespaced imports
5763

5864
## Configuration (os.environ)
5965

‎tests/test_cli_parity_updates.py‎

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
from __future__ import annotations
2+
3+
import io
4+
import unittest
5+
from contextlib import redirect_stdout
6+
from unittest.mock import patch
7+
8+
from auralogger.cli_auth import CliProjectContext, resolve_project_context_for_cli_checks
9+
from auralogger.commands.client_check import run_client_check
10+
from auralogger.commands.init import run_init
11+
from auralogger.commands.server_check import run_server_check
12+
13+
14+
class _FakeSocket:
15+
def __init__(self) -> None:
16+
self.sent: list[str] = []
17+
self.closed = False
18+
19+
def send(self, body: str) -> None:
20+
self.sent.append(body)
21+
22+
def close(self) -> None:
23+
self.closed = True
24+
25+
26+
class CliContextResolverTests(unittest.TestCase):
27+
def test_resolve_project_context_for_cli_checks_success(self) -> None:
28+
with patch(
29+
"auralogger.cli_auth.resolve_project_token_for_init", return_value="ptok"
30+
), patch(
31+
"auralogger.cli_auth.resolve_user_secret_for_init", return_value="usec"
32+
), patch(
33+
"auralogger.cli_auth.fetch_proj_auth_payload",
34+
return_value={"project_id": "pid-1", "project_name": "proj", "session": "sess-1"},
35+
):
36+
ctx = resolve_project_context_for_cli_checks()
37+
38+
self.assertEqual(
39+
ctx,
40+
CliProjectContext(
41+
project_token="ptok",
42+
user_secret="usec",
43+
project_id="pid-1",
44+
project_name="proj",
45+
session="sess-1",
46+
),
47+
)
48+
49+
def test_resolve_project_context_for_cli_checks_validates_required_fields(self) -> None:
50+
with patch(
51+
"auralogger.cli_auth.resolve_project_token_for_init", return_value="ptok"
52+
), patch(
53+
"auralogger.cli_auth.resolve_user_secret_for_init", return_value="usec"
54+
), patch(
55+
"auralogger.cli_auth.fetch_proj_auth_payload",
56+
return_value={"project_id": "pid-1", "session": ""},
57+
):
58+
with self.assertRaises(ValueError):
59+
resolve_project_context_for_cli_checks()
60+
61+
62+
class CliCommandWiringTests(unittest.TestCase):
63+
def test_server_check_uses_shared_context_resolver(self) -> None:
64+
context = CliProjectContext(
65+
project_token="ptok",
66+
user_secret="usec",
67+
project_id="pid-1",
68+
project_name="proj",
69+
session="sess-1",
70+
)
71+
fake_ws = _FakeSocket()
72+
73+
with patch(
74+
"auralogger.commands.server_check.resolve_project_context_for_cli_checks",
75+
return_value=context,
76+
) as resolver, patch(
77+
"auralogger.commands.server_check.resolve_ws_base_url",
78+
return_value="wss://api.auralogger.com",
79+
), patch(
80+
"auralogger.commands.server_check.create_connection", return_value=fake_ws
81+
) as create_conn:
82+
run_server_check()
83+
84+
resolver.assert_called_once_with()
85+
create_conn.assert_called_once()
86+
kwargs = create_conn.call_args.kwargs
87+
self.assertEqual(kwargs["header"], ["Authorization: Bearer usec"])
88+
self.assertEqual(len(fake_ws.sent), 1)
89+
self.assertTrue(fake_ws.closed)
90+
91+
def test_client_check_uses_shared_context_resolver(self) -> None:
92+
context = CliProjectContext(
93+
project_token="ptok",
94+
user_secret="usec",
95+
project_id="pid-1",
96+
project_name="proj",
97+
session="sess-1",
98+
)
99+
fake_ws = _FakeSocket()
100+
101+
with patch(
102+
"auralogger.commands.client_check.resolve_project_context_for_cli_checks",
103+
return_value=context,
104+
) as resolver, patch(
105+
"auralogger.commands.client_check.resolve_ws_base_url",
106+
return_value="wss://api.auralogger.com",
107+
), patch(
108+
"auralogger.commands.client_check.create_connection", return_value=fake_ws
109+
) as create_conn:
110+
run_client_check()
111+
112+
resolver.assert_called_once_with()
113+
create_conn.assert_called_once_with(
114+
"wss://api.auralogger.com/ptok/create_browser_logs",
115+
timeout=5,
116+
)
117+
self.assertEqual(len(fake_ws.sent), 1)
118+
self.assertTrue(fake_ws.closed)
119+
120+
121+
class InitParityTests(unittest.TestCase):
122+
def test_run_init_already_configured_skips_proj_auth(self) -> None:
123+
output = io.StringIO()
124+
with patch(
125+
"auralogger.commands.init.is_full_runtime_env_configured", return_value=True
126+
), patch(
127+
"auralogger.commands.init.fetch_proj_auth_payload"
128+
) as fetch_proj_auth, redirect_stdout(output):
129+
run_init()
130+
131+
fetch_proj_auth.assert_not_called()
132+
rendered = output.getvalue()
133+
self.assertIn("already configured", rendered)
134+
self.assertIn("Server integration snippet (Python)", rendered)
135+
136+
def test_run_init_normal_path_prints_integration_help(self) -> None:
137+
output = io.StringIO()
138+
with patch(
139+
"auralogger.commands.init.is_full_runtime_env_configured", return_value=False
140+
), patch(
141+
"auralogger.commands.init.get_resolved_project_token", return_value=None
142+
), patch(
143+
"auralogger.commands.init._user_secret_explicitly_in_env", return_value=False
144+
), patch(
145+
"auralogger.commands.init.get_resolved_session", return_value=None
146+
), patch(
147+
"auralogger.commands.init.resolve_project_token_for_init", return_value="ptok"
148+
), patch(
149+
"auralogger.commands.init.resolve_user_secret_for_init", return_value="usec"
150+
), patch(
151+
"auralogger.commands.init.fetch_proj_auth_payload",
152+
return_value={"project_id": "pid-1", "session": "sess-1", "styles": []},
153+
), redirect_stdout(output):
154+
run_init()
155+
156+
rendered = output.getvalue()
157+
self.assertIn("Copy-paste env block", rendered)
158+
self.assertIn("Server integration snippet (Python)", rendered)
159+
self.assertIn("auralogger-cli/client", rendered)
160+
161+
162+
if __name__ == "__main__":
163+
unittest.main()

‎user-docs/commands.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ auralogger init
5050

5151
## `auralogger server-check`
5252

53-
**Requirements:** `AURALOGGER_PROJECT_TOKEN`, `AURALOGGER_USER_SECRET` (after CLI `.env` load). Calls `proj_auth`, then opens `WS /{project_token}/create_log` with **`Authorization: Bearer <user_secret>`** and sends one test log frame.
53+
**Credentials:** if `AURALOGGER_PROJECT_TOKEN` or `AURALOGGER_USER_SECRET` is missing after CLI `.env` load, the CLI prompts for missing values (same behavior as `init`). Calls `proj_auth`, then opens `WS /{project_token}/create_log` with **`Authorization: Bearer <user_secret>`** and sends one test log frame.
5454

5555
```bash
5656
auralogger server-check
@@ -111,7 +111,7 @@ auralogger get-logs -data.userId '["06431f39-55e2-4289-80c8-5d0340a8b66e"]'
111111

112112
## `auralogger client-check`
113113

114-
**Requirements:** `AURALOGGER_PROJECT_TOKEN`. Calls `proj_auth` to resolve `session`, then opens `WS /{project_token}/create_browser_logs` with **no auth headers** (path-only auth) and sends one test frame.
114+
**Credentials:** uses the same prompt flow as `server-check` / `init` for missing token/user-secret values, then calls `proj_auth` to resolve `session` and opens `WS /{project_token}/create_browser_logs` with **no auth headers** (path-only auth) and sends one test frame.
115115

116116
```bash
117117
auralogger client-check
@@ -121,7 +121,7 @@ auralogger client-check
121121

122122
## `auralogger test-serverlog`
123123

124-
**Requirements:** same as real server logging (`AURALOGGER_PROJECT_TOKEN`, `AURALOGGER_USER_SECRET`, and reachable API when hydration is needed). Sends 5 logs via `aura_log(...)`, waits briefly, then closes the cached socket.
124+
**Credentials:** prompts for missing `AURALOGGER_PROJECT_TOKEN` / `AURALOGGER_USER_SECRET` values, runs `AuraServer.sync_from_secret(...)` once, then sends 5 logs via `aura_log(...)`, waits briefly, and closes the cached socket.
125125

126126
```bash
127127
auralogger test-serverlog
@@ -131,7 +131,7 @@ auralogger test-serverlog
131131

132132
## `auralogger test-clientlog`
133133

134-
**Requirements:** `AURALOGGER_PROJECT_TOKEN`. Calls `proj_auth` to resolve `session`, opens one `create_browser_logs` socket (path-only auth), sends 5 frames, then closes.
134+
**Credentials:** prompts for missing `AURALOGGER_PROJECT_TOKEN`, calls `proj_auth` to resolve `session`, opens one `create_browser_logs` socket (path-only auth), sends 5 frames, then closes.
135135

136136
```bash
137137
auralogger test-clientlog

‎user-docs/environment.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ Older names such as `AURALOGGER_SECRET_KEY` are **not** read.
3333
| Context | What is required |
3434
|---------|------------------|
3535
| **`auralogger init`** | Loads `.env` / `.env.local` first. Optional: `AURALOGGER_PROJECT_TOKEN` and `AURALOGGER_USER_SECRET` in env to skip prompts. |
36-
| **`auralogger server-check`** | `AURALOGGER_PROJECT_TOKEN` and `AURALOGGER_USER_SECRET`; calls `proj_auth` then opens `WS …/create_log` with Bearer auth and sends a test log. |
36+
| **`auralogger server-check`** | Prompts for missing token/secret when needed, then calls `proj_auth`, opens `WS …/create_log` with Bearer auth, and sends a test log. |
37+
| **`auralogger client-check`** | Uses the same prompt flow as `init`, then calls `proj_auth`, opens `WS …/create_browser_logs` (path-only auth), and sends one test log. |
38+
| **`auralogger test-serverlog`** | Prompts for missing token/secret, runs `AuraServer.sync_from_secret(...)`, sends 5 logs, then closes the cached server socket. |
39+
| **`auralogger test-clientlog`** | Prompts for missing token, then sends a 5-log burst through `create_browser_logs` using path-only auth. |
3740
| **`auralogger get-logs`** | `AURALOGGER_PROJECT_TOKEN` and `AURALOGGER_USER_SECRET`; styles from env or fetched once via `proj_auth` for that run. |
3841
| **`aura_log()`** | `AURALOGGER_PROJECT_TOKEN` and `AURALOGGER_USER_SECRET`; id/session/styles from env or from a cached `proj_auth` fetch. Otherwise console-only with a one-time stderr hint. |
3942

0 commit comments

Comments
 (0)