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.
Files changed (77) hide show
  1. package/AGENTS.md +92 -0
  2. package/LICENSE +21 -0
  3. package/README.md +221 -0
  4. package/README_zh.md +218 -0
  5. package/SKILL.md +722 -0
  6. package/agents/README.md +25 -0
  7. package/agents/openai.yaml +12 -0
  8. package/bin/direxio-deployer.mjs +375 -0
  9. package/package.json +28 -0
  10. package/references/agent-targets.md +128 -0
  11. package/references/architecture.md +44 -0
  12. package/references/bug-history.md +78 -0
  13. package/references/deployment-lessons.md +218 -0
  14. package/references/deployment-optimization-audit.md +317 -0
  15. package/references/deployment-workflow.md +341 -0
  16. package/references/iam-policy.json +52 -0
  17. package/references/runtime-wiring.md +209 -0
  18. package/references/state-machine.md +46 -0
  19. package/references/token-refresh.md +81 -0
  20. package/references/tooling.md +106 -0
  21. package/references/troubleshooting.md +26 -0
  22. package/references/user-journey.md +75 -0
  23. package/references/verification-recovery.md +84 -0
  24. package/references/voip-turn-runbook.md +154 -0
  25. package/references/windows-deployment-notes.md +119 -0
  26. package/scripts/aws-credentials.sh +195 -0
  27. package/scripts/cloud-init/Caddyfile +48 -0
  28. package/scripts/cloud-init/docker-compose.yml +125 -0
  29. package/scripts/cloud-init/init-tokens.sh +238 -0
  30. package/scripts/cloud-init/user-data.yaml +40 -0
  31. package/scripts/destroy.ps1 +77 -0
  32. package/scripts/destroy.sh +589 -0
  33. package/scripts/lib/aws.sh +73 -0
  34. package/scripts/lib/domain.sh +175 -0
  35. package/scripts/lib/operation_report.sh +240 -0
  36. package/scripts/lib/ops.sh +230 -0
  37. package/scripts/lib/paths.sh +35 -0
  38. package/scripts/lib/state.sh +137 -0
  39. package/scripts/mcp-tools-list.mjs +95 -0
  40. package/scripts/orchestrate.ps1 +112 -0
  41. package/scripts/orchestrate.sh +1126 -0
  42. package/scripts/phases/s0_prereq_aws.sh +39 -0
  43. package/scripts/phases/s1_preflight.sh +72 -0
  44. package/scripts/phases/s2_domain.sh +103 -0
  45. package/scripts/phases/s3_provision.sh +421 -0
  46. package/scripts/phases/s4_bootstrap_stack.sh +38 -0
  47. package/scripts/phases/s5_init_tokens.sh +118 -0
  48. package/scripts/phases/s6_wire_local.sh +1435 -0
  49. package/scripts/phases/s7_verify_e2e.sh +136 -0
  50. package/scripts/pricing-estimate.sh +256 -0
  51. package/scripts/render/render-userdata.sh +86 -0
  52. package/scripts/reset-app-data.sh +40 -0
  53. package/scripts/update.sh +30 -0
  54. package/tests/aws_credentials_test.sh +139 -0
  55. package/tests/connect_daemon_runtime_check_test.sh +120 -0
  56. package/tests/default_paths_test.sh +58 -0
  57. package/tests/destroy_local_bridge_test.sh +154 -0
  58. package/tests/destroy_root_identity_test.sh +91 -0
  59. package/tests/destroy_route53_zone_test.sh +80 -0
  60. package/tests/domain_authoritative_dns_test.sh +49 -0
  61. package/tests/mcp_doctor_runtime_check_test.sh +86 -0
  62. package/tests/mcp_smoke_runtime_check_test.sh +121 -0
  63. package/tests/mcp_tools_runtime_check_test.sh +123 -0
  64. package/tests/npm_skill_distribution_test.sh +95 -0
  65. package/tests/operation_report_test.sh +258 -0
  66. package/tests/orchestrate_status_recovery_test.sh +91 -0
  67. package/tests/phase_timeout_test.sh +88 -0
  68. package/tests/pricing_estimate_test.sh +159 -0
  69. package/tests/render_userdata_remote_nodes_test.sh +40 -0
  70. package/tests/root_volume_tracking_test.sh +41 -0
  71. package/tests/route53_overwrite_guard_test.sh +86 -0
  72. package/tests/route53_zone_auto_create_test.sh +66 -0
  73. package/tests/runtime_summary_check_test.sh +203 -0
  74. package/tests/s6_wire_local_test.sh +405 -0
  75. package/tests/skill_structure_test.sh +298 -0
  76. package/tests/update_reset_ops_test.sh +230 -0
  77. package/tests/user_confirmation_gates_test.sh +152 -0
