@se-studio/contentful-cms 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli/program.d.ts.map +1 -1
  3. package/dist/cli/program.js +2 -0
  4. package/dist/cli/program.js.map +1 -1
  5. package/dist/commands/media.d.ts +4 -0
  6. package/dist/commands/media.d.ts.map +1 -0
  7. package/dist/commands/media.js +124 -0
  8. package/dist/commands/media.js.map +1 -0
  9. package/dist/editor-pack/capabilities.d.ts.map +1 -1
  10. package/dist/editor-pack/capabilities.js +15 -0
  11. package/dist/editor-pack/capabilities.js.map +1 -1
  12. package/dist/generated/version.d.ts +1 -1
  13. package/dist/generated/version.js +1 -1
  14. package/dist/hosted/index.d.ts +1 -0
  15. package/dist/hosted/index.d.ts.map +1 -1
  16. package/dist/hosted/index.js +1 -0
  17. package/dist/hosted/index.js.map +1 -1
  18. package/dist/hosted/next/routes/mcp.d.ts.map +1 -1
  19. package/dist/hosted/next/routes/mcp.js +3 -0
  20. package/dist/hosted/next/routes/mcp.js.map +1 -1
  21. package/dist/mcp/prompts/onboarding.d.ts.map +1 -1
  22. package/dist/mcp/prompts/onboarding.js +11 -8
  23. package/dist/mcp/prompts/onboarding.js.map +1 -1
  24. package/dist/mcp/server.d.ts.map +1 -1
  25. package/dist/mcp/server.js +2 -0
  26. package/dist/mcp/server.js.map +1 -1
  27. package/dist/mcp/tools/media.d.ts +4 -0
  28. package/dist/mcp/tools/media.d.ts.map +1 -0
  29. package/dist/mcp/tools/media.js +138 -0
  30. package/dist/mcp/tools/media.js.map +1 -0
  31. package/dist/media-pipeline/actions.d.ts +30 -0
  32. package/dist/media-pipeline/actions.d.ts.map +1 -0
  33. package/dist/media-pipeline/actions.js +257 -0
  34. package/dist/media-pipeline/actions.js.map +1 -0
  35. package/dist/media-pipeline/client.d.ts +67 -0
  36. package/dist/media-pipeline/client.d.ts.map +1 -0
  37. package/dist/media-pipeline/client.js +75 -0
  38. package/dist/media-pipeline/client.js.map +1 -0
  39. package/dist/media-pipeline/config.d.ts +17 -0
  40. package/dist/media-pipeline/config.d.ts.map +1 -0
  41. package/dist/media-pipeline/config.js +52 -0
  42. package/dist/media-pipeline/config.js.map +1 -0
  43. package/dist/media-pipeline/import.d.ts +19 -0
  44. package/dist/media-pipeline/import.d.ts.map +1 -0
  45. package/dist/media-pipeline/import.js +198 -0
  46. package/dist/media-pipeline/import.js.map +1 -0
  47. package/dist/media-pipeline/index.d.ts +6 -0
  48. package/dist/media-pipeline/index.d.ts.map +1 -0
  49. package/dist/media-pipeline/index.js +6 -0
  50. package/dist/media-pipeline/index.js.map +1 -0
  51. package/dist/media-pipeline/job-store.d.ts +41 -0
  52. package/dist/media-pipeline/job-store.d.ts.map +1 -0
  53. package/dist/media-pipeline/job-store.js +172 -0
  54. package/dist/media-pipeline/job-store.js.map +1 -0
  55. package/dist/media-pipeline/limits.d.ts +14 -0
  56. package/dist/media-pipeline/limits.d.ts.map +1 -0
  57. package/dist/media-pipeline/limits.js +14 -0
  58. package/dist/media-pipeline/limits.js.map +1 -0
  59. package/dist/media-pipeline/log.d.ts +3 -0
  60. package/dist/media-pipeline/log.d.ts.map +1 -0
  61. package/dist/media-pipeline/log.js +12 -0
  62. package/dist/media-pipeline/log.js.map +1 -0
  63. package/dist/media-pipeline/validate.d.ts +18 -0
  64. package/dist/media-pipeline/validate.d.ts.map +1 -0
  65. package/dist/media-pipeline/validate.js +38 -0
  66. package/dist/media-pipeline/validate.js.map +1 -0
  67. package/dist/runtime/context.d.ts +5 -0
  68. package/dist/runtime/context.d.ts.map +1 -1
  69. package/dist/runtime/context.js.map +1 -1
  70. package/docs/editor-tasks/create-from-document.md +12 -3
  71. package/docs/editor-tasks/media-pipeline-prepare.md +124 -0
  72. package/docs/editor-tasks/media-reuse-and-upload.md +12 -3
  73. package/docs/editor-tasks/source-readiness-review.md +1 -0
  74. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ import { MEDIA_TTL_HOURS_MAX, MEDIA_TTL_HOURS_MIN } from './limits.js';
