@devrouter/cli 0.0.37 → 0.0.39

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.
package/README.md CHANGED
@@ -70,6 +70,13 @@ devrouter ensure .
70
70
  devrouter exec . -- pnpm seed
71
71
  ```
72
72
 
73
+ Managed repositories may define independent profiles for routed apps, optional
74
+ Compose capabilities, and repository-owned processes. Use
75
+ `devrouter ensure . --profile <name>` for a task-specific environment; native
76
+ Dev Container clients still see the full source configuration. Check
77
+ `devrouter status` or `devrouter doctor` when you need the desired, active, and
78
+ drift state.
79
+
73
80
  Use devrouter lifecycle commands for managed environments. Raw `devpod up`,
74
81
  `stop`, or `delete` bypass ownership locks and exact checkout validation.
75
82
 
@@ -5,6 +5,8 @@ usage() {
5
5
  cat <<'EOF'
6
6
  Usage:
7
7
  devrouter-process ensure --name <name> --match <regex> [options] -- <command> [args...]
8
+ devrouter-process stop --name <name>
9
+ devrouter-process status --name <name>
8
10
 
9
11
  Options:
10
12
  --fingerprint <value> Runtime identity. Defaults to command, workspace, adapter, and safe allowlisted environment identity.
@@ -25,11 +27,16 @@ require_value() {
25
27
  [ "$#" -ge 2 ] || die "$1 requires a value."
26
28
  }
27
29
 
28
- [ "${1:-}" = "ensure" ] || {
30
+ action="${1:-}"
31
+ case "$action" in
32
+ ensure|stop|status)
33
+ shift
34
+ ;;
35
+ *)
29
36
  usage >&2
30
37
  exit 1
31
- }
32
- shift
38
+ ;;
39
+ esac
33
40
 
34
41
  name=""
35
42
  process_match=""
@@ -44,16 +51,19 @@ while [ "$#" -gt 0 ]; do
44
51
  shift 2
45
52
  ;;
46
53
  --match)
54
+ [ "$action" = "ensure" ] || die "--match is only valid for ensure."
47
55
  require_value "$@"
48
56
  process_match="$2"
49
57
  shift 2
50
58
  ;;
51
59
  --fingerprint)
60
+ [ "$action" = "ensure" ] || die "--fingerprint is only valid for ensure."
52
61
  require_value "$@"
53
62
  fingerprint="$2"
54
63
  shift 2
55
64
  ;;
56
65
  --log)
66
+ [ "$action" = "ensure" ] || die "--log is only valid for ensure."
57
67
  require_value "$@"
58
68
  log_file="$2"
59
69
  shift 2
@@ -73,17 +83,26 @@ while [ "$#" -gt 0 ]; do
73
83
  done
74
84
 
75
85
  [[ "$name" =~ ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]] || die "--name must be a safe identifier."
76
- [ -n "$process_match" ] || die "--match is required."
77
- [ "$#" -gt 0 ] || die "A command is required after --."
78
86
  [ -r "/proc/$$/environ" ] || die "Linux /proc process metadata is required."
79
87
 
80
- for tool in awk flock grep pgrep ps setsid sha256sum sort tr; do
88
+ if [ "$action" = "ensure" ]; then
89
+ [ -n "$process_match" ] || die "--match is required."
90
+ [ "$#" -gt 0 ] || die "A command is required after --."
91
+ fi
92
+
93
+ required_tools=(awk flock grep pgrep ps tr)
94
+ if [ "$action" = "ensure" ]; then
95
+ required_tools+=(setsid sha256sum sort)
96
+ fi
97
+ for tool in "${required_tools[@]}"; do
81
98
  command -v "$tool" >/dev/null 2>&1 || die "Required command is unavailable: $tool"
82
99
  done
83
100
 
84
- match_status=0
85
- pgrep -f -- "$process_match" >/dev/null 2>&1 || match_status=$?
86
- [ "$match_status" -le 1 ] || die "--match is not a valid process regular expression."
101
+ if [ "$action" = "ensure" ]; then
102
+ match_status=0
103
+ pgrep -f -- "$process_match" >/dev/null 2>&1 || match_status=$?
104
+ [ "$match_status" -le 1 ] || die "--match is not a valid process regular expression."
105
+ fi
87
106
 
88
107
  fingerprint_env_names=()
89
108
  fingerprint_env_list="${DEVROUTER_PROCESS_FINGERPRINT_ENV:-}"
@@ -107,6 +126,10 @@ if [[ -v DEVROUTER_PROCESS_ADAPTER_SHA256 ]] &&
107
126
  die "DEVROUTER_PROCESS_ADAPTER_SHA256 must be a SHA-256 digest."
108
127
  fi
109
128
 
129
+ if [ "$action" != "ensure" ] && [ "$#" -gt 0 ]; then
130
+ die "Unexpected arguments for $action."
131
+ fi
132
+
110
133
  emit_environment_field() {
111
134
  local field="$1"
112
135
  local variable="$2"
@@ -232,6 +255,10 @@ unknown_process_exists() {
232
255
  marked_process_exists || matching_process_exists
233
256
  }
234
257
 
258
+ report_status() {
259
+ printf '{"name":"%s","status":"%s"}\n' "$name" "$1"
260
+ }
261
+
235
262
  stop_process_group() {
236
263
  local pid="$1"
237
264
  local pgid="$2"
@@ -263,6 +290,67 @@ mkdir -p "$state_dir" "$(dirname "$log_file")"
263
290
  exec 9>"$lock_file"
264
291
  flock -w "$lock_timeout" 9 || die "Timed out waiting for the '$name' process lock."
265
292
 
293
+ if [ "$action" = "status" ]; then
294
+ if [ ! -f "$state_file" ]; then
295
+ if marked_process_exists; then
296
+ report_status foreign
297
+ exit 2
298
+ fi
299
+ report_status stopped
300
+ exit 0
301
+ fi
302
+
303
+ read -r pid pgid stored_fingerprint extra <"$state_file" || true
304
+ if [ -z "$stored_fingerprint" ] ||
305
+ ! [[ "$pid" =~ ^[1-9][0-9]*$ ]] ||
306
+ ! [[ "$pgid" =~ ^[1-9][0-9]*$ ]] ||
307
+ [ -n "$extra" ]; then
308
+ report_status drifted
309
+ exit 2
310
+ fi
311
+ if process_alive "$pid"; then
312
+ if process_owned "$pid" "$pgid" "$stored_fingerprint"; then
313
+ report_status running
314
+ else
315
+ report_status foreign
316
+ exit 2
317
+ fi
318
+ exit 0
319
+ fi
320
+ report_status drifted
321
+ exit 2
322
+ fi
323
+
324
+ if [ "$action" = "stop" ]; then
325
+ if [ ! -f "$state_file" ]; then
326
+ if marked_process_exists; then
327
+ die "Found an unowned '$name' process; refusing to stop it."
328
+ fi
329
+ echo "[devrouter-process] '$name' is already stopped."
330
+ exit 0
331
+ fi
332
+
333
+ read -r pid pgid stored_fingerprint extra <"$state_file" || true
334
+ if [ -z "$stored_fingerprint" ] ||
335
+ ! [[ "$pid" =~ ^[1-9][0-9]*$ ]] ||
336
+ ! [[ "$pgid" =~ ^[1-9][0-9]*$ ]] ||
337
+ [ -n "$extra" ]; then
338
+ die "Invalid state for '$name'; refusing to stop it."
339
+ fi
340
+ if process_alive "$pid"; then
341
+ process_owned "$pid" "$pgid" "$stored_fingerprint" ||
342
+ die "State points at an unowned '$name' process; refusing to stop it."
343
+ stop_process_group "$pid" "$pgid"
344
+ elif process_group_alive "$pgid"; then
345
+ die "State points at a live unowned '$name' process group; refusing to stop it."
346
+ elif marked_process_exists; then
347
+ die "Found an unowned '$name' process; refusing to stop it."
348
+ fi
349
+ rm -f "$state_file"
350
+ echo "[devrouter-process] '$name' is stopped."
351
+ exit 0
352
+ fi
353
+
266
354
  pid=""
267
355
  pgid=""
268
356
  stored_fingerprint=""