@rubytech/create-maxy-code 0.1.108 → 0.1.110

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 (124) hide show
  1. package/dist/index.js +11 -125
  2. package/dist/snap-chromium.js +1 -2
  3. package/dist/uninstall.js +10 -9
  4. package/package.json +1 -1
  5. package/payload/platform/plugins/admin/PLUGIN.md +1 -0
  6. package/payload/platform/plugins/admin/mcp/dist/index.js +1 -1
  7. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  8. package/payload/platform/plugins/admin/skills/upgrade/SKILL.md +34 -0
  9. package/payload/platform/plugins/cloudflare/.claude-plugin/plugin.json +1 -1
  10. package/payload/platform/plugins/cloudflare/PLUGIN.md +9 -16
  11. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +7 -12
  12. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
  13. package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +3 -3
  14. package/payload/platform/plugins/cloudflare/references/manual-setup.md +16 -51
  15. package/payload/platform/plugins/cloudflare/references/reset-guide.md +24 -25
  16. package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +29 -144
  17. package/payload/platform/plugins/docs/references/admin-session.md +1 -1
  18. package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
  19. package/payload/platform/plugins/docs/references/cloudflare.md +20 -29
  20. package/payload/platform/plugins/docs/references/platform.md +4 -22
  21. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  22. package/payload/platform/plugins/docs/references/troubleshooting.md +4 -316
  23. package/payload/platform/plugins/venture-studio/PLUGIN.md +35 -4
  24. package/payload/platform/plugins/venture-studio/bin/scaffold.sh +104 -0
  25. package/payload/platform/plugins/venture-studio/skills/investor-data-room/SKILL.md +3 -1
  26. package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.d.ts +2 -0
  27. package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.d.ts.map +1 -0
  28. package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.js +78 -0
  29. package/payload/platform/plugins/work/mcp/dist/cli/project-create-cli.js.map +1 -0
  30. package/payload/platform/scripts/check-no-task-id-leaks.mjs +1 -1
  31. package/payload/platform/scripts/vnc.sh +4 -3
  32. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  33. package/payload/premium-plugins/venture-studio/PLUGIN.md +35 -4
  34. package/payload/premium-plugins/venture-studio/bin/scaffold.sh +104 -0
  35. package/payload/premium-plugins/venture-studio/skills/investor-data-room/SKILL.md +3 -1
  36. package/payload/server/{chunk-AGFS3TVN.js → chunk-FSPPVVWM.js} +1303 -496
  37. package/payload/server/maxy-edge.js +21 -259
  38. package/payload/server/public/assets/{ChatInput-DJsqm_Gf.js → ChatInput-CsnIedhS.js} +1 -5
  39. package/payload/server/public/assets/{Checkbox-DGZG9BKc.js → Checkbox-CWugFyFT.js} +1 -1
  40. package/payload/server/public/assets/admin-D3gZuyUn.js +217 -0
  41. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Dw16BhiX.js → architectureDiagram-Q4EWVU46-CeTRKWDb.js} +1 -1
  42. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DIOpmf5Y.js → blockDiagram-DXYQGD6D-DeeIX5U3.js} +1 -1
  43. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Tdb_HZeX.js → c4Diagram-AHTNJAMY-CFsqZuil.js} +1 -1
  44. package/payload/server/public/assets/channel-CrSx5mnG.js +1 -0
  45. package/payload/server/public/assets/{chunk-336JU56O-CebpwDDe.js → chunk-336JU56O-DpIXuFM0.js} +2 -2
  46. package/payload/server/public/assets/{chunk-426QAEUC-BtRCmfDU.js → chunk-426QAEUC-Qk8qqrvA.js} +1 -1
  47. package/payload/server/public/assets/{chunk-4TB4RGXK-BZ3GEWs3.js → chunk-4TB4RGXK-DtM8-CUn.js} +1 -1
  48. package/payload/server/public/assets/{chunk-5FUZZQ4R-iDI6Xu0U.js → chunk-5FUZZQ4R-CrSQ4ySU.js} +1 -1
  49. package/payload/server/public/assets/{chunk-5PVQY5BW-SQD_EpYa.js → chunk-5PVQY5BW-Bn2nQwdj.js} +1 -1
  50. package/payload/server/public/assets/{chunk-EDXVE4YY-CtCcA7_e.js → chunk-EDXVE4YY-CzCPnR0P.js} +1 -1
  51. package/payload/server/public/assets/{chunk-ENJZ2VHE-pXVGVCbb.js → chunk-ENJZ2VHE-CgZj9RoG.js} +1 -1
  52. package/payload/server/public/assets/{chunk-ICPOFSXX-Dkzg9o2N.js → chunk-ICPOFSXX-wy-eNjwW.js} +1 -1
  53. package/payload/server/public/assets/{chunk-OYMX7WX6-1ZZWzf9F.js → chunk-OYMX7WX6-CMmJtL8S.js} +1 -1
  54. package/payload/server/public/assets/{chunk-U2HBQHQK-CpQ3kzO0.js → chunk-U2HBQHQK-CFCW7OaT.js} +1 -1
  55. package/payload/server/public/assets/{chunk-X2U36JSP-C2LkxroC.js → chunk-X2U36JSP-Bgh-CJSN.js} +1 -1
  56. package/payload/server/public/assets/{chunk-YZCP3GAM-Bl5jBOt5.js → chunk-YZCP3GAM-BXKwZ4vN.js} +1 -1
  57. package/payload/server/public/assets/{chunk-ZZ45TVLE-CHtnptPS.js → chunk-ZZ45TVLE-BiOuK5NP.js} +1 -1
  58. package/payload/server/public/assets/classDiagram-6PBFFD2Q-C3IDJsqN.js +1 -0
  59. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BOIrJ5Zb.js +1 -0
  60. package/payload/server/public/assets/clone-P8Fkz7JD.js +1 -0
  61. package/payload/server/public/assets/{dagre-BziN0Nkh.js → dagre-DyCxa9Q2.js} +1 -1
  62. package/payload/server/public/assets/{dagre-KV5264BT-B7OG1g1Y.js → dagre-KV5264BT-Cffo_hmf.js} +1 -1
  63. package/payload/server/public/assets/data-DRfwJPja.js +1 -0
  64. package/payload/server/public/assets/{diagram-5BDNPKRD-Bf31nIDs.js → diagram-5BDNPKRD-Bt93Du3V.js} +1 -1
  65. package/payload/server/public/assets/{diagram-G4DWMVQ6-DQu85hhH.js → diagram-G4DWMVQ6-v4r-tBsC.js} +1 -1
  66. package/payload/server/public/assets/{diagram-MMDJMWI5-5tstbs4y.js → diagram-MMDJMWI5-DKrVPOP-.js} +1 -1
  67. package/payload/server/public/assets/{diagram-TYMM5635--MOV1U4o.js → diagram-TYMM5635-B1PzWQb9.js} +1 -1
  68. package/payload/server/public/assets/{erDiagram-SMLLAGMA-BtAnOJmd.js → erDiagram-SMLLAGMA-ClQzsQAs.js} +1 -1
  69. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-CybiCIih.js → flowDiagram-DWJPFMVM-h3IbpN_7.js} +1 -1
  70. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-B44jN7Cy.js → ganttDiagram-T4ZO3ILL-ClaxOJi8.js} +1 -1
  71. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CT7iYcwg.js → gitGraphDiagram-UUTBAWPF-pM-U4ClD.js} +1 -1
  72. package/payload/server/public/assets/graph-B4FFYwus.js +1 -0
  73. package/payload/server/public/assets/graph-labels-Cpk9Ktt0.js +1 -0
  74. package/payload/server/public/assets/{graphlib-DP4o0pYL.js → graphlib-CwimOv_M.js} +1 -1
  75. package/payload/server/public/assets/{infoDiagram-42DDH7IO-CvxFpFHN.js → infoDiagram-42DDH7IO-DcO3Giwe.js} +1 -1
  76. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CUwWLPeR.js → ishikawaDiagram-UXIWVN3A-BcdSL5VS.js} +1 -1
  77. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-y-OnGgLi.js → journeyDiagram-VCZTEJTY-CYahHYzf.js} +1 -1
  78. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-BhrSe8R4.js → kanban-definition-6JOO6SKY-Cl5KhoS4.js} +1 -1
  79. package/payload/server/public/assets/lib--yuBd0Xi.js +33 -0
  80. package/payload/server/public/assets/{line-C9uS7z4J.js → line-d_2oTxLp.js} +1 -1
  81. package/payload/server/public/assets/{mermaid-parser.core-Cg4ZdKp-.js → mermaid-parser.core-CMMDGv3x.js} +1 -1
  82. package/payload/server/public/assets/{mermaid.core-BHdKOsex.js → mermaid.core-DK9ENGbr.js} +3 -3
  83. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-NBYiXHo7.js → mindmap-definition-QFDTVHPH-mA3x3MFG.js} +1 -1
  84. package/payload/server/public/assets/page-CgDmg_fX.js +1 -0
  85. package/payload/server/public/assets/{page-D9YpwhIu.js → page-D9lluVl7.js} +2 -2
  86. package/payload/server/public/assets/{pieDiagram-DEJITSTG-CbojC64C.js → pieDiagram-DEJITSTG-CW1nGFlY.js} +1 -1
  87. package/payload/server/public/assets/public-DdLMkNdS.js +7 -0
  88. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CnnoZXcL.js → quadrantDiagram-34T5L4WZ-DI5igJhR.js} +1 -1
  89. package/payload/server/public/assets/{requirementDiagram-MS252O5E-DFRFRtyJ.js → requirementDiagram-MS252O5E-DyVeI4e_.js} +1 -1
  90. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DU5gcpzO.js → sankeyDiagram-XADWPNL6-CCTTBYKY.js} +1 -1
  91. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BN7HZ6Hq.js → sequenceDiagram-FGHM5R23-hVVf35ly.js} +1 -1
  92. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DG6cCjvg.js → stateDiagram-FHFEXIEX-CSLuQDUX.js} +1 -1
  93. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-C5JejhQU.js +1 -0
  94. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-qmV-8SEV.js → timeline-definition-GMOUNBTQ-sxC3W8Q4.js} +1 -1
  95. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BMXQ1s3n.js → vennDiagram-DHZGUBPP-Ro-ton6Z.js} +1 -1
  96. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-CoAdP1Gw.js → wardleyDiagram-NUSXRM2D-T-dKD0Zx.js} +1 -1
  97. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CZlfGTEn.js → xychartDiagram-5P7HB3ND-CfProPts.js} +1 -1
  98. package/payload/server/public/data.html +4 -4
  99. package/payload/server/public/graph.html +5 -5
  100. package/payload/server/public/index.html +7 -7
  101. package/payload/server/public/public.html +4 -4
  102. package/payload/server/server.js +299 -1392
  103. package/payload/platform/plugins/cloudflare/scripts/__tests__/tunnel-ingress.test.ts +0 -241
  104. package/payload/platform/plugins/cloudflare/scripts/_stream-log.sh +0 -154
  105. package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.sh +0 -98
  106. package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.ts +0 -749
  107. package/payload/platform/plugins/cloudflare/scripts/reset-tunnel.sh +0 -107
  108. package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +0 -854
  109. package/payload/platform/plugins/cloudflare/scripts/tunnel-ingress.ts +0 -291
  110. package/payload/server/chunk-BDFOTLPW.js +0 -759
  111. package/payload/server/chunk-JRBCOVA4.js +0 -1305
  112. package/payload/server/cloudflare-task-tracker-M5ONAGUT.js +0 -22
  113. package/payload/server/public/assets/admin-BvzMvMGo.js +0 -217
  114. package/payload/server/public/assets/channel-DThrH4QF.js +0 -1
  115. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BC6oGTNX.js +0 -1
  116. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-COwC5Umh.js +0 -1
  117. package/payload/server/public/assets/clone-Chp7hvnA.js +0 -1
  118. package/payload/server/public/assets/data-BAgaPs4j.js +0 -1
  119. package/payload/server/public/assets/graph-Cma7EArf.js +0 -1
  120. package/payload/server/public/assets/graph-labels-DyKk6Sxf.js +0 -1
  121. package/payload/server/public/assets/lib-CpkYtEDz.js +0 -29
  122. package/payload/server/public/assets/page-B4IWl3aZ.js +0 -1
  123. package/payload/server/public/assets/public-BtOXjy3A.js +0 -8
  124. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CvVd1Q_q.js +0 -1
