@avocadostudio-ai/orchestrator-core 0.11.2 → 0.11.4

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.
@@ -21,7 +21,7 @@ import { mkdir, writeFile, readFile } from "node:fs/promises";
21
21
  import { resolve, basename } from "node:path";
22
22
  import { randomUUID } from "node:crypto";
23
23
  import { z } from "zod";
24
- import { operationSchema, blockManifestSchema, siteConfigSchema, declareBlockCatalogue, undeclaredBlockTypes, EDITOR_PROTOCOL_VERSION } from "@avocadostudio-ai/shared";
24
+ import { operationSchema, blockManifestSchema, siteConfigSchema, declareBlockCatalogue, undeclaredBlockTypes, EDITOR_PROTOCOL_VERSION, withLoopbackSpellings } from "@avocadostudio-ai/shared";
25
25
  import { chatRequestBodySchema } from "../nlp/intent-detection.js";
26
26
  import { applyOpsAtomically, pickFocusBlockId, pickUpdatedSlug, toErrorDetail, classifyGuardrailError } from "../ops/ops-engine.js";
27
27
  import { runChatStream, formatSseFrame } from "../http/chat-stream.js";
@@ -230,7 +230,22 @@ function corsHeadersFor(request, config) {
230
230
  */
231
231
  return isProductionEnv() ? {} : { "access-control-allow-origin": origin, "vary": "Origin" };
232
232
  }
233
- return config.corsOrigins.includes(origin)
233
+ /*
234
+ * `localhost` and `127.0.0.1` are the same machine, so an allowlist naming
235
+ * one grants the other.
236
+ *
237
+ * A caller writes one spelling — the scaffold's `.env.local` writes
238
+ * `localhost`, our own CLI's banner prints `127.0.0.1` on its first line —
239
+ * and a browser at the other one had every request answered `200` with no
240
+ * `access-control-allow-origin` and discarded before any code could see it.
241
+ * The site's `frame-ancestors` list has expanded both spellings since it was
242
+ * written; this one did not, so the preview frame was permitted and every
243
+ * call inside it was dropped.
244
+ *
245
+ * Only these two names, and only when the configured origin already uses
246
+ * one. Every other widening of an allowlist stays an explicit decision.
247
+ */
248
+ return withLoopbackSpellings(config.corsOrigins).includes(origin)
234
249
  ? { "access-control-allow-origin": origin, "vary": "Origin" }
235
250
  : {};
236
251
  }
@@ -1571,6 +1586,23 @@ export function createOrchestrator(config = {}) {
1571
1586
  {
1572
1587
  id: effectiveSiteId ?? normalizeSiteId(query.siteId),
1573
1588
  mode: "library",
1589
+ /*
1590
+ * The configured name, which this route had never carried.
1591
+ *
1592
+ * `/status/planner` reports it and `/sites` did not, so the
1593
+ * editor's site registry — which is fed from here — had nothing
1594
+ * to show but the id, and fell back to title-casing it. A
1595
+ * project in `demo-a/` configured as "The Avocado Hub" was
1596
+ * greeted as "Demo A" in the header and in the first sentence of
1597
+ * the welcome message.
1598
+ *
1599
+ * `demoContent` for the same reason: it decides which first-screen
1600
+ * suggestions the editor offers, and depending on a single
1601
+ * endpoint for it is what made those suggestions lose a race in
1602
+ * production.
1603
+ */
1604
+ ...(config.siteName ? { name: config.siteName } : {}),
1605
+ demoContent: config.demoContent === true,
1574
1606
  ...(config.previewUrl ? { previewUrl: config.previewUrl } : {}),
1575
1607
  ...(config.draftPath ? { draftPath: config.draftPath } : {}),
1576
1608
  // A registration is more specific than a build-time default, so
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import { readFile } from "node:fs/promises";
19
19
  import { resolve } from "node:path";
20
+ import { withLoopbackSpellings } from "@avocadostudio-ai/shared";
20
21
  import { normalizeSession, scopedSessionKey, getSessionPages, getSiteConfig, publishedPages, publishStatusBySession, listPublishLog, findPublishLogForStatusUpdate, updatePublishLogStatusById } from "../state/session-state.js";
21
22
  import { computePublishDiff } from "../publish/diff-engine.js";
22
23
  import { refreshPublishStatusFromVercel } from "../publish/publish-helpers.js";
@@ -165,7 +166,15 @@ export function isSafeOrigin(raw) {
165
166
  if (isPrivate) {
166
167
  if (process.env.NODE_ENV !== "production")
167
168
  return true;
168
- const corsOrigins = (process.env.ORCHESTRATOR_CORS_ORIGINS ?? "").split(",").map((s) => s.trim().replace(/\/+$/, ""));
169
+ /*
170
+ * Both loopback spellings, for the same reason CORS grants both: the
171
+ * declaration and the request are written by different hands. The scaffold
172
+ * writes `ORCHESTRATOR_CORS_ORIGINS=http://localhost:3000`; a site opened
173
+ * at `127.0.0.1` sends the other one, and `publish/diff` answered
174
+ * `400 siteOrigin is not an allowed URL` for an origin the deployment had
175
+ * in fact declared trusted.
176
+ */
177
+ const corsOrigins = withLoopbackSpellings((process.env.ORCHESTRATOR_CORS_ORIGINS ?? "").split(",").map((s) => s.trim().replace(/\/+$/, "")));
169
178
  const normalized = parsed.origin;
170
179
  if (corsOrigins.some((o) => o === normalized))
171
180
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avocadostudio-ai/orchestrator-core",
3
- "version": "0.11.2",
3
+ "version": "0.11.4",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -22,8 +22,8 @@
22
22
  "openai": "^4.87.1",
23
23
  "sharp": "^0.35.4",
24
24
  "zod": "^4.3.6",
25
- "@avocadostudio-ai/shared": "^0.11.2",
26
- "@avocadostudio-ai/migration-sdk": "^0.11.2"
25
+ "@avocadostudio-ai/shared": "^0.11.4",
26
+ "@avocadostudio-ai/migration-sdk": "^0.11.4"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@anthropic-ai/claude-agent-sdk": "^0.3.220",