airbyte-agent-shopify 0.1.5__py3-none-any.whl → 0.1.13__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.
@@ -634,9 +634,7 @@ class OAuth2AuthStrategy(AuthStrategy):
634
634
  Headers dict with additional headers added
635
635
  """
636
636
  # Build template context with extracted secret values
637
- template_context = {
638
- key: extract_secret_value(value) for key, value in secrets.items()
639
- }
637
+ template_context = {key: extract_secret_value(value) for key, value in secrets.items()}
640
638
 
641
639
  for header_name, value_template in additional_headers.items():
642
640
  # Use Jinja2 templating for variable substitution
@@ -62,6 +62,53 @@ class TokenExtractValidationError(ConnectorModelLoaderError):
62
62
  pass
63
63
 
64
64
 
65
+ # Expected auth_mapping keys for each auth type.
66
+ # These are the auth parameters that each security scheme expects, NOT the user's credential field names.
67
+ EXPECTED_AUTH_MAPPING_KEYS: dict[AuthType, set[str]] = {
68
+ AuthType.BEARER: {"token"},
69
+ AuthType.BASIC: {"username", "password"},
70
+ AuthType.API_KEY: {"api_key"},
71
+ AuthType.OAUTH2: {"access_token", "refresh_token", "client_id", "client_secret"},
72
+ }
73
+
74
+
75
+ def _validate_auth_mapping_keys(
76
+ auth_type: AuthType,
77
+ auth_config: AirbyteAuthConfig | None,
78
+ scheme_name: str = "default",
79
+ ) -> None:
80
+ """Validate that auth_mapping keys match expected parameters for the auth type.
81
+
82
+ The auth_mapping keys must be the parameters expected by the security scheme
83
+ (e.g., "token" for bearer), not the user's credential field names.
84
+
85
+ Args:
86
+ auth_type: The authentication type
87
+ auth_config: The x-airbyte-auth-config containing auth_mapping
88
+ scheme_name: Name of the security scheme for error messages
89
+
90
+ Raises:
91
+ InvalidOpenAPIError: If auth_mapping keys don't match expected parameters
92
+ """
93
+ if auth_config is None or auth_config.auth_mapping is None:
94
+ return # No explicit auth_mapping, will use defaults
95
+
96
+ expected_keys = EXPECTED_AUTH_MAPPING_KEYS.get(auth_type)
97
+ if expected_keys is None:
98
+ return # Unknown auth type, skip validation
99
+
100
+ actual_keys = set(auth_config.auth_mapping.keys())
101
+ invalid_keys = actual_keys - expected_keys
102
+
103
+ if invalid_keys:
104
+ raise InvalidOpenAPIError(
105
+ f"Invalid auth_mapping keys for {auth_type.value} auth in scheme '{scheme_name}': {invalid_keys}. "
106
+ f"Expected keys for {auth_type.value}: {sorted(expected_keys)}. "
107
+ f"Note: auth_mapping keys must be the auth parameters (e.g., 'token' for bearer), "
108
+ f'not your credential field names. Use template syntax to map: token: "${{your_field}}"'
109
+ )
110
+
111
+
65
112
  def extract_path_params(path: str) -> list[str]:
66
113
  """Extract parameter names from path template.
67
114
 
@@ -145,6 +192,87 @@ def _deproxy_schema(obj: Any) -> Any:
145
192
  return obj
146
193
 
147
194
 
195
+ def _type_includes(type_value: Any, target: str) -> bool:
196
+ if isinstance(type_value, list):
197
+ return target in type_value
198
+ return type_value == target
199
+
200
+
201
+ def _flatten_cache_properties(properties: dict[str, Any], prefix: str) -> list[str]:
202
+ entries: list[str] = []
203
+ for prop_name, prop in properties.items():
204
+ path = f"{prefix}{prop_name}" if prefix else prop_name
205
+ entries.append(path)
206
+
207
+ prop_type = getattr(prop, "type", None) if not isinstance(prop, dict) else prop.get("type")
208
+ prop_properties = getattr(prop, "properties", None) if not isinstance(prop, dict) else prop.get("properties")
209
+
210
+ if _type_includes(prop_type, "array"):
211
+ array_path = f"{path}[]"
212
+ entries.append(array_path)
213
+ if isinstance(prop_properties, dict):
214
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{array_path}."))
215
+ elif isinstance(prop_properties, dict):
216
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{path}."))
217
+
218
+ return entries
219
+
220
+
221
+ def _flatten_cache_field_paths(field: Any) -> list[str]:
222
+ field_name = getattr(field, "name", None) if not isinstance(field, dict) else field.get("name")
223
+ if not isinstance(field_name, str) or not field_name:
224
+ return []
225
+
226
+ field_type = getattr(field, "type", None) if not isinstance(field, dict) else field.get("type")
227
+ field_properties = getattr(field, "properties", None) if not isinstance(field, dict) else field.get("properties")
228
+
229
+ entries = [field_name]
230
+ if _type_includes(field_type, "array"):
231
+ array_path = f"{field_name}[]"
232
+ entries.append(array_path)
233
+ if isinstance(field_properties, dict):
234
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{array_path}."))
235
+ elif isinstance(field_properties, dict):
236
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{field_name}."))
237
+
238
+ return entries
239
+
240
+
241
+ def _dedupe_strings(values: list[str]) -> list[str]:
242
+ seen: set[str] = set()
243
+ ordered: list[str] = []
244
+ for value in values:
245
+ if value not in seen:
246
+ seen.add(value)
247
+ ordered.append(value)
248
+ return ordered
249
+
250
+
251
+ def _extract_search_field_paths(spec: OpenAPIConnector) -> dict[str, list[str]]:
252
+ cache_config = getattr(spec.info, "x_airbyte_cache", None)
253
+ entities = getattr(cache_config, "entities", None)
254
+ if not isinstance(entities, list):
255
+ return {}
256
+
257
+ search_fields: dict[str, list[str]] = {}
258
+ for entity in entities:
259
+ entity_name = getattr(entity, "entity", None) if not isinstance(entity, dict) else entity.get("entity")
260
+ if not isinstance(entity_name, str) or not entity_name:
261
+ continue
262
+
263
+ fields = getattr(entity, "fields", None) if not isinstance(entity, dict) else entity.get("fields")
264
+ if not isinstance(fields, list):
265
+ continue
266
+
267
+ field_paths: list[str] = []
268
+ for field in fields:
269
+ field_paths.extend(_flatten_cache_field_paths(field))
270
+
271
+ search_fields[entity_name] = _dedupe_strings(field_paths)
272
+
273
+ return search_fields
274
+
275
+
148
276
  def parse_openapi_spec(raw_config: dict) -> OpenAPIConnector:
