@junghanacs/entwurf 0.15.1 → 0.16.1
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/AGENTS.md +1 -1
- package/CHANGELOG.md +354 -0
- package/DELIVERY.md +3 -2
- package/README.md +68 -88
- package/VERIFY.md +4 -1
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +1 -1
- package/docs/external-mcp-host.md +147 -9
- package/docs/setup-clean-host.md +125 -6
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +65 -20
- package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +19 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +17 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +172 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +152 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +271 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +988 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-receive-omp.js +574 -0
- package/mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js +84 -0
- package/mcp/entwurf-bridge/src/index.ts +65 -19
- package/mcp/entwurf-bridge/tsconfig.build.json +19 -3
- package/mcp/tsconfig.json +10 -0
- package/package.json +19 -9
- package/pi/entwurf-capabilities.json +1 -0
- package/pi/meta-bridge-omp/entwurf-meta-omp/package.json +7 -0
- package/pi/omp-receive/entwurf-receive-omp/package.json +7 -0
- package/pi-extensions/entwurf-control.ts +12 -9
- package/pi-extensions/lib/acp/backend-adapter.ts +19 -9
- package/pi-extensions/lib/acp/backend.ts +125 -7
- package/pi-extensions/lib/acp/claude-acp-launch.js +100 -0
- package/pi-extensions/lib/entwurf-self-address.ts +18 -7
- package/pi-extensions/lib/meta-sender-identity.ts +1 -1
- package/pi-extensions/lib/meta-session.ts +219 -5
- package/pi-extensions/lib/mux-fresh-call.ts +171 -17
- package/pi-extensions/lib/omp-fresh-preflight.ts +275 -0
- package/pi-extensions/meta-bridge-omp.ts +1244 -0
- package/pi-extensions/meta-bridge-receive-omp.ts +666 -0
- package/run.sh +399 -31
- package/scripts/check-acp-launch-namespace.ts +127 -0
- package/scripts/check-acp-prompt-lifecycle.ts +145 -2
- package/scripts/check-copilot-birth-hook.ts +28 -1
- package/scripts/check-entwurf-self-address.ts +31 -0
- package/scripts/check-gate-qualification.ts +7 -3
- package/scripts/check-harness-admission-parity.ts +143 -0
- package/scripts/check-meta-doctor-oracle.sh +20 -0
- package/scripts/check-omp-birth-hook.ts +1049 -0
- package/scripts/check-omp-fresh-preflight.ts +208 -0
- package/scripts/check-omp-receive-arm.ts +516 -0
- package/scripts/check-setup-qualification.sh +40 -2
- package/scripts/copilot-bridge-oracle.sh +14 -6
- package/scripts/fake-copilot-vendor.sh +4 -2
- package/scripts/inventory-verification-surface.ts +1 -1
- package/scripts/meta-bridge-hook-log.sh +9 -1
- package/scripts/mutants/acp-launch-namespace.json +34 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +67 -2
- package/scripts/mutants/copilot-birth.json +10 -10
- package/scripts/mutants/mux-fresh-call.json +22 -22
- package/scripts/mutants/omp-birth.json +173 -0
- package/scripts/mutants/omp-fresh.json +300 -0
- package/scripts/mutants/omp-receive.json +135 -0
- package/scripts/mutants/pack-install.json +2 -2
- package/scripts/mutants/self-address.json +34 -0
- package/scripts/mutants/setup-verdict.json +35 -0
- package/scripts/omp-bridge-doctor.sh +315 -0
- package/scripts/omp-bridge-install.sh +221 -0
- package/scripts/omp-bridge-oracle.sh +154 -0
- package/scripts/omp-bridge-uninstall.sh +57 -0
- package/scripts/omp-config-xdev.py +310 -0
- package/scripts/omp-config-xdev.sh +76 -0
- package/scripts/omp-mcp-bridge.sh +320 -0
- package/scripts/omp-mcp-config.py +392 -0
- package/scripts/omp-receive-doctor.sh +246 -0
- package/scripts/omp-receive-facts.ts +106 -0
- package/scripts/omp-receive-install.sh +228 -0
- package/scripts/omp-receive-uninstall.sh +60 -0
- package/scripts/omp-tool-surface.py +400 -0
- package/scripts/raw-acp-child-exit-measure/README.md +285 -0
- package/scripts/raw-acp-child-exit-measure/acp-turn-population.py +89 -0
- package/scripts/raw-acp-child-exit-measure/reaper-correlation.py +47 -0
- package/scripts/raw-omp-measure/README.md +420 -0
- package/scripts/raw-omp-measure/probe-extension.ts +76 -0
- package/scripts/raw-omp-measure/probe-receive-surface.ts +250 -0
- package/scripts/raw-omp-measure/source-audit.md +414 -0
- package/scripts/smoke-omp-bridge-state.sh +221 -0
- package/scripts/smoke-omp-fresh-live.ts +497 -0
- package/scripts/smoke-omp-mcp-state.sh +327 -0
- package/scripts/smoke-omp-receive-live.ts +340 -0
- package/scripts/smoke-omp-receive-state.sh +196 -0
- package/scripts/smoke-setup-verdict.sh +48 -3
- package/scripts/tsconfig.json +2 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# OMP MCP install adapter (#87 step 5). The birth extension and the MCP hand stay separate
|
|
3
|
+
# surfaces: registration is TOOLS, not identity (`docs/external-mcp-host.md`), and the two
|
|
4
|
+
# fail in different ways for different reasons.
|
|
5
|
+
#
|
|
6
|
+
# Owns ONE `mcpServers.entwurf-bridge` key inside the omp-native user MCP file
|
|
7
|
+
# (`<omp agent dir>/mcp.json`). The key is a PINNED LITERAL — it is byte-identical to the
|
|
8
|
+
# key omp's Claude-import provider produces, which is what makes the native entry SHADOW
|
|
9
|
+
# the import instead of loading beside it (`capability/index.ts:84-91`, `:183`, `:203-207`).
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
REPO_DIR="$(cd "$HERE/.." && pwd)"
|
|
13
|
+
CONFIG_PY="$HERE/omp-mcp-config.py"
|
|
14
|
+
STATE_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/omp-mcp"
|
|
15
|
+
STATE_FILE="$STATE_DIR/install-state.json"
|
|
16
|
+
|
|
17
|
+
log() { printf '%s\n' "$*"; }
|
|
18
|
+
fail() { printf 'FAIL: %s\n' "$*" >&2; exit 1; }
|
|
19
|
+
|
|
20
|
+
# shellcheck source=scripts/omp-bridge-oracle.sh
|
|
21
|
+
. "$HERE/omp-bridge-oracle.sh"
|
|
22
|
+
|
|
23
|
+
AGENT_DIR="$(omp_agent_dir)" || fail "the omp agent directory this host reads is ambiguous (see above) — refusing to write a config file omp may never read."
|
|
24
|
+
# THE TARGET IS NOT CONFIGURABLE (#87 D1). It is exactly `<resolved omp agent dir>/mcp.json`
|
|
25
|
+
# and nothing else. The retired `ENTWURF_OMP_MCP_CONFIG` took an arbitrary path with no
|
|
26
|
+
# descendant check, so the "omp-only" writer could be aimed at ~/.claude.json, ~/.pi/... or
|
|
27
|
+
# any regular file — an explicit env seam lowers the odds of an accident but grants no
|
|
28
|
+
# ownership, and cross-harness non-disturbance is this lane's whole point. Sandboxing is
|
|
29
|
+
# still available where it belongs: `ENTWURF_OMP_AGENT_DIR` moves the AGENT DIR, and the
|
|
30
|
+
# target follows it by construction.
|
|
31
|
+
CONFIG="$AGENT_DIR/mcp.json"
|
|
32
|
+
|
|
33
|
+
# The invocation, split exactly the way the Claude installer splits it (meta-bridge-install.sh
|
|
34
|
+
# `desired_mcp`): an installed package wires the STABLE `entwurf-bridge` bin shim (baking a
|
|
35
|
+
# pnpm store path would go stale on any version bump), a dev clone pins this clone's own
|
|
36
|
+
# start.sh. Both carry the same env, and it is the omp label, not Claude's.
|
|
37
|
+
case "$REPO_DIR" in
|
|
38
|
+
*/node_modules/@junghanacs/entwurf)
|
|
39
|
+
COMMAND="entwurf-bridge"
|
|
40
|
+
ARGS_JSON='[]' ;;
|
|
41
|
+
*)
|
|
42
|
+
COMMAND="bash"
|
|
43
|
+
ARGS_JSON="$(python3 -c 'import json,sys;print(json.dumps([sys.argv[1]]))' "$REPO_DIR/mcp/entwurf-bridge/start.sh")" ;;
|
|
44
|
+
esac
|
|
45
|
+
COMMAND="${ENTWURF_OMP_MCP_COMMAND:-$COMMAND}"
|
|
46
|
+
[ -n "${ENTWURF_OMP_MCP_COMMAND:-}" ] && ARGS_JSON="${ENTWURF_OMP_MCP_ARGS:-[]}"
|
|
47
|
+
|
|
48
|
+
# The Claude-import sources omp translates on this platform, in the vendor's own order
|
|
49
|
+
# (`docs/mcp-config.md` "Imported tool configs"). Read-only: this adapter never writes to
|
|
50
|
+
# another tool's config (neither does the vendor — `mcp/config-writer.ts:301-304`).
|
|
51
|
+
IMPORT_PATHS=("$HOME/.claude.json" "$HOME/.claude/mcp.json" "$HOME/.mcp.json")
|
|
52
|
+
|
|
53
|
+
run_config() {
|
|
54
|
+
local out rc
|
|
55
|
+
set +e
|
|
56
|
+
out="$(python3 "$CONFIG_PY" "$@" 2>&1)"
|
|
57
|
+
rc=$?
|
|
58
|
+
set -e
|
|
59
|
+
printf '%s\n' "$out"
|
|
60
|
+
return "$rc"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
BOOT_PROBED_INVOCATION=""
|
|
64
|
+
BOOT_PROBED_RC=1
|
|
65
|
+
BOOT_PROBED_OUT=""
|
|
66
|
+
BOOT_DETAIL=""
|
|
67
|
+
command_boots() {
|
|
68
|
+
local invocation="$1" out rc
|
|
69
|
+
if [ "$invocation" = "$BOOT_PROBED_INVOCATION" ]; then
|
|
70
|
+
BOOT_DETAIL="$BOOT_PROBED_OUT"
|
|
71
|
+
return "$BOOT_PROBED_RC"
|
|
72
|
+
fi
|
|
73
|
+
set +e
|
|
74
|
+
out="$("$REPO_DIR/run.sh" probe-bridge-command --invocation-json "$invocation" 2>&1)"
|
|
75
|
+
rc=$?
|
|
76
|
+
set -e
|
|
77
|
+
BOOT_PROBED_INVOCATION="$invocation"; BOOT_PROBED_RC="$rc"; BOOT_PROBED_OUT="$out"; BOOT_DETAIL="$out"
|
|
78
|
+
return "$rc"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
do_install() {
|
|
82
|
+
log "[omp-mcp install]"
|
|
83
|
+
log " target: $CONFIG"
|
|
84
|
+
log " command: $COMMAND $ARGS_JSON"
|
|
85
|
+
log " key: entwurf-bridge (pinned literal — same key as the Claude import, which is what shadows it)"
|
|
86
|
+
log " state: $STATE_FILE"
|
|
87
|
+
local out rc
|
|
88
|
+
set +e
|
|
89
|
+
out="$(run_config install "$CONFIG" "$COMMAND" "$ARGS_JSON" "$STATE_FILE")"
|
|
90
|
+
rc=$?
|
|
91
|
+
set -e
|
|
92
|
+
case "$rc" in
|
|
93
|
+
0) log " ok: $out" ;;
|
|
94
|
+
3) fail "refused — $out" ;;
|
|
95
|
+
4) fail "invalid config/state — $out" ;;
|
|
96
|
+
5) fail "usage — $out" ;;
|
|
97
|
+
*) fail "install error (rc=$rc) — $out" ;;
|
|
98
|
+
esac
|
|
99
|
+
log " installed. Verify with: ./run.sh doctor-omp-mcp"
|
|
100
|
+
log " NOTE: an omp session that is ALREADY OPEN keeps the servers it connected at start;"
|
|
101
|
+
log " restart it to pick the native entry up."
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
do_uninstall() {
|
|
105
|
+
log "[omp-mcp uninstall]"
|
|
106
|
+
local out rc
|
|
107
|
+
set +e
|
|
108
|
+
out="$(run_config uninstall "$STATE_FILE")"
|
|
109
|
+
rc=$?
|
|
110
|
+
set -e
|
|
111
|
+
case "$rc" in
|
|
112
|
+
0) log " ok: $out" ;;
|
|
113
|
+
2) log " note: $out" ;;
|
|
114
|
+
3) fail "refused — $out" ;;
|
|
115
|
+
*) fail "uninstall error (rc=$rc) — $out" ;;
|
|
116
|
+
esac
|
|
117
|
+
log " the Claude import (if this host has one) becomes the effective source again —"
|
|
118
|
+
log " which is exactly the borrowed provenance this surface exists to replace."
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
state_target() {
|
|
122
|
+
python3 - "$STATE_FILE" <<'PY'
|
|
123
|
+
import json, os, sys
|
|
124
|
+
try:
|
|
125
|
+
state = json.load(open(sys.argv[1]))
|
|
126
|
+
required = ("serverKey", "command", "args", "detectMode", "configExistedBefore", "preimage")
|
|
127
|
+
if state.get("schemaVersion") != 1 or any(key not in state for key in required): raise ValueError()
|
|
128
|
+
if state.get("serverKey") != "entwurf-bridge": raise ValueError()
|
|
129
|
+
value = state.get("managedConfigPath")
|
|
130
|
+
if not isinstance(value, str) or not os.path.isabs(value): raise ValueError()
|
|
131
|
+
print(os.path.abspath(value))
|
|
132
|
+
except Exception:
|
|
133
|
+
raise SystemExit(1)
|
|
134
|
+
PY
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
do_doctor() {
|
|
138
|
+
log "[omp-mcp doctor]"
|
|
139
|
+
log " agent dir: $AGENT_DIR"
|
|
140
|
+
local hard_fail=0 status out rc installed=0
|
|
141
|
+
[ -f "$STATE_FILE" ] && installed=1
|
|
142
|
+
set +e
|
|
143
|
+
out="$(run_config doctor-static "$CONFIG" "$COMMAND")"
|
|
144
|
+
rc=$?
|
|
145
|
+
set -e
|
|
146
|
+
status="${out##*$'\n'}"
|
|
147
|
+
if [ "$rc" -ne 0 ]; then
|
|
148
|
+
log " config: unexpected config reader failure (rc=$rc): $out"
|
|
149
|
+
[ "$installed" -eq 1 ] && hard_fail=1
|
|
150
|
+
else
|
|
151
|
+
case "$status" in
|
|
152
|
+
configured\ *)
|
|
153
|
+
local invocation
|
|
154
|
+
if ! invocation="$(python3 "$CONFIG_PY" doctor-invocation "$CONFIG" "$COMMAND")"; then
|
|
155
|
+
log " config: configured → '$COMMAND' but its exact invocation is unreadable"
|
|
156
|
+
[ "$installed" -eq 1 ] && hard_fail=1
|
|
157
|
+
elif command_boots "$invocation"; then
|
|
158
|
+
log " config: configured → the exact configured invocation boots the entwurf MCP surface"
|
|
159
|
+
else
|
|
160
|
+
log " config: configured → '$COMMAND' does NOT serve MCP with its configured args/env"
|
|
161
|
+
log " $BOOT_DETAIL"
|
|
162
|
+
[ "$installed" -eq 1 ] && hard_fail=1
|
|
163
|
+
fi ;;
|
|
164
|
+
foreign-provenance)
|
|
165
|
+
# The one status that is red even with no install-state: an entwurf-bridge
|
|
166
|
+
# entry under the omp-native key that introduces this session as some other
|
|
167
|
+
# harness is worse than no entry at all.
|
|
168
|
+
log " config: FOREIGN PROVENANCE — the native entry does not carry ENTWURF_BRIDGE_EXTERNAL_AGENT_ID=external-mcp/omp, so omp sessions would speak under another harness's name"
|
|
169
|
+
hard_fail=1 ;;
|
|
170
|
+
self-disabled)
|
|
171
|
+
log " config: DENYLISTED — 'entwurf-bridge' is in disabledServers. Suppression is by NAME and a suppressed item still claims the dedupe key, so this kills the native entry AND the Claude import (mcp/config.ts:123-127, capability/index.ts:191-196). Remove the denylist entry; it is never the way to hide an import"
|
|
172
|
+
hard_fail=1 ;;
|
|
173
|
+
symlink)
|
|
174
|
+
log " config: REFUSED symlink (someone else's SSOT)"
|
|
175
|
+
[ "$installed" -eq 1 ] && hard_fail=1 ;;
|
|
176
|
+
invalid-json|invalid-entry)
|
|
177
|
+
# RUNTIME truth, independent of OWNERSHIP truth (Hard Rule 13, #87 B4). An
|
|
178
|
+
# unreadable file or a malformed entry under our key is broken for omp
|
|
179
|
+
# whether or not entwurf installed anything here — and the entry still
|
|
180
|
+
# claims the dedupe slot, so the import is suppressed too. Red either way.
|
|
181
|
+
log " config: $status — the effective omp-native configuration is BROKEN (runtime axis; ownership state is a separate question)"
|
|
182
|
+
hard_fail=1 ;;
|
|
183
|
+
file-absent|not-ours)
|
|
184
|
+
if [ "$installed" -eq 1 ]; then
|
|
185
|
+
log " config: $status (owned state present — this is drift)"
|
|
186
|
+
hard_fail=1
|
|
187
|
+
else
|
|
188
|
+
log " config: $status (not ours; run install-omp-mcp to write the native entry)"
|
|
189
|
+
fi ;;
|
|
190
|
+
*)
|
|
191
|
+
log " config: unexpected status '$out'"
|
|
192
|
+
[ "$installed" -eq 1 ] && hard_fail=1 ;;
|
|
193
|
+
esac
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# ── the EFFECTIVE source ────────────────────────────────────────────────
|
|
197
|
+
# A CONFIGURATION read, not a runtime receipt, and it says so. It answers the one
|
|
198
|
+
# question the independent-harness stance turns on: when this host's omp starts, is the
|
|
199
|
+
# `entwurf-bridge` it loads OURS, or the Claude import carrying Claude Code's label?
|
|
200
|
+
# Vendor precedence decides it (native=100 > claude=80, first-wins by NAME), so the
|
|
201
|
+
# answer is decidable from the files — but the vendor's own `/mcp list` pane remains
|
|
202
|
+
# the live oracle, taken once as a LIVE receipt rather than re-derived here.
|
|
203
|
+
local shadow verdict
|
|
204
|
+
shadow="$(run_config doctor-shadow "$CONFIG" "${IMPORT_PATHS[@]}")" || true
|
|
205
|
+
verdict="$(printf '%s\n' "$shadow" | sed -n 's/^verdict //p' | tail -1)"
|
|
206
|
+
printf '%s\n' "$shadow" | sed -n 's/^/ /p'
|
|
207
|
+
case "$verdict" in
|
|
208
|
+
native-wins)
|
|
209
|
+
log " effective: the NATIVE entry wins by vendor precedence (native=100 > claude=80, first-wins on the shared key). Any Claude import of the same name is fully suppressed — not both-loaded, not merged, no warning" ;;
|
|
210
|
+
native-invalid)
|
|
211
|
+
# The worst of both: nothing loads, AND the import is still suppressed because a
|
|
212
|
+
# malformed value under the key keeps claiming the dedupe slot. Never reported
|
|
213
|
+
# as native-wins, and never softened by the absence of install-state.
|
|
214
|
+
log " effective: NOTHING — the entry under the native key is malformed, so omp loads no entwurf-bridge, and it still suppresses any Claude import by claiming the key. Repair or remove that entry (run install-omp-mcp to write a valid one)"
|
|
215
|
+
hard_fail=1 ;;
|
|
216
|
+
import-wins)
|
|
217
|
+
if [ "$installed" -eq 1 ]; then
|
|
218
|
+
log " effective: the CLAUDE IMPORT would win — our native entry is missing while ownership state exists. This is the borrowed-provenance state (#87): omp sessions introduce themselves as claude-code"
|
|
219
|
+
hard_fail=1
|
|
220
|
+
else
|
|
221
|
+
log " effective: the CLAUDE IMPORT is currently the only source (not ours; run install-omp-mcp)"
|
|
222
|
+
fi ;;
|
|
223
|
+
both-suppressed)
|
|
224
|
+
log " effective: NOTHING — 'entwurf-bridge' is denylisted, which suppresses the native entry and the import together"
|
|
225
|
+
hard_fail=1 ;;
|
|
226
|
+
*)
|
|
227
|
+
if [ "$installed" -eq 1 ]; then
|
|
228
|
+
log " effective: no entwurf-bridge source at all while ownership state exists — drift"
|
|
229
|
+
hard_fail=1
|
|
230
|
+
else
|
|
231
|
+
log " effective: no entwurf-bridge source on this host yet"
|
|
232
|
+
fi ;;
|
|
233
|
+
esac
|
|
234
|
+
|
|
235
|
+
# ── tool-surface (tools.xdev) — RUNTIME axis, never ownership (Hard Rule 13)
|
|
236
|
+
# The MCP hand can be installed and still not be callable: omp's vendor default
|
|
237
|
+
# (tools.xdev: true, empty xdevInlineDevices) mounts MCP tools as xd:// devices
|
|
238
|
+
# and drops them from the top-level toolset. Absent file or absent key means
|
|
239
|
+
# that default applies; say so rather than reporting on a file that is not there.
|
|
240
|
+
# RED only when the native hand is the effective source (native-wins) AND the
|
|
241
|
+
# surface is uncovered/unreadable — ownership state is a separate question.
|
|
242
|
+
local surface surface_rc surface_verdict surface_file surface_xdev native_hand=0
|
|
243
|
+
case "$verdict" in
|
|
244
|
+
native-wins) native_hand=1 ;;
|
|
245
|
+
esac
|
|
246
|
+
set +e
|
|
247
|
+
surface="$(python3 "$HERE/omp-tool-surface.py" "$AGENT_DIR" 2>&1)"
|
|
248
|
+
surface_rc=$?
|
|
249
|
+
set -e
|
|
250
|
+
if [ "$surface_rc" -ne 0 ]; then
|
|
251
|
+
log " tool-surface: reader failed (rc=$surface_rc): $surface"
|
|
252
|
+
[ "$native_hand" -eq 1 ] && hard_fail=1
|
|
253
|
+
else
|
|
254
|
+
surface_verdict="$(printf '%s\n' "$surface" | sed -n 's/^verdict //p' | tail -1)"
|
|
255
|
+
surface_file="$(printf '%s\n' "$surface" | sed -n 's/^file //p' | tail -1)"
|
|
256
|
+
surface_xdev="$(printf '%s\n' "$surface" | sed -n 's/^xdev //p' | tail -1)"
|
|
257
|
+
case "$surface_verdict" in
|
|
258
|
+
xdev-off)
|
|
259
|
+
log " tool-surface: tools.xdev is false — MCP tools stay top-level (the required operator setting)" ;;
|
|
260
|
+
xdev-on-covered)
|
|
261
|
+
log " tool-surface: note: tools.xdev is on with an xdevInlineDevices glob covering mcp__entwurf_bridge_* — schemas are inlined but tools stay behind xd:// (write to execute). Prefer tools.xdev: false" ;;
|
|
262
|
+
xdev-on-uncovered)
|
|
263
|
+
if [ "$native_hand" -eq 1 ]; then
|
|
264
|
+
if [ "$surface_file" = "absent" ]; then
|
|
265
|
+
log " tool-surface: FILE ABSENT — vendor default applies (tools.xdev: true, no inline allowlist). MCP tools mount as xd:// and drop from the top-level toolset, so the configured native hand is not reliably callable (runtime axis; ownership is a separate question)"
|
|
266
|
+
elif [ "$surface_xdev" = "default-true" ]; then
|
|
267
|
+
log " tool-surface: tools.xdev is UNSET in $surface_file — vendor default applies (true). MCP tools mount as xd:// and drop from the top-level toolset, so the configured native hand is not reliably callable (runtime axis; ownership is a separate question)"
|
|
268
|
+
else
|
|
269
|
+
log " tool-surface: tools.xdev is on with no xdevInlineDevices glob covering mcp__entwurf_bridge_* — MCP tools mount as xd:// and drop from the top-level toolset; a plain-language send was measured to list then falsely claim delivery (runtime axis; ownership is a separate question)"
|
|
270
|
+
fi
|
|
271
|
+
hard_fail=1
|
|
272
|
+
else
|
|
273
|
+
log " tool-surface: note: vendor default tools.xdev:true would wrap MCP tools as xd://. Not red: no native hand is configured"
|
|
274
|
+
fi ;;
|
|
275
|
+
unreadable)
|
|
276
|
+
log " tool-surface: $surface_file is UNREADABLE — cannot determine tools.xdev (runtime axis)"
|
|
277
|
+
[ "$native_hand" -eq 1 ] && hard_fail=1 ;;
|
|
278
|
+
*)
|
|
279
|
+
log " tool-surface: unexpected verdict '$surface_verdict'"
|
|
280
|
+
[ "$native_hand" -eq 1 ] && hard_fail=1 ;;
|
|
281
|
+
esac
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
if [ -f "$STATE_FILE" ]; then
|
|
285
|
+
local managed expected managed_status
|
|
286
|
+
expected="$(python3 -c 'import os,sys; print(os.path.abspath(sys.argv[1]))' "$CONFIG")"
|
|
287
|
+
if ! managed="$(state_target)"; then
|
|
288
|
+
log " state: CORRUPT — $STATE_FILE has no absolute managedConfigPath"
|
|
289
|
+
hard_fail=1
|
|
290
|
+
elif [ "$managed" != "$expected" ]; then
|
|
291
|
+
log " state: FOREIGN TARGET — state manages '$managed', omp reads '$expected'"
|
|
292
|
+
hard_fail=1
|
|
293
|
+
else
|
|
294
|
+
managed_status="$(run_config doctor-static "$managed" "$COMMAND" | tail -1)"
|
|
295
|
+
case "$managed_status" in
|
|
296
|
+
configured\ *) log " state: install-state present; managed config still configures entwurf-bridge under the pinned key." ;;
|
|
297
|
+
file-absent)
|
|
298
|
+
log " state: ORPHANED — managed config disappeared; removing stale state."
|
|
299
|
+
rm -f "$STATE_FILE" ;;
|
|
300
|
+
*)
|
|
301
|
+
log " state: DRIFT — managed config no longer satisfies the MCP contract ($managed_status)."
|
|
302
|
+
hard_fail=1 ;;
|
|
303
|
+
esac
|
|
304
|
+
fi
|
|
305
|
+
else
|
|
306
|
+
log " state: absent (no MCP ownership recorded)"
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
if [ "$hard_fail" -ne 0 ]; then
|
|
310
|
+
fail "doctor found a broken OMP MCP configuration."
|
|
311
|
+
fi
|
|
312
|
+
log "doctor: ok."
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
case "${1:-}" in
|
|
316
|
+
install) do_install ;;
|
|
317
|
+
uninstall) do_uninstall ;;
|
|
318
|
+
doctor) do_doctor ;;
|
|
319
|
+
*) echo "usage: omp-mcp-bridge.sh <install|uninstall|doctor>" >&2; exit 2 ;;
|
|
320
|
+
esac
|