pipecatcloud 0.3.1__tar.gz → 0.4.1__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 (67) hide show
  1. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/CHANGELOG.md +60 -0
  2. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/PKG-INFO +1 -1
  3. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/_utils/deploy_utils.py +23 -3
  4. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/api.py +66 -1
  5. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/agent.py +8 -19
  6. pipecatcloud-0.4.1/src/pipecatcloud/cli/commands/auth.py +601 -0
  7. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/deploy.py +2 -8
  8. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/docker.py +6 -1
  9. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/config.py +33 -11
  10. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/entry_point.py +10 -4
  11. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/config.py +2 -0
  12. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud.egg-info/PKG-INFO +1 -1
  13. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud.egg-info/SOURCES.txt +3 -1
  14. pipecatcloud-0.4.1/tests/conftest.py +17 -0
  15. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/test_api_client.py +0 -2
  16. pipecatcloud-0.4.1/tests/test_config_file.py +48 -0
  17. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/test_krisp_viva.py +1 -9
  18. pipecatcloud-0.4.1/tests/test_pat_auth.py +160 -0
  19. pipecatcloud-0.3.1/src/pipecatcloud/cli/commands/auth.py +0 -344
  20. pipecatcloud-0.3.1/tests/test_managed_keys.py +0 -426
  21. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/.github/workflows/format.yml +0 -0
  22. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/.github/workflows/publish-pypi.yml +0 -0
  23. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/.github/workflows/publish-test.yml +0 -0
  24. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/.github/workflows/tests.yml +0 -0
  25. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/.gitignore +0 -0
  26. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/.pre-commit-config.yaml +0 -0
  27. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/LICENSE +0 -0
  28. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/README.md +0 -0
  29. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/pipecat-cloud.png +0 -0
  30. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/pyproject.toml +0 -0
  31. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/pytest.ini +0 -0
  32. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/scripts/fix-ruff.sh +0 -0
  33. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/scripts/pre-commit.sh +0 -0
  34. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/setup.cfg +0 -0
  35. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/__init__.py +0 -0
  36. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/__main__.py +0 -0
  37. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/__version__.py +0 -0
  38. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/_utils/__init__.py +0 -0
  39. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/_utils/async_utils.py +0 -0
  40. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/_utils/auth_utils.py +0 -0
  41. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/_utils/build_utils.py +0 -0
  42. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/_utils/console_utils.py +0 -0
  43. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/_utils/regions.py +0 -0
  44. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/agent.py +0 -0
  45. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/__init__.py +0 -0
  46. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/api.py +0 -0
  47. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/__init__.py +0 -0
  48. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/build.py +0 -0
  49. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/organizations.py +0 -0
  50. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/regions.py +0 -0
  51. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/cli/commands/secrets.py +0 -0
  52. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/constants.py +0 -0
  53. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/exception.py +0 -0
  54. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/py.typed +0 -0
  55. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/session.py +0 -0
  56. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud/smallwebrtc/session_manager.py +0 -0
  57. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud.egg-info/dependency_links.txt +0 -0
  58. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud.egg-info/entry_points.txt +0 -0
  59. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud.egg-info/requires.txt +0 -0
  60. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/src/pipecatcloud.egg-info/top_level.txt +0 -0
  61. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/__init__.py +0 -0
  62. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/test_agent_sessions.py +0 -0
  63. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/test_agent_stop.py +0 -0
  64. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/test_deployment_status.py +0 -0
  65. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/test_docker_commands.py +0 -0
  66. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/tests/test_regions.py +0 -0
  67. {pipecatcloud-0.3.1 → pipecatcloud-0.4.1}/uv.lock +0 -0
