flowmesh-cli-stack 0.1.1__tar.gz → 0.1.3rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {flowmesh_cli_stack-0.1.1/src/flowmesh_cli_stack.egg-info → flowmesh_cli_stack-0.1.3rc1}/PKG-INFO +3 -3
  2. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/pyproject.toml +3 -3
  3. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/assets/.env.example +16 -4
  4. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/assets/compose.yml +4 -0
  5. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/env_schema.py +38 -4
  6. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/stack.py +2 -0
  7. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/utils.py +24 -0
  8. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/worker.py +23 -1
  9. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1/src/flowmesh_cli_stack.egg-info}/PKG-INFO +3 -3
  10. flowmesh_cli_stack-0.1.3rc1/src/flowmesh_cli_stack.egg-info/requires.txt +5 -0
  11. flowmesh_cli_stack-0.1.1/src/flowmesh_cli_stack.egg-info/requires.txt +0 -5
  12. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/LICENSE +0 -0
  13. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/README.md +0 -0
  14. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/setup.cfg +0 -0
  15. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/__init__.py +0 -0
  16. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/assets/docker-bake.hcl +0 -0
  17. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack/bundle.py +0 -0
  18. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack.egg-info/SOURCES.txt +0 -0
  19. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack.egg-info/dependency_links.txt +0 -0
  20. {flowmesh_cli_stack-0.1.1 → flowmesh_cli_stack-0.1.3rc1}/src/flowmesh_cli_stack.egg-info/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowmesh-cli-stack
3
- Version: 0.1.1
3
+ Version: 0.1.3rc1
4
4
  Summary: FlowMesh CLI stack commands
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.12
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: flowmesh-cli==0.1.1
10
- Requires-Dist: flowmesh-sdk-stack==0.1.1
9
+ Requires-Dist: flowmesh-cli==0.1.3rc1
10
+ Requires-Dist: flowmesh-sdk-stack==0.1.3rc1
11
11
  Requires-Dist: docker>=7.1.0
12
12
  Requires-Dist: packaging>=24
13
13
  Requires-Dist: pyyaml>=6.0.2
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flowmesh-cli-stack"
7
- version = "0.1.1"
7
+ version = "0.1.3rc1"
8
8
  description = "FlowMesh CLI stack commands"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
11
11
  license = "Apache-2.0"
12
12
  license-files = ["LICENSE"]
