stoobly-agent 1.6.2__py3-none-any.whl → 1.6.3__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 (36) hide show
  1. stoobly_agent/__init__.py +1 -1
  2. stoobly_agent/app/cli/scaffold/app_command.py +15 -0
  3. stoobly_agent/app/cli/scaffold/constants.py +6 -0
  4. stoobly_agent/app/cli/scaffold/docker/builder.py +15 -8
  5. stoobly_agent/app/cli/scaffold/docker/constants.py +7 -2
  6. stoobly_agent/app/cli/scaffold/docker/service/builder.py +10 -8
  7. stoobly_agent/app/cli/scaffold/docker/service/{set_gateway_ports.py → configure_gateway.py} +16 -2
  8. stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py +0 -1
  9. stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +24 -23
  10. stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py +3 -3
  11. stoobly_agent/app/cli/scaffold/docker/workflow/{development_decorator.py → dns_decorator.py} +1 -1
  12. stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +1 -1
  13. stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +0 -13
  14. stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +2 -1
  15. stoobly_agent/app/cli/scaffold/templates/app/.Makefile +2 -3
  16. stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.networks.yml +7 -0
  17. stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +2 -2
  18. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +2 -2
  19. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/docker-compose.yml +10 -6
  20. stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/.docker-compose.mock.yml +1 -7
  21. stoobly_agent/app/cli/scaffold/templates/app/gateway/record/.docker-compose.record.yml +1 -7
  22. stoobly_agent/app/cli/scaffold/templates/app/gateway/test/.docker-compose.test.yml +7 -0
  23. stoobly_agent/app/cli/scaffold/workflow_run_command.py +24 -6
  24. stoobly_agent/app/cli/scaffold/workflow_validate_command.py +0 -7
  25. stoobly_agent/app/cli/scaffold_cli.py +10 -5
  26. stoobly_agent/app/proxy/mock/eval_request_service.py +16 -3
  27. stoobly_agent/lib/api/param_builder.py +0 -1
  28. stoobly_agent/test/app/cli/scaffold/e2e_test.py +1 -3
  29. stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
  30. stoobly_agent/test/mock_data/scaffold/docker-compose-assets-service.yml +1 -6
  31. stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml +1 -7
  32. {stoobly_agent-1.6.2.dist-info → stoobly_agent-1.6.3.dist-info}/METADATA +1 -1
  33. {stoobly_agent-1.6.2.dist-info → stoobly_agent-1.6.3.dist-info}/RECORD +36 -34
  34. {stoobly_agent-1.6.2.dist-info → stoobly_agent-1.6.3.dist-info}/LICENSE +0 -0
  35. {stoobly_agent-1.6.2.dist-info → stoobly_agent-1.6.3.dist-info}/WHEEL +0 -0
  36. {stoobly_agent-1.6.2.dist-info → stoobly_agent-1.6.3.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  COMMAND = 'stoobly-agent'
2
- VERSION = '1.6.2'
2
+ VERSION = '1.6.3'
@@ -5,6 +5,7 @@ import shutil
5
5
  from .app import App
6
6
  from .app_config import AppConfig
7
7
  from .command import Command
8
+ from .docker.constants import DOCKER_COMPOSE_NETWORKS
8
9
 
9
10
  class AppCommand(Command):
10
11
 
@@ -29,6 +30,20 @@ class AppCommand(Command):
29
30
  def app_config_path(self):
30
31
  return self.__config.path
31
32
 
33
+ @property
34
+ def networks_compose_path(self):
35
+ return os.path.join(
36
+ self.data_dir_path,
37
+ self.networks_compose_relative_path
38
+ )
39
+
40
+ @property
41
+ def networks_compose_relative_path(self):
42
+ return os.path.join(
43
+ self.namespace,
44
+ DOCKER_COMPOSE_NETWORKS
45
+ )
46
+
32
47
  @property
33
48
  def scaffold_namespace_path(self):
34
49
  return self.app.scaffold_namespace_path
@@ -32,6 +32,9 @@ SERVICE_SCHEME_ENV = 'SERVICE_SCHEME'
32
32
  SERVICE_PORT = '${SERVICE_PORT}'
33
33
  SERVICE_PORT_ENV = 'SERVICE_PORT'
34
34
  SERVICE_PRIORITY_ENV = 'SERVICE_PRIORITY'
35
+ SERVICE_SCRIPTS = '${SERVICE_SCRIPTS}'
36
+ SERVICE_SCRIPTS_DIR = '/usr/local/bin/services'
37
+ SERVICE_SCRIPTS_ENV = 'SERVICE_SCRIPTS'
35
38
  STOOBLY_HOME_DIR = '/home/stoobly'
36
39
  STOOBLY_DATA_DIR = os.path.join(STOOBLY_HOME_DIR, DATA_DIR_NAME)
37
40
  STOOBLY_CERTS_DIR = os.path.join(STOOBLY_DATA_DIR, CERTS_DIR_NAME)
@@ -52,6 +55,9 @@ WORKFLOW_NAME = '${WORKFLOW_NAME}'
52
55
  WORKFLOW_NAME_ENV = 'WORKFLOW_NAME'
53
56
  WORKFLOW_NAMESPACE_ENV = 'WORKFLOW_NAMESPACE'
54
57
  WORKFLOW_RECORD_TYPE = 'record'
58
+ WORKFLOW_SCRIPTS = '${WORKFLOW_SCRIPTS}'
59
+ WORKFLOW_SCRIPTS_DIR = '/usr/local/bin/workflows'
60
+ WORKFLOW_SCRIPTS_ENV = 'WORKFLOW_SCRIPTS'
55
61
  WORKFLOW_TEMPLATE = '${WORKFLOW_TEMPLATE}'
56
62
  WORKFLOW_TEMPLATE_ENV = 'WORKFLOW_TEMPLATE'
57
63
  WORKFLOW_TEST_TYPE = 'test'
@@ -3,7 +3,7 @@ import pathlib
3
3
  import pdb
4
4
  import yaml
5
5
 
6
- from .constants import APP_NETWORK, APP_NETWORK_NAME, DOCKER_COMPOSE_CUSTOM
6
+ from .constants import APP_EGRESS_NETWORK, APP_EGRESS_NETWORK_NAME, APP_INGRESS_NETWORK, APP_INGRESS_NETWORK_NAME, DOCKER_COMPOSE_CUSTOM
7
7
 
8
8
  class Builder():
9
9
 
@@ -35,12 +35,12 @@ class Builder():
35
35
  return self.__dir_path
36
36
 
37
37
  @property
38
- def public_network(self):
39
- return self.__networks.get(APP_NETWORK_NAME)
38
+ def egress_network(self):
39
+ return self.__networks.get(APP_EGRESS_NETWORK_NAME)
40
40
 
41
41
  @property
42
- def public_network_name(self):
43
- return APP_NETWORK_NAME
42
+ def egress_network_name(self):
43
+ return APP_EGRESS_NETWORK_NAME
44
44
 
45
45
  @property
46
46
  def networks(self):
@@ -86,12 +86,19 @@ class Builder():
86
86
 
87
87
  def with_network(self, network):
88
88
  self.__networks[network] = {
89
- 'name': f"{APP_NETWORK}.{network}"
89
+ 'name': f"{APP_EGRESS_NETWORK}.{network}"
90
90
  }
91
91
  return self
92
92
 
93
- def with_public_network(self, network = APP_NETWORK):
94
- self.__networks[APP_NETWORK_NAME] = {
93
+ def with_egress_network(self, network = APP_EGRESS_NETWORK):
94
+ self.__networks[APP_EGRESS_NETWORK_NAME] = {
95
+ 'external': True,
96
+ 'name': network,
97
+ }
98
+ return self
99
+
100
+ def with_ingress_network(self, network = APP_INGRESS_NETWORK):
101
+ self.__networks[APP_INGRESS_NETWORK_NAME] = {
95
102
  'external': True,
96
103
  'name': network,
97
104
  }
@@ -1,8 +1,13 @@
1
- APP_NETWORK_NAME = 'app'
2
- APP_NETWORK = '${APP_NETWORK}'
1
+ APP_EGRESS_NETWORK = '${APP_NETWORK}.egress'
2
+ APP_EGRESS_NETWORK_NAME = 'app.egress'
3
+ APP_EGRESS_NETWORK_TEMPLATE = '{network}.egress'
4
+ APP_INGRESS_NETWORK = '${APP_NETWORK}.ingress'
5
+ APP_INGRESS_NETWORK_NAME = 'app.ingress'
6
+ APP_INGRESS_NETWORK_TEMPLATE = '{network}.ingress'
3
7
  DOCKER_COMPOSE_BASE = '.docker-compose.base.yml'
4
8
  DOCKER_COMPOSE_BASE_TEMPLATE = '.docker-compose.base.template.yml'
5
9
  DOCKER_COMPOSE_CUSTOM = 'docker-compose.yml'
10
+ DOCKER_COMPOSE_NETWORKS = '.docker-compose.networks.yml'
6
11
  DOCKERFILE_CONTEXT = '.Dockerfile.context'
7
12
  DOCKERFILE_SERVICE = 'Dockerfile.source'
8
13
 
@@ -2,7 +2,7 @@ import os
2
2
  import pdb
3
3
 
4
4
  from ...app_config import AppConfig
5
- from ...constants import SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, WORKFLOW_TEMPLATE
5
+ from ...constants import WORKFLOW_SCRIPTS, WORKFLOW_TEMPLATE
6
6
  from ...service_config import ServiceConfig
7
7
  from ..app_builder import AppBuilder
8
8
  from ..builder import Builder
@@ -22,8 +22,6 @@ class ServiceBuilder(Builder):
22
22
  self.__config = config
23
23
  self.__service_name = os.path.basename(service_path)
24
24
 
25
- self.load()
26
-
27
25
  @property
28
26
  def app_base(self):
29
27
  return f"{self.service_name}.app_base"
@@ -86,7 +84,7 @@ class ServiceBuilder(Builder):
86
84
  def build_init_base(self):
87
85
  environment = {}
88
86
  self.with_service(self.init_base, {
89
- 'command': [f"/usr/local/bin/workflows/{WORKFLOW_TEMPLATE}/.init", 'bin/init'],
87
+ 'command': [f"{WORKFLOW_SCRIPTS}/{WORKFLOW_TEMPLATE}/.init", 'bin/init'],
90
88
  'environment': environment,
91
89
  'extends': {
92
90
  'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
@@ -97,7 +95,7 @@ class ServiceBuilder(Builder):
97
95
  def build_configure_base(self):
98
96
  environment = {}
99
97
  self.with_service(self.configure_base, {
100
- 'command': [f"/usr/local/bin/workflows/{WORKFLOW_TEMPLATE}/.configure", 'bin/configure'],
98
+ 'command': [f"{WORKFLOW_SCRIPTS}/{WORKFLOW_TEMPLATE}/.configure", 'bin/configure'],
101
99
  'environment': environment,
102
100
  'extends': {
103
101
  'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
@@ -112,7 +110,11 @@ class ServiceBuilder(Builder):
112
110
  if self.config.hostname:
113
111
  self.build_proxy_base()
114
112
 
115
- super().write({
116
- 'networks': self.networks,
113
+ compose = {
117
114
  'services': self.services,
118
- })
115
+ }
116
+
117
+ if self.networks:
118
+ compose['networks'] = self.networks
119
+
120
+ super().write(compose)
@@ -8,14 +8,18 @@ from stoobly_agent.app.cli.scaffold.service_config import ServiceConfig
8
8
  from stoobly_agent.app.cli.scaffold.docker.constants import DOCKER_COMPOSE_BASE, DOCKER_COMPOSE_BASE_TEMPLATE
9
9
  from stoobly_agent.app.cli.scaffold.templates.constants import CORE_GATEWAY_SERVICE_NAME
10
10
 
11
- def set_gateway_ports(service_paths: List[str]):
11
+ def configure_gateway(service_paths: List[str], no_publish = False):
12
12
  if len(service_paths) == 0:
13
13
  return
14
14
 
15
15
  ports = []
16
+ hostnames = []
16
17
  for path in service_paths:
17
18
  config = ServiceConfig(path)
18
19
 
20
+ if not config.hostname:
21
+ continue
22
+
19
23
  try:
20
24
  port = int(config.port)
21
25
  except Exception:
@@ -25,6 +29,8 @@ def set_gateway_ports(service_paths: List[str]):
25
29
  if port > 0 and port <= 65535 and port_mapping not in ports:
26
30
  ports.append(port_mapping)
27
31
 
32
+ hostnames.append(config.hostname)
33
+
28
34
  app_dir_path = os.path.dirname(service_paths[0])
29
35
  gateway_service_path = os.path.join(app_dir_path, CORE_GATEWAY_SERVICE_NAME)
30
36
  docker_compose_src_path = os.path.join(gateway_service_path, DOCKER_COMPOSE_BASE_TEMPLATE)
@@ -41,7 +47,15 @@ def set_gateway_ports(service_paths: List[str]):
41
47
  gateway_base = services.get('gateway_base')
42
48
 
43
49
  if gateway_base:
44
- gateway_base['ports'] = ports
50
+ if not no_publish:
51
+ gateway_base['ports'] = ports
52
+
53
+ gateway_base['networks'] = {
54
+ 'app.egress': {},
55
+ 'app.ingress': {
56
+ 'aliases': hostnames
57
+ }
58
+ }
45
59
 
46
60
  with open(docker_compose_dest_path, 'w') as fp:
47
61
  yaml.dump(compose, fp)
@@ -22,7 +22,6 @@ class BuildDecorator():
22
22
  **app_service,
23
23
  **{
24
24
  'extends': service_builder.build_extends_proxy_base(self.workflow_builder.dir_path),
25
- 'hostname': service_builder.config.hostname,
26
25
  'profiles': self.workflow_builder.profiles,
27
26
  }
28
27
  }
@@ -32,11 +32,6 @@ class WorkflowBuilder(Builder):
32
32
  STOOBLY_HOME_DIR, DATA_DIR_NAME, DOCKER_NAMESPACE, self.service_builder.service_name, self.workflow_name
33
33
  )
34
34
 
35
- if self.config.hostname:
36
- self.with_public_network()
37
-
38
- self.load()
39
-
40
35
  @property
41
36
  def app(self):
42
37
  return f"{self.namespace}.app"
@@ -91,8 +86,6 @@ class WorkflowBuilder(Builder):
91
86
 
92
87
  def build_all(self):
93
88
  # Resources
94
- self.with_network(self.service_builder.service_name)
95
-
96
89
  if self.config.detached:
97
90
  self.with_volume(self.service_builder.service_name)
98
91
 
@@ -101,7 +94,6 @@ class WorkflowBuilder(Builder):
101
94
  self.build_configure()
102
95
 
103
96
  if self.config.hostname:
104
- self.with_public_network()
105
97
  self.build_proxy() # Depends on configure, must call build_configure first
106
98
 
107
99
  def build_init(self):
@@ -166,7 +158,7 @@ class WorkflowBuilder(Builder):
166
158
 
167
159
  depends_on = {}
168
160
  environment = { **self.env_dict() }
169
- networks = [self.service_builder.service_name]
161
+ networks = {}
170
162
  volumes = []
171
163
 
172
164
  service = {
@@ -188,14 +180,13 @@ class WorkflowBuilder(Builder):
188
180
  'condition': 'service_completed_successfully',
189
181
  }
190
182
 
191
- if self.config.hostname:
192
- environment['VIRTUAL_HOST'] = SERVICE_HOSTNAME
193
- environment['VIRTUAL_PORT'] = SERVICE_PORT
194
- environment['VIRTUAL_PROTO'] = SERVICE_SCHEME
183
+ environment['VIRTUAL_HOST'] = SERVICE_HOSTNAME
184
+ environment['VIRTUAL_PORT'] = SERVICE_PORT
185
+ environment['VIRTUAL_PROTO'] = SERVICE_SCHEME
195
186
 
196
- # Expose this container service to the public network
197
- # so that it is accessible to other Stoobly services
198
- networks.append(self.public_network_name)
187
+ # Expose this container service to the public network
188
+ # so that it is accessible to other Stoobly services
189
+ networks[self.egress_network_name] = {}
199
190
 
200
191
  if self.config.detached:
201
192
  volumes.append(f"{self.service_builder.service_name}:{STOOBLY_HOME_DIR}/.stoobly")
@@ -213,10 +204,14 @@ class WorkflowBuilder(Builder):
213
204
  dest = self.custom_compose_file_path
214
205
 
215
206
  if not os.path.exists(dest):
216
- super().write({
217
- 'networks': self.networks,
207
+ compose = {
218
208
  'services': {}
219
- }, dest)
209
+ }
210
+
211
+ if self.networks:
212
+ compose['networks'] = self.networks
213
+
214
+ super().write(compose, dest)
220
215
 
221
216
  def with_env(self, v: List[str]):
222
217
  if not isinstance(v, list):
@@ -225,11 +220,17 @@ class WorkflowBuilder(Builder):
225
220
  return self
226
221
 
227
222
  def write(self):
228
- super().write({
229
- 'networks': self.networks,
223
+ compose = {
230
224
  'services': self.services,
231
- 'volumes': self.volumes,
232
- })
225
+ }
226
+
227
+ if self.networks:
228
+ compose['networks'] = self.networks
229
+
230
+ if self.volumes:
231
+ compose['volumes'] = self.volumes
232
+
233
+ super().write(compose)
233
234
 
234
235
  def __with_url_environment(self, environment):
235
236
  environment[SERVICE_HOSTNAME_ENV] = SERVICE_HOSTNAME
@@ -1,7 +1,7 @@
1
1
  from stoobly_agent.app.cli.scaffold.service_config import ServiceConfig
2
2
 
3
3
  from ...constants import WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE
4
- from .development_decorator import DevelopmentDecorator
4
+ from .dns_decorator import DnsDecorator
5
5
  from .mock_decorator import MockDecorator
6
6
  from .reverse_proxy_decorator import ReverseProxyDecorator
7
7
 
@@ -11,11 +11,11 @@ def get_workflow_decorators(workflow: str, service_config: ServiceConfig):
11
11
  if workflow == WORKFLOW_RECORD_TYPE:
12
12
  if service_config.hostname:
13
13
  workflow_decorators.append(ReverseProxyDecorator)
14
- workflow_decorators.append(DevelopmentDecorator)
14
+ workflow_decorators.append(DnsDecorator)
15
15
  elif workflow == WORKFLOW_MOCK_TYPE:
16
16
  if service_config.hostname:
17
17
  workflow_decorators.append(ReverseProxyDecorator if service_config.detached else MockDecorator)
18
- workflow_decorators.append(DevelopmentDecorator)
18
+ workflow_decorators.append(DnsDecorator)
19
19
  else:
20
20
  if service_config.hostname:
21
21
  workflow_decorators.append(ReverseProxyDecorator if service_config.detached else MockDecorator)
@@ -2,7 +2,7 @@ from ...constants import SERVICE_DNS
2
2
 
3
3
  from .builder import WorkflowBuilder
4
4
 
5
- class DevelopmentDecorator():
5
+ class DnsDecorator():
6
6
 
7
7
  def __init__(self, workflow_builder: WorkflowBuilder):
8
8
  self.__workflow_builder = workflow_builder
@@ -41,5 +41,5 @@ class MockDecorator():
41
41
 
42
42
  services[proxy_name] = {
43
43
  **proxy_service,
44
- **{ 'command': command, 'hostname': f"{SERVICE_HOSTNAME}" },
44
+ **{ 'command': command },
45
45
  }
@@ -40,19 +40,6 @@ class ReverseProxyDecorator():
40
40
 
41
41
  additional_properties = { 'command': command }
42
42
 
43
- # Proxying forwards requests to the actual service
44
- # If the destination hostname is the same as the service's hostname, then
45
- # If we set the 'hostname' property, this will cause an "infinite loop"
46
- proxy_mode_toks = config.proxy_mode.split(':', 1)
47
-
48
- if len(proxy_mode_toks) > 1:
49
- directed = proxy_mode_toks[0] == 'reverse' or proxy_mode_toks[0] == 'upstream'
50
- if directed:
51
- spec = proxy_mode_toks[1]
52
- uri = urlparse(spec)
53
- if uri.hostname != self.service_builder.config.hostname:
54
- additional_properties['hostname'] = f"{SERVICE_HOSTNAME}"
55
-
56
43
  service = {
57
44
  **proxy_service,
58
45
  **additional_properties,
@@ -18,6 +18,7 @@ from stoobly_agent.app.cli.scaffold.constants import (
18
18
  WORKFLOW_RECORD_TYPE,
19
19
  WORKFLOW_TEST_TYPE,
20
20
  )
21
+ from stoobly_agent.app.cli.scaffold.docker.constants import APP_EGRESS_NETWORK_TEMPLATE
21
22
  from stoobly_agent.app.cli.scaffold.hosts_file_manager import HostsFileManager
22
23
  from stoobly_agent.app.cli.scaffold.service_command import ServiceCommand
23
24
  from stoobly_agent.app.cli.scaffold.service_docker_compose import ServiceDockerCompose
@@ -113,7 +114,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
113
114
  output = self.docker_client.containers.run(
114
115
  image='curlimages/curl:8.11.0',
115
116
  command=f"curl --max-time {timeout_seconds} {url} --verbose",
116
- network=self.app_config.network,
117
+ network=APP_EGRESS_NETWORK_TEMPLATE.format(network=self.app_config.network),
117
118
  stderr=True,
118
119
  remove=True,
119
120
  )
@@ -25,7 +25,7 @@ exec_namespace=$(shell echo $(context_dir) | (md5 2>/dev/null || md5sum 2>/dev/n
25
25
  user_id_option=--user-id $(USER_ID)
26
26
  stoobly_exec_options=--profile $(EXEC_WORKFLOW_NAME) -p $(exec_namespace)
27
27
  workflow_run_options=--script-path $(workflow_script) $(workflow_service_options)
28
- workflow_up_options=--app-dir-path $(app_dir) --context-dir-path $(context_dir) --ca-certs-dir-path $(ca_certs_dir) --certs-dir-path $(certs_dir) $(user_id_option)
28
+ workflow_up_options=--app-dir-path $(app_dir) --context-dir-path $(context_dir) --ca-certs-dir-path $(ca_certs_dir) --certs-dir-path $(certs_dir) $(user_id_option) $(WORKFLOW_UP_OPTIONS)
29
29
 
30
30
  app_data_dir=$(app_dir)/.stoobly
31
31
  app_namespace_dir=$(app_data_dir)/docker
@@ -88,7 +88,6 @@ nameservers: tmpdir
88
88
  echo "$$nameserver" > $(app_tmp_dir)/.nameservers; \
89
89
  else \
90
90
  echo "/etc/resolv.conf not found." >&2; \
91
- exit 1; \
92
91
  fi
93
92
  intercept/disable:
94
93
  @export EXEC_COMMAND=.disable && \
@@ -191,7 +190,7 @@ workflow/services:
191
190
  export EXEC_ARGS="$(WORKFLOW)" && \
192
191
  $(stoobly_exec_run)
193
192
  workflow/test:
194
- $(eval WORKFLOW=test)
193
+ $(eval WORKFLOW=test) $(eval WORKFLOW_UP_OPTIONS=--no-publish)
195
194
  workflow/up:
196
195
  @export EXEC_COMMAND=.up && \
197
196
  export EXEC_OPTIONS="$(user_id_option) $(workflow_up_options) $(workflow_run_options) $(options)" && \
@@ -0,0 +1,7 @@
1
+ networks:
2
+ app.egress:
3
+ external: true
4
+ name: ${APP_NETWORK}.egress
5
+ app.ingress:
6
+ external: true
7
+ name: ${APP_NETWORK}.ingress
@@ -1,7 +1,7 @@
1
1
  services:
2
2
  build.configure_base:
3
3
  command:
4
- - /usr/local/bin/services/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.configure
4
+ - ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.configure
5
5
  - bin/configure
6
6
  extends:
7
7
  file: ../.docker-compose.base.yml
@@ -9,7 +9,7 @@ services:
9
9
  working_dir: /home/stoobly/.stoobly/docker/build/${WORKFLOW_NAME}
10
10
  build.init_base:
11
11
  command:
12
- - /usr/local/bin/services/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.init
12
+ - ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.init
13
13
  - bin/init
14
14
  extends:
15
15
  file: ../.docker-compose.base.yml
@@ -2,7 +2,7 @@ networks: {}
2
2
  services:
3
3
  entrypoint.configure_base:
4
4
  command:
5
- - /usr/local/bin/services/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.configure
5
+ - ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.configure
6
6
  - bin/configure
7
7
  environment: {}
8
8
  extends:
@@ -11,7 +11,7 @@ services:
11
11
  working_dir: /home/stoobly/.stoobly/docker/entrypoint/${WORKFLOW_NAME}
12
12
  entrypoint.init_base:
13
13
  command:
14
- - /usr/local/bin/services/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.init
14
+ - ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.init
15
15
  - bin/init
16
16
  environment: {}
17
17
  extends:
@@ -1,14 +1,18 @@
1
- # Define networks and services here
1
+ # Define custom services here
2
2
  #
3
- # If a container service needs access to a Stoobly defined service,
4
- # then the container service needs to add the service name to the 'networks' property
5
- # e.g. If we have defined a 'upstream' service, then the following should be added
3
+ # Container services that need to make request(s) to a Stoobly defined service
4
+ # need to have either 'app.ingress' or 'app.egress' added to the 'networks' property
5
+ #
6
+ # e.g. If we want to make a request by hostname, then the following should be added
7
+ # networks:
8
+ # - app.ingress
9
+ #
10
+ # e.g. If we want to make a request by container service name, then the following should be added
6
11
  # networks:
7
- # - upstream
12
+ # - app.egress
8
13
  #
9
14
  # Container services that are intended to be run as part of a workflow need to have the workflow name added to the 'profiles' property
10
15
  # e.g. If we want to run a service as part of the 'mock' workflow, then the following should be added
11
16
  # profiles:
12
17
  # - mock
13
- networks: {}
14
18
  services: {}
@@ -3,11 +3,5 @@ services:
3
3
  extends:
4
4
  file: ../.docker-compose.base.yml
5
5
  service: gateway_base
6
- networks:
7
- - app
8
6
  profiles:
9
- - mock
10
- networks:
11
- app:
12
- name: ${APP_NETWORK}
13
- external: true
7
+ - mock
@@ -3,11 +3,5 @@ services:
3
3
  extends:
4
4
  file: ../.docker-compose.base.yml
5
5
  service: gateway_base
6
- networks:
7
- - app
8
6
  profiles:
9
- - record
10
- networks:
11
- app:
12
- name: ${APP_NETWORK}
13
- external: true
7
+ - record
@@ -0,0 +1,7 @@
1
+ services:
2
+ gateway.service:
3
+ extends:
4
+ file: ../.docker-compose.base.yml
5
+ service: gateway_base
6
+ profiles:
7
+ - test
@@ -13,9 +13,10 @@ from stoobly_agent.lib.logger import Logger
13
13
  from .app import App
14
14
  from .constants import (
15
15
  APP_DIR_ENV, APP_NETWORK_ENV, CA_CERTS_DIR_ENV, CERTS_DIR_ENV, CONTEXT_DIR_ENV, NAMESERVERS_FILE,
16
- SERVICE_DNS_ENV, SERVICE_NAME_ENV, USER_ID_ENV, WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV, WORKFLOW_TEMPLATE_ENV
16
+ SERVICE_DNS_ENV, SERVICE_NAME_ENV, SERVICE_SCRIPTS_DIR, SERVICE_SCRIPTS_ENV, USER_ID_ENV,
17
+ WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV, WORKFLOW_SCRIPTS_DIR, WORKFLOW_SCRIPTS_ENV, WORKFLOW_TEMPLATE_ENV
17
18
  )
18
- from .docker.constants import DOCKERFILE_CONTEXT
19
+ from .docker.constants import APP_EGRESS_NETWORK_TEMPLATE, APP_INGRESS_NETWORK_TEMPLATE, DOCKERFILE_CONTEXT
19
20
  from .workflow_command import WorkflowCommand
20
21
  from .workflow_env import WorkflowEnv
21
22
 
@@ -122,11 +123,17 @@ class WorkflowRunCommand(WorkflowCommand):
122
123
  command.append('|| true')
123
124
  return ' '.join(command)
124
125
 
125
- def create_network(self):
126
- return f"docker network create {self.network} &> /dev/null"
126
+ def create_egress_network(self):
127
+ return f"docker network create {APP_EGRESS_NETWORK_TEMPLATE.format(network=self.network)} &> /dev/null"
127
128
 
128
- def remove_network(self):
129
- return f"docker network rm {self.network} &> /dev/null || true"
129
+ def create_ingress_network(self):
130
+ return f"docker network create {APP_INGRESS_NETWORK_TEMPLATE.format(network=self.network)} &> /dev/null"
131
+
132
+ def remove_egress_network(self):
133
+ return f"docker network rm {APP_EGRESS_NETWORK_TEMPLATE.format(network=self.network)} &> /dev/null || true"
134
+
135
+ def remove_ingress_network(self):
136
+ return f"docker network rm {APP_INGRESS_NETWORK_TEMPLATE.format(network=self.network)} &> /dev/null || true"
130
137
 
131
138
  def up(self, **options: UpOptions):
132
139
  if not os.path.exists(self.compose_path):
@@ -138,6 +145,9 @@ class WorkflowRunCommand(WorkflowCommand):
138
145
  # Add docker compose file
139
146
  command_options.append(f"-f {os.path.relpath(self.compose_path, self.__current_working_dir)}")
140
147
 
148
+ # Add docker compose networks file
149
+ command_options.append(f"-f {os.path.relpath(self.networks_compose_path, os.getcwd())}")
150
+
141
151
  # Add custom docker compose file
142
152
  custom_services = self.custom_services
143
153
  if custom_services:
@@ -203,6 +213,9 @@ class WorkflowRunCommand(WorkflowCommand):
203
213
  # Add docker compose file
204
214
  command.append(f"-f {os.path.relpath(self.compose_path, os.getcwd())}")
205
215
 
216
+ # Add docker compose networks file
217
+ command.append(f"-f {os.path.relpath(self.networks_compose_path, os.getcwd())}")
218
+
206
219
  # Add custom docker compose file
207
220
  if self.custom_services:
208
221
  command.append(f"-f {os.path.relpath(self.custom_compose_path, self.__current_working_dir)}")
@@ -257,8 +270,10 @@ class WorkflowRunCommand(WorkflowCommand):
257
270
  _config[CERTS_DIR_ENV] = self.certs_dir_path
258
271
  _config[CONTEXT_DIR_ENV] = self.context_dir_path
259
272
  _config[SERVICE_NAME_ENV] = self.service_name
273
+ _config[SERVICE_SCRIPTS_ENV] = SERVICE_SCRIPTS_DIR
260
274
  _config[USER_ID_ENV] = user_id or os.getuid()
261
275
  _config[WORKFLOW_NAME_ENV] = self.workflow_name
276
+ _config[WORKFLOW_SCRIPTS_ENV] = WORKFLOW_SCRIPTS_DIR
262
277
  _config[WORKFLOW_TEMPLATE_ENV] = self.workflow_name
263
278
 
264
279
  if namespace:
@@ -267,9 +282,12 @@ class WorkflowRunCommand(WorkflowCommand):
267
282
  if self.network:
268
283
  _config[APP_NETWORK_ENV] = self.network
269
284
 
285
+ # Specified DNS should prioritized, otherwise defaults to internal DNS
270
286
  nameservers = self.nameservers
271
287
  if nameservers:
272
288
  _config[SERVICE_DNS_ENV] = nameservers[0]
289
+ else:
290
+ _config[SERVICE_DNS_ENV] = '8.8.8.8'
273
291
 
274
292
  env_vars = self.config(_config)
275
293
  WorkflowEnv(self.workflow_path).write(env_vars)
@@ -39,13 +39,6 @@ class WorkflowValidateCommand(WorkflowCommand, ValidateCommand):
39
39
  raise ScaffoldValidateException(f"Container '{mock_ui_container_name}' not found for service '{CORE_MOCK_UI_SERVICE_NAME}'")
40
40
 
41
41
  def validate_no_core_components(self):
42
- try:
43
- core_gateway_container = self.docker_client.containers.get(self.managed_services_docker_compose.gateway_container_name)
44
- if core_gateway_container:
45
- raise ScaffoldValidateException(f"Gateway container is running when it shouldn't: {core_gateway_container.name}")
46
- except docker_errors.NotFound:
47
- pass
48
-
49
42
  try:
50
43
  core_mock_ui_container_name = self.docker_client.containers.get(self.managed_services_docker_compose.mock_ui_container_name)
51
44
  if core_mock_ui_container_name:
@@ -14,7 +14,7 @@ from stoobly_agent.app.cli.scaffold.constants import (
14
14
  DOCKER_NAMESPACE, WORKFLOW_CONTAINER_PROXY, WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
15
15
  )
16
16
  from stoobly_agent.app.cli.scaffold.containerized_app import ContainerizedApp
17
- from stoobly_agent.app.cli.scaffold.docker.service.set_gateway_ports import set_gateway_ports
17
+ from stoobly_agent.app.cli.scaffold.docker.service.configure_gateway import configure_gateway
18
18
  from stoobly_agent.app.cli.scaffold.docker.workflow.decorators_factory import get_workflow_decorators
19
19
  from stoobly_agent.app.cli.scaffold.hosts_file_manager import HostsFileManager
20
20
  from stoobly_agent.app.cli.scaffold.service import Service
@@ -341,8 +341,11 @@ def down(**kwargs):
341
341
  remove_image_command = command.remove_image(kwargs['user_id'])
342
342
  print(remove_image_command, file=script)
343
343
 
344
- remove_network_command = command.remove_network()
345
- print(remove_network_command, file=script)
344
+ remove_egress_network_command = command.remove_egress_network()
345
+ print(remove_egress_network_command, file=script)
346
+
347
+ remove_ingress_network_command = command.remove_ingress_network()
348
+ print(remove_ingress_network_command, file=script)
346
349
 
347
350
  __run_script(script, kwargs['dry_run'])
348
351
 
@@ -425,6 +428,7 @@ def logs(**kwargs):
425
428
  @click.option('--namespace', help='Workflow namespace.')
426
429
  @click.option('--network', help='Workflow network name.')
427
430
  @click.option('--no-build', is_flag=True, help='Do not build images before starting containers.')
431
+ @click.option('--no-publish', is_flag=True, help='Do not publish all ports.')
428
432
  @click.option('--pull', is_flag=True, help='Pull image before running.')
429
433
  @click.option('--script-path', help='Path to intermediate script path.')
430
434
  @click.option('--service', multiple=True, help='Select which services to run. Defaults to all.')
@@ -457,7 +461,7 @@ def up(**kwargs):
457
461
 
458
462
  # Gateway ports are dynamically set depending on the workflow run
459
463
  workflow = Workflow(kwargs['workflow_name'], app)
460
- set_gateway_ports(workflow.service_paths_from_services(services))
464
+ configure_gateway(workflow.service_paths_from_services(services), kwargs['no_publish'])
461
465
 
462
466
  commands: List[WorkflowRunCommand] = []
463
467
  for service in services:
@@ -478,7 +482,8 @@ def up(**kwargs):
478
482
  create_image_command = command.create_image(user_id=kwargs['user_id'], verbose=kwargs['verbose'])
479
483
  init_commands.append(create_image_command)
480
484
 
481
- init_commands.append(command.create_network())
485
+ init_commands.append(command.create_egress_network())
486
+ init_commands.append(command.create_ingress_network())
482
487
  joined_command = ' && '.join(init_commands)
483
488
 
484
489
  if not containerized:
@@ -15,12 +15,16 @@ from stoobly_agent.app.settings.match_rule import MatchRule
15
15
  from stoobly_agent.config.constants import custom_headers, query_params as request_query_params
16
16
  from stoobly_agent.lib.api.param_builder import ParamBuilder
17
17
  from stoobly_agent.lib.api.interfaces.requests import RequestResponseShowQueryParams
18
- from stoobly_agent.lib.logger import Logger
18
+ from stoobly_agent.lib.api.keys.project_key import InvalidProjectKey
19
+ from stoobly_agent.lib.api.keys.scenario_key import InvalidScenarioKey
20
+ from stoobly_agent.lib.logger import bcolors, Logger
19
21
 
20
22
  from .hashed_request_decorator import HashedRequestDecorator
21
23
  from .search_endpoint import inject_search_endpoint
22
24
  from ..mitmproxy.request_facade import MitmproxyRequestFacade
23
25
 
26
+ LOG_ID = 'EvalRequest'
27
+
24
28
  class EvalRequestOptions(TypedDict):
25
29
  infer: bool
26
30
  retry: int
@@ -49,10 +53,19 @@ def eval_request(
49
53
  **options: EvalRequestOptions
50
54
  ) -> Response:
51
55
  query_params_builder = ParamBuilder({})
56
+ scenario_key = intercept_settings.scenario_key
57
+
58
+ if not scenario_key:
59
+ Logger.instance(LOG_ID).info(f"{bcolors.WARNING}Missing{bcolors.ENDC} scenario key, defaulting to all requests for mocking")
52
60
 
53
61
  try:
54
- query_params_builder.with_resource_scoping(intercept_settings.project_key, intercept_settings.scenario_key)
55
- except:
62
+ query_params_builder.with_resource_scoping(intercept_settings.project_key, scenario_key)
63
+ except InvalidScenarioKey:
64
+ Logger.instance(LOG_ID).warn(f"{bcolors.WARNING}Invalid{bcolors.ENDC} scenario key ${scenario_key}")
65
+ # If project_key or scenario_key are invalid, assume custom not found
66
+ return CustomNotFoundResponseBuilder().build()
67
+ except InvalidProjectKey:
68
+ Logger.instance(LOG_ID).warn(f"{bcolors.WARNING}Invalid{bcolors.ENDC} project key ${intercept_settings.project_key}")
56
69
  # If project_key or scenario_key are invalid, assume custom not found
57
70
  return CustomNotFoundResponseBuilder().build()
58
71
 
@@ -1,6 +1,5 @@
1
1
  import pdb
2
2
 
3
- from stoobly_agent.app.proxy.intercept_settings import InterceptSettings
4
3
  from stoobly_agent.lib.api.keys.project_key import ProjectKey
5
4
  from stoobly_agent.lib.api.keys.scenario_key import ScenarioKey
6
5
 
@@ -292,7 +292,6 @@ class TestScaffoldE2e():
292
292
  pass
293
293
 
294
294
  def test_assets(self, app_dir_path, target_workflow_name):
295
-
296
295
  app = App(app_dir_path, DOCKER_NAMESPACE)
297
296
  config = {
298
297
  'workflow_name': target_workflow_name,
@@ -300,5 +299,4 @@ class TestScaffoldE2e():
300
299
  }
301
300
 
302
301
  command = ServiceWorkflowValidateCommand(app, **config)
303
- command.validate()
304
-
302
+ command.validate()
@@ -1 +1 @@
1
- 1.6.1
1
+ 1.6.3
@@ -1,15 +1,10 @@
1
1
  # Define services here
2
- networks:
3
- app:
4
- external: true
5
- name: ${APP_NETWORK}
6
-
7
2
  services:
8
3
  assets:
9
4
  hostname: assets
10
5
  image: nginx:1.27
11
6
  networks:
12
- - app
7
+ - app.egress
13
8
  volumes:
14
9
  - ./index.html:/usr/share/nginx/html/index.html
15
10
  profiles: &id001
@@ -1,16 +1,10 @@
1
1
  # Define services here
2
-
3
- networks:
4
- app:
5
- external: true
6
- name: ${APP_NETWORK}
7
-
8
2
  services:
9
3
  my-httpbin:
10
4
  image: kennethreitz/httpbin:latest
11
5
  hostname: my-httpbin.com
12
6
  networks:
13
- - app
7
+ - app.egress
14
8
  profiles: &id001
15
9
  - record
16
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: stoobly-agent
3
- Version: 1.6.2
3
+ Version: 1.6.3
4
4
  Summary: Record, mock, and test HTTP(s) requests. CLI agent for Stoobly
5
5
  License: Apache-2.0
6
6
  Author: Matt Le
@@ -1,4 +1,4 @@
1
- stoobly_agent/__init__.py,sha256=YgNv7ZVr561UUQVGUuChJX5OTSxwob_6G7Si3HBAKk0,44
1
+ stoobly_agent/__init__.py,sha256=0ju0Gr8XihbpTDeP0psa1wNQdyDjYO1NTxBTv9DtSIA,44
2
2
  stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  stoobly_agent/app/api/__init__.py,sha256=ctkB8KR-eXO0SFhj602huHiyvQ3PslFWd8fkcufgrAI,1000
4
4
  stoobly_agent/app/api/application_http_request_handler.py,sha256=Vvz53yB0bR7J-QqMAkLlhcZrA4P64ZEN7w8cMbgl6o0,5261
@@ -68,29 +68,29 @@ stoobly_agent/app/cli/report_cli.py,sha256=ZxJw0Xkx7KFZJn9e45BSKRKon8AD0Msrwy1fb
68
68
  stoobly_agent/app/cli/request_cli.py,sha256=THNloW111l9MLE0oPg4y7hVXL1U7OXoz760g0A1CtJ0,7747
69
69
  stoobly_agent/app/cli/scaffold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
70
  stoobly_agent/app/cli/scaffold/app.py,sha256=bgNc_dWZtgkuDAkS9aHSCMxCrjX907Phlfb2bOasswI,3435
71
- stoobly_agent/app/cli/scaffold/app_command.py,sha256=qtOdG08ehdRk1wujiTL7LCQXoJkGpwExepO2qYBXBNA,2027
71
+ stoobly_agent/app/cli/scaffold/app_command.py,sha256=sliaYulMNmaxbRXpJIDtBDWEBGEv5Tht4rpErzC_xxw,2368
72
72
  stoobly_agent/app/cli/scaffold/app_config.py,sha256=Gs-BynV1vY7_PpTOenn2mqc7lBIu0vBx6as9vtEU1sk,818
73
73
  stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=sy4017eiir9MK0TEbTi1NyG9mhOGlenGGVnq0GsGUrQ,966
74
74
  stoobly_agent/app/cli/scaffold/command.py,sha256=aoTsdkkBzyu7TkVSMdNQQGk0Gj874jNgFcjR14y3TuM,254
75
75
  stoobly_agent/app/cli/scaffold/config.py,sha256=HZU5tkvr3dkPr4JMXZtrJlu2wxxO-134Em6jReFFcq0,688
76
- stoobly_agent/app/cli/scaffold/constants.py,sha256=s6AR9p9fVsPukfcCQ2QIGPtPKMbXkGnZ5a5CJGwou5E,2205
76
+ stoobly_agent/app/cli/scaffold/constants.py,sha256=s5SBy8Xs4-Lg4tYEVBTiDIzeWwerrnz8qHXbXQtXH5U,2465
77
77
  stoobly_agent/app/cli/scaffold/containerized_app.py,sha256=dAjn4RwcZV3aEL0POUmrbF_DC-r9h6s1zx7gT2t45v0,175
78
78
  stoobly_agent/app/cli/scaffold/docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  stoobly_agent/app/cli/scaffold/docker/app_builder.py,sha256=7z5pk5JKlRDHx2USxY-WurttLyyUkIVYfl34_u1x9dE,501
80
- stoobly_agent/app/cli/scaffold/docker/builder.py,sha256=BQzjpDZmkUrii3VDcRCBXQkocxRwdS1omJFy5AkzJzw,2856
81
- stoobly_agent/app/cli/scaffold/docker/constants.py,sha256=xfFBSF-1GQKNnUPTlz08Nm9HZDspUnvPvjwaLViQwVw,419
80
+ stoobly_agent/app/cli/scaffold/docker/builder.py,sha256=uiGqhxBHEasZAqLzjKUGUs-1QEZiMsJQYn4mdJ2jy3A,3137
81
+ stoobly_agent/app/cli/scaffold/docker/constants.py,sha256=1khQdgTaQ89ykGRNdTQh_ejFOdjNFGaZ_3cOGda6y7Y,692
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=ZU7z4bkvdS3OK5O4fJhlA9_PNwnFtZW6t7vNF7V5obQ,1003
84
- stoobly_agent/app/cli/scaffold/docker/service/builder.py,sha256=RnX0KESc6kxV3oyiSmS4JqIXZNMDVMLVGg34c024cm0,3186
85
- stoobly_agent/app/cli/scaffold/docker/service/set_gateway_ports.py,sha256=MH71h24nX5lAd6xNfSjjNyw6PjBsAXv-3kOFEe86huk,1417
84
+ stoobly_agent/app/cli/scaffold/docker/service/builder.py,sha256=AE0tEHKeDM3dDMSjaqUwJA9ZLgzeyr0ErMAhOzNuaSI,3190
85
+ stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py,sha256=bdqY9jESbyGEwPW5cwb51oznYFlzK4MUP770zxxgrds,1701
86
86
  stoobly_agent/app/cli/scaffold/docker/service/types.py,sha256=qB-yYHlu-PZDc0HYgTUvE5bWNpHxaSThC3JUG8okR1k,88
87
87
  stoobly_agent/app/cli/scaffold/docker/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
- stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py,sha256=vbmME0cbN2EnNRlzQ2umj7Y3L7aZT-EHqEpkBFMfe8U,758
89
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py,sha256=G9zyw_jS0HEHtzl0EvoDQ9MlpTavlyWz0xFpgEZoyVc,6844
90
- stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py,sha256=oi0Hkii2W9338IeG7BG2medQxqZUbBp71Cvj-qDunII,987
91
- stoobly_agent/app/cli/scaffold/docker/workflow/development_decorator.py,sha256=cs00Cc2DuOYrGD8W9849PjHz6k2xyx0fSizqJlM7yR8,946
92
- stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=iRHmUPnHaxAVL5ithFf5Mw0WQdogWsVcLkTeCOStDqg,1261
93
- stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256=eeB1Y7L6xYlD9w7oO7a4Ou0Gvfvvd2znnqrZQQz6IlQ,1825
88
+ stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py,sha256=VKD9hXbJGRIWHS5IeYXeX0-FQ0F43zG8VmsegL6eYwA,703
89
+ stoobly_agent/app/cli/scaffold/docker/workflow/builder.py,sha256=i2T3TvWlXBT2O4wqwMcwsN55-4VzAblo36dEwCPf8yU,6736
90
+ stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py,sha256=gU4RfuZ13r_YoB4eLS6FuiB1A4kfuLrWLPmYjZ0EJJw,955
91
+ stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py,sha256=nlDPbyF1hkVItzFH3bk9KlqfDC8usyFZs353-ibqxvA,938
92
+ stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=DcBnwA8YhE-VdrUiWf2xPcqirEMZEQm3AwssAg9CxLo,1226
93
+ stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256=zD4FEtBMiPEtvEb798LOfFH8sJhTWQYpA9THLq5UMbg,1227
94
94
  stoobly_agent/app/cli/scaffold/env.py,sha256=e-Ve4p3RUgzFx22B3SIYttvJ_yLuDtA27oDACZ8n-6E,1140
95
95
  stoobly_agent/app/cli/scaffold/hosts_file_manager.py,sha256=FiX1hYEWN4cJiCOV4h6wOOlY7t71uwIwe6t2upS65aQ,5006
96
96
  stoobly_agent/app/cli/scaffold/managed_services_docker_compose.py,sha256=-wLBXUi7DCWsfm5KzZzd_kdJKOTl1NT924XR7dyjbSY,574
@@ -101,14 +101,15 @@ stoobly_agent/app/cli/scaffold/service_create_command.py,sha256=bmLGgx9qnh-X_i2_
101
101
  stoobly_agent/app/cli/scaffold/service_delete_command.py,sha256=_nBDQjm8eL62MQpzSCxgUHlW04ZXKG8MDlN1BXxlqww,986
102
102
  stoobly_agent/app/cli/scaffold/service_docker_compose.py,sha256=OMUN1-ujQYIZXxDvS4XBf5C9wGalQULkwOiBBQPZbHY,820
103
103
  stoobly_agent/app/cli/scaffold/service_workflow.py,sha256=sQ_Edy_wGHKMXpD0DmhnOWkGEKz7gSgEGNI8f7aXOdg,444
104
- stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=iORyPZEQurgWVxrdMgkdD3VsCz3ar2D15dmyY3RoeKw,9904
104
+ stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=M5fc9RiCgN9ReCXsb3KIVJIKo0aSIOv28Q3izLkEQTo,10036
105
105
  stoobly_agent/app/cli/scaffold/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context,sha256=xnUFSlP8YvEs04lTh1Jj_ggNcwyVjyzZjzF0Vh9pris,158
107
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=zXj6WEYfFhgxB9BioUIlXxOAGI1gpDVWpc57K4AamE4,8390
107
+ stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=EVBaDI6IrdDEFNYtrGsAZ1DwbBG8kL05ZiR7dbZHsto,8442
108
108
  stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml,sha256=6tFqXh3ine8vaD0FCL5TMoY5NjKx2wLUR8XpW3tJtew,245
109
+ stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.networks.yml,sha256=I4PbJpQjFHb5IbAUWNvYM6okDEtmwtKFDQg-yog05WM,141
109
110
  stoobly_agent/app/cli/scaffold/templates/app/Makefile,sha256=TEmPG7Bf0KZOnmfsgdzza3UdwcVMmM5Lj1YdLc4cgjA,79
110
111
  stoobly_agent/app/cli/scaffold/templates/app/build/.config.yml,sha256=8Wt8ZZ5irvBYYS44xGrR_EWlZDuXH9kyWmquzsh7s8g,19
111
- stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml,sha256=A9TI3J6182Bg9YH0GqetEqBz5_5Lf99XLHgHDuesK64,569
112
+ stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml,sha256=SBjI-5APmzvzQafHzRahH_xcoXEEV9M9GIeEmXSwnAU,559
112
113
  stoobly_agent/app/cli/scaffold/templates/app/build/mock/.docker-compose.mock.yml,sha256=3DJ8LTq3QScaz0SA_tXQqJ0kljSCeqkPyDbhLOxBJvc,414
113
114
  stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/configure,sha256=dEOGIKUKJf5kjOwJRdTgZkS0ZjmQHWVuAmKrWUjPij4,44
114
115
  stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/init,sha256=YxWVVQMEdGarcMtBcE1Sj2kdLgUgwY9kXp3MjPsVcmg,46
@@ -119,11 +120,11 @@ stoobly_agent/app/cli/scaffold/templates/app/build/test/.docker-compose.test.yml
119
120
  stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/configure,sha256=dEOGIKUKJf5kjOwJRdTgZkS0ZjmQHWVuAmKrWUjPij4,44
120
121
  stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/init,sha256=YxWVVQMEdGarcMtBcE1Sj2kdLgUgwY9kXp3MjPsVcmg,46
121
122
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.config.yml,sha256=T9VQz6OwAQpKdIoFrnfKAxuX_to0c6EhuWRLKM34Sr4,22
122
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml,sha256=FzDOFouP61eSeklhjQ7qpsE8rSc8r3DuT8aytIiZwN8,640
123
+ stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml,sha256=Sl_E6IUaHSR_Av_Tm3FzCJ6w5qjtR2LiJfiQJAMZUFI,630
123
124
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/.docker-compose.mock.yml,sha256=-YbHd6BrAlBpT8PYg8kOsq0GyDeZ3g_q8IB0B0rGluU,551
124
125
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/configure,sha256=dEOGIKUKJf5kjOwJRdTgZkS0ZjmQHWVuAmKrWUjPij4,44
125
126
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/init,sha256=YxWVVQMEdGarcMtBcE1Sj2kdLgUgwY9kXp3MjPsVcmg,46
126
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/docker-compose.yml,sha256=Zh8h8yUqVXZMi_Y4aUPE48qw2DU-ONO4-rrSTh4E_Ts,591
127
+ stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/docker-compose.yml,sha256=JZztRTAEd_byGjhBbI_TOrj7CML8l5lV4tYOM2syHUw,717
127
128
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/.docker-compose.record.yml,sha256=aIcgjelss4V86lxS3-xN8j_2eR-0YkzhtN_b-g3Y0nE,555
128
129
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/configure,sha256=dEOGIKUKJf5kjOwJRdTgZkS0ZjmQHWVuAmKrWUjPij4,44
129
130
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/init,sha256=YxWVVQMEdGarcMtBcE1Sj2kdLgUgwY9kXp3MjPsVcmg,46
@@ -134,8 +135,9 @@ stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/init,sha256=YxW
134
135
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/docker-compose.yml,sha256=E4gGJwHfnZoFo9-0vM17-Qg5LPjx7vIPpcz2K7eiZg0,591
135
136
  stoobly_agent/app/cli/scaffold/templates/app/gateway/.config.yml,sha256=XnLQZMzzMMIwVycjyPN5QXsmRztkTFAna1kIHYuDfJQ,19
136
137
  stoobly_agent/app/cli/scaffold/templates/app/gateway/.docker-compose.base.template.yml,sha256=bgBvyDhyehS75D2ObEi7sl9DLGV6U3c9JhCAncsvjq8,251
137
- stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/.docker-compose.mock.yml,sha256=_nlhrpml5dm9AWqgDIaEPwrQiD1vAKVHSsY2mTIds2k,224
138
- stoobly_agent/app/cli/scaffold/templates/app/gateway/record/.docker-compose.record.yml,sha256=FvPqOUs1k6Vc-rncnpgiRTZeEQlrizmcGkT8a4HdvUE,226
138
+ stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/.docker-compose.mock.yml,sha256=CDL5x18-ues4F_ujqpNef_zJTG6PyDOkQ11cIHBOW5k,136
139
+ stoobly_agent/app/cli/scaffold/templates/app/gateway/record/.docker-compose.record.yml,sha256=eyLH2h33Peunus8M1sUKL9AALCG2ABhV_heiJKhvgwo,138
140
+ stoobly_agent/app/cli/scaffold/templates/app/gateway/test/.docker-compose.test.yml,sha256=oJO6i0lsuQaQeIH80yoPZo3Vs0LzUAH2WRl853yLq6g,136
139
141
  stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.config.yml,sha256=XnLQZMzzMMIwVycjyPN5QXsmRztkTFAna1kIHYuDfJQ,19
140
142
  stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.docker-compose.base.yml,sha256=bxrtZqf3YtaJCukzScslh5PgWC5q8xkGIP1wKJf33LA,111
141
143
  stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/.docker-compose.exec.yml,sha256=JN89sU5uRf6YqHvN_O63K8rwQIAPJHbhFDLFmuUjKNM,304
@@ -195,9 +197,9 @@ stoobly_agent/app/cli/scaffold/workflow_copy_command.py,sha256=R9hh5dWVz7vGld7pE
195
197
  stoobly_agent/app/cli/scaffold/workflow_create_command.py,sha256=WmGKoqJqOkhL40oLCo8PnWhBKdfbY0dO2UtRn1IKynE,3504
196
198
  stoobly_agent/app/cli/scaffold/workflow_env.py,sha256=x8V5pJmIiklD3f2q2-qq-CORf4YaXYq_r2JpR2MmSwk,416
197
199
  stoobly_agent/app/cli/scaffold/workflow_log_command.py,sha256=Bke4lMOMxuDUFuAx9nlXHbKgYMO4KAg9ASHvjz4aVWc,1372
198
- stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=bbz1QcY5nr7VwPdofngX7yVUk8yVdGLckwlbXq0ozhE,10037
199
- stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=fhHciJXg_u32Wmh8us8LhgQj8D1SxkBADtuBBF4K0FM,4377
200
- stoobly_agent/app/cli/scaffold_cli.py,sha256=nvM1hawDtL9OHKQlCRpXJjcBh_E_MIQz69MkBpLDEjg,28332
200
+ stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=eF3aaK4OIZXYuSBEAeBnhAL7EZrS1G4mSYrJbEiXt2o,11082
201
+ stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=wv1Zrq0GoYO8ryac3oGno0C282c6UjMtniO19mBSyrE,4036
202
+ stoobly_agent/app/cli/scaffold_cli.py,sha256=FX8YW5n_qO1d4kiUbCS5YJLwRLRVCREDC8M5S5eq39U,28645
201
203
  stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
202
204
  stoobly_agent/app/cli/snapshot_cli.py,sha256=cpCjxFYBuVwLuq_b2lIUu-5zWqupRlrp4xWgDytirSM,10047
203
205
  stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
@@ -321,7 +323,7 @@ stoobly_agent/app/proxy/mock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
321
323
  stoobly_agent/app/proxy/mock/context.py,sha256=vDo5_3WBL73mVFnsmQWvcxvPg5nWtRJbigSrE3zGc-o,794
322
324
  stoobly_agent/app/proxy/mock/custom_not_found_response_builder.py,sha256=xHjA2CNhW5A4NyPkDzws6DY5YQeJqF6b3Y3k0iJKNss,611
323
325
  stoobly_agent/app/proxy/mock/eval_fixtures_service.py,sha256=hc4VLnN50HBaWvFnrhQUJqdH-r3jBu9DHrpt8gbvkHY,3847
324
- stoobly_agent/app/proxy/mock/eval_request_service.py,sha256=EKHZT_sKZhGcGFCDa3XOSzYkSJIfJJUg4164BTxKUeY,7365
326
+ stoobly_agent/app/proxy/mock/eval_request_service.py,sha256=A1tcE3wmrC1HwLpz0aRuRw-Nucn0dyHD_yHw5BeQEJU,8146
325
327
  stoobly_agent/app/proxy/mock/hashed_request_decorator.py,sha256=h1ma90fdaYI9LBWpMWMqWBz-RjNwI628O4VuS_uUBX4,5061
326
328
  stoobly_agent/app/proxy/mock/ignored_components_response_builder.py,sha256=E32_E1eSdmPn2SeM_e1jWnqu4xh5w_SnmOs32Shx99E,501
327
329
  stoobly_agent/app/proxy/mock/request_hasher.py,sha256=WeOeksmW_b5Ql0Xb0fL2WMH3MVHrm-9GYWYoKYS1teg,3755
@@ -481,7 +483,7 @@ stoobly_agent/lib/api/keys/resource_key.py,sha256=9qrkp3t8iJsScZvKVcBDvVbcfwmP0y
481
483
  stoobly_agent/lib/api/keys/scenario_key.py,sha256=VAc6gayvJS7shWgDL3SAqVET3fmgBefcygXTsKot07U,629
482
484
  stoobly_agent/lib/api/keys/test_key.py,sha256=-MCWp1oYLkJ3S_Pqs62j8KkkssXpT9quKb4YqMSq1Ks,438
483
485
  stoobly_agent/lib/api/keys/uuid_key.py,sha256=7_aL5wVTKF68bESHvqeQ2RUeC-Fw9-zpSrl8EWuFTJw,603
484
- stoobly_agent/lib/api/param_builder.py,sha256=sCx_m-apHTi5VBd4cvQtrFslxePUHd1CFs2-o_hCgJA,1069
486
+ stoobly_agent/lib/api/param_builder.py,sha256=50eq0zkqRz5MVezPZMIu69fYSB6z4CD5FG26_IU3eq0,996
485
487
  stoobly_agent/lib/api/projects_resource.py,sha256=7leBuQnlpgUwbYUX445lUC3jy-dzLMpTumeNPrqRc5c,1166
486
488
  stoobly_agent/lib/api/query_param_names_resource.py,sha256=k-3nlyfCOa_Vwj-TMI7v8_4g9qAs50baP073gkStAKE,1710
487
489
  stoobly_agent/lib/api/reports_resource.py,sha256=Dtw-96J99u0AEJRYYNUA1awPAneg2Y0IOHyjvuF6juQ,601
@@ -671,7 +673,7 @@ stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=0013aoiMZin-2
671
673
  stoobly_agent/test/app/cli/request/request_test_test.py,sha256=-cJNXKjgryVVfVt-7IN5fIhBwe3NjFoPmeavDH8lAjU,5527
672
674
  stoobly_agent/test/app/cli/scaffold/cli_invoker.py,sha256=_nGDLUsYxqkeqs5DdhvAeXy3IuotpgqKHXKVzu6GDF4,3700
673
675
  stoobly_agent/test/app/cli/scaffold/cli_test.py,sha256=sMNvO845MIu5DVGa1HmwXQDmKDcwrfNTdEb3fK5886w,4557
674
- stoobly_agent/test/app/cli/scaffold/e2e_test.py,sha256=rTPxC72Qh54qW5AmDQwVAyI9bj6i1rD5LOFGm0XdLrE,12974
676
+ stoobly_agent/test/app/cli/scaffold/e2e_test.py,sha256=Zq0nYPObbH6tYjuGHrT2hOT71FO6kDnvRQWGDK1yK4M,12971
675
677
  stoobly_agent/test/app/cli/scaffold/hosts_file_manager_test.py,sha256=sVFHHYBxzyfRbVIe3gnToQ-6JPy71BCBjdQDnboYzac,2306
676
678
  stoobly_agent/test/app/cli/scenario/scenario_create_test.py,sha256=fGqcjO1_1OvdpUMQfGRVkSyFe61u8WIcp_ndLFrf33A,3962
677
679
  stoobly_agent/test/app/cli/scenario/scenario_replay_integration_test.py,sha256=NbGJzmvPsNLBR0ac65yt_cOTfpnsST1IG7i3F0euwAk,7031
@@ -697,7 +699,7 @@ stoobly_agent/test/app/models/factories/resource/local_db/helpers/log_test.py,sh
697
699
  stoobly_agent/test/app/models/factories/resource/local_db/helpers/tiebreak_scenario_request_test.py,sha256=a1SFLyEyRRLuADvAw6ckQQKORFXvyK1lyrbkaLWx8oU,3399
698
700
  stoobly_agent/test/app/models/factories/resource/local_db/request_adapter_test.py,sha256=Pzq1cBPnP9oSWG-p0c-VoymoHxgp483QmNwmV1b78RA,8453
699
701
  stoobly_agent/test/app/models/factories/resource/local_db/response_adapter_test.py,sha256=9P95EKH5rZGOrmRkRIDlQZqtiLJHk9735og18Ffwpfw,2204
700
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=_f4T1QzFg0lLC81UhhuJRJ7MIzez92MEH4M0zL1X0Wc,5
702
+ stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION,sha256=8merClJHSZh5o5xLmNfSrExyEZszRWmdOcyxaN-hVPE,5
701
703
  stoobly_agent/test/app/models/schemas/.stoobly/db/stoobly_agent.sqlite3,sha256=ch8gNx6zIelLKQx65gwFx_LRNqUD3EC5xcHZ0ukIQiU,188416
702
704
  stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6tSLtIlyBefPQuNXq9wio4kBaODKtAU,726
703
705
  stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
@@ -733,13 +735,13 @@ stoobly_agent/test/mock_data/petstore-references.yaml,sha256=OpNrCSb5-qKmUBIO9oG
733
735
  stoobly_agent/test/mock_data/petstore-swagger-io.yaml,sha256=FbOEaArZKl1dV7jEf1hyJQv9jdAb5jXfsRRZB5i4-0Y,22094
734
736
  stoobly_agent/test/mock_data/petstore.yaml,sha256=CCdliJky04Az4FIOkFA883uunwFDHLr2Y8ohtozFi_Q,2714
735
737
  stoobly_agent/test/mock_data/request_show_response.py,sha256=K_a0fP0QT58T8sX9PaM6hqtX1A1depZsqg_GsNPf--k,707
736
- stoobly_agent/test/mock_data/scaffold/docker-compose-assets-service.yml,sha256=TJpkkkSjUlfz3t8Oxsz6Ery1JX7Ed79Fh6oiEuKI4i0,275
737
- stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=_tCvHxpIKcKMWKrtTvJgqSfkzcaNvJN1w7Yb9OUOzLU,241
738
+ stoobly_agent/test/mock_data/scaffold/docker-compose-assets-service.yml,sha256=m2eN4XZLHnUMDq9wub34Yv9rh8GUWOUgJjY6U3wAljQ,220
739
+ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1WGJJzb8tO13RH5toF4g8km0vItksnO2RIjE36nnp-8,185
738
740
  stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
739
741
  stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
740
742
  stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
741
- stoobly_agent-1.6.2.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
742
- stoobly_agent-1.6.2.dist-info/METADATA,sha256=FcqFwOK8OzH1zHTeVf-dAQ0VPOiUsJJvXw4CY0j4H00,3087
743
- stoobly_agent-1.6.2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
744
- stoobly_agent-1.6.2.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
745
- stoobly_agent-1.6.2.dist-info/RECORD,,
743
+ stoobly_agent-1.6.3.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
744
+ stoobly_agent-1.6.3.dist-info/METADATA,sha256=gy2wgmF4gEAbx1NFjr9JegRICSa-SzamtmE4JdUxYOE,3087
745
+ stoobly_agent-1.6.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
746
+ stoobly_agent-1.6.3.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
747
+ stoobly_agent-1.6.3.dist-info/RECORD,,