@@ -5,6 +5,66 @@ All notable changes to **Pipecat Cloud** will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.1] - 2026-03-30
9
+
10
+ ### Added
11
+
12
+ - **Personal Access Token (PAT) support**: Authenticate with the CLI using a
13
+ PAT instead of interactive OAuth login. Set `PIPECAT_TOKEN=pcc_pat_...` as an
14
+ environment variable, or run `pcc auth use-pat <token>` to store it to config.
15
+ PATs work with all CLI commands and inherit the user's org memberships.
16
+
17
+ - `pcc auth use-pat` command: Validates a PAT against the API and stores it for
18
+ persistent use without needing an environment variable.
19
+
20
+ - `pcc auth whoami` now shows the current auth method (PAT or OAuth).
21
+
22
+ ### Changed
23
+
24
+ - **OAuth2 authentication**: `pcc auth login` now uses industry-standard
25
+ OAuth2 Authorization Code + PKCE, replacing the custom device-code flow.
26
+ Tokens auto-refresh transparently — no more "please run `pcc auth login`"
27
+ interruptions.
28
+
29
+ - `pcc auth logout` now revokes tokens server-side. Previously, logout only
30
+ deleted local credentials and the token remained valid until expiry.
31
+
32
+ - `pcc auth whoami` now displays the user's email address instead of the
33
+ internal user ID.
34
+
35
+ ### Fixed
36
+
37
+ - Credentials file with overly permissive permissions is now automatically
38
+ repaired to 600 instead of issuing a suppressible warning. If repair fails,
39
+ the CLI refuses to load the token.
40
+
41
+ ## [0.4.0] - 2026-03-29
42
+
43
+ ### Added
44
+
45
+ - **`--config-file` flag**: Specify an alternate deploy config file on any
46
+ command that uses a config file (`deploy`, `agent sessions`, `agent start`,
47
+ `agent stop`, `docker build-push`). Enables multi-environment workflows
48
+ without renaming files or setting environment variables.
49
+
50
+ ### Changed
51
+
52
+ - Renamed `--config` to `--show-cli-config` for clarity — it displays internal
53
+ CLI state (credentials, active org), not deploy configuration.
54
+
55
+ ### Removed
56
+
57
+ - Removed `--enable-managed-keys` CLI option from `deploy` command.
58
+
59
+ - Removed `enable_managed_keys` from `pcc-deploy.toml` configuration.
60
+
61
+ - Removed "Managed Keys" status display from `agent status` output.
62
+
63
+ ### Fixed
64
+
65
+ - CLI no longer crashes on a malformed credentials file. It warns and allows
66
+ `auth login` to run so the user can fix it.
67
+
8
68
  ## [0.3.1] - 2026-03-13
9
69
 
10
70
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pipecatcloud
3
- Version: 0.3.1
3
+ Version: 0.4.1
4
4
  Summary: Cloud hosting for Pipecat AI applications
5
5
  License: BSD 2-Clause License
6
6
  Project-URL: Website, https://docs.pipecat.daily.co/
@@ -10,10 +10,13 @@ from datetime import datetime, timezone
10
10
  from enum import Enum
11
11
  from typing import Callable, List, Optional
12
12
 
13
+
13
14
  import toml
15
+ import typer
14
16
  from attr import dataclass, field
15
17
  from loguru import logger
16
18
 
19
+ from pipecatcloud.cli import PIPECAT_DEPLOY_CONFIG_PATH
17
20
  from pipecatcloud.constants import KRISP_VIVA_MODELS, KrispVivaAudioFilter
18
21
  from pipecatcloud.exception import ConfigFileError
19
22
 
@@ -280,7 +283,6 @@ class DeployConfigParams:
280
283
  region: Optional[str] = None
281
284
  scaling: ScalingParams = ScalingParams()
282
285
  enable_krisp: bool = False
283
- enable_managed_keys: bool = False
284
286
  docker_config: dict = field(factory=dict)
285
287
  build_config: BuildConfig = field(factory=BuildConfig) # Cloud build configuration
286
288
  agent_profile: Optional[str] = None
@@ -303,7 +305,6 @@ class DeployConfigParams:
303
305
  "region": self.region,
304
306
  "scaling": self.scaling.to_dict() if self.scaling else None,
305
307
  "enable_krisp": self.enable_krisp,
306
- "enable_managed_keys": self.enable_managed_keys,
307
308
  "docker_config": self.docker_config,
308
309
  "build_config": self.build_config.to_dict() if self.build_config else None,
309
310
  "agent_profile": self.agent_profile,
@@ -363,7 +364,6 @@ def load_deploy_config_file() -> Optional[DeployConfigParams]:
363
364
  "region",
364
365
  "scaling",
365
366
  "enable_krisp",
366
- "enable_managed_keys",
367
367
  "docker",
368
368
  "build",
369
369
  "agent_profile",
