@google-cloud/kcmd 0.2.0 → 0.2.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.
@@ -35,7 +35,7 @@ var cac = __toESM(require("cac"));
35
35
  // package.json
36
36
  var package_default = {
37
37
  name: "@google-cloud/kcmd",
38
- version: "0.2.0",
38
+ version: "0.2.1",
39
39
  description: "Google Knowledge Catalog Metadata as Code Library, CLI and MCP Server",
40
40
  repository: {
41
41
  type: "git",
@@ -3743,12 +3743,6 @@ function normalizeModelSugars(m, extended) {
3743
3743
  delete m.entities;
3744
3744
  }
3745
3745
  }
3746
- function deploymentTargetsExtension(targets) {
3747
- return {
3748
- vendorName: GOOGLE_VENDOR,
3749
- data: JSON.stringify({ deploymentTargets: targets })
3750
- };
3751
- }
3752
3746
  function isGoogleVendor(vendorName) {
3753
3747
  return typeof vendorName === "string" && vendorName.toUpperCase() === GOOGLE_VENDOR;
3754
3748
  }
@@ -4359,13 +4353,6 @@ function convertModel(m, version, opts, warnings) {
4359
4353
  model.aiContext = ai;
4360
4354
  if (block.kept)
4361
4355
  model.customExtensions = block.kept;
4362
- const foldedTargets = version === GOOGLE_VERSION ? deployments.map((d) => d.target) : [];
4363
- if (foldedTargets.length) {
4364
- model.customExtensions = [
4365
- ...model.customExtensions ?? [],
4366
- deploymentTargetsExtension(foldedTargets)
4367
- ];
4368
- }
4369
4356
  return model;
4370
4357
  }
4371
4358
  function convertDataset(ds, block, version, opts, warnings, dialect) {
@@ -8947,7 +8934,7 @@ async function initOkfWorkspace(ctx, manifest2, options = {}) {
8947
8934
  const hasConceptFiles = fs8.readdirSync(BUNDLE_DIR).some((f) => f.endsWith(".md"));
8948
8935
  if (!fs8.existsSync(indexPath) && !fs8.existsSync(agentsPath) && !hasConceptFiles) {
8949
8936
  fs8.writeFileSync(indexPath, `---
8950
- okf_version: "0.3"
8937
+ okf_version: "0.2"
8951
8938
  ---
8952
8939
  # ${groupId}
8953
8940
  `, "utf8");
@@ -8957,225 +8944,7 @@ okf_version: "0.3"
8957
8944
 
8958
8945
  // src/libts/semantic/osi_converter.ts
8959
8946
  var yaml12 = __toESM(require("yaml"));
8960
-
8961
- // src/libts/semantic/deployment_target.ts
8962
8947
  var GOOGLE_VENDOR2 = "GOOGLE";
8963
- var BQ_GRAPH_TARGET = /^\/\/bigquery\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/datasets\/([A-Za-z0-9_-]+)\/propertyGraphs\/([A-Za-z0-9_-]+)$/;
8964
- var SPANNER_GRAPH_TARGET = /^\/\/spanner\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/instances\/([A-Za-z0-9_-]+)\/databases\/([A-Za-z0-9_-]+)\/graphs\/([A-Za-z0-9_-]+)$/;
8965
- var SPANNER_PROPERTY_GRAPH = /^(spanner\.googleapis\.com\/projects\/[A-Za-z0-9_-]+\/instances\/[A-Za-z0-9_-]+\/databases\/[A-Za-z0-9_-]+)\/propertyGraphs\/([A-Za-z0-9_-]+)$/;
8966
- var ALLOYDB_TARGET = /^\/\/alloydb\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/locations\/([A-Za-z0-9_-]+)\/clusters\/([A-Za-z0-9_-]+)\/databases\/([A-Za-z0-9_-]+)$/;
8967
- var LEGACY_ALLOYDB_TARGET = /^\/\/alloydb\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/locations\/([A-Za-z0-9_-]+)\/clusters\/([A-Za-z0-9_-]+)\/instances\/([A-Za-z0-9_-]+)\/databases\/([A-Za-z0-9_-]+)$/;
8968
- var BQ_GRAPH_FORM = "bigquery.googleapis.com/projects/<p>/datasets/<d>/propertyGraphs/<g>";
8969
- var SPANNER_GRAPH_FORM = "spanner.googleapis.com/projects/<p>/instances/<i>/databases/<db>/graphs/<g>";
8970
- var ALLOYDB_FORM = "alloydb.googleapis.com/projects/<p>/locations/<l>/clusters/<c>/databases/<db>";
8971
- var CATALOG_PREFIXES = [
8972
- ["bigquery:graph:", BQ_GRAPH_FORM],
8973
- ["bigquery:propertygraph:", BQ_GRAPH_FORM],
8974
- ["spanner:graph:", SPANNER_GRAPH_FORM],
8975
- ["alloydb:", ALLOYDB_FORM]
8976
- ];
8977
- function parseTarget(uri) {
8978
- if (uri.startsWith("//"))
8979
- return;
8980
- const resource = asResourceUri(uri);
8981
- const bq = resource.match(BQ_GRAPH_TARGET);
8982
- if (bq) {
8983
- return {
8984
- engine: "bigquery",
8985
- coordinates: { project: bq[1], dataset: bq[2], graphName: bq[3], uri }
8986
- };
8987
- }
8988
- const sp = resource.match(SPANNER_GRAPH_TARGET);
8989
- if (sp) {
8990
- return {
8991
- engine: "spanner",
8992
- coordinates: {
8993
- project: sp[1],
8994
- instance: sp[2],
8995
- database: sp[3],
8996
- graphName: sp[4],
8997
- uri
8998
- }
8999
- };
9000
- }
9001
- const ad = resource.match(ALLOYDB_TARGET);
9002
- if (ad) {
9003
- return {
9004
- engine: "alloydb",
9005
- coordinates: {
9006
- project: ad[1],
9007
- location: ad[2],
9008
- cluster: ad[3],
9009
- database: ad[4],
9010
- uri
9011
- }
9012
- };
9013
- }
9014
- return;
9015
- }
9016
- function clusterUri(legacy) {
9017
- return `//alloydb.googleapis.com/projects/${legacy[1]}/locations/${legacy[2]}/clusters/${legacy[3]}/databases/${legacy[5]}`;
9018
- }
9019
- function deploymentTargetError(model, d) {
9020
- return targetError(`Model '${model.name}': deployment '${d.name}' targets '${d.target}'`, d.target);
9021
- }
9022
- function googleBlockTargetError(model, target) {
9023
- return targetError(`Model '${model.name}': the GOOGLE custom_extension's ` + `'deploymentTargets' lists '${target}'`, target);
9024
- }
9025
- function spannerGraphName(target) {
9026
- const m = asResourceName(target).match(SPANNER_PROPERTY_GRAPH);
9027
- return m ? `${m[1]}/graphs/${m[2]}` : undefined;
9028
- }
9029
- function targetProblem(at2, target) {
9030
- return parseTarget(target) ? undefined : targetError(at2, target);
9031
- }
9032
- function targetError(at2, target) {
9033
- const legacy = asResourceUri(target).match(LEGACY_ALLOYDB_TARGET);
9034
- if (legacy) {
9035
- return `${at2}, the instance-based AlloyDB name, which names no real ` + `resource. An AlloyDB database belongs to its cluster; write '${asResourceName(clusterUri(legacy))}'.`;
9036
- }
9037
- const name = asResourceName(target);
9038
- if (name !== target && parseTarget(name)) {
9039
- return `${at2}, which is ${resourceUriFix(target)}.`;
9040
- }
9041
- const graph = spannerGraphName(target);
9042
- if (graph) {
9043
- return `${at2}, which names a Spanner graph by 'propertyGraphs'; write ` + `its Dataplex name: '${graph}'.`;
9044
- }
9045
- const form = CATALOG_PREFIXES.find(([prefix]) => target.startsWith(prefix))?.[1];
9046
- if (form) {
9047
- return `${at2}, which is a Knowledge Catalog name. Write the GCP resource ` + `name instead: '${form}'.`;
9048
- }
9049
- return `${at2}, which is not a BigQuery graph, a Spanner graph or an ` + `AlloyDB database. Write a GCP resource name: '${BQ_GRAPH_FORM}', '${SPANNER_GRAPH_FORM}' or '${ALLOYDB_FORM}'.`;
9050
- }
9051
- function parseDeploymentSelection(raw) {
9052
- if (raw === undefined || typeof raw === "boolean")
9053
- return;
9054
- const noNames = `--deployment names no deployment; give one or more ` + `deployment names, comma-separated.`;
9055
- const names = [];
9056
- const parts = Array.isArray(raw) ? raw : [raw];
9057
- for (const part of parts) {
9058
- const pieces = typeof part === "boolean" ? [] : String(part).split(",").map((p) => p.trim()).filter((p) => p);
9059
- if (!pieces.length)
9060
- throw new Error(noNames);
9061
- for (const name of pieces) {
9062
- if (!names.includes(name))
9063
- names.push(name);
9064
- }
9065
- }
9066
- if (!names.length)
9067
- throw new Error(noNames);
9068
- return names;
9069
- }
9070
- function resolveModelDeployments(model, selected) {
9071
- const declared = model.deployments ?? [];
9072
- let chosen = declared;
9073
- if (selected !== undefined) {
9074
- const byName = new Map(declared.map((d) => [d.name, d]));
9075
- chosen = [];
9076
- for (const name of selected) {
9077
- const d = byName.get(name);
9078
- if (!d) {
9079
- const available = declared.length ? declared.map((x) => x.name).join(", ") : "none";
9080
- return {
9081
- error: `Model '${model.name}' has no deployment named '${name}' (available: ${available}).`
9082
- };
9083
- }
9084
- chosen.push(d);
9085
- }
9086
- }
9087
- const deployments = [];
9088
- for (const d of chosen) {
9089
- const parsed = parseTarget(d.target);
9090
- if (!parsed)
9091
- return { error: deploymentTargetError(model, d) };
9092
- deployments.push({
9093
- ...parsed,
9094
- name: d.name,
9095
- profile: d.profile ?? DEFAULT_PROFILE
9096
- });
9097
- }
9098
- return { deployments };
9099
- }
9100
- var targetsCache = new WeakMap;
9101
- function extensionTargets(model) {
9102
- return googleBlockTargets(model.customExtensions, model.name);
9103
- }
9104
- function googleBlockTargets(exts, modelName) {
9105
- const uris = [];
9106
- for (const ext of exts ?? []) {
9107
- if (ext.vendorName !== GOOGLE_VENDOR2) {
9108
- continue;
9109
- }
9110
- let data;
9111
- try {
9112
- data = JSON.parse(ext.data);
9113
- } catch {
9114
- throw new Error(`Model '${modelName}': GOOGLE custom_extension 'data' is not valid JSON.`);
9115
- }
9116
- const list = data?.deploymentTargets;
9117
- if (!Array.isArray(list)) {
9118
- continue;
9119
- }
9120
- for (const uri of list) {
9121
- if (typeof uri !== "string") {
9122
- uris.push(JSON.stringify(uri));
9123
- } else if (uri.trim() !== "") {
9124
- uris.push(uri.trim());
9125
- }
9126
- }
9127
- }
9128
- return uris;
9129
- }
9130
- function unnamedDeployments(targets) {
9131
- const seen = new Set;
9132
- const kept = targets.map((t) => t.trim()).filter((t) => {
9133
- if (t === "" || seen.has(t))
9134
- return false;
9135
- seen.add(t);
9136
- return true;
9137
- });
9138
- return kept.map((target, i) => ({
9139
- name: kept.length === 1 ? "default" : `deployment_${i + 1}`,
9140
- target
9141
- }));
9142
- }
9143
- function googleDeploymentTargets(model) {
9144
- const cached = targetsCache.get(model);
9145
- if (cached)
9146
- return cached;
9147
- const uris = model.deployments !== undefined ? model.deployments.map((d) => d.target) : extensionTargets(model);
9148
- const bigQuery = [];
9149
- const spanner = [];
9150
- const alloyDb = [];
9151
- const malformed = [];
9152
- for (const uri of uris) {
9153
- const parsed = parseTarget(uri);
9154
- if (parsed?.engine === "bigquery") {
9155
- bigQuery.push(parsed.coordinates);
9156
- } else if (parsed?.engine === "spanner") {
9157
- spanner.push(parsed.coordinates);
9158
- } else if (parsed?.engine === "alloydb") {
9159
- alloyDb.push(parsed.coordinates);
9160
- } else {
9161
- malformed.push(uri);
9162
- }
9163
- }
9164
- const result = { uris, bigQuery, spanner, alloyDb, malformed };
9165
- targetsCache.set(model, result);
9166
- return result;
9167
- }
9168
- function bigQueryGraphTargets(model) {
9169
- const { bigQuery, malformed } = googleDeploymentTargets(model);
9170
- return { targets: bigQuery, malformed };
9171
- }
9172
- function spannerGraphTargets(model) {
9173
- const { spanner, malformed } = googleDeploymentTargets(model);
9174
- return { targets: spanner, malformed };
9175
- }
9176
-
9177
- // src/libts/semantic/osi_converter.ts
9178
- var GOOGLE_VENDOR3 = "GOOGLE";
9179
8948
  var CANONICAL_DIALECT = "BIGQUERY";
9180
8949
  var ALLOWED_DIALECT_SET = new Set(ALLOWED_DIALECTS);
9181
8950
  function serializeModelFiles(model, opts = {}) {
@@ -9316,7 +9085,7 @@ function modelDoc(model, warnings, logical, version) {
9316
9085
  target: d.target,
9317
9086
  profile: d.profile
9318
9087
  }));
9319
- const deployments = placement.model.deployments === "native" ? nonEmpty(namedDeployments.length ? namedDeployments : deploymentsFromGoogleExtensions(model)) : undefined;
9088
+ const deployments = placement.model.deployments === "native" ? nonEmpty(namedDeployments) : undefined;
9320
9089
  const blockProfiles = placement.model.profiles === "block" ? nonEmpty(profileDocuments(model, warnings).map((p) => p.doc)) : undefined;
9321
9090
  const blockDeployments = placement.model.deployments === "block" ? nonEmpty(namedDeployments) : undefined;
9322
9091
  const blockPayload = compact({
@@ -9336,7 +9105,7 @@ function modelDoc(model, warnings, logical, version) {
9336
9105
  metrics: nonEmpty((model.metrics ?? []).map((m) => metricDoc(m, warnings, version))),
9337
9106
  actions: placement.model.actions === "native" ? nonEmpty((model.actions ?? []).map((a) => actionDoc(a, warnings, emittedFields))) : undefined,
9338
9107
  constraints: placement.model.constraints === "native" ? nonEmpty((model.constraints ?? []).map((c) => constraintDoc(c))) : undefined,
9339
- custom_extensions: customExtensionsDoc(model.customExtensions, `model '${model.name}'`, isGoogleVersion, true, warnings, false, blockPayload)
9108
+ custom_extensions: customExtensionsDoc(model.customExtensions, `model '${model.name}'`, isGoogleVersion, warnings, false, blockPayload)
9340
9109
  });
9341
9110
  }
9342
9111
  function warnGoogleOnlyContent(model, warnings) {
@@ -9375,7 +9144,7 @@ function datasetDoc(entity, warnings, logical, version) {
9375
9144
  description: entity.description,
9376
9145
  ai_context: aiContextDoc(entity.aiContext, isGoogleVersion),
9377
9146
  fields: nonEmpty((entity.fields ?? []).map((f) => fieldDoc(f, warnings, logical || isGoogleVersion && !!entity.abstract, isGoogleVersion))),
9378
- custom_extensions: customExtensionsDoc(entity.customExtensions, `entity '${entity.name}'`, isGoogleVersion, false, warnings, false, blockPayload)
9147
+ custom_extensions: customExtensionsDoc(entity.customExtensions, `entity '${entity.name}'`, isGoogleVersion, warnings, false, blockPayload)
9379
9148
  });
9380
9149
  }
9381
9150
  function datatypeDoc(type) {
@@ -9394,7 +9163,7 @@ function fieldDoc(field, warnings, logical, isGoogleVersion) {
9394
9163
  dimension: dimensionDoc(field),
9395
9164
  description: field.description,
9396
9165
  ai_context: aiContextDoc(field.aiContext, isGoogleVersion),
9397
- custom_extensions: customExtensionsDoc(field.customExtensions, `field '${field.name}'`, isGoogleVersion, false, warnings, true)
9166
+ custom_extensions: customExtensionsDoc(field.customExtensions, `field '${field.name}'`, isGoogleVersion, warnings, true)
9398
9167
  });
9399
9168
  }
9400
9169
  function metricDoc(metric, warnings, version) {
@@ -9413,7 +9182,7 @@ function metricDoc(metric, warnings, version) {
9413
9182
  datatype: datatypeDoc(metric.type),
9414
9183
  description: metric.description,
9415
9184
  ai_context: aiContextDoc(metric.aiContext, isGoogleVersion),
9416
- custom_extensions: customExtensionsDoc(metric.customExtensions, `metric '${metric.name}'`, isGoogleVersion, false, warnings, false, blockPayload)
9185
+ custom_extensions: customExtensionsDoc(metric.customExtensions, `metric '${metric.name}'`, isGoogleVersion, warnings, false, blockPayload)
9417
9186
  });
9418
9187
  }
