@uipath/conversational-tool 1.203.0-preview.235 → 1.203.0-preview.240
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
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
metadata,
|
|
4
4
|
registerCommands
|
|
5
|
-
} from "./tool-
|
|
5
|
+
} from "./tool-g3s4bh60.js";
|
|
6
6
|
import"./tool-nkyhbf79.js";
|
|
7
7
|
import {
|
|
8
8
|
Command
|
|
9
|
-
} from "./tool-
|
|
9
|
+
} from "./tool-g7k43qwt.js";
|
|
10
10
|
import"./tool-mpvp5rqs.js";
|
|
11
11
|
import"./tool-1yh8g550.js";
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
parseBoundedInt,
|
|
21
21
|
processContext,
|
|
22
22
|
requireConfirmation
|
|
23
|
-
} from "./tool-
|
|
23
|
+
} from "./tool-g7k43qwt.js";
|
|
24
24
|
import {
|
|
25
25
|
__require
|
|
26
26
|
} from "./tool-1yh8g550.js";
|
|
@@ -12481,7 +12481,7 @@ var resolveEntryPoint = (entryPoints, requested) => {
|
|
|
12481
12481
|
var resolveBinding = async (target) => {
|
|
12482
12482
|
const processKey = target.processKey.toLowerCase();
|
|
12483
12483
|
const folderKey = target.folderKey.toLowerCase();
|
|
12484
|
-
const { listVoiceEntryPoints } = await import("./cas-client-
|
|
12484
|
+
const { listVoiceEntryPoints } = await import("./cas-client-7ghvbmbc.js");
|
|
12485
12485
|
const [epError, entryPoints] = await catchError(listVoiceEntryPoints(processKey, folderKey));
|
|
12486
12486
|
if (epError) {
|
|
12487
12487
|
await failFromError(epError);
|
|
@@ -12497,7 +12497,7 @@ var resolveBinding = async (target) => {
|
|
|
12497
12497
|
var registerTrunksCommand = (program) => {
|
|
12498
12498
|
const trunks = program.command("trunks").description("Inspect the SIP trunks (phone numbers) configured for conversational voice.");
|
|
12499
12499
|
trunks.command("list").description("List the tenant's SIP trunks. Voice flows dial out from an outbound-enabled trunk's phone number (`inputs.from` on Create outgoing call); inbound-enabled trunks route incoming calls to a published process. Trunks are connected in Instance Management under Phone numbers.").option("--direction <direction>", "Only trunks enabled for this call direction: inbound or outbound (default: all trunks)", parseDirection).trackedAction(processContext, async (options) => {
|
|
12500
|
-
const { listSipTrunks } = await import("./cas-client-
|
|
12500
|
+
const { listSipTrunks } = await import("./cas-client-7ghvbmbc.js");
|
|
12501
12501
|
const [listError, trunks2] = await catchError(listSipTrunks(options.direction));
|
|
12502
12502
|
if (listError) {
|
|
12503
12503
|
await failFromError(listError);
|
|
@@ -12513,7 +12513,7 @@ var registerTrunksCommand = (program) => {
|
|
|
12513
12513
|
const plan = planAssign(options);
|
|
12514
12514
|
if (plan === null)
|
|
12515
12515
|
return;
|
|
12516
|
-
const { resolveSipTrunk, updateSipTrunkBinding } = await import("./cas-client-
|
|
12516
|
+
const { resolveSipTrunk, updateSipTrunkBinding } = await import("./cas-client-7ghvbmbc.js");
|
|
12517
12517
|
const [trunkError, trunk] = await catchError(resolveSipTrunk(trunkArg));
|
|
12518
12518
|
if (trunkError) {
|
|
12519
12519
|
await failFromError(trunkError);
|
|
@@ -2101,7 +2101,7 @@ var package_default = {
|
|
|
2101
2101
|
name: "@uipath/conversational-tool",
|
|
2102
2102
|
author: "UiPath",
|
|
2103
2103
|
license: "SEE LICENSE IN LICENSE.txt",
|
|
2104
|
-
version: "1.203.0-preview.
|
|
2104
|
+
version: "1.203.0-preview.240",
|
|
2105
2105
|
description: "Handle conversations with deployed UiPath conversational processes.",
|
|
2106
2106
|
publishConfig: {
|
|
2107
2107
|
registry: "https://npm.pkg.github.com/"
|
|
@@ -5090,7 +5090,165 @@ class FilterImplicitLimitError extends Error {
|
|
|
5090
5090
|
this.instructions = "Pass --limit <n> to choose how many records the filter applies to. " + "To filter over all records, pass the command's maximum accepted --limit (see the option's description in --help).";
|
|
5091
5091
|
}
|
|
5092
5092
|
}
|
|
5093
|
-
function
|
|
5093
|
+
function countRecords(data) {
|
|
5094
|
+
if (Array.isArray(data))
|
|
5095
|
+
return data.length;
|
|
5096
|
+
return data == null ? 0 : 1;
|
|
5097
|
+
}
|
|
5098
|
+
function availableFields(data) {
|
|
5099
|
+
const records = Array.isArray(data) ? data : [data];
|
|
5100
|
+
const keys = new Set;
|
|
5101
|
+
for (const record of records) {
|
|
5102
|
+
if (record && typeof record === "object" && !Array.isArray(record)) {
|
|
5103
|
+
for (const key of Object.keys(record))
|
|
5104
|
+
keys.add(key);
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
return [...keys];
|
|
5108
|
+
}
|
|
5109
|
+
function decodeQuotedIdentifier(whole, inner) {
|
|
5110
|
+
const [error, decoded] = catchError(() => JSON.parse(whole));
|
|
5111
|
+
if (!error && typeof decoded === "string")
|
|
5112
|
+
return decoded;
|
|
5113
|
+
return inner.replace(/\\(.)/g, "$1");
|
|
5114
|
+
}
|
|
5115
|
+
function referencedFields(filter) {
|
|
5116
|
+
const top = new Set;
|
|
5117
|
+
const nested = new Set;
|
|
5118
|
+
const withoutValueLiterals = filter.replace(/'(?:\\.|[^'\\])*'/g, " ").replace(/`(?:\\.|[^`\\])*`/g, " ");
|
|
5119
|
+
const isPathTail = (text, index) => text[index - 1] === "." && /[A-Za-z0-9_"]/.test(text[index - 2] ?? "");
|
|
5120
|
+
const quoted = [];
|
|
5121
|
+
const withoutLiterals = withoutValueLiterals.replace(/"((?:\\.|[^"\\])*)"/g, (whole, name, index) => {
|
|
5122
|
+
const isAlias = /^\s*:/.test(withoutValueLiterals.slice(index + whole.length));
|
|
5123
|
+
if (!isAlias) {
|
|
5124
|
+
quoted.push({
|
|
5125
|
+
name: decodeQuotedIdentifier(whole, name),
|
|
5126
|
+
index,
|
|
5127
|
+
tail: isPathTail(withoutValueLiterals, index)
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
5130
|
+
return `"${" ".repeat(whole.length - 2)}"`;
|
|
5131
|
+
});
|
|
5132
|
+
const descent = /[A-Za-z0-9_"]\s*[.[]/.exec(withoutLiterals);
|
|
5133
|
+
const rootEnd = descent ? descent.index + 1 : Number.POSITIVE_INFINITY;
|
|
5134
|
+
const place = (name, index, tail) => {
|
|
5135
|
+
if (tail || index >= rootEnd)
|
|
5136
|
+
nested.add(name);
|
|
5137
|
+
else
|
|
5138
|
+
top.add(name);
|
|
5139
|
+
};
|
|
5140
|
+
for (const entry of quoted)
|
|
5141
|
+
place(entry.name, entry.index, entry.tail);
|
|
5142
|
+
const identifiers = withoutLiterals.matchAll(/([A-Za-z_][A-Za-z0-9_]*)(\s*[:(])?/g);
|
|
5143
|
+
const reserved = new Set(["true", "false", "null", "and", "or", "not"]);
|
|
5144
|
+
for (const match of identifiers) {
|
|
5145
|
+
const [, name, trailing] = match;
|
|
5146
|
+
if (trailing || reserved.has(name))
|
|
5147
|
+
continue;
|
|
5148
|
+
place(name, match.index, isPathTail(withoutLiterals, match.index));
|
|
5149
|
+
}
|
|
5150
|
+
return {
|
|
5151
|
+
top: [...top],
|
|
5152
|
+
nested: [...nested].filter((name) => !top.has(name)),
|
|
5153
|
+
hasPredicate: /\[\s*\?/.test(withoutValueLiterals)
|
|
5154
|
+
};
|
|
5155
|
+
}
|
|
5156
|
+
function appearsBelowTopLevel(data, name) {
|
|
5157
|
+
let budget = 2000;
|
|
5158
|
+
const visit = (value, depth) => {
|
|
5159
|
+
if (budget-- <= 0 || depth > 4)
|
|
5160
|
+
return false;
|
|
5161
|
+
if (value === null || typeof value !== "object")
|
|
5162
|
+
return false;
|
|
5163
|
+
if (Array.isArray(value))
|
|
5164
|
+
return value.some((entry) => visit(entry, depth));
|
|
5165
|
+
const record = value;
|
|
5166
|
+
if (depth > 0 && Object.hasOwn(record, name))
|
|
5167
|
+
return true;
|
|
5168
|
+
return Object.values(record).some((entry) => visit(entry, depth + 1));
|
|
5169
|
+
};
|
|
5170
|
+
return visit(data, 0);
|
|
5171
|
+
}
|
|
5172
|
+
function alwaysNullKeys(records) {
|
|
5173
|
+
const objects = records.filter((r) => r != null && typeof r === "object" && !Array.isArray(r));
|
|
5174
|
+
if (objects.length === 0 || objects.length !== records.length)
|
|
5175
|
+
return [];
|
|
5176
|
+
const keys = [...new Set(objects.flatMap((o) => Object.keys(o)))];
|
|
5177
|
+
if (keys.length === 0)
|
|
5178
|
+
return [];
|
|
5179
|
+
const nullKeys = keys.filter((key) => objects.every((record) => record[key] === null));
|
|
5180
|
+
return nullKeys.length === keys.length ? nullKeys : [];
|
|
5181
|
+
}
|
|
5182
|
+
function carriesOnlyNulls(value) {
|
|
5183
|
+
if (value === null)
|
|
5184
|
+
return true;
|
|
5185
|
+
if (Array.isArray(value)) {
|
|
5186
|
+
return value.length > 0 && value.every(carriesOnlyNulls);
|
|
5187
|
+
}
|
|
5188
|
+
if (typeof value === "object" && value !== undefined) {
|
|
5189
|
+
const values = Object.values(value);
|
|
5190
|
+
return values.length > 0 && values.every(carriesOnlyNulls);
|
|
5191
|
+
}
|
|
5192
|
+
return false;
|
|
5193
|
+
}
|
|
5194
|
+
function nameList(names, cap2 = 12) {
|
|
5195
|
+
const shown = names.slice(0, cap2).map((n) => `\`${n}\``);
|
|
5196
|
+
const rest = names.length - shown.length;
|
|
5197
|
+
return shown.join(", ") + (rest > 0 ? `, and ${rest} more` : "");
|
|
5198
|
+
}
|
|
5199
|
+
function buildFilterWarning(filter, data, outcome) {
|
|
5200
|
+
const present = availableFields(data);
|
|
5201
|
+
const refs = referencedFields(filter);
|
|
5202
|
+
const unknown = refs.top.filter((name) => !present.includes(name) && !appearsBelowTopLevel(data, name));
|
|
5203
|
+
const unchecked = refs.nested.filter((name) => !present.includes(name));
|
|
5204
|
+
const count = countRecords(data);
|
|
5205
|
+
const scope = `${count} record(s)`;
|
|
5206
|
+
const isNotAField = (names) => `${nameList(names)} ${names.length === 1 ? "is not a field" : "are not fields"} on the ${scope} it read`;
|
|
5207
|
+
const soundness = unchecked.length > 0 ? `${nameList(unchecked)} ${unchecked.length === 1 ? "reads" : "read"} below the top level, so ${unchecked.length === 1 ? "it was" : "they were"} not checked against the record keys` : refs.top.length > 0 ? "every field it names exists" : "";
|
|
5208
|
+
const withSoundness = (base) => soundness ? `${base}, and ${soundness}` : base;
|
|
5209
|
+
let detail;
|
|
5210
|
+
let lede;
|
|
5211
|
+
if (outcome.kind === "null-elements") {
|
|
5212
|
+
lede = "returned only null values";
|
|
5213
|
+
const projected = `all ${outcome.count} of the elements it projected are null`;
|
|
5214
|
+
if (unknown.length > 0) {
|
|
5215
|
+
detail = `${projected}, because ${isNotAField(unknown)}`;
|
|
5216
|
+
} else if (unchecked.length > 0) {
|
|
5217
|
+
detail = withSoundness(projected);
|
|
5218
|
+
} else {
|
|
5219
|
+
detail = `${projected}, and every field it names exists — a multi-select list ` + `(\`[A, B]\`) reads each entry against \`Data\` itself, so on a list ` + `result use \`[].[A, B]\` or \`[].{a: A, b: B}\` to read the records`;
|
|
5220
|
+
}
|
|
5221
|
+
} else if (outcome.kind === "null-rows") {
|
|
5222
|
+
lede = "returned only null values";
|
|
5223
|
+
const projected = `every value it projected is null on all ${outcome.count} row(s)`;
|
|
5224
|
+
detail = unknown.length > 0 ? `${projected}, because ${isNotAField(unknown)}` : withSoundness(projected);
|
|
5225
|
+
} else if (outcome.kind === "null-values") {
|
|
5226
|
+
lede = "returned only null values";
|
|
5227
|
+
const projected = `every key it projected (${nameList(outcome.keys)}) is null`;
|
|
5228
|
+
const sameSet = unknown.length === outcome.keys.length && outcome.keys.every((k) => unknown.includes(k));
|
|
5229
|
+
if (sameSet) {
|
|
5230
|
+
detail = `${isNotAField(outcome.keys)}, so every key it projected is null`;
|
|
5231
|
+
} else if (unknown.length > 0) {
|
|
5232
|
+
detail = `${projected}, because ${isNotAField(unknown)}`;
|
|
5233
|
+
} else {
|
|
5234
|
+
detail = withSoundness(`${projected} on all ${scope}`);
|
|
5235
|
+
}
|
|
5236
|
+
} else {
|
|
5237
|
+
lede = "returned nothing";
|
|
5238
|
+
if (unknown.length > 0) {
|
|
5239
|
+
detail = isNotAField(unknown);
|
|
5240
|
+
} else {
|
|
5241
|
+
const selection = refs.hasPredicate ? "no record matched" : "the expression resolved to no value";
|
|
5242
|
+
detail = withSoundness(`${selection} — it read ${scope}`);
|
|
5243
|
+
}
|
|
5244
|
+
}
|
|
5245
|
+
const fields = present.length > 0 ? ` Available fields: ${nameList(present)}.` : "";
|
|
5246
|
+
return `--output-filter ${JSON.stringify(filter)} ${lede}: ${detail}.${fields}`;
|
|
5247
|
+
}
|
|
5248
|
+
function appendWarning(existing, note) {
|
|
5249
|
+
return existing ? `${existing} ${note}` : note;
|
|
5250
|
+
}
|
|
5251
|
+
function applyFilter(data, filter, onWarning) {
|
|
5094
5252
|
const codec = requireJmespath();
|
|
5095
5253
|
let result;
|
|
5096
5254
|
try {
|
|
@@ -5098,9 +5256,34 @@ function applyFilter(data, filter) {
|
|
|
5098
5256
|
} catch (err) {
|
|
5099
5257
|
throw new FilterEvaluationError(filter, err);
|
|
5100
5258
|
}
|
|
5101
|
-
|
|
5259
|
+
const inputCount = countRecords(data);
|
|
5260
|
+
const warn = (outcome) => {
|
|
5261
|
+
if (inputCount === 0)
|
|
5262
|
+
return;
|
|
5263
|
+
const note = buildFilterWarning(filter, data, outcome);
|
|
5264
|
+
getOutputSink().writeErr(`[WARN] ${note}
|
|
5265
|
+
`);
|
|
5266
|
+
onWarning?.(note);
|
|
5267
|
+
};
|
|
5268
|
+
const warnIfAllNull = (records) => {
|
|
5269
|
+
const keys = alwaysNullKeys(records);
|
|
5270
|
+
if (keys.length > 0)
|
|
5271
|
+
warn({ kind: "null-values", keys });
|
|
5272
|
+
};
|
|
5273
|
+
if (result == null) {
|
|
5274
|
+
warn({ kind: "empty" });
|
|
5102
5275
|
return [];
|
|
5276
|
+
}
|
|
5103
5277
|
if (Array.isArray(result)) {
|
|
5278
|
+
if (result.length === 0) {
|
|
5279
|
+
warn({ kind: "empty" });
|
|
5280
|
+
} else if (result.every((entry) => entry === null)) {
|
|
5281
|
+
warn({ kind: "null-elements", count: result.length });
|
|
5282
|
+
} else if (result.every((entry) => Array.isArray(entry)) && carriesOnlyNulls(result)) {
|
|
5283
|
+
warn({ kind: "null-rows", count: result.length });
|
|
5284
|
+
} else {
|
|
5285
|
+
warnIfAllNull(result);
|
|
5286
|
+
}
|
|
5104
5287
|
if (result.length > 0 && typeof result[0] !== "object") {
|
|
5105
5288
|
const fieldMatch = filter.match(/\.(\w+)$/);
|
|
5106
5289
|
const key = fieldMatch ? fieldMatch[1] : "Value";
|
|
@@ -5108,10 +5291,30 @@ function applyFilter(data, filter) {
|
|
|
5108
5291
|
}
|
|
5109
5292
|
return result;
|
|
5110
5293
|
}
|
|
5111
|
-
if (typeof result === "object")
|
|
5294
|
+
if (typeof result === "object") {
|
|
5295
|
+
warnIfAllNull([result]);
|
|
5112
5296
|
return result;
|
|
5297
|
+
}
|
|
5113
5298
|
return { Value: result };
|
|
5114
5299
|
}
|
|
5300
|
+
function filterFailureData(envelope) {
|
|
5301
|
+
const filter = getOutputFilter();
|
|
5302
|
+
const data = envelope.Data;
|
|
5303
|
+
if (!filter || data == null)
|
|
5304
|
+
return;
|
|
5305
|
+
let note;
|
|
5306
|
+
const [filterError, filtered] = catchError(() => applyFilter(data, filter, (n) => {
|
|
5307
|
+
note = n;
|
|
5308
|
+
}));
|
|
5309
|
+
if (filterError) {
|
|
5310
|
+
envelope.Warning = appendWarning(envelope.Warning, `--output-filter was not applied, so Data is shown in full: ${filterError.message}`);
|
|
5311
|
+
return;
|
|
5312
|
+
}
|
|
5313
|
+
envelope.Data = filtered;
|
|
5314
|
+
if (note) {
|
|
5315
|
+
envelope.Warning = appendWarning(envelope.Warning, note);
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5115
5318
|
function defaultErrorCodeForResult(result) {
|
|
5116
5319
|
switch (result) {
|
|
5117
5320
|
case RESULTS.AuthenticationError:
|
|
@@ -5194,8 +5397,14 @@ var OutputFormatter;
|
|
|
5194
5397
|
}
|
|
5195
5398
|
const filter = getOutputFilter();
|
|
5196
5399
|
if (filter) {
|
|
5197
|
-
|
|
5400
|
+
let note;
|
|
5401
|
+
const filtered = applyFilter(data.Data, filter, (n) => {
|
|
5402
|
+
note = n;
|
|
5403
|
+
});
|
|
5198
5404
|
data.Data = normalize ? normalizeDataKeys(filtered, verbatim, subtrees && { refs: subtrees.refs, mode: "skip" }) : filtered;
|
|
5405
|
+
if (note) {
|
|
5406
|
+
data.Warning = appendWarning(data.Warning, note);
|
|
5407
|
+
}
|
|
5199
5408
|
}
|
|
5200
5409
|
logOutput(normalizeOutputKeys(data), getOutputFormat(), options?.tableRowStyle);
|
|
5201
5410
|
}
|
|
@@ -5223,6 +5432,7 @@ var OutputFormatter;
|
|
|
5223
5432
|
delete envelope.TelemetryErrorClass;
|
|
5224
5433
|
delete envelope.TelemetryTerminalOutcome;
|
|
5225
5434
|
delete envelope.TelemetryTerminalSignal;
|
|
5435
|
+
filterFailureData(envelope);
|
|
5226
5436
|
if (!suppressTelemetry) {
|
|
5227
5437
|
telemetry.trackEvent(CommonTelemetryEvents.Error, {
|
|
5228
5438
|
result: data.Result,
|
|
@@ -5271,13 +5481,20 @@ var OutputFormatter;
|
|
|
5271
5481
|
OutputFormatter.log = log;
|
|
5272
5482
|
function formatToString(data) {
|
|
5273
5483
|
const filter = getOutputFilter();
|
|
5484
|
+
const envelope = data;
|
|
5274
5485
|
if ("Data" in data && data.Data != null) {
|
|
5275
5486
|
data.Data = normalizeDataKeys(data.Data);
|
|
5276
5487
|
if (filter) {
|
|
5277
|
-
|
|
5488
|
+
let note;
|
|
5489
|
+
data.Data = normalizeDataKeys(applyFilter(data.Data, filter, (n) => {
|
|
5490
|
+
note = n;
|
|
5491
|
+
}));
|
|
5492
|
+
if (note) {
|
|
5493
|
+
envelope.Warning = appendWarning(envelope.Warning, note);
|
|
5494
|
+
}
|
|
5278
5495
|
}
|
|
5279
5496
|
}
|
|
5280
|
-
const output = normalizeOutputKeys(
|
|
5497
|
+
const output = normalizeOutputKeys(envelope);
|
|
5281
5498
|
const lines = [];
|
|
5282
5499
|
const sink = getOutputSink();
|
|
5283
5500
|
printOutput(output, getOutputFormat(), (msg) => {
|
|
@@ -7409,4 +7626,4 @@ var CLI_EXTERNAL_USER_ID = "app-user-cli-cas";
|
|
|
7409
7626
|
|
|
7410
7627
|
export { package_default, getFileSystem, catchError, extractErrorDetails, extractErrorMessageSync, RESULTS, EXIT_CODES, OutputFormatter, InvalidArgumentError, Command, processContext, requireConfirmation, instructionsFor, canPrompt, parseBoundedInt, getSdkUserAgentToken, addSdkUserAgentHeader, getLoginStatusAsync, CLI_EXTERNAL_USER_ID };
|
|
7411
7628
|
|
|
7412
|
-
//# debugId=
|
|
7629
|
+
//# debugId=CE664EAA6CBC173264756E2164756E21
|
package/dist/tool.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
metadata,
|
|
3
3
|
registerCommands
|
|
4
|
-
} from "./tool-
|
|
4
|
+
} from "./tool-g3s4bh60.js";
|
|
5
5
|
import"./tool-nkyhbf79.js";
|
|
6
|
-
import"./tool-
|
|
6
|
+
import"./tool-g7k43qwt.js";
|
|
7
7
|
import"./tool-mpvp5rqs.js";
|
|
8
8
|
import"./tool-1yh8g550.js";
|
|
9
9
|
export {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@uipath/conversational-tool",
|
|
3
3
|
"author": "UiPath",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
|
-
"version": "1.203.0-preview.
|
|
5
|
+
"version": "1.203.0-preview.240",
|
|
6
6
|
"description": "Handle conversations with deployed UiPath conversational processes.",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -18,5 +18,5 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
|
-
"gitHead": "
|
|
21
|
+
"gitHead": "95409ea61d359dff5465afaf59cb3a5efae542af"
|
|
22
22
|
}
|