stoobly-agent 1.10.5__py3-none-any.whl → 1.11.0__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.
Files changed (30) hide show
  1. stoobly_agent/__init__.py +1 -1
  2. stoobly_agent/app/cli/intercept_cli.py +1 -1
  3. stoobly_agent/app/cli/request_cli.py +19 -1
  4. stoobly_agent/app/cli/scaffold/docker/service/builder.py +3 -6
  5. stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +2 -1
  6. stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +2 -0
  7. stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py +5 -0
  8. stoobly_agent/app/cli/scaffold/local/service/builder.py +3 -10
  9. stoobly_agent/app/cli/scaffold/local/workflow/builder.py +2 -4
  10. stoobly_agent/app/cli/scaffold/local/workflow/run_command.py +7 -0
  11. stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +1 -1
  12. stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context +2 -2
  13. stoobly_agent/app/cli/scaffold/templates/app/.Makefile +21 -11
  14. stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml +2 -0
  15. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.yml +2 -2
  16. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.yml +2 -2
  17. stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/request/log/.list +5 -0
  18. stoobly_agent/app/cli/scaffold/workflow_run_command.py +4 -5
  19. stoobly_agent/app/proxy/handle_mock_service.py +7 -2
  20. stoobly_agent/app/proxy/run.py +4 -1
  21. stoobly_agent/cli.py +16 -1
  22. stoobly_agent/config/constants/custom_headers.py +1 -0
  23. stoobly_agent/lib/intercepted_requests_logger.py +382 -0
  24. stoobly_agent/test/app/cli/scaffold/docker/e2e_test.py +2 -2
  25. stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
  26. {stoobly_agent-1.10.5.dist-info → stoobly_agent-1.11.0.dist-info}/METADATA +1 -1
  27. {stoobly_agent-1.10.5.dist-info → stoobly_agent-1.11.0.dist-info}/RECORD +30 -28
  28. {stoobly_agent-1.10.5.dist-info → stoobly_agent-1.11.0.dist-info}/WHEEL +0 -0
  29. {stoobly_agent-1.10.5.dist-info → stoobly_agent-1.11.0.dist-info}/entry_points.txt +0 -0
  30. {stoobly_agent-1.10.5.dist-info → stoobly_agent-1.11.0.dist-info}/licenses/LICENSE +0 -0
stoobly_agent/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  COMMAND = 'stoobly-agent'
2
- VERSION = '1.10.5'
2
+ VERSION = '1.11.0'
@@ -128,7 +128,7 @@ def configure(**kwargs):
128
128
  active_mode = settings.proxy.intercept.mode
129
129
  valid_policies = __get_policy_options(active_mode)
130
130
 
131
- if not kwargs['policy'] in valid_policies:
131
+ if kwargs['policy'] not in valid_policies:
132
132
  print("Error: Valid policies for {active_mode} are {valid_policies}", file=sys.stderr)
133
133
  sys.exit(1)
134
134
 
@@ -1,6 +1,7 @@
1
1
  import click
2
2
 
3
3
  from stoobly_agent.app.cli.helpers.handle_replay_service import BODY_FORMAT, JSON_FORMAT
4
+ from stoobly_agent.lib.intercepted_requests_logger import InterceptedRequestsLogger
4
5
  from stoobly_agent.app.models.factories.resource.local_db.helpers.log_event import DELETE_ACTION, PUT_ACTION
5
6
  from stoobly_agent.app.settings import Settings
6
7
  from stoobly_agent.config.constants import alias_resolve_strategy, test_filter, test_output_level, test_strategy
@@ -170,4 +171,21 @@ def get(**kwargs) -> None:
170
171
  def query(**kwargs):
171
172
  query_handler(kwargs)
172
173
 
173
- request.add_command(response)
174
+ @click.group(
175
+ epilog="Run 'stoobly-agent request log COMMAND --help' for more information on a command.",
176
+ help="Manage intercepted requests logs"
177
+ )
178
+ @click.pass_context
179
+ def log(ctx):
180
+ pass
181
+
182
+ @log.command(name="list", help="List intercepted requests log entries")
183
+ def log_list(**kwargs):
184
+ InterceptedRequestsLogger.dump_logs()
185
+
186
+ @log.command(name="delete", help="Delete intercepted requests log entries")
187
+ def log_delete(**kwargs):
188
+ InterceptedRequestsLogger.truncate()
189
+
190
+ request.add_command(response)
191
+ request.add_command(log)
@@ -1,21 +1,18 @@
1
1
  import os
2
2
  import pdb
3
3
 
4
- from typing import List
5
-
6
4
  from stoobly_agent.config.data_dir import DATA_DIR_NAME
7
5
 
8
6
  from ...app_config import AppConfig
9
7
  from ...constants import (
10
- APP_DIR, SERVICES_NAMESPACE,
8
+ APP_DIR, SERVICES_NAMESPACE,
11
9
  SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV,
12
- SERVICE_NAME, SERVICE_NAME_ENV,
13
10
  SERVICE_ID,
14
- SERVICE_PORT, SERVICE_PORT_ENV,
11
+ SERVICE_PORT, SERVICE_PORT_ENV,
15
12
  SERVICE_SCHEME, SERVICE_SCHEME_ENV,
16
13
  SERVICE_UPSTREAM_HOSTNAME, SERVICE_UPSTREAM_HOSTNAME_ENV, SERVICE_UPSTREAM_PORT, SERVICE_UPSTREAM_PORT_ENV, SERVICE_UPSTREAM_SCHEME, SERVICE_UPSTREAM_SCHEME_ENV,
17
14
  STOOBLY_HOME_DIR,
18
- WORKFLOW_NAME, WORKFLOW_NAME_ENV, WORKFLOW_SCRIPTS, WORKFLOW_TEMPLATE
15
+ WORKFLOW_SCRIPTS, WORKFLOW_TEMPLATE
19
16
  )
20
17
  from ...service_config import ServiceConfig
21
18
  from ...local.service.builder import ServiceBuilder
@@ -4,7 +4,7 @@ import pdb
4
4
  from typing import List, Union
5
5
 
6
6
  from ...constants import (
7
- WORKFLOW_CONTAINER_CONFIGURE_TEMPLATE, WORKFLOW_CONTAINER_INIT_TEMPLATE, WORKFLOW_CONTAINER_PROXY_TEMPLATE, WORKFLOW_NAME, WORKFLOW_NAME_ENV
7
+ WORKFLOW_CONTAINER_CONFIGURE_TEMPLATE, WORKFLOW_CONTAINER_INIT_TEMPLATE, WORKFLOW_CONTAINER_PROXY_TEMPLATE, WORKFLOW_NAME
8
8
  )
9
9
  from ...local.workflow.builder import WorkflowBuilder
10
10
  from ..builder import Builder
@@ -13,6 +13,7 @@ from ..service.builder import DockerServiceBuilder
13
13
  from .mock_decorator import MockDecorator
14
14
  from .reverse_proxy_decorator import ReverseProxyDecorator
15
15
 
16
+
16
17
  class DockerWorkflowBuilder(Builder, WorkflowBuilder):
17
18
 
18
19
  def __init__(self, workflow_path: str, service_builder: DockerServiceBuilder):
@@ -34,6 +34,8 @@ class MockDecorator(CommandDecorator):
34
34
  command.append('--certs')
35
35
  command.append(os.path.join(STOOBLY_CERTS_DIR, f"{SERVICE_HOSTNAME}-joined.pem"))
36
36
 
37
+ command.append('--request-log-enable')
38
+
37
39
  services = self.workflow_builder.services
38
40
  proxy_name = self.workflow_builder.proxy
39
41
  proxy_service = services.get(proxy_name) or {}
@@ -435,6 +435,11 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
435
435
  if folder != self.workflow_name:
436
436
  workflow_name = file_name.split(self.timestamp_file_extension)[0]
437
437
 
438
+ # If the workflow is namespaced, allow it to run at the same time
439
+ # Same workflow with same namespace is allowed, workflow will be restarted
440
+ if workflow_name == self.workflow_name:
441
+ return
442
+
438
443
  Logger.instance(LOG_ID).error(f"Workflow '{workflow_name}' is running, please stop it first.")
439
444
 
440
445
  if folder != workflow_name:
@@ -3,17 +3,10 @@ from typing import List
3
3
 
4
4
  from stoobly_agent.config.data_dir import DATA_DIR_NAME
5
5
 
