agentful 0.4.16 → 0.4.18
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 +5 -4
- package/dist/index.cjs +214 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,10 +44,11 @@ Inside the interface, `/` lists the Agentful commands (`/setup`, `/push`,
|
|
|
44
44
|
site, and asks you before it changes a file or pushes.
|
|
45
45
|
|
|
46
46
|
`push` respects your `.gitignore` and always excludes `.git`, `node_modules`,
|
|
47
|
-
build output and every `.env` file. A push carries at most
|
|
48
|
-
|
|
49
|
-
an `index.html` at its top level.
|
|
50
|
-
|
|
47
|
+
build output and every `.env` file. A push carries at most 25 MiB of files
|
|
48
|
+
(32 MiB compressed) and 2,000 files, and a static site or a prebuilt upload needs
|
|
49
|
+
an `index.html` at its top level. The preview answers an HTML page over about
|
|
50
|
+
5.5 MiB with an error; `push` names such pages and uploads them all the same.
|
|
51
|
+
`push` checks all of this before it signs you in or creates a project.
|
|
51
52
|
|
|
52
53
|
`agentful push --dry-run` shows that check on its own: what would be uploaded
|
|
53
54
|
and what is left out, the largest files, identical copies, media embedded in
|
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.18" ? "0.4.18" : null.version;
|
|
3048
3048
|
brand = {
|
|
3049
3049
|
name: "agentful",
|
|
3050
3050
|
// the command users type
|
|
@@ -5780,17 +5780,28 @@ var contract_default = {
|
|
|
5780
5780
|
|
|
5781
5781
|
// src/lib/uploadLimits.ts
|
|
5782
5782
|
var MAX_ZIP_UPLOAD_BYTES = contract_default.limits.max_zip_upload_bytes;
|
|
5783
|
+
var MAX_DIRECT_ZIP_UPLOAD_BYTES = 32 * 1024 * 1024;
|
|
5783
5784
|
var MAX_UNPACKED_BYTES = 25 * 1024 * 1024;
|
|
5784
5785
|
var MAX_UPLOAD_FILES = 2e3;
|
|
5785
5786
|
var MAX_INLINE_REQUEST_BYTES = 6 * 1024 * 1024 - 64 * 1024;
|
|
5786
5787
|
function uploadRequestBody(zipBase64, mode, declaration) {
|
|
5787
5788
|
return { zip_base64: zipBase64, mode, clean: true, ...declaration ? { backend_declaration: declaration } : {} };
|
|
5788
5789
|
}
|
|
5789
|
-
function
|
|
5790
|
+
function maxInlineZipBytes(mode = "source", declaration = null) {
|
|
5790
5791
|
const around = Buffer.byteLength(JSON.stringify(uploadRequestBody("", mode, declaration)), "utf8");
|
|
5791
5792
|
const base64Budget = Math.max(0, MAX_INLINE_REQUEST_BYTES - around);
|
|
5792
5793
|
return Math.min(MAX_ZIP_UPLOAD_BYTES, Math.floor(base64Budget / 4) * 3);
|
|
5793
5794
|
}
|
|
5795
|
+
function maxZipBytesForUpload() {
|
|
5796
|
+
return MAX_DIRECT_ZIP_UPLOAD_BYTES;
|
|
5797
|
+
}
|
|
5798
|
+
function announceRequestBody(zipBytes, sha256Hex, mode) {
|
|
5799
|
+
return { upload: { size: zipBytes, sha256: sha256Hex }, mode };
|
|
5800
|
+
}
|
|
5801
|
+
function completeRequestBody(uploadId, mode, declaration) {
|
|
5802
|
+
return { upload_id: uploadId, mode, clean: true, ...declaration ? { backend_declaration: declaration } : {} };
|
|
5803
|
+
}
|
|
5804
|
+
var PREVIEW_MAX_PAGE_BYTES = 5.5 * 1024 * 1024;
|
|
5794
5805
|
var SERVER_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
5795
5806
|
"node_modules",
|
|
5796
5807
|
".git",
|
|
@@ -5954,7 +5965,7 @@ function scanTree(rootDir, mode) {
|
|
|
5954
5965
|
function collectFiles(rootDir, mode) {
|
|
5955
5966
|
return scanTree(rootDir, mode).files;
|
|
5956
5967
|
}
|
|
5957
|
-
function
|
|
5968
|
+
function zipFiles(files, maxBytes = maxZipBytesForUpload()) {
|
|
5958
5969
|
if (files.length === 0) {
|
|
5959
5970
|
throw new ApiError(0, "empty_project", "Nothing to push \u2014 no files found after applying ignore rules.");
|
|
5960
5971
|
}
|
|
@@ -5970,11 +5981,7 @@ function zipToBase64(files, maxBytes = maxZipBytesForUpload()) {
|
|
|
5970
5981
|
`The upload is ${zipped.byteLength.toLocaleString("en-US")} bytes compressed \u2014 a push carries at most ${maxBytes.toLocaleString("en-US")}. \`agentful push --dry-run\` shows what takes the space.`
|
|
5971
5982
|
);
|
|
5972
5983
|
}
|
|
5973
|
-
return {
|
|
5974
|
-
base64: Buffer.from(zipped).toString("base64"),
|
|
5975
|
-
bytes: zipped.byteLength,
|
|
5976
|
-
count: files.length
|
|
5977
|
-
};
|
|
5984
|
+
return { data: zipped, bytes: zipped.byteLength, count: files.length };
|
|
5978
5985
|
}
|
|
5979
5986
|
function detectDistDir(rootDir, override) {
|
|
5980
5987
|
if (override) {
|
|
@@ -6500,6 +6507,9 @@ var CLASS_BY_EXTENSION = new Map(
|
|
|
6500
6507
|
);
|
|
6501
6508
|
var EMBEDDED_SCAN_CLASSES = /* @__PURE__ */ new Set(["html", "css", "script", "data"]);
|
|
6502
6509
|
var baseName = (rel) => rel.slice(rel.lastIndexOf("/") + 1);
|
|
6510
|
+
function previewTooLarge(rel, bytes2) {
|
|
6511
|
+
return /\.html?$/i.test(baseName(rel)) && bytes2 > PREVIEW_MAX_PAGE_BYTES;
|
|
6512
|
+
}
|
|
6503
6513
|
function classOf(rel) {
|
|
6504
6514
|
const base = baseName(rel);
|
|
6505
6515
|
const dot = base.lastIndexOf(".");
|
|
@@ -6612,7 +6622,7 @@ function failedAnalysis(mode, projectDir, blocker) {
|
|
|
6612
6622
|
ok: false,
|
|
6613
6623
|
framework: null,
|
|
6614
6624
|
project: projectLink(projectDir),
|
|
6615
|
-
limits: limits(maxZipBytesForUpload(
|
|
6625
|
+
limits: limits(maxZipBytesForUpload()),
|
|
6616
6626
|
totals: {
|
|
6617
6627
|
files: 0,
|
|
6618
6628
|
bytes: 0,
|
|
@@ -6728,10 +6738,14 @@ function analyzePush(options) {
|
|
|
6728
6738
|
}
|
|
6729
6739
|
duplicates.sort((a, b) => b.redundant_zip_bytes - a.redundant_zip_bytes || (a.paths[0] < b.paths[0] ? -1 : 1));
|
|
6730
6740
|
const redundantZipBytes = duplicates.reduce((total, group) => total + group.redundant_zip_bytes, 0);
|
|
6731
|
-
const maxZipBytes = maxZipBytesForUpload(
|
|
6732
|
-
const
|
|
6741
|
+
const maxZipBytes = maxZipBytesForUpload();
|
|
6742
|
+
const tooBigAsFile = (file) => view.stored.has(file.path) && file.bytes > MAX_UNPACKED_BYTES;
|
|
6743
|
+
const tooBigInZip = (file) => file.zipLower > maxZipBytes;
|
|
6744
|
+
const oversized = measured.filter((file) => tooBigAsFile(file) || tooBigInZip(file)).sort((a, b) => b.bytes - a.bytes || byPath(a, b));
|
|
6733
6745
|
const oversizedPaths = new Set(oversized.map((file) => file.path));
|
|
6746
|
+
const oversizedAsFile = oversized.filter(tooBigAsFile);
|
|
6734
6747
|
const restZipLower = zipLower - oversized.reduce((total, file) => total + file.zipLower, 0);
|
|
6748
|
+
const restStoredBytes = storedBytes - oversizedAsFile.reduce((total, file) => total + file.bytes, 0);
|
|
6735
6749
|
const preflight = mode === "source" ? runPreflight(rootDir) : null;
|
|
6736
6750
|
const framework = preflight?.framework ?? null;
|
|
6737
6751
|
const hasManifest = storedNames.has("package.json");
|
|
@@ -6747,7 +6761,7 @@ function analyzePush(options) {
|
|
|
6747
6761
|
title: file.title,
|
|
6748
6762
|
sha256: file.sha256,
|
|
6749
6763
|
same_content_as: first && first !== file.path ? first : null,
|
|
6750
|
-
...file.
|
|
6764
|
+
...oversizedPaths.has(file.path) ? { larger_than_a_push: true } : {}
|
|
6751
6765
|
};
|
|
6752
6766
|
});
|
|
6753
6767
|
const fittingCandidates = candidates.filter((candidate) => !candidate.larger_than_a_push);
|
|
@@ -6778,28 +6792,39 @@ function analyzePush(options) {
|
|
|
6778
6792
|
message: `${plural(measured.length, "file")} \u2014 the platform accepts at most ${MAX_UPLOAD_FILES.toLocaleString("en-US")} files per push.`
|
|
6779
6793
|
});
|
|
6780
6794
|
}
|
|
6781
|
-
|
|
6795
|
+
const largerOnTheirOwn = (files, sizeOf, limit) => {
|
|
6796
|
+
const named = samplePaths(files.map((file) => `${file.path} (${sizeOverLimit(sizeOf(file), limit)[0]})`));
|
|
6797
|
+
return files.length === 1 ? ` ${named} is larger than that on its own, so no push can carry it.` : ` ${plural(files.length, "file")} are each larger than that on their own, so no push can carry them: ${named}.`;
|
|
6798
|
+
};
|
|
6799
|
+
const filesTooLarge = storedBytes > MAX_UNPACKED_BYTES;
|
|
6800
|
+
if (filesTooLarge) {
|
|
6782
6801
|
const [size, allowed] = sizeOverLimit(storedBytes, MAX_UNPACKED_BYTES);
|
|
6802
|
+
let message = `The files add up to ${size} \u2014 the platform accepts at most ${allowed} of files per push.`;
|
|
6803
|
+
if (oversizedAsFile.length > 0) {
|
|
6804
|
+
message += largerOnTheirOwn(oversizedAsFile, (file) => file.bytes, MAX_UNPACKED_BYTES);
|
|
6805
|
+
message += ` Without ${oversizedAsFile.length === 1 ? "it" : "them"} the files ${restStoredBytes > MAX_UNPACKED_BYTES ? `still add up to ${sizeOverLimit(restStoredBytes, MAX_UNPACKED_BYTES)[0]}` : `add up to ${formatBytes(restStoredBytes)}, which is within that limit`}.`;
|
|
6806
|
+
}
|
|
6783
6807
|
blockers.push({
|
|
6784
6808
|
code: "unpacked_too_large",
|
|
6785
6809
|
overridable: false,
|
|
6786
|
-
message
|
|
6810
|
+
message,
|
|
6811
|
+
...oversizedAsFile.length > 0 ? { paths: oversizedAsFile.map((file) => file.path).slice(0, FINDING_PATHS) } : {}
|
|
6787
6812
|
});
|
|
6788
6813
|
}
|
|
6789
6814
|
if (zipLower > maxZipBytes) {
|
|
6790
6815
|
const [size, allowed] = sizeOverLimit(zipExact ? zipBytes : zipLower, maxZipBytes);
|
|
6791
6816
|
let message = `The upload is ${zipExact ? "" : "at least "}${size} compressed \u2014 a push carries at most ${allowed} compressed.`;
|
|
6817
|
+
const namedHere = oversized.filter((file) => tooBigInZip(file) && !(filesTooLarge && tooBigAsFile(file)));
|
|
6818
|
+
if (namedHere.length > 0) message += largerOnTheirOwn(namedHere, (file) => file.zipLower, maxZipBytes);
|
|
6792
6819
|
if (oversized.length > 0) {
|
|
6793
|
-
const
|
|
6794
|
-
|
|
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`}.`;
|
|
6820
|
+
const without = namedHere.length < oversized.length ? "the files no push can carry" : namedHere.length === 1 ? "it" : "them";
|
|
6821
|
+
message += ` Without ${without} the upload is ${restZipLower > maxZipBytes ? `still ${sizeOverLimit(restZipLower, maxZipBytes)[0]} compressed` : `${formatBytes(restZipLower)} compressed, which is within that limit`}.`;
|
|
6797
6822
|
}
|
|
6798
6823
|
blockers.push({
|
|
6799
6824
|
code: "zip_too_large",
|
|
6800
6825
|
overridable: false,
|
|
6801
6826
|
message,
|
|
6802
|
-
...
|
|
6827
|
+
...namedHere.length > 0 ? { paths: namedHere.map((file) => file.path).slice(0, FINDING_PATHS) } : {}
|
|
6803
6828
|
});
|
|
6804
6829
|
} else if (!zipExact && zipBytes > maxZipBytes) {
|
|
6805
6830
|
warnings.push({
|
|
@@ -6845,6 +6870,15 @@ function analyzePush(options) {
|
|
|
6845
6870
|
});
|
|
6846
6871
|
}
|
|
6847
6872
|
}
|
|
6873
|
+
const tooLargeForPreview = stored.filter((file) => mode === "dist" || framework === "vanilla" || /^(public|static)\//.test(view.stored.get(file.path) ?? "")).filter((file) => previewTooLarge(file.path, file.bytes)).sort((a, b) => b.bytes - a.bytes || byPath(a, b));
|
|
6874
|
+
if (tooLargeForPreview.length > 0) {
|
|
6875
|
+
const one = tooLargeForPreview.length === 1;
|
|
6876
|
+
warnings.push({
|
|
6877
|
+
code: "preview_file_too_large",
|
|
6878
|
+
message: `${plural(tooLargeForPreview.length, "page is", "pages are")} larger than the preview delivers (about ${formatBytes(PREVIEW_MAX_PAGE_BYTES, "down")} for an HTML page): ${samplePaths(tooLargeForPreview.map((file) => `${file.path} (${formatBytes(file.bytes)})`))}. ${one ? "It is" : "They are"} uploaded, but the preview answers with an error instead of the page.`,
|
|
6879
|
+
paths: tooLargeForPreview.map((file) => file.path).slice(0, FINDING_PATHS)
|
|
6880
|
+
});
|
|
6881
|
+
}
|
|
6848
6882
|
const filesOmitted = Math.max(0, measured.length - budgets.fileListCap);
|
|
6849
6883
|
if (filesOmitted > 0) {
|
|
6850
6884
|
warnings.push({
|
|
@@ -6888,14 +6922,16 @@ function analyzePush(options) {
|
|
|
6888
6922
|
].filter(Boolean);
|
|
6889
6923
|
const toFit = ` To fit, at least ${mustStayOut.join(" and ")} ${mustStayOut.length > 1 ? "have" : "has"} to stay out.`;
|
|
6890
6924
|
const spareCopies = duplicates.filter((group) => !oversizedPaths.has(group.paths[0]));
|
|
6891
|
-
const
|
|
6892
|
-
const
|
|
6893
|
-
|
|
6925
|
+
const extraStored = (group) => group.paths.slice(1).filter((path) => view.stored.has(path)).length * group.bytes;
|
|
6926
|
+
const spare = spareCopies.reduce((total, group) => total + (filesTooLarge ? extraStored(group) : group.redundant_zip_bytes), 0);
|
|
6927
|
+
const base = filesTooLarge ? oversized.length > 0 ? restStoredBytes : storedBytes : oversized.length > 0 ? restZipLower : zipBytes;
|
|
6928
|
+
const limit = filesTooLarge ? MAX_UNPACKED_BYTES : maxZipBytes;
|
|
6929
|
+
if (spareCopies.length > 0 && spare >= base / 20) {
|
|
6894
6930
|
const copies = spareCopies.reduce((total, group) => total + group.paths.length - 1, 0);
|
|
6895
|
-
const remaining = base -
|
|
6931
|
+
const remaining = base - spare;
|
|
6896
6932
|
suggestions.push({
|
|
6897
6933
|
code: "remove_identical_copies",
|
|
6898
|
-
message: `${plural(copies, "file is an identical copy", "files are identical copies")} of another file and ${copies === 1 ? "adds" : "add"} ${formatBytes(
|
|
6934
|
+
message: `${plural(copies, "file is an identical copy", "files are identical copies")} of another file and ${copies === 1 ? "adds" : "add"} ${formatBytes(spare)} to the ${filesTooLarge ? "files of the upload" : "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 > limit ? ` \u2014 still above the ${formatBytes(limit, "down")} limit` : ""}.`,
|
|
6899
6935
|
paths: spareCopies.flatMap((group) => group.paths.slice(1)).slice(0, FINDING_PATHS)
|
|
6900
6936
|
});
|
|
6901
6937
|
}
|
|
@@ -7058,6 +7094,128 @@ function wouldUpload(analysis, force = false) {
|
|
|
7058
7094
|
return analysis.blockers.every((blocker) => blocker.overridable && force);
|
|
7059
7095
|
}
|
|
7060
7096
|
|
|
7097
|
+
// src/lib/zipTransfer.ts
|
|
7098
|
+
var import_node_crypto2 = require("crypto");
|
|
7099
|
+
var INLINE_UPLOAD_TIMEOUT_MS = 12e4;
|
|
7100
|
+
var ANNOUNCE_TIMEOUT_MS = 3e4;
|
|
7101
|
+
var PUT_TIMEOUT_MS = 6e5;
|
|
7102
|
+
var COMPLETE_TIMEOUT_MS = 12e4;
|
|
7103
|
+
var PUT_ATTEMPTS = 2;
|
|
7104
|
+
async function uploadZip(upload, deps = { request, fetch }) {
|
|
7105
|
+
const { route, token, zip, mode, declaration, retry } = upload;
|
|
7106
|
+
if (zip.byteLength <= maxInlineZipBytes(mode, declaration)) {
|
|
7107
|
+
return deps.request(route, {
|
|
7108
|
+
token,
|
|
7109
|
+
retry,
|
|
7110
|
+
timeoutMs: INLINE_UPLOAD_TIMEOUT_MS,
|
|
7111
|
+
body: uploadRequestBody(Buffer.from(zip).toString("base64"), mode, declaration)
|
|
7112
|
+
});
|
|
7113
|
+
}
|
|
7114
|
+
const ticket = await announce(upload, deps);
|
|
7115
|
+
await putZip(ticket, zip, deps);
|
|
7116
|
+
return deps.request(route, {
|
|
7117
|
+
token,
|
|
7118
|
+
retry,
|
|
7119
|
+
timeoutMs: COMPLETE_TIMEOUT_MS,
|
|
7120
|
+
body: completeRequestBody(ticket.upload_id, mode, declaration)
|
|
7121
|
+
});
|
|
7122
|
+
}
|
|
7123
|
+
async function announce({ route, token, zip, mode, declaration, retry }, deps) {
|
|
7124
|
+
let answer;
|
|
7125
|
+
try {
|
|
7126
|
+
answer = await deps.request(route, {
|
|
7127
|
+
token,
|
|
7128
|
+
retry,
|
|
7129
|
+
timeoutMs: ANNOUNCE_TIMEOUT_MS,
|
|
7130
|
+
body: announceRequestBody(zip.byteLength, (0, import_node_crypto2.createHash)("sha256").update(zip).digest("hex"), mode)
|
|
7131
|
+
});
|
|
7132
|
+
} catch (err2) {
|
|
7133
|
+
if (err2 instanceof ApiError && (err2.code === "direct_upload_unavailable" || err2.code === "missing_zip")) {
|
|
7134
|
+
const [size, allowed] = sizeOverLimit(zip.byteLength, maxInlineZipBytes(mode, declaration));
|
|
7135
|
+
throw new ApiError(
|
|
7136
|
+
err2.status,
|
|
7137
|
+
"direct_upload_unavailable",
|
|
7138
|
+
`The upload is ${size} compressed, and the platform does not take uploads of that size at the moment \u2014 only up to ${allowed}. Nothing was uploaded. Try again later.`
|
|
7139
|
+
);
|
|
7140
|
+
}
|
|
7141
|
+
throw err2;
|
|
7142
|
+
}
|
|
7143
|
+
const headers = answer?.required_headers;
|
|
7144
|
+
const understood = typeof answer?.upload_id === "string" && isHttpUrl(answer?.upload_url) && headers !== null && typeof headers === "object" && !Array.isArray(headers) && Object.values(headers).every((value) => typeof value === "string");
|
|
7145
|
+
if (!understood) {
|
|
7146
|
+
throw new ApiError(
|
|
7147
|
+
0,
|
|
7148
|
+
"invalid_upload_answer",
|
|
7149
|
+
"The platform answered the upload request in a form this version of the CLI does not understand. Nothing was uploaded."
|
|
7150
|
+
);
|
|
7151
|
+
}
|
|
7152
|
+
return { upload_id: answer.upload_id, upload_url: answer.upload_url, required_headers: headers };
|
|
7153
|
+
}
|
|
7154
|
+
function isHttpUrl(value) {
|
|
7155
|
+
if (typeof value !== "string") return false;
|
|
7156
|
+
try {
|
|
7157
|
+
return ["https:", "http:"].includes(new URL(value).protocol);
|
|
7158
|
+
} catch {
|
|
7159
|
+
return false;
|
|
7160
|
+
}
|
|
7161
|
+
}
|
|
7162
|
+
async function putZip(ticket, zip, deps) {
|
|
7163
|
+
const deadline = Date.now() + PUT_TIMEOUT_MS;
|
|
7164
|
+
for (let attempt = 1; ; attempt++) {
|
|
7165
|
+
let failure;
|
|
7166
|
+
let again = attempt < PUT_ATTEMPTS;
|
|
7167
|
+
try {
|
|
7168
|
+
const resp = await deps.fetch(ticket.upload_url, {
|
|
7169
|
+
method: "PUT",
|
|
7170
|
+
headers: ticket.required_headers,
|
|
7171
|
+
body: zip,
|
|
7172
|
+
signal: AbortSignal.timeout(Math.max(1, deadline - Date.now()))
|
|
7173
|
+
});
|
|
7174
|
+
if (resp.ok) return;
|
|
7175
|
+
const code = /<Code>([\w.]{1,80})<\/Code>/.exec(await resp.text())?.[1];
|
|
7176
|
+
failure = `the storage answered HTTP ${resp.status}${code ? ` (${code})` : ""}`;
|
|
7177
|
+
again &&= resp.status >= 500;
|
|
7178
|
+
} catch (err2) {
|
|
7179
|
+
const timedOut = err2?.name === "TimeoutError";
|
|
7180
|
+
const code = typeof err2?.cause?.code === "string" && /^\w{1,40}$/.test(err2.cause.code) ? ` (${err2.cause.code})` : "";
|
|
7181
|
+
failure = timedOut ? `it did not finish within ${PUT_TIMEOUT_MS / 6e4} minutes` : `the connection to the storage broke${code}`;
|
|
7182
|
+
again &&= !timedOut;
|
|
7183
|
+
}
|
|
7184
|
+
if (!again) {
|
|
7185
|
+
throw new ApiError(
|
|
7186
|
+
0,
|
|
7187
|
+
"upload_failed",
|
|
7188
|
+
`The upload did not go through: ${failure}. Nothing of the project was changed \u2014 run the push again.`
|
|
7189
|
+
);
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
|
|
7194
|
+
// src/lib/publishedCopy.ts
|
|
7195
|
+
function renderPublishedCopyLine(state) {
|
|
7196
|
+
switch (state) {
|
|
7197
|
+
case "updated":
|
|
7198
|
+
return { kind: "ok", text: "Your published site shows this push too." };
|
|
7199
|
+
case "incomplete":
|
|
7200
|
+
return {
|
|
7201
|
+
kind: "warn",
|
|
7202
|
+
text: "Your published site was only partly updated: copying this many files to it ran out of time, so it may show the previous push or a mix of both. The preview shows this push."
|
|
7203
|
+
};
|
|
7204
|
+
case "failed":
|
|
7205
|
+
return {
|
|
7206
|
+
kind: "warn",
|
|
7207
|
+
text: "Your published site was only partly updated: writing to it failed, so it may show the previous push or a mix of both. Push again to retry."
|
|
7208
|
+
};
|
|
7209
|
+
case "check_failed":
|
|
7210
|
+
return {
|
|
7211
|
+
kind: "warn",
|
|
7212
|
+
text: "Could not check whether this project has a published site; if it has one, it still shows the previous push. Push again to retry."
|
|
7213
|
+
};
|
|
7214
|
+
default:
|
|
7215
|
+
return null;
|
|
7216
|
+
}
|
|
7217
|
+
}
|
|
7218
|
+
|
|
7061
7219
|
// src/lib/pushReport.ts
|
|
7062
7220
|
init_branding();
|
|
7063
7221
|
var EXCLUDE_REASON = {
|
|
@@ -7599,7 +7757,7 @@ async function pushCommand(opts) {
|
|
|
7599
7757
|
ui.step(mode === "source" ? "Packing source tree\u2026" : `Packing built output (${rootDir})\u2026`);
|
|
7600
7758
|
const declaration = mode === "source" ? readBackendDeclaration() : null;
|
|
7601
7759
|
const files = collectFiles(rootDir, mode);
|
|
7602
|
-
const zip =
|
|
7760
|
+
const zip = zipFiles(files, maxZipBytesForUpload());
|
|
7603
7761
|
ui.info(`${zip.count} files, ${(zip.bytes / 1024).toFixed(0)} KiB compressed`);
|
|
7604
7762
|
if (declaration) {
|
|
7605
7763
|
const consent = await ensureDatabaseConsent({
|
|
@@ -7617,15 +7775,14 @@ async function pushCommand(opts) {
|
|
|
7617
7775
|
}
|
|
7618
7776
|
}
|
|
7619
7777
|
ui.step("Uploading to your workspace\u2026");
|
|
7620
|
-
const upload = await
|
|
7621
|
-
`${API_URL}/api/workspaces/${userId}/${projectId}/source-zip`,
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
);
|
|
7778
|
+
const upload = await uploadZip({
|
|
7779
|
+
route: `${API_URL}/api/workspaces/${userId}/${projectId}/source-zip`,
|
|
7780
|
+
token: auth.pb_token,
|
|
7781
|
+
zip: zip.data,
|
|
7782
|
+
mode,
|
|
7783
|
+
declaration,
|
|
7784
|
+
retry: platformRetry
|
|
7785
|
+
});
|
|
7629
7786
|
ui.info(`${upload.files_written} files written`);
|
|
7630
7787
|
if (mode === "source") {
|
|
7631
7788
|
ui.step("Building in the cloud (this can take a few minutes)\u2026");
|
|
@@ -7660,6 +7817,9 @@ async function pushCommand(opts) {
|
|
|
7660
7817
|
console.log("");
|
|
7661
7818
|
ui.ok(`Live preview: ${ui.url(url)}`);
|
|
7662
7819
|
ui.info(`Manage in the browser: ${ui.url(`https://app.agentful.dev/workspace/${userId}/${projectId}`)}`);
|
|
7820
|
+
const publishedCopy = renderPublishedCopyLine(upload.published_copy);
|
|
7821
|
+
if (publishedCopy?.kind === "ok") ui.ok(publishedCopy.text);
|
|
7822
|
+
else if (publishedCopy) ui.warn(publishedCopy.text);
|
|
7663
7823
|
if (declaration) {
|
|
7664
7824
|
const served = upload.auth?.applied && upload.auth.status !== "failed" && (upload.auth.requested?.length ?? 0) > 0 ? await fetchServedAuthMethods(projectId) : void 0;
|
|
7665
7825
|
for (const line of renderBackendSyncLines(upload, declaration, served)) {
|
|
@@ -7840,7 +8000,7 @@ init_branding();
|
|
|
7840
8000
|
|
|
7841
8001
|
// src/lib/engine.ts
|
|
7842
8002
|
var import_node_fs11 = require("fs");
|
|
7843
|
-
var
|
|
8003
|
+
var import_node_crypto3 = require("crypto");
|
|
7844
8004
|
var import_node_path13 = require("path");
|
|
7845
8005
|
var import_node_os4 = require("os");
|
|
7846
8006
|
var import_node_child_process3 = require("child_process");
|
|
@@ -7897,7 +8057,7 @@ async function fetchManifest() {
|
|
|
7897
8057
|
}
|
|
7898
8058
|
}
|
|
7899
8059
|
async function sha256File(path) {
|
|
7900
|
-
const hash = (0,
|
|
8060
|
+
const hash = (0, import_node_crypto3.createHash)("sha256");
|
|
7901
8061
|
await (0, import_promises3.pipeline)((0, import_node_fs11.createReadStream)(path), hash);
|
|
7902
8062
|
return hash.digest("hex");
|
|
7903
8063
|
}
|
|
@@ -8214,7 +8374,7 @@ site can never be uploaded in several parts.
|
|
|
8214
8374
|
source), and Managed Server Actions and \`.agentful/backend.json\` are NOT
|
|
8215
8375
|
deployed by it.
|
|
8216
8376
|
|
|
8217
|
-
Limits of one push, either way:
|
|
8377
|
+
Limits of one push, either way: ${formatBytes(MAX_UNPACKED_BYTES)} of files (their sizes added up), ${formatBytes(maxZipBytesForUpload(), "down")} compressed, ${MAX_UPLOAD_FILES.toLocaleString("en-US")} files.
|
|
8218
8378
|
\`--force\` lifts none of them: it only lets the cloud build try despite a
|
|
8219
8379
|
compatibility finding.
|
|
8220
8380
|
|
|
@@ -8225,8 +8385,9 @@ written. Sizes, limits, what is left out and what blocks the upload come from
|
|
|
8225
8385
|
that report and from nowhere else: quote its figures, never estimate or
|
|
8226
8386
|
recompute them. Next to every byte count the report prints the size as text
|
|
8227
8387
|
(\`size\` for \`bytes\`, \`zip_size\` for \`zip_bytes\`): use that text, do not
|
|
8228
|
-
convert bytes yourself. \`limits.
|
|
8229
|
-
|
|
8388
|
+
convert bytes yourself. \`limits.max_unpacked_bytes\` is the limit a site
|
|
8389
|
+
runs into: the sizes of its files added up. Run it before the first push of a
|
|
8390
|
+
session, after a push was stopped,
|
|
8230
8391
|
and after you changed what gets uploaded. In between, \`agentful push\` applies the same
|
|
8231
8392
|
check itself and stops before sign-in when something blocks. A blocker with
|
|
8232
8393
|
\`"overridable": false\` is final, the platform rejects that upload: do not
|
|
@@ -8249,11 +8410,11 @@ answer, such as whether a \`.gitignore\` is in effect
|
|
|
8249
8410
|
up, its relative links would break. If that folder is the whole site, push
|
|
8250
8411
|
the folder (\`suggestions\` carries the command); otherwise adjust the links
|
|
8251
8412
|
and show the diff. Without candidates, ask which folder holds the site.
|
|
8252
|
-
- **Too large or too many files** (\`
|
|
8253
|
-
\`too_many_files\`): settle first whether the site can fit at all.
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8413
|
+
- **Too large or too many files** (\`unpacked_too_large\`, \`zip_too_large\`,
|
|
8414
|
+
\`too_many_files\`): settle first whether the site can fit at all. A size
|
|
8415
|
+
blocker names the files that are each larger than a whole push on their own
|
|
8416
|
+
(all of them in its \`paths\`), and its message says what is left without
|
|
8417
|
+
them. Such a file can only stay out. If
|
|
8257
8418
|
the site needs one of them (the page the user wants as the start page, or a
|
|
8258
8419
|
file a page loads: search the sources for references), the site cannot be
|
|
8259
8420
|
pushed yet: say that plainly, with the file and its size. Do not offer
|
|
@@ -8270,6 +8431,11 @@ answer, such as whether a \`.gitignore\` is in effect
|
|
|
8270
8431
|
than a source push only where a build produces the output folder; a site
|
|
8271
8432
|
without a build step is the same upload either way. Never shrink,
|
|
8272
8433
|
re-encode, move or delete the user's files on your own.
|
|
8434
|
+
- **Pages the preview does not show** (\`preview_file_too_large\` in
|
|
8435
|
+
\`warnings\`): the push goes through, but the preview answers with an
|
|
8436
|
+
error instead of these HTML pages. Say which pages and why, with the
|
|
8437
|
+
wording of the warning, and ask whether to push anyway. Never shrink,
|
|
8438
|
+
convert or split them on your own.
|
|
8273
8439
|
- **Compatibility findings** (\`"overridable": true\`): fix the cause in the
|
|
8274
8440
|
code. \`--force\` only when the user asks to try the cloud build anyway.
|
|
8275
8441
|
- **Files that would be missing on the site:** the warnings
|
|
@@ -8463,7 +8629,7 @@ ${backendSessionLines(backend)}
|
|
|
8463
8629
|
}
|
|
8464
8630
|
|
|
8465
8631
|
// src/branding/commands.ts
|
|
8466
|
-
var PUSH_TOOL_TIMEOUT_MS =
|
|
8632
|
+
var PUSH_TOOL_TIMEOUT_MS = 156e4;
|
|
8467
8633
|
var NEEDS_A_PUSH = 'If the command reports "No Agentful project here", nothing has been deployed from this directory yet \u2014 run `agentful push` first (it creates the project and a preview), then retry.';
|
|
8468
8634
|
var SLASH_COMMANDS = {
|
|
8469
8635
|
setup: {
|
|
@@ -8482,7 +8648,7 @@ var SLASH_COMMANDS = {
|
|
|
8482
8648
|
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.
|
|
8483
8649
|
|
|
8484
8650
|
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.
|
|
8651
|
+
2. If the report or the project calls for a decision (a blocker, a file the site needs that would not be uploaded or that the preview would not show, 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.
|
|
8486
8652
|
3. After a change I approved, run the analysis again.
|
|
8487
8653
|
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.
|
|
8488
8654
|
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.
|
|
@@ -9315,7 +9481,7 @@ function describeModelRoute(model, catalog, localProviders, connectedProviders)
|
|
|
9315
9481
|
}
|
|
9316
9482
|
|
|
9317
9483
|
// src/lib/engineEvents.ts
|
|
9318
|
-
var
|
|
9484
|
+
var import_node_crypto4 = require("crypto");
|
|
9319
9485
|
var import_node_net = require("net");
|
|
9320
9486
|
function payloadOf(event) {
|
|
9321
9487
|
return event.payload && typeof event.payload === "object" ? event.payload : event;
|
|
@@ -9441,7 +9607,7 @@ var EngineLivenessPolicy = class {
|
|
|
9441
9607
|
function engineServerCredentials(env = process.env) {
|
|
9442
9608
|
return {
|
|
9443
9609
|
username: env.OPENCODE_SERVER_USERNAME || void 0,
|
|
9444
|
-
password: env.OPENCODE_SERVER_PASSWORD || (0,
|
|
9610
|
+
password: env.OPENCODE_SERVER_PASSWORD || (0, import_node_crypto4.randomUUID)()
|
|
9445
9611
|
};
|
|
9446
9612
|
}
|
|
9447
9613
|
async function reserveLoopbackPort() {
|