@rubytech/create-maxy-code 0.1.454 → 0.1.455
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/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js +87 -67
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts +37 -11
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js +56 -14
- package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js.map +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-5R0A40Is.js → AdminLoginScreens-Cl7VBbPS.js} +1 -1
- package/payload/server/public/assets/{AdminShell-B2HeKInP.js → AdminShell-lBaQG3aT.js} +1 -1
- package/payload/server/public/assets/{Checkbox-yjCERU3B.js → Checkbox-P8PNtn8n.js} +1 -1
- package/payload/server/public/assets/{admin-BDio0OER.js → admin-CB6VgLUJ.js} +1 -1
- package/payload/server/public/assets/{browser-bSeWXhod.js → browser-CKH8YoIg.js} +1 -1
- package/payload/server/public/assets/{calendar-MNAmsxL3.js → calendar-xQ-OEVti.js} +1 -1
- package/payload/server/public/assets/chat-DH8K5TeZ.js +1 -0
- package/payload/server/public/assets/chevron-left-Ceky-ebO.js +1 -0
- package/payload/server/public/assets/data-Cvv8Kg9J.js +1 -0
- package/payload/server/public/assets/{graph-DRWRwsUZ.js → graph-CrEmAyVE.js} +2 -2
- package/payload/server/public/assets/{graph-labels-Cy7GyGln.js → graph-labels-DbL7Cxod.js} +1 -1
- package/payload/server/public/assets/{maximize-2-BRHW5pKh.js → maximize-2-Up8UP3Iu.js} +1 -1
- package/payload/server/public/assets/{operator-Dv2k2JaW.js → operator-BE366HH-.js} +1 -1
- package/payload/server/public/assets/{page-BZRZZcqM.js → page-CQjjQz0V.js} +1 -1
- package/payload/server/public/assets/{page-CoBzMOIg.js → page-Wvb1S03b.js} +1 -1
- package/payload/server/public/assets/{public-CirpatD6.js → public-ZkXB-CHy.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-tv1JxnBS.js → rotate-ccw-TTz_MVO0.js} +1 -1
- package/payload/server/public/assets/{tasks-B8iXIZ3w.js → tasks-DNCoSRkP.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-MGC9wMbZ.js → time-entry-format-BooKdDRT.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-DdwMdpa-.js → useCopyFeedback-C23_n92I.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-DyoyGg7d.js → useSelectionMode-c4oZYAW0.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-CMi8n_8S.css → useSubAccountSwitcher-CHJo5leK.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-BDtJhfh_.js → useVoiceRecorder-BkxW-GL1.js} +1 -1
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +10 -10
- package/payload/server/public/graph.html +11 -11
- package/payload/server/public/index.html +13 -13
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/tasks.html +5 -5
- package/payload/server/public/assets/chat-Bq4Uq3Ht.js +0 -1
- package/payload/server/public/assets/chevron-left-D5XwUM2x.js +0 -1
- package/payload/server/public/assets/data-DY10qHOy.js +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-B_zj_dY-.js → useSubAccountSwitcher-6UoTftbJ.js} +0 -0
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* UserProfile reads
|
|
2
|
+
* UserProfile timezone reads resolve the account's seeded OWNER, not the
|
|
3
|
+
* acting session user (Task 1684).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* the
|
|
5
|
+
* A house operator acting inside a client sub-account has no UserProfile
|
|
6
|
+
* scoped to that account; the account owner's profile is authoritative
|
|
7
|
+
* (mirrors the 1557/1564 owner-resolution). These tests pin:
|
|
8
|
+
* - Missing acting userId is still a caller bug (MissingUserIdError),
|
|
9
|
+
* surfaced before any query — the read never widens account-wide.
|
|
10
|
+
* - When the account has a seeded owner, the profile read is keyed on the
|
|
11
|
+
* OWNER's userId, not the acting user's — the discriminating check that
|
|
12
|
+
* prevents reversion to acting-user reads.
|
|
13
|
+
* - When the account has NO seeded owner (legacy), it falls back to the
|
|
14
|
+
* acting user's profile.
|
|
15
|
+
* - The profile read still predicates BOTH accountId AND userId.
|
|
16
|
+
* - The "no profile" / "no timezone" errors name the RESOLVED owner row so
|
|
17
|
+
* a profile-update writes and reads the same row.
|
|
11
18
|
*
|
|
12
|
-
* The Neo4j driver is mocked through vi.mock so these tests never need a
|
|
13
|
-
*
|
|
14
|
-
*
|
|
19
|
+
* The Neo4j driver is mocked through vi.mock so these tests never need a live
|
|
20
|
+
* database. The single mocked session.run serves both queries in order:
|
|
21
|
+
* run #1 is the owner-resolution query, run #2 is the profile read.
|
|
15
22
|
*/
|
|
16
23
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
17
24
|
const mockRun = vi.fn();
|
|
@@ -34,23 +41,27 @@ vi.mock("neo4j-driver", () => {
|
|
|
34
41
|
process.env.NEO4J_URI = "bolt://localhost:7687";
|
|
35
42
|
process.env.NEO4J_PASSWORD = "test";
|
|
36
43
|
const { getUserTimezone, MissingUserIdError } = await import("../neo4j.js");
|
|
37
|
-
function
|
|
44
|
+
function tzRecord(timezone) {
|
|
38
45
|
return { get: (key) => (key === "timezone" ? timezone : null) };
|
|
39
46
|
}
|
|
40
|
-
|
|
47
|
+
function ownerRecord(userId) {
|
|
48
|
+
return { get: (key) => (key === "userId" ? userId : null) };
|
|
49
|
+
}
|
|
50
|
+
/** Queue the two run() results for one getUserTimezone call: owner, then profile. */
|
|
51
|
+
function queue(owner, tz) {
|
|
52
|
+
mockRun.mockResolvedValueOnce({ records: owner ? [ownerRecord(owner)] : [] });
|
|
53
|
+
mockRun.mockResolvedValueOnce({ records: tz === "no-row" ? [] : [tzRecord(tz)] });
|
|
54
|
+
}
|
|
55
|
+
describe("getUserTimezone owner-resolution (Task 1684)", () => {
|
|
41
56
|
beforeEach(() => {
|
|
42
57
|
mockRun.mockReset();
|
|
43
58
|
mockClose.mockClear();
|
|
44
59
|
});
|
|
45
|
-
it("throws MissingUserIdError when userId is empty string", async () => {
|
|
60
|
+
it("throws MissingUserIdError when acting userId is empty string — no query runs", async () => {
|
|
46
61
|
await expect(getUserTimezone("acct-abc12345", "")).rejects.toBeInstanceOf(MissingUserIdError);
|
|
47
|
-
// Read-side guard fires before any session.run, so no query is made.
|
|
48
62
|
expect(mockRun).not.toHaveBeenCalled();
|
|
49
63
|
});
|
|
50
|
-
it("throws MissingUserIdError when userId is null/undefined", async () => {
|
|
51
|
-
// The signature is (accountId, userId: string), but at runtime callers can
|
|
52
|
-
// still pass undefined/null when their own wiring is broken — the lib is
|
|
53
|
-
// the last line of defence.
|
|
64
|
+
it("throws MissingUserIdError when acting userId is null/undefined — no query runs", async () => {
|
|
54
65
|
await expect(
|
|
55
66
|
// @ts-expect-error — exercising runtime-undefined case
|
|
56
67
|
getUserTimezone("acct-abc12345", undefined)).rejects.toBeInstanceOf(MissingUserIdError);
|
|
@@ -59,61 +70,70 @@ describe("getUserTimezone read doctrine", () => {
|
|
|
59
70
|
getUserTimezone("acct-abc12345", null)).rejects.toBeInstanceOf(MissingUserIdError);
|
|
60
71
|
expect(mockRun).not.toHaveBeenCalled();
|
|
61
72
|
});
|
|
62
|
-
it("
|
|
63
|
-
|
|
64
|
-
|
|
73
|
+
it("reads the OWNER's profile, not the acting user's, when the account has a seeded owner", async () => {
|
|
74
|
+
// Acting user = house operator with no profile in this account; owner has
|
|
75
|
+
// the Europe/London card. The scheduler must return the owner's tz.
|
|
76
|
+
queue("owner-3f81e7c2", "Europe/London");
|
|
77
|
+
const tz = await getUserTimezone("acct-2078cb54", "operator-436fdf2d");
|
|
65
78
|
expect(tz).toBe("Europe/London");
|
|
79
|
+
// Two queries: owner-resolution, then the profile read.
|
|
80
|
+
expect(mockRun).toHaveBeenCalledTimes(2);
|
|
81
|
+
// The owner-resolution query keys on role:'owner'.
|
|
82
|
+
expect(mockRun.mock.calls[0][0]).toMatch(/role:\s*'owner'/);
|
|
83
|
+
expect(mockRun.mock.calls[0][1]).toEqual({ accountId: "acct-2078cb54" });
|
|
84
|
+
// The profile read is keyed on the OWNER userId — NOT the acting operator.
|
|
85
|
+
expect(mockRun.mock.calls[1][1]).toEqual({
|
|
86
|
+
accountId: "acct-2078cb54",
|
|
87
|
+
userId: "owner-3f81e7c2",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it("falls back to the acting user's profile when the account has NO seeded owner (legacy)", async () => {
|
|
91
|
+
queue(null, "America/Toronto");
|
|
92
|
+
const tz = await getUserTimezone("acct-legacy999", "acting-user-777");
|
|
93
|
+
expect(tz).toBe("America/Toronto");
|
|
94
|
+
// Profile read falls back to the acting user id.
|
|
95
|
+
expect(mockRun).toHaveBeenCalledTimes(2);
|
|
96
|
+
expect(mockRun.mock.calls[1][1]).toEqual({
|
|
97
|
+
accountId: "acct-legacy999",
|
|
98
|
+
userId: "acting-user-777",
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
it("operator acting in their own single-identity account resolves the same profile (no regression)", async () => {
|
|
102
|
+
// Owner-resolution returns the acting user itself; behaviour matches the
|
|
103
|
+
// pre-1684 single-identity read.
|
|
104
|
+
queue("acting-user-777", "Europe/Berlin");
|
|
105
|
+
const tz = await getUserTimezone("acct-solo", "acting-user-777");
|
|
106
|
+
expect(tz).toBe("Europe/Berlin");
|
|
107
|
+
expect(mockRun.mock.calls[1][1]).toEqual({
|
|
108
|
+
accountId: "acct-solo",
|
|
109
|
+
userId: "acting-user-777",
|
|
110
|
+
});
|
|
66
111
|
});
|
|
67
|
-
it("
|
|
68
|
-
|
|
69
|
-
await getUserTimezone("acct-
|
|
70
|
-
|
|
71
|
-
const [query, params] = mockRun.mock.calls[0];
|
|
112
|
+
it("profile read predicates BOTH accountId AND userId — anti-regression against a bare-accountId read", async () => {
|
|
113
|
+
queue("owner-3f81e7c2", "Europe/London");
|
|
114
|
+
await getUserTimezone("acct-2078cb54", "operator-436fdf2d");
|
|
115
|
+
const [query] = mockRun.mock.calls[1];
|
|
72
116
|
expect(query).toMatch(/accountId:\s*\$accountId/);
|
|
73
117
|
expect(query).toMatch(/userId:\s*\$userId/);
|
|
74
|
-
expect(params).toEqual({ accountId: "acct-abc12345", userId: "user-xyz98765" });
|
|
75
118
|
});
|
|
76
|
-
it("
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
await expect(getUserTimezone("acct-abc12345", "user-xyz98765")).rejects.toThrow(/set via profile-update with profileFields: \{ timezone: 'IANA_TZ' \}/);
|
|
119
|
+
it("names the resolved OWNER row when the owner profile has timezone=NULL", async () => {
|
|
120
|
+
// slice(0, 8) of "owner-3f81e7c2" = "owner-3f"
|
|
121
|
+
queue("owner-3f81e7c2", null);
|
|
122
|
+
await expect(getUserTimezone("acct-2078cb54", "operator-436fdf2d")).rejects.toThrow(/No timezone on UserProfile for userId owner-3f/);
|
|
123
|
+
queue("owner-3f81e7c2", null);
|
|
124
|
+
await expect(getUserTimezone("acct-2078cb54", "operator-436fdf2d")).rejects.toThrow(/set via profile-update with profileFields: \{ timezone: 'IANA_TZ' \}/);
|
|
83
125
|
});
|
|
84
|
-
it("
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
|
|
126
|
+
it("names the resolved OWNER row when the owner has no UserProfile at all", async () => {
|
|
127
|
+
// Owner AdminUser exists but has no HAS_PROFILE UserProfile (provisioning
|
|
128
|
+
// gap, Task 1150). The error names the owner, and does NOT fall back to
|
|
129
|
+
// the acting user. slice(0, 8) of "owner-3f81e7c2" = "owner-3f"
|
|
130
|
+
queue("owner-3f81e7c2", "no-row");
|
|
131
|
+
await expect(getUserTimezone("acct-2078cb54", "operator-436fdf2d")).rejects.toThrow(/No UserProfile for userId owner-3f/);
|
|
88
132
|
});
|
|
89
|
-
it("
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// returns ONLY the targeted row — the mock asserts this by inspecting
|
|
94
|
-
// the actual query parameters and returning the appropriate result set.
|
|
95
|
-
const multiRowUniverse = {
|
|
96
|
-
"user-target-001": "Europe/Berlin",
|
|
97
|
-
"user-ghost-aaa": null,
|
|
98
|
-
"user-ghost-bbb": null,
|
|
99
|
-
};
|
|
100
|
-
mockRun.mockImplementation(async (_query, params) => {
|
|
101
|
-
// The lib's query filters on userId, so a correctly-implemented call
|
|
102
|
-
// returns at most one row. If a future regression drops the userId
|
|
103
|
-
// predicate, this mock still returns only the matching row — but the
|
|
104
|
-
// anti-regression check above (query string assertion) would have
|
|
105
|
-
// already caught it.
|
|
106
|
-
const tz = multiRowUniverse[params.userId];
|
|
107
|
-
if (tz === undefined)
|
|
108
|
-
return { records: [] };
|
|
109
|
-
return { records: [makeRecord(tz)] };
|
|
110
|
-
});
|
|
111
|
-
// Targeted user: returns the targeted timezone.
|
|
112
|
-
expect(await getUserTimezone("acct-shared", "user-target-001")).toBe("Europe/Berlin");
|
|
113
|
-
// Ghost user with NULL timezone: throws the "No timezone" branch — does
|
|
114
|
-
// NOT silently leak the targeted user's timezone.
|
|
115
|
-
// slice(0, 8) of "user-ghost-aaa" = "user-gho"
|
|
116
|
-
await expect(getUserTimezone("acct-shared", "user-ghost-aaa")).rejects.toThrow(/No timezone on UserProfile for userId user-gho/);
|
|
133
|
+
it("names the acting-user row in the no-owner fallback when that profile is missing", async () => {
|
|
134
|
+
// slice(0, 8) of "acting-user-777" = "acting-u"
|
|
135
|
+
queue(null, "no-row");
|
|
136
|
+
await expect(getUserTimezone("acct-legacy999", "acting-user-777")).rejects.toThrow(/No UserProfile for userId acting-u/);
|
|
117
137
|
});
|
|
118
138
|
});
|
|
119
139
|
//# sourceMappingURL=getUserTimezone.test.js.map
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getUserTimezone.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/getUserTimezone.test.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"getUserTimezone.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/getUserTimezone.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,20 +11,46 @@ export declare class MissingUserIdError extends Error {
|
|
|
11
11
|
constructor(message: string);
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Task 1684 — resolve the account's seeded owner `AdminUser` userId.
|
|
15
|
+
*
|
|
16
|
+
* A sub-account is seeded (Task 1359 `seedAccountGraphRoot`) with exactly one
|
|
17
|
+
* `AdminUser {role:'owner'}`. When a house operator acts inside that
|
|
18
|
+
* sub-account, the operator's boot userId has no node in the sub-account
|
|
19
|
+
* subgraph, so keying a UserProfile read on it reads empty. The owner is the
|
|
20
|
+
* single, unambiguous locale identity for the account. Returns null when the
|
|
21
|
+
* account has no owner `AdminUser` (a legacy, pre-1359 account) — the caller
|
|
22
|
+
* then falls back to the acting user via an explicit, logged path.
|
|
23
|
+
*
|
|
24
|
+
* Mirrors the memory plugin's `resolve-owner-userid.ts` and
|
|
25
|
+
* `@maxy/active-rules`' `resolveOwnerUserId` — the query literal is duplicated
|
|
26
|
+
* per package deliberately (each keeps its own neo4j coupling) rather than
|
|
27
|
+
* introducing a cross-package import.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveOwnerUserId(session: Session, accountId: string): Promise<string | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Fetches the account's IANA timezone from its owner's UserProfile node.
|
|
15
32
|
*
|
|
16
33
|
* UserProfile read doctrine: single-row consumers key reads on
|
|
17
|
-
* (accountId, userId). AccountId-only reads are forbidden
|
|
18
|
-
* enforces (accountId, userId) uniqueness
|
|
19
|
-
*
|
|
20
|
-
*
|
|
34
|
+
* (accountId, userId). AccountId-only reads are forbidden — the constraint
|
|
35
|
+
* enforces (accountId, userId) uniqueness, so a bare-accountId read would
|
|
36
|
+
* target a non-deterministic row and (in multi-row state) point the caller at
|
|
37
|
+
* a remediation that writes to a different row than the read.
|
|
38
|
+
*
|
|
39
|
+
* Task 1684: the read is keyed on the account's seeded OWNER, not the acting
|
|
40
|
+
* session user. A house operator acting inside a client sub-account has no
|
|
41
|
+
* UserProfile scoped to that account; the owner's profile is authoritative
|
|
42
|
+
* (the same owner-resolution 1557/1564 applied to profile writes and standing
|
|
43
|
+
* rules). The acting user is used only as an explicit fallback when the
|
|
44
|
+
* account has no seeded owner (legacy), which also preserves the pre-1684
|
|
45
|
+
* behaviour for single-identity accounts where the acting user IS the owner.
|
|
21
46
|
*
|
|
22
|
-
* Throws MissingUserIdError when userId is empty — caller forgot to
|
|
23
|
-
* the live userId from the MCP session
|
|
24
|
-
*
|
|
25
|
-
* Throws Error when
|
|
26
|
-
* the
|
|
27
|
-
* profile-update.
|
|
47
|
+
* Throws MissingUserIdError when the acting userId is empty — caller forgot to
|
|
48
|
+
* thread the live userId from the MCP session (still required for the fallback
|
|
49
|
+
* path and for attribution).
|
|
50
|
+
* Throws Error when no UserProfile exists for the resolved (accountId, userId)
|
|
51
|
+
* pair — the message names the resolved row (owner, or acting user in the
|
|
52
|
+
* fallback) so profile-update targets the same row that was read.
|
|
53
|
+
* Throws Error when the row exists but timezone is unset — same naming.
|
|
28
54
|
*/
|
|
29
55
|
export declare function getUserTimezone(accountId: string, userId: string): Promise<string>;
|
|
30
56
|
//# sourceMappingURL=neo4j.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j.d.ts","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuBtD,wBAAgB,SAAS,IAAI,MAAM,CAgBlC;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjD;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"neo4j.d.ts","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuBtD,wBAAgB,SAAS,IAAI,MAAM,CAgBlC;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjD;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CxF"}
|
|
@@ -49,20 +49,53 @@ export class MissingUserIdError extends Error {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* Task 1684 — resolve the account's seeded owner `AdminUser` userId.
|
|
53
|
+
*
|
|
54
|
+
* A sub-account is seeded (Task 1359 `seedAccountGraphRoot`) with exactly one
|
|
55
|
+
* `AdminUser {role:'owner'}`. When a house operator acts inside that
|
|
56
|
+
* sub-account, the operator's boot userId has no node in the sub-account
|
|
57
|
+
* subgraph, so keying a UserProfile read on it reads empty. The owner is the
|
|
58
|
+
* single, unambiguous locale identity for the account. Returns null when the
|
|
59
|
+
* account has no owner `AdminUser` (a legacy, pre-1359 account) — the caller
|
|
60
|
+
* then falls back to the acting user via an explicit, logged path.
|
|
61
|
+
*
|
|
62
|
+
* Mirrors the memory plugin's `resolve-owner-userid.ts` and
|
|
63
|
+
* `@maxy/active-rules`' `resolveOwnerUserId` — the query literal is duplicated
|
|
64
|
+
* per package deliberately (each keeps its own neo4j coupling) rather than
|
|
65
|
+
* introducing a cross-package import.
|
|
66
|
+
*/
|
|
67
|
+
export async function resolveOwnerUserId(session, accountId) {
|
|
68
|
+
const res = await session.run(`MATCH (au:AdminUser {accountId: $accountId, role: 'owner'})
|
|
69
|
+
RETURN au.userId AS userId
|
|
70
|
+
ORDER BY au.createdAt ASC
|
|
71
|
+
LIMIT 1`, { accountId });
|
|
72
|
+
const userId = res.records[0]?.get("userId");
|
|
73
|
+
return typeof userId === "string" && userId.length > 0 ? userId : null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fetches the account's IANA timezone from its owner's UserProfile node.
|
|
53
77
|
*
|
|
54
78
|
* UserProfile read doctrine: single-row consumers key reads on
|
|
55
|
-
* (accountId, userId). AccountId-only reads are forbidden
|
|
56
|
-
* enforces (accountId, userId) uniqueness
|
|
57
|
-
*
|
|
58
|
-
*
|
|
79
|
+
* (accountId, userId). AccountId-only reads are forbidden — the constraint
|
|
80
|
+
* enforces (accountId, userId) uniqueness, so a bare-accountId read would
|
|
81
|
+
* target a non-deterministic row and (in multi-row state) point the caller at
|
|
82
|
+
* a remediation that writes to a different row than the read.
|
|
83
|
+
*
|
|
84
|
+
* Task 1684: the read is keyed on the account's seeded OWNER, not the acting
|
|
85
|
+
* session user. A house operator acting inside a client sub-account has no
|
|
86
|
+
* UserProfile scoped to that account; the owner's profile is authoritative
|
|
87
|
+
* (the same owner-resolution 1557/1564 applied to profile writes and standing
|
|
88
|
+
* rules). The acting user is used only as an explicit fallback when the
|
|
89
|
+
* account has no seeded owner (legacy), which also preserves the pre-1684
|
|
90
|
+
* behaviour for single-identity accounts where the acting user IS the owner.
|
|
59
91
|
*
|
|
60
|
-
* Throws MissingUserIdError when userId is empty — caller forgot to
|
|
61
|
-
* the live userId from the MCP session
|
|
62
|
-
*
|
|
63
|
-
* Throws Error when
|
|
64
|
-
* the
|
|
65
|
-
* profile-update.
|
|
92
|
+
* Throws MissingUserIdError when the acting userId is empty — caller forgot to
|
|
93
|
+
* thread the live userId from the MCP session (still required for the fallback
|
|
94
|
+
* path and for attribution).
|
|
95
|
+
* Throws Error when no UserProfile exists for the resolved (accountId, userId)
|
|
96
|
+
* pair — the message names the resolved row (owner, or acting user in the
|
|
97
|
+
* fallback) so profile-update targets the same row that was read.
|
|
98
|
+
* Throws Error when the row exists but timezone is unset — same naming.
|
|
66
99
|
*/
|
|
67
100
|
export async function getUserTimezone(accountId, userId) {
|
|
68
101
|
if (!userId || userId.trim().length === 0) {
|
|
@@ -71,15 +104,24 @@ export async function getUserTimezone(accountId, userId) {
|
|
|
71
104
|
}
|
|
72
105
|
const session = getSession();
|
|
73
106
|
try {
|
|
107
|
+
const ownerUserId = await resolveOwnerUserId(session, accountId);
|
|
108
|
+
const effectiveUserId = ownerUserId ?? userId;
|
|
109
|
+
const source = ownerUserId ? "owner" : "acting-fallback";
|
|
110
|
+
const ownerTag = ownerUserId ? ownerUserId.slice(0, 8) : "none";
|
|
111
|
+
const traceHead = `[scheduling] tz-resolve accountId=${accountId.slice(0, 8)} ` +
|
|
112
|
+
`actingUserId=${userId.slice(0, 8)} resolved=${source} ownerUserId=${ownerTag}`;
|
|
74
113
|
const result = await session.run(`MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
|
|
75
|
-
RETURN up.timezone AS timezone`, { accountId, userId });
|
|
114
|
+
RETURN up.timezone AS timezone`, { accountId, userId: effectiveUserId });
|
|
76
115
|
if (result.records.length === 0) {
|
|
77
|
-
|
|
116
|
+
console.error(`${traceHead} tz=absent`);
|
|
117
|
+
throw new Error(`No UserProfile for userId ${effectiveUserId.slice(0, 8)}… (account ${accountId.slice(0, 8)}…) — cannot format locale times`);
|
|
78
118
|
}
|
|
79
119
|
const timezone = result.records[0].get("timezone");
|
|
80
120
|
if (!timezone || timezone.trim().length === 0) {
|
|
81
|
-
|
|
121
|
+
console.error(`${traceHead} tz=unset`);
|
|
122
|
+
throw new Error(`No timezone on UserProfile for userId ${effectiveUserId.slice(0, 8)}… (account ${accountId.slice(0, 8)}…) — set via profile-update with profileFields: { timezone: 'IANA_TZ' }`);
|
|
82
123
|
}
|
|
124
|
+
console.error(`${traceHead} tz=${timezone}`);
|
|
83
125
|
return timezone;
|
|
84
126
|
}
|
|
85
127
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j.js","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAO,KAA0B,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,SAAS,YAAY;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAElE,MAAM,YAAY,GAAG,OAAO,CAC1B,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EACxE,wBAAwB,CACzB,CAAC;IAEF,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yCAAyC,YAAY,gCAAgC,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,6DAA6D;QAC7D,oEAAoE;QACpE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,6EAA6E,CAC9E,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;QAC/C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAC;QACxD,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB,EAAE,MAAc;IACrE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,KAAK,CACX,gFAAgF,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CACxG,CAAC;QACF,MAAM,IAAI,kBAAkB,CAC1B,2GAA2G,CAC5G,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B;sCACgC,EAChC,EAAE,SAAS,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"neo4j.js","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"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"}
|
package/payload/server/public/assets/{AdminLoginScreens-5R0A40Is.js → AdminLoginScreens-Cl7VBbPS.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./chunk-Ck5AOu5v.js";import{E as t,S as n,_ as r,x as i,y as a}from"./useSubAccountSwitcher-B_zj_dY-.js";import{E as o}from"./useVoiceRecorder-BDtJhfh_.js";import{v as s,y as c}from"./AdminShell-B2HeKInP.js";import{t as l}from"./Checkbox-yjCERU3B.js";var u=`admin-landing-redirected`,d=`/graph`;function f(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var p=e(t(),1);function m(e=`admin`){let[t,r]=(0,p.useState)(`loading`),[i,a]=(0,p.useState)(``),[s,l]=(0,p.useState)(``),[m,h]=(0,p.useState)(``),[g,_]=(0,p.useState)(!1),[v,y]=(0,p.useState)(!1),[b,x]=(0,p.useState)(!1),[S,C]=(0,p.useState)(!1),[w,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(null),[A,j]=(0,p.useState)(void 0),[M,N]=(0,p.useState)(null),[P,F]=(0,p.useState)(void 0),[I,L]=(0,p.useState)(null),[R,z]=(0,p.useState)(null),[B,V]=(0,p.useState)(void 0),H=(0,p.useRef)(void 0),[U,W]=(0,p.useState)(!1),[G,K]=(0,p.useState)(!0);(0,p.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&W(!0)}).catch(()=>{})},[]);let q=(0,p.useRef)(null),J=(0,p.useRef)(null);(0,p.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),z(n.sessionId??null),j(n.businessName),N(n.role??null),F(n.userName===void 0?null:n.userName),L(n.avatar??null);let i=o(n.thinkingView);return H.current=i,V(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}K(!!a.claude_authenticated);let o=!1;try{o=new URLSearchParams(window.location.search).get(`connect`)===`claude`}catch{}if(o){try{let e=new URL(window.location.href);e.searchParams.delete(`connect`),window.history.replaceState(null,``,e.pathname+e.search+e.hash)}catch{}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,p.useEffect)(()=>{t===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[t]),(0,p.useEffect)(()=>{if(typeof window>`u`)return;let n=!1;try{n=sessionStorage.getItem(u)===`1`}catch{}if(f({appState:t,alreadyRedirected:n,variant:e})){try{sessionStorage.setItem(u,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${d}`),window.location.replace(d)}},[t,e]);let Y=(0,p.useRef)(null);(0,p.useEffect)(()=>{if(t!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();K(t.auth_status!==`dead`&&t.auth_status!==`missing`)}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let t=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(t===`remote-auth-required`){n(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${t}`),Y.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[t,E]),(0,p.useEffect)(()=>{t===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&(K(!0),r(`enter-pin`))}).catch(()=>{})},[t]);async function X(e,t){y(!0);try{let i=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!i.ok){let e=await i.json().catch(()=>({}));if(i.status===401&&e?.code===`remote-auth-required`){n(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let s=await i.json();D(s.session_key),z(s.sessionId??null),j(s.businessName),N(s.role??null),F(s.userName===void 0?null:s.userName),L(s.avatar??null);let c=o(s.thinkingView);H.current=c,V(c);try{sessionStorage.setItem(`maxy-admin-session-key`,s.session_key)}catch{}a(``),r(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}}let Z=(0,p.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let t=s.trim();if(!t){h(`Please enter your name.`);return}if(i.length<4){h(`PIN must be at least 4 characters.`);return}let o=i;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:o,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===401&&t?.code===`remote-auth-required`){n(`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 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 X(o);return}a(``),r(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[i,v,s]),Q=(0,p.useCallback)(async e=>{e.preventDefault(),h(``),await X(i)},[i]),ee=(0,p.useCallback)(async()=>{T(!0);try{if(!await c())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;K(!1),D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}return r(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,p.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}a(``),h(``),r(`enter-pin`)},[]);return(0,p.useEffect)(()=>{Y.current=$},[$]),{appState:t,setAppState:r,pin:i,setPin:a,operatorName:s,setOperatorName:l,pinError:m,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:R,setSessionId:z,expandAll:B,setExpandAll:V,expandAllDefaultRef:H,remoteAuthEnabled:U,claudeConnected:G,pinInputRef:q,setPinFormRef:J,handleSetPin:Z,handleLogin:Q,handleDisconnect:ee,handleLogout:$}}var h=i();function g({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,p.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,h.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,h.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 _(e){let{pin:t,setPin:n,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:_}=e;return(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:a,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,r.productName]}),(0,h.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,r.productName,` who you are, then choose a PIN.`]}),(0,h.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>_(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,h.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:i}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!t||!m.trim(),loading:c,"aria-label":`Set PIN`,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})})}function v(e){let{pin:t,setPin:n,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,h.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,h.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:a,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`h1`,{className:`connect-title`,children:r.productName}),(0,h.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:i,autoFocus:!0}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!t,loading:c,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(`div`,{className:`pin-options`,children:(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})]})}function y(e){let{authPolling:t,setAuthPolling:n,authLoading:i,setAuthLoading:o,pinError:c,setPinError:l,setAppState:u}=e,[d,f]=(0,p.useState)(!1),[m,g]=(0,p.useState)(!1),[_,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(``),[x,S]=(0,p.useState)(``);async function C(){S(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`paste-code`,code:y})})).json();e.accepted?(S(`Code submitted. Finishing sign-in…`),b(``)):S(e.error||`Could not submit code.`)}catch(e){console.error(`[admin] paste-code error:`,e),S(`Could not submit code.`)}}async function w(){g(!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.`)}g(!1)}async function T(){o(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){n(!0),f(!0),o(!1),v(e.transport||``);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.`),n(!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 E(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),n(!1),l(``)}return t||d?(0,h.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,h.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,h.jsx)(`button`,{onClick:E,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,h.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,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,h.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,h.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,h.jsx)(`div`,{style:{marginTop:`12px`},children:(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})}),t&&_===`native`&&(0,h.jsxs)(`div`,{style:{marginTop:`14px`,maxWidth:`420px`,margin:`14px auto 0`,display:`flex`,flexDirection:`column`,gap:`8px`},children:[(0,h.jsx)(`label`,{style:{fontSize:`12px`,color:`#999`,lineHeight:1.4},children:`If the browser didn't finish sign-in, paste the code or full redirect URL from Claude's page:`}),(0,h.jsxs)(`div`,{style:{display:`flex`,gap:`8px`},children:[(0,h.jsx)(`input`,{type:`text`,value:y,onChange:e=>b(e.target.value),placeholder:`Paste code or redirect URL`,style:{flex:1,padding:`8px 10px`,fontSize:`13px`,border:`1px solid #ccc`,borderRadius:`6px`}}),(0,h.jsx)(s,{variant:`primary`,onClick:C,disabled:!y.trim(),children:`Submit`})]}),x&&(0,h.jsx)(`p`,{style:{fontSize:`12px`,color:`#999`,margin:0},children:x})]})]}),(0,h.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,h.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:c})]}):(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsxs)(`div`,{className:`connect-logos`,children:[(0,h.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,h.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,h.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,h.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,h.jsx)(`img`,{src:a,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`span`,{className:`connect-logo-label`,children:r.productName})]})]}),(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,r.productName]}),(0,h.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,h.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,h.jsx)(`button`,{onClick:w,disabled:m,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:m?`Launching…`:`Open browser first`}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:c})]})})}function b({auth:e}){return e.appState===`loading`?(0,h.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,h.jsx)(_,{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,h.jsx)(y,{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,h.jsx)(v,{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`}}):null}export{m as n,b as t};
|
|
1
|
+
import{o as e}from"./chunk-Ck5AOu5v.js";import{E as t,S as n,_ as r,x as i,y as a}from"./useSubAccountSwitcher-6UoTftbJ.js";import{E as o}from"./useVoiceRecorder-BkxW-GL1.js";import{v as s,y as c}from"./AdminShell-lBaQG3aT.js";import{t as l}from"./Checkbox-P8PNtn8n.js";var u=`admin-landing-redirected`,d=`/graph`;function f(e){return e.variant===`operator`?!1:e.appState===`chat`&&!e.alreadyRedirected}var p=e(t(),1);function m(e=`admin`){let[t,r]=(0,p.useState)(`loading`),[i,a]=(0,p.useState)(``),[s,l]=(0,p.useState)(``),[m,h]=(0,p.useState)(``),[g,_]=(0,p.useState)(!1),[v,y]=(0,p.useState)(!1),[b,x]=(0,p.useState)(!1),[S,C]=(0,p.useState)(!1),[w,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(null),[A,j]=(0,p.useState)(void 0),[M,N]=(0,p.useState)(null),[P,F]=(0,p.useState)(void 0),[I,L]=(0,p.useState)(null),[R,z]=(0,p.useState)(null),[B,V]=(0,p.useState)(void 0),H=(0,p.useRef)(void 0),[U,W]=(0,p.useState)(!1),[G,K]=(0,p.useState)(!0);(0,p.useEffect)(()=>{typeof window>`u`||fetch(`/api/remote-auth/status`).then(e=>e.ok?e.json():null).then(e=>{e?.configured&&W(!0)}).catch(()=>{})},[]);let q=(0,p.useRef)(null),J=(0,p.useRef)(null);(0,p.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),z(n.sessionId??null),j(n.businessName),N(n.role??null),F(n.userName===void 0?null:n.userName),L(n.avatar??null);let i=o(n.thinkingView);return H.current=i,V(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}K(!!a.claude_authenticated);let o=!1;try{o=new URLSearchParams(window.location.search).get(`connect`)===`claude`}catch{}if(o){try{let e=new URL(window.location.href);e.searchParams.delete(`connect`),window.history.replaceState(null,``,e.pathname+e.search+e.hash)}catch{}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,p.useEffect)(()=>{t===`chat`&&fetch(`/api/admin/claude-info`).then(e=>{if(e.ok)return e.json()}).then(e=>{e&&k(e)}).catch(()=>{})},[t]),(0,p.useEffect)(()=>{if(typeof window>`u`)return;let n=!1;try{n=sessionStorage.getItem(u)===`1`}catch{}if(f({appState:t,alreadyRedirected:n,variant:e})){try{sessionStorage.setItem(u,`1`)}catch{}console.info(`[admin-ui] landing-redirect target=${d}`),window.location.replace(d)}},[t,e]);let Y=(0,p.useRef)(null);(0,p.useEffect)(()=>{if(t!==`chat`)return;let e=setInterval(async()=>{try{let e=await fetch(`/api/health`);if(e.ok){let t=await e.json();K(t.auth_status!==`dead`&&t.auth_status!==`missing`)}}catch{}if(E)try{let e=await fetch(`/api/admin/session?session_key=${encodeURIComponent(E)}`);if(e.status!==401)return;let t=(await e.clone().json().catch(()=>null))?.code??`unknown-401`;if(t===`remote-auth-required`){n(`heartbeat`,`/api/admin/session`);return}console.warn(`[admin-auth] outcome=heartbeat-detected-expiry code=${t}`),Y.current?.()}catch{}},300*1e3);return()=>clearInterval(e)},[t,E]),(0,p.useEffect)(()=>{t===`connect-claude`&&fetch(`/api/health`).then(e=>e.ok?e.json():null).then(e=>{e?.claude_authenticated&&(K(!0),r(`enter-pin`))}).catch(()=>{})},[t]);async function X(e,t){y(!0);try{let i=await fetch(`/api/admin/session`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:e,...t?{accountId:t}:{}})});if(!i.ok){let e=await i.json().catch(()=>({}));if(i.status===401&&e?.code===`remote-auth-required`){n(`enter-pin`,`/api/admin/session`);return}h(e.error||`Invalid PIN`);return}let s=await i.json();D(s.session_key),z(s.sessionId??null),j(s.businessName),N(s.role??null),F(s.userName===void 0?null:s.userName),L(s.avatar??null);let c=o(s.thinkingView);H.current=c,V(c);try{sessionStorage.setItem(`maxy-admin-session-key`,s.session_key)}catch{}a(``),r(`chat`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}}let Z=(0,p.useCallback)(async e=>{if(e.preventDefault(),v)return;h(``);let t=s.trim();if(!t){h(`Please enter your name.`);return}if(i.length<4){h(`PIN must be at least 4 characters.`);return}let o=i;y(!0);try{let e=await fetch(`/api/onboarding/set-pin`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({pin:o,name:t})});if(!e.ok){let t=await e.json().catch(()=>({}));if(e.status===401&&t?.code===`remote-auth-required`){n(`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 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 X(o);return}a(``),r(`connect-claude`)}catch(e){console.error(`[admin] connection error:`,e),h(`Could not connect.`)}finally{y(!1)}},[i,v,s]),Q=(0,p.useCallback)(async e=>{e.preventDefault(),h(``),await X(i)},[i]),ee=(0,p.useCallback)(async()=>{T(!0);try{if(!await c())return console.warn(`[admin-ui] claude-disconnect not verified — credentials may persist; staying put`),!1;K(!1),D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}return r(`connect-claude`),!0}finally{T(!1)}},[]),$=(0,p.useCallback)(()=>{D(null),N(null),F(void 0),L(null);try{sessionStorage.removeItem(`maxy-admin-session-key`),sessionStorage.removeItem(u)}catch{}a(``),h(``),r(`enter-pin`)},[]);return(0,p.useEffect)(()=>{Y.current=$},[$]),{appState:t,setAppState:r,pin:i,setPin:a,operatorName:s,setOperatorName:l,pinError:m,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:R,setSessionId:z,expandAll:B,setExpandAll:V,expandAllDefaultRef:H,remoteAuthEnabled:U,claudeConnected:G,pinInputRef:q,setPinFormRef:J,handleSetPin:Z,handleLogin:Q,handleDisconnect:ee,handleLogout:$}}var h=i();function g({inputRef:e,value:t,onChange:n,onComplete:r,showPin:i,autoFocus:a}){let o=(0,p.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,h.jsx)(`div`,{className:`pin-field`,children:Array.from({length:6}).map((n,r)=>(0,h.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 _(e){let{pin:t,setPin:n,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,setPinFormRef:f,onSubmit:p,operatorName:m,setOperatorName:_}=e;return(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:a,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Welcome to `,r.productName]}),(0,h.jsxs)(`p`,{className:`connect-subtitle`,children:[`Tell `,r.productName,` who you are, then choose a PIN.`]}),(0,h.jsxs)(`form`,{ref:f,onSubmit:p,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(`input`,{type:`text`,className:`connect-name-input`,placeholder:`Your full name`,value:m,onChange:e=>_(e.target.value),autoComplete:`name`,autoFocus:!0,required:!0,"aria-label":`Your full name`}),(0,h.jsx)(`div`,{style:{width:38,flexShrink:0},"aria-hidden":`true`})]}),(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:i}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!t||!m.trim(),loading:c,"aria-label":`Set PIN`,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})})}function v(e){let{pin:t,setPin:n,showPin:i,setShowPin:o,pinLoading:c,pinError:u,pinInputRef:d,onSubmit:f,remoteAuthEnabled:p,onSignOutRemote:m}=e;return(0,h.jsxs)(`div`,{className:`connect-page`,children:[p&&m&&(0,h.jsx)(`button`,{type:`button`,className:`connect-signout`,onClick:m,children:`Sign out`}),(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsx)(`img`,{src:a,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`h1`,{className:`connect-title`,children:r.productName}),(0,h.jsxs)(`form`,{onSubmit:f,className:`connect-pin-form`,children:[(0,h.jsxs)(`div`,{className:`pin-input-row`,children:[(0,h.jsx)(g,{inputRef:d,value:t,onChange:n,onComplete:()=>{},showPin:i,autoFocus:!0}),(0,h.jsx)(s,{variant:`send`,type:`submit`,disabled:!t,loading:c,children:(0,h.jsxs)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,h.jsx)(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`}),(0,h.jsx)(`polyline`,{points:`12 5 19 12 12 19`})]})})]}),(0,h.jsx)(`div`,{className:`pin-options`,children:(0,h.jsx)(l,{checked:i,onChange:()=>o(e=>!e),label:`Show PIN`})})]}),u&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:u})]})]})}function y(e){let{authPolling:t,setAuthPolling:n,authLoading:i,setAuthLoading:o,pinError:c,setPinError:l,setAppState:u}=e,[d,f]=(0,p.useState)(!1),[m,g]=(0,p.useState)(!1),[_,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(``),[x,S]=(0,p.useState)(``);async function C(){S(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`paste-code`,code:y})})).json();e.accepted?(S(`Code submitted. Finishing sign-in…`),b(``)):S(e.error||`Could not submit code.`)}catch(e){console.error(`[admin] paste-code error:`,e),S(`Could not submit code.`)}}async function w(){g(!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.`)}g(!1)}async function T(){o(!0),l(``);try{let e=await(await fetch(`/api/onboarding/claude-auth`,{method:`POST`})).json();if(e.started){n(!0),f(!0),o(!1),v(e.transport||``);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.`),n(!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 E(){await fetch(`/api/onboarding/claude-auth`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({action:`stop`})}),n(!1),l(``)}return t||d?(0,h.jsxs)(`div`,{style:{display:`flex`,flexDirection:`column`,height:`100dvh`,overflow:`auto`},children:[(0,h.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,h.jsx)(`button`,{onClick:E,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,h.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,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`chat-logo`}),(0,h.jsx)(`h1`,{className:`chat-tagline`,children:`Connect Claude`}),(0,h.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,h.jsx)(`div`,{style:{marginTop:`12px`},children:(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`})}),t&&_===`native`&&(0,h.jsxs)(`div`,{style:{marginTop:`14px`,maxWidth:`420px`,margin:`14px auto 0`,display:`flex`,flexDirection:`column`,gap:`8px`},children:[(0,h.jsx)(`label`,{style:{fontSize:`12px`,color:`#999`,lineHeight:1.4},children:`If the browser didn't finish sign-in, paste the code or full redirect URL from Claude's page:`}),(0,h.jsxs)(`div`,{style:{display:`flex`,gap:`8px`},children:[(0,h.jsx)(`input`,{type:`text`,value:y,onChange:e=>b(e.target.value),placeholder:`Paste code or redirect URL`,style:{flex:1,padding:`8px 10px`,fontSize:`13px`,border:`1px solid #ccc`,borderRadius:`6px`}}),(0,h.jsx)(s,{variant:`primary`,onClick:C,disabled:!y.trim(),children:`Submit`})]}),x&&(0,h.jsx)(`p`,{style:{fontSize:`12px`,color:`#999`,margin:0},children:x})]})]}),(0,h.jsx)(`div`,{style:{flex:1,display:`flex`,flexDirection:`column`,minHeight:0,gap:`10px`,padding:`0 0 16px`},children:(0,h.jsx)(`iframe`,{src:`/vnc-viewer.html`,style:{flex:1,width:`100%`,minHeight:0,border:`none`,background:`#111`,display:`block`},title:`Claude Sign-in`})}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,style:{textAlign:`center`,padding:`0 20px 16px`},children:c})]}):(0,h.jsx)(`div`,{className:`connect-page`,children:(0,h.jsxs)(`div`,{className:`connect-content`,children:[(0,h.jsxs)(`div`,{className:`connect-logos`,children:[(0,h.jsx)(`div`,{className:`connect-logo-wrap`,children:(0,h.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`connect-logo`})}),(0,h.jsx)(`svg`,{className:`connect-arrow`,viewBox:`0 0 48 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,h.jsx)(`path`,{d:`M0 12h44m0 0l-8-8m8 8l-8 8`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`})}),(0,h.jsxs)(`div`,{className:`connect-logo-wrap`,children:[(0,h.jsx)(`img`,{src:a,alt:r.productName,className:`connect-logo connect-logo--maxy`}),!r.logoContainsName&&(0,h.jsx)(`span`,{className:`connect-logo-label`,children:r.productName})]})]}),(0,h.jsxs)(`h1`,{className:`connect-title`,children:[`Connect Claude to power `,r.productName]}),(0,h.jsx)(`p`,{className:`connect-subtitle`,children:`Sign in with your Anthropic account to get started.`}),(0,h.jsx)(s,{variant:`primary`,onClick:T,disabled:i,children:i?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`span`,{className:`spin`,style:{display:`inline-block`},children:`✱`}),` Connecting…`]}):`Sign in to Claude`}),(0,h.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,h.jsx)(`button`,{onClick:w,disabled:m,style:{marginTop:`12px`,background:`none`,border:`none`,color:`var(--color-primary, #666)`,fontSize:`13px`,cursor:`pointer`,textDecoration:`underline`,textUnderlineOffset:`3px`},children:m?`Launching…`:`Open browser first`}),c&&(0,h.jsx)(`p`,{className:`admin-pin-error`,children:c})]})})}function b({auth:e}){return e.appState===`loading`?(0,h.jsx)(`div`,{className:`connect-page`}):e.appState===`set-pin`?(0,h.jsx)(_,{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,h.jsx)(y,{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,h.jsx)(v,{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`}}):null}export{m as n,b as t};
|