switchroom 0.8.1 → 0.10.0

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 (105) hide show
  1. package/README.md +49 -57
  2. package/bin/timezone-hook.sh +9 -7
  3. package/dist/agent-scheduler/index.js +285 -45
  4. package/dist/auth-broker/index.js +13932 -0
  5. package/dist/cli/switchroom.js +15931 -12778
  6. package/dist/host-control/main.js +582 -43
  7. package/dist/vault/approvals/kernel-server.js +276 -47
  8. package/dist/vault/broker/server.js +333 -69
  9. package/examples/minimal.yaml +63 -0
  10. package/examples/personal-google-workspace-mcp/.env.example +34 -0
  11. package/examples/personal-google-workspace-mcp/README.md +194 -0
  12. package/examples/personal-google-workspace-mcp/compose.yaml +66 -0
  13. package/examples/switchroom.yaml +220 -0
  14. package/package.json +6 -4
  15. package/profiles/_base/start.sh.hbs +3 -3
  16. package/profiles/_shared/agent-self-service.md.hbs +126 -0
  17. package/profiles/default/CLAUDE.md +10 -0
  18. package/profiles/default/CLAUDE.md.hbs +16 -0
  19. package/skills/buildkite-agent-infrastructure/SKILL.md +30 -11
  20. package/skills/buildkite-agent-runtime/SKILL.md +44 -11
  21. package/skills/buildkite-api/SKILL.md +31 -8
  22. package/skills/buildkite-cli/SKILL.md +27 -9
  23. package/skills/buildkite-migration/SKILL.md +22 -9
  24. package/skills/buildkite-pipelines/SKILL.md +26 -9
  25. package/skills/buildkite-secure-delivery/SKILL.md +23 -9
  26. package/skills/buildkite-test-engine/SKILL.md +25 -8
  27. package/skills/docx/SKILL.md +1 -1
  28. package/skills/file-bug/SKILL.md +34 -6
  29. package/skills/humanizer/SKILL.md +15 -0
  30. package/skills/humanizer-calibrate/SKILL.md +7 -1
  31. package/skills/mcp-builder/SKILL.md +1 -1
  32. package/skills/pdf/SKILL.md +1 -1
  33. package/skills/pptx/SKILL.md +1 -1
  34. package/skills/skill-creator/SKILL.md +21 -1
  35. package/skills/skill-creator/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  36. package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-313.pyc +0 -0
  37. package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-313.pyc +0 -0
  38. package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-313.pyc +0 -0
  39. package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-313.pyc +0 -0
  40. package/skills/skill-creator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
  41. package/skills/switchroom-cli/SKILL.md +63 -64
  42. package/skills/switchroom-health/SKILL.md +23 -10
  43. package/skills/switchroom-install/SKILL.md +3 -3
  44. package/skills/switchroom-manage/SKILL.md +26 -19
  45. package/skills/switchroom-runtime/SKILL.md +67 -15
  46. package/skills/switchroom-status/SKILL.md +26 -1
  47. package/skills/telegram-test-harness/SKILL.md +3 -0
  48. package/skills/webapp-testing/SKILL.md +31 -1
  49. package/skills/xlsx/SKILL.md +1 -1
  50. package/telegram-plugin/admin-commands/index.ts +7 -5
  51. package/telegram-plugin/dist/gateway/gateway.js +13042 -12844
  52. package/telegram-plugin/gateway/auth-add-flow.ts +326 -0
  53. package/telegram-plugin/gateway/auth-broker-client.ts +75 -0
  54. package/telegram-plugin/gateway/auth-command.ts +794 -0
  55. package/telegram-plugin/gateway/auth-line.ts +123 -0
  56. package/telegram-plugin/gateway/boot-card.ts +22 -36
  57. package/telegram-plugin/gateway/boot-probes.ts +3 -3
  58. package/telegram-plugin/gateway/gateway.ts +313 -798
  59. package/telegram-plugin/gateway/hostd-dispatch.ts +117 -0
  60. package/telegram-plugin/hooks/tool-label-pretool.mjs +11 -0
  61. package/telegram-plugin/hooks/wedge-detect-posttool.mjs +303 -0
  62. package/telegram-plugin/permission-title.ts +56 -0
  63. package/telegram-plugin/quota-check.ts +19 -41
  64. package/telegram-plugin/scripts/build.mjs +0 -1
  65. package/telegram-plugin/shared/bot-runtime.ts +5 -4
  66. package/telegram-plugin/tests/auth-add-flow.test.ts +559 -0
  67. package/telegram-plugin/tests/auth-code-redact.test.ts +8 -4
  68. package/telegram-plugin/tests/auth-command-vernacular.test.ts +531 -0
  69. package/telegram-plugin/tests/boot-probes.test.ts +11 -4
  70. package/telegram-plugin/tests/hostd-dispatch.test.ts +129 -0
  71. package/telegram-plugin/tests/permission-title.test.ts +31 -0
  72. package/telegram-plugin/tests/quota-check.test.ts +5 -35
  73. package/telegram-plugin/uat/SETUP.md +31 -1
  74. package/telegram-plugin/uat/runners/agent-self-sufficiency.ts +457 -0
  75. package/telegram-plugin/uat/runners/paraphrases.ts +231 -0
  76. package/telegram-plugin/uat/runners/report.ts +150 -0
  77. package/telegram-plugin/uat/runners/run-agent-self-sufficiency.sh +50 -0
  78. package/telegram-plugin/uat/runners/scorer.test.ts +196 -0
  79. package/telegram-plugin/uat/runners/scorer.ts +106 -0
  80. package/telegram-plugin/uat/runners/skill-coverage.test.ts +100 -0
  81. package/telegram-plugin/uat/runners/skill-coverage.ts +620 -0
  82. package/telegram-plugin/uat/scenarios/jtbd-interrupt-marker-dm.test.ts +7 -1
  83. package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +7 -1
  84. package/telegram-plugin/auth-dashboard.ts +0 -1104
  85. package/telegram-plugin/auth-slot-parser.ts +0 -497
  86. package/telegram-plugin/dist/foreman/foreman.js +0 -31358
  87. package/telegram-plugin/foreman/foreman-create-flow.ts +0 -202
  88. package/telegram-plugin/foreman/foreman-handlers.ts +0 -493
  89. package/telegram-plugin/foreman/foreman.ts +0 -1165
  90. package/telegram-plugin/foreman/setup-flow.ts +0 -345
  91. package/telegram-plugin/foreman/setup-state.ts +0 -239
  92. package/telegram-plugin/foreman/state.ts +0 -203
  93. package/telegram-plugin/tests/auth-account-identity-surface.test.ts +0 -118
  94. package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +0 -260
  95. package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +0 -140
  96. package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +0 -559
  97. package/telegram-plugin/tests/auth-dashboard.test.ts +0 -1045
  98. package/telegram-plugin/tests/auth-slot-commands.test.ts +0 -640
  99. package/telegram-plugin/tests/boot-card-account-quota.test.ts +0 -137
  100. package/telegram-plugin/tests/foreman-create-flow.test.ts +0 -359
  101. package/telegram-plugin/tests/foreman-handlers.test.ts +0 -347
  102. package/telegram-plugin/tests/foreman-state.test.ts +0 -164
  103. package/telegram-plugin/tests/foreman-write-ops.test.ts +0 -214
  104. package/telegram-plugin/tests/setup-flow.test.ts +0 -510
  105. package/telegram-plugin/tests/setup-state.test.ts +0 -146
