frontpage-host 0.1.6 → 0.1.7
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 +8 -0
- package/dist/commands/prompt-attach.d.ts +59 -0
- package/dist/commands/prompt-attach.js +207 -0
- package/dist/commands/prompt.js +28 -18
- package/dist/completions/generate.js +1 -0
- package/dist/registry.js +19 -4
- package/llms.txt +1 -0
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -119,10 +119,18 @@ Sites are Astro 6 + Tailwind v4. More detail: `frontpage help site-building` or
|
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
121
|
frontpage prompt "make the hero shorter"
|
|
122
|
+
frontpage prompt "Use this logo in the header" --attach ./logo.png
|
|
123
|
+
frontpage prompt "Match this brand PDF" --attach ./guide.pdf --path /
|
|
124
|
+
frontpage prompt "Reuse uploaded art" --attach /__media/uploads/<slug>/<file>
|
|
122
125
|
frontpage runs wait # wait for the agent run to finish
|
|
123
126
|
# Review https://<subdomain>.frontpage.host — do not publish unless asked.
|
|
124
127
|
```
|
|
125
128
|
|
|
129
|
+
- `--attach` accepts a local file (uploads via the same path as `frontpage media upload`), an existing `/__media/…` site path, or an `https://` Blob URL. Repeatable.
|
|
130
|
+
- `--asset-url <https://…>` adds a legacy image URL. Repeatable.
|
|
131
|
+
- Caps match the media proxy: images / PDF / text-code. CLI video and >4 MB binaries are not supported yet (editor attach / Phase E).
|
|
132
|
+
- Prompt text is still required (API). Attachments ride on the same enqueue as the editor.
|
|
133
|
+
|
|
126
134
|
Mid-task continuation is automatic. There is **no** `runs continue` in v1.
|
|
127
135
|
|
|
128
136
|
## Content / JSON-native commands
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Mirror ATTACH_LIMITS in src/lib/agent/attachments.ts (images/docs/text only via proxy media). */
|
|
2
|
+
export declare const CLI_ATTACH_LIMITS: {
|
|
3
|
+
readonly maxImages: 6;
|
|
4
|
+
readonly maxDocuments: 4;
|
|
5
|
+
readonly maxTextFiles: 6;
|
|
6
|
+
};
|
|
7
|
+
export type PromptAttachmentsPayload = {
|
|
8
|
+
images: Array<{
|
|
9
|
+
url: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
}>;
|
|
12
|
+
documents: Array<{
|
|
13
|
+
url: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
}>;
|
|
16
|
+
textFiles: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
content: string;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
21
|
+
export type MediaUploadResult = {
|
|
22
|
+
kind: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
key?: string;
|
|
25
|
+
filename?: string;
|
|
26
|
+
sizeBytes?: number;
|
|
27
|
+
content?: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function emptyAttachments(): PromptAttachmentsPayload;
|
|
30
|
+
export declare function isHttpsUrl(s: string): boolean;
|
|
31
|
+
export declare function isMediaSitePath(s: string): boolean;
|
|
32
|
+
export declare function classifyRemoteByName(nameOrUrl: string): "image" | "document";
|
|
33
|
+
export declare function addHttpsRef(out: PromptAttachmentsPayload, url: string, opts?: {
|
|
34
|
+
name?: string;
|
|
35
|
+
asImageOnly?: boolean;
|
|
36
|
+
}): void;
|
|
37
|
+
export declare function addUploadResult(out: PromptAttachmentsPayload, data: MediaUploadResult): void;
|
|
38
|
+
export declare function uploadLocalForAttach(slug: string, filePath: string): Promise<MediaUploadResult>;
|
|
39
|
+
export declare function resolveSitePathUrl(slug: string, sitePath: string): Promise<{
|
|
40
|
+
url: string;
|
|
41
|
+
name: string;
|
|
42
|
+
type: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve one --attach value (local path, /__media/…, or https URL) into the payload.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveAttachRef(slug: string, ref: string, out: PromptAttachmentsPayload): Promise<void>;
|
|
48
|
+
export declare function buildPromptAttachments(args: {
|
|
49
|
+
slug: string;
|
|
50
|
+
attachRefs: string[];
|
|
51
|
+
assetUrls: string[];
|
|
52
|
+
}): Promise<PromptAttachmentsPayload | undefined>;
|
|
53
|
+
/** Parse prompt argv: prompt words + --path / --attach / --asset-url flags. */
|
|
54
|
+
export declare function parsePromptArgs(rest: string[]): {
|
|
55
|
+
prompt: string;
|
|
56
|
+
currentPath: string;
|
|
57
|
+
attachRefs: string[];
|
|
58
|
+
assetUrls: string[];
|
|
59
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §CLI Phase C — resolve --attach / --asset-url into the agent attachments payload.
|
|
3
|
+
* Local files upload via the same multipart media endpoint as `frontpage media upload`.
|
|
4
|
+
*/
|
|
5
|
+
import { access, readFile } from "node:fs/promises";
|
|
6
|
+
import { basename } from "node:path";
|
|
7
|
+
import { constants as fsConstants } from "node:fs";
|
|
8
|
+
import { v1Get, v1PostMultipart, CliHttpError, EXIT_USAGE, EXIT_UNEXPECTED } from "../http/client.js";
|
|
9
|
+
/** Mirror ATTACH_LIMITS in src/lib/agent/attachments.ts (images/docs/text only via proxy media). */
|
|
10
|
+
export const CLI_ATTACH_LIMITS = {
|
|
11
|
+
maxImages: 6,
|
|
12
|
+
maxDocuments: 4,
|
|
13
|
+
maxTextFiles: 6,
|
|
14
|
+
};
|
|
15
|
+
export function emptyAttachments() {
|
|
16
|
+
return { images: [], documents: [], textFiles: [] };
|
|
17
|
+
}
|
|
18
|
+
export function isHttpsUrl(s) {
|
|
19
|
+
return /^https:\/\/[^\s]+$/i.test(s) && s.length <= 2048;
|
|
20
|
+
}
|
|
21
|
+
export function isMediaSitePath(s) {
|
|
22
|
+
return s.startsWith("/__media/");
|
|
23
|
+
}
|
|
24
|
+
export function classifyRemoteByName(nameOrUrl) {
|
|
25
|
+
const base = nameOrUrl.split(/[?#]/)[0] ?? nameOrUrl;
|
|
26
|
+
const ext = base.includes(".") ? (base.split(".").pop() ?? "").toLowerCase() : "";
|
|
27
|
+
if (ext === "pdf")
|
|
28
|
+
return "document";
|
|
29
|
+
return "image";
|
|
30
|
+
}
|
|
31
|
+
function pushUniqueUrl(list, max, url, name, label) {
|
|
32
|
+
if (list.some((x) => x.url === url))
|
|
33
|
+
return;
|
|
34
|
+
if (list.length >= max) {
|
|
35
|
+
throw new CliHttpError(`Too many ${label} attachments (max ${max}).`, EXIT_USAGE, 400, "bad_request");
|
|
36
|
+
}
|
|
37
|
+
list.push(name ? { url, name } : { url });
|
|
38
|
+
}
|
|
39
|
+
function pushText(list, name, content) {
|
|
40
|
+
if (list.length >= CLI_ATTACH_LIMITS.maxTextFiles) {
|
|
41
|
+
throw new CliHttpError(`Too many text attachments (max ${CLI_ATTACH_LIMITS.maxTextFiles}).`, EXIT_USAGE, 400, "bad_request");
|
|
42
|
+
}
|
|
43
|
+
list.push({ name, content });
|
|
44
|
+
}
|
|
45
|
+
export function addHttpsRef(out, url, opts) {
|
|
46
|
+
if (!isHttpsUrl(url)) {
|
|
47
|
+
throw new CliHttpError(`Not an https URL: ${url}`, EXIT_USAGE, 400, "bad_request");
|
|
48
|
+
}
|
|
49
|
+
const kind = opts?.asImageOnly ? "image" : classifyRemoteByName(opts?.name ?? url);
|
|
50
|
+
if (kind === "document") {
|
|
51
|
+
pushUniqueUrl(out.documents, CLI_ATTACH_LIMITS.maxDocuments, url, opts?.name, "document");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
pushUniqueUrl(out.images, CLI_ATTACH_LIMITS.maxImages, url, opts?.name, "image");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function addUploadResult(out, data) {
|
|
58
|
+
const name = data.filename?.trim() || undefined;
|
|
59
|
+
if (data.kind === "text") {
|
|
60
|
+
if (typeof data.content !== "string" || !data.content) {
|
|
61
|
+
throw new CliHttpError("Text upload returned no content.", EXIT_UNEXPECTED, 500, "internal");
|
|
62
|
+
}
|
|
63
|
+
pushText(out.textFiles, name ?? "snippet.txt", data.content);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (data.kind === "image" || data.kind === "document") {
|
|
67
|
+
if (!data.url || !isHttpsUrl(data.url)) {
|
|
68
|
+
throw new CliHttpError(`Upload of ${name ?? "file"} did not return a Blob URL.`, EXIT_UNEXPECTED, 500, "internal");
|
|
69
|
+
}
|
|
70
|
+
if (data.kind === "document") {
|
|
71
|
+
pushUniqueUrl(out.documents, CLI_ATTACH_LIMITS.maxDocuments, data.url, name, "document");
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
pushUniqueUrl(out.images, CLI_ATTACH_LIMITS.maxImages, data.url, name, "image");
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
throw new CliHttpError(`Unsupported upload kind "${data.kind}" for prompt attach. Use images, PDFs, or text/code files (video/large files need the editor path until Phase E).`, EXIT_USAGE, 400, "bad_request");
|
|
79
|
+
}
|
|
80
|
+
export async function uploadLocalForAttach(slug, filePath) {
|
|
81
|
+
try {
|
|
82
|
+
await access(filePath, fsConstants.R_OK);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
throw new CliHttpError(`Cannot read file for --attach: ${filePath}`, EXIT_USAGE, 400, "bad_request");
|
|
86
|
+
}
|
|
87
|
+
const bytes = await readFile(filePath);
|
|
88
|
+
if (bytes.length === 0) {
|
|
89
|
+
throw new CliHttpError(`File is empty: ${filePath}`, EXIT_USAGE, 400, "bad_request");
|
|
90
|
+
}
|
|
91
|
+
const form = new FormData();
|
|
92
|
+
form.append("file", new Blob([Uint8Array.from(bytes)]), basename(filePath));
|
|
93
|
+
return v1PostMultipart(`/api/v1/sites/${encodeURIComponent(slug)}/media`, form);
|
|
94
|
+
}
|
|
95
|
+
export async function resolveSitePathUrl(slug, sitePath) {
|
|
96
|
+
const data = await v1Get(`/api/v1/sites/${encodeURIComponent(slug)}/media`);
|
|
97
|
+
const norm = (p) => p.replace(/\/+/g, "/").replace(/\/$/, "") || p;
|
|
98
|
+
const want = norm(sitePath);
|
|
99
|
+
const hit = data.assets.find((a) => a.sitePath === sitePath || norm(a.sitePath) === want);
|
|
100
|
+
if (!hit?.url) {
|
|
101
|
+
throw new CliHttpError(`No media asset found for ${sitePath}. Upload with frontpage media upload, or pass a Blob https URL.`, EXIT_USAGE, 404, "not_found");
|
|
102
|
+
}
|
|
103
|
+
return { url: hit.url, name: hit.name, type: hit.type };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolve one --attach value (local path, /__media/…, or https URL) into the payload.
|
|
107
|
+
*/
|
|
108
|
+
export async function resolveAttachRef(slug, ref, out) {
|
|
109
|
+
const raw = ref.trim();
|
|
110
|
+
if (!raw) {
|
|
111
|
+
throw new CliHttpError("--attach requires a path or URL", EXIT_USAGE, 400, "bad_request");
|
|
112
|
+
}
|
|
113
|
+
if (/^https?:\/\//i.test(raw)) {
|
|
114
|
+
if (!isHttpsUrl(raw)) {
|
|
115
|
+
throw new CliHttpError(`--attach URLs must be https:// (got ${raw.split(":")[0]}://)`, EXIT_USAGE, 400, "bad_request");
|
|
116
|
+
}
|
|
117
|
+
addHttpsRef(out, raw);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (isMediaSitePath(raw)) {
|
|
121
|
+
const asset = await resolveSitePathUrl(slug, raw);
|
|
122
|
+
const kind = classifyRemoteByName(asset.name || asset.type || raw);
|
|
123
|
+
if (kind === "document") {
|
|
124
|
+
pushUniqueUrl(out.documents, CLI_ATTACH_LIMITS.maxDocuments, asset.url, asset.name, "document");
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
pushUniqueUrl(out.images, CLI_ATTACH_LIMITS.maxImages, asset.url, asset.name, "image");
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const uploaded = await uploadLocalForAttach(slug, raw);
|
|
132
|
+
addUploadResult(out, uploaded);
|
|
133
|
+
}
|
|
134
|
+
export async function buildPromptAttachments(args) {
|
|
135
|
+
if (args.attachRefs.length === 0 && args.assetUrls.length === 0)
|
|
136
|
+
return undefined;
|
|
137
|
+
const out = emptyAttachments();
|
|
138
|
+
for (const url of args.assetUrls) {
|
|
139
|
+
addHttpsRef(out, url.trim(), { asImageOnly: true });
|
|
140
|
+
}
|
|
141
|
+
for (const ref of args.attachRefs) {
|
|
142
|
+
await resolveAttachRef(args.slug, ref, out);
|
|
143
|
+
}
|
|
144
|
+
if (out.images.length === 0 &&
|
|
145
|
+
out.documents.length === 0 &&
|
|
146
|
+
out.textFiles.length === 0) {
|
|
147
|
+
throw new CliHttpError("No attachments could be resolved from --attach / --asset-url.", EXIT_USAGE, 400, "bad_request");
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
/** Parse prompt argv: prompt words + --path / --attach / --asset-url flags. */
|
|
152
|
+
export function parsePromptArgs(rest) {
|
|
153
|
+
let currentPath = "/";
|
|
154
|
+
const promptParts = [];
|
|
155
|
+
const attachRefs = [];
|
|
156
|
+
const assetUrls = [];
|
|
157
|
+
for (let i = 0; i < rest.length; i++) {
|
|
158
|
+
const a = rest[i];
|
|
159
|
+
if (a === "--path") {
|
|
160
|
+
currentPath = rest[++i] ?? "/";
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (a.startsWith("--path=")) {
|
|
164
|
+
currentPath = a.slice("--path=".length) || "/";
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (a === "--attach") {
|
|
168
|
+
const v = rest[++i];
|
|
169
|
+
if (!v || v.startsWith("--")) {
|
|
170
|
+
throw new CliHttpError('Usage: frontpage prompt "…" --attach <file|/__media/…|https://…>', EXIT_USAGE, 400, "bad_request");
|
|
171
|
+
}
|
|
172
|
+
attachRefs.push(v);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (a.startsWith("--attach=")) {
|
|
176
|
+
const v = a.slice("--attach=".length);
|
|
177
|
+
if (!v) {
|
|
178
|
+
throw new CliHttpError("--attach= requires a value", EXIT_USAGE, 400, "bad_request");
|
|
179
|
+
}
|
|
180
|
+
attachRefs.push(v);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (a === "--asset-url") {
|
|
184
|
+
const v = rest[++i];
|
|
185
|
+
if (!v || v.startsWith("--")) {
|
|
186
|
+
throw new CliHttpError("Usage: frontpage prompt \"…\" --asset-url <https://…>", EXIT_USAGE, 400, "bad_request");
|
|
187
|
+
}
|
|
188
|
+
assetUrls.push(v);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (a.startsWith("--asset-url=")) {
|
|
192
|
+
const v = a.slice("--asset-url=".length);
|
|
193
|
+
if (!v) {
|
|
194
|
+
throw new CliHttpError("--asset-url= requires a value", EXIT_USAGE, 400, "bad_request");
|
|
195
|
+
}
|
|
196
|
+
assetUrls.push(v);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
promptParts.push(a);
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
prompt: promptParts.join(" ").trim(),
|
|
203
|
+
currentPath,
|
|
204
|
+
attachRefs,
|
|
205
|
+
assetUrls,
|
|
206
|
+
};
|
|
207
|
+
}
|
package/dist/commands/prompt.js
CHANGED
|
@@ -1,38 +1,48 @@
|
|
|
1
1
|
import { resolveSlug } from "../site.js";
|
|
2
2
|
import { EXIT_OK, EXIT_USAGE, v1Post } from "../http/client.js";
|
|
3
3
|
import { emitErr, emitOk, emitUsage, isJson } from "../runtime.js";
|
|
4
|
+
import { buildPromptAttachments, parsePromptArgs } from "./prompt-attach.js";
|
|
4
5
|
export async function runPrompt(args) {
|
|
5
6
|
try {
|
|
6
7
|
const { slug, rest } = await resolveSlug(args);
|
|
7
|
-
|
|
8
|
-
const promptParts = [];
|
|
9
|
-
for (let i = 0; i < rest.length; i++) {
|
|
10
|
-
const a = rest[i];
|
|
11
|
-
if (a === "--path") {
|
|
12
|
-
currentPath = rest[++i] ?? "/";
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
if (a.startsWith("--path=")) {
|
|
16
|
-
currentPath = a.slice("--path=".length) || "/";
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
promptParts.push(a);
|
|
20
|
-
}
|
|
21
|
-
const prompt = promptParts.join(" ").trim();
|
|
8
|
+
const { prompt, currentPath, attachRefs, assetUrls } = parsePromptArgs(rest);
|
|
22
9
|
if (!prompt) {
|
|
23
|
-
return emitUsage("prompt", 'Usage: frontpage prompt "..." [--site <slug>] [--path /page]', EXIT_USAGE);
|
|
10
|
+
return emitUsage("prompt", 'Usage: frontpage prompt "..." [--site <slug>] [--path /page] [--attach <file|/__media/…|https://…>]… [--asset-url <https://…>]…', EXIT_USAGE);
|
|
24
11
|
}
|
|
25
12
|
if (!currentPath.startsWith("/")) {
|
|
26
13
|
return emitUsage("prompt", "currentPath must start with /", EXIT_USAGE);
|
|
27
14
|
}
|
|
28
|
-
const
|
|
15
|
+
const attachments = await buildPromptAttachments({ slug, attachRefs, assetUrls });
|
|
16
|
+
const body = { prompt, currentPath };
|
|
17
|
+
if (attachments) {
|
|
18
|
+
body.attachments = attachments;
|
|
19
|
+
// Legacy image list for run display / back-compat readers.
|
|
20
|
+
body.assetUrls = attachments.images.map((i) => i.url);
|
|
21
|
+
}
|
|
22
|
+
const data = await v1Post(`/api/v1/sites/${encodeURIComponent(slug)}/agent-runs`, body);
|
|
29
23
|
if (isJson()) {
|
|
30
|
-
emitOk("prompt", {
|
|
24
|
+
emitOk("prompt", {
|
|
25
|
+
slug,
|
|
26
|
+
...data,
|
|
27
|
+
attached: attachments
|
|
28
|
+
? {
|
|
29
|
+
images: attachments.images.length,
|
|
30
|
+
documents: attachments.documents.length,
|
|
31
|
+
textFiles: attachments.textFiles.length,
|
|
32
|
+
}
|
|
33
|
+
: undefined,
|
|
34
|
+
});
|
|
31
35
|
}
|
|
32
36
|
else {
|
|
33
37
|
console.log(data.runId);
|
|
34
38
|
if (data.coalesced)
|
|
35
39
|
console.error("(coalesced with an identical queued run)");
|
|
40
|
+
if (attachments) {
|
|
41
|
+
const n = attachments.images.length +
|
|
42
|
+
attachments.documents.length +
|
|
43
|
+
attachments.textFiles.length;
|
|
44
|
+
console.error(`(attached ${n} file${n === 1 ? "" : "s"})`);
|
|
45
|
+
}
|
|
36
46
|
}
|
|
37
47
|
return EXIT_OK;
|
|
38
48
|
}
|
|
@@ -26,6 +26,7 @@ _frontpage_completions() {
|
|
|
26
26
|
analytics) COMPREPLY=( $(compgen -W "dashboard summary query funnel timeseries compare sql" -- "\${cur}") ) ;;
|
|
27
27
|
modules) COMPREPLY=( $(compgen -W "list docs request-setup enable disable archive config" -- "\${cur}") ) ;;
|
|
28
28
|
extensions) COMPREPLY=( $(compgen -W "list docs enable disable config" -- "\${cur}") ) ;;
|
|
29
|
+
prompt) COMPREPLY=( $(compgen -W "--path --attach --asset-url --site --json --help" -- "\${cur}") ) ;;
|
|
29
30
|
*) COMPREPLY=( $(compgen -W "--site --help --json --quiet --yes" -- "\${cur}") ) ;;
|
|
30
31
|
esac
|
|
31
32
|
}
|
package/dist/registry.js
CHANGED
|
@@ -184,11 +184,23 @@ export const COMMANDS = [
|
|
|
184
184
|
{
|
|
185
185
|
name: "prompt",
|
|
186
186
|
summary: "Enqueue an agent build",
|
|
187
|
-
usage: 'frontpage prompt "..." [--site <slug>] [--path /] [--json]',
|
|
187
|
+
usage: 'frontpage prompt "..." [--site <slug>] [--path /] [--attach <file|/__media/…|https://…>]… [--asset-url <https://…>]… [--json]',
|
|
188
188
|
topics: ["agent"],
|
|
189
|
-
examples: [
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
examples: [
|
|
190
|
+
'frontpage prompt "make the hero shorter"',
|
|
191
|
+
'frontpage prompt "Use this logo in the header" --attach ./logo.png',
|
|
192
|
+
'frontpage prompt "Match this PDF brand guide" --attach ./guide.pdf --path /',
|
|
193
|
+
'frontpage prompt "Reuse uploaded art" --attach /__media/uploads/acme/uuid.png',
|
|
194
|
+
'frontpage prompt "Use this image" --asset-url "https://….blob.vercel-storage.com/uploads/…"',
|
|
195
|
+
],
|
|
196
|
+
flags: [
|
|
197
|
+
"--path must start with /",
|
|
198
|
+
"--attach: local file (uploads via media), /__media/… site path, or https Blob URL; repeatable",
|
|
199
|
+
"--asset-url: https image URL (legacy); repeatable",
|
|
200
|
+
"Local attach uses the same caps as frontpage media upload (images/PDF/text; no CLI video yet)",
|
|
201
|
+
"stdout is bare runId (or JSON with --json)",
|
|
202
|
+
],
|
|
203
|
+
seeAlso: ["runs", "deploy", "media"],
|
|
192
204
|
},
|
|
193
205
|
{
|
|
194
206
|
name: "runs",
|
|
@@ -467,6 +479,8 @@ export const TOPICS = {
|
|
|
467
479
|
See also: frontpage help sync | content | AGENTS.md`,
|
|
468
480
|
agent: `Agent + publish
|
|
469
481
|
frontpage prompt "make the hero shorter"
|
|
482
|
+
frontpage prompt "Use this logo" --attach ./logo.png
|
|
483
|
+
frontpage prompt "…" --attach /__media/uploads/<slug>/… --asset-url https://…
|
|
470
484
|
frontpage runs # latest + active
|
|
471
485
|
frontpage runs wait [runId]
|
|
472
486
|
frontpage runs cancel <runId>
|
|
@@ -482,6 +496,7 @@ export const TOPICS = {
|
|
|
482
496
|
frontpage modules request-setup <key> --reason "…"
|
|
483
497
|
frontpage extensions
|
|
484
498
|
|
|
499
|
+
--attach uploads local files via media (images/PDF/text) or accepts /__media/… / https URLs.
|
|
485
500
|
Mid-task continuation is automatic — there is no runs continue.
|
|
486
501
|
Do not push while an agent run is active.
|
|
487
502
|
Default after work: review https://<subdomain>.frontpage.host; do not publish.`,
|
package/llms.txt
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
- Search flags: --regex --case-sensitive --whole-word --files-only --context-lines --max-results
|
|
11
11
|
- Modules setup: frontpage modules request-setup <key> --reason "…" (owner status bar; CLI cannot show it)
|
|
12
12
|
- Analytics ops: frontpage analytics summary|query|funnel|timeseries|compare|sql (bare analytics = dashboard)
|
|
13
|
+
- Prompt attachments: frontpage prompt "…" --attach <file|/__media/…|https://…> [--asset-url https://…] (media upload for locals; images/PDF/text)
|
|
13
14
|
- Auth: mint fp_live_ key in Frontpage UI (Developers); frontpage login KEY or FRONTPAGE_API_KEY
|
|
14
15
|
- Site id: subdomain only (frontpage use <subdomain>). Not custom domains.
|
|
15
16
|
- Staging: https://<subdomain>.frontpage.host — push/prompt/edit update staging; no separate staging deploy step for agents
|