frontpage-host 0.3.12 → 0.3.14

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/AGENTS.md CHANGED
@@ -298,4 +298,20 @@ For a manual group, `groups.people` accepts `availability: "all" | "available" |
298
298
 
299
299
  Complete Store edits: use existing items update --file with changed fields and variantUpdates. stock requires expectedStock from a fresh items get. One transaction saves all changes or none. On 409 review current stock; never silently substitute it. firstVariant plus optionNames enables versions. Keep explicit stock/variant commands for single operations. Routines add accepts --request-id UUID; reuse that ID and exact schedule after a lost response. Ordinary form edits live only in the active task; Cancel keeps input, Discard loses unfinished input. Submitted payments, imports, builds, and API-key status are separate operations.
300
300
 
301
- Template history (frontpage-host 0.3.11+): read `mail template get ID` before editing. `mail template history ID --limit 20 --cursor CURSOR` pages saved copies; `mail template history-get ID --history-id COPY_ID` fetches full content. Edit any copy, then `mail template set ID --file content.json` with the stable template ID and content fields only to make a new latest copy. Last template save wins; past copies remain. Campaign and series conflict checks remain. Retry uncertain saves through `mail save` with the same requestKey and exact payload. History begins with content retained at launch; earlier overwritten edits are unavailable. See MAIL.md.
301
+ Template history (frontpage-host 0.3.11+): read `mail template get ID` before editing. `mail template history ID --limit 20 --cursor CURSOR` pages saved copies; `mail template history-get ID --history-id COPY_ID` fetches full content. Edit any copy, then `mail template set ID --file content.json` with the stable template ID and content fields only to make a new latest copy. Last template save wins; past copies remain. Campaign and series conflict checks remain. Retry uncertain saves through `mail save` with the same requestKey and exact payload. History begins with content retained at launch; earlier overwritten edits are unavailable. History reads use the plan’s Rewind window and always include Latest, even when older. Older copies stay stored; lists and direct reads apply the same limit. Window metadata explains the limit and whether older copies exist. See MAIL.md.
302
+
303
+ ## Uploaded video preparation
304
+
305
+ The current CLI attempts browser-compatible H.264 MP4 preparation after a local video
306
+ upload. This applies to `frontpage media upload` and `frontpage prompt --attach ./clip.mov`.
307
+ Use the returned `sitePath`; do not reconstruct a path from the original filename.
308
+ A successful conversion returns `prepared:true` and the MP4's actual filename, size and
309
+ URL. If preparation fails, the original remains usable as an upload with `prepared:false`
310
+ and a compatibility warning. Warnings print on stderr and appear in JSON success data.
311
+ An original's MP4/MOV extension does not establish browser compatibility.
312
+
313
+ `frontpage media list` and the website agent's `list_site_assets` prefer a completed
314
+ prepared copy over its exact original. Different uploads with the same display name
315
+ remain distinct. Existing URLs and published videos are not converted automatically.
316
+ Upgrade the CLI to receive upload-time preparation. Source path and access denials
317
+ remain errors; they do not become original-file fallback successes.
package/MAIL.md CHANGED
@@ -44,9 +44,11 @@ frontpage mail template history TEMPLATE_ID --cursor NEXT_CURSOR --site YOUR_SIT
44
44
  frontpage mail template history-get TEMPLATE_ID --history-id SAVED_COPY_ID --site YOUR_SITE --json
45
45
  ```
46
46
 
47
- History returns `items` (ID, savedAt, name, subject), `currentId` and `nextCursor`. Fetch the next page until `nextCursor` is null. The default page size is 20, with a maximum of 100. `history-get` returns `copy` with full content and `templateId`, the stable ID. The workspace shows the same history under Sender; scrolling loads the next 20 copies.
47
+ History returns `items` (ID, savedAt, name, subject), `currentId`, `nextCursor`, `windowDays`, `unlimited`, `canUpgrade` and `moreBeyondWindow`. The last field reports hidden older copies when the final page is reached. Fetch the next page until `nextCursor` is null. The default page size is 20, with a maximum of 100. `history-get` returns `copy` with full content and `templateId`, the stable ID. The workspace shows the same history under Sender; scrolling loads the next 20 copies.
48
48
 
