@biffo/cli 0.58.1 → 0.59.0

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/core.version CHANGED
@@ -1 +1 @@
1
- 0.58.1
1
+ 0.59.0
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { Command as Command23 } from "commander";
7
7
  import { Command as Command4 } from "commander";
8
8
 
9
9
  // src/commands/core-diff.ts
10
+ import { existsSync as existsSync3 } from "fs";
10
11
  import { join as join3, resolve } from "path";
11
12
  import chalk2 from "chalk";
12
13
  import { Command } from "commander";
@@ -148,6 +149,7 @@ function computeCoreDiff(templateRoot, instanceRoot, manifest) {
148
149
  }
149
150
 
150
151
  // src/lib/core-version.ts
152
+ import { execFileSync } from "child_process";
151
153
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
152
154
  import { dirname as dirname2, join as join2, parse as parsePath } from "path";
153
155
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -225,6 +227,19 @@ function serializeInstanceCoreVersion(version) {
225
227
  function writeInstanceCoreVersion(cwd, version) {
226
228
  writeFileSync(join2(cwd, INSTANCE_CORE_FILE), serializeInstanceCoreVersion(version));
227
229
  }
230
+ function latestCoreVersionFromTags(repo, git = defaultTagRunner) {
231
+ let out;
232
+ try {
233
+ out = git(["-C", repo, "tag", "--list", "core-v*"]);
234
+ } catch {
235
+ return null;
236
+ }
237
+ const versions = out.split("\n").map((line) => line.trim()).filter((line) => line.startsWith(CORE_TAG_PREFIX)).map((tag) => tag.slice(CORE_TAG_PREFIX.length)).filter((v) => SEMVER.test(v));
238
+ if (versions.length === 0) return null;
239
+ return versions.sort(compareCoreVersions).at(-1) ?? null;
240
+ }
241
+ var CORE_TAG_PREFIX = "core-v";
242
+ var defaultTagRunner = (args) => execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
228
243
 
229
244
  // src/lib/logger.ts
230
245
  import chalk from "chalk";
@@ -257,7 +272,7 @@ var coreDiffCommand = new Command("diff").description(
257
272
  async function runCoreDiff(options) {
258
273
  const templateRoot = options.templateRoot ?? resolveTemplateRoot({ guidance: MISSING_TEMPLATE_ROOT_GUIDANCE });
259
274
  const manifest = readCoreManifest(templateRoot);
260
- const templateVersion = readCoreVersionFile(join3(templateRoot, "core.version"));
275
+ const templateVersion = latestCoreVersionFromTags(templateRoot) ?? (existsSync3(join3(templateRoot, CORE_VERSION_FILE)) ? readCoreVersionFile(join3(templateRoot, CORE_VERSION_FILE)) : "(unreleased)");
261
276
  const instanceVersion = readInstanceCoreVersion(options.cwd);
262
277
  const diff = computeCoreDiff(templateRoot, options.cwd, manifest);
263
278
  const total = diff.added.length + diff.removed.length + diff.modified.length;
@@ -337,6 +352,7 @@ async function runCoreStatus(options) {
337
352
 
338
353
  // src/commands/core-upgrade.ts
339
354
  import { execSync as execSync2 } from "child_process";
355
+ import { existsSync as existsSync10 } from "fs";
340
356
  import { join as join11, resolve as resolve3 } from "path";
341
357
  import chalk4 from "chalk";
342
358
  import { execa as execa3 } from "execa";
@@ -344,7 +360,7 @@ import { Command as Command3 } from "commander";
344
360
 
345
361
  // src/adapters/git/index.ts
346
362
  import { randomUUID } from "crypto";
347
- import { existsSync as existsSync3, mkdtempSync, rmSync } from "fs";
363
+ import { existsSync as existsSync4, mkdtempSync, rmSync } from "fs";
348
364
  import { tmpdir } from "os";
349
365
  import { join as join4 } from "path";
350
366
  import { execa } from "execa";
@@ -383,7 +399,7 @@ var GitAdapter = class {
383
399
  throw new Error(`Failed to clone ${repoUrl}: ${err.message}`);
384
400
  }
385
401
  const gitDir = join4(dir, ".git");
386
- if (existsSync3(gitDir)) rmSync(gitDir, { recursive: true, force: true });
402
+ if (existsSync4(gitDir)) rmSync(gitDir, { recursive: true, force: true });
387
403
  return dir;
388
404
  }
389
405
  /**
@@ -1168,7 +1184,7 @@ var GitHubAdapter = class {
1168
1184
 
1169
1185
  // src/lib/core-migrations.ts
1170
1186
  import { createHash } from "crypto";
1171
- import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
1187
+ import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
1172
1188
  import { dirname as dirname3, join as join5 } from "path";
1173
1189
  var MIGRATIONS_VERSIONS_DIR = "services/api/migrations/versions";
1174
1190
  var REVISION_RE = /^(revision\b[^=\n]*=\s*)(.+)$/m;
@@ -1208,7 +1224,7 @@ function parseMigration(file, content) {
1208
1224
  return { file, revision, downRevision, content };
1209
1225
  }
1210
1226
  function readMigrations(versionsDir) {
1211
- if (!existsSync4(versionsDir)) return [];
1227
+ if (!existsSync5(versionsDir)) return [];
1212
1228
  return readdirSync2(versionsDir).filter((f) => f.endsWith(".py") && f !== "__init__.py").sort().map((f) => parseMigration(f, readFileSync3(join5(versionsDir, f), "utf8")));
1213
1229
  }
1214
1230
  function rechainMigration(content, revision, downRevision) {
@@ -1444,7 +1460,7 @@ function applyMigrationCarry(instanceDir, plan) {
1444
1460
  const written = [];
1445
1461
  for (const e of plan.entries) {
1446
1462
  const abs = join5(instanceDir, e.path);
1447
- if (existsSync4(abs)) {
1463
+ if (existsSync5(abs)) {
1448
1464
  throw new Error(`Refusing to overwrite an existing migration: ${e.path}`);
1449
1465
  }
1450
1466
  mkdirSync(dirname3(abs), { recursive: true });
@@ -1455,7 +1471,7 @@ function applyMigrationCarry(instanceDir, plan) {
1455
1471
  }
1456
1472
 
1457
1473
  // src/lib/core-upgrade.ts
1458
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
1474
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
1459
1475
  import { tmpdir as tmpdir2 } from "os";
1460
1476
  import { dirname as dirname4, join as join6 } from "path";
1461
1477
  import { execa as execa2 } from "execa";
@@ -1558,7 +1574,7 @@ function applyUpgradePlan(instanceDir, plan) {
1558
1574
  for (const e of plan.entries) {
1559
1575
  const abs = join6(instanceDir, e.path);
1560
1576
  if (e.status === "removed") {
1561
- if (existsSync5(abs)) {
1577
+ if (existsSync6(abs)) {
1562
1578
  rmSync2(abs);
1563
1579
  deleted.push(e.path);
1564
1580
  }
@@ -1585,7 +1601,7 @@ function upgradeBranchName(from, to) {
1585
1601
  }
1586
1602
 
1587
1603
  // src/lib/core-template-trees.ts
1588
- import { execFileSync } from "child_process";
1604
+ import { execFileSync as execFileSync2 } from "child_process";
1589
1605
  import { mkdtempSync as mkdtempSync3, rmSync as rmSync3 } from "fs";
1590
1606
  import { tmpdir as tmpdir3 } from "os";
1591
1607
  import { join as join7 } from "path";
@@ -1593,7 +1609,7 @@ function coreTag(version) {
1593
1609
  parseCoreVersion(version);
1594
1610
  return `core-v${version}`;
1595
1611
  }
1596
- var defaultGit = (args) => execFileSync("git", args, { encoding: "utf8" });
1612
+ var defaultGit = (args) => execFileSync2("git", args, { encoding: "utf8" });
1597
1613
  function tagExists(repo, tag, git) {
1598
1614
  try {
1599
1615
  git(["-C", repo, "rev-parse", "-q", "--verify", `refs/tags/${tag}`]);
@@ -1619,7 +1635,7 @@ function materializeTemplateAtTag(repo, version, git = defaultGit) {
1619
1635
  const tarball = join7(dir, ".tree.tar");
1620
1636
  try {
1621
1637
  git(["-C", repo, "archive", "--format=tar", "-o", tarball, tag]);
1622
- execFileSync("tar", ["-x", "-f", tarball, "-C", dir]);
1638
+ execFileSync2("tar", ["-x", "-f", tarball, "-C", dir]);
1623
1639
  rmSync3(tarball, { force: true });
1624
1640
  } catch (err) {
1625
1641
  rmSync3(dir, { recursive: true, force: true });
@@ -1629,7 +1645,7 @@ function materializeTemplateAtTag(repo, version, git = defaultGit) {
1629
1645
  }
1630
1646
 
1631
1647
  // src/lib/breaking-changes.ts
1632
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
1648
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
1633
1649
  import { join as join8 } from "path";
1634
1650
  var UPGRADE_GUIDE_PATH = "docs/guides/core-upgrade.md";
1635
1651
  var SECTION_HEADING = "## Breaking changes by version";
@@ -1656,7 +1672,7 @@ function parseBreakingChanges(guide) {
1656
1672
  }
1657
1673
  function readBreakingChanges(templateRoot) {
1658
1674
  const path = join8(templateRoot, UPGRADE_GUIDE_PATH);
1659
- if (!existsSync6(path)) return [];
1675
+ if (!existsSync7(path)) return [];
1660
1676
  return parseBreakingChanges(readFileSync5(path, "utf8"));
1661
1677
  }
1662
1678
  function breakingChangesBetween(from, to, entries) {
@@ -1674,7 +1690,7 @@ var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
1674
1690
  ];
1675
1691
 
1676
1692
  // src/lib/plugin-terraform-wiring.ts
1677
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
1693
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
1678
1694
  import { join as join9 } from "path";
1679
1695
  var TEMPLATE_MODULE_DIR = "_template";
1680
1696
  var DEFAULT_PLUGIN_HANDLER = "src.lambda.main.handler";
@@ -1705,7 +1721,7 @@ function listPluginModules(cwd) {
1705
1721
  var FIRST_PARTY_TERRAFORM = (name) => `../../../services/_plugins/${name}/terraform`;
1706
1722
  var THIRD_PARTY_TERRAFORM = (name) => `../../../modules/plugins/${name}`;
1707
1723
  function isFirstPartyPlugin(cwd, name) {
1708
- return existsSync7(join9(cwd, "services", "_plugins", name, "terraform", "main.tf"));
1724
+ return existsSync8(join9(cwd, "services", "_plugins", name, "terraform", "main.tf"));
1709
1725
  }
1710
1726
  function pluginModuleSource(cwd, name) {
1711
1727
  return isFirstPartyPlugin(cwd, name) ? FIRST_PARTY_TERRAFORM(name) : THIRD_PARTY_TERRAFORM(name);
@@ -1738,7 +1754,7 @@ function listEnvironments(cwd) {
1738
1754
  return [];
1739
1755
  }
1740
1756
  return entries.filter((e) => {
1741
- if (!e.isDirectory() || !existsSync7(join9(dir, e.name, "main.tf"))) return false;
1757
+ if (!e.isDirectory() || !existsSync8(join9(dir, e.name, "main.tf"))) return false;
1742
1758
  return declaredVariables(join9(dir, e.name)).has("enabled_plugins");
1743
1759
  }).map((e) => e.name).sort();
1744
1760
  }
@@ -1751,7 +1767,7 @@ function listUnwirableEnvironments(cwd) {
1751
1767
  return [];
1752
1768
  }
1753
1769
  return entries.filter(
1754
- (e) => e.isDirectory() && existsSync7(join9(dir, e.name, "main.tf")) && !declaredVariables(join9(dir, e.name)).has("enabled_plugins")
1770
+ (e) => e.isDirectory() && existsSync8(join9(dir, e.name, "main.tf")) && !declaredVariables(join9(dir, e.name)).has("enabled_plugins")
1755
1771
  ).map((e) => e.name).sort();
1756
1772
  }
1757
1773
  function declaredVariables(moduleDir) {
@@ -1860,7 +1876,7 @@ function syncPluginTerraform(cwd) {
1860
1876
  [tfPath, `${relBase}/${GENERATED_TF_FILE}`],
1861
1877
  [tfvarsPath, `${relBase}/${GENERATED_TFVARS_FILE}`]
1862
1878
  ]) {
1863
- if (existsSync7(abs)) {
1879
+ if (existsSync8(abs)) {
1864
1880
  rmSync4(abs);
1865
1881
  changedPaths.push(rel);
1866
1882
  }
@@ -1876,7 +1892,7 @@ function syncPluginTerraform(cwd) {
1876
1892
  }
1877
1893
 
1878
1894
  // src/lib/lockfile-refresh.ts
1879
- import { existsSync as existsSync8 } from "fs";
1895
+ import { existsSync as existsSync9 } from "fs";
1880
1896
  import { join as join10 } from "path";
1881
1897
  var LOCKFILE_TRIGGERS = [
1882
1898
  {
@@ -1900,7 +1916,7 @@ function lockfilesNeedingRefresh(changedPaths, instanceDir, triggers = LOCKFILE_
1900
1916
  const locked = changedPaths.filter((p) => !isForeignManifest(p));
1901
1917
  return triggers.filter((t) => {
1902
1918
  const touched = locked.some((p) => p === t.manifest || p.endsWith(`/${t.manifest}`));
1903
- return touched && existsSync8(join10(instanceDir, t.lockfile));
1919
+ return touched && existsSync9(join10(instanceDir, t.lockfile));
1904
1920
  });
1905
1921
  }
1906
1922
  async function refreshLockfiles(instanceDir, triggers, run) {
@@ -1993,9 +2009,9 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
1993
2009
  let toVersion;
1994
2010
  if (options.theirsDir) {
1995
2011
  theirsDir = options.theirsDir;
1996
- toVersion = readCoreVersionFile(join11(theirsDir, "core.version"));
2012
+ toVersion = versionOfCheckout(theirsDir, options.toVersion);
1997
2013
  } else {
1998
- const workingVersion = readCoreVersionFile(join11(templateRepo, "core.version"));
2014
+ const workingVersion = latestCoreVersion(templateRepo);
1999
2015
  toVersion = options.toVersion ?? workingVersion;
2000
2016
  if (toVersion === workingVersion) {
2001
2017
  theirsDir = templateRepo;
@@ -2009,7 +2025,7 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
2009
2025
  let fromVersion;
2010
2026
  if (options.baseDir) {
2011
2027
  baseDir = options.baseDir;
2012
- fromVersion = readCoreVersionFile(join11(baseDir, "core.version"));
2028
+ fromVersion = versionOfCheckout(baseDir, void 0);
2013
2029
  } else {
2014
2030
  if (instanceVersion === null) {
2015
2031
  throw new Error(
@@ -2293,6 +2309,23 @@ function printBreakingChanges(breaking, applying) {
2293
2309
  );
2294
2310
  }
2295
2311
  }
2312
+ function versionOfCheckout(dir, explicit) {
2313
+ if (explicit) return explicit;
2314
+ const file = join11(dir, CORE_VERSION_FILE);
2315
+ if (existsSync10(file)) return readCoreVersionFile(file);
2316
+ throw new Error(
2317
+ `Cannot determine the core version of ${dir}: it has no ${CORE_VERSION_FILE}, and a checkout supplied explicitly is not resolved from a tag. Pass --to to state which version this tree is.`
2318
+ );
2319
+ }
2320
+ function latestCoreVersion(repo) {
2321
+ const fromTags = latestCoreVersionFromTags(repo);
2322
+ if (fromTags) return fromTags;
2323
+ const file = join11(repo, CORE_VERSION_FILE);
2324
+ if (existsSync10(file)) return readCoreVersionFile(file);
2325
+ throw new Error(
2326
+ `Cannot determine the template's core version: ${repo} has no core-v* tags and no ${CORE_VERSION_FILE}. Fetch tags (\`git fetch --tags\`) or pass --to explicitly.`
2327
+ );
2328
+ }
2296
2329
 
2297
2330
  // src/commands/core.ts
2298
2331
  var coreCommand = new Command4("core").description(
@@ -2306,7 +2339,7 @@ coreCommand.addCommand(coreUpgradeCommand);
2306
2339
  import { Command as Command8 } from "commander";
2307
2340
 
2308
2341
  // src/commands/data-apply.ts
2309
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
2342
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
2310
2343
  import { resolve as resolve4 } from "path";
2311
2344
  import chalk5 from "chalk";
2312
2345
  import { Command as Command5 } from "commander";
@@ -2757,7 +2790,7 @@ function isTemplatePlaceholderConfig(raw) {
2757
2790
 
2758
2791
  // src/lib/session.ts
2759
2792
  import {
2760
- existsSync as existsSync9,
2793
+ existsSync as existsSync11,
2761
2794
  mkdirSync as mkdirSync4,
2762
2795
  readdirSync as readdirSync4,
2763
2796
  readFileSync as readFileSync7,
@@ -2782,7 +2815,7 @@ function sessionPath(projectName) {
2782
2815
  }
2783
2816
  function loadSession(projectName) {
2784
2817
  const path = sessionPath(projectName);
2785
- if (!existsSync9(path)) return null;
2818
+ if (!existsSync11(path)) return null;
2786
2819
  try {
2787
2820
  return JSON.parse(readFileSync7(path, "utf8"));
2788
2821
  } catch {
@@ -2791,12 +2824,12 @@ function loadSession(projectName) {
2791
2824
  }
2792
2825
  function findLatestSession() {
2793
2826
  const dir = sessionsDir();
2794
- if (!existsSync9(dir)) return null;
2827
+ if (!existsSync11(dir)) return null;
2795
2828
  const files = readdirSync4(dir).filter((f) => f.endsWith(".json"));
2796
2829
  if (files.length === 0) return null;
2797
2830
  const sorted = files.map((f) => {
2798
2831
  const fullPath = join12(dir, f);
2799
- const mtime = existsSync9(fullPath) ? statSync(fullPath).mtimeMs : -1;
2832
+ const mtime = existsSync11(fullPath) ? statSync(fullPath).mtimeMs : -1;
2800
2833
  return { f, mtime };
2801
2834
  }).sort((a, b) => b.mtime - a.mtime);
2802
2835
  try {
@@ -2807,7 +2840,7 @@ function findLatestSession() {
2807
2840
  }
2808
2841
  function saveSession(session) {
2809
2842
  const dir = sessionsDir();
2810
- if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
2843
+ if (!existsSync11(dir)) mkdirSync4(dir, { recursive: true });
2811
2844
  const name = session.config.project?.name ?? "unknown";
2812
2845
  const prior = loadSession(name);
2813
2846
  if (prior) {
@@ -2829,19 +2862,19 @@ function markStepComplete(session, step) {
2829
2862
  }
2830
2863
  function deleteSession(projectName) {
2831
2864
  const path = sessionPath(projectName);
2832
- if (existsSync9(path)) rmSync5(path);
2865
+ if (existsSync11(path)) rmSync5(path);
2833
2866
  }
2834
2867
  function projectsDir() {
2835
2868
  return process.env["BIFFO_PROJECTS_DIR"] ?? join12(homedir(), ".biffo", "projects");
2836
2869
  }
2837
2870
  function saveProjectConfig(config) {
2838
2871
  const dir = projectsDir();
2839
- if (!existsSync9(dir)) mkdirSync4(dir, { recursive: true });
2872
+ if (!existsSync11(dir)) mkdirSync4(dir, { recursive: true });
2840
2873
  writeFileSync5(join12(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
2841
2874
  }
2842
2875
  function loadProjectConfig(name) {
2843
2876
  const path = join12(projectsDir(), `${name}.json`);
2844
- if (!existsSync9(path)) return null;
2877
+ if (!existsSync11(path)) return null;
2845
2878
  try {
2846
2879
  const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync7(path, "utf8")));
2847
2880
  return result.success ? result.data : null;
@@ -2851,11 +2884,11 @@ function loadProjectConfig(name) {
2851
2884
  }
2852
2885
  function deleteProjectConfig(name) {
2853
2886
  const path = join12(projectsDir(), `${name}.json`);
2854
- if (existsSync9(path)) rmSync5(path);
2887
+ if (existsSync11(path)) rmSync5(path);
2855
2888
  }
2856
2889
  function listProjectConfigs() {
2857
2890
  const dir = projectsDir();
2858
- if (!existsSync9(dir)) return [];
2891
+ if (!existsSync11(dir)) return [];
2859
2892
  return readdirSync4(dir).filter((f) => f.endsWith(".json")).flatMap((f) => {
2860
2893
  try {
2861
2894
  const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync7(join12(dir, f), "utf8")));
@@ -2947,7 +2980,7 @@ async function resolveConfig(options) {
2947
2980
  return cfg;
2948
2981
  }
2949
2982
  const localConfigPath = resolve4(process.cwd(), "biffo.config.json");
2950
- if (existsSync10(localConfigPath)) {
2983
+ if (existsSync12(localConfigPath)) {
2951
2984
  const raw = JSON.parse(readFileSync8(localConfigPath, "utf8"));
2952
2985
  const result = BiffoConfigSchema.safeParse(raw);
2953
2986
  if (result.success) return result.data;
@@ -2994,7 +3027,7 @@ async function resolveConfig(options) {
2994
3027
 
2995
3028
  // src/commands/data-import.ts
2996
3029
  import { execSync as execSync3 } from "child_process";
2997
- import { cpSync, existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
3030
+ import { cpSync, existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync2 } from "fs";
2998
3031
  import { join as join13, resolve as resolve5 } from "path";
2999
3032
  import chalk6 from "chalk";
3000
3033
  import { Command as Command6 } from "commander";
@@ -3036,18 +3069,18 @@ async function runDataImport(name, options, deps) {
3036
3069
  );
3037
3070
  }
3038
3071
  const servicesDir = join13(options.cwd, "services");
3039
- if (!existsSync11(servicesDir)) {
3072
+ if (!existsSync13(servicesDir)) {
3040
3073
  throw new Error(
3041
3074
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
3042
3075
  );
3043
3076
  }
3044
3077
  const targetDir = join13(options.cwd, "db", "imports", name);
3045
- if (existsSync11(targetDir)) {
3078
+ if (existsSync13(targetDir)) {
3046
3079
  throw new Error(
3047
3080
  `DDL import '${name}' is already present at db/imports/${name}/. Remove it first to re-import.`
3048
3081
  );
3049
3082
  }
3050
- const isLocalDir = existsSync11(options.source) && statSync2(options.source).isDirectory();
3083
+ const isLocalDir = existsSync13(options.source) && statSync2(options.source).isDirectory();
3051
3084
  let sourceDir;
3052
3085
  let cleanupClone = null;
3053
3086
  if (isLocalDir) {
@@ -3062,7 +3095,7 @@ async function runDataImport(name, options, deps) {
3062
3095
  sourceDir = options.path ? join13(tmpDir, options.path) : tmpDir;
3063
3096
  }
3064
3097
  try {
3065
- if (!existsSync11(sourceDir)) {
3098
+ if (!existsSync13(sourceDir)) {
3066
3099
  throw new Error(`Source directory does not exist: ${sourceDir}`);
3067
3100
  }
3068
3101
  const sqlFiles = readdirSync5(sourceDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name).sort();
@@ -3139,7 +3172,7 @@ function printDryRun(name, sqlFiles) {
3139
3172
  }
3140
3173
 
3141
3174
  // src/commands/data-list.ts
3142
- import { existsSync as existsSync12, readdirSync as readdirSync6 } from "fs";
3175
+ import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
3143
3176
  import { join as join14, resolve as resolve6 } from "path";
3144
3177
  import chalk7 from "chalk";
3145
3178
  import { Command as Command7 } from "commander";
@@ -3154,7 +3187,7 @@ var dataListCommand = new Command7("list").description("List DDL imports vendore
3154
3187
  });
3155
3188
  async function runDataList(options) {
3156
3189
  const importsDir = join14(options.cwd, "db", "imports");
3157
- if (!existsSync12(importsDir)) {
3190
+ if (!existsSync14(importsDir)) {
3158
3191
  console.log(chalk7.dim("\n No DDL imports in this checkout.\n"));
3159
3192
  return;
3160
3193
  }
@@ -3191,7 +3224,7 @@ dataCommand.addCommand(dataListCommand);
3191
3224
 
3192
3225
  // src/commands/deploy.ts
3193
3226
  import { execSync as execSync4 } from "child_process";
3194
- import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
3227
+ import { existsSync as existsSync15, readFileSync as readFileSync9 } from "fs";
3195
3228
  import { resolve as resolve7 } from "path";
3196
3229
  import chalk8 from "chalk";
3197
3230
  import { Command as Command9 } from "commander";
@@ -3565,7 +3598,7 @@ async function resolveConfig2(options) {
3565
3598
  return cfg;
3566
3599
  }
3567
3600
  const localConfigPath = resolve7(process.cwd(), "biffo.config.json");
3568
- if (existsSync13(localConfigPath)) {
3601
+ if (existsSync15(localConfigPath)) {
3569
3602
  const raw = JSON.parse(readFileSync9(localConfigPath, "utf8"));
3570
3603
  const result = BiffoConfigSchema.safeParse(raw);
3571
3604
  if (result.success) return result.data;
@@ -4117,7 +4150,7 @@ import { Command as Command12 } from "commander";
4117
4150
  import inquirer5 from "inquirer";
4118
4151
 
4119
4152
  // src/lib/build-freshness.ts
4120
- import { existsSync as existsSync14, readdirSync as readdirSync7, statSync as statSync3 } from "fs";
4153
+ import { existsSync as existsSync16, readdirSync as readdirSync7, statSync as statSync3 } from "fs";
4121
4154
  import { dirname as dirname5, join as join15, relative as relative2, sep as sep2 } from "path";
4122
4155
  import { fileURLToPath as fileURLToPath3 } from "url";
4123
4156
  var SKIP_ENV_VAR = "BIFFO_SKIP_BUILD_FRESHNESS_CHECK";
@@ -4141,7 +4174,7 @@ function checkBuildFreshness(options = {}) {
4141
4174
  };
4142
4175
  }
4143
4176
  const srcDir = join15(packageRoot, "src");
4144
- if (!existsSync14(srcDir)) {
4177
+ if (!existsSync16(srcDir)) {
4145
4178
  return {
4146
4179
  status: "skipped",
4147
4180
  reason: "no src/ alongside dist/ \u2014 this is a shipped package",
@@ -4149,7 +4182,7 @@ function checkBuildFreshness(options = {}) {
4149
4182
  };
4150
4183
  }
4151
4184
  const entry = join15(distDir, "index.js");
4152
- if (!existsSync14(entry)) {
4185
+ if (!existsSync16(entry)) {
4153
4186
  return { status: "skipped", reason: `${entry} not found`, newerSources: [] };
4154
4187
  }
4155
4188
  const builtAt = statSync3(entry).mtimeMs;
@@ -4212,7 +4245,7 @@ function collectSourceFiles(srcDir) {
4212
4245
  function findPackageRoot(from) {
4213
4246
  let dir = from;
4214
4247
  for (; ; ) {
4215
- if (existsSync14(join15(dir, "package.json"))) return dir;
4248
+ if (existsSync16(join15(dir, "package.json"))) return dir;
4216
4249
  const parent = dirname5(dir);
4217
4250
  if (parent === dir) return null;
4218
4251
  dir = parent;
@@ -4226,7 +4259,7 @@ function isInside(parent, child) {
4226
4259
 
4227
4260
  // src/lib/credentials.ts
4228
4261
  import { execSync as execSync6 } from "child_process";
4229
- import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
4262
+ import { existsSync as existsSync17, readFileSync as readFileSync11 } from "fs";
4230
4263
  import { homedir as homedir2 } from "os";
4231
4264
  import { join as join16 } from "path";
4232
4265
  import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient2 } from "@aws-sdk/client-sts";
@@ -4410,7 +4443,7 @@ function discoverAwsProfiles() {
4410
4443
  const files = [join16(homedir2(), ".aws", "credentials"), join16(homedir2(), ".aws", "config")];
4411
4444
  const profiles = /* @__PURE__ */ new Set();
4412
4445
  for (const file of files) {
4413
- if (!existsSync15(file)) continue;
4446
+ if (!existsSync17(file)) continue;
4414
4447
  const content = readFileSync11(file, "utf8");
4415
4448
  for (const match of content.matchAll(/^\s*\[([^\]]+)\]\s*$/gm)) {
4416
4449
  const section = match[1]?.trim();
@@ -4501,7 +4534,7 @@ var SiblingConfigSchema = z4.object({
4501
4534
 
4502
4535
  // src/lib/sibling-session.ts
4503
4536
  import {
4504
- existsSync as existsSync16,
4537
+ existsSync as existsSync18,
4505
4538
  mkdirSync as mkdirSync6,
4506
4539
  readdirSync as readdirSync8,
4507
4540
  readFileSync as readFileSync12,
@@ -4519,7 +4552,7 @@ function sessionPath2(projectName) {
4519
4552
  }
4520
4553
  function loadSiblingSession(projectName) {
4521
4554
  const path = sessionPath2(projectName);
4522
- if (!existsSync16(path)) return null;
4555
+ if (!existsSync18(path)) return null;
4523
4556
  try {
4524
4557
  return JSON.parse(readFileSync12(path, "utf8"));
4525
4558
  } catch {
@@ -4528,7 +4561,7 @@ function loadSiblingSession(projectName) {
4528
4561
  }
4529
4562
  function saveSiblingSession(session) {
4530
4563
  const dir = sessionsDir2();
4531
- if (!existsSync16(dir)) mkdirSync6(dir, { recursive: true });
4564
+ if (!existsSync18(dir)) mkdirSync6(dir, { recursive: true });
4532
4565
  const name = session.config.project?.name ?? "unknown";
4533
4566
  const prior = loadSiblingSession(name);
4534
4567
  if (prior) {
@@ -4550,11 +4583,11 @@ function markSiblingStepComplete(session, step) {
4550
4583
  }
4551
4584
  function deleteSiblingSession(projectName) {
4552
4585
  const path = sessionPath2(projectName);
4553
- if (existsSync16(path)) rmSync6(path);
4586
+ if (existsSync18(path)) rmSync6(path);
4554
4587
  }
4555
4588
 
4556
4589
  // src/commands/sibling-create.ts
4557
- import { cpSync as cpSync2, existsSync as existsSync17, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
4590
+ import { cpSync as cpSync2, existsSync as existsSync19, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
4558
4591
  import { tmpdir as tmpdir4 } from "os";
4559
4592
  import { dirname as dirname6, join as join19, resolve as resolve9 } from "path";
4560
4593
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -4618,7 +4651,7 @@ async function runSiblingCreateCommand(name, options) {
4618
4651
  printDryRun2(config, coreConfig, options.templateRoot);
4619
4652
  return;
4620
4653
  }
4621
- if (!existsSync17(options.templateRoot)) {
4654
+ if (!existsSync19(options.templateRoot)) {
4622
4655
  throw new Error(`Sibling template not found at ${options.templateRoot}`);
4623
4656
  }
4624
4657
  let session = null;
@@ -4892,7 +4925,7 @@ async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, git
4892
4925
  }
4893
4926
  }
4894
4927
  function writeSiblingTemplate(templateRoot, targetDir, config, context) {
4895
- if (!existsSync17(templateRoot)) {
4928
+ if (!existsSync19(templateRoot)) {
4896
4929
  throw new Error(`Sibling template not found at ${templateRoot}`);
4897
4930
  }
4898
4931
  cpSync2(templateRoot, targetDir, { recursive: true });
@@ -5098,7 +5131,7 @@ function defaultSiblingTemplateRoot() {
5098
5131
  let dir = start;
5099
5132
  for (; ; ) {
5100
5133
  const candidate = join19(dir, "_skeletons", "sibling-template");
5101
- if (existsSync17(candidate)) return candidate;
5134
+ if (existsSync19(candidate)) return candidate;
5102
5135
  const parent = dirname6(dir);
5103
5136
  if (parent === dir) break;
5104
5137
  dir = parent;
@@ -5555,14 +5588,14 @@ async function promptForConfig(awsAccountId, awsRegion, awsProfile) {
5555
5588
  import { Command as Command20 } from "commander";
5556
5589
 
5557
5590
  // src/commands/plugin-create.ts
5558
- import { existsSync as existsSync20, readFileSync as readFileSync16 } from "fs";
5591
+ import { existsSync as existsSync22, readFileSync as readFileSync16 } from "fs";
5559
5592
  import { dirname as dirname8, join as join22, resolve as resolve11 } from "path";
5560
5593
  import { fileURLToPath as fileURLToPath5 } from "url";
5561
5594
  import chalk13 from "chalk";
5562
5595
  import { Command as Command13 } from "commander";
5563
5596
 
5564
5597
  // src/lib/plugin-locations.ts
5565
- import { existsSync as existsSync18, readdirSync as readdirSync10 } from "fs";
5598
+ import { existsSync as existsSync20, readdirSync as readdirSync10 } from "fs";
5566
5599
  import { join as join20 } from "path";
5567
5600
  var FIRST_PARTY_PLUGINS_DIR = "_plugins";
5568
5601
  var PLUGIN_MANIFEST_FILE = "biffo.plugin.json";
@@ -5570,13 +5603,13 @@ function pluginDir(name, channel) {
5570
5603
  return channel === "first-party" ? `services/${FIRST_PARTY_PLUGINS_DIR}/${name}` : `services/${name}`;
5571
5604
  }
5572
5605
  function scanDir(absDir, relDir, channel) {
5573
- if (!existsSync18(absDir)) return [];
5606
+ if (!existsSync20(absDir)) return [];
5574
5607
  const found = [];
5575
5608
  for (const entry of readdirSync10(absDir, { withFileTypes: true })) {
5576
5609
  if (!entry.isDirectory()) continue;
5577
5610
  if (channel === "third-party" && entry.name === FIRST_PARTY_PLUGINS_DIR) continue;
5578
5611
  const manifestPath = join20(absDir, entry.name, PLUGIN_MANIFEST_FILE);
5579
- if (!existsSync18(manifestPath)) continue;
5612
+ if (!existsSync20(manifestPath)) continue;
5580
5613
  found.push({
5581
5614
  dirName: entry.name,
5582
5615
  relDir: `${relDir}/${entry.name}`,
@@ -5751,7 +5784,7 @@ function validateManifest(raw) {
5751
5784
  // src/lib/plugin-scaffold.ts
5752
5785
  import {
5753
5786
  copyFileSync,
5754
- existsSync as existsSync19,
5787
+ existsSync as existsSync21,
5755
5788
  mkdirSync as mkdirSync8,
5756
5789
  readFileSync as readFileSync15,
5757
5790
  readdirSync as readdirSync11,
@@ -5810,10 +5843,10 @@ function applySubstitutions(text, names) {
5810
5843
  }
5811
5844
  var BINARY_EXTENSIONS = /\.(png|jpe?g|gif|ico|woff2?|ttf|zip|gz)$/i;
5812
5845
  function scaffoldPlugin(skeletonRoot, destDir, names) {
5813
- if (!existsSync19(skeletonRoot)) {
5846
+ if (!existsSync21(skeletonRoot)) {
5814
5847
  throw new Error(`Plugin skeleton not found at ${skeletonRoot}`);
5815
5848
  }
5816
- if (!existsSync19(join21(skeletonRoot, "terraform"))) {
5849
+ if (!existsSync21(join21(skeletonRoot, "terraform"))) {
5817
5850
  throw new Error(
5818
5851
  `Plugin skeleton at ${skeletonRoot} has no terraform/ directory. Refusing to scaffold a plugin that cannot receive events (issue #194) \u2014 the skeleton is broken.`
5819
5852
  );
@@ -5861,7 +5894,7 @@ function findSkeletonRoot(startDir, skeleton) {
5861
5894
  let dir = startDir;
5862
5895
  for (; ; ) {
5863
5896
  const candidate = join21(dir, "_skeletons", skeleton);
5864
- if (existsSync19(candidate)) return candidate;
5897
+ if (existsSync21(candidate)) return candidate;
5865
5898
  const parent = dirname7(dir);
5866
5899
  if (parent === dir) return null;
5867
5900
  dir = parent;
@@ -5898,7 +5931,7 @@ var pluginCreateCommand = new Command13("create").description("Scaffold a new pl
5898
5931
  );
5899
5932
  async function runPluginCreate(name, options, deps) {
5900
5933
  const names = deriveNames(name);
5901
- const isInstance = existsSync20(join22(options.cwd, INSTANCE_CORE_FILE));
5934
+ const isInstance = existsSync22(join22(options.cwd, INSTANCE_CORE_FILE));
5902
5935
  if (options.firstParty && isInstance) {
5903
5936
  throw new Error(
5904
5937
  `--first-party scaffolds into services/_plugins/, which is template-owned: \`biffo core upgrade\` three-way-merges it against the template on every upgrade, and the template has no '${names.slug}'. This checkout is a Biffo instance (${INSTANCE_CORE_FILE} is present), so your plugin belongs in the user-owned ${pluginDir(names.slug, "third-party")}/ \u2014 re-run without --first-party.`
@@ -5908,17 +5941,17 @@ async function runPluginCreate(name, options, deps) {
5908
5941
  const relDir = pluginDir(names.slug, channel);
5909
5942
  const destDir = join22(options.cwd, relDir);
5910
5943
  const servicesDir = join22(options.cwd, "services");
5911
- if (!existsSync20(servicesDir)) {
5944
+ if (!existsSync22(servicesDir)) {
5912
5945
  throw new Error(
5913
5946
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
5914
5947
  );
5915
5948
  }
5916
- if (existsSync20(destDir)) {
5949
+ if (existsSync22(destDir)) {
5917
5950
  throw new Error(`${relDir}/ already exists. Choose a different name, or remove it first.`);
5918
5951
  }
5919
5952
  const here = dirname8(fileURLToPath5(import.meta.url));
5920
5953
  const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join22(options.cwd, "_skeletons", "plugin-template");
5921
- if (!existsSync20(skeletonRoot)) {
5954
+ if (!existsSync22(skeletonRoot)) {
5922
5955
  throw new Error(
5923
5956
  `Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
5924
5957
  );
@@ -6124,7 +6157,7 @@ function printEntry(entry) {
6124
6157
  }
6125
6158
 
6126
6159
  // src/commands/plugin-install.ts
6127
- import { cpSync as cpSync3, existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync17, statSync as statSync5 } from "fs";
6160
+ import { cpSync as cpSync3, existsSync as existsSync23, mkdirSync as mkdirSync9, readFileSync as readFileSync17, statSync as statSync5 } from "fs";
6128
6161
  import { basename, join as join24, relative as relative3, resolve as resolve12 } from "path";
6129
6162
  import chalk15 from "chalk";
6130
6163
  import { Command as Command15 } from "commander";
@@ -6213,14 +6246,14 @@ var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
6213
6246
  ".terraform"
6214
6247
  ]);
6215
6248
  function resolveLocalPlugin(localPath) {
6216
- if (!existsSync21(localPath)) {
6249
+ if (!existsSync23(localPath)) {
6217
6250
  throw new Error(`--local path does not exist: ${localPath}`);
6218
6251
  }
6219
6252
  if (!statSync5(localPath).isDirectory()) {
6220
6253
  throw new Error(`--local path is not a directory: ${localPath}`);
6221
6254
  }
6222
6255
  const manifestPath = join24(localPath, "biffo.plugin.json");
6223
- if (!existsSync21(manifestPath)) {
6256
+ if (!existsSync23(manifestPath)) {
6224
6257
  throw new Error(
6225
6258
  `${localPath} does not contain a biffo.plugin.json manifest at its root \u2014 is it a plugin directory? (Scaffold one with \`biffo plugin create <name>\`.)`
6226
6259
  );
@@ -6247,7 +6280,7 @@ async function cloneAndValidatePlugin(entry, git) {
6247
6280
  const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
6248
6281
  try {
6249
6282
  const manifestPath = join24(tmpDir, "biffo.plugin.json");
6250
- if (!existsSync21(manifestPath)) {
6283
+ if (!existsSync23(manifestPath)) {
6251
6284
  throw new Error(
6252
6285
  `Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
6253
6286
  );
@@ -6276,7 +6309,7 @@ async function runPluginInstall(target, options, deps) {
6276
6309
  );
6277
6310
  }
6278
6311
  const servicesDir = join24(options.cwd, "services");
6279
- if (!existsSync21(servicesDir)) {
6312
+ if (!existsSync23(servicesDir)) {
6280
6313
  throw new Error(
6281
6314
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6282
6315
  );
@@ -6297,7 +6330,7 @@ async function runPluginInstall(target, options, deps) {
6297
6330
  const targetDir = join24(options.cwd, relTargetDir);
6298
6331
  const modulesDir = join24(options.cwd, "modules", "plugins", pluginName);
6299
6332
  const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
6300
- if (existsSync21(targetDir) && !inTreeSource) {
6333
+ if (existsSync23(targetDir) && !inTreeSource) {
6301
6334
  throw new Error(
6302
6335
  `Plugin '${pluginName}' is already installed at ${relTargetDir}/. Remove it first, or wait for a future 'biffo plugin upgrade' command.`
6303
6336
  );
@@ -6341,7 +6374,7 @@ async function runPluginInstall(target, options, deps) {
6341
6374
  }
6342
6375
  const stagePaths = [relTargetDir];
6343
6376
  const tfSourceDir = join24(targetDir, "terraform");
6344
- if (existsSync21(tfSourceDir)) {
6377
+ if (existsSync23(tfSourceDir)) {
6345
6378
  mkdirSync9(modulesDir, { recursive: true });
6346
6379
  cpSync3(tfSourceDir, modulesDir, { recursive: true });
6347
6380
  stagePaths.push(`modules/plugins/${pluginName}`);
@@ -6435,7 +6468,7 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource) {
6435
6468
  }
6436
6469
 
6437
6470
  // src/commands/plugin-list.ts
6438
- import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
6471
+ import { existsSync as existsSync24, readFileSync as readFileSync18 } from "fs";
6439
6472
  import { join as join25, resolve as resolve13 } from "path";
6440
6473
  import chalk16 from "chalk";
6441
6474
  import { Command as Command16 } from "commander";
@@ -6450,7 +6483,7 @@ var pluginListCommand = new Command16("list").description("List plugins installe
6450
6483
  });
6451
6484
  async function runPluginList(options) {
6452
6485
  const servicesDir = join25(options.cwd, "services");
6453
- if (!existsSync22(servicesDir)) {
6486
+ if (!existsSync24(servicesDir)) {
6454
6487
  throw new Error(
6455
6488
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6456
6489
  );
@@ -6495,7 +6528,7 @@ async function runPluginList(options) {
6495
6528
  }
6496
6529
 
6497
6530
  // src/commands/plugin-sync-migrations.ts
6498
- import { existsSync as existsSync23 } from "fs";
6531
+ import { existsSync as existsSync25 } from "fs";
6499
6532
  import { join as join26, relative as relative4, resolve as resolve14 } from "path";
6500
6533
  import chalk17 from "chalk";
6501
6534
  import { Command as Command17 } from "commander";
@@ -6518,10 +6551,10 @@ var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
6518
6551
  );
6519
6552
  async function runPluginSyncMigrations(name, options, deps) {
6520
6553
  const servicesDir = join26(options.cwd, "services");
6521
- if (!existsSync23(servicesDir)) {
6554
+ if (!existsSync25(servicesDir)) {
6522
6555
  throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
6523
6556
  }
6524
- if (name && !existsSync23(join26(servicesDir, name, "biffo.plugin.json"))) {
6557
+ if (name && !existsSync25(join26(servicesDir, name, "biffo.plugin.json"))) {
6525
6558
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
6526
6559
  }
6527
6560
  if (options.dryRun) {
@@ -6557,7 +6590,7 @@ async function runPluginSyncMigrations(name, options, deps) {
6557
6590
  }
6558
6591
 
6559
6592
  // src/commands/plugin-uninstall.ts
6560
- import { existsSync as existsSync24, readFileSync as readFileSync19, rmSync as rmSync7 } from "fs";
6593
+ import { existsSync as existsSync26, readFileSync as readFileSync19, rmSync as rmSync7 } from "fs";
6561
6594
  import { join as join27, resolve as resolve15 } from "path";
6562
6595
  import chalk18 from "chalk";
6563
6596
  import { Command as Command18 } from "commander";
@@ -6591,15 +6624,15 @@ async function runPluginUninstall(name, options, deps) {
6591
6624
  throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
6592
6625
  }
6593
6626
  const servicesDir = join27(options.cwd, "services");
6594
- if (!existsSync24(servicesDir)) {
6627
+ if (!existsSync26(servicesDir)) {
6595
6628
  throw new Error(
6596
6629
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6597
6630
  );
6598
6631
  }
6599
6632
  const targetDir = join27(servicesDir, name);
6600
- if (!existsSync24(targetDir)) {
6633
+ if (!existsSync26(targetDir)) {
6601
6634
  const firstParty = join27(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
6602
- if (existsSync24(firstParty)) {
6635
+ if (existsSync26(firstParty)) {
6603
6636
  throw new Error(
6604
6637
  `Plugin '${name}' is a first-party plugin at ${pluginDir(name, "first-party")}/, which is template-owned \u2014 \`biffo core upgrade\` would restore it on the next upgrade. Disable it instead by removing '${name}' from \`enabled_plugins\` in infra/environments/<env>/main.tf and re-applying.`
6605
6638
  );
@@ -6609,7 +6642,7 @@ async function runPluginUninstall(name, options, deps) {
6609
6642
  const version = readInstalledVersion(targetDir);
6610
6643
  const modulesDir = join27(options.cwd, "modules", "plugins", name);
6611
6644
  const stagePaths = [`services/${name}`];
6612
- if (existsSync24(modulesDir)) {
6645
+ if (existsSync26(modulesDir)) {
6613
6646
  stagePaths.push(`modules/plugins/${name}`);
6614
6647
  }
6615
6648
  if (options.dryRun) {
@@ -6631,7 +6664,7 @@ async function runPluginUninstall(name, options, deps) {
6631
6664
  }
6632
6665
  rmSync7(targetDir, { recursive: true, force: true });
6633
6666
  log.success(`Removed services/${name}/`);
6634
- if (existsSync24(modulesDir)) {
6667
+ if (existsSync26(modulesDir)) {
6635
6668
  rmSync7(modulesDir, { recursive: true, force: true });
6636
6669
  log.success(`Removed modules/plugins/${name}/`);
6637
6670
  const wiring = syncPluginTerraform(options.cwd);
@@ -6669,7 +6702,7 @@ async function runPluginUninstall(name, options, deps) {
6669
6702
  }
6670
6703
  function readInstalledVersion(targetDir) {
6671
6704
  const manifestPath = join27(targetDir, "biffo.plugin.json");
6672
- if (!existsSync24(manifestPath)) return void 0;
6705
+ if (!existsSync26(manifestPath)) return void 0;
6673
6706
  try {
6674
6707
  return validateManifest(JSON.parse(readFileSync19(manifestPath, "utf8"))).version;
6675
6708
  } catch {
@@ -6704,7 +6737,7 @@ function printDryRun5(name, version, stagePaths, keepData) {
6704
6737
  }
6705
6738
 
6706
6739
  // src/commands/plugin-upgrade.ts
6707
- import { cpSync as cpSync4, existsSync as existsSync25, mkdirSync as mkdirSync10, readFileSync as readFileSync20, rmSync as rmSync8 } from "fs";
6740
+ import { cpSync as cpSync4, existsSync as existsSync27, mkdirSync as mkdirSync10, readFileSync as readFileSync20, rmSync as rmSync8 } from "fs";
6708
6741
  import { join as join28, relative as relative5, resolve as resolve16 } from "path";
6709
6742
  import chalk19 from "chalk";
6710
6743
  import { Command as Command19 } from "commander";
@@ -6731,13 +6764,13 @@ var pluginUpgradeCommand = new Command19("upgrade").description(
6731
6764
  async function runPluginUpgrade(target, options, deps) {
6732
6765
  const { name, minor } = parsePluginTarget(target);
6733
6766
  const servicesDir = join28(options.cwd, "services");
6734
- if (!existsSync25(servicesDir)) {
6767
+ if (!existsSync27(servicesDir)) {
6735
6768
  throw new Error(
6736
6769
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6737
6770
  );
6738
6771
  }
6739
6772
  const targetDir = join28(servicesDir, name);
6740
- if (!existsSync25(targetDir)) {
6773
+ if (!existsSync27(targetDir)) {
6741
6774
  throw new Error(
6742
6775
  `Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
6743
6776
  );
@@ -6784,11 +6817,11 @@ async function runPluginUpgrade(target, options, deps) {
6784
6817
  cpSync4(tmpDir, targetDir, { recursive: true });
6785
6818
  log.success(`Upgraded plugin source at services/${entry.name}/`);
6786
6819
  const stagePaths = [`services/${entry.name}`];
6787
- if (existsSync25(modulesDir)) {
6820
+ if (existsSync27(modulesDir)) {
6788
6821
  rmSync8(modulesDir, { recursive: true, force: true });
6789
6822
  }
6790
6823
  const tfSourceDir = join28(targetDir, "terraform");
6791
- if (existsSync25(tfSourceDir)) {
6824
+ if (existsSync27(tfSourceDir)) {
6792
6825
  mkdirSync10(modulesDir, { recursive: true });
6793
6826
  cpSync4(tfSourceDir, modulesDir, { recursive: true });
6794
6827
  stagePaths.push(`modules/plugins/${entry.name}`);
@@ -6823,7 +6856,7 @@ async function runPluginUpgrade(target, options, deps) {
6823
6856
  }
6824
6857
  function readInstalledVersion2(targetDir) {
6825
6858
  const manifestPath = join28(targetDir, "biffo.plugin.json");
6826
- if (!existsSync25(manifestPath)) return void 0;
6859
+ if (!existsSync27(manifestPath)) return void 0;
6827
6860
  try {
6828
6861
  return validateManifest(JSON.parse(readFileSync20(manifestPath, "utf8"))).version;
6829
6862
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.58.1",
3
+ "version": "0.59.0",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",