@rubytech/create-realagent 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.
- package/package.json +1 -1
- package/payload/platform/lib/device-url/dist/index.d.ts +44 -0
- package/payload/platform/lib/device-url/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/device-url/dist/index.js +68 -0
- package/payload/platform/lib/device-url/dist/index.js.map +1 -0
- package/payload/platform/lib/device-url/src/index.ts +78 -0
- package/payload/platform/lib/device-url/tsconfig.json +8 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +12 -5
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +45 -29
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +30 -17
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +78 -34
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
- package/payload/platform/plugins/cloudflare/references/setup-guide.md +5 -6
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +3 -4
- package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
- package/payload/server/public/assets/admin-BxVuKRJZ.js +352 -0
- package/payload/server/public/assets/{public-ZM0fHAOE.js → public-Bgm9WQFZ.js} +2 -2
- package/payload/server/public/assets/useVoiceRecorder-BORuG_su.css +1 -0
- package/payload/server/public/assets/useVoiceRecorder-CiYPZu3g.js +44 -0
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +259 -15
- package/payload/server/public/assets/admin-D7LRdkYB.js +0 -352
- package/payload/server/public/assets/useVoiceRecorder-CaFVzk8y.css +0 -1
- package/payload/server/public/assets/useVoiceRecorder-OB_Gtr0e.js +0 -43
package/package.json
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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.
|
|
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) {
|