salesforce-data-customcode 5.0.0__tar.gz → 6.0.0__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 (98) hide show
  1. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/PKG-INFO +7 -1
  2. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/README.md +6 -0
  3. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/pyproject.toml +6 -5
  4. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/einstein_platform_client.py +27 -4
  5. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/einstein_predictions/impl/default.py +1 -1
  6. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/writer/base.py +11 -1
  7. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/default.py +1 -1
  8. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/.devcontainer/devcontainer.json +1 -1
  9. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/.devcontainer/devcontainer.json +1 -1
  10. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/LICENSE.txt +0 -0
  11. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/__init__.py +0 -0
  12. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/auth.py +0 -0
  13. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/cli.py +0 -0
  14. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/client.py +0 -0
  15. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/cmd.py +0 -0
  16. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/common_config.py +0 -0
  17. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/config.py +0 -0
  18. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/config.yaml +0 -0
  19. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/constants.py +0 -0
  20. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/credentials.py +0 -0
  21. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/deploy.py +0 -0
  22. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/einstein_platform_config.py +0 -0
  23. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/einstein_predictions/__init__.py +0 -0
  24. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/einstein_predictions/base.py +0 -0
  25. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/einstein_predictions/types.py +0 -0
  26. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/einstein_predictions_config.py +0 -0
  27. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/file/__init__.py +0 -0
  28. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/file/base.py +0 -0
  29. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/file/path/__init__.py +0 -0
  30. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/file/path/default.py +0 -0
  31. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/function/__init__.py +0 -0
  32. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/function/base.py +0 -0
  33. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/function/feature_types/__init__.py +0 -0
  34. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/function/feature_types/chunking.py +0 -0
  35. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/function/runtime.py +0 -0
  36. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/function_utils.py +0 -0
  37. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/__init__.py +0 -0
  38. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/base.py +0 -0
  39. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/reader/__init__.py +0 -0
  40. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/reader/base.py +0 -0
  41. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/reader/query_api.py +0 -0
  42. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/reader/sf_cli.py +0 -0
  43. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/reader/utils.py +0 -0
  44. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/writer/__init__.py +0 -0
  45. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/writer/csv.py +0 -0
  46. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/io/writer/print.py +0 -0
  47. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/__init__.py +0 -0
  48. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/base.py +0 -0
  49. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/types/__init__.py +0 -0
  50. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/types/generate_text_request.py +0 -0
  51. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/types/generate_text_request_builder.py +0 -0
  52. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/types/generate_text_response.py +0 -0
  53. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway/types/generate_text_response_builder.py +0 -0
  54. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/llm_gateway_config.py +0 -0
  55. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/mixin.py +0 -0
  56. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/proxy/__init__.py +0 -0
  57. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/proxy/base.py +0 -0
  58. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/proxy/client/__init__.py +0 -0
  59. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/proxy/client/base.py +0 -0
  60. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/py.typed +0 -0
  61. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/run.py +0 -0
  62. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/scan.py +0 -0
  63. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/spark/__init__.py +0 -0
  64. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/spark/base.py +0 -0
  65. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/spark/default.py +0 -0
  66. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/template.py +0 -0
  67. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/__init__.py +0 -0
  68. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/Dockerfile.dependencies +0 -0
  69. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/README.md +0 -0
  70. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/build_native_dependencies.sh +0 -0
  71. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/chunking/payload/config.json +0 -0
  72. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/chunking/payload/entrypoint.py +0 -0
  73. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/chunking/requirements.txt +0 -0
  74. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/example/chunking_with_llm/config.json +0 -0
  75. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py +0 -0
  76. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/example/chunking_with_llm/files/chunking_prompt.txt +0 -0
  77. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/example/chunking_with_llm/tests/test.json +0 -0
  78. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/example/chunking_with_prediction/config.json +0 -0
  79. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py +0 -0
  80. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/example/chunking_with_prediction/tests/test.json +0 -0
  81. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/payload/config.json +0 -0
  82. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/payload/entrypoint.py +0 -0
  83. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/requirements-dev.txt +0 -0
  84. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/function/requirements.txt +0 -0
  85. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/Dockerfile +0 -0
  86. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/Dockerfile.dependencies +0 -0
  87. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/README.md +0 -0
  88. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/account.ipynb +0 -0
  89. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/build_native_dependencies.sh +0 -0
  90. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv +0 -0
  91. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py +0 -0
  92. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/jupyterlab.sh +0 -0
  93. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/payload/config.json +0 -0
  94. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/payload/entrypoint.py +0 -0
  95. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/requirements-dev.txt +0 -0
  96. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/templates/script/requirements.txt +0 -0
  97. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/token_provider.py +0 -0
  98. {salesforce_data_customcode-5.0.0 → salesforce_data_customcode-6.0.0}/src/datacustomcode/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salesforce-data-customcode