@@ -1,241 +0,0 @@
1
- // acceptance grid for tunnel-ingress.ts pure layer.
2
- //
3
- // Runs under `node --test --experimental-strip-types` (no compile step). No
4
- // filesystem reads except via temp files written by the test itself; no
5
- // network; no shell-outs. Mirrors apt-resolve / samba-provision style.
6
-
7
- import test from "node:test";
8
- import assert from "node:assert/strict";
9
- import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
10
- import { join } from "node:path";
11
- import { tmpdir } from "node:os";
12
- import {
13
- renderConfigYml,
14
- renderTunnelState,
15
- readPersistedHostnames,
16
- probeSambaStanza,
17
- renderAccessPolicyActionRequired,
18
- type IngressSpec,
19
- type TunnelState,
20
- } from "../tunnel-ingress.ts";
21
-
22
- // ---------------------------------------------------------------------------
23
- // renderConfigYml
24
- // ---------------------------------------------------------------------------
25
-
26
- test("renderConfigYml: HTTPS-only matches expected shape byte-for-byte", () => {
27
- const spec: IngressSpec = {
28
- tunnelId: "abc-123",
29
- credentialsPath: "/home/admin/.maxy/cloudflared/abc-123.json",
30
- httpPort: 19200,
31
- httpHostnames: ["admin.maxy.bot", "public.maxy.bot"],
32
- };
33
- assert.equal(
34
- renderConfigYml(spec),
35
- [
36
- "tunnel: abc-123",
37
- "credentials-file: /home/admin/.maxy/cloudflared/abc-123.json",
38
- "ingress:",
39
- " - hostname: admin.maxy.bot",
40
- " service: http://localhost:19200",
41
- " - hostname: public.maxy.bot",
42
- " service: http://localhost:19200",
43
- " - service: http_status:404",
44
- "",
45
- ].join("\n"),
46
- );
47
- });
48
-
49
- test("renderConfigYml: SSH+SMB appear AFTER http hostnames and BEFORE catch-all", () => {
50
- const spec: IngressSpec = {
51
- tunnelId: "t-id",
52
- credentialsPath: "/creds.json",
53
- httpPort: 19200,
54
- httpHostnames: ["admin.maxy.bot"],
55
- sshHostname: "ssh.maxy.bot",
56
- smbHostname: "smb.maxy.bot",
57
- };
58
- const out = renderConfigYml(spec);
59
- const idxHttp = out.indexOf("admin.maxy.bot");
60
- const idxSsh = out.indexOf("ssh.maxy.bot");
61
- const idxSmb = out.indexOf("smb.maxy.bot");
62
- const idx404 = out.indexOf("http_status:404");
63
- assert.ok(idxHttp < idxSsh, "HTTP before SSH");
64
- assert.ok(idxSsh < idxSmb, "SSH before SMB");
65
- assert.ok(idxSmb < idx404, "SMB before catch-all 404");
66
- assert.match(out, /\s+service: ssh:\/\/localhost:22$/m);
67
- assert.match(out, /\s+service: tcp:\/\/localhost:445$/m);
68
- });
69
-
70
- test("renderConfigYml: SSH only — no SMB block when smbHostname is null", () => {
71
- const spec: IngressSpec = {
72
- tunnelId: "t-id",
73
- credentialsPath: "/creds.json",
74
- httpPort: 19200,
75
- httpHostnames: ["admin.maxy.bot"],
76
- sshHostname: "ssh.maxy.bot",
77
- smbHostname: null,
78
- };
79
- const out = renderConfigYml(spec);
80
- assert.ok(out.includes("ssh.maxy.bot"));
81
- assert.ok(!out.includes("tcp://localhost:445"));
82
- });
83
-
84
- test("renderConfigYml: empty string hostnames are treated as absent", () => {
85
- const spec: IngressSpec = {
86
- tunnelId: "t-id",
87
- credentialsPath: "/creds.json",
88
- httpPort: 19200,
89
- httpHostnames: ["admin.maxy.bot"],
90
- sshHostname: "",
91
- smbHostname: "",
92
- };
93
- const out = renderConfigYml(spec);
94
- assert.ok(!out.includes("ssh://localhost:22"));
95
- assert.ok(!out.includes("tcp://localhost:445"));
96
- });
97
-
98
- // ---------------------------------------------------------------------------
99
- // renderTunnelState
100
- // ---------------------------------------------------------------------------
101
-
102
- test("renderTunnelState: base fields preserved when ssh/smb absent", () => {
103
- const state: TunnelState = {
104
- tunnelId: "abc",
105
- tunnelName: "maxy",
106
- domain: "admin.maxy.bot",
107
- configPath: "/home/admin/.maxy/cloudflared/config.yml",
108
- credentialsPath: "/home/admin/.maxy/cloudflared/abc.json",
109
- };
110
- const parsed = JSON.parse(renderTunnelState(state));
111
- assert.deepEqual(parsed, {
112
- tunnelId: "abc",
113
- tunnelName: "maxy",
114
- domain: "admin.maxy.bot",
115
- configPath: "/home/admin/.maxy/cloudflared/config.yml",
116
- credentialsPath: "/home/admin/.maxy/cloudflared/abc.json",
117
- });
118
- });
119
-
120
- test("renderTunnelState: ssh/smb hostnames persist when present", () => {
121
- const state: TunnelState = {
122
- tunnelId: "abc",
123
- tunnelName: "maxy",
124
- domain: "admin.maxy.bot",
125
- configPath: "/cfg.yml",
126
- credentialsPath: "/creds.json",
127
- sshHostname: "ssh.maxy.bot",
128
- smbHostname: "smb.maxy.bot",
129
- };
130
- const parsed = JSON.parse(renderTunnelState(state));
131
- assert.equal(parsed.sshHostname, "ssh.maxy.bot");
132
- assert.equal(parsed.smbHostname, "smb.maxy.bot");
133
- });
134
-
135
- // ---------------------------------------------------------------------------
136
- // readPersistedHostnames
137
- // ---------------------------------------------------------------------------
138
-
139
- test("readPersistedHostnames: returns nulls when file missing", () => {
140
- const got = readPersistedHostnames("/nope/does/not/exist.json");
141
- assert.deepEqual(got, { sshHostname: null, smbHostname: null });
142
- });
143
-
144
- test("readPersistedHostnames: round-trips ssh/smb hostnames from disk", () => {
145
- const dir = mkdtempSync(join(tmpdir(), "tunnel-state-"));
146
- const path = join(dir, "tunnel.state");
147
- try {
148
- writeFileSync(
149
- path,
150
- JSON.stringify({
151
- tunnelId: "abc",
152
- tunnelName: "maxy",
153
- domain: "admin.maxy.bot",
154
- configPath: "/cfg.yml",
155
- credentialsPath: "/creds.json",
156
- sshHostname: "ssh.maxy.bot",
157
- smbHostname: "smb.maxy.bot",
158
- }),
159
- );
160
- const got = readPersistedHostnames(path);
161
- assert.deepEqual(got, { sshHostname: "ssh.maxy.bot", smbHostname: "smb.maxy.bot" });
162
- } finally {
163
- rmSync(dir, { recursive: true, force: true });
164
- }
165
- });
166
-
167
- test("readPersistedHostnames: malformed JSON returns nulls (no throw)", () => {
168
- const dir = mkdtempSync(join(tmpdir(), "tunnel-state-bad-"));
169
- const path = join(dir, "tunnel.state");
170
- try {
171
- writeFileSync(path, "not json");
172
- assert.deepEqual(readPersistedHostnames(path), { sshHostname: null, smbHostname: null });
173
- } finally {
174
- rmSync(dir, { recursive: true, force: true });
175
- }
176
- });
177
-
178
- // ---------------------------------------------------------------------------
179
- // probeSambaStanza
180
- // ---------------------------------------------------------------------------
181
-
182
- test("probeSambaStanza: true when [<brand>] header present", () => {
183
- const dir = mkdtempSync(join(tmpdir(), "smb-probe-"));
184
- const path = join(dir, "smb.conf");
185
- try {
186
- writeFileSync(path, "[global]\nworkgroup = WORKGROUP\n\n[maxy]\n path = /home/admin\n");
187
- assert.equal(probeSambaStanza("maxy", path), true);
188
- } finally {
189
- rmSync(dir, { recursive: true, force: true });
190
- }
191
- });
192
-
193
- test("probeSambaStanza: false when only peer brand present (isolation)", () => {
194
- const dir = mkdtempSync(join(tmpdir(), "smb-probe-peer-"));
195
- const path = join(dir, "smb.conf");
196
- try {
197
- writeFileSync(path, "[global]\n\n[realagent]\n path = /home/admin\n");
198
- assert.equal(probeSambaStanza("maxy", path), false);
199
- } finally {
200
- rmSync(dir, { recursive: true, force: true });
201
- }
202
- });
203
-
204
- test("probeSambaStanza: false when smb.conf missing", () => {
205
- assert.equal(probeSambaStanza("maxy", "/nope/smb.conf"), false);
206
- });
207
-
208
- test("probeSambaStanza: brand names with regex meta characters are escaped", () => {
209
- const dir = mkdtempSync(join(tmpdir(), "smb-probe-regex-"));
210
- const path = join(dir, "smb.conf");
211
- try {
212
- writeFileSync(path, "[some.brand]\n path = /x\n");
213
- assert.equal(probeSambaStanza("some.brand", path), true);
214
- assert.equal(probeSambaStanza("someXbrand", path), false);
215
- } finally {
216
- rmSync(dir, { recursive: true, force: true });
217
- }
218
- });
219
-
220
- // ---------------------------------------------------------------------------
221
- // renderAccessPolicyActionRequired
222
- // ---------------------------------------------------------------------------
223
-
224
- test("renderAccessPolicyActionRequired: empty string when neither hostname set", () => {
225
- assert.equal(
226
- renderAccessPolicyActionRequired({ operatorEmail: "x@y.com" }),
227
- "",
228
- );
229
- });
230
-
231
- test("renderAccessPolicyActionRequired: lists each hostname with operator email", () => {
232
- const out = renderAccessPolicyActionRequired({
233
- sshHostname: "ssh.maxy.bot",
234
- smbHostname: "smb.maxy.bot",
235
- operatorEmail: "joel@example.com",
236
- });
237
- assert.match(out, /SSH: ssh\.maxy\.bot/);
238
- assert.match(out, /SMB: smb\.maxy\.bot/);
239
- assert.match(out, /Emails: joel@example\.com/);
240
- assert.match(out, /Zero Trust → Access → Applications/);
241
- });
@@ -1,154 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Shared stream-log helpers sourced by setup-tunnel.sh and reset-tunnel.sh.
3
- #
4
- # The platform exposes STREAM_LOG_PATH in the `claude` spawn env;
5
- # the Bash-tool subprocess inherits it, and opt-in scripts call these
6
- # helpers to emit phase lines and tee subprocess output into the same
7
- # per-conversation file the chat UI's server-side tailer reads.
8
- #
9
- # Contract (read by platform/ui/app/lib/script-stream-tailer.ts tailer and
10
- # .docs/web-chat.md):
11
- # [<ISO-ts>] [script:<scope>] <kv …>
12
- # [<ISO-ts>] [script:<scope>:<subprocess-tag>] <raw line>
13
- # Canonical regex — SCRIPT_STREAM_RE at platform/ui/app/lib/script-stream-tailer.ts:
14
- # ^\[([^\]]+)\] \[script:([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$
15
- # The literal `script:` prefix is the chat-surface namespace. It
16
- # separates chat-surface emitters from log-file-only scopes (`[mcp:<server>]`
17
- # from mcp-stderr-tee, `[init]` / `[api-wait-*]` / `[mcp-init-error]` /
18
- # `[subproc-stderr]` / `[compaction-*]` from claude-agent.ts) which share
19
- # the same per-conversation stream log but must NOT reach the chat UI.
20
- # Callers of phase_line / tee_subprocess / tee_subprocess_capture pass the
21
- # bare scope (e.g. `setup-tunnel`, `list-cf-domains`, `reset-tunnel:cloudflared`);
22
- # these helpers prepend `script:` to the stream-log write. Stderr mirrors
23
- # stay unprefixed — stderr is for direct-SSH operators and route-handler
24
- # `reason=<enum>` regex, not the chat-surface contract.
25
- # <scope> is any `[a-z][a-z0-9-]*` token
26
- # pre-592 enum `setup-tunnel|reset-tunnel`, which silently filtered out the
27
- # `[list-cf-domains]` lines). <subprocess-tag> may contain
28
- # lowercase, digits, `-`, `_`, `:`. Adding a new chat-surface scope requires
29
- # no edit to the regex — the shape is the only contract. Any prefix-shape
30
- # change must be made on both sides (this helper + script-stream-tailer.ts)
31
- # atomically.
32
- #
33
- # Inner layers (e.g. a node/python helper a.sh wrapper spawns — the
34
- # `list-cf-domains.sh` → `list-cf-domains.ts` pattern) must write phase lines
35
- # directly to STREAM_LOG_PATH with the same prefix shape (including the
36
- # `script:` namespace): stderr alone is silently discarded by runFormSpawn on
37
- # exit 0. The build-gate `platform/ui/scripts/check-stream-log-contract.mjs`
38
- # enforces this by rejecting any.sh under `platform/plugins/*/scripts/`
39
- # that sources this helper and invokes an interpreter subprocess whose target
40
- # does not pair a STREAM_LOG_PATH env read with an append/write call. Opt out
41
- # per invocation with `# stream-log-contract: stderr-only (reason: <prose>)`.
42
-
43
- # Exit 1 loudly with the variable name and the invoking scope so direct-SSH
44
- # invocations fail fast and the operator reads exactly what to set. No
45
- # silent fallback — criterion 4 of.
46
- require_stream_log_path() {
47
- local scope="$1"
48
- if [ -z "${STREAM_LOG_PATH:-}" ]; then
49
- echo "ERROR [${scope}]: STREAM_LOG_PATH environment variable is unset." >&2
50
- echo " This script tees subprocess output into a per-conversation" >&2
51
- echo " stream log; it is meaningless without a target path." >&2
52
- echo " The platform sets STREAM_LOG_PATH automatically for every" >&2
53
- echo " \`claude\` spawn. If you are invoking this" >&2
54
- echo " script by hand, export it yourself, for example:" >&2
55
- echo " export STREAM_LOG_PATH=\"\${HOME}/.maxy/logs/manual-invocation.log\"" >&2
56
- exit 1
57
- fi
58
- mkdir -p "$(dirname "${STREAM_LOG_PATH}")"
59
- }
60
-
61
- # ISO-8601 UTC timestamp with millisecond precision.
62
- stream_log_ts() {
63
- date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"
64
- }
65
-
66
- # Append one phase line to STREAM_LOG_PATH AND echo to stderr so the Bash
67
- # tool's stderr capture carries it. Both paths matter: the stream log is
68
- # the live tailer surface; stderr is the exit-time Bash-tool surface.
69
- #
70
- # The stream-log write carries the `script:` chat-surface namespace;
71
- # the stderr mirror stays unprefixed because stderr is read by direct-SSH
72
- # operators and the route-handler `reason=<enum>` regex, not the chat UI.
73
- #
74
- # Usage: phase_line <scope> <key=value …>
75
- phase_line() {
76
- local scope="$1"; shift
77
- local ts
78
- ts="$(stream_log_ts)"
79
- printf '[%s] [script:%s] %s\n' "${ts}" "${scope}" "$*" >> "${STREAM_LOG_PATH}"
80
- printf '[%s] %s\n' "${scope}" "$*" >&2
81
- }
82
-
83
- # Tee a subprocess's combined stdout+stderr into STREAM_LOG_PATH line-by-line
84
- # with the given tag, mirroring each line to stderr for the Bash tool.
85
- # Returns the subprocess's exit code.
86
- #
87
- # Use for fire-and-forget subprocesses whose stdout does NOT need to be
88
- # captured by the caller (the caller only cares about the exit code).
89
- # For subprocesses whose stdout the caller must parse (e.g. JSON output
90
- # feeding `jq`), use `tee_subprocess_capture` instead.
91
- #
92
- # Usage: tee_subprocess <tag> -- <cmd> <args …>
93
- # Example: tee_subprocess reset-tunnel:cloudflared -- cloudflared --origincert … tunnel delete my-tunnel
94
- #
95
- # stdbuf forces line buffering; without it cloudflared holds its stdout in
96
- # libc's 8 KB buffer until exit and the ≤ 1 s latency criterion fails.
97
- tee_subprocess() {
98
- local tag="$1"; shift
99
- if [ "${1:-}" != "--" ]; then
100
- echo "tee_subprocess: expected -- before command" >&2
101
- return 2
102
- fi
103
- shift
104
- # Prefer `stdbuf -oL -eL` to force line buffering on the subprocess; fall
105
- # back to the bare command when stdbuf is unavailable (macOS dev). On the
106
- # Pi (Linux), stdbuf is present via coreutils and cloudflared's output
107
- # reaches the tee as soon as each line is written.
108
- local -a buf_prefix=()
109
- if command -v stdbuf >/dev/null 2>&1; then
110
- buf_prefix=(stdbuf -oL -eL)
111
- fi
112
- "${buf_prefix[@]}" "$@" 2>&1 | while IFS= read -r line; do
113
- local ts
114
- ts="$(stream_log_ts)"
115
- printf '[%s] [script:%s] %s\n' "${ts}" "${tag}" "${line}" >> "${STREAM_LOG_PATH}"
116
- printf '%s\n' "${line}" >&2
117
- done
118
- return "${PIPESTATUS[0]}"
119
- }
120
-
121
- # Tee a subprocess's combined stdout+stderr into STREAM_LOG_PATH line-by-line
122
- # with the given tag, passing each line through on stdout so the caller can
123
- # capture it with `> file` or `$( … )`. Stderr mirroring is dropped so the
124
- # caller's stdout is exactly the subprocess's output — `jq` and friends
125
- # remain parseable.
126
- #
127
- # Usage: tee_subprocess_capture <tag> -- <cmd> <args …> > captured.file
128
- # Example:
129
- # tee_subprocess_capture reset-tunnel:cloudflared -- \
130
- # cloudflared --origincert "${CERT}" tunnel list --output json \
131
- # > "${NAMES_TMP}"
132
- tee_subprocess_capture() {
133
- local tag="$1"; shift
134
- if [ "${1:-}" != "--" ]; then
135
- echo "tee_subprocess_capture: expected -- before command" >&2
136
- return 2
137
- fi
138
- shift
139
- # Prefer `stdbuf -oL -eL` to force line buffering on the subprocess; fall
140
- # back to the bare command when stdbuf is unavailable (macOS dev). On the
141
- # Pi (Linux), stdbuf is present via coreutils and cloudflared's output
142
- # reaches the tee as soon as each line is written.
143
- local -a buf_prefix=()
144
- if command -v stdbuf >/dev/null 2>&1; then
145
- buf_prefix=(stdbuf -oL -eL)
146
- fi
147
- "${buf_prefix[@]}" "$@" 2>&1 | while IFS= read -r line; do
148
- local ts
149
- ts="$(stream_log_ts)"
150
- printf '[%s] [script:%s] %s\n' "${ts}" "${tag}" "${line}" >> "${STREAM_LOG_PATH}"
151
- printf '%s\n' "${line}"
152
- done
153
- return "${PIPESTATUS[0]}"
154
- }
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Deterministic scrape of the operator's Cloudflare dashboard to discover
3
- # the domains on the logged-in account. Output on stdout is a JSON `string[]`,
4
- # sorted and deduped. Every failure exits non-zero with a `reason=<enum>`
5
- # token on stderr tagged `[list-cf-domains]`.
6
- #
7
- # Usage:
8
- # list-cf-domains.sh <brand>
9
- #
10
- # brand arg is REQUIRED — the prior silent default (string
11
- # fallback to "maxy" when $1 was empty) made every non-Maxy brand's CF
12
- # setup form fail on first use because the wrapper's child node helper
13
- # read the Maxy CDP port from a hardcoded fallback. Missing arg now exits
14
- # 1 with `phase=error reason=brand-arg-missing`.
15
- #
16
- # The wrapper also resolves and exports MAXY_PLATFORM_ROOT from its own
17
- # script location so the .ts helper can read `<root>/config/brand.json` for
18
- # the brand's `cdpPort` (the source of truth). Direct-SSH invocation
19
- # works because the script lives at `<install>/platform/plugins/cloudflare/
20
- # scripts/`, making platform root three dirs up from the resolved script.
21
- #
22
- # Runtime: Node 22's `--experimental-strip-types` runs the .ts helper
23
- # directly, so no tsx / playwright / ws dependency exists.
24
-
25
- set -euo pipefail
26
-
27
- # --------------------------------------------------------------------------
28
- # Shared stream-log helpers (require STREAM_LOG_PATH, phase_line, …).
29
- # --------------------------------------------------------------------------
30
-
31
- # shellcheck source=_stream-log.sh
32
- # Resolve symlinks before dirname — ~/list-cf-domains.sh is installed as a
33
- # symlink into $HOME, so the raw BASH_SOURCE[0] points at $HOME, not the
34
- # scripts directory where _stream-log.sh lives.
35
- SCRIPT_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
36
- source "${SCRIPT_DIR}/_stream-log.sh"
37
- require_stream_log_path list-cf-domains
38
-
39
- if [ "$#" -lt 1 ] || [ -z "${1:-}" ]; then
40
- phase_line list-cf-domains phase=error reason=brand-arg-missing
41
- exit 1
42
- fi
43
-
44
- BRAND="${1}"
45
- CONFIG_DIR=".${BRAND}"
46
- mkdir -p "${HOME}/${CONFIG_DIR}/logs"
47
-
48
- # MAXY_PLATFORM_ROOT is set by the systemd service when the admin server
49
- # spawns this script via runFormSpawn. Direct-SSH invocation has no such env
50
- # so derive it from the resolved script location: scripts/ → cloudflare/ →
51
- # plugins/ → platform → install root (three dirs up).
52
- if [ -z "${MAXY_PLATFORM_ROOT:-}" ]; then
53
- MAXY_PLATFORM_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
54
- fi
55
-
56
- phase_line list-cf-domains phase=script-start brand="${BRAND}" config_dir="${CONFIG_DIR}"
57
-
58
- TS_ENTRY="${SCRIPT_DIR}/list-cf-domains.ts"
59
-
60
- if [ ! -f "${TS_ENTRY}" ]; then
61
- phase_line list-cf-domains phase=error reason=entry-missing path="${TS_ENTRY}"
62
- exit 1
63
- fi
64
-
65
- # 30s hard ceiling. The node helper has its own per-phase budgets totalling
66
- # ~27s; the wrapper's extra 3s absorbs process startup and CF network jitter.
67
- # Use `timeout --preserve-status` so the exit code of a timeout is distinct
68
- # (124) from a helper-reported failure (1).
69
- HARD_TIMEOUT_SECS=30
70
-
71
- # Env passed through:
72
- # - CONFIG_DIR — consumed by the node helper when writing selector-drift
73
- # dumps to ~/.${BRAND}/logs/list-cf-domains-<ts>.html.
74
- # - BRAND — names the brand for log lines naming the brand on
75
- # config-class failures (helper does not derive it from
76
- # CONFIG_DIR to keep the two concerns independent).
77
- # - MAXY_PLATFORM_ROOT — install root the helper reads `config/brand.json`
78
- # from for `cdpPort`. Resolved above so direct-SSH
79
- # invocation matches the systemd-spawned path.
80
- #
81
- # `node --experimental-strip-types` (stable in Node 22.22) runs the .ts file
82
- # natively: type annotations are stripped, no enums / decorators are used, so
83
- # no TS type-transform is needed. `--no-warnings` suppresses the experimental
84
- # banner which would otherwise leak into stderr and confuse the route parser.
85
- set +e
86
- CONFIG_DIR="${CONFIG_DIR}" BRAND="${BRAND}" MAXY_PLATFORM_ROOT="${MAXY_PLATFORM_ROOT}" \
87
- timeout --preserve-status --signal=TERM "${HARD_TIMEOUT_SECS}" \
88
- node --experimental-strip-types --no-warnings "${TS_ENTRY}"
89
- EXIT_CODE=$?
90
- set -e
91
-
92
- if [ "${EXIT_CODE}" -eq 124 ]; then
93
- phase_line list-cf-domains phase=script-exit code=124 reason=wrapper-timeout budget_secs="${HARD_TIMEOUT_SECS}"
94
- exit 1
95
- fi
96
-
97
- phase_line list-cf-domains phase=script-exit code="${EXIT_CODE}"
98
- exit "${EXIT_CODE}"