agentful 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -33,9 +33,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
33
  mod
34
34
  ));
35
35
 
36
- // node_modules/commander/lib/error.js
36
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/error.js
37
37
  var require_error = __commonJS({
38
- "node_modules/commander/lib/error.js"(exports2) {
38
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/error.js"(exports2) {
39
39
  "use strict";
40
40
  var CommanderError2 = class extends Error {
41
41
  /**
@@ -69,9 +69,9 @@ var require_error = __commonJS({
69
69
  }
70
70
  });
71
71
 
72
- // node_modules/commander/lib/argument.js
72
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/argument.js
73
73
  var require_argument = __commonJS({
74
- "node_modules/commander/lib/argument.js"(exports2) {
74
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/argument.js"(exports2) {
75
75
  "use strict";
76
76
  var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
77
77
  var Argument2 = class {
@@ -197,9 +197,9 @@ var require_argument = __commonJS({
197
197
  }
198
198
  });
199
199
 
200
- // node_modules/commander/lib/help.js
200
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/help.js
201
201
  var require_help = __commonJS({
202
- "node_modules/commander/lib/help.js"(exports2) {
202
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/help.js"(exports2) {
203
203
  "use strict";
204
204
  var { humanReadableArgName } = require_argument();
205
205
  var Help2 = class {
@@ -612,9 +612,9 @@ var require_help = __commonJS({
612
612
  }
613
613
  });
614
614
 
615
- // node_modules/commander/lib/option.js
615
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/option.js
616
616
  var require_option = __commonJS({
617
- "node_modules/commander/lib/option.js"(exports2) {
617
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/option.js"(exports2) {
618
618
  "use strict";
619
619
  var { InvalidArgumentError: InvalidArgumentError2 } = require_error();
620
620
  var Option2 = class {
@@ -885,9 +885,9 @@ var require_option = __commonJS({
885
885
  }
886
886
  });
887
887
 
888
- // node_modules/commander/lib/suggestSimilar.js
888
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/suggestSimilar.js
889
889
  var require_suggestSimilar = __commonJS({
890
- "node_modules/commander/lib/suggestSimilar.js"(exports2) {
890
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/suggestSimilar.js"(exports2) {
891
891
  "use strict";
892
892
  var maxDistance = 3;
893
893
  function editDistance(a, b) {
@@ -966,9 +966,9 @@ var require_suggestSimilar = __commonJS({
966
966
  }
967
967
  });
968
968
 
969
- // node_modules/commander/lib/command.js
969
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/command.js
970
970
  var require_command = __commonJS({
971
- "node_modules/commander/lib/command.js"(exports2) {
971
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/lib/command.js"(exports2) {
972
972
  "use strict";
973
973
  var EventEmitter = require("events").EventEmitter;
974
974
  var childProcess = require("child_process");
@@ -3010,9 +3010,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3010
3010
  }
3011
3011
  });
3012
3012
 
3013
- // node_modules/commander/index.js
3013
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/index.js
3014
3014
  var require_commander = __commonJS({
3015
- "node_modules/commander/index.js"(exports2) {
3015
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/index.js"(exports2) {
3016
3016
  "use strict";
3017
3017
  var { Argument: Argument2 } = require_argument();
3018
3018
  var { Command: Command2 } = require_command();
@@ -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.5" ? "0.4.5" : null.version;
3047
+ VERSION = "0.4.7" ? "0.4.7" : null.version;
3048
3048
  brand = {
3049
3049
  name: "agentful",
3050
3050
  // the command users type
@@ -3103,9 +3103,9 @@ var init_branding = __esm({
3103
3103
  }
3104
3104
  });
3105
3105
 
3106
- // node_modules/ignore/index.js
3106
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/ignore/index.js
3107
3107
  var require_ignore = __commonJS({
3108
- "node_modules/ignore/index.js"(exports2, module2) {
3108
+ "../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/ignore/index.js"(exports2, module2) {
3109
3109
  "use strict";
3110
3110
  function makeArray(subject) {
3111
3111
  return Array.isArray(subject) ? subject : [subject];
@@ -3824,7 +3824,7 @@ var init_plugin = __esm({
3824
3824
  }
3825
3825
  });
3826
3826
 
3827
- // node_modules/commander/esm.mjs
3827
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/commander/esm.mjs
3828
3828
  var import_index = __toESM(require_commander(), 1);
3829
3829
  var {
3830
3830
  program,
@@ -4785,7 +4785,7 @@ init_branding();
4785
4785
  var import_node_fs4 = require("fs");
4786
4786
  var import_node_path6 = require("path");
4787
4787
 
4788
- // node_modules/fflate/esm/index.mjs
4788
+ // ../../../../../../../../Users/gn/Labor/mainmvp/cli/node_modules/fflate/esm/index.mjs
4789
4789
  var import_module = require("module");
4790
4790
  var require2 = (0, import_module.createRequire)("/");
4791
4791
  var _a;
@@ -5597,18 +5597,28 @@ function detectDistDir(rootDir, override) {
5597
5597
  }
5598
5598
 
5599
5599
  // src/lib/preflight.ts
5600
- var import_node_fs5 = require("fs");
5601
- var import_node_path7 = require("path");
5600
+ var import_node_fs6 = require("fs");
5601
+ var import_node_path8 = require("path");
5602
5602
 
5603
5603
  // ../platform-contract/contract.json
5604
5604
  var contract_default = {
5605
5605
  $comment: "Single source of truth for the platform publish contract (PRD AP6a, planned/cli-push-to-workspace-transition-findings-and-prd-2026-08-17.md). Consumers: cli/src/lib/preflight.ts renders directly from this file; build engine (infrastructure/lambda/build-engine/handler.ts) and executor (opencode-lambda/executor/runtime/opencode_runner.py) are parity-checked against it by ./check-parity.py until AP4/AP5 move them onto it; AP0 instructions/skills render from it later. Bump schema_version on breaking shape changes. See README.md.",
5606
5606
  schema_version: 1,
5607
- contract_statement: "This platform publishes static files. Your app has no server process of its own \u2014 server-side logic runs as Managed Server Actions (short request/response functions) or on an API of your own that you connect. You cannot set build-time env vars or secrets: the workspace build injects only a fixed allowlist of public client values, and the `agentful push` build injects none.",
5607
+ contract_statement: "This platform publishes static files. Your app has no server process of its own \u2014 server-side logic runs as Managed Server Actions (short request/response functions) or on an API of your own that you connect. You cannot set build-time env vars or secrets: every cloud build injects the same fixed allowlist of public client values, and nothing else.",
5608
5608
  publish: {
5609
5609
  server_process: false,
5610
5610
  build_env_vars: false,
5611
- $comment_build_env: "false = nobody can SET a build variable or build secret. Two build paths exist and they differ: the workspace executor injects a fixed allowlist of PUBLIC client values (VITE_PROJECT_ID/PUBLIC_PROJECT_ID, and for a BYO Supabase database its URL + publishable key as VITE_/PUBLIC_/NEXT_PUBLIC_); the build engine behind `agentful push` injects nothing beyond NODE_ENV. Code that must build on both paths keeps public values in an ordinary source constant.",
5611
+ $comment_build_env: "false = nobody can SET a build variable or build secret. Two build paths exist \u2014 the workspace executor (end of an agent turn) and the build engine (the workspace's Build button and `agentful push`) \u2014 and both inject the SAME fixed allowlist of PUBLIC client values, listed under build_env. The values come from one function on both paths (aws-services/workspace-manager/client_config.py); check-parity.py (check 13) pins the names. Until 2026-09 the build engine injected nothing, so an app that read import.meta.env.VITE_SUPABASE_URL worked after the agent's turn and lost its database on the next Build click \u2014 without a build error. A public value outside the allowlist still belongs in an ordinary source constant.",
5612
+ build_env: {
5613
+ $comment: "What a cloud build defines, and nothing else. `injected` = suffix x prefixes; `when: byo_supabase` means only while the project's Backend tab has an own Supabase database connected (URL + publishable key; a secret key stored in that slot is refused, never shipped). A read of <public prefix><injected suffix> that the build does NOT define fails the build (422 undefined_build_env): undefined there is a broken app, not a default. Any other read of a public-prefixed name that nothing defines is a warning, because `import.meta.env.VITE_API_BASE ?? '/api'` is a legitimate pattern. `bundler_builtins` are set by the framework itself and are never reported.",
5614
+ public_prefixes: ["VITE_", "PUBLIC_", "NEXT_PUBLIC_"],
5615
+ injected: [
5616
+ { suffix: "PROJECT_ID", prefixes: ["VITE_", "PUBLIC_"], when: "always" },
5617
+ { suffix: "SUPABASE_URL", prefixes: ["VITE_", "PUBLIC_", "NEXT_PUBLIC_"], when: "byo_supabase" },
5618
+ { suffix: "SUPABASE_ANON_KEY", prefixes: ["VITE_", "PUBLIC_", "NEXT_PUBLIC_"], when: "byo_supabase" }
5619
+ ],
5620
+ bundler_builtins: ["PUBLIC_URL"]
5621
+ },
5612
5622
  published_prefix: "dist/",
5613
5623
  canonical_output_check: "root_index_html",
5614
5624
  $comment_output: "Canonical end-to-end contract (AP4): a root-level index.html inside the framework's output dir. Deliberately stricter than the executor's historical index.htm/200.html acceptance \u2014 both builders converge on THIS, not on each other (deployment gate and publish self-heal require dist/index.html)."
@@ -5618,10 +5628,11 @@ var contract_default = {
5618
5628
  $comment_limits: "Compressed zip bytes. Enforced server-side as MAX_TEMPLATE_ZIP_UPLOAD_BYTES (aws-services/workspace-manager/handler.py); ~8 MB as base64, under the API Gateway 10 MB request cap."
5619
5629
  },
5620
5630
  server_side: {
5621
- $comment: "What 'server' means on this platform (server-option plan 2026-09-18, AP2). THREE separate tracks with separate limits \u2014 never quote one track's limits for another. managed_actions.limits is pinned against the source constants by check-parity.py (check 10) and against the agent instructions (check 11); the behaviour behind action_timeout_budget_s is pinned in aws-services/action-runner/tests/sandbox_contract.test.js. Additive block: no schema_version bump.",
5631
+ $comment: "What 'server' means on this platform (server-option plan 2026-09-18, AP2). THREE separate tracks with separate limits \u2014 never quote one track's limits for another. managed_actions.limits is pinned against the source constants by check-parity.py (check 10) and against the agent instructions (check 11); the behaviour behind action_timeout_budget_s is pinned in aws-services/action-runner/tests/sandbox_contract.test.js, the one behind switch_off_delay_s (and the refusal itself) in tests/server_switch.test.js. The switch is enforced only while infrastructure/lib/s3-lambda-stack.ts says SERVER_SWITCH_ENFORCEMENT 'enforce' \u2014 check 12 fails if switch_note promises it under any other mode. Additive block: no schema_version bump.",
5622
5632
  managed_actions: {
5623
5633
  label: "Managed Server Actions",
5624
5634
  summary: "Short request/response functions the platform runs for the project. One file is one endpoint; JSON in, JSON out. Backend tab: Server \u2192 Managed.",
5635
+ switch_note: 'Actions run only while the managed server is switched on in the Backend tab (Server \u2192 Managed). A managed database alone does not switch it on. While it is off, every action of the project answers 404 server_not_enabled. Switching on takes effect on the next call; switching off takes effect after the switch-off delay given under Limits at the latest, because warm instances remember "on" for that long. Storing is not running: uploading or pushing action files is not gated by the switch.',
5625
5636
  offers: [
5626
5637
  "One file = one endpoint: .server/actions/<name>.js is served at /api/p/{projectId}/actions/{name}, any HTTP method, JSON in, JSON out",
5627
5638
  "Runtime secrets that never reach the browser: await ctx.secrets.get('server.<name>')",
@@ -5674,7 +5685,8 @@ var contract_default = {
5674
5685
  max_body_bytes: 1048576,
5675
5686
  max_code_bytes: 262144,
5676
5687
  project_concurrency: 10,
5677
- max_action_depth: 5
5688
+ max_action_depth: 5,
5689
+ switch_off_delay_s: 60
5678
5690
  },
5679
5691
  limits_note: "The per-action budget is a figure to plan with, not a guaranteed stop: an action still waiting on ctx.fetch or ctx.data when the budget runs out is answered with 408 timeout, synchronous code runs on until the hard limit, and after a 408 the action may still complete calls it had already started. Build actions so that a repeated call ends in the same state."
5680
5692
  },
@@ -5804,7 +5816,7 @@ var contract_default = {
5804
5816
  build_attempt_schema: {
5805
5817
  file: "deployments/latest-build.json",
5806
5818
  status_values: ["running", "success", "failed"],
5807
- phase_codes: ["unsupported_project_type", "install", "build", "missing_output", "upload"],
5819
+ phase_codes: ["unsupported_project_type", "client_env", "install", "build", "missing_output", "upload"],
5808
5820
  fields: {
5809
5821
  schema_version: "int \u2014 copy of this contract's schema_version",
5810
5822
  build_id: "string \u2014 unique per attempt",
@@ -5824,6 +5836,154 @@ var contract_default = {
5824
5836
  }
5825
5837
  };
5826
5838
 
5839
+ // ../platform-contract/buildEnv.ts
5840
+ var import_node_fs5 = require("fs");
5841
+ var import_node_path7 = require("path");
5842
+ var BUILD_ENV = contract_default.publish.build_env;
5843
+ var PUBLIC_PREFIXES = BUILD_ENV.public_prefixes;
5844
+ var BUILTINS = new Set(BUILD_ENV.bundler_builtins);
5845
+ var INJECTABLE_NAMES = new Set(
5846
+ BUILD_ENV.injected.flatMap((e) => e.prefixes.map((p) => `${p}${e.suffix}`))
5847
+ );
5848
+ var ALWAYS_DEFINED_NAMES = new Set(
5849
+ BUILD_ENV.injected.filter((e) => e.when === "always").flatMap((e) => e.prefixes.map((p) => `${p}${e.suffix}`))
5850
+ );
5851
+ var splitName = (name) => {
5852
+ const prefix = PUBLIC_PREFIXES.find((p) => name.startsWith(p));
5853
+ return prefix ? { prefix, suffix: name.slice(prefix.length) } : null;
5854
+ };
5855
+ var injectedEntry = (suffix) => BUILD_ENV.injected.find((e) => e.suffix === suffix);
5856
+ var SCAN_FILE = /\.(js|jsx|ts|tsx|mjs|cjs|vue|svelte|astro)$/;
5857
+ var SKIP_FILE = /\.d\.ts$|\.(test|spec)\.[a-z]+$/;
5858
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", "out", ".nuxt", ".output", "coverage"]);
5859
+ var SCAN_FILE_BUDGET = 5e3;
5860
+ var MAX_SCAN_BYTES = 1024 * 1024;
5861
+ var ENV_OBJECT = String.raw`(?:import\.meta\.env|process\.env)`;
5862
+ var MEMBER_READ = new RegExp(
5863
+ String.raw`\b${ENV_OBJECT}\s*(?:\??\.\s*([A-Z][A-Z0-9_]*)|\[\s*['"]([A-Z][A-Z0-9_]*)['"]\s*\])(\s*(?:\?\?|\|\|))?`,
5864
+ "g"
5865
+ );
5866
+ var DESTRUCTURED_READ = new RegExp(String.raw`\{([^{}]*)\}\s*=\s*${ENV_OBJECT}\b`, "g");
5867
+ var QUOTED = /'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"/g;
5868
+ var MENTIONS_ENV = /\benv\s*[.[]/;
5869
+ var stripNonCode = (text) => text.replace(QUOTED, (quoted) => MENTIONS_ENV.test(quoted) ? "''" : quoted).replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:'"`\\])\/\/[^\n]*/g, "$1");
5870
+ var isReportable = (name) => !BUILTINS.has(name) && splitName(name) !== null;
5871
+ function collectFiles2(dir, out, budget) {
5872
+ if (budget.left <= 0 || !(0, import_node_fs5.existsSync)(dir)) return;
5873
+ for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
5874
+ if (budget.left <= 0) return;
5875
+ const abs = (0, import_node_path7.join)(dir, entry.name);
5876
+ if (entry.isDirectory()) {
5877
+ if (!SKIP_DIRS.has(entry.name)) collectFiles2(abs, out, budget);
5878
+ } else if (entry.isFile() && SCAN_FILE.test(entry.name) && !SKIP_FILE.test(entry.name)) {
5879
+ budget.left--;
5880
+ out.push(abs);
5881
+ }
5882
+ }
5883
+ }
5884
+ function scanEnvReads(rootDir) {
5885
+ const files = [];
5886
+ collectFiles2(rootDir, files, { left: SCAN_FILE_BUDGET });
5887
+ const reads = [];
5888
+ for (const abs of files) {
5889
+ let text;
5890
+ try {
5891
+ text = (0, import_node_fs5.readFileSync)(abs, "utf8");
5892
+ } catch {
5893
+ continue;
5894
+ }
5895
+ if (text.length > MAX_SCAN_BYTES || !/\benv\b/.test(text)) continue;
5896
+ text = stripNonCode(text);
5897
+ const inFile = /* @__PURE__ */ new Map();
5898
+ const note = (name, hasFallback) => {
5899
+ if (isReportable(name)) inFile.set(name, (inFile.get(name) ?? true) && hasFallback);
5900
+ };
5901
+ for (const m of text.matchAll(MEMBER_READ)) note(m[1] || m[2], Boolean(m[3]));
5902
+ for (const m of text.matchAll(DESTRUCTURED_READ)) {
5903
+ for (const part of m[1].split(",")) {
5904
+ const field = /^\s*([A-Z][A-Z0-9_]*)\s*(?::\s*[A-Za-z_$][\w$]*\s*)?(=)?/.exec(part);
5905
+ if (field) note(field[1], Boolean(field[2]));
5906
+ }
5907
+ }
5908
+ const file = (0, import_node_path7.relative)(rootDir, abs).split(import_node_path7.sep).join("/");
5909
+ for (const [name, hasFallback] of inFile) reads.push({ name, file, hasFallback });
5910
+ }
5911
+ return reads;
5912
+ }
5913
+ function envFileNames(rootDir) {
5914
+ const names = /* @__PURE__ */ new Set();
5915
+ if (!(0, import_node_fs5.existsSync)(rootDir)) return names;
5916
+ for (const entry of (0, import_node_fs5.readdirSync)(rootDir, { withFileTypes: true })) {
5917
+ if (!entry.isFile() || !(entry.name === ".env" || entry.name.startsWith(".env."))) continue;
5918
+ let text;
5919
+ try {
5920
+ text = (0, import_node_fs5.readFileSync)((0, import_node_path7.join)(rootDir, entry.name), "utf8");
5921
+ } catch {
5922
+ continue;
5923
+ }
5924
+ for (const line of text.split("\n")) {
5925
+ const m = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
5926
+ if (m) names.add(m[1]);
5927
+ }
5928
+ }
5929
+ return names;
5930
+ }
5931
+ var sampleFiles = (files) => files.slice(0, 3).join(", ") + (files.length > 3 ? ", \u2026" : "");
5932
+ var prefixList = (prefixes) => prefixes.map((p) => `${p}*`).join("/");
5933
+ function platformHint(name, opts) {
5934
+ const { prefix, suffix } = splitName(name);
5935
+ const entry = injectedEntry(suffix);
5936
+ if (!entry.prefixes.includes(prefix)) {
5937
+ return `The platform defines ${suffix} only as ${entry.prefixes.map((p) => `${p}${suffix}`).join(" and ")} \u2014 read one of those, or keep the value in an ordinary source constant.`;
5938
+ }
5939
+ if (entry.when === "byo_supabase") {
5940
+ if ((opts.rejected || []).includes(suffix)) {
5941
+ return "The key stored for this database looks like a Supabase SECRET key. The platform refuses to put it into a public bundle \u2014 store the publishable (anon) key as `database.anon_key` in the Backend tab instead.";
5942
+ }
5943
+ if (opts.provider === "supabase" && (opts.missing || []).includes(suffix)) {
5944
+ return suffix === "SUPABASE_URL" ? "Your own Supabase database is connected, but no https project URL is stored \u2014 set it in the Backend tab (Database)." : "Your own Supabase database is connected, but no publishable key is stored \u2014 add it in the Backend tab as `database.anon_key`.";
5945
+ }
5946
+ return `The platform defines ${suffix} (as ${prefixList(entry.prefixes)}) only while your own Supabase database is connected in the Backend tab (Database \u2192 own Supabase). Connect it there, or keep the public value in an ordinary source constant.`;
5947
+ }
5948
+ return "Keep the public value in an ordinary source constant.";
5949
+ }
5950
+ function judgeEnvReads(reads, opts) {
5951
+ const byName = /* @__PURE__ */ new Map();
5952
+ for (const read of reads) {
5953
+ if (opts.defined.has(read.name)) continue;
5954
+ byName.set(read.name, [...byName.get(read.name) || [], read]);
5955
+ }
5956
+ const verdict = { errors: [], warnings: [] };
5957
+ const build = opts.backendUnknown ? "the cloud build" : "this build";
5958
+ for (const name of [...byName.keys()].sort()) {
5959
+ const group = byName.get(name);
5960
+ const files = sampleFiles(group.map((r) => r.file).sort());
5961
+ const { prefix, suffix } = splitName(name);
5962
+ const entry = injectedEntry(suffix);
5963
+ const taught = entry !== void 0;
5964
+ const bare = group.filter((r) => !r.hasFallback);
5965
+ const local = opts.localOnly?.has(name) ? " Your local .env defines it, but .env files never leave this machine." : "";
5966
+ if (opts.backendUnknown && entry && entry.when === "byo_supabase" && entry.prefixes.includes(prefix)) {
5967
+ verdict.warnings.push(
5968
+ `${name} is read in ${files}. The cloud build defines it only while your own Supabase database is connected in the Backend tab (Database \u2192 own Supabase)` + (bare.length > 0 ? " \u2014 without that it rejects the build, because the value would compile to `undefined` without any build error." : "; without that the app runs on its own fallback.") + local
5969
+ );
5970
+ } else if (taught && bare.length > 0) {
5971
+ verdict.errors.push(
5972
+ `${name} is read in ${sampleFiles(bare.map((r) => r.file).sort())}, but ${build} does not define it \u2014 it would compile to \`undefined\` without any build error. ${platformHint(name, opts)}${local}`
5973
+ );
5974
+ } else if (taught) {
5975
+ verdict.warnings.push(
5976
+ `${name} is read in ${files} and ${build} does not define it \u2014 the app runs on its own fallback. ${platformHint(name, opts)}${local}`
5977
+ );
5978
+ } else {
5979
+ verdict.warnings.push(
5980
+ `${name} is read in ${files}, but no cloud build defines it \u2014 it compiles to \`undefined\`. This platform sets no build variables of your own: keep public values in an ordinary source constant (a fallback such as \`?? '/api'\` is fine).${local}`
5981
+ );
5982
+ }
5983
+ }
5984
+ return verdict;
5985
+ }
5986
+
5827
5987
  // src/lib/preflight.ts
5828
5988
  function feature(id) {
5829
5989
  const found = contract_default.forbidden_server_features.find((f) => f.id === id);
@@ -5851,14 +6011,14 @@ var STATIC_EXPORT = /output\s*:\s*['"]export['"]/;
5851
6011
  var SOURCE_FILE = /\.(js|jsx|ts|tsx|mjs)$/;
5852
6012
  var GET_SERVER_SIDE_PROPS = /export\s+(?:async\s+)?(?:function|const|let|var)\s+getServerSideProps\b|export\s*\{[^}]*\bgetServerSideProps\b[^}]*\}/;
5853
6013
  var USE_SERVER = /^\s*(['"])use server\1\s*;?\s*$/m;
5854
- var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", "out"]);
5855
- var SCAN_FILE_BUDGET = 5e3;
6014
+ var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", "out"]);
6015
+ var SCAN_FILE_BUDGET2 = 5e3;
5856
6016
  function detectFramework(rootDir) {
5857
- const pkgPath = (0, import_node_path7.join)(rootDir, "package.json");
5858
- if (!(0, import_node_fs5.existsSync)(pkgPath)) return "vanilla";
6017
+ const pkgPath = (0, import_node_path8.join)(rootDir, "package.json");
6018
+ if (!(0, import_node_fs6.existsSync)(pkgPath)) return "vanilla";
5859
6019
  let pkg;
5860
6020
  try {
5861
- pkg = JSON.parse((0, import_node_fs5.readFileSync)(pkgPath, "utf8"));
6021
+ pkg = JSON.parse((0, import_node_fs6.readFileSync)(pkgPath, "utf8"));
5862
6022
  } catch {
5863
6023
  return "unknown";
5864
6024
  }
@@ -5868,32 +6028,32 @@ function detectFramework(rootDir) {
5868
6028
  }
5869
6029
  function readDeps(rootDir) {
5870
6030
  try {
5871
- const pkg = JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path7.join)(rootDir, "package.json"), "utf8"));
6031
+ const pkg = JSON.parse((0, import_node_fs6.readFileSync)((0, import_node_path8.join)(rootDir, "package.json"), "utf8"));
5872
6032
  return { ...pkg.dependencies, ...pkg.devDependencies };
5873
6033
  } catch {
5874
6034
  return {};
5875
6035
  }
5876
6036
  }
5877
6037
  function findFiles(rootDir, dir, pattern, out, cap = 20) {
5878
- if (out.length >= cap || !(0, import_node_fs5.existsSync)(dir)) return;
5879
- for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
6038
+ if (out.length >= cap || !(0, import_node_fs6.existsSync)(dir)) return;
6039
+ for (const entry of (0, import_node_fs6.readdirSync)(dir, { withFileTypes: true })) {
5880
6040
  if (out.length >= cap) return;
5881
- const abs = (0, import_node_path7.join)(dir, entry.name);
6041
+ const abs = (0, import_node_path8.join)(dir, entry.name);
5882
6042
  if (entry.isDirectory()) {
5883
- if (!SKIP_DIRS.has(entry.name)) findFiles(rootDir, abs, pattern, out, cap);
6043
+ if (!SKIP_DIRS2.has(entry.name)) findFiles(rootDir, abs, pattern, out, cap);
5884
6044
  } else if (entry.isFile() && pattern.test(entry.name)) {
5885
- out.push((0, import_node_path7.relative)(rootDir, abs).split(import_node_path7.sep).join("/"));
6045
+ out.push((0, import_node_path8.relative)(rootDir, abs).split(import_node_path8.sep).join("/"));
5886
6046
  }
5887
6047
  }
5888
6048
  }
5889
6049
  var sample = (list) => list.slice(0, 3).join(", ") + (list.length > 3 ? ", \u2026" : "");
5890
6050
  function collectSourceFiles(dir, out, budget) {
5891
- if (budget.left <= 0 || !(0, import_node_fs5.existsSync)(dir)) return;
5892
- for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
6051
+ if (budget.left <= 0 || !(0, import_node_fs6.existsSync)(dir)) return;
6052
+ for (const entry of (0, import_node_fs6.readdirSync)(dir, { withFileTypes: true })) {
5893
6053
  if (budget.left <= 0) return;
5894
- const abs = (0, import_node_path7.join)(dir, entry.name);
6054
+ const abs = (0, import_node_path8.join)(dir, entry.name);
5895
6055
  if (entry.isDirectory()) {
5896
- if (!SKIP_DIRS.has(entry.name)) collectSourceFiles(abs, out, budget);
6056
+ if (!SKIP_DIRS2.has(entry.name)) collectSourceFiles(abs, out, budget);
5897
6057
  } else if (entry.isFile() && SOURCE_FILE.test(entry.name)) {
5898
6058
  budget.left--;
5899
6059
  out.push(abs);
@@ -5903,10 +6063,10 @@ function collectSourceFiles(dir, out, budget) {
5903
6063
  function checkNextjs(rootDir) {
5904
6064
  const issues = [];
5905
6065
  const warnings = [];
5906
- const configName = NEXT_CONFIG_BASENAMES.find((name) => (0, import_node_fs5.existsSync)((0, import_node_path7.join)(rootDir, name)));
6066
+ const configName = NEXT_CONFIG_BASENAMES.find((name) => (0, import_node_fs6.existsSync)((0, import_node_path8.join)(rootDir, name)));
5907
6067
  let hasStaticExport = true;
5908
6068
  if (configName) {
5909
- const config = (0, import_node_fs5.readFileSync)((0, import_node_path7.join)(rootDir, configName), "utf8");
6069
+ const config = (0, import_node_fs6.readFileSync)((0, import_node_path8.join)(rootDir, configName), "utf8");
5910
6070
  hasStaticExport = STATIC_EXPORT.test(config);
5911
6071
  if (!hasStaticExport) {
5912
6072
  issues.push(
@@ -5920,31 +6080,31 @@ function checkNextjs(rootDir) {
5920
6080
  const hard = [];
5921
6081
  const pagesApi = [];
5922
6082
  for (const apiDir of PAGES_API_DIRS) {
5923
- findFiles(rootDir, (0, import_node_path7.join)(rootDir, apiDir), /\.(js|ts|jsx|tsx|mjs)$/, pagesApi);
6083
+ findFiles(rootDir, (0, import_node_path8.join)(rootDir, apiDir), /\.(js|ts|jsx|tsx|mjs)$/, pagesApi);
5924
6084
  }
5925
6085
  if (pagesApi.length > 0) hard.push(`pages/api/* routes (${pagesApi.length} file(s))`);
5926
6086
  const middlewareFiles = [];
5927
6087
  for (const base of ["", "src"]) {
5928
- const dir = base ? (0, import_node_path7.join)(rootDir, base) : rootDir;
5929
- if (!(0, import_node_fs5.existsSync)(dir)) continue;
5930
- for (const entry of (0, import_node_fs5.readdirSync)(dir)) {
5931
- if (MIDDLEWARE_FILE.test(entry)) middlewareFiles.push((0, import_node_path7.join)(base, entry).split(import_node_path7.sep).join("/"));
6088
+ const dir = base ? (0, import_node_path8.join)(rootDir, base) : rootDir;
6089
+ if (!(0, import_node_fs6.existsSync)(dir)) continue;
6090
+ for (const entry of (0, import_node_fs6.readdirSync)(dir)) {
6091
+ if (MIDDLEWARE_FILE.test(entry)) middlewareFiles.push((0, import_node_path8.join)(base, entry).split(import_node_path8.sep).join("/"));
5932
6092
  }
5933
6093
  }
5934
6094
  hard.push(...middlewareFiles);
5935
6095
  const sourceFiles = [];
5936
- collectSourceFiles(rootDir, sourceFiles, { left: SCAN_FILE_BUDGET });
6096
+ collectSourceFiles(rootDir, sourceFiles, { left: SCAN_FILE_BUDGET2 });
5937
6097
  const gssp = [];
5938
6098
  const useServer = [];
5939
6099
  for (const abs of sourceFiles) {
5940
6100
  if (gssp.length >= 20 && useServer.length >= 20) break;
5941
6101
  let text;
5942
6102
  try {
5943
- text = (0, import_node_fs5.readFileSync)(abs, "utf8");
6103
+ text = (0, import_node_fs6.readFileSync)(abs, "utf8");
5944
6104
  } catch {
5945
6105
  continue;
5946
6106
  }
5947
- const rel = (0, import_node_path7.relative)(rootDir, abs).split(import_node_path7.sep).join("/");
6107
+ const rel = (0, import_node_path8.relative)(rootDir, abs).split(import_node_path8.sep).join("/");
5948
6108
  if (gssp.length < 20 && GET_SERVER_SIDE_PROPS.test(text)) gssp.push(rel);
5949
6109
  if (useServer.length < 20 && USE_SERVER.test(text)) useServer.push(rel);
5950
6110
  }
@@ -5952,7 +6112,7 @@ function checkNextjs(rootDir) {
5952
6112
  if (useServer.length > 0) hard.push(`'use server' actions (${sample(useServer)})`);
5953
6113
  const routeFiles = [];
5954
6114
  for (const appDir of APP_DIRS) {
5955
- findFiles(rootDir, (0, import_node_path7.join)(rootDir, appDir), ROUTE_HANDLER_FILE, routeFiles);
6115
+ findFiles(rootDir, (0, import_node_path8.join)(rootDir, appDir), ROUTE_HANDLER_FILE, routeFiles);
5956
6116
  }
5957
6117
  if (routeFiles.length > 0) {
5958
6118
  const label = `${routeFiles.length} route handler(s) (${sample(routeFiles)})`;
@@ -5986,6 +6146,17 @@ function checkNextjs(rootDir) {
5986
6146
  }
5987
6147
  return { issues, warnings };
5988
6148
  }
6149
+ function checkBuildEnv(rootDir) {
6150
+ const verdict = judgeEnvReads(scanEnvReads(rootDir), {
6151
+ defined: ALWAYS_DEFINED_NAMES,
6152
+ backendUnknown: true,
6153
+ localOnly: envFileNames(rootDir)
6154
+ });
6155
+ return [
6156
+ ...verdict.errors.map((e) => `The cloud build will reject this project: ${e}`),
6157
+ ...verdict.warnings
6158
+ ];
6159
+ }
5989
6160
  function runPreflight(rootDir) {
5990
6161
  const framework = detectFramework(rootDir);
5991
6162
  const issues = [];
@@ -6000,17 +6171,18 @@ function runPreflight(rootDir) {
6000
6171
  issues.push(...next.issues);
6001
6172
  warnings.push(...next.warnings);
6002
6173
  }
6174
+ if (framework !== "vanilla") warnings.push(...checkBuildEnv(rootDir));
6003
6175
  return { framework, issues, warnings };
6004
6176
  }
6005
6177
 
6006
6178
  // src/lib/backendDecl.ts
6007
- var import_node_fs6 = require("fs");
6008
- var import_node_path8 = require("path");
6179
+ var import_node_fs7 = require("fs");
6180
+ var import_node_path9 = require("path");
6009
6181
  function readBackendDeclaration(dir = process.cwd()) {
6010
- const path = (0, import_node_path8.join)(dir, ".agentful", "backend.json");
6011
- if (!(0, import_node_fs6.existsSync)(path)) return null;
6182
+ const path = (0, import_node_path9.join)(dir, ".agentful", "backend.json");
6183
+ if (!(0, import_node_fs7.existsSync)(path)) return null;
6012
6184
  try {
6013
- const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
6185
+ const parsed = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
6014
6186
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
6015
6187
  const collections = Array.isArray(parsed.collections) ? parsed.collections.filter((c) => c && typeof c === "object" && !Array.isArray(c)) : [];
6016
6188
  const secrets = Array.isArray(parsed.secrets) ? parsed.secrets.filter((s) => s && typeof s === "object" && !Array.isArray(s)) : [];
@@ -6054,19 +6226,19 @@ function describeBackendDeclaration(decl) {
6054
6226
  function backendTabUrl(userId, projectId) {
6055
6227
  return `${APP_URL}/workspace/${userId}/${projectId}?view=backend`;
6056
6228
  }
6057
- var ACTIONS_DIR = (0, import_node_path8.join)(".server", "actions");
6229
+ var ACTIONS_DIR = (0, import_node_path9.join)(".server", "actions");
6058
6230
  var ACTION_NAME = /^[a-z][a-z0-9_-]{0,62}$/;
6059
6231
  function checkBackendDeclaration(rootDir = process.cwd()) {
6060
6232
  const issues = [];
6061
6233
  const warnings = [];
6062
- const actionsDir = (0, import_node_path8.join)(rootDir, ACTIONS_DIR);
6234
+ const actionsDir = (0, import_node_path9.join)(rootDir, ACTIONS_DIR);
6063
6235
  const presentFiles = /* @__PURE__ */ new Set();
6064
- if ((0, import_node_fs6.existsSync)(actionsDir)) {
6065
- for (const entry of (0, import_node_fs6.readdirSync)(actionsDir)) {
6066
- const abs = (0, import_node_path8.join)(actionsDir, entry);
6236
+ if ((0, import_node_fs7.existsSync)(actionsDir)) {
6237
+ for (const entry of (0, import_node_fs7.readdirSync)(actionsDir)) {
6238
+ const abs = (0, import_node_path9.join)(actionsDir, entry);
6067
6239
  let isDir = false;
6068
6240
  try {
6069
- isDir = (0, import_node_fs6.statSync)(abs).isDirectory();
6241
+ isDir = (0, import_node_fs7.statSync)(abs).isDirectory();
6070
6242
  } catch {
6071
6243
  continue;
6072
6244
  }
@@ -6118,6 +6290,7 @@ function checkBackendDeclaration(rootDir = process.cwd()) {
6118
6290
  }
6119
6291
 
6120
6292
  // src/lib/backendSync.ts
6293
+ var SWITCH_OFF_DELAY_S = contract_default.server_side.managed_actions.limits.switch_off_delay_s;
6121
6294
  var joinMethods = (methods) => methods && methods.length ? methods.join(" + ") : "none";
6122
6295
  function purposeOf(declaration, key) {
6123
6296
  const entry = (declaration?.secrets ?? []).find((s) => s.key === key);
@@ -6260,9 +6433,9 @@ async function fetchBackendSessionState(auth, userId, projectId, opts = {}) {
6260
6433
  }
6261
6434
  function describeServerState(state) {
6262
6435
  if (state.serverEnabled) {
6263
- return "Server: managed, enabled (Server Actions \u2014 functions we run for you). There is no activation point: pushed action code can change what the published app does immediately; warm instances may serve the previous code for up to five minutes.";
6436
+ return `Server: managed, enabled (Server Actions \u2014 functions we run for you). There is no activation point: pushed action code can change what the published app does immediately; warm instances may serve the previous code for up to five minutes. Switching it off in the Backend tab stops every action within ${SWITCH_OFF_DELAY_S} s.`;
6264
6437
  }
6265
- const notOn = "Action files you push are still executable through the platform API \u2014 the switch does not turn them off. It controls /api/* routing on the app's own domain and the sync of server.* secrets.";
6438
+ const notOn = "Action files you push are stored, but calls to them answer 404 server_not_enabled until you switch the managed server on in the Backend tab (`agentful backend` opens it \u2014 the CLI can enable the database, not the server). Switching on takes effect on the next call.";
6266
6439
  if (state.serverMode === "byo") {
6267
6440
  return `Server: your own API (${state.serverStatus || "not active"}) \u2014 /api/* on the app's domain is forwarded to it. Managed server: not switched on. ${notOn}`;
6268
6441
  }
@@ -6282,8 +6455,8 @@ function describeBackendState(state) {
6282
6455
  }
6283
6456
 
6284
6457
  // src/lib/backendConsent.ts
6285
- var import_node_fs7 = require("fs");
6286
- var import_node_path9 = require("path");
6458
+ var import_node_fs8 = require("fs");
6459
+ var import_node_path10 = require("path");
6287
6460
  var import_promises2 = require("readline/promises");
6288
6461
  function declaresManagedDatabase(decl) {
6289
6462
  if (!decl) return false;
@@ -6309,10 +6482,10 @@ function consentQuestion(decl) {
6309
6482
  '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).'
6310
6483
  ].join("\n");
6311
6484
  }
6312
- var consentFile = () => (0, import_node_path9.join)(configDir(), "backend-consent.json");
6485
+ var consentFile = () => (0, import_node_path10.join)(configDir(), "backend-consent.json");
6313
6486
  function readConsentFile() {
6314
6487
  try {
6315
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(consentFile(), "utf8"));
6488
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(consentFile(), "utf8"));
6316
6489
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
6317
6490
  } catch {
6318
6491
  return {};
@@ -6324,15 +6497,15 @@ function hasRememberedDecline(projectId) {
6324
6497
  function rememberDecline(projectId) {
6325
6498
  const all = readConsentFile();
6326
6499
  all[projectId] = { declined_at: (/* @__PURE__ */ new Date()).toISOString() };
6327
- (0, import_node_fs7.mkdirSync)(configDir(), { recursive: true });
6328
- (0, import_node_fs7.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
6500
+ (0, import_node_fs8.mkdirSync)(configDir(), { recursive: true });
6501
+ (0, import_node_fs8.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
6329
6502
  }
6330
6503
  function forgetDecline(projectId) {
6331
- if (!(0, import_node_fs7.existsSync)(consentFile())) return;
6504
+ if (!(0, import_node_fs8.existsSync)(consentFile())) return;
6332
6505
  const all = readConsentFile();
6333
6506
  if (!(projectId in all)) return;
6334
6507
  delete all[projectId];
6335
- (0, import_node_fs7.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
6508
+ (0, import_node_fs8.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
6336
6509
  }
6337
6510
  async function enableManagedDatabase(auth, userId, projectId) {
6338
6511
  try {
@@ -6540,13 +6713,14 @@ async function pushCommand(opts) {
6540
6713
  ui.step("Building in the cloud (this can take a few minutes)\u2026");
6541
6714
  const buildStartedAt = Date.now();
6542
6715
  try {
6543
- await request(`${PB_URL}/api/workspaces/${userId}/${projectId}/build`, {
6716
+ const built = await request(`${PB_URL}/api/workspaces/${userId}/${projectId}/build`, {
6544
6717
  method: "POST",
6545
6718
  token: auth.pb_token,
6546
6719
  body: {},
6547
6720
  timeoutMs: 66e4
6548
6721
  });
6549
6722
  ui.info("Build finished");
6723
+ for (const warning of built?.warnings ?? []) ui.warn(warning);
6550
6724
  } catch (err2) {
6551
6725
  if (err2 instanceof ApiError && err2.code === "network_error") {
6552
6726
  ui.warn("Connection dropped during the build \u2014 polling for the result\u2026");
@@ -6709,15 +6883,15 @@ async function openCommand() {
6709
6883
  // src/commands/tui.ts
6710
6884
  var import_node_child_process6 = require("child_process");
6711
6885
  var import_promises4 = require("readline/promises");
6712
- var import_node_path20 = require("path");
6886
+ var import_node_path21 = require("path");
6713
6887
 
6714
6888
  // src/lib/binWrapper.ts
6715
- var import_node_fs8 = require("fs");
6716
- var import_node_path10 = require("path");
6889
+ var import_node_fs9 = require("fs");
6890
+ var import_node_path11 = require("path");
6717
6891
  var import_node_os3 = require("os");
6718
6892
  function ensureCliOnPath() {
6719
- const binDir = (0, import_node_path10.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
6720
- (0, import_node_fs8.mkdirSync)(binDir, { recursive: true });
6893
+ const binDir = (0, import_node_path11.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
6894
+ (0, import_node_fs9.mkdirSync)(binDir, { recursive: true });
6721
6895
  const entry = process.argv[1];
6722
6896
  if (!entry) return binDir;
6723
6897
  const restore = (name) => {
@@ -6726,8 +6900,8 @@ function ensureCliOnPath() {
6726
6900
  ` : `export ${name}=${JSON.stringify(original)}
6727
6901
  `;
6728
6902
  };
6729
- const wrapper = (0, import_node_path10.join)(binDir, "agentful");
6730
- (0, import_node_fs8.writeFileSync)(
6903
+ const wrapper = (0, import_node_path11.join)(binDir, "agentful");
6904
+ (0, import_node_fs9.writeFileSync)(
6731
6905
  wrapper,
6732
6906
  `#!/bin/sh
6733
6907
  # Generated by the Agentful CLI so the coding agent can run \`agentful \u2026\`.
@@ -6737,7 +6911,7 @@ function ensureCliOnPath() {
6737
6911
  "utf8"
6738
6912
  );
6739
6913
  try {
6740
- (0, import_node_fs8.chmodSync)(wrapper, 493);
6914
+ (0, import_node_fs9.chmodSync)(wrapper, 493);
6741
6915
  } catch {
6742
6916
  }
6743
6917
  return binDir;
@@ -6747,9 +6921,9 @@ function ensureCliOnPath() {
6747
6921
  init_branding();
6748
6922
 
6749
6923
  // src/lib/engine.ts
6750
- var import_node_fs9 = require("fs");
6924
+ var import_node_fs10 = require("fs");
6751
6925
  var import_node_crypto = require("crypto");
6752
- var import_node_path11 = require("path");
6926
+ var import_node_path12 = require("path");
6753
6927
  var import_node_os4 = require("os");
6754
6928
  var import_node_child_process3 = require("child_process");
6755
6929
  var import_node_stream = require("stream");
@@ -6794,8 +6968,8 @@ function platformKey() {
6794
6968
  );
6795
6969
  }
6796
6970
  function cacheDir(version) {
6797
- const base = process.env.XDG_CACHE_HOME || (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".cache");
6798
- return (0, import_node_path11.join)(base, "agentful", "engine", version);
6971
+ const base = process.env.XDG_CACHE_HOME || (0, import_node_path12.join)((0, import_node_os4.homedir)(), ".cache");
6972
+ return (0, import_node_path12.join)(base, "agentful", "engine", version);
6799
6973
  }
6800
6974
  async function fetchManifest() {
6801
6975
  try {
@@ -6806,7 +6980,7 @@ async function fetchManifest() {
6806
6980
  }
6807
6981
  async function sha256File(path) {
6808
6982
  const hash = (0, import_node_crypto.createHash)("sha256");
6809
- await (0, import_promises3.pipeline)((0, import_node_fs9.createReadStream)(path), hash);
6983
+ await (0, import_promises3.pipeline)((0, import_node_fs10.createReadStream)(path), hash);
6810
6984
  return hash.digest("hex");
6811
6985
  }
6812
6986
  async function ensureEngine() {
@@ -6815,12 +6989,12 @@ async function ensureEngine() {
6815
6989
  noticeIfOutdated(manifest);
6816
6990
  const version = manifest?.engine_version || FALLBACK_ENGINE_VERSION;
6817
6991
  const dir = cacheDir(version);
6818
- const binPath = (0, import_node_path11.join)(dir, ENGINE_BINARY);
6819
- if ((0, import_node_fs9.existsSync)(binPath)) return binPath;
6992
+ const binPath = (0, import_node_path12.join)(dir, ENGINE_BINARY);
6993
+ if ((0, import_node_fs10.existsSync)(binPath)) return binPath;
6820
6994
  const artifact = manifest?.artifacts?.[key];
6821
6995
  const url = artifact?.url || `${APP_URL}/cli/engine/${version}/${key}${key.startsWith("darwin") ? ".zip" : ".tar.gz"}`;
6822
- (0, import_node_fs9.mkdirSync)(dir, { recursive: true });
6823
- const archivePath = (0, import_node_path11.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
6996
+ (0, import_node_fs10.mkdirSync)(dir, { recursive: true });
6997
+ const archivePath = (0, import_node_path12.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
6824
6998
  ui.step(`Preparing the ${brand.displayName} engine (first run only)\u2026`);
6825
6999
  const resp = await fetch(url, {
6826
7000
  redirect: "follow",
@@ -6834,11 +7008,11 @@ async function ensureEngine() {
6834
7008
  `Could not download the engine (HTTP ${resp.status}). Check your connection and try again.`
6835
7009
  );
6836
7010
  }
6837
- await (0, import_promises3.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs9.createWriteStream)(archivePath));
7011
+ await (0, import_promises3.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs10.createWriteStream)(archivePath));
6838
7012
  if (artifact?.sha256) {
6839
7013
  const actual = await sha256File(archivePath);
6840
7014
  if (actual !== artifact.sha256) {
6841
- (0, import_node_fs9.rmSync)(archivePath, { force: true });
7015
+ (0, import_node_fs10.rmSync)(archivePath, { force: true });
6842
7016
  throw new ApiError(
6843
7017
  0,
6844
7018
  "engine_checksum_mismatch",
@@ -6847,7 +7021,7 @@ async function ensureEngine() {
6847
7021
  }
6848
7022
  }
6849
7023
  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" });
6850
- (0, import_node_fs9.rmSync)(archivePath, { force: true });
7024
+ (0, import_node_fs10.rmSync)(archivePath, { force: true });
6851
7025
  if (extract.status !== 0) {
6852
7026
  throw new ApiError(
6853
7027
  0,
@@ -6855,24 +7029,24 @@ async function ensureEngine() {
6855
7029
  `Could not unpack the engine (needs ${archivePath.endsWith(".zip") ? "unzip" : "tar"} on PATH).`
6856
7030
  );
6857
7031
  }
6858
- const extracted = (0, import_node_path11.join)(dir, "opencode");
6859
- if ((0, import_node_fs9.existsSync)(extracted) && !(0, import_node_fs9.existsSync)(binPath)) (0, import_node_fs9.renameSync)(extracted, binPath);
6860
- if (!(0, import_node_fs9.existsSync)(binPath)) {
7032
+ const extracted = (0, import_node_path12.join)(dir, "opencode");
7033
+ if ((0, import_node_fs10.existsSync)(extracted) && !(0, import_node_fs10.existsSync)(binPath)) (0, import_node_fs10.renameSync)(extracted, binPath);
7034
+ if (!(0, import_node_fs10.existsSync)(binPath)) {
6861
7035
  throw new ApiError(0, "engine_extract_failed", "Engine archive did not contain the expected binary.");
6862
7036
  }
6863
- (0, import_node_fs9.chmodSync)(binPath, 493);
7037
+ (0, import_node_fs10.chmodSync)(binPath, 493);
6864
7038
  ui.ok(`Engine ready.`);
6865
7039
  console.log(paint.dim(" powered by opencode (MIT) \xB7 run `agentful licenses` for details"));
6866
7040
  return binPath;
6867
7041
  }
6868
7042
 
6869
7043
  // src/lib/gatewayToken.ts
6870
- var import_node_fs10 = require("fs");
6871
- var import_node_path12 = require("path");
6872
- var cachePath = () => (0, import_node_path12.join)(configDir(), "gateway.json");
7044
+ var import_node_fs11 = require("fs");
7045
+ var import_node_path13 = require("path");
7046
+ var cachePath = () => (0, import_node_path13.join)(configDir(), "gateway.json");
6873
7047
  async function ensureGatewaySession(auth, projectId) {
6874
7048
  try {
6875
- const cached = JSON.parse((0, import_node_fs10.readFileSync)(cachePath(), "utf8"));
7049
+ const cached = JSON.parse((0, import_node_fs11.readFileSync)(cachePath(), "utf8"));
6876
7050
  if (cached?.token && cached.expires_at - Date.now() / 1e3 > 3600) {
6877
7051
  return cached;
6878
7052
  }
@@ -6883,18 +7057,18 @@ async function ensureGatewaySession(auth, projectId) {
6883
7057
  body: projectId ? { project_id: projectId } : {},
6884
7058
  timeoutMs: 3e4
6885
7059
  });
6886
- (0, import_node_fs10.mkdirSync)(configDir(), { recursive: true });
6887
- (0, import_node_fs10.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
7060
+ (0, import_node_fs11.mkdirSync)(configDir(), { recursive: true });
7061
+ (0, import_node_fs11.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
6888
7062
  try {
6889
- (0, import_node_fs10.chmodSync)(cachePath(), 384);
7063
+ (0, import_node_fs11.chmodSync)(cachePath(), 384);
6890
7064
  } catch {
6891
7065
  }
6892
7066
  return res;
6893
7067
  }
6894
7068
 
6895
7069
  // src/lib/engineConfig.ts
6896
- var import_node_fs12 = require("fs");
6897
- var import_node_path14 = require("path");
7070
+ var import_node_fs13 = require("fs");
7071
+ var import_node_path15 = require("path");
6898
7072
  var import_node_os5 = require("os");
6899
7073
 
6900
7074
  // src/branding/agentful-theme.json
@@ -7037,22 +7211,22 @@ ${cmd.template}
7037
7211
  }
7038
7212
 
7039
7213
  // src/lib/tools.ts
7040
- var import_node_fs11 = require("fs");
7041
- var import_node_path13 = require("path");
7214
+ var import_node_fs12 = require("fs");
7215
+ var import_node_path14 = require("path");
7042
7216
  function assetsDir() {
7043
- const here = (0, import_node_path13.dirname)(process.argv[1] || "");
7044
- for (const candidate of [(0, import_node_path13.join)(here, "..", "assets"), (0, import_node_path13.join)(here, "assets")]) {
7045
- if ((0, import_node_fs11.existsSync)((0, import_node_path13.join)(candidate, "tools", "imagegen.ts"))) return candidate;
7217
+ const here = (0, import_node_path14.dirname)(process.argv[1] || "");
7218
+ for (const candidate of [(0, import_node_path14.join)(here, "..", "assets"), (0, import_node_path14.join)(here, "assets")]) {
7219
+ if ((0, import_node_fs12.existsSync)((0, import_node_path14.join)(candidate, "tools", "imagegen.ts"))) return candidate;
7046
7220
  }
7047
7221
  return null;
7048
7222
  }
7049
7223
  function installEngineTools(configDir2) {
7050
7224
  const assets = assetsDir();
7051
7225
  if (!assets) return false;
7052
- const target = (0, import_node_path13.join)(configDir2, "tools");
7053
- (0, import_node_fs11.mkdirSync)(target, { recursive: true });
7226
+ const target = (0, import_node_path14.join)(configDir2, "tools");
7227
+ (0, import_node_fs12.mkdirSync)(target, { recursive: true });
7054
7228
  try {
7055
- (0, import_node_fs11.cpSync)((0, import_node_path13.join)(assets, "tools"), target, { recursive: true });
7229
+ (0, import_node_fs12.cpSync)((0, import_node_path14.join)(assets, "tools"), target, { recursive: true });
7056
7230
  return true;
7057
7231
  } catch {
7058
7232
  return false;
@@ -7115,11 +7289,20 @@ function backendSessionLines(backend) {
7115
7289
  return lines.join("\n");
7116
7290
  }
7117
7291
  function managedServerLine(backend) {
7292
+ const delay2 = contract_default.server_side.managed_actions.limits.switch_off_delay_s;
7118
7293
  if (backend.serverEnabled) {
7119
- return "Managed server: **enabled**. **There is no activation point:** pushed action source can change what the published app does immediately, while warm instances may keep serving the previous code for up to five minutes.";
7294
+ return `Managed server: **enabled**. **There is no activation point:** pushed action source can change what the published app does immediately, while warm instances may keep serving the previous code for up to five minutes. Switching the server off in the Backend tab stops every action within ${delay2} s.`;
7120
7295
  }
7121
7296
  const ownApi = backend.serverMode === "byo" ? "Server: the owner connected **an API of their own** \u2014 `/api/*` on the app's domain is forwarded to it, and the platform adds no secret and authorizes nobody. " : "";
7122
- return `${ownApi}Managed server: **not switched on** in the Backend tab. **Action files you push are still executable through the platform API** \u2014 the switch does not turn them off. It controls \`/api/*\` routing on the app's own domain and the sync of \`server.*\` secrets.`;
7297
+ return `${ownApi}Managed server: **not switched on** in the Backend tab. **Action files you push are stored, but every call to them answers \`404 server_not_enabled\`** until the owner switches the managed server on (\`agentful backend\` opens the tab; the CLI can enable the database, NOT the server). Switching on takes effect on the next call. Never tell the user an action works before the server is on.`;
7298
+ }
7299
+ function buildEnvList() {
7300
+ const entries = contract_default.publish.build_env.injected;
7301
+ const when = {
7302
+ always: "always",
7303
+ byo_supabase: "only while the owner's own Supabase database is connected in the Backend tab"
7304
+ };
7305
+ return entries.map((e) => ` - ${e.prefixes.map((p) => `\`${p}${e.suffix}\``).join(", ")} \u2014 ${when[e.when] ?? e.when}`).join("\n");
7123
7306
  }
7124
7307
  var bytes = (n) => n >= 1024 * 1024 ? `${n / (1024 * 1024)} MB` : `${n / 1024} KB`;
7125
7308
  function serverSideSection() {
@@ -7130,6 +7313,7 @@ function serverSideSection() {
7130
7313
  `### ${heading} ${t.label}`,
7131
7314
  "",
7132
7315
  t.summary,
7316
+ ...t.switch_note ? ["", t.switch_note] : [],
7133
7317
  "",
7134
7318
  "Offers:",
7135
7319
  ...t.offers.map((o) => `- ${o}`),
@@ -7147,7 +7331,7 @@ function serverSideSection() {
7147
7331
  track(
7148
7332
  "managed_actions",
7149
7333
  "(a)",
7150
- `${a.action_timeout_budget_s} s budget per action, ${a.hard_timeout_s} s hard limit, ${a.memory_mb} MB memory, request body \u2264 ${bytes(a.max_body_bytes)}, action source \u2264 ${bytes(a.max_code_bytes)}, ${a.project_concurrency} concurrent calls per project, self-call depth \u2264 ${a.max_action_depth}.`
7334
+ `${a.action_timeout_budget_s} s budget per action, ${a.hard_timeout_s} s hard limit, ${a.memory_mb} MB memory, request body \u2264 ${bytes(a.max_body_bytes)}, action source \u2264 ${bytes(a.max_code_bytes)}, ${a.project_concurrency} concurrent calls per project, self-call depth \u2264 ${a.max_action_depth}, switch-off delay \u2264 ${a.switch_off_delay_s} s.`
7151
7335
  ),
7152
7336
  track("own_api", "(b)", null),
7153
7337
  track(
@@ -7175,18 +7359,26 @@ is fine \u2014 but then say clearly that \`agentful push\` cannot deploy it.
7175
7359
  Server-side logic has its own place \u2014 see "Where server logic belongs".
7176
7360
  - The canonical build output is a root-level \`index.html\` inside the
7177
7361
  framework's output directory.
7178
- - There are **no build-time env vars**: the cloud build injects nothing beyond
7179
- NODE_ENV. There is no settings tab for build secrets \u2014 do not invent one.
7180
- Backend-tab secrets are runtime-only for Managed Server Actions and never
7181
- reach a build; the workspace executor injects only allowlisted PUBLIC client
7182
- vars (\`database.anon_key\`/\`publishable_key\` expanded as
7183
- \`VITE_\`/\`PUBLIC_\`/\`NEXT_PUBLIC_\`). Never rely on \`.env*\` files reaching the
7184
- cloud \u2014 the CLI and server both strip them.
7185
- - So PUBLIC client values go into an ordinary source constant, not into
7186
- \`import.meta.env\`/\`process.env\`: the project ID (below) and, when the app
7187
- talks to the owner's own Supabase, its project URL and **publishable (anon)
7188
- key**. In an \`agentful push\` build \`import.meta.env.VITE_SUPABASE_URL\` is
7189
- \`undefined\` \u2014 the app would compile and then fail to reach its database.
7362
+ - You **cannot set build-time env vars**, and there is no settings tab for
7363
+ build secrets \u2014 do not invent one. Backend-tab secrets are runtime-only for
7364
+ Managed Server Actions and never reach a build. Every cloud build \u2014 the
7365
+ workspace build and the \`agentful push\` build alike \u2014 defines the same
7366
+ fixed allowlist of PUBLIC client values, and nothing else:
7367
+ ${buildEnvList()}
7368
+ Never rely on \`.env*\` files reaching the cloud \u2014 the CLI never uploads
7369
+ them.
7370
+ - A cloud build **rejects** a read of one of those names while nothing defines
7371
+ it (e.g. \`import.meta.env.VITE_SUPABASE_URL\` with no own Supabase database
7372
+ connected): it would compile to \`undefined\` without any build error and the
7373
+ app would fail to reach its database. A read of any OTHER
7374
+ \`VITE_\`/\`PUBLIC_\`/\`NEXT_PUBLIC_\` name is never defined by the platform; the
7375
+ build warns and inlines \`undefined\`.
7376
+ - So PUBLIC client values of your own go into an ordinary source constant, not
7377
+ into \`import.meta.env\`/\`process.env\`. For the project ID (below) and, when
7378
+ the app talks to the owner's own Supabase, its project URL and **publishable
7379
+ (anon) key**, a source constant is also the form that works in local
7380
+ development: on this machine nothing defines the names above unless the user
7381
+ keeps a local \`.env\`, which never leaves it.
7190
7382
  Never put a Supabase **secret / service-role key** in source: that one
7191
7383
  belongs in Backend-tab secrets and is only readable from a Managed Server
7192
7384
  Action.
@@ -7337,7 +7529,7 @@ var CLOUD_AGENT_PROMPT = `You are the Agentful Cloud build agent: everything you
7337
7529
  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.`;
7338
7530
  function readAuthFile(path) {
7339
7531
  try {
7340
- return (0, import_node_fs12.readFileSync)(path, "utf8");
7532
+ return (0, import_node_fs13.readFileSync)(path, "utf8");
7341
7533
  } catch {
7342
7534
  return null;
7343
7535
  }
@@ -7402,11 +7594,11 @@ function buildEngineConfig(opts) {
7402
7594
  };
7403
7595
  }
7404
7596
  function engineXdg() {
7405
- const root = (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
7597
+ const root = (0, import_node_path15.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
7406
7598
  return {
7407
- configHome: (0, import_node_path14.join)(root, "engine-config"),
7408
- dataHome: (0, import_node_path14.join)(root, "engine-data"),
7409
- stateHome: (0, import_node_path14.join)(root, "engine-state")
7599
+ configHome: (0, import_node_path15.join)(root, "engine-config"),
7600
+ dataHome: (0, import_node_path15.join)(root, "engine-data"),
7601
+ stateHome: (0, import_node_path15.join)(root, "engine-state")
7410
7602
  };
7411
7603
  }
7412
7604
  async function resolveTheme() {
@@ -7425,24 +7617,24 @@ async function resolveTheme() {
7425
7617
  }
7426
7618
  async function writeEngineSession(session, catalog, localProviders = {}, framework = "unknown", skillsInstruction = null, backendState, model) {
7427
7619
  const xdg = engineXdg();
7428
- const configDir2 = (0, import_node_path14.join)(xdg.configHome, "opencode");
7429
- const dataDir = (0, import_node_path14.join)(xdg.dataHome, "opencode");
7430
- const themesDir = (0, import_node_path14.join)(configDir2, "themes");
7431
- const pluginsDir = (0, import_node_path14.join)(configDir2, "plugins");
7432
- const commandsDir = (0, import_node_path14.join)(configDir2, "commands");
7433
- for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path14.join)(xdg.stateHome, "opencode")]) {
7434
- (0, import_node_fs12.mkdirSync)(dir, { recursive: true });
7620
+ const configDir2 = (0, import_node_path15.join)(xdg.configHome, "opencode");
7621
+ const dataDir = (0, import_node_path15.join)(xdg.dataHome, "opencode");
7622
+ const themesDir = (0, import_node_path15.join)(configDir2, "themes");
7623
+ const pluginsDir = (0, import_node_path15.join)(configDir2, "plugins");
7624
+ const commandsDir = (0, import_node_path15.join)(configDir2, "commands");
7625
+ for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path15.join)(xdg.stateHome, "opencode")]) {
7626
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
7435
7627
  }
7436
7628
  const imagegenAvailable = installEngineTools(configDir2) && session.img_on !== false;
7437
- const cloudInstructionsPath = (0, import_node_path14.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
7438
- (0, import_node_fs12.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework, backendState));
7439
- const skillsPath = (0, import_node_path14.join)(configDir2, "AGENTFUL_SKILLS.md");
7629
+ const cloudInstructionsPath = (0, import_node_path15.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
7630
+ (0, import_node_fs13.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework, backendState));
7631
+ const skillsPath = (0, import_node_path15.join)(configDir2, "AGENTFUL_SKILLS.md");
7440
7632
  const instructionPaths = [cloudInstructionsPath];
7441
7633
  if (skillsInstruction) {
7442
- (0, import_node_fs12.writeFileSync)(skillsPath, skillsInstruction);
7634
+ (0, import_node_fs13.writeFileSync)(skillsPath, skillsInstruction);
7443
7635
  instructionPaths.push(skillsPath);
7444
7636
  } else {
7445
- (0, import_node_fs12.rmSync)(skillsPath, { force: true });
7637
+ (0, import_node_fs13.rmSync)(skillsPath, { force: true });
7446
7638
  }
7447
7639
  const config = buildEngineConfig({
7448
7640
  session,
@@ -7452,13 +7644,13 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
7452
7644
  instructionPaths,
7453
7645
  model
7454
7646
  });
7455
- (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
7456
- const authPath2 = (0, import_node_path14.join)(dataDir, "auth.json");
7457
- (0, import_node_fs12.writeFileSync)(authPath2, mergedAuthJson(readAuthFile(authPath2), session.token));
7647
+ (0, import_node_fs13.writeFileSync)((0, import_node_path15.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
7648
+ const authPath2 = (0, import_node_path15.join)(dataDir, "auth.json");
7649
+ (0, import_node_fs13.writeFileSync)(authPath2, mergedAuthJson(readAuthFile(authPath2), session.token));
7458
7650
  const { brandingPluginSource: brandingPluginSource2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
7459
- const pluginPath = (0, import_node_path14.join)(pluginsDir, "agentful-branding.tsx");
7460
- (0, import_node_fs12.writeFileSync)(pluginPath, brandingPluginSource2());
7461
- (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(configDir2, "tui.json"), JSON.stringify({
7651
+ const pluginPath = (0, import_node_path15.join)(pluginsDir, "agentful-branding.tsx");
7652
+ (0, import_node_fs13.writeFileSync)(pluginPath, brandingPluginSource2());
7653
+ (0, import_node_fs13.writeFileSync)((0, import_node_path15.join)(configDir2, "tui.json"), JSON.stringify({
7462
7654
  theme: "agentful",
7463
7655
  plugin: [`file://${pluginPath}`],
7464
7656
  // The engine's own sidebar sections are plugins with fixed ids, and
@@ -7470,13 +7662,13 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
7470
7662
  // state over this map.
7471
7663
  plugin_enabled: DISABLED_ENGINE_PLUGINS
7472
7664
  }, null, 2));
7473
- (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
7665
+ (0, import_node_fs13.writeFileSync)((0, import_node_path15.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
7474
7666
  for (const name of Object.keys(SLASH_COMMANDS)) {
7475
- (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(commandsDir, `${name}.md`), commandMarkdown(name));
7667
+ (0, import_node_fs13.writeFileSync)((0, import_node_path15.join)(commandsDir, `${name}.md`), commandMarkdown(name));
7476
7668
  }
7477
7669
  for (const legacy of ["xdg-config", "xdg-data", "xdg-state"]) {
7478
7670
  try {
7479
- (0, import_node_fs12.rmSync)((0, import_node_path14.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
7671
+ (0, import_node_fs13.rmSync)((0, import_node_path15.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
7480
7672
  } catch {
7481
7673
  }
7482
7674
  }
@@ -7484,8 +7676,8 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
7484
7676
  }
7485
7677
 
7486
7678
  // src/lib/skills.ts
7487
- var import_node_fs13 = require("fs");
7488
- var import_node_path15 = require("path");
7679
+ var import_node_fs14 = require("fs");
7680
+ var import_node_path16 = require("path");
7489
7681
 
7490
7682
  // src/generated/skills.json
7491
7683
  var skills_default = {
@@ -7554,7 +7746,7 @@ var SCAFFOLD_BY_FRAMEWORK = {
7554
7746
  };
7555
7747
  function hasVueDependency(rootDir) {
7556
7748
  try {
7557
- const pkg = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path15.join)(rootDir, "package.json"), "utf8"));
7749
+ const pkg = JSON.parse((0, import_node_fs14.readFileSync)((0, import_node_path16.join)(rootDir, "package.json"), "utf8"));
7558
7750
  return Boolean({ ...pkg.dependencies, ...pkg.devDependencies }["vue"]);
7559
7751
  } catch {
7560
7752
  return false;
@@ -7584,7 +7776,7 @@ function renderSkillsInstruction(names) {
7584
7776
  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";
7585
7777
  }
7586
7778
  function skillsInstructionFor(framework, rootDir) {
7587
- if (!(0, import_node_fs13.existsSync)(rootDir)) return null;
7779
+ if (!(0, import_node_fs14.existsSync)(rootDir)) return null;
7588
7780
  return renderSkillsInstruction(selectSkills(framework, rootDir));
7589
7781
  }
7590
7782
 
@@ -7732,15 +7924,15 @@ function decideLocalProviders(policy) {
7732
7924
  }
7733
7925
 
7734
7926
  // src/lib/localProviders.ts
7735
- var import_node_fs14 = require("fs");
7736
- var import_node_path16 = require("path");
7927
+ var import_node_fs15 = require("fs");
7928
+ var import_node_path17 = require("path");
7737
7929
  var LOCAL_PROVIDERS_FILE = "local-providers.json";
7738
7930
  function localProvidersPath() {
7739
- return (0, import_node_path16.join)(configDir(), LOCAL_PROVIDERS_FILE);
7931
+ return (0, import_node_path17.join)(configDir(), LOCAL_PROVIDERS_FILE);
7740
7932
  }
7741
7933
  function readLocalProviders() {
7742
7934
  try {
7743
- const raw = JSON.parse((0, import_node_fs14.readFileSync)(localProvidersPath(), "utf8"));
7935
+ const raw = JSON.parse((0, import_node_fs15.readFileSync)(localProvidersPath(), "utf8"));
7744
7936
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
7745
7937
  const map = raw.providers && typeof raw.providers === "object" ? raw.providers : raw;
7746
7938
  const usable = {};
@@ -7775,8 +7967,8 @@ function resolveLocalProviders(policy) {
7775
7967
  }
7776
7968
  function writeLocalProvidersExample() {
7777
7969
  const path = localProvidersPath();
7778
- if ((0, import_node_fs14.existsSync)(path)) return path;
7779
- (0, import_node_fs14.mkdirSync)(configDir(), { recursive: true });
7970
+ if ((0, import_node_fs15.existsSync)(path)) return path;
7971
+ (0, import_node_fs15.mkdirSync)(configDir(), { recursive: true });
7780
7972
  const example = {
7781
7973
  _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.',
7782
7974
  _anthropic: {
@@ -7786,17 +7978,17 @@ function writeLocalProvidersExample() {
7786
7978
  models: { "claude-sonnet-4-5": { name: "Claude Sonnet 4.5 (my key)" } }
7787
7979
  }
7788
7980
  };
7789
- (0, import_node_fs14.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
7981
+ (0, import_node_fs15.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
7790
7982
  try {
7791
- (0, import_node_fs14.chmodSync)(path, 384);
7983
+ (0, import_node_fs15.chmodSync)(path, 384);
7792
7984
  } catch {
7793
7985
  }
7794
7986
  return path;
7795
7987
  }
7796
7988
 
7797
7989
  // src/lib/sessions.ts
7798
- var import_node_fs15 = require("fs");
7799
- var import_node_path17 = require("path");
7990
+ var import_node_fs16 = require("fs");
7991
+ var import_node_path18 = require("path");
7800
7992
  var import_node_child_process4 = require("child_process");
7801
7993
  function parseSessionModel(raw) {
7802
7994
  if (!raw) return null;
@@ -7813,8 +8005,8 @@ function parseSessionModel(raw) {
7813
8005
  }
7814
8006
  }
7815
8007
  function modelForSession(sessionID) {
7816
- const dbPath = (0, import_node_path17.join)(engineXdg().dataHome, "opencode", "opencode.db");
7817
- if (!(0, import_node_fs15.existsSync)(dbPath)) return null;
8008
+ const dbPath = (0, import_node_path18.join)(engineXdg().dataHome, "opencode", "opencode.db");
8009
+ if (!(0, import_node_fs16.existsSync)(dbPath)) return null;
7818
8010
  const escaped = sessionID.replace(/'/g, "''");
7819
8011
  const res = (0, import_node_child_process4.spawnSync)(
7820
8012
  "sqlite3",
@@ -7835,8 +8027,8 @@ function modelForSession(sessionID) {
7835
8027
  }
7836
8028
  }
7837
8029
  function lastSessionForDirectory(cwd = process.cwd()) {
7838
- const dbPath = (0, import_node_path17.join)(engineXdg().dataHome, "opencode", "opencode.db");
7839
- if (!(0, import_node_fs15.existsSync)(dbPath)) return null;
8030
+ const dbPath = (0, import_node_path18.join)(engineXdg().dataHome, "opencode", "opencode.db");
8031
+ if (!(0, import_node_fs16.existsSync)(dbPath)) return null;
7840
8032
  const escaped = cwd.replace(/'/g, "''");
7841
8033
  const res = (0, import_node_child_process4.spawnSync)(
7842
8034
  "sqlite3",
@@ -7862,9 +8054,9 @@ function lastSessionForDirectory(cwd = process.cwd()) {
7862
8054
  }
7863
8055
 
7864
8056
  // src/lib/modelPin.ts
7865
- var import_node_fs16 = require("fs");
7866
- var import_node_path18 = require("path");
7867
- var PROJECT_MODEL_PIN = (0, import_node_path18.join)(".agentful", "model.json");
8057
+ var import_node_fs17 = require("fs");
8058
+ var import_node_path19 = require("path");
8059
+ var PROJECT_MODEL_PIN = (0, import_node_path19.join)(".agentful", "model.json");
7868
8060
  function isCanonicalModel(value) {
7869
8061
  const slash = value.indexOf("/");
7870
8062
  return slash > 0 && slash < value.length - 1 && !/\s/.test(value);
@@ -7884,10 +8076,10 @@ function assertModelPin(pin, source) {
7884
8076
  return pin;
7885
8077
  }
7886
8078
  function readProjectModelPin(cwd = process.cwd()) {
7887
- const path = (0, import_node_path18.join)(cwd, PROJECT_MODEL_PIN);
8079
+ const path = (0, import_node_path19.join)(cwd, PROJECT_MODEL_PIN);
7888
8080
  let raw;
7889
8081
  try {
7890
- raw = (0, import_node_fs16.readFileSync)(path, "utf8");
8082
+ raw = (0, import_node_fs17.readFileSync)(path, "utf8");
7891
8083
  } catch (error) {
7892
8084
  const code = error.code;
7893
8085
  if (code === "ENOENT") return null;
@@ -7914,7 +8106,7 @@ function readProjectModelPin(cwd = process.cwd()) {
7914
8106
  function readEngineVariants(xdg) {
7915
8107
  try {
7916
8108
  const parsed = JSON.parse(
7917
- (0, import_node_fs16.readFileSync)((0, import_node_path18.join)(xdg.stateHome, "opencode", "model.json"), "utf8")
8109
+ (0, import_node_fs17.readFileSync)((0, import_node_path19.join)(xdg.stateHome, "opencode", "model.json"), "utf8")
7918
8110
  );
7919
8111
  if (!parsed.variant || typeof parsed.variant !== "object" || Array.isArray(parsed.variant)) return {};
7920
8112
  return Object.fromEntries(
@@ -7926,17 +8118,17 @@ function readEngineVariants(xdg) {
7926
8118
  }
7927
8119
  function writeEngineVariant(xdg, model, variant) {
7928
8120
  assertModelPin({ model, variant }, "Selected");
7929
- const path = (0, import_node_path18.join)(xdg.stateHome, "opencode", "model.json");
8121
+ const path = (0, import_node_path19.join)(xdg.stateHome, "opencode", "model.json");
7930
8122
  let state = {};
7931
8123
  try {
7932
- const parsed = JSON.parse((0, import_node_fs16.readFileSync)(path, "utf8"));
8124
+ const parsed = JSON.parse((0, import_node_fs17.readFileSync)(path, "utf8"));
7933
8125
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) state = parsed;
7934
8126
  } catch {
7935
8127
  }
7936
8128
  const existing = state.variant;
7937
8129
  const variants = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
7938
8130
  state.variant = { ...variants, [model]: variant };
7939
- (0, import_node_fs16.writeFileSync)(path, JSON.stringify(state, null, 2) + "\n", "utf8");
8131
+ (0, import_node_fs17.writeFileSync)(path, JSON.stringify(state, null, 2) + "\n", "utf8");
7940
8132
  }
7941
8133
  function resolveModelPin(opts) {
7942
8134
  const cliPin = opts.cliModel ? assertModelPin({ model: opts.cliModel, variant: opts.cliVariant }, "CLI") : null;
@@ -7985,8 +8177,8 @@ function resolveModelPin(opts) {
7985
8177
  // src/lib/engineModels.ts
7986
8178
  var import_node_child_process5 = require("child_process");
7987
8179
  var import_node_util = require("util");
7988
- var import_node_fs17 = require("fs");
7989
- var import_node_path19 = require("path");
8180
+ var import_node_fs18 = require("fs");
8181
+ var import_node_path20 = require("path");
7990
8182
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process5.execFile);
7991
8183
  function parseEngineModelsOutput(output) {
7992
8184
  return Array.from(new Set(
@@ -8037,7 +8229,7 @@ async function assertEngineModelAvailable(binPath, xdg, model, listModels = list
8037
8229
  function readConnectedEngineProviders(xdg) {
8038
8230
  try {
8039
8231
  const parsed = JSON.parse(
8040
- (0, import_node_fs17.readFileSync)((0, import_node_path19.join)(xdg.dataHome, "opencode", "auth.json"), "utf8")
8232
+ (0, import_node_fs18.readFileSync)((0, import_node_path20.join)(xdg.dataHome, "opencode", "auth.json"), "utf8")
8041
8233
  );
8042
8234
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return [];
8043
8235
  return Object.keys(parsed).filter((id) => id !== "agentful").sort();
@@ -8465,7 +8657,7 @@ async function tuiCommand(opts = {}) {
8465
8657
  ...process.env,
8466
8658
  // Slash commands run `agentful …` through the agent's bash tool, which
8467
8659
  // has no access to the user's shell aliases.
8468
- PATH: `${binDir}${import_node_path20.delimiter}${process.env.PATH || ""}`,
8660
+ PATH: `${binDir}${import_node_path21.delimiter}${process.env.PATH || ""}`,
8469
8661
  XDG_CONFIG_HOME: xdg.configHome,
8470
8662
  XDG_DATA_HOME: xdg.dataHome,
8471
8663
  XDG_STATE_HOME: xdg.stateHome,
@@ -8596,38 +8788,38 @@ async function shareCommand(opts) {
8596
8788
  }
8597
8789
 
8598
8790
  // src/commands/pull.ts
8599
- var import_node_fs19 = require("fs");
8600
- var import_node_path22 = require("path");
8791
+ var import_node_fs20 = require("fs");
8792
+ var import_node_path23 = require("path");
8601
8793
  init_branding();
8602
8794
  init_branding();
8603
8795
 
8604
8796
  // src/lib/pullFiles.ts
8605
- var import_node_fs18 = require("fs");
8606
- var import_node_path21 = require("path");
8797
+ var import_node_fs19 = require("fs");
8798
+ var import_node_path22 = require("path");
8607
8799
  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;
8608
8800
  function inlineContent(value) {
8609
8801
  return typeof value === "object" && value !== null && "content" in value ? String(value.content) : String(value ?? "");
8610
8802
  }
8611
8803
  function writePulledWorkspaceEntry(root, rel, value) {
8612
- const target = (0, import_node_path21.join)(root, rel);
8613
- (0, import_node_fs18.mkdirSync)((0, import_node_path21.dirname)(target), { recursive: true });
8804
+ const target = (0, import_node_path22.join)(root, rel);
8805
+ (0, import_node_fs19.mkdirSync)((0, import_node_path22.dirname)(target), { recursive: true });
8614
8806
  const content = inlineContent(value);
8615
8807
  if (INLINE_BASE64_EXTENSIONS.test(rel)) {
8616
- (0, import_node_fs18.writeFileSync)(target, Buffer.from(content, "base64"));
8808
+ (0, import_node_fs19.writeFileSync)(target, Buffer.from(content, "base64"));
8617
8809
  return;
8618
8810
  }
8619
- (0, import_node_fs18.writeFileSync)(target, content, "utf8");
8811
+ (0, import_node_fs19.writeFileSync)(target, content, "utf8");
8620
8812
  }
8621
8813
  function writePulledBackendDeclaration(root, declaration) {
8622
- const target = (0, import_node_path21.join)(root, ".agentful", "backend.json");
8623
- (0, import_node_fs18.mkdirSync)((0, import_node_path21.dirname)(target), { recursive: true });
8814
+ const target = (0, import_node_path22.join)(root, ".agentful", "backend.json");
8815
+ (0, import_node_fs19.mkdirSync)((0, import_node_path22.dirname)(target), { recursive: true });
8624
8816
  const {
8625
8817
  source: _source,
8626
8818
  received_at: _receivedAt,
8627
8819
  last_sync: _lastSync,
8628
8820
  ...portableDeclaration
8629
8821
  } = declaration;
8630
- (0, import_node_fs18.writeFileSync)(target, `${JSON.stringify(portableDeclaration, null, 2)}
8822
+ (0, import_node_fs19.writeFileSync)(target, `${JSON.stringify(portableDeclaration, null, 2)}
8631
8823
  `, "utf8");
8632
8824
  }
8633
8825
 
@@ -8636,7 +8828,7 @@ async function pullCommand(opts) {
8636
8828
  console.log(banner());
8637
8829
  const auth = await ensureAuth();
8638
8830
  const project = requireProject();
8639
- const nonHidden = (0, import_node_fs19.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
8831
+ const nonHidden = (0, import_node_fs20.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
8640
8832
  if (nonHidden.length > 0 && !opts.force) {
8641
8833
  throw new ApiError(
8642
8834
  0,
@@ -8663,9 +8855,9 @@ async function pullCommand(opts) {
8663
8855
  ui.warn(`Skipped ${rel} (HTTP ${resp.status})`);
8664
8856
  continue;
8665
8857
  }
8666
- const target = (0, import_node_path22.join)(process.cwd(), rel);
8667
- (0, import_node_fs19.mkdirSync)((0, import_node_path22.dirname)(target), { recursive: true });
8668
- (0, import_node_fs19.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
8858
+ const target = (0, import_node_path23.join)(process.cwd(), rel);
8859
+ (0, import_node_fs20.mkdirSync)((0, import_node_path23.dirname)(target), { recursive: true });
8860
+ (0, import_node_fs20.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
8669
8861
  written++;
8670
8862
  }
8671
8863
  } else if (data.files) {