@@ -380,14 +380,34 @@ def load_deploy_config_file() -> Optional[DeployConfigParams]:
380
380
  raise ConfigFileError(str(e))
381
381
 
382
382
 
383
+ CONFIG_FILE_OPTION: Optional[str] = typer.Option(
384
+ None,
385
+ "--config-file",
386
+ help=f"Path to deploy config file (default: {PIPECAT_DEPLOY_CONFIG_PATH})",
387
+ )
388
+
389
+
383
390
  def with_deploy_config(func: Callable) -> Callable:
384
391
  """
385
392
  Decorator that loads the deploy config file and injects it into the function.
386
393
  If the config file exists, it will be loaded and passed to the function as `deploy_config`.
394
+
395
+ If the wrapped function receives a `config_file` kwarg (from a --config-file typer option),
396
+ it will override the default deploy config path before loading.
387
397
  """
388
398
 
389
399
  @functools.wraps(func)
390
400
  def wrapper(*args, **kwargs):
401
+ config_file = kwargs.pop("config_file", None)
402
+ if config_file:
403
+ import pipecatcloud.cli.config as config_module
404
+
405
+ if not os.path.exists(config_file):
406
+ from pipecatcloud._utils.console_utils import console
407
+
408
+ console.error(f"Deploy config file not found: {config_file}")
409
+ raise typer.Exit(1)
410
+ config_module.deploy_config_path = config_file
391
411
  try:
392
412
  deploy_config = load_deploy_config_file()
393
413
  kwargs["deploy_config"] = deploy_config
@@ -5,6 +5,7 @@
5
5
  #
6
6
 
7
7
  import json
8
+ import time
8
9
  from functools import wraps
9
10
  from typing import Callable, List, Optional, Union
10
11
 
@@ -52,6 +53,62 @@ class _API:
52
53
  return {}
53
54
  return {"Authorization": f"Bearer {override_token or self.token}"}
54
55
 
56
+ def _is_pat(self) -> bool:
57
+ """Check if the current token is a Personal Access Token."""
58
+ return bool(self.token and self.token.startswith("pcc_pat_"))
59
+
60
+ def _is_token_expired(self) -> bool:
61
+ """Check if the current OAuth token is near expiry.
62
+
63
+ Returns False for PATs (they don't expire via OAuth) and for
64
+ non-OAuth tokens (no token_expires_at stored).
65
+ """
66
+ if self._is_pat():
67
+ return False
68
+
69
+ from pipecatcloud.cli.config import config as cli_config
70
+
71
+ expires_at = cli_config.get("token_expires_at")
72
+ if expires_at is None:
73
+ return False
74
+ # Refresh 60 seconds before actual expiry to avoid race conditions
75
+ return time.time() > (float(expires_at) - 60)
76
+
77
+ async def _refresh_oauth_token(self) -> Optional[str]:
78
+ """Refresh the OAuth access token using the stored refresh token.
79
+
80
+ Updates both the on-disk config and the in-memory token on this API
81
+ instance. Returns the new access token, or None if refresh failed
82
+ (in which case the request will proceed with the expired token and
83
+ the server will return 401, prompting re-login).
84
+ """
85
+ from pipecatcloud.cli.config import config as cli_config, update_user_config
86
+
87
+ # Inline import: auth.py → cli/api.py → api.py creates a circular
88
+ # dependency at module load time. Importing here (at call time)
89
+ # breaks the cycle.
90
+ from pipecatcloud.cli.commands.auth import refresh_access_token
91
+
92
+ refresh_token = cli_config.get("refresh_token")
93
+ if not refresh_token:
94
+ return None
95
+
96
+ tokens = await refresh_access_token(refresh_token)
97
+ if not tokens:
98
+ return None
99
+
100
+ new_token = tokens["access_token"]
101
+ new_refresh = tokens.get("refresh_token", refresh_token)
102
+ expires_at = time.time() + tokens.get("expires_in", 86400)
103
+
104
+ update_user_config(
105
+ token=new_token,
106
+ refresh_token=new_refresh,
107
+ token_expires_at=expires_at,
108
+ )
109
+ self.token = new_token
110
+ return new_token
111
+
55
112
  async def _base_request(
56
113
  self,
57
114
  method: str,
@@ -61,6 +118,15 @@ class _API:
61
118
  not_found_is_empty: bool = False,
62
119
  override_token: Optional[str] = None,
63
120
  ) -> Optional[dict]:
