agentful 0.3.0 → 0.3.1

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 +244 -99
  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.3.0" ? "0.3.0" : null.version;
3047
+ VERSION = "0.3.1" ? "0.3.1" : null.version;
3048
3048
  brand = {
3049
3049
  name: "agentful",
3050
3050
  // the command users type
@@ -5873,6 +5873,135 @@ function describeBackendState(state) {
5873
5873
  return out;
5874
5874
  }
5875
5875
 
5876
+ // src/lib/backendConsent.ts
5877
+ var import_node_fs7 = require("fs");
5878
+ var import_node_path9 = require("path");
5879
+ var import_promises2 = require("readline/promises");
5880
+ function declaresManagedDatabase(decl) {
5881
+ if (!decl) return false;
5882
+ return Boolean(decl.managed_db) || (decl.collections?.length ?? 0) > 0 || authMethodsOf(decl).length > 0;
5883
+ }
5884
+ function decideConsent(input) {
5885
+ if (!declaresManagedDatabase(input.declaration)) return { kind: "not_applicable" };
5886
+ if (input.state === null) return { kind: "state_unknown" };
5887
+ if (input.state.databaseEnabled) return { kind: "already_enabled" };
5888
+ if (input.yes) return { kind: "enable", via: "flag" };
5889
+ if (input.rememberedDecline) return { kind: "skip", reason: "remembered_decline" };
5890
+ if (!input.interactive) return { kind: "skip", reason: "non_interactive" };
5891
+ return { kind: "ask" };
5892
+ }
5893
+ function consentQuestion(decl) {
5894
+ const names = (decl.collections ?? []).map((c) => c.name).filter(Boolean);
5895
+ const what = names.length ? `a managed database with ${names.length} collection(s): ${names.slice(0, 6).join(", ")}${names.length > 6 ? ", \u2026" : ""}` : "a managed database";
5896
+ return [
5897
+ `This project declares ${what}.`,
5898
+ "Enable it on the platform now? Two things change:",
5899
+ " \u2022 /api/* on your preview domain is routed to the backend \u2014 a static file under /api/ would no longer be served",
5900
+ " \u2022 the data API goes live under the declared access rules (a `public` collection is readable AND writable without login)",
5901
+ '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).'
5902
+ ].join("\n");
5903
+ }
5904
+ var consentFile = () => (0, import_node_path9.join)(configDir(), "backend-consent.json");
5905
+ function readConsentFile() {
5906
+ try {
5907
+ const raw = JSON.parse((0, import_node_fs7.readFileSync)(consentFile(), "utf8"));
5908
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
5909
+ } catch {
5910
+ return {};
5911
+ }
5912
+ }
5913
+ function hasRememberedDecline(projectId) {
5914
+ return Boolean(readConsentFile()[projectId]);
5915
+ }
5916
+ function rememberDecline(projectId) {
5917
+ const all = readConsentFile();
5918
+ all[projectId] = { declined_at: (/* @__PURE__ */ new Date()).toISOString() };
5919
+ (0, import_node_fs7.mkdirSync)(configDir(), { recursive: true });
5920
+ (0, import_node_fs7.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
5921
+ }
5922
+ function forgetDecline(projectId) {
5923
+ if (!(0, import_node_fs7.existsSync)(consentFile())) return;
5924
+ const all = readConsentFile();
5925
+ if (!(projectId in all)) return;
5926
+ delete all[projectId];
5927
+ (0, import_node_fs7.writeFileSync)(consentFile(), JSON.stringify(all, null, 2) + "\n", "utf8");
5928
+ }
5929
+ async function enableManagedDatabase(auth, userId, projectId) {
5930
+ try {
5931
+ const body = await request(`${API_URL}/api/projects/${userId}/${projectId}/backend/database`, {
5932
+ method: "PUT",
5933
+ token: auth.pb_token,
5934
+ timeoutMs: 6e4,
5935
+ body: { mode: "managed", provider: "managed_document" }
5936
+ });
5937
+ const status = String(body?.data?.status ?? body?.status ?? "");
5938
+ return status === "active" ? { ok: true, status } : { ok: false, status, message: body?.error?.message || body?.message };
5939
+ } catch (err2) {
5940
+ return { ok: false, message: err2?.message || String(err2) };
5941
+ }
5942
+ }
5943
+ async function ensureDatabaseConsent(opts) {
5944
+ const lines = [];
5945
+ if (!declaresManagedDatabase(opts.declaration)) {
5946
+ return { decision: { kind: "not_applicable" }, enabled: false, lines };
5947
+ }
5948
+ const state = await fetchBackendSessionState(opts.auth, opts.userId, opts.projectId);
5949
+ const interactive = opts.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
5950
+ let decision = decideConsent({
5951
+ declaration: opts.declaration,
5952
+ state,
5953
+ yes: opts.yes,
5954
+ interactive,
5955
+ rememberedDecline: hasRememberedDecline(opts.projectId)
5956
+ });
5957
+ if (decision.kind === "ask") {
5958
+ const ask = opts.ask ?? (async (q) => {
5959
+ const rl = (0, import_promises2.createInterface)({ input: process.stdin, output: process.stdout });
5960
+ try {
5961
+ return await rl.question(`${q}
5962
+ Enable the managed database? [y/N] `);
5963
+ } finally {
5964
+ rl.close();
5965
+ }
5966
+ });
5967
+ const answer = (await ask(consentQuestion(opts.declaration))).trim().toLowerCase();
5968
+ if (answer === "y" || answer === "yes") {
5969
+ decision = { kind: "enable", via: "answer" };
5970
+ } else {
5971
+ rememberDecline(opts.projectId);
5972
+ decision = { kind: "skip", reason: "declined" };
5973
+ }
5974
+ }
5975
+ switch (decision.kind) {
5976
+ case "state_unknown":
5977
+ lines.push({ kind: "warn", text: "Could not read the backend state \u2014 the database is NOT enabled by this push; the declaration is stored and reported." });
5978
+ return { decision, enabled: false, lines };
5979
+ case "already_enabled":
5980
+ return { decision, enabled: true, lines };
5981
+ case "skip":
5982
+ if (decision.reason === "non_interactive") {
5983
+ lines.push({ kind: "info", text: "Managed database declared but not enabled \u2014 non-interactive push never enables it. Use `agentful push --yes` (CI) or enable it in the Backend tab (`agentful backend`)." });
5984
+ } else if (decision.reason === "remembered_decline") {
5985
+ lines.push({ kind: "info", text: "Managed database declared but not enabled \u2014 you declined earlier for this project. `agentful push --yes` or `agentful backend` enables it." });
5986
+ } else {
5987
+ lines.push({ kind: "info", text: "Not enabled. The declaration is stored; `agentful push --yes` or the Backend tab enables the database later." });
5988
+ }
5989
+ return { decision, enabled: false, lines };
5990
+ case "enable": {
5991
+ const result = await enableManagedDatabase(opts.auth, opts.userId, opts.projectId);
5992
+ if (result.ok) {
5993
+ forgetDecline(opts.projectId);
5994
+ lines.push({ kind: "ok", text: `Managed database enabled${decision.via === "flag" ? " (--yes)" : ""} \u2014 this push applies the declaration.` });
5995
+ return { decision, enabled: true, lines };
5996
+ }
5997
+ lines.push({ kind: "warn", text: `Could not enable the managed database${result.message ? `: ${result.message}` : ""}. The push continues; enable it in the Backend tab (\`agentful backend\`).` });
5998
+ return { decision, enabled: false, lines };
5999
+ }
6000
+ default:
6001
+ return { decision, enabled: false, lines };
6002
+ }
6003
+ }
6004
+
5876
6005
  // src/lib/buildStatus.ts
5877
6006
  init_branding();
5878
6007
  var DIAGNOSIS_MAX_AGE_MS = 15 * 60 * 1e3;
@@ -5962,8 +6091,22 @@ async function pushCommand(opts) {
5962
6091
  `Packed ${(zip.bytes / 1024 / 1024).toFixed(1)} MB compressed \u2014 the platform caps uploads at ${Math.floor(MAX_ZIP_UPLOAD_BYTES / (1024 * 1024))} MB. Remove large media or generated files from the source tree (node_modules, dist and .git are excluded automatically); large assets belong in the platform asset flow.`
5963
6092
  );
5964
6093
  }
5965
- ui.step("Uploading to your workspace\u2026");
5966
6094
  const declaration = mode === "source" ? readBackendDeclaration() : null;
6095
+ if (declaration) {
6096
+ const consent = await ensureDatabaseConsent({
6097
+ auth,
6098
+ userId,
6099
+ projectId,
6100
+ declaration,
6101
+ yes: opts.yes
6102
+ });
6103
+ for (const line of consent.lines) {
6104
+ if (line.kind === "ok") ui.ok(line.text);
6105
+ else if (line.kind === "warn") ui.warn(line.text);
6106
+ else ui.info(line.text);
6107
+ }
6108
+ }
6109
+ ui.step("Uploading to your workspace\u2026");
5967
6110
  const upload = await request(
5968
6111
  `${API_URL}/api/workspaces/${userId}/${projectId}/source-zip`,
5969
6112
  {
@@ -6150,15 +6293,15 @@ async function openCommand() {
6150
6293
 
6151
6294
  // src/commands/tui.ts
6152
6295
  var import_node_child_process5 = require("child_process");
6153
- var import_node_path17 = require("path");
6296
+ var import_node_path18 = require("path");
6154
6297
 
6155
6298
  // src/lib/binWrapper.ts
6156
- var import_node_fs7 = require("fs");
6157
- var import_node_path9 = require("path");
6299
+ var import_node_fs8 = require("fs");
6300
+ var import_node_path10 = require("path");
6158
6301
  var import_node_os3 = require("os");
6159
6302
  function ensureCliOnPath() {
6160
- const binDir = (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
6161
- (0, import_node_fs7.mkdirSync)(binDir, { recursive: true });
6303
+ const binDir = (0, import_node_path10.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
6304
+ (0, import_node_fs8.mkdirSync)(binDir, { recursive: true });
6162
6305
  const entry = process.argv[1];
6163
6306
  if (!entry) return binDir;
6164
6307
  const restore = (name) => {
@@ -6167,8 +6310,8 @@ function ensureCliOnPath() {
6167
6310
  ` : `export ${name}=${JSON.stringify(original)}
6168
6311
  `;
6169
6312
  };
6170
- const wrapper = (0, import_node_path9.join)(binDir, "agentful");
6171
- (0, import_node_fs7.writeFileSync)(
6313
+ const wrapper = (0, import_node_path10.join)(binDir, "agentful");
6314
+ (0, import_node_fs8.writeFileSync)(
6172
6315
  wrapper,
6173
6316
  `#!/bin/sh
6174
6317
  # Generated by the Agentful CLI so the coding agent can run \`agentful \u2026\`.
@@ -6178,7 +6321,7 @@ function ensureCliOnPath() {
6178
6321
  "utf8"
6179
6322
  );
6180
6323
  try {
6181
- (0, import_node_fs7.chmodSync)(wrapper, 493);
6324
+ (0, import_node_fs8.chmodSync)(wrapper, 493);
6182
6325
  } catch {
6183
6326
  }
6184
6327
  return binDir;
@@ -6188,13 +6331,13 @@ function ensureCliOnPath() {
6188
6331
  init_branding();
6189
6332
 
6190
6333
  // src/lib/engine.ts
6191
- var import_node_fs8 = require("fs");
6334
+ var import_node_fs9 = require("fs");
6192
6335
  var import_node_crypto = require("crypto");
6193
- var import_node_path10 = require("path");
6336
+ var import_node_path11 = require("path");
6194
6337
  var import_node_os4 = require("os");
6195
6338
  var import_node_child_process3 = require("child_process");
6196
6339
  var import_node_stream = require("stream");
6197
- var import_promises2 = require("stream/promises");
6340
+ var import_promises3 = require("stream/promises");
6198
6341
  init_branding();
6199
6342
  var ENGINE_BINARY = "agentful-engine";
6200
6343
  var FALLBACK_ENGINE_VERSION = "v1.18.18";
@@ -6235,8 +6378,8 @@ function platformKey() {
6235
6378
  );
6236
6379
  }
6237
6380
  function cacheDir(version) {
6238
- const base = process.env.XDG_CACHE_HOME || (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".cache");
6239
- return (0, import_node_path10.join)(base, "agentful", "engine", version);
6381
+ const base = process.env.XDG_CACHE_HOME || (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".cache");
6382
+ return (0, import_node_path11.join)(base, "agentful", "engine", version);
6240
6383
  }
6241
6384
  async function fetchManifest() {
6242
6385
  try {
@@ -6247,7 +6390,7 @@ async function fetchManifest() {
6247
6390
  }
6248
6391
  async function sha256File(path) {
6249
6392
  const hash = (0, import_node_crypto.createHash)("sha256");
6250
- await (0, import_promises2.pipeline)((0, import_node_fs8.createReadStream)(path), hash);
6393
+ await (0, import_promises3.pipeline)((0, import_node_fs9.createReadStream)(path), hash);
6251
6394
  return hash.digest("hex");
6252
6395
  }
6253
6396
  async function ensureEngine() {
@@ -6256,12 +6399,12 @@ async function ensureEngine() {
6256
6399
  noticeIfOutdated(manifest);
6257
6400
  const version = manifest?.engine_version || FALLBACK_ENGINE_VERSION;
6258
6401
  const dir = cacheDir(version);
6259
- const binPath = (0, import_node_path10.join)(dir, ENGINE_BINARY);
6260
- if ((0, import_node_fs8.existsSync)(binPath)) return binPath;
6402
+ const binPath = (0, import_node_path11.join)(dir, ENGINE_BINARY);
6403
+ if ((0, import_node_fs9.existsSync)(binPath)) return binPath;
6261
6404
  const artifact = manifest?.artifacts?.[key];
6262
6405
  const url = artifact?.url || `${APP_URL}/cli/engine/${version}/${key}${key.startsWith("darwin") ? ".zip" : ".tar.gz"}`;
6263
- (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
6264
- const archivePath = (0, import_node_path10.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
6406
+ (0, import_node_fs9.mkdirSync)(dir, { recursive: true });
6407
+ const archivePath = (0, import_node_path11.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
6265
6408
  ui.step(`Preparing the ${brand.displayName} engine (first run only)\u2026`);
6266
6409
  const resp = await fetch(url, {
6267
6410
  redirect: "follow",
@@ -6275,11 +6418,11 @@ async function ensureEngine() {
6275
6418
  `Could not download the engine (HTTP ${resp.status}). Check your connection and try again.`
6276
6419
  );
6277
6420
  }
6278
- await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs8.createWriteStream)(archivePath));
6421
+ await (0, import_promises3.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs9.createWriteStream)(archivePath));
6279
6422
  if (artifact?.sha256) {
6280
6423
  const actual = await sha256File(archivePath);
6281
6424
  if (actual !== artifact.sha256) {
6282
- (0, import_node_fs8.rmSync)(archivePath, { force: true });
6425
+ (0, import_node_fs9.rmSync)(archivePath, { force: true });
6283
6426
  throw new ApiError(
6284
6427
  0,
6285
6428
  "engine_checksum_mismatch",
@@ -6288,7 +6431,7 @@ async function ensureEngine() {
6288
6431
  }
6289
6432
  }
6290
6433
  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" });
6291
- (0, import_node_fs8.rmSync)(archivePath, { force: true });
6434
+ (0, import_node_fs9.rmSync)(archivePath, { force: true });
6292
6435
  if (extract.status !== 0) {
6293
6436
  throw new ApiError(
6294
6437
  0,
@@ -6296,24 +6439,24 @@ async function ensureEngine() {
6296
6439
  `Could not unpack the engine (needs ${archivePath.endsWith(".zip") ? "unzip" : "tar"} on PATH).`
6297
6440
  );
6298
6441
  }
6299
- const extracted = (0, import_node_path10.join)(dir, "opencode");
6300
- if ((0, import_node_fs8.existsSync)(extracted) && !(0, import_node_fs8.existsSync)(binPath)) (0, import_node_fs8.renameSync)(extracted, binPath);
6301
- if (!(0, import_node_fs8.existsSync)(binPath)) {
6442
+ const extracted = (0, import_node_path11.join)(dir, "opencode");
6443
+ if ((0, import_node_fs9.existsSync)(extracted) && !(0, import_node_fs9.existsSync)(binPath)) (0, import_node_fs9.renameSync)(extracted, binPath);
6444
+ if (!(0, import_node_fs9.existsSync)(binPath)) {
6302
6445
  throw new ApiError(0, "engine_extract_failed", "Engine archive did not contain the expected binary.");
6303
6446
  }
6304
- (0, import_node_fs8.chmodSync)(binPath, 493);
6447
+ (0, import_node_fs9.chmodSync)(binPath, 493);
6305
6448
  ui.ok(`Engine ready.`);
6306
6449
  console.log(paint.dim(" powered by opencode (MIT) \xB7 run `agentful licenses` for details"));
6307
6450
  return binPath;
6308
6451
  }
6309
6452
 
6310
6453
  // src/lib/gatewayToken.ts
6311
- var import_node_fs9 = require("fs");
6312
- var import_node_path11 = require("path");
6313
- var cachePath = () => (0, import_node_path11.join)(configDir(), "gateway.json");
6454
+ var import_node_fs10 = require("fs");
6455
+ var import_node_path12 = require("path");
6456
+ var cachePath = () => (0, import_node_path12.join)(configDir(), "gateway.json");
6314
6457
  async function ensureGatewaySession(auth, projectId) {
6315
6458
  try {
6316
- const cached = JSON.parse((0, import_node_fs9.readFileSync)(cachePath(), "utf8"));
6459
+ const cached = JSON.parse((0, import_node_fs10.readFileSync)(cachePath(), "utf8"));
6317
6460
  if (cached?.token && cached.expires_at - Date.now() / 1e3 > 3600) {
6318
6461
  return cached;
6319
6462
  }
@@ -6324,18 +6467,18 @@ async function ensureGatewaySession(auth, projectId) {
6324
6467
  body: projectId ? { project_id: projectId } : {},
6325
6468
  timeoutMs: 3e4
6326
6469
  });
6327
- (0, import_node_fs9.mkdirSync)(configDir(), { recursive: true });
6328
- (0, import_node_fs9.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
6470
+ (0, import_node_fs10.mkdirSync)(configDir(), { recursive: true });
6471
+ (0, import_node_fs10.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
6329
6472
  try {
6330
- (0, import_node_fs9.chmodSync)(cachePath(), 384);
6473
+ (0, import_node_fs10.chmodSync)(cachePath(), 384);
6331
6474
  } catch {
6332
6475
  }
6333
6476
  return res;
6334
6477
  }
6335
6478
 
6336
6479
  // src/lib/engineConfig.ts
6337
- var import_node_fs11 = require("fs");
6338
- var import_node_path13 = require("path");
6480
+ var import_node_fs12 = require("fs");
6481
+ var import_node_path14 = require("path");
6339
6482
  var import_node_os5 = require("os");
6340
6483
 
6341
6484
  // src/branding/agentful-theme.json
@@ -6478,22 +6621,22 @@ ${cmd.template}
6478
6621
  }
6479
6622
 
6480
6623
  // src/lib/tools.ts
6481
- var import_node_fs10 = require("fs");
6482
- var import_node_path12 = require("path");
6624
+ var import_node_fs11 = require("fs");
6625
+ var import_node_path13 = require("path");
6483
6626
  function assetsDir() {
6484
- const here = (0, import_node_path12.dirname)(process.argv[1] || "");
6485
- for (const candidate of [(0, import_node_path12.join)(here, "..", "assets"), (0, import_node_path12.join)(here, "assets")]) {
6486
- if ((0, import_node_fs10.existsSync)((0, import_node_path12.join)(candidate, "tools", "imagegen.ts"))) return candidate;
6627
+ const here = (0, import_node_path13.dirname)(process.argv[1] || "");
6628
+ for (const candidate of [(0, import_node_path13.join)(here, "..", "assets"), (0, import_node_path13.join)(here, "assets")]) {
6629
+ if ((0, import_node_fs11.existsSync)((0, import_node_path13.join)(candidate, "tools", "imagegen.ts"))) return candidate;
6487
6630
  }
6488
6631
  return null;
6489
6632
  }
6490
6633
  function installEngineTools(configDir2) {
6491
6634
  const assets = assetsDir();
6492
6635
  if (!assets) return false;
6493
- const target = (0, import_node_path12.join)(configDir2, "tools");
6494
- (0, import_node_fs10.mkdirSync)(target, { recursive: true });
6636
+ const target = (0, import_node_path13.join)(configDir2, "tools");
6637
+ (0, import_node_fs11.mkdirSync)(target, { recursive: true });
6495
6638
  try {
6496
- (0, import_node_fs10.cpSync)((0, import_node_path12.join)(assets, "tools"), target, { recursive: true });
6639
+ (0, import_node_fs11.cpSync)((0, import_node_path13.join)(assets, "tools"), target, { recursive: true });
6497
6640
  return true;
6498
6641
  } catch {
6499
6642
  return false;
@@ -6675,7 +6818,9 @@ without an address. Read \`.agentful/project.json\` and give the full URL
6675
6818
  \`https://app.agentful.dev/workspace/<userId>/<projectId>?view=backend\` \u2014 or
6676
6819
  simply the command \`agentful backend\`, which opens exactly that page and
6677
6820
  shows the live state. Declaring a backend never enables it; enabling is the
6678
- user's conscious step in that tab.
6821
+ user's conscious step \u2014 in that tab, or by answering the question
6822
+ \`agentful push\` asks once (it names both consequences; a non-interactive
6823
+ push never enables anything).
6679
6824
 
6680
6825
  ## Diagnosing failed pushes and cloud builds (hard rule)
6681
6826
 
@@ -6703,7 +6848,7 @@ var CLOUD_AGENT_PROMPT = `You are the Agentful Cloud build agent: everything you
6703
6848
  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 (static files only). 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.";
6704
6849
  function readAuthFile(path) {
6705
6850
  try {
6706
- return (0, import_node_fs11.readFileSync)(path, "utf8");
6851
+ return (0, import_node_fs12.readFileSync)(path, "utf8");
6707
6852
  } catch {
6708
6853
  return null;
6709
6854
  }
@@ -6768,11 +6913,11 @@ function buildEngineConfig(opts) {
6768
6913
  };
6769
6914
  }
6770
6915
  function engineXdg() {
6771
- const root = (0, import_node_path13.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
6916
+ const root = (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
6772
6917
  return {
6773
- configHome: (0, import_node_path13.join)(root, "engine-config"),
6774
- dataHome: (0, import_node_path13.join)(root, "engine-data"),
6775
- stateHome: (0, import_node_path13.join)(root, "engine-state")
6918
+ configHome: (0, import_node_path14.join)(root, "engine-config"),
6919
+ dataHome: (0, import_node_path14.join)(root, "engine-data"),
6920
+ stateHome: (0, import_node_path14.join)(root, "engine-state")
6776
6921
  };
6777
6922
  }
6778
6923
  async function resolveTheme() {
@@ -6791,24 +6936,24 @@ async function resolveTheme() {
6791
6936
  }
6792
6937
  async function writeEngineSession(session, catalog, localProviders = {}, framework = "unknown", skillsInstruction = null, backendState) {
6793
6938
  const xdg = engineXdg();
6794
- const configDir2 = (0, import_node_path13.join)(xdg.configHome, "opencode");
6795
- const dataDir = (0, import_node_path13.join)(xdg.dataHome, "opencode");
6796
- const themesDir = (0, import_node_path13.join)(configDir2, "themes");
6797
- const pluginsDir = (0, import_node_path13.join)(configDir2, "plugins");
6798
- const commandsDir = (0, import_node_path13.join)(configDir2, "commands");
6799
- for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path13.join)(xdg.stateHome, "opencode")]) {
6800
- (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
6939
+ const configDir2 = (0, import_node_path14.join)(xdg.configHome, "opencode");
6940
+ const dataDir = (0, import_node_path14.join)(xdg.dataHome, "opencode");
6941
+ const themesDir = (0, import_node_path14.join)(configDir2, "themes");
6942
+ const pluginsDir = (0, import_node_path14.join)(configDir2, "plugins");
6943
+ const commandsDir = (0, import_node_path14.join)(configDir2, "commands");
6944
+ for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path14.join)(xdg.stateHome, "opencode")]) {
6945
+ (0, import_node_fs12.mkdirSync)(dir, { recursive: true });
6801
6946
  }
6802
6947
  const imagegenAvailable = installEngineTools(configDir2) && session.img_on !== false;
6803
- const cloudInstructionsPath = (0, import_node_path13.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
6804
- (0, import_node_fs11.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework, backendState));
6805
- const skillsPath = (0, import_node_path13.join)(configDir2, "AGENTFUL_SKILLS.md");
6948
+ const cloudInstructionsPath = (0, import_node_path14.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
6949
+ (0, import_node_fs12.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework, backendState));
6950
+ const skillsPath = (0, import_node_path14.join)(configDir2, "AGENTFUL_SKILLS.md");
6806
6951
  const instructionPaths = [cloudInstructionsPath];
6807
6952
  if (skillsInstruction) {
6808
- (0, import_node_fs11.writeFileSync)(skillsPath, skillsInstruction);
6953
+ (0, import_node_fs12.writeFileSync)(skillsPath, skillsInstruction);
6809
6954
  instructionPaths.push(skillsPath);
6810
6955
  } else {
6811
- (0, import_node_fs11.rmSync)(skillsPath, { force: true });
6956
+ (0, import_node_fs12.rmSync)(skillsPath, { force: true });
6812
6957
  }
6813
6958
  const config = buildEngineConfig({
6814
6959
  session,
@@ -6817,23 +6962,23 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
6817
6962
  imagegenAvailable,
6818
6963
  instructionPaths
6819
6964
  });
6820
- (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
6821
- const authPath2 = (0, import_node_path13.join)(dataDir, "auth.json");
6822
- (0, import_node_fs11.writeFileSync)(authPath2, mergedAuthJson(readAuthFile(authPath2), session.token));
6965
+ (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
6966
+ const authPath2 = (0, import_node_path14.join)(dataDir, "auth.json");
6967
+ (0, import_node_fs12.writeFileSync)(authPath2, mergedAuthJson(readAuthFile(authPath2), session.token));
6823
6968
  const { brandingPluginSource: brandingPluginSource2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
6824
- const pluginPath = (0, import_node_path13.join)(pluginsDir, "agentful-branding.tsx");
6825
- (0, import_node_fs11.writeFileSync)(pluginPath, brandingPluginSource2());
6826
- (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(configDir2, "tui.json"), JSON.stringify({
6969
+ const pluginPath = (0, import_node_path14.join)(pluginsDir, "agentful-branding.tsx");
6970
+ (0, import_node_fs12.writeFileSync)(pluginPath, brandingPluginSource2());
6971
+ (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(configDir2, "tui.json"), JSON.stringify({
6827
6972
  theme: "agentful",
6828
6973
  plugin: [`file://${pluginPath}`]
6829
6974
  }, null, 2));
6830
- (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
6975
+ (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
6831
6976
  for (const name of Object.keys(SLASH_COMMANDS)) {
6832
- (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(commandsDir, `${name}.md`), commandMarkdown(name));
6977
+ (0, import_node_fs12.writeFileSync)((0, import_node_path14.join)(commandsDir, `${name}.md`), commandMarkdown(name));
6833
6978
  }
6834
6979
  for (const legacy of ["xdg-config", "xdg-data", "xdg-state"]) {
6835
6980
  try {
6836
- (0, import_node_fs11.rmSync)((0, import_node_path13.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
6981
+ (0, import_node_fs12.rmSync)((0, import_node_path14.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
6837
6982
  } catch {
6838
6983
  }
6839
6984
  }
@@ -6841,8 +6986,8 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
6841
6986
  }
6842
6987
 
6843
6988
  // src/lib/skills.ts
6844
- var import_node_fs12 = require("fs");
6845
- var import_node_path14 = require("path");
6989
+ var import_node_fs13 = require("fs");
6990
+ var import_node_path15 = require("path");
6846
6991
 
6847
6992
  // src/generated/skills.json
6848
6993
  var skills_default = {
@@ -6911,7 +7056,7 @@ var SCAFFOLD_BY_FRAMEWORK = {
6911
7056
  };
6912
7057
  function hasVueDependency(rootDir) {
6913
7058
  try {
6914
- const pkg = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path14.join)(rootDir, "package.json"), "utf8"));
7059
+ const pkg = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path15.join)(rootDir, "package.json"), "utf8"));
6915
7060
  return Boolean({ ...pkg.dependencies, ...pkg.devDependencies }["vue"]);
6916
7061
  } catch {
6917
7062
  return false;
@@ -6941,7 +7086,7 @@ function renderSkillsInstruction(names) {
6941
7086
  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";
6942
7087
  }
6943
7088
  function skillsInstructionFor(framework, rootDir) {
6944
- if (!(0, import_node_fs12.existsSync)(rootDir)) return null;
7089
+ if (!(0, import_node_fs13.existsSync)(rootDir)) return null;
6945
7090
  return renderSkillsInstruction(selectSkills(framework, rootDir));
6946
7091
  }
6947
7092
 
@@ -7056,15 +7201,15 @@ function decideLocalProviders(policy) {
7056
7201
  }
7057
7202
 
7058
7203
  // src/lib/localProviders.ts
7059
- var import_node_fs13 = require("fs");
7060
- var import_node_path15 = require("path");
7204
+ var import_node_fs14 = require("fs");
7205
+ var import_node_path16 = require("path");
7061
7206
  var LOCAL_PROVIDERS_FILE = "local-providers.json";
7062
7207
  function localProvidersPath() {
7063
- return (0, import_node_path15.join)(configDir(), LOCAL_PROVIDERS_FILE);
7208
+ return (0, import_node_path16.join)(configDir(), LOCAL_PROVIDERS_FILE);
7064
7209
  }
7065
7210
  function readLocalProviders() {
7066
7211
  try {
7067
- const raw = JSON.parse((0, import_node_fs13.readFileSync)(localProvidersPath(), "utf8"));
7212
+ const raw = JSON.parse((0, import_node_fs14.readFileSync)(localProvidersPath(), "utf8"));
7068
7213
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
7069
7214
  const map = raw.providers && typeof raw.providers === "object" ? raw.providers : raw;
7070
7215
  const usable = {};
@@ -7099,8 +7244,8 @@ function resolveLocalProviders(policy) {
7099
7244
  }
7100
7245
  function writeLocalProvidersExample() {
7101
7246
  const path = localProvidersPath();
7102
- if ((0, import_node_fs13.existsSync)(path)) return path;
7103
- (0, import_node_fs13.mkdirSync)(configDir(), { recursive: true });
7247
+ if ((0, import_node_fs14.existsSync)(path)) return path;
7248
+ (0, import_node_fs14.mkdirSync)(configDir(), { recursive: true });
7104
7249
  const example = {
7105
7250
  _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.',
7106
7251
  _anthropic: {
@@ -7110,21 +7255,21 @@ function writeLocalProvidersExample() {
7110
7255
  models: { "claude-sonnet-4-5": { name: "Claude Sonnet 4.5 (my key)" } }
7111
7256
  }
7112
7257
  };
7113
- (0, import_node_fs13.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
7258
+ (0, import_node_fs14.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
7114
7259
  try {
7115
- (0, import_node_fs13.chmodSync)(path, 384);
7260
+ (0, import_node_fs14.chmodSync)(path, 384);
7116
7261
  } catch {
7117
7262
  }
7118
7263
  return path;
7119
7264
  }
7120
7265
 
7121
7266
  // src/lib/sessions.ts
7122
- var import_node_fs14 = require("fs");
7123
- var import_node_path16 = require("path");
7267
+ var import_node_fs15 = require("fs");
7268
+ var import_node_path17 = require("path");
7124
7269
  var import_node_child_process4 = require("child_process");
7125
7270
  function lastSessionForDirectory(cwd = process.cwd()) {
7126
- const dbPath = (0, import_node_path16.join)(engineXdg().dataHome, "opencode", "opencode.db");
7127
- if (!(0, import_node_fs14.existsSync)(dbPath)) return null;
7271
+ const dbPath = (0, import_node_path17.join)(engineXdg().dataHome, "opencode", "opencode.db");
7272
+ if (!(0, import_node_fs15.existsSync)(dbPath)) return null;
7128
7273
  const escaped = cwd.replace(/'/g, "''");
7129
7274
  const res = (0, import_node_child_process4.spawnSync)(
7130
7275
  "sqlite3",
@@ -7216,7 +7361,7 @@ async function tuiCommand(opts = {}) {
7216
7361
  ...process.env,
7217
7362
  // Slash commands run `agentful …` through the agent's bash tool, which
7218
7363
  // has no access to the user's shell aliases.
7219
- PATH: `${binDir}${import_node_path17.delimiter}${process.env.PATH || ""}`,
7364
+ PATH: `${binDir}${import_node_path18.delimiter}${process.env.PATH || ""}`,
7220
7365
  XDG_CONFIG_HOME: xdg.configHome,
7221
7366
  XDG_DATA_HOME: xdg.dataHome,
7222
7367
  XDG_STATE_HOME: xdg.stateHome,
@@ -7321,28 +7466,28 @@ async function shareCommand(opts) {
7321
7466
  }
7322
7467
 
7323
7468
  // src/commands/pull.ts
7324
- var import_node_fs15 = require("fs");
7325
- var import_node_path18 = require("path");
7469
+ var import_node_fs16 = require("fs");
7470
+ var import_node_path19 = require("path");
7326
7471
  init_branding();
7327
7472
  init_branding();
7328
7473
  function isProbablyBase64Binary(path) {
7329
7474
  return /\.(png|jpe?g|gif|webp|ico|woff2?|ttf|otf|eot|pdf|zip|mp[34]|webm|avif)$/i.test(path);
7330
7475
  }
7331
7476
  function writeEntry(root, rel, value) {
7332
- const target = (0, import_node_path18.join)(root, rel);
7333
- (0, import_node_fs15.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
7477
+ const target = (0, import_node_path19.join)(root, rel);
7478
+ (0, import_node_fs16.mkdirSync)((0, import_node_path19.dirname)(target), { recursive: true });
7334
7479
  const content = typeof value === "object" && value !== null && "content" in value ? String(value.content) : String(value ?? "");
7335
7480
  if (isProbablyBase64Binary(rel)) {
7336
- (0, import_node_fs15.writeFileSync)(target, Buffer.from(content, "base64"));
7481
+ (0, import_node_fs16.writeFileSync)(target, Buffer.from(content, "base64"));
7337
7482
  } else {
7338
- (0, import_node_fs15.writeFileSync)(target, content, "utf8");
7483
+ (0, import_node_fs16.writeFileSync)(target, content, "utf8");
7339
7484
  }
7340
7485
  }
7341
7486
  async function pullCommand(opts) {
7342
7487
  console.log(banner());
7343
7488
  const auth = await ensureAuth();
7344
7489
  const project = requireProject();
7345
- const nonHidden = (0, import_node_fs15.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
7490
+ const nonHidden = (0, import_node_fs16.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
7346
7491
  if (nonHidden.length > 0 && !opts.force) {
7347
7492
  throw new ApiError(
7348
7493
  0,
@@ -7366,9 +7511,9 @@ async function pullCommand(opts) {
7366
7511
  ui.warn(`Skipped ${rel} (HTTP ${resp.status})`);
7367
7512
  continue;
7368
7513
  }
7369
- const target = (0, import_node_path18.join)(process.cwd(), rel);
7370
- (0, import_node_fs15.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
7371
- (0, import_node_fs15.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
7514
+ const target = (0, import_node_path19.join)(process.cwd(), rel);
7515
+ (0, import_node_fs16.mkdirSync)((0, import_node_path19.dirname)(target), { recursive: true });
7516
+ (0, import_node_fs16.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
7372
7517
  written++;
7373
7518
  }
7374
7519
  } else if (data.files) {
@@ -7522,7 +7667,7 @@ program2.command("login").description("Sign in to your Agentful account (device
7522
7667
  program2.command("logout").description("Remove the stored credentials").action(run(logoutCommand));
7523
7668
  program2.command("whoami").description("Show the signed-in account").action(run(whoamiCommand));
7524
7669
  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)));
7525
- 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)));
7670
+ program2.command("push").description("Upload this project and get a live preview URL").option("--prebuilt", "upload a locally built output instead of building in the cloud").option("--dir <path>", "built-output directory for --prebuilt (default: auto-detect)").option("--force", "push even when the pre-upload compatibility check finds blocking issues").option("-y, --yes", "enable the declared managed database without asking (CI); a non-interactive push never enables it").action(run((opts) => pushCommand(opts)));
7526
7671
  program2.command("build-status").description("Show the cloud's record of the last build (the real error on failures)").action(run(buildStatusCommand));
7527
7672
  program2.command("backend").description("Open this project's Backend tab (managed DB + server actions) in the browser").action(run(backendCommand));
7528
7673
  program2.command("open").description("Open the live preview in the browser").action(run(openCommand));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentful",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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",