9419
9188
  function emitted(model) {
@@ -9513,7 +9282,7 @@ function relationshipDoc(rel, warnings, version) {
9513
9282
  to_columns: nonEmpty(rel.destination.columns),
9514
9283
  description: placement.relationship.description === "native" ? description : undefined,
9515
9284
  ai_context: aiContextDoc(rel.aiContext, isGoogleVersion),
9516
- custom_extensions: customExtensionsDoc(rel.customExtensions, `relationship '${rel.name}'`, isGoogleVersion, false, warnings, false, blockPayload)
9285
+ custom_extensions: customExtensionsDoc(rel.customExtensions, `relationship '${rel.name}'`, isGoogleVersion, warnings, false, blockPayload)
9517
9286
  });
9518
9287
  }
9519
9288
  function expressionDoc(item, isGoogleVersion, where, warnings) {
@@ -9579,11 +9348,7 @@ function aiContextDoc(ai, isGoogleVersion) {
9579
9348
  }
9580
9349
  return isGoogleVersion ? { ...base, custom } : { ...custom, ...base };
9581
9350
  }
9582
- function deploymentsFromGoogleExtensions(model) {
9583
- const targets = (model.customExtensions ?? []).flatMap((ext) => blockTargets(ext, model.name));
9584
- return unnamedDeployments(targets);
9585
- }
9586
- function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, isField = false, blockPayload) {
9351
+ function customExtensionsDoc(exts, where, isGoogleVersion, warnings, isField = false, blockPayload) {
9587
9352
  const payload = blockPayload ? { ...blockPayload } : {};
9588
9353
  if (!exts?.length && Object.keys(payload).length === 0)
9589
9354
  return;
@@ -9591,13 +9356,10 @@ function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, is
9591
9356
  const kept2 = [];
9592
9357
  let droppedGoogle2 = 0;
9593
9358
  for (const e of exts ?? []) {
9594
- if (e.vendorName !== GOOGLE_VENDOR3) {
9359
+ if (e.vendorName !== GOOGLE_VENDOR2) {
9595
9360
  kept2.push({ vendor_name: e.vendorName, data: e.data });
9596
9361
  continue;
9597
9362
  }
9598
- if (isModel && payload.deployments !== undefined && isDeploymentTargetsBlock(e)) {
9599
- continue;
9600
- }
9601
9363
  droppedGoogle2 += 1;
9602
9364
  }
9603
9365
  if (droppedGoogle2 > 0) {
@@ -9605,7 +9367,7 @@ function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, is
9605
9367
  }
9606
9368
  if (Object.keys(payload).length > 0) {
9607
9369
  kept2.push({
9608
- vendor_name: GOOGLE_VENDOR3,
9370
+ vendor_name: GOOGLE_VENDOR2,
9609
9371
  data: JSON.stringify(payload)
9610
9372
  });
9611
9373
  }
@@ -9614,12 +9376,10 @@ function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, is
9614
9376
  const kept = [];
9615
9377
  let droppedGoogle = 0;
9616
9378
  for (const ext of exts ?? []) {
9617
- if (ext.vendorName !== GOOGLE_VENDOR3) {
9379
+ if (ext.vendorName !== GOOGLE_VENDOR2) {
9618
9380
  kept.push({ vendor_name: ext.vendorName, data: ext.data });
9619
9381
  continue;
9620
9382
  }
9621
- if (isModel && blockTargets(ext, "").length > 0)
9622
- continue;
9623
9383
  droppedGoogle += 1;
9624
9384
  }
9625
9385
  if (droppedGoogle > 0) {
@@ -9627,21 +9387,6 @@ function customExtensionsDoc(exts, where, isGoogleVersion, isModel, warnings, is
9627
9387
  }
9628
9388
  return nonEmpty(kept);
9629
9389
  }
9630
- function isDeploymentTargetsBlock(e) {
9631
- try {
9632
- const d = JSON.parse(e.data);
9633
- return !!d && typeof d === "object" && Object.hasOwn(d, "deploymentTargets");
9634
- } catch {
9635
- return false;
9636
- }
9637
- }
9638
- function blockTargets(ext, modelName) {
9639
- try {
9640
- return googleBlockTargets([ext], modelName);
9641
- } catch {
9642
- return [];
9643
- }
9644
- }
9645
9390
  function compact(obj) {
9646
9391
  const out = {};
9647
9392
  for (const [k, v] of Object.entries(obj)) {
@@ -10921,6 +10666,189 @@ function quote(s) {
10921
10666
  return `"${escaped}"`;
10922
10667
  }
10923
10668
 
10669
+ // src/libts/semantic/deployment_target.ts
10670
+ var BQ_GRAPH_TARGET = /^\/\/bigquery\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/datasets\/([A-Za-z0-9_-]+)\/propertyGraphs\/([A-Za-z0-9_-]+)$/;
10671
+ var SPANNER_GRAPH_TARGET = /^\/\/spanner\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/instances\/([A-Za-z0-9_-]+)\/databases\/([A-Za-z0-9_-]+)\/graphs\/([A-Za-z0-9_-]+)$/;
10672
+ var SPANNER_PROPERTY_GRAPH = /^(spanner\.googleapis\.com\/projects\/[A-Za-z0-9_-]+\/instances\/[A-Za-z0-9_-]+\/databases\/[A-Za-z0-9_-]+)\/propertyGraphs\/([A-Za-z0-9_-]+)$/;
10673
+ var ALLOYDB_TARGET = /^\/\/alloydb\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/locations\/([A-Za-z0-9_-]+)\/clusters\/([A-Za-z0-9_-]+)\/databases\/([A-Za-z0-9_-]+)$/;
10674
+ var LEGACY_ALLOYDB_TARGET = /^\/\/alloydb\.googleapis\.com\/projects\/([A-Za-z0-9_-]+)\/locations\/([A-Za-z0-9_-]+)\/clusters\/([A-Za-z0-9_-]+)\/instances\/([A-Za-z0-9_-]+)\/databases\/([A-Za-z0-9_-]+)$/;
10675
+ var BQ_GRAPH_FORM = "bigquery.googleapis.com/projects/<p>/datasets/<d>/propertyGraphs/<g>";
10676
+ var SPANNER_GRAPH_FORM = "spanner.googleapis.com/projects/<p>/instances/<i>/databases/<db>/graphs/<g>";
10677
+ var ALLOYDB_FORM = "alloydb.googleapis.com/projects/<p>/locations/<l>/clusters/<c>/databases/<db>";
10678
+ var CATALOG_PREFIXES = [
10679
+ ["bigquery:graph:", BQ_GRAPH_FORM],
10680
+ ["bigquery:propertygraph:", BQ_GRAPH_FORM],
10681
+ ["spanner:graph:", SPANNER_GRAPH_FORM],
10682
+ ["alloydb:", ALLOYDB_FORM]
10683
+ ];
10684
+ function parseTarget(uri) {
10685
+ if (uri.startsWith("//"))
10686
+ return;
10687
+ const resource = asResourceUri(uri);
10688
+ const bq = resource.match(BQ_GRAPH_TARGET);
10689
+ if (bq) {
10690
+ return {
10691
+ engine: "bigquery",
10692
+ coordinates: { project: bq[1], dataset: bq[2], graphName: bq[3], uri }
10693
+ };
10694
+ }
10695
+ const sp = resource.match(SPANNER_GRAPH_TARGET);
10696
+ if (sp) {
10697
+ return {
10698
+ engine: "spanner",
10699
+ coordinates: {
10700
+ project: sp[1],
10701
+ instance: sp[2],
10702
+ database: sp[3],
10703
+ graphName: sp[4],
10704
+ uri
10705
+ }
10706
+ };
10707
+ }
10708
+ const ad = resource.match(ALLOYDB_TARGET);
10709
+ if (ad) {
10710
+ return {
10711
+ engine: "alloydb",
10712
+ coordinates: {
10713
+ project: ad[1],
10714
+ location: ad[2],
10715
+ cluster: ad[3],
10716
+ database: ad[4],
10717
+ uri
10718
+ }
10719
+ };
10720
+ }
10721
+ return;
10722
+ }
10723
+ function clusterUri(legacy) {
10724
+ return `//alloydb.googleapis.com/projects/${legacy[1]}/locations/${legacy[2]}/clusters/${legacy[3]}/databases/${legacy[5]}`;
10725
+ }
10726
+ function deploymentTargetError(model, d) {
10727
+ return targetError(`Model '${model.name}': deployment '${d.name}' targets '${d.target}'`, d.target);
10728
+ }
10729
+ function spannerGraphName(target) {
10730
+ const m = asResourceName(target).match(SPANNER_PROPERTY_GRAPH);
10731
+ return m ? `${m[1]}/graphs/${m[2]}` : undefined;
10732
+ }
10733
+ function targetProblem(at2, target) {
10734
+ return parseTarget(target) ? undefined : targetError(at2, target);
10735
+ }
10736
+ function targetError(at2, target) {
10737
+ const legacy = asResourceUri(target).match(LEGACY_ALLOYDB_TARGET);
10738
+ if (legacy) {
10739
+ return `${at2}, the instance-based AlloyDB name, which names no real ` + `resource. An AlloyDB database belongs to its cluster; write '${asResourceName(clusterUri(legacy))}'.`;
10740
+ }
10741
+ const name = asResourceName(target);
10742
+ if (name !== target && parseTarget(name)) {
10743
+ return `${at2}, which is ${resourceUriFix(target)}.`;
10744
+ }
10745
+ const graph = spannerGraphName(target);
10746
+ if (graph) {
10747
+ return `${at2}, which names a Spanner graph by 'propertyGraphs'; write ` + `its Dataplex name: '${graph}'.`;
10748
+ }
10749
+ const form = CATALOG_PREFIXES.find(([prefix]) => target.startsWith(prefix))?.[1];
10750
+ if (form) {
10751
+ return `${at2}, which is a Knowledge Catalog name. Write the GCP resource ` + `name instead: '${form}'.`;
10752
+ }
10753
+ return `${at2}, which is not a BigQuery graph, a Spanner graph or an ` + `AlloyDB database. Write a GCP resource name: '${BQ_GRAPH_FORM}', '${SPANNER_GRAPH_FORM}' or '${ALLOYDB_FORM}'.`;
10754
+ }
10755
+ function parseDeploymentSelection(raw) {
10756
+ if (raw === undefined || typeof raw === "boolean")
10757
+ return;
10758
+ const noNames = `--deployment names no deployment; give one or more ` + `deployment names, comma-separated.`;
10759
+ const names = [];
10760
+ const parts = Array.isArray(raw) ? raw : [raw];
10761
+ for (const part of parts) {
10762
+ const pieces = typeof part === "boolean" ? [] : String(part).split(",").map((p) => p.trim()).filter((p) => p);
10763
+ if (!pieces.length)
10764
+ throw new Error(noNames);
10765
+ for (const name of pieces) {
10766
+ if (!names.includes(name))
10767
+ names.push(name);
10768
+ }
10769
+ }
10770
+ if (!names.length)
10771
+ throw new Error(noNames);
10772
+ return names;
10773
+ }
10774
+ function resolveModelDeployments(model, selected) {
10775
+ const declared = model.deployments ?? [];
10776
+ let chosen = declared;
10777
+ if (selected !== undefined) {
10778
+ const byName = new Map(declared.map((d) => [d.name, d]));
10779
+ chosen = [];
10780
+ for (const name of selected) {
10781
+ const d = byName.get(name);
10782
+ if (!d) {
10783
+ const available = declared.length ? declared.map((x) => x.name).join(", ") : "none";
10784
+ return {
10785
+ error: `Model '${model.name}' has no deployment named '${name}' (available: ${available}).`
10786
+ };
10787
+ }
10788
+ chosen.push(d);
10789
+ }
10790
+ }
10791
+ const deployments = [];
10792
+ for (const d of chosen) {
10793
+ const parsed = parseTarget(d.target);
10794
+ if (!parsed)
10795
+ return { error: deploymentTargetError(model, d) };
10796
+ deployments.push({
10797
+ ...parsed,
10798
+ name: d.name,
10799
+ profile: d.profile ?? DEFAULT_PROFILE
10800
+ });
10801
+ }
10802
+ return { deployments };
10803
+ }
10804
+ var targetsCache = new WeakMap;
10805
+ function unnamedDeployments(targets) {
10806
+ const seen = new Set;
10807
+ const kept = targets.map((t) => t.trim()).filter((t) => {
10808
+ if (t === "" || seen.has(t))
10809
+ return false;
10810
+ seen.add(t);
10811
+ return true;
10812
+ });
10813
+ return kept.map((target, i) => ({
10814
+ name: kept.length === 1 ? "default" : `deployment_${i + 1}`,
10815
+ target
10816
+ }));
10817
+ }
10818
+ function googleDeploymentTargets(model) {
10819
+ const cached = targetsCache.get(model);
10820
+ if (cached)
10821
+ return cached;
10822
+ const uris = (model.deployments ?? []).map((d) => d.target);
10823
+ const bigQuery = [];
10824
+ const spanner = [];
10825
+ const alloyDb = [];
10826
+ const malformed = [];
10827
+ for (const uri of uris) {
10828
+ const parsed = parseTarget(uri);
10829
+ if (parsed?.engine === "bigquery") {
10830
+ bigQuery.push(parsed.coordinates);
10831
+ } else if (parsed?.engine === "spanner") {
10832
+ spanner.push(parsed.coordinates);
10833
+ } else if (parsed?.engine === "alloydb") {
10834
+ alloyDb.push(parsed.coordinates);
10835
+ } else {
10836
+ malformed.push(uri);
10837
+ }
10838
+ }
10839
+ const result = { uris, bigQuery, spanner, alloyDb, malformed };
10840
+ targetsCache.set(model, result);
10841
+ return result;
10842
+ }
10843
+ function bigQueryGraphTargets(model) {
10844
+ const { bigQuery, malformed } = googleDeploymentTargets(model);
10845
+ return { targets: bigQuery, malformed };
10846
+ }
10847
+ function spannerGraphTargets(model) {
10848
+ const { spanner, malformed } = googleDeploymentTargets(model);
10849
+ return { targets: spanner, malformed };
10850
+ }
10851
+
10924
10852
  // src/libts/semantic/deploy_bigquery.ts
10925
10853
  var MAX_QUERY_POLLS = 30;
10926
10854
  var POLL_BACKOFF_MS = 1000;
@@ -11004,18 +10932,12 @@ async function deployBigQuery(models, ctx, options = {}) {
11004
10932
  };
11005
10933
  for (const { document, model } of models) {
11006
10934
  modelsSeen++;
11007
- let targets;
11008
- let malformed;
11009
- try {
11010
- ({ targets, malformed } = bigQueryGraphTargets(model));
11011
- } catch (err) {
11012
- return fail(`Model '${model.name}' (${document}): ${err.message || err}`);
11013
- }
10935
+ const { targets, malformed } = bigQueryGraphTargets(model);
11014
10936
  if (!targets.length) {
11015
10937
  if (malformed.length) {
11016
- return fail(`Model '${model.name}' (${document}) declares BigQuery Graph ` + `deploymentTarget(s) that could not be parsed: ` + `${malformed.join(", ")}. Expected ${BQ_GRAPH_FORM}.`);
10938
+ return fail(`Model '${model.name}' (${document}) declares BigQuery Graph ` + `deployment target(s) that could not be parsed: ` + `${malformed.join(", ")}. Expected ${BQ_GRAPH_FORM}.`);
11017
10939
  }
11018
- return fail(`Model '${model.name}' (${document}) declares no BigQuery Graph ` + `deploymentTarget in a GOOGLE custom_extension; nothing to deploy.`);
10940
+ return fail(`Model '${model.name}' (${document}) declares no BigQuery Graph ` + `deployment target; nothing to deploy.`);
11019
10941
  }
11020
10942
  for (const bad of malformed) {
11021
10943
  warnings.push(`[${model.name}] ignoring unparseable BigQuery Graph target: ${bad}`);
@@ -11581,13 +11503,15 @@ async function provisionOne(cat, dest, type) {
11581
11503
  const aspectLabel = `aspect type '${type.id}'`;
11582
11504
  const aspect = await cat.createAspectType(home.project, home.location, type.id, type.aspectType);
11583
11505
  if (aspect.status === 409) {
11584
- const upd = await cat.updateAspectType(home.project, home.location, type.id, type.aspectType, ["description", "display_name", "metadata_template"]);
11585
- const reason = upd.status !== 200 ? `${upd.message || upd.status}` : await cat.awaitOperation(upd.result, `updating ${aspectLabel}`);
11506
+ const upd = await cat.updateAspectType(home.project, home.location, type.id, type.aspectType, ["description", "display_name", "metadata_template"]).catch((err) => ({ status: 0, message: String(err?.message || err) }));
11507
+ const reason = upd.status !== 200 ? `${upd.message || upd.status}` : await cat.awaitOperation(upd.result, "the update operation").catch((err) => String(err?.message || err));
11586
11508
  if (upd.status === 403) {
11587
11509
  warnings.push(`no permission to update the existing ${aspectLabel} in project ` + `'${home.project}' (${reason}); left it unchanged`);
11588
- } else if (reason) {
11510
+ } else if (reason && /Backwards incompatible template change/i.test(reason)) {
11589
11511
  const resource = customAspectTypeName(type.id, dest);
11590
- warnings.push(`left the existing ${aspectLabel} unchanged (${reason}). It is ` + `${resource}, and Dataplex will not patch it into the template ` + `this build states -- a template field that was removed or ` + `renumbered is a backwards-incompatible change and is refused. A ` + `push will fail wherever the two disagree. To move it forward, ` + `delete it and re-run this command: gcloud dataplex aspect-types ` + `delete ${type.id} --project=${home.project} ` + `--location=${home.location}. Deleting takes the aspects of every ` + `entry already published with this type, so re-push those models ` + `afterwards.`);
11512
+ warnings.push(`left the existing ${aspectLabel} unchanged (${reason}). It is ` + `${resource}, and Dataplex will not patch it into the template ` + `this build states -- a template field that was removed or ` + `renumbered is a backwards-incompatible change and is refused. A ` + `push will fail wherever the two disagree. To move it forward, ` + `delete the entry type and then the aspect type, and re-run this ` + `command: gcloud dataplex entry-types delete ${type.id} ` + `--project=${home.project} --location=${home.location} && gcloud ` + `dataplex aspect-types delete ${type.id} --project=${home.project} ` + `--location=${home.location}. Deleting the entry type also deletes ` + `every entry of that type in the project, so push the models that ` + `had them again afterwards.`);
11513
+ } else if (reason) {
11514
+ warnings.push(`could not update the existing ${aspectLabel} in project ` + `'${home.project}' (${reason}). Run this command again in a minute ` + `to retry the update.`);
11591
11515
  }
11592
11516
  } else if (aspect.status !== 200) {
11593
11517
  const refused2 = denial(aspectLabel, aspect.status, aspect.message);
@@ -12754,21 +12678,6 @@ function sleep3(ms) {
12754
12678
  }
12755
12679
  async function preflightKnowledgeCatalog(models, ctx, opts) {
12756
12680
  const emit = emitModels(models, opts);
12757
- if (emit.error) {
12758
- return {
12759
- result: {
12760
- success: false,
12761
- details: emit.error,
12762
- warnings: emit.warnings,
12763
- created: 0,
12764
- updated: 0,
12765
- deleted: 0,
12766
- linked: 0,
12767
- unlinked: 0,
12768
- plan: []
12769
- }
12770
- };
12771
- }
12772
12681
  return preflightEmittedModels(emit.emitted, emit.warnings, ctx, opts);
12773
12682
  }
12774
12683
  async function applyKnowledgeCatalog(prepared, ctx, opts) {
@@ -12868,7 +12777,7 @@ var MAX_ASPECT_DATA_BYTES = 120 * 1024;
12868
12777
  var MAX_SCHEMA_ASPECT_DATA_BYTES = 2 * 1024 * 1024;
12869
12778
  function checkCatalogAspects(models, opts) {
12870
12779
  const emit = emitModels(models, opts);
12871
- const error = emit.error ?? emit.emitted.map(emittedAspectsError).find((e) => e);
12780
+ const error = emit.emitted.map(emittedAspectsError).find((e) => e);
12872
12781
  return { error, warnings: emit.warnings };
12873
12782
  }
12874
12783
  function emittedAspectsError(model) {
@@ -12928,26 +12837,17 @@ async function checkCustomTypes(cat, dest, typeIds) {
12928
12837
  function emitModels(models, opts) {
12929
12838
  const emitted2 = [];
12930
12839
  const warnings = [];
12931
- for (const { document, model } of models) {
12932
- let resources;
12933
- try {
12934
- resources = generateCatalogResources(model, {
12935
- project: opts.project,
12936
- location: opts.location,
12937
- entryGroup: opts.entryGroup,
12938
- systemTypeProject: opts.systemTypeProject,
12939
- systemTypeLocation: opts.systemTypeLocation,
12940
- emitExpressions: opts.emitExpressions,
12941
- v2Aspects: opts.v2Aspects,
12942
- overviews: opts.overviews
12943
- });
12944
- } catch (err) {
12945
- return {
12946
- emitted: emitted2,
12947
- warnings,
12948
- error: `Model '${model.name}' (${document}): ${err.message || err}`
12949
- };
12950
- }
12840
+ for (const { model } of models) {
12841
+ const resources = generateCatalogResources(model, {
12842
+ project: opts.project,
12843
+ location: opts.location,
12844
+ entryGroup: opts.entryGroup,
12845
+ systemTypeProject: opts.systemTypeProject,
12846
+ systemTypeLocation: opts.systemTypeLocation,
12847
+ emitExpressions: opts.emitExpressions,
12848
+ v2Aspects: opts.v2Aspects,
12849
+ overviews: opts.overviews
12850
+ });
12951
12851
  for (const w of resources.warnings)
12952
12852
  warnings.push(`[${model.name}] ${w}`);
12953
12853
  emitted2.push({ model: model.name, resources });
@@ -13609,18 +13509,12 @@ async function deploySpanner(models, ctx, options = {}) {
13609
13509
  const backoffMs = options.pollBackoffMs ?? POLL_BACKOFF_MS2;
13610
13510
  for (const { document, model } of models) {
13611
13511
  modelsSeen++;
13612
- let targets;
13613
- let malformed;
13614
- try {
13615
- ({ targets, malformed } = spannerGraphTargets(model));
13616
- } catch (err) {
13617
- return fail(`Model '${model.name}' (${document}): ${err.message || err}`);
13618
- }
13512
+ const { targets, malformed } = spannerGraphTargets(model);
13619
13513
  if (!targets.length) {
13620
13514
  if (malformed.length) {
13621
- return fail(`Model '${model.name}' (${document}) declares Spanner Graph ` + `deploymentTarget(s) that could not be parsed: ` + `${malformed.join(", ")}. Expected ${SPANNER_GRAPH_FORM}.`);
13515
+ return fail(`Model '${model.name}' (${document}) declares Spanner Graph ` + `deployment target(s) that could not be parsed: ` + `${malformed.join(", ")}. Expected ${SPANNER_GRAPH_FORM}.`);
13622
13516
  }
13623
- return fail(`Model '${model.name}' (${document}) declares no Spanner Graph ` + `deploymentTarget in a GOOGLE custom_extension; nothing to deploy.`);
13517
+ return fail(`Model '${model.name}' (${document}) declares no Spanner Graph ` + `deployment target; nothing to deploy.`);
13624
13518
  }
13625
13519
  for (const bad of malformed) {
13626
13520
  warnings.push(`[${model.name}] ignoring unparseable Spanner Graph target: ${bad}`);
@@ -13954,13 +13848,7 @@ function validatePushRequirements(models, opts = {}) {
13954
13848
  const errors = [];
13955
13849
  for (const { document, model } of models) {
13956
13850
  errors.push(...nameAndDescriptionErrors(model, document, !!opts.v2Aspects));
13957
- let deployInfo;
13958
- try {
13959
- deployInfo = googleDeploymentTargets(model);
13960
- } catch (err) {
13961
- errors.push(`${err.message || err} (${document})`);
13962
- continue;
13963
- }
13851
+ const deployInfo = googleDeploymentTargets(model);
13964
13852
  if (deployInfo.bigQuery.length + deployInfo.spanner.length > 0) {
13965
13853
  for (const rel of model.relationships ?? []) {
13966
13854
  if (rel.association)
@@ -15211,11 +15099,7 @@ async function validateBigQueryDataSources(models, bq, defaultProject, graph) {
15211
15099
  return errors;
15212
15100
  }
15213
15101
  function billingProject(model, defaultProject) {
15214
- try {
15215
- return googleDeploymentTargets(model).bigQuery[0]?.project ?? defaultProject;
15216
- } catch {
15217
- return defaultProject;
15218
- }
15102
+ return googleDeploymentTargets(model).bigQuery[0]?.project ?? defaultProject;
15219
15103
  }
15220
15104
  function probeableRef(dataSource) {
15221
15105
  const trimmed = (dataSource ?? "").trim();
@@ -15260,12 +15144,7 @@ async function validateSpannerActionStatements(models, spanner) {
15260
15144
  const statements = pendingStatements(loaded);
15261
15145
  if (!statements.length)
15262
15146
  continue;
15263
- let target;
15264
- try {
15265
- target = spannerGraphTargets(loaded.model).targets[0];
15266
- } catch {
15267
- continue;
15268
- }
15147
+ const target = spannerGraphTargets(loaded.model).targets[0];
15269
15148
  if (!target)
15270
15149
  continue;
15271
15150
  const key = `${target.project}/${target.instance}/${target.database}`;
@@ -16652,10 +16531,9 @@ function withDialects({ fileName, content }, profile, doc) {
16652
16531
  return writeDocument({ document, links, text: text3 }, root);
16653
16532
  }
16654
16533
  function validateDeploymentTargets(model, doc) {
16655
- for (const t of googleDeploymentTargets(model).uris) {
16656
- if (targetProblem("", t)) {
16657
- const d = model.deployments?.find((x) => x.target === t);
16658
- doc.errors.push(d ? deploymentTargetError(model, d) : googleBlockTargetError(model, t));
16534
+ for (const d of model.deployments ?? []) {
16535
+ if (targetProblem("", d.target)) {
16536
+ doc.errors.push(deploymentTargetError(model, d));
16659
16537
  }
16660
16538
  }
16661
16539
  }
@@ -16977,10 +16855,9 @@ function modelsFromCatalogResources(entries, entryLinks = [], opts = {}) {
16977
16855
  if (description !== undefined)
16978
16856
  model.description = description;
16979
16857
  readActionsAndConstraints(model, children(actionEntries2), children(constraintEntries2), warnings);
16980
- const targets = readDeploymentTargets(anchor, name, warnings);
16981
- if (targets) {
16982
- model.customExtensions = [targets.extension];
16983
- model.deployments = targets.deployments;
16858
+ const deployments = readDeploymentTargets(anchor, name, warnings);
16859
+ if (deployments) {
16860
+ model.deployments = deployments;
16984
16861
  }
16985
16862
  const ai = readAiContext(anchor);
16986
16863
  if (ai)
@@ -17347,15 +17224,8 @@ function readDeploymentTargets(anchor, modelName, warnings) {
17347
17224
  if (problem !== undefined)
17348
17225
  warnings.push(problem);
17349
17226
  }
17350
- const targets = deployments.map((d) => d.target);
17351
17227
  warnings.push(`model '${modelName}': deployment names were not recovered (the ` + `catalog stores target URIs only); using ${deployments.map((d) => `'${d.name}'`).join(", ")}`);
17352
- return {
17353
- extension: {
17354
- vendorName: "GOOGLE",
17355
- data: JSON.stringify({ deploymentTargets: targets })
17356
- },
17357
- deployments
17358
- };
17228
+ return deployments;
17359
17229
  }
17360
17230
  function readRelationships(links, modelName, entityByEntryId, warnings) {
17361
17231
  const prefix = linkNamePrefix(modelName);
@@ -17686,18 +17556,16 @@ function strayBindings(model, store) {
17686
17556
  return strays;
17687
17557
  }
17688
17558
  function resolveStore(model, profile = DEFAULT_PROFILE) {
17689
- let all;
17690
- try {
17691
- all = googleDeploymentTargets(model);
17692
- } catch (err) {
17693
- return { error: `${err?.message || err}`, named: true, rejected: true };
17694
- }
17559
+ const all = googleDeploymentTargets(model);
17695
17560
  const vanilla = model.version === OSSIE_VERSION;
17696
17561
  const where = vanilla && model.deployments ? `its GOOGLE block's 'deployments'` : `'deployments'`;
17697
17562
  if (all.malformed.length) {
17698
- const unparsed = model.deployments?.find((d) => d.target === all.malformed[0]);
17699
- const error = unparsed ? deploymentTargetError(model, unparsed) : googleBlockTargetError(model, all.malformed[0]);
17700
- return { error, named: true, rejected: true };
17563
+ const unparsed = model.deployments.find((d) => d.target === all.malformed[0]);
17564
+ return {
17565
+ error: deploymentTargetError(model, unparsed),
17566
+ named: true,
17567
+ rejected: true
17568
+ };
17701
17569
  }
17702
17570
  const used = deploymentsFor(model, profile);
17703
17571
  if ("error" in used)
@@ -17762,9 +17630,7 @@ function profileTargets(model, profile) {
17762
17630
  return "error" in used ? [] : googleDeploymentTargets(used.model).uris;
17763
17631
  }
17764
17632
  function deploymentNames(model, uris, profile) {
17765
- const deployments = model.deployments;
17766
- if (!deployments)
17767
- return "";
17633
+ const deployments = model.deployments ?? [];
17768
17634
  const names = uris.map((uri) => deployments.find((d) => d.target === uri)?.name ?? uri);
17769
17635
  return ` for profile '${profile}' (${names.join(", ")})`;
17770
17636
  }
@@ -18771,12 +18637,8 @@ function okfWorkspace() {
18771
18637
  return readWorkspaceLayout("catalog.yaml") === OKF_FORMAT;
18772
18638
  }
18773
18639
  function hasTargetType(loaded, type) {
18774
- try {
18775
- const t = googleDeploymentTargets(loaded.model);
18776
- return type === "bigquery" ? t.bigQuery.length > 0 : t.spanner.length > 0;
18777
- } catch {
18778
- return false;
18779
- }
18640
+ const t = googleDeploymentTargets(loaded.model);
18641
+ return type === "bigquery" ? t.bigQuery.length > 0 : t.spanner.length > 0;
18780
18642
  }
18781
18643
  function pruningDialect(model) {
18782
18644
  return hasTargetType({ model }, "spanner") ? "SPANNER" : sourcesDialect(model);
@@ -19439,10 +19301,7 @@ async function printStore(options, ctx, snapshot2) {
19439
19301
  return 0;
19440
19302
  }
19441
19303
  function printProfileReport(model, name) {
19442
- let targets = [];
19443
- try {
19444
- targets = profileTargets(model, name);
19445
- } catch {}
19304
+ const targets = profileTargets(model, name);
19446
19305
  const { report } = pruneUnavailable(model, name, pruningDialect({ ...model, deployments: unnamedDeployments(targets) }));
19447
19306
  console.log(` target: ${targets.length ? targets.join(", ") : "(none; no deployment names this profile)"}`);
19448
19307
  console.log(" sources:");
@@ -20198,7 +20057,7 @@ async function startServer(basePath = ".") {
20198
20057
  }
20199
20058
 
20200
20059
  // src/tool/main.ts
20201
- var BUILD_COMMIT = `+${"e2a3a9b"}`;
20060
+ var BUILD_COMMIT = `+${"5d0c5b3"}`;
20202
20061
  async function run(command) {
20203
20062
  let exitCode = 1;
20204
20063
  try {