@rubytech/create-realagent 1.0.868 → 1.0.870

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 (31) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.d.ts +2 -0
  3. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.d.ts.map +1 -0
  4. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.js +140 -0
  5. package/payload/platform/lib/graph-mcp/dist/__tests__/warnings-envelope.test.js.map +1 -0
  6. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.d.ts +85 -0
  7. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.d.ts.map +1 -0
  8. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.js +93 -0
  9. package/payload/platform/lib/graph-mcp/dist/cypher-shim-read.js.map +1 -0
  10. package/payload/platform/lib/graph-mcp/dist/index.js +82 -11
  11. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  12. package/payload/platform/lib/graph-mcp/src/__tests__/warnings-envelope.test.ts +151 -0
  13. package/payload/platform/lib/graph-mcp/src/cypher-shim-read.ts +141 -0
  14. package/payload/platform/lib/graph-mcp/src/index.ts +107 -11
  15. package/payload/platform/plugins/admin/PLUGIN.md +2 -1
  16. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.d.ts +2 -0
  17. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.d.ts.map +1 -0
  18. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.js +98 -0
  19. package/payload/platform/plugins/admin/mcp/dist/__tests__/public-hostname.test.js.map +1 -0
  20. package/payload/platform/plugins/admin/mcp/dist/index.js +31 -0
  21. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  22. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.d.ts +15 -0
  23. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.d.ts.map +1 -0
  24. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.js +73 -0
  25. package/payload/platform/plugins/admin/mcp/dist/lib/public-hostname.js.map +1 -0
  26. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +2 -3
  27. package/payload/platform/plugins/docs/references/internals.md +2 -0
  28. package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
  29. package/payload/server/chunk-B5VSPQQP.js +11320 -0
  30. package/payload/server/maxy-edge.js +1 -1
  31. package/payload/server/server.js +1 -1
