@rubytech/create-maxy-code 0.1.284 → 0.1.285
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/dist/__tests__/installer-settings-permissions.test.js +86 -8
- package/dist/index.js +1 -1
- package/dist/permissions-seed.js +72 -11
- package/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +5 -1
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
- package/payload/platform/plugins/docs/references/platform.md +2 -0
- package/payload/server/public/assets/AdminShell-DwPVr9K1.js +1 -0
- package/payload/server/public/assets/{Checkbox-syHoU9nY.js → Checkbox-BdDmGlvK.js} +1 -1
- package/payload/server/public/assets/{admin-DyuZ4NT5.js → admin-BYtGQJ0d.js} +1 -1
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DsionCio.js → architectureDiagram-Q4EWVU46-CBx_GoUY.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-NOG5yVZk.js → blockDiagram-DXYQGD6D-DvWR4fpv.js} +1 -1
- package/payload/server/public/assets/{browser-XrVAUQdG.js → browser-2PXsTraZ.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-DwFV1k9I.js → c4Diagram-AHTNJAMY-BbSOxeeU.js} +1 -1
- package/payload/server/public/assets/channel-C4NqYS8u.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-iJ4ISPCi.js → chunk-336JU56O-DJzmXTk5.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-sWe0Iexz.js → chunk-426QAEUC-BSwzskM5.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-kKNYi2_i.js → chunk-4TB4RGXK-C-fH8BTZ.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-Car3r0he.js → chunk-5FUZZQ4R-DW-MaiGX.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-DR9EfLvj.js → chunk-5PVQY5BW-DlKbwC1X.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DLUWVRZV.js → chunk-EDXVE4YY-DnkdxwAt.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-B4bNJo6C.js → chunk-ENJZ2VHE-BTfezWsj.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-Qzl8Ki0l.js → chunk-ICPOFSXX-BGg8uTtu.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BenBO6O_.js → chunk-OYMX7WX6-CYJInav8.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-DewM8R0D.js → chunk-U2HBQHQK-B6kD6ZHU.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-C22Q6Ea4.js → chunk-X2U36JSP-7tvWkjym.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-DfUUNTnA.js → chunk-YZCP3GAM-D6NjOb5l.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-CUT6zma2.js → chunk-ZZ45TVLE-Dqz69eJy.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CI3_kz04.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-UP3RcKH9.js +1 -0
- package/payload/server/public/assets/clone-CvAPweMB.js +1 -0
- package/payload/server/public/assets/{dagre-KV5264BT-D4EGb60h.js → dagre-KV5264BT-DswETLwm.js} +1 -1
- package/payload/server/public/assets/{dagre-CYXRIdv0.js → dagre-di-KHpsq.js} +1 -1
- package/payload/server/public/assets/{data-tcu3MXmK.js → data-B6vpe0Zj.js} +1 -1
- package/payload/server/public/assets/{diagram-5BDNPKRD-BzieCEfT.js → diagram-5BDNPKRD-DWmq_Zkl.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DDbO71XD.js → diagram-G4DWMVQ6-BVi6yjLL.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DqQ_UXDA.js → diagram-MMDJMWI5-D6XKyUAT.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CFyxA1aZ.js → diagram-TYMM5635-sU1d7P6W.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-BMaEBbOK.js → erDiagram-SMLLAGMA-CY6qPZvu.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-De5ChZQP.js → flowDiagram-DWJPFMVM-CE9hd869.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CXEMPVaK.js → ganttDiagram-T4ZO3ILL-Cbb885gt.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-OZedr_6y.js → gitGraphDiagram-UUTBAWPF-BNzO3jBu.js} +1 -1
- package/payload/server/public/assets/{graph-I4GyC2MW.js → graph-DfegJdnT.js} +1 -1
- package/payload/server/public/assets/{graph-labels-DSDTSBfM.js → graph-labels-BG4aqzs6.js} +1 -1
- package/payload/server/public/assets/{graphlib-CN4nhD2U.js → graphlib-CIzKPoW_.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-CSqg4nRL.js → infoDiagram-42DDH7IO-DWGtofAT.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-ChlzrZyx.js → ishikawaDiagram-UXIWVN3A-UG1rus6G.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DsrK55ZO.js → journeyDiagram-VCZTEJTY-CYhHYXmj.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DYF-_Rym.js → kanban-definition-6JOO6SKY-CWHj9DHX.js} +1 -1
- package/payload/server/public/assets/{line-3zmwI8XK.js → line-vMN9CmXx.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-BrcX8-cl.js → mermaid-parser.core-5X7WWCZe.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-CBdz3b1N.js → mermaid.core-B0zUZsT6.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BqCqlYZb.js → mindmap-definition-QFDTVHPH-BGr68ynR.js} +1 -1
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-D5LiILuM.js → pieDiagram-DEJITSTG-Bo3zU_w2.js} +1 -1
- package/payload/server/public/assets/{public-udi_Z7la.js → public-DCZ92LR9.js} +3 -3
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-C3iuI5l_.js → quadrantDiagram-34T5L4WZ-o-dLMRj-.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-CTPOw5qQ.js → requirementDiagram-MS252O5E-D_O_ZclX.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-D_5aVlYp.js → sankeyDiagram-XADWPNL6-Doz7uWIn.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-D7h5vocM.js → sequenceDiagram-FGHM5R23-r1VW_jgc.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DyZjuwPj.js → stateDiagram-FHFEXIEX-B7O06aua.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Bo1g66pA.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BqpVYJz_.js → timeline-definition-GMOUNBTQ-WS2AL0yL.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-BnRcJeg2.css → useSelectionMode-lFraIQzT.css} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Czflml0e.js → vennDiagram-DHZGUBPP-_-FLcVAh.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Cqz3pSTK.js → wardleyDiagram-NUSXRM2D-nZ3oO0ih.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CS5dEuUO.js → xychartDiagram-5P7HB3ND-CqTdRM37.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +5 -5
- package/payload/server/public/public.html +4 -4
- package/payload/server/server.js +236 -155
- package/payload/server/public/assets/AdminShell-8Y7-maNi.js +0 -1
- package/payload/server/public/assets/channel-D8Go_zJk.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-BLb1mzrT.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Ctj5qYTS.js +0 -1
- package/payload/server/public/assets/clone-DGZ2vydA.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BiW9pGXy.js +0 -1
- /package/payload/server/public/assets/{useSelectionMode-BPzPjKP_.js → useSelectionMode-mzxTOyBv.js} +0 -0
|
@@ -7,30 +7,51 @@
|
|
|
7
7
|
// the rc-spawn bind — Task 664). Existing top-level keys must be preserved;
|
|
8
8
|
// an already-correct file must be a true no-op (mtime unchanged); a
|
|
9
9
|
// "*"-bearing file must be rewritten on upgrade.
|
|
10
|
+
//
|
|
11
|
+
// Task 739 — the same writer also seeds an `autoMode` block so that when a
|
|
12
|
+
// session runs in `auto` (not bypass) mode, the harness auto-classifier
|
|
13
|
+
// treats this install's own Cloudflare token mint/persist as sanctioned
|
|
14
|
+
// (it otherwise soft-denies it as a credential escalation). The block is
|
|
15
|
+
// belt-and-suspenders: bypassPermissions skips the classifier entirely, so
|
|
16
|
+
// autoMode only takes effect on a session toggled to auto at runtime. A
|
|
17
|
+
// pre-739 file (permissions correct, no autoMode) must upgrade in place;
|
|
18
|
+
// a file with both blocks correct must be a true no-op.
|
|
10
19
|
import test from "node:test";
|
|
11
20
|
import assert from "node:assert/strict";
|
|
12
21
|
import { mkdtempSync, readFileSync, writeFileSync, statSync, rmSync } from "node:fs";
|
|
13
22
|
import { tmpdir } from "node:os";
|
|
14
23
|
import { join } from "node:path";
|
|
15
|
-
import { seedBypassPermissionsSettings, assertBypassPermissionsSeed, BYPASS_PERMISSIONS_BLOCK } from "../permissions-seed.js";
|
|
24
|
+
import { seedBypassPermissionsSettings, assertBypassPermissionsSeed, BYPASS_PERMISSIONS_BLOCK, AUTO_MODE_BLOCK } from "../permissions-seed.js";
|
|
16
25
|
function freshDir() {
|
|
17
26
|
return mkdtempSync(join(tmpdir(), "t583-"));
|
|
18
27
|
}
|
|
19
|
-
|
|
28
|
+
// A plain (non-frozen) deep copy of the seeded autoMode block, for building
|
|
29
|
+
// already-correct fixtures and deep-equal assertions.
|
|
30
|
+
function autoModeFixture() {
|
|
31
|
+
return { environment: [...AUTO_MODE_BLOCK.environment], allow: [...AUTO_MODE_BLOCK.allow] };
|
|
32
|
+
}
|
|
33
|
+
test("empty target: creates settings.json with both the permissions and autoMode blocks", () => {
|
|
20
34
|
const dir = freshDir();
|
|
21
35
|
try {
|
|
22
36
|
const result = seedBypassPermissionsSettings(dir);
|
|
23
37
|
assert.equal(result.action, "seeded");
|
|
38
|
+
assert.equal(result.autoMode, "seeded");
|
|
24
39
|
assert.equal(result.path, join(dir, "settings.json"));
|
|
25
40
|
const parsed = JSON.parse(readFileSync(result.path, "utf-8"));
|
|
26
|
-
assert.deepEqual(parsed, {
|
|
41
|
+
assert.deepEqual(parsed, {
|
|
42
|
+
permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] },
|
|
43
|
+
autoMode: autoModeFixture(),
|
|
44
|
+
});
|
|
45
|
+
// "$defaults" must lead both arrays so built-in guards stay inherited.
|
|
46
|
+
assert.equal(parsed.autoMode.environment[0], "$defaults");
|
|
47
|
+
assert.equal(parsed.autoMode.allow[0], "$defaults");
|
|
27
48
|
assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
|
|
28
49
|
}
|
|
29
50
|
finally {
|
|
30
51
|
rmSync(dir, { recursive: true, force: true });
|
|
31
52
|
}
|
|
32
53
|
});
|
|
33
|
-
test("existing keys preserved verbatim; permissions added", () => {
|
|
54
|
+
test("existing keys preserved verbatim; permissions and autoMode added", () => {
|
|
34
55
|
const dir = freshDir();
|
|
35
56
|
try {
|
|
36
57
|
const settingsPath = join(dir, "settings.json");
|
|
@@ -51,17 +72,49 @@ test("existing keys preserved verbatim; permissions added", () => {
|
|
|
51
72
|
assert.equal(parsed.agentPushNotifEnabled, false);
|
|
52
73
|
assert.equal(parsed.model, "opus");
|
|
53
74
|
assert.deepEqual(parsed.permissions, { defaultMode: "bypassPermissions", allow: [], deny: [] });
|
|
75
|
+
assert.deepEqual(parsed.autoMode, autoModeFixture());
|
|
54
76
|
assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
|
|
55
77
|
}
|
|
56
78
|
finally {
|
|
57
79
|
rmSync(dir, { recursive: true, force: true });
|
|
58
80
|
}
|
|
59
81
|
});
|
|
60
|
-
test("
|
|
82
|
+
test("pre-739 upgrade: permissions correct but no autoMode adds autoMode, preserves other keys", () => {
|
|
61
83
|
const dir = freshDir();
|
|
62
84
|
try {
|
|
63
85
|
const settingsPath = join(dir, "settings.json");
|
|
64
|
-
|
|
86
|
+
// A file written by the Task 664 seed: permissions correct, no autoMode.
|
|
87
|
+
const preTask739 = {
|
|
88
|
+
model: "opus",
|
|
89
|
+
enabledPlugins: { "memory@claude-plugins-official": true },
|
|
90
|
+
permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] },
|
|
91
|
+
};
|
|
92
|
+
writeFileSync(settingsPath, JSON.stringify(preTask739, null, 2));
|
|
93
|
+
const result = seedBypassPermissionsSettings(dir);
|
|
94
|
+
assert.equal(result.action, "seeded");
|
|
95
|
+
assert.equal(result.autoMode, "seeded", "this run added the autoMode block");
|
|
96
|
+
const parsed = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
97
|
+
// permissions and unrelated keys untouched.
|
|
98
|
+
assert.deepEqual(parsed.permissions, { defaultMode: "bypassPermissions", allow: [], deny: [] });
|
|
99
|
+
assert.equal(parsed.model, "opus");
|
|
100
|
+
assert.deepEqual(parsed.enabledPlugins, preTask739.enabledPlugins);
|
|
101
|
+
// autoMode now present and correct.
|
|
102
|
+
assert.deepEqual(parsed.autoMode, autoModeFixture());
|
|
103
|
+
assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
rmSync(dir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
test("idempotent: both blocks already correct triggers no write", () => {
|
|
110
|
+
const dir = freshDir();
|
|
111
|
+
try {
|
|
112
|
+
const settingsPath = join(dir, "settings.json");
|
|
113
|
+
writeFileSync(settingsPath, JSON.stringify({
|
|
114
|
+
model: "opus",
|
|
115
|
+
permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] },
|
|
116
|
+
autoMode: autoModeFixture(),
|
|
117
|
+
}, null, 2));
|
|
65
118
|
const before = statSync(settingsPath).mtimeMs;
|
|
66
119
|
// Small busy-wait so a same-millisecond write would be detectable on
|
|
67
120
|
// filesystems with millisecond mtime granularity.
|
|
@@ -69,6 +122,7 @@ test("idempotent: already-correct block triggers no write", () => {
|
|
|
69
122
|
while (Date.now() < target) { /* spin */ }
|
|
70
123
|
const result = seedBypassPermissionsSettings(dir);
|
|
71
124
|
assert.equal(result.action, "noop");
|
|
125
|
+
assert.equal(result.autoMode, "present");
|
|
72
126
|
const after = statSync(settingsPath).mtimeMs;
|
|
73
127
|
assert.equal(after, before, "mtime must not change on noop");
|
|
74
128
|
}
|
|
@@ -76,13 +130,16 @@ test("idempotent: already-correct block triggers no write", () => {
|
|
|
76
130
|
rmSync(dir, { recursive: true, force: true });
|
|
77
131
|
}
|
|
78
132
|
});
|
|
79
|
-
test("malformed JSON: overwrites with
|
|
133
|
+
test("malformed JSON: overwrites with both seeded blocks", () => {
|
|
80
134
|
const dir = freshDir();
|
|
81
135
|
try {
|
|
82
136
|
const settingsPath = join(dir, "settings.json");
|
|
83
137
|
writeFileSync(settingsPath, "{ not json");
|
|
84
138
|
const result = seedBypassPermissionsSettings(dir);
|
|
85
139
|
assert.equal(result.action, "seeded");
|
|
140
|
+
assert.equal(result.autoMode, "seeded");
|
|
141
|
+
const parsed = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
142
|
+
assert.deepEqual(parsed.autoMode, autoModeFixture());
|
|
86
143
|
assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
|
|
87
144
|
}
|
|
88
145
|
finally {
|
|
@@ -108,12 +165,32 @@ test("assert: partial block (no wildcard) reports missing", () => {
|
|
|
108
165
|
rmSync(dir, { recursive: true, force: true });
|
|
109
166
|
}
|
|
110
167
|
});
|
|
168
|
+
test("assert: permissions correct but autoMode absent reports missing", () => {
|
|
169
|
+
const dir = freshDir();
|
|
170
|
+
try {
|
|
171
|
+
writeFileSync(join(dir, "settings.json"), JSON.stringify({ permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] } }));
|
|
172
|
+
assert.equal(assertBypassPermissionsSeed(dir).status, "missing");
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
rmSync(dir, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
111
178
|
test("BYPASS_PERMISSIONS_BLOCK frozen literal matches expected shape", () => {
|
|
112
179
|
assert.equal(BYPASS_PERMISSIONS_BLOCK.defaultMode, "bypassPermissions");
|
|
113
180
|
assert.deepEqual([...BYPASS_PERMISSIONS_BLOCK.allow], []);
|
|
114
181
|
assert.deepEqual([...BYPASS_PERMISSIONS_BLOCK.deny], []);
|
|
115
182
|
assert.ok(Object.isFrozen(BYPASS_PERMISSIONS_BLOCK));
|
|
116
183
|
});
|
|
184
|
+
test("AUTO_MODE_BLOCK frozen literal leads each array with $defaults and names Cloudflare", () => {
|
|
185
|
+
assert.ok(Object.isFrozen(AUTO_MODE_BLOCK));
|
|
186
|
+
assert.equal(AUTO_MODE_BLOCK.environment[0], "$defaults");
|
|
187
|
+
assert.equal(AUTO_MODE_BLOCK.allow[0], "$defaults");
|
|
188
|
+
// The provisioning and persistence clauses are the sanctioned-flow surface.
|
|
189
|
+
assert.ok(AUTO_MODE_BLOCK.allow.some((e) => /Cloudflare token provisioning/.test(e)));
|
|
190
|
+
assert.ok(AUTO_MODE_BLOCK.allow.some((e) => /Cloudflare token persistence/.test(e)));
|
|
191
|
+
// No machine-specific absolute path is baked in.
|
|
192
|
+
assert.ok(!AUTO_MODE_BLOCK.environment.some((e) => e.includes("/home/")));
|
|
193
|
+
});
|
|
117
194
|
test("assert: wildcard allow reports missing (forces rewrite on upgrade)", () => {
|
|
118
195
|
const dir = freshDir();
|
|
119
196
|
try {
|
|
@@ -124,7 +201,7 @@ test("assert: wildcard allow reports missing (forces rewrite on upgrade)", () =>
|
|
|
124
201
|
rmSync(dir, { recursive: true, force: true });
|
|
125
202
|
}
|
|
126
203
|
});
|
|
127
|
-
test("seeding over a wildcard-bearing file rewrites allow to [] preserving other keys", () => {
|
|
204
|
+
test("seeding over a wildcard-bearing file rewrites allow to [] and adds autoMode preserving other keys", () => {
|
|
128
205
|
const dir = freshDir();
|
|
129
206
|
try {
|
|
130
207
|
const p = join(dir, "settings.json");
|
|
@@ -134,6 +211,7 @@ test("seeding over a wildcard-bearing file rewrites allow to [] preserving other
|
|
|
134
211
|
const parsed = JSON.parse(readFileSync(p, "utf-8"));
|
|
135
212
|
assert.equal(parsed.model, "opus");
|
|
136
213
|
assert.deepEqual(parsed.permissions, { defaultMode: "bypassPermissions", allow: [], deny: [] });
|
|
214
|
+
assert.deepEqual(parsed.autoMode, autoModeFixture());
|
|
137
215
|
assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
|
|
138
216
|
}
|
|
139
217
|
finally {
|
package/dist/index.js
CHANGED
|
@@ -995,7 +995,7 @@ function installClaudeCode() {
|
|
|
995
995
|
// through to the Anthropic remote auto-classifier (which routes Agent
|
|
996
996
|
// dispatch to `ask` and surfaces a prompt no unattended operator clicks).
|
|
997
997
|
const permissionsSeed = seedBypassPermissionsSettings(CLAUDE_CONFIG_DIR);
|
|
998
|
-
logFile(`[install-permissions] action=${permissionsSeed.action} path=${permissionsSeed.path}`);
|
|
998
|
+
logFile(`[install-permissions] action=${permissionsSeed.action} autoMode=${permissionsSeed.autoMode} path=${permissionsSeed.path}`);
|
|
999
999
|
const marketplaceList = spawnSync("claude", ["plugin", "marketplace", "list"], { stdio: "pipe", encoding: "utf-8", env: claudePluginEnv() });
|
|
1000
1000
|
if (marketplaceList.stderr)
|
|
1001
1001
|
process.stderr.write(marketplaceList.stderr);
|
package/dist/permissions-seed.js
CHANGED
|
@@ -13,9 +13,24 @@
|
|
|
13
13
|
// stdout bind banner never prints and every rc-spawn times out (Task 664).
|
|
14
14
|
// The allow array is therefore empty and MUST stay "*"-free.
|
|
15
15
|
//
|
|
16
|
+
// Task 739 — this module also seeds an `autoMode` block. When a session is
|
|
17
|
+
// toggled to `auto` permission mode at runtime (a shift-tab in claude.ai/code,
|
|
18
|
+
// leaving bypass), the harness auto-classifier wakes up and soft-denies the
|
|
19
|
+
// doctrine-sanctioned Cloudflare token mint/persist as a credential
|
|
20
|
+
// escalation, because the install's own Cloudflare infrastructure is not in
|
|
21
|
+
// the classifier's trusted environment out of the box. The autoMode block
|
|
22
|
+
// declares that infrastructure trusted and the scoped token mint/persist
|
|
23
|
+
// sanctioned, with `"$defaults"` retained first in both arrays so the
|
|
24
|
+
// built-in exfiltration / force-push / prod-deploy guards stay intact. It is
|
|
25
|
+
// belt-and-suspenders: bypassPermissions skips the classifier entirely, so
|
|
26
|
+
// this block only takes effect when a session has left bypass for auto. The
|
|
27
|
+
// classifier reads `autoMode` only from user-scope settings (this file),
|
|
28
|
+
// never from a shared project settings.json, so this is the only seed surface.
|
|
29
|
+
//
|
|
16
30
|
// This module is the single writer for the brand-scoped settings.json
|
|
17
|
-
// permissions
|
|
18
|
-
// platform/scripts/setup-account.sh in lockstep with the
|
|
31
|
+
// permissions and autoMode blocks. Account-scoped settings.json is written by
|
|
32
|
+
// platform/scripts/setup-account.sh in lockstep with the permissions block
|
|
33
|
+
// (the classifier ignores autoMode there, so it carries no autoMode block).
|
|
19
34
|
import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from "node:fs";
|
|
20
35
|
import { join, dirname } from "node:path";
|
|
21
36
|
export const BYPASS_PERMISSIONS_BLOCK = Object.freeze({
|
|
@@ -23,7 +38,29 @@ export const BYPASS_PERMISSIONS_BLOCK = Object.freeze({
|
|
|
23
38
|
allow: [],
|
|
24
39
|
deny: [],
|
|
25
40
|
});
|
|
26
|
-
|
|
41
|
+
// Task 739 — the auto-mode trust block. Each array leads with "$defaults" so
|
|
42
|
+
// the harness's built-in environment scope and allow rules are inherited, not
|
|
43
|
+
// replaced; the appended entries add this install's own Cloudflare surface on
|
|
44
|
+
// top. The prose is the v3 block validated on the laptop with
|
|
45
|
+
// `claude auto-mode config`/`critique` (2.1.169), with the one laptop-specific
|
|
46
|
+
// absolute path removed in favour of a relationship phrase so the block ships
|
|
47
|
+
// identically to every brand and machine. `<id>` stays a literal path segment:
|
|
48
|
+
// this brand-scoped file is shared by every account in the install, so the
|
|
49
|
+
// placeholder correctly denotes "the requesting account's own dir" while the
|
|
50
|
+
// "never cross-account" clause does the enforcement.
|
|
51
|
+
export const AUTO_MODE_BLOCK = Object.freeze({
|
|
52
|
+
environment: Object.freeze([
|
|
53
|
+
"$defaults",
|
|
54
|
+
"Organization: a Maxy Code admin agent that operates this single brand's OWN infrastructure. This install's own working tree and the per-account data dirs under data/accounts/<id>/ are its own scope.",
|
|
55
|
+
"api.cloudflare.com is the intended provider endpoint for Cloudflare API tokens held in this install. Sending those tokens to api.cloudflare.com is a Standard Credentials action. api.cloudflare.com is NOT a trusted destination for repo content, secrets, or bulk file uploads — the Data Exfiltration hard block applies normally to any such content (private-source files, secrets, Workers scripts or D1 dumps that embed secrets, whole-tree uploads).",
|
|
56
|
+
]),
|
|
57
|
+
allow: Object.freeze([
|
|
58
|
+
"$defaults",
|
|
59
|
+
"Cloudflare token provisioning: minting a Cloudflare API token for THIS account's own zone/account, scoped to one of {Zone DNS edit, Pages, D1, Workers Scripts, Access apps/policies}, is sanctioned provisioning for the install's own infrastructure. Covered: POST to /accounts/{id}/tokens or /zones/{zone}/tokens where the request body's policies / permission-groups are visible in the transcript and limited to the named scopes, plus listing or revoking this account's own tokens. If the scope cannot be verified from the transcript, or is broader than the named scopes (e.g. account-wide Edit or Global API Key), treat as a Permission Grant soft block. Token creation where the agent inferred the scope from tool output rather than explicit instruction is NOT covered.",
|
|
60
|
+
"Cloudflare token persistence: writing this account's own narrowly-scoped Cloudflare API tokens (as minted above) to that same account's secrets directory data/accounts/<id>/secrets/ is intended local persistence, not Credential Leakage — scoped to this account's own Cloudflare service tokens, never to any .claude/ config path, and never cross-account.",
|
|
61
|
+
]),
|
|
62
|
+
});
|
|
63
|
+
function isPermissionsCorrect(permissions) {
|
|
27
64
|
if (!permissions || typeof permissions !== "object")
|
|
28
65
|
return false;
|
|
29
66
|
const p = permissions;
|
|
@@ -37,6 +74,24 @@ function isAlreadyCorrect(permissions) {
|
|
|
37
74
|
return false;
|
|
38
75
|
return true;
|
|
39
76
|
}
|
|
77
|
+
function stringArraysEqual(a, b) {
|
|
78
|
+
if (!Array.isArray(a) || a.length !== b.length)
|
|
79
|
+
return false;
|
|
80
|
+
return a.every((v, i) => v === b[i]);
|
|
81
|
+
}
|
|
82
|
+
function isAutoModeCorrect(autoMode) {
|
|
83
|
+
if (!autoMode || typeof autoMode !== "object")
|
|
84
|
+
return false;
|
|
85
|
+
const a = autoMode;
|
|
86
|
+
return (stringArraysEqual(a.environment, AUTO_MODE_BLOCK.environment) &&
|
|
87
|
+
stringArraysEqual(a.allow, AUTO_MODE_BLOCK.allow));
|
|
88
|
+
}
|
|
89
|
+
function freshAutoModeBlock() {
|
|
90
|
+
return {
|
|
91
|
+
environment: [...AUTO_MODE_BLOCK.environment],
|
|
92
|
+
allow: [...AUTO_MODE_BLOCK.allow],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
40
95
|
export function seedBypassPermissionsSettings(configDir) {
|
|
41
96
|
const settingsPath = join(configDir, "settings.json");
|
|
42
97
|
let existing = {};
|
|
@@ -51,20 +106,26 @@ export function seedBypassPermissionsSettings(configDir) {
|
|
|
51
106
|
}
|
|
52
107
|
catch {
|
|
53
108
|
// Malformed JSON: treat as empty and overwrite with a well-formed
|
|
54
|
-
// file containing only the permissions
|
|
55
|
-
// file carried are unrecoverable from invalid JSON anyway.
|
|
109
|
+
// file containing only the permissions and autoMode blocks. Any keys
|
|
110
|
+
// the prior file carried are unrecoverable from invalid JSON anyway.
|
|
56
111
|
}
|
|
57
112
|
}
|
|
58
113
|
}
|
|
59
|
-
|
|
60
|
-
|
|
114
|
+
const permissionsOk = isPermissionsCorrect(existing.permissions);
|
|
115
|
+
const autoModeOk = isAutoModeCorrect(existing.autoMode);
|
|
116
|
+
if (permissionsOk && autoModeOk) {
|
|
117
|
+
return { action: "noop", autoMode: "present", path: settingsPath };
|
|
61
118
|
}
|
|
62
119
|
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
63
|
-
const merged = {
|
|
120
|
+
const merged = {
|
|
121
|
+
...existing,
|
|
122
|
+
permissions: { ...BYPASS_PERMISSIONS_BLOCK, allow: [...BYPASS_PERMISSIONS_BLOCK.allow], deny: [] },
|
|
123
|
+
autoMode: freshAutoModeBlock(),
|
|
124
|
+
};
|
|
64
125
|
const tmpPath = `${settingsPath}.task583.tmp`;
|
|
65
126
|
writeFileSync(tmpPath, JSON.stringify(merged, null, 2) + "\n");
|
|
66
127
|
renameSync(tmpPath, settingsPath);
|
|
67
|
-
return { action: "seeded", path: settingsPath };
|
|
128
|
+
return { action: "seeded", autoMode: autoModeOk ? "present" : "seeded", path: settingsPath };
|
|
68
129
|
}
|
|
69
130
|
export function assertBypassPermissionsSeed(configDir) {
|
|
70
131
|
const settingsPath = join(configDir, "settings.json");
|
|
@@ -79,8 +140,8 @@ export function assertBypassPermissionsSeed(configDir) {
|
|
|
79
140
|
}
|
|
80
141
|
if (!parsed || typeof parsed !== "object")
|
|
81
142
|
return { status: "missing", path: settingsPath };
|
|
82
|
-
const
|
|
83
|
-
return
|
|
143
|
+
const record = parsed;
|
|
144
|
+
return isPermissionsCorrect(record.permissions) && isAutoModeCorrect(record.autoMode)
|
|
84
145
|
? { status: "ok", path: settingsPath }
|
|
85
146
|
: { status: "missing", path: settingsPath };
|
|
86
147
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:cc48ff74e2c0782fd1d36ad2f280495df8124e1bb5e3fff9a3d777db7c48b3de
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -310,6 +310,8 @@ If you suspect background processes are piling up, run `grep '\[reaper\]' ~/.{br
|
|
|
310
310
|
|
|
311
311
|
Every install seeds `permissions.allow:[]` plus `defaultMode:"bypassPermissions"` into both the brand-scoped settings file (`~/.{brand}/.claude/settings.json`) and every account-scoped one (`<install>/data/accounts/<id>/.claude/settings.json`). `bypassPermissions` alone stops Claude Code from sending tool calls to its remote auto-classifier (it skips all permission checks), which would otherwise surface a permission prompt in the chat that an unattended session never answers. The `allow` array stays empty and **must never contain `"*"`**: Claude Code ≥ 2.1.167 rejects `"*"` as an allow token and renders a blocking Settings Warning that, under `--remote-control`, stops every `/rc-spawn` from binding (Task 664). What each subagent is allowed to use is still controlled by the `tools:` line in its agent file, not by a top-level allowlist. To verify after an install: `cat ~/.{brand}/.claude/settings.json | jq '.permissions'` (expect `allow: []`). To repair an install seeded before Task 664: `bash <install>/platform/scripts/backfill-bypass-permissions.sh ~/.{brand}`.
|
|
312
312
|
|
|
313
|
+
**`autoMode` trust block (Task 739).** The same brand-scoped writer ([`permissions-seed.ts`](../../../packages/create-maxy-code/src/permissions-seed.ts), `AUTO_MODE_BLOCK`) also seeds the harness `autoMode` block. `bypassPermissions` skips the auto-mode classifier entirely, so the block is **belt-and-suspenders**: it only matters when a session is toggled into `auto` mode, where the classifier otherwise soft-denies the doctrine-sanctioned Cloudflare scoped-token mint ("permission/credential escalation") because the install's own Cloudflare infra is not in the classifier's default trusted environment. The block keeps `"$defaults"` first in `environment` and `allow` (so the built-in Data-Exfiltration / force-push / prod-deploy guards stay intact) and adds prose declaring `api.cloudflare.com` the install's own provider endpoint plus an allow for minting/persisting narrow per-scope tokens. It is seeded **only** into the brand/user settings (`~/.{brand}/.claude/settings.json`) — the classifier never reads `autoMode` from the shared project (account-scoped) settings, so `setup-account.sh` does not carry it. An auto-mode agent cannot seed this itself (the default `hard_deny` "Auto-Mode Bypass" blocks any agent editing `.claude/settings*.json`); installer seed is the only path. To verify after an install: `CLAUDE_CONFIG_DIR=~/.{brand}/.claude claude auto-mode config | jq '.environment, .allow'` (expect the Cloudflare entries, `$defaults` expanded) and `jq '.autoMode' ~/.{brand}/.claude/settings.json`.
|
|
314
|
+
|
|
313
315
|
---
|
|
314
316
|
# Plugins Guide
|
|
315
317
|
Source: https://docs.getmaxy.com/plugins-guide.md
|
|
@@ -2441,6 +2443,8 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
2441
2443
|
| Mode trigger | Per-`(accountId, userId)` `SpawnPreference` for `permissionMode` and `model`; persists across reload, tab, device. | `session-defaults.ts` |
|
|
2442
2444
|
| Nav rows (Chat / People / Agents / Projects / Tasks / Artefacts) | People, Agents, Tasks open the artefact-pane Graph filtered to the matching label. Chat selects the active conversation. Artefacts swaps the list to editable documents. | `graph-search.ts`, `graph-subgraph.ts`, `sidebar-artefacts.ts` |
|
|
2443
2445
|
| Sessions list (Active / Archived / All) | Live row store driven by SSE; manual reconcile button on the segmented control re-fetches the full id set. | `/claude-sessions/events`, `/claude-sessions` |
|
|
2446
|
+
| Channel reader nav rows (WhatsApp / Telegram) | One conditional nav row **per interactive channel that has ≥1 admin session** (Task 738). Each row carries its brand glyph (WhatsApp `#25D366`, Telegram `#229ED9`); a channel with no admin session shows no row, and no install ever shows an always-empty "No conversations" row. The channel list loads **once on sidebar mount** (the per-channel counts must be known before the nav renders), so a failed load shows one muted, non-clickable line instead of channel rows. Clicking a channel row lists that channel's admin conversations; each conversation row shows the **operator display name** (`Person givenName familyName`, resolved server-side from `senderId`; falls back to the raw `senderId` when unbound/unresolved), with the agent session title as the hover tooltip only. | `/whatsapp-reader/conversations` |
|
|
2447
|
+
| Channel transcript (`<Transcript>`) | Reads one session's JSONL over SSE (`/whatsapp-reader/stream`) and renders both sides plus tool calls — the turns claude.ai/code hides because channel inbound is stamped `isMeta`. The thread scrolls inside its grid cell (header/sidebar/footer fixed). The stream sends a 20 s heartbeat so an idle Cloudflare tunnel does not idle-drop, tags each frame with a byte-offset `id:`, and resumes from the client's `Last-Event-ID` on reconnect (no duplicate backlog); the client clears the "Stream disconnected" banner on reopen and latches it only on a terminal CLOSED state. | `/whatsapp-reader/stream` |
|
|
2444
2448
|
| Conversations row hover actions | Inline rename, archive, delete, JSONL view / download per row. The historical `.conversations-modal` CSS block exists in `globals.css` but is no longer mounted from any TSX — Sidebar.tsx now owns every per-row affordance directly. | `claude-sessions.ts` |
|
|
2445
2449
|
| Artefacts list | Lists every `:FileArtifact` under this account's tree (`relativePath STARTS WITH 'accounts/'`, all file types, excluding the `uploads/<id>/` subtree — Task 684) plus this account's IDENTITY / SOUL / KNOWLEDGE / specialist templates. Click downloads the row's backing file (`downloadPath` → `GET /api/admin/files/download`) so the operator opens it in their local app; rows whose file is outside `DATA_ROOT` (bundled-fallback templates) show a "can't be downloaded" pill. The in-app artefact pane is dead pending removal (Task 518). | `sidebar-artefacts.ts`, `files.ts` |
|
|
2446
2450
|
| System-stats widget | CPU / RAM widget at the foot of the sidebar. | `system-stats.ts` (see above) |
|
|
@@ -216,6 +216,8 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
216
216
|
| Mode trigger | Per-`(accountId, userId)` `SpawnPreference` for `permissionMode` and `model`; persists across reload, tab, device. | `session-defaults.ts` |
|
|
217
217
|
| Nav rows (Chat / People / Agents / Projects / Tasks / Artefacts) | People, Agents, Tasks open the artefact-pane Graph filtered to the matching label. Chat selects the active conversation. Artefacts swaps the list to editable documents. | `graph-search.ts`, `graph-subgraph.ts`, `sidebar-artefacts.ts` |
|
|
218
218
|
| Sessions list (Active / Archived / All) | Live row store driven by SSE; manual reconcile button on the segmented control re-fetches the full id set. | `/claude-sessions/events`, `/claude-sessions` |
|
|
219
|
+
| Channel reader nav rows (WhatsApp / Telegram) | One conditional nav row **per interactive channel that has ≥1 admin session** (Task 738). Each row carries its brand glyph (WhatsApp `#25D366`, Telegram `#229ED9`); a channel with no admin session shows no row, and no install ever shows an always-empty "No conversations" row. The channel list loads **once on sidebar mount** (the per-channel counts must be known before the nav renders), so a failed load shows one muted, non-clickable line instead of channel rows. Clicking a channel row lists that channel's admin conversations; each conversation row shows the **operator display name** (`Person givenName familyName`, resolved server-side from `senderId`; falls back to the raw `senderId` when unbound/unresolved), with the agent session title as the hover tooltip only. | `/whatsapp-reader/conversations` |
|
|
220
|
+
| Channel transcript (`<Transcript>`) | Reads one session's JSONL over SSE (`/whatsapp-reader/stream`) and renders both sides plus tool calls — the turns claude.ai/code hides because channel inbound is stamped `isMeta`. The thread scrolls inside its grid cell (header/sidebar/footer fixed). The stream sends a 20 s heartbeat so an idle Cloudflare tunnel does not idle-drop, tags each frame with a byte-offset `id:`, and resumes from the client's `Last-Event-ID` on reconnect (no duplicate backlog); the client clears the "Stream disconnected" banner on reopen and latches it only on a terminal CLOSED state. | `/whatsapp-reader/stream` |
|
|
219
221
|
| Conversations row hover actions | Inline rename, archive, delete, JSONL view / download per row. The historical `.conversations-modal` CSS block exists in `globals.css` but is no longer mounted from any TSX — Sidebar.tsx now owns every per-row affordance directly. | `claude-sessions.ts` |
|
|
220
222
|
| Artefacts list | Lists every `:FileArtifact` under this account's tree (`relativePath STARTS WITH 'accounts/'`, all file types, excluding the `uploads/<id>/` subtree — Task 684) plus this account's IDENTITY / SOUL / KNOWLEDGE / specialist templates. Click downloads the row's backing file (`downloadPath` → `GET /api/admin/files/download`) so the operator opens it in their local app; rows whose file is outside `DATA_ROOT` (bundled-fallback templates) show a "can't be downloaded" pill. The in-app artefact pane is dead pending removal (Task 518). | `sidebar-artefacts.ts`, `files.ts` |
|
|
221
223
|
| System-stats widget | CPU / RAM widget at the foot of the sidebar. | `system-stats.ts` (see above) |
|
|
@@ -195,3 +195,5 @@ If you suspect background processes are piling up, run `grep '\[reaper\]' ~/.{br
|
|
|
195
195
|
## Tool Permissions
|
|
196
196
|
|
|
197
197
|
Every install seeds `permissions.allow:[]` plus `defaultMode:"bypassPermissions"` into both the brand-scoped settings file (`~/.{brand}/.claude/settings.json`) and every account-scoped one (`<install>/data/accounts/<id>/.claude/settings.json`). `bypassPermissions` alone stops Claude Code from sending tool calls to its remote auto-classifier (it skips all permission checks), which would otherwise surface a permission prompt in the chat that an unattended session never answers. The `allow` array stays empty and **must never contain `"*"`**: Claude Code ≥ 2.1.167 rejects `"*"` as an allow token and renders a blocking Settings Warning that, under `--remote-control`, stops every `/rc-spawn` from binding (Task 664). What each subagent is allowed to use is still controlled by the `tools:` line in its agent file, not by a top-level allowlist. To verify after an install: `cat ~/.{brand}/.claude/settings.json | jq '.permissions'` (expect `allow: []`). To repair an install seeded before Task 664: `bash <install>/platform/scripts/backfill-bypass-permissions.sh ~/.{brand}`.
|
|
198
|
+
|
|
199
|
+
**`autoMode` trust block (Task 739).** The same brand-scoped writer ([`permissions-seed.ts`](../../../packages/create-maxy-code/src/permissions-seed.ts), `AUTO_MODE_BLOCK`) also seeds the harness `autoMode` block. `bypassPermissions` skips the auto-mode classifier entirely, so the block is **belt-and-suspenders**: it only matters when a session is toggled into `auto` mode, where the classifier otherwise soft-denies the doctrine-sanctioned Cloudflare scoped-token mint ("permission/credential escalation") because the install's own Cloudflare infra is not in the classifier's default trusted environment. The block keeps `"$defaults"` first in `environment` and `allow` (so the built-in Data-Exfiltration / force-push / prod-deploy guards stay intact) and adds prose declaring `api.cloudflare.com` the install's own provider endpoint plus an allow for minting/persisting narrow per-scope tokens. It is seeded **only** into the brand/user settings (`~/.{brand}/.claude/settings.json`) — the classifier never reads `autoMode` from the shared project (account-scoped) settings, so `setup-account.sh` does not carry it. An auto-mode agent cannot seed this itself (the default `hard_deny` "Auto-Mode Bypass" blocks any agent editing `.claude/settings*.json`); installer seed is the only path. To verify after an install: `CLAUDE_CONFIG_DIR=~/.{brand}/.claude claude auto-mode config | jq '.environment, .allow'` (expect the Cloudflare entries, `$defaults` expanded) and `jq '.autoMode' ~/.{brand}/.claude/settings.json`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as e}from"./chunk-CnGqDkHZ.js";import{d as t,f as n,l as r,m as i,o as a,r as o,t as s,u as c}from"./useSelectionMode-mzxTOyBv.js";var l=n(`archive-restore`,[[`rect`,{width:`20`,height:`5`,x:`2`,y:`3`,rx:`1`,key:`1wp1u1`}],[`path`,{d:`M4 8v11a2 2 0 0 0 2 2h2`,key:`tvwodi`}],[`path`,{d:`M20 8v11a2 2 0 0 1-2 2h-2`,key:`1gkqxj`}],[`path`,{d:`m9 15 3-3 3 3`,key:`1pd0qc`}],[`path`,{d:`M12 12v9`,key:`192myk`}]]),u=n(`archive`,[[`rect`,{width:`20`,height:`5`,x:`2`,y:`3`,rx:`1`,key:`1wp1u1`}],[`path`,{d:`M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8`,key:`1s80jp`}],[`path`,{d:`M10 12h4`,key:`a56b0p`}]]),d=n(`bot`,[[`path`,{d:`M12 8V4H8`,key:`hb8ula`}],[`rect`,{width:`16`,height:`12`,x:`4`,y:`8`,rx:`2`,key:`enze0r`}],[`path`,{d:`M2 14h2`,key:`vft8re`}],[`path`,{d:`M20 14h2`,key:`4cs60a`}],[`path`,{d:`M15 13v2`,key:`1xurst`}],[`path`,{d:`M9 13v2`,key:`rq6x2g`}]]),f=n(`box`,[[`path`,{d:`M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z`,key:`hh9hay`}],[`path`,{d:`m3.3 7 8.7 5 8.7-5`,key:`g66t2b`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}]]),p=n(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),m=n(`database`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 5V19A9 3 0 0 0 21 19V5`,key:`1wlel7`}],[`path`,{d:`M3 12A9 3 0 0 0 21 12`,key:`mv7ke4`}]]),h=n(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]),g=n(`external-link`,[[`path`,{d:`M15 3h6v6`,key:`1q9fwt`}],[`path`,{d:`M10 14 21 3`,key:`gplh6r`}],[`path`,{d:`M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6`,key:`a6xqqp`}]]),_=n(`globe`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20`,key:`13o1zl`}],[`path`,{d:`M2 12h20`,key:`9i4pu4`}]]),v=n(`history`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}],[`path`,{d:`M12 7v5l4 2`,key:`1fdv2h`}]]),y=n(`info`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 16v-4`,key:`1dtifu`}],[`path`,{d:`M12 8h.01`,key:`e9boi3`}]]),b=n(`list-todo`,[[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`m3 17 2 2 4-4`,key:`1jhpwq`}],[`rect`,{x:`3`,y:`4`,width:`6`,height:`6`,rx:`1`,key:`cif1o7`}]]),x=n(`loader-circle`,[[`path`,{d:`M21 12a9 9 0 1 1-6.219-8.56`,key:`13zald`}]]),S=n(`log-out`,[[`path`,{d:`m16 17 5-5-5-5`,key:`1bji2h`}],[`path`,{d:`M21 12H9`,key:`dn1m92`}],[`path`,{d:`M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4`,key:`1uf3rs`}]]),ee=n(`menu`,[[`path`,{d:`M4 5h16`,key:`1tepv9`}],[`path`,{d:`M4 12h16`,key:`1lakjw`}],[`path`,{d:`M4 19h16`,key:`1djgab`}]]),C=n(`panel-right-open`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M15 3v18`,key:`14nvp0`}],[`path`,{d:`m10 15-3-3 3-3`,key:`1pgupc`}]]),w=n(`panel-right`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M15 3v18`,key:`14nvp0`}]]),te=n(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]),T=n(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]),ne=n(`share-2`,[[`circle`,{cx:`18`,cy:`5`,r:`3`,key:`gq8acd`}],[`circle`,{cx:`6`,cy:`12`,r:`3`,key:`w7nqdw`}],[`circle`,{cx:`18`,cy:`19`,r:`3`,key:`1xt0gg`}],[`line`,{x1:`8.59`,x2:`15.42`,y1:`13.51`,y2:`17.49`,key:`47mynk`}],[`line`,{x1:`15.41`,x2:`8.59`,y1:`6.51`,y2:`10.49`,key:`1n3mei`}]]),re=n(`square`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}]]),ie=n(`users`,[[`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`,key:`1yyitq`}],[`path`,{d:`M16 3.128a4 4 0 0 1 0 7.744`,key:`16gr8j`}],[`path`,{d:`M22 21v-2a4 4 0 0 0-3-3.87`,key:`kshegd`}],[`circle`,{cx:`9`,cy:`7`,r:`4`,key:`nufk8`}]]),E=n(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]),D=e(i(),1),O=r(),k=`maxy-shell-side-px`;function A(){if(typeof window>`u`)return 0;let e=document.querySelector(`.platform > .artefact`)?.getBoundingClientRect();return e&&e.width>0?Math.round(e.width):0}function j(e){if(typeof window>`u`)return e;let t=window.innerWidth,n=Math.max(248,t-480-A());return Math.min(Math.max(e,248),n)}function M(){if(typeof window>`u`)return 264;try{let e=window.localStorage.getItem(k);if(!e)return 264;let t=parseInt(e,10);if(Number.isFinite(t)&&t>=248)return j(t)}catch{}return 264}function ae({targetSelector:e=`.platform`}){let t=(0,D.useRef)(null),[n,r]=(0,D.useState)(()=>M()),i=(0,D.useRef)(n);(0,D.useEffect)(()=>{let t=document.querySelector(e);!t||!(t instanceof HTMLElement)||(t.style.setProperty(`--side-px`,`${n}px`),i.current=n)},[n,e]);let a=(0,D.useCallback)(()=>{r(e=>{let t=j(e);return t===e?e:t})},[]);(0,D.useEffect)(()=>{a(),window.addEventListener(`resize`,a);let t=document.querySelector(e),n=null;return t instanceof HTMLElement&&(n=new MutationObserver(a),n.observe(t,{attributes:!0,attributeFilter:[`data-artefact`,`class`]})),()=>{window.removeEventListener(`resize`,a),n?.disconnect()}},[a,e]);let o=e=>{e.preventDefault();let n=t.current;if(!n)return;n.setPointerCapture(e.pointerId),n.classList.add(`dragging`);let a=!1,o=e=>{a=!0,r(j(Math.round(e.clientX)))},s=()=>{if(n.releasePointerCapture(e.pointerId),n.classList.remove(`dragging`),window.removeEventListener(`pointermove`,o),window.removeEventListener(`pointerup`,s),!a)return;let t=i.current;try{window.localStorage.setItem(k,String(t))}catch{}console.info(`[admin-ui] sidebar-resize px=${t}`)};window.addEventListener(`pointermove`,o),window.addEventListener(`pointerup`,s)},s=()=>{let e=j(264);r(e);try{window.localStorage.removeItem(k)}catch{}console.info(`[admin-ui] sidebar-resize px=${e}`)};return(0,O.jsx)(`div`,{ref:t,className:`side-resize-handle`,role:`separator`,"aria-orientation":`vertical`,"aria-label":`Resize sidebar`,onPointerDown:o,onDoubleClick:s})}var N=`maxy-admin-session`,oe=typeof BroadcastChannel<`u`;function P(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.type===`rotation`&&typeof t.newKey==`string`&&t.newKey.length>0&&(t.surface===`chat`||t.surface===`graph`||t.surface===`data`||t.surface===`sessions`)&&typeof t.ts==`number`}function se(e){if(!oe)return()=>{};let t=new BroadcastChannel(N),n=t=>{if(!P(t.data)){console.warn(`[admin-session-broadcast] outcome=received-malformed payload=${JSON.stringify(t.data)}`);return}e(t.data)};return t.addEventListener(`message`,n),()=>{t.removeEventListener(`message`,n),t.close()}}var F=`maxy-admin-session-key`;function I(e,t){return`${e}${e.includes(`?`)?`&`:`?`}session_key=${encodeURIComponent(t)}`}async function L(e){let t;try{t=await e.clone().json()}catch{return`parse-failed`}if(typeof t!=`object`||!t)return`unknown-401`;let n=t.code;return n===`session-missing`||n===`session-not-registered`||n===`session-expired-age`||n===`grant-expired`?n:`unknown-401`}function ce(){try{return sessionStorage.getItem(F)}catch{return null}}function R(e){let{initialCacheKey:t,surface:n}=e,[r,i]=(0,D.useState)(t),a=(0,D.useRef)(t);(0,D.useEffect)(()=>{a.current=r},[r]),(0,D.useEffect)(()=>{t!==a.current&&(i(t),a.current=t,s(e=>e+1))},[t]);let[o,s]=(0,D.useState)(0);(0,D.useEffect)(()=>se(e=>{let t=a.current.slice(0,8),r=e.newKey.slice(0,8);e.newKey!==a.current&&(console.log(`[admin-session-broadcast] outcome=received oldKey=${t} newKey=${r} surface=${n} senderSurface=${e.surface}`),i(e.newKey),s(e=>e+1))}),[n]);let c=(0,D.useCallback)(()=>{try{sessionStorage.removeItem(F)}catch{}window.location.href=`/`},[]);return{adminFetch:(0,D.useCallback)(async(e,t)=>{let r=a.current,o=I(e,r),l=await fetch(o,t);if(l.status!==401)return l;let u=await L(l);if(u===`session-missing`||u!==`session-not-registered`)return console.warn(`[useAdminFetch] outcome=session-expired-redirect reason=${u} surface=${n} path=${e}`),c(),l;let d=ce();if(!d||d===r)return console.warn(`[useAdminFetch] outcome=session-expired-redirect reason=session-not-registered-no-fresh-key surface=${n} path=${e}`),c(),l;i(d),a.current=d,s(e=>e+1),console.log(`[useAdminFetch] outcome=retry-after-rotation oldKey=${r.slice(0,8)} newKey=${d.slice(0,8)} surface=${n} path=${e}`);let f=I(e,d),p=await fetch(f,t);return p.status===401&&(console.warn(`[useAdminFetch] outcome=session-expired-redirect reason=second-401-after-retry surface=${n} path=${e}`),c()),p},[c,n]),cacheKey:r,sessionRefetchNonce:o}}var z=(0,D.createContext)(null);function le({cacheKey:e,onSessionExpired:t,surface:n,children:r}){let i=(0,D.useCallback)(async(r,i)=>{if(!e)return fetch(r,i);let a=I(r,e),o=await fetch(a,i);if(o.status!==401)return o;let s=await L(o),c=e.slice(-8);if(s===`session-not-registered`){let a=ce();if(a&&a!==e){let e=I(r,a),o=await fetch(e,i);return o.status===401?(console.warn(`[admin-auth] outcome=session-expired-redirect code=second-401-after-retry path=${r} cacheKey=${c} surface=${n}`),t({code:`second-401-after-retry`,path:r}),o):o}}return console.warn(`[admin-auth] outcome=session-expired-redirect code=${s} path=${r} cacheKey=${c} surface=${n}`),t({code:s,path:r}),o},[e,t,n]);return(0,O.jsx)(z.Provider,{value:i,children:r})}function B(){return(0,D.useContext)(z)}function ue(e){let t=B();return(0,D.useMemo)(()=>t||(async(t,n)=>fetch(e?I(t,e):t,n)),[t,e])}function V(e){let{businessName:t,onNavigate:n,onToggleSidebar:r,sidebarOpen:i,onLogout:s}=e,[l,u]=(0,D.useState)(!1),d=(0,D.useRef)(null),f=(0,D.useRef)(null),h=ue(null),[v,b]=(0,D.useState)(null),[te,T]=(0,D.useState)(!1),[re,ie]=(0,D.useState)(null),[k,A]=(0,D.useState)(null),[j,M]=(0,D.useState)(null),[ae,N]=(0,D.useState)(``);(0,D.useEffect)(()=>{N(window.location.hostname.startsWith(`admin.`)?window.location.origin.replace(`admin.`,`public.`):window.location.origin)},[]),(0,D.useEffect)(()=>{if(!l)return;let e=e=>{d.current&&!d.current.contains(e.target)&&u(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[l]),(0,D.useEffect)(()=>{if(!l)return;let e=e=>{e.key===`Escape`&&u(!1)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[l]),(0,D.useEffect)(()=>{l&&f.current?.focus()},[l]),(0,D.useEffect)(()=>{if(!l)return;let e=!1;return h(`/api/admin/version`).then(e=>e.json()).then(t=>{e||M(t)}).catch(e=>{console.error(`[admin/version] menu-open client fetch failed:`,e)}),()=>{e=!0}},[l,h]);let oe=(0,D.useCallback)(()=>{u(e=>{let t=!e;return t&&console.info(`[admin-ui] header-menu-open`),t})},[]),P=(0,D.useCallback)(e=>{u(!1),n(e)},[n]),se=(0,D.useCallback)(async()=>{if(v!==null){b(null),A(null);return}T(!0),ie(null);try{let e=await h(`/api/admin/agents`);if(!e.ok)throw Error(`Failed to load agents`);b((await e.json()).agents??[])}catch(e){console.error(`[admin/agents] list failed:`,e),ie(e instanceof Error?e.message:String(e)),b([])}finally{T(!1)}},[v,h]),F=(0,D.useCallback)(e=>{A(null),b(t=>t?.filter(t=>t.slug!==e)??null),h(`/api/admin/agents/${encodeURIComponent(e)}`,{method:`DELETE`}).catch(e=>console.error(`[admin/agents] delete failed:`,e))},[h]),I=(0,D.useCallback)(()=>{u(!1),s()},[s]),L=t||o.productName;return(0,O.jsxs)(`header`,{className:`admin-header`,children:[(0,O.jsx)(`button`,{type:`button`,className:`admin-sidebar-toggle`,"aria-label":i?`Hide sidebar`:`Show sidebar`,title:i?`Hide sidebar`:`Show sidebar`,"aria-expanded":i,onClick:r,children:i?(0,O.jsx)(C,{size:20,strokeWidth:1.5}):(0,O.jsx)(w,{size:20,strokeWidth:1.5})}),(0,O.jsxs)(`div`,{className:`chat-header-label`,children:[(0,O.jsx)(`span`,{className:`chat-logo-btn`,"aria-hidden":`true`,children:(0,O.jsx)(`img`,{src:a,alt:``,className:`chat-logo`})}),(0,O.jsx)(`h1`,{className:`chat-tagline`,children:L})]}),(0,O.jsxs)(`div`,{className:`chat-burger-wrap`,ref:d,children:[(0,O.jsx)(`button`,{type:`button`,className:`admin-burger`,onClick:oe,"aria-label":`Menu`,"aria-haspopup":`true`,"aria-expanded":l,children:(0,O.jsx)(ee,{size:20})}),l&&(0,O.jsxs)(`div`,{className:`admin-menu`,role:`menu`,children:[(0,O.jsxs)(`button`,{ref:f,type:`button`,className:`chat-menu-item`,role:`menuitem`,onClick:()=>P(`data`),children:[(0,O.jsx)(m,{size:14}),` Data`]}),(0,O.jsxs)(`button`,{type:`button`,className:`chat-menu-item`,role:`menuitem`,onClick:()=>P(`graph`),children:[(0,O.jsx)(ne,{size:14}),` Graph`]}),(0,O.jsxs)(`button`,{type:`button`,className:`chat-menu-item`,role:`menuitem`,onClick:()=>P(`browser`),children:[(0,O.jsx)(_,{size:14}),` Browser`]}),(0,O.jsx)(`div`,{className:`chat-menu-divider`}),(0,O.jsxs)(`button`,{type:`button`,className:`chat-menu-item`,role:`menuitem`,onClick:se,children:[(0,O.jsx)(g,{size:14}),` Public`,te&&(0,O.jsx)(x,{size:12,className:`spin`})]}),v!==null&&(0,O.jsxs)(`div`,{className:`chat-menu-agents`,children:[re&&(0,O.jsx)(`span`,{className:`chat-menu-agent-error`,children:re}),v.length===0&&!re&&(0,O.jsx)(`span`,{className:`chat-menu-agent-empty`,children:`No public agents configured`}),v.map(e=>(0,O.jsxs)(`div`,{className:`chat-menu-item chat-menu-agent-item`,children:[(0,O.jsx)(`span`,{className:`agent-status-dot ${e.status}`}),(0,O.jsxs)(`span`,{className:`agent-text`,children:[(0,O.jsx)(`span`,{className:`agent-display-name`,children:e.displayName}),(0,O.jsxs)(`span`,{className:`agent-slug`,children:[`/`,e.slug]})]}),k===e.slug?(0,O.jsxs)(`span`,{className:`agent-actions agent-confirm`,children:[(0,O.jsx)(`button`,{type:`button`,className:`agent-action-btn agent-confirm-yes`,title:`Confirm delete`,onClick:()=>F(e.slug),children:(0,O.jsx)(p,{size:12})}),(0,O.jsx)(`button`,{type:`button`,className:`agent-action-btn agent-confirm-no`,title:`Cancel`,onClick:()=>A(null),children:(0,O.jsx)(E,{size:12})})]}):(0,O.jsxs)(`span`,{className:`agent-actions`,children:[(0,O.jsx)(`a`,{href:`${ae}/${e.slug}`,target:`_blank`,rel:`noopener noreferrer`,className:`agent-action-btn`,title:`Open agent`,onClick:()=>u(!1),children:(0,O.jsx)(g,{size:12})}),(0,O.jsx)(`button`,{type:`button`,className:`agent-action-btn agent-delete-btn`,title:`Delete agent`,onClick:()=>A(e.slug),children:(0,O.jsx)(c,{size:12})})]})]},e.slug))]}),(0,O.jsx)(`div`,{className:`chat-menu-divider`}),(0,O.jsxs)(`div`,{className:`chat-menu-version chat-menu-version-passive`,children:[(0,O.jsx)(y,{size:14}),(0,O.jsxs)(`span`,{className:`version-installed`,children:[j?`v${j.installed}`:`…`,j&&!j.updateAvailable&&(0,O.jsx)(`span`,{className:`version-uptodate-dot`}),j?.updateAvailable&&(0,O.jsx)(`span`,{className:`version-update-dot`})]})]}),(0,O.jsxs)(`button`,{type:`button`,className:`chat-menu-item`,role:`menuitem`,onClick:I,children:[(0,O.jsx)(S,{size:14}),` Log out`]})]})]})]})}var de={whatsapp:{fill:`#25D366`,label:`WhatsApp`,path:`M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.71.306 1.263.489 1.694.625.712.227 1.36.195 1.872.118.571-.085 1.758-.719 2.006-1.413.247-.694.247-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.885-9.885 9.885M20.52 3.449C18.24 1.245 15.24.044 12.045.044 5.463.044.103 5.404.1 11.99c0 2.096.546 4.142 1.588 5.945L0 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.585 0 11.946-5.36 11.949-11.946 0-3.193-1.24-6.19-3.495-8.445`},telegram:{fill:`#229ED9`,label:`Telegram`,path:`M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z`}};function fe({channel:e,size:t=16}){let n=de[e];return(0,O.jsx)(`svg`,{width:t,height:t,viewBox:`0 0 24 24`,fill:n.fill,role:`img`,"aria-label":n.label,className:`channel-icon`,children:(0,O.jsx)(`path`,{d:n.path})})}var pe=[`whatsapp`,`telegram`];function me(e,t,n=`data`){let r=`/api/admin/files/download?session_key=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`+(n===`data`?``:`&root=${encodeURIComponent(n)}`),i=document.createElement(`a`);i.href=r,i.rel=`noopener noreferrer`,document.body.appendChild(i),i.click(),i.remove()}function H(e){if(e<60)return`${e}s`;let t=Math.floor(e/60),n=e%60;return n>0?`${t}m ${n}s`:`${t}m`}function he(e){return e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}var U=`auth-refresh-failed`;function ge({error:e,onClose:t}){if(!e)return null;let n=e.reason===U,r=n?`Claude sign-in expired`:`Session didn’t open`,i=n?`Your claude.ai login has expired. Re-authenticate to resume sessions.`:`The session did not bind within 60 seconds, so there is no live conversation to open.`;return(0,O.jsx)(`div`,{className:`claude-info-overlay`,onClick:t,children:(0,O.jsxs)(`div`,{className:`claude-info-modal`,onClick:e=>e.stopPropagation(),role:`alertdialog`,"aria-label":`Session could not be opened`,children:[(0,O.jsxs)(`div`,{className:`claude-info-header`,children:[(0,O.jsx)(`span`,{children:r}),(0,O.jsx)(`button`,{className:`claude-info-close`,onClick:t,"aria-label":`Close`,children:`✕`})]}),(0,O.jsxs)(`div`,{className:`claude-info-section`,style:{padding:`12px 14px`,fontSize:`11px`,color:`var(--text-secondary)`},children:[i,(0,O.jsxs)(`div`,{className:`claude-info-row`,style:{marginTop:`8px`},children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Reason`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:e.reason})]}),e.sessionId&&(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Session`}),(0,O.jsxs)(`span`,{className:`claude-info-value`,children:[e.sessionId.slice(0,8),`…`]})]})]})]})})}function _e(e){let{show:t,onClose:n,claudeInfo:r,messages:i,sessionElapsed:a,sessionId:o,cacheKey:c}=e,[l,u]=(0,D.useState)(null);if(!t)return null;let d=i.flatMap(e=>e.events?.filter(e=>e.type===`usage`)??[]),f=d.at(-1),p=f?.peak_request_pct==null?f?.context_window?Math.round((f.input_tokens+f.cache_creation_tokens+f.cache_read_tokens)/f.context_window*100):0:Math.round(f.peak_request_pct*100),m=d.reduce((e,t)=>e+t.input_tokens+t.cache_creation_tokens+t.cache_read_tokens+t.output_tokens,0),h=i.flatMap(e=>e.events?.filter(e=>e.type===`rate_limit`)??[]).at(-1),g=d.reduce((e,t)=>e+(t.total_cost_usd??0),0),_=r?.account?.subscriptionType,v=e=>{let t=e*1e3-Date.now();if(t<=0)return`now`;let n=Math.floor(t/36e5),r=Math.floor(t%36e5/6e4);return n>0?`${n}h ${r}m`:`${r}m`};return(0,O.jsx)(`div`,{className:`claude-info-overlay`,onClick:n,children:(0,O.jsxs)(`div`,{className:`claude-info-modal`,onClick:e=>e.stopPropagation(),children:[(0,O.jsxs)(`div`,{className:`claude-info-header`,children:[(0,O.jsx)(`img`,{src:`/brand/claude.png`,alt:`Claude`,className:`claude-info-icon`}),(0,O.jsx)(`span`,{children:`Claude Code`}),(0,O.jsx)(`button`,{className:`claude-info-close`,onClick:n,"aria-label":`Close`,children:`✕`})]}),(0,O.jsxs)(`div`,{className:`claude-info-section`,children:[(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Version`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:r?r.version:`…`})]}),(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Email`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:r?.account?.email??`…`})]})]}),(_||h||g>0)&&(0,O.jsxs)(`div`,{className:`claude-info-section`,children:[_&&(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Plan`}),(0,O.jsx)(`span`,{className:`claude-info-value`,style:{textTransform:`capitalize`},children:_})]}),h&&(0,O.jsxs)(O.Fragment,{children:[(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Usage`}),(0,O.jsxs)(`span`,{className:`claude-info-value`,children:[Math.round(h.utilization*100),`%`]})]}),(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Resets in`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:v(h.resetsAt)})]}),h.isUsingOverage&&(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Overage`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:`Active`})]})]}),g>0&&(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Session cost`}),(0,O.jsxs)(`span`,{className:`claude-info-value`,children:[`$`,g<.01?g.toFixed(4):g.toFixed(2)]})]})]}),(0,O.jsxs)(`div`,{className:`claude-info-section`,children:[(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Model`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:r?.model??`…`})]}),(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Context used`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:p>0?`${p}%`:`—`})]}),(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Tokens`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:m>0?he(m):`—`})]}),(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Session`}),(0,O.jsx)(`span`,{className:`claude-info-value`,children:H(a)})]}),(o||c)&&(()=>{let e=o??c??``;return(0,O.jsxs)(`div`,{className:`claude-info-row`,children:[(0,O.jsx)(`span`,{className:`claude-info-label`,children:`Session`}),(0,O.jsx)(`button`,{type:`button`,className:`claude-info-value claude-info-id-copy`,title:`${e} (${o?`flushed`:`pre-flush`}) — click to copy`,onClick:async()=>{u(await s(e)?`copied`:`failed`),setTimeout(()=>u(null),1200)},children:l===`copied`?`copied ✓`:l===`failed`?`copy failed ✕`:`${e.slice(0,8)}…`})]})})()]})]})})}var W=`https://claude.ai/code`,ve=[500,1e3,1500,2e3,2500,3e3,3e3];function ye(e,t,n){let r=n.slug??n.bridgeSessionId??null;if(e&&r)return{kind:`navigate`,url:`${W}/${r}`};let i=n.reason||n.error||`status ${t}`;return{kind:`error`,sessionId:n.sessionId??null,reason:i}}function be(e){let{businessName:n,cacheKey:r,role:i,userName:a,userAvatar:s,onSelectProjects:p,onSelectPeople:m,onSelectTasks:_,onSelectAgents:y,onCloseMobileDrawer:x,collapsed:S,selectedWhatsappId:ee,onSelectWhatsappConversation:C}=e,w=ue(r),ne=o.productName,E=typeof a==`string`?a:a===null?`name unavailable`:n||ne,k=(E.trim().charAt(0)||`?`).toUpperCase(),[A,j]=(0,D.useState)(`sessions`),[M,ae]=(0,D.useState)([]),[N,oe]=(0,D.useState)(!1),[P,se]=(0,D.useState)(null),[F,I]=(0,D.useState)(!1),[L,ce]=(0,D.useState)(`file`),[R,z]=(0,D.useState)(null),[le,B]=(0,D.useState)(null),[V,de]=(0,D.useState)([]),[H,he]=(0,D.useState)(!1),[U,_e]=(0,D.useState)(null),[W,be]=(0,D.useState)(!1),[G,K]=(0,D.useState)(!1),[q,Se]=(0,D.useState)(!1),[Ce,we]=(0,D.useState)(null),[Te,J]=(0,D.useState)(new Set),[Ee,De]=(0,D.useState)(new Set),[Oe,ke]=(0,D.useState)(new Set),[Ae,je]=(0,D.useState)(new Set),[Y,Me]=(0,D.useState)(!1),[Ne,Pe]=(0,D.useState)([]),[Fe,Ie]=(0,D.useState)(!1),[Le,Re]=(0,D.useState)(null),[X,ze]=(0,D.useState)(`whatsapp`),Be=(0,D.useCallback)(e=>{if(x(),!r){console.error(`[admin-ui] artefact-download-blocked id=${e.id} reason=no-cache-key`),z({message:`Session not ready — try again`,failed:!0});return}if(!e.downloadPath){console.error(`[admin-ui] artefact-download-blocked id=${e.id} reason=not-downloadable`),z({message:`${e.name} can’t be downloaded`,failed:!0});return}console.info(`[admin-ui] artefact-download id=${e.id} root=${e.downloadRoot??`data`} path=${e.downloadPath}`),me(r,e.downloadPath,e.downloadRoot??`data`),z({message:`Downloading ${e.name}`,failed:!1})},[r,x]);(0,D.useEffect)(()=>{if(!R)return;let e=setTimeout(()=>z(null),2500);return()=>clearTimeout(e)},[R]);let Ve=(0,D.useCallback)(async()=>{if(r){I(!0),se(null);try{let e=await w(`/api/admin/sidebar-artefacts`);if(!e.ok)throw Error(`status ${e.status}`);ae((await e.json()).artefacts??[]),oe(!0)}catch(e){let t=e instanceof Error?e.message:String(e);se(`Failed to load artefacts: ${t}`),console.error(`[admin-ui] sidebar-artefacts fetch failed: ${t}`)}finally{I(!1)}}},[r,w]),Z=(0,D.useCallback)(async()=>{if(!r)return null;be(!0),_e(null);try{let e=await w(`/api/admin/sidebar-sessions`);if(!e.ok)throw Error(`status ${e.status}`);let t=await e.json(),n=t.sessions??[];return de(n),we(t.accountId??null),he(!0),n}catch(e){let t=e instanceof Error?e.message:String(e);return _e(`Failed to load sessions: ${t}`),console.error(`[admin-ui] sidebar-sessions fetch failed: ${t}`),null}finally{be(!1)}},[r,w]),He=(0,D.useCallback)(async()=>{if(r){Re(null);try{let e=await w(`/api/whatsapp-reader/conversations`);if(!e.ok)throw Error(`status ${e.status}`);Pe((await e.json()).conversations??[]),Ie(!0)}catch(e){let t=e instanceof Error?e.message:String(e);Re(`Couldn't load conversations: ${t}`),console.error(`[admin-ui] channel-convos fetch failed: ${t}`)}}},[r,w]);(0,D.useEffect)(()=>{!r||H||Z()},[r,H,Z]),(0,D.useEffect)(()=>{r&&He()},[r,He]);let Q=(0,D.useMemo)(()=>{let e=new Map;for(let t of pe)e.set(t,[]);for(let t of Ne)e.get(t.channel)?.push(t);for(let t of pe)console.info(`[admin-ui] sidebar-nav surface=${t} count=${e.get(t).length}`);return e},[Ne]),Ue={whatsapp:`WhatsApp`,telegram:`Telegram`},We=()=>{C(null),j(`artefacts`),console.info(`[admin-ui] sidebar-nav surface=artefacts count=${N?M.length:0} collapsed=${S}`),Ve()},$=1.5,Ge=()=>{console.info(`[admin-ui] sidebar-refresh surface=artefacts`),Ve()},Ke=()=>{C(null),j(`sessions`),console.info(`[admin-ui] sidebar-nav surface=sessions count=${H?V.length:0} collapsed=${S}`),H||Z()},qe=()=>{console.info(`[admin-ui] sidebar-refresh surface=sessions`),Z()},Je=e=>{j(`whatsapp`),ze(e),console.info(`[admin-ui] sidebar-nav surface=${e} count=${Q.get(e)?.length??0} collapsed=${S}`),x()},Ye=(0,D.useCallback)(async e=>{if(e.resumable===!1||Ee.has(e.sessionId))return;De(t=>{let n=new Set(t);return n.add(e.sessionId),n});let t=window.open(``,`_blank`);try{let n=await w(`/api/admin/session-rc-spawn`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sessionId:e.sessionId,name:e.title||e.sessionId})}),r=await n.json().catch(()=>({})),i=ye(n.ok,n.status,{...r,sessionId:r.sessionId??e.sessionId});console.info(`[admin-ui] sidebar-session-resume sessionId=${e.sessionId} status=${n.status} outcome=${i.kind} slug=${r.slug??r.bridgeSessionId??`none`}`),i.kind===`navigate`?(x(),t?t.location.href=i.url:window.open(i.url,`_blank`)):(t?.close(),console.error(`[admin-ui] sidebar-session-resume-failed sessionId=${e.sessionId} status=${n.status} reason=${i.reason}`),B(i))}catch(n){t?.close();let r=n instanceof Error?n.message:String(n);console.error(`[admin-ui] sidebar-session-resume-failed sessionId=${e.sessionId} error=${r}`),B({sessionId:e.sessionId,reason:r})}finally{De(t=>{let n=new Set(t);return n.delete(e.sessionId),n})}},[w,x,Ee]),Xe=(0,D.useCallback)(async e=>{let t=e.slice(0,8);for(let n=1;n<=ve.length;n++){await new Promise(e=>setTimeout(e,ve[n-1]));let r=await Z();if(r&&r.some(t=>t.sessionId===e)){console.info(`[admin-ui] sidebar-new-session-converged sessionId=${t} via=retry attempts=${n}`);return}}console.error(`[admin-ui] sidebar-new-session-converge-timeout sessionId=${t}`)},[Z]),Ze=(0,D.useCallback)(async()=>{if(Y)return;Me(!0);let e=window.open(``,`_blank`);try{let t=await w(`/api/admin/session-rc-spawn`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({})}),n=await t.json().catch(()=>({})),r=ye(t.ok,t.status,n);console.info(`[admin-ui] sidebar-new-session-spawned status=${t.status} outcome=${r.kind} slug=${n.slug??n.bridgeSessionId??`none`}`),r.kind===`navigate`?(e?e.location.href=r.url:window.open(r.url,`_blank`),n.sessionId?Xe(n.sessionId):console.error(`[admin-ui] sidebar-new-session-converge-skipped reason=no-session-id`)):(e?.close(),console.error(`[admin-ui] sidebar-new-session-failed status=${t.status} reason=${r.reason}`),B(r))}catch(t){e?.close();let n=t instanceof Error?t.message:String(t);console.error(`[admin-ui] sidebar-new-session-failed error=${n}`),B({sessionId:null,reason:n})}finally{Me(!1)}},[w,Y,Xe]),Qe=(0,D.useCallback)(async(e,t)=>{if(e.stopPropagation(),!Te.has(t.sessionId)){J(e=>{let n=new Set(e);return n.add(t.sessionId),n});try{let e=await w(`/api/admin/session-delete`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sessionId:t.sessionId})});if(!e.ok){let n=await e.json().catch(()=>({}));console.error(`[admin-ui] sidebar-session-delete-failed sessionId=${t.sessionId} status=${e.status} error=${n.error??`unknown`}`),z({message:`Delete failed: ${n.error??`status ${e.status}`}`,failed:!0});return}let n=await e.json();console.info(`[admin-ui] sidebar-session-delete sessionId=${t.sessionId} pidKilled=${n.pidKilled??`?`} deleted=${n.deleted??`?`}`),de(e=>e.filter(e=>e.sessionId!==t.sessionId)),Z()}catch(e){let n=e instanceof Error?e.message:String(e);console.error(`[admin-ui] sidebar-session-delete-failed sessionId=${t.sessionId} error=${n}`),z({message:`Delete failed: ${n}`,failed:!0})}finally{J(e=>{let n=new Set(e);return n.delete(t.sessionId),n})}}},[w,Te,Z]),$e=(0,D.useCallback)(async(e,t)=>{if(e.stopPropagation(),!Oe.has(t.sessionId)){ke(e=>{let n=new Set(e);return n.add(t.sessionId),n});try{let e=await w(`/api/admin/session-stop`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sessionId:t.sessionId})});if(!e.ok){let n=await e.json().catch(()=>({}));console.error(`[admin-ui] sidebar-session-stop-failed sessionId=${t.sessionId} status=${e.status} error=${n.error??`unknown`}`),z({message:`Stop failed: ${n.error??`status ${e.status}`}`,failed:!0});return}console.info(`[admin-ui] sidebar-session-stop sessionId=${t.sessionId}`),Z()}catch(e){let n=e instanceof Error?e.message:String(e);console.error(`[admin-ui] sidebar-session-stop-failed sessionId=${t.sessionId} error=${n}`),z({message:`Stop failed: ${n}`,failed:!0})}finally{ke(e=>{let n=new Set(e);return n.delete(t.sessionId),n})}}},[w,Oe,Z]),et=(0,D.useCallback)(async(e,t,n)=>{if(e.stopPropagation(),!Ae.has(t.sessionId)){je(e=>{let n=new Set(e);return n.add(t.sessionId),n});try{let e=await w(`/api/admin/session-archive`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sessionId:t.sessionId,mode:n})});if(!e.ok){let r=await e.json().catch(()=>({})),i=r.detail??r.error??`status ${e.status}`;console.error(`[admin-ui] sidebar-session-archive-failed sessionId=${t.sessionId} mode=${n} status=${e.status} error=${r.error??`unknown`}`),z({message:`${n===`archive`?`Archive`:`Unarchive`} failed: ${i}`,failed:!0});return}console.info(`[admin-ui] sidebar-session-archive sessionId=${t.sessionId} mode=${n}`),Z()}catch(e){let r=e instanceof Error?e.message:String(e);console.error(`[admin-ui] sidebar-session-archive-failed sessionId=${t.sessionId} mode=${n} error=${r}`),z({message:`${n===`archive`?`Archive`:`Unarchive`} failed: ${r}`,failed:!0})}finally{je(e=>{let n=new Set(e);return n.delete(t.sessionId),n})}}},[w,Ae,Z]);return(0,O.jsxs)(`aside`,{className:`side`,children:[(0,O.jsx)(`div`,{className:`side-new-session-row`,children:(0,O.jsxs)(`button`,{type:`button`,className:`side-new-session`,onClick:Ze,disabled:Y,"aria-busy":Y,children:[(0,O.jsx)(te,{size:14}),(0,O.jsx)(`span`,{children:Y?`Spawning…`:`New session`})]})}),(0,O.jsxs)(`nav`,{className:`side-nav`,children:[(0,O.jsxs)(`button`,{type:`button`,className:`nav-row`,onClick:()=>{console.info(`[admin-ui] sidebar-nav surface=people`),m(),x()},children:[(0,O.jsx)(ie,{size:20,strokeWidth:$}),(0,O.jsx)(`span`,{className:`label`,children:`People`}),(0,O.jsx)(`span`,{className:`kbd`})]}),(0,O.jsxs)(`button`,{type:`button`,className:`nav-row`,onClick:()=>{console.info(`[admin-ui] sidebar-nav surface=agents`),y(),x()},children:[(0,O.jsx)(d,{size:20,strokeWidth:$}),(0,O.jsx)(`span`,{className:`label`,children:`Agents`}),(0,O.jsx)(`span`,{className:`kbd`})]}),(0,O.jsxs)(`button`,{type:`button`,className:`nav-row`,onClick:()=>{console.info(`[admin-ui] sidebar-nav surface=projects`),p(),x()},children:[(0,O.jsx)(f,{size:20,strokeWidth:$}),(0,O.jsx)(`span`,{className:`label`,children:`Projects`}),(0,O.jsx)(`span`,{className:`kbd`})]}),(0,O.jsxs)(`button`,{type:`button`,className:`nav-row`,onClick:()=>{console.info(`[admin-ui] sidebar-nav surface=tasks`),_(),x()},children:[(0,O.jsx)(b,{size:20,strokeWidth:$}),(0,O.jsx)(`span`,{className:`label`,children:`Tasks`}),(0,O.jsx)(`span`,{className:`kbd`})]}),(0,O.jsxs)(`button`,{type:`button`,className:`nav-row${A===`artefacts`?` active`:``}`,onClick:We,children:[(0,O.jsx)(t,{size:20,strokeWidth:$}),(0,O.jsx)(`span`,{className:`label`,children:`Artefacts`}),(0,O.jsx)(`span`,{className:`kbd`})]}),(0,O.jsxs)(`button`,{type:`button`,className:`nav-row${A===`sessions`?` active`:``}`,onClick:Ke,children:[(0,O.jsx)(v,{size:20,strokeWidth:$}),(0,O.jsx)(`span`,{className:`label`,children:`Sessions`}),(0,O.jsx)(`span`,{className:`kbd`})]}),Fe&&pe.filter(e=>Q.get(e).length>0).map(e=>(0,O.jsxs)(`button`,{type:`button`,className:`nav-row${A===`whatsapp`&&X===e?` active`:``}`,onClick:()=>Je(e),children:[(0,O.jsx)(fe,{channel:e,size:16}),(0,O.jsx)(`span`,{className:`label`,children:Ue[e]}),(0,O.jsx)(`span`,{className:`kbd`})]},e)),Le&&(0,O.jsx)(`div`,{className:`nav-row`,style:{color:`var(--text-tertiary)`,cursor:`default`},"aria-disabled":`true`,children:(0,O.jsx)(`span`,{className:`label`,children:Le})})]}),A===`artefacts`&&(0,O.jsxs)(`div`,{className:`side-list`,children:[(0,O.jsxs)(`div`,{className:`group-head`,children:[(0,O.jsx)(`span`,{children:`Artefacts`}),(0,O.jsxs)(`span`,{className:`group-head-meta`,children:[(0,O.jsx)(`button`,{type:`button`,className:`group-head-refresh`,title:`Refresh artefacts`,"aria-label":`Refresh artefacts`,onClick:Ge,disabled:F,children:(0,O.jsx)(T,{size:12,className:F?`spinning`:void 0})}),(0,O.jsx)(`span`,{children:F?`…`:String(M.length)})]})]}),P&&(0,O.jsx)(`div`,{className:`conv`,style:{color:`var(--text-tertiary)`,cursor:`default`},children:P}),N&&!P&&M.length>0&&(()=>{let e=M.filter(e=>e.kind===`agent-template`).length,t=M.length-e;return(0,O.jsx)(`div`,{className:`artefact-filter-chips`,children:[{key:`all`,label:`All`,count:M.length},{key:`agent`,label:`Agents`,count:e},{key:`file`,label:`Files`,count:t}].map(e=>(0,O.jsxs)(`button`,{type:`button`,className:`artefact-filter-chip${L===e.key?` active`:``}`,onClick:()=>ce(e.key),disabled:e.count===0&&e.key!==`all`,children:[e.label,(0,O.jsx)(`span`,{className:`artefact-filter-chip-count`,children:e.count})]},e.key))})})(),N&&!P&&M.length===0&&(0,O.jsx)(`div`,{className:`conv`,style:{color:`var(--text-tertiary)`,cursor:`default`},children:`No artefacts yet`}),M.filter(e=>L===`all`?!0:L===`agent`?e.kind===`agent-template`:e.kind!==`agent-template`).map(e=>{let n=e.kind===`agent-template`,r=n?d:t,i=Date.parse(e.updatedAt),a=Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`}):``,o=e.downloadPath!==null;return(0,O.jsxs)(`button`,{type:`button`,className:`conv`,onClick:()=>Be(e),disabled:!o,style:o?void 0:{cursor:`default`},title:o?`Download ${e.name}`:`${e.name} can’t be downloaded`,children:[(0,O.jsx)(r,{size:14,className:`conv-icon`,"data-kind":n?`agent`:`file`,"aria-label":n?`agent template`:`file`}),(0,O.jsxs)(`span`,{className:`conv-stack`,children:[(0,O.jsx)(`span`,{className:`conv-name-line`,children:(0,O.jsx)(`span`,{className:`conv-name`,children:e.name})}),a&&(0,O.jsx)(`span`,{className:`conv-timestamp`,children:a})]}),o&&(0,O.jsx)(h,{size:12,className:`conv-rc-icon`,"aria-hidden":`true`})]},e.id)})]}),A===`sessions`&&(()=>{let e=V.filter(e=>G?!0:!e.isSubagent).filter(e=>q?!0:!e.archived),t=V.some(e=>e.isSubagent),n=V.some(e=>e.archived);return(0,O.jsxs)(`div`,{className:`side-list`,children:[(0,O.jsxs)(`div`,{className:`group-head`,children:[(0,O.jsx)(`span`,{children:`Sessions`}),(0,O.jsxs)(`span`,{className:`group-head-meta`,children:[(0,O.jsx)(`button`,{type:`button`,className:`group-head-refresh`,title:`Refresh sessions`,"aria-label":`Refresh sessions`,onClick:qe,disabled:W,children:(0,O.jsx)(T,{size:12,className:W?`spinning`:void 0})}),(0,O.jsx)(`span`,{children:W?`…`:String(e.length)})]})]}),Ce&&(0,O.jsx)(`div`,{className:`side-account-id`,title:`This install's accountId. The first 8 characters match the truncated UUID label on the live Remote Control daemon entry in claude.ai/code.`,children:(0,O.jsx)(`code`,{children:Ce})}),(t||n)&&(0,O.jsxs)(`div`,{className:`artefact-filter-chips`,children:[t&&(0,O.jsx)(`button`,{type:`button`,className:`artefact-filter-chip${G?` active`:``}`,onClick:()=>K(e=>!e),title:G?`Hide subagent sessions`:`Show subagent sessions`,children:`Subagents`}),n&&(0,O.jsx)(`button`,{type:`button`,className:`artefact-filter-chip${q?` active`:``}`,onClick:()=>Se(e=>!e),title:q?`Hide archived sessions`:`Show archived sessions`,children:`Archived`})]}),U&&(0,O.jsx)(`div`,{className:`conv`,style:{color:`var(--text-tertiary)`,cursor:`default`},children:U}),H&&!U&&e.length===0&&(0,O.jsx)(`div`,{className:`conv`,style:{color:`var(--text-tertiary)`,cursor:`default`},children:`No sessions yet`}),e.map(e=>{let t=Date.parse(e.startedAt),n=Number.isFinite(t)?new Date(t).toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`}):``,r=Ee.has(e.sessionId),i=Te.has(e.sessionId),a=Oe.has(e.sessionId),o=Ae.has(e.sessionId);return(0,O.jsxs)(`div`,{className:`conv conv-with-actions`,children:[(0,O.jsxs)(`div`,{className:`conv-main-static`,children:[(0,O.jsx)(`span`,{className:`conv-live-dot`,"data-live":e.live?`1`:`0`,"aria-label":e.live?`live session`:`ended session`}),(0,O.jsxs)(`span`,{className:`conv-stack`,children:[(0,O.jsx)(`span`,{className:`conv-name-line`,children:(0,O.jsx)(`span`,{className:`conv-name`,title:e.title,children:e.title})}),(0,O.jsxs)(`span`,{className:`conv-timestamp`,children:[(0,O.jsx)(`code`,{className:`conv-session-id`,title:`First 8 characters of this session's id — distinguishes rows with identical auto-titles. The resume/delete buttons act on the full id.`,children:e.sessionId.slice(0,8)}),n&&(0,O.jsxs)(`span`,{children:[` · `,n]})]})]})]}),(0,O.jsxs)(`div`,{className:`conv-actions`,children:[e.resumable===!1?(0,O.jsx)(`span`,{className:`conv-ephemeral`,title:`Public webchat session — ephemeral, not resumable`,"aria-label":`Ephemeral public session (not resumable)`,children:(0,O.jsx)(v,{size:12})}):(0,O.jsx)(`button`,{type:`button`,className:`conv-action`,onClick:()=>{Ye(e)},disabled:r||i||a||o,"aria-busy":r,"aria-label":`Resume session ${e.title} in claude.ai/code`,title:`Resume in a fresh local Remote Control PTY`,children:(0,O.jsx)(g,{size:12})}),e.live?(0,O.jsx)(`button`,{type:`button`,className:`conv-action`,onClick:t=>{$e(t,e)},disabled:r||i||a||o,"aria-busy":a,"aria-label":`Stop session ${e.title}`,title:`Stop session process (keeps the conversation, can resume later)`,children:(0,O.jsx)(re,{size:12})}):null,e.archived?(0,O.jsx)(`button`,{type:`button`,className:`conv-action`,onClick:t=>{et(t,e,`unarchive`)},disabled:r||i||a||o,"aria-busy":o,"aria-label":`Unarchive session ${e.title}`,title:`Unarchive (move back to the active list)`,children:(0,O.jsx)(l,{size:12})}):(0,O.jsx)(`button`,{type:`button`,className:`conv-action`,onClick:t=>{et(t,e,`archive`)},disabled:r||i||a||o,"aria-busy":o,"aria-label":`Archive session ${e.title}`,title:`Archive (hide from the list, keeps the conversation resumable)`,children:(0,O.jsx)(u,{size:12})}),(0,O.jsx)(`button`,{type:`button`,className:`conv-action conv-action-danger`,onClick:t=>{Qe(t,e)},disabled:r||i||a||o,"aria-busy":i,"aria-label":`Delete session ${e.title}`,title:`Delete session (stops the process, removes the conversation)`,children:(0,O.jsx)(c,{size:12})})]})]},e.sessionId)})]})})(),A===`whatsapp`&&(0,O.jsxs)(`div`,{className:`side-list`,children:[(0,O.jsxs)(`div`,{className:`group-head`,children:[(0,O.jsx)(`span`,{children:Ue[X]}),(0,O.jsxs)(`span`,{className:`group-head-meta`,children:[(0,O.jsx)(`button`,{type:`button`,className:`group-head-refresh`,title:`Refresh conversations`,"aria-label":`Refresh conversations`,onClick:()=>{He()},children:(0,O.jsx)(T,{size:12})}),(0,O.jsx)(`span`,{children:String(Q.get(X).length)})]})]}),Q.get(X).map(e=>(0,O.jsxs)(`button`,{type:`button`,className:`conv${ee===e.sessionId?` active`:``}`,onClick:()=>{C(e),x()},title:e.title,children:[(0,O.jsx)(fe,{channel:e.channel,size:14}),(0,O.jsx)(`span`,{className:`conv-stack`,children:(0,O.jsx)(`span`,{className:`conv-name-line`,children:(0,O.jsx)(`span`,{className:`conv-name`,children:e.operatorName??e.senderId??e.title})})})]},e.sessionId))]}),(0,O.jsx)(xe,{}),(0,O.jsxs)(`div`,{className:`side-foot`,children:[(0,O.jsx)(`div`,{className:`avatar`,children:s?(0,O.jsx)(`img`,{src:s,alt:E}):k}),(0,O.jsxs)(`div`,{className:`who`,children:[(0,O.jsx)(`span`,{className:`name`,children:E}),(0,O.jsx)(`span`,{className:`role`,children:i??`operator`})]})]}),R&&(0,O.jsx)(`div`,{className:`copy-toast${R.failed?` copy-toast-failed`:``}`,role:`status`,children:R.message}),(0,O.jsx)(ge,{error:le,onClose:()=>B(null)})]})}var G=5e3;function xe(){let[e,t]=(0,D.useState)(null);if((0,D.useEffect)(()=>{let e=!1,n=null;async function r(){try{let n=await fetch(`/api/admin/system-stats`);if(!n.ok){console.error(`[admin-ui] system-stats-fetch-failed status=${n.status}`);return}let r=await n.json();e||t(r)}catch(e){console.error(`[admin-ui] system-stats-fetch-failed reason=${e instanceof Error?e.message:String(e)}`)}}function i(){n===null&&(r(),n=setInterval(()=>{r()},G))}function a(){n!==null&&(clearInterval(n),n=null)}function o(){document.hidden?a():i()}return document.hidden||i(),document.addEventListener(`visibilitychange`,o),()=>{e=!0,a(),document.removeEventListener(`visibilitychange`,o)}},[]),!e||e.platform===`darwin`)return null;let n=e.cpuPct,r=e.memUsedPct,i=n!==null&&n>=.9,a=n!==null&&n>=.98,o=r!==null&&r>=.9,s=r!==null&&r>=.98,c=i||o,l=a||s,u=e=>e===null?`—`:`${Math.round(e*100)}%`,d=e=>{if(e===null)return{width:`0%`,background:`var(--text-tertiary)`};let t=Math.min(1,Math.max(0,e)),n=Math.round(140*(1-t));return{width:`${Math.round(t*100)}%`,background:`hsl(${n}, 65%, 45%)`}},f=[`system-stats`];return c&&f.push(`system-stats--warn`),l&&f.push(`system-stats--crit`),(0,O.jsxs)(`div`,{className:f.join(` `),role:`status`,"aria-label":`host CPU and RAM`,children:[(0,O.jsxs)(`div`,{className:`system-stats__metric`,children:[(0,O.jsxs)(`span`,{className:i?`system-stats__fig system-stats__fig--warn`:`system-stats__fig`,children:[`CPU `,u(n)]}),(0,O.jsx)(`div`,{className:`system-stats__bar`,children:(0,O.jsx)(`div`,{className:`system-stats__bar-fill`,style:d(n)})})]}),(0,O.jsxs)(`div`,{className:`system-stats__metric`,children:[(0,O.jsxs)(`span`,{className:o?`system-stats__fig system-stats__fig--warn`:`system-stats__fig`,children:[`RAM `,u(r)]}),(0,O.jsx)(`div`,{className:`system-stats__bar`,children:(0,O.jsx)(`div`,{className:`system-stats__bar-fill`,style:d(r)})})]})]})}var K=`admin-sidebar-collapsed`,q=`admin-sidebar-drawer-open`;function Se(){if(typeof window>`u`)return!1;try{return window.sessionStorage.getItem(K)===`1`}catch{return!1}}function Ce(e){if(!(typeof window>`u`))try{e?window.sessionStorage.setItem(K,`1`):window.sessionStorage.removeItem(K)}catch{}}function we(){if(typeof window>`u`)return!1;try{return window.sessionStorage.getItem(q)===`1`}catch{return!1}}function Te(e){if(!(typeof window>`u`))try{e?window.sessionStorage.setItem(q,`1`):window.sessionStorage.removeItem(q)}catch{}}var J=720;function Ee(e){let{cacheKey:t,businessName:n,onLogout:r,userName:i,userAvatar:a,role:o,children:s,footer:c}=e,[l,u]=(0,D.useState)(()=>Se()),[d,f]=(0,D.useState)(()=>we()),[p,m]=(0,D.useState)(()=>typeof window<`u`&&window.matchMedia(`(max-width: ${J}px)`).matches),[h,g]=(0,D.useState)(null);(0,D.useEffect)(()=>{if(typeof window>`u`)return;let e=window.matchMedia(`(max-width: ${J}px)`),t=e=>m(e.matches);return e.addEventListener(`change`,t),()=>e.removeEventListener(`change`,t)},[]);let _=(0,D.useCallback)(e=>{Ce(e),u(e)},[]),v=(0,D.useCallback)(e=>{Te(e),f(e)},[]);(0,D.useEffect)(()=>{if(typeof window>`u`)return;let e=window.location?.pathname??``;console.info(`[admin-ui] shell-mount route=${e} collapsed=${l} drawer=${d}`)},[]);let y=p?d:!l,b=(0,D.useCallback)(()=>{if(!(typeof window>`u`))if(window.matchMedia(`(max-width: ${J}px)`).matches){let e=d;console.info(`[admin-ui] header-sidebar-toggle from=${e?`open`:`closed`} mode=drawer`),v(!e)}else{let e=l;console.info(`[admin-ui] header-sidebar-toggle from=${e?`closed`:`open`} mode=collapse`),_(!e)}},[l,d,_,v]),x=(0,D.useCallback)(e=>{console.info(`[admin-ui] header-menu-nav target=${e}`);let t=e===`data`?`/data`:e===`graph`?`/graph`:`/browser`;window.location.href=t},[]),S={collapsed:l,mobileDrawerOpen:d,sidebarOpen:y,onToggleSidebar:b,setMobileDrawerOpen:v,selectedWhatsapp:h,onClearWhatsapp:()=>g(null)};return(0,O.jsxs)(`div`,{className:`admin-shell admin-page admin-shell-root`,children:[(0,O.jsx)(V,{businessName:n,onNavigate:x,onToggleSidebar:b,sidebarOpen:y,onLogout:r}),(0,O.jsxs)(`div`,{className:`platform${d?` menu-open`:``}${l?` sidebar-collapsed`:``}`,"data-artefact":`closed`,children:[(0,O.jsx)(be,{businessName:n,cacheKey:t,role:o??null,userName:i,userAvatar:a??null,onSelectProjects:()=>{window.location.href=`/graph?label=Project`},onSelectPeople:()=>{window.location.href=`/graph?label=Person`},onSelectTasks:()=>{window.location.href=`/graph?label=Task`},onSelectAgents:()=>{window.location.href=`/graph?label=Agent`},onCloseMobileDrawer:()=>v(!1),collapsed:l,mobileDrawerOpen:d,selectedWhatsappId:h?.sessionId??null,onSelectWhatsappConversation:g}),typeof s==`function`?s(S):s]}),d&&(0,O.jsx)(`div`,{className:`sidebar-backdrop menu-open`,"aria-hidden":`true`,onClick:()=>v(!1)}),c]})}export{ue as a,E as c,te as d,x as f,m as g,h,le as i,ne as l,g as m,_e as n,R as o,_ as p,me as r,ae as s,Ee as t,T as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./useSelectionMode-
|
|
1
|
+
import{l as e}from"./useSelectionMode-mzxTOyBv.js";var t=e();function n({checked:e,onChange:n,label:r,disabled:i}){return(0,t.jsxs)(`label`,{className:`maxy-checkbox${i?` maxy-checkbox--disabled`:``}`,children:[(0,t.jsx)(`input`,{type:`checkbox`,checked:e,onChange:e=>n(e.target.checked),disabled:i}),(0,t.jsx)(`span`,{className:`maxy-checkbox__box`,children:`✱`}),r&&(0,t.jsx)(`span`,{className:`maxy-checkbox__label`,children:r})]})}export{n as t};
|