149
277
  """Parse OpenAPI specification from YAML.
150
278
 
@@ -434,6 +562,8 @@ def convert_openapi_to_connector_model(spec: OpenAPIConnector) -> ConnectorModel
434
562
  if not connector_id:
435
563
  raise InvalidOpenAPIError("Missing required x-airbyte-connector-id field")
436
564
 
565
+ search_field_paths = _extract_search_field_paths(spec)
566
+
437
567
  # Create ConnectorModel
438
568
  model = ConnectorModel(
439
569
  id=connector_id,
@@ -444,6 +574,7 @@ def convert_openapi_to_connector_model(spec: OpenAPIConnector) -> ConnectorModel
444
574
  entities=entities,
445
575
  openapi_spec=spec,
446
576
  retry_config=retry_config,
577
+ search_field_paths=search_field_paths,
447
578
  )
448
579
 
449
580
  return model
@@ -627,7 +758,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
627
758
  description="Authentication bearer token",
628
759
  format=None,
629
760
  pattern=None,
630
- airbyte_secret=False,
631
761
  default=None,
632
762
  )
633
763
  },
@@ -647,7 +777,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
647
777
  description="Authentication username",
648
778
  format=None,
649
779
  pattern=None,
650
- airbyte_secret=False,
651
780
  default=None,
652
781
  ),
653
782
  "password": AuthConfigFieldSpec(
@@ -656,7 +785,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
656
785
  description="Authentication password",
657
786
  format=None,
658
787
  pattern=None,
659
- airbyte_secret=False,
660
788
  default=None,
661
789
  ),
662
790
  },
@@ -676,7 +804,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
676
804
  description="API authentication key",
677
805
  format=None,
678
806
  pattern=None,
679
- airbyte_secret=False,
680
807
  default=None,
681
808
  )
682
809
  },
@@ -701,7 +828,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
701
828
  description="OAuth2 access token",
702
829
  format=None,
703
830
  pattern=None,
704
- airbyte_secret=False,
705
831
  default=None,
706
832
  ),
707
833
  "refresh_token": AuthConfigFieldSpec(
@@ -710,7 +836,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
710
836
  description="OAuth2 refresh token (optional)",
711
837
  format=None,
712
838
  pattern=None,
713
- airbyte_secret=False,
714
839
  default=None,
715
840
  ),
716
841
  "client_id": AuthConfigFieldSpec(
@@ -719,7 +844,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
719
844
  description="OAuth2 client ID (optional)",
720
845
  format=None,
721
846
  pattern=None,
722
- airbyte_secret=False,
723
847
  default=None,
724
848
  ),
725
849
  "client_secret": AuthConfigFieldSpec(
@@ -728,7 +852,6 @@ def _generate_default_auth_config(auth_type: AuthType) -> AirbyteAuthConfig:
728
852
  description="OAuth2 client secret (optional)",
729
853
  format=None,
730
854
  pattern=None,
731
- airbyte_secret=False,
732
855
  default=None,
733
856
  ),
734
857
  },
@@ -840,6 +963,9 @@ def _parse_single_security_scheme(scheme: Any) -> AuthConfig:
840
963
  oauth2_config = _parse_oauth2_config(scheme)
841
964
  # Use explicit x-airbyte-auth-config if present, otherwise generate default
842
965
  auth_config_obj = scheme.x_airbyte_auth_config or _generate_default_auth_config(AuthType.OAUTH2)
966
+ # Validate auth_mapping keys if explicitly provided
967
+ if scheme.x_airbyte_auth_config:
968
+ _validate_auth_mapping_keys(AuthType.OAUTH2, scheme.x_airbyte_auth_config)
843
969
  return AuthConfig(
844
970
  type=AuthType.OAUTH2,
845
971
  config=oauth2_config,
@@ -850,6 +976,10 @@ def _parse_single_security_scheme(scheme: Any) -> AuthConfig:
850
976
  # Use explicit x-airbyte-auth-config if present, otherwise generate default
851
977
  auth_config_obj = scheme.x_airbyte_auth_config or _generate_default_auth_config(auth_type)
852
978
 
979
+ # Validate auth_mapping keys if explicitly provided
980
+ if scheme.x_airbyte_auth_config:
981
+ _validate_auth_mapping_keys(auth_type, scheme.x_airbyte_auth_config)
982
+
853
983
  return AuthConfig(
854
984
  type=auth_type,
855
985
  config=auth_config,
@@ -694,9 +694,7 @@ class LocalExecutor:
694
694
  """
695
695
  return {key: value for key, value in params.items() if key in allowed_fields and value is not None}
696
696
 
