@rubytech/create-maxy-code 0.1.362 → 0.1.363
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/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +21 -4
- package/payload/platform/plugins/cloudflare/bin/__tests__/wait-for.test.sh +107 -0
- package/payload/platform/plugins/cloudflare/bin/wait-for.sh +100 -0
- package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/claudeai-connectors.md +20 -3
- package/payload/platform/templates/specialists/agents/coding-assistant.md +2 -0
- package/payload/server/public/assets/{AdminLoginScreens-D6JtrY9a.js → AdminLoginScreens-CJatEEai.js} +1 -1
- package/payload/server/public/assets/{AdminShell-CaVRZTmj.js → AdminShell-D0MDlfI1.js} +1 -1
- package/payload/server/public/assets/{Checkbox-BiHO47mz.js → Checkbox-DqnBuFEU.js} +1 -1
- package/payload/server/public/assets/OperatorConversations-DDUzu1h8.css +1 -0
- package/payload/server/public/assets/{admin-BrCL9qSn.js → admin-Cocx_If1.js} +1 -1
- package/payload/server/public/assets/{browser-DNIV6o_p.js → browser-BsFHTA93.js} +1 -1
- package/payload/server/public/assets/{calendar-Arx94jTH.js → calendar-hwGeTmWC.js} +1 -1
- package/payload/server/public/assets/chat-DMssYitp.js +1 -0
- package/payload/server/public/assets/{data-DBInTT-w.js → data-DOjJ8031.js} +1 -1
- package/payload/server/public/assets/{graph-z-RYpYMZ.js → graph-BIAaLslC.js} +1 -1
- package/payload/server/public/assets/{graph-labels-CVZClzPb.js → graph-labels-CXt55mIP.js} +1 -1
- package/payload/server/public/assets/{operator-CoCf8Lk7.js → operator-CRRc0nD-.js} +1 -1
- package/payload/server/public/assets/{page-CWnkJCxo.js → page-wUvf2I5u.js} +1 -1
- package/payload/server/public/assets/{public-CKMOOQiq.js → public-Ba5G74iP.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-Dgw6hmsm.js → rotate-ccw-BfYwa6ew.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +4 -4
- package/payload/server/public/chat.html +6 -6
- package/payload/server/public/data.html +4 -4
- package/payload/server/public/graph.html +7 -7
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/operator.html +8 -8
- package/payload/server/public/public.html +6 -6
- package/payload/server/public/assets/OperatorConversations-D3UBU6SM.css +0 -1
- package/payload/server/public/assets/chat-BwShkhcz.js +0 -1
- /package/payload/server/public/assets/{OperatorConversations-CzhbxFwN.js → OperatorConversations-BwnO3IUP.js} +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:e863053737ee5f8477f1f3ff4eb5b718f5999d1b3497276922e19ab8d88a866b
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -1722,9 +1722,9 @@ Source: https://docs.getmaxy.com/connectors.md
|
|
|
1722
1722
|
# claude.ai connectors
|
|
1723
1723
|
|
|
1724
1724
|
claude.ai connectors (Zoom, Dropbox, GitHub, Google Calendar, and the rest you add on claude.ai) work
|
|
1725
|
-
inside your admin chat once they are authorised for this Maxy client. This page explains
|
|
1726
|
-
connector you
|
|
1727
|
-
the chat.
|
|
1725
|
+
inside your admin chat once they are authorised for this Maxy client. This page explains how the agent
|
|
1726
|
+
can find a connector you have not added yet, why a connector you already added can still say it needs
|
|
1727
|
+
authentication, and how to fix both without leaving the chat.
|
|
1728
1728
|
|
|
1729
1729
|
## Asking for a connector by name
|
|
1730
1730
|
|
|
@@ -1735,6 +1735,23 @@ below, so it will reach for the connector rather than telling you it has no such
|
|
|
1735
1735
|
connector you added is not yet authenticated for this client, the agent offers you the authentication
|
|
1736
1736
|
step straight away instead of dead-ending.
|
|
1737
1737
|
|
|
1738
|
+
## Finding a connector you have not added yet
|
|
1739
|
+
|
|
1740
|
+
You can also ask for a connector you have never added — "do you work with Stripe?", "can you connect
|
|
1741
|
+
Notion?", "is there a Linear integration?". claude.ai keeps a directory of managed connectors, hundreds
|
|
1742
|
+
of them, and the agent can search it by name. So instead of telling you it has no such integration, the
|
|
1743
|
+
agent looks the connector up and tells you whether it exists and how to turn it on.
|
|
1744
|
+
|
|
1745
|
+
1. The agent searches the directory for the connector you named.
|
|
1746
|
+
2. If it is there, the agent names it and gives you a claude.ai link that enables that exact connector.
|
|
1747
|
+
3. Open the link, enable the connector on claude.ai, and complete its sign-in.
|
|
1748
|
+
4. Tell the agent you have enabled it. The connector flows through to your chat on its own — the agent
|
|
1749
|
+
confirms your chat picked it up and proves it with one live read, so you can see it actually works.
|
|
1750
|
+
|
|
1751
|
+
If the connector is not in the directory, the agent says so plainly and offers you the generic Maxy
|
|
1752
|
+
connector instead, which registers an ordinary REST API with credentials Maxy holds. The agent never
|
|
1753
|
+
invents a connector that the directory does not list.
|
|
1754
|
+
|
|
1738
1755
|
## Why a connector can still need authentication
|
|
1739
1756
|
|
|
1740
1757
|
A claude.ai connector is authorised per client, not per account. Approving a connector in the
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regression test for wait-for.sh. Stubs the poll command with sandbox scripts so
|
|
3
|
+
# a condition can be made never-true, eventually-true, stuck, or exit-code driven.
|
|
4
|
+
# No live network, no real waiting beyond a few seconds.
|
|
5
|
+
#
|
|
6
|
+
# Covers:
|
|
7
|
+
# 1. never-true -> exit 2 (timeout) within the bound, last state reported
|
|
8
|
+
# 2. stuck value -> exit 3 before the timeout would fire, value reported
|
|
9
|
+
# 3. eventually-true -> exit 0, satisfying state reported
|
|
10
|
+
# 4. exit-code mode (no --expect) -> exit 0 when poll flips to rc 0
|
|
11
|
+
# 5. usage error (missing --timeout) -> exit 64
|
|
12
|
+
# 6. a hung poll cannot block past the timeout (per-poll cap)
|
|
13
|
+
# 7. overshoot: a large --interval never overruns --timeout
|
|
14
|
+
# 8. --stuck-after without --expect -> exit 64
|
|
15
|
+
# 9. --stuck-after 0 -> exit 64
|
|
16
|
+
set -u
|
|
17
|
+
|
|
18
|
+
HELPER="$(cd "$(dirname "$0")/.." && pwd)/wait-for.sh"
|
|
19
|
+
[ -x "$HELPER" ] || { echo "FAIL: $HELPER not executable" >&2; exit 1; }
|
|
20
|
+
|
|
21
|
+
PASS=0; FAIL=0
|
|
22
|
+
ok() { echo "PASS: $1"; PASS=$((PASS+1)); }
|
|
23
|
+
bad() { echo "FAIL: $1" >&2; FAIL=$((FAIL+1)); }
|
|
24
|
+
|
|
25
|
+
SANDBOX=$(mktemp -d)
|
|
26
|
+
trap 'rm -rf "$SANDBOX"' EXIT
|
|
27
|
+
|
|
28
|
+
# Stub: always prints a fixed never-satisfying state.
|
|
29
|
+
NEVER="$SANDBOX/never.sh"
|
|
30
|
+
cat > "$NEVER" <<'EOF'
|
|
31
|
+
#!/usr/bin/env bash
|
|
32
|
+
echo "ssl=active http=401"
|
|
33
|
+
EOF
|
|
34
|
+
chmod +x "$NEVER"
|
|
35
|
+
|
|
36
|
+
# Stub: prints http=401 for the first 2 reads, http=200 after (counter file).
|
|
37
|
+
EVENTUAL="$SANDBOX/eventual.sh"
|
|
38
|
+
cat > "$EVENTUAL" <<'EOF'
|
|
39
|
+
#!/usr/bin/env bash
|
|
40
|
+
C="$1"; n=$(cat "$C" 2>/dev/null || echo 0); n=$((n+1)); echo "$n" > "$C"
|
|
41
|
+
if [ "$n" -ge 3 ]; then echo "ssl=active http=200"; else echo "ssl=active http=401"; fi
|
|
42
|
+
EOF
|
|
43
|
+
chmod +x "$EVENTUAL"
|
|
44
|
+
|
|
45
|
+
# Stub: exits 1 until a flag file exists, then exits 0 (exit-code mode).
|
|
46
|
+
FLIP="$SANDBOX/flip.sh"
|
|
47
|
+
cat > "$FLIP" <<'EOF'
|
|
48
|
+
#!/usr/bin/env bash
|
|
49
|
+
C="$1"; F="$2"; n=$(cat "$C" 2>/dev/null || echo 0); n=$((n+1)); echo "$n" > "$C"
|
|
50
|
+
if [ "$n" -ge 2 ]; then : > "$F"; fi
|
|
51
|
+
[ -e "$F" ] && exit 0 || exit 1
|
|
52
|
+
EOF
|
|
53
|
+
chmod +x "$FLIP"
|
|
54
|
+
|
|
55
|
+
run() { # $1 label; rest = helper args. Sets OUT/RC/ELAPSED.
|
|
56
|
+
local label="$1"; shift
|
|
57
|
+
local of t0 t1
|
|
58
|
+
of=$(mktemp); t0=$(date +%s)
|
|
59
|
+
bash "$HELPER" "$@" >"$of" 2>/dev/null
|
|
60
|
+
RC=$?; t1=$(date +%s); ELAPSED=$((t1 - t0)); OUT=$(cat "$of"); rm -f "$of"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# 1. never-true -> timeout (exit 2), bounded, last state reported
|
|
64
|
+
run "never" --timeout 2 --interval 1 --expect 'http=200' -- "$NEVER"
|
|
65
|
+
[ "$RC" -eq 2 ] && ok "never exits 2 (timeout)" || bad "never exit=$RC want 2"
|
|
66
|
+
echo "$OUT" | grep -q "http=401" && ok "never reports last state" || bad "never out='$OUT'"
|
|
67
|
+
[ "$ELAPSED" -le 4 ] && ok "never stays within bound (${ELAPSED}s)" || bad "never ran ${ELAPSED}s"
|
|
68
|
+
|
|
69
|
+
# 2. stuck -> exit 3 early, value reported
|
|
70
|
+
run "stuck" --timeout 30 --interval 1 --expect 'http=200' --stuck-after 3 -- "$NEVER"
|
|
71
|
+
[ "$RC" -eq 3 ] && ok "stuck exits 3" || bad "stuck exit=$RC want 3"
|
|
72
|
+
echo "$OUT" | grep -q "http=401" && ok "stuck reports value" || bad "stuck out='$OUT'"
|
|
73
|
+
[ "$ELAPSED" -lt 10 ] && ok "stuck fires before timeout (${ELAPSED}s)" || bad "stuck ran ${ELAPSED}s"
|
|
74
|
+
|
|
75
|
+
# 3. eventually-true -> exit 0
|
|
76
|
+
run "eventual" --timeout 10 --interval 1 --expect 'http=200' -- "$EVENTUAL" "$SANDBOX/ec"
|
|
77
|
+
[ "$RC" -eq 0 ] && ok "eventual exits 0 (met)" || bad "eventual exit=$RC want 0"
|
|
78
|
+
echo "$OUT" | grep -q "http=200" && ok "eventual reports met state" || bad "eventual out='$OUT'"
|
|
79
|
+
|
|
80
|
+
# 4. exit-code mode -> exit 0 on flip
|
|
81
|
+
run "flip" --timeout 10 --interval 1 -- "$FLIP" "$SANDBOX/fc" "$SANDBOX/flag"
|
|
82
|
+
[ "$RC" -eq 0 ] && ok "flip exits 0 (exit-code mode)" || bad "flip exit=$RC want 0"
|
|
83
|
+
|
|
84
|
+
# 5. usage error -> exit 64
|
|
85
|
+
run "usage" --interval 1 --expect 'x' -- "$NEVER"
|
|
86
|
+
[ "$RC" -eq 64 ] && ok "missing --timeout exits 64" || bad "usage exit=$RC want 64"
|
|
87
|
+
|
|
88
|
+
# 6. a hung poll cannot block past the timeout (per-poll cap)
|
|
89
|
+
run "hung" --timeout 2 --interval 1 -- sleep 30
|
|
90
|
+
[ "$RC" -eq 2 ] && ok "hung poll exits 2 (timeout)" || bad "hung exit=$RC want 2"
|
|
91
|
+
[ "$ELAPSED" -le 4 ] && ok "hung poll bounded (${ELAPSED}s)" || bad "hung blocked ${ELAPSED}s"
|
|
92
|
+
|
|
93
|
+
# 7. overshoot: a large --interval must not overrun --timeout
|
|
94
|
+
run "overshoot" --timeout 2 --interval 30 --expect 'http=200' -- "$NEVER"
|
|
95
|
+
[ "$RC" -eq 2 ] && ok "overshoot exits 2" || bad "overshoot exit=$RC want 2"
|
|
96
|
+
[ "$ELAPSED" -le 4 ] && ok "interval clamped to deadline (${ELAPSED}s)" || bad "overshoot ran ${ELAPSED}s"
|
|
97
|
+
|
|
98
|
+
# 8. --stuck-after without --expect -> usage (stuck is value-based)
|
|
99
|
+
run "stuck-no-expect" --timeout 5 --interval 1 --stuck-after 3 -- "$NEVER"
|
|
100
|
+
[ "$RC" -eq 64 ] && ok "--stuck-after needs --expect (exit 64)" || bad "stuck-no-expect exit=$RC want 64"
|
|
101
|
+
|
|
102
|
+
# 9. --stuck-after 0 -> usage (invalid threshold)
|
|
103
|
+
run "stuck-zero" --timeout 5 --interval 1 --expect 'http=200' --stuck-after 0 -- "$NEVER"
|
|
104
|
+
[ "$RC" -eq 64 ] && ok "--stuck-after 0 rejected (exit 64)" || bad "stuck-zero exit=$RC want 64"
|
|
105
|
+
|
|
106
|
+
echo "----"; echo "PASS=$PASS FAIL=$FAIL"
|
|
107
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Bounded poller for agent-driven async waits. Polls a command until its output
|
|
3
|
+
# satisfies a success condition, a wall-clock timeout elapses, or a non-success
|
|
4
|
+
# value repeats enough times to be a stuck finding. It never loops unbounded and
|
|
5
|
+
# never blocks past the timeout, even when a single poll command hangs: each poll
|
|
6
|
+
# is capped at the remaining wall-clock budget and killed if it overruns.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# wait-for.sh --timeout <sec> --interval <sec> [--expect <regex>] [--stuck-after <n>] -- <poll-cmd...>
|
|
10
|
+
#
|
|
11
|
+
# Success: with --expect, the poll's trimmed stdout matches the extended regex;
|
|
12
|
+
# without --expect, the poll command exits 0. --stuck-after requires --expect,
|
|
13
|
+
# because a stuck finding is about a value stuck at an unexpected reading.
|
|
14
|
+
#
|
|
15
|
+
# Exit codes:
|
|
16
|
+
# 0 met -> prints "met: <laststate>"
|
|
17
|
+
# 2 timeout -> prints "timeout after <elapsed>s, last state: <laststate>"
|
|
18
|
+
# 3 stuck -> prints "stuck: <laststate> repeated <n>x"
|
|
19
|
+
# 64 usage -> usage line on stderr
|
|
20
|
+
set -u
|
|
21
|
+
|
|
22
|
+
usage() {
|
|
23
|
+
echo "usage: wait-for.sh --timeout <sec> --interval <sec> [--expect <regex>] [--stuck-after <n>] -- <poll-cmd...>" >&2
|
|
24
|
+
exit 64
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Run "$@" with a hard cap of $1 seconds. Sets POLL_OUT (stdout) and POLL_RC.
|
|
28
|
+
# A poll that overruns the cap is killed and reported with POLL_RC=124, so a
|
|
29
|
+
# hung poll cannot block past the caller's wall-clock budget.
|
|
30
|
+
POLL_OUT=""; POLL_RC=0
|
|
31
|
+
run_poll() {
|
|
32
|
+
local cap="$1"; shift
|
|
33
|
+
local tmp pid end
|
|
34
|
+
tmp=$(mktemp)
|
|
35
|
+
"$@" >"$tmp" 2>/dev/null &
|
|
36
|
+
pid=$!
|
|
37
|
+
end=$(( $(date +%s) + cap ))
|
|
38
|
+
# Re-check on a sub-second granularity so a fast poll returns promptly while a
|
|
39
|
+
# hung one is still killed at the cap.
|
|
40
|
+
while kill -0 "$pid" 2>/dev/null; do
|
|
41
|
+
if [ "$(date +%s)" -ge "$end" ]; then
|
|
42
|
+
kill -TERM "$pid" 2>/dev/null
|
|
43
|
+
wait "$pid" 2>/dev/null || true
|
|
44
|
+
POLL_RC=124; POLL_OUT="$(cat "$tmp")"; rm -f "$tmp"
|
|
45
|
+
return
|
|
46
|
+
fi
|
|
47
|
+
sleep 0.2
|
|
48
|
+
done
|
|
49
|
+
wait "$pid"; POLL_RC=$?
|
|
50
|
+
POLL_OUT="$(cat "$tmp")"; rm -f "$tmp"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
TIMEOUT=""; INTERVAL=""; EXPECT=""; EXPECT_SET=0; STUCK_AFTER=""
|
|
54
|
+
while [ $# -gt 0 ]; do
|
|
55
|
+
case "$1" in
|
|
56
|
+
--timeout) TIMEOUT="${2:-}"; shift 2 || usage;;
|
|
57
|
+
--interval) INTERVAL="${2:-}"; shift 2 || usage;;
|
|
58
|
+
--expect) EXPECT="${2:-}"; EXPECT_SET=1; shift 2 || usage;;
|
|
59
|
+
--stuck-after) STUCK_AFTER="${2:-}"; shift 2 || usage;;
|
|
60
|
+
--) shift; break;;
|
|
61
|
+
*) usage;;
|
|
62
|
+
esac
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
# --timeout, --interval, and a poll command are required; both must be >= 1.
|
|
66
|
+
[ -n "$TIMEOUT" ] && [ -n "$INTERVAL" ] && [ $# -gt 0 ] || usage
|
|
67
|
+
case "$TIMEOUT" in ''|*[!0-9]*) usage;; esac; [ "$TIMEOUT" -ge 1 ] || usage
|
|
68
|
+
case "$INTERVAL" in ''|*[!0-9]*) usage;; esac; [ "$INTERVAL" -ge 1 ] || usage
|
|
69
|
+
# --stuck-after, when present, must be a positive integer and needs --expect.
|
|
70
|
+
if [ -n "$STUCK_AFTER" ]; then
|
|
71
|
+
case "$STUCK_AFTER" in *[!0-9]*) usage;; esac
|
|
72
|
+
[ "$STUCK_AFTER" -ge 1 ] || usage
|
|
73
|
+
[ "$EXPECT_SET" -eq 1 ] || usage
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
start=$(date +%s)
|
|
77
|
+
deadline=$((start + TIMEOUT))
|
|
78
|
+
laststate=""; prev=""; repeat=0
|
|
79
|
+
|
|
80
|
+
while :; do
|
|
81
|
+
now=$(date +%s); remaining=$((deadline - now))
|
|
82
|
+
if [ "$remaining" -le 0 ]; then echo "timeout after $((now - start))s, last state: $laststate"; exit 2; fi
|
|
83
|
+
|
|
84
|
+
run_poll "$remaining" "$@"
|
|
85
|
+
laststate="$(printf '%s' "$POLL_OUT" | tr '\n' ' ' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
86
|
+
|
|
87
|
+
if [ "$EXPECT_SET" -eq 1 ]; then
|
|
88
|
+
if printf '%s' "$laststate" | grep -Eq -- "$EXPECT"; then echo "met: $laststate"; exit 0; fi
|
|
89
|
+
if [ -n "$STUCK_AFTER" ]; then
|
|
90
|
+
if [ "$laststate" = "$prev" ]; then repeat=$((repeat + 1)); else repeat=1; prev="$laststate"; fi
|
|
91
|
+
if [ "$repeat" -ge "$STUCK_AFTER" ]; then echo "stuck: $laststate repeated ${repeat}x"; exit 3; fi
|
|
92
|
+
fi
|
|
93
|
+
else
|
|
94
|
+
if [ "$POLL_RC" -eq 0 ]; then echo "met: $laststate"; exit 0; fi
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
now=$(date +%s); remaining=$((deadline - now))
|
|
98
|
+
if [ "$remaining" -le 0 ]; then echo "timeout after $((now - start))s, last state: $laststate"; exit 2; fi
|
|
99
|
+
if [ "$INTERVAL" -lt "$remaining" ]; then sleep "$INTERVAL"; else sleep "$remaining"; fi
|
|
100
|
+
done
|
|
@@ -74,7 +74,7 @@ grep -o '<meta property="og:[^>]*>' /tmp/site.html
|
|
|
74
74
|
|
|
75
75
|
The done-gate passes only when all hold:
|
|
76
76
|
|
|
77
|
-
- the domain resolves at all: a `NXDOMAIN` or "could not resolve host" means the custom-domain DNS record was never created, so go back to the custom-domain step and create the CNAME with the `<brand>-dns` token; a `522` is the certificate-provisioning window, so wait
|
|
77
|
+
- the domain resolves at all: a `NXDOMAIN` or "could not resolve host" means the custom-domain DNS record was never created, so go back to the custom-domain step and create the CNAME with the `<brand>-dns` token; a `522` is the certificate-provisioning window, so wait for it to clear with a bounded poll rather than treating it as done or re-curling by hand forever; `bash platform/plugins/cloudflare/bin/wait-for.sh --timeout 180 --interval 15 --expect 'HTTP/[0-9.]+ 200' -- curl -s --max-time 10 -D - -o /dev/null "https://<custom-domain>/?cb=$(date +%s)"` stops at the bound and reports the last status line, so a domain stuck off `200` becomes a reported finding instead of an endless wait,
|
|
78
78
|
- the status line is `HTTP/2 200` (or `HTTP/1.1 200 OK`),
|
|
79
79
|
- the body carries an expected content marker for this site (a brand string or headline you can name in advance), not a stale marker from a previous brand,
|
|
80
80
|
- the `og:*` tags are present and brand-correct,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# claude.ai connectors
|
|
2
2
|
|
|
3
3
|
claude.ai connectors (Zoom, Dropbox, GitHub, Google Calendar, and the rest you add on claude.ai) work
|
|
4
|
-
inside your admin chat once they are authorised for this Maxy client. This page explains
|
|
5
|
-
connector you
|
|
6
|
-
the chat.
|
|
4
|
+
inside your admin chat once they are authorised for this Maxy client. This page explains how the agent
|
|
5
|
+
can find a connector you have not added yet, why a connector you already added can still say it needs
|
|
6
|
+
authentication, and how to fix both without leaving the chat.
|
|
7
7
|
|
|
8
8
|
## Asking for a connector by name
|
|
9
9
|
|
|
@@ -14,6 +14,23 @@ below, so it will reach for the connector rather than telling you it has no such
|
|
|
14
14
|
connector you added is not yet authenticated for this client, the agent offers you the authentication
|
|
15
15
|
step straight away instead of dead-ending.
|
|
16
16
|
|
|
17
|
+
## Finding a connector you have not added yet
|
|
18
|
+
|
|
19
|
+
You can also ask for a connector you have never added — "do you work with Stripe?", "can you connect
|
|
20
|
+
Notion?", "is there a Linear integration?". claude.ai keeps a directory of managed connectors, hundreds
|
|
21
|
+
of them, and the agent can search it by name. So instead of telling you it has no such integration, the
|
|
22
|
+
agent looks the connector up and tells you whether it exists and how to turn it on.
|
|
23
|
+
|
|
24
|
+
1. The agent searches the directory for the connector you named.
|
|
25
|
+
2. If it is there, the agent names it and gives you a claude.ai link that enables that exact connector.
|
|
26
|
+
3. Open the link, enable the connector on claude.ai, and complete its sign-in.
|
|
27
|
+
4. Tell the agent you have enabled it. The connector flows through to your chat on its own — the agent
|
|
28
|
+
confirms your chat picked it up and proves it with one live read, so you can see it actually works.
|
|
29
|
+
|
|
30
|
+
If the connector is not in the directory, the agent says so plainly and offers you the generic Maxy
|
|
31
|
+
connector instead, which registers an ordinary REST API with credentials Maxy holds. The agent never
|
|
32
|
+
invents a connector that the directory does not list.
|
|
33
|
+
|
|
17
34
|
## Why a connector can still need authentication
|
|
18
35
|
|
|
19
36
|
A claude.ai connector is authorised per client, not per account. Approving a connector in the
|
|
@@ -56,6 +56,8 @@ For lookups admin may have already saved (a repo path, a service name, a frequen
|
|
|
56
56
|
|
|
57
57
|
Use `Bash` for one command at a time when possible. Long pipelines and chained commands are fine when the chain expresses one logical operation; do not chain unrelated commands to "save a turn". When a command is long-running (a build, a test suite, a `git clone` of a large repo), run it in the foreground and report the result when it completes; the admin agent waits.
|
|
58
58
|
|
|
59
|
+
A wait on an async external condition (cert issuance, DNS propagation, custom-domain SSL, deploy readiness) is bounded, never open-ended. Do not hand-roll `until ...; do sleep; done` with no timeout: it can block forever on a condition that never becomes true. Use the deterministic helper `platform/plugins/cloudflare/bin/wait-for.sh`, which stops at a wall-clock timeout and reports the last observed state. A polled value stuck at an unexpected reading across repeated reads (for example a repeated `401` where you expected `200`) is a finding to report ("the endpoint is not serving"), not a reason to keep waiting. Before any wait you expect to run longer than a few seconds, tell admin you are about to wait and roughly how long, so the silence never reads as a hang.
|
|
60
|
+
|
|
59
61
|
When a command exits non-zero, return the exit code and the relevant stderr verbatim. Do not retry the same command unless admin asks. Do not present a fallback as if the original request succeeded.
|
|
60
62
|
|
|
61
63
|
## Output contract
|
package/payload/server/public/assets/{AdminLoginScreens-D6JtrY9a.js → AdminLoginScreens-CJatEEai.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./chunk-CAM3fms7.js";import{B as t,S as n,W as r,b as i,j as a,w as o}from"./OperatorConversations-CzhbxFwN.js";import{i as s}from"./admin-types-DJoj6VJv.js";import{u as c,y as l}from"./AdminShell-CaVRZTmj.js";import{t as u}from"./Checkbox-BiHO47mz.js";var d=`admin-landing-redirected`,f=`/graph`;function p(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var m=e(r(),1);function h(e=`admin`){let[n,r]=(0,m.useState)(`loading`),[i,a]=(0,m.useState)(``),[o,c]=(0,m.useState)(``),[u,h]=(0,m.useState)(``),[g,_]=(0,m.useState)(!1),[v,y]=(0,m.useState)(!1),[b,x]=(0,m.useState)(!1),[S,C]=(0,m.useState)(!1),[w,T]=(0,m.useState)(!1),[E,D]=(0,m.useState)(null),[O,k]=(0,m.useState)(null),[A,j]=(0,m.useState)(void 0),[M,N]=(0,m.useState)(null),[P,F]=(0,m.useState)(void 0),[I,L]=(0,m.useState)(null),[ee,R]=(0,m.useState)(null),[z,B]=(0,m.useState)([]),[V,H]=(0,m.useState)(!1),[U,W]=(0,m.useState)(void 0),G=(0,m.useRef)(void 0),[K,q]=(0,m.useState)(!1);(0,m.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&q(!0)}).catch(()=>{})},[]);let J=(0,m.useRef)(null),Y=(0,m.useRef)(null);(0,m.useEffect)(()=>{async function e(){let e=null;try{e=sessionStorage.getItem(`maxy-admin-session-key`)}catch{}if(!e)return!1;try{let t=await fetch(`/api/admin/session?session_key=${encodeURIComponent(e)}`);if(t.status===401){try{sessionStorage.removeItem(`maxy-admin-session-key`)}catch{}return!1}if(!t.ok)return!1;let n=await t.json();D(n.session_key),R(n.sessionId??null),j(n.businessName),N(n.role??null),F(n.userName===void 0?null:n.userName),L(n.avatar??null);let i=s(n.thinkingView);return G.current=i,W(i),r(`chat`),!0}catch(e){return console.error(`[admin] session restore failed:`,e),!1}}async function t(n=2){try{let i=await fetch(`/api/health`);if(!i.ok){if(n>0)return await new Promise(e=>setTimeout(e,1500)),t(n-1);console.error(`[admin] health check returned ${i.status} after retries`),r(`set-pin`);return}let a=await i.json();if(!a.pin_configured){r(`set-pin`);return}if(!a.claude_authenticated){r(`connect-claude`);return}if(await e())return;r(`enter-pin`)}catch(e){if(n>0)return await new Promise(e=>setTimeout(e,1500)),t(n-1);console.error(`[admin] health check failed:`,e),r(`set-pin`)}}t()},[]),(0,m.useEffect)(()=>{n===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[n]),(0,m.useEffect)(()=>{if(typeof window>`u`)return;let t=!1;try{t=sessionStorage.getItem(d)===`1`}catch{}if(p({appState:n,alreadyRedirected:t,variant:e})){try{sessionStorage.setItem(d,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${f}`),window.location.replace(f)}},[n,e]);let X=(0,m.useRef)(null);(0,m.useEffect)(()=>{if(n!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();if(t.auth_status===`dead`||t.auth_status===`missing`){r(`connect-claude`);return}}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let n=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(n===`remote-auth-required`){t(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${n}`),X.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[n,E]),(0,m.useEffect)(()=>{n===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&r(`enter-pin`)}).catch(()=>{})},[n]);async function Z(e,n){y(!0);try{let i=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...n?{accountId:n}:{}})});if(!i.ok){let e=await i.json().catch(()=>({}));if(i.status===401&&e?.code===`remote-auth-required`){t(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let o=await i.json();if(o.accounts&&!o.session_key){console.log(`[admin] account picker shown: userId=${o.userId} accountCount=${o.accounts.length}`),B(o.accounts),r(`account-picker`);return}D(o.session_key),R(o.sessionId??null),j(o.businessName),N(o.role??null),F(o.userName===void 0?null:o.userName),L(o.avatar??null);let c=s(o.thinkingView);if(G.current=c,W(c),n)try{sessionStorage.setItem(`maxy-account-id`,n)}catch{}try{sessionStorage.setItem(`maxy-admin-session-key`,o.session_key)}catch{}a(``),r(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1),H(!1)}}let Q=(0,m.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let n=o.trim();if(!n){h(`Please enter your name.`);return}if(i.length<4){h(`PIN must be at least 4 characters.`);return}let s=i;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:s,name:n})});if(!e.ok){let n=await e.json().catch(()=>({}));if(e.status===401&&n?.code===`remote-auth-required`){t(`set-pin`,`/api/onboarding/set-pin`);return}if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();t.pin_configured&&t.claude_authenticated?r(`enter-pin`):t.pin_configured?r(`connect-claude`):h(n.error||`Failed to set PIN.`)}else r(`enter-pin`)}catch{r(`enter-pin`)}return}h(n.error||`Failed to set PIN.`);return}let i=await fetch(`/api/health`);if((i.ok?await i.json():null)?.claude_authenticated){await Z(s);return}a(``),r(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[i,v,o]),te=(0,m.useCallback)(async e=>{e.preventDefault(),h(``),await Z(i)},[i]),ne=(0,m.useCallback)(async()=>{T(!0);try{if(!await l())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(`maxy-account-id`),sessionStorage.removeItem(d)}catch{}return r(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,m.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(`maxy-account-id`),sessionStorage.removeItem(d)}catch{}a(``),h(``),r(`enter-pin`)},[]);return(0,m.useEffect)(()=>{X.current=$},[$]),{appState:n,setAppState:r,pin:i,setPin:a,operatorName:o,setOperatorName:c,pinError:u,setPinError:h,showPin:g,setShowPin:_,pinLoading:v,authPolling:b,setAuthPolling:x,authLoading:S,setAuthLoading:C,disconnecting:w,cacheKey:E,setCacheKey:D,claudeInfo:O,setClaudeInfo:k,businessName:A,role:M,userName:P,userAvatar:I,sessionId:ee,setSessionId:R,accounts:z,accountPickerLoading:V,expandAll:U,setExpandAll:W,expandAllDefaultRef:G,remoteAuthEnabled:K,pinInputRef:J,setPinFormRef:Y,handleSetPin:Q,handleLogin:te,handleAccountSelect:(0,m.useCallback)(async e=>{H(!0),h(``),await Z(i,e)},[i]),handleDisconnect:ne,handleLogout:$}}var g=o();function _({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,m.useRef)([]);function s(e,r){r.key===`Backspace`?(r.preventDefault(),t[e]?n(t.slice(0,e)+t.slice(e+1)):e>0&&(n(t.slice(0,e-1)+t.slice(e)),o.current[e-1]?.focus())):r.key===`ArrowLeft`&&e>0?o.current[e-1]?.focus():r.key===`ArrowRight`&&e<5?o.current[e+1]?.focus():r.key===`Enter`&&(r.preventDefault(),r.currentTarget.form?.requestSubmit())}function c(e,i){let a=i.nativeEvent.data;if(!a||!/^\d$/.test(a))return;let s=t.split(``);for(s[e]=a;s.length<e;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);n(c),c.length===6?r?.(c):e<5&&o.current[e+1]?.focus()}function l(e){e.preventDefault();let t=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);t&&(n(t),t.length===6?r?.(t):o.current[t.length]?.focus())}return(0,g.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,g.jsx)(`input`,{ref:t=>{o.current[r]=t,r===0&&e&&(e.current=t)},type:`text`,inputMode:`numeric`,className:`pin-box${t[r]?` pin-box-filled`:``}`,value:t[r]?i?t[r]:`•`:``,onKeyDown:e=>s(r,e),onInput:e=>c(r,e),onPaste:l,onFocus:e=>e.target.select(),autoFocus:a&&r===0,autoComplete:`off`,maxLength:1,"aria-label":`PIN digit ${r+1}`},r))})}function v(e){let{pin:t,setPin:r,showPin:a,setShowPin:o,pinLoading:s,pinError:l,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:h}=e;return(0,g.jsx)(`div`,{className:`connect-page`,children:(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,i.productName]}),(0,g.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,i.productName,` who you are, then choose a PIN.`]}),(0,g.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,g.jsxs)(`div`,{className:`pin-input-row`,children:[(0,g.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>h(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,g.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,g.jsxs)(`div`,{className:`pin-input-row`,children:[(0,g.jsx)(_,{inputRef:d,value:t,onChange:r,onComplete:()=>{},showPin:a}),(0,g.jsx)(c,{variant:`send`,type:`submit`,disabled:!t||!m.trim(),loading:s,"aria-label":`Set PIN`,children:(0,g.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,g.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,g.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,g.jsx)(u,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})]}),l&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:l})]})})}function y(e){let{pin:t,setPin:r,showPin:a,setShowPin:o,pinLoading:s,pinError:l,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,g.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,g.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:i.productName}),(0,g.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,g.jsxs)(`div`,{className:`pin-input-row`,children:[(0,g.jsx)(_,{inputRef:d,value:t,onChange:r,onComplete:()=>{},showPin:a,autoFocus:!0}),(0,g.jsx)(c,{variant:`send`,type:`submit`,disabled:!t,loading:s,children:(0,g.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,g.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,g.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,g.jsx)(`div`,{className:`pin-options`,children:(0,g.jsx)(u,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),l&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:l})]})]})}function b(e){let{accounts:t,loading:r,error:o,onSelect:s}=e;return(0,g.jsx)(`div`,{className:`connect-page`,children:(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:i.productName}),(0,g.jsx)(`p`,{className:`connect-subtitle`,children:`Select an account`}),(0,g.jsx)(`div`,{className:`account-picker-list`,children:t.map(e=>(0,g.jsxs)(`button`,{className:`account-picker-card`,onClick:()=>s(e.accountId),disabled:r,type:`button`,children:[(0,g.jsx)(`span`,{className:`account-picker-name`,children:e.businessName||e.accountId}),(0,g.jsx)(`span`,{className:`account-picker-role`,children:e.role}),r&&(0,g.jsx)(a,{className:`account-picker-spinner`,size:16})]},e.accountId))}),o&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:o})]})})}function x(e){let{authPolling:t,setAuthPolling:r,authLoading:a,setAuthLoading:o,pinError:s,setPinError:l,setAppState:u}=e,[d,f]=(0,m.useState)(!1),[p,h]=(0,m.useState)(!1);async function _(){h(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`launch-browser`})})).json();e.launched?f(!0):e.error&&l(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),l(`Could not launch browser.`)}h(!1)}async function v(){o(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){r(!0),f(!0),o(!1);for(let e=0;e<120;e++)if(await new Promise(e=>setTimeout(e,2e3)),(await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`wait`})})).json()).authenticated){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),u(`enter-pin`);return}l(`Timed out waiting for sign-in. Try again.`),r(!1)}else e.error&&l(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),l(`Could not start auth flow.`)}o(!1)}async function y(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),r(!1),l(``)}return t||d?(0,g.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,g.jsxs)(`header`,{className:`chat-header`,style:{paddingBottom:`12px`,flexShrink:0,position:`relative`,maxWidth:`680px`,width:`100%`,margin:`0 auto`,padding:`24px 20px 12px`},children:[t?(0,g.jsx)(`button`,{onClick:y,style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Cancel`,children:`✕`}):(0,g.jsx)(`button`,{onClick:()=>f(!1),style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Close browser`,children:`✕`}),(0,g.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,g.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,g.jsx)(`p`,{className:`chat-intro`,children:t?`Sign in and authorize in the browser below.`:`Open your email or prepare your accounts, then sign in.`}),!t&&(0,g.jsx)(`div`,{style:{marginTop:`12px`},children:(0,g.jsx)(c,{variant:`primary`,onClick:v,disabled:a,children:a?(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})})]}),(0,g.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,g.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),s&&(0,g.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:s})]}):(0,g.jsx)(`div`,{className:`connect-page`,children:(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsxs)(`div`,{className:`connect-logos`,children:[(0,g.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,g.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,g.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,g.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,g.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsx)(`span`,{className:`connect-logo-label`,children:i.productName})]})]}),(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,i.productName]}),(0,g.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,g.jsx)(c,{variant:`primary`,onClick:v,disabled:a,children:a?(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,g.jsx)(`p`,{style:{marginTop:`6px`,fontSize:`11px`,color:`#999`,maxWidth:`300px`,textAlign:`center`,lineHeight:`1.4`},children:`First time? You may need to sign into your email and Anthropic account in the browser before connecting.`}),(0,g.jsx)(`button`,{onClick:_,disabled:p,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:p?`Launching…`:`Open browser first`}),s&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:s})]})})}function S({auth:e}){return e.appState===`loading`?(0,g.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,g.jsx)(v,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,setPinFormRef:e.setPinFormRef,onSubmit:e.handleSetPin,operatorName:e.operatorName,setOperatorName:e.setOperatorName}):e.appState===`connect-claude`?(0,g.jsx)(x,{authPolling:e.authPolling,setAuthPolling:e.setAuthPolling,authLoading:e.authLoading,setAuthLoading:e.setAuthLoading,pinError:e.pinError,setPinError:e.setPinError,setAppState:e.setAppState}):e.appState===`enter-pin`?(0,g.jsx)(y,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,onSubmit:e.handleLogin,remoteAuthEnabled:e.remoteAuthEnabled,onSignOutRemote:()=>{console.info(`[admin-ui] remote-auth sign-out → /__remote-auth/logout`),window.location.href=`/__remote-auth/logout`}}):e.appState===`account-picker`?(0,g.jsx)(b,{accounts:e.accounts,loading:e.accountPickerLoading,error:e.pinError,onSelect:e.handleAccountSelect}):null}export{h as n,S as t};
|
|
1
|
+
import{o as e}from"./chunk-CAM3fms7.js";import{B as t,S as n,W as r,b as i,j as a,w as o}from"./OperatorConversations-BwnO3IUP.js";import{i as s}from"./admin-types-DJoj6VJv.js";import{u as c,y as l}from"./AdminShell-D0MDlfI1.js";import{t as u}from"./Checkbox-DqnBuFEU.js";var d=`admin-landing-redirected`,f=`/graph`;function p(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var m=e(r(),1);function h(e=`admin`){let[n,r]=(0,m.useState)(`loading`),[i,a]=(0,m.useState)(``),[o,c]=(0,m.useState)(``),[u,h]=(0,m.useState)(``),[g,_]=(0,m.useState)(!1),[v,y]=(0,m.useState)(!1),[b,x]=(0,m.useState)(!1),[S,C]=(0,m.useState)(!1),[w,T]=(0,m.useState)(!1),[E,D]=(0,m.useState)(null),[O,k]=(0,m.useState)(null),[A,j]=(0,m.useState)(void 0),[M,N]=(0,m.useState)(null),[P,F]=(0,m.useState)(void 0),[I,L]=(0,m.useState)(null),[ee,R]=(0,m.useState)(null),[z,B]=(0,m.useState)([]),[V,H]=(0,m.useState)(!1),[U,W]=(0,m.useState)(void 0),G=(0,m.useRef)(void 0),[K,q]=(0,m.useState)(!1);(0,m.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&q(!0)}).catch(()=>{})},[]);let J=(0,m.useRef)(null),Y=(0,m.useRef)(null);(0,m.useEffect)(()=>{async function e(){let e=null;try{e=sessionStorage.getItem(`maxy-admin-session-key`)}catch{}if(!e)return!1;try{let t=await fetch(`/api/admin/session?session_key=${encodeURIComponent(e)}`);if(t.status===401){try{sessionStorage.removeItem(`maxy-admin-session-key`)}catch{}return!1}if(!t.ok)return!1;let n=await t.json();D(n.session_key),R(n.sessionId??null),j(n.businessName),N(n.role??null),F(n.userName===void 0?null:n.userName),L(n.avatar??null);let i=s(n.thinkingView);return G.current=i,W(i),r(`chat`),!0}catch(e){return console.error(`[admin] session restore failed:`,e),!1}}async function t(n=2){try{let i=await fetch(`/api/health`);if(!i.ok){if(n>0)return await new Promise(e=>setTimeout(e,1500)),t(n-1);console.error(`[admin] health check returned ${i.status} after retries`),r(`set-pin`);return}let a=await i.json();if(!a.pin_configured){r(`set-pin`);return}if(!a.claude_authenticated){r(`connect-claude`);return}if(await e())return;r(`enter-pin`)}catch(e){if(n>0)return await new Promise(e=>setTimeout(e,1500)),t(n-1);console.error(`[admin] health check failed:`,e),r(`set-pin`)}}t()},[]),(0,m.useEffect)(()=>{n===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[n]),(0,m.useEffect)(()=>{if(typeof window>`u`)return;let t=!1;try{t=sessionStorage.getItem(d)===`1`}catch{}if(p({appState:n,alreadyRedirected:t,variant:e})){try{sessionStorage.setItem(d,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${f}`),window.location.replace(f)}},[n,e]);let X=(0,m.useRef)(null);(0,m.useEffect)(()=>{if(n!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();if(t.auth_status===`dead`||t.auth_status===`missing`){r(`connect-claude`);return}}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let n=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(n===`remote-auth-required`){t(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${n}`),X.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[n,E]),(0,m.useEffect)(()=>{n===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&r(`enter-pin`)}).catch(()=>{})},[n]);async function Z(e,n){y(!0);try{let i=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...n?{accountId:n}:{}})});if(!i.ok){let e=await i.json().catch(()=>({}));if(i.status===401&&e?.code===`remote-auth-required`){t(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let o=await i.json();if(o.accounts&&!o.session_key){console.log(`[admin] account picker shown: userId=${o.userId} accountCount=${o.accounts.length}`),B(o.accounts),r(`account-picker`);return}D(o.session_key),R(o.sessionId??null),j(o.businessName),N(o.role??null),F(o.userName===void 0?null:o.userName),L(o.avatar??null);let c=s(o.thinkingView);if(G.current=c,W(c),n)try{sessionStorage.setItem(`maxy-account-id`,n)}catch{}try{sessionStorage.setItem(`maxy-admin-session-key`,o.session_key)}catch{}a(``),r(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1),H(!1)}}let Q=(0,m.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let n=o.trim();if(!n){h(`Please enter your name.`);return}if(i.length<4){h(`PIN must be at least 4 characters.`);return}let s=i;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:s,name:n})});if(!e.ok){let n=await e.json().catch(()=>({}));if(e.status===401&&n?.code===`remote-auth-required`){t(`set-pin`,`/api/onboarding/set-pin`);return}if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();t.pin_configured&&t.claude_authenticated?r(`enter-pin`):t.pin_configured?r(`connect-claude`):h(n.error||`Failed to set PIN.`)}else r(`enter-pin`)}catch{r(`enter-pin`)}return}h(n.error||`Failed to set PIN.`);return}let i=await fetch(`/api/health`);if((i.ok?await i.json():null)?.claude_authenticated){await Z(s);return}a(``),r(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[i,v,o]),te=(0,m.useCallback)(async e=>{e.preventDefault(),h(``),await Z(i)},[i]),ne=(0,m.useCallback)(async()=>{T(!0);try{if(!await l())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(`maxy-account-id`),sessionStorage.removeItem(d)}catch{}return r(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,m.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(`maxy-account-id`),sessionStorage.removeItem(d)}catch{}a(``),h(``),r(`enter-pin`)},[]);return(0,m.useEffect)(()=>{X.current=$},[$]),{appState:n,setAppState:r,pin:i,setPin:a,operatorName:o,setOperatorName:c,pinError:u,setPinError:h,showPin:g,setShowPin:_,pinLoading:v,authPolling:b,setAuthPolling:x,authLoading:S,setAuthLoading:C,disconnecting:w,cacheKey:E,setCacheKey:D,claudeInfo:O,setClaudeInfo:k,businessName:A,role:M,userName:P,userAvatar:I,sessionId:ee,setSessionId:R,accounts:z,accountPickerLoading:V,expandAll:U,setExpandAll:W,expandAllDefaultRef:G,remoteAuthEnabled:K,pinInputRef:J,setPinFormRef:Y,handleSetPin:Q,handleLogin:te,handleAccountSelect:(0,m.useCallback)(async e=>{H(!0),h(``),await Z(i,e)},[i]),handleDisconnect:ne,handleLogout:$}}var g=o();function _({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,m.useRef)([]);function s(e,r){r.key===`Backspace`?(r.preventDefault(),t[e]?n(t.slice(0,e)+t.slice(e+1)):e>0&&(n(t.slice(0,e-1)+t.slice(e)),o.current[e-1]?.focus())):r.key===`ArrowLeft`&&e>0?o.current[e-1]?.focus():r.key===`ArrowRight`&&e<5?o.current[e+1]?.focus():r.key===`Enter`&&(r.preventDefault(),r.currentTarget.form?.requestSubmit())}function c(e,i){let a=i.nativeEvent.data;if(!a||!/^\d$/.test(a))return;let s=t.split(``);for(s[e]=a;s.length<e;)s.push(``);let c=s.join(``).replace(/\D/g,``).slice(0,6);n(c),c.length===6?r?.(c):e<5&&o.current[e+1]?.focus()}function l(e){e.preventDefault();let t=e.clipboardData.getData(`text`).replace(/\D/g,``).slice(0,6);t&&(n(t),t.length===6?r?.(t):o.current[t.length]?.focus())}return(0,g.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,g.jsx)(`input`,{ref:t=>{o.current[r]=t,r===0&&e&&(e.current=t)},type:`text`,inputMode:`numeric`,className:`pin-box${t[r]?` pin-box-filled`:``}`,value:t[r]?i?t[r]:`•`:``,onKeyDown:e=>s(r,e),onInput:e=>c(r,e),onPaste:l,onFocus:e=>e.target.select(),autoFocus:a&&r===0,autoComplete:`off`,maxLength:1,"aria-label":`PIN digit ${r+1}`},r))})}function v(e){let{pin:t,setPin:r,showPin:a,setShowPin:o,pinLoading:s,pinError:l,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:h}=e;return(0,g.jsx)(`div`,{className:`connect-page`,children:(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,i.productName]}),(0,g.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,i.productName,` who you are, then choose a PIN.`]}),(0,g.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,g.jsxs)(`div`,{className:`pin-input-row`,children:[(0,g.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>h(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,g.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,g.jsxs)(`div`,{className:`pin-input-row`,children:[(0,g.jsx)(_,{inputRef:d,value:t,onChange:r,onComplete:()=>{},showPin:a}),(0,g.jsx)(c,{variant:`send`,type:`submit`,disabled:!t||!m.trim(),loading:s,"aria-label":`Set PIN`,children:(0,g.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,g.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,g.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,g.jsx)(u,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})]}),l&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:l})]})})}function y(e){let{pin:t,setPin:r,showPin:a,setShowPin:o,pinLoading:s,pinError:l,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,g.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,g.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:i.productName}),(0,g.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,g.jsxs)(`div`,{className:`pin-input-row`,children:[(0,g.jsx)(_,{inputRef:d,value:t,onChange:r,onComplete:()=>{},showPin:a,autoFocus:!0}),(0,g.jsx)(c,{variant:`send`,type:`submit`,disabled:!t,loading:s,children:(0,g.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,g.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,g.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,g.jsx)(`div`,{className:`pin-options`,children:(0,g.jsx)(u,{checked:a,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),l&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:l})]})]})}function b(e){let{accounts:t,loading:r,error:o,onSelect:s}=e;return(0,g.jsx)(`div`,{className:`connect-page`,children:(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:i.productName}),(0,g.jsx)(`p`,{className:`connect-subtitle`,children:`Select an account`}),(0,g.jsx)(`div`,{className:`account-picker-list`,children:t.map(e=>(0,g.jsxs)(`button`,{className:`account-picker-card`,onClick:()=>s(e.accountId),disabled:r,type:`button`,children:[(0,g.jsx)(`span`,{className:`account-picker-name`,children:e.businessName||e.accountId}),(0,g.jsx)(`span`,{className:`account-picker-role`,children:e.role}),r&&(0,g.jsx)(a,{className:`account-picker-spinner`,size:16})]},e.accountId))}),o&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:o})]})})}function x(e){let{authPolling:t,setAuthPolling:r,authLoading:a,setAuthLoading:o,pinError:s,setPinError:l,setAppState:u}=e,[d,f]=(0,m.useState)(!1),[p,h]=(0,m.useState)(!1);async function _(){h(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`launch-browser`})})).json();e.launched?f(!0):e.error&&l(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),l(`Could not launch browser.`)}h(!1)}async function v(){o(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){r(!0),f(!0),o(!1);for(let e=0;e<120;e++)if(await new Promise(e=>setTimeout(e,2e3)),(await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`wait`})})).json()).authenticated){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),u(`enter-pin`);return}l(`Timed out waiting for sign-in. Try again.`),r(!1)}else e.error&&l(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),l(`Could not start auth flow.`)}o(!1)}async function y(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),r(!1),l(``)}return t||d?(0,g.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,g.jsxs)(`header`,{className:`chat-header`,style:{paddingBottom:`12px`,flexShrink:0,position:`relative`,maxWidth:`680px`,width:`100%`,margin:`0 auto`,padding:`24px 20px 12px`},children:[t?(0,g.jsx)(`button`,{onClick:y,style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Cancel`,children:`✕`}):(0,g.jsx)(`button`,{onClick:()=>f(!1),style:{position:`absolute`,top:`12px`,right:`12px`,background:`none`,border:`none`,color:`#999`,fontSize:`13px`,cursor:`pointer`,padding:`4px 8px`},"aria-label":`Close browser`,children:`✕`}),(0,g.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,g.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,g.jsx)(`p`,{className:`chat-intro`,children:t?`Sign in and authorize in the browser below.`:`Open your email or prepare your accounts, then sign in.`}),!t&&(0,g.jsx)(`div`,{style:{marginTop:`12px`},children:(0,g.jsx)(c,{variant:`primary`,onClick:v,disabled:a,children:a?(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})})]}),(0,g.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,g.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),s&&(0,g.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:s})]}):(0,g.jsx)(`div`,{className:`connect-page`,children:(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsxs)(`div`,{className:`connect-logos`,children:[(0,g.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,g.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,g.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,g.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,g.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,g.jsx)(`img`,{src:n,alt:i.productName,className:`connect-logo connect-logo--maxy`}),!i.logoContainsName&&(0,g.jsx)(`span`,{className:`connect-logo-label`,children:i.productName})]})]}),(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,i.productName]}),(0,g.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,g.jsx)(c,{variant:`primary`,onClick:v,disabled:a,children:a?(0,g.jsxs)(g.Fragment,{children:[(0,g.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,g.jsx)(`p`,{style:{marginTop:`6px`,fontSize:`11px`,color:`#999`,maxWidth:`300px`,textAlign:`center`,lineHeight:`1.4`},children:`First time? You may need to sign into your email and Anthropic account in the browser before connecting.`}),(0,g.jsx)(`button`,{onClick:_,disabled:p,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:p?`Launching…`:`Open browser first`}),s&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:s})]})})}function S({auth:e}){return e.appState===`loading`?(0,g.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,g.jsx)(v,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,setPinFormRef:e.setPinFormRef,onSubmit:e.handleSetPin,operatorName:e.operatorName,setOperatorName:e.setOperatorName}):e.appState===`connect-claude`?(0,g.jsx)(x,{authPolling:e.authPolling,setAuthPolling:e.setAuthPolling,authLoading:e.authLoading,setAuthLoading:e.setAuthLoading,pinError:e.pinError,setPinError:e.setPinError,setAppState:e.setAppState}):e.appState===`enter-pin`?(0,g.jsx)(y,{pin:e.pin,setPin:e.setPin,showPin:e.showPin,setShowPin:e.setShowPin,pinLoading:e.pinLoading,pinError:e.pinError,pinInputRef:e.pinInputRef,onSubmit:e.handleLogin,remoteAuthEnabled:e.remoteAuthEnabled,onSignOutRemote:()=>{console.info(`[admin-ui] remote-auth sign-out → /__remote-auth/logout`),window.location.href=`/__remote-auth/logout`}}):e.appState===`account-picker`?(0,g.jsx)(b,{accounts:e.accounts,loading:e.accountPickerLoading,error:e.pinError,onSelect:e.handleAccountSelect}):null}export{h as n,S as t};
|