browser-automation-skill 0.71.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 (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/SECURITY.md +39 -0
  4. package/SKILL.md +206 -0
  5. package/bin/cli.mjs +55 -0
  6. package/install.sh +143 -0
  7. package/package.json +54 -0
  8. package/references/adapter-candidates.md +40 -0
  9. package/references/browser-mcp-cheatsheet.md +132 -0
  10. package/references/browser-stats-cheatsheet.md +155 -0
  11. package/references/chrome-devtools-mcp-cheatsheet.md +232 -0
  12. package/references/midscene-integration.md +359 -0
  13. package/references/obscura-cheatsheet.md +103 -0
  14. package/references/playwright-cli-cheatsheet.md +64 -0
  15. package/references/playwright-lib-cheatsheet.md +90 -0
  16. package/references/recipes/add-a-tool-adapter.md +134 -0
  17. package/references/recipes/agent-workflows/README.md +37 -0
  18. package/references/recipes/agent-workflows/cache-driven-bulk-operation.md +110 -0
  19. package/references/recipes/agent-workflows/flow-record-and-replay.md +102 -0
  20. package/references/recipes/agent-workflows/incremental-pattern-discovery.md +125 -0
  21. package/references/recipes/agent-workflows/login-then-scrape.md +100 -0
  22. package/references/recipes/anti-patterns-tool-extension.md +182 -0
  23. package/references/recipes/body-bytes-not-body.md +139 -0
  24. package/references/recipes/cache-write-security.md +210 -0
  25. package/references/recipes/fingerprint-rescue.md +154 -0
  26. package/references/recipes/model-routing.md +143 -0
  27. package/references/recipes/path-security.md +138 -0
  28. package/references/recipes/privacy-canary.md +96 -0
  29. package/references/recipes/visual-rescue-hook.md +182 -0
  30. package/references/stats-prices.json +42 -0
  31. package/references/stats-schema.json +77 -0
  32. package/references/tool-versions.md +8 -0
  33. package/scripts/browser-add-site.sh +113 -0
  34. package/scripts/browser-assert.sh +106 -0
  35. package/scripts/browser-audit.sh +68 -0
  36. package/scripts/browser-baseline.sh +135 -0
  37. package/scripts/browser-click.sh +100 -0
  38. package/scripts/browser-creds-add.sh +254 -0
  39. package/scripts/browser-creds-list.sh +67 -0
  40. package/scripts/browser-creds-migrate.sh +122 -0
  41. package/scripts/browser-creds-remove.sh +69 -0
  42. package/scripts/browser-creds-rotate-totp.sh +109 -0
  43. package/scripts/browser-creds-show.sh +82 -0
  44. package/scripts/browser-creds-totp.sh +94 -0
  45. package/scripts/browser-do.sh +630 -0
  46. package/scripts/browser-doctor.sh +365 -0
  47. package/scripts/browser-drag.sh +90 -0
  48. package/scripts/browser-extract.sh +192 -0
  49. package/scripts/browser-fill.sh +142 -0
  50. package/scripts/browser-flow.sh +316 -0
  51. package/scripts/browser-history.sh +187 -0
  52. package/scripts/browser-hover.sh +92 -0
  53. package/scripts/browser-inspect.sh +188 -0
  54. package/scripts/browser-list-sessions.sh +78 -0
  55. package/scripts/browser-list-sites.sh +42 -0
  56. package/scripts/browser-login.sh +279 -0
  57. package/scripts/browser-mcp.sh +65 -0
  58. package/scripts/browser-migrate.sh +195 -0
  59. package/scripts/browser-open.sh +134 -0
  60. package/scripts/browser-press.sh +80 -0
  61. package/scripts/browser-remove-session.sh +72 -0
  62. package/scripts/browser-remove-site.sh +68 -0
  63. package/scripts/browser-replay.sh +206 -0
  64. package/scripts/browser-route.sh +174 -0
  65. package/scripts/browser-select.sh +122 -0
  66. package/scripts/browser-show-session.sh +57 -0
  67. package/scripts/browser-show-site.sh +37 -0
  68. package/scripts/browser-snapshot.sh +176 -0
  69. package/scripts/browser-stats.sh +522 -0
  70. package/scripts/browser-tab-close.sh +112 -0
  71. package/scripts/browser-tab-list.sh +70 -0
  72. package/scripts/browser-tab-switch.sh +111 -0
  73. package/scripts/browser-upload.sh +132 -0
  74. package/scripts/browser-use.sh +60 -0
  75. package/scripts/browser-vlm.sh +707 -0
  76. package/scripts/browser-wait.sh +97 -0
  77. package/scripts/install-git-hooks.sh +16 -0
  78. package/scripts/lib/capture.sh +356 -0
  79. package/scripts/lib/common.sh +262 -0
  80. package/scripts/lib/credential.sh +237 -0
  81. package/scripts/lib/fingerprint-rescue.js +123 -0
  82. package/scripts/lib/flow.sh +448 -0
  83. package/scripts/lib/flow_record.sh +210 -0
  84. package/scripts/lib/mask.sh +49 -0
  85. package/scripts/lib/memory.sh +427 -0
  86. package/scripts/lib/migrate.sh +390 -0
  87. package/scripts/lib/migrators/README.md +23 -0
  88. package/scripts/lib/migrators/memory/v1_to_v2.sh +15 -0
  89. package/scripts/lib/migrators/recent_urls/README.md +13 -0
  90. package/scripts/lib/migrators/stats/README.md +24 -0
  91. package/scripts/lib/node/chrome-devtools-bridge.mjs +1812 -0
  92. package/scripts/lib/node/mcp-server.mjs +531 -0
  93. package/scripts/lib/node/mcp-tools.json +68 -0
  94. package/scripts/lib/node/playwright-driver.mjs +1104 -0
  95. package/scripts/lib/node/totp-core.mjs +52 -0
  96. package/scripts/lib/node/totp.mjs +52 -0
  97. package/scripts/lib/node/url-pattern-cluster.mjs +102 -0
  98. package/scripts/lib/node/url-pattern-resolver.mjs +77 -0
  99. package/scripts/lib/output.sh +79 -0
  100. package/scripts/lib/router.sh +342 -0
  101. package/scripts/lib/sanitize.sh +107 -0
  102. package/scripts/lib/secret/keychain.sh +91 -0
  103. package/scripts/lib/secret/libsecret.sh +74 -0
  104. package/scripts/lib/secret/plaintext.sh +75 -0
  105. package/scripts/lib/secret_backend_select.sh +57 -0
  106. package/scripts/lib/session.sh +153 -0
  107. package/scripts/lib/site.sh +126 -0
  108. package/scripts/lib/stats.sh +419 -0
  109. package/scripts/lib/tool/.gitkeep +0 -0
  110. package/scripts/lib/tool/chrome-devtools-mcp.sh +349 -0
  111. package/scripts/lib/tool/obscura.sh +249 -0
  112. package/scripts/lib/tool/playwright-cli.sh +155 -0
  113. package/scripts/lib/tool/playwright-lib.sh +106 -0
  114. package/scripts/lib/verb_helpers.sh +222 -0
  115. package/scripts/lib/visual-rescue-default.sh +145 -0
  116. package/scripts/regenerate-docs.sh +99 -0
  117. package/uninstall.sh +51 -0
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/browser-tab-list.sh — enumerate tabs/pages currently held by the daemon.
3
+ # Usage: bash scripts/browser-tab-list.sh [--site NAME] [--tool NAME]
4
+ # [--dry-run] [--raw]
5
+ #
6
+ # Routes to chrome-devtools-mcp by default (Phase 6 part 8-i). Daemon-required:
7
+ # the verb caches the result in the daemon's `tabs` slot so 8-ii (tab-switch)
8
+ # and 8-iii (tab-close) can reference the same `tab_id` shape. Without daemon
9
+ # → exit 41 with hint.
10
+ #
11
+ # Read-only — no flags, no state mutation. The daemon dispatch calls upstream
12
+ # MCP `list_pages` (best-effort name; real upstream may differ) and normalizes
13
+ # to `[{tab_id, url, title}]`. `tab_id` is bridge-assigned (1-based, stable for
14
+ # the lifetime of one list_pages call); upstream's CDP target id never escapes
15
+ # the bridge.
16
+
17
+ set -euo pipefail
18
+ IFS=$'\n\t'
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ # shellcheck source=lib/common.sh
22
+ # shellcheck disable=SC1091
23
+ source "${SCRIPT_DIR}/lib/common.sh"
24
+ # shellcheck source=lib/output.sh
25
+ # shellcheck disable=SC1091
26
+ source "${SCRIPT_DIR}/lib/output.sh"
27
+ # shellcheck source=lib/router.sh
28
+ # shellcheck disable=SC1091
29
+ source "${SCRIPT_DIR}/lib/router.sh"
30
+ # shellcheck source=lib/verb_helpers.sh
31
+ # shellcheck disable=SC1091
32
+ source "${SCRIPT_DIR}/lib/verb_helpers.sh"
33
+
34
+ init_paths
35
+
36
+ SUMMARY_T0="$(now_ms)"; export SUMMARY_T0
37
+
38
+ parse_verb_globals "$@"
39
+
40
+ resolve_session_storage_state
41
+
42
+ # tab-list takes no required flags; pass remaining argv straight through so
43
+ # routing/capability checks still see globals like --tool.
44
+ verb_argv=("${REMAINING_ARGV[@]}")
45
+
46
+ if [ "${ARG_DRY_RUN:-0}" = "1" ]; then
47
+ ok "dry-run: would enumerate tabs via daemon"
48
+ emit_summary verb=tab-list tool=none why=dry-run status=ok dry_run=true
49
+ exit 0
50
+ fi
51
+
52
+ picked="$(pick_tool tab-list "${verb_argv[@]}")"
53
+ tool_name="${picked%%$'\t'*}"
54
+ why="${picked#*$'\t'}"
55
+
56
+ source_picked_adapter "${tool_name}"
57
+
58
+ set +e
59
+ adapter_out="$(invoke_with_retry tab-list "${verb_argv[@]}")"
60
+ adapter_rc=$?
61
+ set -e
62
+
63
+ [ -n "${adapter_out}" ] && printf '%s\n' "${adapter_out}"
64
+
65
+ if [ "${adapter_rc}" -eq 0 ]; then
66
+ emit_summary verb=tab-list tool="${tool_name}" why="${why}" status=ok
67
+ exit 0
68
+ fi
69
+ emit_summary verb=tab-list tool="${tool_name}" why="${why}" status=error
70
+ exit "${adapter_rc}"
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/browser-tab-switch.sh — switch the daemon's active tab.
3
+ # Usage: bash scripts/browser-tab-switch.sh [--site NAME] [--tool NAME]
4
+ # [--dry-run] [--raw]
5
+ # (--by-index N | --by-url-pattern STR)
6
+ #
7
+ # Routes to chrome-devtools-mcp by default (Phase 6 part 8-ii). Daemon-required:
8
+ # the daemon updates its `currentTab` pointer (added in this sub-part) and asks
9
+ # the upstream MCP to focus the corresponding page. Without daemon → exit 41.
10
+ #
11
+ # Mutex: exactly one of --by-index / --by-url-pattern. --by-index is 1-based
12
+ # (matches `tab_id` from tab-list). --by-url-pattern is substring-contains
13
+ # (case-sensitive, first match wins).
14
+ #
15
+ # If the daemon's tabs[] cache is empty (no preceding tab-list call), the
16
+ # daemon transparently auto-calls list_pages before resolving the selector.
17
+
18
+ set -euo pipefail
19
+ IFS=$'\n\t'
20
+
21
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
22
+ # shellcheck source=lib/common.sh
23
+ # shellcheck disable=SC1091
24
+ source "${SCRIPT_DIR}/lib/common.sh"
25
+ # shellcheck source=lib/output.sh
26
+ # shellcheck disable=SC1091
27
+ source "${SCRIPT_DIR}/lib/output.sh"
28
+ # shellcheck source=lib/router.sh
29
+ # shellcheck disable=SC1091
30
+ source "${SCRIPT_DIR}/lib/router.sh"
31
+ # shellcheck source=lib/verb_helpers.sh
32
+ # shellcheck disable=SC1091
33
+ source "${SCRIPT_DIR}/lib/verb_helpers.sh"
34
+
35
+ init_paths
36
+
37
+ SUMMARY_T0="$(now_ms)"; export SUMMARY_T0
38
+
39
+ parse_verb_globals "$@"
40
+
41
+ resolve_session_storage_state
42
+
43
+ by_index="" by_url_pattern=""
44
+ verb_argv=()
45
+ i=0
46
+ while [ "${i}" -lt "${#REMAINING_ARGV[@]}" ]; do
47
+ case "${REMAINING_ARGV[i]}" in
48
+ --by-index)
49
+ by_index="${REMAINING_ARGV[i+1]:-}"
50
+ [ -n "${by_index}" ] || die "${EXIT_USAGE_ERROR}" "--by-index requires a value"
51
+ verb_argv+=(--by-index "${by_index}")
52
+ i=$((i + 2))
53
+ ;;
54
+ --by-url-pattern)
55
+ by_url_pattern="${REMAINING_ARGV[i+1]:-}"
56
+ [ -n "${by_url_pattern}" ] || die "${EXIT_USAGE_ERROR}" "--by-url-pattern requires a value"
57
+ verb_argv+=(--by-url-pattern "${by_url_pattern}")
58
+ i=$((i + 2))
59
+ ;;
60
+ *)
61
+ verb_argv+=("${REMAINING_ARGV[i]}")
62
+ i=$((i + 1))
63
+ ;;
64
+ esac
65
+ done
66
+
67
+ # Mutex + presence: exactly one selector required.
68
+ if [ -n "${by_index}" ] && [ -n "${by_url_pattern}" ]; then
69
+ die "${EXIT_USAGE_ERROR}" "--by-index and --by-url-pattern are mutually exclusive"
70
+ fi
71
+ if [ -z "${by_index}" ] && [ -z "${by_url_pattern}" ]; then
72
+ die "${EXIT_USAGE_ERROR}" "tab-switch requires exactly one of --by-index N or --by-url-pattern STR"
73
+ fi
74
+
75
+ # Validate by-index is a positive integer (1-based).
76
+ if [ -n "${by_index}" ]; then
77
+ if ! printf '%s' "${by_index}" | grep -Eq '^[1-9][0-9]*$'; then
78
+ die "${EXIT_USAGE_ERROR}" "--by-index must be a positive integer (1-based); got: ${by_index}"
79
+ fi
80
+ fi
81
+
82
+ if [ "${ARG_DRY_RUN:-0}" = "1" ]; then
83
+ if [ -n "${by_index}" ]; then
84
+ ok "dry-run: would switch to tab #${by_index}"
85
+ emit_summary verb=tab-switch tool=none why=dry-run status=ok by_index="${by_index}" dry_run=true
86
+ else
87
+ ok "dry-run: would switch to first tab matching ${by_url_pattern}"
88
+ emit_summary verb=tab-switch tool=none why=dry-run status=ok by_url_pattern="${by_url_pattern}" dry_run=true
89
+ fi
90
+ exit 0
91
+ fi
92
+
93
+ picked="$(pick_tool tab-switch "${verb_argv[@]}")"
94
+ tool_name="${picked%%$'\t'*}"
95
+ why="${picked#*$'\t'}"
96
+
97
+ source_picked_adapter "${tool_name}"
98
+
99
+ set +e
100
+ adapter_out="$(invoke_with_retry tab-switch "${verb_argv[@]}")"
101
+ adapter_rc=$?
102
+ set -e
103
+
104
+ [ -n "${adapter_out}" ] && printf '%s\n' "${adapter_out}"
105
+
106
+ if [ "${adapter_rc}" -eq 0 ]; then
107
+ emit_summary verb=tab-switch tool="${tool_name}" why="${why}" status=ok
108
+ exit 0
109
+ fi
110
+ emit_summary verb=tab-switch tool="${tool_name}" why="${why}" status=error
111
+ exit "${adapter_rc}"
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/browser-upload.sh — fill <input type=file> by --ref + --path.
3
+ # Usage: bash scripts/browser-upload.sh [--site NAME] [--tool NAME] [--dry-run]
4
+ # [--raw] --ref eN --path PATH
5
+ # [--allow-sensitive]
6
+ #
7
+ # Routes to chrome-devtools-mcp by default (Phase 6 part 6). Stateful —
8
+ # requires running daemon (refMap precondition).
9
+ #
10
+ # SECURITY (path validation, performed bash-side BEFORE invoking adapter):
11
+ # 1. Path must exist and be a regular file (not dir, not device, not symlink-to-elsewhere).
12
+ # 2. Path must be readable by the current user.
13
+ # 3. Path must NOT match common sensitive patterns (~/.ssh/*, ~/.aws/*,
14
+ # ~/.config/*credentials*, *.env). Override via --allow-sensitive
15
+ # (typed acknowledgment that the agent is uploading sensitive material
16
+ # intentionally — covers legit use cases like "upload my GPG key").
17
+ # Path is then resolved (`readlink -f` / `realpath`) and forwarded to MCP.
18
+
19
+ set -euo pipefail
20
+ IFS=$'\n\t'
21
+
22
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
+ # shellcheck source=lib/common.sh
24
+ # shellcheck disable=SC1091
25
+ source "${SCRIPT_DIR}/lib/common.sh"
26
+ # shellcheck source=lib/output.sh
27
+ # shellcheck disable=SC1091
28
+ source "${SCRIPT_DIR}/lib/output.sh"
29
+ # shellcheck source=lib/router.sh
30
+ # shellcheck disable=SC1091
31
+ source "${SCRIPT_DIR}/lib/router.sh"
32
+ # shellcheck source=lib/verb_helpers.sh
33
+ # shellcheck disable=SC1091
34
+ source "${SCRIPT_DIR}/lib/verb_helpers.sh"
35
+
36
+ init_paths
37
+
38
+ SUMMARY_T0="$(now_ms)"; export SUMMARY_T0
39
+
40
+ parse_verb_globals "$@"
41
+
42
+ resolve_session_storage_state
43
+
44
+ ref="" path="" allow_sensitive=0
45
+ verb_argv=()
46
+ i=0
47
+ while [ "${i}" -lt "${#REMAINING_ARGV[@]}" ]; do
48
+ case "${REMAINING_ARGV[i]}" in
49
+ --ref)
50
+ ref="${REMAINING_ARGV[i+1]:-}"
51
+ [ -n "${ref}" ] || die "${EXIT_USAGE_ERROR}" "--ref requires a value"
52
+ verb_argv+=(--ref "${ref}")
53
+ i=$((i + 2))
54
+ ;;
55
+ --path)
56
+ path="${REMAINING_ARGV[i+1]:-}"
57
+ [ -n "${path}" ] || die "${EXIT_USAGE_ERROR}" "--path requires a value"
58
+ i=$((i + 2))
59
+ ;;
60
+ --allow-sensitive)
61
+ allow_sensitive=1
62
+ i=$((i + 1))
63
+ ;;
64
+ *)
65
+ verb_argv+=("${REMAINING_ARGV[i]}")
66
+ i=$((i + 1))
67
+ ;;
68
+ esac
69
+ done
70
+
71
+ [ -n "${ref}" ] || die "${EXIT_USAGE_ERROR}" "upload requires --ref eN"
72
+ [ -n "${path}" ] || die "${EXIT_USAGE_ERROR}" "upload requires --path PATH"
73
+
74
+ # Path security validation (bash-side, before adapter dispatch).
75
+ # 1. File must exist + be a regular file.
76
+ if [ ! -e "${path}" ]; then
77
+ die "${EXIT_USAGE_ERROR}" "upload: path does not exist: ${path}"
78
+ fi
79
+ if [ ! -f "${path}" ]; then
80
+ die "${EXIT_USAGE_ERROR}" "upload: path is not a regular file (directory, device, or other): ${path}"
81
+ fi
82
+ # 2. File must be readable.
83
+ if [ ! -r "${path}" ]; then
84
+ die "${EXIT_USAGE_ERROR}" "upload: path is not readable by the current user: ${path}"
85
+ fi
86
+ # 3. Sensitive-pattern reject. Common locations agents shouldn't accidentally
87
+ # upload from. Override with --allow-sensitive when intentional.
88
+ if [ "${allow_sensitive}" -ne 1 ]; then
89
+ case "${path}" in
90
+ *.ssh/*|*/.ssh/*|*.aws/credentials|*/.aws/credentials|*/.env|*.env|\
91
+ */credentials|*/credentials.json|*/secrets.json|*/private_key*|*/id_rsa*|\
92
+ */id_ed25519*|*/id_ecdsa*)
93
+ die "${EXIT_USAGE_ERROR}" "upload: path '${path}' matches a sensitive pattern (SSH key / AWS / .env / private_key); pass --allow-sensitive to override"
94
+ ;;
95
+ esac
96
+ fi
97
+
98
+ # Resolve to canonical path (eliminate symlink shenanigans). Cross-platform:
99
+ # `readlink -f` is GNU; macOS BSD has it via Xcode 11+ but `realpath` is more
100
+ # portable. Try realpath first, fall back to readlink -f.
101
+ canonical_path="$(realpath "${path}" 2>/dev/null || readlink -f "${path}" 2>/dev/null || printf '%s' "${path}")"
102
+
103
+ verb_argv+=(--path "${canonical_path}")
104
+
105
+ if [ "${ARG_DRY_RUN:-0}" = "1" ]; then
106
+ ok "dry-run: would upload ${canonical_path} to ${ref}"
107
+ emit_summary verb=upload tool=none why=dry-run status=ok \
108
+ ref="${ref}" path="${canonical_path}" dry_run=true
109
+ exit 0
110
+ fi
111
+
112
+ picked="$(pick_tool upload "${verb_argv[@]}")"
113
+ tool_name="${picked%%$'\t'*}"
114
+ why="${picked#*$'\t'}"
115
+
116
+ source_picked_adapter "${tool_name}"
117
+
118
+ set +e
119
+ adapter_out="$(invoke_with_retry upload "${verb_argv[@]}")"
120
+ adapter_rc=$?
121
+ set -e
122
+
123
+ [ -n "${adapter_out}" ] && printf '%s\n' "${adapter_out}"
124
+
125
+ if [ "${adapter_rc}" -eq 0 ]; then
126
+ emit_summary verb=upload tool="${tool_name}" why="${why}" status=ok \
127
+ ref="${ref}" path="${canonical_path}"
128
+ exit 0
129
+ fi
130
+ emit_summary verb=upload tool="${tool_name}" why="${why}" status=error \
131
+ ref="${ref}" path="${canonical_path}"
132
+ exit "${adapter_rc}"
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+ # use — get / set / clear the current site (CURRENT_FILE).
3
+ set -euo pipefail
4
+ IFS=$'\n\t'
5
+ umask 077
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ # shellcheck source=lib/common.sh
9
+ # shellcheck disable=SC1091
10
+ source "${SCRIPT_DIR}/lib/common.sh"
11
+ # shellcheck source=lib/site.sh
12
+ # shellcheck disable=SC1091
13
+ source "${SCRIPT_DIR}/lib/site.sh"
14
+ init_paths
15
+
16
+ mode=""; arg=""
17
+ usage() {
18
+ cat <<'USAGE'
19
+ Usage: use --set NAME | --show | --clear
20
+ USAGE
21
+ }
22
+ while [ $# -gt 0 ]; do
23
+ case "$1" in
24
+ --set) mode=set; arg="$2"; shift 2 ;;
25
+ --show) mode=show; shift ;;
26
+ --clear) mode=clear; shift ;;
27
+ -h|--help) usage; exit 0 ;;
28
+ *) die "${EXIT_USAGE_ERROR}" "unknown flag: $1" ;;
29
+ esac
30
+ done
31
+ [ -n "${mode}" ] || { usage; die "${EXIT_USAGE_ERROR}" "specify --set NAME, --show, or --clear"; }
32
+
33
+ started_at_ms="$(now_ms)"
34
+
35
+ case "${mode}" in
36
+ set)
37
+ [ -n "${arg}" ] || die "${EXIT_USAGE_ERROR}" "--set requires NAME"
38
+ current_set "${arg}"
39
+ ok "current site: ${arg}"
40
+ why="set"
41
+ ;;
42
+ show)
43
+ why="show"
44
+ ;;
45
+ clear)
46
+ current_clear
47
+ ok "current site cleared"
48
+ why="clear"
49
+ ;;
50
+ esac
51
+
52
+ current="$(current_get)"
53
+ duration_ms=$(( $(now_ms) - started_at_ms ))
54
+ if [ -z "${current}" ]; then
55
+ jq -cn --arg w "${why}" --argjson d "${duration_ms}" \
56
+ '{verb: "use", tool: "none", why: $w, status: "ok", current: null, duration_ms: $d}'
57
+ else
58
+ jq -cn --arg w "${why}" --arg c "${current}" --argjson d "${duration_ms}" \
59
+ '{verb: "use", tool: "none", why: $w, status: "ok", current: $c, duration_ms: $d}'
60
+ fi