697
- def _extract_header_params(
698
- self, endpoint: EndpointDefinition, params: dict[str, Any], body: dict[str, Any] | None = None
699
- ) -> dict[str, str]:
697
+ def _extract_header_params(self, endpoint: EndpointDefinition, params: dict[str, Any], body: dict[str, Any] | None = None) -> dict[str, str]:
700
698
  """Extract header parameters from params and schema defaults.
701
699
 
702
700
  Also adds Content-Type header when there's a request body (unless already specified
@@ -1034,7 +1032,9 @@ class LocalExecutor:
1034
1032
  if "variables" in graphql_config and graphql_config["variables"]:
1035
1033
  variables = self._interpolate_variables(graphql_config["variables"], params, param_defaults)
1036
1034
  # Filter out None values (optional fields not provided) - matches REST _extract_body() behavior
1037
- body["variables"] = {k: v for k, v in variables.items() if v is not None}
1035
+ # But preserve None for variables explicitly marked as nullable (e.g., to unassign a user)
1036
+ nullable_vars = set(graphql_config.get("x-airbyte-nullable-variables") or [])
1037
+ body["variables"] = {k: v for k, v in variables.items() if v is not None or k in nullable_vars}
1038
1038
 
1039
1039
  # Add operation name if specified
1040
1040
  if "operationName" in graphql_config:
@@ -18,6 +18,185 @@ from typing import Any, Protocol
18
18
  MAX_EXAMPLE_QUESTIONS = 5 # Maximum number of example questions to include in description
19
19
 
20
20
 
21
+ def _type_includes(type_value: Any, target: str) -> bool:
22
+ if isinstance(type_value, list):
23
+ return target in type_value
24
+ return type_value == target
25
+
26
+
27
+ def _is_object_schema(schema: dict[str, Any]) -> bool:
28
+ if "properties" in schema:
29
+ return True
30
+ return _type_includes(schema.get("type"), "object")
31
+
32
+
33
+ def _is_array_schema(schema: dict[str, Any]) -> bool:
34
+ if "items" in schema:
35
+ return True
36
+ return _type_includes(schema.get("type"), "array")
37
+
38
+
39
+ def _dedupe_param_entries(entries: list[tuple[str, bool]]) -> list[tuple[str, bool]]:
40
+ seen: dict[str, bool] = {}
41
+ ordered: list[str] = []
42
+ for name, required in entries:
43
+ if name not in seen:
44
+ seen[name] = required
45
+ ordered.append(name)
46
+ else:
47
+ seen[name] = seen[name] or required
48
+ return [(name, seen[name]) for name in ordered]
49
+
50
+
51
+ def _flatten_schema_params(
52
+ schema: dict[str, Any],
53
+ prefix: str = "",
54
+ parent_required: bool = True,
55
+ seen_stack: set[int] | None = None,
56
+ ) -> list[tuple[str, bool]]:
57
+ if not isinstance(schema, dict):
58
+ return []
59
+
60
+ if seen_stack is None:
61
+ seen_stack = set()
62
+
63
+ schema_id = id(schema)
64
+ if schema_id in seen_stack:
65
+ return []
66
+
67
+ seen_stack.add(schema_id)
68
+ try:
69
+ entries: list[tuple[str, bool]] = []
70
+
71
+ for subschema in schema.get("allOf", []) or []:
72
+ if isinstance(subschema, dict):
73
+ entries.extend(_flatten_schema_params(subschema, prefix, parent_required, seen_stack))
74
+
75
+ for keyword in ("anyOf", "oneOf"):
76
+ for subschema in schema.get(keyword, []) or []:
77
+ if isinstance(subschema, dict):
78
+ entries.extend(_flatten_schema_params(subschema, prefix, False, seen_stack))
79
+
80
+ properties = schema.get("properties")
81
+ if isinstance(properties, dict):
82
+ required_fields = set(schema.get("required", [])) if isinstance(schema.get("required"), list) else set()
83
+ for prop_name, prop_schema in properties.items():
84
+ path = f"{prefix}{prop_name}" if prefix else prop_name
85
+ is_required = parent_required and prop_name in required_fields
86
+ entries.append((path, is_required))
87
+
88
+ if isinstance(prop_schema, dict):
89
+ if _is_array_schema(prop_schema):
90
+ array_path = f"{path}[]"
91
+ entries.append((array_path, is_required))
92
+ items = prop_schema.get("items")
93
+ if isinstance(items, dict):
94
+ entries.extend(_flatten_schema_params(items, prefix=f"{array_path}.", parent_required=is_required, seen_stack=seen_stack))
95
+ if _is_object_schema(prop_schema):
96
+ entries.extend(_flatten_schema_params(prop_schema, prefix=f"{path}.", parent_required=is_required, seen_stack=seen_stack))
97
+
98
+ return _dedupe_param_entries(entries)
99
+ finally:
100
+ seen_stack.remove(schema_id)
101
+
102
+
103
+ def _cache_field_value(field: Any, key: str) -> Any:
104
+ if isinstance(field, dict):
105
+ return field.get(key)
106
+ return getattr(field, key, None)
107
+
108
+
109
+ def _flatten_cache_properties(properties: dict[str, Any], prefix: str) -> list[str]:
110
+ entries: list[str] = []
111
+ for prop_name, prop in properties.items():
112
+ path = f"{prefix}{prop_name}" if prefix else prop_name
113
+ entries.append(path)
114
+
115
+ prop_type = _cache_field_value(prop, "type")
116
+ prop_properties = _cache_field_value(prop, "properties")
117
+
118
+ if _type_includes(prop_type, "array"):
119
+ array_path = f"{path}[]"
120
+ entries.append(array_path)
121
+ if isinstance(prop_properties, dict):
122
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{array_path}."))
123
+ elif isinstance(prop_properties, dict):
124
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{path}."))
125
+
126
+ return entries
127
+
128
+
129
+ def _flatten_cache_field_paths(field: Any) -> list[str]:
130
+ field_name = _cache_field_value(field, "name")
131
+ if not isinstance(field_name, str) or not field_name:
132
+ return []
133
+
134
+ field_type = _cache_field_value(field, "type")
135
+ field_properties = _cache_field_value(field, "properties")
136
+
137
+ entries = [field_name]
138
+ if _type_includes(field_type, "array"):
139
+ array_path = f"{field_name}[]"
140
+ entries.append(array_path)
141
+ if isinstance(field_properties, dict):
142
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{array_path}."))
143
+ elif isinstance(field_properties, dict):
144
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{field_name}."))
145
+
146
+ return entries
147
+
148
+
149
+ def _dedupe_strings(values: list[str]) -> list[str]:
150
+ seen: set[str] = set()
151
+ ordered: list[str] = []
152
+ for value in values:
153
+ if value not in seen:
154
+ seen.add(value)
155
+ ordered.append(value)
156
+ return ordered
157
+
158
+
159
+ def _collect_search_field_paths(model: ConnectorModelProtocol) -> dict[str, list[str]]:
160
+ search_field_paths = getattr(model, "search_field_paths", None)
161
+ if isinstance(search_field_paths, dict) and search_field_paths:
162
+ normalized: dict[str, list[str]] = {}
163
+ for entity, fields in search_field_paths.items():
164
+ if not isinstance(entity, str) or not entity:
165
+ continue
166
+ if isinstance(fields, list):
167
+ normalized[entity] = _dedupe_strings([field for field in fields if isinstance(field, str) and field])
168
+ return normalized
169
+
170
+ openapi_spec = getattr(model, "openapi_spec", None)
171
+ info = getattr(openapi_spec, "info", None)
172
+ cache_config = getattr(info, "x_airbyte_cache", None)
173
+ entities = getattr(cache_config, "entities", None)
174
+ if not isinstance(entities, list):
175
+ return {}
176
+
177
+ search_fields: dict[str, list[str]] = {}
178
+ for entity in entities:
179
+ entity_name = _cache_field_value(entity, "entity")
180
+ if not isinstance(entity_name, str) or not entity_name:
181
+ continue
182
+
183
+ fields = _cache_field_value(entity, "fields") or []
184
+ if not isinstance(fields, list):
185
+ continue
186
+ field_paths: list[str] = []
187
+ for field in fields:
188
+ field_paths.extend(_flatten_cache_field_paths(field))
189
+
190
+ search_fields[entity_name] = _dedupe_strings(field_paths)
191
+
192
+ return search_fields
193
+
194
+
195
+ def _format_search_param_signature() -> str:
196
+ params = ["query*", "limit?", "cursor?", "fields?"]
197
+ return f"({', '.join(params)})"
198
+
199
+
21
200
  class EndpointProtocol(Protocol):
22
201
  """Protocol defining the expected interface for endpoint parameters.
23
202
 
@@ -54,6 +233,9 @@ class ConnectorModelProtocol(Protocol):
54
233
  @property
55
234
  def openapi_spec(self) -> Any: ...
56
235
 
236
+ @property
237
+ def search_field_paths(self) -> dict[str, list[str]] | None: ...
238
+
57
239
 
58
240
  def format_param_signature(endpoint: EndpointProtocol) -> str:
59
241
  """Format parameter signature for an endpoint action.
@@ -86,9 +268,12 @@ def format_param_signature(endpoint: EndpointProtocol) -> str:
86
268
  required = schema.get("required", False)
87
269
  params.append(f"{name}{'*' if required else '?'}")
88
270
 
89
- # Body fields
90
- if request_schema:
91
- required_fields = set(request_schema.get("required", []))
271
+ # Body fields (include nested params from schema when available)
272
+ if isinstance(request_schema, dict):
273
+ for name, required in _flatten_schema_params(request_schema):
274
+ params.append(f"{name}{'*' if required else '?'}")
275
+ elif request_schema:
276
+ required_fields = set(request_schema.get("required", [])) if isinstance(request_schema, dict) else set()
92
277
  for name in body_fields:
93
278
  params.append(f"{name}{'*' if name in required_fields else '?'}")
94
279
 
@@ -99,7 +284,7 @@ def describe_entities(model: ConnectorModelProtocol) -> list[dict[str, Any]]:
99
284
  """Generate entity descriptions from ConnectorModel.
100
285
 
101
286
  Returns a list of entity descriptions with detailed parameter information
102
- for each action. This is used by generated connectors' describe() method.
287
+ for each action. This is used by generated connectors' list_entities() method.
103
288
 
104
289
  Args:
105
290
  model: Object conforming to ConnectorModelProtocol (e.g., ConnectorModel)
@@ -203,8 +388,8 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
203
388
  - Response structure documentation with pagination hints
204
389
  - Example questions if available in the OpenAPI spec
205
390
 
206
- This is used by the Connector.describe class method decorator to populate
207
- function docstrings for AI framework integration.
391
+ This is used by the Connector.tool_utils decorator to populate function
392
+ docstrings for AI framework integration.
208
393
 
209
394
  Args:
210
395
  model: Object conforming to ConnectorModelProtocol (e.g., ConnectorModel)
@@ -213,8 +398,11 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
213
398
  Formatted description string suitable for AI tool documentation
214
399
  """
215
400
  lines = []
401
+ # NOTE: Do not insert blank lines in the docstring; pydantic-ai parsing truncates
402
+ # at the first empty line and only keeps the initial section.
216
403
 
217
404
  # Entity/action parameter details (including pagination params like limit, starting_after)
405
+ search_field_paths = _collect_search_field_paths(model)
218
406
  lines.append("ENTITIES AND PARAMETERS:")
219
407
  for entity in model.entities:
220
408
  lines.append(f" {entity.name}:")
@@ -228,14 +416,41 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
228
416
  lines.append(f" - {action_str}{param_sig}")
229
417
  else:
230
418
  lines.append(f" - {action_str}()")
419
+ if entity.name in search_field_paths:
420
+ search_sig = _format_search_param_signature()
421
+ lines.append(f" - search{search_sig}")
231
422
 
232
423
  # Response structure (brief, includes pagination hint)
233
- lines.append("")
234
424
  lines.append("RESPONSE STRUCTURE:")
235
425
  lines.append(" - list/api_search: {data: [...], meta: {has_more: bool}}")
236
426
  lines.append(" - get: Returns entity directly (no envelope)")
237
427
  lines.append(" To paginate: pass starting_after=<last_id> while has_more is true")
238
428
 
429
+ lines.append("GUIDELINES:")
430
+ lines.append(' - Prefer cached search over direct API calls when using execute(): action="search" whenever possible.')
431
+ lines.append(" - Direct API actions (list/get/download) are slower and should be used only if search cannot answer the query.")
432
+ lines.append(" - Keep results small: use params.fields, params.query.filter, small params.limit, and cursor pagination.")
433
+ lines.append(" - If output is too large, refine the query with tighter filters/fields/limit.")
434
+
435
+ if search_field_paths:
436
+ lines.append("SEARCH (PREFERRED):")
437
+ lines.append(' execute(entity, action="search", params={')
438
+ lines.append(' "query": {"filter": <condition>, "sort": [{"field": "asc|desc"}, ...]},')
439
+ lines.append(' "limit": <int>, "cursor": <str>, "fields": ["field", "nested.field", ...]')
440
+ lines.append(" })")
441
+ lines.append(' Example: {"query": {"filter": {"eq": {"title": "Intro to Airbyte | Miinto"}}}, "limit": 1,')
442
+ lines.append(' "fields": ["id", "title", "started", "primaryUserId"]}')
443
+ lines.append(" Conditions are composable:")
444
+ lines.append(" - eq, neq, gt, gte, lt, lte, in, like, fuzzy, keyword, contains, any")
445
+ lines.append(' - and/or/not to combine conditions (e.g., {"and": [cond1, cond2]})')
446
+
447
+ lines.append("SEARCHABLE FIELDS:")
448
+ for entity_name, field_paths in search_field_paths.items():
449
+ if field_paths:
450
+ lines.append(f" {entity_name}: {', '.join(field_paths)}")
451
+ else:
452
+ lines.append(f" {entity_name}: (no fields listed)")
453
+
239
454
  # Add example questions if available in openapi_spec
240
455
  openapi_spec = getattr(model, "openapi_spec", None)
241
456
  if openapi_spec:
@@ -245,18 +460,15 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
245
460
  if example_questions:
246
461
  supported = getattr(example_questions, "supported", None)
247
462
  if supported:
248
- lines.append("")
249
463
  lines.append("EXAMPLE QUESTIONS:")
250
464
  for q in supported[:MAX_EXAMPLE_QUESTIONS]:
251
465
  lines.append(f" - {q}")
252
466
 
253
467
  # Generic parameter description for function signature
254
- lines.append("")
255
468
  lines.append("FUNCTION PARAMETERS:")
256
469
  lines.append(" - entity: Entity name (string)")
257
470
  lines.append(" - action: Operation to perform (string)")
258
471
  lines.append(" - params: Operation parameters (dict) - see entity details above")
259
- lines.append("")
260
472
  lines.append("Parameter markers: * = required, ? = optional")
261
473
 
262
474
  return "\n".join(lines)
@@ -13,7 +13,7 @@ from uuid import UUID
13
13
  from pydantic import BaseModel, ConfigDict, Field, field_validator
14
14
  from pydantic_core import Url
15
15
 
16
- from .extensions import CacheConfig, RetryConfig
16
+ from .extensions import CacheConfig, ReplicationConfig, RetryConfig
17
17
 
18
18
 
19
19
  class ExampleQuestions(BaseModel):
@@ -106,6 +106,7 @@ class Info(BaseModel):
106
106
  - x-airbyte-retry-config: Retry configuration for transient errors (Airbyte extension)
107
107
  - x-airbyte-example-questions: Example questions for AI connector README (Airbyte extension)
108
108
  - x-airbyte-cache: Cache configuration for field mapping between API and cache schemas (Airbyte extension)
109
+ - x-airbyte-replication-config: Replication configuration for MULTI mode connectors (Airbyte extension)
109
110
  """
110
111
 
111
112
  model_config = ConfigDict(populate_by_name=True, extra="forbid")
@@ -124,6 +125,7 @@ class Info(BaseModel):
124
125
  x_airbyte_retry_config: RetryConfig | None = Field(None, alias="x-airbyte-retry-config")
125
126
  x_airbyte_example_questions: ExampleQuestions | None = Field(None, alias="x-airbyte-example-questions")
126
127
  x_airbyte_cache: CacheConfig | None = Field(None, alias="x-airbyte-cache")
