agentful 0.4.13 → 0.4.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/README.md +16 -1
- package/dist/index.cjs +1405 -450
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1904,10 +1904,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1904
1904
|
args = args.slice();
|
|
1905
1905
|
let launchWithNode = false;
|
|
1906
1906
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1907
|
-
function findFile(baseDir,
|
|
1908
|
-
const localBin = path.resolve(baseDir,
|
|
1907
|
+
function findFile(baseDir, baseName2) {
|
|
1908
|
+
const localBin = path.resolve(baseDir, baseName2);
|
|
1909
1909
|
if (fs.existsSync(localBin)) return localBin;
|
|
1910
|
-
if (sourceExt.includes(path.extname(
|
|
1910
|
+
if (sourceExt.includes(path.extname(baseName2))) return void 0;
|
|
1911
1911
|
const foundExt = sourceExt.find(
|
|
1912
1912
|
(ext) => fs.existsSync(`${localBin}${ext}`)
|
|
1913
1913
|
);
|
|
@@ -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.15" ? "0.4.15" : null.version;
|
|
3048
3048
|
brand = {
|
|
3049
3049
|
name: "agentful",
|
|
3050
3050
|
// the command users type
|
|
@@ -5539,102 +5539,6 @@ function zipSync(data, opts) {
|
|
|
5539
5539
|
|
|
5540
5540
|
// src/lib/zip.ts
|
|
5541
5541
|
var import_ignore = __toESM(require_ignore());
|
|
5542
|
-
var MAX_ZIP_BYTES = 6 * 1024 * 1024;
|
|
5543
|
-
var ALWAYS_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
5544
|
-
".git",
|
|
5545
|
-
"node_modules",
|
|
5546
|
-
".agentful",
|
|
5547
|
-
".opencode",
|
|
5548
|
-
".cache",
|
|
5549
|
-
"__macosx",
|
|
5550
|
-
"dist",
|
|
5551
|
-
"build",
|
|
5552
|
-
"out",
|
|
5553
|
-
".next",
|
|
5554
|
-
".output",
|
|
5555
|
-
".svelte-kit",
|
|
5556
|
-
".astro",
|
|
5557
|
-
".vercel",
|
|
5558
|
-
".netlify"
|
|
5559
|
-
]);
|
|
5560
|
-
var ALWAYS_EXCLUDE_BASENAMES = /* @__PURE__ */ new Set([".ds_store", "thumbs.db"]);
|
|
5561
|
-
var isEnvFile = (name) => name === ".env" || name.startsWith(".env.");
|
|
5562
|
-
function collectFiles(rootDir, mode) {
|
|
5563
|
-
const ig = (0, import_ignore.default)();
|
|
5564
|
-
if (mode === "source") {
|
|
5565
|
-
const gitignorePath = (0, import_node_path6.join)(rootDir, ".gitignore");
|
|
5566
|
-
if ((0, import_node_fs4.existsSync)(gitignorePath)) {
|
|
5567
|
-
ig.add((0, import_node_fs4.readFileSync)(gitignorePath, "utf8"));
|
|
5568
|
-
}
|
|
5569
|
-
}
|
|
5570
|
-
const results = [];
|
|
5571
|
-
const walk = (dir) => {
|
|
5572
|
-
for (const entry of (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true })) {
|
|
5573
|
-
const abs = (0, import_node_path6.join)(dir, entry.name);
|
|
5574
|
-
const rel = (0, import_node_path6.relative)(rootDir, abs).split(import_node_path6.sep).join("/");
|
|
5575
|
-
const lower = entry.name.toLowerCase();
|
|
5576
|
-
if (entry.isSymbolicLink()) continue;
|
|
5577
|
-
if (entry.isDirectory()) {
|
|
5578
|
-
if (mode === "source" && (ALWAYS_EXCLUDE_DIRS.has(lower) || ig.ignores(rel + "/"))) continue;
|
|
5579
|
-
walk(abs);
|
|
5580
|
-
continue;
|
|
5581
|
-
}
|
|
5582
|
-
if (!entry.isFile()) continue;
|
|
5583
|
-
if (ALWAYS_EXCLUDE_BASENAMES.has(lower)) continue;
|
|
5584
|
-
if (mode === "source") {
|
|
5585
|
-
if (isEnvFile(entry.name)) continue;
|
|
5586
|
-
if (ig.ignores(rel)) continue;
|
|
5587
|
-
}
|
|
5588
|
-
results.push({ rel, abs });
|
|
5589
|
-
}
|
|
5590
|
-
};
|
|
5591
|
-
walk(rootDir);
|
|
5592
|
-
return results;
|
|
5593
|
-
}
|
|
5594
|
-
function zipToBase64(files) {
|
|
5595
|
-
if (files.length === 0) {
|
|
5596
|
-
throw new ApiError(0, "empty_project", "Nothing to push \u2014 no files found after applying ignore rules.");
|
|
5597
|
-
}
|
|
5598
|
-
const entries = {};
|
|
5599
|
-
for (const f of files) {
|
|
5600
|
-
entries[f.rel] = (0, import_node_fs4.readFileSync)(f.abs);
|
|
5601
|
-
}
|
|
5602
|
-
const zipped = zipSync(entries, { level: 6 });
|
|
5603
|
-
if (zipped.byteLength > MAX_ZIP_BYTES) {
|
|
5604
|
-
throw new ApiError(
|
|
5605
|
-
0,
|
|
5606
|
-
"zip_too_large",
|
|
5607
|
-
`Project zip is ${(zipped.byteLength / 1024 / 1024).toFixed(1)} MB \u2014 the push cap is ${MAX_ZIP_BYTES / 1024 / 1024} MB. Check for large media/binary files; assets belong in the platform asset flow.`
|
|
5608
|
-
);
|
|
5609
|
-
}
|
|
5610
|
-
return {
|
|
5611
|
-
base64: Buffer.from(zipped).toString("base64"),
|
|
5612
|
-
bytes: zipped.byteLength,
|
|
5613
|
-
count: files.length
|
|
5614
|
-
};
|
|
5615
|
-
}
|
|
5616
|
-
function detectDistDir(rootDir, override) {
|
|
5617
|
-
if (override) {
|
|
5618
|
-
const dir = (0, import_node_path6.join)(rootDir, override);
|
|
5619
|
-
if (!(0, import_node_fs4.existsSync)(dir) || !(0, import_node_fs4.statSync)(dir).isDirectory()) {
|
|
5620
|
-
throw new ApiError(0, "dist_not_found", `--dir ${override} does not exist or is not a directory.`);
|
|
5621
|
-
}
|
|
5622
|
-
return dir;
|
|
5623
|
-
}
|
|
5624
|
-
for (const candidate of ["dist", "build", "out", (0, import_node_path6.join)(".output", "public")]) {
|
|
5625
|
-
const dir = (0, import_node_path6.join)(rootDir, candidate);
|
|
5626
|
-
if ((0, import_node_fs4.existsSync)((0, import_node_path6.join)(dir, "index.html"))) return dir;
|
|
5627
|
-
}
|
|
5628
|
-
throw new ApiError(
|
|
5629
|
-
0,
|
|
5630
|
-
"dist_not_found",
|
|
5631
|
-
"No built output found (looked for dist/, build/, out/, .output/public with an index.html). Run your build first or pass --dir <path>."
|
|
5632
|
-
);
|
|
5633
|
-
}
|
|
5634
|
-
|
|
5635
|
-
// src/lib/preflight.ts
|
|
5636
|
-
var import_node_fs6 = require("fs");
|
|
5637
|
-
var import_node_path8 = require("path");
|
|
5638
5542
|
|
|
5639
5543
|
// ../platform-contract/contract.json
|
|
5640
5544
|
var contract_default = {
|
|
@@ -5874,9 +5778,350 @@ var contract_default = {
|
|
|
5874
5778
|
}
|
|
5875
5779
|
};
|
|
5876
5780
|
|
|
5877
|
-
//
|
|
5781
|
+
// src/lib/uploadLimits.ts
|
|
5782
|
+
var MAX_ZIP_UPLOAD_BYTES = contract_default.limits.max_zip_upload_bytes;
|
|
5783
|
+
var MAX_UNPACKED_BYTES = 25 * 1024 * 1024;
|
|
5784
|
+
var MAX_UPLOAD_FILES = 2e3;
|
|
5785
|
+
var MAX_INLINE_REQUEST_BYTES = 6 * 1024 * 1024 - 64 * 1024;
|
|
5786
|
+
function uploadRequestBody(zipBase64, mode, declaration) {
|
|
5787
|
+
return { zip_base64: zipBase64, mode, clean: true, ...declaration ? { backend_declaration: declaration } : {} };
|
|
5788
|
+
}
|
|
5789
|
+
function maxZipBytesForUpload(mode = "source", declaration = null) {
|
|
5790
|
+
const around = Buffer.byteLength(JSON.stringify(uploadRequestBody("", mode, declaration)), "utf8");
|
|
5791
|
+
const base64Budget = Math.max(0, MAX_INLINE_REQUEST_BYTES - around);
|
|
5792
|
+
return Math.min(MAX_ZIP_UPLOAD_BYTES, Math.floor(base64Budget / 4) * 3);
|
|
5793
|
+
}
|
|
5794
|
+
var SERVER_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
5795
|
+
"node_modules",
|
|
5796
|
+
".git",
|
|
5797
|
+
".opencode",
|
|
5798
|
+
".cache",
|
|
5799
|
+
"__macosx",
|
|
5800
|
+
".svelte-kit",
|
|
5801
|
+
".next",
|
|
5802
|
+
".nuxt",
|
|
5803
|
+
".output",
|
|
5804
|
+
".astro",
|
|
5805
|
+
".vercel",
|
|
5806
|
+
".netlify",
|
|
5807
|
+
".wrangler",
|
|
5808
|
+
".turbo",
|
|
5809
|
+
"dist",
|
|
5810
|
+
"build",
|
|
5811
|
+
"out"
|
|
5812
|
+
]);
|
|
5813
|
+
var SERVER_SKIP_BASENAMES = /* @__PURE__ */ new Set([".ds_store", "thumbs.db"]);
|
|
5814
|
+
var SERVER_SECRET_MARKERS = [
|
|
5815
|
+
".server/secrets/",
|
|
5816
|
+
"/secrets/",
|
|
5817
|
+
"secrets/",
|
|
5818
|
+
".git/",
|
|
5819
|
+
"node_modules/",
|
|
5820
|
+
"opencode-logs/",
|
|
5821
|
+
".opencode/"
|
|
5822
|
+
];
|
|
5823
|
+
var isMacosMetadata = (rel) => {
|
|
5824
|
+
const lowered = rel.toLowerCase();
|
|
5825
|
+
if (lowered === "__macosx" || lowered.startsWith("__macosx/")) return true;
|
|
5826
|
+
return rel.slice(rel.lastIndexOf("/") + 1).startsWith("._");
|
|
5827
|
+
};
|
|
5828
|
+
function secretMatch(rel) {
|
|
5829
|
+
const base = rel.slice(rel.lastIndexOf("/") + 1);
|
|
5830
|
+
if (base === ".env" || base.startsWith(".env.") || base.endsWith(".env")) return { reason: "env_file", matched: base };
|
|
5831
|
+
const lowered = rel.toLowerCase();
|
|
5832
|
+
if (lowered.startsWith("knowledge/")) return { reason: "secret_path", matched: "knowledge/" };
|
|
5833
|
+
const marker = SERVER_SECRET_MARKERS.find((m) => lowered.includes(m));
|
|
5834
|
+
return marker ? { reason: "secret_path", matched: marker } : null;
|
|
5835
|
+
}
|
|
5836
|
+
function serverView(rels) {
|
|
5837
|
+
const topLevels = /* @__PURE__ */ new Set();
|
|
5838
|
+
let nested = false;
|
|
5839
|
+
for (const rel of rels) {
|
|
5840
|
+
if (isMacosMetadata(rel)) continue;
|
|
5841
|
+
const slash = rel.indexOf("/");
|
|
5842
|
+
topLevels.add(slash === -1 ? rel : rel.slice(0, slash));
|
|
5843
|
+
if (slash !== -1) nested = true;
|
|
5844
|
+
}
|
|
5845
|
+
const strippedPrefix = topLevels.size === 1 && nested ? `${[...topLevels][0]}/` : "";
|
|
5846
|
+
const stored = /* @__PURE__ */ new Map();
|
|
5847
|
+
const dropped = [];
|
|
5848
|
+
for (const rel of rels) {
|
|
5849
|
+
if (isMacosMetadata(rel)) {
|
|
5850
|
+
dropped.push({ rel, reason: "macos_metadata", matched: rel.slice(rel.lastIndexOf("/") + 1) });
|
|
5851
|
+
continue;
|
|
5852
|
+
}
|
|
5853
|
+
const inner = strippedPrefix && rel.startsWith(strippedPrefix) ? rel.slice(strippedPrefix.length) : rel;
|
|
5854
|
+
if (!inner) continue;
|
|
5855
|
+
const parts = inner.split("/");
|
|
5856
|
+
const skippedDir = parts.slice(0, -1).find((part) => SERVER_SKIP_DIRS.has(part.toLowerCase()));
|
|
5857
|
+
if (skippedDir) {
|
|
5858
|
+
dropped.push({ rel, reason: "skipped_directory", matched: `${skippedDir}/` });
|
|
5859
|
+
continue;
|
|
5860
|
+
}
|
|
5861
|
+
const base = parts[parts.length - 1];
|
|
5862
|
+
if (SERVER_SKIP_BASENAMES.has(base.toLowerCase())) {
|
|
5863
|
+
dropped.push({ rel, reason: "junk_file", matched: base });
|
|
5864
|
+
continue;
|
|
5865
|
+
}
|
|
5866
|
+
const secret = secretMatch(inner);
|
|
5867
|
+
if (secret) {
|
|
5868
|
+
dropped.push({ rel, ...secret });
|
|
5869
|
+
continue;
|
|
5870
|
+
}
|
|
5871
|
+
stored.set(rel, inner);
|
|
5872
|
+
}
|
|
5873
|
+
return { strippedPrefix, stored, dropped };
|
|
5874
|
+
}
|
|
5875
|
+
|
|
5876
|
+
// src/lib/zip.ts
|
|
5877
|
+
var ZIP_LEVEL = 6;
|
|
5878
|
+
var ALWAYS_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
5879
|
+
".git",
|
|
5880
|
+
"node_modules",
|
|
5881
|
+
".agentful",
|
|
5882
|
+
".opencode",
|
|
5883
|
+
".cache",
|
|
5884
|
+
"__macosx",
|
|
5885
|
+
"dist",
|
|
5886
|
+
"build",
|
|
5887
|
+
"out",
|
|
5888
|
+
".next",
|
|
5889
|
+
".output",
|
|
5890
|
+
".svelte-kit",
|
|
5891
|
+
".astro",
|
|
5892
|
+
".vercel",
|
|
5893
|
+
".netlify"
|
|
5894
|
+
]);
|
|
5895
|
+
var ALWAYS_EXCLUDE_BASENAMES = /* @__PURE__ */ new Set([".ds_store", "thumbs.db"]);
|
|
5896
|
+
var isEnvFile = (name) => name === ".env" || name.startsWith(".env.");
|
|
5897
|
+
function scanTree(rootDir, mode) {
|
|
5898
|
+
const ig = (0, import_ignore.default)();
|
|
5899
|
+
let gitignoreApplied = false;
|
|
5900
|
+
if (mode === "source") {
|
|
5901
|
+
const gitignorePath = (0, import_node_path6.join)(rootDir, ".gitignore");
|
|
5902
|
+
if ((0, import_node_fs4.existsSync)(gitignorePath)) {
|
|
5903
|
+
ig.add((0, import_node_fs4.readFileSync)(gitignorePath, "utf8"));
|
|
5904
|
+
gitignoreApplied = true;
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
const files = [];
|
|
5908
|
+
const excluded = [];
|
|
5909
|
+
const walk = (dir) => {
|
|
5910
|
+
for (const entry of (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true })) {
|
|
5911
|
+
const abs = (0, import_node_path6.join)(dir, entry.name);
|
|
5912
|
+
const rel = (0, import_node_path6.relative)(rootDir, abs).split(import_node_path6.sep).join("/");
|
|
5913
|
+
const lower = entry.name.toLowerCase();
|
|
5914
|
+
if (entry.isSymbolicLink()) {
|
|
5915
|
+
excluded.push({ rel, kind: "symlink", reason: "symlink" });
|
|
5916
|
+
continue;
|
|
5917
|
+
}
|
|
5918
|
+
if (entry.isDirectory()) {
|
|
5919
|
+
if (mode === "source" && ALWAYS_EXCLUDE_DIRS.has(lower)) {
|
|
5920
|
+
excluded.push({ rel, kind: "dir", reason: "always_excluded_dir" });
|
|
5921
|
+
continue;
|
|
5922
|
+
}
|
|
5923
|
+
if (mode === "source" && ig.ignores(rel + "/")) {
|
|
5924
|
+
excluded.push({ rel, kind: "dir", reason: "gitignore" });
|
|
5925
|
+
continue;
|
|
5926
|
+
}
|
|
5927
|
+
walk(abs);
|
|
5928
|
+
continue;
|
|
5929
|
+
}
|
|
5930
|
+
if (!entry.isFile()) {
|
|
5931
|
+
excluded.push({ rel, kind: "other", reason: "not_a_regular_file" });
|
|
5932
|
+
continue;
|
|
5933
|
+
}
|
|
5934
|
+
if (ALWAYS_EXCLUDE_BASENAMES.has(lower)) {
|
|
5935
|
+
excluded.push({ rel, kind: "file", reason: "os_junk" });
|
|
5936
|
+
continue;
|
|
5937
|
+
}
|
|
5938
|
+
if (mode === "source") {
|
|
5939
|
+
if (isEnvFile(entry.name)) {
|
|
5940
|
+
excluded.push({ rel, kind: "file", reason: "env_file" });
|
|
5941
|
+
continue;
|
|
5942
|
+
}
|
|
5943
|
+
if (ig.ignores(rel)) {
|
|
5944
|
+
excluded.push({ rel, kind: "file", reason: "gitignore" });
|
|
5945
|
+
continue;
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
files.push({ rel, abs });
|
|
5949
|
+
}
|
|
5950
|
+
};
|
|
5951
|
+
walk(rootDir);
|
|
5952
|
+
return { files, excluded, gitignoreApplied };
|
|
5953
|
+
}
|
|
5954
|
+
function collectFiles(rootDir, mode) {
|
|
5955
|
+
return scanTree(rootDir, mode).files;
|
|
5956
|
+
}
|
|
5957
|
+
function zipToBase64(files, maxBytes = maxZipBytesForUpload()) {
|
|
5958
|
+
if (files.length === 0) {
|
|
5959
|
+
throw new ApiError(0, "empty_project", "Nothing to push \u2014 no files found after applying ignore rules.");
|
|
5960
|
+
}
|
|
5961
|
+
const entries = {};
|
|
5962
|
+
for (const f of files) {
|
|
5963
|
+
entries[f.rel] = (0, import_node_fs4.readFileSync)(f.abs);
|
|
5964
|
+
}
|
|
5965
|
+
const zipped = zipSync(entries, { level: ZIP_LEVEL });
|
|
5966
|
+
if (zipped.byteLength > maxBytes) {
|
|
5967
|
+
throw new ApiError(
|
|
5968
|
+
0,
|
|
5969
|
+
"zip_too_large",
|
|
5970
|
+
`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
|
+
);
|
|
5972
|
+
}
|
|
5973
|
+
return {
|
|
5974
|
+
base64: Buffer.from(zipped).toString("base64"),
|
|
5975
|
+
bytes: zipped.byteLength,
|
|
5976
|
+
count: files.length
|
|
5977
|
+
};
|
|
5978
|
+
}
|
|
5979
|
+
function detectDistDir(rootDir, override) {
|
|
5980
|
+
if (override) {
|
|
5981
|
+
const dir = (0, import_node_path6.join)(rootDir, override);
|
|
5982
|
+
if (!(0, import_node_fs4.existsSync)(dir) || !(0, import_node_fs4.statSync)(dir).isDirectory()) {
|
|
5983
|
+
throw new ApiError(0, "dist_not_found", `--dir ${override} does not exist or is not a directory.`);
|
|
5984
|
+
}
|
|
5985
|
+
return dir;
|
|
5986
|
+
}
|
|
5987
|
+
for (const candidate of ["dist", "build", "out", (0, import_node_path6.join)(".output", "public")]) {
|
|
5988
|
+
const dir = (0, import_node_path6.join)(rootDir, candidate);
|
|
5989
|
+
if ((0, import_node_fs4.existsSync)((0, import_node_path6.join)(dir, "index.html"))) return dir;
|
|
5990
|
+
}
|
|
5991
|
+
throw new ApiError(
|
|
5992
|
+
0,
|
|
5993
|
+
"dist_not_found",
|
|
5994
|
+
"No built output found (looked for dist/, build/, out/, .output/public with an index.html). Run your build first or pass --dir <path>."
|
|
5995
|
+
);
|
|
5996
|
+
}
|
|
5997
|
+
|
|
5998
|
+
// src/lib/pushAnalysis.ts
|
|
5999
|
+
var import_node_crypto = require("crypto");
|
|
6000
|
+
var import_node_fs8 = require("fs");
|
|
6001
|
+
var import_node_path10 = require("path");
|
|
6002
|
+
init_branding();
|
|
6003
|
+
|
|
6004
|
+
// src/lib/backendDecl.ts
|
|
5878
6005
|
var import_node_fs5 = require("fs");
|
|
5879
6006
|
var import_node_path7 = require("path");
|
|
6007
|
+
function readBackendDeclaration(dir = process.cwd()) {
|
|
6008
|
+
const path = (0, import_node_path7.join)(dir, ".agentful", "backend.json");
|
|
6009
|
+
if (!(0, import_node_fs5.existsSync)(path)) return null;
|
|
6010
|
+
try {
|
|
6011
|
+
const parsed = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf8"));
|
|
6012
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
6013
|
+
const collections = Array.isArray(parsed.collections) ? parsed.collections.filter((c) => c && typeof c === "object" && !Array.isArray(c)) : [];
|
|
6014
|
+
const secrets = Array.isArray(parsed.secrets) ? parsed.secrets.filter((s) => s && typeof s === "object" && !Array.isArray(s)) : [];
|
|
6015
|
+
const auth = parsed.auth && typeof parsed.auth === "object" && !Array.isArray(parsed.auth) ? parsed.auth : void 0;
|
|
6016
|
+
const decl = {
|
|
6017
|
+
...parsed,
|
|
6018
|
+
schema_version: Number(parsed.schema_version) || 1,
|
|
6019
|
+
managed_db: Boolean(parsed.managed_db),
|
|
6020
|
+
actions: Array.isArray(parsed.actions) ? parsed.actions.filter((a) => typeof a === "string") : [],
|
|
6021
|
+
contract_doc: typeof parsed.contract_doc === "string" ? parsed.contract_doc : void 0,
|
|
6022
|
+
collections,
|
|
6023
|
+
secrets,
|
|
6024
|
+
auth
|
|
6025
|
+
};
|
|
6026
|
+
const loginMethods = authMethodsOf(decl);
|
|
6027
|
+
if (!decl.managed_db && decl.actions.length === 0 && collections.length === 0 && secrets.length === 0 && loginMethods.length === 0) {
|
|
6028
|
+
return null;
|
|
6029
|
+
}
|
|
6030
|
+
return decl;
|
|
6031
|
+
} catch {
|
|
6032
|
+
return null;
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
function authMethodsOf(decl) {
|
|
6036
|
+
const raw = decl?.auth?.end_user_login;
|
|
6037
|
+
return Array.isArray(raw) ? raw.filter((m) => typeof m === "string") : [];
|
|
6038
|
+
}
|
|
6039
|
+
function describeBackendDeclaration(decl) {
|
|
6040
|
+
const parts = [];
|
|
6041
|
+
const collections = decl.collections ?? [];
|
|
6042
|
+
if (decl.managed_db || collections.length > 0) {
|
|
6043
|
+
parts.push(collections.length > 0 ? `managed DB with ${collections.length} collection(s)` : "managed DB");
|
|
6044
|
+
}
|
|
6045
|
+
if (decl.actions && decl.actions.length > 0) parts.push(`${decl.actions.length} server action(s)`);
|
|
6046
|
+
if (decl.secrets && decl.secrets.length > 0) parts.push(`${decl.secrets.length} secret(s)`);
|
|
6047
|
+
const login = authMethodsOf(decl);
|
|
6048
|
+
if (login.length > 0) parts.push(`login: ${login.join(" + ")}`);
|
|
6049
|
+
const base = parts.join(" + ") || "managed backend";
|
|
6050
|
+
return decl.contract_doc ? `${base} (contract: ${decl.contract_doc})` : base;
|
|
6051
|
+
}
|
|
6052
|
+
function backendTabUrl(userId, projectId) {
|
|
6053
|
+
return `${APP_URL}/workspace/${userId}/${projectId}?view=backend`;
|
|
6054
|
+
}
|
|
6055
|
+
var ACTIONS_DIR = (0, import_node_path7.join)(".server", "actions");
|
|
6056
|
+
var ACTION_NAME = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
6057
|
+
function checkBackendDeclaration(rootDir = process.cwd()) {
|
|
6058
|
+
const issues = [];
|
|
6059
|
+
const warnings = [];
|
|
6060
|
+
const actionsDir = (0, import_node_path7.join)(rootDir, ACTIONS_DIR);
|
|
6061
|
+
const presentFiles = /* @__PURE__ */ new Set();
|
|
6062
|
+
if ((0, import_node_fs5.existsSync)(actionsDir)) {
|
|
6063
|
+
for (const entry of (0, import_node_fs5.readdirSync)(actionsDir)) {
|
|
6064
|
+
const abs = (0, import_node_path7.join)(actionsDir, entry);
|
|
6065
|
+
let isDir = false;
|
|
6066
|
+
try {
|
|
6067
|
+
isDir = (0, import_node_fs5.statSync)(abs).isDirectory();
|
|
6068
|
+
} catch {
|
|
6069
|
+
continue;
|
|
6070
|
+
}
|
|
6071
|
+
if (isDir) {
|
|
6072
|
+
issues.push(
|
|
6073
|
+
`.server/actions/${entry}/ is a directory \u2014 action files must live directly under .server/actions/ (the platform rejects subdirectories).`
|
|
6074
|
+
);
|
|
6075
|
+
continue;
|
|
6076
|
+
}
|
|
6077
|
+
presentFiles.add(entry);
|
|
6078
|
+
const name = entry.endsWith(".js") ? entry.slice(0, -3) : null;
|
|
6079
|
+
if (name === null || !ACTION_NAME.test(name)) {
|
|
6080
|
+
issues.push(
|
|
6081
|
+
`.server/actions/${entry}: invalid action filename \u2014 must match [a-z][a-z0-9_-]{0,62}.js (names starting with _ are reserved). The platform rejects the whole push otherwise.`
|
|
6082
|
+
);
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
6086
|
+
const decl = readBackendDeclaration(rootDir);
|
|
6087
|
+
const declared = decl?.actions ?? [];
|
|
6088
|
+
for (const name of declared) {
|
|
6089
|
+
if (!ACTION_NAME.test(name)) {
|
|
6090
|
+
issues.push(
|
|
6091
|
+
`.agentful/backend.json declares action "${name}" \u2014 names must match [a-z][a-z0-9_-]{0,62} (the filename without .js).`
|
|
6092
|
+
);
|
|
6093
|
+
continue;
|
|
6094
|
+
}
|
|
6095
|
+
if (!presentFiles.has(`${name}.js`)) {
|
|
6096
|
+
issues.push(
|
|
6097
|
+
`.agentful/backend.json declares action "${name}" but .server/actions/${name}.js does not exist \u2014 create the file or remove the declaration entry.`
|
|
6098
|
+
);
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
const declaredSet = new Set(declared);
|
|
6102
|
+
for (const file of presentFiles) {
|
|
6103
|
+
const name = file.endsWith(".js") ? file.slice(0, -3) : file;
|
|
6104
|
+
if (ACTION_NAME.test(name) && !declaredSet.has(name)) {
|
|
6105
|
+
warnings.push(
|
|
6106
|
+
`.server/actions/${file} is not declared in .agentful/backend.json \u2014 it will deploy, but \`agentful backend\` and the Backend tab won't know about it. Add "${name}" to the actions list.`
|
|
6107
|
+
);
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
if (decl && decl.schema_version < 2 && ((decl.collections?.length ?? 0) > 0 || (decl.secrets?.length ?? 0) > 0 || authMethodsOf(decl).length > 0)) {
|
|
6111
|
+
issues.push(
|
|
6112
|
+
'.agentful/backend.json declares collections/secrets/auth but has "schema_version": 1 \u2014 set it to 2 (schema_version 1 declares actions only).'
|
|
6113
|
+
);
|
|
6114
|
+
}
|
|
6115
|
+
return { issues, warnings };
|
|
6116
|
+
}
|
|
6117
|
+
|
|
6118
|
+
// src/lib/preflight.ts
|
|
6119
|
+
var import_node_fs7 = require("fs");
|
|
6120
|
+
var import_node_path9 = require("path");
|
|
6121
|
+
|
|
6122
|
+
// ../platform-contract/buildEnv.ts
|
|
6123
|
+
var import_node_fs6 = require("fs");
|
|
6124
|
+
var import_node_path8 = require("path");
|
|
5880
6125
|
var BUILD_ENV = contract_default.publish.build_env;
|
|
5881
6126
|
var PUBLIC_PREFIXES = BUILD_ENV.public_prefixes;
|
|
5882
6127
|
var BUILTINS = new Set(BUILD_ENV.bundler_builtins);
|
|
@@ -5907,10 +6152,10 @@ var MENTIONS_ENV = /\benv\s*[.[]/;
|
|
|
5907
6152
|
var stripNonCode = (text) => text.replace(QUOTED, (quoted) => MENTIONS_ENV.test(quoted) ? "''" : quoted).replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:'"`\\])\/\/[^\n]*/g, "$1");
|
|
5908
6153
|
var isReportable = (name) => !BUILTINS.has(name) && splitName(name) !== null;
|
|
5909
6154
|
function collectFiles2(dir, out, budget) {
|
|
5910
|
-
if (budget.left <= 0 || !(0,
|
|
5911
|
-
for (const entry of (0,
|
|
6155
|
+
if (budget.left <= 0 || !(0, import_node_fs6.existsSync)(dir)) return;
|
|
6156
|
+
for (const entry of (0, import_node_fs6.readdirSync)(dir, { withFileTypes: true })) {
|
|
5912
6157
|
if (budget.left <= 0) return;
|
|
5913
|
-
const abs = (0,
|
|
6158
|
+
const abs = (0, import_node_path8.join)(dir, entry.name);
|
|
5914
6159
|
if (entry.isDirectory()) {
|
|
5915
6160
|
if (!SKIP_DIRS.has(entry.name)) collectFiles2(abs, out, budget);
|
|
5916
6161
|
} else if (entry.isFile() && SCAN_FILE.test(entry.name) && !SKIP_FILE.test(entry.name)) {
|
|
@@ -5926,7 +6171,7 @@ function scanEnvReads(rootDir) {
|
|
|
5926
6171
|
for (const abs of files) {
|
|
5927
6172
|
let text;
|
|
5928
6173
|
try {
|
|
5929
|
-
text = (0,
|
|
6174
|
+
text = (0, import_node_fs6.readFileSync)(abs, "utf8");
|
|
5930
6175
|
} catch {
|
|
5931
6176
|
continue;
|
|
5932
6177
|
}
|
|
@@ -5943,19 +6188,19 @@ function scanEnvReads(rootDir) {
|
|
|
5943
6188
|
if (field) note(field[1], Boolean(field[2]));
|
|
5944
6189
|
}
|
|
5945
6190
|
}
|
|
5946
|
-
const file = (0,
|
|
6191
|
+
const file = (0, import_node_path8.relative)(rootDir, abs).split(import_node_path8.sep).join("/");
|
|
5947
6192
|
for (const [name, hasFallback] of inFile) reads.push({ name, file, hasFallback });
|
|
5948
6193
|
}
|
|
5949
6194
|
return reads;
|
|
5950
6195
|
}
|
|
5951
6196
|
function envFileNames(rootDir) {
|
|
5952
6197
|
const names = /* @__PURE__ */ new Set();
|
|
5953
|
-
if (!(0,
|
|
5954
|
-
for (const entry of (0,
|
|
6198
|
+
if (!(0, import_node_fs6.existsSync)(rootDir)) return names;
|
|
6199
|
+
for (const entry of (0, import_node_fs6.readdirSync)(rootDir, { withFileTypes: true })) {
|
|
5955
6200
|
if (!entry.isFile() || !(entry.name === ".env" || entry.name.startsWith(".env."))) continue;
|
|
5956
6201
|
let text;
|
|
5957
6202
|
try {
|
|
5958
|
-
text = (0,
|
|
6203
|
+
text = (0, import_node_fs6.readFileSync)((0, import_node_path8.join)(rootDir, entry.name), "utf8");
|
|
5959
6204
|
} catch {
|
|
5960
6205
|
continue;
|
|
5961
6206
|
}
|
|
@@ -6031,7 +6276,9 @@ function feature(id) {
|
|
|
6031
6276
|
var FRAMEWORK_TABLE = contract_default.frameworks;
|
|
6032
6277
|
var SUPPORTED_FRAMEWORKS = FRAMEWORK_TABLE.map((f) => f.display_name).map((name, i, all) => i === all.length - 1 ? `or ${name}` : name).join(", ");
|
|
6033
6278
|
var PLATFORM_CONTRACT = contract_default.contract_statement;
|
|
6034
|
-
|
|
6279
|
+
function frameworkDisplayName(id) {
|
|
6280
|
+
return FRAMEWORK_TABLE.find((f) => f.id === id)?.display_name ?? id;
|
|
6281
|
+
}
|
|
6035
6282
|
var NEXT_CONFIG_BASENAMES = ["next.config.js", "next.config.ts", "next.config.mjs", "next.config.cjs"];
|
|
6036
6283
|
var ROUTES_FEATURE = feature("app_route_handlers");
|
|
6037
6284
|
var ROUTE_HANDLER_FILE = new RegExp(
|
|
@@ -6052,11 +6299,11 @@ var USE_SERVER = /^\s*(['"])use server\1\s*;?\s*$/m;
|
|
|
6052
6299
|
var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", "out"]);
|
|
6053
6300
|
var SCAN_FILE_BUDGET2 = 5e3;
|
|
6054
6301
|
function detectFramework(rootDir) {
|
|
6055
|
-
const pkgPath = (0,
|
|
6056
|
-
if (!(0,
|
|
6302
|
+
const pkgPath = (0, import_node_path9.join)(rootDir, "package.json");
|
|
6303
|
+
if (!(0, import_node_fs7.existsSync)(pkgPath)) return "vanilla";
|
|
6057
6304
|
let pkg;
|
|
6058
6305
|
try {
|
|
6059
|
-
pkg = JSON.parse((0,
|
|
6306
|
+
pkg = JSON.parse((0, import_node_fs7.readFileSync)(pkgPath, "utf8"));
|
|
6060
6307
|
} catch {
|
|
6061
6308
|
return "unknown";
|
|
6062
6309
|
}
|
|
@@ -6066,30 +6313,30 @@ function detectFramework(rootDir) {
|
|
|
6066
6313
|
}
|
|
6067
6314
|
function readDeps(rootDir) {
|
|
6068
6315
|
try {
|
|
6069
|
-
const pkg = JSON.parse((0,
|
|
6316
|
+
const pkg = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path9.join)(rootDir, "package.json"), "utf8"));
|
|
6070
6317
|
return { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6071
6318
|
} catch {
|
|
6072
6319
|
return {};
|
|
6073
6320
|
}
|
|
6074
6321
|
}
|
|
6075
6322
|
function findFiles(rootDir, dir, pattern, out, cap = 20) {
|
|
6076
|
-
if (out.length >= cap || !(0,
|
|
6077
|
-
for (const entry of (0,
|
|
6323
|
+
if (out.length >= cap || !(0, import_node_fs7.existsSync)(dir)) return;
|
|
6324
|
+
for (const entry of (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true })) {
|
|
6078
6325
|
if (out.length >= cap) return;
|
|
6079
|
-
const abs = (0,
|
|
6326
|
+
const abs = (0, import_node_path9.join)(dir, entry.name);
|
|
6080
6327
|
if (entry.isDirectory()) {
|
|
6081
6328
|
if (!SKIP_DIRS2.has(entry.name)) findFiles(rootDir, abs, pattern, out, cap);
|
|
6082
6329
|
} else if (entry.isFile() && pattern.test(entry.name)) {
|
|
6083
|
-
out.push((0,
|
|
6330
|
+
out.push((0, import_node_path9.relative)(rootDir, abs).split(import_node_path9.sep).join("/"));
|
|
6084
6331
|
}
|
|
6085
6332
|
}
|
|
6086
6333
|
}
|
|
6087
6334
|
var sample = (list) => list.slice(0, 3).join(", ") + (list.length > 3 ? ", \u2026" : "");
|
|
6088
6335
|
function collectSourceFiles(dir, out, budget) {
|
|
6089
|
-
if (budget.left <= 0 || !(0,
|
|
6090
|
-
for (const entry of (0,
|
|
6336
|
+
if (budget.left <= 0 || !(0, import_node_fs7.existsSync)(dir)) return;
|
|
6337
|
+
for (const entry of (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true })) {
|
|
6091
6338
|
if (budget.left <= 0) return;
|
|
6092
|
-
const abs = (0,
|
|
6339
|
+
const abs = (0, import_node_path9.join)(dir, entry.name);
|
|
6093
6340
|
if (entry.isDirectory()) {
|
|
6094
6341
|
if (!SKIP_DIRS2.has(entry.name)) collectSourceFiles(abs, out, budget);
|
|
6095
6342
|
} else if (entry.isFile() && SOURCE_FILE.test(entry.name)) {
|
|
@@ -6101,10 +6348,10 @@ function collectSourceFiles(dir, out, budget) {
|
|
|
6101
6348
|
function checkNextjs(rootDir) {
|
|
6102
6349
|
const issues = [];
|
|
6103
6350
|
const warnings = [];
|
|
6104
|
-
const configName = NEXT_CONFIG_BASENAMES.find((name) => (0,
|
|
6351
|
+
const configName = NEXT_CONFIG_BASENAMES.find((name) => (0, import_node_fs7.existsSync)((0, import_node_path9.join)(rootDir, name)));
|
|
6105
6352
|
let hasStaticExport = true;
|
|
6106
6353
|
if (configName) {
|
|
6107
|
-
const config = (0,
|
|
6354
|
+
const config = (0, import_node_fs7.readFileSync)((0, import_node_path9.join)(rootDir, configName), "utf8");
|
|
6108
6355
|
hasStaticExport = STATIC_EXPORT.test(config);
|
|
6109
6356
|
if (!hasStaticExport) {
|
|
6110
6357
|
issues.push(
|
|
@@ -6118,15 +6365,15 @@ function checkNextjs(rootDir) {
|
|
|
6118
6365
|
const hard = [];
|
|
6119
6366
|
const pagesApi = [];
|
|
6120
6367
|
for (const apiDir of PAGES_API_DIRS) {
|
|
6121
|
-
findFiles(rootDir, (0,
|
|
6368
|
+
findFiles(rootDir, (0, import_node_path9.join)(rootDir, apiDir), /\.(js|ts|jsx|tsx|mjs)$/, pagesApi);
|
|
6122
6369
|
}
|
|
6123
6370
|
if (pagesApi.length > 0) hard.push(`pages/api/* routes (${pagesApi.length} file(s))`);
|
|
6124
6371
|
const middlewareFiles = [];
|
|
6125
6372
|
for (const base of ["", "src"]) {
|
|
6126
|
-
const dir = base ? (0,
|
|
6127
|
-
if (!(0,
|
|
6128
|
-
for (const entry of (0,
|
|
6129
|
-
if (MIDDLEWARE_FILE.test(entry)) middlewareFiles.push((0,
|
|
6373
|
+
const dir = base ? (0, import_node_path9.join)(rootDir, base) : rootDir;
|
|
6374
|
+
if (!(0, import_node_fs7.existsSync)(dir)) continue;
|
|
6375
|
+
for (const entry of (0, import_node_fs7.readdirSync)(dir)) {
|
|
6376
|
+
if (MIDDLEWARE_FILE.test(entry)) middlewareFiles.push((0, import_node_path9.join)(base, entry).split(import_node_path9.sep).join("/"));
|
|
6130
6377
|
}
|
|
6131
6378
|
}
|
|
6132
6379
|
hard.push(...middlewareFiles);
|
|
@@ -6138,11 +6385,11 @@ function checkNextjs(rootDir) {
|
|
|
6138
6385
|
if (gssp.length >= 20 && useServer.length >= 20) break;
|
|
6139
6386
|
let text;
|
|
6140
6387
|
try {
|
|
6141
|
-
text = (0,
|
|
6388
|
+
text = (0, import_node_fs7.readFileSync)(abs, "utf8");
|
|
6142
6389
|
} catch {
|
|
6143
6390
|
continue;
|
|
6144
6391
|
}
|
|
6145
|
-
const rel = (0,
|
|
6392
|
+
const rel = (0, import_node_path9.relative)(rootDir, abs).split(import_node_path9.sep).join("/");
|
|
6146
6393
|
if (gssp.length < 20 && GET_SERVER_SIDE_PROPS.test(text)) gssp.push(rel);
|
|
6147
6394
|
if (useServer.length < 20 && USE_SERVER.test(text)) useServer.push(rel);
|
|
6148
6395
|
}
|
|
@@ -6150,7 +6397,7 @@ function checkNextjs(rootDir) {
|
|
|
6150
6397
|
if (useServer.length > 0) hard.push(`'use server' actions (${sample(useServer)})`);
|
|
6151
6398
|
const routeFiles = [];
|
|
6152
6399
|
for (const appDir of APP_DIRS) {
|
|
6153
|
-
findFiles(rootDir, (0,
|
|
6400
|
+
findFiles(rootDir, (0, import_node_path9.join)(rootDir, appDir), ROUTE_HANDLER_FILE, routeFiles);
|
|
6154
6401
|
}
|
|
6155
6402
|
if (routeFiles.length > 0) {
|
|
6156
6403
|
const label = `${routeFiles.length} route handler(s) (${sample(routeFiles)})`;
|
|
@@ -6213,118 +6460,700 @@ function runPreflight(rootDir) {
|
|
|
6213
6460
|
return { framework, issues, warnings };
|
|
6214
6461
|
}
|
|
6215
6462
|
|
|
6216
|
-
// src/lib/
|
|
6217
|
-
var
|
|
6218
|
-
var
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6463
|
+
// src/lib/pushAnalysis.ts
|
|
6464
|
+
var ANALYSIS_SCHEMA = "agentful.push-analysis";
|
|
6465
|
+
var ANALYSIS_SCHEMA_VERSION = 1;
|
|
6466
|
+
var DEFAULT_BUDGETS = {
|
|
6467
|
+
exactFileBytes: 128 * 1024 * 1024,
|
|
6468
|
+
exactTotalBytes: 256 * 1024 * 1024,
|
|
6469
|
+
hashTotalBytes: 2 * 1024 * 1024 * 1024,
|
|
6470
|
+
fileListCap: 5e3
|
|
6471
|
+
};
|
|
6472
|
+
var ZIP_ENTRY_HEADER_BYTES = 76;
|
|
6473
|
+
var ZIP_END_BYTES = 22;
|
|
6474
|
+
var DEFLATE_MAX_RATIO = 1032;
|
|
6475
|
+
var deflateUpperBound = (size) => size + 5 * (Math.floor(size / 16384) + 1);
|
|
6476
|
+
var LARGEST_FILES = 15;
|
|
6477
|
+
var TOP_LEVEL_GROUPS = 20;
|
|
6478
|
+
var DUPLICATE_GROUPS = 50;
|
|
6479
|
+
var HTML_CANDIDATES = 50;
|
|
6480
|
+
var EXCLUDED_ENTRIES = 200;
|
|
6481
|
+
var FINDING_PATHS = 20;
|
|
6482
|
+
var TITLE_SCAN_BYTES = 64 * 1024;
|
|
6483
|
+
var EMBEDDED_REPORT_MIN_CHARS = 64 * 1024;
|
|
6484
|
+
var EMBEDDED_SCAN_MIN_BYTES = EMBEDDED_REPORT_MIN_CHARS;
|
|
6485
|
+
var CLASS_EXTENSIONS = {
|
|
6486
|
+
html: ["html", "htm", "xhtml"],
|
|
6487
|
+
css: ["css", "scss", "sass", "less"],
|
|
6488
|
+
script: ["js", "mjs", "cjs", "jsx", "ts", "tsx", "vue", "svelte", "astro"],
|
|
6489
|
+
data: ["json", "geojson", "topojson", "xml", "csv", "tsv", "yaml", "yml", "toml", "txt", "md", "map", "webmanifest"],
|
|
6490
|
+
image: ["png", "jpg", "jpeg", "gif", "webp", "avif", "svg", "ico", "bmp", "tif", "tiff", "heic"],
|
|
6491
|
+
video: ["mp4", "m4v", "webm", "ogv", "mov", "avi", "mkv"],
|
|
6492
|
+
audio: ["mp3", "wav", "ogg", "oga", "m4a", "flac", "aac", "opus"],
|
|
6493
|
+
font: ["woff", "woff2", "ttf", "otf", "eot"],
|
|
6494
|
+
document: ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "odt", "ods", "odp"],
|
|
6495
|
+
archive: ["zip", "gz", "tgz", "tar", "bz2", "xz", "7z", "rar", "br", "zst"],
|
|
6496
|
+
other: []
|
|
6497
|
+
};
|
|
6498
|
+
var CLASS_BY_EXTENSION = new Map(
|
|
6499
|
+
Object.entries(CLASS_EXTENSIONS).flatMap(([cls, exts]) => exts.map((ext) => [ext, cls]))
|
|
6500
|
+
);
|
|
6501
|
+
var EMBEDDED_SCAN_CLASSES = /* @__PURE__ */ new Set(["html", "css", "script", "data"]);
|
|
6502
|
+
var baseName = (rel) => rel.slice(rel.lastIndexOf("/") + 1);
|
|
6503
|
+
function classOf(rel) {
|
|
6504
|
+
const base = baseName(rel);
|
|
6505
|
+
const dot = base.lastIndexOf(".");
|
|
6506
|
+
if (dot <= 0) return "other";
|
|
6507
|
+
return CLASS_BY_EXTENSION.get(base.slice(dot + 1).toLowerCase()) ?? "other";
|
|
6508
|
+
}
|
|
6509
|
+
var byPath = (a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0;
|
|
6510
|
+
var sha256Of = (data) => (0, import_node_crypto.createHash)("sha256").update(data).digest("hex");
|
|
6511
|
+
function sha256OfFile(abs) {
|
|
6512
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
6513
|
+
const fd2 = (0, import_node_fs8.openSync)(abs, "r");
|
|
6222
6514
|
try {
|
|
6223
|
-
const
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
const decl = {
|
|
6229
|
-
...parsed,
|
|
6230
|
-
schema_version: Number(parsed.schema_version) || 1,
|
|
6231
|
-
managed_db: Boolean(parsed.managed_db),
|
|
6232
|
-
actions: Array.isArray(parsed.actions) ? parsed.actions.filter((a) => typeof a === "string") : [],
|
|
6233
|
-
contract_doc: typeof parsed.contract_doc === "string" ? parsed.contract_doc : void 0,
|
|
6234
|
-
collections,
|
|
6235
|
-
secrets,
|
|
6236
|
-
auth
|
|
6237
|
-
};
|
|
6238
|
-
const loginMethods = authMethodsOf(decl);
|
|
6239
|
-
if (!decl.managed_db && decl.actions.length === 0 && collections.length === 0 && secrets.length === 0 && loginMethods.length === 0) {
|
|
6240
|
-
return null;
|
|
6515
|
+
const chunk = Buffer.allocUnsafe(1024 * 1024);
|
|
6516
|
+
for (; ; ) {
|
|
6517
|
+
const read = (0, import_node_fs8.readSync)(fd2, chunk, 0, chunk.length, null);
|
|
6518
|
+
if (read === 0) break;
|
|
6519
|
+
hash.update(chunk.subarray(0, read));
|
|
6241
6520
|
}
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
return null;
|
|
6521
|
+
} finally {
|
|
6522
|
+
(0, import_node_fs8.closeSync)(fd2);
|
|
6245
6523
|
}
|
|
6524
|
+
return hash.digest("hex");
|
|
6246
6525
|
}
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6526
|
+
var TITLE = /<title[^>]*>([\s\S]*?)<\/title>/i;
|
|
6527
|
+
function htmlTitle(content) {
|
|
6528
|
+
const match = TITLE.exec(content.subarray(0, TITLE_SCAN_BYTES).toString("utf8"));
|
|
6529
|
+
const title = match?.[1].replace(/\s+/g, " ").trim();
|
|
6530
|
+
return title ? title.slice(0, 120) : null;
|
|
6531
|
+
}
|
|
6532
|
+
var DATA_URL = /data:([a-z0-9][a-z0-9.+-]*\/[a-z0-9][a-z0-9.+-]*)(?:;[a-z0-9-]+=[^;,\s"')]*)*;base64,([a-z0-9+/]+={0,2})/gi;
|
|
6533
|
+
function scanEmbeddedMedia(content) {
|
|
6534
|
+
const text = content.toString("latin1");
|
|
6535
|
+
if (!text.includes(";base64,")) return null;
|
|
6536
|
+
let references = 0;
|
|
6537
|
+
let base64Chars = 0;
|
|
6538
|
+
const mimeTypes = {};
|
|
6539
|
+
DATA_URL.lastIndex = 0;
|
|
6540
|
+
for (let match = DATA_URL.exec(text); match; match = DATA_URL.exec(text)) {
|
|
6541
|
+
references += 1;
|
|
6542
|
+
base64Chars += match[2].length;
|
|
6543
|
+
const mime = match[1].toLowerCase();
|
|
6544
|
+
mimeTypes[mime] = (mimeTypes[mime] ?? 0) + 1;
|
|
6545
|
+
}
|
|
6546
|
+
if (references === 0) return null;
|
|
6547
|
+
return {
|
|
6548
|
+
references,
|
|
6549
|
+
base64_chars: base64Chars,
|
|
6550
|
+
decoded_bytes: Math.floor(base64Chars * 3 / 4),
|
|
6551
|
+
mime_types: Object.fromEntries(Object.entries(mimeTypes).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0))
|
|
6552
|
+
};
|
|
6250
6553
|
}
|
|
6251
|
-
function
|
|
6252
|
-
const
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
const
|
|
6262
|
-
return
|
|
6554
|
+
function formatBytes(bytes2, rounding = "nearest") {
|
|
6555
|
+
const units = ["B", "KiB", "MiB", "GiB"];
|
|
6556
|
+
let value = bytes2;
|
|
6557
|
+
let unit = 0;
|
|
6558
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
6559
|
+
value /= 1024;
|
|
6560
|
+
unit += 1;
|
|
6561
|
+
}
|
|
6562
|
+
if (unit === 0 || Number.isInteger(value)) return `${value} ${units[unit]}`;
|
|
6563
|
+
const tenths = value * 10;
|
|
6564
|
+
const rounded = (rounding === "down" ? Math.floor(tenths) : Math.round(tenths)) / 10;
|
|
6565
|
+
return `${rounded.toFixed(1)} ${units[unit]}`;
|
|
6566
|
+
}
|
|
6567
|
+
function sizeOverLimit(bytes2, limit) {
|
|
6568
|
+
const size = formatBytes(bytes2);
|
|
6569
|
+
const allowed = formatBytes(limit, "down");
|
|
6570
|
+
const [value, unit] = size.split(" ");
|
|
6571
|
+
const [limitValue, limitUnit] = allowed.split(" ");
|
|
6572
|
+
if (unit !== limitUnit || Number(value) > Number(limitValue)) return [size, allowed];
|
|
6573
|
+
return [`${bytes2.toLocaleString("en-US")} bytes`, `${limit.toLocaleString("en-US")} bytes`];
|
|
6574
|
+
}
|
|
6575
|
+
var plural = (count2, singular, pluralForm = `${singular}s`) => `${count2.toLocaleString("en-US")} ${count2 === 1 ? singular : pluralForm}`;
|
|
6576
|
+
var samplePaths = (paths) => paths.slice(0, 3).join(", ") + (paths.length > 3 ? `, and ${paths.length - 3} more` : "");
|
|
6577
|
+
function groupSizes(files, keyOf) {
|
|
6578
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6579
|
+
for (const file of files) {
|
|
6580
|
+
const name = keyOf(file);
|
|
6581
|
+
const group = groups.get(name) ?? { name, files: 0, bytes: 0, zip_bytes: 0 };
|
|
6582
|
+
group.files += 1;
|
|
6583
|
+
group.bytes += file.bytes;
|
|
6584
|
+
group.zip_bytes += file.zip_bytes;
|
|
6585
|
+
groups.set(name, group);
|
|
6586
|
+
}
|
|
6587
|
+
return [...groups.values()].sort((a, b) => b.bytes - a.bytes || (a.name < b.name ? -1 : 1));
|
|
6588
|
+
}
|
|
6589
|
+
var publicFile = ({ path, bytes: bytes2, zip_bytes, sha256, class: cls, zip_bytes_estimated }) => ({
|
|
6590
|
+
path,
|
|
6591
|
+
bytes: bytes2,
|
|
6592
|
+
zip_bytes,
|
|
6593
|
+
sha256,
|
|
6594
|
+
class: cls,
|
|
6595
|
+
...zip_bytes_estimated ? { zip_bytes_estimated } : {}
|
|
6596
|
+
});
|
|
6597
|
+
function limits(maxZipBytes) {
|
|
6598
|
+
return { max_zip_bytes: maxZipBytes, max_unpacked_bytes: MAX_UNPACKED_BYTES, max_files: MAX_UPLOAD_FILES };
|
|
6263
6599
|
}
|
|
6264
|
-
function
|
|
6265
|
-
|
|
6600
|
+
function projectLink(projectDir) {
|
|
6601
|
+
const config = readProjectConfig(projectDir);
|
|
6602
|
+
return { linked: config !== null, project_id: config?.projectId ?? null };
|
|
6266
6603
|
}
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6604
|
+
function failedAnalysis(mode, projectDir, blocker) {
|
|
6605
|
+
return {
|
|
6606
|
+
schema: ANALYSIS_SCHEMA,
|
|
6607
|
+
schema_version: ANALYSIS_SCHEMA_VERSION,
|
|
6608
|
+
cli_version: VERSION,
|
|
6609
|
+
view: "full",
|
|
6610
|
+
mode,
|
|
6611
|
+
root: null,
|
|
6612
|
+
ok: false,
|
|
6613
|
+
framework: null,
|
|
6614
|
+
project: projectLink(projectDir),
|
|
6615
|
+
limits: limits(maxZipBytesForUpload(mode)),
|
|
6616
|
+
totals: {
|
|
6617
|
+
files: 0,
|
|
6618
|
+
bytes: 0,
|
|
6619
|
+
zip_bytes: 0,
|
|
6620
|
+
zip_bytes_exact: true,
|
|
6621
|
+
zip_bytes_without_duplicates: 0,
|
|
6622
|
+
stored_files: 0,
|
|
6623
|
+
stored_bytes: 0
|
|
6624
|
+
},
|
|
6625
|
+
entry: { required: mode === "dist", expected: "index.html", found: false, path: null, candidates: [] },
|
|
6626
|
+
flattened_prefix: null,
|
|
6627
|
+
largest_files: [],
|
|
6628
|
+
by_class: [],
|
|
6629
|
+
by_top_level: [],
|
|
6630
|
+
duplicates: [],
|
|
6631
|
+
embedded_media: [],
|
|
6632
|
+
excluded: { gitignore_applied: false, entries: [], omitted: 0 },
|
|
6633
|
+
dropped_by_platform: [],
|
|
6634
|
+
blockers: [{ overridable: false, ...blocker }],
|
|
6635
|
+
warnings: [],
|
|
6636
|
+
suggestions: [],
|
|
6637
|
+
files: [],
|
|
6638
|
+
files_omitted: 0,
|
|
6639
|
+
fingerprint: `sha256:${sha256Of(Buffer.from(JSON.stringify({ v: ANALYSIS_SCHEMA_VERSION, mode, files: [] })))}`
|
|
6640
|
+
};
|
|
6641
|
+
}
|
|
6642
|
+
function analyzePush(options) {
|
|
6643
|
+
const { rootDir, mode } = options;
|
|
6644
|
+
const projectDir = options.projectDir ?? rootDir;
|
|
6645
|
+
const budgets = { ...DEFAULT_BUDGETS, ...options.budgets };
|
|
6646
|
+
const scan = scanTree(rootDir, mode);
|
|
6647
|
+
const collected = [...scan.files].sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
6648
|
+
const measured = [];
|
|
6649
|
+
const unreadable = [];
|
|
6650
|
+
const embeddedByHash = /* @__PURE__ */ new Map();
|
|
6651
|
+
const embedded = [];
|
|
6652
|
+
let exactUsed = 0;
|
|
6653
|
+
let hashUsed = 0;
|
|
6654
|
+
for (const file of collected) {
|
|
6655
|
+
const cls = classOf(file.rel);
|
|
6656
|
+
const headerBytes = ZIP_ENTRY_HEADER_BYTES + 2 * Buffer.byteLength(file.rel, "utf8");
|
|
6657
|
+
try {
|
|
6658
|
+
const size = (0, import_node_fs8.statSync)(file.abs).size;
|
|
6659
|
+
if (size <= budgets.exactFileBytes && exactUsed + size <= budgets.exactTotalBytes) {
|
|
6660
|
+
const content = (0, import_node_fs8.readFileSync)(file.abs);
|
|
6661
|
+
exactUsed += content.length;
|
|
6662
|
+
const sha2562 = sha256Of(content);
|
|
6663
|
+
const zipBytes2 = headerBytes + deflateSync(content, { level: ZIP_LEVEL }).length;
|
|
6664
|
+
if (EMBEDDED_SCAN_CLASSES.has(cls) && content.length >= EMBEDDED_SCAN_MIN_BYTES) {
|
|
6665
|
+
if (!embeddedByHash.has(sha2562)) embeddedByHash.set(sha2562, scanEmbeddedMedia(content));
|
|
6666
|
+
const found = embeddedByHash.get(sha2562);
|
|
6667
|
+
if (found && found.base64_chars >= EMBEDDED_REPORT_MIN_CHARS) embedded.push({ path: file.rel, ...found });
|
|
6668
|
+
}
|
|
6669
|
+
measured.push({
|
|
6670
|
+
path: file.rel,
|
|
6671
|
+
bytes: content.length,
|
|
6672
|
+
zip_bytes: zipBytes2,
|
|
6673
|
+
zipLower: zipBytes2,
|
|
6674
|
+
sha256: sha2562,
|
|
6675
|
+
class: cls,
|
|
6676
|
+
title: cls === "html" ? htmlTitle(content) : null
|
|
6677
|
+
});
|
|
6678
|
+
continue;
|
|
6679
|
+
}
|
|
6680
|
+
let sha256 = null;
|
|
6681
|
+
if (hashUsed + size <= budgets.hashTotalBytes) {
|
|
6682
|
+
sha256 = sha256OfFile(file.abs);
|
|
6683
|
+
hashUsed += size;
|
|
6684
|
+
}
|
|
6685
|
+
measured.push({
|
|
6686
|
+
path: file.rel,
|
|
6687
|
+
bytes: size,
|
|
6688
|
+
sha256,
|
|
6689
|
+
class: cls,
|
|
6690
|
+
title: null,
|
|
6691
|
+
zip_bytes: headerBytes + deflateUpperBound(size),
|
|
6692
|
+
zipLower: headerBytes + Math.ceil(size / DEFLATE_MAX_RATIO),
|
|
6693
|
+
zip_bytes_estimated: true
|
|
6694
|
+
});
|
|
6695
|
+
} catch (err2) {
|
|
6696
|
+
unreadable.push({ path: file.rel, error: err2.code ?? String(err2) });
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
6699
|
+
const zipExact = measured.every((file) => !file.zip_bytes_estimated);
|
|
6700
|
+
const sum = (pick) => measured.reduce((total, file) => total + pick(file), 0);
|
|
6701
|
+
const totalBytes = sum((file) => file.bytes);
|
|
6702
|
+
const zipBytes = measured.length > 0 ? sum((file) => file.zip_bytes) + ZIP_END_BYTES : 0;
|
|
6703
|
+
const zipLower = measured.length > 0 ? sum((file) => file.zipLower) + ZIP_END_BYTES : 0;
|
|
6704
|
+
const view = serverView(measured.map((file) => file.path));
|
|
6705
|
+
const byPathMap = new Map(measured.map((file) => [file.path, file]));
|
|
6706
|
+
const stored = measured.filter((file) => view.stored.has(file.path));
|
|
6707
|
+
const storedBytes = stored.reduce((total, file) => total + file.bytes, 0);
|
|
6708
|
+
const storedNames = new Map([...view.stored].map(([local, name]) => [name, local]));
|
|
6709
|
+
const dropped = view.dropped.map((drop) => ({ path: drop.rel, bytes: byPathMap.get(drop.rel)?.bytes ?? 0, reason: drop.reason, matched: drop.matched })).sort(byPath);
|
|
6710
|
+
const byHash = /* @__PURE__ */ new Map();
|
|
6711
|
+
for (const file of measured) {
|
|
6712
|
+
if (!file.sha256 || file.bytes === 0) continue;
|
|
6713
|
+
const group = byHash.get(file.sha256);
|
|
6714
|
+
if (group) group.push(file);
|
|
6715
|
+
else byHash.set(file.sha256, [file]);
|
|
6716
|
+
}
|
|
6717
|
+
const firstOfContent = /* @__PURE__ */ new Map();
|
|
6718
|
+
const duplicates = [];
|
|
6719
|
+
for (const [sha256, group] of byHash) {
|
|
6720
|
+
firstOfContent.set(sha256, group[0].path);
|
|
6721
|
+
if (group.length < 2) continue;
|
|
6722
|
+
duplicates.push({
|
|
6723
|
+
sha256,
|
|
6724
|
+
bytes: group[0].bytes,
|
|
6725
|
+
paths: group.map((file) => file.path),
|
|
6726
|
+
redundant_zip_bytes: group.slice(1).reduce((total, file) => total + file.zip_bytes, 0)
|
|
6727
|
+
});
|
|
6728
|
+
}
|
|
6729
|
+
duplicates.sort((a, b) => b.redundant_zip_bytes - a.redundant_zip_bytes || (a.paths[0] < b.paths[0] ? -1 : 1));
|
|
6730
|
+
const redundantZipBytes = duplicates.reduce((total, group) => total + group.redundant_zip_bytes, 0);
|
|
6731
|
+
const preflight = mode === "source" ? runPreflight(rootDir) : null;
|
|
6732
|
+
const framework = preflight?.framework ?? null;
|
|
6733
|
+
const hasManifest = storedNames.has("package.json");
|
|
6734
|
+
const entryRequired = mode === "dist" || !hasManifest;
|
|
6735
|
+
const entryPath = storedNames.get("index.html") ?? null;
|
|
6736
|
+
const entryMissing = entryRequired && entryPath === null && measured.length > 0;
|
|
6737
|
+
const candidates = !entryMissing ? [] : stored.filter((file) => file.class === "html").sort((a, b) => a.path.split("/").length - b.path.split("/").length || byPath(a, b)).slice(0, HTML_CANDIDATES).map((file) => {
|
|
6738
|
+
const first = file.sha256 ? firstOfContent.get(file.sha256) : void 0;
|
|
6739
|
+
return {
|
|
6740
|
+
path: file.path,
|
|
6741
|
+
bytes: file.bytes,
|
|
6742
|
+
title: file.title,
|
|
6743
|
+
sha256: file.sha256,
|
|
6744
|
+
same_content_as: first && first !== file.path ? first : null
|
|
6745
|
+
};
|
|
6746
|
+
});
|
|
6747
|
+
const maxZipBytes = maxZipBytesForUpload(mode, mode === "source" ? readBackendDeclaration(rootDir) : null);
|
|
6748
|
+
const zipLimit = formatBytes(maxZipBytes, "down");
|
|
6749
|
+
const blockers = [];
|
|
6271
6750
|
const warnings = [];
|
|
6272
|
-
const
|
|
6273
|
-
const
|
|
6274
|
-
if (
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6751
|
+
const suggestions = [];
|
|
6752
|
+
const dirLabel = mode === "dist" ? (0, import_node_path10.relative)(projectDir, rootDir).split(import_node_path10.sep).join("/") || "." : ".";
|
|
6753
|
+
if (measured.length === 0 && unreadable.length === 0) {
|
|
6754
|
+
blockers.push({
|
|
6755
|
+
code: "empty_upload",
|
|
6756
|
+
overridable: false,
|
|
6757
|
+
message: "Nothing to push \u2014 no files found after applying ignore rules."
|
|
6758
|
+
});
|
|
6759
|
+
}
|
|
6760
|
+
if (unreadable.length > 0) {
|
|
6761
|
+
blockers.push({
|
|
6762
|
+
code: "unreadable_file",
|
|
6763
|
+
overridable: false,
|
|
6764
|
+
message: `${plural(unreadable.length, "file")} cannot be read (${unreadable[0].error}): ${samplePaths(unreadable.map((f) => f.path))}.`,
|
|
6765
|
+
paths: unreadable.map((f) => f.path).slice(0, FINDING_PATHS)
|
|
6766
|
+
});
|
|
6767
|
+
}
|
|
6768
|
+
if (measured.length > MAX_UPLOAD_FILES) {
|
|
6769
|
+
blockers.push({
|
|
6770
|
+
code: "too_many_files",
|
|
6771
|
+
overridable: false,
|
|
6772
|
+
message: `${plural(measured.length, "file")} \u2014 the platform accepts at most ${MAX_UPLOAD_FILES.toLocaleString("en-US")} files per push.`
|
|
6773
|
+
});
|
|
6774
|
+
}
|
|
6775
|
+
if (storedBytes > MAX_UNPACKED_BYTES) {
|
|
6776
|
+
const [size, allowed] = sizeOverLimit(storedBytes, MAX_UNPACKED_BYTES);
|
|
6777
|
+
blockers.push({
|
|
6778
|
+
code: "unpacked_too_large",
|
|
6779
|
+
overridable: false,
|
|
6780
|
+
message: `The files add up to ${size} \u2014 the platform accepts at most ${allowed} of files per push.`
|
|
6781
|
+
});
|
|
6782
|
+
}
|
|
6783
|
+
if (zipLower > maxZipBytes) {
|
|
6784
|
+
const [size, allowed] = sizeOverLimit(zipExact ? zipBytes : zipLower, maxZipBytes);
|
|
6785
|
+
blockers.push({
|
|
6786
|
+
code: "zip_too_large",
|
|
6787
|
+
overridable: false,
|
|
6788
|
+
message: `The upload is ${zipExact ? "" : "at least "}${size} compressed \u2014 a push carries at most ${allowed} compressed.`
|
|
6789
|
+
});
|
|
6790
|
+
} else if (!zipExact && zipBytes > maxZipBytes) {
|
|
6791
|
+
warnings.push({
|
|
6792
|
+
code: "zip_size_unknown",
|
|
6793
|
+
message: `The compressed size could not be determined: part of the tree is too large to compress during the analysis. It is between ${formatBytes(zipLower)} and ${formatBytes(zipBytes)}; a push carries at most ${zipLimit} compressed.`
|
|
6794
|
+
});
|
|
6795
|
+
}
|
|
6796
|
+
if (entryMissing) {
|
|
6797
|
+
blockers.push({
|
|
6798
|
+
code: "entry_missing",
|
|
6799
|
+
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."
|
|
6801
|
+
});
|
|
6802
|
+
}
|
|
6803
|
+
const backend = mode === "source" ? checkBackendDeclaration(rootDir) : null;
|
|
6804
|
+
for (const message of preflight?.issues ?? []) blockers.push({ code: "preflight", overridable: true, message });
|
|
6805
|
+
for (const message of backend?.issues ?? []) blockers.push({ code: "backend_declaration", overridable: true, message });
|
|
6806
|
+
for (const message of preflight?.warnings ?? []) warnings.push({ code: "preflight", message });
|
|
6807
|
+
for (const message of backend?.warnings ?? []) warnings.push({ code: "backend_declaration", message });
|
|
6808
|
+
if (view.strippedPrefix) {
|
|
6809
|
+
warnings.push({
|
|
6810
|
+
code: "wrapper_directory_flattened",
|
|
6811
|
+
message: `Everything sits in one folder, ${view.strippedPrefix} \u2014 the platform stores its content at the top level (${view.strippedPrefix}index.html becomes index.html).`
|
|
6812
|
+
});
|
|
6813
|
+
}
|
|
6814
|
+
if (dropped.length > 0) {
|
|
6815
|
+
const matched = [...new Set(dropped.map((drop) => drop.matched))];
|
|
6816
|
+
warnings.push({
|
|
6817
|
+
code: "dropped_by_platform",
|
|
6818
|
+
message: `${plural(dropped.length, "file")} (${formatBytes(dropped.reduce((total, drop) => total + drop.bytes, 0))}) will be uploaded but not stored \u2014 the platform drops these paths by name (${samplePaths(matched)}): ${samplePaths(dropped.map((drop) => drop.path))}. If the site needs them, they have to live under another name.`,
|
|
6819
|
+
paths: dropped.map((drop) => drop.path).slice(0, FINDING_PATHS)
|
|
6820
|
+
});
|
|
6821
|
+
}
|
|
6822
|
+
if (mode === "source") {
|
|
6823
|
+
const lostDirs = scan.excluded.filter((entry) => entry.reason === "always_excluded_dir" && ["dist", "build", "out"].includes(baseName(entry.rel).toLowerCase())).filter((entry) => framework === "vanilla" || /^(public|static)\//.test(entry.rel)).map((entry) => `${entry.rel}/`).sort();
|
|
6824
|
+
if (lostDirs.length > 0) {
|
|
6825
|
+
warnings.push({
|
|
6826
|
+
code: "excluded_static_directory",
|
|
6827
|
+
message: `${samplePaths(lostDirs)} ${lostDirs.length === 1 ? "is" : "are"} never uploaded: folders named dist, build or out are treated as build output. ${framework === "vanilla" ? "This site has no build step" : "Files under public/ and static/ are served as they are"}, so nothing recreates them \u2014 if the site needs these files, move them to a folder with another name.`,
|
|
6828
|
+
paths: lostDirs.slice(0, FINDING_PATHS)
|
|
6829
|
+
});
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
const filesOmitted = Math.max(0, measured.length - budgets.fileListCap);
|
|
6833
|
+
if (filesOmitted > 0) {
|
|
6834
|
+
warnings.push({
|
|
6835
|
+
code: "file_list_truncated",
|
|
6836
|
+
message: `The file list of this report holds the first ${budgets.fileListCap.toLocaleString("en-US")} of ${plural(measured.length, "file")}; the totals cover all of them.`
|
|
6837
|
+
});
|
|
6838
|
+
}
|
|
6839
|
+
const tooLarge = blockers.some((b) => b.code === "zip_too_large" || b.code === "unpacked_too_large");
|
|
6840
|
+
if (entryMissing) {
|
|
6841
|
+
if (candidates.length > 0) {
|
|
6842
|
+
suggestions.push({
|
|
6843
|
+
code: "name_start_page",
|
|
6844
|
+
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: candidates.map((candidate) => candidate.path).slice(0, FINDING_PATHS)
|
|
6846
|
+
});
|
|
6847
|
+
}
|
|
6848
|
+
const siteDirs = /* @__PURE__ */ new Set();
|
|
6849
|
+
for (const file of stored) {
|
|
6850
|
+
if (baseName(file.path) === "index.html" && file.path.includes("/")) siteDirs.add(file.path.slice(0, file.path.lastIndexOf("/")));
|
|
6851
|
+
}
|
|
6852
|
+
if (mode === "source") {
|
|
6853
|
+
for (const dir of ["dist", "build", "out", ".output/public"]) {
|
|
6854
|
+
if ((0, import_node_fs8.existsSync)((0, import_node_path10.join)(rootDir, dir, "index.html"))) siteDirs.add(dir);
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
for (const dir of [...siteDirs].sort().slice(0, 5)) {
|
|
6858
|
+
const target = mode === "dist" ? `${dirLabel === "." ? "" : `${dirLabel}/`}${dir}` : dir;
|
|
6859
|
+
suggestions.push({
|
|
6860
|
+
code: "push_site_directory",
|
|
6861
|
+
message: `${target}/ has an index.html of its own. If that folder is the finished site, upload it on its own \u2014 only the files inside it are served.`,
|
|
6862
|
+
command: `agentful push --prebuilt --dir ${target}`
|
|
6863
|
+
});
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
if (tooLarge) {
|
|
6867
|
+
const oversized = measured.filter((file) => file.zipLower > maxZipBytes).sort((a, b) => b.bytes - a.bytes || byPath(a, b));
|
|
6868
|
+
if (oversized.length > 0) {
|
|
6869
|
+
suggestions.push({
|
|
6870
|
+
code: "oversized_files",
|
|
6871
|
+
message: `${samplePaths(oversized.map((file) => `${file.path} (${formatBytes(file.zipLower)} compressed)`))} ${oversized.length === 1 ? "is" : "are each"} larger than a whole push can carry. The upload cannot fit while ${oversized.length === 1 ? "it is" : "they are"} part of it.`,
|
|
6872
|
+
paths: oversized.map((file) => file.path).slice(0, FINDING_PATHS)
|
|
6873
|
+
});
|
|
6874
|
+
}
|
|
6875
|
+
if (duplicates.length > 0 && redundantZipBytes >= zipBytes / 20) {
|
|
6876
|
+
const copies = duplicates.reduce((total, group) => total + group.paths.length - 1, 0);
|
|
6877
|
+
const remaining = zipBytes - redundantZipBytes;
|
|
6878
|
+
suggestions.push({
|
|
6879
|
+
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(redundantZipBytes)} to the compressed upload. If nothing links to the extra names, one copy each brings it to ${formatBytes(remaining)}${remaining > maxZipBytes ? ` \u2014 still above the ${zipLimit} limit` : ""}.`,
|
|
6881
|
+
paths: duplicates.flatMap((group) => group.paths.slice(1)).slice(0, FINDING_PATHS)
|
|
6882
|
+
});
|
|
6883
|
+
}
|
|
6884
|
+
suggestions.push(mode === "source" ? {
|
|
6885
|
+
code: "exclude_with_gitignore",
|
|
6886
|
+
message: "Files and folders listed in .gitignore are not uploaded. Leave out what the site does not serve \u2014 originals, working files, exports."
|
|
6887
|
+
} : {
|
|
6888
|
+
code: "narrow_prebuilt_directory",
|
|
6889
|
+
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."
|
|
6890
|
+
});
|
|
6891
|
+
suggestions.push({
|
|
6892
|
+
code: "upload_limit",
|
|
6893
|
+
message: `A push carries at most ${zipLimit} compressed and ${formatBytes(MAX_UNPACKED_BYTES)} of files. A site that stays above that cannot be pushed yet.`
|
|
6894
|
+
});
|
|
6895
|
+
}
|
|
6896
|
+
const excludedEntries = [...scan.excluded].sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0).slice(0, EXCLUDED_ENTRIES).map((entry) => {
|
|
6897
|
+
let bytes2;
|
|
6898
|
+
if (entry.kind === "file") {
|
|
6278
6899
|
try {
|
|
6279
|
-
|
|
6900
|
+
bytes2 = (0, import_node_fs8.lstatSync)((0, import_node_path10.join)(rootDir, entry.rel)).size;
|
|
6280
6901
|
} catch {
|
|
6281
|
-
continue;
|
|
6282
6902
|
}
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6903
|
+
}
|
|
6904
|
+
return { path: entry.rel, kind: entry.kind, reason: entry.reason, ...bytes2 === void 0 ? {} : { bytes: bytes2 } };
|
|
6905
|
+
});
|
|
6906
|
+
const fingerprint = sha256Of(Buffer.from(JSON.stringify({
|
|
6907
|
+
v: ANALYSIS_SCHEMA_VERSION,
|
|
6908
|
+
mode,
|
|
6909
|
+
files: measured.map((file) => [file.path, file.bytes, file.sha256])
|
|
6910
|
+
})));
|
|
6911
|
+
return {
|
|
6912
|
+
schema: ANALYSIS_SCHEMA,
|
|
6913
|
+
schema_version: ANALYSIS_SCHEMA_VERSION,
|
|
6914
|
+
cli_version: VERSION,
|
|
6915
|
+
view: "full",
|
|
6916
|
+
mode,
|
|
6917
|
+
root: rootDir,
|
|
6918
|
+
ok: blockers.length === 0,
|
|
6919
|
+
framework,
|
|
6920
|
+
project: projectLink(projectDir),
|
|
6921
|
+
limits: limits(maxZipBytes),
|
|
6922
|
+
totals: {
|
|
6923
|
+
files: measured.length,
|
|
6924
|
+
bytes: totalBytes,
|
|
6925
|
+
zip_bytes: zipBytes,
|
|
6926
|
+
zip_bytes_exact: zipExact,
|
|
6927
|
+
zip_bytes_without_duplicates: zipBytes - redundantZipBytes,
|
|
6928
|
+
stored_files: stored.length,
|
|
6929
|
+
stored_bytes: storedBytes
|
|
6930
|
+
},
|
|
6931
|
+
entry: { required: entryRequired, expected: "index.html", found: entryPath !== null, path: entryPath, candidates },
|
|
6932
|
+
flattened_prefix: view.strippedPrefix || null,
|
|
6933
|
+
largest_files: [...measured].sort((a, b) => b.bytes - a.bytes || byPath(a, b)).slice(0, LARGEST_FILES).map(publicFile),
|
|
6934
|
+
by_class: groupSizes(measured, (file) => file.class),
|
|
6935
|
+
by_top_level: groupSizes(measured, (file) => file.path.includes("/") ? file.path.slice(0, file.path.indexOf("/")) : ".").slice(0, TOP_LEVEL_GROUPS),
|
|
6936
|
+
duplicates: duplicates.slice(0, DUPLICATE_GROUPS),
|
|
6937
|
+
embedded_media: embedded.sort((a, b) => b.base64_chars - a.base64_chars || byPath(a, b)),
|
|
6938
|
+
excluded: {
|
|
6939
|
+
gitignore_applied: scan.gitignoreApplied,
|
|
6940
|
+
entries: excludedEntries,
|
|
6941
|
+
omitted: Math.max(0, scan.excluded.length - EXCLUDED_ENTRIES)
|
|
6942
|
+
},
|
|
6943
|
+
dropped_by_platform: dropped,
|
|
6944
|
+
blockers,
|
|
6945
|
+
warnings,
|
|
6946
|
+
suggestions,
|
|
6947
|
+
files: measured.slice(0, budgets.fileListCap).map(publicFile),
|
|
6948
|
+
files_omitted: filesOmitted,
|
|
6949
|
+
fingerprint: `sha256:${fingerprint}`
|
|
6950
|
+
};
|
|
6951
|
+
}
|
|
6952
|
+
var SUMMARY_LISTS = {
|
|
6953
|
+
largest_files: 8,
|
|
6954
|
+
by_top_level: 8,
|
|
6955
|
+
duplicates: 8,
|
|
6956
|
+
duplicate_paths: 8,
|
|
6957
|
+
embedded_media: 8,
|
|
6958
|
+
entry_candidates: 20,
|
|
6959
|
+
excluded_entries: 30,
|
|
6960
|
+
dropped_by_platform: 20,
|
|
6961
|
+
finding_paths: 8
|
|
6962
|
+
};
|
|
6963
|
+
var SUMMARY_MAX_BYTES = 24 * 1024;
|
|
6964
|
+
var printAnalysis = (analysis) => `${JSON.stringify(analysis, null, 2)}
|
|
6965
|
+
`;
|
|
6966
|
+
function summarize(analysis, divisor) {
|
|
6967
|
+
const shortened = {};
|
|
6968
|
+
const cut = (name, list, cap, alreadyOmitted = 0) => {
|
|
6969
|
+
const keep = Math.max(1, Math.floor(cap / divisor));
|
|
6970
|
+
const omitted = Math.max(0, list.length - keep) + alreadyOmitted;
|
|
6971
|
+
if (omitted > 0) shortened[name] = (shortened[name] ?? 0) + omitted;
|
|
6972
|
+
return list.slice(0, keep);
|
|
6973
|
+
};
|
|
6974
|
+
const finding = (kind) => (item) => item.paths ? { ...item, paths: cut(`${kind}.paths`, item.paths, SUMMARY_LISTS.finding_paths) } : item;
|
|
6975
|
+
if (analysis.totals.files > 0) shortened.files = analysis.totals.files;
|
|
6976
|
+
const excludedEntries = cut(
|
|
6977
|
+
"excluded.entries",
|
|
6978
|
+
analysis.excluded.entries,
|
|
6979
|
+
SUMMARY_LISTS.excluded_entries,
|
|
6980
|
+
analysis.excluded.omitted
|
|
6981
|
+
);
|
|
6982
|
+
return {
|
|
6983
|
+
schema: analysis.schema,
|
|
6984
|
+
schema_version: analysis.schema_version,
|
|
6985
|
+
cli_version: analysis.cli_version,
|
|
6986
|
+
view: "summary",
|
|
6987
|
+
shortened,
|
|
6988
|
+
mode: analysis.mode,
|
|
6989
|
+
root: analysis.root,
|
|
6990
|
+
ok: analysis.ok,
|
|
6991
|
+
framework: analysis.framework,
|
|
6992
|
+
project: analysis.project,
|
|
6993
|
+
limits: analysis.limits,
|
|
6994
|
+
totals: analysis.totals,
|
|
6995
|
+
blockers: analysis.blockers.map(finding("blockers")),
|
|
6996
|
+
// The note about the cap of the full file list describes a list that is not here.
|
|
6997
|
+
warnings: analysis.warnings.filter((warning) => warning.code !== "file_list_truncated").map(finding("warnings")),
|
|
6998
|
+
suggestions: analysis.suggestions.map(finding("suggestions")),
|
|
6999
|
+
entry: {
|
|
7000
|
+
...analysis.entry,
|
|
7001
|
+
candidates: cut("entry.candidates", analysis.entry.candidates, SUMMARY_LISTS.entry_candidates)
|
|
7002
|
+
},
|
|
7003
|
+
flattened_prefix: analysis.flattened_prefix,
|
|
7004
|
+
largest_files: cut("largest_files", analysis.largest_files, SUMMARY_LISTS.largest_files),
|
|
7005
|
+
by_class: analysis.by_class,
|
|
7006
|
+
by_top_level: cut("by_top_level", analysis.by_top_level, SUMMARY_LISTS.by_top_level),
|
|
7007
|
+
duplicates: cut("duplicates", analysis.duplicates, SUMMARY_LISTS.duplicates).map((group) => ({ ...group, paths: cut("duplicates.paths", group.paths, SUMMARY_LISTS.duplicate_paths) })),
|
|
7008
|
+
embedded_media: cut("embedded_media", analysis.embedded_media, SUMMARY_LISTS.embedded_media),
|
|
7009
|
+
excluded: {
|
|
7010
|
+
...analysis.excluded,
|
|
7011
|
+
entries: excludedEntries,
|
|
7012
|
+
omitted: analysis.excluded.omitted + analysis.excluded.entries.length - excludedEntries.length
|
|
7013
|
+
},
|
|
7014
|
+
dropped_by_platform: cut("dropped_by_platform", analysis.dropped_by_platform, SUMMARY_LISTS.dropped_by_platform),
|
|
7015
|
+
files: [],
|
|
7016
|
+
files_omitted: analysis.totals.files,
|
|
7017
|
+
fingerprint: analysis.fingerprint
|
|
7018
|
+
};
|
|
7019
|
+
}
|
|
7020
|
+
function summarizeAnalysis(analysis) {
|
|
7021
|
+
let summary = summarize(analysis, 1);
|
|
7022
|
+
for (let divisor = 2; divisor <= 32 && Buffer.byteLength(printAnalysis(summary), "utf8") > SUMMARY_MAX_BYTES; divisor *= 2) {
|
|
7023
|
+
summary = summarize(analysis, divisor);
|
|
7024
|
+
}
|
|
7025
|
+
return summary;
|
|
7026
|
+
}
|
|
7027
|
+
function wouldUpload(analysis, force = false) {
|
|
7028
|
+
return analysis.blockers.every((blocker) => blocker.overridable && force);
|
|
7029
|
+
}
|
|
7030
|
+
|
|
7031
|
+
// src/lib/pushReport.ts
|
|
7032
|
+
init_branding();
|
|
7033
|
+
var EXCLUDE_REASON = {
|
|
7034
|
+
always_excluded_dir: "never uploaded",
|
|
7035
|
+
gitignore: ".gitignore",
|
|
7036
|
+
env_file: "env file, never uploaded",
|
|
7037
|
+
os_junk: "system file",
|
|
7038
|
+
symlink: "symbolic link",
|
|
7039
|
+
not_a_regular_file: "not a regular file"
|
|
7040
|
+
};
|
|
7041
|
+
var SHOWN_FILES = 8;
|
|
7042
|
+
var SHOWN_GROUPS = 6;
|
|
7043
|
+
var SHOWN_EXCLUDED = 12;
|
|
7044
|
+
var count = (n, singular, pluralForm = `${singular}s`) => `${n.toLocaleString("en-US")} ${n === 1 ? singular : pluralForm}`;
|
|
7045
|
+
var more = (total, shown, what) => total > shown ? [paint.dim(` \u2026 and ${count(total - shown, `more ${what}`, `more ${what}s`)}`)] : [];
|
|
7046
|
+
var bullet = (message) => message.split("\n").map((line, index) => index === 0 ? ` \u2022 ${line}` : ` ${line.trimStart()}`);
|
|
7047
|
+
var sizeColumn = (bytes2) => formatBytes(bytes2).padStart(9);
|
|
7048
|
+
var fileLine = (file) => ` ${sizeColumn(file.bytes)} ${file.path}` + paint.dim(` (${file.zip_bytes_estimated ? "up to " : ""}${formatBytes(file.zip_bytes)} compressed)`);
|
|
7049
|
+
function headline(analysis) {
|
|
7050
|
+
if (analysis.mode === "dist") return "prebuilt upload, served as it is (no cloud build)";
|
|
7051
|
+
if (analysis.framework === "vanilla") return "source push, static site without a build step";
|
|
7052
|
+
if (analysis.framework === "unknown" || analysis.framework === null) return "source push";
|
|
7053
|
+
return `source push, built in the cloud as ${frameworkDisplayName(analysis.framework)}`;
|
|
7054
|
+
}
|
|
7055
|
+
function renderAnalysis(analysis, options = {}) {
|
|
7056
|
+
const { totals, limits: limits2 } = analysis;
|
|
7057
|
+
const lines = [];
|
|
7058
|
+
const section = (title) => {
|
|
7059
|
+
lines.push("", paint.bold(title));
|
|
7060
|
+
};
|
|
7061
|
+
lines.push(paint.bold("Upload analysis") + paint.dim(`: ${headline(analysis)}`));
|
|
7062
|
+
if (analysis.root) lines.push(paint.dim(analysis.root));
|
|
7063
|
+
if (totals.files > 0) {
|
|
7064
|
+
lines.push(
|
|
7065
|
+
`${count(totals.files, "file")} \xB7 ${formatBytes(totals.bytes)} \xB7 ${totals.zip_bytes_exact ? "" : "up to "}${formatBytes(totals.zip_bytes)} compressed`
|
|
7066
|
+
);
|
|
7067
|
+
}
|
|
7068
|
+
lines.push(paint.dim(
|
|
7069
|
+
`Limits per push: ${formatBytes(limits2.max_zip_bytes, "down")} compressed \xB7 ${formatBytes(limits2.max_unpacked_bytes)} of files \xB7 ${limits2.max_files.toLocaleString("en-US")} files`
|
|
7070
|
+
));
|
|
7071
|
+
if (analysis.blockers.length > 0) {
|
|
7072
|
+
lines.push("", `${sym.fail} ${paint.bold("This push cannot go through as it is:")}`);
|
|
7073
|
+
for (const blocker of analysis.blockers) {
|
|
7074
|
+
lines.push(...bullet(blocker.message + (blocker.overridable ? paint.dim(" [--force uploads anyway]") : "")));
|
|
7075
|
+
}
|
|
7076
|
+
}
|
|
7077
|
+
if (analysis.warnings.length > 0) {
|
|
7078
|
+
section("Warnings");
|
|
7079
|
+
for (const warning of analysis.warnings) lines.push(...bullet(warning.message));
|
|
7080
|
+
}
|
|
7081
|
+
if (analysis.entry.required && totals.files > 0) {
|
|
7082
|
+
section("Start page");
|
|
7083
|
+
if (analysis.entry.found) {
|
|
7084
|
+
lines.push(` ${analysis.entry.path}`);
|
|
7085
|
+
} else {
|
|
7086
|
+
lines.push(" missing \u2014 the platform serves index.html from the top level");
|
|
7087
|
+
if (analysis.entry.candidates.length > 0) {
|
|
7088
|
+
lines.push(" HTML files found:");
|
|
7089
|
+
for (const candidate of analysis.entry.candidates.slice(0, SHOWN_FILES)) {
|
|
7090
|
+
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})`) : "")
|
|
7092
|
+
);
|
|
7093
|
+
}
|
|
7094
|
+
lines.push(...more(analysis.entry.candidates.length, SHOWN_FILES, "HTML file"));
|
|
7095
|
+
} else {
|
|
7096
|
+
lines.push(" no HTML file in the upload");
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
const sizeMatters = analysis.blockers.some((b) => ["zip_too_large", "unpacked_too_large", "too_many_files"].includes(b.code)) || analysis.warnings.some((w) => w.code === "zip_size_unknown");
|
|
7101
|
+
if (totals.files > 0 && (sizeMatters || options.dryRun)) {
|
|
7102
|
+
section("Largest files");
|
|
7103
|
+
for (const file of analysis.largest_files.slice(0, SHOWN_FILES)) lines.push(fileLine(file));
|
|
7104
|
+
lines.push(...more(totals.files, Math.min(SHOWN_FILES, analysis.largest_files.length), "file"));
|
|
7105
|
+
section("By type");
|
|
7106
|
+
for (const group of analysis.by_class.slice(0, SHOWN_GROUPS)) {
|
|
7107
|
+
lines.push(
|
|
7108
|
+
` ${sizeColumn(group.bytes)} ${group.name.padEnd(9)} ${count(group.files, "file")}` + paint.dim(` (${formatBytes(group.zip_bytes)} compressed)`)
|
|
7109
|
+
);
|
|
7110
|
+
}
|
|
7111
|
+
if (analysis.duplicates.length > 0) {
|
|
7112
|
+
section("Identical content");
|
|
7113
|
+
for (const group of analysis.duplicates.slice(0, SHOWN_GROUPS)) {
|
|
7114
|
+
lines.push(
|
|
7115
|
+
` ${group.paths.length} \xD7 ${formatBytes(group.bytes)} ${group.paths.join(" = ")}` + paint.dim(` (the extra ${group.paths.length === 2 ? "copy is" : "copies are"} ${formatBytes(group.redundant_zip_bytes)} of the compressed upload)`)
|
|
6286
7116
|
);
|
|
6287
|
-
continue;
|
|
6288
7117
|
}
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
7118
|
+
lines.push(...more(analysis.duplicates.length, SHOWN_GROUPS, "group"));
|
|
7119
|
+
}
|
|
7120
|
+
if (analysis.embedded_media.length > 0) {
|
|
7121
|
+
section("Media embedded in text files");
|
|
7122
|
+
const sizeOf = new Map(analysis.files.map((file) => [file.path, file.bytes]));
|
|
7123
|
+
for (const item of analysis.embedded_media.slice(0, SHOWN_GROUPS)) {
|
|
7124
|
+
const fileBytes = sizeOf.get(item.path);
|
|
7125
|
+
lines.push(
|
|
7126
|
+
` ${item.path}: ${count(item.references, "embedded file")} (data: URLs), ${formatBytes(item.base64_chars)}` + (fileBytes ? ` of its ${formatBytes(fileBytes)}` : "")
|
|
6294
7127
|
);
|
|
6295
7128
|
}
|
|
7129
|
+
lines.push(...more(analysis.embedded_media.length, SHOWN_GROUPS, "file"));
|
|
6296
7130
|
}
|
|
6297
7131
|
}
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
`.agentful/backend.json declares action "${name}" \u2014 names must match [a-z][a-z0-9_-]{0,62} (the filename without .js).`
|
|
6304
|
-
);
|
|
6305
|
-
continue;
|
|
6306
|
-
}
|
|
6307
|
-
if (!presentFiles.has(`${name}.js`)) {
|
|
6308
|
-
issues.push(
|
|
6309
|
-
`.agentful/backend.json declares action "${name}" but .server/actions/${name}.js does not exist \u2014 create the file or remove the declaration entry.`
|
|
7132
|
+
if (options.dryRun && analysis.excluded.entries.length > 0) {
|
|
7133
|
+
section("Not uploaded");
|
|
7134
|
+
for (const entry of analysis.excluded.entries.slice(0, SHOWN_EXCLUDED)) {
|
|
7135
|
+
lines.push(
|
|
7136
|
+
` ${entry.path}${entry.kind === "dir" ? "/" : ""}` + paint.dim(` (${EXCLUDE_REASON[entry.reason] ?? entry.reason}${entry.bytes === void 0 ? "" : `, ${formatBytes(entry.bytes)}`})`)
|
|
6310
7137
|
);
|
|
6311
7138
|
}
|
|
7139
|
+
lines.push(...more(analysis.excluded.entries.length + analysis.excluded.omitted, SHOWN_EXCLUDED, "entry"));
|
|
6312
7140
|
}
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
const
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
`.server/actions/${file} is not declared in .agentful/backend.json \u2014 it will deploy, but \`agentful backend\` and the Backend tab won't know about it. Add "${name}" to the actions list.`
|
|
6319
|
-
);
|
|
7141
|
+
if (analysis.suggestions.length > 0) {
|
|
7142
|
+
section("What you can do");
|
|
7143
|
+
for (const suggestion of analysis.suggestions) {
|
|
7144
|
+
lines.push(...bullet(suggestion.message));
|
|
7145
|
+
if (suggestion.command) lines.push(` ${paint.accent(suggestion.command)}`);
|
|
6320
7146
|
}
|
|
6321
7147
|
}
|
|
6322
|
-
if (
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
7148
|
+
if (options.dryRun) {
|
|
7149
|
+
lines.push("");
|
|
7150
|
+
if (wouldUpload(analysis, options.force)) {
|
|
7151
|
+
lines.push(`${sym.ok} Nothing blocks this push.` + paint.dim(" Dry run: nothing was uploaded, no project was created."));
|
|
7152
|
+
} else {
|
|
7153
|
+
lines.push(`${sym.fail} ${paint.error("agentful push would stop here.")}` + paint.dim(" Dry run: nothing was uploaded, no project was created."));
|
|
7154
|
+
}
|
|
6326
7155
|
}
|
|
6327
|
-
return
|
|
7156
|
+
return lines;
|
|
6328
7157
|
}
|
|
6329
7158
|
|
|
6330
7159
|
// src/lib/backendSync.ts
|
|
@@ -6493,8 +7322,8 @@ function describeBackendState(state) {
|
|
|
6493
7322
|
}
|
|
6494
7323
|
|
|
6495
7324
|
// src/lib/backendConsent.ts
|
|
6496
|
-
var
|
|
6497
|
-
var
|
|
7325
|
+
var import_node_fs9 = require("fs");
|
|
7326
|
+
var import_node_path11 = require("path");
|
|
6498
7327
|
var import_promises2 = require("readline/promises");
|
|
6499
7328
|
function declaresManagedDatabase(decl) {
|
|
6500
7329
|
if (!decl) return false;
|
|
@@ -6520,10 +7349,10 @@ function consentQuestion(decl) {
|
|
|
6520
7349
|
'It is the same switch as the Backend tab; this push then applies the declaration. A "no" is remembered for this project (`agentful push --yes` or `agentful backend` enables it later).'
|
|
6521
7350
|
].join("\n");
|
|
6522
7351
|
}
|
|
6523
|
-
var consentFile = () => (0,
|
|
7352
|
+
var consentFile = () => (0, import_node_path11.join)(configDir(), "backend-consent.json");
|
|
6524
7353
|
function readConsentFile() {
|
|
6525
7354
|
try {
|
|
6526
|
-
const raw = JSON.parse((0,
|
|
7355
|
+
const raw = JSON.parse((0, import_node_fs9.readFileSync)(consentFile(), "utf8"));
|
|
6527
7356
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
6528
7357
|
} catch {
|
|
6529
7358
|
return {};
|
|
@@ -6535,15 +7364,15 @@ function hasRememberedDecline(projectId) {
|
|
|
6535
7364
|
function rememberDecline(projectId) {
|
|
6536
7365
|
const all = readConsentFile();
|
|
6537
7366
|
all[projectId] = { declined_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6538
|
-
(0,
|
|
6539
|
-
(0,
|
|
7367
|
+
(0, import_node_fs9.mkdirSync)(configDir(), { recursive: true });
|
|
7368
|
+
(0, import_node_fs9.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
|
|
6540
7369
|
}
|
|
6541
7370
|
function forgetDecline(projectId) {
|
|
6542
|
-
if (!(0,
|
|
7371
|
+
if (!(0, import_node_fs9.existsSync)(consentFile())) return;
|
|
6543
7372
|
const all = readConsentFile();
|
|
6544
7373
|
if (!(projectId in all)) return;
|
|
6545
7374
|
delete all[projectId];
|
|
6546
|
-
(0,
|
|
7375
|
+
(0, import_node_fs9.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
|
|
6547
7376
|
}
|
|
6548
7377
|
async function enableManagedDatabase(auth, userId, projectId) {
|
|
6549
7378
|
try {
|
|
@@ -6666,25 +7495,60 @@ function brandPreviewUrl(previewUrl, projectId) {
|
|
|
6666
7495
|
}
|
|
6667
7496
|
}
|
|
6668
7497
|
async function pushCommand(opts) {
|
|
6669
|
-
|
|
7498
|
+
if (opts.json && !opts.dryRun) {
|
|
7499
|
+
throw new ApiError(
|
|
7500
|
+
0,
|
|
7501
|
+
"invalid_options",
|
|
7502
|
+
"--json belongs to --dry-run: `agentful push --dry-run --json` prints the upload analysis and uploads nothing."
|
|
7503
|
+
);
|
|
7504
|
+
}
|
|
7505
|
+
if (opts.summary && !opts.json) {
|
|
7506
|
+
throw new ApiError(
|
|
7507
|
+
0,
|
|
7508
|
+
"invalid_options",
|
|
7509
|
+
"--summary belongs to --dry-run --json: it prints the analysis without the list of every file."
|
|
7510
|
+
);
|
|
7511
|
+
}
|
|
7512
|
+
if (!opts.json) console.log(banner());
|
|
6670
7513
|
const mode = opts.prebuilt ? "dist" : "source";
|
|
6671
|
-
const
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
7514
|
+
const cwd = process.cwd();
|
|
7515
|
+
let rootDir = cwd;
|
|
7516
|
+
let analysis;
|
|
7517
|
+
try {
|
|
7518
|
+
if (opts.prebuilt) rootDir = detectDistDir(cwd, opts.dir);
|
|
7519
|
+
analysis = analyzePush({ rootDir, mode, projectDir: cwd });
|
|
7520
|
+
} catch (err2) {
|
|
7521
|
+
if (!opts.dryRun || !(err2 instanceof ApiError)) throw err2;
|
|
7522
|
+
analysis = failedAnalysis(mode, cwd, { code: err2.code, message: err2.message });
|
|
7523
|
+
}
|
|
7524
|
+
if (opts.dryRun) {
|
|
7525
|
+
if (opts.json) process.stdout.write(printAnalysis(opts.summary ? summarizeAnalysis(analysis) : analysis));
|
|
7526
|
+
else for (const line of renderAnalysis(analysis, { force: opts.force, dryRun: true })) console.log(line);
|
|
7527
|
+
if (!wouldUpload(analysis, opts.force)) process.exitCode = 1;
|
|
7528
|
+
return;
|
|
7529
|
+
}
|
|
7530
|
+
const overridable = analysis.blockers.filter((blocker) => blocker.overridable);
|
|
7531
|
+
if (analysis.blockers.length > overridable.length) {
|
|
7532
|
+
for (const line of renderAnalysis(analysis, { force: opts.force })) console.log(line);
|
|
7533
|
+
console.log("");
|
|
7534
|
+
throw new ApiError(
|
|
7535
|
+
0,
|
|
7536
|
+
"push_blocked",
|
|
7537
|
+
"Push stopped before sign-in: nothing was uploaded and no project was created. `agentful push --dry-run` shows this analysis again."
|
|
7538
|
+
);
|
|
7539
|
+
}
|
|
7540
|
+
for (const warning of analysis.warnings) ui.warn(warning.message);
|
|
7541
|
+
if (overridable.length > 0) {
|
|
7542
|
+
const issues = overridable.map((blocker) => blocker.message);
|
|
7543
|
+
if (opts.force) {
|
|
7544
|
+
ui.warn(`Preflight found ${issues.length} blocking issue(s) \u2014 continuing because of --force:`);
|
|
7545
|
+
for (const issue of issues) ui.info(issue);
|
|
7546
|
+
} else {
|
|
7547
|
+
throw new ApiError(
|
|
7548
|
+
0,
|
|
7549
|
+
"preflight_failed",
|
|
7550
|
+
"This project cannot run on the platform as pushed:\n" + issues.map((issue) => `\u2022 ${issue}`).join("\n") + "\nPush with --force to attempt the cloud build anyway."
|
|
7551
|
+
);
|
|
6688
7552
|
}
|
|
6689
7553
|
}
|
|
6690
7554
|
const auth = await ensureAuth();
|
|
@@ -6703,17 +7567,10 @@ async function pushCommand(opts) {
|
|
|
6703
7567
|
);
|
|
6704
7568
|
}
|
|
6705
7569
|
ui.step(mode === "source" ? "Packing source tree\u2026" : `Packing built output (${rootDir})\u2026`);
|
|
6706
|
-
const files = collectFiles(rootDir, mode);
|
|
6707
|
-
const zip = zipToBase64(files);
|
|
6708
|
-
ui.info(`${zip.count} files, ${(zip.bytes / 1024).toFixed(0)} KB compressed`);
|
|
6709
|
-
if (zip.bytes > MAX_ZIP_UPLOAD_BYTES) {
|
|
6710
|
-
throw new ApiError(
|
|
6711
|
-
0,
|
|
6712
|
-
"zip_too_large",
|
|
6713
|
-
`Packed ${(zip.bytes / 1024 / 1024).toFixed(1)} MB compressed \u2014 the platform caps uploads at ${Math.floor(MAX_ZIP_UPLOAD_BYTES / (1024 * 1024))} MB. Remove large media or generated files from the source tree (node_modules, dist and .git are excluded automatically); large assets belong in the platform asset flow.`
|
|
6714
|
-
);
|
|
6715
|
-
}
|
|
6716
7570
|
const declaration = mode === "source" ? readBackendDeclaration() : null;
|
|
7571
|
+
const files = collectFiles(rootDir, mode);
|
|
7572
|
+
const zip = zipToBase64(files, maxZipBytesForUpload(mode, declaration));
|
|
7573
|
+
ui.info(`${zip.count} files, ${(zip.bytes / 1024).toFixed(0)} KiB compressed`);
|
|
6717
7574
|
if (declaration) {
|
|
6718
7575
|
const consent = await ensureDatabaseConsent({
|
|
6719
7576
|
auth,
|
|
@@ -6734,12 +7591,7 @@ async function pushCommand(opts) {
|
|
|
6734
7591
|
`${API_URL}/api/workspaces/${userId}/${projectId}/source-zip`,
|
|
6735
7592
|
{
|
|
6736
7593
|
token: auth.pb_token,
|
|
6737
|
-
body:
|
|
6738
|
-
zip_base64: zip.base64,
|
|
6739
|
-
mode,
|
|
6740
|
-
clean: true,
|
|
6741
|
-
...declaration ? { backend_declaration: declaration } : {}
|
|
6742
|
-
},
|
|
7594
|
+
body: uploadRequestBody(zip.base64, mode, declaration),
|
|
6743
7595
|
timeoutMs: 12e4,
|
|
6744
7596
|
retry: platformRetry
|
|
6745
7597
|
}
|
|
@@ -6919,15 +7771,15 @@ async function openCommand() {
|
|
|
6919
7771
|
// src/commands/tui.ts
|
|
6920
7772
|
var import_node_child_process6 = require("child_process");
|
|
6921
7773
|
var import_promises4 = require("readline/promises");
|
|
6922
|
-
var
|
|
7774
|
+
var import_node_path22 = require("path");
|
|
6923
7775
|
|
|
6924
7776
|
// src/lib/binWrapper.ts
|
|
6925
|
-
var
|
|
6926
|
-
var
|
|
7777
|
+
var import_node_fs10 = require("fs");
|
|
7778
|
+
var import_node_path12 = require("path");
|
|
6927
7779
|
var import_node_os3 = require("os");
|
|
6928
7780
|
function ensureCliOnPath() {
|
|
6929
|
-
const binDir = (0,
|
|
6930
|
-
(0,
|
|
7781
|
+
const binDir = (0, import_node_path12.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
|
|
7782
|
+
(0, import_node_fs10.mkdirSync)(binDir, { recursive: true });
|
|
6931
7783
|
const entry = process.argv[1];
|
|
6932
7784
|
if (!entry) return binDir;
|
|
6933
7785
|
const restore = (name) => {
|
|
@@ -6936,8 +7788,8 @@ function ensureCliOnPath() {
|
|
|
6936
7788
|
` : `export ${name}=${JSON.stringify(original)}
|
|
6937
7789
|
`;
|
|
6938
7790
|
};
|
|
6939
|
-
const wrapper = (0,
|
|
6940
|
-
(0,
|
|
7791
|
+
const wrapper = (0, import_node_path12.join)(binDir, "agentful");
|
|
7792
|
+
(0, import_node_fs10.writeFileSync)(
|
|
6941
7793
|
wrapper,
|
|
6942
7794
|
`#!/bin/sh
|
|
6943
7795
|
# Generated by the Agentful CLI so the coding agent can run \`agentful \u2026\`.
|
|
@@ -6947,7 +7799,7 @@ function ensureCliOnPath() {
|
|
|
6947
7799
|
"utf8"
|
|
6948
7800
|
);
|
|
6949
7801
|
try {
|
|
6950
|
-
(0,
|
|
7802
|
+
(0, import_node_fs10.chmodSync)(wrapper, 493);
|
|
6951
7803
|
} catch {
|
|
6952
7804
|
}
|
|
6953
7805
|
return binDir;
|
|
@@ -6957,9 +7809,9 @@ function ensureCliOnPath() {
|
|
|
6957
7809
|
init_branding();
|
|
6958
7810
|
|
|
6959
7811
|
// src/lib/engine.ts
|
|
6960
|
-
var
|
|
6961
|
-
var
|
|
6962
|
-
var
|
|
7812
|
+
var import_node_fs11 = require("fs");
|
|
7813
|
+
var import_node_crypto2 = require("crypto");
|
|
7814
|
+
var import_node_path13 = require("path");
|
|
6963
7815
|
var import_node_os4 = require("os");
|
|
6964
7816
|
var import_node_child_process3 = require("child_process");
|
|
6965
7817
|
var import_node_stream = require("stream");
|
|
@@ -7004,8 +7856,8 @@ function platformKey() {
|
|
|
7004
7856
|
);
|
|
7005
7857
|
}
|
|
7006
7858
|
function cacheDir(version) {
|
|
7007
|
-
const base = process.env.XDG_CACHE_HOME || (0,
|
|
7008
|
-
return (0,
|
|
7859
|
+
const base = process.env.XDG_CACHE_HOME || (0, import_node_path13.join)((0, import_node_os4.homedir)(), ".cache");
|
|
7860
|
+
return (0, import_node_path13.join)(base, "agentful", "engine", version);
|
|
7009
7861
|
}
|
|
7010
7862
|
async function fetchManifest() {
|
|
7011
7863
|
try {
|
|
@@ -7015,8 +7867,8 @@ async function fetchManifest() {
|
|
|
7015
7867
|
}
|
|
7016
7868
|
}
|
|
7017
7869
|
async function sha256File(path) {
|
|
7018
|
-
const hash = (0,
|
|
7019
|
-
await (0, import_promises3.pipeline)((0,
|
|
7870
|
+
const hash = (0, import_node_crypto2.createHash)("sha256");
|
|
7871
|
+
await (0, import_promises3.pipeline)((0, import_node_fs11.createReadStream)(path), hash);
|
|
7020
7872
|
return hash.digest("hex");
|
|
7021
7873
|
}
|
|
7022
7874
|
async function ensureEngine() {
|
|
@@ -7025,12 +7877,12 @@ async function ensureEngine() {
|
|
|
7025
7877
|
noticeIfOutdated(manifest);
|
|
7026
7878
|
const version = manifest?.engine_version || FALLBACK_ENGINE_VERSION;
|
|
7027
7879
|
const dir = cacheDir(version);
|
|
7028
|
-
const binPath = (0,
|
|
7029
|
-
if ((0,
|
|
7880
|
+
const binPath = (0, import_node_path13.join)(dir, ENGINE_BINARY);
|
|
7881
|
+
if ((0, import_node_fs11.existsSync)(binPath)) return binPath;
|
|
7030
7882
|
const artifact = manifest?.artifacts?.[key];
|
|
7031
7883
|
const url = artifact?.url || `${APP_URL}/cli/engine/${version}/${key}${key.startsWith("darwin") ? ".zip" : ".tar.gz"}`;
|
|
7032
|
-
(0,
|
|
7033
|
-
const archivePath = (0,
|
|
7884
|
+
(0, import_node_fs11.mkdirSync)(dir, { recursive: true });
|
|
7885
|
+
const archivePath = (0, import_node_path13.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
|
|
7034
7886
|
ui.step(`Preparing the ${brand.displayName} engine (first run only)\u2026`);
|
|
7035
7887
|
const resp = await fetch(url, {
|
|
7036
7888
|
redirect: "follow",
|
|
@@ -7044,11 +7896,11 @@ async function ensureEngine() {
|
|
|
7044
7896
|
`Could not download the engine (HTTP ${resp.status}). Check your connection and try again.`
|
|
7045
7897
|
);
|
|
7046
7898
|
}
|
|
7047
|
-
await (0, import_promises3.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0,
|
|
7899
|
+
await (0, import_promises3.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs11.createWriteStream)(archivePath));
|
|
7048
7900
|
if (artifact?.sha256) {
|
|
7049
7901
|
const actual = await sha256File(archivePath);
|
|
7050
7902
|
if (actual !== artifact.sha256) {
|
|
7051
|
-
(0,
|
|
7903
|
+
(0, import_node_fs11.rmSync)(archivePath, { force: true });
|
|
7052
7904
|
throw new ApiError(
|
|
7053
7905
|
0,
|
|
7054
7906
|
"engine_checksum_mismatch",
|
|
@@ -7057,7 +7909,7 @@ async function ensureEngine() {
|
|
|
7057
7909
|
}
|
|
7058
7910
|
}
|
|
7059
7911
|
const extract = archivePath.endsWith(".zip") ? (0, import_node_child_process3.spawnSync)("unzip", ["-oq", archivePath], { cwd: dir, stdio: "ignore" }) : (0, import_node_child_process3.spawnSync)("tar", ["-xzf", archivePath], { cwd: dir, stdio: "ignore" });
|
|
7060
|
-
(0,
|
|
7912
|
+
(0, import_node_fs11.rmSync)(archivePath, { force: true });
|
|
7061
7913
|
if (extract.status !== 0) {
|
|
7062
7914
|
throw new ApiError(
|
|
7063
7915
|
0,
|
|
@@ -7065,24 +7917,24 @@ async function ensureEngine() {
|
|
|
7065
7917
|
`Could not unpack the engine (needs ${archivePath.endsWith(".zip") ? "unzip" : "tar"} on PATH).`
|
|
7066
7918
|
);
|
|
7067
7919
|
}
|
|
7068
|
-
const extracted = (0,
|
|
7069
|
-
if ((0,
|
|
7070
|
-
if (!(0,
|
|
7920
|
+
const extracted = (0, import_node_path13.join)(dir, "opencode");
|
|
7921
|
+
if ((0, import_node_fs11.existsSync)(extracted) && !(0, import_node_fs11.existsSync)(binPath)) (0, import_node_fs11.renameSync)(extracted, binPath);
|
|
7922
|
+
if (!(0, import_node_fs11.existsSync)(binPath)) {
|
|
7071
7923
|
throw new ApiError(0, "engine_extract_failed", "Engine archive did not contain the expected binary.");
|
|
7072
7924
|
}
|
|
7073
|
-
(0,
|
|
7925
|
+
(0, import_node_fs11.chmodSync)(binPath, 493);
|
|
7074
7926
|
ui.ok(`Engine ready.`);
|
|
7075
7927
|
console.log(paint.dim(" powered by opencode (MIT) \xB7 run `agentful licenses` for details"));
|
|
7076
7928
|
return binPath;
|
|
7077
7929
|
}
|
|
7078
7930
|
|
|
7079
7931
|
// src/lib/gatewayToken.ts
|
|
7080
|
-
var
|
|
7081
|
-
var
|
|
7082
|
-
var cachePath = () => (0,
|
|
7932
|
+
var import_node_fs12 = require("fs");
|
|
7933
|
+
var import_node_path14 = require("path");
|
|
7934
|
+
var cachePath = () => (0, import_node_path14.join)(configDir(), "gateway.json");
|
|
7083
7935
|
async function ensureGatewaySession(auth, projectId) {
|
|
7084
7936
|
try {
|
|
7085
|
-
const cached = JSON.parse((0,
|
|
7937
|
+
const cached = JSON.parse((0, import_node_fs12.readFileSync)(cachePath(), "utf8"));
|
|
7086
7938
|
if (cached?.token && cached.expires_at - Date.now() / 1e3 > 3600) {
|
|
7087
7939
|
return cached;
|
|
7088
7940
|
}
|
|
@@ -7093,18 +7945,18 @@ async function ensureGatewaySession(auth, projectId) {
|
|
|
7093
7945
|
body: projectId ? { project_id: projectId } : {},
|
|
7094
7946
|
timeoutMs: 3e4
|
|
7095
7947
|
});
|
|
7096
|
-
(0,
|
|
7097
|
-
(0,
|
|
7948
|
+
(0, import_node_fs12.mkdirSync)(configDir(), { recursive: true });
|
|
7949
|
+
(0, import_node_fs12.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
|
|
7098
7950
|
try {
|
|
7099
|
-
(0,
|
|
7951
|
+
(0, import_node_fs12.chmodSync)(cachePath(), 384);
|
|
7100
7952
|
} catch {
|
|
7101
7953
|
}
|
|
7102
7954
|
return res;
|
|
7103
7955
|
}
|
|
7104
7956
|
|
|
7105
7957
|
// src/lib/engineConfig.ts
|
|
7106
|
-
var
|
|
7107
|
-
var
|
|
7958
|
+
var import_node_fs14 = require("fs");
|
|
7959
|
+
var import_node_path16 = require("path");
|
|
7108
7960
|
var import_node_os5 = require("os");
|
|
7109
7961
|
|
|
7110
7962
|
// src/branding/agentful-theme.json
|
|
@@ -7199,80 +8051,11 @@ var agentful_theme_default = {
|
|
|
7199
8051
|
|
|
7200
8052
|
// src/branding/commands.ts
|
|
7201
8053
|
init_branding();
|
|
7202
|
-
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.';
|
|
7203
|
-
var SLASH_COMMANDS = {
|
|
7204
|
-
setup: {
|
|
7205
|
-
description: `${brand.displayName}: create or link the cloud project for this directory`,
|
|
7206
|
-
template: 'Check whether .agentful/project.json exists. If it does, report the linked project title and id and stop. Otherwise run `agentful init --title "<name>"` using the bash tool, where <name> is $ARGUMENTS if I gave one, otherwise the current directory name. Report what it created and mention the title I can change it to. Do not modify any other project files.'
|
|
7207
|
-
},
|
|
7208
|
-
push: {
|
|
7209
|
-
description: `${brand.displayName}: deploy this project and get a preview URL`,
|
|
7210
|
-
template: "Run `agentful push` from the project root using the bash tool. It creates the project automatically if this directory is not linked yet. Stream what it prints, and when it finishes report the preview URL on its own line. If it fails: quote the printed error verbatim, and if the failure happened in the cloud (not in the pre-upload preflight), 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. Do not modify project files unless I ask."
|
|
7211
|
-
},
|
|
7212
|
-
"build-status": {
|
|
7213
|
-
description: `${brand.displayName}: show the last cloud build result`,
|
|
7214
|
-
template: "Run `agentful build-status` from the project root using the bash tool and report what it prints: status, phase, framework, when it was recorded, and the error/details verbatim if it failed. That record is the only source of truth about the cloud build \u2014 do not speculate beyond it. " + NEEDS_A_PUSH
|
|
7215
|
-
},
|
|
7216
|
-
backend: {
|
|
7217
|
-
description: `${brand.displayName}: open the Backend tab (managed DB + server actions)`,
|
|
7218
|
-
template: "Run `agentful backend` from the project root using the bash tool and report the URL it opened. If it prints a declared backend (managed DB / server actions), summarize that and remind me that declared-in-code serves nothing until enabled in that tab. " + NEEDS_A_PUSH
|
|
7219
|
-
},
|
|
7220
|
-
preview: {
|
|
7221
|
-
description: `${brand.displayName}: open the live preview in the browser`,
|
|
7222
|
-
template: "Run `agentful open` from the project root using the bash tool and report the URL it opened. Note that `open` neither builds nor uploads. " + NEEDS_A_PUSH
|
|
7223
|
-
},
|
|
7224
|
-
publish: {
|
|
7225
|
-
description: `${brand.displayName}: publish on a public subdomain`,
|
|
7226
|
-
template: "Ask me for the subdomain if I did not provide one in $ARGUMENTS, then run `agentful publish <subdomain>` from the project root using the bash tool and report the public URL. " + NEEDS_A_PUSH
|
|
7227
|
-
},
|
|
7228
|
-
share: {
|
|
7229
|
-
description: `${brand.displayName}: make the live preview public (or private again)`,
|
|
7230
|
-
template: "Run `agentful share` from the project root using the bash tool (add `--private` if $ARGUMENTS says private) and report the resulting visibility and URL. " + NEEDS_A_PUSH
|
|
7231
|
-
},
|
|
7232
|
-
status: {
|
|
7233
|
-
description: `${brand.displayName}: show the linked project and account`,
|
|
7234
|
-
template: "Run `agentful whoami` using the bash tool. Then read .agentful/project.json and report the linked project title and id, plus the preview URL https://preview-<projectId>.agentful.page/. If the file does not exist, say that this directory is not linked yet and that `/setup` creates the link."
|
|
7235
|
-
}
|
|
7236
|
-
};
|
|
7237
|
-
function commandMarkdown(name) {
|
|
7238
|
-
const cmd = SLASH_COMMANDS[name];
|
|
7239
|
-
const description = JSON.stringify(cmd.description);
|
|
7240
|
-
return `---
|
|
7241
|
-
description: ${description}
|
|
7242
|
-
agent: ${cmd.agent ?? "agentful-build"}
|
|
7243
|
-
---
|
|
7244
|
-
|
|
7245
|
-
${cmd.template}
|
|
7246
|
-
`;
|
|
7247
|
-
}
|
|
7248
|
-
|
|
7249
|
-
// src/lib/tools.ts
|
|
7250
|
-
var import_node_fs12 = require("fs");
|
|
7251
|
-
var import_node_path14 = require("path");
|
|
7252
|
-
function assetsDir() {
|
|
7253
|
-
const here = (0, import_node_path14.dirname)(process.argv[1] || "");
|
|
7254
|
-
for (const candidate of [(0, import_node_path14.join)(here, "..", "assets"), (0, import_node_path14.join)(here, "assets")]) {
|
|
7255
|
-
if ((0, import_node_fs12.existsSync)((0, import_node_path14.join)(candidate, "tools", "imagegen.ts"))) return candidate;
|
|
7256
|
-
}
|
|
7257
|
-
return null;
|
|
7258
|
-
}
|
|
7259
|
-
function installEngineTools(configDir2) {
|
|
7260
|
-
const assets = assetsDir();
|
|
7261
|
-
if (!assets) return false;
|
|
7262
|
-
const target = (0, import_node_path14.join)(configDir2, "tools");
|
|
7263
|
-
(0, import_node_fs12.mkdirSync)(target, { recursive: true });
|
|
7264
|
-
try {
|
|
7265
|
-
(0, import_node_fs12.cpSync)((0, import_node_path14.join)(assets, "tools"), target, { recursive: true });
|
|
7266
|
-
return true;
|
|
7267
|
-
} catch {
|
|
7268
|
-
return false;
|
|
7269
|
-
}
|
|
7270
|
-
}
|
|
7271
8054
|
|
|
7272
8055
|
// src/lib/cloudTarget.ts
|
|
7273
8056
|
var CLOUD_INSTRUCTIONS_FILENAME = "AGENTFUL_CLOUD.md";
|
|
7274
8057
|
var FRAMEWORKS = contract_default.frameworks;
|
|
7275
|
-
function
|
|
8058
|
+
function frameworkDisplayName2(framework) {
|
|
7276
8059
|
if (framework === "unknown") return "no supported framework";
|
|
7277
8060
|
return FRAMEWORKS.find((f) => f.id === framework)?.display_name ?? framework;
|
|
7278
8061
|
}
|
|
@@ -7285,7 +8068,7 @@ function frameworkTable() {
|
|
|
7285
8068
|
return rows.join("\n");
|
|
7286
8069
|
}
|
|
7287
8070
|
function sessionLine(framework) {
|
|
7288
|
-
const name =
|
|
8071
|
+
const name = frameworkDisplayName2(framework);
|
|
7289
8072
|
switch (framework) {
|
|
7290
8073
|
case "nextjs":
|
|
7291
8074
|
return `Detected framework in this directory: **${name}** \u2014 next.config must keep \`output: 'export'\`; no route handlers/middleware/server actions.`;
|
|
@@ -7343,7 +8126,7 @@ function buildEnvList() {
|
|
|
7343
8126
|
var bytes = (n) => n >= 1024 * 1024 ? `${n / (1024 * 1024)} MB` : `${n / 1024} KB`;
|
|
7344
8127
|
function serverSideSection() {
|
|
7345
8128
|
const tracks = contract_default.server_side;
|
|
7346
|
-
const track = (key, heading,
|
|
8129
|
+
const track = (key, heading, limits2) => {
|
|
7347
8130
|
const t = tracks[key];
|
|
7348
8131
|
const out = [
|
|
7349
8132
|
`### ${heading} ${t.label}`,
|
|
@@ -7357,7 +8140,7 @@ function serverSideSection() {
|
|
|
7357
8140
|
"Does NOT offer \u2014 name the limit and offer the way out, never promise it:",
|
|
7358
8141
|
...t.not_offered.map((n) => `- **${n.what}.** ${n.why}. Way out: ${n.workaround}.`)
|
|
7359
8142
|
];
|
|
7360
|
-
if (
|
|
8143
|
+
if (limits2) out.push("", `Limits: ${limits2}`);
|
|
7361
8144
|
if (t.limits_note) out.push("", t.limits_note);
|
|
7362
8145
|
return out.join("\n");
|
|
7363
8146
|
};
|
|
@@ -7377,6 +8160,87 @@ function serverSideSection() {
|
|
|
7377
8160
|
)
|
|
7378
8161
|
].join("\n\n");
|
|
7379
8162
|
}
|
|
8163
|
+
var PUSH_ANALYSIS_COMMAND = "agentful push --dry-run --json --summary";
|
|
8164
|
+
function pushSection() {
|
|
8165
|
+
const excludedDirs = [...ALWAYS_EXCLUDE_DIRS].filter((name) => /^[a-z]/.test(name)).map((name) => `\`${name}\``).join(", ");
|
|
8166
|
+
return `## Pushing to the platform (\`agentful push\`)
|
|
8167
|
+
|
|
8168
|
+
Two ways to upload. Every push replaces the previous upload completely, so a
|
|
8169
|
+
site can never be uploaded in several parts.
|
|
8170
|
+
|
|
8171
|
+
- **Source push**, \`agentful push\`: uploads this directory, then builds it in
|
|
8172
|
+
the cloud. Never uploaded: paths listed in \`.gitignore\`, \`.env*\` files,
|
|
8173
|
+
symlinks, tool folders such as \`.git\` or \`.next\`, and any folder named
|
|
8174
|
+
${excludedDirs}, at any depth.
|
|
8175
|
+
The cloud build runs \`npm ci\` (with a \`package-lock.json\`, otherwise
|
|
8176
|
+
\`npm install\`) and then the build command from the table above. Nothing
|
|
8177
|
+
else runs: no other package manager, no separate data or prepare script, no
|
|
8178
|
+
CI workflow step.
|
|
8179
|
+
- **Prebuilt push**, \`agentful push --prebuilt --dir <folder>\`: uploads a
|
|
8180
|
+
finished output folder and serves it as it is. No cloud build, \`.gitignore\`
|
|
8181
|
+
is not applied, and \`index.html\` must sit at the top of that folder. It
|
|
8182
|
+
carries the website only: the project source in the cloud stays as it was (a
|
|
8183
|
+
later build in the cloud workspace replaces the site with a build of that
|
|
8184
|
+
source), and Managed Server Actions and \`.agentful/backend.json\` are NOT
|
|
8185
|
+
deployed by it.
|
|
8186
|
+
|
|
8187
|
+
Limits of one push, either way: about ${formatBytes(maxZipBytesForUpload(), "down")} compressed, ${formatBytes(MAX_UNPACKED_BYTES)} of files, ${MAX_UPLOAD_FILES.toLocaleString("en-US")} files.
|
|
8188
|
+
\`--force\` lifts none of them: it only lets the cloud build try despite a
|
|
8189
|
+
compatibility finding.
|
|
8190
|
+
|
|
8191
|
+
**The analysis decides, not an estimate.** \`${PUSH_ANALYSIS_COMMAND}\`
|
|
8192
|
+
(with \`--prebuilt --dir <folder>\` for a prebuilt push) reads the files and
|
|
8193
|
+
reports what a push would upload: no sign-in, no project, no upload, nothing
|
|
8194
|
+
written. Sizes, limits, what is left out and what blocks the upload come from
|
|
8195
|
+
that report and from nowhere else: quote its figures, never estimate or
|
|
8196
|
+
recompute them (\`limits.max_zip_bytes\` is the exact limit for this push).
|
|
8197
|
+
Run it before the first push of a session, after a push was stopped, and after
|
|
8198
|
+
you changed what gets uploaded. In between, \`agentful push\` applies the same
|
|
8199
|
+
check itself and stops before sign-in when something blocks. A blocker with
|
|
8200
|
+
\`"overridable": false\` is final, the platform rejects that upload: do not
|
|
8201
|
+
retry, do not add \`--force\`, do not look for another route.
|
|
8202
|
+
|
|
8203
|
+
Every change to the user's files for a push (including \`.gitignore\`) and every
|
|
8204
|
+
project script you run for it needs the user's OK first: name the concrete
|
|
8205
|
+
change, ask, wait for the answer.
|
|
8206
|
+
|
|
8207
|
+
- **No start page** (\`entry_missing\`): the platform serves \`index.html\` from
|
|
8208
|
+
the top level. With \`entry.candidates\`: show them (path and title;
|
|
8209
|
+
\`same_content_as\` marks copies) and ask which page opens first. A candidate
|
|
8210
|
+
at the top level can be renamed to \`index.html\`, or copied, which keeps the
|
|
8211
|
+
old address and adds the file's size to the upload: ask which. A candidate
|
|
8212
|
+
in a subfolder must not be copied up, its relative links would break. If
|
|
8213
|
+
that folder is the whole site, push the folder (\`suggestions\` carries the
|
|
8214
|
+
command); otherwise adjust the links and show the diff. Without candidates,
|
|
8215
|
+
ask which folder holds the site.
|
|
8216
|
+
- **Too large or too many files** (\`zip_too_large\`, \`unpacked_too_large\`,
|
|
8217
|
+
\`too_many_files\`): explain where the size is, from \`largest_files\`,
|
|
8218
|
+
\`by_top_level\`, \`duplicates\` and \`embedded_media\` (media stored inside text
|
|
8219
|
+
files as \`data:\` URLs). The ways out that exist: leave out files the site
|
|
8220
|
+
does not load (\`.gitignore\`, source push), or push only the finished output
|
|
8221
|
+
folder (prebuilt). Before proposing to leave a file out, search the sources
|
|
8222
|
+
for references to it and say what you checked: identical content does not
|
|
8223
|
+
make a copy unused. Never shrink, re-encode, move or delete the user's files
|
|
8224
|
+
on your own. If what the site needs stays above the limits, say plainly that
|
|
8225
|
+
it cannot be pushed yet. Larger uploads are not available.
|
|
8226
|
+
- **Compatibility findings** (\`"overridable": true\`): fix the cause in the
|
|
8227
|
+
code. \`--force\` only when the user asks to try the cloud build anyway.
|
|
8228
|
+
- **Files that would be missing on the site:** the warnings
|
|
8229
|
+
\`dropped_by_platform\` and \`excluded_static_directory\`, and entries of
|
|
8230
|
+
\`excluded.entries\` with reason \`gitignore\` that the site loads at runtime
|
|
8231
|
+
(generated data under \`public/\`, for example). The build can succeed and the
|
|
8232
|
+
site still be broken: tell the user before pushing and ask.
|
|
8233
|
+
- **Steps the cloud build does not run:** before the first push of a project
|
|
8234
|
+
you did not create in this session, read the \`package.json\` scripts, the
|
|
8235
|
+
README and the CI workflows for data generation, code generation or another
|
|
8236
|
+
package manager. If the site needs such a step, a source push is the wrong
|
|
8237
|
+
way: build locally with the project's own commands and its real data, keep
|
|
8238
|
+
its checks, and push the output folder prebuilt. Never put sample data in
|
|
8239
|
+
place of real data to get a push through.
|
|
8240
|
+
|
|
8241
|
+
A finished build or upload does not prove that the site works: say what you
|
|
8242
|
+
verified and what you did not.`;
|
|
8243
|
+
}
|
|
7380
8244
|
function renderCloudInstructions(framework, backend) {
|
|
7381
8245
|
return `# Agentful Cloud target (platform contract)
|
|
7382
8246
|
|
|
@@ -7531,6 +8395,8 @@ user's conscious step \u2014 in that tab, or by answering the question
|
|
|
7531
8395
|
\`agentful push\` asks once (it names both consequences; a non-interactive
|
|
7532
8396
|
push never enables anything).
|
|
7533
8397
|
|
|
8398
|
+
${pushSection()}
|
|
8399
|
+
|
|
7534
8400
|
## Diagnosing failed pushes and cloud builds (hard rule)
|
|
7535
8401
|
|
|
7536
8402
|
Diagnose **only from evidence**: the preflight output of \`agentful push\`, and
|
|
@@ -7549,6 +8415,93 @@ ${backendSessionLines(backend)}
|
|
|
7549
8415
|
`;
|
|
7550
8416
|
}
|
|
7551
8417
|
|
|
8418
|
+
// src/branding/commands.ts
|
|
8419
|
+
var PUSH_TOOL_TIMEOUT_MS = 9e5;
|
|
8420
|
+
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.';
|
|
8421
|
+
var SLASH_COMMANDS = {
|
|
8422
|
+
setup: {
|
|
8423
|
+
description: `${brand.displayName}: create or link the cloud project for this directory`,
|
|
8424
|
+
template: 'Check whether .agentful/project.json exists. If it does, report the linked project title and id and stop. Otherwise run `agentful init --title "<name>"` using the bash tool, where <name> is $ARGUMENTS if I gave one, otherwise the current directory name. Report what it created and mention the title I can change it to. Do not modify any other project files.'
|
|
8425
|
+
},
|
|
8426
|
+
// The analysis comes first (upload plan AP7): the report, not the model,
|
|
8427
|
+
// says what would be uploaded, how large it is and what blocks it, and the
|
|
8428
|
+
// rules for acting on it are in AGENTFUL_CLOUD.md. Two things the agent's
|
|
8429
|
+
// shell tool forces on this template: `--summary`, because the tool cuts
|
|
8430
|
+
// output after 2,000 lines or 50 KB and the full report lists every file;
|
|
8431
|
+
// and an explicit timeout, because the tool ends a command after two
|
|
8432
|
+
// minutes by default and a cloud build takes longer (engine 1.18.32).
|
|
8433
|
+
push: {
|
|
8434
|
+
description: `${brand.displayName}: check, deploy and get a preview URL`,
|
|
8435
|
+
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
|
+
|
|
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. One question at a time, then wait for my answer. Until then do not change any file, do not run project scripts and do not push.
|
|
8439
|
+
3. After a change I approved, run the analysis again.
|
|
8440
|
+
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
|
+
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.
|
|
8442
|
+
|
|
8443
|
+
Apart from a change I approved in step 2, do not modify project files.`
|
|
8444
|
+
},
|
|
8445
|
+
"build-status": {
|
|
8446
|
+
description: `${brand.displayName}: show the last cloud build result`,
|
|
8447
|
+
template: "Run `agentful build-status` from the project root using the bash tool and report what it prints: status, phase, framework, when it was recorded, and the error/details verbatim if it failed. That record is the only source of truth about the cloud build \u2014 do not speculate beyond it. " + NEEDS_A_PUSH
|
|
8448
|
+
},
|
|
8449
|
+
backend: {
|
|
8450
|
+
description: `${brand.displayName}: open the Backend tab (managed DB + server actions)`,
|
|
8451
|
+
template: "Run `agentful backend` from the project root using the bash tool and report the URL it opened. If it prints a declared backend (managed DB / server actions), summarize that and remind me that declared-in-code serves nothing until enabled in that tab. " + NEEDS_A_PUSH
|
|
8452
|
+
},
|
|
8453
|
+
preview: {
|
|
8454
|
+
description: `${brand.displayName}: open the live preview in the browser`,
|
|
8455
|
+
template: "Run `agentful open` from the project root using the bash tool and report the URL it opened. Note that `open` neither builds nor uploads. " + NEEDS_A_PUSH
|
|
8456
|
+
},
|
|
8457
|
+
publish: {
|
|
8458
|
+
description: `${brand.displayName}: publish on a public subdomain`,
|
|
8459
|
+
template: "Ask me for the subdomain if I did not provide one in $ARGUMENTS, then run `agentful publish <subdomain>` from the project root using the bash tool and report the public URL. " + NEEDS_A_PUSH
|
|
8460
|
+
},
|
|
8461
|
+
share: {
|
|
8462
|
+
description: `${brand.displayName}: make the live preview public (or private again)`,
|
|
8463
|
+
template: "Run `agentful share` from the project root using the bash tool (add `--private` if $ARGUMENTS says private) and report the resulting visibility and URL. " + NEEDS_A_PUSH
|
|
8464
|
+
},
|
|
8465
|
+
status: {
|
|
8466
|
+
description: `${brand.displayName}: show the linked project and account`,
|
|
8467
|
+
template: "Run `agentful whoami` using the bash tool. Then read .agentful/project.json and report the linked project title and id, plus the preview URL https://preview-<projectId>.agentful.page/. If the file does not exist, say that this directory is not linked yet and that `/setup` creates the link."
|
|
8468
|
+
}
|
|
8469
|
+
};
|
|
8470
|
+
function commandMarkdown(name) {
|
|
8471
|
+
const cmd = SLASH_COMMANDS[name];
|
|
8472
|
+
const description = JSON.stringify(cmd.description);
|
|
8473
|
+
return `---
|
|
8474
|
+
description: ${description}
|
|
8475
|
+
agent: ${cmd.agent ?? "agentful-build"}
|
|
8476
|
+
---
|
|
8477
|
+
|
|
8478
|
+
${cmd.template}
|
|
8479
|
+
`;
|
|
8480
|
+
}
|
|
8481
|
+
|
|
8482
|
+
// src/lib/tools.ts
|
|
8483
|
+
var import_node_fs13 = require("fs");
|
|
8484
|
+
var import_node_path15 = require("path");
|
|
8485
|
+
function assetsDir() {
|
|
8486
|
+
const here = (0, import_node_path15.dirname)(process.argv[1] || "");
|
|
8487
|
+
for (const candidate of [(0, import_node_path15.join)(here, "..", "assets"), (0, import_node_path15.join)(here, "assets")]) {
|
|
8488
|
+
if ((0, import_node_fs13.existsSync)((0, import_node_path15.join)(candidate, "tools", "imagegen.ts"))) return candidate;
|
|
8489
|
+
}
|
|
8490
|
+
return null;
|
|
8491
|
+
}
|
|
8492
|
+
function installEngineTools(configDir2) {
|
|
8493
|
+
const assets = assetsDir();
|
|
8494
|
+
if (!assets) return false;
|
|
8495
|
+
const target = (0, import_node_path15.join)(configDir2, "tools");
|
|
8496
|
+
(0, import_node_fs13.mkdirSync)(target, { recursive: true });
|
|
8497
|
+
try {
|
|
8498
|
+
(0, import_node_fs13.cpSync)((0, import_node_path15.join)(assets, "tools"), target, { recursive: true });
|
|
8499
|
+
return true;
|
|
8500
|
+
} catch {
|
|
8501
|
+
return false;
|
|
8502
|
+
}
|
|
8503
|
+
}
|
|
8504
|
+
|
|
7552
8505
|
// src/lib/engineConfig.ts
|
|
7553
8506
|
var PROVIDER_ID = "agentful";
|
|
7554
8507
|
var DISABLED_ENGINE_PLUGINS = {
|
|
@@ -7566,7 +8519,7 @@ var CLOUD_AGENT_PROMPT = `You are the Agentful Cloud build agent: everything you
|
|
|
7566
8519
|
var LOCAL_AGENT_PROMPT = `You are in local-only mode: free local development with no cloud deployment target \u2014 any stack is fine. Be clear that this directory will NOT be deployable with \`agentful push\` unless it satisfies the platform contract in AGENTFUL_CLOUD.md (${SERVER_SIDE_IN_ONE_SENTENCE}). If the user asks to deploy or push, check the contract first and say honestly whether the project can ship on the platform; never claim cloud deployability without evidence.`;
|
|
7567
8520
|
function readAuthFile(path) {
|
|
7568
8521
|
try {
|
|
7569
|
-
return (0,
|
|
8522
|
+
return (0, import_node_fs14.readFileSync)(path, "utf8");
|
|
7570
8523
|
} catch {
|
|
7571
8524
|
return null;
|
|
7572
8525
|
}
|
|
@@ -7636,11 +8589,11 @@ function buildEngineConfig(opts) {
|
|
|
7636
8589
|
};
|
|
7637
8590
|
}
|
|
7638
8591
|
function engineXdg() {
|
|
7639
|
-
const root = (0,
|
|
8592
|
+
const root = (0, import_node_path16.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
|
|
7640
8593
|
return {
|
|
7641
|
-
configHome: (0,
|
|
7642
|
-
dataHome: (0,
|
|
7643
|
-
stateHome: (0,
|
|
8594
|
+
configHome: (0, import_node_path16.join)(root, "engine-config"),
|
|
8595
|
+
dataHome: (0, import_node_path16.join)(root, "engine-data"),
|
|
8596
|
+
stateHome: (0, import_node_path16.join)(root, "engine-state")
|
|
7644
8597
|
};
|
|
7645
8598
|
}
|
|
7646
8599
|
async function resolveTheme() {
|
|
@@ -7659,24 +8612,24 @@ async function resolveTheme() {
|
|
|
7659
8612
|
}
|
|
7660
8613
|
async function writeEngineSession(session, catalog, localProviders = {}, framework = "unknown", skillsInstruction = null, backendState, model) {
|
|
7661
8614
|
const xdg = engineXdg();
|
|
7662
|
-
const configDir2 = (0,
|
|
7663
|
-
const dataDir = (0,
|
|
7664
|
-
const themesDir = (0,
|
|
7665
|
-
const pluginsDir = (0,
|
|
7666
|
-
const commandsDir = (0,
|
|
7667
|
-
for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0,
|
|
7668
|
-
(0,
|
|
8615
|
+
const configDir2 = (0, import_node_path16.join)(xdg.configHome, "opencode");
|
|
8616
|
+
const dataDir = (0, import_node_path16.join)(xdg.dataHome, "opencode");
|
|
8617
|
+
const themesDir = (0, import_node_path16.join)(configDir2, "themes");
|
|
8618
|
+
const pluginsDir = (0, import_node_path16.join)(configDir2, "plugins");
|
|
8619
|
+
const commandsDir = (0, import_node_path16.join)(configDir2, "commands");
|
|
8620
|
+
for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path16.join)(xdg.stateHome, "opencode")]) {
|
|
8621
|
+
(0, import_node_fs14.mkdirSync)(dir, { recursive: true });
|
|
7669
8622
|
}
|
|
7670
8623
|
const imagegenAvailable = installEngineTools(configDir2) && session.img_on !== false;
|
|
7671
|
-
const cloudInstructionsPath = (0,
|
|
7672
|
-
(0,
|
|
7673
|
-
const skillsPath = (0,
|
|
8624
|
+
const cloudInstructionsPath = (0, import_node_path16.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
|
|
8625
|
+
(0, import_node_fs14.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework, backendState));
|
|
8626
|
+
const skillsPath = (0, import_node_path16.join)(configDir2, "AGENTFUL_SKILLS.md");
|
|
7674
8627
|
const instructionPaths = [cloudInstructionsPath];
|
|
7675
8628
|
if (skillsInstruction) {
|
|
7676
|
-
(0,
|
|
8629
|
+
(0, import_node_fs14.writeFileSync)(skillsPath, skillsInstruction);
|
|
7677
8630
|
instructionPaths.push(skillsPath);
|
|
7678
8631
|
} else {
|
|
7679
|
-
(0,
|
|
8632
|
+
(0, import_node_fs14.rmSync)(skillsPath, { force: true });
|
|
7680
8633
|
}
|
|
7681
8634
|
const config = buildEngineConfig({
|
|
7682
8635
|
session,
|
|
@@ -7686,13 +8639,13 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
|
|
|
7686
8639
|
instructionPaths,
|
|
7687
8640
|
model
|
|
7688
8641
|
});
|
|
7689
|
-
(0,
|
|
7690
|
-
const authPath2 = (0,
|
|
7691
|
-
(0,
|
|
8642
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
|
|
8643
|
+
const authPath2 = (0, import_node_path16.join)(dataDir, "auth.json");
|
|
8644
|
+
(0, import_node_fs14.writeFileSync)(authPath2, mergedAuthJson(readAuthFile(authPath2), session.token));
|
|
7692
8645
|
const { brandingPluginSource: brandingPluginSource2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
|
|
7693
|
-
const pluginPath = (0,
|
|
7694
|
-
(0,
|
|
7695
|
-
(0,
|
|
8646
|
+
const pluginPath = (0, import_node_path16.join)(pluginsDir, "agentful-branding.tsx");
|
|
8647
|
+
(0, import_node_fs14.writeFileSync)(pluginPath, brandingPluginSource2());
|
|
8648
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(configDir2, "tui.json"), JSON.stringify({
|
|
7696
8649
|
theme: "agentful",
|
|
7697
8650
|
plugin: [`file://${pluginPath}`],
|
|
7698
8651
|
// The engine's own sidebar sections are plugins with fixed ids, and
|
|
@@ -7704,13 +8657,13 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
|
|
|
7704
8657
|
// state over this map.
|
|
7705
8658
|
plugin_enabled: DISABLED_ENGINE_PLUGINS
|
|
7706
8659
|
}, null, 2));
|
|
7707
|
-
(0,
|
|
8660
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
|
|
7708
8661
|
for (const name of Object.keys(SLASH_COMMANDS)) {
|
|
7709
|
-
(0,
|
|
8662
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(commandsDir, `${name}.md`), commandMarkdown(name));
|
|
7710
8663
|
}
|
|
7711
8664
|
for (const legacy of ["xdg-config", "xdg-data", "xdg-state"]) {
|
|
7712
8665
|
try {
|
|
7713
|
-
(0,
|
|
8666
|
+
(0, import_node_fs14.rmSync)((0, import_node_path16.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
|
|
7714
8667
|
} catch {
|
|
7715
8668
|
}
|
|
7716
8669
|
}
|
|
@@ -7718,8 +8671,8 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
|
|
|
7718
8671
|
}
|
|
7719
8672
|
|
|
7720
8673
|
// src/lib/skills.ts
|
|
7721
|
-
var
|
|
7722
|
-
var
|
|
8674
|
+
var import_node_fs15 = require("fs");
|
|
8675
|
+
var import_node_path17 = require("path");
|
|
7723
8676
|
|
|
7724
8677
|
// src/generated/skills.json
|
|
7725
8678
|
var skills_default = {
|
|
@@ -7786,18 +8739,20 @@ var SCAFFOLD_BY_FRAMEWORK = {
|
|
|
7786
8739
|
// cra/nuxt get no scaffold skill: the react/vue skills are Vite-specific
|
|
7787
8740
|
// and would teach the wrong shape — the template contract still applies.
|
|
7788
8741
|
};
|
|
7789
|
-
function
|
|
8742
|
+
function dependencies(rootDir) {
|
|
7790
8743
|
try {
|
|
7791
|
-
const pkg = JSON.parse((0,
|
|
7792
|
-
return
|
|
8744
|
+
const pkg = JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path17.join)(rootDir, "package.json"), "utf8"));
|
|
8745
|
+
return { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7793
8746
|
} catch {
|
|
7794
|
-
return
|
|
8747
|
+
return {};
|
|
7795
8748
|
}
|
|
7796
8749
|
}
|
|
7797
8750
|
function selectSkills(framework, rootDir) {
|
|
7798
8751
|
const names = [CONTRACT_SKILL];
|
|
7799
8752
|
if (framework === "vite") {
|
|
7800
|
-
|
|
8753
|
+
const deps = dependencies(rootDir);
|
|
8754
|
+
if (deps.vue) names.push("vue-scaffold");
|
|
8755
|
+
else if (deps.react) names.push("react-scaffold");
|
|
7801
8756
|
} else {
|
|
7802
8757
|
const scaffold = SCAFFOLD_BY_FRAMEWORK[framework];
|
|
7803
8758
|
if (scaffold) names.push(scaffold);
|
|
@@ -7818,7 +8773,7 @@ function renderSkillsInstruction(names) {
|
|
|
7818
8773
|
return "# Platform deployment skills (mandatory)\n\nThese are the same skills the cloud build agent loads for this stack.\nFollow them when scaffolding or editing \u2014 they are not optional hints.\n\n" + sections.join("\n\n---\n\n") + "\n";
|
|
7819
8774
|
}
|
|
7820
8775
|
function skillsInstructionFor(framework, rootDir) {
|
|
7821
|
-
if (!(0,
|
|
8776
|
+
if (!(0, import_node_fs15.existsSync)(rootDir)) return null;
|
|
7822
8777
|
return renderSkillsInstruction(selectSkills(framework, rootDir));
|
|
7823
8778
|
}
|
|
7824
8779
|
|
|
@@ -7977,15 +8932,15 @@ function decideLocalProviders(policy) {
|
|
|
7977
8932
|
}
|
|
7978
8933
|
|
|
7979
8934
|
// src/lib/localProviders.ts
|
|
7980
|
-
var
|
|
7981
|
-
var
|
|
8935
|
+
var import_node_fs16 = require("fs");
|
|
8936
|
+
var import_node_path18 = require("path");
|
|
7982
8937
|
var LOCAL_PROVIDERS_FILE = "local-providers.json";
|
|
7983
8938
|
function localProvidersPath() {
|
|
7984
|
-
return (0,
|
|
8939
|
+
return (0, import_node_path18.join)(configDir(), LOCAL_PROVIDERS_FILE);
|
|
7985
8940
|
}
|
|
7986
8941
|
function readLocalProviders() {
|
|
7987
8942
|
try {
|
|
7988
|
-
const raw = JSON.parse((0,
|
|
8943
|
+
const raw = JSON.parse((0, import_node_fs16.readFileSync)(localProvidersPath(), "utf8"));
|
|
7989
8944
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
7990
8945
|
const map = raw.providers && typeof raw.providers === "object" ? raw.providers : raw;
|
|
7991
8946
|
const usable = {};
|
|
@@ -8020,8 +8975,8 @@ function resolveLocalProviders(policy) {
|
|
|
8020
8975
|
}
|
|
8021
8976
|
function writeLocalProvidersExample() {
|
|
8022
8977
|
const path = localProvidersPath();
|
|
8023
|
-
if ((0,
|
|
8024
|
-
(0,
|
|
8978
|
+
if ((0, import_node_fs16.existsSync)(path)) return path;
|
|
8979
|
+
(0, import_node_fs16.mkdirSync)(configDir(), { recursive: true });
|
|
8025
8980
|
const example = {
|
|
8026
8981
|
_comment: 'Providers with your own API key, running locally. Requests go directly to the provider \u2014 no Agentful credits, no platform region guarantee. Remove the leading underscore from "_anthropic" and fill in your key to enable it.',
|
|
8027
8982
|
_anthropic: {
|
|
@@ -8031,17 +8986,17 @@ function writeLocalProvidersExample() {
|
|
|
8031
8986
|
models: { "claude-sonnet-4-5": { name: "Claude Sonnet 4.5 (my key)" } }
|
|
8032
8987
|
}
|
|
8033
8988
|
};
|
|
8034
|
-
(0,
|
|
8989
|
+
(0, import_node_fs16.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
|
|
8035
8990
|
try {
|
|
8036
|
-
(0,
|
|
8991
|
+
(0, import_node_fs16.chmodSync)(path, 384);
|
|
8037
8992
|
} catch {
|
|
8038
8993
|
}
|
|
8039
8994
|
return path;
|
|
8040
8995
|
}
|
|
8041
8996
|
|
|
8042
8997
|
// src/lib/sessions.ts
|
|
8043
|
-
var
|
|
8044
|
-
var
|
|
8998
|
+
var import_node_fs17 = require("fs");
|
|
8999
|
+
var import_node_path19 = require("path");
|
|
8045
9000
|
var import_node_child_process4 = require("child_process");
|
|
8046
9001
|
function parseSessionModel(raw) {
|
|
8047
9002
|
if (!raw) return null;
|
|
@@ -8058,8 +9013,8 @@ function parseSessionModel(raw) {
|
|
|
8058
9013
|
}
|
|
8059
9014
|
}
|
|
8060
9015
|
function modelForSession(sessionID) {
|
|
8061
|
-
const dbPath = (0,
|
|
8062
|
-
if (!(0,
|
|
9016
|
+
const dbPath = (0, import_node_path19.join)(engineXdg().dataHome, "opencode", "opencode.db");
|
|
9017
|
+
if (!(0, import_node_fs17.existsSync)(dbPath)) return null;
|
|
8063
9018
|
const escaped = sessionID.replace(/'/g, "''");
|
|
8064
9019
|
const res = (0, import_node_child_process4.spawnSync)(
|
|
8065
9020
|
"sqlite3",
|
|
@@ -8080,8 +9035,8 @@ function modelForSession(sessionID) {
|
|
|
8080
9035
|
}
|
|
8081
9036
|
}
|
|
8082
9037
|
function lastSessionForDirectory(cwd = process.cwd()) {
|
|
8083
|
-
const dbPath = (0,
|
|
8084
|
-
if (!(0,
|
|
9038
|
+
const dbPath = (0, import_node_path19.join)(engineXdg().dataHome, "opencode", "opencode.db");
|
|
9039
|
+
if (!(0, import_node_fs17.existsSync)(dbPath)) return null;
|
|
8085
9040
|
const escaped = cwd.replace(/'/g, "''");
|
|
8086
9041
|
const res = (0, import_node_child_process4.spawnSync)(
|
|
8087
9042
|
"sqlite3",
|
|
@@ -8107,9 +9062,9 @@ function lastSessionForDirectory(cwd = process.cwd()) {
|
|
|
8107
9062
|
}
|
|
8108
9063
|
|
|
8109
9064
|
// src/lib/modelPin.ts
|
|
8110
|
-
var
|
|
8111
|
-
var
|
|
8112
|
-
var PROJECT_MODEL_PIN = (0,
|
|
9065
|
+
var import_node_fs18 = require("fs");
|
|
9066
|
+
var import_node_path20 = require("path");
|
|
9067
|
+
var PROJECT_MODEL_PIN = (0, import_node_path20.join)(".agentful", "model.json");
|
|
8113
9068
|
function isCanonicalModel(value) {
|
|
8114
9069
|
const slash = value.indexOf("/");
|
|
8115
9070
|
return slash > 0 && slash < value.length - 1 && !/\s/.test(value);
|
|
@@ -8129,10 +9084,10 @@ function assertModelPin(pin, source) {
|
|
|
8129
9084
|
return pin;
|
|
8130
9085
|
}
|
|
8131
9086
|
function readProjectModelPin(cwd = process.cwd()) {
|
|
8132
|
-
const path = (0,
|
|
9087
|
+
const path = (0, import_node_path20.join)(cwd, PROJECT_MODEL_PIN);
|
|
8133
9088
|
let raw;
|
|
8134
9089
|
try {
|
|
8135
|
-
raw = (0,
|
|
9090
|
+
raw = (0, import_node_fs18.readFileSync)(path, "utf8");
|
|
8136
9091
|
} catch (error) {
|
|
8137
9092
|
const code = error.code;
|
|
8138
9093
|
if (code === "ENOENT") return null;
|
|
@@ -8159,7 +9114,7 @@ function readProjectModelPin(cwd = process.cwd()) {
|
|
|
8159
9114
|
function readEngineVariants(xdg) {
|
|
8160
9115
|
try {
|
|
8161
9116
|
const parsed = JSON.parse(
|
|
8162
|
-
(0,
|
|
9117
|
+
(0, import_node_fs18.readFileSync)((0, import_node_path20.join)(xdg.stateHome, "opencode", "model.json"), "utf8")
|
|
8163
9118
|
);
|
|
8164
9119
|
if (!parsed.variant || typeof parsed.variant !== "object" || Array.isArray(parsed.variant)) return {};
|
|
8165
9120
|
return Object.fromEntries(
|
|
@@ -8171,17 +9126,17 @@ function readEngineVariants(xdg) {
|
|
|
8171
9126
|
}
|
|
8172
9127
|
function writeEngineVariant(xdg, model, variant) {
|
|
8173
9128
|
assertModelPin({ model, variant }, "Selected");
|
|
8174
|
-
const path = (0,
|
|
9129
|
+
const path = (0, import_node_path20.join)(xdg.stateHome, "opencode", "model.json");
|
|
8175
9130
|
let state = {};
|
|
8176
9131
|
try {
|
|
8177
|
-
const parsed = JSON.parse((0,
|
|
9132
|
+
const parsed = JSON.parse((0, import_node_fs18.readFileSync)(path, "utf8"));
|
|
8178
9133
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) state = parsed;
|
|
8179
9134
|
} catch {
|
|
8180
9135
|
}
|
|
8181
9136
|
const existing = state.variant;
|
|
8182
9137
|
const variants = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
8183
9138
|
state.variant = { ...variants, [model]: variant };
|
|
8184
|
-
(0,
|
|
9139
|
+
(0, import_node_fs18.writeFileSync)(path, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
8185
9140
|
}
|
|
8186
9141
|
function resolveModelPin(opts) {
|
|
8187
9142
|
const cliPin = opts.cliModel ? assertModelPin({ model: opts.cliModel, variant: opts.cliVariant }, "CLI") : null;
|
|
@@ -8230,8 +9185,8 @@ function resolveModelPin(opts) {
|
|
|
8230
9185
|
// src/lib/engineModels.ts
|
|
8231
9186
|
var import_node_child_process5 = require("child_process");
|
|
8232
9187
|
var import_node_util = require("util");
|
|
8233
|
-
var
|
|
8234
|
-
var
|
|
9188
|
+
var import_node_fs19 = require("fs");
|
|
9189
|
+
var import_node_path21 = require("path");
|
|
8235
9190
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process5.execFile);
|
|
8236
9191
|
function parseEngineModelsOutput(output) {
|
|
8237
9192
|
return Array.from(new Set(
|
|
@@ -8282,7 +9237,7 @@ async function assertEngineModelAvailable(binPath, xdg, model, listModels = list
|
|
|
8282
9237
|
function readConnectedEngineProviders(xdg) {
|
|
8283
9238
|
try {
|
|
8284
9239
|
const parsed = JSON.parse(
|
|
8285
|
-
(0,
|
|
9240
|
+
(0, import_node_fs19.readFileSync)((0, import_node_path21.join)(xdg.dataHome, "opencode", "auth.json"), "utf8")
|
|
8286
9241
|
);
|
|
8287
9242
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return [];
|
|
8288
9243
|
return Object.keys(parsed).filter((id) => id !== "agentful").sort();
|
|
@@ -8313,7 +9268,7 @@ function describeModelRoute(model, catalog, localProviders, connectedProviders)
|
|
|
8313
9268
|
}
|
|
8314
9269
|
|
|
8315
9270
|
// src/lib/engineEvents.ts
|
|
8316
|
-
var
|
|
9271
|
+
var import_node_crypto3 = require("crypto");
|
|
8317
9272
|
var import_node_net = require("net");
|
|
8318
9273
|
function payloadOf(event) {
|
|
8319
9274
|
return event.payload && typeof event.payload === "object" ? event.payload : event;
|
|
@@ -8439,7 +9394,7 @@ var EngineLivenessPolicy = class {
|
|
|
8439
9394
|
function engineServerCredentials(env = process.env) {
|
|
8440
9395
|
return {
|
|
8441
9396
|
username: env.OPENCODE_SERVER_USERNAME || void 0,
|
|
8442
|
-
password: env.OPENCODE_SERVER_PASSWORD || (0,
|
|
9397
|
+
password: env.OPENCODE_SERVER_PASSWORD || (0, import_node_crypto3.randomUUID)()
|
|
8443
9398
|
};
|
|
8444
9399
|
}
|
|
8445
9400
|
async function reserveLoopbackPort() {
|
|
@@ -8644,7 +9599,7 @@ async function tuiCommand(opts = {}) {
|
|
|
8644
9599
|
));
|
|
8645
9600
|
} else {
|
|
8646
9601
|
ui.info(paint.dim(
|
|
8647
|
-
`Mode: ${CLOUD_AGENT} \u2014 building for Agentful Cloud (${
|
|
9602
|
+
`Mode: ${CLOUD_AGENT} \u2014 building for Agentful Cloud (${frameworkDisplayName2(framework)}). Tab switches to ${LOCAL_AGENT} for work that is not meant to deploy.`
|
|
8648
9603
|
));
|
|
8649
9604
|
}
|
|
8650
9605
|
const localCount = Object.keys(local.providers).length;
|
|
@@ -8718,7 +9673,7 @@ async function tuiCommand(opts = {}) {
|
|
|
8718
9673
|
...process.env,
|
|
8719
9674
|
// Slash commands run `agentful …` through the agent's bash tool, which
|
|
8720
9675
|
// has no access to the user's shell aliases.
|
|
8721
|
-
PATH: `${binDir}${
|
|
9676
|
+
PATH: `${binDir}${import_node_path22.delimiter}${process.env.PATH || ""}`,
|
|
8722
9677
|
XDG_CONFIG_HOME: xdg.configHome,
|
|
8723
9678
|
XDG_DATA_HOME: xdg.dataHome,
|
|
8724
9679
|
XDG_STATE_HOME: xdg.stateHome,
|
|
@@ -8852,38 +9807,38 @@ async function shareCommand(opts) {
|
|
|
8852
9807
|
}
|
|
8853
9808
|
|
|
8854
9809
|
// src/commands/pull.ts
|
|
8855
|
-
var
|
|
8856
|
-
var
|
|
9810
|
+
var import_node_fs21 = require("fs");
|
|
9811
|
+
var import_node_path24 = require("path");
|
|
8857
9812
|
init_branding();
|
|
8858
9813
|
init_branding();
|
|
8859
9814
|
|
|
8860
9815
|
// src/lib/pullFiles.ts
|
|
8861
|
-
var
|
|
8862
|
-
var
|
|
9816
|
+
var import_node_fs20 = require("fs");
|
|
9817
|
+
var import_node_path23 = require("path");
|
|
8863
9818
|
var INLINE_BASE64_EXTENSIONS = /\.(?:png|jpe?g|gif|webp|avif|svg|ico|bmp|tiff?|woff2?|ttf|otf|eot|pdf|docx?|xlsx?|pptx?|zip|tar|gz|7z|rar|bz2|mp[34]|m4a|wav|ogg|avi|mov|mkv|flac|webm|exe|dll|so|dylib|bin|wasm)$/i;
|
|
8864
9819
|
function inlineContent(value) {
|
|
8865
9820
|
return typeof value === "object" && value !== null && "content" in value ? String(value.content) : String(value ?? "");
|
|
8866
9821
|
}
|
|
8867
9822
|
function writePulledWorkspaceEntry(root, rel, value) {
|
|
8868
|
-
const target = (0,
|
|
8869
|
-
(0,
|
|
9823
|
+
const target = (0, import_node_path23.join)(root, rel);
|
|
9824
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path23.dirname)(target), { recursive: true });
|
|
8870
9825
|
const content = inlineContent(value);
|
|
8871
9826
|
if (INLINE_BASE64_EXTENSIONS.test(rel)) {
|
|
8872
|
-
(0,
|
|
9827
|
+
(0, import_node_fs20.writeFileSync)(target, Buffer.from(content, "base64"));
|
|
8873
9828
|
return;
|
|
8874
9829
|
}
|
|
8875
|
-
(0,
|
|
9830
|
+
(0, import_node_fs20.writeFileSync)(target, content, "utf8");
|
|
8876
9831
|
}
|
|
8877
9832
|
function writePulledBackendDeclaration(root, declaration) {
|
|
8878
|
-
const target = (0,
|
|
8879
|
-
(0,
|
|
9833
|
+
const target = (0, import_node_path23.join)(root, ".agentful", "backend.json");
|
|
9834
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path23.dirname)(target), { recursive: true });
|
|
8880
9835
|
const {
|
|
8881
9836
|
source: _source,
|
|
8882
9837
|
received_at: _receivedAt,
|
|
8883
9838
|
last_sync: _lastSync,
|
|
8884
9839
|
...portableDeclaration
|
|
8885
9840
|
} = declaration;
|
|
8886
|
-
(0,
|
|
9841
|
+
(0, import_node_fs20.writeFileSync)(target, `${JSON.stringify(portableDeclaration, null, 2)}
|
|
8887
9842
|
`, "utf8");
|
|
8888
9843
|
}
|
|
8889
9844
|
|
|
@@ -8892,7 +9847,7 @@ async function pullCommand(opts) {
|
|
|
8892
9847
|
console.log(banner());
|
|
8893
9848
|
const auth = await ensureAuth();
|
|
8894
9849
|
const project = requireProject();
|
|
8895
|
-
const nonHidden = (0,
|
|
9850
|
+
const nonHidden = (0, import_node_fs21.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
|
|
8896
9851
|
if (nonHidden.length > 0 && !opts.force) {
|
|
8897
9852
|
throw new ApiError(
|
|
8898
9853
|
0,
|
|
@@ -8919,9 +9874,9 @@ async function pullCommand(opts) {
|
|
|
8919
9874
|
ui.warn(`Skipped ${rel} (HTTP ${resp.status})`);
|
|
8920
9875
|
continue;
|
|
8921
9876
|
}
|
|
8922
|
-
const target = (0,
|
|
8923
|
-
(0,
|
|
8924
|
-
(0,
|
|
9877
|
+
const target = (0, import_node_path24.join)(process.cwd(), rel);
|
|
9878
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path24.dirname)(target), { recursive: true });
|
|
9879
|
+
(0, import_node_fs21.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
|
|
8925
9880
|
written++;
|
|
8926
9881
|
}
|
|
8927
9882
|
} else if (data.files) {
|
|
@@ -9126,7 +10081,7 @@ program2.command("login").description("Sign in to your Agentful account (device
|
|
|
9126
10081
|
program2.command("logout").description("Remove the stored credentials").action(run(logoutCommand));
|
|
9127
10082
|
program2.command("whoami").description("Show the signed-in account").action(run(whoamiCommand));
|
|
9128
10083
|
program2.command("init").description("Create (or link) the Agentful cloud project for this directory").argument("[directory]", "with --template: new folder to scaffold into (created for you)").option("--link <projectId>", "link an existing project instead of creating one").option("--title <title>", "project title (skips the prompt)").option("--template <framework>", "scaffold from a platform starter (nextjs, react, vue, vanilla)").action(run((directory, opts) => initCommand(opts, directory)));
|
|
9129
|
-
program2.command("push").description("Upload this project and get a live preview URL").option("--prebuilt", "upload a locally built output instead of building in the cloud").option("--dir <path>", "built-output directory for --prebuilt (default: auto-detect)").option("--force", "push even when the pre-upload compatibility check finds blocking issues").option("-y, --yes", "enable the declared managed database without asking (CI); a non-interactive push never enables it").action(run((opts) => pushCommand(opts)));
|
|
10084
|
+
program2.command("push").description("Upload this project and get a live preview URL").option("--prebuilt", "upload a locally built output instead of building in the cloud").option("--dir <path>", "built-output directory for --prebuilt (default: auto-detect)").option("--force", "push even when the pre-upload compatibility check finds blocking issues").option("-y, --yes", "enable the declared managed database without asking (CI); a non-interactive push never enables it").option("--dry-run", "analyse only: what would be uploaded, how large, and what would block it \u2014 no sign-in, no project, no upload").option("--json", "with --dry-run: print the analysis as JSON (for scripts and agents)").option("--summary", "with --dry-run --json: totals and findings without the list of every file").action(run((opts) => pushCommand(opts)));
|
|
9130
10085
|
program2.command("build-status").description("Show the cloud's record of the last build (the real error on failures)").action(run(buildStatusCommand));
|
|
9131
10086
|
program2.command("backend").description("Open this project's Backend tab (managed DB + server actions) in the browser").action(run(backendCommand));
|
|
9132
10087
|
program2.command("open").description("Open the live preview in the browser").action(run(openCommand));
|