@openfn/cli 1.13.0 → 1.13.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.
package/dist/index.js CHANGED
@@ -291,6 +291,14 @@ var force = {
291
291
  default: false
292
292
  }
293
293
  };
294
+ var keepUnsupported = {
295
+ name: "keep-unsupported",
296
+ yargs: {
297
+ boolean: true,
298
+ description: "Keep adaptors that do not support metadata (do not remove them)",
299
+ default: false
300
+ }
301
+ };
294
302
  var immutable = {
295
303
  name: "immutable",
296
304
  yargs: {
@@ -916,6 +924,7 @@ var options6 = [
916
924
  expandAdaptors,
917
925
  adaptors,
918
926
  force,
927
+ keepUnsupported,
919
928
  log,
920
929
  logJson,
921
930
  repoDir,
@@ -320,7 +320,11 @@ var get_autoinstall_targets_default = getAutoinstallTargets;
320
320
  // src/repo/handler.ts
321
321
  import { exec } from "node:child_process";
322
322
  import treeify from "treeify";
323
- import { install as rtInstall, loadRepoPkg } from "@openfn/runtime";
323
+ import {
324
+ install as rtInstall,
325
+ loadRepoPkg,
326
+ getNameAndVersion as getNameAndVersion2
327
+ } from "@openfn/runtime";
324
328
  var install = async (opts, log = defaultLogger) => {
325
329
  let { packages, adaptors, repoDir } = opts;
326
330
  const targets = [].concat(packages ?? [], adaptors ?? []);
@@ -335,6 +339,29 @@ var install = async (opts, log = defaultLogger) => {
335
339
  }
336
340
  return [];
337
341
  };
342
+ var removePackage = async (packageSpecifier, repoDir, logger) => {
343
+ const { name, version } = getNameAndVersion2(packageSpecifier);
344
+ if (!version) {
345
+ logger.warn(`Cannot remove ${packageSpecifier}: no version specified`);
346
+ return;
347
+ }
348
+ const aliasedName = `${name}_${version}`;
349
+ logger.info(`Removing package ${aliasedName} from repo...`);
350
+ try {
351
+ await new Promise((resolve, reject) => {
352
+ exec(`npm uninstall ${aliasedName}`, { cwd: repoDir }, (error) => {
353
+ if (error) {
354
+ reject(error);
355
+ } else {
356
+ resolve();
357
+ }
358
+ });
359
+ });
360
+ logger.success(`Successfully removed ${aliasedName}`);
361
+ } catch (error) {
362
+ logger.warn(`Failed to remove ${aliasedName}: ${error.message}`);
363
+ }
364
+ };
338
365
  var clean = async (options, logger) => {
339
366
  if (options.repoDir) {
340
367
  const doIt = await logger.confirm(
@@ -688,7 +715,7 @@ var expand_adaptors_default = (input) => {
688
715
  import { readFile as readFile2 } from "node:fs/promises";
689
716
  import path3 from "node:path";
690
717
  import assert from "node:assert";
691
- import { getNameAndVersion as getNameAndVersion2 } from "@openfn/runtime";
718
+ import { getNameAndVersion as getNameAndVersion3 } from "@openfn/runtime";
692
719
  var validateMonoRepo = async (repoPath, log) => {
693
720
  try {
694
721
  const raw = await readFile2(`${repoPath}/package.json`, "utf8");
@@ -703,7 +730,7 @@ var updatePath = (adaptor, repoPath, log) => {
703
730
  if (adaptor.match("=")) {
704
731
  return adaptor;
705
732
  }
706
- const { name, version } = getNameAndVersion2(adaptor);
733
+ const { name, version } = getNameAndVersion3(adaptor);
707
734
  if (version) {
708
735
  log.warn(
709
736
  `Warning: Ignoring version specifier on ${adaptor} as loading from the adaptors monorepo`
@@ -1649,7 +1676,7 @@ import { writeFile as writeFile5 } from "node:fs/promises";
1649
1676
  import { readFileSync, writeFileSync, mkdirSync, rmSync } from "node:fs";
1650
1677
  import path7 from "node:path";
1651
1678
  import { describePackage } from "@openfn/describe-package";
1652
- import { getNameAndVersion as getNameAndVersion3 } from "@openfn/runtime";
1679
+ import { getNameAndVersion as getNameAndVersion4 } from "@openfn/runtime";
1653
1680
  var RETRY_DURATION = 500;
1654
1681
  var RETRY_COUNT = 20;
1655
1682
  var TIMEOUT_MS = 1e3 * 60;
@@ -1701,7 +1728,7 @@ var waitForDocs = async (docs, path12, logger, retryDuration = RETRY_DURATION) =
1701
1728
  };
1702
1729
  var docgenHandler = (options, logger, docgen = actualDocGen, retryDuration = RETRY_DURATION) => {
1703
1730
  const { specifier, repoDir } = options;
1704
- const { version } = getNameAndVersion3(specifier);
1731
+ const { version } = getNameAndVersion4(specifier);
1705
1732
  if (!version) {
1706
1733
  logger.error("Error: No version number detected");
1707
1734
  logger.error("eg, @openfn/language-common@1.7.5");
@@ -1742,7 +1769,7 @@ var handler_default7 = docgenHandler;
1742
1769
  // src/docs/handler.ts
1743
1770
  import { readFile as readFile4 } from "node:fs/promises";
1744
1771
  import c from "chalk";
1745
- import { getNameAndVersion as getNameAndVersion4, getLatestVersion } from "@openfn/runtime";
1772
+ import { getNameAndVersion as getNameAndVersion5, getLatestVersion } from "@openfn/runtime";
1746
1773
  var describeFn = (adaptorName, fn) => [
1747
1774
  c.green(
1748
1775
  `## ${fn.name}(${fn.parameters.map(({ name }) => name).join(",")})`
@@ -1773,7 +1800,7 @@ var docsHandler = async (options, logger) => {
1773
1800
  const { adaptor, operation, repoDir } = options;
1774
1801
  const adaptors = expand_adaptors_default([adaptor]);
1775
1802
  const [adaptorName] = adaptors;
1776
- let { name, version } = getNameAndVersion4(adaptorName);
1803
+ let { name, version } = getNameAndVersion5(adaptorName);
1777
1804
  if (!version) {
1778
1805
  logger.info("No version number provided, looking for latest...");
1779
1806
  version = await getLatestVersion(name);
@@ -1827,11 +1854,28 @@ var docsHandler = async (options, logger) => {
1827
1854
  var handler_default8 = docsHandler;
1828
1855
 
1829
1856
  // src/metadata/cache.ts
1857
+ import { getNameAndVersion as getNameAndVersion6 } from "@openfn/runtime";
1830
1858
  import { createHash } from "node:crypto";
1831
- import { readFileSync as readFileSync2 } from "node:fs";
1859
+ import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile6, readdir, rm } from "node:fs/promises";
1832
1860
  import path8 from "node:path";
1833
- import { writeFile as writeFile6, mkdir as mkdir3 } from "node:fs/promises";
1834
- var getPath = (repoDir, key) => `${repoDir}/meta/${key}.json`;
1861
+ var UNSUPPORTED_FILE_NAME = "unsupported.json";
1862
+ var getCachePath2 = (repoDir, key) => {
1863
+ const base = path8.join(repoDir, "meta");
1864
+ if (key) {
1865
+ return path8.join(base, key.endsWith(".json") ? key : `${key}.json`);
1866
+ }
1867
+ return base;
1868
+ };
1869
+ var getCache = async (repoDir, key) => {
1870
+ try {
1871
+ const cachePath = getCachePath2(repoDir, key);
1872
+ const content = await readFile5(cachePath, "utf8");
1873
+ return JSON.parse(content);
1874
+ } catch (e) {
1875
+ return null;
1876
+ }
1877
+ };
1878
+ var getUnsupportedCache = (repoDir) => getCache(repoDir, UNSUPPORTED_FILE_NAME);
1835
1879
  var sortKeys = (obj) => {
1836
1880
  const newObj = {};
1837
1881
  Object.keys(obj).sort().forEach((k) => {
@@ -1846,24 +1890,83 @@ var sortKeys = (obj) => {
1846
1890
  };
1847
1891
  var generateKey = (config, adaptor) => {
1848
1892
  const sorted = sortKeys(config);
1849
- const key = `${JSON.stringify(sorted)}-${adaptor}}`;
1893
+ const key = `${JSON.stringify(sorted)}-${adaptor}`;
1850
1894
  return createHash("sha256").update(key).digest("hex");
1851
1895
  };
1852
- var get2 = (repoPath, key) => {
1896
+ var get2 = async (repoPath, key) => {
1897
+ const p = getCachePath2(repoPath, key);
1853
1898
  try {
1854
- const data = readFileSync2(getPath(repoPath, key), "utf8");
1855
- const json = JSON.parse(data);
1856
- return json;
1899
+ const result = await readFile5(p, "utf8");
1900
+ return JSON.parse(result);
1857
1901
  } catch (e) {
1858
1902
  return null;
1859
1903
  }
1860
1904
  };
1861
- var set2 = async (repoPath, key, data) => {
1862
- const fullPath = getPath(repoPath, key);
1863
- await mkdir3(path8.dirname(fullPath), { recursive: true });
1864
- await writeFile6(fullPath, JSON.stringify(data));
1905
+ var set2 = async (repoPath, key, result) => {
1906
+ const p = getCachePath2(repoPath, key);
1907
+ await mkdir3(path8.dirname(p), { recursive: true });
1908
+ await writeFile6(p, JSON.stringify(result));
1909
+ };
1910
+ var getUnsupportedCachePath = (repoDir) => {
1911
+ return getCachePath2(repoDir, UNSUPPORTED_FILE_NAME);
1912
+ };
1913
+ var parseVersion = (version) => {
1914
+ const parts = version.split(".").map(Number);
1915
+ return {
1916
+ major: parts[0] || 0,
1917
+ minor: parts[1] || 0,
1918
+ patch: parts[2] || 0,
1919
+ majorMinor: `${parts[0] || 0}.${parts[1] || 0}`
1920
+ };
1921
+ };
1922
+ var compareVersions = (version1, version2) => {
1923
+ const v1 = parseVersion(version1);
1924
+ const v2 = parseVersion(version2);
1925
+ if (v1.major !== v2.major)
1926
+ return v1.major - v2.major;
1927
+ if (v1.minor !== v2.minor)
1928
+ return v1.minor - v2.minor;
1929
+ return v1.patch - v2.patch;
1930
+ };
1931
+ var isAdaptorUnsupported = async (adaptorSpecifier, repoDir) => {
1932
+ const { name, version } = getNameAndVersion6(adaptorSpecifier);
1933
+ if (!version)
1934
+ return false;
1935
+ const cache = await getUnsupportedCache(repoDir);
1936
+ if (!cache || !cache[name]) {
1937
+ return false;
1938
+ }
1939
+ const cached = cache[name];
1940
+ const currentParsed = parseVersion(version);
1941
+ const cachedParsed = parseVersion(cached.lastCheckedVersion);
1942
+ if (currentParsed.major > cachedParsed.major || currentParsed.major === cachedParsed.major && currentParsed.minor > cachedParsed.minor) {
1943
+ return false;
1944
+ }
1945
+ return true;
1946
+ };
1947
+ var markAdaptorAsUnsupported = async (adaptorSpecifier, repoDir) => {
1948
+ const { name, version } = getNameAndVersion6(adaptorSpecifier);
1949
+ if (!version)
1950
+ return;
1951
+ const cachePath = getUnsupportedCachePath(repoDir);
1952
+ let cache = {};
1953
+ try {
1954
+ const cacheContent = await readFile5(cachePath, "utf8");
1955
+ cache = JSON.parse(cacheContent);
1956
+ } catch (error) {
1957
+ }
1958
+ const parsed = parseVersion(version);
1959
+ const existing = cache[name];
1960
+ if (!existing || compareVersions(version, existing.lastCheckedVersion) > 0) {
1961
+ cache[name] = {
1962
+ lastCheckedVersion: version,
1963
+ majorMinor: parsed.majorMinor,
1964
+ timestamp: Date.now()
1965
+ };
1966
+ await mkdir3(path8.dirname(cachePath), { recursive: true });
1967
+ await writeFile6(cachePath, JSON.stringify(cache, null, 2));
1968
+ }
1865
1969
  };
1866
- var cache_default = { get: get2, set: set2, generateKey, getPath, sortKeys };
1867
1970
 
1868
1971
  // src/metadata/handler.ts
1869
1972
  import { getModuleEntryPoint } from "@openfn/runtime";
@@ -1900,8 +2003,15 @@ var getAdaptorPath = async (adaptor, logger, repoDir) => {
1900
2003
  };
1901
2004
  var shouldAutoinstall = (adaptor) => adaptor?.length > 0 && !adaptor.startsWith("/") && !adaptor.includes("=");
1902
2005
  var metadataHandler = async (options, logger) => {
1903
- const { repoDir, adaptors } = options;
1904
- const adaptor = adaptors[0];
2006
+ const { repoDir, adaptors, keepUnsupported } = options;
2007
+ let adaptor = adaptors[0];
2008
+ if (await isAdaptorUnsupported(adaptor, repoDir)) {
2009
+ logger.info(
2010
+ `Adaptor ${adaptor} is known to not support metadata (cached) - skipping lookup`
2011
+ );
2012
+ logger.error("No metadata helper found");
2013
+ process.exit(1);
2014
+ }
1905
2015
  const state = await load_state_default({}, options, logger);
1906
2016
  logger.success(`Generating metadata`);
1907
2017
  logger.info("config:", state);
@@ -1912,31 +2022,58 @@ var metadataHandler = async (options, logger) => {
1912
2022
  }
1913
2023
  const finish2 = () => {
1914
2024
  logger.success("Done!");
1915
- logger.print(cache_default.getPath(repoDir, id));
2025
+ logger.print(getCachePath2(repoDir, id));
1916
2026
  };
1917
- const id = cache_default.generateKey(config, adaptor);
2027
+ const id = generateKey(config, adaptor);
1918
2028
  if (!options.force) {
1919
2029
  logger.debug("config hash: ", id);
1920
- const cached = await cache_default.get(repoDir, id);
2030
+ const cached = await get2(repoDir, id);
1921
2031
  if (cached) {
1922
2032
  logger.success("Returning metadata from cache");
1923
2033
  return finish2();
1924
2034
  }
1925
2035
  }
2036
+ let wasAutoInstalled = false;
1926
2037
  try {
1927
2038
  if (shouldAutoinstall(adaptor)) {
1928
- await install({ packages: [adaptor], repoDir }, logger);
2039
+ const autoinstallResult = await install(
2040
+ { packages: [adaptor], repoDir },
2041
+ logger
2042
+ );
2043
+ wasAutoInstalled = true;
2044
+ adaptor = autoinstallResult[0];
1929
2045
  }
1930
2046
  const adaptorPath = await getAdaptorPath(adaptor, logger, options.repoDir);
2047
+ if (!adaptorPath) {
2048
+ throw new Error(`Could not resolve adaptor path for ${adaptor}`);
2049
+ }
1931
2050
  const mod = await import(adaptorPath);
1932
- if (mod.metadata) {
2051
+ if (mod.metadata && typeof mod.metadata === "function") {
1933
2052
  logger.info("Metadata function found. Generating metadata...");
1934
2053
  const result = await mod.metadata(config);
1935
2054
  decorateMetadata(result);
1936
- await cache_default.set(repoDir, id, result);
2055
+ await set2(
2056
+ repoDir,
2057
+ id,
2058
+ result
2059
+ );
1937
2060
  finish2();
1938
2061
  } else {
1939
2062
  logger.error("No metadata helper found");
2063
+ if (wasAutoInstalled && !keepUnsupported) {
2064
+ logger.info("Removing unsupported adaptor from disk...");
2065
+ await removePackage(adaptor, repoDir, logger);
2066
+ await markAdaptorAsUnsupported(adaptor, repoDir);
2067
+ logger.info("Adaptor removed and marked as unsupported");
2068
+ } else if (wasAutoInstalled && keepUnsupported) {
2069
+ if (adaptor === "@openfn/language-openfn") {
2070
+ logger.log({ wasAutoInstalled, keepUnsupported });
2071
+ }
2072
+ logger.info(
2073
+ "Keeping unsupported adaptor as requested by --keep-unsupported flag"
2074
+ );
2075
+ await markAdaptorAsUnsupported(adaptor, repoDir);
2076
+ }
1940
2077
  process.exit(1);
1941
2078
  }
1942
2079
  } catch (e) {
@@ -2128,10 +2265,10 @@ function pickFirst2(...args) {
2128
2265
  var handler_default10 = pullHandler;
2129
2266
 
2130
2267
  // src/util/print-versions.ts
2131
- import { readFileSync as readFileSync3 } from "node:fs";
2268
+ import { readFileSync as readFileSync2 } from "node:fs";
2132
2269
  import path11 from "node:path";
2133
2270
  import url from "node:url";
2134
- import { getNameAndVersion as getNameAndVersion5 } from "@openfn/runtime";
2271
+ import { getNameAndVersion as getNameAndVersion7 } from "@openfn/runtime";
2135
2272
  import { mainSymbols } from "figures";
2136
2273
  var NODE = "node.js";
2137
2274
  var CLI2 = "cli";
@@ -2141,7 +2278,7 @@ var { triangleRightSmall: t } = mainSymbols;
2141
2278
  var loadVersionFromPath = (adaptorPath) => {
2142
2279
  try {
2143
2280
  const pkg = JSON.parse(
2144
- readFileSync3(path11.resolve(adaptorPath, "package.json"), "utf8")
2281
+ readFileSync2(path11.resolve(adaptorPath, "package.json"), "utf8")
2145
2282
  );
2146
2283
  return pkg.version;
2147
2284
  } catch (e) {
@@ -2158,12 +2295,12 @@ var printVersions = async (logger, options = {}, includeComponents = false) => {
2158
2295
  if (adaptor.match("=")) {
2159
2296
  const [namePart, pathPart] = adaptor.split("=");
2160
2297
  adaptorVersion = loadVersionFromPath(pathPart);
2161
- adaptorName = getNameAndVersion5(namePart).name;
2298
+ adaptorName = getNameAndVersion7(namePart).name;
2162
2299
  } else if (options.monorepoPath) {
2163
- adaptorName = getNameAndVersion5(adaptor).name;
2300
+ adaptorName = getNameAndVersion7(adaptor).name;
2164
2301
  adaptorVersion = "monorepo";
2165
2302
  } else {
2166
- const { name, version: version2 } = getNameAndVersion5(adaptor);
2303
+ const { name, version: version2 } = getNameAndVersion7(adaptor);
2167
2304
  adaptorName = name;
2168
2305
  adaptorVersion = version2 || "latest";
2169
2306
  }
@@ -2177,7 +2314,7 @@ var printVersions = async (logger, options = {}, includeComponents = false) => {
2177
2314
  );
2178
2315
  const prefix = (str) => ` ${t} ${str.padEnd(longest + 4, " ")}`;
2179
2316
  const dirname3 = path11.dirname(url.fileURLToPath(import.meta.url));
2180
- const pkg = JSON.parse(readFileSync3(`${dirname3}/../../package.json`, "utf8"));
2317
+ const pkg = JSON.parse(readFileSync2(`${dirname3}/../../package.json`, "utf8"));
2181
2318
  const { version, dependencies } = pkg;
2182
2319
  const compilerVersion = dependencies["@openfn/compiler"];
2183
2320
  const runtimeVersion = dependencies["@openfn/runtime"];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openfn/cli",
3
- "version": "1.13.0",
4
- "description": "CLI devtools for the openfn toolchain.",
3
+ "version": "1.13.1",
4
+ "description": "CLI devtools for the OpenFn toolchain",
5
5
  "engines": {
6
6
  "node": ">=18",
7
7
  "pnpm": ">=7"
@@ -47,13 +47,13 @@
47
47
  "undici": "^7.1.0",
48
48
  "ws": "^8.18.0",
49
49
  "yargs": "^17.7.2",
50
- "@openfn/compiler": "1.1.0",
51
- "@openfn/describe-package": "0.1.4",
52
50
  "@openfn/deploy": "0.11.2",
51
+ "@openfn/compiler": "1.1.0",
53
52
  "@openfn/lexicon": "^1.2.2",
54
- "@openfn/logger": "1.0.5",
53
+ "@openfn/describe-package": "0.1.4",
55
54
  "@openfn/project": "^0.1.0",
56
- "@openfn/runtime": "1.7.0"
55
+ "@openfn/logger": "1.0.5",
56
+ "@openfn/runtime": "1.7.1"
57
57
  },
58
58
  "files": [
59
59
  "dist",