13
13
  dependencies = [
14
- "flowmesh-cli==0.1.1",
15
- "flowmesh-sdk-stack==0.1.1",
14
+ "flowmesh-cli==0.1.3rc1",
15
+ "flowmesh-sdk-stack==0.1.3rc1",
16
16
  "docker>=7.1.0",
17
17
  "packaging>=24",
18
18
  "pyyaml>=6.0.2",
@@ -93,6 +93,13 @@ SSH_DEFAULT_IDLE_SEC=
93
93
  SSH_MAX_TTL_SEC=
94
94
  SSH_POLL_INTERVAL_SEC=
95
95
  SSH_STOP_TIMEOUT_SEC=
96
+ SSH_MAX_CPU=
97
+ SSH_MAX_MEMORY=
98
+ SSH_MAX_PIDS=
99
+ # Whether to apply requested GPU limits to SSH tasks.
100
+ # If false, SSH tasks are allocated all available GPUs
101
+ # regardless of their resource requests.
102
+ ENABLE_SSH_GPU_LIMIT=false
96
103
 
97
104
  # ==== General Settings ====
98
105
  TZ=Asia/Singapore
@@ -110,6 +117,7 @@ TASK_MERGE_MAX_BATCH_SIZE=4
110
117
  ENABLE_CONTEXT_REUSE=true
111
118
  WORKER_CACHE_TTL_SEC=3600
112
119
  ENABLE_STAGE_WEIGHT_STICKINESS=false
120
+ TASK_NO_WORKER_GRACE_SEC=60
113
121
  ENABLE_WORKER_WATCHDOG=true
114
122
  WORKER_DEATH_CHECK_INTERVAL=30
115
123
  WORKER_DEATH_GRACE_SEC=60
@@ -166,11 +174,15 @@ NEBULA_API_TOKEN=
166
174
 
167
175
  # ==== External Plugins ====
168
176
  # Plugins are Python packages dropped under FLOWMESH_PLUGIN_DIR
169
- # (host-mounted to /app/plugins on the server) and selected by
170
- # FLOWMESH_PLUGINS as a comma-separated list of top-level module
171
- # names. Each named module must expose `install()` returning a
172
- # `HookBindings`. Leave both empty unless you ship a plugin.
177
+ # (read-only at /app/plugins) and selected by FLOWMESH_PLUGINS as
178
+ # a comma-separated list of top-level module names. Each must
179
+ # expose `install()` returning a `HookBindings`.
180
+ # FLOWMESH_PLUGIN_DATA_DIR is writable at /app/plugin-data for
181
+ # plugin state. Leave all empty unless you ship a plugin.
173
182
  FLOWMESH_PLUGIN_DIR=./plugins
183
+ # A path (`./x`, `/abs/x`) -> host bind-mount (auto-created).
184
+ # A bare name -> external Docker volume of that name.
185
+ FLOWMESH_PLUGIN_DATA_DIR=./plugin-data
174
186
  FLOWMESH_PLUGINS=
175
187
 
176
188
  # ==== Agent Executor (youtu-agent / utu) ====
@@ -166,6 +166,7 @@ services:
166
166
  - /var/run/docker.sock:/var/run/docker.sock
167
167
  - ${SERVER_WORKER_CONFIG:-./configs/worker_config.yaml}:/etc/flowmesh/worker_config.yaml:ro
168
168
  - ${FLOWMESH_PLUGIN_DIR:-./plugins}:/app/plugins:ro
169
+ - ${FLOWMESH_PLUGIN_DATA_DIR:-./plugin-data}:/app/plugin-data
169
170
  - ${REDIS_TLS_DIR:-./secrets/tls/redis}:/etc/ssl/redis:ro
170
171
  - ${SERVER_TLS_DIR:-./secrets/tls/server}:/etc/ssl/server:ro
171
172
  restart: unless-stopped
@@ -199,3 +200,6 @@ volumes:
199
200
  name: ${FLOWMESH_STACK_SLUG:-flowmesh_node}_metrics
200
201
  flowmesh_server_logs:
201
202
  name: ${FLOWMESH_STACK_SLUG:-flowmesh_node}_server_logs
203
+ flowmesh_plugin_data:
204
+ external: true
205
+ name: ${FLOWMESH_PLUGIN_DATA_VOLUME:-${FLOWMESH_STACK_SLUG:-flowmesh_node}_plugin_data}
@@ -301,6 +301,24 @@ STACK_ENV_SCHEMA = EnvSchema(
301
301
  EnvVar("SSH_MAX_TTL_SEC", var_type=EnvVarType.FLOAT, min_value=0),
302
302
  EnvVar("SSH_POLL_INTERVAL_SEC", var_type=EnvVarType.FLOAT, min_value=0),
303
303
  EnvVar("SSH_STOP_TIMEOUT_SEC", var_type=EnvVarType.FLOAT, min_value=0),
304
+ EnvVar(
305
+ "SSH_MAX_CPU",
306
+ var_type=EnvVarType.FLOAT,
307
+ min_value=0,
308
+ min_inclusive=False,
309
+ ),
310
+ EnvVar("SSH_MAX_MEMORY"),
311
+ EnvVar("SSH_MAX_PIDS", var_type=EnvVarType.INT, min_value=1),
312
+ EnvVar(
313
+ "ENABLE_SSH_GPU_LIMIT",
314
+ "false",
315
+ var_type=EnvVarType.BOOL,
316
+ description=[
317
+ "Whether to apply requested GPU limits to SSH tasks.",
318
+ "If false, SSH tasks are allocated all available GPUs",
319
+ "regardless of their resource requests.",
320
+ ],
321
+ ),
304
322
  ],
305
323
  ),