6
- from ...app_config import AppConfig
7
6
  from ...constants import (
8
- APP_DIR, SERVICES_NAMESPACE,
9
- SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV,
10
- SERVICE_NAME, SERVICE_NAME_ENV,
11
- SERVICE_ID,
12
- SERVICE_PORT, SERVICE_PORT_ENV,
13
- SERVICE_SCHEME, SERVICE_SCHEME_ENV,
14
- SERVICE_UPSTREAM_HOSTNAME, SERVICE_UPSTREAM_HOSTNAME_ENV, SERVICE_UPSTREAM_PORT, SERVICE_UPSTREAM_PORT_ENV, SERVICE_UPSTREAM_SCHEME, SERVICE_UPSTREAM_SCHEME_ENV,
7
+ SERVICES_NAMESPACE, SERVICE_NAME, SERVICE_NAME_ENV,
15
8
  STOOBLY_HOME_DIR,
16
- WORKFLOW_NAME, WORKFLOW_NAME_ENV, WORKFLOW_SCRIPTS, WORKFLOW_TEMPLATE
9
+ WORKFLOW_NAME, WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV
17
10
  )
18
11
  from ...service_config import ServiceConfig
19
12
 
@@ -23,7 +16,7 @@ class ServiceBuilder():
23
16
  self.__config = config
24
17
  self.__dir_path = config.dir
25
18
  self.__upstream_port = None
26
- self.__env = [SERVICE_NAME_ENV, WORKFLOW_NAME_ENV]
19
+ self.__env = [SERVICE_NAME_ENV, WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV]
27
20
  self.__service_name = os.path.basename(config.dir)
28
21
  self.__working_dir = os.path.join(
29
22
  STOOBLY_HOME_DIR, DATA_DIR_NAME, SERVICES_NAMESPACE, SERVICE_NAME, WORKFLOW_NAME
@@ -1,14 +1,12 @@
1
1
  import os
2
2
 
3
- from typing import Union
4
-
5
- from ...constants import SERVICE_NAME_ENV, WORKFLOW_NAME_ENV
3
+ from ...constants import SERVICE_NAME_ENV, WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV
6
4
  from ..service.builder import ServiceBuilder
7
5
 
8
6
  class WorkflowBuilder():
9
7
 
10
8
  def __init__(self, workflow_path: str, service_builder: ServiceBuilder):
11
- self._env = [SERVICE_NAME_ENV, WORKFLOW_NAME_ENV]
9
+ self._env = [SERVICE_NAME_ENV, WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV]
12
10
  self._service_builder = service_builder
13
11
  self._workflow_name = os.path.basename(workflow_path)
14
12
 
@@ -283,9 +283,16 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
283
283
  else:
284
284
  file_name = os.path.basename(pid_file_path)
285
285
  workflow_name = self.workflow_name
286
+
287
+ # If the folder is not the same as the workflow name, then the workflow is namespaced, get the real workflow name
286
288
  if folder != self.workflow_name:
287
289
  workflow_name = file_name.split(self.pid_file_extension)[0]
288
290
 
291
+ # If the workflow is namespaced, allow it to run at the same time
292
+ # Same workflow with same namespace is covered by pid_file_path check
293
+ if workflow_name == self.workflow_name:
294
+ return
295
+
289
296
  Logger.instance(LOG_ID).error(f"Workflow {workflow_name} is already running with PID {pid}")
290
297
 
291
298
  if folder != workflow_name:
@@ -135,7 +135,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
135
135
  # connection is working, but we haven't recorded anything yet
136
136
  logs = output.decode('ascii')
137
137
  if logs != '200' and logs != '499':
138
- raise ScaffoldValidateException(f"Error reaching {url} from inside Docker network")
138
+ raise ScaffoldValidateException(f"Error reaching {url} from inside Docker network with logs: \n{logs}")
139
139
 
140
140
  # Check public folder exists in container
141
141
  def validate_public_folder(self, container: Container):
@@ -1,6 +1,6 @@
1
- FROM stoobly/agent:1.10
1
+ FROM stoobly/agent:1.11
2
2
 
3
3
  ARG USER_ID
4
4
 
5
5
  # Change user id of stoobly user to that of host's user id
6
- RUN if [ -n "$USER_ID" ]; then usermod -u $USER_ID stoobly; fi
6
+ RUN if [ -n "$USER_ID" ]; then usermod -u $USER_ID stoobly; fi
@@ -110,10 +110,11 @@ intercept/disable:
110
110
  intercept/enable:
111
111
  @export EXEC_COMMAND=intercept/.enable EXEC_OPTIONS="" EXEC_ARGS=$(scenario_key) && \
112
112
  $(stoobly_exec)
113
- mock: workflow/mock ca-cert/install workflow/up nameservers workflow/hostname/install workflow/run
113
+ mock: workflow/mock ca-cert/install workflow/up nameservers workflow/hostname/install workflow/up/run
114
+ mock/down: workflow/mock workflow/down workflow/down/run workflow/hostname/uninstall
115
+ mock/logs: workflow/mock workflow/logs workflow/logs/run
116
+ mock/report: workflow/mock workflow/report
114
117
  mock/services: workflow/mock workflow/services
115
- mock/logs: workflow/mock workflow/logs workflow/run
116
- mock/down: workflow/mock workflow/down workflow/run workflow/hostname/uninstall
117
118
  pipx/install:
118
119
  @if ! command -v pipx >/dev/null 2>&1; then \
119
120
  echo "pipx is not installed. Installing pipx..."; \
@@ -124,10 +125,11 @@ python/validate:
124
125
  echo "Error: Python 3.10, 3.11, or 3.12 is required."; \
125
126
  exit 1; \
126
127
  fi
127
- record: workflow/record ca-cert/install workflow/up nameservers workflow/hostname/install workflow/run
128
- record/down: workflow/record workflow/down workflow/run workflow/hostname/uninstall
128
+ record: workflow/record ca-cert/install workflow/up nameservers workflow/hostname/install workflow/up/run
129
+ record/down: workflow/record workflow/down workflow/down/run workflow/hostname/uninstall
130
+ record/logs: workflow/record workflow/logs workflow/logs/run
131
+ record/report: workflow/record workflow/report
129
132
  record/services: workflow/record workflow/services
130
- record/logs: workflow/record workflow/logs workflow/run
131
133
  scenario/create:
132
134
  # Create a scenario
133
135
  @export EXEC_COMMAND=scenario/.create EXEC_OPTIONS="$(options)" EXEC_ARGS="$(name)" && \
@@ -157,15 +159,18 @@ stoobly/install: python/validate pipx/install
157
159
  echo "stoobly-agent not found. Installing..."; \
158
160
  pipx install stoobly-agent || { echo "Failed to install stoobly-agent"; exit 1; }; \
159
161
  fi
160
- test: workflow/test workflow/up workflow/run
162
+ test: workflow/test workflow/up workflow/up/run
163
+ test/down: workflow/test workflow/down workflow/down/run
164
+ test/logs: workflow/test workflow/logs workflow/logs/run
165
+ test/report: workflow/test workflow/report
161
166
  test/services: workflow/test workflow/services
162
- test/logs: workflow/test workflow/logs workflow/run
163
- test/down: workflow/test workflow/down workflow/run
164
167
  tmpdir:
165
168
  @mkdir -p $(app_tmp_dir)
166
169
  workflow/down: dotenv
167
170
  @export EXEC_COMMAND=scaffold/.down EXEC_OPTIONS="$(workflow_down_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
168
171
  $(stoobly_exec_run)
172
+ workflow/down/run:
173
+ @bash "$(app_dir)/$(workflow_script)"
169
174
  workflow/hostname: stoobly/install
170
175
  @if [ -n "$$STOOBLY_HOSTNAME_INSTALL_CONFIRM" ]; then \
171
176
  confirm="$$STOOBLY_HOSTNAME_INSTALL_CONFIRM"; \
@@ -187,14 +192,17 @@ workflow/hostname/uninstall: action/uninstall workflow/hostname
187
192
  workflow/logs:
188
193
  @export EXEC_COMMAND=scaffold/.logs EXEC_OPTIONS="$(workflow_log_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
189
194
  $(stoobly_exec_run)
195
+ workflow/logs/run:
196
+ @bash "$(app_dir)/$(workflow_script)"
190
197
  workflow/mock:
191
198
  $(eval workflow=mock)
192
199
  workflow/namespace: tmpdir
193
200
  @mkdir -p $(workflow_namespace_dir)
194
201
  workflow/record:
195
202
  $(eval workflow=record)
196
- workflow/run:
197
- @bash "$(app_dir)/$(workflow_script)"
203
+ workflow/report:
204
+ @export EXEC_COMMAND=request/log/.list EXEC_OPTIONS="$(options)" EXEC_ARGS="" && \
205
+ $(stoobly_exec)
198
206
  workflow/services:
199
207
  @export EXEC_COMMAND=scaffold/.services EXEC_OPTIONS="$(workflow_service_options) $(options)" EXEC_ARGS="$(workflow)" && \
200
208
  $(stoobly_exec_run)
@@ -203,3 +211,5 @@ workflow/test:
203
211
  workflow/up: dotenv
204
212
  @export EXEC_COMMAND=scaffold/.up EXEC_OPTIONS="$(workflow_up_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
205
213
  $(stoobly_exec_run)
214
+ workflow/up/run:
215
+ @bash "$(app_dir)/$(workflow_script)"
@@ -4,6 +4,8 @@ services:
4
4
  service: stoobly_base
5
5
  volumes:
6
6
  - ${CONTEXT_DIR}/.stoobly:/home/stoobly/.stoobly
7
+ - ${APP_DIR}/.stoobly/ca_certs:/home/stoobly/.stoobly/ca_certs
8
+ - ${APP_DIR}/.stoobly/certs:/home/stoobly/.stoobly/certs
7
9
  - ${APP_DIR}/.stoobly/services:/home/stoobly/.stoobly/services
8
10
  stoobly_base:
9
11
  image: stoobly.${USER_ID}
@@ -5,8 +5,8 @@ services:
5
5
  file: ../.docker-compose.base.yml
6
6
  service: stoobly_ui.base
7
7
  networks:
8
- app.egress:
9
- aliases:
8
+ app.egress:
9
+ aliases:
10
10
  - local.stoobly.com
11
11
  ports:
12
12
  - '${APP_UI_PORT}:4200'
@@ -5,8 +5,8 @@ services:
5
5
  file: ../.docker-compose.base.yml
6
6
  service: stoobly_ui.base
7
7
  networks:
8
- app.egress:
9
- aliases:
8
+ app.egress:
9
+ aliases:
10
10
  - local.stoobly.com
11
11
  ports:
12
12
  - '${APP_UI_PORT}:4200'
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ extra_options=$EXEC_OPTIONS
4
+
5
+ stoobly-agent request log list $extra_options
@@ -8,8 +8,8 @@ from stoobly_agent.config.data_dir import DataDir
8
8
 
9
9
  from .app import App
10
10
  from .constants import (
11
- APP_DIR_ENV, APP_NETWORK_ENV, APP_PLUGINS_ENV, CA_CERTS_DIR_ENV, CERTS_DIR_ENV, CONTEXT_DIR_ENV,
12
- SERVICE_DNS_ENV, SERVICE_NAME_ENV, SERVICE_SCRIPTS_DIR, SERVICE_SCRIPTS_ENV, USER_ID_ENV,
11
+ APP_DIR_ENV, APP_NETWORK_ENV, CA_CERTS_DIR_ENV, CERTS_DIR_ENV, CONTEXT_DIR_ENV,
12
+ SERVICE_DNS_ENV, SERVICE_NAME_ENV, SERVICE_SCRIPTS_DIR, SERVICE_SCRIPTS_ENV, USER_ID_ENV,
13
13
  WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV, WORKFLOW_SCRIPTS_DIR, WORKFLOW_SCRIPTS_ENV, WORKFLOW_TEMPLATE_ENV
14
14
  )
15
15
 
@@ -139,9 +139,8 @@ class WorkflowRunCommand(WorkflowCommand):
139
139
  _config[WORKFLOW_NAME_ENV] = self.workflow_name
140
140
  _config[WORKFLOW_SCRIPTS_ENV] = WORKFLOW_SCRIPTS_DIR
141
141
  _config[WORKFLOW_TEMPLATE_ENV] = self.workflow_name
142
-
143
- if namespace:
144
- _config[WORKFLOW_NAMESPACE_ENV] = namespace
142
+ # Default to the workflow name if a namespace isn't given
143
+ _config[WORKFLOW_NAMESPACE_ENV] = namespace if namespace else _config[WORKFLOW_NAME_ENV]
145
144
 
146
145
  if self.network:
147
146
  _config[APP_NETWORK_ENV] = self.network
@@ -10,8 +10,8 @@ from stoobly_agent.app.models.request_model import RequestModel
10
10
  from stoobly_agent.app.proxy.mitmproxy.request_facade import MitmproxyRequestFacade
11
11
  from stoobly_agent.app.proxy.utils.rewrite_rules_to_ignored_components_service import rewrite_rules_to_ignored_components
12
12
  from stoobly_agent.config.constants import custom_headers, env_vars, lifecycle_hooks, mock_policy, request_origin
13
- from stoobly_agent.lib.logger import bcolors, Logger
14
-
13
+ from stoobly_agent.lib.logger import bcolors, Logger, DEBUG, INFO, WARNING, ERROR
14
+ from stoobly_agent.lib.intercepted_requests_logger import InterceptedRequestsLogger
15
15
  from .constants import custom_response_codes
16
16
  from .mock.context import MockContext
17
17
  from .mock.eval_fixtures_service import eval_fixtures
@@ -153,6 +153,9 @@ def handle_response_mock(context: MockContext):
153
153
  def __handle_mock_failure(context: MockContext) -> None:
154
154
  flow = context.flow
155
155
  request = flow.request
156
+ response = context.response
157
+
158
+ InterceptedRequestsLogger.error("Mock failure", request=request, response=response)
156
159
 
157
160
  if request.method.upper() != 'OPTIONS':
158
161
  return False
@@ -187,10 +190,12 @@ def __handle_mock_success(context: MockContext) -> None:
187
190
  request_key = response.headers.get(custom_headers.MOCK_REQUEST_KEY)
188
191
  if request_key:
189
192
  Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}Mocked{bcolors.ENDC} {request.url} -> {request_key}")
