@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.
Files changed (79) hide show
  1. package/dist/__tests__/installer-settings-permissions.test.js +86 -8
  2. package/dist/index.js +1 -1
  3. package/dist/permissions-seed.js +72 -11
  4. package/package.json +1 -1
  5. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +5 -1
  6. package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
  7. package/payload/platform/plugins/docs/references/platform.md +2 -0
  8. package/payload/server/public/assets/AdminShell-DwPVr9K1.js +1 -0
  9. package/payload/server/public/assets/{Checkbox-syHoU9nY.js → Checkbox-BdDmGlvK.js} +1 -1
  10. package/payload/server/public/assets/{admin-DyuZ4NT5.js → admin-BYtGQJ0d.js} +1 -1
  11. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DsionCio.js → architectureDiagram-Q4EWVU46-CBx_GoUY.js} +1 -1
  12. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-NOG5yVZk.js → blockDiagram-DXYQGD6D-DvWR4fpv.js} +1 -1
  13. package/payload/server/public/assets/{browser-XrVAUQdG.js → browser-2PXsTraZ.js} +1 -1
  14. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-DwFV1k9I.js → c4Diagram-AHTNJAMY-BbSOxeeU.js} +1 -1
  15. package/payload/server/public/assets/channel-C4NqYS8u.js +1 -0
  16. package/payload/server/public/assets/{chunk-336JU56O-iJ4ISPCi.js → chunk-336JU56O-DJzmXTk5.js} +2 -2
  17. package/payload/server/public/assets/{chunk-426QAEUC-sWe0Iexz.js → chunk-426QAEUC-BSwzskM5.js} +1 -1
  18. package/payload/server/public/assets/{chunk-4TB4RGXK-kKNYi2_i.js → chunk-4TB4RGXK-C-fH8BTZ.js} +1 -1
  19. package/payload/server/public/assets/{chunk-5FUZZQ4R-Car3r0he.js → chunk-5FUZZQ4R-DW-MaiGX.js} +1 -1
  20. package/payload/server/public/assets/{chunk-5PVQY5BW-DR9EfLvj.js → chunk-5PVQY5BW-DlKbwC1X.js} +1 -1
  21. package/payload/server/public/assets/{chunk-EDXVE4YY-DLUWVRZV.js → chunk-EDXVE4YY-DnkdxwAt.js} +1 -1
  22. package/payload/server/public/assets/{chunk-ENJZ2VHE-B4bNJo6C.js → chunk-ENJZ2VHE-BTfezWsj.js} +1 -1
  23. package/payload/server/public/assets/{chunk-ICPOFSXX-Qzl8Ki0l.js → chunk-ICPOFSXX-BGg8uTtu.js} +1 -1
  24. package/payload/server/public/assets/{chunk-OYMX7WX6-BenBO6O_.js → chunk-OYMX7WX6-CYJInav8.js} +1 -1
  25. package/payload/server/public/assets/{chunk-U2HBQHQK-DewM8R0D.js → chunk-U2HBQHQK-B6kD6ZHU.js} +1 -1
  26. package/payload/server/public/assets/{chunk-X2U36JSP-C22Q6Ea4.js → chunk-X2U36JSP-7tvWkjym.js} +1 -1
  27. package/payload/server/public/assets/{chunk-YZCP3GAM-DfUUNTnA.js → chunk-YZCP3GAM-D6NjOb5l.js} +1 -1
  28. package/payload/server/public/assets/{chunk-ZZ45TVLE-CUT6zma2.js → chunk-ZZ45TVLE-Dqz69eJy.js} +1 -1
  29. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CI3_kz04.js +1 -0
  30. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-UP3RcKH9.js +1 -0
  31. package/payload/server/public/assets/clone-CvAPweMB.js +1 -0
  32. package/payload/server/public/assets/{dagre-KV5264BT-D4EGb60h.js → dagre-KV5264BT-DswETLwm.js} +1 -1
  33. package/payload/server/public/assets/{dagre-CYXRIdv0.js → dagre-di-KHpsq.js} +1 -1
  34. package/payload/server/public/assets/{data-tcu3MXmK.js → data-B6vpe0Zj.js} +1 -1
  35. package/payload/server/public/assets/{diagram-5BDNPKRD-BzieCEfT.js → diagram-5BDNPKRD-DWmq_Zkl.js} +1 -1
  36. package/payload/server/public/assets/{diagram-G4DWMVQ6-DDbO71XD.js → diagram-G4DWMVQ6-BVi6yjLL.js} +1 -1
  37. package/payload/server/public/assets/{diagram-MMDJMWI5-DqQ_UXDA.js → diagram-MMDJMWI5-D6XKyUAT.js} +1 -1
  38. package/payload/server/public/assets/{diagram-TYMM5635-CFyxA1aZ.js → diagram-TYMM5635-sU1d7P6W.js} +1 -1
  39. package/payload/server/public/assets/{erDiagram-SMLLAGMA-BMaEBbOK.js → erDiagram-SMLLAGMA-CY6qPZvu.js} +1 -1
  40. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-De5ChZQP.js → flowDiagram-DWJPFMVM-CE9hd869.js} +1 -1
  41. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CXEMPVaK.js → ganttDiagram-T4ZO3ILL-Cbb885gt.js} +1 -1
  42. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-OZedr_6y.js → gitGraphDiagram-UUTBAWPF-BNzO3jBu.js} +1 -1
  43. package/payload/server/public/assets/{graph-I4GyC2MW.js → graph-DfegJdnT.js} +1 -1
  44. package/payload/server/public/assets/{graph-labels-DSDTSBfM.js → graph-labels-BG4aqzs6.js} +1 -1
  45. package/payload/server/public/assets/{graphlib-CN4nhD2U.js → graphlib-CIzKPoW_.js} +1 -1
  46. package/payload/server/public/assets/{infoDiagram-42DDH7IO-CSqg4nRL.js → infoDiagram-42DDH7IO-DWGtofAT.js} +1 -1
  47. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-ChlzrZyx.js → ishikawaDiagram-UXIWVN3A-UG1rus6G.js} +1 -1
  48. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DsrK55ZO.js → journeyDiagram-VCZTEJTY-CYhHYXmj.js} +1 -1
  49. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DYF-_Rym.js → kanban-definition-6JOO6SKY-CWHj9DHX.js} +1 -1
  50. package/payload/server/public/assets/{line-3zmwI8XK.js → line-vMN9CmXx.js} +1 -1
  51. package/payload/server/public/assets/{mermaid-parser.core-BrcX8-cl.js → mermaid-parser.core-5X7WWCZe.js} +1 -1
  52. package/payload/server/public/assets/{mermaid.core-CBdz3b1N.js → mermaid.core-B0zUZsT6.js} +3 -3
  53. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BqCqlYZb.js → mindmap-definition-QFDTVHPH-BGr68ynR.js} +1 -1
  54. package/payload/server/public/assets/{pieDiagram-DEJITSTG-D5LiILuM.js → pieDiagram-DEJITSTG-Bo3zU_w2.js} +1 -1
  55. package/payload/server/public/assets/{public-udi_Z7la.js → public-DCZ92LR9.js} +3 -3
  56. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-C3iuI5l_.js → quadrantDiagram-34T5L4WZ-o-dLMRj-.js} +1 -1
  57. package/payload/server/public/assets/{requirementDiagram-MS252O5E-CTPOw5qQ.js → requirementDiagram-MS252O5E-D_O_ZclX.js} +1 -1
  58. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-D_5aVlYp.js → sankeyDiagram-XADWPNL6-Doz7uWIn.js} +1 -1
  59. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-D7h5vocM.js → sequenceDiagram-FGHM5R23-r1VW_jgc.js} +1 -1
  60. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DyZjuwPj.js → stateDiagram-FHFEXIEX-B7O06aua.js} +1 -1
  61. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Bo1g66pA.js +1 -0
  62. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BqpVYJz_.js → timeline-definition-GMOUNBTQ-WS2AL0yL.js} +1 -1
  63. package/payload/server/public/assets/{useSelectionMode-BnRcJeg2.css → useSelectionMode-lFraIQzT.css} +1 -1
  64. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Czflml0e.js → vennDiagram-DHZGUBPP-_-FLcVAh.js} +1 -1
  65. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Cqz3pSTK.js → wardleyDiagram-NUSXRM2D-nZ3oO0ih.js} +1 -1
  66. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CS5dEuUO.js → xychartDiagram-5P7HB3ND-CqTdRM37.js} +1 -1
  67. package/payload/server/public/browser.html +4 -4
  68. package/payload/server/public/data.html +5 -5
  69. package/payload/server/public/graph.html +6 -6
  70. package/payload/server/public/index.html +5 -5
  71. package/payload/server/public/public.html +4 -4
  72. package/payload/server/server.js +236 -155
  73. package/payload/server/public/assets/AdminShell-8Y7-maNi.js +0 -1
  74. package/payload/server/public/assets/channel-D8Go_zJk.js +0 -1
  75. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BLb1mzrT.js +0 -1
  76. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Ctj5qYTS.js +0 -1
  77. package/payload/server/public/assets/clone-DGZ2vydA.js +0 -1
  78. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BiW9pGXy.js +0 -1
  79. /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
- test("empty target: creates settings.json with the permissions block", () => {
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, { permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] } });
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("idempotent: already-correct block triggers no write", () => {
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
- writeFileSync(settingsPath, JSON.stringify({ model: "opus", permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] } }, null, 2));
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 the seeded block", () => {
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);
@@ -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 block. Account-scoped settings.json is written by
18
- // platform/scripts/setup-account.sh in lockstep with the same shape.
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
- function isAlreadyCorrect(permissions) {
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 block. Any keys the prior
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
- if (isAlreadyCorrect(existing.permissions)) {
60
- return { action: "noop", path: settingsPath };
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 = { ...existing, permissions: { ...BYPASS_PERMISSIONS_BLOCK, allow: [...BYPASS_PERMISSIONS_BLOCK.allow], deny: [] } };
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 permissions = parsed.permissions;
83
- return isAlreadyCorrect(permissions)
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.284",
3
+ "version": "0.1.285",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -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:5dae80dcc0e3d36e755dcf26570c4d7c8893b6198b8cd769b3e5c6c1c79169f5
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-BPzPjKP_.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};
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};