mainsequence 4.0.0__tar.gz → 4.0.3__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 (124) hide show
  1. {mainsequence-4.0.0 → mainsequence-4.0.3}/PKG-INFO +1 -1
  2. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/bootstrap.py +2 -1
  3. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/api.py +139 -31
  4. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/cli.py +250 -221
  5. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/config.py +117 -58
  6. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/project_status.py +30 -6
  7. mainsequence-4.0.3/mainsequence/client/__init__.py +16 -0
  8. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/models_tdag.py +34 -29
  9. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/utils.py +0 -16
  10. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence.egg-info/PKG-INFO +1 -1
  11. {mainsequence-4.0.0 → mainsequence-4.0.3}/pyproject.toml +1 -1
  12. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_cli.py +235 -81
  13. mainsequence-4.0.0/mainsequence/client/__init__.py +0 -8
  14. {mainsequence-4.0.0 → mainsequence-4.0.3}/LICENSE +0 -0
  15. {mainsequence-4.0.0 → mainsequence-4.0.3}/README.md +0 -0
  16. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/AGENTS.md +0 -0
  17. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  18. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  19. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  20. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  21. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  22. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  23. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  24. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  25. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  26. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  27. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  28. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  29. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  30. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  31. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/maintenance/local_journal/SKILL.md +0 -0
  32. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  33. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  34. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  35. {mainsequence-4.0.0 → mainsequence-4.0.3}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  36. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/__init__.py +0 -0
  37. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/__main__.py +0 -0
  38. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/__init__.py +0 -0
  39. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/browser_auth.py +0 -0
  40. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/docker_utils.py +0 -0
  41. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/doctor.py +0 -0
  42. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/local_ops.py +0 -0
  43. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/model_filters.py +0 -0
  44. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/pydantic_cli.py +0 -0
  45. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/sdk_utils.py +0 -0
  46. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/ssh_utils.py +0 -0
  47. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/cli/ui.py +0 -0
  48. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/agent_runtime_models.py +0 -0
  49. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/base.py +0 -0
  50. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/client.py +0 -0
  51. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/command_center/__init__.py +0 -0
  52. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/command_center/app_component.py +0 -0
  53. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/command_center/connections.py +0 -0
  54. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/command_center/data_models.py +0 -0
  55. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/command_center/workspace.py +0 -0
  56. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  57. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  58. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  59. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/data_sources_interfaces/timescale.py +0 -0
  60. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/exceptions.py +0 -0
  61. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/fastapi/__init__.py +0 -0
  62. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/fastapi/auth.py +0 -0
  63. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/models_helpers.py +0 -0
  64. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/models_metatables.py +0 -0
  65. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/client/models_user.py +0 -0
  66. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/compute_validation.py +0 -0
  67. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/defaults.py +0 -0
  68. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/instrumentation/__init__.py +0 -0
  69. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/instrumentation/utils.py +0 -0
  70. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/logconf.py +0 -0
  71. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/runtime_flags.py +0 -0
  72. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/__init__.py +0 -0
  73. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/__main__.py +0 -0
  74. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/base_persist_managers.py +0 -0
  75. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/config.py +0 -0
  76. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/configuration_models.py +0 -0
  77. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/__init__.py +0 -0
  78. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/build_operations.py +0 -0
  79. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/data_nodes.py +0 -0
  80. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/filters.py +0 -0
  81. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/models.py +0 -0
  82. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/namespacing.py +0 -0
  83. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/persist_managers.py +0 -0
  84. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/run_operations.py +0 -0
  85. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/data_nodes/utils.py +0 -0
  86. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/filters.py +0 -0
  87. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/future_registry.py +0 -0
  88. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/meta_tables/__init__.py +0 -0
  89. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/meta_tables/compiled_sql.py +0 -0
  90. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/meta_tables/hashing.py +0 -0
  91. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/meta_tables/sqlalchemy_contracts.py +0 -0
  92. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/pydantic_metadata.py +0 -0
  93. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence/tdag/utils.py +0 -0
  94. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence.egg-info/SOURCES.txt +0 -0
  95. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence.egg-info/dependency_links.txt +0 -0
  96. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence.egg-info/entry_points.txt +0 -0
  97. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence.egg-info/requires.txt +0 -0
  98. {mainsequence-4.0.0 → mainsequence-4.0.3}/mainsequence.egg-info/top_level.txt +0 -0
  99. {mainsequence-4.0.0 → mainsequence-4.0.3}/setup.cfg +0 -0
  100. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_auth_precedence.py +0 -0
  101. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_build_operations_hashing.py +0 -0
  102. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_cli_browser_auth.py +0 -0
  103. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_client.py +0 -0
  104. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_command_center_app_component_models.py +0 -0
  105. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_command_center_data_models.py +0 -0
  106. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_command_center_models.py +0 -0
  107. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  108. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_data_node_search_join_filters.py +0 -0
  109. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_data_node_storage_dimension_queries.py +0 -0
  110. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_data_node_update_flow.py +0 -0
  111. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_dependency_extras.py +0 -0
  112. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_filter_normalization.py +0 -0
  113. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_logconf.py +0 -0
  114. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_meta_tables_client_models.py +0 -0
  115. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
  116. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_models_user_request_bound_auth.py +0 -0
  117. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_pod_project_resolution.py +0 -0
  118. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_project_batch_jobs_from_file.py +0 -0
  119. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_run_configuration.py +0 -0
  120. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_source_table_configuration.py +0 -0
  121. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_update_runner_uid_runtime.py +0 -0
  122. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_update_statistics.py +0 -0
  123. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_update_uid_guards.py +0 -0
  124. {mainsequence-4.0.0 → mainsequence-4.0.3}/tests/test_workspace_snapshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.0.0
