@walkeros/cli 4.4.0 → 4.5.1-next-1788726985928

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
@@ -139,9 +139,17 @@ function createCLILoggerConfig(options = {}) {
139
139
  // at DEBUG, ERROR always reaches the handler (and the ring) even without
140
140
  // --verbose.
141
141
  level: Level.DEBUG,
142
- handler: (level, message, _context, scope) => {
142
+ handler: (level, message, context2, scope) => {
143
143
  const scopePath = scope.length > 0 ? `[${scope.join(":")}] ` : "";
144
- const fullMessage = scrubSecrets(`${scopePath}${message}`);
144
+ let meta = "";
145
+ if (Object.keys(context2).length > 0) {
146
+ try {
147
+ meta = ` ${JSON.stringify(context2)}`;
148
+ } catch {
149
+ meta = " [unserializable context]";
150
+ }
151
+ }
152
+ const fullMessage = scrubSecrets(`${scopePath}${message}${meta}`);
145
153
  try {
146
154
  options.onLine?.(level, fullMessage);
147
155
  } catch {
@@ -1280,6 +1288,127 @@ var init_structural_validators = __esm({
1280
1288
  }
1281
1289
  });
1282
1290
 
1291
+ // src/core/step-packages.ts
1292
+ import { packageNameToVariable } from "@walkeros/core";
1293
+ function getFlowSection(flow, section) {
1294
+ switch (section) {
1295
+ case "sources":
1296
+ return flow.sources;
1297
+ case "destinations":
1298
+ return flow.destinations;
1299
+ case "transformers":
1300
+ return flow.transformers;
1301
+ case "stores":
1302
+ return flow.stores;
1303
+ }
1304
+ }
1305
+ function parsePackageSpec(spec) {
1306
+ const at = spec.lastIndexOf("@");
1307
+ if (at <= 0) return { name: spec };
1308
+ const version = spec.slice(at + 1);
1309
+ return version ? { name: spec.slice(0, at), version } : { name: spec.slice(0, at) };
1310
+ }
1311
+ function detectStepPackages(flowSettings, section) {
1312
+ const packages = /* @__PURE__ */ new Set();
1313
+ const steps = getFlowSection(flowSettings, section);
1314
+ if (steps) {
1315
+ for (const [, stepConfig] of Object.entries(steps)) {
1316
+ if (typeof stepConfig !== "object" || stepConfig === null) continue;
1317
+ if (typeof stepConfig.package === "string") {
1318
+ packages.add(stepConfig.package);
1319
+ }
1320
+ }
1321
+ }
1322
+ return packages;
1323
+ }
1324
+ function collectAllStepPackages(flowSettings) {
1325
+ const allPackages = /* @__PURE__ */ new Set();
1326
+ const sections = [
1327
+ "sources",
1328
+ "destinations",
1329
+ "transformers",
1330
+ "stores"
1331
+ ];
1332
+ for (const section of sections) {
1333
+ for (const pkg of detectStepPackages(flowSettings, section)) {
1334
+ allPackages.add(pkg);
1335
+ }
1336
+ }
1337
+ return allPackages;
1338
+ }
1339
+ function applyStepPackages(flowSettings, packages, logger) {
1340
+ const stepPackages = collectAllStepPackages(flowSettings);
1341
+ const originalPins = /* @__PURE__ */ new Map();
1342
+ const inlineSeen = /* @__PURE__ */ new Map();
1343
+ const rewriteSteps = (from, to) => {
1344
+ for (const section of [
1345
+ "sources",
1346
+ "destinations",
1347
+ "transformers",
1348
+ "stores"
1349
+ ]) {
1350
+ const steps = getFlowSection(flowSettings, section);
1351
+ if (!steps) continue;
1352
+ for (const step of Object.values(steps)) {
1353
+ if (step.package === from) {
1354
+ step.package = to;
1355
+ }
1356
+ }
1357
+ }
1358
+ };
1359
+ for (const pkg of stepPackages) {
1360
+ const isLocalPath = pkg.startsWith(".") || pkg.startsWith("/");
1361
+ if (isLocalPath) {
1362
+ const varName = packageNameToVariable(pkg);
1363
+ if (!packages[varName]) {
1364
+ packages[varName] = {
1365
+ path: pkg
1366
+ };
1367
+ }
1368
+ rewriteSteps(pkg, varName);
1369
+ continue;
1370
+ }
1371
+ const { name, version } = parsePackageSpec(pkg);
1372
+ if (!originalPins.has(name)) {
1373
+ const entry = packages[name];
1374
+ originalPins.set(
1375
+ name,
1376
+ entry?.version !== void 0 || entry?.path !== void 0
1377
+ );
1378
+ }
1379
+ const hasBundlePin = originalPins.get(name) === true;
1380
+ if (name !== pkg) {
1381
+ rewriteSteps(pkg, name);
1382
+ }
1383
+ if (version && !hasBundlePin) {
1384
+ const seen = inlineSeen.get(name);
1385
+ if (seen !== void 0 && seen !== version) {
1386
+ throw new Error(
1387
+ `Conflicting inline versions for ${name}: "${seen}" and "${version}" are declared by different steps. Pin one version in config.bundle.packages.`
1388
+ );
1389
+ }
1390
+ inlineSeen.set(name, version);
1391
+ }
1392
+ const existing = packages[name];
1393
+ if (!existing) {
1394
+ packages[name] = version ? { version } : {};
1395
+ } else if (version && !existing.path) {
1396
+ if (!existing.version) {
1397
+ existing.version = version;
1398
+ } else if (existing.version !== version) {
1399
+ logger.warn(
1400
+ `Package ${name}: config.bundle.packages pins ${existing.version}; a step declares ${version} inline. Using the bundle pin.`
1401
+ );
1402
+ }
1403
+ }
1404
+ }
1405
+ }
1406
+ var init_step_packages = __esm({
1407
+ "src/core/step-packages.ts"() {
1408
+ "use strict";
1409
+ }
1410
+ });
1411
+
1283
1412
  // src/core/cache-utils.ts
1284
1413
  import { getHashServer } from "@walkeros/server-core";
1285
1414
  import semver from "semver";
@@ -1318,7 +1447,7 @@ var init_cache_utils = __esm({
1318
1447
  }
1319
1448
  });
1320
1449
 
1321
- // src/commands/bundle/package-manager.ts
1450
+ // src/core/package-manager.ts
1322
1451
  import pacote from "pacote";
1323
1452
  import path8 from "path";
1324
1453
  import fs7 from "fs-extra";
@@ -1867,9 +1996,9 @@ Each package must use the same version across all declarations. Please update yo
1867
1996
  }
1868
1997
  var PACOTE_OPTS, PACOTE_RETRY_ATTEMPTS, PACOTE_PER_ATTEMPT_TIMEOUT_MS, PACOTE_MAX_TOTAL_MS, MIN_ATTEMPT_BUDGET_MS, BASE_BACKOFF_MS, JITTER, PERMANENT_ERROR_CODES, SOURCE_PRIORITY;
1869
1998
  var init_package_manager = __esm({
1870
- "src/commands/bundle/package-manager.ts"() {
1999
+ "src/core/package-manager.ts"() {
1871
2000
  "use strict";
1872
- init_core();
2001
+ init_local_packages();
1873
2002
  init_cache_utils();
1874
2003
  init_tmp();
1875
2004
  PACOTE_OPTS = {
@@ -2188,7 +2317,7 @@ import { builtinModules } from "module";
2188
2317
  import path12 from "path";
2189
2318
  import fs11 from "fs-extra";
2190
2319
  import {
2191
- packageNameToVariable,
2320
+ packageNameToVariable as packageNameToVariable2,
2192
2321
  ENV_MARKER_PREFIX,
2193
2322
  SECRET_MARKER_PREFIX as SECRET_MARKER_PREFIX2,
2194
2323
  isPathStepEntry,
@@ -2198,18 +2327,6 @@ import { getHashServer as getHashServer3 } from "@walkeros/server-core";
2198
2327
  function isInlineCode2(code) {
2199
2328
  return code !== null && typeof code === "object" && !Array.isArray(code) && "push" in code;
2200
2329
  }
2201
- function getFlowSection(flow, section) {
2202
- switch (section) {
2203
- case "sources":
2204
- return flow.sources;
2205
- case "destinations":
2206
- return flow.destinations;
2207
- case "transformers":
2208
- return flow.transformers;
2209
- case "stores":
2210
- return flow.stores;
2211
- }
2212
- }
2213
2330
  function hasCodeReference2(code) {
2214
2331
  return isInlineCode2(code) || typeof code === "string";
2215
2332
  }
@@ -2674,19 +2791,6 @@ function createEsbuildOptions(buildOptions, entryPath, outputPath, tempDir, pack
2674
2791
  baseOptions.target = resolveTarget(buildOptions);
2675
2792
  return baseOptions;
2676
2793
  }
2677
- function detectStepPackages(flowSettings, section) {
2678
- const packages = /* @__PURE__ */ new Set();
2679
- const steps = getFlowSection(flowSettings, section);
2680
- if (steps) {
2681
- for (const [, stepConfig] of Object.entries(steps)) {
2682
- if (typeof stepConfig !== "object" || stepConfig === null) continue;
2683
- if (typeof stepConfig.package === "string") {
2684
- packages.add(stepConfig.package);
2685
- }
2686
- }
2687
- }
2688
- return packages;
2689
- }
2690
2794
  function getNodeExternals() {
2691
2795
  const externals = [];
2692
2796
  for (const mod of builtinModules) {
@@ -2746,84 +2850,6 @@ async function runNftServerPath(outputPath, flowSettings, buildOptions, tempDir,
2746
2850
  `nft-trace: copied ${result.copied} file(s); wrote ${sidecarPath}`
2747
2851
  );
2748
2852
  }
2749
- function collectAllStepPackages(flowSettings) {
2750
- const allPackages = /* @__PURE__ */ new Set();
2751
- const sections = [
2752
- "sources",
2753
- "destinations",
2754
- "transformers",
2755
- "stores"
2756
- ];
2757
- for (const section of sections) {
2758
- for (const pkg of detectStepPackages(flowSettings, section)) {
2759
- allPackages.add(pkg);
2760
- }
2761
- }
2762
- return allPackages;
2763
- }
2764
- function applyStepPackages(flowSettings, packages, logger) {
2765
- const stepPackages = collectAllStepPackages(flowSettings);
2766
- const originalVersions = /* @__PURE__ */ new Map();
2767
- const inlineSeen = /* @__PURE__ */ new Map();
2768
- const rewriteSteps = (from, to) => {
2769
- for (const section of [
2770
- "sources",
2771
- "destinations",
2772
- "transformers",
2773
- "stores"
2774
- ]) {
2775
- const steps = getFlowSection(flowSettings, section);
2776
- if (!steps) continue;
2777
- for (const step of Object.values(steps)) {
2778
- if (step.package === from) {
2779
- step.package = to;
2780
- }
2781
- }
2782
- }
2783
- };
2784
- for (const pkg of stepPackages) {
2785
- const isLocalPath = pkg.startsWith(".") || pkg.startsWith("/");
2786
- if (isLocalPath) {
2787
- const varName = packageNameToVariable(pkg);
2788
- if (!packages[varName]) {
2789
- packages[varName] = {
2790
- path: pkg
2791
- };
2792
- }
2793
- rewriteSteps(pkg, varName);
2794
- continue;
2795
- }
2796
- const { name, version } = parsePackageSpec(pkg);
2797
- if (!originalVersions.has(name)) {
2798
- originalVersions.set(name, packages[name]?.version);
2799
- }
2800
- const bundlePinnedVersion = originalVersions.get(name);
2801
- if (name !== pkg) {
2802
- rewriteSteps(pkg, name);
2803
- }
2804
- if (version && !bundlePinnedVersion) {
2805
- const seen = inlineSeen.get(name);
2806
- if (seen !== void 0 && seen !== version) {
2807
- throw new Error(
2808
- `Conflicting inline versions for ${name}: "${seen}" and "${version}" are declared by different steps. Pin one version in config.bundle.packages.`
2809
- );
2810
- }
2811
- inlineSeen.set(name, version);
2812
- }
2813
- const existing = packages[name];
2814
- if (!existing) {
2815
- packages[name] = version ? { version } : {};
2816
- } else if (version) {
2817
- if (!existing.version) {
2818
- existing.version = version;
2819
- } else if (existing.version !== version) {
2820
- logger.warn(
2821
- `Package ${name}: config.bundle.packages pins ${existing.version}; a step declares ${version} inline. Using the bundle pin.`
2822
- );
2823
- }
2824
- }
2825
- }
2826
- }
2827
2853
  function detectNamedImports(flowSettings) {
2828
2854
  const namedImports = /* @__PURE__ */ new Map();
2829
2855
  const addNamed = (pkg, importName) => {
@@ -2857,7 +2883,7 @@ async function generateImportStatements(packages, destinationPackages, sourcePac
2857
2883
  const hasNamed = namedImports.has(packageName);
2858
2884
  const namedImportsToGenerate = [];
2859
2885
  if (isUsedByDestOrSource && !hasNamed) {
2860
- const varName = packageNameToVariable(packageName);
2886
+ const varName = packageNameToVariable2(packageName);
2861
2887
  importStatements.push(`import ${varName} from '${packageName}';`);
2862
2888
  }
2863
2889
  if (hasNamed) {
@@ -2912,12 +2938,6 @@ async function computeDevPackages(usedPackages, packagePaths) {
2912
2938
  }
2913
2939
  return devPackages;
2914
2940
  }
2915
- function parsePackageSpec(spec) {
2916
- const at = spec.lastIndexOf("@");
2917
- if (at <= 0) return { name: spec };
2918
- const version = spec.slice(at + 1);
2919
- return version ? { name: spec.slice(0, at), version } : { name: spec.slice(0, at) };
2920
- }
2921
2941
  function packageSpecName(spec) {
2922
2942
  return parsePackageSpec(spec).name;
2923
2943
  }
@@ -3068,7 +3088,7 @@ function buildSplitConfigObject(flowSettings, namedImports) {
3068
3088
  if (typeof step.import === "string" && step.package) {
3069
3089
  return step.import;
3070
3090
  }
3071
- return packageNameToVariable(step.package);
3091
+ return packageNameToVariable2(step.package);
3072
3092
  }
3073
3093
  function getStepProps(step) {
3074
3094
  const props = {};
@@ -3528,6 +3548,7 @@ var init_bundler = __esm({
3528
3548
  init_config_classifier();
3529
3549
  init_structural_validators();
3530
3550
  init_structural_validators();
3551
+ init_step_packages();
3531
3552
  init_package_manager();
3532
3553
  init_nft_trace();
3533
3554
  init_assert_consumer_deps();
@@ -8249,7 +8270,7 @@ function validateMapping(input) {
8249
8270
  // src/commands/validate/validators/entry.ts
8250
8271
  import Ajv from "ajv";
8251
8272
  import { fetchPackageSchema } from "@walkeros/core";
8252
- var CLIENT_HEADER = "walkeros-cli/4.4.0";
8273
+ var CLIENT_HEADER = "walkeros-cli/4.5.1-next-1788726985928";
8253
8274
  var SECTIONS = ["destinations", "sources", "transformers"];
8254
8275
  function resolveEntry(path20, flowConfig) {
8255
8276
  const flows = flowConfig.flows;
@@ -8814,8 +8835,8 @@ import createClient from "openapi-fetch";
8814
8835
  init_config_file();
8815
8836
  import { createHash } from "crypto";
8816
8837
  import semver4 from "semver";
8817
- var bakedContractVersion = true ? "4.3.0" : PLACEHOLDER;
8818
- var bakedContractHash = true ? "921ca196fc71f3bf88bb0104338240438eb9411b274641d35c82b1462b4865b5" : "";
8838
+ var bakedContractVersion = true ? "4.4.0" : PLACEHOLDER;
8839
+ var bakedContractHash = true ? "c381f0b647eb8c72a2d187c95956284be75be59527a5e63710314be29ba33b55" : "";
8819
8840
  function isRecord3(value) {
8820
8841
  return value !== null && typeof value === "object" && !Array.isArray(value);
8821
8842
  }
@@ -10400,6 +10421,149 @@ async function endObserveSession(options) {
10400
10421
  }
10401
10422
  }
10402
10423
 
10424
+ // src/commands/hub/index.ts
10425
+ init_auth();
10426
+ init_http();
10427
+ async function readJson(response, fallback) {
10428
+ if (!response.ok) {
10429
+ const body = await response.json().catch(() => ({}));
10430
+ throwApiResponseError(response, body, fallback);
10431
+ }
10432
+ return response.json();
10433
+ }
10434
+ async function listReleases(options) {
10435
+ const pid = options.projectId ?? requireProjectId();
10436
+ const params = new URLSearchParams({ rationale: "true" });
10437
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
10438
+ if (options.offset !== void 0)
10439
+ params.set("offset", String(options.offset));
10440
+ const response = await apiFetch(
10441
+ `/api/projects/${pid}/flows/${options.flowId}/releases?${params.toString()}`
10442
+ );
10443
+ return readJson(response, "Failed to list releases");
10444
+ }
10445
+ async function getRelease(options) {
10446
+ const pid = options.projectId ?? requireProjectId();
10447
+ const segment = "versionId" in options.ref ? options.ref.versionId : String(options.ref.versionNumber);
10448
+ const response = await apiFetch(
10449
+ `/api/projects/${pid}/flows/${options.flowId}/releases/${encodeURIComponent(segment)}`
10450
+ );
10451
+ return readJson(response, "Failed to read release");
10452
+ }
10453
+ async function listStepHistory(options) {
10454
+ const pid = options.projectId ?? requireProjectId();
10455
+ const params = new URLSearchParams({ step: options.step });
10456
+ if (options.flow !== void 0) params.set("flow", options.flow);
10457
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
10458
+ const response = await apiFetch(
10459
+ `/api/projects/${pid}/flows/${options.flowId}/releases/step-history?${params.toString()}`
10460
+ );
10461
+ return readJson(response, "Failed to read step history");
10462
+ }
10463
+ async function setReleaseRationale(options) {
10464
+ const pid = options.projectId ?? requireProjectId();
10465
+ const response = await apiFetch(
10466
+ `/api/projects/${pid}/flows/${options.flowId}/releases/annotations`,
10467
+ {
10468
+ method: "PUT",
10469
+ headers: { "Content-Type": "application/json" },
10470
+ body: JSON.stringify({
10471
+ versionId: options.versionId,
10472
+ humanText: options.text
10473
+ })
10474
+ }
10475
+ );
10476
+ return readJson(response, "Failed to write release rationale");
10477
+ }
10478
+ async function listThreads(options) {
10479
+ const pid = options.projectId ?? requireProjectId();
10480
+ const params = new URLSearchParams();
10481
+ if (options.anchorType !== void 0)
10482
+ params.set("anchorType", options.anchorType);
10483
+ if (options.anchorKey !== void 0)
10484
+ params.set("anchorKey", options.anchorKey);
10485
+ if (options.status !== void 0) params.set("status", options.status);
10486
+ params.set("includeMessages", options.includeMessages ? "true" : "false");
10487
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
10488
+ const response = await apiFetch(
10489
+ `/api/projects/${pid}/flows/${options.flowId}/threads?${params.toString()}`
10490
+ );
10491
+ return readJson(response, "Failed to list threads");
10492
+ }
10493
+ async function createThread(options) {
10494
+ const pid = options.projectId ?? requireProjectId();
10495
+ const response = await apiFetch(
10496
+ `/api/projects/${pid}/flows/${options.flowId}/threads`,
10497
+ {
10498
+ method: "POST",
10499
+ headers: { "Content-Type": "application/json" },
10500
+ body: JSON.stringify({
10501
+ anchorType: options.anchorType,
10502
+ anchorKey: options.anchorKey,
10503
+ ...options.anchorLabel !== void 0 ? { anchorLabel: options.anchorLabel } : {},
10504
+ text: options.text
10505
+ })
10506
+ }
10507
+ );
10508
+ return readJson(response, "Failed to open thread");
10509
+ }
10510
+ async function addThreadMessage(options) {
10511
+ const pid = options.projectId ?? requireProjectId();
10512
+ const response = await apiFetch(
10513
+ `/api/projects/${pid}/flows/${options.flowId}/threads/${encodeURIComponent(options.threadId)}/messages`,
10514
+ {
10515
+ method: "POST",
10516
+ headers: { "Content-Type": "application/json" },
10517
+ body: JSON.stringify({ text: options.text })
10518
+ }
10519
+ );
10520
+ return readJson(response, "Failed to add message");
10521
+ }
10522
+ async function listKnowledge(options) {
10523
+ const pid = options.projectId ?? requireProjectId();
10524
+ const params = new URLSearchParams();
10525
+ if (options.pageKey !== void 0) params.set("pageKey", options.pageKey);
10526
+ if (options.frameId !== void 0) params.set("frameId", options.frameId);
10527
+ if (options.markId !== void 0) params.set("markId", options.markId);
10528
+ params.set("includeMessages", options.includeMessages ? "true" : "false");
10529
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
10530
+ const response = await apiFetch(
10531
+ `/api/projects/${pid}/knowledge?${params.toString()}`
10532
+ );
10533
+ return readJson(response, "Failed to read knowledge");
10534
+ }
10535
+
10536
+ // src/commands/frames/index.ts
10537
+ init_auth();
10538
+ init_http();
10539
+ async function readJson2(response, fallback) {
10540
+ if (!response.ok) {
10541
+ const body = await response.json().catch(() => ({}));
10542
+ throwApiResponseError(response, body, fallback);
10543
+ }
10544
+ return response.json();
10545
+ }
10546
+ async function listFrames(options = {}) {
10547
+ const pid = options.projectId ?? requireProjectId();
10548
+ const response = await apiFetch(`/api/projects/${pid}/frames`);
10549
+ return readJson2(response, "Failed to list frames");
10550
+ }
10551
+ async function listPageFrames(options) {
10552
+ const pid = options.projectId ?? requireProjectId();
10553
+ const params = new URLSearchParams({ pageKey: options.pageKey });
10554
+ const response = await apiFetch(
10555
+ `/api/projects/${pid}/frames?${params.toString()}`
10556
+ );
10557
+ return readJson2(response, "Failed to list page frames");
10558
+ }
10559
+ async function getFrame(options) {
10560
+ const pid = options.projectId ?? requireProjectId();
10561
+ const response = await apiFetch(
10562
+ `/api/projects/${pid}/frames/${encodeURIComponent(options.frameId)}`
10563
+ );
10564
+ return readJson2(response, "Failed to read frame");
10565
+ }
10566
+
10403
10567
  // src/commands/secrets/index.ts
10404
10568
  init_auth();
10405
10569
  init_http();
@@ -10771,6 +10935,7 @@ export {
10771
10935
  ApiError,
10772
10936
  DeploymentAmbiguityError,
10773
10937
  VERSION,
10938
+ addThreadMessage,
10774
10939
  annotateErrorWithDrift,
10775
10940
  apiFetch,
10776
10941
  bakedContractHash,
@@ -10794,6 +10959,7 @@ export {
10794
10959
  createProject,
10795
10960
  createProjectCommand,
10796
10961
  createSecret,
10962
+ createThread,
10797
10963
  deleteConfig,
10798
10964
  deleteDeployment,
10799
10965
  deleteDeploymentByFlowId,
@@ -10824,21 +10990,29 @@ export {
10824
10990
  getFeedbackPreference,
10825
10991
  getFlow,
10826
10992
  getFlowCommand,
10993
+ getFrame,
10827
10994
  getObserveSession,
10828
10995
  getPreview,
10829
10996
  getProject,
10830
10997
  getProjectCommand,
10998
+ getRelease,
10831
10999
  getToken,
10832
11000
  listAllFlows,
10833
11001
  listDeployments,
10834
11002
  listDeploymentsCommand,
10835
11003
  listFlows,
10836
11004
  listFlowsCommand,
11005
+ listFrames,
10837
11006
  listJourneys,
11007
+ listKnowledge,
11008
+ listPageFrames,
10838
11009
  listPreviews,
10839
11010
  listProjects,
10840
11011
  listProjectsCommand,
11012
+ listReleases,
10841
11013
  listSecrets,
11014
+ listStepHistory,
11015
+ listThreads,
10842
11016
  loadConfig,
10843
11017
  loadJsonConfig,
10844
11018
  loginCommand,
@@ -10861,6 +11035,7 @@ export {
10861
11035
  setClientContext,
10862
11036
  setDefaultProject,
10863
11037
  setFeedbackPreference,
11038
+ setReleaseRationale,
10864
11039
  simulateCollector,
10865
11040
  simulateDestination,
10866
11041
  simulateSource,