ardent-cli 0.0.45 → 0.0.46

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.js +150 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1911,12 +1911,85 @@ async function handleReplicaIdentityPreflight(connectorId, options, behavior = {
1911
1911
  return { submitted: true, preflight: refreshed };
1912
1912
  }
1913
1913
 
1914
+ // src/lib/connector_name.ts
1915
+ var SUFFIX_RESERVE = 5;
1916
+ var MAX_BASE_LENGTH = MAX_RESOURCE_NAME_LENGTH - SUFFIX_RESERVE;
1917
+ var MAX_COLLISION_SUFFIX = 1e3;
1918
+ function slugifyToResourceName(input) {
1919
+ const slug = input.normalize("NFKD").replace(new RegExp("\\p{Mn}", "gu"), "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_BASE_LENGTH).replace(/-+$/g, "");
1920
+ if (slug.length === 0) {
1921
+ return null;
1922
+ }
1923
+ try {
1924
+ validateResourceName(slug);
1925
+ } catch {
1926
+ return null;
1927
+ }
1928
+ return slug;
1929
+ }
1930
+ function deriveConnectorName(options) {
1931
+ let base = null;
1932
+ for (const source of options.sources) {
1933
+ if (!source) {
1934
+ continue;
1935
+ }
1936
+ const candidate = slugifyToResourceName(source);
1937
+ if (candidate) {
1938
+ base = candidate;
1939
+ break;
1940
+ }
1941
+ }
1942
+ if (base === null) {
1943
+ base = slugifyToResourceName(options.fallbackBase);
1944
+ }
1945
+ if (base === null) {
1946
+ throw new Error(
1947
+ `Could not derive a connector name: fallback base '${options.fallbackBase}' is not a valid resource name`
1948
+ );
1949
+ }
1950
+ const taken = new Set(options.existingNames);
1951
+ if (!taken.has(base)) {
1952
+ return base;
1953
+ }
1954
+ for (let suffix = 2; suffix <= MAX_COLLISION_SUFFIX; suffix++) {
1955
+ const candidate = `${base}-${suffix}`;
1956
+ if (!taken.has(candidate)) {
1957
+ return candidate;
1958
+ }
1959
+ }
1960
+ throw new Error(
1961
+ `Could not derive a unique connector name from base '${base}' after ${MAX_COLLISION_SUFFIX} attempts; pass --name explicitly`
1962
+ );
1963
+ }
1964
+
1914
1965
  // src/commands/connector/create.ts
1915
1966
  function printEngineSetupRecoveryHint(connectorName) {
1916
1967
  console.error("\u2717 Engine setup did not complete for this connector.");
1917
1968
  console.error(" Inspect: ardent connector list");
1918
1969
  console.error(` Retry: ardent connector retry-setup ${connectorName}`);
1919
1970
  }
1971
+ async function deriveDefaultConnectorName(input) {
1972
+ let existingNames;
1973
+ try {
1974
+ const existing = await api.get(
1975
+ `/v1/cli/connectors?project_id=${input.projectId}`
1976
+ );
1977
+ existingNames = existing.connectors.map((connector) => connector.name);
1978
+ } catch (listErr) {
1979
+ if (isPermissionError(listErr)) {
1980
+ throw listErr;
1981
+ }
1982
+ const detail = listErr instanceof Error ? listErr.message : String(listErr);
1983
+ throw new Error(
1984
+ `Could not look up existing connectors to choose a default name (${detail}). Re-run with --name <name>.`
1985
+ );
1986
+ }
1987
+ return deriveConnectorName({
1988
+ sources: [input.projectName, input.sourceDatabase, input.sourceHost],
1989
+ existingNames,
1990
+ fallbackBase: input.serviceType
1991
+ });
1992
+ }
1920
1993
  async function promptForUnsupportedExtensions(connectorId, unsupported, alreadyPersisted) {
1921
1994
  function mergeAllowlist(newlyAccepted) {
1922
1995
  return Array.from(/* @__PURE__ */ new Set([...alreadyPersisted, ...newlyAccepted])).sort();
@@ -2027,7 +2100,16 @@ async function createAction2(type, url, options) {
2027
2100
  process.exit(1);
2028
2101
  }
2029
2102
  try {
2030
- const connectorName = options.name || (isByocNeon ? "my-neon-connection" : "my-postgresql-connection");
2103
+ if (options.name) {
2104
+ try {
2105
+ validateResourceName(options.name);
2106
+ } catch (validationError) {
2107
+ const message = validationError instanceof Error ? validationError.message : String(validationError);
2108
+ trackEvent("CLI: connector create failed", { reason: "invalid_name" });
2109
+ console.error(`\u2717 ${message}`);
2110
+ process.exit(1);
2111
+ }
2112
+ }
2031
2113
  const currentProjectId = getConfig("currentProjectId");
2032
2114
  if (!currentProjectId) {
2033
2115
  console.error("\u2717 No current project set. Switch to a project first:");
@@ -2035,6 +2117,22 @@ async function createAction2(type, url, options) {
2035
2117
  console.error(" ardent project switch <name>");
2036
2118
  process.exit(1);
2037
2119
  }
2120
+ let parsedUrl;
2121
+ if (!isByocNeon) {
2122
+ parsedUrl = parsePostgresUrl(url);
2123
+ if (!parsedUrl.password) {
2124
+ console.error("\u2717 Password required in connection URL");
2125
+ console.error(" Example: postgresql://user:password@host:5432/db");
2126
+ process.exit(1);
2127
+ }
2128
+ }
2129
+ const connectorName = options.name ? options.name : await deriveDefaultConnectorName({
2130
+ projectId: currentProjectId,
2131
+ projectName: getConfig("currentProjectName"),
2132
+ sourceDatabase: parsedUrl?.database,
2133
+ sourceHost: parsedUrl?.host,
2134
+ serviceType: type.toLowerCase()
2135
+ });
2038
2136
  let createPayload;
2039
2137
  if (isByocNeon) {
2040
2138
  console.log(
@@ -2050,12 +2148,7 @@ async function createAction2(type, url, options) {
2050
2148
  connection_details: {}
2051
2149
  };
2052
2150
  } else {
2053
- const parsed = parsePostgresUrl(url);
2054
- if (!parsed.password) {
2055
- console.error("\u2717 Password required in connection URL");
2056
- console.error(" Example: postgresql://user:password@host:5432/db");
2057
- process.exit(1);
2058
- }
2151
+ const parsed = parsedUrl;
2059
2152
  console.log(
2060
2153
  `Creating connector${useByocEnvironment ? " (BYOC environment)" : ""}...`
2061
2154
  );
@@ -2212,6 +2305,7 @@ async function createAction2(type, url, options) {
2212
2305
  });
2213
2306
  if (isDegraded) {
2214
2307
  console.log("\u2713 Connector created");
2308
+ console.log(` Name: ${connectorName}`);
2215
2309
  console.log(` ID: ${connectorId}`);
2216
2310
  console.log("");
2217
2311
  let warnings = [];
@@ -2225,6 +2319,7 @@ async function createAction2(type, url, options) {
2225
2319
  printDegradedWarnings(warnings);
2226
2320
  } else {
2227
2321
  console.log("\u2713 Connector created and ready");
2322
+ console.log(` Name: ${connectorName}`);
2228
2323
  console.log(` ID: ${connectorId}`);
2229
2324
  }
2230
2325
  showNextStep();
@@ -2277,18 +2372,27 @@ function renderConnectorIcon(connector) {
2277
2372
  }
2278
2373
 
2279
2374
  // src/commands/connector/list.ts
2375
+ function printOtherProjectsHint(summary, currentProjectHasConnectors, dim2, reset2) {
2376
+ if (!summary) return;
2377
+ const connectorWord = summary.connectorCount === 1 ? "connector" : "connectors";
2378
+ const projectWord = summary.projectCount === 1 ? "project" : "projects";
2379
+ const countPhrase = currentProjectHasConnectors ? `${summary.connectorCount} more ${connectorWord}` : `${summary.connectorCount} ${connectorWord}`;
2380
+ console.log(
2381
+ `${dim2}more: ${countPhrase} across ${summary.projectCount} other ${projectWord} \u2014 switch with: ardent project switch <name>${reset2}`
2382
+ );
2383
+ }
2280
2384
  async function listAction2() {
2385
+ const currentProjectId = getConfig("currentProjectId");
2386
+ if (!currentProjectId) {
2387
+ console.error("\u2717 No current project set. Switch to a project first:");
2388
+ console.error(" ardent project list");
2389
+ console.error(" ardent project switch <name>");
2390
+ process.exit(1);
2391
+ }
2281
2392
  let connectors = [];
2282
2393
  let fromCache = false;
2283
2394
  let cacheTime = "";
2284
2395
  try {
2285
- const currentProjectId = getConfig("currentProjectId");
2286
- if (!currentProjectId) {
2287
- console.error("\u2717 No current project set. Switch to a project first:");
2288
- console.error(" ardent project list");
2289
- console.error(" ardent project switch <name>");
2290
- process.exit(1);
2291
- }
2292
2396
  const result = await api.get(`/v1/cli/connectors?project_id=${currentProjectId}`);
2293
2397
  if (!result.connectors) {
2294
2398
  throw new Error("API returned invalid response: missing connectors array");
@@ -2313,26 +2417,47 @@ async function listAction2() {
2313
2417
  process.exit(1);
2314
2418
  }
2315
2419
  }
2420
+ let otherProjectsSummary = null;
2421
+ if (!fromCache) {
2422
+ try {
2423
+ const all = await api.get("/v1/cli/connectors");
2424
+ const others = (all.connectors ?? []).filter(
2425
+ (connector) => connector.project_id && connector.project_id !== currentProjectId
2426
+ );
2427
+ if (others.length > 0) {
2428
+ otherProjectsSummary = {
2429
+ connectorCount: others.length,
2430
+ projectCount: new Set(others.map((connector) => connector.project_id)).size
2431
+ };
2432
+ }
2433
+ } catch {
2434
+ otherProjectsSummary = null;
2435
+ }
2436
+ }
2316
2437
  trackEvent("CLI: connector list succeeded", { connector_count: connectors.length, from_cache: fromCache });
2317
2438
  if (fromCache) {
2318
2439
  console.log(`\u26A0 Offline - showing cached data from ${cacheTime}
2319
2440
  `);
2320
2441
  }
2321
- if (connectors.length === 0) {
2322
- const green3 = "\x1B[32m";
2323
- const reset3 = "\x1B[0m";
2324
- console.log("No connectors found");
2325
- console.log(`${green3} Create one with: ardent connector create postgresql <url>${reset3}`);
2326
- return;
2327
- }
2328
- const selectedConnector = fromCache ? void 0 : reconcileSelectedConnector(connectors);
2329
- const currentConnectorId = selectedConnector?.id ?? getConfig("currentConnectorId");
2330
2442
  const green2 = "\x1B[32m";
2331
2443
  const dim2 = "\x1B[2m";
2332
2444
  const yellow = "\x1B[33m";
2333
2445
  const red = "\x1B[31m";
2334
2446
  const reset2 = "\x1B[0m";
2335
- console.log("Connectors:\n");
2447
+ const projectLabel = getConfig("currentProjectName") ?? currentProjectId;
2448
+ console.log(`Project: ${projectLabel}
2449
+ `);
2450
+ if (connectors.length === 0) {
2451
+ console.log(" No connectors in this project.");
2452
+ console.log(`${green2} Create one with: ardent connector create postgresql <url>${reset2}`);
2453
+ if (otherProjectsSummary) {
2454
+ console.log("");
2455
+ printOtherProjectsHint(otherProjectsSummary, false, dim2, reset2);
2456
+ }
2457
+ return;
2458
+ }
2459
+ const selectedConnector = fromCache ? void 0 : reconcileSelectedConnector(connectors);
2460
+ const currentConnectorId = selectedConnector?.id ?? getConfig("currentConnectorId");
2336
2461
  let enginePendingCount = 0;
2337
2462
  for (const connector of connectors) {
2338
2463
  const isCurrent = connector.id === currentConnectorId;
@@ -2368,6 +2493,7 @@ async function listAction2() {
2368
2493
  connector_count: connectors.length
2369
2494
  });
2370
2495
  }
2496
+ printOtherProjectsHint(otherProjectsSummary, true, dim2, reset2);
2371
2497
  }
2372
2498
 
2373
2499
  // src/commands/connector/delete.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ardent-cli",
3
- "version": "0.0.45",
3
+ "version": "0.0.46",
4
4
  "description": "Git for Data infrastructure",
5
5
  "type": "module",
6
6
  "bin": {