@rubytech/create-realagent 1.0.620 → 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 +136 -36
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +69 -4
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +126 -21
- package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
- package/payload/platform/plugins/cloudflare/references/setup-guide.md +38 -19
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +24 -15
- package/payload/platform/plugins/docs/references/cloudflare.md +3 -3
- package/payload/platform/templates/agents/admin/IDENTITY.md +8 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +8 -0
- 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 +287 -2
- 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
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Real Agent</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/admin-
|
|
8
|
+
<script type="module" crossorigin src="/assets/admin-BxVuKRJZ.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-Be6NvmcD.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-rov5CBGT.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/useVoiceRecorder-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-CiYPZu3g.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/useVoiceRecorder-BORuG_su.css">
|
|
13
13
|
<link rel="stylesheet" href="/brand-defaults.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Real Agent</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/public-
|
|
8
|
+
<script type="module" crossorigin src="/assets/public-Bgm9WQFZ.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-Be6NvmcD.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-rov5CBGT.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/useVoiceRecorder-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-CiYPZu3g.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/useVoiceRecorder-BORuG_su.css">
|
|
13
13
|
<link rel="stylesheet" href="/brand-defaults.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
package/payload/server/server.js
CHANGED
|
@@ -10573,6 +10573,66 @@ function defaultRules() {
|
|
|
10573
10573
|
thresholdWindowMinutes: 0,
|
|
10574
10574
|
suggestedAction: "This laptop is signed into a Cloudflare account that does not own the hostnames the tunnel is configured to serve. Run `tunnel-status` to confirm, then tell the operator verbatim: 'The tunnel is running on this laptop but nothing from the internet is reaching it. The Cloudflare account this laptop is signed into doesn't own your domain. Open Cloudflare in your browser \u2014 is the account name in the top-left the one that owns your domain? If not, switch to the correct one, then tell me and I will re-sign-in.' When the operator confirms the correct account is selected, run `tunnel-login force=true`."
|
|
10575
10575
|
},
|
|
10576
|
+
{
|
|
10577
|
+
// Task 545: tunnel-login's terminal-failure class — cloudflared's
|
|
10578
|
+
// login process died without writing cert.pem. Covers every reason
|
|
10579
|
+
// the handler emits on the `failed` branch: either an unknown exit
|
|
10580
|
+
// (`-without-cert`), an exit preceded by the courtesy browser-launch
|
|
10581
|
+
// marker (`-with-marker`), auth URL never produced (`-timeout`), or
|
|
10582
|
+
// crashed before producing it at all. Task 541's original pattern
|
|
10583
|
+
// matched `reason=browser-launch-fetch-error` — Task 545 retired
|
|
10584
|
+
// that reason because the marker alone is no longer terminal
|
|
10585
|
+
// (cloudflared keeps its OAuth-callback loop alive after emitting
|
|
10586
|
+
// it). Use this pattern for any new terminal reason the handler
|
|
10587
|
+
// gains: extend the alternation rather than adding parallel rules.
|
|
10588
|
+
id: "cloudflare-tunnel-login-failed",
|
|
10589
|
+
name: "Cloudflare tunnel-login process terminated without writing cert",
|
|
10590
|
+
type: "silent-catch",
|
|
10591
|
+
logSource: "any",
|
|
10592
|
+
pattern: "\\[cloudflare:tunnel-login:failed\\] reason=(login-process-exited-without-cert|login-process-exited-with-marker|auth-url-timeout|process-exited-before-auth-url)",
|
|
10593
|
+
thresholdCount: 0,
|
|
10594
|
+
thresholdWindowMinutes: 0,
|
|
10595
|
+
suggestedAction: "The cloudflared login process died before cert.pem landed on disk. For `login-process-exited-*` reasons the tunnel-login tool detected the dead process on the next call and has already respawned it \u2014 relay the new sign-in URL and wait for the operator to authorize in the VNC browser. For `auth-url-timeout` / `process-exited-before-auth-url`, the tool's most recent call returned an error and did not respawn \u2014 call `tunnel-login` again to spawn a fresh attempt. Never open the Cloudflare dashboard in any other surface; the only auth path is the sign-in URL the tool produces."
|
|
10596
|
+
},
|
|
10597
|
+
{
|
|
10598
|
+
// Task 545: non-terminal advisory. cloudflared's browser-launch
|
|
10599
|
+
// subcommand failed (DISPLAY unreachable, xdg-open absent, etc.) but
|
|
10600
|
+
// its OAuth-callback listener is still running — the login can still
|
|
10601
|
+
// complete if a human opens the URL. This rule fires so the admin
|
|
10602
|
+
// agent can relay "open the URL yourself" to the operator the moment
|
|
10603
|
+
// the condition appears, rather than waiting for the operator to
|
|
10604
|
+
// notice nothing is happening in their browser. Task 546 will
|
|
10605
|
+
// obsolete the advisory by rendering auth URLs that auto-open in
|
|
10606
|
+
// the VNC browser.
|
|
10607
|
+
id: "cloudflare-tunnel-login-browser-launch-failed",
|
|
10608
|
+
name: "cloudflared couldn't open the sign-in URL (login still live)",
|
|
10609
|
+
type: "silent-catch",
|
|
10610
|
+
logSource: "any",
|
|
10611
|
+
pattern: "\\[cloudflare:tunnel-login:browser-launch-failed\\]",
|
|
10612
|
+
thresholdCount: 0,
|
|
10613
|
+
thresholdWindowMinutes: 0,
|
|
10614
|
+
suggestedAction: "cloudflared's browser-launch courtesy failed on this laptop, but the sign-in URL is still live \u2014 the process is waiting for the OAuth callback. Tell the operator to open the sign-in URL in the VNC browser themselves and complete the authorization. The tunnel-login tool already includes the URL and the advisory in its most recent response; do not restart the login (that would invalidate the URL the operator is about to open)."
|
|
10615
|
+
},
|
|
10616
|
+
{
|
|
10617
|
+
// Task 545: raw-log surface. cloudflared-login.log is now read by
|
|
10618
|
+
// the review-detector's `cloudflared` source (see sources.ts). The
|
|
10619
|
+
// literal "Failed to fetch resource" is emitted by cloudflared
|
|
10620
|
+
// itself when its browser-launch subcommand can't reach a display.
|
|
10621
|
+
// This rule catches the cloudflared-side event even if the MCP
|
|
10622
|
+
// handler's classification drifts or the platform is restarting
|
|
10623
|
+
// mid-login and the advisory log line is not yet written. Keeping
|
|
10624
|
+
// this rule on a different source (log-line, not MCP stderr) makes
|
|
10625
|
+
// the detection redundant in the "defence in depth" sense — if the
|
|
10626
|
+
// MCP classification ever regresses, this still fires.
|
|
10627
|
+
id: "cloudflared-login-browser-launch-failed-raw",
|
|
10628
|
+
name: "cloudflared login \u2014 browser-launch fetch error in log",
|
|
10629
|
+
type: "silent-catch",
|
|
10630
|
+
logSource: "cloudflared",
|
|
10631
|
+
pattern: "Failed to fetch resource",
|
|
10632
|
+
thresholdCount: 0,
|
|
10633
|
+
thresholdWindowMinutes: 0,
|
|
10634
|
+
suggestedAction: "cloudflared-login.log shows the browser-launch courtesy failed. The sign-in URL from the last tunnel-login call is still live \u2014 the cloudflared process waits for the OAuth callback regardless of whether it could open the browser itself. Tell the operator to open the sign-in URL manually in the VNC browser on the device."
|
|
10635
|
+
},
|
|
10576
10636
|
{
|
|
10577
10637
|
// Task 540: cloudflared.log is the one file most likely to carry the
|
|
10578
10638
|
// "tunnel is having real-world connectivity problems" signal — QUIC
|
|
@@ -10588,6 +10648,54 @@ function defaultRules() {
|
|
|
10588
10648
|
thresholdCount: 0,
|
|
10589
10649
|
thresholdWindowMinutes: 0,
|
|
10590
10650
|
suggestedAction: "cloudflared is reporting edge connectivity errors in its daemon log. Read the last 20 lines of `cloudflared.log` to see the surrounding context. Transient QUIC drops are normal; sustained failures (more than a handful in a minute) point to either a network issue on this laptop or an edge-side routing problem. Run `tunnel-status` to check whether end-to-end probing still succeeds."
|
|
10651
|
+
},
|
|
10652
|
+
{
|
|
10653
|
+
// Task 543: fires when an agent turn contains an opposing-axis choice-fork
|
|
10654
|
+
// question ("Want me to X, or Y?"). Every occurrence is a violation of the
|
|
10655
|
+
// IDENTITY § Questions rule — Rule A (one-sided questions) catches them
|
|
10656
|
+
// all, and the sharper sub-class (Rule B — no menu when a tool returned a
|
|
10657
|
+
// deterministic signal) is isolated by Task 544's `preceded-by` tightening.
|
|
10658
|
+
// logSource is "any" so the rule catches violations on both the admin
|
|
10659
|
+
// stream (claude-agent-stream-*) and the public agent stream
|
|
10660
|
+
// (public-agent-stream-*); the regex is agent-phrasing-specific and has
|
|
10661
|
+
// not been observed in other log contexts. Session scope groups matches
|
|
10662
|
+
// by conversationId so a single offending turn fires exactly once.
|
|
10663
|
+
id: "agent-choice-fork",
|
|
10664
|
+
name: "Agent emitted a choice-fork question",
|
|
10665
|
+
type: "repeated-error",
|
|
10666
|
+
logSource: "any",
|
|
10667
|
+
pattern: "Want me to [^\\n]+, or [^\\n]+\\?",
|
|
10668
|
+
thresholdCount: 1,
|
|
10669
|
+
thresholdWindowMinutes: 60,
|
|
10670
|
+
scope: "session",
|
|
10671
|
+
suggestedAction: 'Agent emitted a choice-fork question ("Want me to X, or Y?") instead of a one-sided question or the prescribed action. Review Task 543 IDENTITY \xA7 Questions \u2014 the agent asked an opposing-axis question, or degraded a deterministic tool signal into a menu. The log sample shows the offending turn verbatim.'
|
|
10672
|
+
},
|
|
10673
|
+
{
|
|
10674
|
+
// Task 546: fires when the operator clicks a device-bound URL affordance
|
|
10675
|
+
// and the chat UI cannot drive the device browser — either CDP is
|
|
10676
|
+
// unreachable, the navigation timed out, or CDP returned an error. The
|
|
10677
|
+
// log line carries intent, hostname, and navigateResult so the admin
|
|
10678
|
+
// agent can name the affected flow and hostname verbatim on its next
|
|
10679
|
+
// turn. Every occurrence is worth surfacing (thresholdCount: 0) because
|
|
10680
|
+
// this is the exact class of silent failure Task 546 exists to close:
|
|
10681
|
+
// the operator clicked, nothing happened on the device, and if we don't
|
|
10682
|
+
// review the click telemetry the agent has no way to know the flow is
|
|
10683
|
+
// stuck.
|
|
10684
|
+
id: "device-url-click-failed",
|
|
10685
|
+
name: "Device-bound URL click failed to drive the VNC browser",
|
|
10686
|
+
type: "silent-catch",
|
|
10687
|
+
logSource: "server",
|
|
10688
|
+
// Enumerate the NavigateResult union explicitly rather than relying
|
|
10689
|
+
// on a (?!ok) negative lookahead anchored to a specific token order.
|
|
10690
|
+
// If a new member is added to the union in cdp-client.ts, this rule
|
|
10691
|
+
// must be updated in the same commit — the pattern is order-agnostic
|
|
10692
|
+
// (browser= and navigateResult= can appear in either order) and the
|
|
10693
|
+
// enumerated list compile-fails the source if it ever drifts from
|
|
10694
|
+
// the shared type in device-url-schema.ts.
|
|
10695
|
+
pattern: "\\[device-url:click\\][^\\n]*(?:browser=fallback|navigateResult=(?:timeout|cdp-unreachable|error))",
|
|
10696
|
+
thresholdCount: 0,
|
|
10697
|
+
thresholdWindowMinutes: 0,
|
|
10698
|
+
suggestedAction: "A device-bound URL click failed to drive Chromium on the device's VNC display. Identify the `intent` and `hostname` from the log line, then check the VNC surface: read `vnc-boot.log` and confirm Chromium on :99 is responding on CDP port 9222. If CDP is unreachable, the operator needs to restart the VNC stack; if CDP is reachable but navigation errored, the URL itself may be malformed upstream \u2014 grep the stream log for the originating `[device-url:render]` line."
|
|
10591
10699
|
}
|
|
10592
10700
|
];
|
|
10593
10701
|
}
|
|
@@ -10817,8 +10925,12 @@ function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
|
|
|
10817
10925
|
return existsSync8(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
|
|
10818
10926
|
}
|
|
10819
10927
|
if (logicalSource === "cloudflared") {
|
|
10820
|
-
const
|
|
10821
|
-
|
|
10928
|
+
const files2 = [];
|
|
10929
|
+
const daemon = resolve8(configDir2, "logs", "cloudflared.log");
|
|
10930
|
+
if (existsSync8(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
|
|
10931
|
+
const login = resolve8(configDir2, "logs", "cloudflared-login.log");
|
|
10932
|
+
if (existsSync8(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
|
|
10933
|
+
return files2;
|
|
10822
10934
|
}
|
|
10823
10935
|
const prefix = {
|
|
10824
10936
|
system: "claude-agent-stream-",
|
|
@@ -32092,6 +32204,174 @@ async function POST27(req, remoteAddress) {
|
|
|
32092
32204
|
}
|
|
32093
32205
|
}
|
|
32094
32206
|
|
|
32207
|
+
// app/lib/cdp-client.ts
|
|
32208
|
+
var CDP_HOST = "127.0.0.1";
|
|
32209
|
+
var CDP_PORT = 9222;
|
|
32210
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
32211
|
+
async function cdpNavigateNewTab(url2, opts = {}) {
|
|
32212
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
32213
|
+
const endpoint = `http://${CDP_HOST}:${CDP_PORT}/json/new?${encodeURIComponent(url2)}`;
|
|
32214
|
+
let res;
|
|
32215
|
+
try {
|
|
32216
|
+
res = await fetch(endpoint, {
|
|
32217
|
+
method: "PUT",
|
|
32218
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
32219
|
+
});
|
|
32220
|
+
} catch (err) {
|
|
32221
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
32222
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("ENOTFOUND")) {
|
|
32223
|
+
return { result: "cdp-unreachable", detail: msg };
|
|
32224
|
+
}
|
|
32225
|
+
if (err instanceof Error && err.name === "TimeoutError") {
|
|
32226
|
+
return { result: "timeout", detail: msg };
|
|
32227
|
+
}
|
|
32228
|
+
return { result: "error", detail: msg };
|
|
32229
|
+
}
|
|
32230
|
+
if (!res.ok) {
|
|
32231
|
+
const body = await res.text().catch(() => "<unreadable>");
|
|
32232
|
+
return {
|
|
32233
|
+
result: "error",
|
|
32234
|
+
detail: `CDP /json/new returned ${res.status}: ${body.slice(0, 200)}`
|
|
32235
|
+
};
|
|
32236
|
+
}
|
|
32237
|
+
let target;
|
|
32238
|
+
try {
|
|
32239
|
+
target = await res.json();
|
|
32240
|
+
} catch (err) {
|
|
32241
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
32242
|
+
return { result: "error", detail: `CDP /json/new response not JSON: ${msg}` };
|
|
32243
|
+
}
|
|
32244
|
+
return { result: "ok", targetId: target?.id };
|
|
32245
|
+
}
|
|
32246
|
+
|
|
32247
|
+
// app/api/admin/device-browser/navigate/route.ts
|
|
32248
|
+
async function POST28(req, remoteAddress) {
|
|
32249
|
+
const TAG18 = "[device-url:click]";
|
|
32250
|
+
let body;
|
|
32251
|
+
try {
|
|
32252
|
+
body = await req.json();
|
|
32253
|
+
} catch (err) {
|
|
32254
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
32255
|
+
console.error(`${TAG18} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
|
|
32256
|
+
return Response.json(
|
|
32257
|
+
{ ok: false, navigateResult: "error", browser: "fallback", detail: "Request body was not valid JSON" },
|
|
32258
|
+
{ status: 400 }
|
|
32259
|
+
);
|
|
32260
|
+
}
|
|
32261
|
+
const url2 = typeof body.url === "string" ? body.url : "";
|
|
32262
|
+
const intent = typeof body.intent === "string" ? body.intent : "";
|
|
32263
|
+
const hostname4 = typeof body.hostname === "string" ? body.hostname : "";
|
|
32264
|
+
if (!url2) {
|
|
32265
|
+
console.error(`${TAG18} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
|
|
32266
|
+
return Response.json(
|
|
32267
|
+
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url field is required" },
|
|
32268
|
+
{ status: 400 }
|
|
32269
|
+
);
|
|
32270
|
+
}
|
|
32271
|
+
let parsed;
|
|
32272
|
+
try {
|
|
32273
|
+
parsed = new URL(url2);
|
|
32274
|
+
} catch {
|
|
32275
|
+
console.error(`${TAG18} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url2} browser=fallback navigateResult=error`);
|
|
32276
|
+
return Response.json(
|
|
32277
|
+
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url is not a valid URL" },
|
|
32278
|
+
{ status: 400 }
|
|
32279
|
+
);
|
|
32280
|
+
}
|
|
32281
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
32282
|
+
console.error(
|
|
32283
|
+
`${TAG18} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
|
|
32284
|
+
);
|
|
32285
|
+
return Response.json(
|
|
32286
|
+
{
|
|
32287
|
+
ok: false,
|
|
32288
|
+
navigateResult: "error",
|
|
32289
|
+
browser: "fallback",
|
|
32290
|
+
detail: `URL scheme ${parsed.protocol} is not permitted; only http and https are accepted`
|
|
32291
|
+
},
|
|
32292
|
+
{ status: 400 }
|
|
32293
|
+
);
|
|
32294
|
+
}
|
|
32295
|
+
const transport = resolveBrowserTransport(req, remoteAddress);
|
|
32296
|
+
const cdpOk = await ensureCdp(transport);
|
|
32297
|
+
if (!cdpOk) {
|
|
32298
|
+
console.error(
|
|
32299
|
+
`${TAG18} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname4)}`
|
|
32300
|
+
);
|
|
32301
|
+
return Response.json(
|
|
32302
|
+
{
|
|
32303
|
+
ok: false,
|
|
32304
|
+
navigateResult: "cdp-unreachable",
|
|
32305
|
+
browser: "fallback",
|
|
32306
|
+
detail: "Device browser (Chromium/CDP on :9222) did not start"
|
|
32307
|
+
},
|
|
32308
|
+
{ status: 502 }
|
|
32309
|
+
);
|
|
32310
|
+
}
|
|
32311
|
+
const outcome = await cdpNavigateNewTab(parsed.toString());
|
|
32312
|
+
const browser = outcome.result === "ok" ? "vnc" : "fallback";
|
|
32313
|
+
const detailStr = outcome.detail ? ` detail=${JSON.stringify(outcome.detail.length > 230 ? outcome.detail.slice(0, 227) + "..." : outcome.detail)}` : "";
|
|
32314
|
+
console.error(
|
|
32315
|
+
`${TAG18} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname4)} targetId=${outcome.targetId ?? "none"}${detailStr}`
|
|
32316
|
+
);
|
|
32317
|
+
if (outcome.result !== "ok") {
|
|
32318
|
+
return Response.json(
|
|
32319
|
+
{
|
|
32320
|
+
ok: false,
|
|
32321
|
+
navigateResult: outcome.result,
|
|
32322
|
+
browser: "fallback",
|
|
32323
|
+
detail: outcome.detail
|
|
32324
|
+
},
|
|
32325
|
+
{ status: 502 }
|
|
32326
|
+
);
|
|
32327
|
+
}
|
|
32328
|
+
return Response.json({
|
|
32329
|
+
ok: true,
|
|
32330
|
+
navigateResult: "ok",
|
|
32331
|
+
browser: "vnc",
|
|
32332
|
+
targetId: outcome.targetId
|
|
32333
|
+
});
|
|
32334
|
+
}
|
|
32335
|
+
|
|
32336
|
+
// app/api/admin/events/route.ts
|
|
32337
|
+
var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
32338
|
+
"device-url:render",
|
|
32339
|
+
"device-url:fallback-shown",
|
|
32340
|
+
"device-url:vnc-surface-shown",
|
|
32341
|
+
"device-url:malformed"
|
|
32342
|
+
]);
|
|
32343
|
+
async function POST29(req) {
|
|
32344
|
+
const TAG18 = "[admin:events]";
|
|
32345
|
+
let body;
|
|
32346
|
+
try {
|
|
32347
|
+
body = await req.json();
|
|
32348
|
+
} catch (err) {
|
|
32349
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
32350
|
+
console.error(`${TAG18} reject reason=body-not-json detail=${detail}`);
|
|
32351
|
+
return Response.json({ ok: false, detail: "Request body was not valid JSON" }, { status: 400 });
|
|
32352
|
+
}
|
|
32353
|
+
const event = typeof body.event === "string" ? body.event : "";
|
|
32354
|
+
if (!ALLOWED_EVENTS.has(event)) {
|
|
32355
|
+
console.error(`${TAG18} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
32356
|
+
return Response.json({ ok: false, detail: `Event "${event}" is not allowed` }, { status: 400 });
|
|
32357
|
+
}
|
|
32358
|
+
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
32359
|
+
const safeFields = {};
|
|
32360
|
+
for (const [key, value] of Object.entries(rawFields)) {
|
|
32361
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_-]{0,39}$/.test(key)) continue;
|
|
32362
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
32363
|
+
safeFields[key] = value;
|
|
32364
|
+
}
|
|
32365
|
+
}
|
|
32366
|
+
const formatted = Object.entries(safeFields).map(([k, v]) => {
|
|
32367
|
+
if (typeof v !== "string") return `${k}=${String(v)}`;
|
|
32368
|
+
const trunc = v.length > 230 ? v.slice(0, 227) + "..." : v;
|
|
32369
|
+
return `${k}=${JSON.stringify(trunc)}`;
|
|
32370
|
+
}).join(" ");
|
|
32371
|
+
console.error(`[${event}] ${formatted}`);
|
|
32372
|
+
return Response.json({ ok: true });
|
|
32373
|
+
}
|
|
32374
|
+
|
|
32095
32375
|
// server/index.ts
|
|
32096
32376
|
var PLATFORM_ROOT12 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
32097
32377
|
var BRAND_JSON_PATH = PLATFORM_ROOT12 ? join13(PLATFORM_ROOT12, "config", "brand.json") : "";
|
|
@@ -32482,6 +32762,11 @@ app.delete(
|
|
|
32482
32762
|
(c) => DELETE2(c.req.raw, { params: Promise.resolve({ slug: c.req.param("slug") }) })
|
|
32483
32763
|
);
|
|
32484
32764
|
app.post("/api/admin/browser/launch", (c) => POST27(c.req.raw, c.env?.incoming?.socket?.remoteAddress));
|
|
32765
|
+
app.post(
|
|
32766
|
+
"/api/admin/device-browser/navigate",
|
|
32767
|
+
(c) => POST28(c.req.raw, c.env?.incoming?.socket?.remoteAddress)
|
|
32768
|
+
);
|
|
32769
|
+
app.post("/api/admin/events", (c) => POST29(c.req.raw));
|
|
32485
32770
|
app.get("/api/admin/sessions", (c) => GET15(c.req.raw));
|
|
32486
32771
|
app.post("/api/admin/sessions/new", (c) => POST24(c.req.raw));
|
|
32487
32772
|
app.delete(
|