@rubytech/create-maxy-code 0.1.327 → 0.1.328
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/plainly/SKILL.md +19 -1
- package/payload/premium-plugins/management-consulting/skills/decision-memo/SKILL.md +1 -1
- package/payload/premium-plugins/management-consulting/skills/narrative-builder/SKILL.md +1 -1
- package/payload/premium-plugins/management-consulting/skills/pitch-deck/SKILL.md +63 -81
- package/payload/server/public/assets/{AdminLoginScreens-DNsCEyvZ.js → AdminLoginScreens-DstNf4M-.js} +1 -1
- package/payload/server/public/assets/{AdminShell-DqyjA9x-.js → AdminShell-B5EcXCYR.js} +1 -1
- package/payload/server/public/assets/{Checkbox-C3WNcLH8.js → Checkbox-DWGpkJD5.js} +1 -1
- package/payload/server/public/assets/{OperatorConversations-BQudGb45.css → OperatorConversations-BWu6GMQM.css} +1 -1
- package/payload/server/public/assets/{admin-3jmCp4G3.js → admin-CnIlm4Yi.js} +1 -1
- package/payload/server/public/assets/{browser-DarU3c-V.js → browser-ePr5LxPd.js} +1 -1
- package/payload/server/public/assets/chat-XjXSornk.js +1 -0
- package/payload/server/public/assets/{data-4jByS_fv.js → data-DuObS80U.js} +1 -1
- package/payload/server/public/assets/{graph-CylZ1wV1.js → graph-B5uxt5YB.js} +1 -1
- package/payload/server/public/assets/{graph-labels-hGbD_0qZ.js → graph-labels-fx-bv3Y-.js} +1 -1
- package/payload/server/public/assets/{operator-DgGt6Hc0.js → operator-CIGXegWd.js} +1 -1
- package/payload/server/public/assets/{page-Dl3SpCO3.js → page-BVOAwj-e.js} +2 -2
- package/payload/server/public/assets/{public-8hwaomIS.js → public-DXAdRKJe.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/chat.html +5 -5
- package/payload/server/public/data.html +4 -4
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/operator.html +7 -7
- package/payload/server/public/public.html +5 -5
- package/payload/server/public/assets/chat-CG-r6QZ2.js +0 -1
- /package/payload/server/public/assets/{OperatorConversations-BqFmHTF1.js → OperatorConversations-CSQnX7Dx.js} +0 -0
package/package.json
CHANGED
|
@@ -21,9 +21,27 @@ Textbook explanations are technically correct and pedagogically poor. They conve
|
|
|
21
21
|
|
|
22
22
|
Failing the aloud test produces opacity; failing the understanding test produces parroting; failing the new-to-topic test produces *appearance of clarity*: the answer reads as crisp to the writer but lands as a wall of jargon to the audience. A reader who can teach the concept onward to someone else has cleared all three.
|
|
23
23
|
|
|
24
|
+
## Match the register to the output
|
|
25
|
+
|
|
26
|
+
Two registers, and they are not the same thing. Pick by what the output is.
|
|
27
|
+
|
|
28
|
+
**Explaining a concept** (a definition, a summary, teaching the idea): use the shape-of-natural-writing moves below. A little voice belongs here. You are bringing a reader up to speed, so the dropped-in opening, the one earned turn, the concrete anchor, and the landing close all help the idea land.
|
|
29
|
+
|
|
30
|
+
**Stating what a thing is** (a document, a deck, a proposal, a memo, a report: anything that goes to a reader as a record of what is): the register is flatter and more exact. State what the thing is and how it works, in a sensible order, and stop. The reader should not be able to tell it was written. It just says what is. The essay moves below are wrong here, and reaching for them is most of what makes a business document read as AI. Specifically:
|
|
31
|
+
|
|
32
|
+
- **The header names the thing. It does not sell or tease it.** "Using AI to handle insurance rejections", not "Rejections, sorted". A title that performs is doing the wrong job.
|
|
33
|
+
- **No hook, no arc, no earned turn, no landing reframe.** A document is not an essay. Say the point in the order that makes it clear, and stop. Do not build tension and then resolve it.
|
|
34
|
+
- **No stance or relationship-posturing.** "We want to listen first", "we're here to help", "they're good at it, and that's the problem" are stances, not facts. State the facts and let the reader draw the conclusion.
|
|
35
|
+
- **No rhetorical setup or reveal.** "And that's the problem", "here's the thing", "it comes down to four steps". Cut the setup and say the content.
|
|
36
|
+
- **Prefer the precise term to the vivid image.** This is the one place this register reverses the anchor move below. "Manual data transfer and reconciliation", not "copying numbers between screens". In a deliverable the exact word reads as matter-of-fact; the image reads as performed.
|
|
37
|
+
- **Name a thing once, then reuse the name.** Pick the term ("the centralised database") and keep it. Paraphrasing the same thing three ways for variety makes the reader stop to check whether they are the same thing.
|
|
38
|
+
- **Say the one true thing.** If one accurate sentence covers it, stop. Three supporting bullets where one sentence is true is padding. A whole section can be one sentence when one sentence is the truth.
|
|
39
|
+
|
|
40
|
+
Both registers still obey the recursive rule, the aloud test, and the AI-tells catalogue below. This fork only decides whether you reach for a little voice or stay flat.
|
|
41
|
+
|
|
24
42
|
## The shape of natural writing
|
|
25
43
|
|
|
26
|
-
The AI-tells catalogue below says what to strip. This section says what to reach *for*. The model is the short-essay voice of good pre-GenAI blog writing: plain words, one idea, and a reader who is dropped straight into the middle of it. Read a handful of those posts and the first thing you notice is how *spoken* they are. The sentences are uneven. Nothing warms up. The piece says one true thing and stops. That texture is not decoration; it is what "human" reads as, and it is exactly what AI prose sands off.
|
|
44
|
+
The shape-of-natural-writing moves here are the **explaining-a-concept** register from the fork above; for documents and deliverables, stay flat per the statement register. The AI-tells catalogue below says what to strip. This section says what to reach *for*. The model is the short-essay voice of good pre-GenAI blog writing: plain words, one idea, and a reader who is dropped straight into the middle of it. Read a handful of those posts and the first thing you notice is how *spoken* they are. The sentences are uneven. Nothing warms up. The piece says one true thing and stops. That texture is not decoration; it is what "human" reads as, and it is exactly what AI prose sands off.
|
|
27
45
|
|
|
28
46
|
These are tools, not a template. Use the ones the content actually calls for. Stamping all of them onto every output is its own tell.
|
|
29
47
|
|
|
@@ -23,7 +23,7 @@ A finished memo rests on a clear read of its audience, the decision in front of
|
|
|
23
23
|
|
|
24
24
|
The memo goes out under the business name, so its prose runs through plain English and the house voice before the first sentence.
|
|
25
25
|
|
|
26
|
-
1. **Plain
|
|
26
|
+
1. **Plain, matter-of-fact register.** The first prose action is `skill-load skillName=plainly`. Write in its statement register: say plainly what the memo recommends and why, with no selling, no story arc, and no performance. Apply it to every line: the recommendation, the options table, the body. `plainly` carries the full rule and the habits to strip.
|
|
27
27
|
2. **Voice mirror (hard gate).** Before drafting, call `voice-retrieve-conditioning` with `format: "article"`, `length: "long"`, `scope: "org"`, and `topic` set to the decision's domain. The tool emits its own `[voice-retrieve] …` line. Gate on `status`: on **`ok`**, condition every line on the returned style card and exemplars, then draft. On **`no-data` | `error`**, stop and emit no fabricated prose: tell the operator the voice is not established and offer to establish it (the voice-mirror skill) or to supply the prose for the skill to structure and plainly-polish. Emit `[decision-memo] voice-gate status=<ok|no-data|error> action=<conditioned|stopped|operator-prose>`.
|
|
28
28
|
|
|
29
29
|
## Output Format
|
|
@@ -28,7 +28,7 @@ Lead with the answer, structure support in a pyramid, and anticipate the questio
|
|
|
28
28
|
|
|
29
29
|
The narrative goes out under the business name, so its headlines and body run through plain English and the house voice before the first line.
|
|
30
30
|
|
|
31
|
-
1. **Plain
|
|
31
|
+
1. **Plain, matter-of-fact register.** The first prose action is `skill-load skillName=plainly`. Write in its statement register: state the recommendation and the support plainly, with no selling, no story arc, and no performance. Apply it to every headline and the body. `plainly` carries the full rule and the habits to strip.
|
|
32
32
|
2. **Voice mirror (hard gate).** Before drafting, call `voice-retrieve-conditioning` with `format: "article"`, `length: "long"`, `scope: "org"`, and `topic` set to the recommendation's domain. The tool emits its own `[voice-retrieve] …` line. Gate on `status`: on **`ok`**, condition every headline and line on the returned style card and exemplars, then draft. On **`no-data` | `error`**, stop and emit no fabricated prose: tell the operator the voice is not established and offer to establish it (the voice-mirror skill) or to supply the prose for the skill to structure and plainly-polish. Emit `[narrative-builder] voice-gate status=<ok|no-data|error> action=<conditioned|stopped|operator-prose>`.
|
|
33
33
|
|
|
34
34
|
## Output Format
|
|
@@ -1,137 +1,119 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pitch-deck
|
|
3
|
-
description: Composes a
|
|
3
|
+
description: Composes a presentation deck in the operator's own voice. It states plainly what is being proposed and how it works; it does not sell or perform. Three kinds: a deck that wins new work, a deck that presents findings and a recommendation, and a deck that sets out a point of view. Use for a consulting pitch deck, a capabilities or credentials deck, a client findings or recommendation deck, a board or steering-committee deck, or a thought-leadership deck. Writes the full text of the deck first and gets the operator to approve it before building anything; only then chooses light or dark mode and applies the brand's design tokens. Never invents a number. Not a founder fundraise deck; that is `venture-studio:investor-data-room`. Not a generic renderer for arbitrary HTML slides; that is `admin:deck-pages`.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Pitch Deck
|
|
7
7
|
|
|
8
|
-
The consulting bundle's
|
|
8
|
+
The consulting bundle's deck. It states plainly what is being proposed and how it works. The words come first, and the operator approves them before any deck is built. It composes only from facts the operator already curated, and it never invents a number.
|
|
9
9
|
|
|
10
10
|
## When to use
|
|
11
11
|
|
|
12
|
-
Pick the
|
|
12
|
+
Pick the kind of deck from the brief. All three are built the same way: words first, approved, then rendered.
|
|
13
13
|
|
|
14
|
-
|
|
|
15
|
-
|
|
16
|
-
| **
|
|
17
|
-
| **
|
|
18
|
-
| **
|
|
14
|
+
| Kind | Use it when |
|
|
15
|
+
|------|-------------|
|
|
16
|
+
| **Win the work** | A capabilities or credentials deck, a "why us" deck for a prospect. |
|
|
17
|
+
| **Present findings and a recommendation** | A findings deck, a recommendation, a board or steering-committee deck. |
|
|
18
|
+
| **Set out a point of view** | A topic or trends deck, a conference talk. |
|
|
19
19
|
|
|
20
20
|
### When not to use
|
|
21
21
|
|
|
22
|
-
- **Founder fundraise deck** (seed
|
|
23
|
-
- **
|
|
22
|
+
- **Founder fundraise deck** (seed or Series raise, investor pitch under the founder's own name) routes to `venture-studio:investor-data-room`.
|
|
23
|
+
- **A deck the operator already wrote, an arbitrary fixed-dimension slide layout, "save this HTML as a deck"** routes to `admin:deck-pages` (the format spec and generic slide renderer).
|
|
24
24
|
|
|
25
25
|
## Where this runs
|
|
26
26
|
|
|
27
|
-
Invoked from `specialists:content-producer`. The admin seat delegates the deck from the first point: it is refused this skill and dispatches content-producer (Agent tool, `subagent_type: content-producer`). Everything then happens on the one seat. content-producer carries `voice-retrieve-conditioning`, loads `plainly`, and holds the render tools, so the
|
|
27
|
+
Invoked from `specialists:content-producer`. The admin seat delegates the deck from the first point: it is refused this skill and dispatches content-producer (Agent tool, `subagent_type: content-producer`). Everything then happens on the one seat. content-producer carries `voice-retrieve-conditioning`, loads `plainly`, and holds the render tools, so the words, the voice, and the build all run together.
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## How it works: words first, then the deck
|
|
30
30
|
|
|
31
|
-
The
|
|
31
|
+
The order matters and it is not optional.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- **MECE grouping.** Every breakdown (findings, services, segments, causes) has parts that do not overlap and together leave no gap. If two points say the same thing, merge them.
|
|
37
|
-
- **Action titles.** Every slide headline is a full-sentence conclusion, not a topic label. "The European market contracted 12% in Q3", not "Market overview". If you cannot write the action title, the slide has no point yet; find the point first.
|
|
38
|
-
- **One message per slide, vertical logic.** Each slide makes exactly one point. The body proves the title and nothing else. Cut anything that does not.
|
|
39
|
-
- **"So what" prune.** For every slide, answer "so what?". A slide that survives only as background is appendix material or is cut.
|
|
40
|
-
- **Reverse-storyboard the horizontal logic.** Read the action titles top to bottom as a list, with no bodies and no charts. They must tell one coherent story and the recommendation must feel inevitable by the end. If the line of titles jumps or repeats, fix the titles before touching the bodies.
|
|
41
|
-
- **Source footnote on every data slide.** Every slide that carries a number shows where the number came from: a graph node, a named publication, or the operator's own figures. A numbered slide with no source line is a defect.
|
|
42
|
-
- **Never invent numbers.** Figures come only from the operator's data or from a sibling skill's output. If a number is missing, leave a visible gap and ask for it; do not fill it.
|
|
33
|
+
1. **Write the words.** Compose the full text of every slide: the header and the body and any lists. Write it in the matter-of-fact register and gate it on voice first (both below). This is the whole deck as plain text, with no layout.
|
|
34
|
+
2. **Get the words approved.** Show the operator the text, every slide. Iterate until they approve it. Build nothing before then. A deck built on words the operator has not signed off wastes the build, and layout hides wording problems instead of surfacing them.
|
|
35
|
+
3. **Build the deck.** Only after the words are approved: decide light or dark mode, apply the brand's design tokens, then render through `slides:deck-system` to HTML and a PDF.
|
|
43
36
|
|
|
44
|
-
##
|
|
37
|
+
## Writing the words
|
|
45
38
|
|
|
46
|
-
|
|
39
|
+
Load `plainly` and write in its **statement register**: say what each thing is, in a sensible order, with no selling, no story arc, and no performance. `plainly` carries the full rule and the habits to strip; do not restate them here, apply them. The points that matter most for a deck:
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
- **Each slide's header says what the slide is, or states its one fact.** "Using AI to handle insurance rejections", not a slogan. Never bend a header to land a word in a set place or to match the shape of another header.
|
|
42
|
+
- **Put the point first.** Say what you are proposing, then explain it. Do not build up to it.
|
|
43
|
+
- **Order the slides so the whole thing is clear:** where they are now, what you would do, how it works, what it costs to start, what you are asking for. Read the headers on their own, in order. They should lay the whole thing out plainly.
|
|
44
|
+
- **Each section covers something the others do not.** No overlap, no gaps. If two slides say the same thing, merge them.
|
|
45
|
+
- **One point per slide.** Cut any slide that does not say something the reader needs.
|
|
46
|
+
- **Every slide with a number shows where the number came from:** a graph node, a named source, or the operator's own figures. A number with no source is a defect.
|
|
47
|
+
- **Never invent a number.** If one is missing, leave a visible gap and ask for it. Do not fill it.
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
### Voice (hard gate)
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
A deck ships under the business name, so it uses the **house voice**, not any one person's personal voice. Before drafting any prose, call `voice-retrieve-conditioning` with `format: "article"`, `length: "long"`, `scope: "org"`, and `topic` set to the engagement domain (for example "operating-model redesign for a regional bank"). The tool itself emits `[voice-retrieve] scope=org format=article status=<s> styleCardBytes=<n> exemplarCount=<k>`; you do not write that line.
|
|
51
|
+
A deck goes out under the business name, so it uses the **house voice**, not any one person's personal voice. Before writing any of the words, call `voice-retrieve-conditioning` with `format: "article"`, `length: "long"`, `scope: "org"`, and `topic` set to the engagement domain. The tool emits its own `[voice-retrieve] …` line; you do not write that line.
|
|
55
52
|
|
|
56
53
|
Then gate on `status`. This diverges from the sibling composers, which silently degrade to a default register; this skill stops instead.
|
|
57
54
|
|
|
58
|
-
- **`ok`** (a style card
|
|
59
|
-
- **`no-data` | `error
|
|
60
|
-
|
|
61
|
-
A `scope: "org"` request returns only `ok`, `no-data`, or `error`; `fallback-org` arises solely on a personal-scope request, so it does not occur here. If it ever does, treat it as `ok` since it still carries an org style card.
|
|
55
|
+
- **`ok`** (a style card or exemplars came back): write every line conditioned on the style card and exemplars.
|
|
56
|
+
- **`no-data` | `error`**: stop. Write no slide text. Tell the operator the deck voice is not established, and offer two routes: establish the house voice through the voice-mirror skill, then re-run; or supply the words themselves, which this skill will only structure and run through `plainly`, never generate.
|
|
62
57
|
|
|
63
|
-
|
|
58
|
+
A `scope: "org"` request returns only `ok`, `no-data`, or `error`. Emit one decision line after the gate:
|
|
64
59
|
|
|
65
60
|
```
|
|
66
61
|
[pitch-deck] voice-gate status=<ok|no-data|error> action=<conditioned|stopped|operator-prose>
|
|
67
62
|
```
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
## Mode A: new-business / credentials pitch
|
|
72
|
-
|
|
73
|
-
Win the work. A fixed spine appears in every deck of this kind; a short domain-themed middle and an optional section set flex the deck to 11–14 slides for the engagement.
|
|
74
|
-
|
|
75
|
-
**Spine (always present, in order):** Cover, Contents, Introduction, Philosophy / Approach, **Services**, *[domain-themed middle]*, **Case Studies**, **Performance Metrics (KPIs)**, **Team**, Contact.
|
|
64
|
+
## Get the words approved
|
|
76
65
|
|
|
77
|
-
|
|
66
|
+
Show the operator the full text of the deck, every slide, before building anything. Take their edits and fold them in. Repeat until they approve. The approval is the gate to the build step; do not render a deck the operator has not signed off.
|
|
78
67
|
|
|
79
|
-
|
|
68
|
+
## Build the deck
|
|
80
69
|
|
|
81
|
-
|
|
70
|
+
Only after the words are approved.
|
|
82
71
|
|
|
83
|
-
|
|
72
|
+
1. **Choose light or dark mode.** Ask the operator, or take it from the brand if the brand sets one. The choice is made once, here, before rendering.
|
|
73
|
+
2. **Apply the brand's design tokens.** Read `brand.json` and take the colours and fonts from it. This is a prerequisite of the build, not a finishing pass. Do not invent colours; if the brand omits a value, fall back to the default brand's value.
|
|
74
|
+
3. **Render.** Hand the approved words to `slides:deck-system`, which carries the components, the themes, and the PDF export. content-producer renders on the same seat and exports the PDF through the existing path. There is no new renderer.
|
|
84
75
|
|
|
85
|
-
|
|
76
|
+
If `slides` is not enabled, say so and fall back to the always-present `admin:deck-pages` format spec. Never return a blank turn because a dependency is absent. Build in the operator's own brand; never embed a named firm's branded template or assets.
|
|
86
77
|
|
|
87
|
-
|
|
78
|
+
## The three kinds, and how each is ordered
|
|
88
79
|
|
|
89
|
-
|
|
80
|
+
The voice and register are the same for all three. They differ only in what slides they hold and in what order.
|
|
90
81
|
|
|
91
|
-
|
|
82
|
+
- **Win the work.** A fixed set of slides, plus a few specific to the engagement: who you are, how you work, what you do, the work you have done before, the people, how to reach you. Add only the sections this prospect needs. Aim for eleven to fourteen slides.
|
|
83
|
+
- **Present findings and a recommendation.** One slide that states the recommendation and the few facts behind it, then where things stand now, what is wrong, what you found, what you recommend, how it would roll out, and what happens next. The first slide stands on its own: a reader who sees only it knows the recommendation, the reason, and the next step.
|
|
84
|
+
- **Set out a point of view.** The topic first, then the data, then what it means and what to do about it, then a short note on who you are at the end. No client-specific recommendation and no sell.
|
|
92
85
|
|
|
93
|
-
|
|
86
|
+
## Charts
|
|
94
87
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Match the chart to the message. `slides:deck-system` ships no consulting-chart component, so build these on-token using its freestyle rules (stay on the theme's colours, fonts, and spacing; name the class; keep the CSS inline). Lead with them in Mode B; they are available to all modes.
|
|
88
|
+
Match the chart to the point. `slides:deck-system` ships no consulting-chart component, so build these on the theme's colours, fonts, and spacing.
|
|
98
89
|
|
|
99
90
|
| Chart | Use it for |
|
|
100
91
|
|-------|-----------|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
92
|
+
| Waterfall | A figure moving between two points (a cost or margin walk). |
|
|
93
|
+
| Marimekko | Segments by size and share in one view. |
|
|
94
|
+
| Two-by-two | Sorting things into four boxes (a portfolio or a priority call). |
|
|
95
|
+
| Gantt | A plan over time, with owners and dates. |
|
|
105
96
|
|
|
106
|
-
##
|
|
97
|
+
## Where the content comes from
|
|
107
98
|
|
|
108
99
|
The deck composes the outputs of the other consulting skills. Numbers come from those outputs or the operator's data only.
|
|
109
100
|
|
|
110
101
|
| Sibling output | Lands in |
|
|
111
102
|
|----------------|----------|
|
|
112
|
-
| `situation-assessment` |
|
|
113
|
-
| `market-mapping` / `profit-pool-analysis` |
|
|
114
|
-
| `strategic-options` |
|
|
115
|
-
| `business-case-builder` |
|
|
116
|
-
| `transformation-roadmap` |
|
|
117
|
-
| `narrative-builder` / `stakeholder-alignment` |
|
|
118
|
-
|
|
119
|
-
## Render handoff
|
|
120
|
-
|
|
121
|
-
When `slides` is enabled, the render path is `slides:deck-system`: it carries 25 HTML components, the design tokens, the themes, and the PDF export. content-producer renders the composed slides through `deck-system` on the same seat; the PDF exports via the existing path. There is no new renderer.
|
|
122
|
-
|
|
123
|
-
When `slides` is not enabled, say so and fall back to the always-present `admin:deck-pages` format spec for a slide-based PDF. Never return a blank turn because a dependency is absent.
|
|
124
|
-
|
|
125
|
-
Implement open frameworks in the operator's own brand. Never embed a named firm's branded template or assets.
|
|
103
|
+
| `situation-assessment` | Where things stand now |
|
|
104
|
+
| `market-mapping` / `profit-pool-analysis` | The findings, and a Marimekko |
|
|
105
|
+
| `strategic-options` | The recommendation, and a two-by-two |
|
|
106
|
+
| `business-case-builder` | The economics, and a waterfall |
|
|
107
|
+
| `transformation-roadmap` | The rollout, and a Gantt |
|
|
108
|
+
| `narrative-builder` / `stakeholder-alignment` | The recommendation slide, and pre-wiring |
|
|
126
109
|
|
|
127
|
-
##
|
|
110
|
+
## Done when
|
|
128
111
|
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
- The voice gate decided before any prose was drafted, and the decision is logged.
|
|
112
|
+
- Every line passes the said-aloud test in `plainly`'s statement register: read it to a colleague across the table and you would say it that way. It states what the thing is, with no selling, no arc, and none of the habits `plainly` strips.
|
|
113
|
+
- The operator approved the words before the deck was built.
|
|
114
|
+
- Light or dark mode was chosen and the brand's design tokens were applied before rendering.
|
|
115
|
+
- Every slide with a number shows its source, and every number traces to the brief or a sibling output.
|
|
116
|
+
- The deck renders to HTML and exports a PDF.
|
|
135
117
|
|
|
136
118
|
## Observability
|
|
137
119
|
|
|
@@ -139,4 +121,4 @@ Implement open frameworks in the operator's own brand. Never embed a named firm'
|
|
|
139
121
|
[pitch-deck] voice-gate status=<ok|no-data|error> action=<conditioned|stopped|operator-prose>
|
|
140
122
|
```
|
|
141
123
|
|
|
142
|
-
The `[voice-retrieve] …` line is emitted by the voice tool
|
|
124
|
+
The `[voice-retrieve] …` line is emitted by the voice tool. A render or export failure surfaces through `content-producer`'s existing `[tool-failure-diag]` line. A misroute shows as the absence of a `skill-load skillName=pitch-deck` line in a consulting-deck turn.
|
package/payload/server/public/assets/{AdminLoginScreens-DNsCEyvZ.js → AdminLoginScreens-DstNf4M-.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./chunk-Pqm5yXtL.js";import{L as t,O as n,V as r,b as i,x as a,y as o}from"./OperatorConversations-BqFmHTF1.js";import{i as s}from"./admin-types-CJrGd46U.js";import{h as c,l}from"./AdminShell-DqyjA9x-.js";import{t as u}from"./Checkbox-C3WNcLH8.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,l]=(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,t){y(!0);try{let n=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!n.ok){h((await n.json().catch(()=>({}))).error||`Invalid PIN`);return}let i=await n.json();if(i.accounts&&!i.session_key){console.log(`[admin] account picker shown: userId=${i.userId} accountCount=${i.accounts.length}`),B(i.accounts),r(`account-picker`);return}D(i.session_key),R(i.sessionId??null),j(i.businessName),N(i.role??null),F(i.userName===void 0?null:i.userName),L(i.avatar??null);let o=s(i.thinkingView);if(G.current=o,W(o),t)try{sessionStorage.setItem(`maxy-account-id`,t)}catch{}try{sessionStorage.setItem(`maxy-admin-session-key`,i.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 t=o.trim();if(!t){h(`Please enter your name.`);return}if(i.length<4){h(`PIN must be at least 4 characters.`);return}let n=i;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:n,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let n=await e.json();n.pin_configured&&n.claude_authenticated?r(`enter-pin`):n.pin_configured?r(`connect-claude`):h(t.error||`Failed to set PIN.`)}else r(`enter-pin`)}catch{r(`enter-pin`)}return}h(t.error||`Failed to set PIN.`);return}let i=await fetch(`/api/health`);if((i.ok?await i.json():null)?.claude_authenticated){await Z(n);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 c())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:l,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:$,handleChangePin:(0,m.useCallback)(async()=>{if(!i){h(`Enter your current PIN first.`);return}y(!0),h(``);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify({currentPin:i})});if(!e.ok){h((await e.json().catch(()=>({error:`Incorrect PIN.`}))).error||`Incorrect PIN.`);return}a(``),h(``),r(`set-pin`)}catch(e){console.error(`[admin-auth] change pin failed:`,e),h(e instanceof Error?e.message:String(e))}finally{y(!1)}},[i])}}var g=a();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:n,showPin:r,setShowPin:a,pinLoading:s,pinError:c,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:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,o.productName]}),(0,g.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,o.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:n,onComplete:()=>{},showPin:r}),(0,g.jsx)(l,{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:r,onChange:()=>a(e=>!e),label:`Show PIN`})]}),c&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:c})]})})}function y(e){let{pin:t,setPin:n,showPin:r,setShowPin:a,pinLoading:s,pinError:c,pinInputRef:d,onSubmit:f,onChangePin:p,remoteAuthEnabled:m,onSignOutRemote:h}=e;return(0,g.jsxs)(`div`,{className:`connect-page`,children:[m&&h&&(0,g.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:h,children:`Sign out`}),(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:o.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:n,onComplete:()=>{},showPin:r,autoFocus:!0}),(0,g.jsx)(l,{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.jsxs)(`div`,{className:`pin-options`,children:[(0,g.jsx)(u,{checked:r,onChange:()=>a(e=>!e),label:`Show PIN`}),(0,g.jsx)(l,{type:`button`,variant:`ghost`,onClick:p,children:`Change PIN`})]})]}),c&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:c})]})]})}function b(e){let{accounts:t,loading:r,error:a,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:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:o.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)(n,{className:`account-picker-spinner`,size:16})]},e.accountId))}),a&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:a})]})})}function x(e){let{authPolling:t,setAuthPolling:n,authLoading:r,setAuthLoading:a,pinError:s,setPinError:c,setAppState:u}=e,[d,f]=(0,m.useState)(!1),[p,h]=(0,m.useState)(!1);async function _(){h(!0),c(``);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&&c(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),c(`Could not launch browser.`)}h(!1)}async function v(){a(!0),c(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){n(!0),f(!0),a(!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}c(`Timed out waiting for sign-in. Try again.`),n(!1)}else e.error&&c(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),c(`Could not start auth flow.`)}a(!1)}async function y(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),n(!1),c(``)}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)(l,{variant:`primary`,onClick:v,disabled:r,children:r?(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:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsx)(`span`,{className:`connect-logo-label`,children:o.productName})]})]}),(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,o.productName]}),(0,g.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,g.jsx)(l,{variant:`primary`,onClick:v,disabled:r,children:r?(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,onChangePin:e.handleChangePin,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-Pqm5yXtL.js";import{L as t,O as n,V as r,b as i,x as a,y as o}from"./OperatorConversations-CSQnX7Dx.js";import{i as s}from"./admin-types-CJrGd46U.js";import{h as c,l}from"./AdminShell-B5EcXCYR.js";import{t as u}from"./Checkbox-DWGpkJD5.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,l]=(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,t){y(!0);try{let n=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!n.ok){h((await n.json().catch(()=>({}))).error||`Invalid PIN`);return}let i=await n.json();if(i.accounts&&!i.session_key){console.log(`[admin] account picker shown: userId=${i.userId} accountCount=${i.accounts.length}`),B(i.accounts),r(`account-picker`);return}D(i.session_key),R(i.sessionId??null),j(i.businessName),N(i.role??null),F(i.userName===void 0?null:i.userName),L(i.avatar??null);let o=s(i.thinkingView);if(G.current=o,W(o),t)try{sessionStorage.setItem(`maxy-account-id`,t)}catch{}try{sessionStorage.setItem(`maxy-admin-session-key`,i.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 t=o.trim();if(!t){h(`Please enter your name.`);return}if(i.length<4){h(`PIN must be at least 4 characters.`);return}let n=i;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:n,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===409){console.log(`[admin] PIN already configured — re-checking health`);try{let e=await fetch(`/api/health`);if(e.ok){let n=await e.json();n.pin_configured&&n.claude_authenticated?r(`enter-pin`):n.pin_configured?r(`connect-claude`):h(t.error||`Failed to set PIN.`)}else r(`enter-pin`)}catch{r(`enter-pin`)}return}h(t.error||`Failed to set PIN.`);return}let i=await fetch(`/api/health`);if((i.ok?await i.json():null)?.claude_authenticated){await Z(n);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 c())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:l,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:$,handleChangePin:(0,m.useCallback)(async()=>{if(!i){h(`Enter your current PIN first.`);return}y(!0),h(``);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify({currentPin:i})});if(!e.ok){h((await e.json().catch(()=>({error:`Incorrect PIN.`}))).error||`Incorrect PIN.`);return}a(``),h(``),r(`set-pin`)}catch(e){console.error(`[admin-auth] change pin failed:`,e),h(e instanceof Error?e.message:String(e))}finally{y(!1)}},[i])}}var g=a();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:n,showPin:r,setShowPin:a,pinLoading:s,pinError:c,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:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,o.productName]}),(0,g.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,o.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:n,onComplete:()=>{},showPin:r}),(0,g.jsx)(l,{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:r,onChange:()=>a(e=>!e),label:`Show PIN`})]}),c&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:c})]})})}function y(e){let{pin:t,setPin:n,showPin:r,setShowPin:a,pinLoading:s,pinError:c,pinInputRef:d,onSubmit:f,onChangePin:p,remoteAuthEnabled:m,onSignOutRemote:h}=e;return(0,g.jsxs)(`div`,{className:`connect-page`,children:[m&&h&&(0,g.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:h,children:`Sign out`}),(0,g.jsxs)(`div`,{className:`connect-content`,children:[(0,g.jsx)(`img`,{src:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:o.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:n,onComplete:()=>{},showPin:r,autoFocus:!0}),(0,g.jsx)(l,{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.jsxs)(`div`,{className:`pin-options`,children:[(0,g.jsx)(u,{checked:r,onChange:()=>a(e=>!e),label:`Show PIN`}),(0,g.jsx)(l,{type:`button`,variant:`ghost`,onClick:p,children:`Change PIN`})]})]}),c&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:c})]})]})}function b(e){let{accounts:t,loading:r,error:a,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:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsx)(`h1`,{className:`connect-title`,children:o.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)(n,{className:`account-picker-spinner`,size:16})]},e.accountId))}),a&&(0,g.jsx)(`p`,{className:`admin-pin-error`,children:a})]})})}function x(e){let{authPolling:t,setAuthPolling:n,authLoading:r,setAuthLoading:a,pinError:s,setPinError:c,setAppState:u}=e,[d,f]=(0,m.useState)(!1),[p,h]=(0,m.useState)(!1);async function _(){h(!0),c(``);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&&c(e.error)}catch(e){console.error(`[admin] browser launch error:`,e),c(`Could not launch browser.`)}h(!1)}async function v(){a(!0),c(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){n(!0),f(!0),a(!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}c(`Timed out waiting for sign-in. Try again.`),n(!1)}else e.error&&c(e.error)}catch(e){console.error(`[admin] auth flow error:`,e),c(`Could not start auth flow.`)}a(!1)}async function y(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),n(!1),c(``)}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)(l,{variant:`primary`,onClick:v,disabled:r,children:r?(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:i,alt:o.productName,className:`connect-logo connect-logo--maxy`}),!o.logoContainsName&&(0,g.jsx)(`span`,{className:`connect-logo-label`,children:o.productName})]})]}),(0,g.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,o.productName]}),(0,g.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,g.jsx)(l,{variant:`primary`,onClick:v,disabled:r,children:r?(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,onChangePin:e.handleChangePin,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};
|