package/README.md CHANGED
@@ -60,7 +60,7 @@ So I built this.
60
60
  | Feature | What it does |
61
61
  |---|---|
62
62
  | **Progress cards** | Pinned, in-place, every tool call visible. The headline UX. |
63
- | **Claude Pro/Max auth** | OAuth, not API keys. No per-token billing. Multi-account fallback pool per agent. |
63
+ | **Claude Pro/Max auth** | OAuth, not API keys. No per-token billing. Fleet-wide active account + fallback order; broker-owned refresh and credential fanout. |
64
64
  | **Approval kernel** | Inline allow/deny cards in Telegram for every gated tool. TTL'd grants, full audit trail. |
65
65
  | **Sub-agents** | Opus plans, Sonnet implements. Sub-agent work surfaces in the parent card. |
66
66
  | **Config cascade** | Defaults, then profiles, then per-agent YAML. Change one line, every agent updates. |
@@ -119,7 +119,7 @@ Each agent is a long-running service. They survive reboots, network drops, and y
119
119
  - **Auto-restart.** Agent containers come up with `restart: unless-stopped`, and each service has a healthcheck — a crashed or wedged agent is brought back automatically. No silent dropped work.
120
120
  - **Resume protocol.** When an agent reboots mid-turn, `start.sh` exports `SWITCHROOM_PENDING_TURN=true` plus the original chat / message ids. The agent's first action on boot is to acknowledge the gap and ask the user how to proceed (start over, summarise and continue, or drop it).