2
+ const DRIVE_FOLDER_RE = /^https:\/\/drive\.google\.com\/drive\/(?:u\/\d+\/)?folders\/([a-zA-Z0-9_-]+)(?:[/?#].*)?$/;
3
+ export function parseDriveFolderUrl(url) {
4
+ const trimmed = url.trim();
5
+ const match = DRIVE_FOLDER_RE.exec(trimmed);
6
+ if (!match) {
7
+ return {
8
+ ok: false,
9
+ error: 'driveFolderUrl must be a Google Drive folder URL (https://drive.google.com/drive/folders/…).',
10
+ };
11
+ }
12
+ return {
13
+ ok: true,
14
+ folderId: match[1],
15
+ url: `https://drive.google.com/drive/folders/${match[1]}`,
16
+ };
17
+ }
18
+ export function clampTtlHours(value, defaultTtl) {
19
+ const raw = value ?? defaultTtl;
20
+ if (!Number.isFinite(raw)) {
21
+ return defaultTtl;
22
+ }
23
+ return Math.min(MEDIA_TTL_HOURS_MAX, Math.max(MEDIA_TTL_HOURS_MIN, Math.floor(raw)));
24
+ }
25
+ export function assertAllowedProfile(profile, allowed) {
26
+ if (!allowed.includes(profile)) {
27
+ return {
28
+ ok: false,
29
+ error: `Profile "${profile}" is not allowed on this host. Allowed: ${allowed.join(', ')}`,
30
+ };
31
+ }
32
+ return { ok: true };
33
+ }
34
+ /** Job ids from media-pipeline are URL-safe tokens; keep validation loose but bounded. */
35
+ export function isPlausibleJobId(jobId) {
36
+ return /^[a-zA-Z0-9_-]{8,128}$/.test(jobId.trim());
37
+ }
38
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/media-pipeline/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,eAAe,GACnB,2FAA2F,CAAC;AAE9F,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAM7C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EACH,8FAA8F;SACjG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAE;QACnB,GAAG,EAAE,0CAA0C,KAAK,CAAC,CAAC,CAAE,EAAE;KAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAyB,EAAE,UAAkB;IACzE,MAAM,GAAG,GAAG,KAAK,IAAI,UAAU,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,OAAe,EACf,OAAiB;IAEjB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,YAAY,OAAO,2CAA2C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SAC1F,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
  import type { CmsEditConfig } from '../config/types.js';
3
3
  import type { IndexCacheStore } from '../index/cache-store.js';
4
+ import type { MediaPipelineJobStore } from '../media-pipeline/job-store.js';
4
5
  import type { SessionStore } from '../session/session-store.js';
5
6
  import type { StagedUploadStore } from '../staged-upload/store.js';
6
7
  export interface ContentfulUserIdentity {
@@ -18,6 +19,10 @@ export interface CmsEditRuntimeContext {
18
19
  user?: ContentfulUserIdentity;
19
20
  /** Staged file uploads for hosted asset upload (Redis). */
20
21
  stagedUploadStore?: StagedUploadStore;
22
+ /** Media pipeline job ownership + rate limits (Redis on hosted). */
23
+ mediaPipelineJobStore?: MediaPipelineJobStore;
24
+ /** Hosted project key (e.g. se2026) for media job bindings / logs. */
25
+ projectKey?: string;
21
26
  }
22
27
  export declare const cmsEditContext: AsyncLocalStorage<CmsEditRuntimeContext>;
23
28
  export declare function getRuntimeContext(): CmsEditRuntimeContext | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,qEAAqE;IACrE,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gEAAgE;IAChE,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAED,eAAO,MAAM,cAAc,0CAAiD,CAAC;AAE7E,wBAAgB,iBAAiB,IAAI,qBAAqB,GAAG,SAAS,CAErE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,qBAAqB,EAC9B,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhB"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,qEAAqE;IACrE,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gEAAgE;IAChE,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,oEAAoE;IACpE,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAC9C,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,0CAAiD,CAAC;AAE7E,wBAAgB,iBAAiB,IAAI,qBAAqB,GAAG,SAAS,CAErE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,qBAAqB,EAC9B,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAwBrD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,iBAAiB,EAAyB,CAAC;AAE7E,MAAM,UAAU,iBAAiB;IAC/B,OAAO,cAAc,CAAC,QAAQ,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAA8B,EAC9B,EAAwB;IAExB,OAAO,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AA6BrD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,iBAAiB,EAAyB,CAAC;AAE7E,MAAM,UAAU,iBAAiB;IAC/B,OAAO,cAAc,CAAC,QAAQ,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAA8B,EAC9B,EAAwB;IAExB,OAAO,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC"}
@@ -27,14 +27,21 @@
27
27
  3. On **NO-GO**, stop. On **GO** / **GO-WITH-GAPS**, keep the soft-proof as the build plan.
28
28
  4. **Do not rewrite source copy.** Structure and place only.
29
29
 
30
- ### Phase 1 — Plan and create
30
+ ### Phase 1 — Media pack (when the package has Drive/binary masters)
31
+
32
+ 1. If the package includes a **folder of images/videos** (typical case study): run **`task-media-pipeline-prepare`** **before** building blocks.
33
+ 2. Prefer pipeline **ready public URLs** + reuse-first import over mid-build staged upload of oversized masters.
34
+ 3. Keep the `sourceName → assetId` map for Phase 2 field linking.
35
+ 4. Skip this phase only when every needed visual is already in the Contentful library (confirmed via `asset search`).
36
+
37
+ ### Phase 2 — Plan and create
31
38
 
32
39
  1. Read `cms-edit://customer/tasks-index` and relevant playbooks (`articles` / `pages`).
33
40
  2. Map soft-proof beats to **registered** component types only — prefer **source order** over CMS convenience (do not invent side-by-side pairs unless the source is a true pair).
34
- 3. **Propose plan** (can reuse the soft-proof): slug, title, template/article type, ordered component list, gap/placeholder list — **ask user to confirm** with a light **yes** if not already confirmed.
41
+ 3. **Propose plan** (can reuse the soft-proof): slug, title, template/article type, ordered component list, gap/placeholder list, **asset map** — **ask user to confirm** with a light **yes** if not already confirmed.
35
42
  4. For pages: `list --type template` — pick template; check template `preContent` / `postContent` so heroes are not duplicated.
36
43
  5. For articles: resolve `articleType`; apply site SEO and lab rules from `articles` playbook.
37
- 6. Build `create from-json` payload **or** prefer incremental `open` → `add` / `set` / `rtf` for large multi-block work.
44
+ 6. Build `create from-json` payload **or** prefer incremental `open` → `add` / `set` / `rtf` for large multi-block work. Link visuals using the pack map from Phase 1.
38
45
  7. `cms_edit ["create", "from-json", "--json", "<compact-json>", "--dry-run", "--strict"]` when using JSON (or `--json-base64` for large payloads).
39
46
  8. On approval, create and `save`.
40
47
  9. `cms_edit ["preview", "urls", "<slug>"]` — follow **`task-preview-verify`** (including multi-block soft-proof QA when relevant).
@@ -54,6 +61,7 @@ Use MCP prompts `cms_edit_create_from_doc` or `cms_edit_create_article_from_doc`
54
61
  - Publish
55
62
  - **Rewriting source copy** (flag only)
56
63
  - Asset upload without reuse check (see **Media** capability)
64
+ - Mid-article ffmpeg / Drive size thrash — use **`task-media-pipeline-prepare`** first
57
65
  - Inventing layout, section titles, or pair collections absent from the source
58
66
 
59
67
  ## Related resources
@@ -61,6 +69,7 @@ Use MCP prompts `cms_edit_create_from_doc` or `cms_edit_create_article_from_doc`
61
69
  - `cms-edit://customer/components-index`
62
70
  - `cms-edit://customer/articles` / `pages`
63
71
  - `task-source-readiness-review`
72
+ - `task-media-pipeline-prepare`
64
73
  - `task-create-article`
65
74
  - `task-create-page`
66
75
  - `task-media-reuse-and-upload`
@@ -0,0 +1,124 @@
1
+ # Capability: Media pipeline — prepare pack
2
+
3
+ Normalize a **folder of designer masters** into short-lived **public HTTPS URLs** under cms-edit upload limits, then import into Contentful with reuse-first.
4
+
5
+ **Never** expect the media pipeline service to write Contentful. Pipeline → ready URLs only. **cms-edit** owns assets/entries.
6
+
7
+ ## Intent phrases
8
+
9
+ - process media folder
10
+ - prepare Drive assets for CMS
11
+ - media pipeline pack
12
+ - normalize oversized images/videos
13
+ - process case study assets
14
+ - upload from media pipeline
15
+
16
+ ## Requires capabilities
17
+
18
+ `assetSearch` and `assetUpload`
19
+
20
+ ## Prerequisites
21
+
22
+ - Hosted cms-edit with media pipeline **configured on the host** (ops env):
23
+ - `MEDIA_PIPELINE_BASE_URL`
24
+ - `MEDIA_PIPELINE_SERVICE_KEY` (server-only — **never ask the agent/user for this**)
25
+ - `MEDIA_PIPELINE_TENANT` (e.g. `se`, not the cms-edit projectKey)
26
+ - Drive folder shared **Viewer** with pipeline service account
27
+ `media-pipeline-reader@se-media-pipeline.iam.gserviceaccount.com`
28
+ (or “anyone with the link” if using API-key Drive mode on the pipeline)
29
+ - Profile default: `case-study-v1`
30
+
31
+ ## When to use
32
+
33
+ | Situation | Action |
34
+ |-----------|--------|
35
+ | Drive/Figma package with large JPEGs/videos | **This task first**, then structure build |
36
+ | Single small image already in library | `task-media-reuse-and-upload` only |
37
+ | Masters already under limits and catalogued | Reuse search only |
38
+
39
+ ## Steps (agent — single auth)
40
+
41
+ ### 1. Prepare
42
+
43
+ Call MCP tool **`cms_edit_media`**:
44
+
45
+ ```json
46
+ {
47
+ "action": "prepare",
48
+ "driveFolderUrl": "https://drive.google.com/drive/folders/…",
49
+ "profile": "case-study-v1"
50
+ }
51
+ ```
52
+
53
+ CLI: `cms-edit media prepare --drive-folder "https://drive.google.com/drive/folders/…"`
54
+
55
+ Expect `{ ok: true, jobId, status: "queued", pollHint }`.
56
+
57
+ If `{ ok: false, code: "pipeline_unconfigured" }` → host ops must set env vars. **Do not** ask the user for a pipeline API key.
58
+
59
+ ### 2. Wait / status
60
+
61
+ ```json
62
+ { "action": "wait", "jobId": "<jobId>" }
63
+ ```
64
+
65
+ or
66
+
67
+ ```json
68
+ { "action": "status", "jobId": "<jobId>" }
69
+ ```
70
+
71
+ Statuses: `queued` → `running` → `succeeded` | `partial` | `failed`.
72
+
73
+ - **succeeded** — all files ready
74
+ - **partial** — some ready; `gaps[]` lists failures
75
+ - **failed** — nothing usable; stop and report gaps
76
+
77
+ **Do not** invent assets for gaps.
78
+
79
+ ### 3. Catalog
80
+
81
+ ```json
82
+ { "action": "catalog", "jobId": "<jobId>" }
83
+ ```
84
+
85
+ Each asset includes:
86
+
87
+ | Field | Use |
88
+ |-------|-----|
89
+ | `readyUrl` | `asset upload --url` into Contentful |
90
+ | `previewUrl` / `posterUrl` | Agent vision for **title / fileName / alt** (when present) |
91
+ | `altDraft` / `titleDraft` | Filename seeds only — improve before save |
92
+
93
+ ### 4. Import into Contentful (reuse-first)
94
+
95
+ After drafting titles/alts from **previewUrl** / **posterUrl**:
96
+
97
+ ```json
98
+ {
99
+ "action": "import",
100
+ "jobId": "<jobId>",
101
+ "withMedia": true,
102
+ "overrides": [
103
+ { "sourceName": "OM1_Hero.jpg", "title": "OM1 hero", "alt": "…" }
104
+ ]
105
+ }
106
+ ```
107
+
108
+ Optional: `"dryRun": true` to preview the map without writing.
109
+
110
+ CLI: `cms-edit media import --job <id> [--dry-run] [--with-media] [--overrides-json '[…]']`
111
+
112
+ Import:
113
+
114
+ 1. Searches existing assets by fileName
115
+ 2. Else uploads from `readyUrl` as the authenticated Contentful user
116
+ 3. Returns `map: { sourceName → { assetId, mediaId?, … } }` for page build
117
+
118
+ Manual `asset upload --url` remains available for one-offs.
119
+
120
+ ### Hardening notes (for agents)
121
+
122
+ - Jobs are **owned by the OAuth user** who prepared them — you cannot poll another user’s `jobId`.
123
+ - Rate limits apply (prepares/hour, concurrent jobs).
124
+ - Never document or request `MEDIA_PIPELINE_API_KEY` in chat.
@@ -28,9 +28,16 @@
28
28
 
29
29
  ## Upload only when no match
30
30
 
31
+ ### Oversized masters / whole Drive folders
32
+
33
+ If files are near or over **4 MB** (hosted staged) or **10 MB** (URL/local), or the user shared a **Drive folder of masters**, do **not** improvise compression in chat.
34
+
35
+ 1. Run **`task-media-pipeline-prepare`** (Drive folder → ready public URLs under limits)
36
+ 2. Return here and import with **`--url`** + reuse-first filename search
37
+
31
38
  ### Hosted MCP (default)
32
39
 
33
- **Always use staged upload** for images and other binary files. Do not pass base64 in `cms_edit` args — hosted MCP rejects `asset upload --base64`.
40
+ **Always use staged upload** for ad-hoc small binaries. Do not pass base64 in `cms_edit` args — hosted MCP rejects `asset upload --base64`.
34
41
 
35
42
  1. `cms_edit_request_staged_upload` with `fileName`, `mimeType`, optional `byteLength`, optional `title`
36
43
  2. Run the returned `curlCommand` in a shell (POST file to `uploadUrl`)
@@ -57,10 +64,10 @@ To rename an existing Media wrapper: `cms_edit ["batch", "set", "<media-entry-id
57
64
 
58
65
  Avoid `asset upload --with-media` when multiple entries will share the same image (e.g. news composites) — it creates duplicate Media entries. Upload the asset only, then one shared `create media`, then link the media ID on each entry.
59
66
 
60
- **Last resort `--url`** only when the file is already on public HTTPS and staged upload is impractical:
67
+ **Prefer `--url`** when the file is already on **public HTTPS** under size limits — especially **media-pipeline ready pack URLs**:
61
68
 
62
69
  ```
63
- cms_edit ["asset", "upload", "--url", "https://<public-https-url>/file.png", "--mime", "image/png", "--file-name", "headshot-name-1200.png", "--title", "…"]
70
+ cms_edit ["asset", "upload", "--url", "https://<public-https-url>/file.png", "--mime", "image/png", "--file-name", "headshot-name-1200.png", "--title", "…", "--if-exists-by-filename"]
64
71
  ```
65
72
 
66
73
  The URL must be fetchable by the hosted MCP server. Do not use local-only URLs.
@@ -99,5 +106,7 @@ Always `diff` → `save`. Verify with `preview urls` on the public path.
99
106
 
100
107
  ## Related resources
101
108
 
109
+ - `task-media-pipeline-prepare`
110
+
102
111
  - `cms-edit://customer/defaults` (featured image rules)
103
112
  - `cms-edit://customer/people` (person `media` field, when present)
@@ -111,5 +111,6 @@ Copy: unchanged from source (list any human decisions only)
111
111
  - `task-create-from-document`
112
112
  - `task-create-article`
113
113
  - `task-create-page`
114
+ - `task-media-pipeline-prepare`
114
115
  - `task-media-reuse-and-upload`
115
116
  - `task-preview-verify`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/contentful-cms",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "CLI tool for AI agents to read and edit Contentful draft content without publish permissions",
5
5
  "repository": {
6
6
  "type": "git",