3
- Version: 5.0.0
3
+ Version: 6.0.0
4
4
  Summary: Data Cloud Custom Code SDK
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE.txt
@@ -254,12 +254,18 @@ Instead of using `datacustomcode configure`, you can also set credentials via en
254
254
  | `SFDC_REFRESH_TOKEN` | OAuth refresh token |
255
255
  | `SFDC_ACCESS_TOKEN` | (Optional) OAuth core/access token |
256
256
 
257
+ **Einstein Platform API Environment (Optional):**
258
+ | Variable | Description |
259
+ |----------|-------------|
260
+ | `SFDC_EINSTEIN_API_ENV` | Einstein Platform API environment: `dev`, `test`, `stage`, or `prod`. If not set, automatically inferred from login URL. Set this explicitly if auto-detection fails. |
261
+
257
262
  Example usage:
258
263
  ```bash
259
264
  export SFDC_LOGIN_URL="https://login.salesforce.com"
260
265
  export SFDC_CLIENT_ID="your_client_id"
261
266
  export SFDC_CLIENT_SECRET="your_client_secret"
262
267
  export SFDC_REFRESH_TOKEN="your_refresh_token"
268
+ export SFDC_EINSTEIN_API_ENV="test" # optional
263
269
 
264
270
  datacustomcode run ./payload/entrypoint.py
265
271
  ```
@@ -229,12 +229,18 @@ Instead of using `datacustomcode configure`, you can also set credentials via en
229
229
  | `SFDC_REFRESH_TOKEN` | OAuth refresh token |
230
230
  | `SFDC_ACCESS_TOKEN` | (Optional) OAuth core/access token |
231
231
 
232
+ **Einstein Platform API Environment (Optional):**
233
+ | Variable | Description |
234
+ |----------|-------------|
235
+ | `SFDC_EINSTEIN_API_ENV` | Einstein Platform API environment: `dev`, `test`, `stage`, or `prod`. If not set, automatically inferred from login URL. Set this explicitly if auto-detection fails. |
236
+
232
237
  Example usage:
233
238
  ```bash
234
239
  export SFDC_LOGIN_URL="https://login.salesforce.com"
235
240
  export SFDC_CLIENT_ID="your_client_id"
236
241
  export SFDC_CLIENT_SECRET="your_client_secret"
237
242
  export SFDC_REFRESH_TOKEN="your_refresh_token"
243
+ export SFDC_EINSTEIN_API_ENV="test" # optional
238
244
 
239
245
  datacustomcode run ./payload/entrypoint.py
240
246
  ```
@@ -18,7 +18,7 @@ license = "Apache-2.0"
18
18
  name = "salesforce-data-customcode"
19
19
  readme = "README.md"
20
20
  requires-python = ">=3.10,<3.12"
21
- version = "5.0.0"
21
+ version = "6.0.0"
22
22
 
23
23
  [tool.black]