193
+ InterceptedRequestsLogger.info("Mock success", request=request, response=response, request_key=request_key)
190
194
 
191
195
  fixture_path = response.headers.get(custom_headers.MOCK_FIXTURE_PATH)
192
196
  if fixture_path:
193
197
  Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}Mocked{bcolors.ENDC} {request.url} -> {fixture_path}")
198
+ InterceptedRequestsLogger.info("Mock success", request=request, response=response, fixture_path=fixture_path)
194
199
 
195
200
  if os.environ.get(env_vars.AGENT_SIMULATE_LATENCY):
196
201
  response = context.response
@@ -138,4 +138,7 @@ def __filter_options(options):
138
138
  del options['log_level']
139
139
  del options['proxy_host']
140
140
  del options['proxy_mode']
141
- del options['proxy_port']
141
+ del options['proxy_port']
142
+ del options['request_log_enable']
143
+ del options['request_log_level']
144
+ del options['request_log_truncate']
stoobly_agent/cli.py CHANGED
@@ -12,9 +12,9 @@ from stoobly_agent.app.cli.helpers.validations import validate_project_key, vali
12
12
  from stoobly_agent.app.cli.intercept_cli import mode_options
13
13
  from stoobly_agent.app.proxy.constants import custom_response_codes
14
14
  from stoobly_agent.app.proxy.replay.replay_request_service import replay as replay_request
15
- from stoobly_agent.app.settings.constants import intercept_mode
16
15
  from stoobly_agent.config.constants import env_vars, mode
17
16
  from stoobly_agent.config.data_dir import DataDir
17
+ from stoobly_agent.lib.intercepted_requests_logger import InterceptedRequestsLogger
18
18
  from stoobly_agent.lib.logger import Logger
19
19
  from stoobly_agent.lib.utils.conditional_decorator import ConditionalDecorator
20
20
 
@@ -123,6 +123,9 @@ def init(**kwargs):
123
123
  @click.option('--proxy-port', default=8080, type=click.IntRange(1, 65535), help='Proxy service port.')
124
124
  @click.option('--public-directory-path', multiple=True, help='Path to public files. Used for mocking requests. Can take the form <FOLDER-PATH>[:<ORIGIN>].')
125
125
  @click.option('--response-fixtures-path', multiple=True, help='Path to response fixtures yaml. Used for mocking requests. Can take the form <FILE-PATH>[:<ORIGIN>].')
126
+ @click.option('--request-log-enable', is_flag=True, default=False, required=False, help='Enable intercepted requests logging')
127
+ @click.option('--request-log-level', default=logger.INFO, type=click.Choice([logger.DEBUG, logger.INFO, logger.WARNING, logger.ERROR]), help='Log level for intercepted requests.')
128
+ @click.option('--request-log-truncate', is_flag=True, default=True, required=False, help='Truncate the intercepted requests log')
126
129
  @click.option('--ssl-insecure', is_flag=True, default=False, help='Do not verify upstream server SSL/TLS certificates.')
127
130
  @click.option('--ui-host', default='0.0.0.0', help='Address to bind UI to.')
128
131
  @click.option('--ui-port', default=4200, type=click.IntRange(1, 65535), help='UI service port.')
@@ -180,6 +183,18 @@ def run(**kwargs):
180
183
  os.environ[env_vars.AGENT_PROXY_URL] = proxy_url
181
184
  settings.proxy.url = proxy_url
182
185
 