49
- To use an old copy, fetch it, edit it if needed, then save normally. Put only `name`, `subject`, `preheader`, `blocks` and `variables` in a JSON file, then run `frontpage mail template set TEMPLATE_ID --file content.json --site YOUR_SITE --json`. Use the stable TEMPLATE_ID, never the saved-copy ID. This creates a new latest copy; it does not change the old one. There is no separate restore command. Read `template get` before each edit so you start with current work. For response-loss retries use `mail save` with one unchanged `requestKey` and payload; replaying that save creates no duplicate copy.
49
+ Template history reads follow the site plan’s Rewind window: Starter 7 days, Growth 30 days, Scale 90 days; Enterprise and Internal use the existing uncapped history rule. Latest remains readable at any age. Older copies stay stored and become readable again within a larger window after an upgrade. Both history lists and history-get enforce the window; direct saved-copy IDs do not bypass it. Existing feature and CLI plan gates still apply.
50
+
51
+ To use an available old copy, fetch it, edit it if needed, then save normally. Put only `name`, `subject`, `preheader`, `blocks` and `variables` in a JSON file, then run `frontpage mail template set TEMPLATE_ID --file content.json --site YOUR_SITE --json`. Use the stable TEMPLATE_ID, never the saved-copy ID. This creates a new latest copy; it does not change the old one. There is no separate restore command. Read `template get` before each edit so you start with current work. For response-loss retries use `mail save` with one unchanged `requestKey` and payload; replaying that save creates no duplicate copy.
50
52
 
51
53
  Public API reads: `GET /api/v1/sites/SLUG/mail/templates/TEMPLATE_ID/history?limit=20&cursor=…` and `GET /api/v1/sites/SLUG/mail/templates/TEMPLATE_ID/history/SAVED_COPY_ID`. Viewers may read; editors and owners may save. Site access and plan checks still apply.
52
54
 
package/PEOPLE.md CHANGED
@@ -788,4 +788,4 @@ Writes are not universally idempotent. Preserve documented request keys, receipt
788
788
 
789
789
  For MCP CSV exports, continue `contacts.export` with `textOffset` set to `textPage.nextOffset` and `expectedTextVersion` set to `textPage.version`. The tool returns bounded `csv` text and a version hash; a changed export returns a conflict instead of combining different snapshots. `textLength` defaults to 4,000 UTF-16 units and accepts up to 8,000.
790
790
 
791
- Template history uses the existing Email tools. Read get_mail_template before editing. list_mail_template_history({id,cursor?,limit?}) pages copies; get_mail_template_history({id,historyId}) reads full content. Edit any copy and save through update_mail_template with the stable ID to make it latest. Both simultaneous saves remain; last template save wins. Group, rule and series checks remain. CLI equivalents and full instructions: MAIL.md, Template history.
791
+ Template history uses the existing Email tools. Read get_mail_template before editing. list_mail_template_history({id,cursor?,limit?}) pages copies; get_mail_template_history({id,historyId}) reads full content. Edit any copy and save through update_mail_template with the stable ID to make it latest. Both simultaneous saves remain; last template save wins. Group, rule and series checks remain. History lists and direct reads follow the plan’s Rewind window, plus Latest at any age. Older copies stay stored. CLI equivalents and full instructions: MAIL.md, Template history.
package/README.md CHANGED
@@ -122,3 +122,19 @@ GPT 6 Luna is the default for new website runs. Explicit site model preferences
122
122
  ### Template history
123
123
 
124
124
  Template history (0.3.11+): `frontpage mail template history ID` lists saved copies; `history-get ID --history-id COPY_ID` reads full content. Edit any copy and save with `template set ID --file content.json` to make it latest. Use the stable template ID, read current content before editing, and see [MAIL.md](./MAIL.md#template-history) for paging, access and retry-safe saves.
125
+
126
+ ## Uploaded video preparation
127
+
128
+ The current CLI attempts browser-compatible H.264 MP4 preparation after a local video
129
+ upload. This applies to `frontpage media upload` and `frontpage prompt --attach ./clip.mov`.
130
+ Use the returned `sitePath`; do not reconstruct a path from the original filename.
131
+ A successful conversion returns `prepared:true` and the MP4's actual filename, size and
132
+ URL. If preparation fails, the original remains usable as an upload with `prepared:false`
133
+ and a compatibility warning. Warnings print on stderr and appear in JSON success data.
134
+ An original's MP4/MOV extension does not establish browser compatibility.
135
+
136
+ `frontpage media list` and the website agent's `list_site_assets` prefer a completed
137
+ prepared copy over its exact original. Different uploads with the same display name
138
+ remain distinct. Existing URLs and published videos are not converted automatically.
139
+ Upgrade the CLI to receive upload-time preparation. Source path and access denials
140
+ remain errors; they do not become original-file fallback successes.
@@ -23,6 +23,7 @@ export type PromptAttachmentsPayload = {
23
23
  url: string;
24
24
  name?: string;
25
25
  sizeBytes?: number;
26
+ warning?: string;
26
27
  }>;
27
28
  };
28
29
  export type MediaUploadResult = UploadedMedia;
