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.
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/SECURITY.md +39 -0
- package/SKILL.md +206 -0
- package/bin/cli.mjs +55 -0
- package/install.sh +143 -0
- package/package.json +54 -0
- package/references/adapter-candidates.md +40 -0
- package/references/browser-mcp-cheatsheet.md +132 -0
- package/references/browser-stats-cheatsheet.md +155 -0
- package/references/chrome-devtools-mcp-cheatsheet.md +232 -0
- package/references/midscene-integration.md +359 -0
- package/references/obscura-cheatsheet.md +103 -0
- package/references/playwright-cli-cheatsheet.md +64 -0
- package/references/playwright-lib-cheatsheet.md +90 -0
- package/references/recipes/add-a-tool-adapter.md +134 -0
- package/references/recipes/agent-workflows/README.md +37 -0
- package/references/recipes/agent-workflows/cache-driven-bulk-operation.md +110 -0
- package/references/recipes/agent-workflows/flow-record-and-replay.md +102 -0
- package/references/recipes/agent-workflows/incremental-pattern-discovery.md +125 -0
- package/references/recipes/agent-workflows/login-then-scrape.md +100 -0
- package/references/recipes/anti-patterns-tool-extension.md +182 -0
- package/references/recipes/body-bytes-not-body.md +139 -0
- package/references/recipes/cache-write-security.md +210 -0
- package/references/recipes/fingerprint-rescue.md +154 -0
- package/references/recipes/model-routing.md +143 -0
- package/references/recipes/path-security.md +138 -0
- package/references/recipes/privacy-canary.md +96 -0
- package/references/recipes/visual-rescue-hook.md +182 -0
- package/references/stats-prices.json +42 -0
- package/references/stats-schema.json +77 -0
- package/references/tool-versions.md +8 -0
- package/scripts/browser-add-site.sh +113 -0
- package/scripts/browser-assert.sh +106 -0
- package/scripts/browser-audit.sh +68 -0
- package/scripts/browser-baseline.sh +135 -0
- package/scripts/browser-click.sh +100 -0
- package/scripts/browser-creds-add.sh +254 -0
- package/scripts/browser-creds-list.sh +67 -0
- package/scripts/browser-creds-migrate.sh +122 -0
- package/scripts/browser-creds-remove.sh +69 -0
- package/scripts/browser-creds-rotate-totp.sh +109 -0
- package/scripts/browser-creds-show.sh +82 -0
- package/scripts/browser-creds-totp.sh +94 -0
- package/scripts/browser-do.sh +630 -0
- package/scripts/browser-doctor.sh +365 -0
- package/scripts/browser-drag.sh +90 -0
- package/scripts/browser-extract.sh +192 -0
- package/scripts/browser-fill.sh +142 -0
- package/scripts/browser-flow.sh +316 -0
- package/scripts/browser-history.sh +187 -0
- package/scripts/browser-hover.sh +92 -0
- package/scripts/browser-inspect.sh +188 -0
- package/scripts/browser-list-sessions.sh +78 -0
- package/scripts/browser-list-sites.sh +42 -0
- package/scripts/browser-login.sh +279 -0
- package/scripts/browser-mcp.sh +65 -0
- package/scripts/browser-migrate.sh +195 -0
- package/scripts/browser-open.sh +134 -0
- package/scripts/browser-press.sh +80 -0
- package/scripts/browser-remove-session.sh +72 -0
- package/scripts/browser-remove-site.sh +68 -0
- package/scripts/browser-replay.sh +206 -0
- package/scripts/browser-route.sh +174 -0
- package/scripts/browser-select.sh +122 -0
- package/scripts/browser-show-session.sh +57 -0
- package/scripts/browser-show-site.sh +37 -0
- package/scripts/browser-snapshot.sh +176 -0
- package/scripts/browser-stats.sh +522 -0
- package/scripts/browser-tab-close.sh +112 -0
- package/scripts/browser-tab-list.sh +70 -0
- package/scripts/browser-tab-switch.sh +111 -0
- package/scripts/browser-upload.sh +132 -0
- package/scripts/browser-use.sh +60 -0
- package/scripts/browser-vlm.sh +707 -0
- package/scripts/browser-wait.sh +97 -0
- package/scripts/install-git-hooks.sh +16 -0
- package/scripts/lib/capture.sh +356 -0
- package/scripts/lib/common.sh +262 -0
- package/scripts/lib/credential.sh +237 -0
- package/scripts/lib/fingerprint-rescue.js +123 -0
- package/scripts/lib/flow.sh +448 -0
- package/scripts/lib/flow_record.sh +210 -0
- package/scripts/lib/mask.sh +49 -0
- package/scripts/lib/memory.sh +427 -0
- package/scripts/lib/migrate.sh +390 -0
- package/scripts/lib/migrators/README.md +23 -0
- package/scripts/lib/migrators/memory/v1_to_v2.sh +15 -0
- package/scripts/lib/migrators/recent_urls/README.md +13 -0
- package/scripts/lib/migrators/stats/README.md +24 -0
- package/scripts/lib/node/chrome-devtools-bridge.mjs +1812 -0
- package/scripts/lib/node/mcp-server.mjs +531 -0
- package/scripts/lib/node/mcp-tools.json +68 -0
- package/scripts/lib/node/playwright-driver.mjs +1104 -0
- package/scripts/lib/node/totp-core.mjs +52 -0
- package/scripts/lib/node/totp.mjs +52 -0
- package/scripts/lib/node/url-pattern-cluster.mjs +102 -0
- package/scripts/lib/node/url-pattern-resolver.mjs +77 -0
- package/scripts/lib/output.sh +79 -0
- package/scripts/lib/router.sh +342 -0
- package/scripts/lib/sanitize.sh +107 -0
- package/scripts/lib/secret/keychain.sh +91 -0
- package/scripts/lib/secret/libsecret.sh +74 -0
- package/scripts/lib/secret/plaintext.sh +75 -0
- package/scripts/lib/secret_backend_select.sh +57 -0
- package/scripts/lib/session.sh +153 -0
- package/scripts/lib/site.sh +126 -0
- package/scripts/lib/stats.sh +419 -0
- package/scripts/lib/tool/.gitkeep +0 -0
- package/scripts/lib/tool/chrome-devtools-mcp.sh +349 -0
- package/scripts/lib/tool/obscura.sh +249 -0
- package/scripts/lib/tool/playwright-cli.sh +155 -0
- package/scripts/lib/tool/playwright-lib.sh +106 -0
- package/scripts/lib/verb_helpers.sh +222 -0
- package/scripts/lib/visual-rescue-default.sh +145 -0
- package/scripts/regenerate-docs.sh +99 -0
- package/uninstall.sh +51 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# creds-remove — typed-name confirmation, then delete metadata + secret via
|
|
3
|
+
# backend. Mirrors remove-session UX exactly: --yes-i-know skips prompt,
|
|
4
|
+
# --dry-run reports without writing.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
IFS=$'\n\t'
|
|
8
|
+
umask 077
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
# shellcheck source=lib/common.sh
|
|
12
|
+
# shellcheck disable=SC1091
|
|
13
|
+
source "${SCRIPT_DIR}/lib/common.sh"
|
|
14
|
+
# shellcheck source=lib/credential.sh
|
|
15
|
+
# shellcheck disable=SC1091
|
|
16
|
+
source "${SCRIPT_DIR}/lib/credential.sh"
|
|
17
|
+
init_paths
|
|
18
|
+
|
|
19
|
+
name=""; yes=0; dry_run=0
|
|
20
|
+
usage() {
|
|
21
|
+
cat <<'USAGE'
|
|
22
|
+
Usage: creds-remove --as CRED_NAME [--yes-i-know] [--dry-run]
|
|
23
|
+
|
|
24
|
+
--as CRED_NAME credential to remove (required)
|
|
25
|
+
--yes-i-know skip the typed-name confirmation
|
|
26
|
+
--dry-run print planned action; remove nothing
|
|
27
|
+
USAGE
|
|
28
|
+
}
|
|
29
|
+
while [ $# -gt 0 ]; do
|
|
30
|
+
case "$1" in
|
|
31
|
+
--as) name="$2"; shift 2 ;;
|
|
32
|
+
--yes-i-know) yes=1; shift ;;
|
|
33
|
+
--dry-run) dry_run=1; shift ;;
|
|
34
|
+
-h|--help) usage; exit 0 ;;
|
|
35
|
+
*) die "${EXIT_USAGE_ERROR}" "unknown flag: $1" ;;
|
|
36
|
+
esac
|
|
37
|
+
done
|
|
38
|
+
[ -n "${name}" ] || { usage; die "${EXIT_USAGE_ERROR}" "--as is required"; }
|
|
39
|
+
assert_safe_name "${name}" "credential-name"
|
|
40
|
+
|
|
41
|
+
started_at_ms="$(now_ms)"
|
|
42
|
+
|
|
43
|
+
if ! credential_exists "${name}"; then
|
|
44
|
+
die "${EXIT_SITE_NOT_FOUND}" "credential not found: ${name}"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ "${dry_run}" -eq 1 ]; then
|
|
48
|
+
ok "dry-run: would remove credential ${name}"
|
|
49
|
+
duration_ms=$(( $(now_ms) - started_at_ms ))
|
|
50
|
+
summary_json verb=creds-remove tool=none why=dry-run status=ok would_run=true \
|
|
51
|
+
credential="${name}" duration_ms="${duration_ms}"
|
|
52
|
+
exit "${EXIT_OK}"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [ "${yes}" -ne 1 ]; then
|
|
56
|
+
printf 'Type the credential name (%s) to confirm removal: ' "${name}" >&2
|
|
57
|
+
answer=""
|
|
58
|
+
IFS= read -r answer || true
|
|
59
|
+
if [ "${answer}" != "${name}" ]; then
|
|
60
|
+
die "${EXIT_USAGE_ERROR}" "removal aborted (confirmation mismatch)"
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
credential_delete "${name}"
|
|
65
|
+
ok "credential removed: ${name}"
|
|
66
|
+
|
|
67
|
+
duration_ms=$(( $(now_ms) - started_at_ms ))
|
|
68
|
+
summary_json verb=creds-remove tool=none why=delete status=ok \
|
|
69
|
+
credential="${name}" duration_ms="${duration_ms}"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# creds-rotate-totp — re-enroll the TOTP shared secret for an existing
|
|
3
|
+
# credential. Use case: service forces a new TOTP secret (re-issued QR code
|
|
4
|
+
# during account recovery, security-incident rotation, etc.). Replaces the
|
|
5
|
+
# `<name>__totp` backend slot with a new value; metadata.totp_enabled stays
|
|
6
|
+
# true; password slot untouched.
|
|
7
|
+
#
|
|
8
|
+
# Usage: bash scripts/browser-creds-rotate-totp.sh \
|
|
9
|
+
# --as CRED_NAME --totp-secret-stdin [--yes-i-know] [--dry-run]
|
|
10
|
+
#
|
|
11
|
+
# Phase-5 part 4-iv. Mirrors creds-migrate's typed-phrase confirmation.
|
|
12
|
+
# AP-7: TOTP secret comes via stdin only — never argv.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
IFS=$'\n\t'
|
|
16
|
+
umask 077
|
|
17
|
+
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
# shellcheck source=lib/common.sh
|
|
20
|
+
# shellcheck disable=SC1091
|
|
21
|
+
source "${SCRIPT_DIR}/lib/common.sh"
|
|
22
|
+
# shellcheck source=lib/credential.sh
|
|
23
|
+
# shellcheck disable=SC1091
|
|
24
|
+
source "${SCRIPT_DIR}/lib/credential.sh"
|
|
25
|
+
init_paths
|
|
26
|
+
|
|
27
|
+
name=""; yes=0; dry_run=0; read_stdin=0
|
|
28
|
+
|
|
29
|
+
usage() {
|
|
30
|
+
cat <<'USAGE'
|
|
31
|
+
Usage: creds-rotate-totp --as CRED_NAME --totp-secret-stdin [options]
|
|
32
|
+
|
|
33
|
+
--as CRED_NAME credential to rotate (must be totp_enabled).
|
|
34
|
+
--totp-secret-stdin REQUIRED — read NEW base32 TOTP secret from stdin
|
|
35
|
+
(one chunk; no NUL needed). AP-7: never argv.
|
|
36
|
+
--yes-i-know skip the typed-name confirmation prompt.
|
|
37
|
+
--dry-run print planned action; backend unchanged.
|
|
38
|
+
-h, --help this message
|
|
39
|
+
|
|
40
|
+
Behavior:
|
|
41
|
+
1. Validates cred exists + is totp_enabled.
|
|
42
|
+
2. Reads new TOTP secret from stdin.
|
|
43
|
+
3. Typed-phrase confirmation (unless --yes-i-know).
|
|
44
|
+
4. Overwrites <name>__totp backend slot.
|
|
45
|
+
5. Metadata + password slot UNCHANGED.
|
|
46
|
+
|
|
47
|
+
Privacy: summary JSON NEVER includes the new TOTP secret value.
|
|
48
|
+
USAGE
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
while [ $# -gt 0 ]; do
|
|
52
|
+
case "$1" in
|
|
53
|
+
--as) name="$2"; shift 2 ;;
|
|
54
|
+
--totp-secret-stdin) read_stdin=1; shift ;;
|
|
55
|
+
--yes-i-know) yes=1; shift ;;
|
|
56
|
+
--dry-run) dry_run=1; shift ;;
|
|
57
|
+
-h|--help) usage; exit 0 ;;
|
|
58
|
+
*) die "${EXIT_USAGE_ERROR}" "unknown flag: $1" ;;
|
|
59
|
+
esac
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
[ -n "${name}" ] || { usage; die "${EXIT_USAGE_ERROR}" "--as is required"; }
|
|
63
|
+
[ "${read_stdin}" -eq 1 ] || { usage; die "${EXIT_USAGE_ERROR}" "--totp-secret-stdin is required (AP-7: secrets via stdin only)"; }
|
|
64
|
+
assert_safe_name "${name}" "credential-name"
|
|
65
|
+
|
|
66
|
+
if ! credential_exists "${name}"; then
|
|
67
|
+
die "${EXIT_SITE_NOT_FOUND}" "credential not found: ${name}"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
cred_meta="$(credential_load "${name}")"
|
|
71
|
+
totp_enabled="$(printf '%s' "${cred_meta}" | jq -r '.totp_enabled // false')"
|
|
72
|
+
if [ "${totp_enabled}" != "true" ]; then
|
|
73
|
+
die "${EXIT_USAGE_ERROR}" "credential ${name} is not totp_enabled (use creds-add --enable-totp instead)"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Read new TOTP secret from stdin. Single chunk — no NUL splitting needed.
|
|
77
|
+
new_totp="$(cat)"
|
|
78
|
+
if [ -z "${new_totp}" ]; then
|
|
79
|
+
die "${EXIT_USAGE_ERROR}" "--totp-secret-stdin: empty secret on stdin"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
started_at_ms="$(now_ms)"
|
|
83
|
+
|
|
84
|
+
if [ "${dry_run}" -eq 1 ]; then
|
|
85
|
+
ok "dry-run: would rotate TOTP secret for ${name} (${#new_totp} chars on stdin)"
|
|
86
|
+
duration_ms=$(( $(now_ms) - started_at_ms ))
|
|
87
|
+
summary_json verb=creds-rotate-totp tool=none why=dry-run status=ok would_run=true \
|
|
88
|
+
credential="${name}" duration_ms="${duration_ms}"
|
|
89
|
+
exit "${EXIT_OK}"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if [ "${yes}" -ne 1 ]; then
|
|
93
|
+
printf 'Type the credential name (%s) to confirm TOTP rotation: ' "${name}" >&2
|
|
94
|
+
answer=""
|
|
95
|
+
IFS= read -r answer || true
|
|
96
|
+
if [ "${answer}" != "${name}" ]; then
|
|
97
|
+
die "${EXIT_USAGE_ERROR}" "rotation aborted (confirmation mismatch)"
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Overwrite the <name>__totp backend slot. credential_set_totp_secret reads
|
|
102
|
+
# stdin — pipe the new secret in.
|
|
103
|
+
printf '%s' "${new_totp}" | credential_set_totp_secret "${name}"
|
|
104
|
+
|
|
105
|
+
ok "TOTP secret rotated: ${name}"
|
|
106
|
+
|
|
107
|
+
duration_ms=$(( $(now_ms) - started_at_ms ))
|
|
108
|
+
summary_json verb=creds-rotate-totp tool=none why=rotate-totp status=ok \
|
|
109
|
+
credential="${name}" duration_ms="${duration_ms}"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# creds-show — emit credential metadata. Optional --reveal exposes the secret
|
|
3
|
+
# value behind a typed-phrase confirmation gate.
|
|
4
|
+
#
|
|
5
|
+
# CRITICAL SECURITY INVARIANT (default mode): this verb NEVER emits the secret
|
|
6
|
+
# payload. Only the metadata sidecar (site, account, backend, auto_relogin,
|
|
7
|
+
# totp_enabled, created_at) is surfaced. The agent has no business seeing raw
|
|
8
|
+
# secret material; downstream auth flows pass the payload via stdin pipes.
|
|
9
|
+
#
|
|
10
|
+
# --reveal flow: typed-phrase confirmation (mirror remove-session UX). User
|
|
11
|
+
# must type the credential name back via stdin (single line). On match, the
|
|
12
|
+
# secret is fetched (via credential_get_secret) and emitted alongside its
|
|
13
|
+
# masked preview (via mask_string). On mismatch, the verb dies with a
|
|
14
|
+
# self-healing hint. The masked preview lets the user confirm visually they
|
|
15
|
+
# revealed the right thing without re-leaking the value.
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
IFS=$'\n\t'
|
|
19
|
+
umask 077
|
|
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/credential.sh
|
|
26
|
+
# shellcheck disable=SC1091
|
|
27
|
+
source "${SCRIPT_DIR}/lib/credential.sh"
|
|
28
|
+
# shellcheck source=lib/mask.sh
|
|
29
|
+
# shellcheck disable=SC1091
|
|
30
|
+
source "${SCRIPT_DIR}/lib/mask.sh"
|
|
31
|
+
init_paths
|
|
32
|
+
|
|
33
|
+
name=""; reveal=0
|
|
34
|
+
usage() {
|
|
35
|
+
cat <<'USAGE'
|
|
36
|
+
Usage: creds-show --as CRED_NAME [--reveal]
|
|
37
|
+
|
|
38
|
+
--as CRED_NAME credential to show (required)
|
|
39
|
+
--reveal after typed-phrase confirmation, include secret value
|
|
40
|
+
+ masked preview in the output JSON
|
|
41
|
+
USAGE
|
|
42
|
+
}
|
|
43
|
+
while [ $# -gt 0 ]; do
|
|
44
|
+
case "$1" in
|
|
45
|
+
--as) name="$2"; shift 2 ;;
|
|
46
|
+
--reveal) reveal=1; shift ;;
|
|
47
|
+
-h|--help) usage; exit 0 ;;
|
|
48
|
+
*) die "${EXIT_USAGE_ERROR}" "unknown flag: $1" ;;
|
|
49
|
+
esac
|
|
50
|
+
done
|
|
51
|
+
[ -n "${name}" ] || { usage; die "${EXIT_USAGE_ERROR}" "--as is required"; }
|
|
52
|
+
assert_safe_name "${name}" "credential-name"
|
|
53
|
+
|
|
54
|
+
started_at_ms="$(now_ms)"
|
|
55
|
+
|
|
56
|
+
if ! credential_exists "${name}"; then
|
|
57
|
+
die "${EXIT_SITE_NOT_FOUND}" "credential not found: ${name}"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
meta="$(credential_load "${name}")"
|
|
61
|
+
|
|
62
|
+
if [ "${reveal}" -eq 1 ]; then
|
|
63
|
+
printf 'Type the credential name (%s) to confirm reveal: ' "${name}" >&2
|
|
64
|
+
answer=""
|
|
65
|
+
IFS= read -r answer || true
|
|
66
|
+
if [ "${answer}" != "${name}" ]; then
|
|
67
|
+
die "${EXIT_USAGE_ERROR}" "reveal aborted (confirmation mismatch)"
|
|
68
|
+
fi
|
|
69
|
+
secret="$(credential_get_secret "${name}")"
|
|
70
|
+
secret_masked="$(mask_string "${secret}")"
|
|
71
|
+
duration_ms=$(( $(now_ms) - started_at_ms ))
|
|
72
|
+
jq -cn --arg n "${name}" --argjson m "${meta}" --arg s "${secret}" --arg sm "${secret_masked}" --argjson d "${duration_ms}" \
|
|
73
|
+
'{verb: "creds-show", tool: "none", why: "reveal", status: "ok",
|
|
74
|
+
credential: $n, meta: $m, secret_masked: $sm, secret: $s,
|
|
75
|
+
duration_ms: $d}'
|
|
76
|
+
exit "${EXIT_OK}"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
duration_ms=$(( $(now_ms) - started_at_ms ))
|
|
80
|
+
jq -cn --arg n "${name}" --argjson m "${meta}" --argjson d "${duration_ms}" \
|
|
81
|
+
'{verb: "creds-show", tool: "none", why: "show", status: "ok",
|
|
82
|
+
credential: $n, meta: $m, duration_ms: $d}'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/browser-creds-totp.sh — produce the current TOTP code for a stored
|
|
3
|
+
# credential. Reads the base32 shared secret from the backend (`<name>:totp`
|
|
4
|
+
# slot stored at `creds-add --enable-totp --totp-secret-stdin` time), generates
|
|
5
|
+
# the RFC 6238 6-digit code via scripts/lib/node/totp.mjs, prints to stdout.
|
|
6
|
+
#
|
|
7
|
+
# Usage: bash scripts/browser-creds-totp.sh --as CRED_NAME [--dry-run]
|
|
8
|
+
#
|
|
9
|
+
# Phase-5 part 4-ii. login --auto auto-replay of TOTP codes is part 4-iii.
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
IFS=$'\n\t'
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
# shellcheck source=lib/common.sh
|
|
16
|
+
# shellcheck disable=SC1091
|
|
17
|
+
source "${SCRIPT_DIR}/lib/common.sh"
|
|
18
|
+
# shellcheck source=lib/output.sh
|
|
19
|
+
# shellcheck disable=SC1091
|
|
20
|
+
source "${SCRIPT_DIR}/lib/output.sh"
|
|
21
|
+
# shellcheck source=lib/credential.sh
|
|
22
|
+
# shellcheck disable=SC1091
|
|
23
|
+
source "${SCRIPT_DIR}/lib/credential.sh"
|
|
24
|
+
|
|
25
|
+
init_paths
|
|
26
|
+
|
|
27
|
+
SUMMARY_T0="$(now_ms)"; export SUMMARY_T0
|
|
28
|
+
|
|
29
|
+
as=""
|
|
30
|
+
dry_run=0
|
|
31
|
+
|
|
32
|
+
usage() {
|
|
33
|
+
cat <<'USAGE'
|
|
34
|
+
Usage: creds-totp --as CRED_NAME [--dry-run]
|
|
35
|
+
|
|
36
|
+
--as CRED_NAME credential name (must be totp_enabled, must have a
|
|
37
|
+
stored TOTP secret).
|
|
38
|
+
--dry-run report planned action; emit nothing on stdout.
|
|
39
|
+
-h, --help this message
|
|
40
|
+
|
|
41
|
+
Stdout: 6-digit code (or empty on dry-run).
|
|
42
|
+
Stderr: human-friendly status / errors.
|
|
43
|
+
USAGE
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
while [ $# -gt 0 ]; do
|
|
47
|
+
case "$1" in
|
|
48
|
+
--as) as="$2"; shift 2 ;;
|
|
49
|
+
--dry-run) dry_run=1; shift ;;
|
|
50
|
+
-h|--help) usage; exit 0 ;;
|
|
51
|
+
*) die "${EXIT_USAGE_ERROR}" "unknown flag: $1" ;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
[ -n "${as}" ] || { usage; die "${EXIT_USAGE_ERROR}" "--as is required"; }
|
|
56
|
+
assert_safe_name "${as}" "credential-name"
|
|
57
|
+
|
|
58
|
+
if ! credential_exists "${as}"; then
|
|
59
|
+
die "${EXIT_SITE_NOT_FOUND}" "credential not found: ${as}"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
cred_meta="$(credential_load "${as}")"
|
|
63
|
+
totp_enabled="$(printf '%s' "${cred_meta}" | jq -r '.totp_enabled // false')"
|
|
64
|
+
if [ "${totp_enabled}" != "true" ]; then
|
|
65
|
+
die "${EXIT_USAGE_ERROR}" "credential ${as} is not totp_enabled (re-add with --enable-totp --totp-secret-stdin)"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if [ "${dry_run}" -eq 1 ]; then
|
|
69
|
+
ok "dry-run: would generate TOTP code for ${as}"
|
|
70
|
+
duration_ms=$(( $(now_ms) - $(printf '%s' "${SUMMARY_T0}") ))
|
|
71
|
+
summary_json verb=creds-totp tool=node why=dry-run status=ok would_run=true \
|
|
72
|
+
credential="${as}" duration_ms="${duration_ms}"
|
|
73
|
+
exit "${EXIT_OK}"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Pipe the TOTP secret to the node generator via stdin (AP-7: never argv).
|
|
77
|
+
node_bin="${BROWSER_SKILL_NODE_BIN:-node}"
|
|
78
|
+
totp_script="${SCRIPT_DIR}/lib/node/totp.mjs"
|
|
79
|
+
|
|
80
|
+
set +e
|
|
81
|
+
code="$(credential_get_totp_secret "${as}" | "${node_bin}" "${totp_script}")"
|
|
82
|
+
gen_rc=$?
|
|
83
|
+
set -e
|
|
84
|
+
|
|
85
|
+
if [ "${gen_rc}" -ne 0 ]; then
|
|
86
|
+
die "${EXIT_GENERIC_ERROR}" "TOTP code generation failed (rc=${gen_rc})"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Emit code on stdout — agent reads it then types into browser.
|
|
90
|
+
printf '%s\n' "${code}"
|
|
91
|
+
|
|
92
|
+
duration_ms=$(( $(now_ms) - $(printf '%s' "${SUMMARY_T0}") ))
|
|
93
|
+
summary_json verb=creds-totp tool=node why=generate-totp-code status=ok \
|
|
94
|
+
credential="${as}" duration_ms="${duration_ms}"
|