@rubytech/create-maxy 1.0.633 → 1.0.635
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +6 -0
- package/package.json +1 -1
- package/payload/platform/lib/graph-mcp/dist/index.js +98 -8
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-mcp/src/index.ts +115 -12
- package/payload/platform/plugins/admin/mcp/dist/index.js +6 -7
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +7 -1
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +130 -28
- package/payload/platform/plugins/docs/references/cloudflare.md +3 -2
- package/payload/platform/plugins/docs/references/plugins-guide.md +8 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +15 -0
- package/payload/server/public/assets/{admin-Cacbe9nP.js → admin-BTjragNx.js} +41 -41
- package/payload/server/public/assets/public-Lwkux_Q4.js +5 -0
- package/payload/server/public/assets/useVoiceRecorder-Cbo-LQPY.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-xC0cS-GO.js → useVoiceRecorder-D4y_EM_A.js} +2 -2
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +47 -17
- package/payload/server/public/assets/public-CBVU1ErT.js +0 -5
- package/payload/server/public/assets/useVoiceRecorder-baV6L6a0.css +0 -1
package/dist/index.js
CHANGED
|
@@ -1578,6 +1578,7 @@ Environment=KEEP_ALIVE_TIMEOUT=61000
|
|
|
1578
1578
|
Environment=DISPLAY=:99
|
|
1579
1579
|
Environment=PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium
|
|
1580
1580
|
Environment=MAXY_PLATFORM_ROOT=${INSTALL_DIR}/platform
|
|
1581
|
+
Environment=PATH=%h/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
|
1581
1582
|
EnvironmentFile=-${persistDir}/.env
|
|
1582
1583
|
StandardOutput=append:${persistDir}/logs/server.log
|
|
1583
1584
|
StandardError=append:${persistDir}/logs/server.log
|
|
@@ -1586,6 +1587,11 @@ StandardError=append:${persistDir}/logs/server.log
|
|
|
1586
1587
|
WantedBy=default.target
|
|
1587
1588
|
`;
|
|
1588
1589
|
writeFileSync(join(serviceDir, BRAND.serviceName), serviceFile);
|
|
1590
|
+
// Task 560: the unit declares Environment=PATH=%h/.local/bin:... so the graph
|
|
1591
|
+
// MCP shim's spawn("uvx", ...) resolves against uv's install location. Without
|
|
1592
|
+
// this line, the service inherits the default systemd-user PATH — which
|
|
1593
|
+
// excludes ~/.local/bin — and the shim exits ENOENT before any query lands.
|
|
1594
|
+
logFile(` ${BRAND.serviceName}: PATH env includes %h/.local/bin for uvx resolution`);
|
|
1589
1595
|
// WiFi AP provisioning service — system-level (requires root for hostapd/dnsmasq).
|
|
1590
1596
|
// Runs at boot to check connectivity. If no saved WiFi and no ethernet, activates
|
|
1591
1597
|
// a temporary AP with a captive portal for first-time WiFi configuration.
|
package/package.json
CHANGED
|
@@ -32,6 +32,46 @@ const index_js_1 = require("../../mcp-stderr-tee/dist/index.js");
|
|
|
32
32
|
const SERVER_NAME = "graph";
|
|
33
33
|
const UPSTREAM_PACKAGE = "mcp-neo4j-cypher@0.6.0";
|
|
34
34
|
(0, index_js_1.initStderrTee)(SERVER_NAME);
|
|
35
|
+
// Sync-write failure line to the per-server raw file AND the per-conversation
|
|
36
|
+
// stream log. Used only on exit paths (missing password, uvx unresolvable,
|
|
37
|
+
// upstream spawn/exit error) where the async stderr tee's createWriteStream
|
|
38
|
+
// buffer will not flush before process.exit() outraces it. `appendFileSync`
|
|
39
|
+
// returns after the kernel has queued the write, so the line survives an
|
|
40
|
+
// immediate exit. Each destination is wrapped independently — an unwritable
|
|
41
|
+
// destination must not mask the primary failure. The final process.stderr.write
|
|
42
|
+
// goes through the patched writer installed by initStderrTee; any async tee
|
|
43
|
+
// writes it schedules are discarded by the subsequent process.exit, but its
|
|
44
|
+
// inline write to the original stderr still reaches Claude Code's consumer.
|
|
45
|
+
function syncEmit(line) {
|
|
46
|
+
const msg = `[graph-mcp] ${line}`;
|
|
47
|
+
const iso = new Date().toISOString();
|
|
48
|
+
const logDir = process.env.LOG_DIR;
|
|
49
|
+
if (logDir) {
|
|
50
|
+
try {
|
|
51
|
+
(0, node_fs_1.mkdirSync)(logDir, { recursive: true });
|
|
52
|
+
const date = iso.slice(0, 10);
|
|
53
|
+
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(logDir, `mcp-${SERVER_NAME}-stderr-${date}.log`), `${msg}\n`);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* unwritable destination — preserve primary failure */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const streamLogPath = process.env.STREAM_LOG_PATH;
|
|
60
|
+
if (streamLogPath) {
|
|
61
|
+
try {
|
|
62
|
+
(0, node_fs_1.appendFileSync)(streamLogPath, `[${iso}] [mcp:${SERVER_NAME}] ${msg}\n`);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* unwritable destination — preserve primary failure */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
process.stderr.write(`${msg}\n`);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* stderr closed — nothing else to do */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
35
75
|
function resolvePassword() {
|
|
36
76
|
if (process.env.NEO4J_PASSWORD)
|
|
37
77
|
return process.env.NEO4J_PASSWORD;
|
|
@@ -43,10 +83,57 @@ function resolvePassword() {
|
|
|
43
83
|
return (0, node_fs_1.readFileSync)(file, "utf-8").trim();
|
|
44
84
|
}
|
|
45
85
|
catch {
|
|
46
|
-
|
|
86
|
+
syncEmit(`password unavailable — file=${file} env=NEO4J_PASSWORD=unset`);
|
|
47
87
|
process.exit(1);
|
|
48
88
|
}
|
|
49
89
|
}
|
|
90
|
+
function resolveUvxPath() {
|
|
91
|
+
const envPath = process.env.UVX_PATH ?? "";
|
|
92
|
+
if (envPath) {
|
|
93
|
+
try {
|
|
94
|
+
(0, node_fs_1.accessSync)(envPath, node_fs_1.constants.X_OK);
|
|
95
|
+
return { path: envPath, source: "env", envChecked: envPath, homeLocalChecked: "skipped", whichChecked: "skipped" };
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* fall through */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const home = process.env.HOME ?? "";
|
|
102
|
+
const homeLocalPath = home ? (0, node_path_1.resolve)(home, ".local/bin/uvx") : "";
|
|
103
|
+
if (homeLocalPath) {
|
|
104
|
+
try {
|
|
105
|
+
const stats = (0, node_fs_1.statSync)(homeLocalPath);
|
|
106
|
+
if (stats.isFile()) {
|
|
107
|
+
(0, node_fs_1.accessSync)(homeLocalPath, node_fs_1.constants.X_OK);
|
|
108
|
+
return { path: homeLocalPath, source: "home-local", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath, whichChecked: "skipped" };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* fall through */
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
let whichPath = "";
|
|
116
|
+
try {
|
|
117
|
+
whichPath = (0, node_child_process_1.execFileSync)("which", ["uvx"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
whichPath = "";
|
|
121
|
+
}
|
|
122
|
+
if (whichPath) {
|
|
123
|
+
// Verify the result is still executable. `which` prints the first PATH
|
|
124
|
+
// hit by name; if the target was removed or the symlink dangles, the
|
|
125
|
+
// spawn would ENOENT and reintroduce the silent-fail class Task 560
|
|
126
|
+
// closes.
|
|
127
|
+
try {
|
|
128
|
+
(0, node_fs_1.accessSync)(whichPath, node_fs_1.constants.X_OK);
|
|
129
|
+
return { path: whichPath, source: "which", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: whichPath };
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: `${whichPath} (not-executable)` };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: "not-found" };
|
|
136
|
+
}
|
|
50
137
|
const brand = process.env.BRAND ?? "maxy";
|
|
51
138
|
const neo4jUri = process.env.NEO4J_URI ?? "bolt://localhost:7687";
|
|
52
139
|
const neo4jUser = process.env.NEO4J_USERNAME ?? process.env.NEO4J_USER ?? "neo4j";
|
|
@@ -68,7 +155,14 @@ const childEnv = {
|
|
|
68
155
|
};
|
|
69
156
|
console.error(`[graph-mcp] boot brand=${brand} uri=${neo4jUri} user=${neo4jUser} ` +
|
|
70
157
|
`namespace=${namespace} readOnly=${readOnly} tokenLimit=${responseTokenLimit}`);
|
|
71
|
-
const
|
|
158
|
+
const uvx = resolveUvxPath();
|
|
159
|
+
if (!uvx.path) {
|
|
160
|
+
syncEmit(`uvx unresolvable — checked env=${uvx.envChecked} home-local=${uvx.homeLocalChecked} which=${uvx.whichChecked}`);
|
|
161
|
+
syncEmit("install uv (if missing): curl -LsSf https://astral.sh/uv/install.sh | sh");
|
|
162
|
+
process.exit(127);
|
|
163
|
+
}
|
|
164
|
+
console.error(`[graph-mcp] uvx resolved path=${uvx.path} source=${uvx.source}`);
|
|
165
|
+
const child = (0, node_child_process_1.spawn)(uvx.path, [UPSTREAM_PACKAGE, "--transport", "stdio"], {
|
|
72
166
|
stdio: ["pipe", "pipe", "pipe"],
|
|
73
167
|
env: childEnv,
|
|
74
168
|
});
|
|
@@ -77,15 +171,11 @@ child.on("spawn", () => {
|
|
|
77
171
|
});
|
|
78
172
|
child.on("error", (err) => {
|
|
79
173
|
const msg = err instanceof Error ? err.message : String(err);
|
|
80
|
-
|
|
81
|
-
if (err.code === "ENOENT") {
|
|
82
|
-
console.error("[graph-mcp] uvx not found on PATH — install uv: " +
|
|
83
|
-
"curl -LsSf https://astral.sh/uv/install.sh | sh -s -- -y");
|
|
84
|
-
}
|
|
174
|
+
syncEmit(`spawn error: ${msg} uvx=${uvx.path ?? "unresolved"}`);
|
|
85
175
|
process.exit(127);
|
|
86
176
|
});
|
|
87
177
|
child.on("exit", (code, signal) => {
|
|
88
|
-
|
|
178
|
+
syncEmit(`uvx exited code=${code ?? "null"} signal=${signal ?? "null"}`);
|
|
89
179
|
process.exit(code ?? (signal ? 1 : 0));
|
|
90
180
|
});
|
|
91
181
|
// Forward child stderr to our own stderr so the stderr tee picks it up.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;AAEH,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* own password. This shim trusts that — no query-layer tenant filtering.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { spawn } from "node:child_process";
|
|
27
|
-
import { readFileSync } from "node:fs";
|
|
26
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
27
|
+
import { accessSync, appendFileSync, constants, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
28
28
|
import { resolve } from "node:path";
|
|
29
29
|
import { StringDecoder } from "node:string_decoder";
|
|
30
30
|
import { initStderrTee } from "../../mcp-stderr-tee/dist/index.js";
|
|
@@ -34,6 +34,44 @@ const UPSTREAM_PACKAGE = "mcp-neo4j-cypher@0.6.0";
|
|
|
34
34
|
|
|
35
35
|
initStderrTee(SERVER_NAME);
|
|
36
36
|
|
|
37
|
+
// Sync-write failure line to the per-server raw file AND the per-conversation
|
|
38
|
+
// stream log. Used only on exit paths (missing password, uvx unresolvable,
|
|
39
|
+
// upstream spawn/exit error) where the async stderr tee's createWriteStream
|
|
40
|
+
// buffer will not flush before process.exit() outraces it. `appendFileSync`
|
|
41
|
+
// returns after the kernel has queued the write, so the line survives an
|
|
42
|
+
// immediate exit. Each destination is wrapped independently — an unwritable
|
|
43
|
+
// destination must not mask the primary failure. The final process.stderr.write
|
|
44
|
+
// goes through the patched writer installed by initStderrTee; any async tee
|
|
45
|
+
// writes it schedules are discarded by the subsequent process.exit, but its
|
|
46
|
+
// inline write to the original stderr still reaches Claude Code's consumer.
|
|
47
|
+
function syncEmit(line: string): void {
|
|
48
|
+
const msg = `[graph-mcp] ${line}`;
|
|
49
|
+
const iso = new Date().toISOString();
|
|
50
|
+
const logDir = process.env.LOG_DIR;
|
|
51
|
+
if (logDir) {
|
|
52
|
+
try {
|
|
53
|
+
mkdirSync(logDir, { recursive: true });
|
|
54
|
+
const date = iso.slice(0, 10);
|
|
55
|
+
appendFileSync(resolve(logDir, `mcp-${SERVER_NAME}-stderr-${date}.log`), `${msg}\n`);
|
|
56
|
+
} catch {
|
|
57
|
+
/* unwritable destination — preserve primary failure */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const streamLogPath = process.env.STREAM_LOG_PATH;
|
|
61
|
+
if (streamLogPath) {
|
|
62
|
+
try {
|
|
63
|
+
appendFileSync(streamLogPath, `[${iso}] [mcp:${SERVER_NAME}] ${msg}\n`);
|
|
64
|
+
} catch {
|
|
65
|
+
/* unwritable destination — preserve primary failure */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
process.stderr.write(`${msg}\n`);
|
|
70
|
+
} catch {
|
|
71
|
+
/* stderr closed — nothing else to do */
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
37
75
|
function resolvePassword(): string {
|
|
38
76
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
39
77
|
// __dirname points at `lib/graph-mcp/dist` after compilation; the platform
|
|
@@ -43,11 +81,70 @@ function resolvePassword(): string {
|
|
|
43
81
|
try {
|
|
44
82
|
return readFileSync(file, "utf-8").trim();
|
|
45
83
|
} catch {
|
|
46
|
-
|
|
84
|
+
syncEmit(`password unavailable — file=${file} env=NEO4J_PASSWORD=unset`);
|
|
47
85
|
process.exit(1);
|
|
48
86
|
}
|
|
49
87
|
}
|
|
50
88
|
|
|
89
|
+
// Resolve `uvx` to an absolute path at boot so the systemd-user default PATH
|
|
90
|
+
// (which excludes ~/.local/bin where the installer's installUv drops uvx)
|
|
91
|
+
// does not ENOENT the spawn. Precedence: UVX_PATH env override > ~/.local/bin/uvx
|
|
92
|
+
// (direct filesystem check, skips PATH lookup) > `which uvx` (catches custom
|
|
93
|
+
// installs). Each source is recorded in the diagnostic log line so a later
|
|
94
|
+
// "uvx unresolvable" emission names every checked location, not just a bare
|
|
95
|
+
// "not found".
|
|
96
|
+
interface UvxResolution {
|
|
97
|
+
path: string | null;
|
|
98
|
+
source: "env" | "home-local" | "which" | null;
|
|
99
|
+
envChecked: string;
|
|
100
|
+
homeLocalChecked: string;
|
|
101
|
+
whichChecked: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function resolveUvxPath(): UvxResolution {
|
|
105
|
+
const envPath = process.env.UVX_PATH ?? "";
|
|
106
|
+
if (envPath) {
|
|
107
|
+
try {
|
|
108
|
+
accessSync(envPath, constants.X_OK);
|
|
109
|
+
return { path: envPath, source: "env", envChecked: envPath, homeLocalChecked: "skipped", whichChecked: "skipped" };
|
|
110
|
+
} catch {
|
|
111
|
+
/* fall through */
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const home = process.env.HOME ?? "";
|
|
115
|
+
const homeLocalPath = home ? resolve(home, ".local/bin/uvx") : "";
|
|
116
|
+
if (homeLocalPath) {
|
|
117
|
+
try {
|
|
118
|
+
const stats = statSync(homeLocalPath);
|
|
119
|
+
if (stats.isFile()) {
|
|
120
|
+
accessSync(homeLocalPath, constants.X_OK);
|
|
121
|
+
return { path: homeLocalPath, source: "home-local", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath, whichChecked: "skipped" };
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
/* fall through */
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
let whichPath = "";
|
|
128
|
+
try {
|
|
129
|
+
whichPath = execFileSync("which", ["uvx"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
130
|
+
} catch {
|
|
131
|
+
whichPath = "";
|
|
132
|
+
}
|
|
133
|
+
if (whichPath) {
|
|
134
|
+
// Verify the result is still executable. `which` prints the first PATH
|
|
135
|
+
// hit by name; if the target was removed or the symlink dangles, the
|
|
136
|
+
// spawn would ENOENT and reintroduce the silent-fail class Task 560
|
|
137
|
+
// closes.
|
|
138
|
+
try {
|
|
139
|
+
accessSync(whichPath, constants.X_OK);
|
|
140
|
+
return { path: whichPath, source: "which", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: whichPath };
|
|
141
|
+
} catch {
|
|
142
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: `${whichPath} (not-executable)` };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: "not-found" };
|
|
146
|
+
}
|
|
147
|
+
|
|
51
148
|
const brand = process.env.BRAND ?? "maxy";
|
|
52
149
|
const neo4jUri = process.env.NEO4J_URI ?? "bolt://localhost:7687";
|
|
53
150
|
const neo4jUser = process.env.NEO4J_USERNAME ?? process.env.NEO4J_USER ?? "neo4j";
|
|
@@ -74,7 +171,19 @@ console.error(
|
|
|
74
171
|
`namespace=${namespace} readOnly=${readOnly} tokenLimit=${responseTokenLimit}`,
|
|
75
172
|
);
|
|
76
173
|
|
|
77
|
-
const
|
|
174
|
+
const uvx = resolveUvxPath();
|
|
175
|
+
if (!uvx.path) {
|
|
176
|
+
syncEmit(
|
|
177
|
+
`uvx unresolvable — checked env=${uvx.envChecked} home-local=${uvx.homeLocalChecked} which=${uvx.whichChecked}`,
|
|
178
|
+
);
|
|
179
|
+
syncEmit(
|
|
180
|
+
"install uv (if missing): curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
181
|
+
);
|
|
182
|
+
process.exit(127);
|
|
183
|
+
}
|
|
184
|
+
console.error(`[graph-mcp] uvx resolved path=${uvx.path} source=${uvx.source}`);
|
|
185
|
+
|
|
186
|
+
const child = spawn(uvx.path, [UPSTREAM_PACKAGE, "--transport", "stdio"], {
|
|
78
187
|
stdio: ["pipe", "pipe", "pipe"],
|
|
79
188
|
env: childEnv,
|
|
80
189
|
});
|
|
@@ -85,18 +194,12 @@ child.on("spawn", () => {
|
|
|
85
194
|
|
|
86
195
|
child.on("error", (err) => {
|
|
87
196
|
const msg = err instanceof Error ? err.message : String(err);
|
|
88
|
-
|
|
89
|
-
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
90
|
-
console.error(
|
|
91
|
-
"[graph-mcp] uvx not found on PATH — install uv: " +
|
|
92
|
-
"curl -LsSf https://astral.sh/uv/install.sh | sh -s -- -y",
|
|
93
|
-
);
|
|
94
|
-
}
|
|
197
|
+
syncEmit(`spawn error: ${msg} uvx=${uvx.path ?? "unresolved"}`);
|
|
95
198
|
process.exit(127);
|
|
96
199
|
});
|
|
97
200
|
|
|
98
201
|
child.on("exit", (code, signal) => {
|
|
99
|
-
|
|
202
|
+
syncEmit(`uvx exited code=${code ?? "null"} signal=${signal ?? "null"}`);
|
|
100
203
|
process.exit(code ?? (signal ? 1 : 0));
|
|
101
204
|
});
|
|
102
205
|
|
|
@@ -1235,16 +1235,15 @@ server.tool("session-resume", "Resume a previous session. Loads the selected ses
|
|
|
1235
1235
|
"and routes new messages to that conversation. Call after the user selects a session from the " +
|
|
1236
1236
|
"session-list results. Pass the conversationId from the selected session.", { conversationId: z.string().uuid().describe("The conversationId of the session to resume") }, async () => ({ content: [{ type: "text", text: "resumed" }] }));
|
|
1237
1237
|
server.tool("remote-auth-set-password", "Set the remote access password. Hashes with scrypt and writes to the platform's brand-specific config directory with mode 0600. " +
|
|
1238
|
-
"Validates strength (8+ chars, digit, special character, no
|
|
1238
|
+
"Validates strength (8+ chars, digit, special character, no spaces anywhere). " +
|
|
1239
1239
|
"Protects the admin interface when exposed over the tunnel — has no effect on the public endpoint or the tunnel itself. " +
|
|
1240
1240
|
"Set before `setup-tunnel.sh` — the script's post-restart verification curls the admin hostname and fails if remote-auth is not configured.", { password: z.string() }, async ({ password }) => {
|
|
1241
|
-
const trimmed = password.trim();
|
|
1242
1241
|
// Validate strength — same rules as the web server's validatePasswordStrength
|
|
1243
1242
|
const checks = [
|
|
1244
|
-
{ label: "at least 8 characters", met:
|
|
1245
|
-
{ label: "contains a number", met: /\d/.test(
|
|
1246
|
-
{ label: "contains a special character", met: /[^A-Za-z0-9]/.test(
|
|
1247
|
-
{ label: "no
|
|
1243
|
+
{ label: "at least 8 characters", met: password.length >= 8 },
|
|
1244
|
+
{ label: "contains a number", met: /\d/.test(password) },
|
|
1245
|
+
{ label: "contains a special character", met: /[^A-Za-z0-9]/.test(password) },
|
|
1246
|
+
{ label: "no spaces", met: password.length > 0 && !/\s/.test(password) },
|
|
1248
1247
|
];
|
|
1249
1248
|
const failed = checks.filter((c) => !c.met);
|
|
1250
1249
|
if (failed.length > 0) {
|
|
@@ -1257,7 +1256,7 @@ server.tool("remote-auth-set-password", "Set the remote access password. Hashes
|
|
|
1257
1256
|
const { scrypt, randomBytes } = await import("node:crypto");
|
|
1258
1257
|
const salt = randomBytes(32);
|
|
1259
1258
|
const hash = await new Promise((res, rej) => {
|
|
1260
|
-
scrypt(
|
|
1259
|
+
scrypt(password, salt, 64, { N: 16384, r: 8, p: 1 }, (err, key) => {
|
|
1261
1260
|
if (err)
|
|
1262
1261
|
rej(err);
|
|
1263
1262
|
else
|