@@ -0,0 +1,41 @@
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
+ export P2P_WORKDIR="$tmp/work"
10
+ mkdir -p "$HOME" "$P2P_WORKDIR"
11
+
12
+ fakebin="$tmp/bin"
13
+ mkdir -p "$fakebin"
14
+ cat > "$fakebin/aws" <<'EOF'
15
+ #!/usr/bin/env bash
16
+ set -euo pipefail
17
+ case "${1:-} ${2:-}" in
18
+ "ec2 describe-instances")
19
+ printf 'vol-root-test\n'
20
+ ;;
21
+ *)
22
+ echo "unexpected aws command: $*" >&2
23
+ exit 1
24
+ ;;
25
+ esac
26
+ EOF
27
+ chmod 700 "$fakebin/aws"
28
+ export PATH="$fakebin:$PATH"
29
+
30
+ # shellcheck disable=SC1091
31
+ source "$ROOT/scripts/lib/state.sh"
32
+ state_init >/dev/null 2>&1
33
+
34
+ # shellcheck disable=SC1091
35
+ source "$ROOT/scripts/phases/s3_provision.sh"
36
+
37
+ _record_root_volume_id i-root-test
38
+
39
+ jq -e '.resources.root_volume_id == "vol-root-test"' "$STATE_JSON" >/dev/null
40
+
41
+ echo "root volume tracking 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
+ export P2P_WORKDIR="$tmp/work"
10
+ mkdir -p "$HOME" "$P2P_WORKDIR"
11
+
12
+ fakebin="$tmp/bin"
13
+ mkdir -p "$fakebin"
14
+ cat > "$fakebin/aws" <<'EOF'
15
+ #!/usr/bin/env bash
16
+ set -euo pipefail
17
+ printf 'aws' >> "$CALLS"
18
+ printf ' %q' "$@" >> "$CALLS"
19
+ printf '\n' >> "$CALLS"
20
+
21
+ case "${1:-} ${2:-}" in
22
+ "route53 list-hosted-zones")
23
+ printf '{"HostedZones":[{"Id":"/hostedzone/ZEXISTING","Name":"overwrite.example.test."}]}\n'
24
+ ;;
25
+ "route53 list-resource-record-sets")
26
+ printf '{"ResourceRecordSets":[{"Name":"overwrite.example.test.","Type":"A","TTL":60,"ResourceRecords":[{"Value":"198.51.100.10"}]}]}\n'
27
+ ;;
28
+ "route53 change-resource-record-sets")
29
+ printf '{"ChangeInfo":{"Id":"/change/C123","Status":"PENDING"}}\n'
30
+ ;;
31
+ "route53 wait")
32
+ exit 0
33
+ ;;
34
+ *)
35
+ echo "unexpected aws command: $*" >&2
36
+ exit 1
37
+ ;;
38
+ esac
39
+ EOF
40
+ chmod 700 "$fakebin/aws"
41
+ export PATH="$fakebin:$PATH"
42
+
43
+ # shellcheck disable=SC1091
44
+ source "$ROOT/scripts/lib/state.sh"
45
+ state_init >/dev/null 2>&1
46
+ state_set run_id route53-overwrite-test
47
+ state_set domain overwrite.example.test
48
+ state_set domain_mode route53
49
+
50
+ # shellcheck disable=SC1091
51
+ source "$ROOT/scripts/phases/s3_provision.sh"
52
+
53
+ CALLS="$tmp/blocked.calls"
54
+ export CALLS
55
+ set +e
56
+ _upsert_route53_record overwrite.example.test 203.0.113.88 > "$tmp/blocked.out" 2>&1
57
+ blocked_rc=$?
58
+ set -e
59
+ [ "$blocked_rc" -eq 2 ] || {
60
+ echo "Route53 overwrite without confirmation should return waiting_user rc=2" >&2
61
+ cat "$tmp/blocked.out" >&2
62
+ exit 1
63
+ }
64
+ grep -q 'Route53 A record overwrite requires confirmation' "$tmp/blocked.out"
65
+ if grep -q 'route53 change-resource-record-sets' "$CALLS"; then
66
+ echo "Route53 overwrite without confirmation must not change records" >&2
67
+ cat "$CALLS" >&2
68
+ exit 1
69
+ fi
70
+ jq -e '
71
+ .phases.S3_PROVISION.status == "waiting_user"
72
+ and .resources.route53_existing_a_value == "198.51.100.10"
73
+ and .resources.route53_pending_a_value == "203.0.113.88"
74
+ ' "$STATE_JSON" >/dev/null
75
+
76
+ CALLS="$tmp/confirmed.calls"
77
+ export CALLS
78
+ DIREXIO_CONFIRM_DNS_OVERWRITE=1 _upsert_route53_record overwrite.example.test 203.0.113.88
79
+ grep -q 'route53 change-resource-record-sets' "$CALLS"
80
+ jq -e '
81
+ .resources.route53_existing_a_value == "198.51.100.10"
82
+ and .resources.route53_pending_a_value == "203.0.113.88"
83
+ and .resources.route53_overwrite_confirmed == "true"
84
+ ' "$STATE_JSON" >/dev/null
85
+
86
+ echo "route53 overwrite guard ok"
@@ -0,0 +1,66 @@
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
+ export P2P_WORKDIR="$tmp/work"
10
+ mkdir -p "$HOME" "$P2P_WORKDIR"
11
+
12
+ fakebin="$tmp/bin"
13
+ mkdir -p "$fakebin"
14
+ cat > "$fakebin/aws" <<'EOF'
15
+ #!/usr/bin/env bash
16
+ set -euo pipefail
17
+ printf 'aws' >> "$CALLS"
18
+ printf ' %q' "$@" >> "$CALLS"
19
+ printf '\n' >> "$CALLS"
20
+
21
+ case "${1:-} ${2:-}" in
22
+ "route53 list-hosted-zones")
23
+ printf '{"HostedZones":[]}\n'
24
+ ;;
25
+ "route53 create-hosted-zone")
26
+ printf '{"HostedZone":{"Id":"/hostedzone/ZCREATE","Name":"auto-zone.example.test."},"DelegationSet":{"NameServers":["ns-1.awsdns.test","ns-2.awsdns.test"]}}\n'
27
+ ;;
28
+ "route53 change-resource-record-sets")
29
+ printf '{"ChangeInfo":{"Id":"/change/C123","Status":"PENDING"}}\n'
30
+ ;;
31
+ "route53 wait")
32
+ exit 0
33
+ ;;
34
+ *)
35
+ echo "unexpected aws command: $*" >&2
36
+ exit 1
37
+ ;;
38
+ esac
39
+ EOF
40
+ chmod 700 "$fakebin/aws"
41
+ export CALLS="$tmp/aws.calls"
42
+ export PATH="$fakebin:$PATH"
43
+
44
+ # shellcheck disable=SC1091
45
+ source "$ROOT/scripts/lib/state.sh"
46
+ state_init >/dev/null 2>&1
47
+ state_set run_id route53-test
48
+ state_set domain auto-zone.example.test
49
+ state_set domain_mode route53
50
+
51
+ # shellcheck disable=SC1091
52
+ source "$ROOT/scripts/phases/s3_provision.sh"
53
+
54
+ _upsert_route53_record auto-zone.example.test 203.0.113.88
55
+
56
+ jq -e '
57
+ .resources.route53_zone_id == "ZCREATE"
58
+ and .resources.route53_zone_name == "auto-zone.example.test"
59
+ and .resources.route53_zone_created_by_deployer == "true"
60
+ and (.resources.route53_name_servers | contains("ns-1.awsdns.test"))
61
+ ' "$STATE_JSON" >/dev/null
62
+
63
+ grep -q 'route53 create-hosted-zone' "$CALLS"
64
+ grep -q 'route53 change-resource-record-sets' "$CALLS"
65
+
66
+ echo "route53 zone auto create ok"
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT=$(cd "$(dirname "$0")/.." && pwd)
5
+ tmp=$(mktemp -d "$ROOT/.tmp-runtime-summary.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-connect" <<'EOF'
34
+ #!/usr/bin/env bash
35
+ set -euo pipefail
36
+ [ "${1:-}" = "daemon" ]
37
+ [ "${2:-}" = "status" ]
38
+ [ "${3:-}" = "--service-name" ]
39
+ [ "${4:-}" = "runtime-summary.example.test" ]
40
+ cat <<STATUS
41
+ cc-connect daemon status
42
+
43
+ Status: Running
44
+ Platform: test
45
+ WorkDir: ${CONNECT_WORK_DIR:-}
46
+ STATUS
47
+ EOF
48
+ chmod 700 "$fakebin/direxio-connect"
49
+
50
+ cat > "$fakebin/direxio-mcp" <<'EOF'
51
+ #!/usr/bin/env bash
52
+ set -euo pipefail
53
+ if [ "${DIREXIO_CREDENTIALS_FILE:-}" != "${EXPECTED_CREDENTIALS_FILE:-}" ]; then
54
+ echo "wrong DIREXIO_CREDENTIALS_FILE" >&2
55
+ exit 1
56
+ fi
57
+
58
+ if [ "${1:-}" = "doctor" ] && [ "${2:-}" = "--json" ]; then
59
+ printf '{"ok":true,"domain":"runtime-summary.example.test","agent_room_id":"!agent:runtime-summary.example.test","token":"redacted"}\n'
60
+ exit 0
61
+ fi
62
+
63
+ frame() {
64
+ local body=$1
65
+ printf 'Content-Length: %s\r\n\r\n%s' "${#body}" "$body"
66
+ }
67
+
68
+ frame '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"fake-direxio-mcp","version":"0.0.0"}}}'
69
+ frame '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"search_rooms"},{"name":"send_message"},{"name":"list_messages"}]}}'
70
+ EOF
71
+ chmod 700 "$fakebin/direxio-mcp"
72
+
73
+ cat > "$tmp/fake-mcp.ps1" <<'EOF'
74
+ if ($env:DIREXIO_CREDENTIALS_FILE -ne $env:EXPECTED_CREDENTIALS_FILE) {
75
+ [Console]::Error.WriteLine("wrong DIREXIO_CREDENTIALS_FILE")
76
+ exit 1
77
+ }
78
+
79
+ if (($args.Count -ge 2) -and ($args[0] -eq "doctor") -and ($args[1] -eq "--json")) {
80
+ [Console]::Out.WriteLine('{"ok":true,"domain":"runtime-summary.example.test","agent_room_id":"!agent:runtime-summary.example.test","token":"redacted"}')
81
+ exit 0
82
+ }
83
+
84
+ function Frame($body) {
85
+ [Console]::Out.Write("Content-Length: $($body.Length)`r`n`r`n$body")
86
+ }
87
+
88
+ Frame '{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"fake-direxio-mcp","version":"0.0.0"}}}'
89
+ Frame '{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"search_rooms"},{"name":"send_message"},{"name":"list_messages"}]}}'
90
+ EOF
91
+
92
+ mcp_command=direxio-mcp
93
+ case "$(uname -s)" in
94
+ MINGW*|MSYS*|CYGWIN*) use_windows_mcp=1 ;;
95
+ *) use_windows_mcp=0 ;;
96
+ esac
97
+ if { [ "$use_windows_mcp" = "1" ] || ! command -v node >/dev/null 2>&1; } && command -v node.exe >/dev/null 2>&1; then
98
+ fake_mcp_ps1=$(windows_path "$tmp/fake-mcp.ps1")
99
+ mcp_command="powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"$fake_mcp_ps1\""
100
+ fi
101
+
102
+ cat > "$fakebin/curl" <<'EOF'
103
+ #!/usr/bin/env bash
104
+ set -euo pipefail
105
+ body_path=""
106
+ write_code=0
107
+ while [ "$#" -gt 0 ]; do
108
+ case "$1" in
109
+ -o) body_path=$2; shift 2 ;;
110
+ -w) write_code=1; shift 2 ;;
111
+ *) shift ;;
112
+ esac
113
+ done
114
+ payload='{"room_id":"!agent:runtime-summary.example.test","messages":[]}'
115
+ if [ -n "$body_path" ]; then
116
+ printf '%s\n' "$payload" > "$body_path"
117
+ else
118
+ printf '%s\n' "$payload"
119
+ fi
120
+ [ "$write_code" -eq 1 ] && printf '200'
121
+ EOF
122
+ chmod 700 "$fakebin/curl"
123
+
124
+ service_dir="$HOME/.direxio/nodes/runtime-summary.example.test"
125
+ mkdir -p "$service_dir/cc-connect"
126
+ credentials="$service_dir/credentials.json"
127
+ config="$service_dir/cc-connect/config.toml"
128
+ : > "$credentials"
129
+ : > "$config"
130
+ expected_credentials="$credentials"
131
+ if command -v cygpath >/dev/null 2>&1; then
132
+ expected_credentials=$(cygpath -m "$expected_credentials")
133
+ fi
134
+ state="$service_dir/state.json"
135
+ jq -n \
136
+ --arg service_dir "$service_dir" \
137
+ --arg credentials "$credentials" \
138
+ --arg config "$config" \
139
+ --arg mcp_command "$mcp_command" \
140
+ '{
141
+ run_id: "runtime-summary-test",
142
+ region: "ap-northeast-1",
143
+ domain_mode: "user",
144
+ domain: "runtime-summary.example.test",
145
+ as_url: "https://runtime-summary.example.test",
146
+ agent_service_id: "runtime-summary.example.test",
147
+ agent_service_dir: $service_dir,
148
+ agent_credentials_file: $credentials,
149
+ mcp_credentials_file: $credentials,
150
+ mcp_command: $mcp_command,
151
+ agent_token: "AGENT_TOKEN_RUNTIME",
152
+ agent_room_id: "!agent:runtime-summary.example.test",
153
+ cc_connect_config: $config,
154
+ cc_connect_binary: "direxio-connect",
155
+ phase: "S7_VERIFY_E2E",
156
+ phases: {
157
+ S0_PREREQ_AWS: {status: "done"},
158
+ S1_PREFLIGHT: {status: "done"},
159
+ S2_DOMAIN: {status: "done"},
160
+ S3_PROVISION: {status: "done"},
161
+ S4_BOOTSTRAP_STACK: {status: "done"},
162
+ S5_INIT_TOKENS: {status: "done"},
163
+ S6_WIRE_LOCAL: {status: "done"},
164
+ S7_VERIFY_E2E: {status: "done"}
165
+ },
166
+ resources: {}
167
+ }' > "$state"
168
+
169
+ verify_output=$(P2P_WORKDIR="$service_dir" PATH="$fakebin:$PATH" EXPECTED_CREDENTIALS_FILE="$expected_credentials" CONNECT_WORK_DIR="$service_dir/cc-connect" bash "$ROOT/scripts/orchestrate.sh" verify runtime)
170
+ printf '%s\n' "$verify_output" | grep -q 'verified runtime checks: passed'
171
+
172
+ jq -e '
173
+ .runtime_checks.summary.status == "passed"
174
+ and .runtime_checks.summary.failed_count == 0
175
+ and .runtime_checks.summary.checks.connect_daemon == "passed"
176
+ and .runtime_checks.summary.checks.mcp_doctor == "passed"
177
+ and .runtime_checks.summary.checks.mcp_tools == "passed"
178
+ and .runtime_checks.summary.checks.mcp_smoke == "passed"
179
+ and (.user_confirmations.agent_mcp_runtime | not)
180
+ ' "$state" >/dev/null
181
+
182
+ report_output=$(P2P_WORKDIR="$service_dir" bash "$ROOT/scripts/orchestrate.sh" report new_deploy)
183
+ report_path=$(printf '%s\n' "$report_output" | sed -nE 's/^operation report: //p' | tail -n 1)
184
+ jq -e '.runtime_checks.summary.status == "passed"' "$report_path" >/dev/null
185
+
186
+ set +e
187
+ P2P_WORKDIR="$service_dir" PATH="$fakebin:$PATH" EXPECTED_CREDENTIALS_FILE="$expected_credentials" CONNECT_WORK_DIR="$HOME/.direxio/nodes/other.example.test/cc-connect" bash "$ROOT/scripts/orchestrate.sh" verify runtime > "$tmp/runtime-fail.out" 2>&1
188
+ fail_rc=$?
189
+ set -e
190
+ [ "$fail_rc" -ne 0 ] || {
191
+ echo "runtime summary must fail when any runtime check fails" >&2
192
+ exit 1
193
+ }
194
+ jq -e '
195
+ .runtime_checks.summary.status == "failed"
196
+ and .runtime_checks.summary.failed_count == 1
197
+ and .runtime_checks.summary.checks.connect_daemon == "failed"
198
+ and .runtime_checks.summary.checks.mcp_doctor == "passed"
199
+ and .runtime_checks.summary.checks.mcp_tools == "passed"
200
+ and .runtime_checks.summary.checks.mcp_smoke == "passed"
201
+ ' "$state" >/dev/null
202
+
203
+ echo "runtime summary check ok"