186
+ if kwargs.get('request_log_enable'):
187
+ # If truncating, do that first (it handles enable internally)
188
+ if kwargs.get('request_log_truncate'):
189
+ InterceptedRequestsLogger.truncate()
190
+ else:
191
+ InterceptedRequestsLogger.enable_logger_file()
192
+
193
+ # Set log level after logger is enabled
194
+ request_log_level = kwargs.get('request_log_level')
195
+ if request_log_level:
196
+ InterceptedRequestsLogger.set_log_level(request_log_level)
197
+
183
198
  if kwargs.get('detached'):
184
199
  # Run in detached mode with output redirection
185
200
  import subprocess
@@ -25,6 +25,7 @@ SESSION_ID = 'X-Stoobly-Session-Id'
25
25
  SERVICE_URL = 'X-Stoobly-Service-Url'
26
26
  TEST_FILTER = 'X-Stoobly-Test-Filter'
27
27
  TEST_ID = 'X-Stoobly-Test-Id'
28
+ TEST_NAME = 'X-Stoobly-Test-Name'
28
29
  TEST_POLICY = 'X-Stoobly-Test-Policy'
29
30
  TEST_SAVE_RESULTS = 'X-Stoobly-Test-Save-Results'
30
31
  TEST_SKIP = 'X-Stoobly-Test-Skip'
