agentful 0.2.1 → 0.2.3

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.
Files changed (2) hide show
  1. package/dist/index.cjs +178 -96
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3044,7 +3044,7 @@ var VERSION, brand, useColor, wrap, paint, sym, ui;
3044
3044
  var init_branding = __esm({
3045
3045
  "src/branding.ts"() {
3046
3046
  "use strict";
3047
- VERSION = "0.2.1" ? "0.2.1" : null.version;
3047
+ VERSION = "0.2.3" ? "0.2.3" : null.version;
3048
3048
  brand = {
3049
3049
  name: "agentful",
3050
3050
  // the command users type
@@ -5607,6 +5607,38 @@ function runPreflight(rootDir) {
5607
5607
  return { framework, issues, warnings };
5608
5608
  }
5609
5609
 
5610
+ // src/lib/backendDecl.ts
5611
+ var import_node_fs6 = require("fs");
5612
+ var import_node_path8 = require("path");
5613
+ function readBackendDeclaration(dir = process.cwd()) {
5614
+ const path = (0, import_node_path8.join)(dir, ".agentful", "backend.json");
5615
+ if (!(0, import_node_fs6.existsSync)(path)) return null;
5616
+ try {
5617
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
5618
+ if (!parsed || typeof parsed !== "object") return null;
5619
+ const decl = {
5620
+ schema_version: Number(parsed.schema_version) || 1,
5621
+ managed_db: Boolean(parsed.managed_db),
5622
+ actions: Array.isArray(parsed.actions) ? parsed.actions.filter((a) => typeof a === "string") : [],
5623
+ contract_doc: typeof parsed.contract_doc === "string" ? parsed.contract_doc : void 0
5624
+ };
5625
+ if (!decl.managed_db && decl.actions.length === 0) return null;
5626
+ return decl;
5627
+ } catch {
5628
+ return null;
5629
+ }
5630
+ }
5631
+ function describeBackendDeclaration(decl) {
5632
+ const parts = [];
5633
+ if (decl.managed_db) parts.push("managed DB");
5634
+ if (decl.actions && decl.actions.length > 0) parts.push(`${decl.actions.length} server action(s)`);
5635
+ const base = parts.join(" + ") || "managed backend";
5636
+ return decl.contract_doc ? `${base} (contract: ${decl.contract_doc})` : base;
5637
+ }
5638
+ function backendTabUrl(userId, projectId) {
5639
+ return `${APP_URL}/workspace/${userId}/${projectId}?view=backend`;
5640
+ }
5641
+
5610
5642
  // src/lib/buildStatus.ts
5611
5643
  init_branding();
5612
5644
  var DIAGNOSIS_MAX_AGE_MS = 15 * 60 * 1e3;
@@ -5732,6 +5764,10 @@ async function pushCommand(opts) {
5732
5764
  console.log("");
5733
5765
  ui.ok(`Live preview: ${ui.url(url)}`);
5734
5766
  ui.info(`Manage in the browser: ${ui.url(`https://app.agentful.dev/workspace/${userId}/${projectId}`)}`);
5767
+ const backend = readBackendDeclaration();
5768
+ if (backend) {
5769
+ ui.info(`Declared managed backend: ${describeBackendDeclaration(backend)} \u2014 enable/manage it in the Backend tab: \`agentful backend\` takes you there.`);
5770
+ }
5735
5771
  }
5736
5772
  async function pollBuild(token, userId, projectId, startedAtMs) {
5737
5773
  const deadline = Date.now() + 10 * 60 * 1e3;
@@ -5817,6 +5853,24 @@ async function buildStatusCommand() {
5817
5853
  }
5818
5854
  }
5819
5855
 
5856
+ // src/commands/backend.ts
5857
+ init_branding();
5858
+ async function backendCommand() {
5859
+ console.log(banner());
5860
+ const project = requireProject();
5861
+ const decl = readBackendDeclaration();
5862
+ if (decl) {
5863
+ ui.info(`This project declares: ${describeBackendDeclaration(decl)}.`);
5864
+ ui.info("Declared in code is not enabled on the platform \u2014 that happens in the Backend tab.");
5865
+ }
5866
+ const url = backendTabUrl(project.userId, project.projectId);
5867
+ ui.step(`Opening the Backend tab: ${ui.url(url)}`);
5868
+ if (!openInBrowser(url)) {
5869
+ ui.info("Could not launch a browser \u2014 open this URL manually:");
5870
+ ui.info(ui.url(url));
5871
+ }
5872
+ }
5873
+
5820
5874
  // src/commands/open.ts
5821
5875
  init_branding();
5822
5876
  async function openCommand() {
@@ -5833,15 +5887,15 @@ async function openCommand() {
5833
5887
 
5834
5888
  // src/commands/tui.ts
5835
5889
  var import_node_child_process5 = require("child_process");
5836
- var import_node_path16 = require("path");
5890
+ var import_node_path17 = require("path");
5837
5891
 
5838
5892
  // src/lib/binWrapper.ts
5839
- var import_node_fs6 = require("fs");
5840
- var import_node_path8 = require("path");
5893
+ var import_node_fs7 = require("fs");
5894
+ var import_node_path9 = require("path");
5841
5895
  var import_node_os3 = require("os");
5842
5896
  function ensureCliOnPath() {
5843
- const binDir = (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
5844
- (0, import_node_fs6.mkdirSync)(binDir, { recursive: true });
5897
+ const binDir = (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
5898
+ (0, import_node_fs7.mkdirSync)(binDir, { recursive: true });
5845
5899
  const entry = process.argv[1];
5846
5900
  if (!entry) return binDir;
5847
5901
  const restore = (name) => {
@@ -5850,8 +5904,8 @@ function ensureCliOnPath() {
5850
5904
  ` : `export ${name}=${JSON.stringify(original)}
5851
5905
  `;
5852
5906
  };
5853
- const wrapper = (0, import_node_path8.join)(binDir, "agentful");
5854
- (0, import_node_fs6.writeFileSync)(
5907
+ const wrapper = (0, import_node_path9.join)(binDir, "agentful");
5908
+ (0, import_node_fs7.writeFileSync)(
5855
5909
  wrapper,
5856
5910
  `#!/bin/sh
5857
5911
  # Generated by the Agentful CLI so the coding agent can run \`agentful \u2026\`.
@@ -5861,7 +5915,7 @@ function ensureCliOnPath() {
5861
5915
  "utf8"
5862
5916
  );
5863
5917
  try {
5864
- (0, import_node_fs6.chmodSync)(wrapper, 493);
5918
+ (0, import_node_fs7.chmodSync)(wrapper, 493);
5865
5919
  } catch {
5866
5920
  }
5867
5921
  return binDir;
@@ -5871,9 +5925,9 @@ function ensureCliOnPath() {
5871
5925
  init_branding();
5872
5926
 
5873
5927
  // src/lib/engine.ts
5874
- var import_node_fs7 = require("fs");
5928
+ var import_node_fs8 = require("fs");
5875
5929
  var import_node_crypto = require("crypto");
5876
- var import_node_path9 = require("path");
5930
+ var import_node_path10 = require("path");
5877
5931
  var import_node_os4 = require("os");
5878
5932
  var import_node_child_process3 = require("child_process");
5879
5933
  var import_node_stream = require("stream");
@@ -5914,8 +5968,8 @@ function platformKey() {
5914
5968
  );
5915
5969
  }
5916
5970
  function cacheDir(version) {
5917
- const base = process.env.XDG_CACHE_HOME || (0, import_node_path9.join)((0, import_node_os4.homedir)(), ".cache");
5918
- return (0, import_node_path9.join)(base, "agentful", "engine", version);
5971
+ const base = process.env.XDG_CACHE_HOME || (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".cache");
5972
+ return (0, import_node_path10.join)(base, "agentful", "engine", version);
5919
5973
  }
5920
5974
  async function fetchManifest() {
5921
5975
  try {
@@ -5926,7 +5980,7 @@ async function fetchManifest() {
5926
5980
  }
5927
5981
  async function sha256File(path) {
5928
5982
  const hash = (0, import_node_crypto.createHash)("sha256");
5929
- await (0, import_promises2.pipeline)((0, import_node_fs7.createReadStream)(path), hash);
5983
+ await (0, import_promises2.pipeline)((0, import_node_fs8.createReadStream)(path), hash);
5930
5984
  return hash.digest("hex");
5931
5985
  }
5932
5986
  async function ensureEngine() {
@@ -5935,12 +5989,12 @@ async function ensureEngine() {
5935
5989
  noticeIfOutdated(manifest);
5936
5990
  const version = manifest?.engine_version || FALLBACK_ENGINE_VERSION;
5937
5991
  const dir = cacheDir(version);
5938
- const binPath = (0, import_node_path9.join)(dir, ENGINE_BINARY);
5939
- if ((0, import_node_fs7.existsSync)(binPath)) return binPath;
5992
+ const binPath = (0, import_node_path10.join)(dir, ENGINE_BINARY);
5993
+ if ((0, import_node_fs8.existsSync)(binPath)) return binPath;
5940
5994
  const artifact = manifest?.artifacts?.[key];
5941
5995
  const url = artifact?.url || `${APP_URL}/cli/engine/${version}/${key}${key.startsWith("darwin") ? ".zip" : ".tar.gz"}`;
5942
- (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
5943
- const archivePath = (0, import_node_path9.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
5996
+ (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
5997
+ const archivePath = (0, import_node_path10.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
5944
5998
  ui.step(`Preparing the ${brand.displayName} engine (first run only)\u2026`);
5945
5999
  const resp = await fetch(url, {
5946
6000
  redirect: "follow",
@@ -5954,11 +6008,11 @@ async function ensureEngine() {
5954
6008
  `Could not download the engine (HTTP ${resp.status}). Check your connection and try again.`
5955
6009
  );
5956
6010
  }
5957
- await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs7.createWriteStream)(archivePath));
6011
+ await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs8.createWriteStream)(archivePath));
5958
6012
  if (artifact?.sha256) {
5959
6013
  const actual = await sha256File(archivePath);
5960
6014
  if (actual !== artifact.sha256) {
5961
- (0, import_node_fs7.rmSync)(archivePath, { force: true });
6015
+ (0, import_node_fs8.rmSync)(archivePath, { force: true });
5962
6016
  throw new ApiError(
5963
6017
  0,
5964
6018
  "engine_checksum_mismatch",
@@ -5967,7 +6021,7 @@ async function ensureEngine() {
5967
6021
  }
5968
6022
  }
5969
6023
  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" });
5970
- (0, import_node_fs7.rmSync)(archivePath, { force: true });
6024
+ (0, import_node_fs8.rmSync)(archivePath, { force: true });
5971
6025
  if (extract.status !== 0) {
5972
6026
  throw new ApiError(
5973
6027
  0,
@@ -5975,24 +6029,24 @@ async function ensureEngine() {
5975
6029
  `Could not unpack the engine (needs ${archivePath.endsWith(".zip") ? "unzip" : "tar"} on PATH).`
5976
6030
  );
5977
6031
  }
5978
- const extracted = (0, import_node_path9.join)(dir, "opencode");
5979
- if ((0, import_node_fs7.existsSync)(extracted) && !(0, import_node_fs7.existsSync)(binPath)) (0, import_node_fs7.renameSync)(extracted, binPath);
5980
- if (!(0, import_node_fs7.existsSync)(binPath)) {
6032
+ const extracted = (0, import_node_path10.join)(dir, "opencode");
6033
+ if ((0, import_node_fs8.existsSync)(extracted) && !(0, import_node_fs8.existsSync)(binPath)) (0, import_node_fs8.renameSync)(extracted, binPath);
6034
+ if (!(0, import_node_fs8.existsSync)(binPath)) {
5981
6035
  throw new ApiError(0, "engine_extract_failed", "Engine archive did not contain the expected binary.");
5982
6036
  }
5983
- (0, import_node_fs7.chmodSync)(binPath, 493);
6037
+ (0, import_node_fs8.chmodSync)(binPath, 493);
5984
6038
  ui.ok(`Engine ready.`);
5985
6039
  console.log(paint.dim(" powered by opencode (MIT) \xB7 run `agentful licenses` for details"));
5986
6040
  return binPath;
5987
6041
  }
5988
6042
 
5989
6043
  // src/lib/gatewayToken.ts
5990
- var import_node_fs8 = require("fs");
5991
- var import_node_path10 = require("path");
5992
- var cachePath = () => (0, import_node_path10.join)(configDir(), "gateway.json");
6044
+ var import_node_fs9 = require("fs");
6045
+ var import_node_path11 = require("path");
6046
+ var cachePath = () => (0, import_node_path11.join)(configDir(), "gateway.json");
5993
6047
  async function ensureGatewaySession(auth, projectId) {
5994
6048
  try {
5995
- const cached = JSON.parse((0, import_node_fs8.readFileSync)(cachePath(), "utf8"));
6049
+ const cached = JSON.parse((0, import_node_fs9.readFileSync)(cachePath(), "utf8"));
5996
6050
  if (cached?.token && cached.expires_at - Date.now() / 1e3 > 3600) {
5997
6051
  return cached;
5998
6052
  }
@@ -6003,18 +6057,18 @@ async function ensureGatewaySession(auth, projectId) {
6003
6057
  body: projectId ? { project_id: projectId } : {},
6004
6058
  timeoutMs: 3e4
6005
6059
  });
6006
- (0, import_node_fs8.mkdirSync)(configDir(), { recursive: true });
6007
- (0, import_node_fs8.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
6060
+ (0, import_node_fs9.mkdirSync)(configDir(), { recursive: true });
6061
+ (0, import_node_fs9.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
6008
6062
  try {
6009
- (0, import_node_fs8.chmodSync)(cachePath(), 384);
6063
+ (0, import_node_fs9.chmodSync)(cachePath(), 384);
6010
6064
  } catch {
6011
6065
  }
6012
6066
  return res;
6013
6067
  }
6014
6068
 
6015
6069
  // src/lib/engineConfig.ts
6016
- var import_node_fs10 = require("fs");
6017
- var import_node_path12 = require("path");
6070
+ var import_node_fs11 = require("fs");
6071
+ var import_node_path13 = require("path");
6018
6072
  var import_node_os5 = require("os");
6019
6073
 
6020
6074
  // src/branding/agentful-theme.json
@@ -6123,6 +6177,10 @@ var SLASH_COMMANDS = {
6123
6177
  description: `${brand.displayName}: show the last cloud build result`,
6124
6178
  template: "Run `agentful build-status` from the project root using the bash tool and report what it prints: status, phase, framework, when it was recorded, and the error/details verbatim if it failed. That record is the only source of truth about the cloud build \u2014 do not speculate beyond it. " + NEEDS_A_PUSH
6125
6179
  },
6180
+ backend: {
6181
+ description: `${brand.displayName}: open the Backend tab (managed DB + server actions)`,
6182
+ template: "Run `agentful backend` from the project root using the bash tool and report the URL it opened. If it prints a declared backend (managed DB / server actions), summarize that and remind me that declared-in-code serves nothing until enabled in that tab. " + NEEDS_A_PUSH
6183
+ },
6126
6184
  preview: {
6127
6185
  description: `${brand.displayName}: open the live preview in the browser`,
6128
6186
  template: "Run `agentful open` from the project root using the bash tool and report the URL it opened. Note that `open` neither builds nor uploads. " + NEEDS_A_PUSH
@@ -6153,22 +6211,22 @@ ${cmd.template}
6153
6211
  }
6154
6212
 
6155
6213
  // src/lib/tools.ts
6156
- var import_node_fs9 = require("fs");
6157
- var import_node_path11 = require("path");
6214
+ var import_node_fs10 = require("fs");
6215
+ var import_node_path12 = require("path");
6158
6216
  function assetsDir() {
6159
- const here = (0, import_node_path11.dirname)(process.argv[1] || "");
6160
- for (const candidate of [(0, import_node_path11.join)(here, "..", "assets"), (0, import_node_path11.join)(here, "assets")]) {
6161
- if ((0, import_node_fs9.existsSync)((0, import_node_path11.join)(candidate, "tools", "imagegen.ts"))) return candidate;
6217
+ const here = (0, import_node_path12.dirname)(process.argv[1] || "");
6218
+ for (const candidate of [(0, import_node_path12.join)(here, "..", "assets"), (0, import_node_path12.join)(here, "assets")]) {
6219
+ if ((0, import_node_fs10.existsSync)((0, import_node_path12.join)(candidate, "tools", "imagegen.ts"))) return candidate;
6162
6220
  }
6163
6221
  return null;
6164
6222
  }
6165
6223
  function installEngineTools(configDir2) {
6166
6224
  const assets = assetsDir();
6167
6225
  if (!assets) return false;
6168
- const target = (0, import_node_path11.join)(configDir2, "tools");
6169
- (0, import_node_fs9.mkdirSync)(target, { recursive: true });
6226
+ const target = (0, import_node_path12.join)(configDir2, "tools");
6227
+ (0, import_node_fs10.mkdirSync)(target, { recursive: true });
6170
6228
  try {
6171
- (0, import_node_fs9.cpSync)((0, import_node_path11.join)(assets, "tools"), target, { recursive: true });
6229
+ (0, import_node_fs10.cpSync)((0, import_node_path12.join)(assets, "tools"), target, { recursive: true });
6172
6230
  return true;
6173
6231
  } catch {
6174
6232
  return false;
@@ -6246,6 +6304,19 @@ statically evaluable; prefer client-side fetch or Managed Server Actions.
6246
6304
  (b) an external backend called from the client. Never a bundled server
6247
6305
  process, and never a script the user must run locally to produce the result.
6248
6306
 
6307
+ When you design a managed backend (DB schema, server actions), also write the
6308
+ machine-readable declaration \`.agentful/backend.json\`:
6309
+ \`{"schema_version": 1, "managed_db": true, "actions": ["<action-name>", \u2026], "contract_doc": "docs/backend-contract.md"}\`
6310
+ \u2014 \`agentful push\` and \`agentful backend\` use it to tell the user honestly
6311
+ what is declared in code but not yet enabled on the platform.
6312
+
6313
+ When you point the user at the Backend tab, NEVER say "the Backend tab"
6314
+ without an address. Read \`.agentful/project.json\` and give the full URL
6315
+ \`https://app.agentful.dev/workspace/<userId>/<projectId>?view=backend\` \u2014 or
6316
+ simply the command \`agentful backend\`, which opens exactly that page.
6317
+ Declaring a backend never enables it; enabling is the user's conscious step
6318
+ in that tab.
6319
+
6249
6320
  ## Diagnosing failed pushes and cloud builds (hard rule)
6250
6321
 
6251
6322
  Diagnose **only from evidence**: the preflight output of \`agentful push\`, and
@@ -6313,11 +6384,11 @@ function buildEngineConfig(opts) {
6313
6384
  };
6314
6385
  }
6315
6386
  function engineXdg() {
6316
- const root = (0, import_node_path12.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
6387
+ const root = (0, import_node_path13.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
6317
6388
  return {
6318
- configHome: (0, import_node_path12.join)(root, "engine-config"),
6319
- dataHome: (0, import_node_path12.join)(root, "engine-data"),
6320
- stateHome: (0, import_node_path12.join)(root, "engine-state")
6389
+ configHome: (0, import_node_path13.join)(root, "engine-config"),
6390
+ dataHome: (0, import_node_path13.join)(root, "engine-data"),
6391
+ stateHome: (0, import_node_path13.join)(root, "engine-state")
6321
6392
  };
6322
6393
  }
6323
6394
  async function resolveTheme() {
@@ -6336,24 +6407,24 @@ async function resolveTheme() {
6336
6407
  }
6337
6408
  async function writeEngineSession(session, catalog, localProviders = {}, framework = "unknown", skillsInstruction = null) {
6338
6409
  const xdg = engineXdg();
6339
- const configDir2 = (0, import_node_path12.join)(xdg.configHome, "opencode");
6340
- const dataDir = (0, import_node_path12.join)(xdg.dataHome, "opencode");
6341
- const themesDir = (0, import_node_path12.join)(configDir2, "themes");
6342
- const pluginsDir = (0, import_node_path12.join)(configDir2, "plugins");
6343
- const commandsDir = (0, import_node_path12.join)(configDir2, "commands");
6344
- for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path12.join)(xdg.stateHome, "opencode")]) {
6345
- (0, import_node_fs10.mkdirSync)(dir, { recursive: true });
6410
+ const configDir2 = (0, import_node_path13.join)(xdg.configHome, "opencode");
6411
+ const dataDir = (0, import_node_path13.join)(xdg.dataHome, "opencode");
6412
+ const themesDir = (0, import_node_path13.join)(configDir2, "themes");
6413
+ const pluginsDir = (0, import_node_path13.join)(configDir2, "plugins");
6414
+ const commandsDir = (0, import_node_path13.join)(configDir2, "commands");
6415
+ for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path13.join)(xdg.stateHome, "opencode")]) {
6416
+ (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
6346
6417
  }
6347
6418
  const imagegenAvailable = installEngineTools(configDir2) && session.img_on !== false;
6348
- const cloudInstructionsPath = (0, import_node_path12.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
6349
- (0, import_node_fs10.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework));
6350
- const skillsPath = (0, import_node_path12.join)(configDir2, "AGENTFUL_SKILLS.md");
6419
+ const cloudInstructionsPath = (0, import_node_path13.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
6420
+ (0, import_node_fs11.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework));
6421
+ const skillsPath = (0, import_node_path13.join)(configDir2, "AGENTFUL_SKILLS.md");
6351
6422
  const instructionPaths = [cloudInstructionsPath];
6352
6423
  if (skillsInstruction) {
6353
- (0, import_node_fs10.writeFileSync)(skillsPath, skillsInstruction);
6424
+ (0, import_node_fs11.writeFileSync)(skillsPath, skillsInstruction);
6354
6425
  instructionPaths.push(skillsPath);
6355
6426
  } else {
6356
- (0, import_node_fs10.rmSync)(skillsPath, { force: true });
6427
+ (0, import_node_fs11.rmSync)(skillsPath, { force: true });
6357
6428
  }
6358
6429
  const config = buildEngineConfig({
6359
6430
  session,
@@ -6362,24 +6433,24 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
6362
6433
  imagegenAvailable,
6363
6434
  instructionPaths
6364
6435
  });
6365
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
6366
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(dataDir, "auth.json"), JSON.stringify({
6436
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
6437
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(dataDir, "auth.json"), JSON.stringify({
6367
6438
  [PROVIDER_ID]: { apiKey: session.token }
6368
6439
  }, null, 2));
6369
6440
  const { brandingPluginSource: brandingPluginSource2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
6370
- const pluginPath = (0, import_node_path12.join)(pluginsDir, "agentful-branding.tsx");
6371
- (0, import_node_fs10.writeFileSync)(pluginPath, brandingPluginSource2());
6372
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(configDir2, "tui.json"), JSON.stringify({
6441
+ const pluginPath = (0, import_node_path13.join)(pluginsDir, "agentful-branding.tsx");
6442
+ (0, import_node_fs11.writeFileSync)(pluginPath, brandingPluginSource2());
6443
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(configDir2, "tui.json"), JSON.stringify({
6373
6444
  theme: "agentful",
6374
6445
  plugin: [`file://${pluginPath}`]
6375
6446
  }, null, 2));
6376
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
6447
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
6377
6448
  for (const name of Object.keys(SLASH_COMMANDS)) {
6378
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(commandsDir, `${name}.md`), commandMarkdown(name));
6449
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(commandsDir, `${name}.md`), commandMarkdown(name));
6379
6450
  }
6380
6451
  for (const legacy of ["xdg-config", "xdg-data", "xdg-state"]) {
6381
6452
  try {
6382
- (0, import_node_fs10.rmSync)((0, import_node_path12.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
6453
+ (0, import_node_fs11.rmSync)((0, import_node_path13.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
6383
6454
  } catch {
6384
6455
  }
6385
6456
  }
@@ -6387,8 +6458,8 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
6387
6458
  }
6388
6459
 
6389
6460
  // src/lib/skills.ts
6390
- var import_node_fs11 = require("fs");
6391
- var import_node_path13 = require("path");
6461
+ var import_node_fs12 = require("fs");
6462
+ var import_node_path14 = require("path");
6392
6463
 
6393
6464
  // src/generated/skills.json
6394
6465
  var skills_default = {
@@ -6415,7 +6486,7 @@ var SCAFFOLD_BY_FRAMEWORK = {
6415
6486
  };
6416
6487
  function hasVueDependency(rootDir) {
6417
6488
  try {
6418
- const pkg = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path13.join)(rootDir, "package.json"), "utf8"));
6489
+ const pkg = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path14.join)(rootDir, "package.json"), "utf8"));
6419
6490
  return Boolean({ ...pkg.dependencies, ...pkg.devDependencies }["vue"]);
6420
6491
  } catch {
6421
6492
  return false;
@@ -6443,7 +6514,7 @@ function renderSkillsInstruction(names) {
6443
6514
  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";
6444
6515
  }
6445
6516
  function skillsInstructionFor(framework, rootDir) {
6446
- if (!(0, import_node_fs11.existsSync)(rootDir)) return null;
6517
+ if (!(0, import_node_fs12.existsSync)(rootDir)) return null;
6447
6518
  return renderSkillsInstruction(selectSkills(framework, rootDir));
6448
6519
  }
6449
6520
 
@@ -6558,15 +6629,15 @@ function decideLocalProviders(policy) {
6558
6629
  }
6559
6630
 
6560
6631
  // src/lib/localProviders.ts
6561
- var import_node_fs12 = require("fs");
6562
- var import_node_path14 = require("path");
6632
+ var import_node_fs13 = require("fs");
6633
+ var import_node_path15 = require("path");
6563
6634
  var LOCAL_PROVIDERS_FILE = "local-providers.json";
6564
6635
  function localProvidersPath() {
6565
- return (0, import_node_path14.join)(configDir(), LOCAL_PROVIDERS_FILE);
6636
+ return (0, import_node_path15.join)(configDir(), LOCAL_PROVIDERS_FILE);
6566
6637
  }
6567
6638
  function readLocalProviders() {
6568
6639
  try {
6569
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(localProvidersPath(), "utf8"));
6640
+ const raw = JSON.parse((0, import_node_fs13.readFileSync)(localProvidersPath(), "utf8"));
6570
6641
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
6571
6642
  const map = raw.providers && typeof raw.providers === "object" ? raw.providers : raw;
6572
6643
  const usable = {};
@@ -6601,8 +6672,8 @@ function resolveLocalProviders(policy) {
6601
6672
  }
6602
6673
  function writeLocalProvidersExample() {
6603
6674
  const path = localProvidersPath();
6604
- if ((0, import_node_fs12.existsSync)(path)) return path;
6605
- (0, import_node_fs12.mkdirSync)(configDir(), { recursive: true });
6675
+ if ((0, import_node_fs13.existsSync)(path)) return path;
6676
+ (0, import_node_fs13.mkdirSync)(configDir(), { recursive: true });
6606
6677
  const example = {
6607
6678
  _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.',
6608
6679
  _anthropic: {
@@ -6612,21 +6683,21 @@ function writeLocalProvidersExample() {
6612
6683
  models: { "claude-sonnet-4-5": { name: "Claude Sonnet 4.5 (my key)" } }
6613
6684
  }
6614
6685
  };
6615
- (0, import_node_fs12.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
6686
+ (0, import_node_fs13.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
6616
6687
  try {
6617
- (0, import_node_fs12.chmodSync)(path, 384);
6688
+ (0, import_node_fs13.chmodSync)(path, 384);
6618
6689
  } catch {
6619
6690
  }
6620
6691
  return path;
6621
6692
  }
6622
6693
 
6623
6694
  // src/lib/sessions.ts
6624
- var import_node_fs13 = require("fs");
6625
- var import_node_path15 = require("path");
6695
+ var import_node_fs14 = require("fs");
6696
+ var import_node_path16 = require("path");
6626
6697
  var import_node_child_process4 = require("child_process");
6627
6698
  function lastSessionForDirectory(cwd = process.cwd()) {
6628
- const dbPath = (0, import_node_path15.join)(engineXdg().dataHome, "opencode", "opencode.db");
6629
- if (!(0, import_node_fs13.existsSync)(dbPath)) return null;
6699
+ const dbPath = (0, import_node_path16.join)(engineXdg().dataHome, "opencode", "opencode.db");
6700
+ if (!(0, import_node_fs14.existsSync)(dbPath)) return null;
6630
6701
  const escaped = cwd.replace(/'/g, "''");
6631
6702
  const res = (0, import_node_child_process4.spawnSync)(
6632
6703
  "sqlite3",
@@ -6714,7 +6785,7 @@ async function tuiCommand(opts = {}) {
6714
6785
  ...process.env,
6715
6786
  // Slash commands run `agentful …` through the agent's bash tool, which
6716
6787
  // has no access to the user's shell aliases.
6717
- PATH: `${binDir}${import_node_path16.delimiter}${process.env.PATH || ""}`,
6788
+ PATH: `${binDir}${import_node_path17.delimiter}${process.env.PATH || ""}`,
6718
6789
  XDG_CONFIG_HOME: xdg.configHome,
6719
6790
  XDG_DATA_HOME: xdg.dataHome,
6720
6791
  XDG_STATE_HOME: xdg.stateHome,
@@ -6819,28 +6890,28 @@ async function shareCommand(opts) {
6819
6890
  }
6820
6891
 
6821
6892
  // src/commands/pull.ts
6822
- var import_node_fs14 = require("fs");
6823
- var import_node_path17 = require("path");
6893
+ var import_node_fs15 = require("fs");
6894
+ var import_node_path18 = require("path");
6824
6895
  init_branding();
6825
6896
  init_branding();
6826
6897
  function isProbablyBase64Binary(path) {
6827
6898
  return /\.(png|jpe?g|gif|webp|ico|woff2?|ttf|otf|eot|pdf|zip|mp[34]|webm|avif)$/i.test(path);
6828
6899
  }
6829
6900
  function writeEntry(root, rel, value) {
6830
- const target = (0, import_node_path17.join)(root, rel);
6831
- (0, import_node_fs14.mkdirSync)((0, import_node_path17.dirname)(target), { recursive: true });
6901
+ const target = (0, import_node_path18.join)(root, rel);
6902
+ (0, import_node_fs15.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
6832
6903
  const content = typeof value === "object" && value !== null && "content" in value ? String(value.content) : String(value ?? "");
6833
6904
  if (isProbablyBase64Binary(rel)) {
6834
- (0, import_node_fs14.writeFileSync)(target, Buffer.from(content, "base64"));
6905
+ (0, import_node_fs15.writeFileSync)(target, Buffer.from(content, "base64"));
6835
6906
  } else {
6836
- (0, import_node_fs14.writeFileSync)(target, content, "utf8");
6907
+ (0, import_node_fs15.writeFileSync)(target, content, "utf8");
6837
6908
  }
6838
6909
  }
6839
6910
  async function pullCommand(opts) {
6840
6911
  console.log(banner());
6841
6912
  const auth = await ensureAuth();
6842
6913
  const project = requireProject();
6843
- const nonHidden = (0, import_node_fs14.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
6914
+ const nonHidden = (0, import_node_fs15.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
6844
6915
  if (nonHidden.length > 0 && !opts.force) {
6845
6916
  throw new ApiError(
6846
6917
  0,
@@ -6864,9 +6935,9 @@ async function pullCommand(opts) {
6864
6935
  ui.warn(`Skipped ${rel} (HTTP ${resp.status})`);
6865
6936
  continue;
6866
6937
  }
6867
- const target = (0, import_node_path17.join)(process.cwd(), rel);
6868
- (0, import_node_fs14.mkdirSync)((0, import_node_path17.dirname)(target), { recursive: true });
6869
- (0, import_node_fs14.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
6938
+ const target = (0, import_node_path18.join)(process.cwd(), rel);
6939
+ (0, import_node_fs15.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
6940
+ (0, import_node_fs15.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
6870
6941
  written++;
6871
6942
  }
6872
6943
  } else if (data.files) {
@@ -6957,13 +7028,23 @@ async function upgradeCommand() {
6957
7028
  console.log(banner());
6958
7029
  let latest = "";
6959
7030
  try {
6960
- const manifest = await request(
6961
- `${APP_URL}/cli/engine-manifest.json`,
7031
+ const registry = await request(
7032
+ `https://registry.npmjs.org/${brand.name}/latest`,
6962
7033
  { timeoutMs: 8e3 }
6963
7034
  );
6964
- latest = manifest.latest_cli_version || "";
7035
+ latest = registry.version || "";
6965
7036
  } catch {
6966
7037
  }
7038
+ if (!latest) {
7039
+ try {
7040
+ const manifest = await request(
7041
+ `${APP_URL}/cli/engine-manifest.json`,
7042
+ { timeoutMs: 8e3 }
7043
+ );
7044
+ latest = manifest.latest_cli_version || "";
7045
+ } catch {
7046
+ }
7047
+ }
6967
7048
  if (latest && compareVersions(VERSION, latest) >= 0) {
6968
7049
  ui.ok(`Already on the latest version (v${VERSION}).`);
6969
7050
  return;
@@ -7012,6 +7093,7 @@ program2.command("whoami").description("Show the signed-in account").action(run(
7012
7093
  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)));
7013
7094
  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").action(run((opts) => pushCommand(opts)));
7014
7095
  program2.command("build-status").description("Show the cloud's record of the last build (the real error on failures)").action(run(buildStatusCommand));
7096
+ program2.command("backend").description("Open this project's Backend tab (managed DB + server actions) in the browser").action(run(backendCommand));
7015
7097
  program2.command("open").description("Open the live preview in the browser").action(run(openCommand));
7016
7098
  program2.command("tui").description("Start the Agentful coding TUI with your Agentful account").option("-s, --session <id>", "resume a previous session").action(run((opts) => tuiCommand(opts)));
7017
7099
  program2.command("publish <subdomain>").description("Publish the built project at https://<subdomain>.agentful.dev").action(run(publishCommand));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentful",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Agentful in your terminal — local development with push-to-cloud previews",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://agentful.dev",