frontpage-host 0.3.13 → 0.3.15

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
@@ -264,7 +264,7 @@ Manual group selection uses `groups.people` (20 per page, phone-only contacts in
264
264
 
265
265
  ## AI Support Agent
266
266
 
267
- Scale and internal sites, after owner activation. No pilot allowlist. Use `frontpage customer-inbox get|put|post RESOURCE`, stable write keys and current versions. Full guide: [CUSTOMER-INBOX.md](./CUSTOMER-INBOX.md). `export CONVERSATION_ID --out FILE` writes authorized paginated history and file metadata; `download FILE_ID --out FILE` downloads through a file grant. Outputs never overwrite existing files. `frontpage usage` reports shared site credits, mail and storage. The owner toolbar uses `manage_customer_inbox` for all site-wide Inbox settings, unlocked knowledge, files, customer conversations, private guidance, replies, takeover/return and recovery. The support agent has a separate server-bound customer scope and requires verification for People data. Both agents cannot edit or unlock locked knowledge; human UI/CLI can unlock it. Never lend a website agent a human CLI key.
267
+ Scale and internal sites, after owner activation. No pilot allowlist. Use `frontpage customer-inbox get|put|post RESOURCE`, stable write keys and current versions for settings, sources and conversations. Knowledge saves use last-save-wins; read the latest note before editing. Knowledge history supports limit/cursor pagination and full revision reads by version, within the plan Rewind allowance plus the latest revision. Archive notes through POST knowledge/ID/archive with {} to stop future use while preserving all records; do not archive without the owner asking. Full guide: [CUSTOMER-INBOX.md](./CUSTOMER-INBOX.md). `export CONVERSATION_ID --out FILE` writes authorized paginated history and file metadata; `download FILE_ID --out FILE` downloads through a file grant. Outputs never overwrite existing files. `frontpage usage` reports shared site credits, mail and storage. The owner toolbar uses `manage_customer_inbox` for all site-wide Inbox settings, unlocked knowledge, files, customer conversations, private guidance, replies, takeover/return and recovery. The support agent has a separate server-bound customer scope and requires verification for People data. Both agents cannot edit, archive or unlock locked knowledge; human UI/CLI can unlock it. Never lend a website agent a human CLI key.
268
268
 
269
269
 
270
270
  Payments, invoices and recurring billing: see packaged [PAYMENTS.md](./PAYMENTS.md), `frontpage help payments`, and https://frontpage.host/developers/payments. Version 0.2.0 removes writable currency, Store checkout mode and Store-owned money commands; Store stays cart-only. Preview/review before sends; keep stable operation keys on retries. Autopay requires the customer’s own verified authorization.
@@ -299,3 +299,19 @@ For a manual group, `groups.people` accepts `availability: "all" | "available" |
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
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/CUSTOMER-INBOX.md CHANGED
@@ -50,9 +50,11 @@ Aliases use 3–48 ASCII letters, numbers and single hyphens. The service normal
50
50
  | --- | --- | --- |
51
51
  | `knowledge` | GET | Recent notes; optional `?q=search` |
52
52
  | `knowledge/<id>` | GET | One full note |
53
- | `knowledge/<id>/history` | GET | Immutable revisions for that note |
54
- | `knowledge` | PUT | `{"title":"Evening visits","body":"Ask the owner before promising an evening visit.","expectedVersion":0,"reason":"Owner policy"}` |
55
- | `knowledge/<id>` | PUT | Current title/body, `expectedVersion`, `reason`, optional `locked` or `restoreVersion` |
53
+ | `knowledge/<id>/history?limit=20` | GET | Revision summaries, `nextCursor`, and the plan history allowance; follow `&cursor=...` |
54
+ | `knowledge/<id>/history?version=3` | GET | One full revision within the plan allowance, or the latest version |
55
+ | `knowledge/<id>/archive` | POST | `{}`; keeps content and history, removes the note from future use |
56
+ | `knowledge` | PUT | `{"title":"Evening visits","body":"Ask the owner before promising an evening visit.","reason":"Owner policy"}` |
57
+ | `knowledge/<id>` | PUT | Current title/body, `reason`, optional `locked` or `restoreVersion`; legacy `expectedVersion` is accepted but does not reject a save |
56
58
  | `sources` | GET | Approved source records; `?overview=true` also includes the latest learning job status |
57
59
  | `sources` | PUT | `{"url":"https://example.com/help","enabled":true,"expectedVersion":0}`; fetches public text |
58
60
 
@@ -73,7 +75,19 @@ The response data is the same source with `enabled:false` and version `3`; its s
73
75
 
74
76
  Supply `body` on a source write to save an owner-approved excerpt instead of fetching. Published sources refresh when the agent reads a source older than 24 hours. The fetcher reuses Core's DNS-pinned public HTTP infrastructure, rejects private addresses, checks redirects and bounds content. Sources over 16000 text characters require an owner-approved excerpt. No scripts run. A source version/digest fences knowledge updates against a changed source.
75
77
 
76
- Knowledge search uses bounded literal keywords from natural-language queries and ranks notes by matched terms. Read the full matching note before updating it; update its current ID/version instead of creating a conflicting duplicate.
78
+ Knowledge search uses bounded literal keywords from natural-language queries and ranks notes by matched terms. Read the full matching note before updating it; read its latest content and update that same ID instead of creating a duplicate. Knowledge uses last-save-wins: two saves can both succeed; the last completed save becomes current, and each saved change stays in history. This does not change version checks for settings, approved sources, or conversations.
79
+
80
+ History uses the same central Rewind allowance as template history. Scale includes 90 days; internal sites have uncapped history. The latest revision stays available regardless of age. Older snapshots remain stored, but history reads and restores only allow revisions within the allowance or the latest revision. A paginated response includes `revisions` (version, actor, createdAt), `currentVersion`, `nextCursor`, `windowDays`, `unlimited`, `canUpgrade`, and `moreBeyondWindow`. A `version` request returns `{revision}` with full saved content. Older callers that omit pagination keep their full-revision response shape, bounded to 100 entries and the same allowance.
81
+
82
+ ```bash
83
+ frontpage customer-inbox get 'knowledge/NOTE_UUID/history?limit=20' --site my-site --json
84
+ frontpage customer-inbox get 'knowledge/NOTE_UUID/history?limit=20&cursor=21' --site my-site --json
85
+ frontpage customer-inbox get 'knowledge/NOTE_UUID/history?version=3' --site my-site --json
86
+ # archive.json contains {}. Use the same file and key when retrying.
87
+ frontpage customer-inbox post knowledge/NOTE_UUID/archive --file archive.json --request-key archive_note_001 --site my-site --json
88
+ ```
89
+
90
+ Archiving retains the note and every revision, hides it from normal lists/searches and future agent reads, and prevents later saves from reactivating it. There is no archived-notes screen or permanent-delete action. Runs already in progress keep what they loaded. Archiving does not remove matching information from approved webpages or replies already sent.
77
91
 
78
92
  Lock a knowledge note with `locked:true`. Neither the support agent nor the owner toolbar agent can change locked notes or locks. Human UI/CLI actions can unlock them. Restore an older note with `restoreVersion`; the service creates a new revision and preserves the old history. The agent can change only internal knowledge, using verified owner guidance or a current approved source. It cannot edit public pages or articles.
79
93
 
@@ -372,7 +386,7 @@ The support agent has its own system prompt plus the versioned private business
372
386
  | --- | --- | --- |
373
387
  | `knowledge_read` | `query`, `id` or `sourceId` | Search internal notes or read complete approved evidence. |
374
388
  | `history_read` | `before`, optional `query` | Retrieve up to ten older eligible messages in the assigned conversation under the `knowledge_read` grant; bounded to 6000 text characters. |
375
- | `knowledge_update` | Optional `id`; `title`, `body`, `expectedVersion`, `reason`, `evidenceKind`, `evidenceId`; source `evidenceVersion` | Available only in a restricted learning job. Update unlocked business facts from that job's assigned owner guidance or approved source revision. One-off guidance stays in the conversation. No public editing. |
389
+ | `knowledge_update` | Optional `id` and legacy `expectedVersion`; `title`, `body`, `reason`, `evidenceKind`, `evidenceId`; source `evidenceVersion` | Available only in a restricted learning job. Update unlocked business facts from that job's assigned owner guidance or approved source revision. One-off guidance stays in the conversation. No public editing. |
376
390
  | `customer_read` | `sections` (`profile`, `notes`, `orders`, `activity`), optional `before` | Read only the request-bound verified customer. Notes page 25 at a time; orders page 20. A cursor must belong to that same customer and site. Order ownership ambiguity for an app identity returns `account_mapping_required`, with no orders. |
377
391
  | `verify_customer` | None | Request proof at the server-bound mailbox and pause. Resume unanswered work after confirmation and People projection. No recipient selector. |
378
392
  | `customer_update` | `name`/`phone`, `evidenceMessageId` | Enrich from an explicit customer statement; cannot change identity or permission. |
package/README.md CHANGED
@@ -113,7 +113,7 @@ An explicit stock target already equal to the current count is a no-op. Repeatin
113
113
 
114
114
  ### AI Support Agent
115
115
 
116
- Included on Scale and internal sites after owner activation. Use `frontpage customer-inbox` for settings, knowledge and customer conversations. The command and API paths keep their existing names. See [AI Support Agent](./CUSTOMER-INBOX.md) for permissions, private guidance, takeover, verification and shared usage.
116
+ Included on Scale and internal sites after owner activation. Use `frontpage customer-inbox` for settings, knowledge and customer conversations. Knowledge saves use last-save-wins. Read paginated revision history within the plan Rewind allowance, or archive a note to stop future use without deleting its records. The command and API paths keep their existing names. See [AI Support Agent](./CUSTOMER-INBOX.md) for permissions, private guidance, takeover, verification and shared usage.
117
117
 
118
118
  ### Website models and AI credits
119
119
 
@@ -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.
@@ -5,7 +5,7 @@ import { takeFlag, assertNoUnknownFlags } from "../argv.js";
5
5
  import { getApiKey } from "../auth/keychain.js";
6
6
  import { apiBase, v1Get, httpErrorFromV1, CliHttpError, EXIT_AUTH, EXIT_USAGE, EXIT_OK, exitCodeForError, } from "../http/client.js";
7
7
  import { emitOk, emitErr, isJson } from "../runtime.js";
8
- const RESOURCE = /^(?:status|activation|address|widget|website|settings|knowledge(?:\/[0-9a-f-]+(?:\/history)?)?|sources(?:\/[0-9a-f-]+)?|blocks(?:\/[0-9a-f-]+\/unblock)?|jobs(?:\/[0-9a-f-]+(?:\/resume)?)?|files(?:\/[0-9a-f-]+)?|receipts(?:\/[0-9a-f-]+)?|conversations(?:\/[0-9a-f-]+(?:\/(?:messages|routing|guidance|reply|control|state|revoke-sessions|verification))?)?)(?:\?[^#]*)?$/;
8
+ const RESOURCE = /^(?:status|activation|address|widget|website|settings|knowledge(?:\/[0-9a-f-]+(?:\/(?:history|archive))?)?|sources(?:\/[0-9a-f-]+)?|blocks(?:\/[0-9a-f-]+\/unblock)?|jobs(?:\/[0-9a-f-]+(?:\/resume)?)?|files(?:\/[0-9a-f-]+)?|receipts(?:\/[0-9a-f-]+)?|conversations(?:\/[0-9a-f-]+(?:\/(?:messages|routing|guidance|reply|control|state|revoke-sessions|verification))?)?)(?:\?[^#]*)?$/;
9
9
  export function inboxCommand(args) {
10
10
  const work = [...args], verb = work.shift(), resource = work.shift(), file = takeFlag(work, "--file"), key = takeFlag(work, "--request-key"), out = takeFlag(work, "--out");
11
11
  assertNoUnknownFlags(work, "customer-inbox");
@@ -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",
@@ -425,12 +428,16 @@ export const COMMANDS = [
425
428
  "frontpage customer-inbox put settings --file ./inbox-settings.json --request-key settings_revision_1",
426
429
  "frontpage customer-inbox get conversations",
427
430
  "frontpage customer-inbox get knowledge",
431
+ "frontpage customer-inbox get 'knowledge/NOTE_UUID/history?limit=20'",
432
+ "frontpage customer-inbox post knowledge/NOTE_UUID/archive --file archive.json --request-key archive_note_001",
428
433
  ],
429
434
  flags: [
430
435
  "Available on Scale and internal sites after owner activation; no pilot allowlist",
431
436
  "Writes require a stable --request-key and a JSON file; reuse both on retries",
432
437
  "Human CLI and owner-agent tools manage authorized conversations; customer support tools stay bound to the verified customer",
433
438
  "Luna and 10× AI billing are fixed",
439
+ "Knowledge uses last-save-wins; history follows plan Rewind limits and always includes the latest revision",
440
+ "Archive knowledge with {} to stop future use while keeping the note and history",
434
441
  "Full request examples: CUSTOMER-INBOX.md",
435
442
  ],
436
443
  seeAlso: ["people", "usage"],
package/llms.txt CHANGED
@@ -59,3 +59,7 @@ The workspace validates and saves unfinished group settings before opening email
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
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.
64
+
65
+ AI Support Agent knowledge: read the latest note, then save the same ID. Last save wins and prior saved changes remain in history. Knowledge history supports limit/cursor and version reads within the plan Rewind allowance plus the latest version. Archive with customer-inbox post knowledge/ID/archive and {} to stop future use without deleting records. See CUSTOMER-INBOX.md.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontpage-host",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "Frontpage CLI — manage your site from the terminal",
5
5
  "type": "module",
6
6
  "bin": {