@@ -0,0 +1,382 @@
1
+
2
+ import os
3
+ import logging
4
+ import json
5
+ from datetime import datetime
6
+ from typing import Final
7
+
8
+ from mitmproxy.http import Request as MitmproxyRequest
9
+ from requests import Response
10
+ from stoobly_agent.app.cli.scaffold.constants import WORKFLOW_NAMESPACE_ENV, SERVICE_NAME_ENV
11
+ from stoobly_agent.app.settings import Settings
12
+ from stoobly_agent.app.proxy.intercept_settings import InterceptSettings
13
+ from stoobly_agent.config.data_dir import DataDir
14
+ from stoobly_agent.lib.api.keys.scenario_key import ScenarioKey, InvalidScenarioKey
15
+ from stoobly_agent.lib.api.keys.request_key import RequestKey, InvalidRequestKey
16
+ from stoobly_agent.lib.orm.scenario import Scenario
17
+ from stoobly_agent.lib.orm.request import Request
18
+ from stoobly_agent.lib.logger import DEBUG, ERROR, INFO, WARNING, Logger
19
+ from stoobly_agent.config.constants import custom_headers
20
+
21
+
22
+ class InterceptedRequestsLogger():
23
+ __LOG_ID: Final[str] = "InterceptedRequestsLogger"
24
+ __logger: Logger = Logger.instance(__LOG_ID)
25
+
26
+ __settings: Settings = Settings.instance()
27
+ __NAMESPACE: str = os.environ.get(WORKFLOW_NAMESPACE_ENV, __settings.proxy.intercept.mode)
28
+ __file_path: str = None
29
+ __previous_scenario_key: str = None
30
+
31
+ # Initialize logger as disabled by default
32
+ __logger.disabled = True
33
+
34
+ class JSONFormatter(logging.Formatter):
35
+ def __init__(self, settings: Settings):
36
+ super().__init__()
37
+ self.__settings = settings
38
+
39
+ def _get_base_ui_url(self) -> str:
40
+ base_url = self.__settings.ui.url
41
+
42
+ # If base URL is not set in settings.yml OR it's set to the URL from within a Scaffold workflow
43
+ # Then return the localhost URL so users can access it.
44
+ if not base_url or base_url == 'http://local.stoobly.com:4200':
45
+ base_url = 'http://localhost:4200'
46
+ return base_url
47
+
48
+ def format(self, record: logging.LogRecord) -> str:
49
+ # Handle delimiter entries such as when changing scenarios
50
+ if hasattr(record, 'delimiter'):
51
+ delimiter_entry = {
52
+ "timestamp": datetime.fromtimestamp(record.created).isoformat(),
53
+ **record.delimiter
54
+ }
55
+ return json.dumps(delimiter_entry)
56
+
57
+ log_entry = {
58
+ "timestamp": datetime.fromtimestamp(record.created).isoformat(),
59
+ "level": record.levelname,
60
+ "message": record.getMessage()
61
+ }
62
+
63
+ # Extract fields from request
64
+ if hasattr(record, 'request') and record.request is not None:
65
+ request: MitmproxyRequest = record.request
66
+ log_entry.update({
67
+ "method": request.method,
68
+ "url": request.pretty_url,
69
+ })
70
+
71
+ # Extract fields from response
72
+ if hasattr(record, 'response') and record.response is not None:
73
+ response = record.response
74
+ log_entry.update({
75
+ "status_code": response.status_code,
76
+ })
77
+
78
+ # Set scenario key
79
+ intercept_settings = InterceptSettings(self.__settings)
80
+ scenario_key = intercept_settings.scenario_key
81
+ log_entry.update({
82
+ "scenario_key": scenario_key
83
+ })
84
+
85
+ # Set scenario name if scenario_key is set
86
+ scenario_name = ""
87
+ if scenario_key:
88
+ scenario_name = InterceptedRequestsLogger._get_scenario_name(scenario_key)
89
+ log_entry.update({
90
+ "scenario_name": scenario_name
91
+ })
92
+
93
+ # Set scaffold service name
94
+ service_name = ""
95
+ env_var_service_name = os.environ.get(SERVICE_NAME_ENV)
96
+ if env_var_service_name:
97
+ service_name = env_var_service_name
98
+ log_entry.update({
99
+ "service_name": service_name
100
+ })
101
+
102
+ # Set scaffold namespace name
103
+ env_var_namespace_name = os.environ.get(WORKFLOW_NAMESPACE_ENV)
104
+ if env_var_namespace_name:
105
+ log_entry.update({
106
+ "namespace": env_var_namespace_name
107
+ })
108
+
109
+ # Set scenario UI URL if scenario_key has a value
110
+ stoobly_ui_scenario_url = ""
111
+ if scenario_key:
112
+ scenario_id = InterceptedRequestsLogger._get_scenario_id(scenario_key)
113
+ if scenario_id:
114
+ base_url = self._get_base_ui_url()
115
+ stoobly_ui_scenario_url = f"{base_url}/agent/scenarios/{scenario_id}"
116
+ log_entry.update({
117
+ "stoobly_ui_scenario_url": stoobly_ui_scenario_url
118
+ })
119
+
120
+ # Set request key and UI URL - prioritize passed-in value, fallback to response headers
121
+ stoobly_ui_request_url = ""
122
+ request_key = None
123
+
124
+ # Check for passed-in request_key first
125
+ if hasattr(record, 'request_key') and record.request_key is not None:
126
+ request_key = record.request_key
127
+ # Fallback to extracting from response headers
128
+ elif hasattr(record, 'response') and record.response is not None:
129
+ request_key = InterceptedRequestsLogger._extract_request_key(record.response)
130
+
131
+ if request_key:
132
+ log_entry.update({
133
+ "request_key": request_key
134
+ })
135
+
136
+ request_id = InterceptedRequestsLogger._get_request_id(request_key)
137
+ if request_id:
138
+ base_url = self._get_base_ui_url()
139
+ stoobly_ui_request_url = f"{base_url}/agent/requests/{request_id}"
140
+
141
+ log_entry.update({
142
+ "stoobly_ui_request_url": stoobly_ui_request_url
143
+ })
144
+
145
+ # Set fixture path if provided
146
+ if hasattr(record, 'fixture_path') and record.fixture_path is not None:
147
+ log_entry.update({
148
+ "fixture_path": record.fixture_path
149
+ })
150
+
151
+ # Set test name if available in request headers
152
+ if hasattr(record, 'request') and record.request is not None:
153
+ request: MitmproxyRequest = record.request
154
+ if hasattr(request, 'headers') and request.headers:
155
+ test_name = request.headers.get(custom_headers.TEST_NAME, "")
156
+ if test_name:
157
+ log_entry.update({
158
+ "test_name": test_name
159
+ })
160
+
161
+ return json.dumps(log_entry)
162
+
163
+ @classmethod
164
+ def set_file_path(cls, file_path: str) -> None:
165
+ cls.__file_path = file_path
166
+
167
+ @classmethod
168
+ def reset_scenario_key(cls) -> None:
169
+ cls.__previous_scenario_key = None
170
+
171
+ @classmethod
172
+ def set_log_level(cls, log_level: str) -> None:
173
+ # Convert string log level to Python standard logging constants
174
+ level_mapping = {
175
+ DEBUG: logging.DEBUG,
176
+ INFO: logging.INFO,
177
+ WARNING: logging.WARNING,
178
+ ERROR: logging.ERROR,
179
+ }
180
+
181
+ numeric_level = level_mapping.get(log_level.lower())
182
+ if numeric_level is None:
183
+ raise ValueError(f"Invalid log level: {log_level}. Must be one of: {', '.join(level_mapping.keys())}")
184
+ cls.__logger.setLevel(numeric_level)
185
+
186
+ @classmethod
187
+ def _get_scenario_name(cls, scenario_key: str) -> str:
188
+ if not scenario_key:
189
+ return None
190
+
191
+ try:
192
+ parsed_key = ScenarioKey(scenario_key)
193
+ scenario = Scenario.find_by(uuid=parsed_key.id)
194
+ return scenario.name if scenario else None
195
+ except (InvalidScenarioKey, Exception):
196
+ return None
197
+
198
+ @classmethod
199
+ def _get_scenario_id(cls, scenario_key: str) -> int:
200
+ if not scenario_key:
201
+ return None
202
+
203
+ try:
204
+ parsed_key = ScenarioKey(scenario_key)
205
+ scenario = Scenario.find_by(uuid=parsed_key.id)
206
+ return scenario.id if scenario else None
207
+ except (InvalidScenarioKey, Exception):
208
+ return None
209
+
210
+ @classmethod
211
+ def _extract_request_key(cls, response) -> str:
212
+ if not response or not hasattr(response, 'headers'):
213
+ return None
214
+
215
+ try:
216
+ return response.headers.get('X-Stoobly-Request-Key')
217
+ except (AttributeError, Exception):
218
+ return None
219
+
220
+ @classmethod
221
+ def _get_request_id(cls, request_key: str) -> int:
222
+ if not request_key:
223
+ return None
224
+
225
+ try:
226
+ parsed_key = RequestKey(request_key)
227
+ request = Request.find_by(uuid=parsed_key.id)
228
+ return request.id if request else None
229
+ except (InvalidRequestKey, Exception):
230
+ return None
231
+
232
+ @classmethod
233
+ def __get_file_path(cls) -> str:
234
+ if cls.__file_path is not None:
235
+ return cls.__file_path
236
+
237
+ data_dir_path = DataDir.instance().path
238
+ return f"{data_dir_path}/tmp/{cls.__NAMESPACE}/logs/requests.json"
239
+
240
+ @classmethod
241
+ def enable_logger_file(cls) -> None:
242
+ cls.__ensure_directory()
243
+
244
+ # Enable the logger before setup so error logging works
245
+ cls.__logger.disabled = False
246
+
247
+ try:
248
+ # Remove all existing handlers to prevent logging to stdout
249
+ cls.__logger.handlers.clear()
250
+ # Prevent propagation to parent loggers which may have console handlers
251
+ cls.__logger.propagate = False
252
+
253
+ file_handler = logging.FileHandler(cls.__get_file_path())
254
+ json_formatter = cls.JSONFormatter(cls.__settings)
255
+ file_handler.setFormatter(json_formatter)
256
+ cls.__logger.addHandler(file_handler)
257
+
258
+ except IOError as e:
259
+ cls.__logger.error(f"Failed to configure logger file output: {e}")
260
+
261
+ @classmethod
262
+ def __log_scenario_change_delimiter(cls, previous_scenario_key: str, current_scenario_key: str) -> None:
263
+ cls.__ensure_directory()
264
+
265
+ previous_name = cls._get_scenario_name(previous_scenario_key)
266
+ current_name = cls._get_scenario_name(current_scenario_key)
267
+
268
+ extra = {
269
+ 'delimiter': {
270
+ "type": "----- Scenario change delimiter -----",
271
+ "previous_scenario_key": previous_scenario_key or "",
272
+ "previous_scenario_name": previous_name or "",
273
+ "current_scenario_key": current_scenario_key or "",
274
+ "current_scenario_name": current_name or "",
275
+ }
276
+ }
277
+
278
+ cls.__logger.info(f"Scenario changed to {current_name or ''}", extra=extra)
279
+
280
+ @classmethod
281
+ def __setup_logging(cls, request: MitmproxyRequest = None, response: Response = None, request_key: str = None, fixture_path: str = None) -> dict:
282
+ cls.__ensure_directory()
283
+ cls.__check_scenario_key_changes()
284
+ extra = {}
285
+
286
+ if request is not None:
287
+ extra['request'] = request
288
+ if response is not None:
289
+ extra['response'] = response
290
+ if request_key is not None:
291
+ extra['request_key'] = request_key
292
+ if fixture_path is not None:
293
+ extra['fixture_path'] = fixture_path
294
+
295
+ return extra
296
+
297
+ @classmethod
298
+ def __check_scenario_key_changes(cls) -> None:
299
+ intercept_settings = InterceptSettings(cls.__settings)
300
+ current_scenario_key = intercept_settings.scenario_key
301
+
302
+ if cls.__previous_scenario_key != current_scenario_key:
303
+ cls.__log_scenario_change_delimiter(cls.__previous_scenario_key, current_scenario_key)
304
+ cls.__previous_scenario_key = current_scenario_key
305
+
306
+ @classmethod
307
+ def debug(cls, message: str, *, request: MitmproxyRequest = None, response: Response = None, request_key: str = None, fixture_path: str = None) -> None:
308
+ extra = cls.__setup_logging(request, response, request_key, fixture_path)
309
+ cls.__logger.debug(message, extra=extra if extra else None)
310
+
311
+ @classmethod
312
+ def info(cls, message: str, *, request: MitmproxyRequest = None, response: Response = None, request_key: str = None, fixture_path: str = None) -> None:
313
+ extra = cls.__setup_logging(request, response, request_key, fixture_path)
314
+ cls.__logger.info(message, extra=extra if extra else None)
315
+
316
+ @classmethod
317
+ def warning(cls, message: str, *, request: MitmproxyRequest = None, response: Response = None, request_key: str = None, fixture_path: str = None) -> None:
318
+ extra = cls.__setup_logging(request, response, request_key, fixture_path)
319
+ cls.__logger.warning(message, extra=extra if extra else None)
320
+
321
+ @classmethod
322
+ def error(cls, message: str, *, request: MitmproxyRequest = None, response: Response = None, request_key: str = None, fixture_path: str = None) -> None:
323
+ extra = cls.__setup_logging(request, response, request_key, fixture_path)
324
+ cls.__logger.error(message, extra=extra if extra else None)
325
+
326
+ @classmethod
327
+ def dump_logs(cls):
328
+ file_path = cls.__get_file_path()
329
+ if not os.path.exists(file_path):
330
+ return
331
+
332
+ try:
333
+ with open(file_path, 'r', encoding='utf-8') as f:
334
+ content = f.read()
335
+ if content.strip():
336
+ print(content)
337
+ else:
338
+ print(end='')
339
+ except IOError as e:
340
+ cls.__logger.error(f"Failed to read log file: {e}")
341
+ print(f"Failed to read log file: {e}")
342
+
343
+ @classmethod
344
+ def truncate(cls) -> None:
345
+ cls.__ensure_directory()
346
+
347
+ file_path = cls.__get_file_path()
348
+
349
+ if not os.path.exists(file_path):
350
+ cls.enable_logger_file()
351
+ return
352
+
353
+ try:
354
+ # Close and remove existing handler to release the file lock
355
+ for handler in cls.__logger.handlers[:]:
356
+ if isinstance(handler, logging.FileHandler):
357
+ handler.close()
358
+ cls.__logger.removeHandler(handler)
359
+
360
+ # Now truncate the file
361
+ with open(file_path, 'w', encoding='utf-8') as f:
362
+ f.write('')
363
+
364
+ # Re-enable logging with a fresh handler
365
+ cls.enable_logger_file()
366
+ cls.__logger.debug(f"Cleared log file: {file_path}")
367
+
368
+ cls.reset_scenario_key()
369
+ except IOError as e:
370
+ cls.__logger.error(f"Failed to clear log file: {e}")
371
+
372
+ @classmethod
373
+ def __ensure_directory(cls):
374
+ file_path = cls.__get_file_path()
375
+ directory = os.path.dirname(file_path)
376
+
377
+ if directory and not os.path.exists(directory):
378
+ try:
379
+ cls.__logger.debug(f"created missing directory: {directory}")
380
+ os.makedirs(directory, exist_ok=True)
381
+ except OSError as e:
382
+ cls.__logger.error(f"Failed to create log directory: {e}")
@@ -109,11 +109,11 @@ class TestScaffoldE2e():
109
109
  def create_scaffold_setup(self, runner, app_dir_path, app_name, target_workflow_name, external_service_docker_compose, external_https_service_docker_compose, local_service_docker_compose, local_service_mock_docker_compose_path):
110
110
  ScaffoldCliInvoker.cli_app_create(runner, app_dir_path, app_name)
111
111
 
112
- # Create external user defined service
112
+ # Create user defined services
113
113
  ScaffoldCliInvoker.cli_service_create(runner, app_dir_path, external_service_docker_compose.hostname, external_service_docker_compose.service_name, False)
114
114
  ScaffoldCliInvoker.cli_service_create(runner, app_dir_path, external_https_service_docker_compose.hostname, external_https_service_docker_compose.service_name, True)
115
115
 