3
+ Version: 4.0.3
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -20,7 +20,7 @@ def _read_local_env_values(env_path: pathlib.Path) -> dict[str, str]:
20
20
  return {}
21
21
 
22
22
  values: dict[str, str] = {}
23
- for key in ("MAINSEQUENCE_ENDPOINT", "MAIN_SEQUENCE_PROJECT_ID"):
23
+ for key in ("MAINSEQUENCE_ENDPOINT", "MAIN_SEQUENCE_PROJECT_UID", "MAIN_SEQUENCE_PROJECT_ID"):
24
24
  match = re.search(rf"(?m)^{re.escape(key)}=(.+?)\s*$", content)
25
25
  if match:
26
26
  values[key] = match.group(1).strip()
@@ -35,6 +35,7 @@ def prime_runtime_env() -> None:
35
35
  local_values = _read_local_env_values(pathlib.Path.cwd() / ".env")
36
36
 
37
37
  _set_if_missing("MAINSEQUENCE_ENDPOINT", local_values.get("MAINSEQUENCE_ENDPOINT"))
38
+ _set_if_missing("MAIN_SEQUENCE_PROJECT_UID", local_values.get("MAIN_SEQUENCE_PROJECT_UID"))
38
39
  _set_if_missing("MAIN_SEQUENCE_PROJECT_ID", local_values.get("MAIN_SEQUENCE_PROJECT_ID"))
39
40
 
40
41
  try:
@@ -432,6 +432,7 @@ def _run_sdk_model_operation(
432
432
  class_name: str,
433
433
  operation,
434
434
  project_id_env: int | str | None = None,
435
+ project_uid_env: str | None = None,
435
436
  ):
436
437
  tokens = get_tokens()
437
438
  access = (tokens.get("access") or "").strip()
@@ -447,6 +448,7 @@ def _run_sdk_model_operation(
447
448
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
448
449
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
449
450
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
451
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
450
452
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
451
453
  }
452
454
 
@@ -464,6 +466,12 @@ def _run_sdk_model_operation(
464
466
  else:
465
467
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
466
468
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
469
+ if project_uid_env is not None:
470
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = str(project_uid_env)
471
+ elif project_id_env is not None and not str(project_id_env).strip().isdigit():
472
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = str(project_id_env)
473
+ else:
474
+ os.environ.pop("MAIN_SEQUENCE_PROJECT_UID", None)
467
475
  if project_id_env is not None:
468
476
  os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_id_env)
469
477
  else:
@@ -521,20 +529,30 @@ def _run_sdk_model_operation(
521
529
 
522
530
  def get_current_user_profile() -> dict:
523
531
  """
524
- Return current user profile (username + organization name) via backend endpoints.
532
+ Return current user profile (username + organization name) via the canonical user-details endpoint.
525
533
 
526
534
  Returns:
527
535
  dict: {"username": "...", "organization": "..."} or {}
528
536
  """
529
- who = authed("GET", AUTH_PATHS["ping"])
530
- d = who.json() if who.ok else {}
531
- uid = d.get("id") or d.get("pk") or (d.get("user") or {}).get("id") or d.get("user_id")
532
- if not uid:
533
- return {}
534
- full = authed("GET", f"/user/api/user/{uid}/")
535
- u = full.json() if full.ok else {}
536
- org_name = (u.get("organization") or {}).get("name") or u.get("organization_name") or ""
537
- return {"username": u.get("username") or "", "organization": org_name}
537
+ details = authed("GET", "/user/api/user/get_user_details/")
538
+ payload = details.json() if details.ok else {}
539
+ user = payload.get("user") if isinstance(payload, dict) else {}
540
+ if not isinstance(user, dict):
541
+ user = {}
542
+ organization = user.get("organization") if isinstance(user, dict) else {}
543
+ if not isinstance(organization, dict):
544
+ organization = {}
545
+ payload_organization = payload.get("organization") if isinstance(payload, dict) else {}
546
+ if not isinstance(payload_organization, dict):
547
+ payload_organization = {}
548
+ org_name = (
549
+ organization.get("name")
550
+ or payload_organization.get("name")
551
+ or payload.get("organization_name")
552
+ or payload.get("organization")
553
+ or ""
554
+ )
555
+ return {"username": user.get("username") or payload.get("username") or "", "organization": org_name}
538
556
 
539
557
 
540
558
  def get_logged_user_details() -> dict[str, Any]:
@@ -676,6 +694,65 @@ def get_projects() -> list[dict]:
676
694
  return data.get("results") or []
677
695
 
678
696
 
697
+ def _normalize_project_reference(project_ref: int | str) -> str:
698
+ normalized = str(project_ref).strip()
699
+ if not normalized:
700
+ raise ApiError("Project UID is required.")
701
+ return normalized
702
+
703
+
704
+ def _project_matches_reference(project_payload: dict[str, Any], project_ref: str) -> bool:
705
+ return (
706
+ str(project_payload.get("uid") or "").strip() == project_ref
707
+ or str(project_payload.get("id") or "").strip() == project_ref
708
+ )
709
+
710
+
711
+ def resolve_project(project_ref: int | str) -> dict[str, Any]:
712
+ normalized_ref = _normalize_project_reference(project_ref)
713
+
714
+ try:
715
+ payload = get_project(normalized_ref)
716
+ if isinstance(payload, dict) and payload:
717
+ return payload
718
+ except ApiError:
719
+ pass
720
+
721
+ for project_payload in get_projects():
722
+ if isinstance(project_payload, dict) and _project_matches_reference(project_payload, normalized_ref):
723
+ return project_payload
724
+
725
+ raise ApiError(f"Project not found: {normalized_ref}")
726
+
727
+
728
+ def resolve_project_uid(project_ref: int | str) -> str:
729
+ normalized_ref = _normalize_project_reference(project_ref)
730
+ if normalized_ref.isdigit():
731
+ return normalized_ref
732
+
733
+ payload = resolve_project(project_ref)
734
+ normalized_uid = str(payload.get("uid") or "").strip()
735
+ if normalized_uid:
736
+ return normalized_uid
737
+ if not normalized_ref.isdigit():
738
+ return normalized_ref
739
+ raise ApiError(f"Project UID is not available for project reference: {normalized_ref}")
740
+
741
+
742
+ def resolve_project_row_id(project_ref: int | str) -> int:
743
+ normalized_ref = _normalize_project_reference(project_ref)
744
+ if normalized_ref.isdigit():
745
+ return int(normalized_ref)
746
+
747
+ payload = resolve_project(project_ref)
748
+ row_id = payload.get("id")
749
+ if row_id is None:
750
+ if normalized_ref.isdigit():
751
+ return int(normalized_ref)
752
+ raise ApiError(f"Backend row id is not available for project reference: {normalized_ref}")
753
+ return int(row_id)
754
+
755
+
679
756
  def search_projects(
680
757
  q: str,
681
758
  *,
@@ -1395,7 +1472,7 @@ def get_agent_run(
1395
1472
 
1396
1473
  def get_project(project_id: int | str) -> dict:
1397
1474
  """
1398
- Fetch a single project by id.
1475
+ Fetch a single project by public reference.
1399
1476
  """
1400
1477
  r = authed("GET", f"/orm/api/pods/projects/{project_id}/")
1401
1478
  if not r.ok:
@@ -1748,6 +1825,7 @@ def get_project_data_node_updates(project_id: int | str, *, timeout: int | None
1748
1825
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
1749
1826
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
1750
1827
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
1828
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
1751
1829
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
1752
1830
  }
1753
1831
 
@@ -1765,7 +1843,10 @@ def get_project_data_node_updates(project_id: int | str, *, timeout: int | None
1765
1843
  else:
1766
1844
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
1767
1845
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
1768
- os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_id)
1846
+ project_uid = resolve_project_uid(project_id)
1847
+ project_row_id = resolve_project_row_id(project_id)
1848
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = project_uid
1849
+ os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_row_id)
1769
1850
 
1770
1851
  from mainsequence.client import utils as _client_utils
1771
1852
  from mainsequence.client.base import BaseObjectOrm
@@ -1782,7 +1863,7 @@ def get_project_data_node_updates(project_id: int | str, *, timeout: int | None
1782
1863
  BaseObjectOrm.ROOT_URL = root_url
1783
1864
  ClientProject.ROOT_URL = root_url
1784
1865
 
1785
- project = ClientProject.get(pk=project_id, timeout=timeout)
1866
+ project = ClientProject.get(pk=project_uid, timeout=timeout)
1786
1867
  updates = project.get_data_nodes_updates(timeout=timeout)
1787
1868
 
1788
1869
  out: list[dict[str, Any]] = []
@@ -1839,14 +1920,16 @@ def sync_project_after_commit(project_id: int | str, *, timeout: int | None = No
1839
1920
  - delegates request behavior and payload parsing to `Project.sync_project_after_commit()`
1840
1921
  """
1841
1922
  try:
1923
+ project_uid = resolve_project_uid(project_id)
1842
1924
  payload = _run_sdk_model_operation(
1843
1925
  module_name="mainsequence.client.models_tdag",
1844
1926
  class_name="Project",
1845
1927
  operation=lambda ClientProject: ClientProject.sync_project_after_commit(
1846
- int(project_id),
1928
+ project_uid,
1847
1929
  timeout=timeout,
1848
1930
  ),
1849
- project_id_env=project_id,
1931
+ project_id_env=resolve_project_row_id(project_id),
1932
+ project_uid_env=project_uid,
1850
1933
  )
1851
1934
  if payload is None:
1852
1935
  return None
@@ -1911,6 +1994,7 @@ def create_project_image(
1911
1994
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
1912
1995
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
1913
1996
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
1997
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
1914
1998
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
1915
1999
  }
1916
2000
 
@@ -1927,7 +2011,10 @@ def create_project_image(
1927
2011
  else:
1928
2012
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
1929
2013
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
1930
- os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(related_project_id)
2014
+ project_uid = resolve_project_uid(related_project_id)
2015
+ project_row_id = resolve_project_row_id(related_project_id)
2016
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = project_uid
2017
+ os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_row_id)
1931
2018
 
1932
2019
  from mainsequence.client import utils as _client_utils
1933
2020
  from mainsequence.client.base import BaseObjectOrm
@@ -1946,7 +2033,7 @@ def create_project_image(
1946
2033
 
1947
2034
  created = ClientProjectImage.create(
1948
2035
  project_repo_hash=project_repo_hash,
1949
- related_project_id=int(related_project_id),
2036
+ related_project_id=project_row_id,
1950
2037
  base_image_id=base_image_id,
1951
2038
  timeout=timeout,
1952
2039
  )
@@ -2018,6 +2105,7 @@ def list_project_images(
2018
2105
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
2019
2106
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
2020
2107
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
2108
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
2021
2109
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
2022
2110
  }
2023
2111
 
@@ -2034,7 +2122,10 @@ def list_project_images(
2034
2122
  else:
2035
2123
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
2036
2124
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
2037
- os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(related_project_id)
2125
+ project_uid = resolve_project_uid(related_project_id)
2126
+ project_row_id = resolve_project_row_id(related_project_id)
2127
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = project_uid
2128
+ os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_row_id)
2038
2129
 
2039
2130
  from mainsequence.client import utils as _client_utils
2040
2131
  from mainsequence.client.base import BaseObjectOrm
@@ -2052,7 +2143,7 @@ def list_project_images(
2052
2143
  ClientProjectImage.ROOT_URL = root_url
2053
2144
 
2054
2145
  merged_filters = dict(filters or {})
2055
- merged_filters["related_project__id__in"] = [int(related_project_id)]
2146
+ merged_filters["related_project__id__in"] = [project_row_id]
2056
2147
  images = ClientProjectImage.filter(timeout=timeout, **merged_filters)
2057
2148
 
2058
2149
  out: list[dict[str, Any]] = []
@@ -2243,6 +2334,7 @@ def list_project_jobs(
2243
2334
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
2244
2335
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
2245
2336
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
2337
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
2246
2338
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
2247
2339
  }
2248
2340
 
@@ -2259,7 +2351,10 @@ def list_project_jobs(
2259
2351
  else:
2260
2352
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
2261
2353
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
2262
- os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_id)
2354
+ project_uid = resolve_project_uid(project_id)
2355
+ project_row_id = resolve_project_row_id(project_id)
2356
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = project_uid
2357
+ os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_row_id)
2263
2358
 
2264
2359
  from mainsequence.client import utils as _client_utils
2265
2360
  from mainsequence.client.base import BaseObjectOrm
@@ -2279,7 +2374,7 @@ def list_project_jobs(
2279
2374
  extra_filters = dict(filters or {})
2280
2375
  jobs = ClientJob.filter(
2281
2376
  timeout=timeout,
2282
- **{**extra_filters, "project__id": int(project_id)},
2377
+ **{**extra_filters, "project__id": project_row_id},
2283
2378
  )
2284
2379
 
2285
2380
  out: list[dict[str, Any]] = []
@@ -2355,6 +2450,7 @@ def list_project_resources(
2355
2450
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
2356
2451
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
2357
2452
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
2453
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
2358
2454
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
2359
2455
  }
2360
2456
 
@@ -2371,7 +2467,10 @@ def list_project_resources(
2371
2467
  else:
2372
2468
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
2373
2469
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
2374
- os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_id)
2470
+ project_uid = resolve_project_uid(project_id)
2471
+ project_row_id = resolve_project_row_id(project_id)
2472
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = project_uid
2473
+ os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_row_id)
2375
2474
 
2376
2475
  from mainsequence.client import utils as _client_utils
2377
2476
  from mainsequence.client.base import BaseObjectOrm
@@ -2391,7 +2490,7 @@ def list_project_resources(
2391
2490
  merged_filters: dict[str, Any] = dict(filters or {})
2392
2491
  merged_filters.update(
2393
2492
  {
2394
- "project__id": int(project_id),
2493
+ "project__id": project_row_id,
2395
2494
  "repo_commit_sha": str(repo_commit_sha).strip(),
2396
2495
  }
2397
2496
  )
@@ -4210,6 +4309,7 @@ def create_project_job(
4210
4309
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
4211
4310
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
4212
4311
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
4312
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
4213
4313
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
4214
4314
  }
4215
4315
 
@@ -4226,7 +4326,10 @@ def create_project_job(
4226
4326
  else:
4227
4327
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
4228
4328
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
4229
- os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_id)
4329
+ project_uid = resolve_project_uid(project_id)
4330
+ project_row_id = resolve_project_row_id(project_id)
4331
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = project_uid
4332
+ os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_row_id)
4230
4333
 
4231
4334
  from mainsequence.client import utils as _client_utils
4232
4335
  from mainsequence.client.base import BaseObjectOrm
@@ -4245,7 +4348,7 @@ def create_project_job(
4245
4348
 
4246
4349
  created = ClientJob.create(
4247
4350
  name=name,
4248
- project_id=int(project_id),
4351
+ project_id=project_row_id,
4249
4352
  execution_path=execution_path,
4250
4353
  app_name=app_name,
4251
4354
  task_schedule=task_schedule,
@@ -4331,6 +4434,7 @@ def schedule_batch_project_jobs(
4331
4434
  "MAINSEQUENCE_ACCESS_TOKEN": os.environ.get("MAINSEQUENCE_ACCESS_TOKEN"),
4332
4435
  "MAINSEQUENCE_REFRESH_TOKEN": os.environ.get("MAINSEQUENCE_REFRESH_TOKEN"),
4333
4436
  "MAINSEQUENCE_ENDPOINT": os.environ.get("MAINSEQUENCE_ENDPOINT"),
4437
+ "MAIN_SEQUENCE_PROJECT_UID": os.environ.get("MAIN_SEQUENCE_PROJECT_UID"),
4334
4438
  "MAIN_SEQUENCE_PROJECT_ID": os.environ.get("MAIN_SEQUENCE_PROJECT_ID"),
4335
4439
  }
4336
4440
 
@@ -4347,7 +4451,10 @@ def schedule_batch_project_jobs(
4347
4451
  else:
4348
4452
  os.environ.pop("MAINSEQUENCE_REFRESH_TOKEN", None)
4349
4453
  os.environ["MAINSEQUENCE_ENDPOINT"] = endpoint
4350
- os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_id)
4454
+ project_uid = resolve_project_uid(project_id)
4455
+ project_row_id = resolve_project_row_id(project_id)
4456
+ os.environ["MAIN_SEQUENCE_PROJECT_UID"] = project_uid
4457
+ os.environ["MAIN_SEQUENCE_PROJECT_ID"] = str(project_row_id)
4351
4458
 
4352
4459
  from mainsequence.client import utils as _client_utils
4353
4460
  from mainsequence.client.base import BaseObjectOrm
@@ -4366,7 +4473,7 @@ def schedule_batch_project_jobs(
4366
4473
 
4367
4474
  created = ClientJob.bulk_get_or_create(
4368
4475
  yaml_file=file_path,
4369
- project_id=int(project_id),
4476
+ project_id=project_row_id,
4370
4477
  strict=bool(strict),
4371
4478
  timeout=timeout,
4372
4479
  )
@@ -4866,13 +4973,14 @@ def create_project(
4866
4973
 
4867
4974
  def delete_project(project_id: int | str, *, delete_repositories: bool = False) -> dict[str, Any] | None:
4868
4975
  """
4869
- Delete a project by id.
4976
+ Delete a project by public reference.
4870
4977
 
4871
4978
  Mirrors backend behavior:
4872
- - DELETE /orm/api/pods/projects/{id}/
4979
+ - DELETE /orm/api/pods/projects/{uid}/
4873
4980
  - optional query param delete_repositories=true
4874
4981
  """
4875
- path = f"/orm/api/pods/projects/{project_id}/"
4982
+ project_uid = resolve_project_uid(project_id)
4983
+ path = f"/orm/api/pods/projects/{project_uid}/"
4876
4984
  if delete_repositories:
4877
4985
  path = f"{path}?delete_repositories=true"
4878
4986
 
@@ -4942,7 +5050,7 @@ def add_deploy_key(project_id: int | str, key_title: str, public_key: str) -> No
4942
5050
  """
4943
5051
  r = authed(
4944
5052
  "POST",
4945
- f"/orm/api/pods/projects/{project_id}/add_deploy_key/",
5053
+ f"/orm/api/pods/projects/{resolve_project_uid(project_id)}/add_deploy_key/",
4946
5054
  {"key_title": key_title, "public_key": public_key},
4947
5055
  )
4948
5056
  r.raise_for_status()