@rubytech/create-maxy 1.0.621 → 1.0.622

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 (29) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/device-url/dist/index.d.ts +44 -0
  3. package/payload/platform/lib/device-url/dist/index.d.ts.map +1 -0
  4. package/payload/platform/lib/device-url/dist/index.js +68 -0
  5. package/payload/platform/lib/device-url/dist/index.js.map +1 -0
  6. package/payload/platform/lib/device-url/src/index.ts +78 -0
  7. package/payload/platform/lib/device-url/tsconfig.json +8 -0
  8. package/payload/platform/package.json +2 -2
  9. package/payload/platform/plugins/admin/mcp/dist/index.js +12 -5
  10. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  11. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +45 -29
  12. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
  13. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +30 -17
  14. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
  15. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +78 -34
  16. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
  17. package/payload/platform/plugins/cloudflare/references/setup-guide.md +5 -6
  18. package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +3 -4
  19. package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
  20. package/payload/server/public/assets/admin-BxVuKRJZ.js +352 -0
  21. package/payload/server/public/assets/{public-ZM0fHAOE.js → public-Bgm9WQFZ.js} +2 -2
  22. package/payload/server/public/assets/useVoiceRecorder-BORuG_su.css +1 -0
  23. package/payload/server/public/assets/useVoiceRecorder-CiYPZu3g.js +44 -0
  24. package/payload/server/public/index.html +3 -3
  25. package/payload/server/public/public.html +3 -3
  26. package/payload/server/server.js +259 -15
  27. package/payload/server/public/assets/admin-D7LRdkYB.js +0 -352
  28. package/payload/server/public/assets/useVoiceRecorder-CaFVzk8y.css +0 -1
  29. package/payload/server/public/assets/useVoiceRecorder-OB_Gtr0e.js +0 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.621",
