direxio-deployer 0.1.0 → 0.1.2
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/README.md +10 -2
- package/README_zh.md +10 -2
- package/SKILL.md +32 -8
- package/bin/direxio-deployer.mjs +1 -2
- package/package.json +2 -3
- package/references/agent-targets.md +7 -1
- package/references/deployment-lessons.md +5 -7
- package/references/deployment-workflow.md +8 -4
- package/references/runtime-wiring.md +5 -5
- package/references/tooling.md +11 -12
- package/references/user-journey.md +2 -2
- package/references/voip-turn-runbook.md +2 -2
- package/references/windows-deployment-notes.md +2 -1
- package/scripts/destroy.sh +24 -43
- package/scripts/json.mjs +841 -0
- package/scripts/lib/aws.sh +5 -1
- package/scripts/lib/json.sh +114 -0
- package/scripts/lib/operation_report.sh +8 -195
- package/scripts/lib/ops.sh +8 -21
- package/scripts/lib/state.sh +18 -44
- package/scripts/mcp-tools-list.mjs +66 -5
- package/scripts/orchestrate.sh +166 -249
- package/scripts/phases/s3_provision.sh +5 -10
- package/scripts/phases/s5_init_tokens.sh +7 -17
- package/scripts/phases/s6_wire_local.sh +22 -42
- package/scripts/phases/s7_verify_e2e.sh +5 -5
- package/scripts/pricing-estimate.sh +36 -80
- package/tests/aws_credentials_test.sh +0 -139
- package/tests/connect_daemon_runtime_check_test.sh +0 -120
- package/tests/default_paths_test.sh +0 -58
- package/tests/destroy_local_bridge_test.sh +0 -154
- package/tests/destroy_root_identity_test.sh +0 -91
- package/tests/destroy_route53_zone_test.sh +0 -80
- package/tests/domain_authoritative_dns_test.sh +0 -49
- package/tests/mcp_doctor_runtime_check_test.sh +0 -86
- package/tests/mcp_smoke_runtime_check_test.sh +0 -121
- package/tests/mcp_tools_runtime_check_test.sh +0 -123
- package/tests/npm_skill_distribution_test.sh +0 -95
- package/tests/operation_report_test.sh +0 -258
- package/tests/orchestrate_status_recovery_test.sh +0 -91
- package/tests/phase_timeout_test.sh +0 -88
- package/tests/pricing_estimate_test.sh +0 -159
- package/tests/render_userdata_remote_nodes_test.sh +0 -40
- package/tests/root_volume_tracking_test.sh +0 -41
- package/tests/route53_overwrite_guard_test.sh +0 -86
- package/tests/route53_zone_auto_create_test.sh +0 -66
- package/tests/runtime_summary_check_test.sh +0 -203
- package/tests/s6_wire_local_test.sh +0 -405
- package/tests/skill_structure_test.sh +0 -298
- package/tests/update_reset_ops_test.sh +0 -230
- package/tests/user_confirmation_gates_test.sh +0 -152
|
@@ -1,123 +0,0 @@
|
|
|
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"
|
|
@@ -1,95 +0,0 @@
|
|
|
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"
|
|
@@ -1,258 +0,0 @@
|
|
|
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
|
-
assert_file_exists() {
|
|
12
|
-
[ -s "$1" ] || {
|
|
13
|
-
echo "expected non-empty file: $1" >&2
|
|
14
|
-
exit 1
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
assert_not_contains_secret() {
|
|
19
|
-
local path=$1
|
|
20
|
-
if grep -E '12345678|87654321|ACCESS_SECRET|AGENT_SECRET|AWS_SECRET' "$path" >/dev/null; then
|
|
21
|
-
echo "operation report leaked a secret: $path" >&2
|
|
22
|
-
cat "$path" >&2
|
|
23
|
-
exit 1
|
|
24
|
-
fi
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
service_dir="$HOME/.direxio/nodes/report.example.test"
|
|
28
|
-
mkdir -p "$service_dir"
|
|
29
|
-
state="$service_dir/state.json"
|
|
30
|
-
jq -n \
|
|
31
|
-
--arg service_dir "$service_dir" \
|
|
32
|
-
'{
|
|
33
|
-
run_id: "report-test",
|
|
34
|
-
region: "ap-northeast-1",
|
|
35
|
-
domain_mode: "route53",
|
|
36
|
-
domain: "report.example.test",
|
|
37
|
-
as_url: "https://report.example.test",
|
|
38
|
-
instance_type: "t3.small",
|
|
39
|
-
password: "12345678",
|
|
40
|
-
access_token: "ACCESS_SECRET",
|
|
41
|
-
agent_token: "AGENT_SECRET",
|
|
42
|
-
agent_room_id: "!room:report.example.test",
|
|
43
|
-
agent_node_id: "node-report",
|
|
44
|
-
agent_service_id: "report.example.test",
|
|
45
|
-
agent_service_dir: $service_dir,
|
|
46
|
-
agent_credentials_file: ($service_dir + "/credentials.json"),
|
|
47
|
-
cc_connect_config: ($service_dir + "/cc-connect/config.toml"),
|
|
48
|
-
cc_connect_agent: "acp",
|
|
49
|
-
cc_connect_npm_package: "direxio-connent@latest",
|
|
50
|
-
mcp_npm_package: "direxio-mcp@latest",
|
|
51
|
-
mcp_server_name: "direxio-report-example-test",
|
|
52
|
-
mcp_config_dir: ($service_dir + "/mcp"),
|
|
53
|
-
mcp_codex_config: ($service_dir + "/mcp/codex.toml"),
|
|
54
|
-
mcp_openclaw_config: ($service_dir + "/mcp/openclaw.md"),
|
|
55
|
-
mcp_hermes_config: ($service_dir + "/mcp/hermes.mcp.json"),
|
|
56
|
-
mcp_doctor_command: "DIREXIO_CREDENTIALS_FILE=<redacted> direxio-mcp doctor --json",
|
|
57
|
-
phase: "S7_VERIFY_E2E",
|
|
58
|
-
phases: {
|
|
59
|
-
S0_PREREQ_AWS: {status: "done"},
|
|
60
|
-
S1_PREFLIGHT: {status: "done"},
|
|
61
|
-
S2_DOMAIN: {status: "done"},
|
|
62
|
-
S3_PROVISION: {status: "done"},
|
|
63
|
-
S4_BOOTSTRAP_STACK: {status: "done"},
|
|
64
|
-
S5_INIT_TOKENS: {status: "done"},
|
|
65
|
-
S6_WIRE_LOCAL: {status: "done"},
|
|
66
|
-
S7_VERIFY_E2E: {status: "done"}
|
|
67
|
-
},
|
|
68
|
-
user_confirmations: {
|
|
69
|
-
app_initialization: {
|
|
70
|
-
status: "confirmed",
|
|
71
|
-
ts: "2026-06-28T01:02:03Z",
|
|
72
|
-
evidence: "user completed app initialization with code 12345678; old screenshot showed code 87654321"
|
|
73
|
-
},
|
|
74
|
-
real_chat: {
|
|
75
|
-
status: "confirmed",
|
|
76
|
-
ts: "2026-06-28T01:03:04Z",
|
|
77
|
-
evidence: "user saw the agent reply; token ACCESS_SECRET stayed local"
|
|
78
|
-
},
|
|
79
|
-
agent_mcp_runtime: {
|
|
80
|
-
status: "confirmed",
|
|
81
|
-
ts: "2026-06-28T01:04:05Z",
|
|
82
|
-
evidence: "runtime channel probe confirmed with agent token AGENT_SECRET",
|
|
83
|
-
runtime_summary_status: "passed",
|
|
84
|
-
runtime_probe_confirmed: true
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
resources: {
|
|
88
|
-
instance_id: "i-report",
|
|
89
|
-
root_volume_id: "vol-report-root",
|
|
90
|
-
public_ip: "203.0.113.42",
|
|
91
|
-
eip_id: "eipalloc-report",
|
|
92
|
-
route53_zone_id: "ZREPORT",
|
|
93
|
-
route53_zone_name: "report.example.test",
|
|
94
|
-
route53_zone_created_by_deployer: "true",
|
|
95
|
-
route53_existing_a_value: "198.51.100.20",
|
|
96
|
-
route53_pending_a_value: "203.0.113.42",
|
|
97
|
-
route53_overwrite_confirmed: "true",
|
|
98
|
-
sg_id: "sg-report",
|
|
99
|
-
key_name: "direxio-report"
|
|
100
|
-
}
|
|
101
|
-
}' > "$state"
|
|
102
|
-
|
|
103
|
-
report_output=$(P2P_WORKDIR="$service_dir" bash "$ROOT/scripts/orchestrate.sh" report new_deploy)
|
|
104
|
-
report_path=$(printf '%s\n' "$report_output" | sed -nE 's/^operation report: //p' | tail -n 1)
|
|
105
|
-
assert_file_exists "$report_path"
|
|
106
|
-
assert_not_contains_secret "$report_path"
|
|
107
|
-
|
|
108
|
-
jq -e '
|
|
109
|
-
.operation_type == "new_deploy"
|
|
110
|
-
and .status == "automated_gates_complete_user_confirmation_pending"
|
|
111
|
-
and .domain == "report.example.test"
|
|
112
|
-
and .delivery.app_domain == "report.example.test"
|
|
113
|
-
and (.delivery | has("service_url") | not)
|
|
114
|
-
and .delivery.init_code_status == "available_in_state_password_field_redacted"
|
|
115
|
-
and .delivery.init_code_secret_redacted == true
|
|
116
|
-
and .delivery.product_completion_status == "automated_gates_complete_user_confirmation_pending"
|
|
117
|
-
and .agent.node_id == "node-report"
|
|
118
|
-
and .agent.room_id == "!room:report.example.test"
|
|
119
|
-
and .agent.runtime == "unknown"
|
|
120
|
-
and .gates.automated.S7_VERIFY_E2E == "done"
|
|
121
|
-
and .gates.user_confirmation.app_initialization == "confirmed"
|
|
122
|
-
and .gates.user_confirmation.real_chat == "confirmed"
|
|
123
|
-
and .gates.user_confirmation.agent_mcp_runtime == "confirmed"
|
|
124
|
-
and .gates.user_confirmation_details.app_initialization.status == "confirmed"
|
|
125
|
-
and .gates.user_confirmation_details.app_initialization.ts == "2026-06-28T01:02:03Z"
|
|
126
|
-
and .gates.user_confirmation_details.app_initialization.evidence == "user completed app initialization with code <redacted>; old screenshot showed code <redacted>"
|
|
127
|
-
and .gates.user_confirmation_details.real_chat.evidence == "user saw the agent reply; token <redacted> stayed local"
|
|
128
|
-
and .gates.user_confirmation_details.agent_mcp_runtime.evidence == "runtime channel probe confirmed with agent token <redacted>"
|
|
129
|
-
and .gates.user_confirmation_details.agent_mcp_runtime.runtime_summary_status == "passed"
|
|
130
|
-
and .gates.user_confirmation_details.agent_mcp_runtime.runtime_probe_confirmed == true
|
|
131
|
-
and .credentials.values_redacted == true
|
|
132
|
-
and .security.secrets_included == false
|
|
133
|
-
and .mcp.package == "direxio-mcp@latest"
|
|
134
|
-
and .resources.route53_zone_id == "ZREPORT"
|
|
135
|
-
and .resources.route53_zone_name == "report.example.test"
|
|
136
|
-
and .resources.route53_existing_a_value == "198.51.100.20"
|
|
137
|
-
and .resources.route53_pending_a_value == "203.0.113.42"
|
|
138
|
-
and .resources.route53_overwrite_confirmed == "true"
|
|
139
|
-
and .resources.root_volume_id == "vol-report-root"
|
|
140
|
-
and (.billing.recorded_billable_resources | index("EC2 i-report") != null)
|
|
141
|
-
and (.billing.recorded_billable_resources | index("EBS root volume vol-report-root") != null)
|
|
142
|
-
and (.billing.recorded_billable_resources | index("public IPv4 203.0.113.42") != null)
|
|
143
|
-
and (.billing.recorded_billable_resources | index("Route53 hosted zone ZREPORT") != null)
|
|
144
|
-
and .security.root_access_key_allowed == true
|
|
145
|
-
and .security.temporary_iam_cleanup_required == true
|
|
146
|
-
and (.security.temporary_iam_cleanup_action | contains("delete or disable"))
|
|
147
|
-
' "$report_path" >/dev/null
|
|
148
|
-
|
|
149
|
-
fakebin="$tmp/bin"
|
|
150
|
-
mkdir -p "$fakebin"
|
|
151
|
-
cat > "$fakebin/aws" <<'EOF'
|
|
152
|
-
#!/usr/bin/env bash
|
|
153
|
-
set -euo pipefail
|
|
154
|
-
case "${1:-} ${2:-}" in
|
|
155
|
-
"sts get-caller-identity")
|
|
156
|
-
case "$*" in
|
|
157
|
-
*"--query Arn"*) printf 'arn:aws:iam::123456789012:user/DirexioDeployer-Test\n' ;;
|
|
158
|
-
*"--query Account"*) printf '123456789012\n' ;;
|
|
159
|
-
*) printf '{"Account":"123456789012","Arn":"arn:aws:iam::123456789012:user/DirexioDeployer-Test"}\n' ;;
|
|
160
|
-
esac
|
|
161
|
-
;;
|
|
162
|
-
"ec2 terminate-instances") exit 0 ;;
|
|
163
|
-
"ec2 wait") exit 0 ;;
|
|
164
|
-
"ec2 release-address") exit 0 ;;
|
|
165
|
-
"ec2 delete-security-group") exit 0 ;;
|
|
166
|
-
"ec2 delete-key-pair") exit 0 ;;
|
|
167
|
-
"ec2 describe-instances") printf 'terminated\n' ;;
|
|
168
|
-
"ec2 describe-addresses") exit 255 ;;
|
|
169
|
-
"ec2 describe-volumes") exit 255 ;;
|
|
170
|
-
"ec2 describe-security-groups") exit 255 ;;
|
|
171
|
-
"ec2 describe-key-pairs") exit 255 ;;
|
|
172
|
-
"route53 change-resource-record-sets") printf '{"ChangeInfo":{"Id":"/change/CDELETE","Status":"PENDING"}}\n' ;;
|
|
173
|
-
"route53 wait") exit 0 ;;
|
|
174
|
-
"route53 list-resource-record-sets") printf '{"ResourceRecordSets":[]}\n' ;;
|
|
175
|
-
"route53 delete-hosted-zone") exit 0 ;;
|
|
176
|
-
"route53 get-hosted-zone") exit 255 ;;
|
|
177
|
-
*) exit 0 ;;
|
|
178
|
-
esac
|
|
179
|
-
EOF
|
|
180
|
-
chmod 700 "$fakebin/aws"
|
|
181
|
-
|
|
182
|
-
cat > "$fakebin/direxio-connect" <<'EOF'
|
|
183
|
-
#!/usr/bin/env bash
|
|
184
|
-
set -euo pipefail
|
|
185
|
-
if [ "${1:-}" = "daemon" ] && [ "${2:-}" = "status" ]; then
|
|
186
|
-
cat <<STATUS
|
|
187
|
-
cc-connect daemon status
|
|
188
|
-
|
|
189
|
-
Status: Running
|
|
190
|
-
WorkDir: ${STATUS_WORK_DIR:-}
|
|
191
|
-
STATUS
|
|
192
|
-
fi
|
|
193
|
-
EOF
|
|
194
|
-
chmod 700 "$fakebin/direxio-connect"
|
|
195
|
-
|
|
196
|
-
mkdir -p "$service_dir/cc-connect"
|
|
197
|
-
PATH="$fakebin:$PATH" STATUS_WORK_DIR="$service_dir/cc-connect" bash "$ROOT/scripts/destroy.sh" "$state" >/dev/null
|
|
198
|
-
destroy_report="$HOME/.direxio/reports/report.example.test/operation-report.json"
|
|
199
|
-
assert_file_exists "$destroy_report"
|
|
200
|
-
assert_not_contains_secret "$destroy_report"
|
|
201
|
-
|
|
202
|
-
jq -e '
|
|
203
|
-
.operation_type == "destroy"
|
|
204
|
-
and .status == "destroy_processed"
|
|
205
|
-
and .domain == "report.example.test"
|
|
206
|
-
and .resources.instance_id == "i-report"
|
|
207
|
-
and .resources.root_volume_id == "vol-report-root"
|
|
208
|
-
and .resources.eip_id == "eipalloc-report"
|
|
209
|
-
and .security.secrets_included == false
|
|
210
|
-
and .destroy.user_managed_dns_not_removed == true
|
|
211
|
-
and .destroy.purchased_domain_not_removed == true
|
|
212
|
-
and .destroy.evidence.ec2_instance.status == "terminated"
|
|
213
|
-
and .destroy.evidence.ebs_root_volume.status == "deleted"
|
|
214
|
-
and .destroy.evidence.elastic_ip.status == "released"
|
|
215
|
-
and .destroy.evidence.security_group.status == "deleted"
|
|
216
|
-
and .destroy.evidence.key_pair.status == "deleted"
|
|
217
|
-
and .destroy.evidence.route53_a_record.status == "deleted"
|
|
218
|
-
and .destroy.evidence.route53_hosted_zone.status == "deleted"
|
|
219
|
-
and .billing.destroy_cleanup_status == "no_recorded_billable_resource_residue"
|
|
220
|
-
and (.billing.possible_remaining_billable_resources | length == 0)
|
|
221
|
-
' "$destroy_report" >/dev/null
|
|
222
|
-
|
|
223
|
-
residual_dir="$HOME/.direxio/nodes/residual.example.test"
|
|
224
|
-
mkdir -p "$residual_dir"
|
|
225
|
-
residual_state="$residual_dir/state.json"
|
|
226
|
-
jq -n \
|
|
227
|
-
--arg residual_dir "$residual_dir" \
|
|
228
|
-
'{
|
|
229
|
-
domain: "residual.example.test",
|
|
230
|
-
agent_service_id: "residual.example.test",
|
|
231
|
-
agent_service_dir: $residual_dir,
|
|
232
|
-
resources: {
|
|
233
|
-
instance_id: "i-residual",
|
|
234
|
-
root_volume_id: "vol-residual",
|
|
235
|
-
eip_id: "eipalloc-residual",
|
|
236
|
-
route53_zone_id: "ZRESIDUAL"
|
|
237
|
-
},
|
|
238
|
-
destroy_evidence: {
|
|
239
|
-
ec2_instance: {status: "running"},
|
|
240
|
-
ebs_root_volume: {status: "available"},
|
|
241
|
-
elastic_ip: {status: "still_allocated"},
|
|
242
|
-
route53_hosted_zone: {status: "still_present"}
|
|
243
|
-
}
|
|
244
|
-
}' > "$residual_state"
|
|
245
|
-
|
|
246
|
-
residual_report_output=$(P2P_WORKDIR="$residual_dir" bash "$ROOT/scripts/orchestrate.sh" report destroy)
|
|
247
|
-
residual_report_path=$(printf '%s\n' "$residual_report_output" | sed -nE 's/^operation report: //p' | tail -n 1)
|
|
248
|
-
assert_file_exists "$residual_report_path"
|
|
249
|
-
jq -e '
|
|
250
|
-
.operation_type == "destroy"
|
|
251
|
-
and .billing.destroy_cleanup_status == "possible_billable_resource_residue"
|
|
252
|
-
and (.billing.possible_remaining_billable_resources | index("EC2 i-residual status=running") != null)
|
|
253
|
-
and (.billing.possible_remaining_billable_resources | index("EBS root volume vol-residual status=available") != null)
|
|
254
|
-
and (.billing.possible_remaining_billable_resources | index("Elastic IP eipalloc-residual status=still_allocated") != null)
|
|
255
|
-
and (.billing.possible_remaining_billable_resources | index("Route53 hosted zone ZRESIDUAL status=still_present") != null)
|
|
256
|
-
' "$residual_report_path" >/dev/null
|
|
257
|
-
|
|
258
|
-
echo "operation report ok"
|
|
@@ -1,91 +0,0 @@
|
|
|
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
|
-
assert_contains() {
|
|
12
|
-
local haystack=$1 needle=$2
|
|
13
|
-
if [[ "$haystack" != *"$needle"* ]]; then
|
|
14
|
-
echo "expected output to contain: $needle" >&2
|
|
15
|
-
echo "--- output ---" >&2
|
|
16
|
-
printf '%s\n' "$haystack" >&2
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
write_state() {
|
|
22
|
-
local workdir=$1 phase=$2 status=$3 resources_json=$4
|
|
23
|
-
mkdir -p "$workdir"
|
|
24
|
-
jq -n \
|
|
25
|
-
--arg run_id "status-test" \
|
|
26
|
-
--arg region "ap-northeast-1" \
|
|
27
|
-
--arg domain "status.example.test" \
|
|
28
|
-
--arg phase "$phase" \
|
|
29
|
-
--arg status "$status" \
|
|
30
|
-
--argjson resources "$resources_json" \
|
|
31
|
-
'{
|
|
32
|
-
run_id: $run_id,
|
|
33
|
-
region: $region,
|
|
34
|
-
domain_mode: "user",
|
|
35
|
-
domain: $domain,
|
|
36
|
-
instance_type: "t3.small",
|
|
37
|
-
dns_ready: false,
|
|
38
|
-
phase: $phase,
|
|
39
|
-
phases: {
|
|
40
|
-
S0_PREREQ_AWS: {status: (if $phase == "S0_PREREQ_AWS" then $status else "done" end)},
|
|
41
|
-
S1_PREFLIGHT: {status: (if $phase == "S0_PREREQ_AWS" then "pending" elif $phase == "S1_PREFLIGHT" then $status else "done" end)},
|
|
42
|
-
S2_DOMAIN: {status: (if ($phase == "S0_PREREQ_AWS" or $phase == "S1_PREFLIGHT") then "pending" elif $phase == "S2_DOMAIN" then $status else "done" end)},
|
|
43
|
-
S3_PROVISION: {status: (if ($phase == "S0_PREREQ_AWS" or $phase == "S1_PREFLIGHT" or $phase == "S2_DOMAIN") then "pending" elif $phase == "S3_PROVISION" then $status else "done" end)},
|
|
44
|
-
S4_BOOTSTRAP_STACK: {status: (if ($phase == "S0_PREREQ_AWS" or $phase == "S1_PREFLIGHT" or $phase == "S2_DOMAIN" or $phase == "S3_PROVISION") then "pending" elif $phase == "S4_BOOTSTRAP_STACK" then $status else "done" end)},
|
|
45
|
-
S5_INIT_TOKENS: {status: (if ($phase == "S0_PREREQ_AWS" or $phase == "S1_PREFLIGHT" or $phase == "S2_DOMAIN" or $phase == "S3_PROVISION" or $phase == "S4_BOOTSTRAP_STACK") then "pending" elif $phase == "S5_INIT_TOKENS" then $status else "done" end)},
|
|
46
|
-
S6_WIRE_LOCAL: {status: (if ($phase == "S0_PREREQ_AWS" or $phase == "S1_PREFLIGHT" or $phase == "S2_DOMAIN" or $phase == "S3_PROVISION" or $phase == "S4_BOOTSTRAP_STACK" or $phase == "S5_INIT_TOKENS") then "pending" elif $phase == "S6_WIRE_LOCAL" then $status else "done" end)},
|
|
47
|
-
S7_VERIFY_E2E: {status: (if $phase == "S7_VERIFY_E2E" then $status else "pending" end)}
|
|
48
|
-
},
|
|
49
|
-
resources: $resources
|
|
50
|
-
}' > "$workdir/state.json"
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
pre_resource_workdir="$tmp/pre-resource"
|
|
54
|
-
write_state "$pre_resource_workdir" "S2_DOMAIN" "waiting_user" '{}'
|
|
55
|
-
pre_resource_output=$(P2P_WORKDIR="$pre_resource_workdir" bash "$ROOT/scripts/orchestrate.sh" status)
|
|
56
|
-
|
|
57
|
-
assert_contains "$pre_resource_output" "Recovery summary"
|
|
58
|
-
assert_contains "$pre_resource_output" "Where it is blocked: S2_DOMAIN"
|
|
59
|
-
assert_contains "$pre_resource_output" "Billing impact: no EC2, public IPv4, or EBS resource is recorded yet"
|
|
60
|
-
assert_contains "$pre_resource_output" "Resume safety: safe to rerun the same command after the next action is complete"
|
|
61
|
-
assert_contains "$pre_resource_output" "Next action: confirm the long-lived domain, DNS authority, and irreversible Matrix server_name binding"
|
|
62
|
-
assert_contains "$pre_resource_output" "Stop-loss: no recorded cloud resources need destroy from this state"
|
|
63
|
-
|
|
64
|
-
billable_workdir="$tmp/billable"
|
|
65
|
-
write_state "$billable_workdir" "S4_BOOTSTRAP_STACK" "failed" '{"instance_id":"i-status","root_volume_id":"vol-status-root","public_ip":"203.0.113.10","eip_id":"eipalloc-status","route53_zone_id":"ZSTATUS"}'
|
|
66
|
-
billable_output=$(P2P_WORKDIR="$billable_workdir" bash "$ROOT/scripts/orchestrate.sh" status)
|
|
67
|
-
|
|
68
|
-
assert_contains "$billable_output" "Recovery summary"
|
|
69
|
-
assert_contains "$billable_output" "Where it is blocked: S4_BOOTSTRAP_STACK"
|
|
70
|
-
assert_contains "$billable_output" "Billing impact: recorded AWS resources may keep billing: EC2 i-status, EBS root volume vol-status-root, public IPv4 203.0.113.10, Elastic IP eipalloc-status, Route53 hosted zone ZSTATUS"
|
|
71
|
-
assert_contains "$billable_output" "Resume safety: do not reset state; fix the issue and rerun with P2P_EXISTING_STATE_ACTION=continue"
|
|
72
|
-
assert_contains "$billable_output" "Next action: inspect cloud-init, Docker, Caddy/TLS, and message-server logs over SSH"
|
|
73
|
-
assert_contains "$billable_output" "Stop-loss: ask the agent to run destroy, or run:"
|
|
74
|
-
assert_contains "$billable_output" "destroy.sh"
|
|
75
|
-
|
|
76
|
-
refresh_workdir="$tmp/refresh-pending"
|
|
77
|
-
write_state "$refresh_workdir" "S4_BOOTSTRAP_STACK" "pending" '{"instance_id":"i-refresh","root_volume_id":"vol-refresh-root","public_ip":"203.0.113.20"}'
|
|
78
|
-
jq '
|
|
79
|
-
.agent_install_status = "refresh_pending"
|
|
80
|
-
| .agent_service_id = "status.example.test"
|
|
81
|
-
| .user_confirmations.app_initialization = {status:"confirmed", evidence:"old app proof"}
|
|
82
|
-
| .runtime_checks.summary = {status:"passed"}
|
|
83
|
-
' "$refresh_workdir/state.json" > "$refresh_workdir/state.json.tmp"
|
|
84
|
-
mv "$refresh_workdir/state.json.tmp" "$refresh_workdir/state.json"
|
|
85
|
-
refresh_output=$(P2P_WORKDIR="$refresh_workdir" bash "$ROOT/scripts/orchestrate.sh" status)
|
|
86
|
-
|
|
87
|
-
assert_contains "$refresh_output" "Recovery summary"
|
|
88
|
-
assert_contains "$refresh_output" "Local refresh: update/reset cleared old credentials, user confirmations, runtime checks, and bridge install proof"
|
|
89
|
-
assert_contains "$refresh_output" "Next action: rerun the deployment workflow to refresh S4-S7, local credentials, MCP snippets, and runtime checks"
|
|
90
|
-
|
|
91
|
-
echo "orchestrate status recovery ok"
|
|
@@ -1,88 +0,0 @@
|
|
|
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 DOMAIN="timeout.example.test"
|
|
10
|
-
mkdir -p "$HOME" "$tmp/bin"
|
|
11
|
-
|
|
12
|
-
cat > "$tmp/bin/curl" <<'EOF'
|
|
13
|
-
#!/usr/bin/env bash
|
|
14
|
-
printf '%s\n' "$*" >> "$CURL_LOG"
|
|
15
|
-
exit 0
|
|
16
|
-
EOF
|
|
17
|
-
chmod 700 "$tmp/bin/curl"
|
|
18
|
-
|
|
19
|
-
cat > "$tmp/bin/ssh" <<'EOF'
|
|
20
|
-
#!/usr/bin/env bash
|
|
21
|
-
printf '%s\n' "$*" >> "$SSH_LOG"
|
|
22
|
-
case " $* " in
|
|
23
|
-
*" ConnectTimeout=6 "*" BatchMode=yes "*" ServerAliveInterval=4 "*" ServerAliveCountMax=3 "*) printf '{"password":"12345678","agent_token":"agent","access_token":"access"}'; exit 0 ;;
|
|
24
|
-
*) exit 2 ;;
|
|
25
|
-
esac
|
|
26
|
-
EOF
|
|
27
|
-
chmod 700 "$tmp/bin/ssh"
|
|
28
|
-
|
|
29
|
-
cat > "$tmp/bin/timeout" <<'EOF'
|
|
30
|
-
#!/usr/bin/env bash
|
|
31
|
-
printf '%s\n' "$1" >> "$TIMEOUT_LOG"
|
|
32
|
-
shift
|
|
33
|
-
exec "$@"
|
|
34
|
-
EOF
|
|
35
|
-
chmod 700 "$tmp/bin/timeout"
|
|
36
|
-
|
|
37
|
-
export PATH="$tmp/bin:$PATH"
|
|
38
|
-
export CURL_LOG="$tmp/curl.log"
|
|
39
|
-
export SSH_LOG="$tmp/ssh.log"
|
|
40
|
-
export TIMEOUT_LOG="$tmp/timeout.log"
|
|
41
|
-
export HEALTH_CURL_CONNECT_TIMEOUT=7
|
|
42
|
-
export HEALTH_CURL_MAX_TIME=11
|
|
43
|
-
export SSH_CONNECT_TIMEOUT=6
|
|
44
|
-
export SSH_SERVER_ALIVE_INTERVAL=4
|
|
45
|
-
export SSH_SERVER_ALIVE_COUNT_MAX=3
|
|
46
|
-
export SSH_COMMAND_TIMEOUT=19
|
|
47
|
-
|
|
48
|
-
# shellcheck disable=SC1090
|
|
49
|
-
source "$ROOT/scripts/lib/state.sh"
|
|
50
|
-
state_init >/dev/null 2>&1
|
|
51
|
-
res_set public_ip "203.0.113.10"
|
|
52
|
-
|
|
53
|
-
# shellcheck disable=SC1090
|
|
54
|
-
source "$ROOT/scripts/phases/s4_bootstrap_stack.sh"
|
|
55
|
-
_healthz_ok "timeout.example.test"
|
|
56
|
-
grep -q -- '--connect-timeout 7' "$CURL_LOG"
|
|
57
|
-
grep -q -- '--max-time 11' "$CURL_LOG"
|
|
58
|
-
grep -q -- '--resolve timeout.example.test:443:203.0.113.10' "$CURL_LOG"
|
|
59
|
-
|
|
60
|
-
# shellcheck disable=SC1090
|
|
61
|
-
source "$ROOT/scripts/phases/s5_init_tokens.sh"
|
|
62
|
-
_read_remote_bootstrap "$tmp/key.pem" "203.0.113.10" "$tmp/bootstrap.json"
|
|
63
|
-
grep -q 'ConnectTimeout=6' "$SSH_LOG"
|
|
64
|
-
grep -q 'BatchMode=yes' "$SSH_LOG"
|
|
65
|
-
grep -q 'ServerAliveInterval=4' "$SSH_LOG"
|
|
66
|
-
grep -q 'ServerAliveCountMax=3' "$SSH_LOG"
|
|
67
|
-
grep -q '^19$' "$TIMEOUT_LOG"
|
|
68
|
-
jq -e '.password == "12345678" and .agent_token == "agent" and .access_token == "access"' "$tmp/bootstrap.json" >/dev/null
|
|
69
|
-
|
|
70
|
-
printf '{"password":"01234567","agent_token":"agent","access_token":"access"}\n' > "$tmp/bootstrap-leading-zero.json"
|
|
71
|
-
IFS=$'\t' read -r password token access_token < <(_extract_output_tokens "$tmp/bootstrap-leading-zero.json")
|
|
72
|
-
[ "$password" = "01234567" ]
|
|
73
|
-
[ "$token" = "agent" ]
|
|
74
|
-
[ "$access_token" = "access" ]
|
|
75
|
-
|
|
76
|
-
printf '{"password":"8848121","agent_token":"agent","access_token":"access"}\n' > "$tmp/bootstrap-short-code.json"
|
|
77
|
-
if _extract_output_tokens "$tmp/bootstrap-short-code.json" >/dev/null; then
|
|
78
|
-
echo "S5 must reject initialization codes that are not exactly eight digits" >&2
|
|
79
|
-
exit 1
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
printf '{"password":12345678,"agent_token":"agent","access_token":"access"}\n' > "$tmp/bootstrap-numeric-code.json"
|
|
83
|
-
if _extract_output_tokens "$tmp/bootstrap-numeric-code.json" >/dev/null; then
|
|
84
|
-
echo "S5 must reject numeric initialization codes; they must stay JSON strings to preserve leading zeros" >&2
|
|
85
|
-
exit 1
|
|
86
|
-
fi
|
|
87
|
-
|
|
88
|
-
echo "phase timeout guards ok"
|