116
- # Create local user defined service
116
+ # Create user defined custom container services
117
117
  ScaffoldCliInvoker.cli_service_create(runner, app_dir_path, local_service_docker_compose.hostname, local_service_docker_compose.service_name, False)
118
118
 
119
119
  # Validate docker-compose path exists
@@ -1 +1 @@
1
- 1.10.2
1
+ 1.10.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stoobly-agent
3
- Version: 1.10.5
3
+ Version: 1.11.0
4
4
  Summary: Record, mock, and test HTTP(s) requests. CLI agent for Stoobly
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -1,4 +1,4 @@
1
- stoobly_agent/__init__.py,sha256=sxrNGBqNJn6xpatZ-k8nV1eIVOpFpk1_oBfoMJlWMWA,45
1
+ stoobly_agent/__init__.py,sha256=K4x_RYNZ1yt5BujLPBK0239IWwUcGdZ8CwmPVeTMXuY,45
2
2
  stoobly_agent/__main__.py,sha256=tefOkFZeCFU4l3C-Y4R_lR9Yt-FETISiXGUnbh6Os54,146
3
3
  stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  stoobly_agent/app/api/__init__.py,sha256=NIgcbX7iiWrApsCITXlmhr4SYbWS0fwb01x-F3jTFdo,666
@@ -61,11 +61,11 @@ stoobly_agent/app/cli/helpers/trace_aliases.py,sha256=8N8pnsnONwVv-fabCTvDlXec-W
61
61
  stoobly_agent/app/cli/helpers/trace_context_facade.py,sha256=3MDjY_bdhvE2dad_B_w9gemCZZoiVjotbE8o9hrmVYo,1027
62
62
  stoobly_agent/app/cli/helpers/validations.py,sha256=o9_DqlAIw98QcMNXGsAnELQ6DeXdSRa1TLiTZu3V7ns,6236
63
63
  stoobly_agent/app/cli/helpers/verify_raw_request_service.py,sha256=tmLeRBYhNgAQeJD-rF6Nj22F_TfVktVSb1wjRPFblBQ,658
64
- stoobly_agent/app/cli/intercept_cli.py,sha256=tgGtl1EZerIdcuMR8KKHdrrn_7-XWSg0D6XdTuSq8k0,6698
64
+ stoobly_agent/app/cli/intercept_cli.py,sha256=ULJPgN21qNBhO97O13HtQplXYlAz1HEOg7y9Y3RnrcM,6698
65
65
  stoobly_agent/app/cli/main_group.py,sha256=3UzBxin2f2p5KNqo6Oayo_I1VI2pHpqOoRexUnJ74a4,2187
66
66
  stoobly_agent/app/cli/project_cli.py,sha256=EXjeLjbnq9PhfCjvyfZ0UnJ2tejeCS0SIAo3Nc4fKOc,3852
67
67
  stoobly_agent/app/cli/report_cli.py,sha256=ZxJw0Xkx7KFZJn9e45BSKRKon8AD0Msrwy1fbPfbv0c,2543
68
- stoobly_agent/app/cli/request_cli.py,sha256=NVO6alj2acwaYLvU2445yB5qAQIK32Ll19tOErxzNR8,7863
68
+ stoobly_agent/app/cli/request_cli.py,sha256=TEZNAKqDaZClNEJsRq1UcYrH60s0E20OPGfxXDdFOJ4,8448
69
69
  stoobly_agent/app/cli/scaffold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
70
  stoobly_agent/app/cli/scaffold/app.py,sha256=7d5nHnQwyBxEwSC2Jrvbk2U3dj9OnhaHrE2zmC37oOs,3922
71
71
  stoobly_agent/app/cli/scaffold/app_command.py,sha256=x--ejtVSBL0Jz8OiakBtxfI2IZFAWJWCwuSJo7TEU9Y,2386
@@ -81,29 +81,29 @@ stoobly_agent/app/cli/scaffold/docker/builder.py,sha256=uiGqhxBHEasZAqLzjKUGUs-1
81
81
  stoobly_agent/app/cli/scaffold/docker/constants.py,sha256=itm2owGfW53yDrfgkKQ4HIBdiwRC0IPOUTwUT9P78Lw,837
82
82
  stoobly_agent/app/cli/scaffold/docker/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
83
  stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py,sha256=fGWtt_TFOaIVirlkUWVKVEhDPmn0A4D53mNSud-qzHg,1019
84
- stoobly_agent/app/cli/scaffold/docker/service/builder.py,sha256=s1fC8snapTP1_D3LTOJDPVidVURk_Y0Vago2Vn_AdeU,6236
84
+ stoobly_agent/app/cli/scaffold/docker/service/builder.py,sha256=_cTBKTv0X-b1mAEA9GItLvPp8pTHvymgHyNmw04t0V4,6140
85
85
  stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py,sha256=Wsyd2Mche2greULTFYWm-SIhenh-I5hlauSOdGG2urU,4011
86
86
  stoobly_agent/app/cli/scaffold/docker/service/types.py,sha256=qB-yYHlu-PZDc0HYgTUvE5bWNpHxaSThC3JUG8okR1k,88
87
87
  stoobly_agent/app/cli/scaffold/docker/template_files.py,sha256=oqcocCAyXRJ8aq8M1zVLUUrxU3VU6D6V7ibZUYTr2b4,4724
88
88
  stoobly_agent/app/cli/scaffold/docker/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
89
  stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py,sha256=K9yNlOc_r0vvXZ5BZDwk5IMpzgrx6v-igsTs_8oae6s,720
90
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py,sha256=cOPDT2wDiCwk4_6gLpCCUacXe3jzJNtB1HPe32hFPig,4414
90
+ stoobly_agent/app/cli/scaffold/docker/workflow/builder.py,sha256=22uKov9GJ5PqM8bEpKhiqqLexlHIrrAhNphWi104pSc,4396
91
91
  stoobly_agent/app/cli/scaffold/docker/workflow/command_decorator.py,sha256=6gVdtTKIOyDlAfzOWefoffp6CtOC7gHz4dUQ6Z9lxWE,874
92
92
  stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py,sha256=BkxPzj__rBQa4_WXkfSV71-T9LayFzCOgCLyuJA4s-Q,1174
93
93
  stoobly_agent/app/cli/scaffold/docker/workflow/detached_decorator.py,sha256=qrvIYeCgnQNAs4n_g4DfTgO5fl9nW0e6wOIMhFELqXw,1178
94
94
  stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py,sha256=DGaSlbOvAAa16buFwQnkGHyqht4VR3rnIBOWr-ImIf8,952
95
95
  stoobly_agent/app/cli/scaffold/docker/workflow/local_decorator.py,sha256=xp1TmP8drOyl9Zhm5B1ci6NqPqRFDr2yxipmvSljgiE,717
96
- stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=QO1TbAj6QthIyHvy7itV_d9NteNcjClYaan1GX-0kLc,1201
96
+ stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=CJyO_0Oo3pUrafqmdqLl5bJIj35zKohl8PVGgokRfW4,1245
97
97
  stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256=tSPnscsBZusBaSt_NlB4exrZ2MnWMRAUJgw_NaEdHiw,1199
98
- stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py,sha256=aqRVHTbCooan8zTMfKjxEjssf255YTEdLzoPUXna29o,18010
98
+ stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py,sha256=LLiC7KCgo9ZlmXcdaidmcN449GuDK_8dYykCNFjfhyo,18217
99
99
  stoobly_agent/app/cli/scaffold/env.py,sha256=dT33tHoQaUxfsFCYm8kfaAv-qPVrUPmNFQmLnFQhZeQ,1107
100
100
  stoobly_agent/app/cli/scaffold/hosts_file_manager.py,sha256=zNX5wh6zXQ4J2BA0YYdD7_CPqDz02b_ghXsY3oTjjB4,4999
101
101
  stoobly_agent/app/cli/scaffold/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
102
  stoobly_agent/app/cli/scaffold/local/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
- stoobly_agent/app/cli/scaffold/local/service/builder.py,sha256=uZNPIQWo4UcLy3bcE6Wvntle6ONPpWjS5oAq3g0Punk,1852
103
+ stoobly_agent/app/cli/scaffold/local/service/builder.py,sha256=9awizOv_LYAWgEXVguQOLZhO8gGEa_X_x1MRsQJEioU,1522
104
104
  stoobly_agent/app/cli/scaffold/local/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
- stoobly_agent/app/cli/scaffold/local/workflow/builder.py,sha256=O8Iwyd231-ciQWkiGB5HAiFJXMDUtXIoyl0S_Jrj3lQ,806
106
- stoobly_agent/app/cli/scaffold/local/workflow/run_command.py,sha256=BJrG5g-c4kf_cgQ1hE41KUKQsvtwVVKzaTgQ7eCaPUk,15625
105
+ stoobly_agent/app/cli/scaffold/local/workflow/builder.py,sha256=EghacafZjr06igvQtWTjt9vwILHtJxSl3rPEW-VD6iU,828
106
+ stoobly_agent/app/cli/scaffold/local/workflow/run_command.py,sha256=7XhI6X-TKAGv-HuOvh6Knm7R5DDpfi8ZZ8BJnHQwGtY,15956
107
107
  stoobly_agent/app/cli/scaffold/managed_services_docker_compose.py,sha256=-wLBXUi7DCWsfm5KzZzd_kdJKOTl1NT924XR7dyjbSY,574