3
+ "version": "1.0.622",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Device-bound URL emission helper.
3
+ *
4
+ * A device-bound URL is one whose intent is "the device running the Maxy
5
+ * platform needs to open this URL so its local state machine can progress"
6
+ * — as opposed to a generic external URL that opens wherever the operator
7
+ * is viewing the chat from.
8
+ *
9
+ * The contract: tool results that want a URL to open on the device
10
+ * embed a fenced markdown code block with language identifier
11
+ * `maxy-device-url`, containing a JSON payload { url, intent, hostname? }.
12
+ * The chat UI dispatches on the language identifier (alongside `mermaid`)
13
+ * and renders the block as a button that navigates the device's VNC
14
+ * browser via CDP.
15
+ *
16
+ * See `.docs/web-chat.md` § URL rendering contract for the full spec.
17
+ *
18
+ * This module is the single source of truth for the emit-side block
19
+ * format. Plugin MCP tools import it so every emitter produces an
20
+ * identical shape.
21
+ */
22
+ export interface DeviceUrlOptions {
23
+ /** The actual URL the device should navigate to. Must be http(s). */
24
+ url: string;
25
+ /** Short human-readable label shown on the chat UI button (e.g. "Sign in to Cloudflare"). */
26
+ intent: string;
27
+ /** Optional hostname of the device — used in the fallback UI when VNC is unreachable. */
28
+ hostname?: string;
29
+ }
30
+ export declare const DEVICE_URL_LANGUAGE = "maxy-device-url";
31
+ /**
32
+ * Build a fenced markdown code block that declares a device-bound URL.
33
+ *
34
+ * The returned string is meant to be concatenated into a tool-result
35
+ * `text` content block. The chat UI parses the fence and renders a button.
36
+ * When the chat UI is older than this contract (or rendering in a
37
+ * fallback environment), the block degrades to a readable JSON code
38
+ * block — no broken link, no silent swallow.
39
+ *
40
+ * Throws on invalid input so emit-side bugs surface at call time rather
41
+ * than producing a malformed block that the render side has to catch.
42
+ */
43
+ export declare function deviceUrlBlock(opts: DeviceUrlOptions): string;
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,6FAA6F;IAC7F,MAAM,EAAE,MAAM,CAAC;IACf,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAgC7D"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * Device-bound URL emission helper.
4
+ *
5
+ * A device-bound URL is one whose intent is "the device running the Maxy
6
+ * platform needs to open this URL so its local state machine can progress"
7
+ * — as opposed to a generic external URL that opens wherever the operator
8
+ * is viewing the chat from.
9
+ *
10
+ * The contract: tool results that want a URL to open on the device
11
+ * embed a fenced markdown code block with language identifier
12
+ * `maxy-device-url`, containing a JSON payload { url, intent, hostname? }.
13
+ * The chat UI dispatches on the language identifier (alongside `mermaid`)
14
+ * and renders the block as a button that navigates the device's VNC
15
+ * browser via CDP.
16
+ *
17
+ * See `.docs/web-chat.md` § URL rendering contract for the full spec.
18
+ *
19
+ * This module is the single source of truth for the emit-side block
20
+ * format. Plugin MCP tools import it so every emitter produces an
21
+ * identical shape.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.DEVICE_URL_LANGUAGE = void 0;
25
+ exports.deviceUrlBlock = deviceUrlBlock;
26
+ exports.DEVICE_URL_LANGUAGE = "maxy-device-url";
27
+ /**
28
+ * Build a fenced markdown code block that declares a device-bound URL.
29
+ *
30
+ * The returned string is meant to be concatenated into a tool-result
31
+ * `text` content block. The chat UI parses the fence and renders a button.
32
+ * When the chat UI is older than this contract (or rendering in a
33
+ * fallback environment), the block degrades to a readable JSON code
34
+ * block — no broken link, no silent swallow.
35
+ *
36
+ * Throws on invalid input so emit-side bugs surface at call time rather
37
+ * than producing a malformed block that the render side has to catch.
38
+ */
39
+ function deviceUrlBlock(opts) {
40
+ const { url, intent, hostname } = opts;
41
+ if (typeof url !== "string" || url.length === 0) {
42
+ throw new Error("[device-url] url is required");
43
+ }
44
+ let parsed;
45
+ try {
46
+ parsed = new URL(url);
47
+ }
48
+ catch {
49
+ throw new Error(`[device-url] url is not a valid URL: ${url}`);
50
+ }
51
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
52
+ throw new Error(`[device-url] url scheme must be http or https (got ${parsed.protocol})`);
53
+ }
54
+ if (typeof intent !== "string" || intent.length === 0) {
55
+ throw new Error("[device-url] intent is required and must be a non-empty string");
56
+ }
57
+ if (intent.length > 100) {
58
+ throw new Error(`[device-url] intent exceeds 100 chars (got ${intent.length})`);
59
+ }
60
+ if (hostname !== undefined && typeof hostname !== "string") {
61
+ throw new Error("[device-url] hostname must be a string if provided");
62
+ }
63
+ const payload = hostname
64
+ ? { url, intent, hostname }
65
+ : { url, intent };
66
+ return "```" + exports.DEVICE_URL_LANGUAGE + "\n" + JSON.stringify(payload) + "\n```";
67
+ }
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAyBH,wCAgCC;AA9CY,QAAA,mBAAmB,GAAG,iBAAiB,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,SAAgB,cAAc,CAAC,IAAsB;IACnD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAEvC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wCAAwC,GAAG,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,sDAAsD,MAAM,CAAC,QAAQ,GAAG,CACzE,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACpF,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,8CAA8C,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,OAAO,GAAqB,QAAQ;QACxC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC3B,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAEpB,OAAO,KAAK,GAAG,2BAAmB,GAAG,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;AAChF,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Device-bound URL emission helper.
3
+ *
4
+ * A device-bound URL is one whose intent is "the device running the Maxy
5
+ * platform needs to open this URL so its local state machine can progress"
6
+ * — as opposed to a generic external URL that opens wherever the operator
7
+ * is viewing the chat from.
8
+ *
9
+ * The contract: tool results that want a URL to open on the device
10
+ * embed a fenced markdown code block with language identifier
11
+ * `maxy-device-url`, containing a JSON payload { url, intent, hostname? }.
12
+ * The chat UI dispatches on the language identifier (alongside `mermaid`)
13
+ * and renders the block as a button that navigates the device's VNC
14
+ * browser via CDP.
15
+ *
16
+ * See `.docs/web-chat.md` § URL rendering contract for the full spec.
17
+ *
18
+ * This module is the single source of truth for the emit-side block
19
+ * format. Plugin MCP tools import it so every emitter produces an
20
+ * identical shape.
21
+ */
22
+
23
+ export interface DeviceUrlOptions {
24
+ /** The actual URL the device should navigate to. Must be http(s). */
25
+ url: string;
26
+ /** Short human-readable label shown on the chat UI button (e.g. "Sign in to Cloudflare"). */
27
+ intent: string;
28
+ /** Optional hostname of the device — used in the fallback UI when VNC is unreachable. */
29
+ hostname?: string;
30
+ }
31
+
32
+ export const DEVICE_URL_LANGUAGE = "maxy-device-url";
33
+
34
+ /**
35
+ * Build a fenced markdown code block that declares a device-bound URL.
36
+ *
37
+ * The returned string is meant to be concatenated into a tool-result
38
+ * `text` content block. The chat UI parses the fence and renders a button.
39
+ * When the chat UI is older than this contract (or rendering in a
40
+ * fallback environment), the block degrades to a readable JSON code
41
+ * block — no broken link, no silent swallow.
42
+ *
43
+ * Throws on invalid input so emit-side bugs surface at call time rather
44
+ * than producing a malformed block that the render side has to catch.
45
+ */
46
+ export function deviceUrlBlock(opts: DeviceUrlOptions): string {
47
+ const { url, intent, hostname } = opts;
48
+
49
+ if (typeof url !== "string" || url.length === 0) {
50
+ throw new Error("[device-url] url is required");
51
+ }
52
+ let parsed: URL;
53
+ try {
54
+ parsed = new URL(url);
55
+ } catch {
56
+ throw new Error(`[device-url] url is not a valid URL: ${url}`);
57
+ }
58
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
59
+ throw new Error(
60
+ `[device-url] url scheme must be http or https (got ${parsed.protocol})`,
61
+ );
62
+ }
63
+ if (typeof intent !== "string" || intent.length === 0) {
64
+ throw new Error("[device-url] intent is required and must be a non-empty string");
65
+ }
66
+ if (intent.length > 100) {
67
+ throw new Error(`[device-url] intent exceeds 100 chars (got ${intent.length})`);
68
+ }
69
+ if (hostname !== undefined && typeof hostname !== "string") {
70
+ throw new Error("[device-url] hostname must be a string if provided");
71
+ }
72
+
73
+ const payload: DeviceUrlOptions = hostname
74
+ ? { url, intent, hostname }
75
+ : { url, intent };
76
+
77
+ return "```" + DEVICE_URL_LANGUAGE + "\n" + JSON.stringify(payload) + "\n```";
78
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src"]
8
+ }
@@ -6,8 +6,8 @@
6
6
  "plugins/*/mcp"
