@ebitex/content-mcp 1.0.245 → 1.1.247
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/README.md +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/resources/helpCorpus.generated.d.ts.map +1 -1
- package/dist/resources/helpCorpus.generated.js +8 -3
- package/dist/resources/helpCorpus.generated.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +8 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +11 -1
- package/dist/session.js.map +1 -1
- package/dist/tools/whoami.d.ts.map +1 -1
- package/dist/tools/whoami.js +4 -2
- package/dist/tools/whoami.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ is hosted by ebitex, so your write-capable key never leaves your machine.
|
|
|
27
27
|
| `EBITEX_CONTENT_API_BASE` | Defaults to `https://api.ebitex.io` |
|
|
28
28
|
| `EBITEX_CONTENT_MCP_READ_ONLY` | Set to `1` to hide every write tool regardless of the key's scopes |
|
|
29
29
|
| `EBITEX_CONTENT_TARGET_MANAGEMENT_KEY` | A second key, for `content_transfer` to import into |
|
|
30
|
+
| `EBITEX_CONTENT_REALM` | The realm key the management key is expected to act in. Startup stops if it does not (a key is bound to one realm; this checks, it does not select). Unset: not checked |
|
|
30
31
|
|
|
31
32
|
## Mint the key deliberately
|
|
32
33
|
|
package/dist/config.d.ts
CHANGED
|
@@ -20,6 +20,11 @@ export declare const BASE_VAR = "EBITEX_CONTENT_API_BASE";
|
|
|
20
20
|
export declare const READ_ONLY_VAR = "EBITEX_CONTENT_MCP_READ_ONLY";
|
|
21
21
|
/** A second key, bound to the environment `content_transfer` imports *into*. */
|
|
22
22
|
export declare const TARGET_KEY_VAR = "EBITEX_CONTENT_TARGET_MANAGEMENT_KEY";
|
|
23
|
+
/**
|
|
24
|
+
* The realm this session expects its key to act in. A key is bound to one realm, so this never selects one: it is a check, made once against `GET /whoami`
|
|
25
|
+
* at startup, so a config block pointing at another brand's key stops instead of writing there. Unset, nothing is checked.
|
|
26
|
+
*/
|
|
27
|
+
export declare const REALM_VAR = "EBITEX_CONTENT_REALM";
|
|
23
28
|
export declare const DEFAULT_BASE = "https://api.ebitex.io";
|
|
24
29
|
export type ServerConfig = {
|
|
25
30
|
/** Origin only — `/content/management/v1` is appended by the client, never by a caller. */
|
|
@@ -28,6 +33,8 @@ export type ServerConfig = {
|
|
|
28
33
|
/** Present only when {@link TARGET_KEY_VAR} is set; `content_transfer` is otherwise export-only. */
|
|
29
34
|
targetKey: string | null;
|
|
30
35
|
readOnly: boolean;
|
|
36
|
+
/** The realm key the operator expects the key to act in, or null for no check. */
|
|
37
|
+
expectedRealm?: string | null;
|
|
31
38
|
};
|
|
32
39
|
export declare class ConfigError extends Error {
|
|
33
40
|
}
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gGAAgG;AAChG,eAAO,MAAM,OAAO,kCAAkC,CAAA;AAEtD,oFAAoF;AACpF,eAAO,MAAM,QAAQ,4BAA4B,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,iCAAiC,CAAA;AAE3D,gFAAgF;AAChF,eAAO,MAAM,cAAc,yCAAyC,CAAA;AAEpE,eAAO,MAAM,YAAY,0BAA0B,CAAA;AAEnD,MAAM,MAAM,YAAY,GAAG;IACzB,2FAA2F;IAC3F,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,oGAAoG;IACpG,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gGAAgG;AAChG,eAAO,MAAM,OAAO,kCAAkC,CAAA;AAEtD,oFAAoF;AACpF,eAAO,MAAM,QAAQ,4BAA4B,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,iCAAiC,CAAA;AAE3D,gFAAgF;AAChF,eAAO,MAAM,cAAc,yCAAyC,CAAA;AAEpE;;;GAGG;AACH,eAAO,MAAM,SAAS,yBAAyB,CAAA;AAE/C,eAAO,MAAM,YAAY,0BAA0B,CAAA;AAEnD,MAAM,MAAM,YAAY,GAAG;IACzB,2FAA2F;IAC3F,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,oGAAoG;IACpG,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;IACjB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B,CAAA;AAED,qBAAa,WAAY,SAAQ,KAAK;CAAG;AAEzC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,YAAY,CA8B9F"}
|
package/dist/config.js
CHANGED
|
@@ -20,6 +20,11 @@ export const BASE_VAR = 'EBITEX_CONTENT_API_BASE';
|
|
|
20
20
|
export const READ_ONLY_VAR = 'EBITEX_CONTENT_MCP_READ_ONLY';
|
|
21
21
|
/** A second key, bound to the environment `content_transfer` imports *into*. */
|
|
22
22
|
export const TARGET_KEY_VAR = 'EBITEX_CONTENT_TARGET_MANAGEMENT_KEY';
|
|
23
|
+
/**
|
|
24
|
+
* The realm this session expects its key to act in. A key is bound to one realm, so this never selects one: it is a check, made once against `GET /whoami`
|
|
25
|
+
* at startup, so a config block pointing at another brand's key stops instead of writing there. Unset, nothing is checked.
|
|
26
|
+
*/
|
|
27
|
+
export const REALM_VAR = 'EBITEX_CONTENT_REALM';
|
|
23
28
|
export const DEFAULT_BASE = 'https://api.ebitex.io';
|
|
24
29
|
export class ConfigError extends Error {
|
|
25
30
|
}
|
|
@@ -54,6 +59,7 @@ export function readConfig(env = process.env) {
|
|
|
54
59
|
key,
|
|
55
60
|
targetKey: env[TARGET_KEY_VAR]?.trim() || null,
|
|
56
61
|
readOnly: isTruthy(env[READ_ONLY_VAR]),
|
|
62
|
+
expectedRealm: env[REALM_VAR]?.trim() || null,
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
function isTruthy(value) {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gGAAgG;AAChG,MAAM,CAAC,MAAM,OAAO,GAAG,+BAA+B,CAAA;AAEtD,oFAAoF;AACpF,MAAM,CAAC,MAAM,QAAQ,GAAG,yBAAyB,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,8BAA8B,CAAA;AAE3D,gFAAgF;AAChF,MAAM,CAAC,MAAM,cAAc,GAAG,sCAAsC,CAAA;AAEpE,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gGAAgG;AAChG,MAAM,CAAC,MAAM,OAAO,GAAG,+BAA+B,CAAA;AAEtD,oFAAoF;AACpF,MAAM,CAAC,MAAM,QAAQ,GAAG,yBAAyB,CAAA;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,8BAA8B,CAAA;AAE3D,gFAAgF;AAChF,MAAM,CAAC,MAAM,cAAc,GAAG,sCAAsC,CAAA;AAEpE;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,sBAAsB,CAAA;AAE/C,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAA;AAanD,MAAM,OAAO,WAAY,SAAQ,KAAK;CAAG;AAEzC;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAA0C,OAAO,CAAC,GAAG;IAC9E,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAA;IAChC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,WAAW;QACnB,kFAAkF;QAClF,4FAA4F;QAC5F,6FAA6F;QAC7F,wFAAwF;QACxF,GAAG,OAAO,mDAAmD;YAC3D,kFAAkF,CACrF,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,YAAY,CAAA;IAClD,IAAI,OAAe,CAAA;IACnB,IAAI,CAAC;QACH,8FAA8F;QAC9F,yEAAyE;QACzE,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAA;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,WAAW,CAAC,GAAG,QAAQ,wBAAwB,IAAI,EAAE,CAAC,CAAA;IAClE,CAAC;IAED,OAAO;QACL,OAAO;QACP,GAAG;QACH,SAAS,EAAE,GAAG,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI;QAC9C,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACtC,aAAa,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI;KAC9C,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAyB;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC7C,OAAO,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,KAAK,CAAA;AAC5E,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpCorpus.generated.d.ts","sourceRoot":"","sources":["../../src/resources/helpCorpus.generated.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAErE,eAAO,MAAM,WAAW,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"helpCorpus.generated.d.ts","sourceRoot":"","sources":["../../src/resources/helpCorpus.generated.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAErE,eAAO,MAAM,WAAW,EAAE,SAAS,EAkJlC,CAAA"}
|
|
@@ -36,7 +36,7 @@ export const HELP_TOPICS = [
|
|
|
36
36
|
{
|
|
37
37
|
"slug": "content-environments",
|
|
38
38
|
"title": "Content environments",
|
|
39
|
-
"body": "# Content environments\n\nBy default, your organization has one authoring space and one place published content goes live —\nyou'll never need this page if that's all you use. If you want separate spaces to draft in (a\nStaging environment you can experiment in before anything reaches Production, say), this is where\nthat's configured.\n\n## What's an environment?\n\nThere are two kinds:\n\n- **Authoring environments** are separate drafting spaces. Contracts, Templates, folders,\n Components and the Experience tree are specific to whichever authoring\n environment you're currently working in — content in one is completely invisible from another,\n as if they were separate organizations.\n- **Delivery environments** are separate published targets — what the Content Delivery API actually\n serves to the outside world, and what a delivery key reads from.\n\nEvery organization starts with one of each, marked **Default**. The default is where publishing,\nnew delivery keys, and the environment switcher land when nothing more specific is chosen — you can\nchange which environment holds that role at any time (see **Managing environments** below).\n\n## Switching environments\n\nIf more than one authoring environment exists, a switcher appears in the header next to your\naccount. Pick one to start working there — every page reloads to show that environment's own\ncontent. Your choice is remembered per-organization, so it's still selected next time you sign in.\n\n## Managing environments\n\nOpen **Environments** under **Configure** in the app navigation (requires the \"Manage Environments\"\npermission — ask an Owner or administrator if it's not available to you). The page has four tabs:\n**Environments** (the authoring and delivery lists), **Topology**, **Members** and **Access rules**.\n\n- **Create** a new authoring or delivery environment, give it a name. Your plan limits authoring\n and delivery environments **separately**, not as one combined total — Subscriptions & Billing\n shows both, with how many you're using. A newly created environment is added to the end of its\n list.\n- **Reorder** the authoring or delivery list to whatever arrangement makes sense to your team —\n drag a row to where you want it, or use the ▲/▼ buttons on each row. While dragging, the row\n shows as a placeholder that moves to preview exactly where it'll land if you release. This sets\n the order these lists and the environment switcher use, and where a not-yet-arranged box is\n placed on the topology canvas below; once you've dragged a box on the canvas, the canvas keeps\n where you put it. (A list with only one environment has nothing to reorder against, so dragging\n is unavailable there.)\n- **Rename** any environment at any time — nothing else changes.\n- **Set as default**: on any non-default environment's row, this moves the Default badge to it and\n clears it from wherever it was. Publishing, new delivery keys, and the environment switcher's own\n default all follow immediately.\n- **Delete** any environment except the last remaining one of its kind. This is permanent and\n immediate: everything authored (or, for a delivery environment, everything published) there is\n gone, with no recovery. You'll be asked to type the environment's name to confirm. If the\n environment currently publishes to a delivery target, promotes into another environment, or is\n itself a promotion target, the confirmation names those connections — deleting still removes them\n along with everything else. The current default can't be deleted directly while another\n environment of its kind exists — set a different one as default first, then delete it.\n- **Refresh from…**: replace this environment's content wholesale from another authoring\n environment — the routine \"copy Production down into Staging\" move. See\n [environment-refresh.md](environment-refresh.md) for the full workflow, including how to rescue\n work that would otherwise be lost.\n\n### The topology canvas\n\nOne canvas shows every way your environments connect to each other. Each environment is a box with\nfour small circles — **connectors** — one on each side. Each connection is a labelled line drawn\nbetween two boxes, and every line points the same way: **content can move along the arrow**.\n\nThere are two kinds, and the legend under the canvas names both:\n\n- **publishes to** — an authoring environment publishes to a delivery environment.\n- **promotes into** — an authoring environment can promote content into another authoring\n environment.\n\nSharing content across a family of brands or sites is not drawn here: that is what **realms** are for, and they are not environments.\n\nBoth are routing, not content. Changing one redirects where the next publish or promote\nlands; it does not move anything by itself.\n\n**Drawing a connection.** Select a connector on the environment you want to connect *from*, then a\nconnector on the one you want to connect *to*. It works the same with a mouse (click one then the\nother, or drag between them) and with the keyboard alone (<kbd>Tab</kbd> to a connector,\n<kbd>Enter</kbd>, <kbd>Tab</kbd> to another, <kbd>Enter</kbd>). <kbd>Escape</kbd> cancels at any\npoint.\n\n**The connectors you choose are the ones the line uses**, and they stay that way — moving a box\nre-routes the line between the same two connectors rather than picking different ones. Pick whichever\nsides make your topology read best; if you want a line to come in from below, connect to the bottom\nconnector and that is where it will arrive, for everyone, after every reload.\n\nConnecting two *authoring* environments means promotion: **{A} promotes into {B}**. Everything else is\ndecided by what you connected.\n\n**A delivery environment only receives.** Its box is marked *Receives only*, and its connectors\ncan't start a connection at all — there's nothing to click or drag from. That's not a rule you'll\ndiscover by being told off after the fact; the canvas simply can't draw the line.\n\n**When something isn't allowed**, a short message appears where you dropped the connection and\nnothing is saved:\n\n- *That would create a cycle in the promotion pipeline* — content can't\n flow in a circle, directly or through a chain of other environments.\n- *Already connected* — that exact connection is already there.\n\n**A delivery environment can be published from only one authoring environment at a time.** Connect\none that's already claimed and you'll be asked to confirm reassigning it, naming the environment\nthat holds it now; confirming removes the other mapping. An authoring environment can promote into\nany number of others, and any number can promote into the same target — promotion has no such\nexclusivity.\n\n**Removing a connection.** Select the **×** on the line's label, or **Remove** on the matching row\nin the Connections list below. Publishing and promotion lines go immediately — they're routing, and\nremoving one destroys nothing.\n\n**Removing a publishing line does not take your live site down.** Anything already published stays\npublished and keeps being served exactly as before: a published page is a finished copy held by the\ndelivery environment, not a view onto the authoring one. What stops is publishing itself — until you\ndraw the line again, you can't publish anything new to that environment or unpublish anything from\nit, and you'll be told so if you try. A delivery box in that state says *Still serving content\npublished from …* so it's clear the site is up even though nothing is connected to it.\n\n**Arranging the canvas.** Drag any box to where you want it. The arrangement is saved for your whole\norganization, so everyone sees the topology laid out the way you left it. Pan by dragging the\nbackground, zoom with the scroll wheel or the +/− buttons, and use the ⤢ button to fill the screen\n(<kbd>Escape</kbd> leaves full screen). An environment nobody has arranged yet is placed\nautomatically — authoring on the left, delivery on the right, in list order.\n\n**The Connections list** below the canvas says the same thing in words — *Live publishes to\nProduction* — grouped by the environment each connection starts from, with its own Remove on every\nrow. Use whichever you prefer; they're the same connections and the same actions.\n\nWithout the \"Manage Environments\" permission the canvas is read-only: you can pan, zoom, go full\nscreen and read the Connections list, but nothing offers to change anything.\n\n## Limiting who can use an environment\n\nBy default every member of your organization can open every authoring environment, with the role\nthey hold in the organization. That's usually what you want. When it isn't — an agency working on\none client's site, a contractor who should only see a Staging environment — you can limit a member\nto specific environments, and give them a different role in each.\n\nOpen **Environment access** at the bottom of the Environments page. Pick an environment, then choose\na member and the role they should have *in that environment*, and select **Bind member**. You can\nchange a bound member's role there at any time, or **Remove** them.\n\nThis needs the \"Assign member roles\" permission — the same one Hub uses to change a member's role —\nbecause giving someone a role in an environment is giving them that role. You also need \"View roles\",\nso the section can list the roles to choose from; if you have only one of the two, the section says\nwhich is missing.\n\n**What changes for a bound member:**\n\n- They can open **only** the environments they're bound to. The environment switcher shows just\n those, and anything that names another environment is refused.\n- In each one they have the role you chose there, not their organization role — so someone can be\n an editor in one environment and read-only in another.\n- The topology canvas is hidden from them, because it would show connections to environments they\n can't see.\n\n**Owners are never limited.** They always reach every environment with full access, so they don't\nappear in the member list and the Owner role can't be chosen.\n\n**Removing a member's last binding gives them back access to every environment** with their\norganization role — it doesn't lock them out. Having no bindings at all is the normal, unrestricted\nstate. To take someone out of Content entirely, change their role in Hub instead.\n\n**Uploaded files follow the same boundary.** Someone limited to certain environments can open an\nimage or file that's used by content in one of those environments, or that they uploaded there\nthemselves. Anything else reports as not found, exactly as a file that doesn't exist would — so they\ncan't tell what exists elsewhere. Nobody unrestricted, and no Owner, is affected.\n\n**Setting up an outside collaborator.** Give them a minimal organization role in Hub, then bind their\nreal role per environment here — **Content Author** is usually the one you want. Their organization\nrole is what applies everywhere outside Content, and the bound role is what applies inside it. See\n**Working with someone outside your team** in the Hub organizations guide for the Hub half, and\n**What roles can do in an environment** below if they should write content but not publish it.\n\n## What roles can do in an environment\n\nBeing able to open an environment isn't the same as being able to do everything in it. **What roles\ncan do here**, below Environment access on the Environments page, sets what each role may do\nthroughout one authoring environment.\n\nTick a role for **Edit**, **Delete** or **Publish**. Viewing is included automatically with any of\nthem, so there's no separate tick for it.\n\nThe part that reads backwards, so it's worth saying plainly: **an action with no ticks at all is\nunrestricted.** Anyone who can open the environment can do it. Ticking roles is what restricts an\naction to those roles, and removing the last tick for an action makes it unrestricted again — so\nclearing ticks *widens* access rather than removing it. An environment where you've never set\nanything up behaves exactly as it always has.\n\nThis is the only place that can restrict content filed outside any folder. Rules you set on a\nfolder or a page still win for that branch, so the usual shape is: restrict publishing across the\nenvironment here, then grant it back on the one section a team owns.\n\nA typical setup for a writer who shouldn't publish:\n\n1. Tick **Edit** for Content Author, so editing is restricted to roles you name — including them.\n2. Leave **Publish** ticked only for the roles that should publish. Content Author isn't one.\n3. They can now write and change content anywhere in the environment, and publish nothing.\n\nAnyone holding the \"Manage access\" permission is unaffected by these rules, so an environment can\nnever be configured into a state nobody can fix.\n\n## Promoting content\n\nPromoting copies a Component or Experience node — and everything it needs (its Contract, Template,\nreferenced Components, ancestor folders) — from the\nenvironment you're in into another one, ready to be worked on or published there. This is different\nfrom publishing: promoting moves *draft* content between authoring spaces; publishing takes draft\ncontent live to visitors.\n\nSelect **Promote…** on a Component or Experience node — a target is already picked for you (the\nfirst one configured, changeable from the dropdown if more than one exists), so you'll usually go\nstraight to reviewing the plan below rather than choosing first. If your current environment has no\npromotion target configured at all, **Promote…** is disabled with an explanation — set one up under\n[Managing environments](#managing-environments) first. Once a target is resolved, you'll see\neverything about to move, each marked:\n\n- **New** — doesn't exist in the target yet.\n- **Update** — already exists there, unchanged since it was last promoted; safe to overwrite.\n- **Diverged** — already exists there, but has been edited directly in the target (or promoted from\n somewhere else) since the last time this same source promoted it. You'll need to explicitly\n confirm overwriting it — nothing diverged is ever silently replaced. Whatever was there before\n stays recoverable in its own version history.\n\nContracts, Templates, and ancestor folders/nodes always move along with the item you're\npromoting — there's no separate choice for those. Referenced Components can be unchecked\nindividually if you don't want them to move yet; a skipped reference is simply left as-is in the\ntarget, pointing at whatever (if anything) is already there. If the item you're skipping doesn't\nexist in the target at all yet, a note appears explaining that this leaves a dangling reference\nthere until it's promoted — see [Unresolved references](#unresolved-references-component-sync)\nbelow for how to find and fix one later.\n\nPromoting requires the same **Publish** permission the item's own branch requires; promoting a\nContract or Template along with it additionally requires the permission to manage that\nkind of thing directly.\n\n### Promoting a Contract or Template on its own\n\nA schema-only change — a field added to a Contract, an adjusted Template — can be promoted by itself, without touching any Component that happens to use it.\nSelect **Promote…** directly from the Contract builder, or the Template builder. The same **New**/**Update**/**Diverged** states and\noverwrite confirmation apply; a Template's own settings Contract and a provider-contributed\nContract can't be promoted this way (promote the Template instead, or refresh/manage the provider\nregistration). This needs the permission to manage that kind of thing directly — there's no\n**Publish** branch involved, since Contracts/Templates aren't organized into branches.\n\n## Unresolved references (Component Sync)\n\nSkipping a referenced Component during promotion (above) — or publishing a Component whose\nreference was already skipped in an earlier promotion — leaves that reference pointing at\nsomething that doesn't exist in this environment. This is allowed on purpose: it never blocks a\npromotion or a publish, and delivery itself handles it gracefully (the missing reference is simply\nomitted from what's served). But nothing else ever tells you it happened unless you go looking, so\n**Component Sync**, under **Tools** in the app navigation, lists every one currently in your\nenvironment: which Component or Experience node has the broken reference, which field it's in, and\n— where this environment has a direct upstream promotion source that still has the missing item —\nwhich environment that is.\n\nYou'll also see it called out in two other places, right where it matters: opening a Component\nwhose reference is broken shows an **Unresolved** badge in place of the usual resolved name, with a\nlink straight to its Component Sync entry; and publishing a closure that contains one shows a\nnon-blocking banner naming the affected item (publishing still proceeds — this is a heads-up, not a\nnew restriction).\n\n**To fix one:** promote the missing item forward from the environment Component Sync names — but\nthat step alone isn't enough on its own, because promoting the missing item doesn't touch the\nComponent that references it. Once the missing item exists here, **re-promote the referencing\nComponent too** (from wherever you originally promoted it), so its reference is re-checked against\nwhat's now available. After that second promotion, the entry disappears from Component Sync, the\nbadge on the Component resolves back to a normal name, and any publish banner clears.\n\n## Sharing content across brands and sites\n\nEnvironments are lifecycle stages (Staging, then Live). They no longer share content with each other through a parent: the old \"inherits from\" link and the **Sync now**\nbutton are gone. If you have content a whole family of brands or sites should all draw from (shared design-system blocks, legal boilerplate, a common set of media\nContracts), that is what **realms** do: a realm reads its own content and its ancestors' automatically, with nothing to sync, and an edit to something it inherits is\nwritten in the realm that owns it.\n\nIf your organization had two environments linked by inheritance before realms, nothing was deleted when the link was removed: the copies in the child environment are\nnow ordinary content of that environment, which you can edit freely. They no longer follow the parent.\n\n## How this affects everything else\n\nIf you never create a second environment, nothing here changes anything: the switcher never\nappears, publishing and delivery keys keep using your one delivery environment automatically, and\nthere's no promotion to do. See [publishing.md](publishing.md) for the publish lifecycle,\n[content-personalization.md](content-personalization.md) for Audiences (which, unlike everything else\nyou author, belong to your organization rather than one environment), and [content-providers.md](content-providers.md) for\ncopying a registered content provider between environments.\n"
|
|
39
|
+
"body": "# Content environments\n\nBy default, your organization has one authoring space and one place published content goes live —\nyou'll never need this page if that's all you use. If you want separate spaces to draft in (a\nStaging environment you can experiment in before anything reaches Production, say), this is where\nthat's configured.\n\n## What's an environment?\n\nThere are two kinds:\n\n- **Authoring environments** are separate drafting spaces. Contracts, Templates, folders,\n Components and the Experience tree are specific to whichever authoring\n environment you're currently working in — content in one is completely invisible from another,\n as if they were separate organizations.\n- **Delivery environments** are separate published targets — what the Content Delivery API actually\n serves to the outside world, and what a delivery key reads from.\n\nEvery organization starts with one of each, marked **Default**. The default is where publishing,\nnew delivery keys, and the environment switcher land when nothing more specific is chosen — you can\nchange which environment holds that role at any time (see **Managing environments** below).\n\n## Switching environments\n\nIf more than one authoring environment exists, a switcher appears in the header next to your\naccount. Pick one to start working there — every page reloads to show that environment's own\ncontent. Your choice is remembered per-organization, so it's still selected next time you sign in.\n\n## Managing environments\n\nOpen **Environments** under **Configure** in the app navigation (requires the \"Manage Environments\"\npermission — ask an Owner or administrator if it's not available to you). The page has four tabs:\n**Environments** (the authoring and delivery lists), **Topology**, **Members** and **Access rules**.\n\n- **Create** a new authoring or delivery environment, give it a name. Your plan limits authoring\n and delivery environments **separately**, not as one combined total — Subscriptions & Billing\n shows both, with how many you're using. A newly created environment is added to the end of its\n list.\n- **Reorder** the authoring or delivery list to whatever arrangement makes sense to your team —\n drag a row to where you want it, or use the ▲/▼ buttons on each row. While dragging, the row\n shows as a placeholder that moves to preview exactly where it'll land if you release. This sets\n the order these lists and the environment switcher use, and where a not-yet-arranged box is\n placed on the topology canvas below; once you've dragged a box on the canvas, the canvas keeps\n where you put it. (A list with only one environment has nothing to reorder against, so dragging\n is unavailable there.)\n- **Rename** any environment at any time — nothing else changes.\n- **Set as default**: on any non-default environment's row, this moves the Default badge to it and\n clears it from wherever it was. Publishing, new delivery keys, and the environment switcher's own\n default all follow immediately.\n- **Delete** any environment except the last remaining one of its kind. This is permanent and\n immediate: everything authored (or, for a delivery environment, everything published) there is\n gone, with no recovery. You'll be asked to type the environment's name to confirm. If the\n environment currently publishes to a delivery target, promotes into another environment, or is\n itself a promotion target, the confirmation names those connections — deleting still removes them\n along with everything else. The current default can't be deleted directly while another\n environment of its kind exists — set a different one as default first, then delete it.\n- **Refresh from…**: replace this environment's content wholesale from another authoring\n environment — the routine \"copy Production down into Staging\" move. See\n [environment-refresh.md](environment-refresh.md) for the full workflow, including how to rescue\n work that would otherwise be lost.\n\n### The topology canvas\n\nOne canvas shows every way your environments connect to each other. Each environment is a box with\nfour small circles — **connectors** — one on each side. Each connection is a labelled line drawn\nbetween two boxes, and every line points the same way: **content can move along the arrow**.\n\nThere are two kinds, and the legend under the canvas names both:\n\n- **publishes to** — an authoring environment publishes to a delivery environment.\n- **promotes into** — an authoring environment can promote content into another authoring\n environment.\n\nSharing content across a family of brands or sites is not drawn here: that is what [realms](realms.md) are for, and they are not environments.\n\nBoth are routing, not content. Changing one redirects where the next publish or promote\nlands; it does not move anything by itself.\n\n**Drawing a connection.** Select a connector on the environment you want to connect *from*, then a\nconnector on the one you want to connect *to*. It works the same with a mouse (click one then the\nother, or drag between them) and with the keyboard alone (<kbd>Tab</kbd> to a connector,\n<kbd>Enter</kbd>, <kbd>Tab</kbd> to another, <kbd>Enter</kbd>). <kbd>Escape</kbd> cancels at any\npoint.\n\n**The connectors you choose are the ones the line uses**, and they stay that way — moving a box\nre-routes the line between the same two connectors rather than picking different ones. Pick whichever\nsides make your topology read best; if you want a line to come in from below, connect to the bottom\nconnector and that is where it will arrive, for everyone, after every reload.\n\nConnecting two *authoring* environments means promotion: **{A} promotes into {B}**. Everything else is\ndecided by what you connected.\n\n**A delivery environment only receives.** Its box is marked *Receives only*, and its connectors\ncan't start a connection at all — there's nothing to click or drag from. That's not a rule you'll\ndiscover by being told off after the fact; the canvas simply can't draw the line.\n\n**When something isn't allowed**, a short message appears where you dropped the connection and\nnothing is saved:\n\n- *That would create a cycle in the promotion pipeline* — content can't\n flow in a circle, directly or through a chain of other environments.\n- *Already connected* — that exact connection is already there.\n\n**A delivery environment can be published from only one authoring environment at a time.** Connect\none that's already claimed and you'll be asked to confirm reassigning it, naming the environment\nthat holds it now; confirming removes the other mapping. An authoring environment can promote into\nany number of others, and any number can promote into the same target — promotion has no such\nexclusivity.\n\n**Removing a connection.** Select the **×** on the line's label, or **Remove** on the matching row\nin the Connections list below. Publishing and promotion lines go immediately — they're routing, and\nremoving one destroys nothing.\n\n**Removing a publishing line does not take your live site down.** Anything already published stays\npublished and keeps being served exactly as before: a published page is a finished copy held by the\ndelivery environment, not a view onto the authoring one. What stops is publishing itself — until you\ndraw the line again, you can't publish anything new to that environment or unpublish anything from\nit, and you'll be told so if you try. A delivery box in that state says *Still serving content\npublished from …* so it's clear the site is up even though nothing is connected to it.\n\n**Arranging the canvas.** Drag any box to where you want it. The arrangement is saved for your whole\norganization, so everyone sees the topology laid out the way you left it. Pan by dragging the\nbackground, zoom with the scroll wheel or the +/− buttons, and use the ⤢ button to fill the screen\n(<kbd>Escape</kbd> leaves full screen). An environment nobody has arranged yet is placed\nautomatically — authoring on the left, delivery on the right, in list order.\n\n**The Connections list** below the canvas says the same thing in words — *Live publishes to\nProduction* — grouped by the environment each connection starts from, with its own Remove on every\nrow. Use whichever you prefer; they're the same connections and the same actions.\n\nWithout the \"Manage Environments\" permission the canvas is read-only: you can pan, zoom, go full\nscreen and read the Connections list, but nothing offers to change anything.\n\n## Limiting who can use an environment\n\nBy default every member of your organization can open every authoring environment, with the role\nthey hold in the organization. That's usually what you want. When it isn't — an agency working on\none client's site, a contractor who should only see a Staging environment — you can limit a member\nto specific environments, and give them a different role in each.\n\nOpen **Environment access** at the bottom of the Environments page. Pick an environment, then choose\na member and the role they should have *in that environment*, and select **Bind member**. You can\nchange a bound member's role there at any time, or **Remove** them.\n\nThis needs the \"Assign member roles\" permission — the same one Hub uses to change a member's role —\nbecause giving someone a role in an environment is giving them that role. You also need \"View roles\",\nso the section can list the roles to choose from; if you have only one of the two, the section says\nwhich is missing.\n\n**What changes for a bound member:**\n\n- They can open **only** the environments they're bound to. The environment switcher shows just\n those, and anything that names another environment is refused.\n- In each one they have the role you chose there, not their organization role — so someone can be\n an editor in one environment and read-only in another.\n- The topology canvas is hidden from them, because it would show connections to environments they\n can't see.\n\n**Owners are never limited.** They always reach every environment with full access, so they don't\nappear in the member list and the Owner role can't be chosen.\n\n**Removing a member's last binding gives them back access to every environment** with their\norganization role — it doesn't lock them out. Having no bindings at all is the normal, unrestricted\nstate. To take someone out of Content entirely, change their role in Hub instead.\n\n**Uploaded files follow the same boundary.** Someone limited to certain environments can open an\nimage or file that's used by content in one of those environments, or that they uploaded there\nthemselves. Anything else reports as not found, exactly as a file that doesn't exist would — so they\ncan't tell what exists elsewhere. Nobody unrestricted, and no Owner, is affected.\n\n**Setting up an outside collaborator.** Give them a minimal organization role in Hub, then bind their\nreal role per environment here — **Content Author** is usually the one you want. Their organization\nrole is what applies everywhere outside Content, and the bound role is what applies inside it. See\n**Working with someone outside your team** in the Hub organizations guide for the Hub half, and\n**What roles can do in an environment** below if they should write content but not publish it.\n\n## What roles can do in an environment\n\nBeing able to open an environment isn't the same as being able to do everything in it. **What roles\ncan do here**, below Environment access on the Environments page, sets what each role may do\nthroughout one authoring environment.\n\nTick a role for **Edit**, **Delete** or **Publish**. Viewing is included automatically with any of\nthem, so there's no separate tick for it.\n\nThe part that reads backwards, so it's worth saying plainly: **an action with no ticks at all is\nunrestricted.** Anyone who can open the environment can do it. Ticking roles is what restricts an\naction to those roles, and removing the last tick for an action makes it unrestricted again — so\nclearing ticks *widens* access rather than removing it. An environment where you've never set\nanything up behaves exactly as it always has.\n\nThis is the only place that can restrict content filed outside any folder. Rules you set on a\nfolder or a page still win for that branch, so the usual shape is: restrict publishing across the\nenvironment here, then grant it back on the one section a team owns.\n\nA typical setup for a writer who shouldn't publish:\n\n1. Tick **Edit** for Content Author, so editing is restricted to roles you name — including them.\n2. Leave **Publish** ticked only for the roles that should publish. Content Author isn't one.\n3. They can now write and change content anywhere in the environment, and publish nothing.\n\nAnyone holding the \"Manage access\" permission is unaffected by these rules, so an environment can\nnever be configured into a state nobody can fix.\n\n## Promoting content\n\nPromoting copies a Component or Experience node — and everything it needs (its Contract, Template,\nreferenced Components, ancestor folders) — from the\nenvironment you're in into another one, ready to be worked on or published there. This is different\nfrom publishing: promoting moves *draft* content between authoring spaces; publishing takes draft\ncontent live to visitors.\n\nSelect **Promote…** on a Component or Experience node — a target is already picked for you (the\nfirst one configured, changeable from the dropdown if more than one exists), so you'll usually go\nstraight to reviewing the plan below rather than choosing first. If your current environment has no\npromotion target configured at all, **Promote…** is disabled with an explanation — set one up under\n[Managing environments](#managing-environments) first. Once a target is resolved, you'll see\neverything about to move, each marked:\n\n- **New** — doesn't exist in the target yet.\n- **Update** — already exists there, unchanged since it was last promoted; safe to overwrite.\n- **Diverged** — already exists there, but has been edited directly in the target (or promoted from\n somewhere else) since the last time this same source promoted it. You'll need to explicitly\n confirm overwriting it — nothing diverged is ever silently replaced. Whatever was there before\n stays recoverable in its own version history.\n\nContracts, Templates, and ancestor folders/nodes always move along with the item you're\npromoting — there's no separate choice for those. Referenced Components can be unchecked\nindividually if you don't want them to move yet; a skipped reference is simply left as-is in the\ntarget, pointing at whatever (if anything) is already there. If the item you're skipping doesn't\nexist in the target at all yet, a note appears explaining that this leaves a dangling reference\nthere until it's promoted — see [Unresolved references](#unresolved-references-component-sync)\nbelow for how to find and fix one later.\n\nPromoting requires the same **Publish** permission the item's own branch requires; promoting a\nContract or Template along with it additionally requires the permission to manage that\nkind of thing directly.\n\n### Promoting a Contract or Template on its own\n\nA schema-only change — a field added to a Contract, an adjusted Template — can be promoted by itself, without touching any Component that happens to use it.\nSelect **Promote…** directly from the Contract builder, or the Template builder. The same **New**/**Update**/**Diverged** states and\noverwrite confirmation apply; a Template's own settings Contract and a provider-contributed\nContract can't be promoted this way (promote the Template instead, or refresh/manage the provider\nregistration). This needs the permission to manage that kind of thing directly — there's no\n**Publish** branch involved, since Contracts/Templates aren't organized into branches.\n\n## Unresolved references (Component Sync)\n\nSkipping a referenced Component during promotion (above) — or publishing a Component whose\nreference was already skipped in an earlier promotion — leaves that reference pointing at\nsomething that doesn't exist in this environment. This is allowed on purpose: it never blocks a\npromotion or a publish, and delivery itself handles it gracefully (the missing reference is simply\nomitted from what's served). But nothing else ever tells you it happened unless you go looking, so\n**Component Sync**, under **Tools** in the app navigation, lists every one currently in your\nenvironment: which Component or Experience node has the broken reference, which field it's in, and\n— where this environment has a direct upstream promotion source that still has the missing item —\nwhich environment that is.\n\nYou'll also see it called out in two other places, right where it matters: opening a Component\nwhose reference is broken shows an **Unresolved** badge in place of the usual resolved name, with a\nlink straight to its Component Sync entry; and publishing a closure that contains one shows a\nnon-blocking banner naming the affected item (publishing still proceeds — this is a heads-up, not a\nnew restriction).\n\n**To fix one:** promote the missing item forward from the environment Component Sync names — but\nthat step alone isn't enough on its own, because promoting the missing item doesn't touch the\nComponent that references it. Once the missing item exists here, **re-promote the referencing\nComponent too** (from wherever you originally promoted it), so its reference is re-checked against\nwhat's now available. After that second promotion, the entry disappears from Component Sync, the\nbadge on the Component resolves back to a normal name, and any publish banner clears.\n\n## Sharing content across brands and sites\n\nEnvironments are lifecycle stages (Staging, then Live). They no longer share content with each other through a parent: the old \"inherits from\" link and the **Sync now**\nbutton are gone. If you have content a whole family of brands or sites should all draw from (shared design-system blocks, legal boilerplate, a common set of media\nContracts), that is what **realms** do: a realm reads its own content and its ancestors' automatically, with nothing to sync, and an edit to something it inherits is\nwritten in the realm that owns it. See [realms.md](realms.md) for the whole picture, including who can enter which realm and how promotion handles content that comes from a realm above.\n\nIf your organization had two environments linked by inheritance before realms, nothing was deleted when the link was removed: the copies in the child environment are\nnow ordinary content of that environment, which you can edit freely. They no longer follow the parent.\n\n## How this affects everything else\n\nIf you never create a second environment, nothing here changes anything: the switcher never\nappears, publishing and delivery keys keep using your one delivery environment automatically, and\nthere's no promotion to do. See [publishing.md](publishing.md) for the publish lifecycle,\n[content-personalization.md](content-personalization.md) for Audiences (which, unlike everything else\nyou author, belong to your organization rather than one environment), and [content-providers.md](content-providers.md) for\ncopying a registered content provider between environments.\n"
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"slug": "content-links",
|
|
@@ -76,7 +76,7 @@ export const HELP_TOPICS = [
|
|
|
76
76
|
{
|
|
77
77
|
"slug": "deployment-keys",
|
|
78
78
|
"title": "Deployment keys",
|
|
79
|
-
"body": "# Deployment keys\n\nA **deployment key** lets a release pipeline promote content from one environment to the next, for\nexample from Staging to Production, without anyone clicking **Promote**.\n\nIt does one thing: it plans and runs a promotion along **one** edge. It can't read or write content, publish,\nor promote anywhere else. If you're promoting by hand, you don't need one.\n\n> Deployment keys need the **Pro** plan or above. A trial counts.\n\n## Before you start\n\nYou need a promotion edge: a source and a target environment that are already connected for\npromotion on the **Environments** page. A key is made for one edge and stays on it.\n\nTo create a key you need both **Manage environments** and **Assign roles**. Choosing the role a key acts as is\nwhat Assign roles governs. Revoking a key needs only Manage environments.\n\n## Create a key\n\n1. Open **Settings → API Keys**, and choose the **Deployment** tab.\n2. Choose **New deployment key**.\n3. Give it a name, choose the **edge**, and choose the **role** it acts as.\n4. Tick what it may do, and decide on overwriting (below).\n5. Choose **Create key**, then **copy the key straight away**. It's shown once and can't be recovered.\n If you lose it, revoke it and make another.\n\n### The role\n\nThe key acts exactly as the role you choose, checked against the **source** environment, the same as\nwhen a person with that role promotes. A role that can't promote a folder can't have a key that does.\nPick the least powerful role that can do the job. You can't create a key more capable than you are.\n\n### Permissions\n\n| Permission | Lets the key |\n|---|---|\n| **Plan** | Show what a promotion would change. It writes nothing, so it's safe for a pull request check |\n| **Execute** | Carry out a promotion in the target environment |\n\n### Overwriting changes made in the target\n\nIf someone has edited a promoted item in the target since the last promotion, promoting it again would\nreplace their edit. By default a deployment key **won't**: the promotion stops, lists those items, and\nnothing is written.\n\nTurn on **Allow overwriting changes made in the target** only if the pipeline is meant to replace\nthose edits. Even then, each item has to be confirmed in the request.\n\n## What the list shows\n\n| Status | Meaning |\n|---|---|\n| **Active** | The key works |\n| **Edge removed** | The edge is no longer in your environment graph. The key stays, and works again if the edge is added back |\n| **Not working** | Its role was deleted, so it can no longer authenticate. Revoke it |\n| **Expired** / **Revoked** | The key is past its expiry date or was revoked |\n\n## Revoking\n\nChoose **Revoke…** on the key. Pipelines using it stop immediately, and this can't be undone. Deleting\nthe role a key acts as, or either environment on its edge, revokes the key too.\n\nA key can't be edited or extended. To change one, create a replacement and revoke the old one.\n\n## Troubleshooting\n\n| You see | Usually means |\n|---|---|\n| \"Promotion target not allowed\" (`not_a_promotion_target`) | The edge was removed from the Environments page |\n| \"Promotion conflict\" (`promotion_conflict`) | An item was edited in the target. Allow overwriting and confirm the item, or reconcile it by hand |\n| \"Promotion blocked\" | The plan has blockers; the response lists them |\n| \"Scope denied\" | The key doesn't hold Plan or Execute for that call |\n| \"Tier required\" | Your plan is below Pro |\n\n## Related\n\n- [Management keys](management-keys.md): a different key, for creating and publishing content in one environment\n- [Content environments](content-environments.md): environments and promotion\n- For developers: [Promote content from a pipeline](https://developers.ebitex.io/guides/promote-from-a-pipeline/)\n"
|
|
79
|
+
"body": "# Deployment keys\n\nA **deployment key** lets a release pipeline promote content from one environment to the next, for\nexample from Staging to Production, without anyone clicking **Promote**.\n\nIt does one thing: it plans and runs a promotion along **one** edge. It can't read or write content, publish,\nor promote anywhere else. If you're promoting by hand, you don't need one.\n\n> Deployment keys need the **Pro** plan or above. A trial counts.\n\n## Before you start\n\nYou need a promotion edge: a source and a target environment that are already connected for\npromotion on the **Environments** page. A key is made for one edge and stays on it.\n\nTo create a key you need both **Manage environments** and **Assign roles**. Choosing the role a key acts as is\nwhat Assign roles governs. Revoking a key needs only Manage environments.\n\n## Create a key\n\n1. Open **Settings → API Keys**, and choose the **Deployment** tab.\n2. Choose **New deployment key**.\n3. Give it a name, choose the **edge**, and choose the **role** it acts as. If your organization has more than one [realm](realms.md), choose the **realm** too: the key promotes only that realm's content, and cannot include items from a realm above its own.\n4. Tick what it may do, and decide on overwriting (below).\n5. Choose **Create key**, then **copy the key straight away**. It's shown once and can't be recovered.\n If you lose it, revoke it and make another.\n\n### The role\n\nThe key acts exactly as the role you choose, checked against the **source** environment, the same as\nwhen a person with that role promotes. A role that can't promote a folder can't have a key that does.\nPick the least powerful role that can do the job. You can't create a key more capable than you are.\n\n### Permissions\n\n| Permission | Lets the key |\n|---|---|\n| **Plan** | Show what a promotion would change. It writes nothing, so it's safe for a pull request check |\n| **Execute** | Carry out a promotion in the target environment |\n\n### Overwriting changes made in the target\n\nIf someone has edited a promoted item in the target since the last promotion, promoting it again would\nreplace their edit. By default a deployment key **won't**: the promotion stops, lists those items, and\nnothing is written.\n\nTurn on **Allow overwriting changes made in the target** only if the pipeline is meant to replace\nthose edits. Even then, each item has to be confirmed in the request.\n\n## What the list shows\n\n| Status | Meaning |\n|---|---|\n| **Active** | The key works |\n| **Edge removed** | The edge is no longer in your environment graph. The key stays, and works again if the edge is added back |\n| **Not working** | Its role was deleted, so it can no longer authenticate. Revoke it |\n| **Expired** / **Revoked** | The key is past its expiry date or was revoked |\n\n## Revoking\n\nChoose **Revoke…** on the key. Pipelines using it stop immediately, and this can't be undone. Deleting\nthe role a key acts as, or either environment on its edge, revokes the key too.\n\nA key can't be edited or extended. To change one, create a replacement and revoke the old one.\n\n## Troubleshooting\n\n| You see | Usually means |\n|---|---|\n| \"Promotion target not allowed\" (`not_a_promotion_target`) | The edge was removed from the Environments page |\n| \"Promotion conflict\" (`promotion_conflict`) | An item was edited in the target. Allow overwriting and confirm the item, or reconcile it by hand |\n| \"Promotion blocked\" | The plan has blockers; the response lists them |\n| \"Scope denied\" | The key doesn't hold Plan or Execute for that call |\n| \"Tier required\" | Your plan is below Pro |\n\n## Related\n\n- [Management keys](management-keys.md): a different key, for creating and publishing content in one environment\n- [Content environments](content-environments.md): environments and promotion\n- For developers: [Promote content from a pipeline](https://developers.ebitex.io/guides/promote-from-a-pipeline/)\n"
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
"slug": "environment-refresh",
|
|
@@ -101,7 +101,7 @@ export const HELP_TOPICS = [
|
|
|
101
101
|
{
|
|
102
102
|
"slug": "management-keys",
|
|
103
103
|
"title": "Management keys",
|
|
104
|
-
"body": "# Management keys\n\nA **management key** lets a script, a build pipeline, or another system create and publish content in\nContent — the things you would otherwise do by hand in the app.\n\nThey're for automation. If you're editing content yourself, you don't need one.\n\n> Management keys need the **Pro** plan or above. A trial counts, so you can use them straight away.\n\n## What a key can do\n\nTwo things decide that, and you choose both when you create the key.\n\n**Its role** decides *which content* the key can touch. A key acts exactly as the role you give it —\nthe same folder permissions, the same approval workflows, the same limits. A key bound to a role that\ncan't edit a folder can't edit that folder, and a page waiting for approval can't be published by a\nscript any more than by a person.\n\n**Its permissions** decide *what kind of thing* the key can do:\n\n| Permission | Lets the key |\n|---|---|\n| **Read** | Look at what's there, and preview what a change would do |\n| **Write** | Apply a whole bundle of changes at once, and upload files |\n| **Publish** | Make content live |\n| **Authoring** | Create, edit and **delete** individual items — a Contract, a Component, a page |\n| **Proposals** | Offer content for review — and nothing else at all |\n\nThey combine. A key with Read only is genuinely safe to use in a check that comments on a pull\nrequest: it can report exactly what *would* change and is incapable of changing it.\n\n**Write, Publish, and Authoring each need Read too** — checking one of them checks Read\nautomatically, and you can't uncheck Read while any of them is checked. A key that could only write\nwould be unable to do almost anything with that write (most of this API needs Read first), so this\ncombination isn't offered. Proposals is the exception: it's designed to grant neither Read nor Write,\nso it isn't affected.\n\n**Authoring is the only one that can delete anything**, which is why it is separate from Write rather\nthan part of it. Write applies a bundle, and a bundle only ever adds or updates — it never removes a\nContract, a Component or a page. So a key that has always worked fine for your pipeline keeps doing\nexactly what it did; nothing widens on its own. Add Authoring when you want a script or a tool to\nbuild a model item by item, and leave it off when you do not.\n\n**Proposals is the one permission that grants no access to your content.** A key holding only it\ncan offer something for review and can do nothing else — it cannot read a single Component and\ncannot write one. That is the point: an outside system offering content should not be able to look\nat what is already there. See [Proposals](proposals.md).\n\n**Its environment.** A key works in one authoring environment and no other. To run a pipeline against\nstaging and production, create two keys. That's deliberate — it means a staging pipeline cannot\ntouch production even by mistake.\n\n## Creating a key\n\n1. Go to **Settings → API Keys**, and find **Management keys**.\n2. Check the **environment** shown in the switcher at the top of the page. The key is bound to\n whichever authoring environment you are currently in, so switch first if that is not the one you\n want.\n3. Choose a **name** you'll recognize later — \"Marketing site CI\", not \"key 2\".\n4. Choose the **role** it acts as. Pick the least powerful role that can do the job.\n5. Tick the **permissions** it needs. If in doubt, start with Read and add more when something fails.\n6. Choose when it **expires** — 30 days, 90 days, a year, or never. New keys default to 90 days.\n7. Optionally, restrict it to **IP ranges** — if your build server has a fixed address, this is worth\n doing.\n\n**The key is shown once.** Copy it into your secret store before closing the dialog; it can't be\nshown again. If you lose it, revoke it and create another.\n\n> You can't create a key more powerful than you are. If you don't hold a permission yourself, you\n> can't give it to a key.\n\n## Keeping a key safe\n\nA management key can change your content. Treat it like a password.\n\n- **Never put one in a browser, a web page, or a front-end bundle.** It won't work there — we\n deliberately prevent it — but the attempt means the key has been somewhere it shouldn't.\n- Store it in your CI system's secret store, never in a repository or a command you type.\n- Give it the narrowest role and fewest permissions that work.\n- Set an expiry if the key is for something temporary.\n- Restrict it to your build server's IP range where you can.\n\n## Revoking a key\n\n**Settings → API Keys → Management keys → Revoke.** It stops working immediately.\n\nThe record stays — who created it, when, and when it was last used — because that history is exactly\nwhat you need after a key has leaked.\n\nDeleting the **role** a key is bound to also stops that key working, immediately and permanently.\nThat's intentional: taking away a role takes away everything acting on its behalf.\n\n## Seeing what a key did\n\nEvery change a key makes is recorded in your organization's activity log under the key's name rather\nthan a person's, so you can always tell an automated change from someone's edit.\n\n## Troubleshooting\n\n| What you see | What it means |\n|---|---|\n| \"Invalid API key\" | Revoked, expired, mistyped — or the role it was bound to was deleted. The key list shows an **Expired** badge, so check there first |\n| \"Scope denied\" | The key lacks that permission. It names which one |\n| \"Tier required\" | Your plan doesn't include management keys. Pro or above is needed |\n| \"IP denied\" | The key is restricted to IP ranges and the caller isn't in one |\n| \"Confirm overwrite required\" | Something changed in Content since your script last looked. Review before overwriting |\n| \"Unknown property\" | Your request had a field we don't recognize — usually a typo. It names the field |\n| \"Quota exceeded\" | Too many requests for your plan. It names the limit |\n\n## Related\n\n- [Building a content model through the API](agent-authoring.md) — the model in API terms\n- [Getting help](../general/getting-help.md)\n- For developers: the [Content Management API guide](https://developers.ebitex.io/getting-started/content-as-code/) and its\n [reference](https://developers.ebitex.io/api/),\n and the [Content MCP server](https://developers.ebitex.io/getting-started/content-as-code/#ai-agents-ebitexcontent-mcp), which lets an AI agent build a content\n model using a key you mint. Mint it against a **non-production** authoring environment, and leave\n the authoring scope off unless the agent needs to write.\n"
|
|
104
|
+
"body": "# Management keys\n\nA **management key** lets a script, a build pipeline, or another system create and publish content in\nContent — the things you would otherwise do by hand in the app.\n\nThey're for automation. If you're editing content yourself, you don't need one.\n\n> Management keys need the **Pro** plan or above. A trial counts, so you can use them straight away.\n\n## What a key can do\n\nTwo things decide that, and you choose both when you create the key.\n\n**Its role** decides *which content* the key can touch. A key acts exactly as the role you give it —\nthe same folder permissions, the same approval workflows, the same limits. A key bound to a role that\ncan't edit a folder can't edit that folder, and a page waiting for approval can't be published by a\nscript any more than by a person.\n\n**Its permissions** decide *what kind of thing* the key can do:\n\n| Permission | Lets the key |\n|---|---|\n| **Read** | Look at what's there, and preview what a change would do |\n| **Write** | Apply a whole bundle of changes at once, and upload files |\n| **Publish** | Make content live |\n| **Authoring** | Create, edit and **delete** individual items — a Contract, a Component, a page |\n| **Proposals** | Offer content for review — and nothing else at all |\n\nThey combine. A key with Read only is genuinely safe to use in a check that comments on a pull\nrequest: it can report exactly what *would* change and is incapable of changing it.\n\n**Write, Publish, and Authoring each need Read too** — checking one of them checks Read\nautomatically, and you can't uncheck Read while any of them is checked. A key that could only write\nwould be unable to do almost anything with that write (most of this API needs Read first), so this\ncombination isn't offered. Proposals is the exception: it's designed to grant neither Read nor Write,\nso it isn't affected.\n\n**Authoring is the only one that can delete anything**, which is why it is separate from Write rather\nthan part of it. Write applies a bundle, and a bundle only ever adds or updates — it never removes a\nContract, a Component or a page. So a key that has always worked fine for your pipeline keeps doing\nexactly what it did; nothing widens on its own. Add Authoring when you want a script or a tool to\nbuild a model item by item, and leave it off when you do not.\n\n**Proposals is the one permission that grants no access to your content.** A key holding only it\ncan offer something for review and can do nothing else — it cannot read a single Component and\ncannot write one. That is the point: an outside system offering content should not be able to look\nat what is already there. See [Proposals](proposals.md).\n\n**Its environment.** A key works in one authoring environment and no other. To run a pipeline against\nstaging and production, create two keys. That's deliberate — it means a staging pipeline cannot\ntouch production even by mistake.\n\n## Creating a key\n\n1. Go to **Settings → API Keys**, and find **Management keys**.\n2. Check the **environment** shown in the switcher at the top of the page. The key is bound to\n whichever authoring environment you are currently in, so switch first if that is not the one you\n want.\n3. Choose a **name** you'll recognize later — \"Marketing site CI\", not \"key 2\".\n4. If your organization has more than one [realm](realms.md), choose the **realm** the key acts in. A key reads and writes only that realm (and reads the realms above it); a key made before realms existed acts in the root realm.\n4. Choose the **role** it acts as. Pick the least powerful role that can do the job.\n5. Tick the **permissions** it needs. If in doubt, start with Read and add more when something fails.\n6. Choose when it **expires** — 30 days, 90 days, a year, or never. New keys default to 90 days.\n7. Optionally, restrict it to **IP ranges** — if your build server has a fixed address, this is worth\n doing.\n\n**The key is shown once.** Copy it into your secret store before closing the dialog; it can't be\nshown again. If you lose it, revoke it and create another.\n\n> You can't create a key more powerful than you are. If you don't hold a permission yourself, you\n> can't give it to a key.\n\n## Keeping a key safe\n\nA management key can change your content. Treat it like a password.\n\n- **Never put one in a browser, a web page, or a front-end bundle.** It won't work there — we\n deliberately prevent it — but the attempt means the key has been somewhere it shouldn't.\n- Store it in your CI system's secret store, never in a repository or a command you type.\n- Give it the narrowest role and fewest permissions that work.\n- Set an expiry if the key is for something temporary.\n- Restrict it to your build server's IP range where you can.\n\n## Revoking a key\n\n**Settings → API Keys → Management keys → Revoke.** It stops working immediately.\n\nThe record stays — who created it, when, and when it was last used — because that history is exactly\nwhat you need after a key has leaked.\n\nDeleting the **role** a key is bound to also stops that key working, immediately and permanently.\nThat's intentional: taking away a role takes away everything acting on its behalf.\n\n## Seeing what a key did\n\nEvery change a key makes is recorded in your organization's activity log under the key's name rather\nthan a person's, so you can always tell an automated change from someone's edit.\n\n## Troubleshooting\n\n| What you see | What it means |\n|---|---|\n| \"Invalid API key\" | Revoked, expired, mistyped — or the role it was bound to was deleted. The key list shows an **Expired** badge, so check there first |\n| \"Scope denied\" | The key lacks that permission. It names which one |\n| \"Tier required\" | Your plan doesn't include management keys. Pro or above is needed |\n| \"IP denied\" | The key is restricted to IP ranges and the caller isn't in one |\n| \"Confirm overwrite required\" | Something changed in Content since your script last looked. Review before overwriting |\n| \"Unknown property\" | Your request had a field we don't recognize — usually a typo. It names the field |\n| \"Quota exceeded\" | Too many requests for your plan. It names the limit |\n\n## Related\n\n- [Building a content model through the API](agent-authoring.md) — the model in API terms\n- [Getting help](../general/getting-help.md)\n- For developers: the [Content Management API guide](https://developers.ebitex.io/getting-started/content-as-code/) and its\n [reference](https://developers.ebitex.io/api/),\n and the [Content MCP server](https://developers.ebitex.io/getting-started/content-as-code/#ai-agents-ebitexcontent-mcp), which lets an AI agent build a content\n model using a key you mint. Mint it against a **non-production** authoring environment, and leave\n the authoring scope off unless the agent needs to write.\n"
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"slug": "proposals",
|
|
@@ -113,6 +113,11 @@ export const HELP_TOPICS = [
|
|
|
113
113
|
"title": "Publishing content",
|
|
114
114
|
"body": "# Publishing content\n\nEverything you build in Content — Components and Experience nodes — stays\nprivate to your organization until you **publish** it. Publishing takes a\nsnapshot of what you're looking at and copies it into a separate,\nread-optimized space that the Content Delivery API serves to the outside\nworld. Editing something after it's published never changes what's live;\nyou have to publish again. (Developers building the site that shows this\ncontent start from [Rendering your site](rendering-your-site.md).)\n\n## The lifecycle\n\nEvery publishable item shows a status badge:\n\n- **Draft** — never published.\n- **Published** — a snapshot is live, and it matches your current edits.\n- **Modified since publish** — a snapshot is live, but you've made\n changes since. Visitors still see the old snapshot until you publish\n again.\n- **Unpublished** — was published once, then taken down. The item still\n exists in Content; it's just not being served anymore.\n\n## Publishing an item\n\nSelect **Publish…** on a Component or an Experience node's detail panel.\nThis opens the **publish plan** — a list of everything that needs to go\nout together: the item itself, plus anything it depends on (a node's\nbound Component, a Component's own references, and so on, however deep\nthat chain goes).\n\nOn an item that's already live, the same button reads **Republish…** and\nsits next to **Unpublish** — use it to push new snapshots without taking\nanything down first. This is also how a change *inside* the dependency\nchain goes live: editing a published node's bound Component doesn't\nchange the node's own badge, but republishing the node re-freezes the\nwhole plan, the edited Component included.\n\nFor each item in the plan you'll see:\n\n- Its current status.\n- Whether it's valid to publish as-is. An invalid item is shown with its\n error and can't be included — fix the problem first, then reopen the\n plan.\n- A warning (not a blocker) if the item doesn't validate against the\n *latest* version of its Contract or Template, even though it's fine\n against the version it's actually pinned to. It will still publish; the\n warning is a heads-up that something upstream has moved on.\n\nUncheck anything you don't want to publish yet — a skipped item simply\nisn't included this time; nothing else in the plan is affected. If your\norganization has mapped more than one delivery environment from where\nyou're authoring (see [content-environments.md](content-environments.md)),\nchoose which one to publish to — otherwise it goes to the one delivery\nenvironment mapped here, exactly as before. Select **Publish** to go live\nwith everything still checked.\n\nPublishing requires the **Publish** permission on that item's branch\n(folder for Components, node branch for Experience nodes) — a separate\npermission from **Edit**, so an organization can have editors who can't\npush content live, and publishers who review before it goes out. If you\ndon't have it, the button is disabled and explains why.\n\n### What the publish panel tells you\n\n**Publish** (or **Republish**, once it has been published) opens a panel that says where the content will go,\nfor example *Publishing from Authoring → Production*, and what will happen: how many items will go live, how many\nare already up to date, and how many need attention first.\n\n- A page or Component is published together with the Components it uses, so the live page never points at\n something that is missing. Only the ones that changed are ticked. Open **Show 1 Component this page uses**\n to see them.\n- If everything is already live and unchanged, the panel says so and nothing is ticked. Tick an item to publish\n it again anyway.\n- An item that cannot be published says why beside it, for example that it is waiting in a review step or is\n missing a required field.\n- If your organization publishes to more than one place, choose the target first. Your choice is remembered.\n\nPublishing uses the **saved** version. If you have unsaved changes when you press **Publish…**\nor **Promote…**, you are asked first, with each change listed: **Save and publish** (or **Save and\npromote**) saves them and carries on, **Publish the saved version** carries on without them, and\n**Cancel** goes back. A save that fails stops there and shows its error.\n\nThe panel stays open while it works, with a count such as *Publishing 1 of 3*, and then lists what went live.\nYou can close it at any time and the publish carries on; a message tells you when it finishes.\n\nIf something could not be published, the list names the item that stopped the run and the reason. Items after it\nwere not attempted, and items before it are already live. Fix the reason, then press **Try the failed items again**\n(or **Reload plan** when nothing went live). If you contact support, quote the reference shown under the list.\n\n## Unpublishing\n\nSelect **Unpublish** to take a live item down immediately. Its URL (if it\nhad one) stops resolving. The item itself is untouched in Content — you\ncan publish it again at any time.\n\n**Images and files are different: unpublishing doesn't take them down.**\nThe first time an image or file field is published, it gets a permanent,\npublic link — anyone who has that link can still open it even after you\nunpublish the item that used it, or after you replace the image with a\ndifferent one. This is because the file's link never changes once\npublished (so it keeps working in caches, emails, and anywhere else it was\ncopied), and there's no way to \"recall\" a link once it's been shared.\nNothing changes about who can *edit* the file or where it appears in your\nsite — only the direct link to the file itself stays reachable.\n\nIf you publish a Component or page whose image field hasn't been made\npublic yet, **publishing itself requires a one-time setup on our end** —\nif that hasn't happened yet, publishing is blocked with a message saying\nso rather than silently publishing a broken image link. Contact support if\nyou see this.\n\n## Promoting content between environments\n\nIf your organization authors in more than one environment (Staging and\nProduction, say), **Promote…** — next to **Publish…** on a Component or\nExperience node — copies it, and everything it depends on, into another\nauthoring environment, ready to be published there in turn. This is\nseparate from publishing: promoting moves draft content between authoring\nspaces; publishing takes draft content live. See\n[content-environments.md](content-environments.md) for the full picture.\n\n## Renaming or moving a published Experience node\n\nIf you change a published node's slug (or move it), republishing\nre-computes every affected path. The **old** path automatically becomes a\nredirect to the new one, so existing links and bookmarks keep working —\nyou don't have to do anything extra. You can review and remove these\nredirects from a site's own **Manage** view under **Sites** (see below).\n\n## Keeping a page out of search results\n\nSome published pages shouldn't be advertised to search engines — a search\nresults page, an internal index, a section you'd rather people reached only\nfrom a link you sent them.\n\nOpen the page in **Composer**, go to the **Configuration** tab, and use\n**Search visibility**. Turning it off (and selecting **Save**) means the page is left out of the\n**sitemap** — the list of URLs your site gives search engines. Everything\nelse stays exactly as it was: the page is still published, still live, and\nstill works for anyone who visits or links to its URL.\n\nTwo things to know:\n\n- **It applies to everything beneath it.** Excluding a section page\n excludes every page under it too, however deep. A page excluded this way\n shows you which parent is responsible, with a link to it, so you can\n change it there.\n- **It takes effect when you publish.** Toggling it marks the page as\n **Modified**, exactly like editing its content — publish the page and the\n change reaches your live site.\n\nThis isn't the same as telling search engines not to *index* a page they\nalready know about. Leaving a page out of the sitemap means you aren't\nadvertising it; a search engine that finds it another way (a link from\nsomewhere else, say) can still list it.\n\n## When search engines are told a page changed\n\nYour sitemap tells search engines when each page last changed, so they know\nwhich pages are worth re-reading. You don't configure this and there's\nnothing to keep up to date — but it's worth knowing what it reflects:\n\n- It's the date the page's **content** last changed, not the date it was\n last published. Republishing a page you haven't edited doesn't move it,\n so publishing a whole site doesn't tell search engines that everything\n changed at once.\n- Renaming a page or editing its content moves it. Reordering pages or\n moving one to a different parent doesn't — neither changes what's on the\n page.\n- Editing the content a page is built from — the Component it's bound to —\n moves it too, even if you only publish that Component. Content nested a\n further step away (a Component referenced by *that* Component) doesn't,\n so the date can occasionally be a little older than the last real change.\n That's deliberate: an early date costs nothing, while a date that's too\n recent teaches search engines to stop trusting it.\n\n## Delivery settings\n\nThe **Delivery** page (in the app navigation) is where you manage delivery\nkeys — credentials external systems use to call the Content Delivery API.\nSelect **Create key**, give it a name, and copy the value shown — it's only\ndisplayed once. If more than one delivery environment is mapped here,\nchoose which one the key reads from. Revoke a key at any time to cut off\nwhatever's using it immediately.\n\nThere are two ways to use a key, and the **Browser-safe key** switch decides\nwhich you're creating:\n\n- **Server-side** (the default) — works from anywhere, so it must stay on\n your server, in a build step, or behind an edge function. Never put one in\n a web page.\n- **Browser-safe** — only works from the **allowed origins** (the websites\n you list, pre-filled from your sites' host mappings; add\n `http://localhost:<port>` for local development) and, optionally, the\n **allowed IP ranges** you list. Because it can't be used from anywhere\n else, it's safe to include in a website's own code. Everything a delivery\n key can read is already published, so a browser-safe key never exposes\n anything a visitor to your site couldn't see.\n\nEach key shows a **Browser-safe** or **Server-side** badge, and **Manage**\nlets you change a key's restrictions later — for example to add a new\nhostname. Removing every restriction from a browser-safe key asks you to\nconfirm, since that key may already be in a shipped site.\n\nRunning the same site against several delivery environments (development,\ntest, acceptance, production)? Create one browser-safe key per environment\nand give the site the matching key per deployment — the site's code stays\nidentical. To keep a non-production environment private, restrict its key\nto your office or VPN address range; the CMS never refuses to issue a key\nfor an environment. Not sure which address your office or server shows up\nas? Open `https://api.ebitex.io/diagnostics/client-address` from it — the\n`clientAddress` it returns is the one to allow.\n\nHost mapping and redirect aliases live on the **Sites** page instead — see\nbelow.\n\n## Host mapping and redirect aliases\n\nOpen **Sites** (in the app navigation) and select **Manage** on a site to\nreach:\n\n- **Host mapping** — which hostname(s) serve this site, for systems that\n resolve content by host rather than passing a site id explicitly. Enter\n one hostname per line and select **Save hosts**.\n- **Redirect aliases** — the list of old paths automatically created by\n renaming/moving this site's published nodes (see above). Delete one if\n you no longer want that old link to redirect.\n\nIf your authoring environment publishes to more than one delivery\nenvironment, a **Delivery environment** picker appears above the host\nmapping and redirect-alias sections — each delivery environment has its\nown independent set of hostnames and aliases, so choose which one you're\nviewing or editing before making a change. With a single delivery\nenvironment mapped, the picker stays hidden and there's nothing extra to\nchoose.\n\nManaging keys uses the same permission as Hub's API Keys page; host\nmapping and aliases use the same access-management permission as branch\nrules.\n"
|
|
115
115
|
},
|
|
116
|
+
{
|
|
117
|
+
"slug": "realms",
|
|
118
|
+
"title": "Realms",
|
|
119
|
+
"body": "# Realms\n\nIf your organization runs one brand, one site or one team, you will never need this page: Content has a single realm, nothing about it shows, and everything works as it always has. Realms are for organizations that publish several brands or sites which should **share some content and keep the rest apart**: shared design-system blocks, legal text and media Contracts that every brand uses, with each brand's own pages kept to itself.\n\n## What a realm is\n\nA realm is a place in a tree. Your organization has one **root** realm (named *Default* until you rename it), and you can create realms below it: *Shared* at the top, *Brand A* and *Brand B* underneath, and so on.\n\n- A realm sees what it owns **and** what every realm above it owns. Brand A sees Shared's Contracts, Templates, folders and Components without copying anything and without a sync step, so nothing can be out of date.\n- A realm changes only what it owns. Brand A cannot see Brand B, and Shared cannot see inside Brand A.\n- Realms are not environments. An environment is a stage a piece of content passes through (Staging, then Live). A realm is *whose* content it is. Every realm exists in every environment, so \"Live · Brand A\" and \"Staging · Brand A\" are two views of the same brand.\n\n## Switching realm\n\nWhen your organization has more than one realm, a **realm switcher** appears in the header beside the environment switcher, and the page title shows the current cell, for example \"Live · Brand A\". You only see the realms you are allowed to enter (see **Who can do what** below). Search results, My tasks and the review queue show items from every realm you can enter and open the right one when you select a result.\n\n## Owned and inherited items\n\nIn lists, folder trees and pickers, an item that another realm owns is drawn dimmer with a dotted outline and an **Owned by {realm}** chip. A legend under the list explains it, and a filter lets you show **All**, **Owned here** or **Inherited**. Whether an item is inherited depends only on which realm you are in; nothing is stored on the item.\n\nThings you can do with an inherited item:\n\n- **Use it.** Reference an inherited Contract from a Component, put an inherited Component in a page, choose an inherited Template. This is what inheriting is for.\n- **Create under it.** You can create items inside an inherited folder. They belong to your realm and sit in the shared folder, and the folder's own access rules and Allowed contracts still apply. You cannot add to the folder itself; make a subfolder of your own for that.\n- **Edit it.** Saving a change to an inherited Component, Contract, Template, Adapter or Stream is written in the realm that owns it. Before you save, the editor says so (\"Owned by Shared. Saving here changes it for every realm that reads Shared.\") and asks you to confirm. You need permission to edit in your realm **and** in the owning realm; if you lack the second you are told which realm to ask.\n- **Not from here.** Deleting, moving, restoring an older version and changing taxonomy vocabulary are refused on an inherited item, and the message names the owner. Do those in the owning realm.\n\n## Managing realms\n\nOpen **Realms** under **Configure** (requires the \"Manage Content realms\" permission, and realms must be switched on for your organization). The page shows the tree.\n\n- **Create** a realm under any other. The name is what people see in the switcher. Your plan limits how many realms you can have; Subscriptions & Billing shows the number you use. Only creating is refused when you reach the limit: nothing you already have is locked.\n- **Rename** a realm at any time. Its key, which tools use, does not change.\n- **Re-order** realms among their siblings, or **move a realm under another realm**. Moving is refused, with a list, if content in the realm or below it uses something from a realm the new position would not see.\n- **Delete** a realm that has no realms below it. This permanently deletes everything the realm owns in **every** environment and cannot be undone; you type the realm's key to confirm. It is refused while anything still points at the realm (a key, a member's access, an environment owned by it, anything published).\n\n### Move to realm, and duplicate into this realm\n\nFrom an item's row actions:\n\n- **Move to realm…** changes which realm owns an item (or a folder with everything inside it). It is refused, with the list of what would break, when moving would leave something unable to see an item it uses: for instance moving a Contract into Brand A while Brand B's Components use it.\n- **Duplicate into this realm…** makes a copy of an inherited item in your realm, with new ids, so you can change it without changing it for everyone. References between the copied items are re-pointed to the copies; references to anything else are kept.\n\n### When something is still in use below you\n\nDeleting or narrowing something that realms below yours depend on is refused with a count per realm, such as \"used by 3 items in Brand A\". You are never shown their content, only how many items and which realm. Change those items from inside that realm, or ask someone who can, then try again.\n\n### Environments owned by a realm\n\nAn authoring environment can be shared (the default) or **owned by one realm**, in which case it is listed and usable only inside that realm and the realms below it. Choose **Realm…** in the environment's row menu on the Environments page; it is offered once your organization has more than one realm. Your default authoring and delivery environments are always shared.\n\n## Who can do what\n\n- **Members.** On the Environments page, **Members** tab, a person's role in an environment can be limited to one realm. Someone bound to Brand A in Live can enter Brand A and the realms below it, cannot see Brand B, and cannot edit Shared unless they are also bound there. A binding with no realm applies to every realm.\n- **API keys.** A **Management** or **deployment** key acts in exactly one realm, chosen when you create it. A **delivery** key can serve every realm or be limited to one realm and the realms below it, so Brand A's key cannot be used to read Brand B's site. Keys created before realms act in the root realm.\n\n## Publishing and promoting across realms\n\n- **Publishing** a page publishes everything it needs, including items its realm inherits, once. You cannot unpublish an item that a published page still references.\n- **Promoting** works for the realm you are in. If the content needs items a realm above yours owns that the target environment lacks or has behind, promotion stops and lists them by realm. Where you are allowed to promote in that realm, choose **Include {realm}** and they go first. **Promote everything** walks the realms from the top down.\n- **Environment refresh** is not available while either environment holds content in a realm other than the root; use promotion instead.\n\n## Related\n\nSee [content-environments.md](content-environments.md) for environments and promotion, [management-keys.md](management-keys.md) and [deployment-keys.md](deployment-keys.md) for keys, and [export-import.md](export-import.md) for moving content between organizations.\n"
|
|
120
|
+
},
|
|
116
121
|
{
|
|
117
122
|
"slug": "rendering-your-site",
|
|
118
123
|
"title": "Rendering your site",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpCorpus.generated.js","sourceRoot":"","sources":["../../src/resources/helpCorpus.generated.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,kGAAkG;AAClG,oGAAoG;AAIpG,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC;QACE,MAAM,EAAE,iBAAiB;QACzB,OAAO,EAAE,0CAA0C;QACnD,MAAM,EAAE,2yZAA2yZ;KACpzZ;IACD;QACE,MAAM,EAAE,aAAa;QACrB,OAAO,EAAE,aAAa;QACtB,MAAM,EAAE,wnGAAwnG;KACjoG;IACD;QACE,MAAM,EAAE,YAAY;QACpB,OAAO,EAAE,sBAAsB;QAC/B,MAAM,EAAE,4s0BAA4s0B;KACrt0B;IACD;QACE,MAAM,EAAE,kBAAkB;QAC1B,OAAO,EAAE,kCAAkC;QAC3C,MAAM,EAAE,06nBAA06nB;KACn7nB;IACD;QACE,MAAM,EAAE,mBAAmB;QAC3B,OAAO,EAAE,0BAA0B;QACnC,MAAM,EAAE,i9OAAi9O;KAC19O;IACD;QACE,MAAM,EAAE,kBAAkB;QAC1B,OAAO,EAAE,gCAAgC;QACzC,MAAM,EAAE,60sBAA60sB;KACt1sB;IACD;QACE,MAAM,EAAE,sBAAsB;QAC9B,OAAO,EAAE,sBAAsB;QAC/B,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"helpCorpus.generated.js","sourceRoot":"","sources":["../../src/resources/helpCorpus.generated.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -56,7 +56,7 @@ export async function buildServer(options) {
|
|
|
56
56
|
const target = config.targetKey
|
|
57
57
|
? createClient({ baseUrl: config.baseUrl, key: config.targetKey, fetchImpl: options?.fetchImpl })
|
|
58
58
|
: null;
|
|
59
|
-
const session = await resolveSession(client, { readOnlyRequested: config.readOnly });
|
|
59
|
+
const session = await resolveSession(client, { readOnlyRequested: config.readOnly, expectedRealm: config.expectedRealm });
|
|
60
60
|
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, {
|
|
61
61
|
instructions: 'Content is ebitex\'s CMS. Start with content_describe — it returns every field type with ' +
|
|
62
62
|
'its own settings schema and the Contracts and Templates this environment already has, ' +
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAA;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD;;;GAGG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG,gBAAgB,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAW,WAAW,EAAE,CAAA;AAEnD;;;;;GAKG;AACH,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAA0B,CAAA;QACtF,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAA;IAClG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,CAAA;IACxB,CAAC;AACH,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAGjC;IACC,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,CAAA;IAE9C,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;IACxG,MAAM,MAAM,GAAyB,MAAM,CAAC,SAAS;QACnD,CAAC,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;QACjG,CAAC,CAAC,IAAI,CAAA;IAER,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,EAAE,iBAAiB,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/session.d.ts
CHANGED
|
@@ -24,6 +24,13 @@ export type WhoAmI = {
|
|
|
24
24
|
organizationSlug: string | null;
|
|
25
25
|
environmentId: string;
|
|
26
26
|
environmentName: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* the realm the key acts in. Absent from an API that predates realms, which is the root by construction; the root of an organization with one
|
|
29
|
+
* realm is never named in {@link Session.contextLine}, so nothing changes for it.
|
|
30
|
+
*/
|
|
31
|
+
realmId?: string;
|
|
32
|
+
realmKey?: string | null;
|
|
33
|
+
realmName?: string | null;
|
|
27
34
|
roleName: string | null;
|
|
28
35
|
keyName: string | null;
|
|
29
36
|
scopes: string[];
|
|
@@ -50,5 +57,6 @@ export declare class StartupError extends Error {
|
|
|
50
57
|
}
|
|
51
58
|
export declare function resolveSession(client: ContentClient, options: {
|
|
52
59
|
readOnlyRequested: boolean;
|
|
60
|
+
expectedRealm?: string | null;
|
|
53
61
|
}): Promise<Session>;
|
|
54
62
|
//# sourceMappingURL=session.d.ts.map
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,UAAU,4BAA4B,CAAA;AACnD,eAAO,MAAM,WAAW,6BAA6B,CAAA;AACrD,eAAO,MAAM,aAAa,+BAA+B,CAAA;AACzD,eAAO,MAAM,eAAe,iCAAiC,CAAA;AAE7D,MAAM,MAAM,MAAM,GAAG;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,mFAAmF;IACnF,iBAAiB,EAAE,OAAO,CAAA;IAC1B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B;;;OAGG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAA;IAC5C,iFAAiF;IACjF,WAAW,IAAI,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE;IAAE,iBAAiB,EAAE,OAAO,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,UAAU,4BAA4B,CAAA;AACnD,eAAO,MAAM,WAAW,6BAA6B,CAAA;AACrD,eAAO,MAAM,aAAa,+BAA+B,CAAA;AACzD,eAAO,MAAM,eAAe,iCAAiC,CAAA;AAE7D,MAAM,MAAM,MAAM,GAAG;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,mFAAmF;IACnF,iBAAiB,EAAE,OAAO,CAAA;IAC1B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B;;;OAGG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAA;IAC5C,iFAAiF;IACjF,WAAW,IAAI,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE;IAAE,iBAAiB,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrE,OAAO,CAAC,OAAO,CAAC,CA8BlB"}
|
package/dist/session.js
CHANGED
|
@@ -28,6 +28,12 @@ export async function resolveSession(client, options) {
|
|
|
28
28
|
catch (error) {
|
|
29
29
|
throw new StartupError(startupMessage(error, client.baseUrl));
|
|
30
30
|
}
|
|
31
|
+
const actualRealm = who.realmKey ?? 'root';
|
|
32
|
+
if (options.expectedRealm && options.expectedRealm !== actualRealm) {
|
|
33
|
+
throw new StartupError(who.realmKey == null
|
|
34
|
+
? `EBITEX_CONTENT_REALM is ${options.expectedRealm}, but ${client.baseUrl} does not report realms, so the key acts in the root realm.`
|
|
35
|
+
: `EBITEX_CONTENT_REALM is ${options.expectedRealm}, but this key acts in the realm ${who.realmName ?? actualRealm} (${actualRealm}). A key is bound to one realm: use a key minted for ${options.expectedRealm}.`);
|
|
36
|
+
}
|
|
31
37
|
const scopes = new Set(who.scopes ?? []);
|
|
32
38
|
const permissions = Array.isArray(who.permissions) ? new Set(who.permissions) : null;
|
|
33
39
|
return {
|
|
@@ -37,9 +43,13 @@ export async function resolveSession(client, options) {
|
|
|
37
43
|
// which asks this and nothing else — needs no second concept of "but the operator said no".
|
|
38
44
|
has: (scope) => (options.readOnlyRequested && isWriteScope(scope) ? false : scopes.has(scope)),
|
|
39
45
|
lacksPermission: (permission) => permissions !== null && !permissions.has(permission),
|
|
40
|
-
contextLine: () => `[${who.organizationName ?? who.organizationId} · ${who.environmentName ?? who.environmentId} · as ${who.roleName ?? 'unknown role'}]`,
|
|
46
|
+
contextLine: () => `[${who.organizationName ?? who.organizationId} · ${who.environmentName ?? who.environmentId}${realmSegment(who)} · as ${who.roleName ?? 'unknown role'}]`,
|
|
41
47
|
};
|
|
42
48
|
}
|
|
49
|
+
/** The realm, named only when it is not the root: an organization that never made one sees the line it always saw. */
|
|
50
|
+
function realmSegment(who) {
|
|
51
|
+
return who.realmKey && who.realmKey !== 'root' ? ` · ${who.realmName ?? who.realmKey}` : '';
|
|
52
|
+
}
|
|
43
53
|
function isWriteScope(scope) {
|
|
44
54
|
return scope === SCOPE_WRITE || scope === SCOPE_PUBLISH || scope === SCOPE_AUTHORING;
|
|
45
55
|
}
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;;;;;;;;;;;;GAcG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,yBAAyB,CAAA;AACnD,MAAM,CAAC,MAAM,WAAW,GAAG,0BAA0B,CAAA;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,4BAA4B,CAAA;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/tools/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C;;;;;;;;;;;GAWG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,6FAA6F;AAC7F,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,aAAa,EAAE,MAAM,EACrB,aAAa,GAAE,SAAS,YAAY,EAAO,GAC1C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/tools/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C;;;;;;;;;;;GAWG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,6FAA6F;AAC7F,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,aAAa,EAAE,MAAM,EACrB,aAAa,GAAE,SAAS,YAAY,EAAO,GAC1C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB"}
|
package/dist/tools/whoami.js
CHANGED
|
@@ -14,6 +14,8 @@ export function describeSession(session, withheld, serverVersion, withheldKinds
|
|
|
14
14
|
return {
|
|
15
15
|
organization: { id: who.organizationId, name: who.organizationName, slug: who.organizationSlug },
|
|
16
16
|
environment: { id: who.environmentId, name: who.environmentName },
|
|
17
|
+
// the realm the key is bound to. Absent from an API that predates realms, where the key acts in the root.
|
|
18
|
+
realm: who.realmKey ? { id: who.realmId ?? null, key: who.realmKey, name: who.realmName ?? null } : null,
|
|
17
19
|
actingAs: { roleName: who.roleName, keyName: who.keyName },
|
|
18
20
|
scopes: who.scopes,
|
|
19
21
|
// Reported even though it is not a property of the credential, because a key whose scopes list
|
|
@@ -30,8 +32,8 @@ export function describeSession(session, withheld, serverVersion, withheldKinds
|
|
|
30
32
|
// The sentence an agent needs *before* it misreads its first empty result. An empty list from
|
|
31
33
|
// this server is always scoped, and saying so is cheaper than the duplicates an agent creates
|
|
32
34
|
// after concluding that nothing exists.
|
|
33
|
-
note: 'This key reads and writes exactly one authoring environment,
|
|
34
|
-
'means nothing matched here — not that nothing exists.',
|
|
35
|
+
note: 'This key reads and writes exactly one authoring environment (and, where the organization has realms, ' +
|
|
36
|
+
'one realm in it), as one role. An empty result means nothing matched here — not that nothing exists.',
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
39
|
//# sourceMappingURL=whoami.js.map
|
package/dist/tools/whoami.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/tools/whoami.ts"],"names":[],"mappings":"AA2BA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAgB,EAChB,QAAiC,EACjC,aAAqB,EACrB,gBAAyC,EAAE;IAE3C,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IAEvB,OAAO;QACL,YAAY,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,GAAG,CAAC,gBAAgB,EAAE;QAChG,WAAW,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,eAAe,EAAE;QACjE,QAAQ,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE;QAC1D,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,+FAA+F;QAC/F,yFAAyF;QACzF,gGAAgG;QAChG,gGAAgG;QAChG,4CAA4C;QAC5C,cAAc,EAAE,OAAO,CAAC,iBAAiB;QACzC,aAAa;QACb,QAAQ;QACR,6FAA6F;QAC7F,iGAAiG;QACjG,aAAa;QACb,8FAA8F;QAC9F,8FAA8F;QAC9F,wCAAwC;QACxC,IAAI,EACF,
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/tools/whoami.ts"],"names":[],"mappings":"AA2BA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAgB,EAChB,QAAiC,EACjC,aAAqB,EACrB,gBAAyC,EAAE;IAE3C,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IAEvB,OAAO;QACL,YAAY,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,GAAG,CAAC,gBAAgB,EAAE;QAChG,WAAW,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,eAAe,EAAE;QACjE,0GAA0G;QAC1G,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;QACxG,QAAQ,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE;QAC1D,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,+FAA+F;QAC/F,yFAAyF;QACzF,gGAAgG;QAChG,gGAAgG;QAChG,4CAA4C;QAC5C,cAAc,EAAE,OAAO,CAAC,iBAAiB;QACzC,aAAa;QACb,QAAQ;QACR,6FAA6F;QAC7F,iGAAiG;QACjG,aAAa;QACb,8FAA8F;QAC9F,8FAA8F;QAC9F,wCAAwC;QACxC,IAAI,EACF,uGAAuG;YACvG,sGAAsG;KACzG,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebitex/content-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.247",
|
|
4
4
|
"description": "An MCP server for ebitex Content: build and publish a content model from an AI agent.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://developers.ebitex.io/getting-started/content-as-code/",
|