108
108
  stoobly_agent/app/cli/scaffold/service.py,sha256=74JwjTRRkk6lo-k9hre1iGztbKa9zDqjPVx3Qgpze-s,699
109
109
  stoobly_agent/app/cli/scaffold/service_command.py,sha256=j-lkG5Zth_CBHa6Z9Kv3dJwxX9gylFBZMZbW691R8ZU,1480
@@ -114,11 +114,11 @@ stoobly_agent/app/cli/scaffold/service_dependency.py,sha256=olr_s_cfn51Pz5FlIihl
114
114
  stoobly_agent/app/cli/scaffold/service_docker_compose.py,sha256=fVUZ-oo-bn5GVZp8JgGq7AkiQQ6-JkxwK_OMlinS9WM,915
115
115
  stoobly_agent/app/cli/scaffold/service_update_command.py,sha256=oWusBKfvjt4RnK03_V3CJYWrfsCI4_LcR7W12eLXMR4,2579
116
116
  stoobly_agent/app/cli/scaffold/service_workflow.py,sha256=sQ_Edy_wGHKMXpD0DmhnOWkGEKz7gSgEGNI8f7aXOdg,444
117
- stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=DPYLBAwUsh4C8ho-4SwjBe32xuBSUpWlWgRF9JIbKRc,11768
117
+ stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=uV5HcgWZu1RubPImVDyIUlZUNTok4Qr6CueQQswBoF8,11788
118
118
  stoobly_agent/app/cli/scaffold/templates/__init__.py,sha256=x8C_a0VoO_vUbosp4_6IC1U7Ge9NnUdVKDPpVMtMkeY,171
119
- stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context,sha256=9DQK-OXnRKjjKWsUSIRAio6dkR4eGxD1vizPT7Q5sp8,159
120
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=9E46Dzs3xCKy2fP6L6Q4DxourYxrwKCcoUhEf3Y7DEs,9619
121
- stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml,sha256=SABod6_InBFyOm-uulK7r27SR0sWRUiL0h69g_jvNJA,249
119
+ stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context,sha256=tQgVLNA2SWy1K_dl-loPKau55F3nyPp4vhCppol-fiQ,160
120
+ stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=cywqyJkxHDRr3JOQSwx0pFwSi-b_iw6GzHxeIoVP66k,10028
121
+ stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml,sha256=cs6bRu6p8djhTKRic9UFTZujlNpHGudR2SrKauntmHg,381
122
122
  stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.networks.yml,sha256=I4PbJpQjFHb5IbAUWNvYM6okDEtmwtKFDQg-yog05WM,141
123
123
  stoobly_agent/app/cli/scaffold/templates/app/Makefile,sha256=TEmPG7Bf0KZOnmfsgdzza3UdwcVMmM5Lj1YdLc4cgjA,79
124
124
  stoobly_agent/app/cli/scaffold/templates/app/build/.config.yml,sha256=8Wt8ZZ5irvBYYS44xGrR_EWlZDuXH9kyWmquzsh7s8g,19
@@ -160,8 +160,8 @@ stoobly_agent/app/cli/scaffold/templates/app/gateway/test/.docker-compose.yml,sh
160
160
  stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.config.yml,sha256=XnLQZMzzMMIwVycjyPN5QXsmRztkTFAna1kIHYuDfJQ,19
161
161
  stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.docker-compose.base.yml,sha256=W6cUnaJ6dmq4VOgrue_YaW4TyFx9zd4YrrRsxOimPEA,140
162
162
  stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/.docker-compose.yml,sha256=_lku3K0t2TRLMOcoSKcisCLQO_YCXZoevr_q6anjhL8,327
163
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.yml,sha256=06goBt7JXmt1jv5PPFp6T2D8mXkakPnoFMrCruYugLo,344
164
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.yml,sha256=06goBt7JXmt1jv5PPFp6T2D8mXkakPnoFMrCruYugLo,344
163
+ stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.yml,sha256=e4Bjb0deT6afbjHWG9aPLj83uJrcgeZq0xR0gOpPMBE,342
164
+ stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.yml,sha256=e4Bjb0deT6afbjHWG9aPLj83uJrcgeZq0xR0gOpPMBE,342
165
165
  stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.configure,sha256=B9re5KYSNfvbcawVQnU5JX-RdAWEjWP3327L29dTEK0,392
166
166
  stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.init,sha256=wyh47rAOxaqOa_O-t0kaqZE6aAc0h5bsX4t-PSkVPJM,302
167
167
  stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.run,sha256=0ib4oiLYiU-27nwhHFCNzQXz4fsAxypYECoAhZhkVUI,239
@@ -182,6 +182,7 @@ stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.init,sh
182
182
  stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.run,sha256=LQs_bJiN3LaC0eJnaF_Fnrr9ZIlxkaM8tnWMxjCovXM,446
183
183
  stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/intercept/.disable,sha256=xVf4Pk1RLvJm7Ff0rbGoWhYHPv0ME5e93fxS2yFqLnE,45
184
184
  stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/intercept/.enable,sha256=sfUSPG4uHdXX95BLgivXQYLbsLBP2DjJIiSTXRtvXuY,188
185
+ stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/request/log/.list,sha256=JyfIkHOJbkjkJMibFVdRNDGtgSMRNE1PEHJEOZMY7tE,88
185
186
  stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.down,sha256=Eb-DY3NYLBny2Akx4R3ckqHqi_5gzYKNp9tJgFbeFOQ,205
186
187
  stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.logs,sha256=ebsSW7N3RbT4asqDVpdKWzztC3FZxXLpjnxsiblMSGQ,184
187
188
  stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.mkcert,sha256=vyHaXmvy-7oL2RD8rIxwT-fdJS5kXmB0yHK5fRMo_cM,46
@@ -240,7 +241,7 @@ stoobly_agent/app/cli/scaffold/workflow_create_command.py,sha256=5gSkxNxrXLeobLU
240
241
  stoobly_agent/app/cli/scaffold/workflow_env.py,sha256=shPjoX1SWe7K6pGpZvw2fPVHWd6j_azTe58jvOjGUns,607
241
242
  stoobly_agent/app/cli/scaffold/workflow_log_command.py,sha256=Bke4lMOMxuDUFuAx9nlXHbKgYMO4KAg9ASHvjz4aVWc,1372
242
243
  stoobly_agent/app/cli/scaffold/workflow_namesapce.py,sha256=VNaZrcqMMeqrzpPGhD9-oaZems1k0ebRc6wR74EvA8c,1170
243
- stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=35wxoc9qH3ZGuPcKgFGQLEMvGwDVWbjpXqve4iz2wBY,6185
244
+ stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=IPKQyKZ5zZ1fBn5JyT5dEZxCLzW7gFbkLywWEmArBMg,6248
244
245
  stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=Uo_yo6rVR1ZR7xpvsQvlH48AyMBVLRupd4G-bRjzm_Q,5584
245
246
  stoobly_agent/app/cli/scaffold_cli.py,sha256=YnRKrC_XQ5OdQ1tgA-oL4rmamw70DlMm17SqIAQ9sfU,33677
246
247
  stoobly_agent/app/cli/scenario_cli.py,sha256=lA9a4UNnLzrbJX5JJE07KGb5i9pBd2c2vdFUW6_7k0E,8345
@@ -350,7 +351,7 @@ stoobly_agent/app/proxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
350
351
  stoobly_agent/app/proxy/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
351
352
  stoobly_agent/app/proxy/constants/custom_response_codes.py,sha256=1CaApt_6W7GrxvN8_Ozbf_SEodVEQaNZRR2sMYpI0U8,40
352
353
  stoobly_agent/app/proxy/context.py,sha256=m6iu6QSZsim8meZS8H8DsZeXyjfoC-MRMHhQD1MFKM0,532
353
- stoobly_agent/app/proxy/handle_mock_service.py,sha256=worESliNvSoyVg8EvlC51aN4aaPA8k-FzuwlidIC4U8,9461
354
+ stoobly_agent/app/proxy/handle_mock_service.py,sha256=t05JS8LTWWbAh06hQ_4C1gi2TzdF6VIQmckuTD6-QAc,9936
354
355
  stoobly_agent/app/proxy/handle_record_service.py,sha256=TXIxT5zYOCd96LoINvpCPm2PSnwCAPQQLqfjch9iVlo,4516
