direxio-deployer 0.1.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/AGENTS.md +92 -0
- package/LICENSE +21 -0
- package/README.md +221 -0
- package/README_zh.md +218 -0
- package/SKILL.md +722 -0
- package/agents/README.md +25 -0
- package/agents/openai.yaml +12 -0
- package/bin/direxio-deployer.mjs +375 -0
- package/package.json +28 -0
- package/references/agent-targets.md +128 -0
- package/references/architecture.md +44 -0
- package/references/bug-history.md +78 -0
- package/references/deployment-lessons.md +218 -0
- package/references/deployment-optimization-audit.md +317 -0
- package/references/deployment-workflow.md +341 -0
- package/references/iam-policy.json +52 -0
- package/references/runtime-wiring.md +209 -0
- package/references/state-machine.md +46 -0
- package/references/token-refresh.md +81 -0
- package/references/tooling.md +106 -0
- package/references/troubleshooting.md +26 -0
- package/references/user-journey.md +75 -0
- package/references/verification-recovery.md +84 -0
- package/references/voip-turn-runbook.md +154 -0
- package/references/windows-deployment-notes.md +119 -0
- package/scripts/aws-credentials.sh +195 -0
- package/scripts/cloud-init/Caddyfile +48 -0
- package/scripts/cloud-init/docker-compose.yml +125 -0
- package/scripts/cloud-init/init-tokens.sh +238 -0
- package/scripts/cloud-init/user-data.yaml +40 -0
- package/scripts/destroy.ps1 +77 -0
- package/scripts/destroy.sh +589 -0
- package/scripts/lib/aws.sh +73 -0
- package/scripts/lib/domain.sh +175 -0
- package/scripts/lib/operation_report.sh +240 -0
- package/scripts/lib/ops.sh +230 -0
- package/scripts/lib/paths.sh +35 -0
- package/scripts/lib/state.sh +137 -0
- package/scripts/mcp-tools-list.mjs +95 -0
- package/scripts/orchestrate.ps1 +112 -0
- package/scripts/orchestrate.sh +1126 -0
- package/scripts/phases/s0_prereq_aws.sh +39 -0
- package/scripts/phases/s1_preflight.sh +72 -0
- package/scripts/phases/s2_domain.sh +103 -0
- package/scripts/phases/s3_provision.sh +421 -0
- package/scripts/phases/s4_bootstrap_stack.sh +38 -0
- package/scripts/phases/s5_init_tokens.sh +118 -0
- package/scripts/phases/s6_wire_local.sh +1435 -0
- package/scripts/phases/s7_verify_e2e.sh +136 -0
- package/scripts/pricing-estimate.sh +256 -0
- package/scripts/render/render-userdata.sh +86 -0
- package/scripts/reset-app-data.sh +40 -0
- package/scripts/update.sh +30 -0
- package/tests/aws_credentials_test.sh +139 -0
- package/tests/connect_daemon_runtime_check_test.sh +120 -0
- package/tests/default_paths_test.sh +58 -0
- package/tests/destroy_local_bridge_test.sh +154 -0
- package/tests/destroy_root_identity_test.sh +91 -0
- package/tests/destroy_route53_zone_test.sh +80 -0
- package/tests/domain_authoritative_dns_test.sh +49 -0
- package/tests/mcp_doctor_runtime_check_test.sh +86 -0
- package/tests/mcp_smoke_runtime_check_test.sh +121 -0
- package/tests/mcp_tools_runtime_check_test.sh +123 -0
- package/tests/npm_skill_distribution_test.sh +95 -0
- package/tests/operation_report_test.sh +258 -0
- package/tests/orchestrate_status_recovery_test.sh +91 -0
- package/tests/phase_timeout_test.sh +88 -0
- package/tests/pricing_estimate_test.sh +159 -0
- package/tests/render_userdata_remote_nodes_test.sh +40 -0
- package/tests/root_volume_tracking_test.sh +41 -0
- package/tests/route53_overwrite_guard_test.sh +86 -0
- package/tests/route53_zone_auto_create_test.sh +66 -0
- package/tests/runtime_summary_check_test.sh +203 -0
- package/tests/s6_wire_local_test.sh +405 -0
- package/tests/skill_structure_test.sh +298 -0
- package/tests/update_reset_ops_test.sh +230 -0
- package/tests/user_confirmation_gates_test.sh +152 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
|
5
|
+
tmp=$(mktemp -d)
|
|
6
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
7
|
+
|
|
8
|
+
fakebin="$tmp/bin"
|
|
9
|
+
mkdir -p "$fakebin"
|
|
10
|
+
cat > "$fakebin/dig" <<'EOF'
|
|
11
|
+
#!/usr/bin/env bash
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
if [ "${1:-}" = "+short" ] && [ "${2:-}" = "A" ] && [ "${3:-}" = "app.example.test" ]; then
|
|
15
|
+
printf '203.0.113.88\n'
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [ "${1:-}" = "+short" ] && [ "${2:-}" = "NS" ] && [ "${3:-}" = "app.example.test" ]; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ "${1:-}" = "+short" ] && [ "${2:-}" = "NS" ] && [ "${3:-}" = "example.test" ]; then
|
|
24
|
+
printf 'ns1.example.test.\n'
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if [ "${1:-}" = "+short" ] && [ "${2:-}" = "@ns1.example.test" ] && [ "${3:-}" = "A" ] && [ "${4:-}" = "app.example.test" ]; then
|
|
29
|
+
printf '%s\n' "${AUTHORITATIVE_A:-198.51.100.10}"
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
echo "unexpected dig call: $*" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
EOF
|
|
36
|
+
chmod 700 "$fakebin/dig"
|
|
37
|
+
|
|
38
|
+
PATH="$fakebin:$PATH"
|
|
39
|
+
# shellcheck disable=SC1091
|
|
40
|
+
source "$ROOT/scripts/lib/domain.sh"
|
|
41
|
+
|
|
42
|
+
if AUTHORITATIVE_A=198.51.100.10 domain_resolves_to_ip app.example.test 203.0.113.88; then
|
|
43
|
+
echo "recursive DNS alone must not pass when authoritative DNS still points at a different IP" >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
AUTHORITATIVE_A=203.0.113.88 domain_resolves_to_ip app.example.test 203.0.113.88
|
|
48
|
+
|
|
49
|
+
echo "domain authoritative dns ok"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
|
5
|
+
tmp=$(mktemp -d)
|
|
6
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
7
|
+
|
|
8
|
+
export HOME="$tmp/home"
|
|
9
|
+
mkdir -p "$HOME"
|
|
10
|
+
|
|
11
|
+
fakebin="$tmp/bin"
|
|
12
|
+
mkdir -p "$fakebin"
|
|
13
|
+
cat > "$fakebin/direxio-mcp" <<'EOF'
|
|
14
|
+
#!/usr/bin/env bash
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
[ "${1:-}" = "doctor" ]
|
|
18
|
+
[ "${2:-}" = "--json" ]
|
|
19
|
+
[ "${DIREXIO_CREDENTIALS_FILE:-}" = "${EXPECTED_CREDENTIALS_FILE:-}" ]
|
|
20
|
+
|
|
21
|
+
cat <<JSON
|
|
22
|
+
{
|
|
23
|
+
"ok": true,
|
|
24
|
+
"domain": "mcp-check.example.test",
|
|
25
|
+
"agent_room_id": "!agent:mcp-check.example.test",
|
|
26
|
+
"token": "redacted"
|
|
27
|
+
}
|
|
28
|
+
JSON
|
|
29
|
+
EOF
|
|
30
|
+
chmod 700 "$fakebin/direxio-mcp"
|
|
31
|
+
|
|
32
|
+
service_dir="$HOME/.direxio/nodes/mcp-check.example.test"
|
|
33
|
+
mkdir -p "$service_dir"
|
|
34
|
+
credentials="$service_dir/credentials.json"
|
|
35
|
+
: > "$credentials"
|
|
36
|
+
expected_credentials="$credentials"
|
|
37
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
38
|
+
expected_credentials=$(cygpath -m "$expected_credentials")
|
|
39
|
+
fi
|
|
40
|
+
state="$service_dir/state.json"
|
|
41
|
+
jq -n \
|
|
42
|
+
--arg service_dir "$service_dir" \
|
|
43
|
+
--arg credentials "$credentials" \
|
|
44
|
+
'{
|
|
45
|
+
run_id: "mcp-doctor-test",
|
|
46
|
+
region: "ap-northeast-1",
|
|
47
|
+
domain_mode: "user",
|
|
48
|
+
domain: "mcp-check.example.test",
|
|
49
|
+
agent_service_id: "mcp-check.example.test",
|
|
50
|
+
agent_service_dir: $service_dir,
|
|
51
|
+
agent_credentials_file: $credentials,
|
|
52
|
+
mcp_credentials_file: $credentials,
|
|
53
|
+
mcp_command: "direxio-mcp",
|
|
54
|
+
phase: "S7_VERIFY_E2E",
|
|
55
|
+
phases: {
|
|
56
|
+
S0_PREREQ_AWS: {status: "done"},
|
|
57
|
+
S1_PREFLIGHT: {status: "done"},
|
|
58
|
+
S2_DOMAIN: {status: "done"},
|
|
59
|
+
S3_PROVISION: {status: "done"},
|
|
60
|
+
S4_BOOTSTRAP_STACK: {status: "done"},
|
|
61
|
+
S5_INIT_TOKENS: {status: "done"},
|
|
62
|
+
S6_WIRE_LOCAL: {status: "done"},
|
|
63
|
+
S7_VERIFY_E2E: {status: "done"}
|
|
64
|
+
},
|
|
65
|
+
resources: {}
|
|
66
|
+
}' > "$state"
|
|
67
|
+
|
|
68
|
+
verify_output=$(P2P_WORKDIR="$service_dir" PATH="$fakebin:$PATH" EXPECTED_CREDENTIALS_FILE="$expected_credentials" bash "$ROOT/scripts/orchestrate.sh" verify mcp_doctor)
|
|
69
|
+
printf '%s\n' "$verify_output" | grep -q 'verified runtime check: mcp_doctor'
|
|
70
|
+
|
|
71
|
+
jq -e '
|
|
72
|
+
.runtime_checks.mcp_doctor.status == "passed"
|
|
73
|
+
and .runtime_checks.mcp_doctor.domain == "mcp-check.example.test"
|
|
74
|
+
and .runtime_checks.mcp_doctor.agent_room_id == "!agent:mcp-check.example.test"
|
|
75
|
+
and .runtime_checks.mcp_doctor.token == "redacted"
|
|
76
|
+
and (.user_confirmations.agent_mcp_runtime | not)
|
|
77
|
+
' "$state" >/dev/null
|
|
78
|
+
|
|
79
|
+
report_output=$(P2P_WORKDIR="$service_dir" bash "$ROOT/scripts/orchestrate.sh" report new_deploy)
|
|
80
|
+
report_path=$(printf '%s\n' "$report_output" | sed -nE 's/^operation report: //p' | tail -n 1)
|
|
81
|
+
jq -e '
|
|
82
|
+
.runtime_checks.mcp_doctor.status == "passed"
|
|
83
|
+
and .gates.user_confirmation.agent_mcp_runtime == "pending_runtime_confirmation"
|
|
84
|
+
' "$report_path" >/dev/null
|
|
85
|
+
|
|
86
|
+
echo "mcp doctor runtime check ok"
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
|
5
|
+
tmp=$(mktemp -d)
|
|
6
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
7
|
+
|
|
8
|
+
export HOME="$tmp/home"
|
|
9
|
+
mkdir -p "$HOME"
|
|
10
|
+
|
|
11
|
+
fakebin="$tmp/bin"
|
|
12
|
+
mkdir -p "$fakebin"
|
|
13
|
+
cat > "$fakebin/curl" <<'EOF'
|
|
14
|
+
#!/usr/bin/env bash
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
printf '%s\n' "$*" >> "$CURL_CALLS"
|
|
18
|
+
|
|
19
|
+
want_url="https://mcp-smoke.example.test/_p2p/query"
|
|
20
|
+
case " $* " in
|
|
21
|
+
*" $want_url "*|*" $want_url")
|
|
22
|
+
;;
|
|
23
|
+
*)
|
|
24
|
+
echo "unexpected curl URL: $*" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
;;
|
|
27
|
+
esac
|
|
28
|
+
|
|
29
|
+
case " $* " in
|
|
30
|
+
*"Authorization: Bearer AGENT_TOKEN_SMOKE"*) ;;
|
|
31
|
+
*)
|
|
32
|
+
echo "missing or wrong Authorization header: $*" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
case " $* " in
|
|
38
|
+
*'"action":"mcp.messages.list"'*'"room_id":"!agent:mcp-smoke.example.test"'*) ;;
|
|
39
|
+
*)
|
|
40
|
+
echo "wrong smoke request body: $*" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
|
|
45
|
+
body_path=""
|
|
46
|
+
write_code=0
|
|
47
|
+
while [ "$#" -gt 0 ]; do
|
|
48
|
+
case "$1" in
|
|
49
|
+
-o)
|
|
50
|
+
body_path=$2
|
|
51
|
+
shift 2
|
|
52
|
+
;;
|
|
53
|
+
-w)
|
|
54
|
+
write_code=1
|
|
55
|
+
shift 2
|
|
56
|
+
;;
|
|
57
|
+
*)
|
|
58
|
+
shift
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
done
|
|
62
|
+
|
|
63
|
+
payload='{"room_id":"!agent:mcp-smoke.example.test","messages":[]}'
|
|
64
|
+
if [ -n "$body_path" ]; then
|
|
65
|
+
printf '%s\n' "$payload" > "$body_path"
|
|
66
|
+
else
|
|
67
|
+
printf '%s\n' "$payload"
|
|
68
|
+
fi
|
|
69
|
+
[ "$write_code" -eq 1 ] && printf '200'
|
|
70
|
+
EOF
|
|
71
|
+
chmod 700 "$fakebin/curl"
|
|
72
|
+
|
|
73
|
+
service_dir="$HOME/.direxio/nodes/mcp-smoke.example.test"
|
|
74
|
+
mkdir -p "$service_dir"
|
|
75
|
+
state="$service_dir/state.json"
|
|
76
|
+
jq -n \
|
|
77
|
+
--arg service_dir "$service_dir" \
|
|
78
|
+
'{
|
|
79
|
+
run_id: "mcp-smoke-test",
|
|
80
|
+
region: "ap-northeast-1",
|
|
81
|
+
domain_mode: "user",
|
|
82
|
+
domain: "mcp-smoke.example.test",
|
|
83
|
+
as_url: "https://mcp-smoke.example.test",
|
|
84
|
+
agent_service_id: "mcp-smoke.example.test",
|
|
85
|
+
agent_service_dir: $service_dir,
|
|
86
|
+
agent_token: "AGENT_TOKEN_SMOKE",
|
|
87
|
+
agent_room_id: "!agent:mcp-smoke.example.test",
|
|
88
|
+
phase: "S7_VERIFY_E2E",
|
|
89
|
+
phases: {
|
|
90
|
+
S0_PREREQ_AWS: {status: "done"},
|
|
91
|
+
S1_PREFLIGHT: {status: "done"},
|
|
92
|
+
S2_DOMAIN: {status: "done"},
|
|
93
|
+
S3_PROVISION: {status: "done"},
|
|
94
|
+
S4_BOOTSTRAP_STACK: {status: "done"},
|
|
95
|
+
S5_INIT_TOKENS: {status: "done"},
|
|
96
|
+
S6_WIRE_LOCAL: {status: "done"},
|
|
97
|
+
S7_VERIFY_E2E: {status: "done"}
|
|
98
|
+
},
|
|
99
|
+
resources: {}
|
|
100
|
+
}' > "$state"
|
|
101
|
+
|
|
102
|
+
calls="$tmp/curl.calls"
|
|
103
|
+
verify_output=$(P2P_WORKDIR="$service_dir" PATH="$fakebin:$PATH" CURL_CALLS="$calls" bash "$ROOT/scripts/orchestrate.sh" verify mcp_smoke)
|
|
104
|
+
printf '%s\n' "$verify_output" | grep -q 'verified runtime check: mcp_smoke'
|
|
105
|
+
|
|
106
|
+
jq -e '
|
|
107
|
+
.runtime_checks.mcp_smoke.status == "passed"
|
|
108
|
+
and .runtime_checks.mcp_smoke.action == "mcp.messages.list"
|
|
109
|
+
and .runtime_checks.mcp_smoke.room_id == "!agent:mcp-smoke.example.test"
|
|
110
|
+
and .runtime_checks.mcp_smoke.response_messages_type == "array"
|
|
111
|
+
and (.user_confirmations.agent_mcp_runtime | not)
|
|
112
|
+
' "$state" >/dev/null
|
|
113
|
+
|
|
114
|
+
report_output=$(P2P_WORKDIR="$service_dir" bash "$ROOT/scripts/orchestrate.sh" report new_deploy)
|
|
115
|
+
report_path=$(printf '%s\n' "$report_output" | sed -nE 's/^operation report: //p' | tail -n 1)
|
|
116
|
+
jq -e '
|
|
117
|
+
.runtime_checks.mcp_smoke.status == "passed"
|
|
118
|
+
and .gates.user_confirmation.agent_mcp_runtime == "pending_runtime_confirmation"
|
|
119
|
+
' "$report_path" >/dev/null
|
|
120
|
+
|
|
121
|
+
echo "mcp smoke runtime check ok"
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
|
5
|
+
tmp=$(mktemp -d "$ROOT/.tmp-mcp-tools.XXXXXX")
|
|
6
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
7
|
+
|
|
8
|
+
export HOME="$tmp/home"
|
|
9
|
+
mkdir -p "$HOME"
|
|
10
|
+
|
|
11
|
+
fakebin="$tmp/bin"
|
|
12
|
+
mkdir -p "$fakebin"
|
|
13
|
+
|
|
14
|
+
windows_path() {
|
|
15
|
+
local path=$1 drive rest
|
|
16
|
+
case "$path" in
|
|
17
|
+
/mnt/[A-Za-z]/*)
|
|
18
|
+
drive=${path#/mnt/}
|
|
19
|
+
drive=${drive%%/*}
|
|
20
|
+
rest=${path#/mnt/$drive/}
|
|
21
|
+
printf '%s:\\%s\n' "$(printf '%s' "$drive" | tr '[:lower:]' '[:upper:]')" "$(printf '%s' "$rest" | sed 's#/#\\#g')"
|
|
22
|
+
;;
|
|
23
|
+
/[A-Za-z]/*)
|
|
24
|
+
drive=${path#/}
|
|
25
|
+
drive=${drive%%/*}
|
|
26
|
+
rest=${path#/$drive/}
|
|
27
|
+
printf '%s:\\%s\n' "$(printf '%s' "$drive" | tr '[:lower:]' '[:upper:]')" "$(printf '%s' "$rest" | sed 's#/#\\#g')"
|
|
28
|
+
;;
|
|
29
|
+
*) printf '%s\n' "$path" ;;
|
|
30
|
+
esac
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
cat > "$fakebin/direxio-mcp" <<'EOF'
|
|
34
|
+
#!/usr/bin/env bash
|
|
35
|
+
set -euo pipefail
|
|
36
|
+
if [ "${DIREXIO_CREDENTIALS_FILE:-}" != "${EXPECTED_CREDENTIALS_FILE:-}" ]; then
|
|
37
|
+
echo "wrong DIREXIO_CREDENTIALS_FILE" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
printf '%s\n' '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"fake-direxio-mcp","version":"0.0.0"}}}'
|
|
42
|
+
printf '%s\n' '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"search_rooms","description":"Search rooms"},{"name":"send_message","description":"Send message"},{"name":"list_messages","description":"List messages"}]}}'
|
|
43
|
+
EOF
|
|
44
|
+
chmod 700 "$fakebin/direxio-mcp"
|
|
45
|
+
|
|
46
|
+
cat > "$tmp/fake-mcp.ps1" <<'EOF'
|
|
47
|
+
if ($env:DIREXIO_CREDENTIALS_FILE -ne $env:EXPECTED_CREDENTIALS_FILE) {
|
|
48
|
+
[Console]::Error.WriteLine("wrong DIREXIO_CREDENTIALS_FILE")
|
|
49
|
+
exit 1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
[Console]::Out.WriteLine('{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"fake-direxio-mcp","version":"0.0.0"}}}')
|
|
53
|
+
[Console]::Out.WriteLine('{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"search_rooms","description":"Search rooms"},{"name":"send_message","description":"Send message"},{"name":"list_messages","description":"List messages"}]}}')
|
|
54
|
+
EOF
|
|
55
|
+
|
|
56
|
+
mcp_command=direxio-mcp
|
|
57
|
+
case "$(uname -s)" in
|
|
58
|
+
MINGW*|MSYS*|CYGWIN*) use_windows_mcp=1 ;;
|
|
59
|
+
*) use_windows_mcp=0 ;;
|
|
60
|
+
esac
|
|
61
|
+
if { [ "$use_windows_mcp" = "1" ] || ! command -v node >/dev/null 2>&1; } && command -v node.exe >/dev/null 2>&1; then
|
|
62
|
+
fake_mcp_ps1=$(windows_path "$tmp/fake-mcp.ps1")
|
|
63
|
+
mcp_command="powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"$fake_mcp_ps1\""
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
service_dir="$HOME/.direxio/nodes/mcp-tools.example.test"
|
|
67
|
+
mkdir -p "$service_dir"
|
|
68
|
+
credentials="$service_dir/credentials.json"
|
|
69
|
+
: > "$credentials"
|
|
70
|
+
expected_credentials="$credentials"
|
|
71
|
+
if command -v cygpath >/dev/null 2>&1; then
|
|
72
|
+
expected_credentials=$(cygpath -m "$expected_credentials")
|
|
73
|
+
fi
|
|
74
|
+
state="$service_dir/state.json"
|
|
75
|
+
jq -n \
|
|
76
|
+
--arg service_dir "$service_dir" \
|
|
77
|
+
--arg credentials "$credentials" \
|
|
78
|
+
--arg mcp_command "$mcp_command" \
|
|
79
|
+
'{
|
|
80
|
+
run_id: "mcp-tools-test",
|
|
81
|
+
region: "ap-northeast-1",
|
|
82
|
+
domain_mode: "user",
|
|
83
|
+
domain: "mcp-tools.example.test",
|
|
84
|
+
agent_service_id: "mcp-tools.example.test",
|
|
85
|
+
agent_service_dir: $service_dir,
|
|
86
|
+
agent_credentials_file: $credentials,
|
|
87
|
+
mcp_credentials_file: $credentials,
|
|
88
|
+
mcp_command: $mcp_command,
|
|
89
|
+
phase: "S7_VERIFY_E2E",
|
|
90
|
+
phases: {
|
|
91
|
+
S0_PREREQ_AWS: {status: "done"},
|
|
92
|
+
S1_PREFLIGHT: {status: "done"},
|
|
93
|
+
S2_DOMAIN: {status: "done"},
|
|
94
|
+
S3_PROVISION: {status: "done"},
|
|
95
|
+
S4_BOOTSTRAP_STACK: {status: "done"},
|
|
96
|
+
S5_INIT_TOKENS: {status: "done"},
|
|
97
|
+
S6_WIRE_LOCAL: {status: "done"},
|
|
98
|
+
S7_VERIFY_E2E: {status: "done"}
|
|
99
|
+
},
|
|
100
|
+
resources: {}
|
|
101
|
+
}' > "$state"
|
|
102
|
+
|
|
103
|
+
verify_output=$(P2P_WORKDIR="$service_dir" PATH="$fakebin:$PATH" EXPECTED_CREDENTIALS_FILE="$expected_credentials" bash "$ROOT/scripts/orchestrate.sh" verify mcp_tools)
|
|
104
|
+
printf '%s\n' "$verify_output" | grep -q 'verified runtime check: mcp_tools'
|
|
105
|
+
|
|
106
|
+
jq -e '
|
|
107
|
+
.runtime_checks.mcp_tools.status == "passed"
|
|
108
|
+
and .runtime_checks.mcp_tools.tool_count == 3
|
|
109
|
+
and (.runtime_checks.mcp_tools.tools | index("search_rooms") != null)
|
|
110
|
+
and (.runtime_checks.mcp_tools.tools | index("send_message") != null)
|
|
111
|
+
and (.runtime_checks.mcp_tools.tools | index("list_messages") != null)
|
|
112
|
+
and (.user_confirmations.agent_mcp_runtime | not)
|
|
113
|
+
' "$state" >/dev/null
|
|
114
|
+
|
|
115
|
+
report_output=$(P2P_WORKDIR="$service_dir" bash "$ROOT/scripts/orchestrate.sh" report new_deploy)
|
|
116
|
+
report_path=$(printf '%s\n' "$report_output" | sed -nE 's/^operation report: //p' | tail -n 1)
|
|
117
|
+
jq -e '
|
|
118
|
+
.runtime_checks.mcp_tools.status == "passed"
|
|
119
|
+
and .runtime_checks.mcp_tools.tool_count == 3
|
|
120
|
+
and .gates.user_confirmation.agent_mcp_runtime == "pending_runtime_confirmation"
|
|
121
|
+
' "$report_path" >/dev/null
|
|
122
|
+
|
|
123
|
+
echo "mcp tools runtime check ok"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
|
5
|
+
cd "$ROOT"
|
|
6
|
+
|
|
7
|
+
assert_file_exists() {
|
|
8
|
+
[ -f "$1" ] || {
|
|
9
|
+
echo "missing expected file: $1" >&2
|
|
10
|
+
exit 1
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
assert_contains() {
|
|
15
|
+
local path=$1 pattern=$2
|
|
16
|
+
grep -q "$pattern" "$path" || {
|
|
17
|
+
echo "expected $path to contain: $pattern" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
tmp=$(mktemp -d)
|
|
23
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
24
|
+
|
|
25
|
+
NODE_BIN=${NODE:-node}
|
|
26
|
+
|
|
27
|
+
"$NODE_BIN" -e '
|
|
28
|
+
const pkg = require("./package.json");
|
|
29
|
+
if (pkg.name !== "direxio-deployer") throw new Error("unexpected package name");
|
|
30
|
+
if (!pkg.bin || pkg.bin["direxio-deployer"] !== "bin/direxio-deployer.mjs") {
|
|
31
|
+
throw new Error("missing direxio-deployer bin");
|
|
32
|
+
}
|
|
33
|
+
'
|
|
34
|
+
|
|
35
|
+
project="$tmp/project"
|
|
36
|
+
mkdir -p "$project"
|
|
37
|
+
|
|
38
|
+
"$NODE_BIN" bin/direxio-deployer.mjs skill install --agent codex --scope project --project "$project" > "$tmp/install.out"
|
|
39
|
+
target="$project/.codex/skills/direxio-deployer"
|
|
40
|
+
assert_file_exists "$target/SKILL.md"
|
|
41
|
+
assert_file_exists "$target/references/agent-targets.md"
|
|
42
|
+
assert_file_exists "$target/scripts/orchestrate.sh"
|
|
43
|
+
assert_file_exists "$target/.direxio-skill-install.json"
|
|
44
|
+
assert_contains "$target/.direxio-skill-install.json" '"agent": "codex"'
|
|
45
|
+
assert_contains "$target/.direxio-skill-install.json" '"scope": "project"'
|
|
46
|
+
|
|
47
|
+
printf 'stale\n' > "$target/STALE.txt"
|
|
48
|
+
"$NODE_BIN" bin/direxio-deployer.mjs skill update --agent codex --scope project --project "$project" > "$tmp/update.out"
|
|
49
|
+
if [ -f "$target/STALE.txt" ]; then
|
|
50
|
+
echo "managed update should replace stale target contents" >&2
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
unmanaged_project="$tmp/unmanaged"
|
|
55
|
+
mkdir -p "$unmanaged_project/.codex/skills/direxio-deployer"
|
|
56
|
+
printf 'manual\n' > "$unmanaged_project/.codex/skills/direxio-deployer/manual.txt"
|
|
57
|
+
if "$NODE_BIN" bin/direxio-deployer.mjs skill install --agent codex --scope project --project "$unmanaged_project" >"$tmp/unmanaged.out" 2>"$tmp/unmanaged.err"; then
|
|
58
|
+
echo "unmanaged install should require --force" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
assert_contains "$tmp/unmanaged.err" 'refusing to overwrite unmanaged target'
|
|
62
|
+
|
|
63
|
+
"$NODE_BIN" bin/direxio-deployer.mjs skill install --agent codex --scope project --project "$unmanaged_project" --force > "$tmp/force.out"
|
|
64
|
+
assert_file_exists "$unmanaged_project/.codex/skills/direxio-deployer/SKILL.md"
|
|
65
|
+
if [ -f "$unmanaged_project/.codex/skills/direxio-deployer/manual.txt" ]; then
|
|
66
|
+
echo "forced install should replace unmanaged contents" >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
"$NODE_BIN" bin/direxio-deployer.mjs skill install --agent gemini --scope global --home "$tmp/home" --dry-run > "$tmp/dry-run.out"
|
|
71
|
+
assert_contains "$tmp/dry-run.out" '"dryRun": true'
|
|
72
|
+
assert_contains "$tmp/dry-run.out" '.gemini'
|
|
73
|
+
if [ -e "$tmp/home/.gemini" ]; then
|
|
74
|
+
echo "dry-run should not create global target directories" >&2
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
PI_CODING_AGENT_DIR="$tmp/pi-agent-root" "$NODE_BIN" bin/direxio-deployer.mjs skill install --agent pi --scope global --dry-run > "$tmp/pi-global.out"
|
|
79
|
+
assert_contains "$tmp/pi-global.out" 'pi-agent-root'
|
|
80
|
+
assert_contains "$tmp/pi-global.out" 'skills'
|
|
81
|
+
if grep -q 'pi-agent-root.*/agent/skills' "$tmp/pi-global.out"; then
|
|
82
|
+
echo "PI_CODING_AGENT_DIR already points at the agent root and must not append another agent segment" >&2
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
custom_target="$tmp/custom target/skill"
|
|
87
|
+
"$NODE_BIN" bin/direxio-deployer.mjs skill install --agent codex --target "$custom_target" > "$tmp/custom-target.out"
|
|
88
|
+
assert_file_exists "$custom_target/SKILL.md"
|
|
89
|
+
assert_file_exists "$custom_target/.direxio-skill-install.json"
|
|
90
|
+
|
|
91
|
+
"$NODE_BIN" bin/direxio-deployer.mjs skill refresh --agent codex --scope project --project "$project" --dry-run > "$tmp/refresh.out"
|
|
92
|
+
assert_contains "$tmp/refresh.out" '"command": "refresh"'
|
|
93
|
+
assert_contains "$tmp/refresh.out" '"target"'
|
|
94
|
+
|
|
95
|
+
echo "npm skill distribution ok"
|