@cohortapp/agent-sdk 2.11.15 → 2.13.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.
- package/.env.example +37 -22
- package/README.md +2 -0
- package/bin/maestro.mjs +117 -39
- package/bin/maestro.test.mjs +175 -5
- package/docs/guides/front-door-session.md +313 -0
- package/docs/guides/mac-mini.md +100 -28
- package/docs/guides/org-onboarding.md +1 -1
- package/docs/guides/setup-wizard.md +9 -5
- package/docs/runbooks/cohort-cutover.md +11 -1
- package/docs/runbooks/mac-mini-bootstrap.md +38 -63
- package/lib/cadence-bus-requeue.test.mjs +83 -0
- package/lib/cadence-bus.mjs +43 -7
- package/lib/channels/inbox-item.mjs +59 -2
- package/lib/cli/board.mjs +285 -0
- package/lib/cli/board.test.mjs +227 -0
- package/lib/cli/design.mjs +185 -0
- package/lib/cli/design.test.mjs +270 -0
- package/lib/cli/doctor-checks.mjs +441 -0
- package/lib/cli/doctor-checks.test.mjs +336 -0
- package/lib/cli/global-setup-extras.mjs +454 -0
- package/lib/cli/global-setup-extras.test.mjs +462 -0
- package/lib/cli/inbox.mjs +304 -0
- package/lib/cli/inbox.test.mjs +230 -0
- package/lib/cli/session-ack.mjs +63 -0
- package/lib/cli/session-ack.test.mjs +63 -0
- package/lib/cli/session.mjs +760 -0
- package/lib/cli/session.test.mjs +613 -0
- package/lib/collective/global-config.mjs +209 -6
- package/lib/collective/global-config.test.mjs +145 -0
- package/lib/collective/global-skills.mjs +145 -0
- package/lib/collective/global-skills.test.mjs +126 -0
- package/lib/collective/presence.mjs +4 -3
- package/lib/collective/vendor-skills.mjs +305 -0
- package/lib/collective/vendor-skills.test.mjs +306 -0
- package/lib/comms/send-gate.mjs +115 -0
- package/lib/comms/send-gate.test.mjs +113 -0
- package/lib/design/design-md.mjs +793 -0
- package/lib/design/design-md.test.mjs +318 -0
- package/lib/design/fixtures/DESIGN.golden.md +238 -0
- package/lib/design/fixtures/PRODUCT.golden.md +67 -0
- package/lib/design/fixtures/foundation.json +133 -0
- package/lib/design/refresh-gate.mjs +154 -0
- package/lib/design/refresh-gate.test.mjs +144 -0
- package/lib/design/write.mjs +275 -0
- package/lib/design/write.test.mjs +241 -0
- package/lib/feature-init.mjs +2 -2
- package/lib/mcp/server.test.mjs +9 -4
- package/lib/model-router/spawn.test.mjs +21 -0
- package/lib/org/board-mine-cache.mjs +99 -0
- package/lib/org/board-mine-cache.test.mjs +53 -0
- package/lib/org/board.mjs +11 -0
- package/lib/org/board.test.mjs +11 -1
- package/lib/org/client.mjs +36 -0
- package/lib/org/client.test.mjs +46 -0
- package/lib/org/inbound/directedness.mjs +18 -2
- package/lib/org/inbound/directedness.test.mjs +58 -0
- package/lib/org/inbound/index.mjs +8 -1
- package/lib/org/inbound/index.test.mjs +22 -0
- package/lib/org/mesh-directives.test.mjs +110 -0
- package/lib/org/mesh.mjs +61 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +52 -0
- package/lib/org/protocol.test.mjs +12 -1
- package/lib/org/registry.mjs +3 -2
- package/lib/org/tool-surface.mjs +120 -0
- package/lib/org/tool-surface.test.mjs +118 -5
- package/lib/prompts/parallelism.mjs +79 -0
- package/lib/prompts/parallelism.test.mjs +177 -0
- package/lib/security/external-content.mjs +1 -1
- package/lib/security/external-content.test.mjs +17 -0
- package/lib/session/config.mjs +137 -0
- package/lib/session/config.test.mjs +92 -0
- package/lib/session/feed-core.mjs +229 -0
- package/lib/session/feed-core.test.mjs +198 -0
- package/lib/session/first-run.mjs +126 -0
- package/lib/session/first-run.test.mjs +121 -0
- package/lib/session/frontdoor.mjs +266 -0
- package/lib/session/frontdoor.test.mjs +205 -0
- package/lib/session/handoffs.mjs +295 -0
- package/lib/session/handoffs.test.mjs +183 -0
- package/lib/session/identity.mjs +220 -0
- package/lib/session/identity.test.mjs +180 -0
- package/lib/session/inbox-claims.mjs +434 -0
- package/lib/session/inbox-claims.test.mjs +286 -0
- package/lib/session/launch-args.mjs +161 -0
- package/lib/session/launch-args.test.mjs +157 -0
- package/lib/session/liveness.mjs +174 -0
- package/lib/session/liveness.test.mjs +100 -0
- package/lib/session/status-summary.mjs +172 -0
- package/lib/session/status-summary.test.mjs +118 -0
- package/lib/session-permissions.mjs +39 -3
- package/lib/session-permissions.test.mjs +20 -0
- package/lib/setup/claude-probe.mjs +161 -24
- package/lib/setup/claude-probe.test.mjs +187 -0
- package/lib/setup/sections/learning.mjs +2 -1
- package/lib/setup/sections/model.mjs +104 -24
- package/lib/setup/sections/model.test.mjs +240 -0
- package/lib/setup/sections/org.mjs +27 -2
- package/lib/setup/sections/org.test.mjs +35 -2
- package/lib/setup/sections/verify.mjs +5 -0
- package/lib/setup/state.mjs +30 -10
- package/lib/setup/state.test.mjs +24 -1
- package/lib/singleton.js +11 -3
- package/lib/singleton.test.mjs +16 -0
- package/lib/subagents/lock.mjs +1 -1
- package/lib/telemetry/collect.mjs +270 -6
- package/lib/telemetry/collect.test.mjs +196 -1
- package/lib/upgrade/global-refresh.mjs +108 -0
- package/lib/upgrade/global-refresh.test.mjs +65 -0
- package/lib/upgrade/launchd-reconcile.mjs +327 -0
- package/lib/upgrade/launchd-reconcile.test.mjs +272 -0
- package/lib/upgrade/post-steps.mjs +151 -0
- package/lib/upgrade/post-steps.test.mjs +200 -0
- package/lib/upgrade/verify.mjs +215 -0
- package/lib/upgrade/verify.test.mjs +164 -0
- package/lib/voice/outbound.mjs +3 -2
- package/lib/voice/post-call-brief.mjs +2 -1
- package/lib/voice/session-rotation.mjs +6 -1
- package/lib/voice/session-rotation.test.mjs +114 -0
- package/package.json +3 -3
- package/plugins/maestro-skills/plugin.json +25 -1
- package/plugins/maestro-skills/skills/board-work.md +63 -0
- package/plugins/maestro-skills/skills/cohort-design.md +153 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +80 -0
- package/plugins/maestro-skills/skills/main-session.md +102 -0
- package/plugins/maestro-skills/skills/peer-sessions.md +65 -0
- package/plugins/maestro-skills/skills/persona-discipline.md +75 -0
- package/plugins/maestro-skills/vendor/emilkowalski/LICENSE +21 -0
- package/plugins/maestro-skills/vendor/emilkowalski/UPSTREAM.json +70 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/RECIPES.md +324 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/SKILL.md +199 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/animation-vocabulary/SKILL.md +173 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/apple-design/SKILL.md +282 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/emil-design-eng/SKILL.md +674 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/find-animation-opportunities/SKILL.md +132 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/AUDIT.md +115 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/SKILL.md +101 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/PICKER.md +197 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/SKILL.md +90 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/SKILL.md +112 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/STANDARDS.md +187 -0
- package/plugins/maestro-skills/vendor/impeccable/LICENSE +191 -0
- package/plugins/maestro-skills/vendor/impeccable/NOTICE.md +11 -0
- package/plugins/maestro-skills/vendor/impeccable/SKILL.md +86 -0
- package/plugins/maestro-skills/vendor/impeccable/UPSTREAM.json +201 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-asset-producer.md +42 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-documenter.md +29 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-finish-reviewer.md +43 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-manual-edit-applier.md +97 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/adapt.md +312 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/adapt.native.md +58 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/android.md +46 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/animate.md +89 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/audit.md +136 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/audit.native.md +139 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/bolder.md +33 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/clarify.md +94 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/colorize.md +86 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/craft-floor.md +44 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/craft.md +5 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/critique.md +806 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/asset-producer.md +37 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/documenter.md +24 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/finish-reviewer.md +38 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/delight.md +70 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/distill.md +111 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/doctor.md +54 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/document.md +416 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/extract.md +69 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/harden.md +336 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/hooks.md +111 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/init.md +131 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/ios.md +51 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/layout.md +84 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/live-setup.md +104 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/live.md +325 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/new-work.md +147 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/onboard.md +234 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/operate.md +61 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/optimize.md +258 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/overdrive.md +127 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/polish.md +105 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/quieter.md +99 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/routing.md +24 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/shape.md +59 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/typeset.md +80 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/visualize.md +46 -0
- package/plugins/maestro-skills/vendor/taste-skill/LICENSE +21 -0
- package/plugins/maestro-skills/vendor/taste-skill/UPSTREAM.json +37 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/minimalist-skill/SKILL.md +85 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/redesign-skill/SKILL.md +178 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/soft-skill/SKILL.md +98 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/taste-skill/SKILL.md +1206 -0
- package/plugins/maestro-skills/vendor/unlazy/LICENSE +21 -0
- package/plugins/maestro-skills/vendor/unlazy/SECURITY.md +72 -0
- package/plugins/maestro-skills/vendor/unlazy/SKILL.md +104 -0
- package/plugins/maestro-skills/vendor/unlazy/UPSTREAM.json +94 -0
- package/plugins/maestro-skills/vendor/unlazy/references/dispatch.md +82 -0
- package/plugins/maestro-skills/vendor/unlazy/references/gates.md +149 -0
- package/plugins/maestro-skills/vendor/unlazy/references/method.md +49 -0
- package/plugins/maestro-skills/vendor/unlazy/references/orchestration.md +107 -0
- package/plugins/maestro-skills/vendor/unlazy/references/parallel.md +133 -0
- package/plugins/maestro-skills/vendor/unlazy/references/token-economy.md +48 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/dispatch-check.mjs +139 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/gate-check.mjs +960 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/gate-lint.mjs +245 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/check-supervisor.mjs +46 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/dispatch.mjs +293 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/gates.mjs +953 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/process-tree.mjs +161 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/regex-worker.mjs +9 -0
- package/plugins/maestro-skills/vendor/unlazy/templates/PLAN.md +116 -0
- package/plugins/maestro-skills/vendor/unlazy/templates/gates-leaf.md +51 -0
- package/plugins/maestro-skills/vendor/unlazy/templates/gates-node.md +51 -0
- package/scaffold/CLAUDE.md +24 -0
- package/scripts/ci/check-durable-write-seam.mjs +147 -0
- package/scripts/ci/check-durable-write-seam.test.mjs +90 -0
- package/scripts/ci/check-skill-packs.mjs +388 -0
- package/scripts/ci/check-skill-packs.test.mjs +495 -0
- package/scripts/ci/check.mjs +6 -0
- package/scripts/collective/hook-runner.mjs +39 -4
- package/scripts/collective/hook-runner.test.mjs +85 -2
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +96 -0
- package/scripts/daemon/agent-daemon-design.test.mjs +238 -0
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +60 -0
- package/scripts/daemon/agent-daemon.mjs +249 -10
- package/scripts/daemon/agent-daemon.test.mjs +73 -0
- package/scripts/daemon/assurance-e2e.test.mjs +141 -6
- package/scripts/daemon/assurance.mjs +461 -37
- package/scripts/daemon/assurance.test.mjs +408 -43
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +393 -0
- package/scripts/daemon/cadence-consumer.mjs +289 -89
- package/scripts/daemon/cadence-handlers.mjs +53 -0
- package/scripts/daemon/classifier.mjs +1 -1
- package/scripts/daemon/dispatcher-resume.test.mjs +166 -0
- package/scripts/daemon/dispatcher.mjs +127 -19
- package/scripts/daemon/health.mjs +12 -1
- package/scripts/daemon/inbox-deferral-session.test.mjs +49 -0
- package/scripts/daemon/inbox-deferral.mjs +6 -0
- package/scripts/daemon/lib/self-echo.mjs +201 -0
- package/scripts/daemon/lib/self-echo.test.mjs +153 -0
- package/scripts/daemon/maestro-daemon.mjs +3 -0
- package/scripts/daemon/prompt-builder.mjs +19 -3
- package/scripts/daemon/responder.mjs +51 -40
- package/scripts/daemon/sdk-version.mjs +51 -0
- package/scripts/daemon/sdk-version.test.mjs +31 -0
- package/scripts/hooks/pre-send-audit.sh +97 -4
- package/scripts/hooks/pre-send-audit.test.mjs +140 -1
- package/scripts/local-triggers/autoupdate.sh +243 -19
- package/scripts/local-triggers/autoupdate.test.mjs +518 -0
- package/scripts/local-triggers/generate-plists.sh +24 -1
- package/scripts/local-triggers/generate-plists.test.mjs +49 -11
- package/scripts/org/send-orgmail.first-contact.test.mjs +102 -0
- package/scripts/org/send-orgmail.mjs +27 -3
- package/scripts/poller/inbox-privilege-injection.test.mjs +167 -0
- package/scripts/poller/slack-poller.mjs +13 -1
- package/scripts/poller/utils.mjs +46 -1
- package/scripts/poller-launchd/install.sh +19 -11
- package/scripts/poller-launchd/install.test.mjs +243 -0
- package/scripts/poller-launchd/launchd-poller-wrapper.sh +92 -0
- package/scripts/poller-launchd/migrate.sh +66 -0
- package/scripts/poller-launchd/poller.plist.template +4 -2
- package/scripts/session/feed.mjs +237 -0
- package/scripts/session/feed.test.mjs +196 -0
- package/scripts/session/supervisor-sh.test.mjs +218 -0
- package/scripts/session/supervisor.mjs +328 -0
- package/scripts/session/supervisor.sh +141 -0
- package/scripts/session/supervisor.test.mjs +482 -0
- package/scripts/setup/configure-macos.sh +250 -55
- package/scripts/setup/configure-macos.test.mjs +306 -0
- package/scripts/setup/init-agent.sh +112 -7
- package/scripts/setup/init-agent.test.mjs +220 -1
- package/scripts/vendor/skill-packs.mjs +354 -0
- package/scripts/vendor/sync-skill-packs.mjs +242 -0
- package/scripts/vendor/sync-skill-packs.test.mjs +103 -0
- package/scripts/watchdog/memory-watchdog.sh +37 -1
- package/scripts/watchdog/memory-watchdog.test.mjs +64 -0
- package/scripts/setup/boot-claude-session.sh +0 -94
|
@@ -11,11 +11,21 @@
|
|
|
11
11
|
# 5. Sets up BlackHole virtual audio for voice integration
|
|
12
12
|
# 6. Configures Login Items for all required apps
|
|
13
13
|
# 7. Installs launchd agents for boot-time service startup
|
|
14
|
+
# 8. Retires the legacy Terminal.app boot-claude-session job (the front-door
|
|
15
|
+
# session is now the launchd `ai.maestro.<first>-session` supervisor job
|
|
16
|
+
# that scripts/local-triggers/generate-plists.sh emits)
|
|
17
|
+
# 9. `--tailscale-ssh`: advertises Tailscale SSH + turns Remote Login on so
|
|
18
|
+
# an operator can reach the seat without a console (audit F4)
|
|
14
19
|
#
|
|
15
20
|
# Usage:
|
|
16
21
|
# sudo ./scripts/setup/configure-macos.sh # Full configuration
|
|
17
22
|
# sudo ./scripts/setup/configure-macos.sh --check # Verify current config
|
|
18
23
|
# ./scripts/setup/configure-macos.sh --apps-only # Non-sudo app setup only
|
|
24
|
+
# ./scripts/setup/configure-macos.sh --tailscale-ssh # Operator SSH over the tailnet
|
|
25
|
+
#
|
|
26
|
+
# Testing seam: `MAESTRO_CONFIGURE_MACOS_NO_MAIN=1 source configure-macos.sh`
|
|
27
|
+
# defines the functions without running main(), so configure-macos.test.mjs can
|
|
28
|
+
# exercise one function at a time against stub binaries.
|
|
19
29
|
#
|
|
20
30
|
# IMPORTANT: Some steps require sudo (auto-login, power management).
|
|
21
31
|
# Run with sudo for full configuration.
|
|
@@ -335,63 +345,207 @@ configure_app_launches() {
|
|
|
335
345
|
done
|
|
336
346
|
ok "Log and state directories verified (owned by $real_user)"
|
|
337
347
|
|
|
338
|
-
# ---
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
348
|
+
# --- Legacy Terminal.app boot session: retired ------------------------------
|
|
349
|
+
# The front-door session is now a launchd KeepAlive job
|
|
350
|
+
# (ai.maestro.<first>-session → scripts/session/supervisor.sh) generated by
|
|
351
|
+
# scripts/local-triggers/generate-plists.sh. Unload + remove the old job so
|
|
352
|
+
# two sessions can never race on the same machine.
|
|
353
|
+
retire_boot_claude_session
|
|
354
|
+
}
|
|
342
355
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
356
|
+
# =============================================================================
|
|
357
|
+
# 6b. RETIRE THE LEGACY boot-claude-session LAUNCHD JOB
|
|
358
|
+
# =============================================================================
|
|
359
|
+
#
|
|
360
|
+
# F15: the Terminal.app boot script had no KeepAlive, no lock, no headless mode and
|
|
361
|
+
# needed Terminal.app. It is replaced by the supervisor job. This is idempotent:
|
|
362
|
+
# absent plist → nothing to do, exit 0. Runs as the real user when invoked
|
|
363
|
+
# under sudo so launchctl addresses the right (gui) domain.
|
|
347
364
|
|
|
348
|
-
|
|
349
|
-
|
|
365
|
+
retire_boot_claude_session() {
|
|
366
|
+
local real_user="${SUDO_USER:-$CURRENT_USER}"
|
|
367
|
+
# Under sudo the plist lives in the invoking user's home; otherwise $HOME is
|
|
368
|
+
# authoritative (a `~user` expansion would bypass an overridden HOME).
|
|
369
|
+
local real_home="$HOME"
|
|
370
|
+
if [ "$(id -u)" -eq 0 ] && [ -n "${SUDO_USER:-}" ]; then
|
|
371
|
+
real_home=$(eval echo "~$SUDO_USER")
|
|
372
|
+
fi
|
|
373
|
+
local LABEL="ai.maestro.boot-claude-session"
|
|
374
|
+
local PLIST_PATH="$real_home/Library/LaunchAgents/${LABEL}.plist"
|
|
350
375
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
<key>RunAtLoad</key>
|
|
365
|
-
<true/>
|
|
366
|
-
<key>StandardOutPath</key>
|
|
367
|
-
<string>${AGENT_DIR}/logs/watchdog/boot-session-stdout.log</string>
|
|
368
|
-
<key>StandardErrorPath</key>
|
|
369
|
-
<string>${AGENT_DIR}/logs/watchdog/boot-session-stderr.log</string>
|
|
370
|
-
<key>EnvironmentVariables</key>
|
|
371
|
-
<dict>
|
|
372
|
-
<key>PATH</key>
|
|
373
|
-
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:${real_home}/.local/bin</string>
|
|
374
|
-
<key>HOME</key>
|
|
375
|
-
<string>${real_home}</string>
|
|
376
|
-
</dict>
|
|
377
|
-
</dict>
|
|
378
|
-
</plist>
|
|
379
|
-
PLIST_EOF
|
|
376
|
+
if [ ! -f "$PLIST_PATH" ]; then
|
|
377
|
+
log "Legacy $LABEL job not present — nothing to retire"
|
|
378
|
+
return 0
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
if [ "$(id -u)" -eq 0 ] && [ "$real_user" != "root" ]; then
|
|
382
|
+
su "$real_user" -c "launchctl unload '$PLIST_PATH' 2>/dev/null || launchctl bootout gui/\$(id -u)/$LABEL 2>/dev/null" 2>/dev/null || true
|
|
383
|
+
else
|
|
384
|
+
launchctl unload "$PLIST_PATH" 2>/dev/null || launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
|
|
385
|
+
fi
|
|
386
|
+
rm -f "$PLIST_PATH"
|
|
387
|
+
ok "Retired legacy $LABEL job (replaced by the ai.maestro.<first>-session supervisor)"
|
|
388
|
+
}
|
|
380
389
|
|
|
381
|
-
|
|
390
|
+
# =============================================================================
|
|
391
|
+
# 6c. TAILSCALE SSH (operator access without a console)
|
|
392
|
+
# =============================================================================
|
|
393
|
+
#
|
|
394
|
+
# Audit F4: no operator could reach any of 41 seats over SSH. Tailscale SSH
|
|
395
|
+
# (`tailscale set --ssh`) advertises the host to the tailnet; macOS still gates
|
|
396
|
+
# inbound SSH behind Remote Login (`systemsetup -setremotelogin on`, root).
|
|
397
|
+
# Neither is reachable until the tailnet ACL grants an `ssh` rule, which only a
|
|
398
|
+
# tailnet admin can write — hence the reminder printed at the end.
|
|
399
|
+
#
|
|
400
|
+
# MAESTRO_TAILSCALE_BIN overrides the CLI path (tests; non-standard installs).
|
|
382
401
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
402
|
+
resolve_tailscale_bin() {
|
|
403
|
+
if [ -n "${MAESTRO_TAILSCALE_BIN:-}" ]; then
|
|
404
|
+
[ -x "$MAESTRO_TAILSCALE_BIN" ] && echo "$MAESTRO_TAILSCALE_BIN"
|
|
405
|
+
return 0
|
|
406
|
+
fi
|
|
407
|
+
local c
|
|
408
|
+
for c in "$(command -v tailscale 2>/dev/null)" \
|
|
409
|
+
/Applications/Tailscale.app/Contents/MacOS/Tailscale \
|
|
410
|
+
/usr/local/bin/tailscale /opt/homebrew/bin/tailscale; do
|
|
411
|
+
if [ -n "$c" ] && [ -x "$c" ]; then echo "$c"; return 0; fi
|
|
412
|
+
done
|
|
413
|
+
return 0
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# Which Tailscale is this? The Tailscale SSH server ships ONLY in the
|
|
417
|
+
# open-source `tailscaled` variant; Tailscale.app (Mac App Store / standalone
|
|
418
|
+
# GUI) cannot honour RunSSH no matter what `tailscale set --ssh` says. The
|
|
419
|
+
# fleet runs the GUI app, whose /usr/local/bin/tailscale is a two-line sh shim
|
|
420
|
+
# onto /Applications/Tailscale.app/Contents/MacOS/tailscale.
|
|
421
|
+
# gui — Tailscale.app (path under the bundle, or the shim naming it)
|
|
422
|
+
# open-source — a tailscaled is installed next to the CLI
|
|
423
|
+
# unknown — cannot tell; proceed as before
|
|
424
|
+
# MAESTRO_TAILSCALED_BIN overrides the daemon path (tests; non-standard installs).
|
|
425
|
+
tailscale_variant() {
|
|
426
|
+
local TS="$1" c
|
|
427
|
+
[ -z "$TS" ] && { echo "unknown"; return 0; }
|
|
428
|
+
case "$TS" in */Tailscale.app/*) echo "gui"; return 0 ;; esac
|
|
429
|
+
if head -c 512 "$TS" 2>/dev/null | grep -aq "Tailscale.app"; then echo "gui"; return 0; fi
|
|
430
|
+
for c in "${MAESTRO_TAILSCALED_BIN:-}" "$(command -v tailscaled 2>/dev/null)" \
|
|
431
|
+
/opt/homebrew/bin/tailscaled /usr/local/bin/tailscaled \
|
|
432
|
+
/opt/homebrew/opt/tailscale/bin/tailscaled; do
|
|
433
|
+
if [ -n "$c" ] && [ -x "$c" ]; then echo "open-source"; return 0; fi
|
|
434
|
+
done
|
|
435
|
+
echo "unknown"
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
# The seat's tailnet IPv4, for the sshd-over-tailnet fallback line. Empty when unknown.
|
|
439
|
+
tailscale_ip4() {
|
|
440
|
+
local TS="$1"
|
|
441
|
+
[ -n "$TS" ] && "$TS" ip -4 2>/dev/null | head -1 || true
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
print_tailscale_gui_remedy() { # $1 = tailscale bin
|
|
445
|
+
local ip
|
|
446
|
+
ip="$(tailscale_ip4 "$1")"
|
|
447
|
+
warn "Tailscale SSH is NOT available on Tailscale.app (GUI): only the open-source tailscaled carries the SSH server."
|
|
448
|
+
log "Two ways to reach this seat:"
|
|
449
|
+
log " 1. Switch to the open-source variant (Tailscale SSH):"
|
|
450
|
+
log " quit Tailscale.app, then: brew install tailscale && sudo brew services start tailscale"
|
|
451
|
+
log " sign in: tailscale up --ssh — then re-run: ./scripts/setup/configure-macos.sh --tailscale-ssh"
|
|
452
|
+
log " 2. Keep the GUI app and use Remote Login (sshd) over the tailnet IP, enabled below:"
|
|
453
|
+
log " ssh $(whoami)@${ip:-<tailnet-ip>} (the operator's key must be in ~/.ssh/authorized_keys)"
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
print_tailscale_acl_reminder() {
|
|
457
|
+
echo ""
|
|
458
|
+
log "Tailnet ACL reminder — SSH is advertised but NOT reachable until an admin"
|
|
459
|
+
log "adds an ssh rule at https://login.tailscale.com/admin/acls, e.g.:"
|
|
460
|
+
echo ' "ssh": [{'
|
|
461
|
+
echo ' "action": "check",'
|
|
462
|
+
echo ' "src": ["autogroup:admin"],'
|
|
463
|
+
echo ' "dst": ["autogroup:self", "tag:agent-seat"],'
|
|
464
|
+
echo ' "users": ["autogroup:nonroot"]'
|
|
465
|
+
echo ' }]'
|
|
466
|
+
log "Then from an operator machine: tailscale ssh $(whoami)@$(hostname -s 2>/dev/null || hostname)"
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
configure_tailscale_ssh() {
|
|
470
|
+
section "Tailscale SSH (operator access)"
|
|
471
|
+
|
|
472
|
+
local TS
|
|
473
|
+
TS="$(resolve_tailscale_bin)"
|
|
474
|
+
if [ -z "$TS" ]; then
|
|
475
|
+
fail "tailscale CLI not found — install Tailscale (brew install --cask tailscale, sign in), then re-run --tailscale-ssh"
|
|
476
|
+
return 1
|
|
477
|
+
fi
|
|
478
|
+
ok "tailscale CLI: $TS"
|
|
479
|
+
|
|
480
|
+
local variant
|
|
481
|
+
variant="$(tailscale_variant "$TS")"
|
|
482
|
+
if [ "$variant" = "gui" ]; then
|
|
483
|
+
print_tailscale_gui_remedy "$TS"
|
|
484
|
+
elif "$TS" set --ssh; then
|
|
485
|
+
ok "Tailscale SSH advertised (tailscale set --ssh)"
|
|
486
|
+
else
|
|
487
|
+
warn "tailscale set --ssh failed — is the node up and signed in? Check: $TS status"
|
|
488
|
+
fi
|
|
489
|
+
|
|
490
|
+
# Remote Login (sshd). systemsetup needs root; under sudo -n a missing
|
|
491
|
+
# credential fails fast instead of prompting from an unattended run.
|
|
492
|
+
if [ "$(id -u)" -eq 0 ]; then
|
|
493
|
+
systemsetup -setremotelogin on >/dev/null 2>&1 && ok "Remote Login enabled (sshd)" \
|
|
494
|
+
|| warn "systemsetup -setremotelogin on failed — enable Remote Login in System Settings > General > Sharing"
|
|
495
|
+
else
|
|
496
|
+
if sudo -n systemsetup -setremotelogin on >/dev/null 2>&1; then
|
|
497
|
+
ok "Remote Login enabled (sshd)"
|
|
386
498
|
else
|
|
387
|
-
|
|
388
|
-
launchctl load "$PLIST_PATH" 2>/dev/null || true
|
|
499
|
+
warn "Remote Login needs sudo: run sudo systemsetup -setremotelogin on"
|
|
389
500
|
fi
|
|
501
|
+
fi
|
|
390
502
|
|
|
391
|
-
|
|
503
|
+
if [ "$variant" = "gui" ]; then
|
|
504
|
+
log "Tailnet ACL reminder does not apply until Tailscale SSH is available on this seat."
|
|
392
505
|
else
|
|
393
|
-
|
|
506
|
+
print_tailscale_acl_reminder
|
|
394
507
|
fi
|
|
508
|
+
return 0
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
# --check helpers: "on" / "off" / "unavailable" / "unknown" for each half of
|
|
512
|
+
# operator access. "unavailable" = the GUI app, where RunSSH can never be true.
|
|
513
|
+
tailscale_ssh_state() {
|
|
514
|
+
local TS
|
|
515
|
+
TS="$(resolve_tailscale_bin)"
|
|
516
|
+
[ -z "$TS" ] && { echo "unknown"; return 0; }
|
|
517
|
+
local prefs
|
|
518
|
+
prefs="$("$TS" debug prefs 2>/dev/null || true)"
|
|
519
|
+
if printf '%s' "$prefs" | grep -Eq '"RunSSH":[[:space:]]*true'; then echo "on"
|
|
520
|
+
elif [ "$(tailscale_variant "$TS")" = "gui" ]; then echo "unavailable"
|
|
521
|
+
elif printf '%s' "$prefs" | grep -Eq '"RunSSH":[[:space:]]*false'; then echo "off"
|
|
522
|
+
else echo "unknown"; fi
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
# `launchctl list` in the CALLER's gui domain. Under `sudo … --check` the
|
|
526
|
+
# shell is root, whose `launchctl list` cannot see gui/<uid> jobs — every
|
|
527
|
+
# ai.maestro.* agent would read as "not loaded" on a healthy seat. As root
|
|
528
|
+
# with SUDO_USER set, ask launchd on that user's behalf.
|
|
529
|
+
gui_launchctl_list() {
|
|
530
|
+
if [ "$(id -u)" -eq 0 ] && [ -n "${SUDO_USER:-}" ]; then
|
|
531
|
+
launchctl asuser "$(id -u "$SUDO_USER")" launchctl list 2>/dev/null
|
|
532
|
+
else
|
|
533
|
+
launchctl list 2>/dev/null
|
|
534
|
+
fi
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
remote_login_state() {
|
|
538
|
+
local out
|
|
539
|
+
if [ "$(id -u)" -eq 0 ]; then
|
|
540
|
+
out="$(systemsetup -getremotelogin 2>/dev/null || true)"
|
|
541
|
+
else
|
|
542
|
+
out="$(sudo -n systemsetup -getremotelogin 2>/dev/null || true)"
|
|
543
|
+
fi
|
|
544
|
+
case "$out" in
|
|
545
|
+
*On*|*on*) echo "on" ;;
|
|
546
|
+
*Off*|*off*) echo "off" ;;
|
|
547
|
+
*) echo "unknown" ;;
|
|
548
|
+
esac
|
|
395
549
|
}
|
|
396
550
|
|
|
397
551
|
# =============================================================================
|
|
@@ -470,7 +624,7 @@ configure_boot_services() {
|
|
|
470
624
|
|
|
471
625
|
# Check which are loaded
|
|
472
626
|
local loaded_count
|
|
473
|
-
loaded_count=$(
|
|
627
|
+
loaded_count=$(gui_launchctl_list | grep -cE 'ai\.(maestro|adaptic)' || true)
|
|
474
628
|
if [ "$loaded_count" -gt 0 ]; then
|
|
475
629
|
ok "$loaded_count agents currently loaded"
|
|
476
630
|
else
|
|
@@ -481,7 +635,7 @@ configure_boot_services() {
|
|
|
481
635
|
fi
|
|
482
636
|
|
|
483
637
|
# Check if the daemon is running
|
|
484
|
-
if
|
|
638
|
+
if gui_launchctl_list | grep -q "daemon"; then
|
|
485
639
|
ok "Main daemon is loaded"
|
|
486
640
|
else
|
|
487
641
|
warn "Main daemon not loaded — the orchestrator won't start on boot"
|
|
@@ -580,7 +734,7 @@ verify_all() {
|
|
|
580
734
|
fi
|
|
581
735
|
|
|
582
736
|
# Check memory watchdog
|
|
583
|
-
if
|
|
737
|
+
if gui_launchctl_list | grep -q "ai.maestro.memory-watchdog"; then
|
|
584
738
|
ok "Memory watchdog: running"
|
|
585
739
|
elif [ -f "$HOME/Library/LaunchAgents/ai.maestro.memory-watchdog.plist" ]; then
|
|
586
740
|
warn "Memory watchdog: installed but not loaded"
|
|
@@ -603,6 +757,40 @@ verify_all() {
|
|
|
603
757
|
warn "Heartbeat: no heartbeat file found"
|
|
604
758
|
fi
|
|
605
759
|
|
|
760
|
+
# Front-door session job (ai.maestro.<first>-session, KeepAlive)
|
|
761
|
+
if gui_launchctl_list | grep -Eq 'ai\.maestro\.[^[:space:]]+-session$'; then
|
|
762
|
+
ok "Front-door session job: loaded"
|
|
763
|
+
elif ls "$HOME"/Library/LaunchAgents/ai.maestro.*-session.plist >/dev/null 2>&1; then
|
|
764
|
+
warn "Front-door session job: installed but not loaded — maestro session start"
|
|
765
|
+
else
|
|
766
|
+
warn "Front-door session job: not installed — run: npm run init-agent"
|
|
767
|
+
fi
|
|
768
|
+
if [ -f "$HOME/Library/LaunchAgents/ai.maestro.boot-claude-session.plist" ]; then
|
|
769
|
+
warn "Legacy boot-claude-session job still present — re-run this script to retire it"
|
|
770
|
+
fi
|
|
771
|
+
|
|
772
|
+
# Operator access over the tailnet (audit F4): both halves must be on.
|
|
773
|
+
local ts_state rl_state
|
|
774
|
+
ts_state="$(tailscale_ssh_state)"
|
|
775
|
+
rl_state="$(remote_login_state)"
|
|
776
|
+
case "$ts_state" in
|
|
777
|
+
on) ok "Tailscale SSH: on (advertised)" ;;
|
|
778
|
+
off) fail "Tailscale SSH: off — run: ./scripts/setup/configure-macos.sh --tailscale-ssh"; issues=$((issues + 1)) ;;
|
|
779
|
+
unavailable)
|
|
780
|
+
if [ "$rl_state" = "on" ]; then
|
|
781
|
+
warn "Tailscale SSH: unavailable on Tailscale.app (GUI) — Remote Login over the tailnet IP is the access path; for Tailscale SSH: brew install tailscale (open-source tailscaled)"
|
|
782
|
+
else
|
|
783
|
+
fail "Tailscale SSH: unavailable on Tailscale.app (GUI) and Remote Login is off — run: sudo ./scripts/setup/configure-macos.sh --tailscale-ssh (or brew install tailscale for the open-source tailscaled)"
|
|
784
|
+
issues=$((issues + 1))
|
|
785
|
+
fi ;;
|
|
786
|
+
*) warn "Tailscale SSH: unknown (tailscale CLI missing or node not up)" ;;
|
|
787
|
+
esac
|
|
788
|
+
case "$rl_state" in
|
|
789
|
+
on) ok "Remote Login: on (sshd)" ;;
|
|
790
|
+
off) fail "Remote Login: off — run: sudo ./scripts/setup/configure-macos.sh --tailscale-ssh"; issues=$((issues + 1)) ;;
|
|
791
|
+
*) warn "Remote Login: unknown (needs sudo to read: sudo systemsetup -getremotelogin)" ;;
|
|
792
|
+
esac
|
|
793
|
+
|
|
606
794
|
# Summary
|
|
607
795
|
echo ""
|
|
608
796
|
if [ "$issues" -eq 0 ]; then
|
|
@@ -632,6 +820,9 @@ main() {
|
|
|
632
820
|
--check|check)
|
|
633
821
|
verify_all
|
|
634
822
|
;;
|
|
823
|
+
--tailscale-ssh|tailscale-ssh)
|
|
824
|
+
configure_tailscale_ssh
|
|
825
|
+
;;
|
|
635
826
|
--apps-only|apps)
|
|
636
827
|
configure_parsec
|
|
637
828
|
configure_slack_cdp
|
|
@@ -661,14 +852,18 @@ main() {
|
|
|
661
852
|
echo ""
|
|
662
853
|
;;
|
|
663
854
|
*)
|
|
664
|
-
echo "Usage: $0 [--full|--check|--apps-only]"
|
|
855
|
+
echo "Usage: $0 [--full|--check|--apps-only|--tailscale-ssh]"
|
|
665
856
|
echo ""
|
|
666
|
-
echo " --full
|
|
667
|
-
echo " --check
|
|
668
|
-
echo " --apps-only
|
|
857
|
+
echo " --full Full configuration (requires sudo for auto-login and power)"
|
|
858
|
+
echo " --check Verify current configuration"
|
|
859
|
+
echo " --apps-only Configure apps and services only (no sudo needed)"
|
|
860
|
+
echo " --tailscale-ssh Advertise Tailscale SSH + enable Remote Login (operator access)"
|
|
669
861
|
exit 1
|
|
670
862
|
;;
|
|
671
863
|
esac
|
|
672
864
|
}
|
|
673
865
|
|
|
674
|
-
|
|
866
|
+
# Sourcing seam for tests: define everything, run nothing.
|
|
867
|
+
if [ "${MAESTRO_CONFIGURE_MACOS_NO_MAIN:-0}" != "1" ]; then
|
|
868
|
+
main "$@"
|
|
869
|
+
fi
|