The probe's four questions are answered by the operator in Cowork, but three of them leave traces on this Mac, and a trace is a measurement where "what did you see in the UI" is a recollection. This reads the traces so each step can be verified before the next one starts. What it can actually see, discovered by measurement rather than assumed: - Claude Desktop writes one log per MCP server, named mcp-server-<name>.log, and it records the full JSON-RPC handshake (verified against the existing mcp-server-filesystem.log: initialize, notifications/initialized, tools/list all appear as "Message from client: method=..."). So if Cowork spawns probe-tools, the entire exchange is readable from disk -- including the server's own stderr line, which carries the interpreter version. - The absence of that log file is itself informative: it means Cowork never attempted to start the server, which is a different finding from starting and failing. - pgrep on probe_tools.py confirms a live process, but its absence is not a nei: a server may be spawned on demand. The log is the record; the process check is corroboration. The script says so rather than over-claiming. The Cowork store search is bounded to the session directory by name plus the small manifests. The first version ran a recursive content grep over the whole Claude support directory and did not finish inside two minutes -- it walks caches and VM images. A check that hangs is not a check. Bounded version runs in 0.17s. It reports, it does not gate: exit 0 when everything has landed, 1 while anything is pending. Baseline now: vehicle and archive done, three pending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
126 lines
5 KiB
Bash
Executable file
126 lines
5 KiB
Bash
Executable file
#!/bin/bash
|
|
# Report, from the host, how far the Cowork probe has actually got.
|
|
#
|
|
# The probe asks four questions that only the operator can answer in a Cowork
|
|
# session. But three of them leave traces on this Mac, and a trace is a
|
|
# measurement where "what did you see in the UI" is a recollection. This script
|
|
# reads those traces so each step can be verified before the next one starts.
|
|
#
|
|
# It reports; it does not gate. Exit 0 when every check has landed, 1 while any
|
|
# is still pending, 2 on a broken precondition.
|
|
#
|
|
# bash 3.2-clean, ASCII-only, read-only: it opens nothing and writes nothing.
|
|
|
|
set -u
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
|
|
PROBE_DOC="$REPO_ROOT/docs/cowork-probe.md"
|
|
FIXTURE="$REPO_ROOT/tests/fixtures/cowork-probe"
|
|
ARCHIVE="${JOBBSOK_PROBE_ARCHIVE:-/tmp/jobbsok-probe.plugin}"
|
|
CLAUDE_LOGS="$HOME/Library/Logs/Claude"
|
|
MCP_LOG="$CLAUDE_LOGS/mcp-server-probe-tools.log"
|
|
CLAUDE_SUPPORT="$HOME/Library/Application Support/Claude"
|
|
|
|
pending=0
|
|
|
|
say() { printf '%s\n' "$1"; }
|
|
done_() { printf ' [DONE] %s\n' "$1"; }
|
|
pend() { printf ' [PENDING] %s\n' "$1"; pending=$((pending+1)); }
|
|
info() { printf ' %s\n' "$1"; }
|
|
|
|
say ""
|
|
say "=== 1. Probe vehicle and archive ============================================"
|
|
n=$(find "$FIXTURE" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
if [ "$n" = "4" ]; then
|
|
done_ "probe vehicle: 4 files under tests/fixtures/cowork-probe"
|
|
else
|
|
pend "probe vehicle: expected 4 files, found $n"
|
|
fi
|
|
if [ -f "$ARCHIVE" ]; then
|
|
a=$(unzip -Z1 "$ARCHIVE" 2>/dev/null | wc -l | tr -d ' ')
|
|
leak=$(unzip -Z1 "$ARCHIVE" 2>/dev/null | grep -cE '^\.git|STATE\.md|\.venv|^\.claude/')
|
|
if [ "$a" = "4" ] && [ "$leak" = "0" ]; then
|
|
done_ "archive: $ARCHIVE ($a files, no leaked entries)"
|
|
else
|
|
pend "archive: $ARCHIVE has $a entries and $leak leaked ones - rebuild it"
|
|
fi
|
|
else
|
|
pend "archive not built yet: $ARCHIVE"
|
|
info "cd tests/fixtures/cowork-probe && zip -X $ARCHIVE .claude-plugin/plugin.json .mcp.json probe_tools.py skills/probe-versjon/SKILL.md"
|
|
fi
|
|
|
|
say ""
|
|
say "=== 2. Is jobbsok-probe installed in Cowork? ================================="
|
|
# Bounded on purpose. A recursive content grep over the whole Claude support
|
|
# directory walks caches and VM images and does not finish; the plugin store is
|
|
# a known, shallow place, so look there by name and read only the small
|
|
# manifests. A check that hangs is not a check.
|
|
SESSIONS="$CLAUDE_SUPPORT/local-agent-mode-sessions"
|
|
hits=$(find "$SESSIONS" -maxdepth 6 -name '*jobbsok-probe*' 2>/dev/null | head -5)
|
|
for m in $(find "$SESSIONS" -maxdepth 4 \( -name 'manifest.json' -o -name 'cowork_settings.json' -o -name 'known_marketplaces.json' \) 2>/dev/null); do
|
|
if grep -q 'jobbsok-probe' "$m" 2>/dev/null; then
|
|
hits="$hits
|
|
$m"
|
|
fi
|
|
done
|
|
hits=$(printf '%s\n' "$hits" | grep -v '^$')
|
|
if [ -n "$hits" ]; then
|
|
done_ "Cowork store mentions jobbsok-probe:"
|
|
printf '%s\n' "$hits" | sed 's|^| |'
|
|
else
|
|
pend "no trace of jobbsok-probe in Cowork's store yet"
|
|
info "Upload the archive, or drag it into the Cowork chat and accept it."
|
|
fi
|
|
|
|
say ""
|
|
say "=== 3. Did Cowork spawn the probe-tools server on THIS Mac? =================="
|
|
say " (this is the -Host-MCP: question, measured rather than recalled)"
|
|
if [ -f "$MCP_LOG" ]; then
|
|
done_ "log exists: $MCP_LOG"
|
|
info "last written: $(stat -f '%Sm' "$MCP_LOG" 2>/dev/null)"
|
|
for m in initialize tools/list tools/call; do
|
|
if grep -q "method=\"$m\"" "$MCP_LOG" 2>/dev/null; then
|
|
done_ "handshake: $m seen"
|
|
else
|
|
pend "handshake: $m NOT seen in the log"
|
|
fi
|
|
done
|
|
started=$(grep -h '\[probe-tools\] started on' "$MCP_LOG" 2>/dev/null | tail -1)
|
|
if [ -n "$started" ]; then
|
|
done_ "server's own stderr: $started"
|
|
else
|
|
pend "the server never printed its start line - it may have failed to launch"
|
|
fi
|
|
else
|
|
pend "no log at $MCP_LOG"
|
|
info "Claude names each server's log mcp-server-<name>.log. No file means"
|
|
info "Cowork never even attempted to start probe-tools."
|
|
fi
|
|
if pgrep -f probe_tools.py >/dev/null 2>&1; then
|
|
done_ "process alive right now: $(pgrep -fl probe_tools.py | head -1 | cut -c1-100)"
|
|
else
|
|
info "no probe_tools.py process running at this instant (it may be started"
|
|
info "on demand, so this alone is not a nei - the log above is the record)"
|
|
fi
|
|
|
|
say ""
|
|
say "=== 4. Are the four answers written down? ==================================="
|
|
RE='^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT): +(lokal VM|sky|ja|nei)\b'
|
|
c=$(grep -cE "$RE" "$PROBE_DOC" 2>/dev/null)
|
|
if [ "$c" = "4" ]; then
|
|
done_ "all four answered - plan Step 1 Verify passes"
|
|
grep -E '^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT):' "$PROBE_DOC" | sed 's|^| |'
|
|
else
|
|
pend "$c of 4 answered in docs/cowork-probe.md"
|
|
grep -E '^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT):' "$PROBE_DOC" 2>/dev/null | sed 's|^| |'
|
|
fi
|
|
|
|
say ""
|
|
say "============================================================================"
|
|
if [ "$pending" -eq 0 ]; then
|
|
say "ALL CHECKS LANDED."
|
|
exit 0
|
|
fi
|
|
say "$pending check(s) still pending."
|
|
exit 1
|