355
356
  stoobly_agent/app/proxy/handle_replay_service.py,sha256=6gB3y6xT6WEXduxFgK5bd3xiH2utrsfDSBV4i2Ai34Y,3085
356
357
  stoobly_agent/app/proxy/handle_test_service.py,sha256=WkMPbM4argVtl-TQB7VdQIvB8cOwURAahxFX5Vkqwws,8405
@@ -396,7 +397,7 @@ stoobly_agent/app/proxy/replay/replay_request_service.py,sha256=pRsKx3d7caXiHrEH
396
397
  stoobly_agent/app/proxy/replay/replay_scenario_service.py,sha256=9jV-iO5EBg8geUblEtjjWRFIkom_Pqmo7P-lTc3S4Xw,2824
397
398
  stoobly_agent/app/proxy/replay/rewrite_params_service.py,sha256=jEHlT6_OHq_VBa09Hd6QaRyErv7tZnziDvW7m3Q8CQg,2234
398
399
  stoobly_agent/app/proxy/replay/trace_context.py,sha256=lKpnQWVCUTcMPE-SOi90za3U1yL0VeBlIgj_KP63VsE,10435
399
- stoobly_agent/app/proxy/run.py,sha256=4TclB013474dHhIax__0qpM_UZH9YnMvrlymp8qUcIQ,3977
400
+ stoobly_agent/app/proxy/run.py,sha256=viThhDVG0WR5r0xMTU6-9zgJW08S6WEAAMn_f5t77tw,4093
400
401
  stoobly_agent/app/proxy/settings.py,sha256=R0LkSa9HrkUXvCd-nur4syJePjbQZdlnAnOPpGnCx38,2172
401
402
  stoobly_agent/app/proxy/simulate_intercept_service.py,sha256=R-L2dh2dfYFebttWXU0NwyxFI_jP6Ud36oKPC-T8HiI,1942
402
403
  stoobly_agent/app/proxy/test/__init__.py,sha256=uW0Ab27oyH2odTeVRjcuUJF8A1FLbTT5sBMzhGZr1so,89
@@ -458,11 +459,11 @@ stoobly_agent/app/settings/types/remote_settings.py,sha256=4PvEGKULXM0zv29XTDzV7
458
459
  stoobly_agent/app/settings/types/ui_settings.py,sha256=BqPy2F32AbODqzi2mp2kRk28QVUydQIwVmvftn46pco,84
459
460
  stoobly_agent/app/settings/ui_settings.py,sha256=D4sP26dEhOlGPdeQgm4GyAYN3ENWXkfZ9s0ETlt3QEI,1175
460
461
  stoobly_agent/app/settings/url_rule.py,sha256=Qx7YrIpVRSC-4LeNiCAfCtE50Jou4423hojMW-4qUYg,954
461
- stoobly_agent/cli.py,sha256=FLoD1cldpv8UIH4WEdng8wiX1B9wl9mOZ7QjVFcz_SA,13482
462
+ stoobly_agent/cli.py,sha256=Lre8Nu99cjhwOh0znXUjKFB1LSI1bV7aKghJOe273F0,14406
462
463
  stoobly_agent/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
463
464
  stoobly_agent/config/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
464
465
  stoobly_agent/config/constants/alias_resolve_strategy.py,sha256=_R1tVqFnyGxCraVS5-dhSskaDj_X8-NthsY7i_bEt9M,119
465
- stoobly_agent/config/constants/custom_headers.py,sha256=4143apfSnmVHykFXum-lJzC3jw0q1SPEElL9iRZojfg,1444
466
+ stoobly_agent/config/constants/custom_headers.py,sha256=NDuZ25zdtgObs_xoZ9BBXXwrFGCr6akyWJPnIZcSNus,1478
466
467
  stoobly_agent/config/constants/env_vars.py,sha256=pS6U1RHFljamBfoVyPiroH3SVy4-sTeESo6p5n19yls,1408
467
468
  stoobly_agent/config/constants/headers.py,sha256=Hfv7R8_NPXAGaMiZPqywGZDnr0qcVUyfenPb4g465rE,169
468
469
  stoobly_agent/config/constants/intercept_policy.py,sha256=5hIgOft8PQmCRdOHb5OEvEj10tU66DIQF3GYltlWyM8,25
@@ -546,6 +547,7 @@ stoobly_agent/lib/api/test_responses_resource.py,sha256=RdNj7N6xC7jW5zUoKfgNg8Ne
546
547
  stoobly_agent/lib/api/tests_resource.py,sha256=EKyI0xTgzRSPyGdepgteVH3EHUkXIzrt3HXahBau6CY,855
547
548
  stoobly_agent/lib/api/users_resource.py,sha256=xR0ug5xifuvRw4jdF4cVjrWBdkFccmL9_fy8ZxOPX74,477
548
549
  stoobly_agent/lib/cache.py,sha256=7sYq2wh6I5LmtKqvJYoY89h_PvZ21zvS9pCBCEutuCM,2266
550
+ stoobly_agent/lib/intercepted_requests_logger.py,sha256=82ZfwV27NZGIWRArL7Oqv9LX66k2I-MqVcOccDrOdVU,14923
549
551
  stoobly_agent/lib/logger.py,sha256=6s_8UxXLJt1jor-Ez0xCBWhuAD932sTEBQBnNXpFTtQ,1971
550
552
  stoobly_agent/lib/orm/__init__.py,sha256=UCAL40_L9qf1sUqvUWKzc551jKLR4HKqRNLPdOuXl2w,1199
551
553
  stoobly_agent/lib/orm/base.py,sha256=3PZx0OWK241vrr8pLLQL0GFhKxvYVDFyVMMdYzhxMPM,358
@@ -723,7 +725,7 @@ stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=3kMmv0CuvnMXL
723
725
  stoobly_agent/test/app/cli/request/request_test_test.py,sha256=-cJNXKjgryVVfVt-7IN5fIhBwe3NjFoPmeavDH8lAjU,5527
724
726
  stoobly_agent/test/app/cli/scaffold/docker/cli_invoker.py,sha256=W4ZZgrgUU0hBm_l0x04eR_9eGVyfbLSMbDVGqhpi2qE,5391
725
727
  stoobly_agent/test/app/cli/scaffold/docker/cli_test.py,sha256=wkYnRQo8OBDds-pGmdc2cYs5yxgO_2GiDxzI1imGntc,4301
726
- stoobly_agent/test/app/cli/scaffold/docker/e2e_test.py,sha256=RxHvIMyVhpXy0fkfCJPFLZ2DtyC3dieVGETWOq-rQos,13655
728
+ stoobly_agent/test/app/cli/scaffold/docker/e2e_test.py,sha256=cdsXrN1YK4s2mugGWC2NCFOsgcebaPugEEUh6rxsP1w,13659
727
729
  stoobly_agent/test/app/cli/scaffold/hosts_file_manager_test.py,sha256=ztcPh1x0ZCW1FWA5YL4ulEVjfbW9TOPgk1bnSDPNmCw,2287
728
730
  stoobly_agent/test/app/cli/scaffold/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
729
731
  stoobly_agent/test/app/cli/scaffold/local/cli_invoker.py,sha256=YLQuz5JjvzfqFKk-BFT-Bl9EZgj37ZDZtwNsvqNRDHA,4138
@@ -755,7 +757,7 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
755
757
  stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=a1SFLyEyRRLuADvAw6ckQQKORFXvyK1lyrbkaLWx8oU,3399
756
758
  stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
757
759
  stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
758
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=NNWX20PKU7L9csy90a96D-I4wsC4Mh2tj0OhYTFD_GI,6
760
+ stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=UAIPUXjU5C_KBbJFX74l8Kihm3U3urn6h0KexeviDeE,7
759
761
  stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
760
762
  stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
761
763
  stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
@@ -797,8 +799,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
797
799
  stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
798
800
  stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
799
801
  stoobly_agent/test/test_helper.py,sha256=6v4AHeqYPw7vtRoxET_ubmRWPJoSmTR_DVHay3FxNbQ,1299
800
- stoobly_agent-1.10.5.dist-info/METADATA,sha256=7QB_Saw0H6Pz_qkMRM9HKiXta5p-noQJIMFN5lGVm4g,3203
801
- stoobly_agent-1.10.5.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
802
- stoobly_agent-1.10.5.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
803
- stoobly_agent-1.10.5.dist-info/licenses/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
804
- stoobly_agent-1.10.5.dist-info/RECORD,,
802
+ stoobly_agent-1.11.0.dist-info/METADATA,sha256=MMcUv_P6qdNNHfgupNdDP4MYHuqfN7rth4YkbS_c1gc,3203
803
+ stoobly_agent-1.11.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
804
+ stoobly_agent-1.11.0.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
805
+ stoobly_agent-1.11.0.dist-info/licenses/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
806
+ stoobly_agent-1.11.0.dist-info/RECORD,,