mainsequence 6.0.30__tar.gz → 6.0.32__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.
- {mainsequence-6.0.30/mainsequence.egg-info → mainsequence-6.0.32}/PKG-INFO +1 -1
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +39 -7
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/api.py +10 -10
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/cli.py +140 -55
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/metatables/core.py +4 -28
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/models_helpers.py +79 -32
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/compiled_sql/v1.py +7 -6
- {mainsequence-6.0.30 → mainsequence-6.0.32/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-6.0.30 → mainsequence-6.0.32}/pyproject.toml +1 -1
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_cli.py +169 -165
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_filter_normalization.py +138 -23
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_meta_tables_client_models.py +4 -35
- {mainsequence-6.0.30 → mainsequence-6.0.32}/LICENSE +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/README.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/maintenance/project-maintenance/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/agent_scaffold/skills/sdk_project_execution/SKILL.md +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/__main__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/bootstrap.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/config.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/cli/ui.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/base.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/client.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/models_user.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/client/utils.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/defaults.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/logconf.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/alembic.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/env.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/provider.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/registry.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/project_skills.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/runtime_context.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence/scaffold_skills.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/setup.cfg +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_auth_precedence.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_canonical_api_route_contract.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_cli_migrations.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_client.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_dependency_extras.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_instrumentation.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_logconf.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_notification_uid_contract.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_project_branch_contract.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_project_skills.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_run_configuration.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_scaffold_skills.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_schema_names.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_secret_client_model.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_update_statistics.py +0 -0
- {mainsequence-6.0.30 → mainsequence-6.0.32}/tests/test_update_uid_guards.py +0 -0
|
@@ -27,7 +27,8 @@ This skill is for:
|
|
|
27
27
|
- author backend-managed declarations under `.mainsequence/workflows/`
|
|
28
28
|
- validate workflow files through the ProjectBranch workflow endpoints
|
|
29
29
|
- create or select project images
|
|
30
|
-
-
|
|
30
|
+
- bind every job to one exact project image
|
|
31
|
+
- configure standalone Job automatic deployment as future exact-image promotion
|
|
31
32
|
- inspect job runs and logs
|
|
32
33
|
- reason about project resources and resource releases
|
|
33
34
|
- decide whether a `ResourceRelease` should opt into `automatic_deployment`
|
|
@@ -91,7 +92,9 @@ Before changing orchestration or release behavior, collect or infer:
|
|
|
91
92
|
- Streamlit dashboard release creation
|
|
92
93
|
- whether a `ResourceRelease` should be manually pinned or opted into repository-sync automatic deployment
|
|
93
94
|
- whether Artifact inputs or outputs are part of the run
|
|
94
|
-
-
|
|
95
|
+
- who owns the exact initial image: the caller for manual pinning or the
|
|
96
|
+
backend for automatic deployment
|
|
97
|
+
- whether future qualifying immutable repository events may promote a standalone Job
|
|
95
98
|
- for Streamlit dashboard deployment, the `README.md` resource next to `app.py`
|
|
96
99
|
|
|
97
100
|
If the execution target or image strategy is unclear, stop before scheduling anything.
|
|
@@ -102,7 +105,8 @@ For every non-trivial orchestration task, decide:
|
|
|
102
105
|
|
|
103
106
|
1. Is this a one-off/manual workflow or a repository-managed recurring workflow?
|
|
104
107
|
2. Should the jobs live in a `.mainsequence/workflows/*.yaml` declaration?
|
|
105
|
-
3.
|
|
108
|
+
3. Is the Job manually pinned to a caller-selected exact ready image, or does
|
|
109
|
+
automatic deployment make the backend derive it from the synchronized commit?
|
|
106
110
|
4. Does the workflow depend on Artifacts?
|
|
107
111
|
5. Is the task actually a release/resource problem instead of only a job problem?
|
|
108
112
|
6. For Streamlit dashboard deployment, do the selected app resource, README resource, and project image all refer to the intended pushed commit?
|
|
@@ -131,15 +135,37 @@ an existing backend resource.
|
|
|
131
135
|
|
|
132
136
|
Do not hide important recurring schedules in ad hoc shell history or one-off manual commands.
|
|
133
137
|
|
|
134
|
-
### 2.
|
|
138
|
+
### 2. Every Job requires one exact image
|
|
135
139
|
|
|
136
|
-
|
|
140
|
+
Every persisted direct Job has one exact project image, but the creation mode
|
|
141
|
+
determines who supplies it.
|
|
137
142
|
|
|
138
143
|
Remember:
|
|
139
144
|
|
|
140
145
|
- images are built from pushed commits
|
|
141
146
|
- if a commit is not on the remote, it cannot be used for an image
|
|
142
|
-
-
|
|
147
|
+
- there is no dynamic, blank-image, branch-tip, or `latest` Job mode
|
|
148
|
+
- a manual Job requires the caller to select `related_image_uid`
|
|
149
|
+
- an automatic Job forbids caller image selection; the backend derives the
|
|
150
|
+
exact initial image from the ProjectBranch's persisted synchronized commit
|
|
151
|
+
|
|
152
|
+
When standalone Job automatic deployment is enabled during creation, the
|
|
153
|
+
backend prepares the exact initial image and later qualifying immutable
|
|
154
|
+
repository events may atomically promote another exact image. On an existing
|
|
155
|
+
Job, enabling or disabling it retains the current exact image. The backend owns
|
|
156
|
+
repository-event truth, policy evaluation, image preparation, and promotion;
|
|
157
|
+
project code and the SDK must not inspect Git or choose a runtime branch.
|
|
158
|
+
|
|
159
|
+
For direct creation use:
|
|
160
|
+
|
|
161
|
+
- `--automatic-deployment` to permit future exact-image promotion
|
|
162
|
+
- `--no-automatic-deployment` to keep future repository events from changing
|
|
163
|
+
the Job image
|
|
164
|
+
- `--automatic-redeployment-tag-regex` to require a full matching immutable
|
|
165
|
+
tag; omit the regex for every otherwise-eligible exact event
|
|
166
|
+
|
|
167
|
+
Use `--related-image-uid` only for manual pinning. Omit it with
|
|
168
|
+
`--automatic-deployment`; passing both is invalid.
|
|
143
169
|
|
|
144
170
|
### 3. Jobs must be verifiable after creation
|
|
145
171
|
|
|
@@ -263,6 +289,9 @@ When reviewing an orchestration task, look for:
|
|
|
263
289
|
- direct job creation where a batch file should exist
|
|
264
290
|
- missing or wrong `related_image_uid`
|
|
265
291
|
- jobs tied to moving repository state instead of a pinned image
|
|
292
|
+
- manual Job creation that omits an exact ready image
|
|
293
|
+
- automatic Job creation that supplies a caller-selected image
|
|
294
|
+
- client code that interprets automatic deployment as a branch-tip or `latest` selector
|
|
266
295
|
- no run/log verification after creation
|
|
267
296
|
- unsafe use of `--strict`
|
|
268
297
|
- workflows depending on laptop-specific file paths instead of Artifacts
|
|
@@ -282,7 +311,8 @@ Do not claim success until you have checked:
|
|
|
282
311
|
- interval
|
|
283
312
|
- crontab
|
|
284
313
|
- one-off
|
|
285
|
-
-
|
|
314
|
+
- manual image selection or backend-owned automatic image preparation is intentional
|
|
315
|
+
- standalone Job automatic deployment and tag policy are intentionally enabled or disabled
|
|
286
316
|
- the job exists after direct creation or repository workflow application
|
|
287
317
|
- runs and logs were inspected when execution success matters
|
|
288
318
|
- resources and releases were verified when deployment success matters
|
|
@@ -308,6 +338,8 @@ If the workflow uses Artifacts, also check:
|
|
|
308
338
|
- the execution target is unclear
|
|
309
339
|
- the image strategy is unclear but reproducibility matters
|
|
310
340
|
- the backend rejects the workflow version, resource kind, or requested field
|
|
341
|
+
- a manually pinned Job has no exact ready initial image
|
|
342
|
+
- an automatically deployed Job has no persisted synchronized ProjectBranch commit
|
|
311
343
|
- the workflow depends on local file paths that should be platform Artifacts
|
|
312
344
|
- automatic deployment is requested but the deployment source branch/current synced project version is unclear
|
|
313
345
|
- automatic deployment is requested but the resource path or required README is not stable
|
|
@@ -60,6 +60,7 @@ MCP_MAX_RESOURCE_PAGES = 100
|
|
|
60
60
|
S = requests.Session()
|
|
61
61
|
S.headers.update({"Content-Type": "application/json"})
|
|
62
62
|
|
|
63
|
+
|
|
63
64
|
class ApiError(RuntimeError):
|
|
64
65
|
"""Base error for API failures."""
|
|
65
66
|
|
|
@@ -942,9 +943,7 @@ def render_project_branch_default_redeployment_tag(
|
|
|
942
943
|
{"version": normalized_version},
|
|
943
944
|
)
|
|
944
945
|
if not r.ok:
|
|
945
|
-
raise ApiError(
|
|
946
|
-
f"Default redeployment tag rendering failed ({r.status_code})."
|
|
947
|
-
)
|
|
946
|
+
raise ApiError(f"Default redeployment tag rendering failed ({r.status_code}).")
|
|
948
947
|
payload = r.json()
|
|
949
948
|
if not isinstance(payload, dict):
|
|
950
949
|
raise ApiError("Default redeployment tag rendering returned an unexpected payload.")
|
|
@@ -1123,9 +1122,7 @@ def update_organization_team(
|
|
|
1123
1122
|
class_name="Team",
|
|
1124
1123
|
operation=lambda ClientTeam: ClientTeam.get_by_uid(
|
|
1125
1124
|
str(team_uid), timeout=timeout
|
|
1126
|
-
).patch(
|
|
1127
|
-
**updates
|
|
1128
|
-
),
|
|
1125
|
+
).patch(**updates),
|
|
1129
1126
|
)
|
|
1130
1127
|
return _sdk_object_to_dict(team)
|
|
1131
1128
|
except Exception as e:
|
|
@@ -2866,7 +2863,6 @@ def list_meta_tables(
|
|
|
2866
2863
|
raise ApiError(f"MetaTables fetch failed: {e}") from e
|
|
2867
2864
|
|
|
2868
2865
|
|
|
2869
|
-
|
|
2870
2866
|
def _serialize_sdk_search_response(payload: Any) -> dict[str, Any] | list[dict[str, Any]]:
|
|
2871
2867
|
if isinstance(payload, list):
|
|
2872
2868
|
return [_sdk_object_to_dict(item) for item in payload]
|
|
@@ -4372,6 +4368,8 @@ def create_project_job(
|
|
|
4372
4368
|
spot: bool | None = None,
|
|
4373
4369
|
max_runtime_seconds: int | None = None,
|
|
4374
4370
|
related_image_uid: str | None = None,
|
|
4371
|
+
automatic_deployment: bool = False,
|
|
4372
|
+
automatic_redeployment_tag_regex: str | None = None,
|
|
4375
4373
|
timeout: int | None = None,
|
|
4376
4374
|
) -> dict[str, Any]:
|
|
4377
4375
|
"""
|
|
@@ -4440,6 +4438,10 @@ def create_project_job(
|
|
|
4440
4438
|
spot=spot,
|
|
4441
4439
|
max_runtime_seconds=max_runtime_seconds,
|
|
4442
4440
|
related_image_uid=related_image_uid,
|
|
4441
|
+
automatic_deployment=automatic_deployment,
|
|
4442
|
+
automatic_redeployment_policy={
|
|
4443
|
+
"tag_regex": automatic_redeployment_tag_regex,
|
|
4444
|
+
},
|
|
4443
4445
|
timeout=timeout,
|
|
4444
4446
|
)
|
|
4445
4447
|
if isinstance(created, dict):
|
|
@@ -5031,9 +5033,7 @@ def create_project(
|
|
|
5031
5033
|
|
|
5032
5034
|
if default_metatables_data_source_uid in (None, ""):
|
|
5033
5035
|
raise ValueError("default_metatables_data_source_uid is required")
|
|
5034
|
-
payload["default_metatables_data_source_uid"] = str(
|
|
5035
|
-
default_metatables_data_source_uid
|
|
5036
|
-
)
|
|
5036
|
+
payload["default_metatables_data_source_uid"] = str(default_metatables_data_source_uid)
|
|
5037
5037
|
if default_base_image_uid not in (None, ""):
|
|
5038
5038
|
payload["default_base_image_uid"] = str(default_base_image_uid)
|
|
5039
5039
|
if github_org_uid not in (None, ""):
|
|
@@ -746,11 +746,14 @@ def _render_projects_table(items: list[dict], base_dir: str, org_slug: str) -> s
|
|
|
746
746
|
for p in items:
|
|
747
747
|
public_id = _project_identity_value(p)
|
|
748
748
|
name = p.get("project_name") or "(unnamed)"
|
|
749
|
-
branches =
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
749
|
+
branches = (
|
|
750
|
+
", ".join(
|
|
751
|
+
str(branch.get("repository_branch") or "-")
|
|
752
|
+
for branch in list(p.get("branches") or [])
|
|
753
|
+
if isinstance(branch, dict)
|
|
754
|
+
)
|
|
755
|
+
or "-"
|
|
756
|
+
)
|
|
754
757
|
|
|
755
758
|
local_path = _find_local_project_dir(base_dir, org_slug, public_id, name)
|
|
756
759
|
local = "Local" if local_path else "-"
|
|
@@ -2454,9 +2457,7 @@ def login(
|
|
|
2454
2457
|
"tool": handoff["mcp_tool"],
|
|
2455
2458
|
"arguments": handoff["mcp_arguments"],
|
|
2456
2459
|
}
|
|
2457
|
-
info(
|
|
2458
|
-
"Authorize this CLI from the connected Main Sequence MCP session:"
|
|
2459
|
-
)
|
|
2460
|
+
info("Authorize this CLI from the connected Main Sequence MCP session:")
|
|
2460
2461
|
typer.echo(json.dumps(tool_call, separators=(",", ":")))
|
|
2461
2462
|
|
|
2462
2463
|
flow = login_via_mcp_handoff(
|
|
@@ -2466,9 +2467,7 @@ def login(
|
|
|
2466
2467
|
access = (flow.get("access") or "").strip()
|
|
2467
2468
|
refresh = (flow.get("refresh") or "").strip()
|
|
2468
2469
|
if not access or not refresh:
|
|
2469
|
-
raise ApiError(
|
|
2470
|
-
"MCP handoff login did not return access and refresh tokens."
|
|
2471
|
-
)
|
|
2470
|
+
raise ApiError("MCP handoff login did not return access and refresh tokens.")
|
|
2472
2471
|
|
|
2473
2472
|
persisted = cfg.save_tokens("", access, refresh)
|
|
2474
2473
|
user_payload = flow.get("user")
|
|
@@ -2480,9 +2479,7 @@ def login(
|
|
|
2480
2479
|
if isinstance(profile, dict):
|
|
2481
2480
|
username = (profile.get("username") or "").strip()
|
|
2482
2481
|
if username:
|
|
2483
|
-
persisted = bool(
|
|
2484
|
-
cfg.save_tokens(username, access, refresh) and persisted
|
|
2485
|
-
)
|
|
2482
|
+
persisted = bool(cfg.save_tokens(username, access, refresh) and persisted)
|
|
2486
2483
|
|
|
2487
2484
|
res = {
|
|
2488
2485
|
"username": username,
|
|
@@ -2801,9 +2798,7 @@ def _organization_teams_list_impl(
|
|
|
2801
2798
|
)
|
|
2802
2799
|
|
|
2803
2800
|
if rows:
|
|
2804
|
-
print_table(
|
|
2805
|
-
"Organization Teams", ["UID", "Name", "Description", "Members", "Active"], rows
|
|
2806
|
-
)
|
|
2801
|
+
print_table("Organization Teams", ["UID", "Name", "Description", "Members", "Active"], rows)
|
|
2807
2802
|
else:
|
|
2808
2803
|
info("No organization teams.")
|
|
2809
2804
|
info(f"Total organization teams: {len(teams_payload)}")
|
|
@@ -4013,9 +4008,7 @@ def _agent_session_list_impl(
|
|
|
4013
4008
|
command_label="Agent Sessions",
|
|
4014
4009
|
)
|
|
4015
4010
|
if agent_uid and any(key in filters for key in ("agent_uid", "agent_uid__in")):
|
|
4016
|
-
error(
|
|
4017
|
-
"Do not pass `--filter agent_uid=...` with `--agent-uid`. " "Use only one agent scope."
|
|
4018
|
-
)
|
|
4011
|
+
error("Do not pass `--filter agent_uid=...` with `--agent-uid`. Use only one agent scope.")
|
|
4019
4012
|
raise typer.Exit(1)
|
|
4020
4013
|
|
|
4021
4014
|
_require_login()
|
|
@@ -4511,7 +4504,6 @@ def _parse_cli_csv_list(values: list[str] | None) -> list[str]:
|
|
|
4511
4504
|
return items
|
|
4512
4505
|
|
|
4513
4506
|
|
|
4514
|
-
|
|
4515
4507
|
def _data_node_storage_list_impl(
|
|
4516
4508
|
timeout: int | None,
|
|
4517
4509
|
filter_entries: list[str] | None,
|
|
@@ -4772,7 +4764,6 @@ def _print_data_node_storage_search_section(
|
|
|
4772
4764
|
return len(storages)
|
|
4773
4765
|
|
|
4774
4766
|
|
|
4775
|
-
|
|
4776
4767
|
@agent.command("list")
|
|
4777
4768
|
def agent_list_cmd(
|
|
4778
4769
|
filter_entries: list[str] | None = typer.Option(None, "--filter", help=LIST_FILTER_OPTION_HELP),
|
|
@@ -7515,11 +7506,7 @@ def project_create_cmd(
|
|
|
7515
7506
|
ds_rows: list[list[str]] = []
|
|
7516
7507
|
for item in ds_items:
|
|
7517
7508
|
uid = _require_item_uid(item, prompt_label="data source uid")
|
|
7518
|
-
ds_name = (
|
|
7519
|
-
item.get("display_name")
|
|
7520
|
-
or item.get("class_type")
|
|
7521
|
-
or f"data-source-{uid}"
|
|
7522
|
-
)
|
|
7509
|
+
ds_name = item.get("display_name") or item.get("class_type") or f"data-source-{uid}"
|
|
7523
7510
|
ds_details = (
|
|
7524
7511
|
f"class={item.get('class_type') or '-'}, status={item.get('status') or '-'}"
|
|
7525
7512
|
)
|
|
@@ -8843,7 +8830,9 @@ def project_images_create_cmd(
|
|
|
8843
8830
|
path: str | None = typer.Option(
|
|
8844
8831
|
None, "--path", help="Project repository path (default: current project)"
|
|
8845
8832
|
),
|
|
8846
|
-
base_image_uid: str | None = typer.Option(
|
|
8833
|
+
base_image_uid: str | None = typer.Option(
|
|
8834
|
+
None, "--base-image-uid", help="Project base image UID"
|
|
8835
|
+
),
|
|
8847
8836
|
timeout: int = typer.Option(
|
|
8848
8837
|
300, "--timeout", help="Maximum wait time in seconds for the image to become ready."
|
|
8849
8838
|
),
|
|
@@ -8905,7 +8894,9 @@ def project_create_image_cmd(
|
|
|
8905
8894
|
path: str | None = typer.Option(
|
|
8906
8895
|
None, "--path", help="Project repository path (default: current project)"
|
|
8907
8896
|
),
|
|
8908
|
-
base_image_uid: str | None = typer.Option(
|
|
8897
|
+
base_image_uid: str | None = typer.Option(
|
|
8898
|
+
None, "--base-image-uid", help="Project base image UID"
|
|
8899
|
+
),
|
|
8909
8900
|
timeout: int = typer.Option(
|
|
8910
8901
|
300, "--timeout", help="Maximum wait time in seconds for the image to become ready."
|
|
8911
8902
|
),
|
|
@@ -8990,6 +8981,33 @@ def _project_jobs_list_impl(
|
|
|
8990
8981
|
],
|
|
8991
8982
|
rows,
|
|
8992
8983
|
)
|
|
8984
|
+
|
|
8985
|
+
deployment_rows = [
|
|
8986
|
+
[
|
|
8987
|
+
str(job.get("uid") or "-"),
|
|
8988
|
+
str(job.get("project_repo_hash") or "-"),
|
|
8989
|
+
str(job.get("related_image_uid") or job.get("related_image") or "-"),
|
|
8990
|
+
str(job.get("image_status") or "-"),
|
|
8991
|
+
str(bool(job.get("automatic_deployment"))).lower(),
|
|
8992
|
+
str(
|
|
8993
|
+
(job.get("automatic_redeployment_policy") or {}).get("tag_regex")
|
|
8994
|
+
or "every qualifying exact event"
|
|
8995
|
+
),
|
|
8996
|
+
]
|
|
8997
|
+
for job in jobs
|
|
8998
|
+
]
|
|
8999
|
+
print_table(
|
|
9000
|
+
"Project Job Image And Promotion State",
|
|
9001
|
+
[
|
|
9002
|
+
"Job UID",
|
|
9003
|
+
"Exact Commit",
|
|
9004
|
+
"Image UID",
|
|
9005
|
+
"Image Status",
|
|
9006
|
+
"Automatic Deployment",
|
|
9007
|
+
"Promotion Tag Regex",
|
|
9008
|
+
],
|
|
9009
|
+
deployment_rows,
|
|
9010
|
+
)
|
|
8993
9011
|
else:
|
|
8994
9012
|
info("No project jobs.")
|
|
8995
9013
|
info(f"Total jobs: {len(jobs)}")
|
|
@@ -9048,6 +9066,21 @@ def _project_job_runs_list_impl(
|
|
|
9048
9066
|
],
|
|
9049
9067
|
rows,
|
|
9050
9068
|
)
|
|
9069
|
+
|
|
9070
|
+
snapshot_rows = [
|
|
9071
|
+
[
|
|
9072
|
+
str(run.get("uid") or "-"),
|
|
9073
|
+
str(run.get("runtime_image_uid") or "-"),
|
|
9074
|
+
str(run.get("runtime_image_digest") or "-"),
|
|
9075
|
+
str(run.get("commit_hash") or "-"),
|
|
9076
|
+
]
|
|
9077
|
+
for run in runs
|
|
9078
|
+
]
|
|
9079
|
+
print_table(
|
|
9080
|
+
"Immutable Job Run Image Snapshots",
|
|
9081
|
+
["Job Run UID", "Runtime Image UID", "Runtime Image Digest", "Commit Hash"],
|
|
9082
|
+
snapshot_rows,
|
|
9083
|
+
)
|
|
9051
9084
|
else:
|
|
9052
9085
|
info("No job runs.")
|
|
9053
9086
|
info(f"Total job runs: {len(runs)}")
|
|
@@ -9358,6 +9391,8 @@ def _project_jobs_create_impl(
|
|
|
9358
9391
|
gpu_type: str | None,
|
|
9359
9392
|
spot: bool | None,
|
|
9360
9393
|
max_runtime_seconds: int | None,
|
|
9394
|
+
automatic_deployment: bool,
|
|
9395
|
+
automatic_redeployment_tag_regex: str | None,
|
|
9361
9396
|
timeout: int | None,
|
|
9362
9397
|
) -> None:
|
|
9363
9398
|
_require_login()
|
|
@@ -9384,34 +9419,46 @@ def _project_jobs_create_impl(
|
|
|
9384
9419
|
error("Job name is required.")
|
|
9385
9420
|
raise typer.Exit(1)
|
|
9386
9421
|
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9422
|
+
if automatic_deployment:
|
|
9423
|
+
if related_image_uid is not None:
|
|
9424
|
+
error(
|
|
9425
|
+
"Do not provide --related-image-uid with --automatic-deployment. "
|
|
9426
|
+
"The backend derives and prepares the initial exact image."
|
|
9427
|
+
)
|
|
9428
|
+
raise typer.Exit(1)
|
|
9429
|
+
info(
|
|
9430
|
+
"Automatic deployment: the backend will derive the initial exact image "
|
|
9431
|
+
"from the ProjectBranch synchronized commit."
|
|
9391
9432
|
)
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9433
|
+
else:
|
|
9434
|
+
try:
|
|
9435
|
+
project_images = list_project_images(
|
|
9436
|
+
related_project_branch_uid=project_branch_uid,
|
|
9437
|
+
timeout=timeout,
|
|
9438
|
+
)
|
|
9439
|
+
except ApiError as e:
|
|
9440
|
+
error(f"Project images fetch failed: {e}")
|
|
9441
|
+
raise typer.Exit(1) from e
|
|
9395
9442
|
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9443
|
+
if related_image_uid is None and project_images:
|
|
9444
|
+
image_rows = [
|
|
9445
|
+
[
|
|
9446
|
+
str(img.get("uid") or "-"),
|
|
9447
|
+
str(img.get("project_repo_hash") or "-"),
|
|
9448
|
+
_format_base_image_label(img.get("base_image")),
|
|
9449
|
+
]
|
|
9450
|
+
for img in project_images
|
|
9402
9451
|
]
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
rows=image_rows,
|
|
9410
|
-
)
|
|
9452
|
+
related_image_uid = _prompt_select_uid(
|
|
9453
|
+
title="Available Project Images",
|
|
9454
|
+
prompt_label="Related image UID",
|
|
9455
|
+
items=project_images,
|
|
9456
|
+
rows=image_rows,
|
|
9457
|
+
)
|
|
9411
9458
|
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9459
|
+
if related_image_uid is None:
|
|
9460
|
+
error("related_image_uid is required when automatic deployment is disabled.")
|
|
9461
|
+
raise typer.Exit(1)
|
|
9415
9462
|
|
|
9416
9463
|
if execution_path is None and app_name is None:
|
|
9417
9464
|
execution_path = (
|
|
@@ -9487,6 +9534,8 @@ def _project_jobs_create_impl(
|
|
|
9487
9534
|
spot=spot,
|
|
9488
9535
|
max_runtime_seconds=max_runtime_seconds,
|
|
9489
9536
|
related_image_uid=related_image_uid,
|
|
9537
|
+
automatic_deployment=automatic_deployment,
|
|
9538
|
+
automatic_redeployment_tag_regex=automatic_redeployment_tag_regex,
|
|
9490
9539
|
timeout=timeout,
|
|
9491
9540
|
)
|
|
9492
9541
|
except ApiError as e:
|
|
@@ -9523,6 +9572,19 @@ def _project_jobs_create_impl(
|
|
|
9523
9572
|
"Max Runtime Seconds",
|
|
9524
9573
|
str(created.get("max_runtime_seconds") or max_runtime_seconds),
|
|
9525
9574
|
),
|
|
9575
|
+
("Image Status", str(created.get("image_status") or "-")),
|
|
9576
|
+
("Exact Commit", str(created.get("project_repo_hash") or "-")),
|
|
9577
|
+
(
|
|
9578
|
+
"Automatic Deployment",
|
|
9579
|
+
str(created.get("automatic_deployment", automatic_deployment)).lower(),
|
|
9580
|
+
),
|
|
9581
|
+
(
|
|
9582
|
+
"Promotion Tag Regex",
|
|
9583
|
+
str(
|
|
9584
|
+
(created.get("automatic_redeployment_policy") or {}).get("tag_regex")
|
|
9585
|
+
or "every qualifying exact event"
|
|
9586
|
+
),
|
|
9587
|
+
),
|
|
9526
9588
|
],
|
|
9527
9589
|
)
|
|
9528
9590
|
|
|
@@ -9548,7 +9610,10 @@ def project_jobs_create_cmd(
|
|
|
9548
9610
|
"related_image_uid",
|
|
9549
9611
|
None,
|
|
9550
9612
|
"--related-image-uid",
|
|
9551
|
-
extra_help=
|
|
9613
|
+
extra_help=(
|
|
9614
|
+
"Required for manually pinned Jobs. Omit it with "
|
|
9615
|
+
"--automatic-deployment so the backend owns initial image preparation."
|
|
9616
|
+
),
|
|
9552
9617
|
),
|
|
9553
9618
|
schedule_type: str | None = pydantic_option(
|
|
9554
9619
|
INTERVAL_SCHEDULE_MODEL_REF,
|
|
@@ -9620,12 +9685,30 @@ def project_jobs_create_cmd(
|
|
|
9620
9685
|
"--max-runtime-seconds",
|
|
9621
9686
|
extra_help="Defaults to 86400 when omitted.",
|
|
9622
9687
|
),
|
|
9688
|
+
automatic_deployment: bool = typer.Option(
|
|
9689
|
+
False,
|
|
9690
|
+
"--automatic-deployment/--no-automatic-deployment",
|
|
9691
|
+
help=(
|
|
9692
|
+
"Let the backend derive and prepare the initial exact image from the "
|
|
9693
|
+
"synchronized ProjectBranch commit, then promote future qualifying exact events."
|
|
9694
|
+
),
|
|
9695
|
+
),
|
|
9696
|
+
automatic_redeployment_tag_regex: str | None = typer.Option(
|
|
9697
|
+
None,
|
|
9698
|
+
"--automatic-redeployment-tag-regex",
|
|
9699
|
+
help=(
|
|
9700
|
+
"Full-match regex for qualifying immutable Git tags. Omit it to "
|
|
9701
|
+
"allow every qualifying exact event while automatic deployment is enabled."
|
|
9702
|
+
),
|
|
9703
|
+
),
|
|
9623
9704
|
timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
|
|
9624
9705
|
):
|
|
9625
9706
|
"""
|
|
9626
9707
|
Create a job for a project.
|
|
9627
9708
|
|
|
9628
|
-
Uses SDK client `Job.create()` as the single source of truth.
|
|
9709
|
+
Uses SDK client `Job.create()` as the single source of truth. Manual Jobs
|
|
9710
|
+
require `--related-image-uid`; automatically deployed Jobs forbid it and
|
|
9711
|
+
delegate initial exact-image preparation to the backend.
|
|
9629
9712
|
When compute settings are omitted, the CLI applies safe defaults:
|
|
9630
9713
|
`cpu_request=0.25`, `memory_request=0.5`, `spot=false`, `max_runtime_seconds=86400`.
|
|
9631
9714
|
If schedule arguments are omitted, the CLI asks whether to build an interval or crontab schedule.
|
|
@@ -9657,6 +9740,8 @@ def project_jobs_create_cmd(
|
|
|
9657
9740
|
gpu_type=gpu_type,
|
|
9658
9741
|
spot=spot,
|
|
9659
9742
|
max_runtime_seconds=max_runtime_seconds,
|
|
9743
|
+
automatic_deployment=automatic_deployment,
|
|
9744
|
+
automatic_redeployment_tag_regex=automatic_redeployment_tag_regex,
|
|
9660
9745
|
timeout=timeout,
|
|
9661
9746
|
)
|
|
9662
9747
|
|
|
@@ -533,40 +533,16 @@ class MetaTableOperationScope(BasePydanticModel):
|
|
|
533
533
|
min_length=1,
|
|
534
534
|
validation_alias=AliasChoices("data_source_uid", "dataSourceUid"),
|
|
535
535
|
description=(
|
|
536
|
-
"
|
|
537
|
-
"SQL execution connection. If omitted, the
|
|
538
|
-
"
|
|
539
|
-
"
|
|
536
|
+
"Optional public UID of the DataSource that owns the compiled "
|
|
537
|
+
"SQL execution connection. If omitted, the backend derives the "
|
|
538
|
+
"connection from the declared MetaTable scope and rejects mixed "
|
|
539
|
+
"data sources."
|
|
540
540
|
),
|
|
541
541
|
)
|
|
542
542
|
tables: list[MetaTableOperationScopeTable] = Field(..., min_length=1)
|
|
543
543
|
|
|
544
544
|
model_config = ConfigDict(populate_by_name=True)
|
|
545
545
|
|
|
546
|
-
@model_validator(mode="after")
|
|
547
|
-
def resolve_default_data_source_uid(self):
|
|
548
|
-
if self.data_source_uid not in (None, ""):
|
|
549
|
-
self.data_source_uid = str(self.data_source_uid)
|
|
550
|
-
return self
|
|
551
|
-
|
|
552
|
-
try:
|
|
553
|
-
data_source = get_session_data_source()
|
|
554
|
-
except Exception as exc:
|
|
555
|
-
raise ValueError(
|
|
556
|
-
"MetaTable compiled SQL scope requires data_source_uid or a "
|
|
557
|
-
"configured project/session default data source. "
|
|
558
|
-
f"Default data source resolution failed: {exc}"
|
|
559
|
-
) from exc
|
|
560
|
-
|
|
561
|
-
uid = getattr(data_source, "uid", None) or getattr(data_source, "data_source_uid", None)
|
|
562
|
-
if uid in (None, ""):
|
|
563
|
-
raise ValueError(
|
|
564
|
-
"Configured project/session default data source does not expose a uid."
|
|
565
|
-
)
|
|
566
|
-
self.data_source_uid = str(uid)
|
|
567
|
-
return self
|
|
568
|
-
|
|
569
|
-
|
|
570
546
|
class MetaTableProjectContextRequest(BaseModel):
|
|
571
547
|
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
572
548
|
|