121
121
  - **Wake-audit.** On every fresh boot the agent checks for owed replies, orphan sub-agents, and stale in-progress todos. If everything's clean it stays quiet. If it owed you a reply, it tells you.
122
- - **Token refresh.** Runs unattended for weeks via a `refresh-tick` daemon. Multi-account fallback pool kicks in when the active slot hits its quota window.
122
+ - **Token refresh.** The `switchroom-auth-broker` daemon owns the refresh loop and is the sole writer of every `credentials.json`. Per-account quota state fans out across the fleet in seconds; `auth.fallback_order` cycles when an account is exhausted.
123
123
 
124
124
  ## How it stacks up
125
125
 
@@ -138,75 +138,62 @@ The wedge against OpenClaw and NanoClaw isn't the substrate — it's the stock `
138
138
 
139
139
  ## Install
140
140
 
141
- Runs on the box you already have. The supported production runtime is Linux + Docker (Ubuntu 24.04 LTS with 4GB RAM is the canonical target; other Linux distros work with minor tweaks). `switchroom apply` scaffolds every agent and writes a `docker-compose.yml` from your `switchroom.yaml`; you bring the fleet up yourself with `docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d`. Five published images on GHCR (`switchroom-base`, `switchroom-agent`, `switchroom-broker`, `switchroom-kernel`, `switchroom-scheduler`) — no `docker build` on the operator's host. macOS (Docker Desktop) works for development but is not yet release-validated.
141
+ Runs on the box you already have. The supported production runtime is Linux + Docker. **Canonical target: Ubuntu 24.04 LTS with ≥4 GiB RAM** (8 GiB recommended once you run more than one agent). Other Debian-derivatives work with the same script; non-apt distros need a manual prereq install. macOS (Docker Desktop) works for development but is not yet release-validated.
142
142
 
143
- > **Heads up on the package name.** The npm package was originally `switchroom-ai`. It's now just `switchroom`. The old name is deprecated and will stop receiving updates — `npm install -g switchroom` is the current path.
143
+ > **Full new-user walkthrough [`docs/install.md`](docs/install.md).** Zero to first Telegram message in ~15 minutes. Includes the [BotFather walkthrough](docs/botfather-walkthrough.md). Read that first if you're installing from scratch.
144
144
 
145
- ### From inside Claude Code (the on-ramp)
145
+ > **Heads up on the package name.** The npm package was originally `switchroom-ai`. It's now just `switchroom`. The old name is deprecated and will stop receiving updates — `npm install -g switchroom` is the current path.
146
146
 
147
- If you already use Claude Code, this is the shortest path. Inside any session:
147
+ ### Fresh Linux box one script
148
148
 
149
- ```
150
- /plugin marketplace add switchroom/switchroom
151
- /plugin install switchroom@switchroom
152
- /switchroom:setup
149
+ ```bash
150
+ curl -fsSL https://github.com/switchroom/switchroom/raw/main/scripts/install-deps.sh | sudo bash
153
151
  ```
154
152
 
155
- `/switchroom:setup` walks you through deps, `switchroom setup` (Telegram + vault + first agent), and `switchroom agent start`. Day-to-day: `/switchroom:start`, `/switchroom:stop`, `/switchroom:status`. See [`docs/publishing.md`](docs/publishing.md).
153
+ Installs Docker Engine + Compose v2, Node.js 20.11+, Bun, and the `@anthropic-ai/claude-code` + `switchroom` CLIs. Idempotent. Adds the invoking user to the `docker` group. Tested on Ubuntu 24.04 LTS and 26.04 LTS. Warns (does not block) on hosts under 4 GiB RAM.
156
154
 
