agentful 0.4.11 → 0.4.14
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 +13 -2
- package/dist/index.cjs +1209 -442
- 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.14" ? "0.4.14" : null.version;
|
|
3048
3048
|
brand = {
|
|
3049
3049
|
name: "agentful",
|
|
3050
3050
|
// the command users type
|
|
@@ -4015,7 +4015,7 @@ var PB_URL = (process.env.AGENTFUL_PB_URL || "https://pb.agentful.dev").replace(
|
|
|
4015
4015
|
var API_URL = (process.env.AGENTFUL_API_URL || PB_URL).replace(/\/$/, "");
|
|
4016
4016
|
var APP_URL = (process.env.AGENTFUL_APP_URL || "https://app.agentful.dev").replace(/\/$/, "");
|
|
4017
4017
|
var PUBLIC_API_URL = (process.env.AGENTFUL_PUBLIC_API_URL || "https://api.agentful.dev").replace(/\/$/, "");
|
|
4018
|
-
var PREVIEW_DOMAIN = "agentful.
|
|
4018
|
+
var PREVIEW_DOMAIN = "agentful.page";
|
|
4019
4019
|
var PUBLISHED_DOMAIN = "agentful.page";
|
|
4020
4020
|
function configDir() {
|
|
4021
4021
|
if (process.platform === "win32" && process.env.APPDATA) {
|
|
@@ -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)})`;
|
|
@@ -6161,170 +6408,675 @@ function checkNextjs(rootDir) {
|
|
|
6161
6408
|
} else {
|
|
6162
6409
|
hard.push(label);
|
|
6163
6410
|
}
|
|
6164
|
-
}
|
|
6165
|
-
const authDeps = Object.keys(readDeps(rootDir)).filter(isAuthDep);
|
|
6166
|
-
if (authDeps.length > 0) {
|
|
6167
|
-
const isAuthPath = (p) => p.toLowerCase().split("/").some((seg) => seg === "auth" || seg.includes("nextauth"));
|
|
6168
|
-
const authRoutes = [...routeFiles, ...pagesApi].filter(isAuthPath);
|
|
6169
|
-
if (authRoutes.length > 0) {
|
|
6170
|
-
hard.push(`${authDeps.join(", ")} with its server-side auth route (${sample(authRoutes)})`);
|
|
6171
|
-
} else {
|
|
6172
|
-
warnings.push(
|
|
6173
|
-
`${authDeps.join(", ")} is in dependencies but no auth route was found \u2014 server-side auth cannot run on the static platform. Remove the dependency, or keep auth in an external backend called from the client.`
|
|
6174
|
-
);
|
|
6411
|
+
}
|
|
6412
|
+
const authDeps = Object.keys(readDeps(rootDir)).filter(isAuthDep);
|
|
6413
|
+
if (authDeps.length > 0) {
|
|
6414
|
+
const isAuthPath = (p) => p.toLowerCase().split("/").some((seg) => seg === "auth" || seg.includes("nextauth"));
|
|
6415
|
+
const authRoutes = [...routeFiles, ...pagesApi].filter(isAuthPath);
|
|
6416
|
+
if (authRoutes.length > 0) {
|
|
6417
|
+
hard.push(`${authDeps.join(", ")} with its server-side auth route (${sample(authRoutes)})`);
|
|
6418
|
+
} else {
|
|
6419
|
+
warnings.push(
|
|
6420
|
+
`${authDeps.join(", ")} is in dependencies but no auth route was found \u2014 server-side auth cannot run on the static platform. Remove the dependency, or keep auth in an external backend called from the client.`
|
|
6421
|
+
);
|
|
6422
|
+
}
|
|
6423
|
+
}
|
|
6424
|
+
if (hard.length > 0) {
|
|
6425
|
+
issues.push(
|
|
6426
|
+
`${PLATFORM_CONTRACT}
|
|
6427
|
+
Your project needs a server runtime (found: ${hard.join("; ")}).
|
|
6428
|
+
Options: (a) convert to a static export and move server logic to Managed Server Actions,
|
|
6429
|
+
(b) keep the backend elsewhere and call it from the client, (c) don't push this project.`
|
|
6430
|
+
);
|
|
6431
|
+
}
|
|
6432
|
+
return { issues, warnings };
|
|
6433
|
+
}
|
|
6434
|
+
function checkBuildEnv(rootDir) {
|
|
6435
|
+
const verdict = judgeEnvReads(scanEnvReads(rootDir), {
|
|
6436
|
+
defined: ALWAYS_DEFINED_NAMES,
|
|
6437
|
+
backendUnknown: true,
|
|
6438
|
+
localOnly: envFileNames(rootDir)
|
|
6439
|
+
});
|
|
6440
|
+
return [
|
|
6441
|
+
...verdict.errors.map((e) => `The cloud build will reject this project: ${e}`),
|
|
6442
|
+
...verdict.warnings
|
|
6443
|
+
];
|
|
6444
|
+
}
|
|
6445
|
+
function runPreflight(rootDir) {
|
|
6446
|
+
const framework = detectFramework(rootDir);
|
|
6447
|
+
const issues = [];
|
|
6448
|
+
const warnings = [];
|
|
6449
|
+
if (framework === "unknown") {
|
|
6450
|
+
issues.push(
|
|
6451
|
+
`No supported framework found in package.json. The cloud build supports: ${SUPPORTED_FRAMEWORKS}.`
|
|
6452
|
+
);
|
|
6453
|
+
}
|
|
6454
|
+
if (framework === "nextjs") {
|
|
6455
|
+
const next = checkNextjs(rootDir);
|
|
6456
|
+
issues.push(...next.issues);
|
|
6457
|
+
warnings.push(...next.warnings);
|
|
6458
|
+
}
|
|
6459
|
+
if (framework !== "vanilla") warnings.push(...checkBuildEnv(rootDir));
|
|
6460
|
+
return { framework, issues, warnings };
|
|
6461
|
+
}
|
|
6462
|
+
|
|
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");
|
|
6514
|
+
try {
|
|
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));
|
|
6520
|
+
}
|
|
6521
|
+
} finally {
|
|
6522
|
+
(0, import_node_fs8.closeSync)(fd2);
|
|
6523
|
+
}
|
|
6524
|
+
return hash.digest("hex");
|
|
6525
|
+
}
|
|
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
|
+
};
|
|
6553
|
+
}
|
|
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 };
|
|
6599
|
+
}
|
|
6600
|
+
function projectLink(projectDir) {
|
|
6601
|
+
const config = readProjectConfig(projectDir);
|
|
6602
|
+
return { linked: config !== null, project_id: config?.projectId ?? null };
|
|
6603
|
+
}
|
|
6604
|
+
function failedAnalysis(mode, projectDir, blocker) {
|
|
6605
|
+
return {
|
|
6606
|
+
schema: ANALYSIS_SCHEMA,
|
|
6607
|
+
schema_version: ANALYSIS_SCHEMA_VERSION,
|
|
6608
|
+
cli_version: VERSION,
|
|
6609
|
+
mode,
|
|
6610
|
+
root: null,
|
|
6611
|
+
ok: false,
|
|
6612
|
+
framework: null,
|
|
6613
|
+
project: projectLink(projectDir),
|
|
6614
|
+
limits: limits(maxZipBytesForUpload(mode)),
|
|
6615
|
+
totals: {
|
|
6616
|
+
files: 0,
|
|
6617
|
+
bytes: 0,
|
|
6618
|
+
zip_bytes: 0,
|
|
6619
|
+
zip_bytes_exact: true,
|
|
6620
|
+
zip_bytes_without_duplicates: 0,
|
|
6621
|
+
stored_files: 0,
|
|
6622
|
+
stored_bytes: 0
|
|
6623
|
+
},
|
|
6624
|
+
entry: { required: mode === "dist", expected: "index.html", found: false, path: null, candidates: [] },
|
|
6625
|
+
flattened_prefix: null,
|
|
6626
|
+
largest_files: [],
|
|
6627
|
+
by_class: [],
|
|
6628
|
+
by_top_level: [],
|
|
6629
|
+
duplicates: [],
|
|
6630
|
+
embedded_media: [],
|
|
6631
|
+
excluded: { gitignore_applied: false, entries: [], omitted: 0 },
|
|
6632
|
+
dropped_by_platform: [],
|
|
6633
|
+
blockers: [{ overridable: false, ...blocker }],
|
|
6634
|
+
warnings: [],
|
|
6635
|
+
suggestions: [],
|
|
6636
|
+
files: [],
|
|
6637
|
+
files_omitted: 0,
|
|
6638
|
+
fingerprint: `sha256:${sha256Of(Buffer.from(JSON.stringify({ v: ANALYSIS_SCHEMA_VERSION, mode, files: [] })))}`
|
|
6639
|
+
};
|
|
6640
|
+
}
|
|
6641
|
+
function analyzePush(options) {
|
|
6642
|
+
const { rootDir, mode } = options;
|
|
6643
|
+
const projectDir = options.projectDir ?? rootDir;
|
|
6644
|
+
const budgets = { ...DEFAULT_BUDGETS, ...options.budgets };
|
|
6645
|
+
const scan = scanTree(rootDir, mode);
|
|
6646
|
+
const collected = [...scan.files].sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
6647
|
+
const measured = [];
|
|
6648
|
+
const unreadable = [];
|
|
6649
|
+
const embeddedByHash = /* @__PURE__ */ new Map();
|
|
6650
|
+
const embedded = [];
|
|
6651
|
+
let exactUsed = 0;
|
|
6652
|
+
let hashUsed = 0;
|
|
6653
|
+
for (const file of collected) {
|
|
6654
|
+
const cls = classOf(file.rel);
|
|
6655
|
+
const headerBytes = ZIP_ENTRY_HEADER_BYTES + 2 * Buffer.byteLength(file.rel, "utf8");
|
|
6656
|
+
try {
|
|
6657
|
+
const size = (0, import_node_fs8.statSync)(file.abs).size;
|
|
6658
|
+
if (size <= budgets.exactFileBytes && exactUsed + size <= budgets.exactTotalBytes) {
|
|
6659
|
+
const content = (0, import_node_fs8.readFileSync)(file.abs);
|
|
6660
|
+
exactUsed += content.length;
|
|
6661
|
+
const sha2562 = sha256Of(content);
|
|
6662
|
+
const zipBytes2 = headerBytes + deflateSync(content, { level: ZIP_LEVEL }).length;
|
|
6663
|
+
if (EMBEDDED_SCAN_CLASSES.has(cls) && content.length >= EMBEDDED_SCAN_MIN_BYTES) {
|
|
6664
|
+
if (!embeddedByHash.has(sha2562)) embeddedByHash.set(sha2562, scanEmbeddedMedia(content));
|
|
6665
|
+
const found = embeddedByHash.get(sha2562);
|
|
6666
|
+
if (found && found.base64_chars >= EMBEDDED_REPORT_MIN_CHARS) embedded.push({ path: file.rel, ...found });
|
|
6667
|
+
}
|
|
6668
|
+
measured.push({
|
|
6669
|
+
path: file.rel,
|
|
6670
|
+
bytes: content.length,
|
|
6671
|
+
zip_bytes: zipBytes2,
|
|
6672
|
+
zipLower: zipBytes2,
|
|
6673
|
+
sha256: sha2562,
|
|
6674
|
+
class: cls,
|
|
6675
|
+
title: cls === "html" ? htmlTitle(content) : null
|
|
6676
|
+
});
|
|
6677
|
+
continue;
|
|
6678
|
+
}
|
|
6679
|
+
let sha256 = null;
|
|
6680
|
+
if (hashUsed + size <= budgets.hashTotalBytes) {
|
|
6681
|
+
sha256 = sha256OfFile(file.abs);
|
|
6682
|
+
hashUsed += size;
|
|
6683
|
+
}
|
|
6684
|
+
measured.push({
|
|
6685
|
+
path: file.rel,
|
|
6686
|
+
bytes: size,
|
|
6687
|
+
sha256,
|
|
6688
|
+
class: cls,
|
|
6689
|
+
title: null,
|
|
6690
|
+
zip_bytes: headerBytes + deflateUpperBound(size),
|
|
6691
|
+
zipLower: headerBytes + Math.ceil(size / DEFLATE_MAX_RATIO),
|
|
6692
|
+
zip_bytes_estimated: true
|
|
6693
|
+
});
|
|
6694
|
+
} catch (err2) {
|
|
6695
|
+
unreadable.push({ path: file.rel, error: err2.code ?? String(err2) });
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
const zipExact = measured.every((file) => !file.zip_bytes_estimated);
|
|
6699
|
+
const sum = (pick) => measured.reduce((total, file) => total + pick(file), 0);
|
|
6700
|
+
const totalBytes = sum((file) => file.bytes);
|
|
6701
|
+
const zipBytes = measured.length > 0 ? sum((file) => file.zip_bytes) + ZIP_END_BYTES : 0;
|
|
6702
|
+
const zipLower = measured.length > 0 ? sum((file) => file.zipLower) + ZIP_END_BYTES : 0;
|
|
6703
|
+
const view = serverView(measured.map((file) => file.path));
|
|
6704
|
+
const byPathMap = new Map(measured.map((file) => [file.path, file]));
|
|
6705
|
+
const stored = measured.filter((file) => view.stored.has(file.path));
|
|
6706
|
+
const storedBytes = stored.reduce((total, file) => total + file.bytes, 0);
|
|
6707
|
+
const storedNames = new Map([...view.stored].map(([local, name]) => [name, local]));
|
|
6708
|
+
const dropped = view.dropped.map((drop) => ({ path: drop.rel, bytes: byPathMap.get(drop.rel)?.bytes ?? 0, reason: drop.reason, matched: drop.matched })).sort(byPath);
|
|
6709
|
+
const byHash = /* @__PURE__ */ new Map();
|
|
6710
|
+
for (const file of measured) {
|
|
6711
|
+
if (!file.sha256 || file.bytes === 0) continue;
|
|
6712
|
+
const group = byHash.get(file.sha256);
|
|
6713
|
+
if (group) group.push(file);
|
|
6714
|
+
else byHash.set(file.sha256, [file]);
|
|
6715
|
+
}
|
|
6716
|
+
const firstOfContent = /* @__PURE__ */ new Map();
|
|
6717
|
+
const duplicates = [];
|
|
6718
|
+
for (const [sha256, group] of byHash) {
|
|
6719
|
+
firstOfContent.set(sha256, group[0].path);
|
|
6720
|
+
if (group.length < 2) continue;
|
|
6721
|
+
duplicates.push({
|
|
6722
|
+
sha256,
|
|
6723
|
+
bytes: group[0].bytes,
|
|
6724
|
+
paths: group.map((file) => file.path),
|
|
6725
|
+
redundant_zip_bytes: group.slice(1).reduce((total, file) => total + file.zip_bytes, 0)
|
|
6726
|
+
});
|
|
6727
|
+
}
|
|
6728
|
+
duplicates.sort((a, b) => b.redundant_zip_bytes - a.redundant_zip_bytes || (a.paths[0] < b.paths[0] ? -1 : 1));
|
|
6729
|
+
const redundantZipBytes = duplicates.reduce((total, group) => total + group.redundant_zip_bytes, 0);
|
|
6730
|
+
const preflight = mode === "source" ? runPreflight(rootDir) : null;
|
|
6731
|
+
const framework = preflight?.framework ?? null;
|
|
6732
|
+
const hasManifest = storedNames.has("package.json");
|
|
6733
|
+
const entryRequired = mode === "dist" || !hasManifest;
|
|
6734
|
+
const entryPath = storedNames.get("index.html") ?? null;
|
|
6735
|
+
const entryMissing = entryRequired && entryPath === null && measured.length > 0;
|
|
6736
|
+
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) => {
|
|
6737
|
+
const first = file.sha256 ? firstOfContent.get(file.sha256) : void 0;
|
|
6738
|
+
return {
|
|
6739
|
+
path: file.path,
|
|
6740
|
+
bytes: file.bytes,
|
|
6741
|
+
title: file.title,
|
|
6742
|
+
sha256: file.sha256,
|
|
6743
|
+
same_content_as: first && first !== file.path ? first : null
|
|
6744
|
+
};
|
|
6745
|
+
});
|
|
6746
|
+
const maxZipBytes = maxZipBytesForUpload(mode, mode === "source" ? readBackendDeclaration(rootDir) : null);
|
|
6747
|
+
const zipLimit = formatBytes(maxZipBytes, "down");
|
|
6748
|
+
const blockers = [];
|
|
6749
|
+
const warnings = [];
|
|
6750
|
+
const suggestions = [];
|
|
6751
|
+
const dirLabel = mode === "dist" ? (0, import_node_path10.relative)(projectDir, rootDir).split(import_node_path10.sep).join("/") || "." : ".";
|
|
6752
|
+
if (measured.length === 0 && unreadable.length === 0) {
|
|
6753
|
+
blockers.push({
|
|
6754
|
+
code: "empty_upload",
|
|
6755
|
+
overridable: false,
|
|
6756
|
+
message: "Nothing to push \u2014 no files found after applying ignore rules."
|
|
6757
|
+
});
|
|
6758
|
+
}
|
|
6759
|
+
if (unreadable.length > 0) {
|
|
6760
|
+
blockers.push({
|
|
6761
|
+
code: "unreadable_file",
|
|
6762
|
+
overridable: false,
|
|
6763
|
+
message: `${plural(unreadable.length, "file")} cannot be read (${unreadable[0].error}): ${samplePaths(unreadable.map((f) => f.path))}.`,
|
|
6764
|
+
paths: unreadable.map((f) => f.path).slice(0, FINDING_PATHS)
|
|
6765
|
+
});
|
|
6766
|
+
}
|
|
6767
|
+
if (measured.length > MAX_UPLOAD_FILES) {
|
|
6768
|
+
blockers.push({
|
|
6769
|
+
code: "too_many_files",
|
|
6770
|
+
overridable: false,
|
|
6771
|
+
message: `${plural(measured.length, "file")} \u2014 the platform accepts at most ${MAX_UPLOAD_FILES.toLocaleString("en-US")} files per push.`
|
|
6772
|
+
});
|
|
6773
|
+
}
|
|
6774
|
+
if (storedBytes > MAX_UNPACKED_BYTES) {
|
|
6775
|
+
const [size, allowed] = sizeOverLimit(storedBytes, MAX_UNPACKED_BYTES);
|
|
6776
|
+
blockers.push({
|
|
6777
|
+
code: "unpacked_too_large",
|
|
6778
|
+
overridable: false,
|
|
6779
|
+
message: `The files add up to ${size} \u2014 the platform accepts at most ${allowed} of files per push.`
|
|
6780
|
+
});
|
|
6781
|
+
}
|
|
6782
|
+
if (zipLower > maxZipBytes) {
|
|
6783
|
+
const [size, allowed] = sizeOverLimit(zipExact ? zipBytes : zipLower, maxZipBytes);
|
|
6784
|
+
blockers.push({
|
|
6785
|
+
code: "zip_too_large",
|
|
6786
|
+
overridable: false,
|
|
6787
|
+
message: `The upload is ${zipExact ? "" : "at least "}${size} compressed \u2014 a push carries at most ${allowed} compressed.`
|
|
6788
|
+
});
|
|
6789
|
+
} else if (!zipExact && zipBytes > maxZipBytes) {
|
|
6790
|
+
warnings.push({
|
|
6791
|
+
code: "zip_size_unknown",
|
|
6792
|
+
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.`
|
|
6793
|
+
});
|
|
6794
|
+
}
|
|
6795
|
+
if (entryMissing) {
|
|
6796
|
+
blockers.push({
|
|
6797
|
+
code: "entry_missing",
|
|
6798
|
+
overridable: false,
|
|
6799
|
+
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."
|
|
6800
|
+
});
|
|
6801
|
+
}
|
|
6802
|
+
const backend = mode === "source" ? checkBackendDeclaration(rootDir) : null;
|
|
6803
|
+
for (const message of preflight?.issues ?? []) blockers.push({ code: "preflight", overridable: true, message });
|
|
6804
|
+
for (const message of backend?.issues ?? []) blockers.push({ code: "backend_declaration", overridable: true, message });
|
|
6805
|
+
for (const message of preflight?.warnings ?? []) warnings.push({ code: "preflight", message });
|
|
6806
|
+
for (const message of backend?.warnings ?? []) warnings.push({ code: "backend_declaration", message });
|
|
6807
|
+
if (view.strippedPrefix) {
|
|
6808
|
+
warnings.push({
|
|
6809
|
+
code: "wrapper_directory_flattened",
|
|
6810
|
+
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).`
|
|
6811
|
+
});
|
|
6812
|
+
}
|
|
6813
|
+
if (dropped.length > 0) {
|
|
6814
|
+
const matched = [...new Set(dropped.map((drop) => drop.matched))];
|
|
6815
|
+
warnings.push({
|
|
6816
|
+
code: "dropped_by_platform",
|
|
6817
|
+
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.`,
|
|
6818
|
+
paths: dropped.map((drop) => drop.path).slice(0, FINDING_PATHS)
|
|
6819
|
+
});
|
|
6820
|
+
}
|
|
6821
|
+
if (mode === "source") {
|
|
6822
|
+
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();
|
|
6823
|
+
if (lostDirs.length > 0) {
|
|
6824
|
+
warnings.push({
|
|
6825
|
+
code: "excluded_static_directory",
|
|
6826
|
+
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.`,
|
|
6827
|
+
paths: lostDirs.slice(0, FINDING_PATHS)
|
|
6828
|
+
});
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
const filesOmitted = Math.max(0, measured.length - budgets.fileListCap);
|
|
6832
|
+
if (filesOmitted > 0) {
|
|
6833
|
+
warnings.push({
|
|
6834
|
+
code: "file_list_truncated",
|
|
6835
|
+
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.`
|
|
6836
|
+
});
|
|
6837
|
+
}
|
|
6838
|
+
const tooLarge = blockers.some((b) => b.code === "zip_too_large" || b.code === "unpacked_too_large");
|
|
6839
|
+
if (entryMissing) {
|
|
6840
|
+
if (candidates.length > 0) {
|
|
6841
|
+
suggestions.push({
|
|
6842
|
+
code: "name_start_page",
|
|
6843
|
+
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.",
|
|
6844
|
+
paths: candidates.map((candidate) => candidate.path).slice(0, FINDING_PATHS)
|
|
6845
|
+
});
|
|
6846
|
+
}
|
|
6847
|
+
const siteDirs = /* @__PURE__ */ new Set();
|
|
6848
|
+
for (const file of stored) {
|
|
6849
|
+
if (baseName(file.path) === "index.html" && file.path.includes("/")) siteDirs.add(file.path.slice(0, file.path.lastIndexOf("/")));
|
|
6850
|
+
}
|
|
6851
|
+
if (mode === "source") {
|
|
6852
|
+
for (const dir of ["dist", "build", "out", ".output/public"]) {
|
|
6853
|
+
if ((0, import_node_fs8.existsSync)((0, import_node_path10.join)(rootDir, dir, "index.html"))) siteDirs.add(dir);
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
for (const dir of [...siteDirs].sort().slice(0, 5)) {
|
|
6857
|
+
const target = mode === "dist" ? `${dirLabel === "." ? "" : `${dirLabel}/`}${dir}` : dir;
|
|
6858
|
+
suggestions.push({
|
|
6859
|
+
code: "push_site_directory",
|
|
6860
|
+
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.`,
|
|
6861
|
+
command: `agentful push --prebuilt --dir ${target}`
|
|
6862
|
+
});
|
|
6175
6863
|
}
|
|
6176
6864
|
}
|
|
6177
|
-
if (
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6865
|
+
if (tooLarge) {
|
|
6866
|
+
const oversized = measured.filter((file) => file.zipLower > maxZipBytes).sort((a, b) => b.bytes - a.bytes || byPath(a, b));
|
|
6867
|
+
if (oversized.length > 0) {
|
|
6868
|
+
suggestions.push({
|
|
6869
|
+
code: "oversized_files",
|
|
6870
|
+
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.`,
|
|
6871
|
+
paths: oversized.map((file) => file.path).slice(0, FINDING_PATHS)
|
|
6872
|
+
});
|
|
6873
|
+
}
|
|
6874
|
+
if (duplicates.length > 0 && redundantZipBytes >= zipBytes / 20) {
|
|
6875
|
+
const copies = duplicates.reduce((total, group) => total + group.paths.length - 1, 0);
|
|
6876
|
+
const remaining = zipBytes - redundantZipBytes;
|
|
6877
|
+
suggestions.push({
|
|
6878
|
+
code: "remove_identical_copies",
|
|
6879
|
+
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` : ""}.`,
|
|
6880
|
+
paths: duplicates.flatMap((group) => group.paths.slice(1)).slice(0, FINDING_PATHS)
|
|
6881
|
+
});
|
|
6882
|
+
}
|
|
6883
|
+
suggestions.push(mode === "source" ? {
|
|
6884
|
+
code: "exclude_with_gitignore",
|
|
6885
|
+
message: "Files and folders listed in .gitignore are not uploaded. Leave out what the site does not serve \u2014 originals, working files, exports."
|
|
6886
|
+
} : {
|
|
6887
|
+
code: "narrow_prebuilt_directory",
|
|
6888
|
+
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."
|
|
6889
|
+
});
|
|
6890
|
+
suggestions.push({
|
|
6891
|
+
code: "upload_limit",
|
|
6892
|
+
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.`
|
|
6893
|
+
});
|
|
6184
6894
|
}
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6895
|
+
const excludedEntries = [...scan.excluded].sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0).slice(0, EXCLUDED_ENTRIES).map((entry) => {
|
|
6896
|
+
let bytes2;
|
|
6897
|
+
if (entry.kind === "file") {
|
|
6898
|
+
try {
|
|
6899
|
+
bytes2 = (0, import_node_fs8.lstatSync)((0, import_node_path10.join)(rootDir, entry.rel)).size;
|
|
6900
|
+
} catch {
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
return { path: entry.rel, kind: entry.kind, reason: entry.reason, ...bytes2 === void 0 ? {} : { bytes: bytes2 } };
|
|
6192
6904
|
});
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6905
|
+
const fingerprint = sha256Of(Buffer.from(JSON.stringify({
|
|
6906
|
+
v: ANALYSIS_SCHEMA_VERSION,
|
|
6907
|
+
mode,
|
|
6908
|
+
files: measured.map((file) => [file.path, file.bytes, file.sha256])
|
|
6909
|
+
})));
|
|
6910
|
+
return {
|
|
6911
|
+
schema: ANALYSIS_SCHEMA,
|
|
6912
|
+
schema_version: ANALYSIS_SCHEMA_VERSION,
|
|
6913
|
+
cli_version: VERSION,
|
|
6914
|
+
mode,
|
|
6915
|
+
root: rootDir,
|
|
6916
|
+
ok: blockers.length === 0,
|
|
6917
|
+
framework,
|
|
6918
|
+
project: projectLink(projectDir),
|
|
6919
|
+
limits: limits(maxZipBytes),
|
|
6920
|
+
totals: {
|
|
6921
|
+
files: measured.length,
|
|
6922
|
+
bytes: totalBytes,
|
|
6923
|
+
zip_bytes: zipBytes,
|
|
6924
|
+
zip_bytes_exact: zipExact,
|
|
6925
|
+
zip_bytes_without_duplicates: zipBytes - redundantZipBytes,
|
|
6926
|
+
stored_files: stored.length,
|
|
6927
|
+
stored_bytes: storedBytes
|
|
6928
|
+
},
|
|
6929
|
+
entry: { required: entryRequired, expected: "index.html", found: entryPath !== null, path: entryPath, candidates },
|
|
6930
|
+
flattened_prefix: view.strippedPrefix || null,
|
|
6931
|
+
largest_files: [...measured].sort((a, b) => b.bytes - a.bytes || byPath(a, b)).slice(0, LARGEST_FILES).map(publicFile),
|
|
6932
|
+
by_class: groupSizes(measured, (file) => file.class),
|
|
6933
|
+
by_top_level: groupSizes(measured, (file) => file.path.includes("/") ? file.path.slice(0, file.path.indexOf("/")) : ".").slice(0, TOP_LEVEL_GROUPS),
|
|
6934
|
+
duplicates: duplicates.slice(0, DUPLICATE_GROUPS),
|
|
6935
|
+
embedded_media: embedded.sort((a, b) => b.base64_chars - a.base64_chars || byPath(a, b)),
|
|
6936
|
+
excluded: {
|
|
6937
|
+
gitignore_applied: scan.gitignoreApplied,
|
|
6938
|
+
entries: excludedEntries,
|
|
6939
|
+
omitted: Math.max(0, scan.excluded.length - EXCLUDED_ENTRIES)
|
|
6940
|
+
},
|
|
6941
|
+
dropped_by_platform: dropped,
|
|
6942
|
+
blockers,
|
|
6943
|
+
warnings,
|
|
6944
|
+
suggestions,
|
|
6945
|
+
files: measured.slice(0, budgets.fileListCap).map(publicFile),
|
|
6946
|
+
files_omitted: filesOmitted,
|
|
6947
|
+
fingerprint: `sha256:${fingerprint}`
|
|
6948
|
+
};
|
|
6197
6949
|
}
|
|
6198
|
-
function
|
|
6199
|
-
|
|
6200
|
-
const issues = [];
|
|
6201
|
-
const warnings = [];
|
|
6202
|
-
if (framework === "unknown") {
|
|
6203
|
-
issues.push(
|
|
6204
|
-
`No supported framework found in package.json. The cloud build supports: ${SUPPORTED_FRAMEWORKS}.`
|
|
6205
|
-
);
|
|
6206
|
-
}
|
|
6207
|
-
if (framework === "nextjs") {
|
|
6208
|
-
const next = checkNextjs(rootDir);
|
|
6209
|
-
issues.push(...next.issues);
|
|
6210
|
-
warnings.push(...next.warnings);
|
|
6211
|
-
}
|
|
6212
|
-
if (framework !== "vanilla") warnings.push(...checkBuildEnv(rootDir));
|
|
6213
|
-
return { framework, issues, warnings };
|
|
6950
|
+
function wouldUpload(analysis, force = false) {
|
|
6951
|
+
return analysis.blockers.every((blocker) => blocker.overridable && force);
|
|
6214
6952
|
}
|
|
6215
6953
|
|
|
6216
|
-
// src/lib/
|
|
6217
|
-
|
|
6218
|
-
var
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6954
|
+
// src/lib/pushReport.ts
|
|
6955
|
+
init_branding();
|
|
6956
|
+
var EXCLUDE_REASON = {
|
|
6957
|
+
always_excluded_dir: "never uploaded",
|
|
6958
|
+
gitignore: ".gitignore",
|
|
6959
|
+
env_file: "env file, never uploaded",
|
|
6960
|
+
os_junk: "system file",
|
|
6961
|
+
symlink: "symbolic link",
|
|
6962
|
+
not_a_regular_file: "not a regular file"
|
|
6963
|
+
};
|
|
6964
|
+
var SHOWN_FILES = 8;
|
|
6965
|
+
var SHOWN_GROUPS = 6;
|
|
6966
|
+
var SHOWN_EXCLUDED = 12;
|
|
6967
|
+
var count = (n, singular, pluralForm = `${singular}s`) => `${n.toLocaleString("en-US")} ${n === 1 ? singular : pluralForm}`;
|
|
6968
|
+
var more = (total, shown, what) => total > shown ? [paint.dim(` \u2026 and ${count(total - shown, `more ${what}`, `more ${what}s`)}`)] : [];
|
|
6969
|
+
var bullet = (message) => message.split("\n").map((line, index) => index === 0 ? ` \u2022 ${line}` : ` ${line.trimStart()}`);
|
|
6970
|
+
var sizeColumn = (bytes2) => formatBytes(bytes2).padStart(9);
|
|
6971
|
+
var fileLine = (file) => ` ${sizeColumn(file.bytes)} ${file.path}` + paint.dim(` (${file.zip_bytes_estimated ? "up to " : ""}${formatBytes(file.zip_bytes)} compressed)`);
|
|
6972
|
+
function headline(analysis) {
|
|
6973
|
+
if (analysis.mode === "dist") return "prebuilt upload, served as it is (no cloud build)";
|
|
6974
|
+
if (analysis.framework === "vanilla") return "source push, static site without a build step";
|
|
6975
|
+
if (analysis.framework === "unknown" || analysis.framework === null) return "source push";
|
|
6976
|
+
return `source push, built in the cloud as ${frameworkDisplayName(analysis.framework)}`;
|
|
6977
|
+
}
|
|
6978
|
+
function renderAnalysis(analysis, options = {}) {
|
|
6979
|
+
const { totals, limits: limits2 } = analysis;
|
|
6980
|
+
const lines = [];
|
|
6981
|
+
const section = (title) => {
|
|
6982
|
+
lines.push("", paint.bold(title));
|
|
6983
|
+
};
|
|
6984
|
+
lines.push(paint.bold("Upload analysis") + paint.dim(`: ${headline(analysis)}`));
|
|
6985
|
+
if (analysis.root) lines.push(paint.dim(analysis.root));
|
|
6986
|
+
if (totals.files > 0) {
|
|
6987
|
+
lines.push(
|
|
6988
|
+
`${count(totals.files, "file")} \xB7 ${formatBytes(totals.bytes)} \xB7 ${totals.zip_bytes_exact ? "" : "up to "}${formatBytes(totals.zip_bytes)} compressed`
|
|
6989
|
+
);
|
|
6990
|
+
}
|
|
6991
|
+
lines.push(paint.dim(
|
|
6992
|
+
`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`
|
|
6993
|
+
));
|
|
6994
|
+
if (analysis.blockers.length > 0) {
|
|
6995
|
+
lines.push("", `${sym.fail} ${paint.bold("This push cannot go through as it is:")}`);
|
|
6996
|
+
for (const blocker of analysis.blockers) {
|
|
6997
|
+
lines.push(...bullet(blocker.message + (blocker.overridable ? paint.dim(" [--force uploads anyway]") : "")));
|
|
6241
6998
|
}
|
|
6242
|
-
return decl;
|
|
6243
|
-
} catch {
|
|
6244
|
-
return null;
|
|
6245
6999
|
}
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
return Array.isArray(raw) ? raw.filter((m) => typeof m === "string") : [];
|
|
6250
|
-
}
|
|
6251
|
-
function describeBackendDeclaration(decl) {
|
|
6252
|
-
const parts = [];
|
|
6253
|
-
const collections = decl.collections ?? [];
|
|
6254
|
-
if (decl.managed_db || collections.length > 0) {
|
|
6255
|
-
parts.push(collections.length > 0 ? `managed DB with ${collections.length} collection(s)` : "managed DB");
|
|
7000
|
+
if (analysis.warnings.length > 0) {
|
|
7001
|
+
section("Warnings");
|
|
7002
|
+
for (const warning of analysis.warnings) lines.push(...bullet(warning.message));
|
|
6256
7003
|
}
|
|
6257
|
-
if (
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
const presentFiles = /* @__PURE__ */ new Set();
|
|
6274
|
-
if ((0, import_node_fs7.existsSync)(actionsDir)) {
|
|
6275
|
-
for (const entry of (0, import_node_fs7.readdirSync)(actionsDir)) {
|
|
6276
|
-
const abs = (0, import_node_path9.join)(actionsDir, entry);
|
|
6277
|
-
let isDir = false;
|
|
6278
|
-
try {
|
|
6279
|
-
isDir = (0, import_node_fs7.statSync)(abs).isDirectory();
|
|
6280
|
-
} catch {
|
|
6281
|
-
continue;
|
|
7004
|
+
if (analysis.entry.required && totals.files > 0) {
|
|
7005
|
+
section("Start page");
|
|
7006
|
+
if (analysis.entry.found) {
|
|
7007
|
+
lines.push(` ${analysis.entry.path}`);
|
|
7008
|
+
} else {
|
|
7009
|
+
lines.push(" missing \u2014 the platform serves index.html from the top level");
|
|
7010
|
+
if (analysis.entry.candidates.length > 0) {
|
|
7011
|
+
lines.push(" HTML files found:");
|
|
7012
|
+
for (const candidate of analysis.entry.candidates.slice(0, SHOWN_FILES)) {
|
|
7013
|
+
lines.push(
|
|
7014
|
+
` ${sizeColumn(candidate.bytes)} ${candidate.path}` + (candidate.title ? paint.dim(` "${candidate.title}"`) : "") + (candidate.same_content_as ? paint.dim(` (same content as ${candidate.same_content_as})`) : "")
|
|
7015
|
+
);
|
|
7016
|
+
}
|
|
7017
|
+
lines.push(...more(analysis.entry.candidates.length, SHOWN_FILES, "HTML file"));
|
|
7018
|
+
} else {
|
|
7019
|
+
lines.push(" no HTML file in the upload");
|
|
6282
7020
|
}
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
7021
|
+
}
|
|
7022
|
+
}
|
|
7023
|
+
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");
|
|
7024
|
+
if (totals.files > 0 && (sizeMatters || options.dryRun)) {
|
|
7025
|
+
section("Largest files");
|
|
7026
|
+
for (const file of analysis.largest_files.slice(0, SHOWN_FILES)) lines.push(fileLine(file));
|
|
7027
|
+
lines.push(...more(totals.files, Math.min(SHOWN_FILES, analysis.largest_files.length), "file"));
|
|
7028
|
+
section("By type");
|
|
7029
|
+
for (const group of analysis.by_class.slice(0, SHOWN_GROUPS)) {
|
|
7030
|
+
lines.push(
|
|
7031
|
+
` ${sizeColumn(group.bytes)} ${group.name.padEnd(9)} ${count(group.files, "file")}` + paint.dim(` (${formatBytes(group.zip_bytes)} compressed)`)
|
|
7032
|
+
);
|
|
7033
|
+
}
|
|
7034
|
+
if (analysis.duplicates.length > 0) {
|
|
7035
|
+
section("Identical content");
|
|
7036
|
+
for (const group of analysis.duplicates.slice(0, SHOWN_GROUPS)) {
|
|
7037
|
+
lines.push(
|
|
7038
|
+
` ${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
7039
|
);
|
|
6287
|
-
continue;
|
|
6288
7040
|
}
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
7041
|
+
lines.push(...more(analysis.duplicates.length, SHOWN_GROUPS, "group"));
|
|
7042
|
+
}
|
|
7043
|
+
if (analysis.embedded_media.length > 0) {
|
|
7044
|
+
section("Media embedded in text files");
|
|
7045
|
+
const sizeOf = new Map(analysis.files.map((file) => [file.path, file.bytes]));
|
|
7046
|
+
for (const item of analysis.embedded_media.slice(0, SHOWN_GROUPS)) {
|
|
7047
|
+
const fileBytes = sizeOf.get(item.path);
|
|
7048
|
+
lines.push(
|
|
7049
|
+
` ${item.path}: ${count(item.references, "embedded file")} (data: URLs), ${formatBytes(item.base64_chars)}` + (fileBytes ? ` of its ${formatBytes(fileBytes)}` : "")
|
|
6294
7050
|
);
|
|
6295
7051
|
}
|
|
7052
|
+
lines.push(...more(analysis.embedded_media.length, SHOWN_GROUPS, "file"));
|
|
6296
7053
|
}
|
|
6297
7054
|
}
|
|
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.`
|
|
7055
|
+
if (options.dryRun && analysis.excluded.entries.length > 0) {
|
|
7056
|
+
section("Not uploaded");
|
|
7057
|
+
for (const entry of analysis.excluded.entries.slice(0, SHOWN_EXCLUDED)) {
|
|
7058
|
+
lines.push(
|
|
7059
|
+
` ${entry.path}${entry.kind === "dir" ? "/" : ""}` + paint.dim(` (${EXCLUDE_REASON[entry.reason] ?? entry.reason}${entry.bytes === void 0 ? "" : `, ${formatBytes(entry.bytes)}`})`)
|
|
6310
7060
|
);
|
|
6311
7061
|
}
|
|
7062
|
+
lines.push(...more(analysis.excluded.entries.length + analysis.excluded.omitted, SHOWN_EXCLUDED, "entry"));
|
|
6312
7063
|
}
|
|
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
|
-
);
|
|
7064
|
+
if (analysis.suggestions.length > 0) {
|
|
7065
|
+
section("What you can do");
|
|
7066
|
+
for (const suggestion of analysis.suggestions) {
|
|
7067
|
+
lines.push(...bullet(suggestion.message));
|
|
7068
|
+
if (suggestion.command) lines.push(` ${paint.accent(suggestion.command)}`);
|
|
6320
7069
|
}
|
|
6321
7070
|
}
|
|
6322
|
-
if (
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
7071
|
+
if (options.dryRun) {
|
|
7072
|
+
lines.push("");
|
|
7073
|
+
if (wouldUpload(analysis, options.force)) {
|
|
7074
|
+
lines.push(`${sym.ok} Nothing blocks this push.` + paint.dim(" Dry run: nothing was uploaded, no project was created."));
|
|
7075
|
+
} else {
|
|
7076
|
+
lines.push(`${sym.fail} ${paint.error("agentful push would stop here.")}` + paint.dim(" Dry run: nothing was uploaded, no project was created."));
|
|
7077
|
+
}
|
|
6326
7078
|
}
|
|
6327
|
-
return
|
|
7079
|
+
return lines;
|
|
6328
7080
|
}
|
|
6329
7081
|
|
|
6330
7082
|
// src/lib/backendSync.ts
|
|
@@ -6493,8 +7245,8 @@ function describeBackendState(state) {
|
|
|
6493
7245
|
}
|
|
6494
7246
|
|
|
6495
7247
|
// src/lib/backendConsent.ts
|
|
6496
|
-
var
|
|
6497
|
-
var
|
|
7248
|
+
var import_node_fs9 = require("fs");
|
|
7249
|
+
var import_node_path11 = require("path");
|
|
6498
7250
|
var import_promises2 = require("readline/promises");
|
|
6499
7251
|
function declaresManagedDatabase(decl) {
|
|
6500
7252
|
if (!decl) return false;
|
|
@@ -6520,10 +7272,10 @@ function consentQuestion(decl) {
|
|
|
6520
7272
|
'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
7273
|
].join("\n");
|
|
6522
7274
|
}
|
|
6523
|
-
var consentFile = () => (0,
|
|
7275
|
+
var consentFile = () => (0, import_node_path11.join)(configDir(), "backend-consent.json");
|
|
6524
7276
|
function readConsentFile() {
|
|
6525
7277
|
try {
|
|
6526
|
-
const raw = JSON.parse((0,
|
|
7278
|
+
const raw = JSON.parse((0, import_node_fs9.readFileSync)(consentFile(), "utf8"));
|
|
6527
7279
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
6528
7280
|
} catch {
|
|
6529
7281
|
return {};
|
|
@@ -6535,15 +7287,15 @@ function hasRememberedDecline(projectId) {
|
|
|
6535
7287
|
function rememberDecline(projectId) {
|
|
6536
7288
|
const all = readConsentFile();
|
|
6537
7289
|
all[projectId] = { declined_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6538
|
-
(0,
|
|
6539
|
-
(0,
|
|
7290
|
+
(0, import_node_fs9.mkdirSync)(configDir(), { recursive: true });
|
|
7291
|
+
(0, import_node_fs9.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
|
|
6540
7292
|
}
|
|
6541
7293
|
function forgetDecline(projectId) {
|
|
6542
|
-
if (!(0,
|
|
7294
|
+
if (!(0, import_node_fs9.existsSync)(consentFile())) return;
|
|
6543
7295
|
const all = readConsentFile();
|
|
6544
7296
|
if (!(projectId in all)) return;
|
|
6545
7297
|
delete all[projectId];
|
|
6546
|
-
(0,
|
|
7298
|
+
(0, import_node_fs9.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
|
|
6547
7299
|
}
|
|
6548
7300
|
async function enableManagedDatabase(auth, userId, projectId) {
|
|
6549
7301
|
try {
|
|
@@ -6659,34 +7411,61 @@ function brandPreviewUrl(previewUrl, projectId) {
|
|
|
6659
7411
|
if (!previewUrl) return fallback;
|
|
6660
7412
|
try {
|
|
6661
7413
|
const url = new URL(previewUrl);
|
|
6662
|
-
|
|
6663
|
-
url.hostname = url.hostname.replace(/\.mainmvp\.com$/, `.${PREVIEW_DOMAIN}`);
|
|
6664
|
-
}
|
|
7414
|
+
url.hostname = url.hostname.replace(/\.(?:mainmvp\.com|agentful\.dev)$/, `.${PREVIEW_DOMAIN}`);
|
|
6665
7415
|
return url.toString();
|
|
6666
7416
|
} catch {
|
|
6667
7417
|
return fallback;
|
|
6668
7418
|
}
|
|
6669
7419
|
}
|
|
6670
7420
|
async function pushCommand(opts) {
|
|
6671
|
-
|
|
7421
|
+
if (opts.json && !opts.dryRun) {
|
|
7422
|
+
throw new ApiError(
|
|
7423
|
+
0,
|
|
7424
|
+
"invalid_options",
|
|
7425
|
+
"--json belongs to --dry-run: `agentful push --dry-run --json` prints the upload analysis and uploads nothing."
|
|
7426
|
+
);
|
|
7427
|
+
}
|
|
7428
|
+
if (!opts.json) console.log(banner());
|
|
6672
7429
|
const mode = opts.prebuilt ? "dist" : "source";
|
|
6673
|
-
const
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
7430
|
+
const cwd = process.cwd();
|
|
7431
|
+
let rootDir = cwd;
|
|
7432
|
+
let analysis;
|
|
7433
|
+
try {
|
|
7434
|
+
if (opts.prebuilt) rootDir = detectDistDir(cwd, opts.dir);
|
|
7435
|
+
analysis = analyzePush({ rootDir, mode, projectDir: cwd });
|
|
7436
|
+
} catch (err2) {
|
|
7437
|
+
if (!opts.dryRun || !(err2 instanceof ApiError)) throw err2;
|
|
7438
|
+
analysis = failedAnalysis(mode, cwd, { code: err2.code, message: err2.message });
|
|
7439
|
+
}
|
|
7440
|
+
if (opts.dryRun) {
|
|
7441
|
+
if (opts.json) process.stdout.write(`${JSON.stringify(analysis, null, 2)}
|
|
7442
|
+
`);
|
|
7443
|
+
else for (const line of renderAnalysis(analysis, { force: opts.force, dryRun: true })) console.log(line);
|
|
7444
|
+
if (!wouldUpload(analysis, opts.force)) process.exitCode = 1;
|
|
7445
|
+
return;
|
|
7446
|
+
}
|
|
7447
|
+
const overridable = analysis.blockers.filter((blocker) => blocker.overridable);
|
|
7448
|
+
if (analysis.blockers.length > overridable.length) {
|
|
7449
|
+
for (const line of renderAnalysis(analysis, { force: opts.force })) console.log(line);
|
|
7450
|
+
console.log("");
|
|
7451
|
+
throw new ApiError(
|
|
7452
|
+
0,
|
|
7453
|
+
"push_blocked",
|
|
7454
|
+
"Push stopped before sign-in: nothing was uploaded and no project was created. `agentful push --dry-run` shows this analysis again."
|
|
7455
|
+
);
|
|
7456
|
+
}
|
|
7457
|
+
for (const warning of analysis.warnings) ui.warn(warning.message);
|
|
7458
|
+
if (overridable.length > 0) {
|
|
7459
|
+
const issues = overridable.map((blocker) => blocker.message);
|
|
7460
|
+
if (opts.force) {
|
|
7461
|
+
ui.warn(`Preflight found ${issues.length} blocking issue(s) \u2014 continuing because of --force:`);
|
|
7462
|
+
for (const issue of issues) ui.info(issue);
|
|
7463
|
+
} else {
|
|
7464
|
+
throw new ApiError(
|
|
7465
|
+
0,
|
|
7466
|
+
"preflight_failed",
|
|
7467
|
+
"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."
|
|
7468
|
+
);
|
|
6690
7469
|
}
|
|
6691
7470
|
}
|
|
6692
7471
|
const auth = await ensureAuth();
|
|
@@ -6705,17 +7484,10 @@ async function pushCommand(opts) {
|
|
|
6705
7484
|
);
|
|
6706
7485
|
}
|
|
6707
7486
|
ui.step(mode === "source" ? "Packing source tree\u2026" : `Packing built output (${rootDir})\u2026`);
|
|
6708
|
-
const files = collectFiles(rootDir, mode);
|
|
6709
|
-
const zip = zipToBase64(files);
|
|
6710
|
-
ui.info(`${zip.count} files, ${(zip.bytes / 1024).toFixed(0)} KB compressed`);
|
|
6711
|
-
if (zip.bytes > MAX_ZIP_UPLOAD_BYTES) {
|
|
6712
|
-
throw new ApiError(
|
|
6713
|
-
0,
|
|
6714
|
-
"zip_too_large",
|
|
6715
|
-
`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.`
|
|
6716
|
-
);
|
|
6717
|
-
}
|
|
6718
7487
|
const declaration = mode === "source" ? readBackendDeclaration() : null;
|
|
7488
|
+
const files = collectFiles(rootDir, mode);
|
|
7489
|
+
const zip = zipToBase64(files, maxZipBytesForUpload(mode, declaration));
|
|
7490
|
+
ui.info(`${zip.count} files, ${(zip.bytes / 1024).toFixed(0)} KiB compressed`);
|
|
6719
7491
|
if (declaration) {
|
|
6720
7492
|
const consent = await ensureDatabaseConsent({
|
|
6721
7493
|
auth,
|
|
@@ -6736,12 +7508,7 @@ async function pushCommand(opts) {
|
|
|
6736
7508
|
`${API_URL}/api/workspaces/${userId}/${projectId}/source-zip`,
|
|
6737
7509
|
{
|
|
6738
7510
|
token: auth.pb_token,
|
|
6739
|
-
body:
|
|
6740
|
-
zip_base64: zip.base64,
|
|
6741
|
-
mode,
|
|
6742
|
-
clean: true,
|
|
6743
|
-
...declaration ? { backend_declaration: declaration } : {}
|
|
6744
|
-
},
|
|
7511
|
+
body: uploadRequestBody(zip.base64, mode, declaration),
|
|
6745
7512
|
timeoutMs: 12e4,
|
|
6746
7513
|
retry: platformRetry
|
|
6747
7514
|
}
|
|
@@ -6921,15 +7688,15 @@ async function openCommand() {
|
|
|
6921
7688
|
// src/commands/tui.ts
|
|
6922
7689
|
var import_node_child_process6 = require("child_process");
|
|
6923
7690
|
var import_promises4 = require("readline/promises");
|
|
6924
|
-
var
|
|
7691
|
+
var import_node_path22 = require("path");
|
|
6925
7692
|
|
|
6926
7693
|
// src/lib/binWrapper.ts
|
|
6927
|
-
var
|
|
6928
|
-
var
|
|
7694
|
+
var import_node_fs10 = require("fs");
|
|
7695
|
+
var import_node_path12 = require("path");
|
|
6929
7696
|
var import_node_os3 = require("os");
|
|
6930
7697
|
function ensureCliOnPath() {
|
|
6931
|
-
const binDir = (0,
|
|
6932
|
-
(0,
|
|
7698
|
+
const binDir = (0, import_node_path12.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
|
|
7699
|
+
(0, import_node_fs10.mkdirSync)(binDir, { recursive: true });
|
|
6933
7700
|
const entry = process.argv[1];
|
|
6934
7701
|
if (!entry) return binDir;
|
|
6935
7702
|
const restore = (name) => {
|
|
@@ -6938,8 +7705,8 @@ function ensureCliOnPath() {
|
|
|
6938
7705
|
` : `export ${name}=${JSON.stringify(original)}
|
|
6939
7706
|
`;
|
|
6940
7707
|
};
|
|
6941
|
-
const wrapper = (0,
|
|
6942
|
-
(0,
|
|
7708
|
+
const wrapper = (0, import_node_path12.join)(binDir, "agentful");
|
|
7709
|
+
(0, import_node_fs10.writeFileSync)(
|
|
6943
7710
|
wrapper,
|
|
6944
7711
|
`#!/bin/sh
|
|
6945
7712
|
# Generated by the Agentful CLI so the coding agent can run \`agentful \u2026\`.
|
|
@@ -6949,7 +7716,7 @@ function ensureCliOnPath() {
|
|
|
6949
7716
|
"utf8"
|
|
6950
7717
|
);
|
|
6951
7718
|
try {
|
|
6952
|
-
(0,
|
|
7719
|
+
(0, import_node_fs10.chmodSync)(wrapper, 493);
|
|
6953
7720
|
} catch {
|
|
6954
7721
|
}
|
|
6955
7722
|
return binDir;
|
|
@@ -6959,9 +7726,9 @@ function ensureCliOnPath() {
|
|
|
6959
7726
|
init_branding();
|
|
6960
7727
|
|
|
6961
7728
|
// src/lib/engine.ts
|
|
6962
|
-
var
|
|
6963
|
-
var
|
|
6964
|
-
var
|
|
7729
|
+
var import_node_fs11 = require("fs");
|
|
7730
|
+
var import_node_crypto2 = require("crypto");
|
|
7731
|
+
var import_node_path13 = require("path");
|
|
6965
7732
|
var import_node_os4 = require("os");
|
|
6966
7733
|
var import_node_child_process3 = require("child_process");
|
|
6967
7734
|
var import_node_stream = require("stream");
|
|
@@ -7006,8 +7773,8 @@ function platformKey() {
|
|
|
7006
7773
|
);
|
|
7007
7774
|
}
|
|
7008
7775
|
function cacheDir(version) {
|
|
7009
|
-
const base = process.env.XDG_CACHE_HOME || (0,
|
|
7010
|
-
return (0,
|
|
7776
|
+
const base = process.env.XDG_CACHE_HOME || (0, import_node_path13.join)((0, import_node_os4.homedir)(), ".cache");
|
|
7777
|
+
return (0, import_node_path13.join)(base, "agentful", "engine", version);
|
|
7011
7778
|
}
|
|
7012
7779
|
async function fetchManifest() {
|
|
7013
7780
|
try {
|
|
@@ -7017,8 +7784,8 @@ async function fetchManifest() {
|
|
|
7017
7784
|
}
|
|
7018
7785
|
}
|
|
7019
7786
|
async function sha256File(path) {
|
|
7020
|
-
const hash = (0,
|
|
7021
|
-
await (0, import_promises3.pipeline)((0,
|
|
7787
|
+
const hash = (0, import_node_crypto2.createHash)("sha256");
|
|
7788
|
+
await (0, import_promises3.pipeline)((0, import_node_fs11.createReadStream)(path), hash);
|
|
7022
7789
|
return hash.digest("hex");
|
|
7023
7790
|
}
|
|
7024
7791
|
async function ensureEngine() {
|
|
@@ -7027,12 +7794,12 @@ async function ensureEngine() {
|
|
|
7027
7794
|
noticeIfOutdated(manifest);
|
|
7028
7795
|
const version = manifest?.engine_version || FALLBACK_ENGINE_VERSION;
|
|
7029
7796
|
const dir = cacheDir(version);
|
|
7030
|
-
const binPath = (0,
|
|
7031
|
-
if ((0,
|
|
7797
|
+
const binPath = (0, import_node_path13.join)(dir, ENGINE_BINARY);
|
|
7798
|
+
if ((0, import_node_fs11.existsSync)(binPath)) return binPath;
|
|
7032
7799
|
const artifact = manifest?.artifacts?.[key];
|
|
7033
7800
|
const url = artifact?.url || `${APP_URL}/cli/engine/${version}/${key}${key.startsWith("darwin") ? ".zip" : ".tar.gz"}`;
|
|
7034
|
-
(0,
|
|
7035
|
-
const archivePath = (0,
|
|
7801
|
+
(0, import_node_fs11.mkdirSync)(dir, { recursive: true });
|
|
7802
|
+
const archivePath = (0, import_node_path13.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
|
|
7036
7803
|
ui.step(`Preparing the ${brand.displayName} engine (first run only)\u2026`);
|
|
7037
7804
|
const resp = await fetch(url, {
|
|
7038
7805
|
redirect: "follow",
|
|
@@ -7046,11 +7813,11 @@ async function ensureEngine() {
|
|
|
7046
7813
|
`Could not download the engine (HTTP ${resp.status}). Check your connection and try again.`
|
|
7047
7814
|
);
|
|
7048
7815
|
}
|
|
7049
|
-
await (0, import_promises3.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0,
|
|
7816
|
+
await (0, import_promises3.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs11.createWriteStream)(archivePath));
|
|
7050
7817
|
if (artifact?.sha256) {
|
|
7051
7818
|
const actual = await sha256File(archivePath);
|
|
7052
7819
|
if (actual !== artifact.sha256) {
|
|
7053
|
-
(0,
|
|
7820
|
+
(0, import_node_fs11.rmSync)(archivePath, { force: true });
|
|
7054
7821
|
throw new ApiError(
|
|
7055
7822
|
0,
|
|
7056
7823
|
"engine_checksum_mismatch",
|
|
@@ -7059,7 +7826,7 @@ async function ensureEngine() {
|
|
|
7059
7826
|
}
|
|
7060
7827
|
}
|
|
7061
7828
|
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" });
|
|
7062
|
-
(0,
|
|
7829
|
+
(0, import_node_fs11.rmSync)(archivePath, { force: true });
|
|
7063
7830
|
if (extract.status !== 0) {
|
|
7064
7831
|
throw new ApiError(
|
|
7065
7832
|
0,
|
|
@@ -7067,24 +7834,24 @@ async function ensureEngine() {
|
|
|
7067
7834
|
`Could not unpack the engine (needs ${archivePath.endsWith(".zip") ? "unzip" : "tar"} on PATH).`
|
|
7068
7835
|
);
|
|
7069
7836
|
}
|
|
7070
|
-
const extracted = (0,
|
|
7071
|
-
if ((0,
|
|
7072
|
-
if (!(0,
|
|
7837
|
+
const extracted = (0, import_node_path13.join)(dir, "opencode");
|
|
7838
|
+
if ((0, import_node_fs11.existsSync)(extracted) && !(0, import_node_fs11.existsSync)(binPath)) (0, import_node_fs11.renameSync)(extracted, binPath);
|
|
7839
|
+
if (!(0, import_node_fs11.existsSync)(binPath)) {
|
|
7073
7840
|
throw new ApiError(0, "engine_extract_failed", "Engine archive did not contain the expected binary.");
|
|
7074
7841
|
}
|
|
7075
|
-
(0,
|
|
7842
|
+
(0, import_node_fs11.chmodSync)(binPath, 493);
|
|
7076
7843
|
ui.ok(`Engine ready.`);
|
|
7077
7844
|
console.log(paint.dim(" powered by opencode (MIT) \xB7 run `agentful licenses` for details"));
|
|
7078
7845
|
return binPath;
|
|
7079
7846
|
}
|
|
7080
7847
|
|
|
7081
7848
|
// src/lib/gatewayToken.ts
|
|
7082
|
-
var
|
|
7083
|
-
var
|
|
7084
|
-
var cachePath = () => (0,
|
|
7849
|
+
var import_node_fs12 = require("fs");
|
|
7850
|
+
var import_node_path14 = require("path");
|
|
7851
|
+
var cachePath = () => (0, import_node_path14.join)(configDir(), "gateway.json");
|
|
7085
7852
|
async function ensureGatewaySession(auth, projectId) {
|
|
7086
7853
|
try {
|
|
7087
|
-
const cached = JSON.parse((0,
|
|
7854
|
+
const cached = JSON.parse((0, import_node_fs12.readFileSync)(cachePath(), "utf8"));
|
|
7088
7855
|
if (cached?.token && cached.expires_at - Date.now() / 1e3 > 3600) {
|
|
7089
7856
|
return cached;
|
|
7090
7857
|
}
|
|
@@ -7095,18 +7862,18 @@ async function ensureGatewaySession(auth, projectId) {
|
|
|
7095
7862
|
body: projectId ? { project_id: projectId } : {},
|
|
7096
7863
|
timeoutMs: 3e4
|
|
7097
7864
|
});
|
|
7098
|
-
(0,
|
|
7099
|
-
(0,
|
|
7865
|
+
(0, import_node_fs12.mkdirSync)(configDir(), { recursive: true });
|
|
7866
|
+
(0, import_node_fs12.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
|
|
7100
7867
|
try {
|
|
7101
|
-
(0,
|
|
7868
|
+
(0, import_node_fs12.chmodSync)(cachePath(), 384);
|
|
7102
7869
|
} catch {
|
|
7103
7870
|
}
|
|
7104
7871
|
return res;
|
|
7105
7872
|
}
|
|
7106
7873
|
|
|
7107
7874
|
// src/lib/engineConfig.ts
|
|
7108
|
-
var
|
|
7109
|
-
var
|
|
7875
|
+
var import_node_fs14 = require("fs");
|
|
7876
|
+
var import_node_path16 = require("path");
|
|
7110
7877
|
var import_node_os5 = require("os");
|
|
7111
7878
|
|
|
7112
7879
|
// src/branding/agentful-theme.json
|
|
@@ -7233,7 +8000,7 @@ var SLASH_COMMANDS = {
|
|
|
7233
8000
|
},
|
|
7234
8001
|
status: {
|
|
7235
8002
|
description: `${brand.displayName}: show the linked project and account`,
|
|
7236
|
-
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.
|
|
8003
|
+
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."
|
|
7237
8004
|
}
|
|
7238
8005
|
};
|
|
7239
8006
|
function commandMarkdown(name) {
|
|
@@ -7249,22 +8016,22 @@ ${cmd.template}
|
|
|
7249
8016
|
}
|
|
7250
8017
|
|
|
7251
8018
|
// src/lib/tools.ts
|
|
7252
|
-
var
|
|
7253
|
-
var
|
|
8019
|
+
var import_node_fs13 = require("fs");
|
|
8020
|
+
var import_node_path15 = require("path");
|
|
7254
8021
|
function assetsDir() {
|
|
7255
|
-
const here = (0,
|
|
7256
|
-
for (const candidate of [(0,
|
|
7257
|
-
if ((0,
|
|
8022
|
+
const here = (0, import_node_path15.dirname)(process.argv[1] || "");
|
|
8023
|
+
for (const candidate of [(0, import_node_path15.join)(here, "..", "assets"), (0, import_node_path15.join)(here, "assets")]) {
|
|
8024
|
+
if ((0, import_node_fs13.existsSync)((0, import_node_path15.join)(candidate, "tools", "imagegen.ts"))) return candidate;
|
|
7258
8025
|
}
|
|
7259
8026
|
return null;
|
|
7260
8027
|
}
|
|
7261
8028
|
function installEngineTools(configDir2) {
|
|
7262
8029
|
const assets = assetsDir();
|
|
7263
8030
|
if (!assets) return false;
|
|
7264
|
-
const target = (0,
|
|
7265
|
-
(0,
|
|
8031
|
+
const target = (0, import_node_path15.join)(configDir2, "tools");
|
|
8032
|
+
(0, import_node_fs13.mkdirSync)(target, { recursive: true });
|
|
7266
8033
|
try {
|
|
7267
|
-
(0,
|
|
8034
|
+
(0, import_node_fs13.cpSync)((0, import_node_path15.join)(assets, "tools"), target, { recursive: true });
|
|
7268
8035
|
return true;
|
|
7269
8036
|
} catch {
|
|
7270
8037
|
return false;
|
|
@@ -7274,7 +8041,7 @@ function installEngineTools(configDir2) {
|
|
|
7274
8041
|
// src/lib/cloudTarget.ts
|
|
7275
8042
|
var CLOUD_INSTRUCTIONS_FILENAME = "AGENTFUL_CLOUD.md";
|
|
7276
8043
|
var FRAMEWORKS = contract_default.frameworks;
|
|
7277
|
-
function
|
|
8044
|
+
function frameworkDisplayName2(framework) {
|
|
7278
8045
|
if (framework === "unknown") return "no supported framework";
|
|
7279
8046
|
return FRAMEWORKS.find((f) => f.id === framework)?.display_name ?? framework;
|
|
7280
8047
|
}
|
|
@@ -7287,7 +8054,7 @@ function frameworkTable() {
|
|
|
7287
8054
|
return rows.join("\n");
|
|
7288
8055
|
}
|
|
7289
8056
|
function sessionLine(framework) {
|
|
7290
|
-
const name =
|
|
8057
|
+
const name = frameworkDisplayName2(framework);
|
|
7291
8058
|
switch (framework) {
|
|
7292
8059
|
case "nextjs":
|
|
7293
8060
|
return `Detected framework in this directory: **${name}** \u2014 next.config must keep \`output: 'export'\`; no route handlers/middleware/server actions.`;
|
|
@@ -7345,7 +8112,7 @@ function buildEnvList() {
|
|
|
7345
8112
|
var bytes = (n) => n >= 1024 * 1024 ? `${n / (1024 * 1024)} MB` : `${n / 1024} KB`;
|
|
7346
8113
|
function serverSideSection() {
|
|
7347
8114
|
const tracks = contract_default.server_side;
|
|
7348
|
-
const track = (key, heading,
|
|
8115
|
+
const track = (key, heading, limits2) => {
|
|
7349
8116
|
const t = tracks[key];
|
|
7350
8117
|
const out = [
|
|
7351
8118
|
`### ${heading} ${t.label}`,
|
|
@@ -7359,7 +8126,7 @@ function serverSideSection() {
|
|
|
7359
8126
|
"Does NOT offer \u2014 name the limit and offer the way out, never promise it:",
|
|
7360
8127
|
...t.not_offered.map((n) => `- **${n.what}.** ${n.why}. Way out: ${n.workaround}.`)
|
|
7361
8128
|
];
|
|
7362
|
-
if (
|
|
8129
|
+
if (limits2) out.push("", `Limits: ${limits2}`);
|
|
7363
8130
|
if (t.limits_note) out.push("", t.limits_note);
|
|
7364
8131
|
return out.join("\n");
|
|
7365
8132
|
};
|
|
@@ -7568,7 +8335,7 @@ var CLOUD_AGENT_PROMPT = `You are the Agentful Cloud build agent: everything you
|
|
|
7568
8335
|
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.`;
|
|
7569
8336
|
function readAuthFile(path) {
|
|
7570
8337
|
try {
|
|
7571
|
-
return (0,
|
|
8338
|
+
return (0, import_node_fs14.readFileSync)(path, "utf8");
|
|
7572
8339
|
} catch {
|
|
7573
8340
|
return null;
|
|
7574
8341
|
}
|
|
@@ -7638,11 +8405,11 @@ function buildEngineConfig(opts) {
|
|
|
7638
8405
|
};
|
|
7639
8406
|
}
|
|
7640
8407
|
function engineXdg() {
|
|
7641
|
-
const root = (0,
|
|
8408
|
+
const root = (0, import_node_path16.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
|
|
7642
8409
|
return {
|
|
7643
|
-
configHome: (0,
|
|
7644
|
-
dataHome: (0,
|
|
7645
|
-
stateHome: (0,
|
|
8410
|
+
configHome: (0, import_node_path16.join)(root, "engine-config"),
|
|
8411
|
+
dataHome: (0, import_node_path16.join)(root, "engine-data"),
|
|
8412
|
+
stateHome: (0, import_node_path16.join)(root, "engine-state")
|
|
7646
8413
|
};
|
|
7647
8414
|
}
|
|
7648
8415
|
async function resolveTheme() {
|
|
@@ -7661,24 +8428,24 @@ async function resolveTheme() {
|
|
|
7661
8428
|
}
|
|
7662
8429
|
async function writeEngineSession(session, catalog, localProviders = {}, framework = "unknown", skillsInstruction = null, backendState, model) {
|
|
7663
8430
|
const xdg = engineXdg();
|
|
7664
|
-
const configDir2 = (0,
|
|
7665
|
-
const dataDir = (0,
|
|
7666
|
-
const themesDir = (0,
|
|
7667
|
-
const pluginsDir = (0,
|
|
7668
|
-
const commandsDir = (0,
|
|
7669
|
-
for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0,
|
|
7670
|
-
(0,
|
|
8431
|
+
const configDir2 = (0, import_node_path16.join)(xdg.configHome, "opencode");
|
|
8432
|
+
const dataDir = (0, import_node_path16.join)(xdg.dataHome, "opencode");
|
|
8433
|
+
const themesDir = (0, import_node_path16.join)(configDir2, "themes");
|
|
8434
|
+
const pluginsDir = (0, import_node_path16.join)(configDir2, "plugins");
|
|
8435
|
+
const commandsDir = (0, import_node_path16.join)(configDir2, "commands");
|
|
8436
|
+
for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path16.join)(xdg.stateHome, "opencode")]) {
|
|
8437
|
+
(0, import_node_fs14.mkdirSync)(dir, { recursive: true });
|
|
7671
8438
|
}
|
|
7672
8439
|
const imagegenAvailable = installEngineTools(configDir2) && session.img_on !== false;
|
|
7673
|
-
const cloudInstructionsPath = (0,
|
|
7674
|
-
(0,
|
|
7675
|
-
const skillsPath = (0,
|
|
8440
|
+
const cloudInstructionsPath = (0, import_node_path16.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
|
|
8441
|
+
(0, import_node_fs14.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework, backendState));
|
|
8442
|
+
const skillsPath = (0, import_node_path16.join)(configDir2, "AGENTFUL_SKILLS.md");
|
|
7676
8443
|
const instructionPaths = [cloudInstructionsPath];
|
|
7677
8444
|
if (skillsInstruction) {
|
|
7678
|
-
(0,
|
|
8445
|
+
(0, import_node_fs14.writeFileSync)(skillsPath, skillsInstruction);
|
|
7679
8446
|
instructionPaths.push(skillsPath);
|
|
7680
8447
|
} else {
|
|
7681
|
-
(0,
|
|
8448
|
+
(0, import_node_fs14.rmSync)(skillsPath, { force: true });
|
|
7682
8449
|
}
|
|
7683
8450
|
const config = buildEngineConfig({
|
|
7684
8451
|
session,
|
|
@@ -7688,13 +8455,13 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
|
|
|
7688
8455
|
instructionPaths,
|
|
7689
8456
|
model
|
|
7690
8457
|
});
|
|
7691
|
-
(0,
|
|
7692
|
-
const authPath2 = (0,
|
|
7693
|
-
(0,
|
|
8458
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
|
|
8459
|
+
const authPath2 = (0, import_node_path16.join)(dataDir, "auth.json");
|
|
8460
|
+
(0, import_node_fs14.writeFileSync)(authPath2, mergedAuthJson(readAuthFile(authPath2), session.token));
|
|
7694
8461
|
const { brandingPluginSource: brandingPluginSource2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
|
|
7695
|
-
const pluginPath = (0,
|
|
7696
|
-
(0,
|
|
7697
|
-
(0,
|
|
8462
|
+
const pluginPath = (0, import_node_path16.join)(pluginsDir, "agentful-branding.tsx");
|
|
8463
|
+
(0, import_node_fs14.writeFileSync)(pluginPath, brandingPluginSource2());
|
|
8464
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(configDir2, "tui.json"), JSON.stringify({
|
|
7698
8465
|
theme: "agentful",
|
|
7699
8466
|
plugin: [`file://${pluginPath}`],
|
|
7700
8467
|
// The engine's own sidebar sections are plugins with fixed ids, and
|
|
@@ -7706,13 +8473,13 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
|
|
|
7706
8473
|
// state over this map.
|
|
7707
8474
|
plugin_enabled: DISABLED_ENGINE_PLUGINS
|
|
7708
8475
|
}, null, 2));
|
|
7709
|
-
(0,
|
|
8476
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
|
|
7710
8477
|
for (const name of Object.keys(SLASH_COMMANDS)) {
|
|
7711
|
-
(0,
|
|
8478
|
+
(0, import_node_fs14.writeFileSync)((0, import_node_path16.join)(commandsDir, `${name}.md`), commandMarkdown(name));
|
|
7712
8479
|
}
|
|
7713
8480
|
for (const legacy of ["xdg-config", "xdg-data", "xdg-state"]) {
|
|
7714
8481
|
try {
|
|
7715
|
-
(0,
|
|
8482
|
+
(0, import_node_fs14.rmSync)((0, import_node_path16.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
|
|
7716
8483
|
} catch {
|
|
7717
8484
|
}
|
|
7718
8485
|
}
|
|
@@ -7720,8 +8487,8 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
|
|
|
7720
8487
|
}
|
|
7721
8488
|
|
|
7722
8489
|
// src/lib/skills.ts
|
|
7723
|
-
var
|
|
7724
|
-
var
|
|
8490
|
+
var import_node_fs15 = require("fs");
|
|
8491
|
+
var import_node_path17 = require("path");
|
|
7725
8492
|
|
|
7726
8493
|
// src/generated/skills.json
|
|
7727
8494
|
var skills_default = {
|
|
@@ -7790,7 +8557,7 @@ var SCAFFOLD_BY_FRAMEWORK = {
|
|
|
7790
8557
|
};
|
|
7791
8558
|
function hasVueDependency(rootDir) {
|
|
7792
8559
|
try {
|
|
7793
|
-
const pkg = JSON.parse((0,
|
|
8560
|
+
const pkg = JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path17.join)(rootDir, "package.json"), "utf8"));
|
|
7794
8561
|
return Boolean({ ...pkg.dependencies, ...pkg.devDependencies }["vue"]);
|
|
7795
8562
|
} catch {
|
|
7796
8563
|
return false;
|
|
@@ -7820,7 +8587,7 @@ function renderSkillsInstruction(names) {
|
|
|
7820
8587
|
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";
|
|
7821
8588
|
}
|
|
7822
8589
|
function skillsInstructionFor(framework, rootDir) {
|
|
7823
|
-
if (!(0,
|
|
8590
|
+
if (!(0, import_node_fs15.existsSync)(rootDir)) return null;
|
|
7824
8591
|
return renderSkillsInstruction(selectSkills(framework, rootDir));
|
|
7825
8592
|
}
|
|
7826
8593
|
|
|
@@ -7979,15 +8746,15 @@ function decideLocalProviders(policy) {
|
|
|
7979
8746
|
}
|
|
7980
8747
|
|
|
7981
8748
|
// src/lib/localProviders.ts
|
|
7982
|
-
var
|
|
7983
|
-
var
|
|
8749
|
+
var import_node_fs16 = require("fs");
|
|
8750
|
+
var import_node_path18 = require("path");
|
|
7984
8751
|
var LOCAL_PROVIDERS_FILE = "local-providers.json";
|
|
7985
8752
|
function localProvidersPath() {
|
|
7986
|
-
return (0,
|
|
8753
|
+
return (0, import_node_path18.join)(configDir(), LOCAL_PROVIDERS_FILE);
|
|
7987
8754
|
}
|
|
7988
8755
|
function readLocalProviders() {
|
|
7989
8756
|
try {
|
|
7990
|
-
const raw = JSON.parse((0,
|
|
8757
|
+
const raw = JSON.parse((0, import_node_fs16.readFileSync)(localProvidersPath(), "utf8"));
|
|
7991
8758
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
7992
8759
|
const map = raw.providers && typeof raw.providers === "object" ? raw.providers : raw;
|
|
7993
8760
|
const usable = {};
|
|
@@ -8022,8 +8789,8 @@ function resolveLocalProviders(policy) {
|
|
|
8022
8789
|
}
|
|
8023
8790
|
function writeLocalProvidersExample() {
|
|
8024
8791
|
const path = localProvidersPath();
|
|
8025
|
-
if ((0,
|
|
8026
|
-
(0,
|
|
8792
|
+
if ((0, import_node_fs16.existsSync)(path)) return path;
|
|
8793
|
+
(0, import_node_fs16.mkdirSync)(configDir(), { recursive: true });
|
|
8027
8794
|
const example = {
|
|
8028
8795
|
_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.',
|
|
8029
8796
|
_anthropic: {
|
|
@@ -8033,17 +8800,17 @@ function writeLocalProvidersExample() {
|
|
|
8033
8800
|
models: { "claude-sonnet-4-5": { name: "Claude Sonnet 4.5 (my key)" } }
|
|
8034
8801
|
}
|
|
8035
8802
|
};
|
|
8036
|
-
(0,
|
|
8803
|
+
(0, import_node_fs16.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
|
|
8037
8804
|
try {
|
|
8038
|
-
(0,
|
|
8805
|
+
(0, import_node_fs16.chmodSync)(path, 384);
|
|
8039
8806
|
} catch {
|
|
8040
8807
|
}
|
|
8041
8808
|
return path;
|
|
8042
8809
|
}
|
|
8043
8810
|
|
|
8044
8811
|
// src/lib/sessions.ts
|
|
8045
|
-
var
|
|
8046
|
-
var
|
|
8812
|
+
var import_node_fs17 = require("fs");
|
|
8813
|
+
var import_node_path19 = require("path");
|
|
8047
8814
|
var import_node_child_process4 = require("child_process");
|
|
8048
8815
|
function parseSessionModel(raw) {
|
|
8049
8816
|
if (!raw) return null;
|
|
@@ -8060,8 +8827,8 @@ function parseSessionModel(raw) {
|
|
|
8060
8827
|
}
|
|
8061
8828
|
}
|
|
8062
8829
|
function modelForSession(sessionID) {
|
|
8063
|
-
const dbPath = (0,
|
|
8064
|
-
if (!(0,
|
|
8830
|
+
const dbPath = (0, import_node_path19.join)(engineXdg().dataHome, "opencode", "opencode.db");
|
|
8831
|
+
if (!(0, import_node_fs17.existsSync)(dbPath)) return null;
|
|
8065
8832
|
const escaped = sessionID.replace(/'/g, "''");
|
|
8066
8833
|
const res = (0, import_node_child_process4.spawnSync)(
|
|
8067
8834
|
"sqlite3",
|
|
@@ -8082,8 +8849,8 @@ function modelForSession(sessionID) {
|
|
|
8082
8849
|
}
|
|
8083
8850
|
}
|
|
8084
8851
|
function lastSessionForDirectory(cwd = process.cwd()) {
|
|
8085
|
-
const dbPath = (0,
|
|
8086
|
-
if (!(0,
|
|
8852
|
+
const dbPath = (0, import_node_path19.join)(engineXdg().dataHome, "opencode", "opencode.db");
|
|
8853
|
+
if (!(0, import_node_fs17.existsSync)(dbPath)) return null;
|
|
8087
8854
|
const escaped = cwd.replace(/'/g, "''");
|
|
8088
8855
|
const res = (0, import_node_child_process4.spawnSync)(
|
|
8089
8856
|
"sqlite3",
|
|
@@ -8109,9 +8876,9 @@ function lastSessionForDirectory(cwd = process.cwd()) {
|
|
|
8109
8876
|
}
|
|
8110
8877
|
|
|
8111
8878
|
// src/lib/modelPin.ts
|
|
8112
|
-
var
|
|
8113
|
-
var
|
|
8114
|
-
var PROJECT_MODEL_PIN = (0,
|
|
8879
|
+
var import_node_fs18 = require("fs");
|
|
8880
|
+
var import_node_path20 = require("path");
|
|
8881
|
+
var PROJECT_MODEL_PIN = (0, import_node_path20.join)(".agentful", "model.json");
|
|
8115
8882
|
function isCanonicalModel(value) {
|
|
8116
8883
|
const slash = value.indexOf("/");
|
|
8117
8884
|
return slash > 0 && slash < value.length - 1 && !/\s/.test(value);
|
|
@@ -8131,10 +8898,10 @@ function assertModelPin(pin, source) {
|
|
|
8131
8898
|
return pin;
|
|
8132
8899
|
}
|
|
8133
8900
|
function readProjectModelPin(cwd = process.cwd()) {
|
|
8134
|
-
const path = (0,
|
|
8901
|
+
const path = (0, import_node_path20.join)(cwd, PROJECT_MODEL_PIN);
|
|
8135
8902
|
let raw;
|
|
8136
8903
|
try {
|
|
8137
|
-
raw = (0,
|
|
8904
|
+
raw = (0, import_node_fs18.readFileSync)(path, "utf8");
|
|
8138
8905
|
} catch (error) {
|
|
8139
8906
|
const code = error.code;
|
|
8140
8907
|
if (code === "ENOENT") return null;
|
|
@@ -8161,7 +8928,7 @@ function readProjectModelPin(cwd = process.cwd()) {
|
|
|
8161
8928
|
function readEngineVariants(xdg) {
|
|
8162
8929
|
try {
|
|
8163
8930
|
const parsed = JSON.parse(
|
|
8164
|
-
(0,
|
|
8931
|
+
(0, import_node_fs18.readFileSync)((0, import_node_path20.join)(xdg.stateHome, "opencode", "model.json"), "utf8")
|
|
8165
8932
|
);
|
|
8166
8933
|
if (!parsed.variant || typeof parsed.variant !== "object" || Array.isArray(parsed.variant)) return {};
|
|
8167
8934
|
return Object.fromEntries(
|
|
@@ -8173,17 +8940,17 @@ function readEngineVariants(xdg) {
|
|
|
8173
8940
|
}
|
|
8174
8941
|
function writeEngineVariant(xdg, model, variant) {
|
|
8175
8942
|
assertModelPin({ model, variant }, "Selected");
|
|
8176
|
-
const path = (0,
|
|
8943
|
+
const path = (0, import_node_path20.join)(xdg.stateHome, "opencode", "model.json");
|
|
8177
8944
|
let state = {};
|
|
8178
8945
|
try {
|
|
8179
|
-
const parsed = JSON.parse((0,
|
|
8946
|
+
const parsed = JSON.parse((0, import_node_fs18.readFileSync)(path, "utf8"));
|
|
8180
8947
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) state = parsed;
|
|
8181
8948
|
} catch {
|
|
8182
8949
|
}
|
|
8183
8950
|
const existing = state.variant;
|
|
8184
8951
|
const variants = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
8185
8952
|
state.variant = { ...variants, [model]: variant };
|
|
8186
|
-
(0,
|
|
8953
|
+
(0, import_node_fs18.writeFileSync)(path, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
8187
8954
|
}
|
|
8188
8955
|
function resolveModelPin(opts) {
|
|
8189
8956
|
const cliPin = opts.cliModel ? assertModelPin({ model: opts.cliModel, variant: opts.cliVariant }, "CLI") : null;
|
|
@@ -8232,8 +8999,8 @@ function resolveModelPin(opts) {
|
|
|
8232
8999
|
// src/lib/engineModels.ts
|
|
8233
9000
|
var import_node_child_process5 = require("child_process");
|
|
8234
9001
|
var import_node_util = require("util");
|
|
8235
|
-
var
|
|
8236
|
-
var
|
|
9002
|
+
var import_node_fs19 = require("fs");
|
|
9003
|
+
var import_node_path21 = require("path");
|
|
8237
9004
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process5.execFile);
|
|
8238
9005
|
function parseEngineModelsOutput(output) {
|
|
8239
9006
|
return Array.from(new Set(
|
|
@@ -8284,7 +9051,7 @@ async function assertEngineModelAvailable(binPath, xdg, model, listModels = list
|
|
|
8284
9051
|
function readConnectedEngineProviders(xdg) {
|
|
8285
9052
|
try {
|
|
8286
9053
|
const parsed = JSON.parse(
|
|
8287
|
-
(0,
|
|
9054
|
+
(0, import_node_fs19.readFileSync)((0, import_node_path21.join)(xdg.dataHome, "opencode", "auth.json"), "utf8")
|
|
8288
9055
|
);
|
|
8289
9056
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return [];
|
|
8290
9057
|
return Object.keys(parsed).filter((id) => id !== "agentful").sort();
|
|
@@ -8315,7 +9082,7 @@ function describeModelRoute(model, catalog, localProviders, connectedProviders)
|
|
|
8315
9082
|
}
|
|
8316
9083
|
|
|
8317
9084
|
// src/lib/engineEvents.ts
|
|
8318
|
-
var
|
|
9085
|
+
var import_node_crypto3 = require("crypto");
|
|
8319
9086
|
var import_node_net = require("net");
|
|
8320
9087
|
function payloadOf(event) {
|
|
8321
9088
|
return event.payload && typeof event.payload === "object" ? event.payload : event;
|
|
@@ -8441,7 +9208,7 @@ var EngineLivenessPolicy = class {
|
|
|
8441
9208
|
function engineServerCredentials(env = process.env) {
|
|
8442
9209
|
return {
|
|
8443
9210
|
username: env.OPENCODE_SERVER_USERNAME || void 0,
|
|
8444
|
-
password: env.OPENCODE_SERVER_PASSWORD || (0,
|
|
9211
|
+
password: env.OPENCODE_SERVER_PASSWORD || (0, import_node_crypto3.randomUUID)()
|
|
8445
9212
|
};
|
|
8446
9213
|
}
|
|
8447
9214
|
async function reserveLoopbackPort() {
|
|
@@ -8646,7 +9413,7 @@ async function tuiCommand(opts = {}) {
|
|
|
8646
9413
|
));
|
|
8647
9414
|
} else {
|
|
8648
9415
|
ui.info(paint.dim(
|
|
8649
|
-
`Mode: ${CLOUD_AGENT} \u2014 building for Agentful Cloud (${
|
|
9416
|
+
`Mode: ${CLOUD_AGENT} \u2014 building for Agentful Cloud (${frameworkDisplayName2(framework)}). Tab switches to ${LOCAL_AGENT} for work that is not meant to deploy.`
|
|
8650
9417
|
));
|
|
8651
9418
|
}
|
|
8652
9419
|
const localCount = Object.keys(local.providers).length;
|
|
@@ -8720,7 +9487,7 @@ async function tuiCommand(opts = {}) {
|
|
|
8720
9487
|
...process.env,
|
|
8721
9488
|
// Slash commands run `agentful …` through the agent's bash tool, which
|
|
8722
9489
|
// has no access to the user's shell aliases.
|
|
8723
|
-
PATH: `${binDir}${
|
|
9490
|
+
PATH: `${binDir}${import_node_path22.delimiter}${process.env.PATH || ""}`,
|
|
8724
9491
|
XDG_CONFIG_HOME: xdg.configHome,
|
|
8725
9492
|
XDG_DATA_HOME: xdg.dataHome,
|
|
8726
9493
|
XDG_STATE_HOME: xdg.stateHome,
|
|
@@ -8854,38 +9621,38 @@ async function shareCommand(opts) {
|
|
|
8854
9621
|
}
|
|
8855
9622
|
|
|
8856
9623
|
// src/commands/pull.ts
|
|
8857
|
-
var
|
|
8858
|
-
var
|
|
9624
|
+
var import_node_fs21 = require("fs");
|
|
9625
|
+
var import_node_path24 = require("path");
|
|
8859
9626
|
init_branding();
|
|
8860
9627
|
init_branding();
|
|
8861
9628
|
|
|
8862
9629
|
// src/lib/pullFiles.ts
|
|
8863
|
-
var
|
|
8864
|
-
var
|
|
9630
|
+
var import_node_fs20 = require("fs");
|
|
9631
|
+
var import_node_path23 = require("path");
|
|
8865
9632
|
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;
|
|
8866
9633
|
function inlineContent(value) {
|
|
8867
9634
|
return typeof value === "object" && value !== null && "content" in value ? String(value.content) : String(value ?? "");
|
|
8868
9635
|
}
|
|
8869
9636
|
function writePulledWorkspaceEntry(root, rel, value) {
|
|
8870
|
-
const target = (0,
|
|
8871
|
-
(0,
|
|
9637
|
+
const target = (0, import_node_path23.join)(root, rel);
|
|
9638
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path23.dirname)(target), { recursive: true });
|
|
8872
9639
|
const content = inlineContent(value);
|
|
8873
9640
|
if (INLINE_BASE64_EXTENSIONS.test(rel)) {
|
|
8874
|
-
(0,
|
|
9641
|
+
(0, import_node_fs20.writeFileSync)(target, Buffer.from(content, "base64"));
|
|
8875
9642
|
return;
|
|
8876
9643
|
}
|
|
8877
|
-
(0,
|
|
9644
|
+
(0, import_node_fs20.writeFileSync)(target, content, "utf8");
|
|
8878
9645
|
}
|
|
8879
9646
|
function writePulledBackendDeclaration(root, declaration) {
|
|
8880
|
-
const target = (0,
|
|
8881
|
-
(0,
|
|
9647
|
+
const target = (0, import_node_path23.join)(root, ".agentful", "backend.json");
|
|
9648
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path23.dirname)(target), { recursive: true });
|
|
8882
9649
|
const {
|
|
8883
9650
|
source: _source,
|
|
8884
9651
|
received_at: _receivedAt,
|
|
8885
9652
|
last_sync: _lastSync,
|
|
8886
9653
|
...portableDeclaration
|
|
8887
9654
|
} = declaration;
|
|
8888
|
-
(0,
|
|
9655
|
+
(0, import_node_fs20.writeFileSync)(target, `${JSON.stringify(portableDeclaration, null, 2)}
|
|
8889
9656
|
`, "utf8");
|
|
8890
9657
|
}
|
|
8891
9658
|
|
|
@@ -8894,7 +9661,7 @@ async function pullCommand(opts) {
|
|
|
8894
9661
|
console.log(banner());
|
|
8895
9662
|
const auth = await ensureAuth();
|
|
8896
9663
|
const project = requireProject();
|
|
8897
|
-
const nonHidden = (0,
|
|
9664
|
+
const nonHidden = (0, import_node_fs21.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
|
|
8898
9665
|
if (nonHidden.length > 0 && !opts.force) {
|
|
8899
9666
|
throw new ApiError(
|
|
8900
9667
|
0,
|
|
@@ -8921,9 +9688,9 @@ async function pullCommand(opts) {
|
|
|
8921
9688
|
ui.warn(`Skipped ${rel} (HTTP ${resp.status})`);
|
|
8922
9689
|
continue;
|
|
8923
9690
|
}
|
|
8924
|
-
const target = (0,
|
|
8925
|
-
(0,
|
|
8926
|
-
(0,
|
|
9691
|
+
const target = (0, import_node_path24.join)(process.cwd(), rel);
|
|
9692
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path24.dirname)(target), { recursive: true });
|
|
9693
|
+
(0, import_node_fs21.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
|
|
8927
9694
|
written++;
|
|
8928
9695
|
}
|
|
8929
9696
|
} else if (data.files) {
|
|
@@ -9128,7 +9895,7 @@ program2.command("login").description("Sign in to your Agentful account (device
|
|
|
9128
9895
|
program2.command("logout").description("Remove the stored credentials").action(run(logoutCommand));
|
|
9129
9896
|
program2.command("whoami").description("Show the signed-in account").action(run(whoamiCommand));
|
|
9130
9897
|
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)));
|
|
9131
|
-
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)));
|
|
9898
|
+
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)").action(run((opts) => pushCommand(opts)));
|
|
9132
9899
|
program2.command("build-status").description("Show the cloud's record of the last build (the real error on failures)").action(run(buildStatusCommand));
|
|
9133
9900
|
program2.command("backend").description("Open this project's Backend tab (managed DB + server actions) in the browser").action(run(backendCommand));
|
|
9134
9901
|
program2.command("open").description("Open the live preview in the browser").action(run(openCommand));
|