agentful 0.4.15 → 0.4.16
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 +7 -4
- package/dist/index.cjs +94 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,11 +51,14 @@ in or creates a project.
|
|
|
51
51
|
|
|
52
52
|
`agentful push --dry-run` shows that check on its own: what would be uploaded
|
|
53
53
|
and what is left out, the largest files, identical copies, media embedded in
|
|
54
|
-
HTML, and every reason the upload would be rejected.
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
HTML, and every reason the upload would be rejected. A file that is larger
|
|
55
|
+
than a whole push on its own is named as such: it cannot be uploaded at all,
|
|
56
|
+
whatever else is left out. It uploads nothing, creates no project and changes
|
|
57
|
+
no file; it exits with 1 when the push would be blocked. `--dry-run --json`
|
|
58
|
+
prints the same analysis as one JSON document
|
|
57
59
|
(`"schema": "agentful.push-analysis"`) for scripts and agents; with `--summary`
|
|
58
|
-
it leaves out the list of every file
|
|
60
|
+
it leaves out the list of every file, keeps totals, limits and findings, and
|
|
61
|
+
prints every size as text next to its byte count.
|
|
59
62
|
|
|
60
63
|
## What you can deploy
|
|
61
64
|
|
package/dist/index.cjs
CHANGED
|
@@ -3044,7 +3044,7 @@ var VERSION, brand, useColor, wrap, paint, tuiDesign, sym, ui;
|
|
|
3044
3044
|
var init_branding = __esm({
|
|
3045
3045
|
"src/branding.ts"() {
|
|
3046
3046
|
"use strict";
|
|
3047
|
-
VERSION = "0.4.
|
|
3047
|
+
VERSION = "0.4.16" ? "0.4.16" : null.version;
|
|
3048
3048
|
brand = {
|
|
3049
3049
|
name: "agentful",
|
|
3050
3050
|
// the command users type
|
|
@@ -6561,7 +6561,7 @@ function formatBytes(bytes2, rounding = "nearest") {
|
|
|
6561
6561
|
}
|
|
6562
6562
|
if (unit === 0 || Number.isInteger(value)) return `${value} ${units[unit]}`;
|
|
6563
6563
|
const tenths = value * 10;
|
|
6564
|
-
const rounded = (rounding === "down" ? Math.floor(tenths) : Math.round(tenths)) / 10;
|
|
6564
|
+
const rounded = (rounding === "down" ? Math.floor(tenths) : rounding === "up" ? Math.ceil(tenths) : Math.round(tenths)) / 10;
|
|
6565
6565
|
return `${rounded.toFixed(1)} ${units[unit]}`;
|
|
6566
6566
|
}
|
|
6567
6567
|
function sizeOverLimit(bytes2, limit) {
|
|
@@ -6728,6 +6728,10 @@ function analyzePush(options) {
|
|
|
6728
6728
|
}
|
|
6729
6729
|
duplicates.sort((a, b) => b.redundant_zip_bytes - a.redundant_zip_bytes || (a.paths[0] < b.paths[0] ? -1 : 1));
|
|
6730
6730
|
const redundantZipBytes = duplicates.reduce((total, group) => total + group.redundant_zip_bytes, 0);
|
|
6731
|
+
const maxZipBytes = maxZipBytesForUpload(mode, mode === "source" ? readBackendDeclaration(rootDir) : null);
|
|
6732
|
+
const oversized = measured.filter((file) => file.zipLower > maxZipBytes).sort((a, b) => b.zipLower - a.zipLower || byPath(a, b));
|
|
6733
|
+
const oversizedPaths = new Set(oversized.map((file) => file.path));
|
|
6734
|
+
const restZipLower = zipLower - oversized.reduce((total, file) => total + file.zipLower, 0);
|
|
6731
6735
|
const preflight = mode === "source" ? runPreflight(rootDir) : null;
|
|
6732
6736
|
const framework = preflight?.framework ?? null;
|
|
6733
6737
|
const hasManifest = storedNames.has("package.json");
|
|
@@ -6739,12 +6743,14 @@ function analyzePush(options) {
|
|
|
6739
6743
|
return {
|
|
6740
6744
|
path: file.path,
|
|
6741
6745
|
bytes: file.bytes,
|
|
6746
|
+
zip_bytes: file.zip_bytes,
|
|
6742
6747
|
title: file.title,
|
|
6743
6748
|
sha256: file.sha256,
|
|
6744
|
-
same_content_as: first && first !== file.path ? first : null
|
|
6749
|
+
same_content_as: first && first !== file.path ? first : null,
|
|
6750
|
+
...file.zipLower > maxZipBytes ? { larger_than_a_push: true } : {}
|
|
6745
6751
|
};
|
|
6746
6752
|
});
|
|
6747
|
-
const
|
|
6753
|
+
const fittingCandidates = candidates.filter((candidate) => !candidate.larger_than_a_push);
|
|
6748
6754
|
const zipLimit = formatBytes(maxZipBytes, "down");
|
|
6749
6755
|
const blockers = [];
|
|
6750
6756
|
const warnings = [];
|
|
@@ -6782,10 +6788,18 @@ function analyzePush(options) {
|
|
|
6782
6788
|
}
|
|
6783
6789
|
if (zipLower > maxZipBytes) {
|
|
6784
6790
|
const [size, allowed] = sizeOverLimit(zipExact ? zipBytes : zipLower, maxZipBytes);
|
|
6791
|
+
let message = `The upload is ${zipExact ? "" : "at least "}${size} compressed \u2014 a push carries at most ${allowed} compressed.`;
|
|
6792
|
+
if (oversized.length > 0) {
|
|
6793
|
+
const one = oversized.length === 1;
|
|
6794
|
+
const named = samplePaths(oversized.map((file) => `${file.path} (${sizeOverLimit(file.zipLower, maxZipBytes)[0]})`));
|
|
6795
|
+
message += one ? ` ${named} is larger than that on its own, so no push can carry it.` : ` ${plural(oversized.length, "file")} are each larger than that on their own, so no push can carry them: ${named}.`;
|
|
6796
|
+
message += ` Without ${one ? "it" : "them"} the upload is ${restZipLower > maxZipBytes ? `still ${sizeOverLimit(restZipLower, maxZipBytes)[0]} compressed` : `${formatBytes(restZipLower)} compressed, which is within that limit`}.`;
|
|
6797
|
+
}
|
|
6785
6798
|
blockers.push({
|
|
6786
6799
|
code: "zip_too_large",
|
|
6787
6800
|
overridable: false,
|
|
6788
|
-
message
|
|
6801
|
+
message,
|
|
6802
|
+
...oversized.length > 0 ? { paths: oversized.map((file) => file.path).slice(0, FINDING_PATHS) } : {}
|
|
6789
6803
|
});
|
|
6790
6804
|
} else if (!zipExact && zipBytes > maxZipBytes) {
|
|
6791
6805
|
warnings.push({
|
|
@@ -6794,10 +6808,12 @@ function analyzePush(options) {
|
|
|
6794
6808
|
});
|
|
6795
6809
|
}
|
|
6796
6810
|
if (entryMissing) {
|
|
6811
|
+
const tooBig = candidates.filter((candidate) => candidate.larger_than_a_push);
|
|
6812
|
+
const cannotBecomeIt = tooBig.length === 0 ? "" : fittingCandidates.length === 0 ? candidates.length === 1 ? " The one HTML file here cannot become it as it is: it is larger than a whole push." : ` None of the ${candidates.length} HTML files here can become it as they are: each is larger than a whole push.` : ` ${tooBig.length} of the ${candidates.length} HTML files here cannot become it as ${tooBig.length === 1 ? "it is" : "they are"}, larger than a whole push: ${samplePaths(tooBig.map((candidate) => candidate.path))}.`;
|
|
6797
6813
|
blockers.push({
|
|
6798
6814
|
code: "entry_missing",
|
|
6799
6815
|
overridable: false,
|
|
6800
|
-
message: mode === "dist" ? `No index.html at the top level of ${dirLabel === "." ? "the uploaded folder" : `${dirLabel}/`}. A prebuilt upload is served as it is, and index.html is its start page.` : "No index.html at the top level. Without a package.json this is a static site, and the platform serves index.html as its start page."
|
|
6816
|
+
message: (mode === "dist" ? `No index.html at the top level of ${dirLabel === "." ? "the uploaded folder" : `${dirLabel}/`}. A prebuilt upload is served as it is, and index.html is its start page.` : "No index.html at the top level. Without a package.json this is a static site, and the platform serves index.html as its start page.") + cannotBecomeIt
|
|
6801
6817
|
});
|
|
6802
6818
|
}
|
|
6803
6819
|
const backend = mode === "source" ? checkBackendDeclaration(rootDir) : null;
|
|
@@ -6838,11 +6854,11 @@ function analyzePush(options) {
|
|
|
6838
6854
|
}
|
|
6839
6855
|
const tooLarge = blockers.some((b) => b.code === "zip_too_large" || b.code === "unpacked_too_large");
|
|
6840
6856
|
if (entryMissing) {
|
|
6841
|
-
if (
|
|
6857
|
+
if (fittingCandidates.length > 0) {
|
|
6842
6858
|
suggestions.push({
|
|
6843
6859
|
code: "name_start_page",
|
|
6844
6860
|
message: "Rename or copy the page that should open first to index.html at the top level, check that its relative links still resolve, then push again.",
|
|
6845
|
-
paths:
|
|
6861
|
+
paths: fittingCandidates.map((candidate) => candidate.path).slice(0, FINDING_PATHS)
|
|
6846
6862
|
});
|
|
6847
6863
|
}
|
|
6848
6864
|
const siteDirs = /* @__PURE__ */ new Set();
|
|
@@ -6864,29 +6880,31 @@ function analyzePush(options) {
|
|
|
6864
6880
|
}
|
|
6865
6881
|
}
|
|
6866
6882
|
if (tooLarge) {
|
|
6867
|
-
const
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6883
|
+
const overZip = zipLower - maxZipBytes;
|
|
6884
|
+
const overFiles = storedBytes - MAX_UNPACKED_BYTES;
|
|
6885
|
+
const mustStayOut = [
|
|
6886
|
+
overZip > 0 ? `${formatBytes(overZip, "up")} of the ${formatBytes(zipLower)} compressed` : "",
|
|
6887
|
+
overFiles > 0 ? `${formatBytes(overFiles, "up")} of the ${formatBytes(storedBytes)} of files` : ""
|
|
6888
|
+
].filter(Boolean);
|
|
6889
|
+
const toFit = ` To fit, at least ${mustStayOut.join(" and ")} ${mustStayOut.length > 1 ? "have" : "has"} to stay out.`;
|
|
6890
|
+
const spareCopies = duplicates.filter((group) => !oversizedPaths.has(group.paths[0]));
|
|
6891
|
+
const spareZipBytes = spareCopies.reduce((total, group) => total + group.redundant_zip_bytes, 0);
|
|
6892
|
+
const base = oversized.length > 0 ? restZipLower : zipBytes;
|
|
6893
|
+
if (spareCopies.length > 0 && spareZipBytes >= base / 20) {
|
|
6894
|
+
const copies = spareCopies.reduce((total, group) => total + group.paths.length - 1, 0);
|
|
6895
|
+
const remaining = base - spareZipBytes;
|
|
6878
6896
|
suggestions.push({
|
|
6879
6897
|
code: "remove_identical_copies",
|
|
6880
|
-
message: `${plural(copies, "file is an identical copy", "files are identical copies")} of another file and ${copies === 1 ? "adds" : "add"} ${formatBytes(
|
|
6881
|
-
paths:
|
|
6898
|
+
message: `${plural(copies, "file is an identical copy", "files are identical copies")} of another file and ${copies === 1 ? "adds" : "add"} ${formatBytes(spareZipBytes)} to the compressed upload. If nothing links to the extra names, one copy each brings ${oversized.length > 0 ? "what is left without the files no push can carry" : "it"} to ${formatBytes(remaining)}${remaining > maxZipBytes ? ` \u2014 still above the ${zipLimit} limit` : ""}.`,
|
|
6899
|
+
paths: spareCopies.flatMap((group) => group.paths.slice(1)).slice(0, FINDING_PATHS)
|
|
6882
6900
|
});
|
|
6883
6901
|
}
|
|
6884
6902
|
suggestions.push(mode === "source" ? {
|
|
6885
6903
|
code: "exclude_with_gitignore",
|
|
6886
|
-
message:
|
|
6904
|
+
message: `Files and folders listed in .gitignore are not uploaded. Leave out what the site does not serve \u2014 originals, working files, exports.${toFit}`
|
|
6887
6905
|
} : {
|
|
6888
6906
|
code: "narrow_prebuilt_directory",
|
|
6889
|
-
message:
|
|
6907
|
+
message: `A prebuilt upload takes every file in the folder. Put the finished site into a folder of its own and pass it with --dir.${toFit}`
|
|
6890
6908
|
});
|
|
6891
6909
|
suggestions.push({
|
|
6892
6910
|
code: "upload_limit",
|
|
@@ -6961,7 +6979,19 @@ var SUMMARY_LISTS = {
|
|
|
6961
6979
|
finding_paths: 8
|
|
6962
6980
|
};
|
|
6963
6981
|
var SUMMARY_MAX_BYTES = 24 * 1024;
|
|
6964
|
-
var
|
|
6982
|
+
var sizeLabelKey = (key) => key === "base64_chars" ? "base64_size" : /(^|_)bytes(_|$)/.test(key) ? key.replace("bytes", "size") : null;
|
|
6983
|
+
function withSizeLabels(value) {
|
|
6984
|
+
if (Array.isArray(value)) return value.map(withSizeLabels);
|
|
6985
|
+
if (!value || typeof value !== "object") return value;
|
|
6986
|
+
const labelled = {};
|
|
6987
|
+
for (const [key, inner] of Object.entries(value)) {
|
|
6988
|
+
labelled[key] = withSizeLabels(inner);
|
|
6989
|
+
const label = typeof inner === "number" ? sizeLabelKey(key) : null;
|
|
6990
|
+
if (label) labelled[label] = formatBytes(inner, key.startsWith("max_") ? "down" : "nearest");
|
|
6991
|
+
}
|
|
6992
|
+
return labelled;
|
|
6993
|
+
}
|
|
6994
|
+
var printAnalysis = (analysis) => `${JSON.stringify(analysis.view === "summary" ? withSizeLabels(analysis) : analysis, null, 2)}
|
|
6965
6995
|
`;
|
|
6966
6996
|
function summarize(analysis, divisor) {
|
|
6967
6997
|
const shortened = {};
|
|
@@ -7088,7 +7118,7 @@ function renderAnalysis(analysis, options = {}) {
|
|
|
7088
7118
|
lines.push(" HTML files found:");
|
|
7089
7119
|
for (const candidate of analysis.entry.candidates.slice(0, SHOWN_FILES)) {
|
|
7090
7120
|
lines.push(
|
|
7091
|
-
` ${sizeColumn(candidate.bytes)} ${candidate.path}` + (candidate.title ? paint.dim(` "${candidate.title}"`) : "") + (candidate.same_content_as ? paint.dim(` (same content as ${candidate.same_content_as})`) : "")
|
|
7121
|
+
` ${sizeColumn(candidate.bytes)} ${candidate.path}` + (candidate.title ? paint.dim(` "${candidate.title}"`) : "") + (candidate.same_content_as ? paint.dim(` (same content as ${candidate.same_content_as})`) : "") + (candidate.larger_than_a_push ? paint.dim(" (larger than a whole push)") : "")
|
|
7092
7122
|
);
|
|
7093
7123
|
}
|
|
7094
7124
|
lines.push(...more(analysis.entry.candidates.length, SHOWN_FILES, "HTML file"));
|
|
@@ -8193,36 +8223,53 @@ compatibility finding.
|
|
|
8193
8223
|
reports what a push would upload: no sign-in, no project, no upload, nothing
|
|
8194
8224
|
written. Sizes, limits, what is left out and what blocks the upload come from
|
|
8195
8225
|
that report and from nowhere else: quote its figures, never estimate or
|
|
8196
|
-
recompute them
|
|
8197
|
-
|
|
8198
|
-
|
|
8226
|
+
recompute them. Next to every byte count the report prints the size as text
|
|
8227
|
+
(\`size\` for \`bytes\`, \`zip_size\` for \`zip_bytes\`): use that text, do not
|
|
8228
|
+
convert bytes yourself. \`limits.max_zip_bytes\` is the exact limit for this
|
|
8229
|
+
push. Run it before the first push of a session, after a push was stopped,
|
|
8230
|
+
and after you changed what gets uploaded. In between, \`agentful push\` applies the same
|
|
8199
8231
|
check itself and stops before sign-in when something blocks. A blocker with
|
|
8200
8232
|
\`"overridable": false\` is final, the platform rejects that upload: do not
|
|
8201
8233
|
retry, do not add \`--force\`, do not look for another route.
|
|
8202
8234
|
|
|
8203
8235
|
Every change to the user's files for a push (including \`.gitignore\`) and every
|
|
8204
8236
|
project script you run for it needs the user's OK first: name the concrete
|
|
8205
|
-
change, ask, wait for the answer.
|
|
8237
|
+
change, ask, wait for the answer. One question per reply: one decision, not
|
|
8238
|
+
several joined together. Never ask what the report or the project files
|
|
8239
|
+
answer, such as whether a \`.gitignore\` is in effect
|
|
8240
|
+
(\`excluded.gitignore_applied\`), which files are large or which are copies.
|
|
8206
8241
|
|
|
8207
8242
|
- **No start page** (\`entry_missing\`): the platform serves \`index.html\` from
|
|
8208
8243
|
the top level. With \`entry.candidates\`: show them (path and title;
|
|
8209
|
-
\`same_content_as\` marks copies
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8244
|
+
\`same_content_as\` marks copies, \`larger_than_a_push\` marks pages no push
|
|
8245
|
+
can carry: they cannot be the start page as they are, say so) and ask which
|
|
8246
|
+
of the others opens first. A candidate at the top level can be renamed to
|
|
8247
|
+
\`index.html\`, or copied, which keeps the old address and adds the file's
|
|
8248
|
+
size to the upload: ask which. A candidate in a subfolder must not be copied
|
|
8249
|
+
up, its relative links would break. If that folder is the whole site, push
|
|
8250
|
+
the folder (\`suggestions\` carries the command); otherwise adjust the links
|
|
8251
|
+
and show the diff. Without candidates, ask which folder holds the site.
|
|
8216
8252
|
- **Too large or too many files** (\`zip_too_large\`, \`unpacked_too_large\`,
|
|
8217
|
-
\`too_many_files\`):
|
|
8218
|
-
\`
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8253
|
+
\`too_many_files\`): settle first whether the site can fit at all. The
|
|
8254
|
+
\`zip_too_large\` blocker names the files that are each larger than a whole
|
|
8255
|
+
push on their own (all of them in its \`paths\`), and its message says what
|
|
8256
|
+
is left without them. Such a file can only stay out. If
|
|
8257
|
+
the site needs one of them (the page the user wants as the start page, or a
|
|
8258
|
+
file a page loads: search the sources for references), the site cannot be
|
|
8259
|
+
pushed yet: say that plainly, with the file and its size. Do not offer
|
|
8260
|
+
\`.gitignore\` or a prebuilt push as a way around it, neither lifts a limit,
|
|
8261
|
+
and larger uploads are not available.
|
|
8262
|
+
Where no such file is needed: explain where the size is, from
|
|
8263
|
+
\`largest_files\`, \`by_top_level\`, \`duplicates\` and \`embedded_media\` (media
|
|
8264
|
+
stored inside text files as \`data:\` URLs), and propose which files the site
|
|
8265
|
+
does not load to leave out with \`.gitignore\`. The \`exclude_with_gitignore\`
|
|
8266
|
+
suggestion says how much has to stay out; if the site needs more than fits,
|
|
8267
|
+
say plainly that it cannot be pushed yet. Before proposing to leave a file
|
|
8268
|
+
out, search the sources for references to it and say what you checked:
|
|
8269
|
+
identical content does not make a copy unused. A prebuilt push is smaller
|
|
8270
|
+
than a source push only where a build produces the output folder; a site
|
|
8271
|
+
without a build step is the same upload either way. Never shrink,
|
|
8272
|
+
re-encode, move or delete the user's files on your own.
|
|
8226
8273
|
- **Compatibility findings** (\`"overridable": true\`): fix the cause in the
|
|
8227
8274
|
code. \`--force\` only when the user asks to try the cloud build anyway.
|
|
8228
8275
|
- **Files that would be missing on the site:** the warnings
|
|
@@ -8434,8 +8481,8 @@ var SLASH_COMMANDS = {
|
|
|
8434
8481
|
description: `${brand.displayName}: check, deploy and get a preview URL`,
|
|
8435
8482
|
template: `Deploy this project, following "Pushing to the platform" in AGENTFUL_CLOUD.md. Run every command from the project root using the bash tool. What I typed after the command (may be empty): "$ARGUMENTS". If it names \`--prebuilt\`, a folder for \`--dir\`, or \`--force\`, pass that on to both commands below.
|
|
8436
8483
|
|
|
8437
|
-
1. Run \`${PUSH_ANALYSIS_COMMAND}\`. It only reads files: no sign-in, no upload. Its JSON report is the only source for sizes, limits and what blocks the upload: quote its figures, never estimate them.
|
|
8438
|
-
2. If the report or the project calls for a decision (a blocker, a file the site needs that would not be uploaded, a build step the cloud does not run), explain the finding in plain words with the figures of the report and ask me how to proceed
|
|
8484
|
+
1. Run \`${PUSH_ANALYSIS_COMMAND}\`. It only reads files: no sign-in, no upload. Its JSON report is the only source for sizes, limits and what blocks the upload: quote its figures as printed (the \`size\` texts next to the byte counts), never estimate or convert them.
|
|
8485
|
+
2. If the report or the project calls for a decision (a blocker, a file the site needs that would not be uploaded, a build step the cloud does not run), explain the finding in plain words with the figures of the report. If the report shows that what I want to publish cannot fit (a page or file it needs is larger than a whole push, or it stays above the limits without the files it does not load), say that plainly and do not offer ways out that do not get it under the limits. Otherwise ask me how to proceed: exactly one question per reply, and never one the report or the project files answer. Then wait for my answer. Until then do not change any file, do not run project scripts and do not push.
|
|
8439
8486
|
3. After a change I approved, run the analysis again.
|
|
8440
8487
|
4. When nothing blocks, run \`agentful push\` with the bash tool's \`timeout\` set to ${PUSH_TOOL_TIMEOUT_MS} (milliseconds): the cloud build takes minutes and the default timeout would cut the push off halfway. The command never waits for input. It creates the project automatically if this directory is not linked yet.
|
|
8441
8488
|
5. Report the preview URL on its own line and what the push printed about the backend. If it fails: quote the printed error verbatim, and if the failure happened in the cloud (not before the upload), also run \`agentful build-status\` and quote its phase/error/details as the cause. Diagnose ONLY from those records \u2014 never invent causes, env vars, or settings they do not show.
|