157
- ### One-liner (static binary)
155
+ Then log out and back in so the docker group takes effect, and:
158
156
 
159
157
  ```bash
160
- curl -fsSL https://github.com/switchroom/switchroom/raw/main/install.sh | sh
158
+ switchroom setup # interactive: Telegram + vault + first agent
159
+ switchroom auth add me --from-oauth # OAuth into your Claude Pro or Max account (one flow, fleet-wide)
160
+ switchroom apply # write ~/.switchroom/compose/docker-compose.yml
161
+ docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
161
162
  ```
162
163
 
163
- Auto-detects your platform (linux / macos) and arch (amd64 / arm64), downloads the matching pre-built binary from the latest [GitHub release](https://github.com/switchroom/switchroom/releases/latest), verifies its SHA256, and drops it in `/usr/local/bin` (or `~/.local/bin` if not writable). Source is [`install.sh`](install.sh).
164
-
165
- The static binary still needs the `claude` CLI to run agents: `npm i -g @anthropic-ai/claude-code` (Node 20.11+).
164
+ After the last command you talk to the agent from Telegram. You don't touch the server again.
166
165
 
167
- **Manual install** if you'd rather not pipe to sh:
166
+ ### Already have Docker + Node 20.11
168
167
 
169
168
  ```bash
170
- # Pick the artifact for your platform/arch from the latest release page
171
- curl -fsSL -o switchroom https://github.com/switchroom/switchroom/releases/latest/download/switchroom-linux-amd64
172
- chmod +x switchroom
173
- sudo mv switchroom /usr/local/bin/
169
+ sudo npm install -g bun @anthropic-ai/claude-code switchroom
170
+ switchroom setup
174
171
  ```
175
172
 
176
- Replace `switchroom-linux-amd64` with `switchroom-linux-arm64`, `switchroom-macos-amd64`, or `switchroom-macos-arm64` as needed. Verify against `switchroom-checksums.txt` from the same release.
173
+ `bun` is a hard runtime dep — the `switchroom` CLI's entrypoint is a Bun script. A Node-only CLI build is on the roadmap but not yet shipped.
177
174
 
178
- **macOS Gatekeeper note.** Releases are not yet Apple-code-signed. After installing on macOS you may need to clear the quarantine xattr so the binary will run: `xattr -d com.apple.quarantine /usr/local/bin/switchroom`. The `install.sh` one-liner handles this automatically.
179
-
180
- **Mac (Sequoia+) one-time.** macOS 15 adds a second-stage notarization check that the `xattr` strip alone does not bypass — you may still see a Gatekeeper "cannot verify the developer" dialog the first time you run `switchroom`. `install.sh` attempts `sudo spctl --add /usr/local/bin/switchroom` automatically (best-effort, ignored if sudo isn't available). If the dialog still fires, run that `spctl --add` manually, or open System Settings → Privacy & Security → "Open Anyway" once.
175
+ ### From inside Claude Code (the on-ramp)
181
176
 
182
- Then:
177
+ If you already use Claude Code, this is the shortest path. Inside any session:
183
178
 
184
- ```bash
185
- switchroom setup # interactive Telegram wiring
186
- switchroom agent create coach --profile health-coach # scaffold your first agent
187
- switchroom auth login coach # link your Pro or Max session
188
- switchroom apply # write docker-compose.yml
189
- docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
179
+ ```
180
+ /plugin marketplace add switchroom/switchroom
181
+ /plugin install switchroom@switchroom
182
+ /switchroom:setup
190
183
  ```
191
184
 
192
- After the last command you talk to the agent from Telegram. You don't touch the server again.
193
-
194
- ### Already have node?
185
+ `/switchroom:setup` walks you through deps, `switchroom setup` (Telegram + vault + first agent), and `switchroom agent start`. Day-to-day: `/switchroom:start`, `/switchroom:stop`, `/switchroom:status`. See [`docs/publishing.md`](docs/publishing.md).
195
186
 
196
- ```bash
197
- npm install -g @anthropic-ai/claude-code switchroom
198
- switchroom setup
199
- ```
187
+ ### Static binary (planned, not yet shipped)
200
188
 
201
- Node 20.11+. `switchroom setup` is the interactive first-time wizard. Scaffolds config, handles Telegram wiring, sets up the vault.
189
+ Pre-built single-binary releases (no Node or Bun required on the host) are scaffolded in [`install.sh`](install.sh) and referenced from the GitHub Releases page, but **the release workflow that publishes those binaries does not yet exist**. Until v0.9, use the one-script or npm paths above. Tracking work: switching the release pipeline to actually upload `switchroom-linux-{amd64,arm64}` and `switchroom-macos-{amd64,arm64}` on every tag.
202
190
 
203
191
  ### One-shot happy path (no wizard)
204
192
 
205
- If you already have Telegram credentials in `~/.switchroom/switchroom.yaml`, skip `switchroom setup`. `agent create --profile` writes a minimal entry for you, and auth is scoped per-agent:
193
+ If you already have Telegram credentials in `~/.switchroom/switchroom.yaml` and one Anthropic account already added, skip `switchroom setup`. `agent create --profile` writes a minimal entry; the new agent inherits the fleet-wide active account automatically — no per-agent OAuth flow:
206
194
 
207
195
  ```bash
208
196
  switchroom agent create coach --profile health-coach
209
- switchroom auth login coach
210
197
  switchroom apply && docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
211
198
  ```
212
199
 
@@ -336,26 +323,29 @@ If the agent is already in yaml, `--profile` must match the existing `extends:`
336
323
 
337
324
  Model aliases: the bare names `opus`, `sonnet`, `haiku` are accepted alongside the full IDs (`claude-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5`). Use whichever reads cleaner in your config.
338
325
 
339
- ### Authentication (multi-account slot pool)
326
+ ### Authentication (one OAuth, many agents)
340
327
 
341
- Each agent has a pool of Claude OAuth slots. The **active** slot is what the agent uses; other slots are automatic fallbacks when the active slot hits its quota window. Every `<slot>` option defaults to the active slot if omitted.
328
+ The **Anthropic account is the unit of authentication.** One OAuth flow per account, then every agent in the fleet inherits the fleet-wide active account. The `switchroom-auth-broker` daemon owns the refresh loop and is the sole writer of every `credentials.json`. Per-account quota state fans out across the fleet in seconds. See [`docs/auth.md`](docs/auth.md) for the full operator guide.
342
329
 
343
330
  ```bash
344
- switchroom auth status # All agents, one table
345
- switchroom auth login <agent> # First-time OAuth into the active slot
346
- switchroom auth code <agent> <browser-code> # Paste the code back from the browser
347
- switchroom auth cancel <agent> # Abandon a pending login
348
- switchroom auth reauth <agent> [--slot <s>] # Fresh OAuth, replace existing token
349
- switchroom auth refresh <agent> # Alias for reauth (back-compat)
350
- switchroom auth refresh-tick [--threshold-ms N] # Daemon: rotate tokens nearing expiry (cron/timer)
351
-
352
- switchroom auth add <agent> [--slot <name>] # Add another account to the fallback pool
353
- switchroom auth use <agent> <slot> # Switch the active slot
354
- switchroom auth list <agent> [--json] # Show slots: health, quota status, expiry
355
- switchroom auth rm <agent> <slot> [--force] # Remove a slot (refuses active/last slot)
331
+ switchroom auth add <label> --from-oauth # New account via OAuth (one flow per Anthropic account)
332
+ switchroom auth add <label> --from-agent <name> # Seed from an existing agent's creds
333
+ switchroom auth add <label> --from-credentials <path> # Import a credentials.json
334
+ switchroom auth add <label> --from-oauth --replace # Re-auth an existing label (drift recovery)
335
+
336
+ switchroom auth list # Accounts + health + which one is fleet-active
337
+ switchroom auth show [agent] # Full snapshot (fleet + agents + consumers), or one agent
338
+ switchroom auth use <label> # Fleet-wide active swap
339
+ switchroom auth rotate # Cycle to next non-exhausted in fallback_order
340
+ switchroom auth rm <label> # Remove an account (refused if it's the only one)
341
+
342
+ switchroom auth agent override <agent> <label> # Edge case: one agent on a different account
343
+ switchroom auth agent override <agent> --clear # Back to fleet active
344
+
345
+ switchroom auth refresh [label] # Diagnostic: force a refresh tick
356
346
  ```
357
347
 
358
- The fallback pool also works from Telegram. The switchroom MCP plugin exposes the same verbs as `/auth add|use|list|rm` inside the chat.
348
+ The same surface is reachable from Telegram in any agent's chat: `/auth show` (read-only), `/auth use <label>`, `/auth rotate`. Mutating verbs are admin-gated against the per-agent `admin: true` flag (the same flag that gates `/agents`, `/restart`, `/update`, etc.). One knob to make an agent the fleet control panel.
359
349
 
360
350
  ### Workspace (agent bootstrap layer)
361
351
 
@@ -416,6 +406,8 @@ Overlay entries win on collision with built-in defaults. Unknown files that appe
416
406
 
417
407
  | Guide | Description |
418
408
  |---|---|
409
+ | **[Install](docs/install.md)** | Zero-to-first-message new-user walkthrough |
410
+ | **[BotFather walkthrough](docs/botfather-walkthrough.md)** | Step-by-step bot creation in Telegram |
419
411
  | **[Changelog](CHANGELOG.md)** | Release notes, every version |
420
412
  | **[Configuration](docs/configuration.md)** | Full field reference, cascade semantics, profiles |
421
413
  | **[Vault](docs/vault.md)** | Architecture, per-cron secrets, ACL, audit log, threat model |
@@ -10,16 +10,18 @@
10
10
  # This hook fires on every UserPromptSubmit and prints a one-line hint that
11
11
  # Claude Code prepends to the prompt as additionalContext, so the LLM sees
12
12
  # fresh local time each turn. The resolved zone is passed in via
13
- # SWITCHROOM_TIMEZONE (baked into the agent's systemd unit at scaffold/
14
- # reconcile time). If unset we fall back to UTC same default as the
15
- # resolver so the hook never fails loudly.
13
+ # SWITCHROOM_TIMEZONE (set in the agent container's `environment:` block by
14
+ # the compose generator at `switchroom apply` timesee
15
+ # src/agents/compose.ts). If unset we fall back to UTC — same default as
16
+ # the resolver — so the hook never fails loudly.
16
17
  #
17
18
  # Stale-install detection: when SWITCHROOM_TIMEZONE is unset we also annotate
18
19
  # the hint with an in-band WARNING. This makes the failure visible to the
19
20
  # agent (and thus to the user), rather than silently emitting "UTC" while the
20
- # operator's real zone is Australia/Melbourne. Typical cause: an install
21
- # upgraded past the timezone-awareness PR without re-running
22
- # `switchroom systemd install` to refresh the unit files.
21
+ # operator's real zone is Australia/Melbourne. Typical cause: the compose
22
+ # env block is stale because `switchroom apply` hasn't run since the
23
+ # operator added a `timezone:` cascade entry (or the agent container was
24
+ # rebuilt from an image that predates the #1198 compose-TZ wiring fix).
23
25
  #
24
26
  # Failure modes are silent: hooks that error block the turn in Claude Code,
25
27
  # and a missing timezone hint is never worse than no hint at all.
@@ -46,7 +48,7 @@ ROUNDED=$(( NOW_UNIX - (NOW_UNIX % 900) ))
46
48
  NOW=$(TZ="$TZ_VAL" date -d "@$ROUNDED" '+%Y-%m-%d %H:%M %Z (UTC%:z)')
47
49
 
48
50
  if [ "$TZ_UNSET" = "1" ]; then
49
- MSG="Current local time: $NOW ($TZ_VAL — WARNING: SWITCHROOM_TIMEZONE unset; systemd unit may be stale, run \`switchroom systemd install\` to refresh)"
51
+ MSG="Current local time: $NOW ($TZ_VAL — WARNING: SWITCHROOM_TIMEZONE unset; compose env may be stale, run \`switchroom apply && switchroom agent restart <agent>\` to refresh)"
50
52
  else
51
53
  MSG="Current local time: $NOW ($TZ_VAL)"
52
54
  fi