7
7
  ],
8
8
  "scripts": {
9
- "build": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
10
- "build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json",
9
+ "build": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
10
+ "build:lib": "tsc -p lib/models/tsconfig.json && tsc -p lib/anthropic-key/tsconfig.json && tsc -p lib/mcp-stderr-tee/tsconfig.json && tsc -p lib/screening-patterns/tsconfig.json && tsc -p lib/device-url/tsconfig.json",
11
11
  "build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
12
12
  "build:contacts": "tsc -p plugins/contacts/mcp/tsconfig.json",
13
13
  "build:telegram": "tsc -p plugins/telegram/mcp/tsconfig.json",
@@ -8,6 +8,7 @@ import { resolve, join } from "node:path";
8
8
  import { execFileSync } from "node:child_process";
9
9
  import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
10
10
  import { writeKey, validateKey, hasKey, keyFilePath } from "../../../../lib/anthropic-key/dist/index.js";
11
+ import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
11
12
  import { createHash, randomInt, randomUUID } from "node:crypto";
12
13
  import { createConnection } from "node:net";
13
14
  import { homedir, hostname as osHostname } from "node:os";
@@ -333,7 +334,7 @@ server.tool("system-status", "Check health of all Maxy platform services: Neo4j,
333
334
  .join("\n");
334
335
  return { content: [{ type: "text", text: formatted }] };
335
336
  });
336
- server.tool("remote-auth-status", "Check whether the remote access password is configured. Returns configured (true/false) and the setupUrl for the user to set a password in their browser. The agent never constructs the password file path or runs shell commands — this tool is the single authority.", {}, async () => {
337
+ server.tool("remote-auth-status", "Check whether the remote access password is configured. When not configured, emits a device-bound URL affordance (maxy-device-url fenced block) pointing at the password setup page — this URL opens on the device's own screen when the operator clicks it. The agent never constructs the password file path or runs shell commands — this tool is the single authority.", {}, async () => {
337
338
  const TAG = "[remote-auth-status]";
338
339
  const platformPort = parseInt(PLATFORM_PORT, 10);
339
340
  const setupUrl = `http://${osHostname()}.local:${platformPort}/__remote-auth/setup`;
@@ -344,11 +345,17 @@ server.tool("remote-auth-status", "Check whether the remote access password is c
344
345
  const body = await res.json();
345
346
  const configured = body.configured === true;
346
347
  console.error(`${TAG} configured=${configured} port=${platformPort}`);
348
+ // Shape the response for both the agent (deciding what to say) and
349
+ // the user (seeing the device-URL button when they need to act).
350
+ // Configured → plain status, no URL. Unconfigured → include the
351
+ // fenced block so the agent can surface the button verbatim.
352
+ const text = configured
353
+ ? `Remote access password is configured.`
354
+ : `Remote access password is not configured. The admin interface will refuse to expose over the tunnel until one is set.\n\n` +
355
+ `${deviceUrlBlock({ url: setupUrl, intent: "Set remote access password", hostname: osHostname() })}\n\n` +
356
+ `Click the button above to open the setup page on the device's browser. Do NOT collect the password in chat.`;
347
357
  return {
348
- content: [{
349
- type: "text",
350
- text: JSON.stringify({ configured, setupUrl }),
351
- }],
358
+ content: [{ type: "text", text }],
352
359
  };
353
360
  }
354
361
  catch (err) {