24
24
  exclude = '''
@@ -88,8 +88,8 @@ warn_unused_configs = true
88
88
 
89
89
  [tool.poetry]
90
90
  include = [
91
- {path = "src/datacustomcode/templates/**/*", format = "sdist"},
92
- {path = "src/datacustomcode/config.yaml", format = "sdist"}
91
+ {path = "src/datacustomcode/templates/**/*"},
92
+ {path = "src/datacustomcode/config.yaml"}
93
93
  ]
94
94
  packages = [{include = "datacustomcode", from = "src"}]
95
95
 
@@ -133,8 +133,9 @@ datacustomcode = "datacustomcode.cli:cli"
133
133
 
134
134
  [tool.poetry-dynamic-versioning]
135
135
  enable = false
136
- pattern = "^v(?P<base>.+)$"
137
- style = "semver"
136
+ format-jinja = "{% if distance == 0 %}{{ base }}{{ stage or '' }}{% else %}{{ bump_version(base) }}.dev{{ distance }}+{{ commit }}{% endif %}"
137
+ pattern = "^v(?P<base>\\d+\\.\\d+\\.\\d+)(?P<stage>\\.dev\\d+|a\\d+|b\\d+|rc\\d+)?$"
138
+ style = "pep440"
138
139
  vcs = "git"
139
140
 
140
141
  [tool.pytest.ini_options]
@@ -13,6 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ import os
16
17
  from typing import (
17
18
  Any,
18
19
  Dict,
@@ -30,10 +31,6 @@ from datacustomcode.token_provider import (
30
31
 
31
32
 
32
33
  class EinsteinPlatformClient:
33
- EINSTEIN_PLATFORM_MODELS_URL = (
34
- "https://api.salesforce.com/einstein/platform/v1/models"
35
- )
36
-
37
34
  def __init__(
38
35
  self,
39
36
  credentials_profile: Optional[str] = None,
@@ -48,8 +45,34 @@ class EinsteinPlatformClient:
48
45
  self._token_provider = CredentialsTokenProvider(profile)
49
46
  logger.debug(f"Using credentials token provider with profile: {profile}")
50
47
  self.token_response = None
48
+ self._einstein_url_cache: Optional[str] = None
51
49
  super().__init__(**kwargs)
52
50
 
51
+ def _get_einstein_platform_url(self) -> str:
52
+ if self._einstein_url_cache is not None:
53
+ return self._einstein_url_cache
54
+
55
+ env = os.environ.get("SFDC_EINSTEIN_API_ENV", "prod").lower()
56
+ if env not in ("dev", "test", "stage", "prod"):
57
+ logger.warning(
58
+ f"Unknown SFDC_EINSTEIN_API_ENV value '{env}', defaulting to prod"
59
+ )
60
+ env = "prod"
61
+
62
+ base_url = self._get_base_url_for_env(env)
63
+ logger.info(f"Using Einstein Platform API endpoint: {base_url} (env={env})")
64
+ self._einstein_url_cache = f"{base_url}/einstein/platform/v1/models"
65
+ return self._einstein_url_cache
66
+
67
+ def _get_base_url_for_env(self, env: str) -> str:
68
+ env_map = {
69
+ "dev": "https://dev.api.salesforce.com",
70
+ "test": "https://test.api.salesforce.com",
71
+ "stage": "https://stage.api.salesforce.com",
72
+ "prod": "https://api.salesforce.com",
73
+ }
74
+ return env_map.get(env, "https://api.salesforce.com")
75
+
53
76
  def _get_headers(self):
54
77
  if self.token_response is None:
55
78
  self.token_response = self._token_provider.get_token()
@@ -48,7 +48,7 @@ class DefaultEinsteinPredictions(EinsteinPlatformClient, EinsteinPredictions):
48
48
  )
49
49
 
50
50
  api_url = (
51
- f"{self.EINSTEIN_PLATFORM_MODELS_URL}/{request.model_api_name}/{endpoint}"
51
+ f"{self._get_einstein_platform_url()}/{request.model_api_name}/{endpoint}"
52
52
  )
53
53
 
54
54
  prediction_columns: List[Dict[str, Any]] = []
@@ -27,11 +27,21 @@ if TYPE_CHECKING:
27
27
  class WriteMode(str, Enum):
28
28
  APPEND = "append"
29
29
  OVERWRITE = "overwrite"
30
- OVERWRITE_PARTITIONS = "overwrite_partitions"
30
+ OVERWRITE_PARTITIONS = "overwrite_partitions" # Deprecated: raises error if used
31
31
  MERGE = "merge"
32
32
  MERGE_UPSERT_DELETE = "merge_upsert_delete"
33
33
 
34
34
 
35
+ class MergeRecordType(str, Enum):
36
+ """Values for the _merge_record_type column used by MERGE_UPSERT_DELETE."""
37
+
38
+ UPSERT = "UPSERT"
39
+ DELETE = "DELETE"
40
+
41
+
42
+ MERGE_RECORD_TYPE_COLUMN = "_merge_record_type"
43
+
44
+
35
45
  class BaseDataCloudWriter(BaseDataAccessLayer):
36
46
  """Base class for Data Cloud writers."""
37
47
 
@@ -29,7 +29,7 @@ class DefaultLLMGateway(EinsteinPlatformClient, LLMGateway):
29
29
 
30
30
  def generate_text(self, request: GenerateTextRequest) -> GenerateTextResponse:
31
31
  api_url = (
32
- f"{self.EINSTEIN_PLATFORM_MODELS_URL}/{request.model_name}/generations"
32
+ f"{self._get_einstein_platform_url()}/{request.model_name}/generations"
33
33
  )
34
34
 
35
35
  payload: Dict[str, Any] = {"prompt": request.prompt}
@@ -5,6 +5,6 @@
5
5
  "dockerfile": "../Dockerfile"
6
6
  },
7
7
  "features": {
8
- "ghcr.io/devcontainers/features/git:1": {},
8
+ "ghcr.io/devcontainers/features/git:1": {}
9
9
  }
10
10
  }
@@ -5,6 +5,6 @@
5
5
  "dockerfile": "../Dockerfile"
6
6
  },
7
7
  "features": {
8
- "ghcr.io/devcontainers/features/git:1": {},
8
+ "ghcr.io/devcontainers/features/git:1": {}
9
9
  }
10
10
  }