@rubytech/create-maxy-code 0.1.361 → 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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/connector-directory/SKILL.md +53 -0
  3. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +21 -4
  4. package/payload/platform/plugins/cloudflare/bin/__tests__/wait-for.test.sh +107 -0
  5. package/payload/platform/plugins/cloudflare/bin/wait-for.sh +100 -0
  6. package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +1 -1
  7. package/payload/platform/plugins/docs/references/claudeai-connectors.md +20 -3
  8. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  9. package/payload/platform/services/webchat-channel/dist/instructions.js +11 -0
  10. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  11. package/payload/platform/templates/specialists/agents/coding-assistant.md +2 -0
  12. package/payload/server/public/assets/{AdminLoginScreens-D6JtrY9a.js → AdminLoginScreens-CJatEEai.js} +1 -1
  13. package/payload/server/public/assets/{AdminShell-CaVRZTmj.js → AdminShell-D0MDlfI1.js} +1 -1
  14. package/payload/server/public/assets/{Checkbox-BiHO47mz.js → Checkbox-DqnBuFEU.js} +1 -1
  15. package/payload/server/public/assets/OperatorConversations-DDUzu1h8.css +1 -0
  16. package/payload/server/public/assets/{admin-BrCL9qSn.js → admin-Cocx_If1.js} +1 -1
  17. package/payload/server/public/assets/{browser-DNIV6o_p.js → browser-BsFHTA93.js} +1 -1
  18. package/payload/server/public/assets/{calendar-Arx94jTH.js → calendar-hwGeTmWC.js} +1 -1
  19. package/payload/server/public/assets/chat-DMssYitp.js +1 -0
  20. package/payload/server/public/assets/{data-DBInTT-w.js → data-DOjJ8031.js} +1 -1
  21. package/payload/server/public/assets/{graph-z-RYpYMZ.js → graph-BIAaLslC.js} +1 -1
  22. package/payload/server/public/assets/{graph-labels-CVZClzPb.js → graph-labels-CXt55mIP.js} +1 -1
  23. package/payload/server/public/assets/{operator-CoCf8Lk7.js → operator-CRRc0nD-.js} +1 -1
  24. package/payload/server/public/assets/{page-CWnkJCxo.js → page-wUvf2I5u.js} +1 -1
  25. package/payload/server/public/assets/{public-CKMOOQiq.js → public-Ba5G74iP.js} +1 -1
  26. package/payload/server/public/assets/{rotate-ccw-Dgw6hmsm.js → rotate-ccw-BfYwa6ew.js} +1 -1
  27. package/payload/server/public/browser.html +4 -4
  28. package/payload/server/public/calendar.html +4 -4
  29. package/payload/server/public/chat.html +6 -6
  30. package/payload/server/public/data.html +4 -4
  31. package/payload/server/public/graph.html +7 -7
  32. package/payload/server/public/index.html +6 -6
  33. package/payload/server/public/operator.html +8 -8
  34. package/payload/server/public/public.html +6 -6
  35. package/payload/server/public/assets/OperatorConversations-D3UBU6SM.css +0 -1
  36. package/payload/server/public/assets/chat-BwShkhcz.js +0 -1
  37. /package/payload/server/public/assets/{OperatorConversations-CzhbxFwN.js → OperatorConversations-BwnO3IUP.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.361",
3
+ "version": "0.1.363",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: connector-directory
3
+ description: Use when the operator asks to use, connect, install, or integrate with a service you are not already connected to, such as "can you use the Stripe connector", "connect my Notion", "install the official Linear plugin", "do you integrate with Figma". This is how you discover a claude.ai directory connector that is not yet loaded for this client, before you deny it or fall back to the generic static-key connector. It searches Anthropic's public connector catalogue by keyword, hands the operator the claude.ai link to enable the one they want, then confirms the connection inherited and proves it with one live read. Discovery is catalogue-driven: any connector in the directory is findable with no per-connector setup here. Not for a connector that already shows as loaded (use it directly) or one that shows as needing authentication (that is the connector-authenticate path, not discovery).
4
+ ---
5
+
6
+ # Connector directory
7
+
8
+ Invoked by the admin agent directly.
9
+
10
+ claude.ai offers a directory of managed connectors like Stripe, Notion, Linear, Figma, Cloudflare and hundreds more. A connector the operator has already enabled on claude.ai shows up here on its own and you use it directly. This skill is for the other case: the operator asks about a connector that is **not** loaded for this client yet, and you need to find out whether it exists and route the operator to enable it.
11
+
12
+ Without this skill you can only act on connectors already connected, so you would either deny a real integration or fall back to the generic static-key connector. The connector directory is the catalogue that closes that gap.
13
+
14
+ ## When this applies
15
+
16
+ The operator asks to use, connect, or install an integration, and `capabilities-here` (or `claude mcp list`) does **not** already list it. Before you tell the operator there is no such integration, and before you reach for the generic static-key connector, search the directory.
17
+
18
+ ## Discover it
19
+
20
+ The catalogue is a public endpoint. Search it by the operator's keyword:
21
+
22
+ ```sh
23
+ curl -s "https://api.anthropic.com/mcp-registry/v0/servers?version=latest&limit=100&visibility=commercial,gsuite,enterprise,health&search=stripe" \
24
+ | python3 -c 'import sys,json
25
+ for e in json.load(sys.stdin).get("servers",[]):
26
+ s=e.get("server",{}); m=e.get("_meta",{}).get("com.anthropic.api/mcp-registry",{})
27
+ print(m.get("displayName") or s.get("title"), "|", m.get("oneLiner"))
28
+ print(" enable:", m.get("directoryUrl")); print(" server:", m.get("url"), "| authless:", m.get("isAuthless"), "| tools:", len(m.get("toolNames",[])))'
29
+ ```
30
+
31
+ Swap `search=stripe` for the connector the operator named. The fields that matter per match are the display name, the one-line description, the `directoryUrl` (the claude.ai link that enables this exact connector), the server `url`, and the tool count. Running this is also the record of the discovery in the session log, so a later "you missed a connector I asked for" is diagnosable from the transcript.
32
+
33
+ If the search returns nothing, the connector is not in the directory. Say so plainly and offer the generic static-key connector instead. Do not invent a directory entry or a connector that the search did not return.
34
+
35
+ If the endpoint itself does not answer, do not deny the request on that basis: point the operator at the directory at `https://claude.ai/code#settings/customize-connectors` and ask them to browse for the connector there.
36
+
37
+ ## Route the enable step to claude.ai
38
+
39
+ You enable nothing from here. Enabling a directory connector happens on claude.ai, by the operator. Send them the `directoryUrl` from the match and ask them to open it, enable the connector, and complete its sign-in. If a match has no `directoryUrl`, send them to `https://claude.ai/code#settings/customize-connectors` and tell them to choose **Browse connectors** and find it by name. Do not try to render an in-chat connect button; that affordance does not exist here, the claude.ai link is the enable path.
40
+
41
+ ## Confirm it inherited
42
+
43
+ A connector enabled on claude.ai propagates to this client on its own; there is no step to trigger here. Once the operator says they have enabled it, confirm this client picked it up:
44
+
45
+ ```sh
46
+ claude mcp list
47
+ ```
48
+
49
+ The connector reads as **Connected** rather than needing authentication. If it still shows as needing authentication, the claude.ai side is not finished or this client needs the one-off per-client authentication, which is the connector-authenticate path, not a directory problem.
50
+
51
+ ## Prove it
52
+
53
+ Make one live read against the connector and confirm a result comes back: list something, fetch one record, or read one item the connector grants. A read that returns a result is the proof the connector works end to end for this client. A read that fails while `claude mcp list` shows it Connected points at the connector's granted scope; report what the read returned rather than assuming a cause.
@@ -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:aa31039d259f4517fbd2bcda93ca7c32c55aa794ba124b8620df32597ffa4ad3
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 why a
1726
- connector you already added can still say it needs authentication, and how to fix it without leaving
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 and re-curl rather than treating it as done,
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 why a
5
- connector you already added can still say it needs authentication, and how to fix it without leaving
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
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,CAAA;AAEnD;4EAC4E;AAC5E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,kBAAkB,CAE9E;AA6BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAIrE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAErE;AAQD,wBAAgB,oCAAoC,IAAI,MAAM,CAU7D;AAmBD,eAAO,MAAM,yBAAyB,QAwDC,CAAA"}
1
+ {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,CAAA;AAEnD;4EAC4E;AAC5E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,kBAAkB,CAE9E;AA6BD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAIrE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAErE;AAQD,wBAAgB,oCAAoC,IAAI,MAAM,CAU7D;AAmBD,eAAO,MAAM,yBAAyB,QAmEC,CAAA"}
@@ -96,6 +96,17 @@ export const ADMIN_REFRAME_INSTRUCTION = 'Choose the installed specialist that o
96
96
  'check that list and ToolSearch for mcp__claude_ai_* tools: a connector denial is valid only once the skills AND the connectors both come back empty. ' +
97
97
  'When a matching connector exists, use its tool; if the tool result says it needs authentication, call the connector_authenticate tool; ' +
98
98
  'and when capabilities-here already shows the connector as needs-auth, offer that authenticate step rather than denying. ' +
99
+ // Task 1208 — even when the skills and the loaded connectors both come back
100
+ // empty, the integration the operator named may exist in the claude.ai
101
+ // connector directory as a connector this client has not added yet. The
102
+ // managed registry tool is absent from this session, so discovery is
103
+ // catalogue-driven via the connector-directory skill, which searches the
104
+ // public catalogue and routes the enable step to claude.ai. This stops the
105
+ // agent denying a real connector or defaulting to the generic static-key
106
+ // connector before it has looked.
107
+ 'When the skills and the loaded connectors both come back empty, do not stop at a denial or jump to the generic static-key connector: ' +
108
+ 'a connector you have not added to this client yet may still exist in the claude.ai connector directory, ' +
109
+ 'so search the connector directory (load the connector-directory skill) first, and route any match\'s enable step to its claude.ai directory link. ' +
99
110
  'Then carry it out. ' +
100
111
  'Call the reply tool right away to acknowledge you are on it, ' +
101
112
  'send a brief progress note with the reply tool before each long step, and call the reply tool again with the result — ' +
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"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"}
@@ -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
@@ -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};