128
+ x_airbyte_replication_config: ReplicationConfig | None = Field(None, alias="x-airbyte-replication-config")
127
129
 
128
130
 
129
131
  class ServerVariable(BaseModel):
@@ -140,6 +142,33 @@ class ServerVariable(BaseModel):
140
142
  description: str | None = None
141
143
 
142
144
 
145
+ class EnvironmentMappingTransform(BaseModel):
146
+ """
147
+ Structured transform for environment mapping values.
148
+
149
+ Allows transforming environment values before storing in source_config.
150
+
151
+ Example:
152
+ source: subdomain
153
+ format: "{value}.atlassian.net"
154
+
155
+ The format string uses {value} as a placeholder for the source value.
156
+ """
157
+
158
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
159
+
160
+ source: str = Field(description="The environment config key to read the value from")
161
+ format: str | None = Field(
162
+ default=None,
163
+ description="Optional format string to transform the value. Use {value} as placeholder.",
164
+ )
165
+
166
+
167
+ # Type alias for environment mapping values: either a simple string (config key)
168
+ # or a structured transform with source and optional transform template
169
+ EnvironmentMappingValue = str | EnvironmentMappingTransform
170
+
171
+
143
172
  class Server(BaseModel):
144
173
  """
145
174
  Server URL and variable definitions.
@@ -152,7 +181,10 @@ class Server(BaseModel):
152
181
  url: str
153
182
  description: str | None = None
154
183
  variables: Dict[str, ServerVariable] = Field(default_factory=dict)
155
- x_airbyte_replication_environment_mapping: Dict[str, str] | None = Field(default=None, alias="x-airbyte-replication-environment-mapping")
184
+ x_airbyte_replication_environment_mapping: Dict[str, EnvironmentMappingValue] | None = Field(
185
+ default=None,
186
+ alias="x-airbyte-replication-environment-mapping",
187
+ )
156
188
  x_airbyte_replication_environment_constants: Dict[str, Any] | None = Field(
157
189
  default=None,
158
190
  alias="x-airbyte-replication-environment-constants",
@@ -134,6 +134,11 @@ class GraphQLBodyConfig(BaseModel):
134
134
  None,
135
135
  description="Default fields to select if not provided in request parameters. Can be a string or array of field names.",
136
136
  )
137
+ nullable_variables: List[str] | None = Field(
138
+ default=None,
139
+ alias="x-airbyte-nullable-variables",
140
+ description="Variable names that can be explicitly set to null (e.g., to unassign a user)",
141
+ )
137
142
 
138
143
 
139
144
  # Union type for all body type configs (extensible for future types like XML, SOAP, etc.)
@@ -182,6 +182,77 @@ class CacheEntityConfig(BaseModel):
182
182
  return self.x_airbyte_name or self.entity
183
183
 
184
184
 
185
+ class ReplicationConfigProperty(BaseModel):
186
+ """
187
+ Property definition for replication configuration fields.
188
+
189
+ Defines a single field in the replication configuration with its type,
190
+ description, and optional default value.
191
+
192
+ Example YAML usage:
193
+ x-airbyte-replication-config:
194
+ properties:
195
+ start_date:
196
+ type: string
197
+ title: Start Date
198
+ description: UTC date and time from which to replicate data
199
+ format: date-time
200
+ """
201
+
202
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
203
+
204
+ type: str
205
+ title: str | None = None
206
+ description: str | None = None
207
+ format: str | None = None
208
+ default: str | int | float | bool | None = None
209
+ enum: list[str] | None = None
210
+
211
+
212
+ class ReplicationConfig(BaseModel):
213
+ """
214
+ Replication configuration extension (x-airbyte-replication-config).
215
+
216
+ Defines replication-specific settings for MULTI mode connectors that need
217
+ to configure the underlying replication connector. This allows users who
218
+ use the direct-style API (credentials + environment) to also specify
219
+ replication settings like start_date, lookback_window, etc.
220
+
221
+ This extension is added to the Info model and provides field definitions
222
+ for replication configuration that gets merged into the source config
223
+ when creating sources.
224
+
225
+ Example YAML usage:
226
+ info:
227
+ title: HubSpot API
228
+ x-airbyte-replication-config:
229
+ title: Replication Configuration
230
+ description: Settings for data replication
231
+ properties:
232
+ start_date:
233
+ type: string
234
+ title: Start Date
235
+ description: UTC date and time from which to replicate data
236
+ format: date-time
237
+ required:
238
+ - start_date
239
+ replication_config_key_mapping:
240
+ start_date: start_date
241
+ """
242
+
243
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
244
+
245
+ title: str | None = None
246
+ description: str | None = None
247
+ properties: dict[str, ReplicationConfigProperty] = Field(default_factory=dict)
248
+ required: list[str] = Field(default_factory=list)
249
+ replication_config_key_mapping: dict[str, str] = Field(
250
+ default_factory=dict,
251
+ alias="replication_config_key_mapping",
252
+ description="Mapping from replication_config field names to source_config field names",
253
+ )
254
+
255
+
185
256
  class CacheConfig(BaseModel):
186
257
  """
187
258
  Cache configuration extension (x-airbyte-cache).
@@ -55,7 +55,6 @@ class AuthConfigFieldSpec(BaseModel):
55
55
  description: str | None = None
56
56
  format: str | None = None # e.g., "email", "uri"
57
57
  pattern: str | None = None # Regex validation
58
- airbyte_secret: bool = Field(False, alias="airbyte_secret")
59
58
  default: Any | None = None
60
59
 
61
60
 
@@ -252,3 +252,4 @@ class ConnectorModel(BaseModel):
252
252
  entities: list[EntityDefinition]
253
253
  openapi_spec: Any | None = None # Optional reference to OpenAPIConnector
254
254
  retry_config: RetryConfig | None = None # Optional retry configuration
255
+ search_field_paths: dict[str, list[str]] | None = None
@@ -4,8 +4,11 @@ Shopify connector.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import inspect
8
+ import json
7
9
  import logging
8
- from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload
10
+ from functools import wraps
11
+ from typing import TYPE_CHECKING, Any, Callable, Mapping, TypeVar, overload
9
12
  try:
10
13
  from typing import Literal
11
14
  except ImportError:
