mainsequence 9.0.21.dev63__py3-none-any.whl → 9.0.22.dev65__py3-none-any.whl
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.
- mainsequence/cli/api.py +22 -2
- mainsequence/cli/cli.py +4 -0
- mainsequence/client/agent_runtime_models.py +1 -1
- mainsequence/client/utils.py +4 -4
- mainsequence/logconf.py +38 -10
- {mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/METADATA +1 -1
- {mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/RECORD +12 -12
- /mainsequence/{client/_transport.py → _transport.py} +0 -0
- {mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/WHEEL +0 -0
- {mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/entry_points.txt +0 -0
- {mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/licenses/LICENSE +0 -0
- {mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/top_level.txt +0 -0
mainsequence/cli/api.py
CHANGED
|
@@ -63,7 +63,27 @@ MCP_PROTOCOL_VERSION = "2025-11-25"
|
|
|
63
63
|
MCP_CLIENT_NAME = "mainsequence-cli"
|
|
64
64
|
MCP_MAX_RESOURCE_PAGES = 100
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
|
|
67
|
+
class _CliSession(requests.Session):
|
|
68
|
+
"""The CLI's HTTP session.
|
|
69
|
+
|
|
70
|
+
A request sent without its own timeout gets the SDK's default request
|
|
71
|
+
budget: 5 s to connect, 120 s to read and 125 s in total, inside any budget
|
|
72
|
+
that is already running.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def request(self, method, url, **kwargs):
|
|
76
|
+
if kwargs.get("timeout") is not None:
|
|
77
|
+
return super().request(method, url, **kwargs)
|
|
78
|
+
# The CLI imports the SDK client on its first request, not when it starts.
|
|
79
|
+
from mainsequence._transport import DEFAULT_TIMEOUT, request_budget
|
|
80
|
+
|
|
81
|
+
with request_budget(DEFAULT_TIMEOUT) as budget:
|
|
82
|
+
kwargs["timeout"] = budget.timeout()
|
|
83
|
+
return super().request(method, url, **kwargs)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
S = _CliSession()
|
|
67
87
|
S.headers.update({"Content-Type": "application/json"})
|
|
68
88
|
|
|
69
89
|
|
|
@@ -494,7 +514,7 @@ def _mcp_json_rpc(
|
|
|
494
514
|
default request budget: 5 s to connect, 120 s to read, 125 s in total.
|
|
495
515
|
"""
|
|
496
516
|
# The CLI imports the SDK client only where it needs it.
|
|
497
|
-
from mainsequence.
|
|
517
|
+
from mainsequence._transport import DEFAULT_TIMEOUT, request_budget
|
|
498
518
|
|
|
499
519
|
payload = {
|
|
500
520
|
"jsonrpc": "2.0",
|
mainsequence/cli/cli.py
CHANGED
|
@@ -1946,6 +1946,10 @@ def login(
|
|
|
1946
1946
|
except ApiError as e:
|
|
1947
1947
|
error(f"Login failed: {e}")
|
|
1948
1948
|
raise typer.Exit(1) from e
|
|
1949
|
+
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e:
|
|
1950
|
+
# Never echo the exception: a transport error can carry request details.
|
|
1951
|
+
error(f"The backend at {normalized_backend} could not be reached ({type(e).__name__}).")
|
|
1952
|
+
raise typer.Exit(1) from e
|
|
1949
1953
|
finally:
|
|
1950
1954
|
if previous_backend_override is None:
|
|
1951
1955
|
os.environ.pop("MAINSEQUENCE_ENDPOINT", None)
|
|
@@ -12,7 +12,7 @@ from typing import Any, ClassVar, Literal
|
|
|
12
12
|
import requests
|
|
13
13
|
from pydantic import ConfigDict, Field, model_validator
|
|
14
14
|
|
|
15
|
-
from
|
|
15
|
+
from .._transport import request_budget, request_within_budget
|
|
16
16
|
from .base import (
|
|
17
17
|
BaseObjectOrm,
|
|
18
18
|
BasePydanticModel,
|
mainsequence/client/utils.py
CHANGED
|
@@ -28,10 +28,10 @@ from mainsequence.runtime_credential_exchange import (
|
|
|
28
28
|
identity_token_file_from_environment,
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
from
|
|
32
|
-
from
|
|
33
|
-
from
|
|
34
|
-
from
|
|
31
|
+
from .._transport import DEFAULT_ALLOWED_METHODS as DEFAULT_ALLOWED_METHODS
|
|
32
|
+
from .._transport import DEFAULT_STATUS_FORCELIST as DEFAULT_STATUS_FORCELIST
|
|
33
|
+
from .._transport import DEFAULT_TIMEOUT as DEFAULT_TIMEOUT
|
|
34
|
+
from .._transport import DeadlineHTTPAdapter, remaining_timeout, request_budget
|
|
35
35
|
|
|
36
36
|
# ---- Backend defaults (single source of truth) ----
|
|
37
37
|
MAINSEQUENCE_ENDPOINT = resolve_backend_endpoint()
|
mainsequence/logconf.py
CHANGED
|
@@ -5,7 +5,6 @@ import logging
|
|
|
5
5
|
import logging.config
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
-
import time
|
|
9
8
|
import traceback
|
|
10
9
|
from collections.abc import Mapping
|
|
11
10
|
from importlib.metadata import PackageNotFoundError
|
|
@@ -20,6 +19,7 @@ from structlog.contextvars import bind_contextvars, unbind_contextvars
|
|
|
20
19
|
from structlog.dev import ConsoleRenderer
|
|
21
20
|
from structlog.stdlib import BoundLogger
|
|
22
21
|
|
|
22
|
+
from ._transport import DeadlineHTTPAdapter, request_budget
|
|
23
23
|
from .defaults import resolve_backend_endpoint
|
|
24
24
|
from .instrumentation import OTelJSONRenderer
|
|
25
25
|
from .repository_identity_security import UNSUPPORTED_SOURCE_IDENTITY_ENV_NAMES
|
|
@@ -76,14 +76,27 @@ class CustomConsoleRenderer(ConsoleRenderer):
|
|
|
76
76
|
return rendered
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def _request_job_startup_state(*, timeout_s: float =
|
|
79
|
+
def _request_job_startup_state(*, timeout_s: float | None = None) -> dict[str, Any]:
|
|
80
80
|
"""
|
|
81
81
|
Fetch startup state from backend using current env vars (JWT auth, endpoint, job_run_uid).
|
|
82
82
|
Safe to call later after auth when access/refresh tokens become available.
|
|
83
|
+
|
|
84
|
+
The whole read, token renewal included, ends within one request budget:
|
|
85
|
+
``timeout_s`` seconds in total, or the SDK's default request budget when it is
|
|
86
|
+
None. The GET is retried like any other SDK read. The startup state only
|
|
87
|
+
enriches the logger, so when the backend cannot be reached or does not answer
|
|
88
|
+
in time this reports it on stderr and returns an empty state instead of
|
|
89
|
+
failing the import.
|
|
83
90
|
"""
|
|
84
91
|
if not is_running_in_pod():
|
|
85
92
|
return {}
|
|
86
93
|
|
|
94
|
+
with request_budget(timeout_s) as budget:
|
|
95
|
+
return _read_job_startup_state(budget)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _read_job_startup_state(budget) -> dict[str, Any]:
|
|
99
|
+
|
|
87
100
|
auth_mode = (os.getenv("MAINSEQUENCE_AUTH_MODE") or "jwt").strip().lower()
|
|
88
101
|
|
|
89
102
|
def _backend_base_url() -> str:
|
|
@@ -110,7 +123,7 @@ def _request_job_startup_state(*, timeout_s: float = 10.0) -> dict[str, Any]:
|
|
|
110
123
|
token_url,
|
|
111
124
|
headers={"Content-Type": "application/json"},
|
|
112
125
|
json=body,
|
|
113
|
-
timeout=
|
|
126
|
+
timeout=budget.timeout(),
|
|
114
127
|
allow_redirects=False,
|
|
115
128
|
)
|
|
116
129
|
|
|
@@ -119,7 +132,7 @@ def _request_job_startup_state(*, timeout_s: float = 10.0) -> dict[str, Any]:
|
|
|
119
132
|
_post,
|
|
120
133
|
credential_id=(os.getenv(RUNTIME_CREDENTIAL_ID_ENV) or "").strip(),
|
|
121
134
|
identity_token_file=identity_token_file_from_environment(),
|
|
122
|
-
deadline=
|
|
135
|
+
deadline=budget.deadline,
|
|
123
136
|
)
|
|
124
137
|
except Exception:
|
|
125
138
|
return False
|
|
@@ -155,7 +168,7 @@ def _request_job_startup_state(*, timeout_s: float = 10.0) -> dict[str, Any]:
|
|
|
155
168
|
f"{_backend_base_url()}/auth/jwt-token/token/refresh/",
|
|
156
169
|
headers={"Content-Type": "application/json"},
|
|
157
170
|
json={"refresh": refresh_token},
|
|
158
|
-
timeout=
|
|
171
|
+
timeout=budget.timeout(),
|
|
159
172
|
)
|
|
160
173
|
except Exception:
|
|
161
174
|
return False
|
|
@@ -202,10 +215,25 @@ def _request_job_startup_state(*, timeout_s: float = 10.0) -> dict[str, Any]:
|
|
|
202
215
|
endpoint = f"{_backend_base_url()}/api/v1/job-runs/{job_run_uid}/startup-state/"
|
|
203
216
|
params: dict[str, Any] = {}
|
|
204
217
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
218
|
+
try:
|
|
219
|
+
with requests.Session() as session:
|
|
220
|
+
adapter = DeadlineHTTPAdapter(retries=3, backoff_factor=0.5)
|
|
221
|
+
session.mount("https://", adapter)
|
|
222
|
+
session.mount("http://", adapter)
|
|
223
|
+
resp = session.get(endpoint, headers=headers, params=params, timeout=budget.timeout())
|
|
224
|
+
if resp.status_code in {401, 403} and using_jwt and _refresh_access_token():
|
|
225
|
+
headers, _ = _auth_headers()
|
|
226
|
+
resp = session.get(
|
|
227
|
+
endpoint, headers=headers, params=params, timeout=budget.timeout()
|
|
228
|
+
)
|
|
229
|
+
except requests.RequestException as exc:
|
|
230
|
+
# The exception text is not echoed: it can carry the request URL and headers.
|
|
231
|
+
print(
|
|
232
|
+
"mainsequence: the Job startup state could not be read "
|
|
233
|
+
f"({type(exc).__name__}); continuing without it.",
|
|
234
|
+
file=sys.stderr,
|
|
235
|
+
)
|
|
236
|
+
return {}
|
|
209
237
|
|
|
210
238
|
if resp.status_code != 200:
|
|
211
239
|
# Do not spam stdout during CLI usage when auth context is not present yet.
|
|
@@ -426,7 +454,7 @@ def build_application_logger(application_name: str = "ms-sdk", **metadata):
|
|
|
426
454
|
return logger
|
|
427
455
|
|
|
428
456
|
|
|
429
|
-
def refresh_application_logger_bindings(*, timeout_s: float =
|
|
457
|
+
def refresh_application_logger_bindings(*, timeout_s: float | None = None) -> BoundLogger:
|
|
430
458
|
"""
|
|
431
459
|
Importable helper: call this AFTER the user authenticates / token exists.
|
|
432
460
|
It re-fetches backend state and binds the same keys as build_application_logger().
|
|
@@ -11,19 +11,20 @@ agent_scaffold/skills/sdk_code_repository_execution/SKILL.md,sha256=zly7MheB-Sjd
|
|
|
11
11
|
mainsequence/__init__.py,sha256=70CbIo7ZG1mzi5uwkqDtVESunBE0RanHjeFg4ptfsA0,385
|
|
12
12
|
mainsequence/__main__.py,sha256=KuAP69uduS2ImrPIgNMzbGBRepZzjqvUONu5vFh4v4A,176
|
|
13
13
|
mainsequence/_request_identity.py,sha256=vsbnQeJ9ycRB2p8oFTUxfPkzDh7-SBQ5Pxzn04pjqVM,4183
|
|
14
|
+
mainsequence/_transport.py,sha256=gFSw5bl4O2o3NwZOk-ueNnTQvySBecyhC2-7OPb-BPk,8425
|
|
14
15
|
mainsequence/bootstrap.py,sha256=AvG55Y46RcshVqJZsyqvUDN62CJg4CDbHu0Bvsbwfzs,2910
|
|
15
16
|
mainsequence/code_repository_context.py,sha256=JRrWjwpnUy8I-OjSd9hHlrcq8-Ow3Zeh_N6cYvmtA3o,28545
|
|
16
17
|
mainsequence/code_repository_skills.py,sha256=AyC1HFAxQXHXNBGbarVUJS2cHd4X5HwDx6u8XBophbE,23611
|
|
17
18
|
mainsequence/defaults.py,sha256=5r6s7AI-X40rpm1D3IgcNsWam7iJg4_lIxB3F_1u2z0,408
|
|
18
|
-
mainsequence/logconf.py,sha256=
|
|
19
|
+
mainsequence/logconf.py,sha256=q4aTXScwR3lW1nOB3KdamwUDRR7dNeKvmfFtp36msI0,20274
|
|
19
20
|
mainsequence/repository_identity_security.py,sha256=Cdywndhiy1zJFUPh-FfM90RjZ3Hg3tjhxBr64FRhmXs,1035
|
|
20
21
|
mainsequence/runtime_credential_exchange.py,sha256=D-gDGHljKnGbMfOiUi_uJ9b5yVFkYB1CB2hikYxTdO4,6152
|
|
21
22
|
mainsequence/runtime_flags.py,sha256=1eTM7VRRhnuZ_GXcmFhqAmdEKwyQYMyZcPsio88vJRQ,378
|
|
22
23
|
mainsequence/scaffold_skills.py,sha256=k0V6eKC5AwbHr9wB9UGMJNF-8eJ0_1izF4-BU01KHrg,10606
|
|
23
24
|
mainsequence/cli/__init__.py,sha256=sMWTnomCaISjnureLuXFRqOzkH2Y77uYEivsd-M6_8Y,28
|
|
24
|
-
mainsequence/cli/api.py,sha256=
|
|
25
|
+
mainsequence/cli/api.py,sha256=u3vjwJ5w_u4o76ufkePXKIh6dixOHDok_r8Os0iA_04,128865
|
|
25
26
|
mainsequence/cli/browser_auth.py,sha256=Vb5MBOnPC05HLZzVNUDODobJ_p5wEIzH_h8rIlpShlE,9984
|
|
26
|
-
mainsequence/cli/cli.py,sha256=
|
|
27
|
+
mainsequence/cli/cli.py,sha256=8EM0j2rRvGX-t1ByfkVD8HUUx5QgK1BiurCImZfjqYI,269846
|
|
27
28
|
mainsequence/cli/code_repository_status.py,sha256=SHEnU8YSWFowPG6dEdE4Yd8CS1tIcJSE1MXFLfvD3Os,5975
|
|
28
29
|
mainsequence/cli/config.py,sha256=TJYnM11kg5pH2O7AGWFLpNEkC9UEu0g7k2RLjhAHCzo,45724
|
|
29
30
|
mainsequence/cli/docker_utils.py,sha256=eAnp9MI042cHWwKasA5IF2I7INL3A6tV2JX-cSIe7rc,10687
|
|
@@ -35,8 +36,7 @@ mainsequence/cli/sdk_utils.py,sha256=SFuSzh8WDNf21mLiDa51K8Fqe5XiLWh2KhHj8zUjCXo
|
|
|
35
36
|
mainsequence/cli/ssh_utils.py,sha256=m7HgFHZQ545sxY9igfQLpaNjWHm4SiN9VwCkAuGy2yw,11124
|
|
36
37
|
mainsequence/cli/ui.py,sha256=memd6pmjALfGj8zndGCx_z80ztQeqTcOvcofU0un34U,2277
|
|
37
38
|
mainsequence/client/__init__.py,sha256=lsr9JWxeVqOG0KRoHErW5M40ACPSR7SAEE508dTN5XM,672
|
|
38
|
-
mainsequence/client/
|
|
39
|
-
mainsequence/client/agent_runtime_models.py,sha256=6YbX1K8fA4UxbuOb9GMjbHYJJ9z50oKHExRITp78yyA,74616
|
|
39
|
+
mainsequence/client/agent_runtime_models.py,sha256=xjXq0MyLGrXvwJRsz39EDv56I4X-iCbMn51nB_5Yte8,74617
|
|
40
40
|
mainsequence/client/base.py,sha256=ZxuwUIscBNQGvByd7NGbn03VtNU9nazqkEouyKfVLk0,43129
|
|
41
41
|
mainsequence/client/client.py,sha256=Jaw9RYObdVDS5J2O0pln99-XpuW9e4vW-SsbJRNldfw,3866
|
|
42
42
|
mainsequence/client/compute_validation.py,sha256=ByeX0CkIE7qq06ggZFfN92U3Qp2e9cvo2RTIGWa5_Vw,6662
|
|
@@ -47,16 +47,16 @@ mainsequence/client/models_foundry.py,sha256=JwQdPLDSO4dZI-KC0SUdmN6izmNIKMb9IzZ
|
|
|
47
47
|
mainsequence/client/models_helpers.py,sha256=CQRjBjx3DsREJLRk0vqFsC-Dcll5lUP2ofu6e5UMvhk,49051
|
|
48
48
|
mainsequence/client/models_user.py,sha256=aOs_ouI1j9sr2oAaV94xhrD6KcN1DYKBcjPMZxkhD88,46235
|
|
49
49
|
mainsequence/client/observability.py,sha256=_WrDKCaNk_TXmKElKCXC1inork6Exxxe8FRY4m1GI9s,16008
|
|
50
|
-
mainsequence/client/utils.py,sha256=
|
|
50
|
+
mainsequence/client/utils.py,sha256=Rk2z-78swO6GepibLlQNdxL1eqYy8vHGrjiZdV3G5CM,31641
|
|
51
51
|
mainsequence/client/value_sets.py,sha256=7TA-mpuAd3hOduNgO1WTQQTuNOGaFgue-VqCsDWKaC8,6181
|
|
52
52
|
mainsequence/instrumentation/__init__.py,sha256=J_9XmfjprElvz0u2t9rTyJT0kFdyKpxdbkdQiaw28_U,316
|
|
53
53
|
mainsequence/instrumentation/utils.py,sha256=b-ihjSioAepeLfMpudM2SqCtvnxMqIXileHx3klBCQU,3377
|
|
54
54
|
mainsequence/server/__init__.py,sha256=BHUAhtdWPDRdxX07NYO_6wrBv5mf0GsXflecO52PmUc,73
|
|
55
55
|
mainsequence/server/caller_assertions.py,sha256=7OEgPXxiGv-P98K6-XVKFSiNcYZkEFDmehBc_oJKkFM,12563
|
|
56
56
|
mainsequence/server/fastapi.py,sha256=ZMtazH8ZN2C82uTkBtl0UQdx6F9mlCHD1HurgZQdHbU,18512
|
|
57
|
-
mainsequence-9.0.
|
|
58
|
-
mainsequence-9.0.
|
|
59
|
-
mainsequence-9.0.
|
|
60
|
-
mainsequence-9.0.
|
|
61
|
-
mainsequence-9.0.
|
|
62
|
-
mainsequence-9.0.
|
|
57
|
+
mainsequence-9.0.22.dev65.dist-info/licenses/LICENSE,sha256=b6Ecsk8wjsEby3lbSzdljR6hHiFGHBizt9iWV2p1c_I,1074
|
|
58
|
+
mainsequence-9.0.22.dev65.dist-info/METADATA,sha256=iVI-Dap3Gl5LvTSpmppG5uR9Q8vMKnRauM7J-GURhew,7467
|
|
59
|
+
mainsequence-9.0.22.dev65.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
60
|
+
mainsequence-9.0.22.dev65.dist-info/entry_points.txt,sha256=XGAOq3-mF2lWWpeRJvoaWyMarRcK167mH1g124aGdUs,88
|
|
61
|
+
mainsequence-9.0.22.dev65.dist-info/top_level.txt,sha256=BFeblM_Q9q0cUWBvTxnfWcWQEHtf5XZi9nOS-Gdfwus,28
|
|
62
|
+
mainsequence-9.0.22.dev65.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{mainsequence-9.0.21.dev63.dist-info → mainsequence-9.0.22.dev65.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|