@@ -0,0 +1,15 @@
1
+ export type HostnameSource = "cloudflared-config.yml" | "alias-domains.json";
2
+ export interface PublicHostnameHit {
3
+ hostname: string;
4
+ isApex: boolean;
5
+ source: HostnameSource;
6
+ }
7
+ export interface PublicHostnameMiss {
8
+ hostname: null;
9
+ isApex: null;
10
+ source: null;
11
+ reason: "no-tunnel";
12
+ }
13
+ export type PublicHostnameResult = PublicHostnameHit | PublicHostnameMiss;
14
+ export declare function resolvePublicHostname(configDir: string): PublicHostnameResult;
15
+ //# sourceMappingURL=public-hostname.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-hostname.d.ts","sourceRoot":"","sources":["../../src/lib/public-hostname.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,cAAc,GAAG,wBAAwB,GAAG,oBAAoB,CAAC;AAE7E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AA8C1E,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,oBAAoB,CAY7E"}
@@ -0,0 +1,73 @@
1
+ // Task 971 — public-hostname resolver reads filesystem (4th recurrence of
2
+ // llm-framing-deterministic).
3
+ //
4
+ // Reads the same two files that already drive routing:
5
+ // - <configDir>/cloudflared/config.yml — the ingress map cloudflared itself
6
+ // reads to decide where to route a hostname.
7
+ // - <configDir>/alias-domains.json — the alias list platform/ui/server's
8
+ // isPublicHost() watches via watchFile.
9
+ // hasPublicEndpointConfigured() in admin/mcp/src/index.ts already parses both
10
+ // with the same regex; this resolver is the read-only twin that returns the
11
+ // chosen hostname instead of a boolean.
12
+ //
13
+ // Graph queries are gone. The previous version (Task 970) returned (none) on
14
+ // any account that bootstrapped its tunnel without writing :CloudflareTunnel
15
+ // nodes — exactly the laptop Real Agent state that produced this recurrence.
16
+ import { existsSync, readFileSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ const INGRESS_RE = /^\s*-\s*hostname:\s*(\S+)/gm;
19
+ function isExcluded(host) {
20
+ return host === "localhost" || host.endsWith(".local");
21
+ }
22
+ // Apex = single domain (e.g. "realagent.app"), with optional `www.` prefix
23
+ // stripped. Subdomain = anything else ("public.realagent.app").
24
+ function isApex(host) {
25
+ const stripped = host.startsWith("www.") ? host.slice(4) : host;
26
+ return stripped.split(".").length === 2;
27
+ }
28
+ function readIngressHostnames(configDir) {
29
+ const path = join(configDir, "cloudflared", "config.yml");
30
+ if (!existsSync(path))
31
+ return [];
32
+ try {
33
+ const yaml = readFileSync(path, "utf-8");
34
+ return [...yaml.matchAll(INGRESS_RE)].map((m) => m[1]).filter((h) => !isExcluded(h));
35
+ }
36
+ catch {
37
+ return [];
38
+ }
39
+ }
40
+ function readAliasDomains(configDir) {
41
+ const path = join(configDir, "alias-domains.json");
42
+ if (!existsSync(path))
43
+ return [];
44
+ try {
45
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
46
+ if (!Array.isArray(parsed))
47
+ return [];
48
+ return parsed.filter((h) => typeof h === "string" && h.length > 0 && !isExcluded(h));
49
+ }
50
+ catch {
51
+ return [];
52
+ }
53
+ }
54
+ // Tiebreak: apex wins over subdomain; otherwise first-in-file.
55
+ function pickHostname(candidates) {
56
+ if (candidates.length === 0)
57
+ return null;
58
+ return candidates.find((h) => isApex(h)) ?? candidates[0];
59
+ }
60
+ export function resolvePublicHostname(configDir) {
61
+ const ingress = readIngressHostnames(configDir);
62
+ const chosen = pickHostname(ingress);
63
+ if (chosen !== null) {
64
+ return { hostname: chosen, isApex: isApex(chosen), source: "cloudflared-config.yml" };
65
+ }
66
+ const alias = readAliasDomains(configDir);
67
+ const aliasChosen = pickHostname(alias);
68
+ if (aliasChosen !== null) {
69
+ return { hostname: aliasChosen, isApex: isApex(aliasChosen), source: "alias-domains.json" };
70
+ }
71
+ return { hostname: null, isApex: null, source: null, reason: "no-tunnel" };
72
+ }
73
+ //# sourceMappingURL=public-hostname.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-hostname.js","sourceRoot":"","sources":["../../src/lib/public-hostname.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  # Publish Site
2
2
 
3
- Move an already-extracted static-site tree into the per-account static-publish surface (`<accountDir>/sites/<slug>/`) and emit exactly one canonical path slug for the operator to share. The operator pastes the slug after their own public-host root the public hostname is whatever their tunnel terminates at, and is not knowable from inside this skill.
3
+ Move an already-extracted static-site tree into the per-account static-publish surface (`<accountDir>/sites/<slug>/`) and emit exactly one canonical path slug. Pair the slug with the deterministic public hostname returned by `mcp__admin__public-hostname` to surface the full URL in a single turn.
4
4
 
5
5
  **Invoked from `specialists:content-producer`** when the brief carries a host-website / publish-site / put-online intent (Task 966). Admin's IDENTITY.md routes those intents to that specialist on turn 1; running this skill inline on the main admin runner exhausts the 10-turn budget on per-turn ToolSearch + plugin-read discovery before publish-site executes.
6
6
 
@@ -54,7 +54,7 @@ The operator message for `ambiguous-html` names the candidate files and asks the
54
54
  4. **Choose the canonical path.** `index.html` present at top level → path is `/sites/<slug>/`. Otherwise the single top-level HTML file → path is `/sites/<slug>/<file>.html`.
55
55
  5. **Emit.** One log line:
56
56
  `[publish-site] url emitted=<path-slug> kind=<index|file>`
57
- 6. **Operator-facing chat output.** One line: the path slug, framed as "paste this after your public-host root"no scheme, no host, no decoration. The route at `/sites/*` (see [server/routes/sites.ts](../../../../ui/server/routes/sites.ts)) takes care of the trailing-slash redirect on the dir form, so the slug is correct as-emitted whether the operator's HTML uses an `index.html` entry-point or a publisher-named landing file.
57
+ 6. **Resolve the public hostname and emit the full URL.** Call `mcp__admin__public-hostname` to fetch this account's hostname; the tool reads `cloudflared/config.yml` + `alias-domains.json` — the same files the platform server trusts to route. Concatenate `https://<hostname><path-slug>` and surface that one URL to the operator. If the tool returns `reason: no-tunnel`, relay the tool's remediation message verbatim do not improvise the URL. The route at `/sites/*` (see [server/routes/sites.ts](../../../../ui/server/routes/sites.ts)) handles the trailing-slash redirect on the dir form, so the slug is correct as-emitted whether the operator's HTML uses an `index.html` entry-point or a publisher-named landing file.
58
58
 
59
59
  ## Log lines (grep targets)
60
60
 
@@ -66,7 +66,6 @@ The operator message for `ambiguous-html` names the candidate files and asks the
66
66
 
67
67
  ## Out of scope
68
68
 
69
- - Discovering or emitting the operator's public hostname. The hostname is whatever their tunnel terminates at — not knowable from inside this skill, and not the platform's concern. The operator pairs the slug with their own host root.
70
69
  - Cleanup of pre-existing synthetic files left by earlier sessions (e.g. a stray `index.html` produced by an earlier `cp brochure.html index.html` workaround). Refuse with `destination-occupied` and let the operator clean up explicitly.
71
70
  - Extraction. `unzip-attachment` already owns the extract step; the source path is its output.
72
71
  - DNS, tunnels, certificates, or any public-host configuration. The route at `/sites/*` is the wire contract; this skill is a placement + URL-shape skill only.
@@ -328,6 +328,8 @@ Each row in the Conversations modal exposes a `View logs` row-action that opens
328
328
 
329
329
  **Directory canonicalisation.** A request whose disk target is a directory is `301`'d to the trailing-slash form (query string preserved) before any body is served — RFC 3986 §5.3 base resolution requires the trailing slash so relative refs in the served HTML resolve under the directory, not its parent. After the redirect the route serves `<dir>/index.html` if it exists on disk; otherwise `404`. There is **no** implicit-`index.html` invention for missing paths — the publisher owns canonical URLs. A brochure shipped without `index.html` is reached at `/sites/<slug>/<file>.html`, and the admin skill `publish-site` is the sanctioned surface that moves the extracted tree under `<accountDir>/sites/<slug>/` and emits the canonical path slug. Operator-side: drop a brochure at `<accountDir>/sites/properties/<id>/brochure/output/` and it serves at `<public-host>/sites/properties/<id>/brochure/output/brochure.html` (or `<public-host>/sites/properties/<id>/brochure/output/` if that directory contains an `index.html`). See `.docs/web-chat.md` `/sites/*` route entry for the wire contract and `[sites]` log lines (`serve|redirect-trailing-slash|not-found|path-traversal-rejected|symlink-escape-rejected|no-account`).
330
330
 
331
+ **Deterministic public-hostname surface.** The `<public-host>` half of the URL the operator pastes is resolved by the `mcp__admin__public-hostname` MCP tool. It reads `<configDir>/cloudflared/config.yml` (ingress list) then falls back to `<configDir>/alias-domains.json` — the same two files `cloudflared` and `platform/ui/server/index.ts`'s `isPublicHost()` already trust to route. Returns `{hostname, isApex, source}` on hit (`source` is `"cloudflared-config.yml"` or `"alias-domains.json"`), or `{hostname:null, source:null, reason:"no-tunnel"}` on miss. Tiebreak: apex wins over subdomain (single-label, or `www.<apex>` stripped). `publish-site` step 6 calls it after the move and emits the full URL (`https://<hostname><path-slug>`) in the same turn. Graph queries are no longer involved — any earlier graph-backed resolver returned `(none)` on accounts bootstrapped without `cloudflare-task-tracker.ts` writes (laptop Real Agent, manual `cloudflared` setup), the `llm-framing-deterministic` recurrence class. The graph-mcp shim additionally runs a sequential envelope-warning probe on every read response — when Neo4j emits `gql_status` codes matching `^0[12]N5\d$` (e.g. `01N52` "property does not exist"), the shim stitches them into a prefix content block on the response so property-name misses surface to the agent inline instead of returning silent `[]`. Probe failure is best-effort: the upstream response forwards unchanged with `[mcp:graph] probe-error`.
332
+
331
333
  ### Cross-tab session rotation
332
334
 
333
335
  When you click "New conversation" in the chat tab, {{productName}} mints a fresh admin session key on the server and clears the old one. Sibling admin tabs (`/graph`, `/data`) opened in the same browser keep working without re-login: the chat tab broadcasts the new key on a same-origin channel so each sibling tab updates its captured key instantly, and any in-flight admin request that 401s with the rotation-orphan code retries once after re-reading the latest key from per-tab storage. If neither path recovers (browser locked down, second 401 after retry, session expired), the tab shows a single banner — "Your admin session was renewed in another tab. Click to reload." — and one click sends you back through login. No silent 401s; no re-clicking through the same trash icon hoping it sticks. See `.docs/web-chat.md` "Cross-tab rotation contract" for the wire-level `code` taxonomy and observability surfaces.
@@ -3,7 +3,7 @@ name: content-producer
3
3
  description: "Visual production and static-site hosting — reads from the populated graph to produce visual artifacts (image generation, PDF rendering, component delivery) and hosts already-prepared static sites by extracting attached archives via unzip-attachment then placing the tree under <accountDir>/sites/<slug>/ via publish-site. Delegate for: generating images, saving rendered pages as PDF, or any 'host this website' / 'publish this site' / 'put this online' intent carrying an HTML+assets archive. **Not** document ingestion — graph ingestion of any kind routes to `specialists:database-operator`. Static-site zips are extracted to disk for publication, never written to the graph."
4
4
  summary: "Produces visual output from your graph — generates images, renders pages to PDF, and hosts static websites you upload as a zip. For example, when you need a cover image for a brief, want to save a rendered page as PDF, or upload a brochure zip and ask to put it online."
5
5
  model: claude-sonnet-4-6
6
- tools: Bash, mcp__memory__memory-search, mcp__replicate__image-generate, mcp__plugin_playwright_playwright__browser_navigate, mcp__plugin_playwright_playwright__browser_snapshot, mcp__plugin_playwright_playwright__browser_take_screenshot, mcp__plugin_playwright_playwright__browser_pdf_save, mcp__admin__render-component, mcp__admin__file-attach, mcp__admin__plugin-read
6
+ tools: Bash, mcp__memory__memory-search, mcp__replicate__image-generate, mcp__plugin_playwright_playwright__browser_navigate, mcp__plugin_playwright_playwright__browser_snapshot, mcp__plugin_playwright_playwright__browser_take_screenshot, mcp__plugin_playwright_playwright__browser_pdf_save, mcp__admin__render-component, mcp__admin__file-attach, mcp__admin__plugin-read, mcp__admin__public-hostname
7
7
  ---
8
8
 
9
9
  # Content Producer