306
324
  EnvSection(
@@ -370,6 +388,12 @@ STACK_ENV_SCHEMA = EnvSchema(
370
388
  "false",
371
389
  var_type=EnvVarType.BOOL,
372
390
  ),
391
+ EnvVar(
392
+ "TASK_NO_WORKER_GRACE_SEC",
393
+ "60",
394
+ var_type=EnvVarType.INT,
395
+ min_value=0,
396
+ ),
373
397
  EnvVar("ENABLE_WORKER_WATCHDOG", "true", var_type=EnvVarType.BOOL),
374
398
  EnvVar(
375
399
  "WORKER_DEATH_CHECK_INTERVAL",
@@ -512,10 +536,11 @@ STACK_ENV_SCHEMA = EnvSchema(
512
536
  title="External Plugins",
513
537
  description=[
514
538
  "Plugins are Python packages dropped under FLOWMESH_PLUGIN_DIR ",
515
- "(host-mounted to /app/plugins on the server) and selected by ",
516
- "FLOWMESH_PLUGINS as a comma-separated list of top-level module ",
517
- "names. Each named module must expose `install()` returning a ",
518
- "`HookBindings`. Leave both empty unless you ship a plugin.",
539
+ "(read-only at /app/plugins) and selected by FLOWMESH_PLUGINS as ",
540
+ "a comma-separated list of top-level module names. Each must ",
541
+ "expose `install()` returning a `HookBindings`. ",
542
+ "FLOWMESH_PLUGIN_DATA_DIR is writable at /app/plugin-data for ",
543
+ "plugin state. Leave all empty unless you ship a plugin.",
519
544
  ],
520
545
  vars=[
521
546
  EnvVar(
@@ -525,6 +550,15 @@ STACK_ENV_SCHEMA = EnvSchema(
525
550
  use_default=True,
526
551
  ensure_path="create",
527
552
  ),
553
+ EnvVar(
554
+ "FLOWMESH_PLUGIN_DATA_DIR",
555
+ "./plugin-data",
556
+ use_default=True,
557
+ description=[
558
+ "A path (`./x`, `/abs/x`) -> host bind-mount (auto-created).",
559
+ "A bare name -> external Docker volume of that name.",
560
+ ],
561
+ ),
528
562
  EnvVar("FLOWMESH_PLUGINS", ""),
529
563
  ],
530
564
  ),
@@ -35,6 +35,7 @@ from .env_schema import STACK_ENV_SCHEMA, deploy_overrides, role_overrides
35
35
  from .utils import (
36
36
  DEFAULT_ENV_FILE,
37
37
  STACK_PATH_KEYS,
38
+ apply_plugin_data_env,
38
39
  apply_stack_resource_env,
39
40
  ensure_deploy_paths,
40
41
  parse_node_role,
@@ -58,6 +59,7 @@ def _stack() -> DockerComposeStack:
58
59
  except ValueError as exc:
59
60
  logging.error(str(exc))
60
61
  raise typer.Exit(code=1)
62
+ apply_plugin_data_env(Path.cwd())
61
63
 
62
64
  return DockerComposeStack(
63
65
  compose_file=stack_compose_file(),
@@ -57,6 +57,22 @@ def apply_stack_resource_env() -> None:
57
57
  os.environ[WORKER_RESULTS_DIR_ENV] = results_volume
58
58
 
59
59
 
60
+ _PLUGIN_DATA_PATH_PREFIXES = ("/", "./", "../", "~")
61
+ _PLUGIN_DATA_ALIAS = "flowmesh_plugin_data"
62
+ _PLUGIN_DATA_DEFAULT = "./plugin-data"
63
+ _PLUGIN_DATA_VOLUME_ENV = "FLOWMESH_PLUGIN_DATA_VOLUME"
64
+
65
+
66
+ def apply_plugin_data_env(base_dir: Path) -> None:
67
+ raw = os.environ.get("FLOWMESH_PLUGIN_DATA_DIR", "").strip()
68
+ if not raw or raw.startswith(_PLUGIN_DATA_PATH_PREFIXES):
69
+ resolved = resolve_path(raw, default=_PLUGIN_DATA_DEFAULT, base_dir=base_dir)
70
+ os.environ["FLOWMESH_PLUGIN_DATA_DIR"] = resolved.as_posix()
71
+ else:
72
+ os.environ[_PLUGIN_DATA_VOLUME_ENV] = raw
73
+ os.environ["FLOWMESH_PLUGIN_DATA_DIR"] = _PLUGIN_DATA_ALIAS
74
+
75
+
60
76
  def stack_compose_file() -> Path:
61
77
  return asset_path("flowmesh_cli_stack.assets", "compose.yml")
62
78
 
@@ -118,6 +134,14 @@ def ensure_deploy_paths(base_dir: Path) -> None:
118
134
  base_dir=base_dir,
119
135
  )
120
136
  )
137
+ if not os.environ.get(_PLUGIN_DATA_VOLUME_ENV):
138
+ ensure_dir(
139
+ resolve_path(
140
+ os.getenv("FLOWMESH_PLUGIN_DATA_DIR", ""),
141
+ default=_PLUGIN_DATA_DEFAULT,
142
+ base_dir=base_dir,
143
+ )
144
+ )
121
145
 
122
146
 
123
147
  def parse_node_role(raw: str) -> NodeRole:
@@ -18,7 +18,13 @@ from flowmesh_stack.workers import (
18
18
  select_worker_images,
19
19
  )
20
20
 
21
- from .utils import DEFAULT_ENV_FILE, STACK_PATH_KEYS, stack_node_client
21
+ from .utils import (
22
+ DEFAULT_ENV_FILE,
23
+ STACK_PATH_KEYS,
24
+ STACK_SLUG_ENV,
25
+ stack_node_client,
26
+ stack_resource_env_overrides,
27
+ )
22
28
 
23
29
  app = get_typer(help="Create and manage workers on the local node.")
24
30
 
@@ -74,6 +80,15 @@ def worker_up(
74
80
  "Repeat --config-raw to provide multiple configs."
75
81
  ),
76
82
  ),
83
+ name_template: str | None = typer.Option(
84
+ None,
85
+ "--name-template",
86
+ help=(
87
+ "Worker name template for cpu/gpu presets. Placeholders: "
88
+ "{slug}, {kind}, {idx}, {gpu}. "
89
+ "Default: '{slug}_worker_{kind}_{idx|gpu}'."
90
+ ),
91
+ ),
77
92
  env_file: Path = typer.Option(
78
93
  DEFAULT_ENV_FILE, "--env-file", help="Env file to load defaults"
79
94
  ),
@@ -84,6 +99,11 @@ def worker_up(
84
99
  ) -> None:
85
100
  """Create and start one or more workers from presets or a custom config file."""
86
101
  client = stack_node_client(env_file, base_url, token or None)
102
+ try:
103
+ slug = stack_resource_env_overrides(os.environ)[STACK_SLUG_ENV]
104
+ except ValueError as exc:
105
+ logging.error(str(exc))
106
+ raise typer.Exit(code=1)
87
107
  logging.info("Creating workers...")
88
108
  try:
89
109
  created = create_workers(
@@ -93,6 +113,8 @@ def worker_up(
93
113
  targets=targets,
94
114
  config_paths=config,
95
115
  config_raw=config_raw,
116
+ name_template=name_template,
117
+ slug=slug,
96
118
  )
97
119
  except FlowMeshError as exc:
98
120
  logging.error(str(exc))
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowmesh-cli-stack
3
- Version: 0.1.1
3
+ Version: 0.1.3rc1
4
4
  Summary: FlowMesh CLI stack commands
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.12
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
- Requires-Dist: flowmesh-cli==0.1.1
10
- Requires-Dist: flowmesh-sdk-stack==0.1.1
9
+ Requires-Dist: flowmesh-cli==0.1.3rc1
10
+ Requires-Dist: flowmesh-sdk-stack==0.1.3rc1
11
11
  Requires-Dist: docker>=7.1.0
12
12
  Requires-Dist: packaging>=24
13
13
  Requires-Dist: pyyaml>=6.0.2
@@ -0,0 +1,5 @@
1
+ flowmesh-cli==0.1.3rc1
2
+ flowmesh-sdk-stack==0.1.3rc1
3
+ docker>=7.1.0
4
+ packaging>=24
5
+ pyyaml>=6.0.2
@@ -1,5 +0,0 @@
1
- flowmesh-cli==0.1.1
2
- flowmesh-sdk-stack==0.1.1
3
- docker>=7.1.0
4
- packaging>=24
5
- pyyaml>=6.0.2