@@ -101,8 +101,9 @@ export function addUploadResult(out, data) {
101
101
  if (!out.videos.some((x) => x.url === data.url)) {
102
102
  out.videos.push({
103
103
  url: data.url,
104
- name,
104
+ name: data.prepared ? data.filename ?? name : name,
105
105
  sizeBytes: data.sizeBytes,
106
+ ...(data.warning ? { warning: data.warning } : {}),
106
107
  });
107
108
  }
108
109
  return;
@@ -122,7 +123,7 @@ export async function uploadLocalForAttach(slug, filePath) {
122
123
  return uploadLocalMedia({ slug, filePath });
123
124
  }
124
125
  export async function resolveSitePathUrl(slug, sitePath) {
125
- const data = await v1Get(`/api/v1/sites/${encodeURIComponent(slug)}/media`);
126
+ const data = await v1Get(`/api/v1/sites/${encodeURIComponent(slug)}/media?path=${encodeURIComponent(sitePath.replace(/\/+/g, "/").replace(/\/$/, ""))}`);
126
127
  const norm = (p) => p.replace(/\/+/g, "/").replace(/\/$/, "") || p;
127
128
  const want = norm(sitePath);
128
129
  const hit = data.assets.find((a) => a.sitePath === sitePath || norm(a.sitePath) === want);
@@ -8,6 +8,8 @@ export type UploadedMedia = {
8
8
  contentType?: string;
9
9
  content?: string;
10
10
  transport?: "proxy" | "client";
11
+ prepared?: boolean;
12
+ warning?: string;
11
13
  };
12
14
  /** Upload a local file using the Phase E transport matrix. */
13
15
  export declare function uploadLocalMedia(args: {
@@ -5,7 +5,7 @@ import { randomUUID } from "node:crypto";
5
5
  import { readFile } from "node:fs/promises";
6
6
  import { basename } from "node:path";
7
7
  import { put } from "@vercel/blob/client";
8
- import { apiBase, CliHttpError, EXIT_AUTH, EXIT_UNEXPECTED, EXIT_USAGE, getBearerAuthHeader, httpErrorFromV1, v1PostMultipart, } from "../http/client.js";
8
+ import { apiBase, CliHttpError, EXIT_AUTH, EXIT_UNEXPECTED, EXIT_USAGE, getBearerAuthHeader, httpErrorFromV1, v1PostMultipart, v1Post, } from "../http/client.js";
9
9
  import { contentTypeForExt, sitePathFromKey } from "./limits.js";
10
10
  import { selectMediaTransport } from "./select-transport.js";
11
11
  async function requestClientToken(args) {
@@ -81,7 +81,7 @@ async function uploadViaClient(args) {
81
81
  multipart,
82
82
  });
83
83
  const key = blob.pathname.replace(/^\//, "");
84
- return {
84
+ const original = {
85
85
  kind: args.kind,
86
86
  url: blob.url,
87
87
  key,
@@ -91,8 +91,28 @@ async function uploadViaClient(args) {
91
91
  contentType: args.contentType || contentTypeForExt(args.ext),
92
92
  transport: "client",
93
93
  };
94
+ if (args.kind !== "video")
95
+ return original;
96
+ try {
97
+ const prepared = await v1Post(`/api/v1/sites/${encodeURIComponent(args.slug)}/media/prepare`, { key, filename: args.filename }, { timeoutMs: 110000 });
98
+ if (typeof prepared.url !== "string" || typeof prepared.sitePath !== "string" || typeof prepared.filename !== "string" || typeof prepared.prepared !== "boolean")
99
+ throw new Error("Invalid video preparation response");
100
+ const result = { ...prepared, transport: "client" };
101
+ if (result.warning)
102
+ console.error(result.warning);
103
+ return result;
104
+ }
105
+ catch (error) {
106
+ if (error instanceof CliHttpError && error.status >= 400 && error.status < 500 && ![408, 429].includes(error.status))
107
+ throw error;
108
+ const warning = "Uploaded original video. Some browsers may not play this format.";
109
+ console.error(warning);
110
+ return { ...original, prepared: false, warning };
111
+ }
94
112
  }
95
113
  catch (err) {
114
+ if (err instanceof CliHttpError)
115
+ throw err;
96
116
  const msg = err instanceof Error ? err.message : String(err);
97
117
  throw new CliHttpError(`Upload failed: ${msg}`, EXIT_UNEXPECTED, 500);
98
118
  }
package/dist/registry.js CHANGED
@@ -407,6 +407,9 @@ export const COMMANDS = [
407
407
  ],
408
408
  flags: [
409
409
  "Auto-selects proxy (≤4 MB) vs direct-to-Blob for large rasters/PDFs and all video",
410
+ "Video uploads then attempt browser-compatible MP4 preparation; use the returned sitePath",
411
+ "Preparation failure keeps the original with a stderr warning and prepared:false in JSON",
412
+ "media list prefers an exact completed prepared video over its original",
410
413
  "Human success: sitePath first (markup-safe), then url: (blob URL for debugging)",
411
414
  "Use sitePath (/__media/uploads/<slug>/…) in markup — never raw blob hosts in pages",
412
415
  "Text/CSV upload is inline only — not a media-library asset (no /__media link); --json omits full content",
@@ -593,7 +596,7 @@ export const COMMANDS = [
593
596
  "sends list --q --status --type campaign|series|confirmation|template|test|inline --page 1 --limit 20.",
594
597
  "Series status active means On; paused/draft mean Off. Enrollment while Off waits at step 1, without sending. Existing people resume their next unsent step.",
595
598
  "campaign preview|audience|send|get. --id DRAFT_ID, --file audience.json ({audience:{groupIds|stageIds|personIds|filters}}). send requires --request-key; reuse it after a network error. Templates/series are for automation; campaigns have separate drafts. See PEOPLE.md.",
596
- "template history <id> pages saved copies with --limit (20 by default, max 100) and --cursor. history-get <id> --history-id reads full content. Use template set with the stable template ID to save edited content as latest; past copies stay unchanged. Read template get before editing. History begins with content retained when history shipped, not lost earlier edits.",
599
+ "template history <id> pages saved copies with --limit (20 by default, max 100) and --cursor. history-get <id> --history-id reads full content. Both reads follow the plan Rewind window, plus Latest at any age; older copies stay stored. Lists report windowDays, unlimited, canUpgrade and moreBeyondWindow. Use template set with the stable template ID to save edited content as latest; past copies stay unchanged. Read template get before editing. History begins with content retained when history shipped, not lost earlier edits.",
597
600
  "email list is unsent drafts only. Sent one-offs are mail sends list",
598
601
  "send --file is an inline one-off (no saved document)",
599
602
  "Blocks and series steps are JSON (--file). Unknown leftover flags fail closed (exit 2)",
package/llms.txt CHANGED
@@ -58,4 +58,6 @@ The workspace validates and saves unfinished group settings before opening email
58
58
 
59
59
  Complete Store edits: use existing items update --file with changed fields and variantUpdates. stock requires expectedStock from a fresh items get. One transaction saves all changes or none. On 409 review current stock; never silently substitute it. firstVariant plus optionNames enables versions. Keep explicit stock/variant commands for single operations. Routines add accepts --request-id UUID; reuse that ID and exact schedule after a lost response. Ordinary form edits live only in the active task; Cancel keeps input, Discard loses unfinished input. Submitted payments, imports, builds, and API-key status are separate operations.
60
60
 
61
- Template history (frontpage-host 0.3.11+): read `mail template get ID` before editing. `mail template history ID --limit 20 --cursor CURSOR` pages saved copies; `mail template history-get ID --history-id COPY_ID` fetches full content. Edit any copy, then `mail template set ID --file content.json` with the stable template ID and content fields only to make a new latest copy. Last template save wins; past copies remain. Campaign and series conflict checks remain. Retry uncertain saves through `mail save` with the same requestKey and exact payload. History begins with content retained at launch; earlier overwritten edits are unavailable. See MAIL.md.
61
+ Template history (frontpage-host 0.3.11+): read `mail template get ID` before editing. `mail template history ID --limit 20 --cursor CURSOR` pages saved copies; `mail template history-get ID --history-id COPY_ID` fetches full content. Edit any copy, then `mail template set ID --file content.json` with the stable template ID and content fields only to make a new latest copy. Last template save wins; past copies remain. Campaign and series conflict checks remain. Retry uncertain saves through `mail save` with the same requestKey and exact payload. History begins with content retained at launch; earlier overwritten edits are unavailable. History reads use the plan’s Rewind window and always include Latest, even when older. Older copies stay stored; lists and direct reads apply the same limit. Window metadata explains the limit and whether older copies exist. See MAIL.md.
62
+
63
+ Video uploads: the current CLI prepares a browser-compatible MP4 after uploading a local video, including prompt --attach. Use the returned sitePath and actual filename. prepared:false plus warning means the original was retained and playback compatibility is not assured. media list prefers an exact completed prepared copy. Existing video URLs are unchanged. See AGENTS.md for the upload and fallback contract.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontpage-host",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "description": "Frontpage CLI — manage your site from the terminal",
5
5
  "type": "module",
6
6
  "bin": {