@@ -133,6 +136,38 @@ from .models import (
133
136
  # TypeVar for decorator type preservation
134
137
  _F = TypeVar("_F", bound=Callable[..., Any])
135
138
 
139
+ DEFAULT_MAX_OUTPUT_CHARS = 50_000 # ~50KB default, configurable per-tool
140
+
141
+
142
+ def _raise_output_too_large(message: str) -> None:
143
+ try:
144
+ from pydantic_ai import ModelRetry # type: ignore[import-not-found]
145
+ except Exception as exc:
146
+ raise RuntimeError(message) from exc
147
+ raise ModelRetry(message)
148
+
149
+
150
+ def _check_output_size(result: Any, max_chars: int | None, tool_name: str) -> Any:
151
+ if max_chars is None or max_chars <= 0:
152
+ return result
153
+
154
+ try:
155
+ serialized = json.dumps(result, default=str)
156
+ except (TypeError, ValueError):
157
+ return result
158
+
159
+ if len(serialized) > max_chars:
160
+ truncated_preview = serialized[:500] + "..." if len(serialized) > 500 else serialized
161
+ _raise_output_too_large(
162
+ f"Tool '{tool_name}' output too large ({len(serialized):,} chars, limit {max_chars:,}). "
163
+ "Please narrow your query by: using the 'fields' parameter to select only needed fields, "
164
+ "adding filters, or reducing the 'limit'. "
165
+ f"Preview: {truncated_preview}"
166
+ )
167
+
168
+ return result
169
+
170
+
136
171
 
137
172
 
138
173
  class ShopifyConnector:
@@ -143,7 +178,7 @@ class ShopifyConnector:
143
178
  """
144
179
 
145
180
  connector_name = "shopify"
146
- connector_version = "0.1.1"
181
+ connector_version = "0.1.2"
147
182
  vendored_sdk_version = "0.1.0" # Version of vendored connector-sdk
148
183
 
149
184
  # Map of (entity, action) -> needs_envelope for envelope wrapping decision
@@ -795,15 +830,15 @@ class ShopifyConnector:
795
830
  async def execute(
796
831
  self,
797
832
  entity: str,
798
- action: str,
799
- params: dict[str, Any]
833
+ action: Literal["list", "get"],
834
+ params: Mapping[str, Any]
800
835
  ) -> ShopifyExecuteResult[Any] | ShopifyExecuteResultWithMeta[Any, Any] | Any: ...
801
836
 
802
837
  async def execute(
803
838
  self,
804
839
  entity: str,
805
- action: str,
806
- params: dict[str, Any] | None = None
840
+ action: Literal["list", "get"],
841
+ params: Mapping[str, Any] | None = None
807
842
  ) -> Any:
808
843
  """
809
844
  Execute an entity operation with full type safety.
@@ -831,16 +866,17 @@ class ShopifyConnector:
831
866
  from ._vendored.connector_sdk.executor import ExecutionConfig
832
867
 
833
868
  # Remap parameter names from snake_case (TypedDict keys) to API parameter names
834
- if params:
869
+ resolved_params = dict(params) if params is not None else None
870
+ if resolved_params:
835
871
  param_map = self._PARAM_MAP.get((entity, action), {})
836
872
  if param_map:
837
- params = {param_map.get(k, k): v for k, v in params.items()}
873
+ resolved_params = {param_map.get(k, k): v for k, v in resolved_params.items()}
838
874
 
839
875
  # Use ExecutionConfig for both local and hosted executors
840
876
  config = ExecutionConfig(
841
877
  entity=entity,
842
878
  action=action,
843
- params=params
879
+ params=resolved_params
844
880
  )
845
881
 
846
882
  result = await self._executor.execute(config)
@@ -867,41 +903,67 @@ class ShopifyConnector:
867
903
  # ===== INTROSPECTION METHODS =====
868
904
 
869
905
  @classmethod
870
- def describe(cls, func: _F) -> _F:
906
+ def tool_utils(
907
+ cls,
908
+ func: _F | None = None,
909
+ *,
910
+ update_docstring: bool = True,
911
+ max_output_chars: int | None = DEFAULT_MAX_OUTPUT_CHARS,
912
+ ) -> _F | Callable[[_F], _F]:
871
913
  """
872
- Decorator that populates a function's docstring with connector capabilities.
873
-
874
- This class method can be used as a decorator to automatically generate
875
- comprehensive documentation for AI tool functions.
914
+ Decorator that adds tool utilities like docstring augmentation and output limits.
876
915
 
877
916
  Usage:
878
917
  @mcp.tool()
879
- @ShopifyConnector.describe
918
+ @ShopifyConnector.tool_utils
880
919
  async def execute(entity: str, action: str, params: dict):
881
- '''Execute operations.'''
882
920
  ...
883
921
 
884
- The decorated function's __doc__ will be updated with:
885
- - Available entities and their actions
886
- - Parameter signatures with required (*) and optional (?) markers
887
- - Response structure documentation
888
- - Example questions (if available in OpenAPI spec)
922
+ @mcp.tool()
923
+ @ShopifyConnector.tool_utils(update_docstring=False, max_output_chars=None)
924
+ async def execute(entity: str, action: str, params: dict):
925
+ ...
889
926
 
890
927
  Args:
891
- func: The function to decorate
928
+ update_docstring: When True, append connector capabilities to __doc__.
929
+ max_output_chars: Max serialized output size before raising. Use None to disable.
930
+ """
931
+
932
+ def decorate(inner: _F) -> _F:
933
+ if update_docstring:
934
+ description = generate_tool_description(ShopifyConnectorModel)
935
+ original_doc = inner.__doc__ or ""
936
+ if original_doc.strip():
937
+ full_doc = f"{original_doc.strip()}\n{description}"
938
+ else:
939
+ full_doc = description
940
+ else:
941
+ full_doc = ""
892
942
 
893
- Returns:
894
- The same function with updated __doc__
895
- """
896
- description = generate_tool_description(ShopifyConnectorModel)
943
+ if inspect.iscoroutinefunction(inner):
897
944
 
898
- original_doc = func.__doc__ or ""
899
- if original_doc.strip():
900
- func.__doc__ = f"{original_doc.strip()}\n{description}"
901
- else:
902
- func.__doc__ = description
945
+ @wraps(inner)
946
+ async def aw(*args: Any, **kwargs: Any) -> Any:
947
+ result = await inner(*args, **kwargs)
948
+ return _check_output_size(result, max_output_chars, inner.__name__)
949
+
950
+ wrapped = aw
951
+ else:
952
+
953
+ @wraps(inner)
954
+ def sw(*args: Any, **kwargs: Any) -> Any:
955
+ result = inner(*args, **kwargs)
956
+ return _check_output_size(result, max_output_chars, inner.__name__)
957
+
958
+ wrapped = sw
959
+
960
+ if update_docstring:
961
+ wrapped.__doc__ = full_doc
962
+ return wrapped # type: ignore[return-value]
903
963
 
904
- return func
964
+ if func is not None:
965
+ return decorate(func)
966
+ return decorate
905
967
 
906
968
  def list_entities(self) -> list[dict[str, Any]]:
907
969
  """
@@ -26,7 +26,7 @@ from uuid import (
26
26
  ShopifyConnectorModel: ConnectorModel = ConnectorModel(
27
27
  id=UUID('9da77001-af33-4bcd-be46-6252bf9342b9'),
28
28
  name='shopify',
29
- version='0.1.1',
29
+ version='0.1.2',
30
30
  base_url='https://{shop}.myshopify.com/admin/api/2025-01',
31
31
  auth=AuthConfig(
32
32
  type=AuthType.API_KEY,
@@ -39,7 +39,6 @@ ShopifyConnectorModel: ConnectorModel = ConnectorModel(
39
39
  'api_key': AuthConfigFieldSpec(
40
40
  title='Access Token',
41
41
  description='Your Shopify Admin API access token',
42
- airbyte_secret=True,
43
42
  ),
44
43
  'shop': AuthConfigFieldSpec(
45
44
  title='Shop Name',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airbyte-agent-shopify
3
- Version: 0.1.5
3
+ Version: 0.1.13
4
4
  Summary: Airbyte Shopify Connector for AI platforms
5
5
  Project-URL: Homepage, https://github.com/airbytehq/airbyte-agent-connectors
6
6
  Project-URL: Documentation, https://docs.airbyte.com/ai-agents/
@@ -93,7 +93,7 @@ connector = ShopifyConnector(
93
93
  )
94
94
 
95
95
  @agent.tool_plain # assumes you're using Pydantic AI
96
- @ShopifyConnector.describe
96
+ @ShopifyConnector.tool_utils
97
97
  async def shopify_execute(entity: str, action: str, params: dict | None = None):
98
98
  return await connector.execute(entity, action, params or {})
99
99
  ```
@@ -108,17 +108,18 @@ This example assumes you've already authenticated your connector with Airbyte. S
108
108
  from airbyte_agent_shopify import ShopifyConnector
109
109
 
110
110
  connector = ShopifyConnector(
111
- external_user_id="<your-scoped-token>",
111
+ external_user_id="<your_external_user_id>",
112
112
  airbyte_client_id="<your-client-id>",
113
113
  airbyte_client_secret="<your-client-secret>"
114
114
  )
115
115
 
116
116
  @agent.tool_plain # assumes you're using Pydantic AI
117
- @ShopifyConnector.describe
117
+ @ShopifyConnector.tool_utils
118
118
  async def shopify_execute(entity: str, action: str, params: dict | None = None):
119
119
  return await connector.execute(entity, action, params or {})
120
120
  ```
121
121
 
122
+
122
123
  ## Full documentation
123
124
 
124
125
  This connector supports the following entities and actions.
@@ -167,6 +168,6 @@ For the service's official API docs, see the [Shopify API reference](https://sho
167
168
 
168
169
  ## Version information
169
170
 
170
- - **Package version:** 0.1.5
171
- - **Connector version:** 0.1.1
172
- - **Generated with Connector SDK commit SHA:** b27328e2162813ea6315b9f890a6784b0fb9caba
171
+ - **Package version:** 0.1.13
172
+ - **Connector version:** 0.1.2
173
+ - **Generated with Connector SDK commit SHA:** c9b05509eb899e313055660f378d9c1f1e9129c7
@@ -1,27 +1,27 @@
1
1
  airbyte_agent_shopify/__init__.py,sha256=uFn4x1bUldfWTPBITTIGYrjD5bRufgEVvKVviDOUd_0,9661
2
- airbyte_agent_shopify/connector.py,sha256=Cax_d6uRrhn8h6H6uyPSe2crZCGDv-9UzNRF3vwbxgI,91544
3
- airbyte_agent_shopify/connector_model.py,sha256=xZDsz1CqmSkPTrlvK7hheWLs2RCDsIFD4JiZTu5ll-s,691051
2
+ airbyte_agent_shopify/connector.py,sha256=uDA7DNbgXNQQ94sp2Kinq2Rgu2FgBZEWzbgKTLEteZM,93793
3
+ airbyte_agent_shopify/connector_model.py,sha256=AVa7n--wns14y5tdsiKymVUMS7HboqVD8sdzvKPlBRM,691010
4
4
  airbyte_agent_shopify/models.py,sha256=hBMmLBLBlmqU8TdcJGpm3fQyyDVP3BEpDJ5C2cInBso,60917
5
5
  airbyte_agent_shopify/types.py,sha256=AwQ4PR2wZmHNuryriCFdRxf9q48J1mnPwspUB1b-L_o,10221
6
6
  airbyte_agent_shopify/_vendored/__init__.py,sha256=ILl7AHXMui__swyrjxrh9yRa4dLiwBvV6axPWFWty80,38
7
7
  airbyte_agent_shopify/_vendored/connector_sdk/__init__.py,sha256=T5o7roU6NSpH-lCAGZ338sE5dlh4ZU6i6IkeG1zpems,1949
8
- airbyte_agent_shopify/_vendored/connector_sdk/auth_strategies.py,sha256=Ve7ObJc_A0N99b8ouuicQzVauDunOvFNG1SRKDfV4ag,42110
8
+ airbyte_agent_shopify/_vendored/connector_sdk/auth_strategies.py,sha256=5Sb9moUp623o67Q2wMa8iZldJH08y4gQdoutoO_75Iw,42088
9
9
  airbyte_agent_shopify/_vendored/connector_sdk/auth_template.py,sha256=nju4jqlFC_KI82ILNumNIyiUtRJcy7J94INIZ0QraI4,4454
10
- airbyte_agent_shopify/_vendored/connector_sdk/connector_model_loader.py,sha256=SY_Juqw-cap156MsdgrMfe5MAuFdX0vUcSbH5LUYNK0,36295
10
+ airbyte_agent_shopify/_vendored/connector_sdk/connector_model_loader.py,sha256=AW9bsdggzuc3ydy2bYYF33L6LxLKLQer9Wm47IOuQw0,41492
11
11
  airbyte_agent_shopify/_vendored/connector_sdk/constants.py,sha256=AtzOvhDMWbRJgpsQNWl5tkogHD6mWgEY668PgRmgtOY,2737
12
12
  airbyte_agent_shopify/_vendored/connector_sdk/exceptions.py,sha256=ss5MGv9eVPmsbLcLWetuu3sDmvturwfo6Pw3M37Oq5k,481
13
13
  airbyte_agent_shopify/_vendored/connector_sdk/extensions.py,sha256=XWRRoJOOrwUHSKbuQt5DU7CCu8ePzhd_HuP7c_uD77w,21376
14
14
  airbyte_agent_shopify/_vendored/connector_sdk/http_client.py,sha256=yucwu3OvJh5wLQa1mk-gTKjtqjKKucMw5ltmlE7mk1c,28000
15
- airbyte_agent_shopify/_vendored/connector_sdk/introspection.py,sha256=2CyKXZHT74-1Id97uw1RLeyOi6TV24_hoNbQ6-6y7uI,10335
15
+ airbyte_agent_shopify/_vendored/connector_sdk/introspection.py,sha256=kRVI4TDQDLdcCnTBUML8ycAtdqAQufVh-027sMkb4i8,19165
16
16
  airbyte_agent_shopify/_vendored/connector_sdk/secrets.py,sha256=J9ezMu4xNnLW11xY5RCre6DHP7YMKZCqwGJfk7ufHAM,6855
17
- airbyte_agent_shopify/_vendored/connector_sdk/types.py,sha256=d8PidSD5nzhSSgFwUeYtRKw8pTm0Gft_IHsGeELifuk,8748
17
+ airbyte_agent_shopify/_vendored/connector_sdk/types.py,sha256=in8gHsn5nsScujOfHZmkOgNmqmJKiPyNNjg59m5fGWc,8807
18
18
  airbyte_agent_shopify/_vendored/connector_sdk/utils.py,sha256=G4LUXOC2HzPoND2v4tQW68R9uuPX9NQyCjaGxb7Kpl0,1958
19
19
  airbyte_agent_shopify/_vendored/connector_sdk/validation.py,sha256=4MPrxYmQh8TbCU0KdvvRKe35Lg1YYLEBd0u4aKySl_E,32122
20
20
  airbyte_agent_shopify/_vendored/connector_sdk/cloud_utils/__init__.py,sha256=4799Hv9f2zxDVj1aLyQ8JpTEuFTp_oOZMRz-NZCdBJg,134
21
21
  airbyte_agent_shopify/_vendored/connector_sdk/cloud_utils/client.py,sha256=YxdRpQr9XjDzih6csSseBVGn9kfMtaqbOCXP0TPuzFY,7189
22
22
  airbyte_agent_shopify/_vendored/connector_sdk/executor/__init__.py,sha256=EmG9YQNAjSuYCVB4D5VoLm4qpD1KfeiiOf7bpALj8p8,702
23
23
  airbyte_agent_shopify/_vendored/connector_sdk/executor/hosted_executor.py,sha256=ydHcG-biRS1ITT5ELwPShdJW-KYpvK--Fos1ipNgHho,6995
24
- airbyte_agent_shopify/_vendored/connector_sdk/executor/local_executor.py,sha256=AgBQBlYIra7kyWcZtRRWSJLqrPbuDq38j13hoVoWJJA,73863
24
+ airbyte_agent_shopify/_vendored/connector_sdk/executor/local_executor.py,sha256=tVbfstxOrm5qJt1NawTwjhIIpDgPCC4wSrKM5eALPSQ,74064
25
25
  airbyte_agent_shopify/_vendored/connector_sdk/executor/models.py,sha256=lYVT_bNcw-PoIks4WHNyl2VY-lJVf2FntzINSOBIheE,5845
26
26
  airbyte_agent_shopify/_vendored/connector_sdk/http/__init__.py,sha256=y8fbzZn-3yV9OxtYz8Dy6FFGI5v6TOqADd1G3xHH3Hw,911
27
27
  airbyte_agent_shopify/_vendored/connector_sdk/http/config.py,sha256=6J7YIIwHC6sRu9i-yKa5XvArwK2KU60rlnmxzDZq3lw,3283
@@ -42,16 +42,16 @@ airbyte_agent_shopify/_vendored/connector_sdk/performance/__init__.py,sha256=Sp5
42
42
  airbyte_agent_shopify/_vendored/connector_sdk/performance/instrumentation.py,sha256=_dXvNiqdndIBwDjeDKNViWzn_M5FkSUsMmJtFldrmsM,1504
43
43
  airbyte_agent_shopify/_vendored/connector_sdk/performance/metrics.py,sha256=FRff7dKt4iwt_A7pxV5n9kAGBR756PC7q8-weWygPSM,2817
44
44
  airbyte_agent_shopify/_vendored/connector_sdk/schema/__init__.py,sha256=Uymu-QuzGJuMxexBagIvUxpVAigIuIhz3KeBl_Vu4Ko,1638
45
- airbyte_agent_shopify/_vendored/connector_sdk/schema/base.py,sha256=zBW5svQohGCdn46Snc8DG4PsqfmZDovp693vRgugDFE,5374
46
- airbyte_agent_shopify/_vendored/connector_sdk/schema/components.py,sha256=x3YCM1p2n_xHi50fMeOX0mXUiPqjGlLHs3Go8jXokb0,7895
45
+ airbyte_agent_shopify/_vendored/connector_sdk/schema/base.py,sha256=IoAucZQ0j0xTdm4VWotB636R4jsrkYnppMQhXE0uoyU,6541
46
+ airbyte_agent_shopify/_vendored/connector_sdk/schema/components.py,sha256=nJIPieavwX3o3ODvdtLHPk84d_V229xmg6LDfwEHjzc,8119
47
47
  airbyte_agent_shopify/_vendored/connector_sdk/schema/connector.py,sha256=mSZk1wr2YSdRj9tTRsPAuIlCzd_xZLw-Bzl1sMwE0rE,3731
48
- airbyte_agent_shopify/_vendored/connector_sdk/schema/extensions.py,sha256=f7VhHrcIYxaPOJHMc4g0lpy04pZTbx5nlroNzAu5B9Q,7135
48
+ airbyte_agent_shopify/_vendored/connector_sdk/schema/extensions.py,sha256=5hgpFHK7fzpzegCkJk882DeIP79bCx_qairKJhvPMZ8,9590
49
49
  airbyte_agent_shopify/_vendored/connector_sdk/schema/operations.py,sha256=RpzGtAI4yvAtMHAfMUMcUwgHv_qJojnKlNb75_agUF8,5729
50
- airbyte_agent_shopify/_vendored/connector_sdk/schema/security.py,sha256=6ljzf_JHs4amnQX9AhePcEsT8P3ZnTSC4xeg7-cvsNQ,9100
50
+ airbyte_agent_shopify/_vendored/connector_sdk/schema/security.py,sha256=1CVCavrPdHHyk7B6JtUD75yRS_hWLCemZF1zwGbdqxg,9036
51
51
  airbyte_agent_shopify/_vendored/connector_sdk/telemetry/__init__.py,sha256=RaLgkBU4dfxn1LC5Y0Q9rr2PJbrwjxvPgBLmq8_WafE,211
52
52
  airbyte_agent_shopify/_vendored/connector_sdk/telemetry/config.py,sha256=tLmQwAFD0kP1WyBGWBS3ysaudN9H3e-3EopKZi6cGKg,885
53
53
  airbyte_agent_shopify/_vendored/connector_sdk/telemetry/events.py,sha256=8Y1NbXiwISX-V_wRofY7PqcwEXD0dLMnntKkY6XFU2s,1328
54
54
  airbyte_agent_shopify/_vendored/connector_sdk/telemetry/tracker.py,sha256=Ftrk0_ddfM7dZG8hF9xBuPwhbc9D6JZ7Q9qs5o3LEyA,5579
55
- airbyte_agent_shopify-0.1.5.dist-info/METADATA,sha256=FFre8Oj2mqmv1X_UlLmy7GlkTp4SO1vjM9W0lMhSvNo,7831
56
- airbyte_agent_shopify-0.1.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
57
- airbyte_agent_shopify-0.1.5.dist-info/RECORD,,
55
+ airbyte_agent_shopify-0.1.13.dist-info/METADATA,sha256=srNNoLgB5qbfylUD6eNQh3hJkjs51CIeSEYio99nbbg,7842
56
+ airbyte_agent_shopify-0.1.13.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
57
+ airbyte_agent_shopify-0.1.13.dist-info/RECORD,,