121
+ # Auto-refresh expired OAuth tokens before making the request.
122
+ # Only runs for CLI usage (self.is_cli) and only when an OAuth
123
+ # refresh_token is stored (token_expires_at is set). Old device-code
124
+ # tokens are unaffected since _is_token_expired() returns False.
125
+ if self.is_cli and not override_token and self._is_token_expired():
126
+ new_token = await self._refresh_oauth_token()
127
+ if new_token is None:
128
+ logger.debug("Token refresh failed, proceeding with expired token")
129
+
64
130
  async with aiohttp.ClientSession() as session:
65
131
  logger.debug(f"Request: {method} {url} {params} {json}")
66
132
 
@@ -336,7 +402,6 @@ class _API:
336
402
  "maxAgents": deploy_config.scaling.max_agents,
337
403
  },
338
404
  "enableKrisp": deploy_config.enable_krisp,
339
- "enableIntegratedKeysProxy": deploy_config.enable_managed_keys, # API expects this field name
340
405
  "agentProfile": deploy_config.agent_profile,
341
406
  "krispViva": {
342
407
  "audioFilter": deploy_config.krisp_viva.audio_filter,
@@ -28,7 +28,11 @@ from pipecatcloud._utils.console_utils import (
28
28
  format_duration,
29
29
  format_timestamp,
30
30
  )
31
- from pipecatcloud._utils.deploy_utils import DeployConfigParams, with_deploy_config
31
+ from pipecatcloud._utils.deploy_utils import (
32
+ CONFIG_FILE_OPTION,
33
+ DeployConfigParams,
34
+ with_deploy_config,
35
+ )
32
36
  from pipecatcloud._utils.regions import get_region_codes, validate_region
33
37
  from pipecatcloud.cli import PIPECAT_CLI_NAME
34
38
  from pipecatcloud.cli.api import API
@@ -206,24 +210,6 @@ async def status(
206
210
  str(data.get("updatedAt", "N/A")),
207
211
  )
208
212
 
209
- # Check for Managed Keys status
210
- # API returns integratedKeysProxy but we display as "Managed Keys"
211
- integrated_keys = (
212
- data.get("deployment", {})
213
- .get("manifest", {})
214
- .get("spec", {})
215
- .get("integratedKeysProxy", {})
216
- )
217
- if isinstance(integrated_keys, dict):
218
- integrated_keys_enabled = integrated_keys.get("enabled", False)
219
- else:
220
- integrated_keys_enabled = bool(integrated_keys)
221
-
222
- deployment_table.add_row(
223
- "[bold]Managed Keys:[/bold]",
224
- "[green]Enabled[/green]" if integrated_keys_enabled else "[dim]Disabled[/dim]",
225
- )
226
-
227
213
  # Check for Krisp VIVA status (reverse-mapped by API)
228
214
  krisp_viva = data.get("krispViva")
229
215
  krisp_viva_status = "[dim]Disabled[/dim]"
@@ -346,6 +332,7 @@ async def status(
346
332
  @with_deploy_config
347
333
  async def sessions(
348
334
  deploy_config=typer.Option(None, hidden=True),
335
+ config_file: Optional[str] = CONFIG_FILE_OPTION,
349
336
  agent_name: str = typer.Argument(
350
337
  None, help="Name of the agent to list sessions for e.g. 'my-agent'", show_default=False
351
338
  ),
@@ -781,6 +768,7 @@ async def deployments(
781
768
  @with_deploy_config
782
769
  async def start(
783
770
  deploy_config=typer.Option(None, hidden=True),
771
+ config_file: Optional[str] = CONFIG_FILE_OPTION,
784
772
  agent_name: str = typer.Argument(None, help="Name of the agent to start e.g. 'my-agent'"),
785
773
  force: bool = typer.Option(
786
774
  False,
@@ -947,6 +935,7 @@ async def start(
947
935
  @with_deploy_config
948
936
  async def stop(
949
937
  deploy_config=typer.Option(None, hidden=True),
938
+ config_file: Optional[str] = CONFIG_FILE_OPTION,
950
939
  agent_name: str = typer.Argument(None, help="Name of the agent e.g. 'my-agent'"),
951
940
  session_id: str = typer.Option(
952
941
  ...,