@automagik/omni 2.260326.1 → 2.260331.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/connect.d.ts +12 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/instances.d.ts.map +1 -1
- package/dist/commands/providers-setup.d.ts.map +1 -1
- package/dist/commands/providers.d.ts.map +1 -1
- package/dist/index.js +634 -563
- package/dist/server/index.js +1208 -1704
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -29524,7 +29524,7 @@ var require_node3 = __commonJS((exports, module) => {
|
|
|
29524
29524
|
var tty3 = __require("tty");
|
|
29525
29525
|
var util3 = __require("util");
|
|
29526
29526
|
exports.init = init;
|
|
29527
|
-
exports.log =
|
|
29527
|
+
exports.log = log18;
|
|
29528
29528
|
exports.formatArgs = formatArgs;
|
|
29529
29529
|
exports.save = save;
|
|
29530
29530
|
exports.load = load2;
|
|
@@ -29656,7 +29656,7 @@ var require_node3 = __commonJS((exports, module) => {
|
|
|
29656
29656
|
}
|
|
29657
29657
|
return new Date().toISOString() + " ";
|
|
29658
29658
|
}
|
|
29659
|
-
function
|
|
29659
|
+
function log18(...args) {
|
|
29660
29660
|
return process.stderr.write(util3.formatWithOptions(exports.inspectOpts, ...args) + `
|
|
29661
29661
|
`);
|
|
29662
29662
|
}
|
|
@@ -29891,14 +29891,14 @@ var require_require_in_the_middle = __commonJS((exports, module) => {
|
|
|
29891
29891
|
moduleName = parsedPath.name;
|
|
29892
29892
|
basedir = parsedPath.dir;
|
|
29893
29893
|
} else {
|
|
29894
|
-
const
|
|
29895
|
-
if (
|
|
29894
|
+
const stat = moduleDetailsFromPath(filename);
|
|
29895
|
+
if (stat === undefined) {
|
|
29896
29896
|
debug("could not parse filename: %s", filename);
|
|
29897
29897
|
return exports2;
|
|
29898
29898
|
}
|
|
29899
|
-
moduleName =
|
|
29900
|
-
basedir =
|
|
29901
|
-
const fullModuleName = resolveModuleName(
|
|
29899
|
+
moduleName = stat.name;
|
|
29900
|
+
basedir = stat.basedir;
|
|
29901
|
+
const fullModuleName = resolveModuleName(stat);
|
|
29902
29902
|
debug("resolved filename to module: %s (id: %s, resolved: %s, basedir: %s)", moduleName, id, fullModuleName, basedir);
|
|
29903
29903
|
let matchFound = false;
|
|
29904
29904
|
if (hasWhitelist) {
|
|
@@ -29960,9 +29960,9 @@ var require_require_in_the_middle = __commonJS((exports, module) => {
|
|
|
29960
29960
|
}
|
|
29961
29961
|
}
|
|
29962
29962
|
};
|
|
29963
|
-
function resolveModuleName(
|
|
29964
|
-
const normalizedPath = path.sep !== "/" ?
|
|
29965
|
-
return path.posix.join(
|
|
29963
|
+
function resolveModuleName(stat) {
|
|
29964
|
+
const normalizedPath = path.sep !== "/" ? stat.path.split(path.sep).join("/") : stat.path;
|
|
29965
|
+
return path.posix.join(stat.name, normalizedPath).replace(normalize, "");
|
|
29966
29966
|
}
|
|
29967
29967
|
});
|
|
29968
29968
|
|
|
@@ -32156,7 +32156,7 @@ var require_debug_logger = __commonJS((exports) => {
|
|
|
32156
32156
|
function isEnabled() {
|
|
32157
32157
|
return _getLoggerSettings().enabled;
|
|
32158
32158
|
}
|
|
32159
|
-
function
|
|
32159
|
+
function log18(...args) {
|
|
32160
32160
|
_maybeLog("log", ...args);
|
|
32161
32161
|
}
|
|
32162
32162
|
function warn2(...args) {
|
|
@@ -32185,7 +32185,7 @@ var require_debug_logger = __commonJS((exports) => {
|
|
|
32185
32185
|
enable,
|
|
32186
32186
|
disable,
|
|
32187
32187
|
isEnabled,
|
|
32188
|
-
log:
|
|
32188
|
+
log: log18,
|
|
32189
32189
|
warn: warn2,
|
|
32190
32190
|
error: error2
|
|
32191
32191
|
};
|
|
@@ -37184,13 +37184,13 @@ var require_internal = __commonJS((exports) => {
|
|
|
37184
37184
|
setLogAttribute(processedLogAttributes, "sentry.trace.parent_span_id", span?.spanContext().spanId);
|
|
37185
37185
|
const processedLog = { ...beforeLog, attributes: processedLogAttributes };
|
|
37186
37186
|
client.emit("beforeCaptureLog", processedLog);
|
|
37187
|
-
const
|
|
37188
|
-
if (!
|
|
37187
|
+
const log18 = beforeSendLog ? debugLogger.consoleSandbox(() => beforeSendLog(processedLog)) : processedLog;
|
|
37188
|
+
if (!log18) {
|
|
37189
37189
|
client.recordDroppedEvent("before_send", "log_item", 1);
|
|
37190
37190
|
debugBuild.DEBUG_BUILD && debugLogger.debug.warn("beforeSendLog returned null, log will not be captured.");
|
|
37191
37191
|
return;
|
|
37192
37192
|
}
|
|
37193
|
-
const { level, message: message2, attributes: logAttributes = {}, severityNumber } =
|
|
37193
|
+
const { level, message: message2, attributes: logAttributes = {}, severityNumber } = log18;
|
|
37194
37194
|
const serializedLog = {
|
|
37195
37195
|
timestamp: time.timestampInSeconds(),
|
|
37196
37196
|
level,
|
|
@@ -37203,7 +37203,7 @@ var require_internal = __commonJS((exports) => {
|
|
|
37203
37203
|
}
|
|
37204
37204
|
};
|
|
37205
37205
|
captureSerializedLog(client, serializedLog);
|
|
37206
|
-
client.emit("afterCaptureLog",
|
|
37206
|
+
client.emit("afterCaptureLog", log18);
|
|
37207
37207
|
}
|
|
37208
37208
|
function _INTERNAL_flushLogsBuffer(client, maybeLogBuffer) {
|
|
37209
37209
|
const logBuffer = maybeLogBuffer ?? _INTERNAL_getLogBuffer(client) ?? [];
|
|
@@ -38241,12 +38241,12 @@ Reason: ${reason}`);
|
|
|
38241
38241
|
weight += 8;
|
|
38242
38242
|
return weight + estimateAttributesSizeInBytes(metric.attributes);
|
|
38243
38243
|
}
|
|
38244
|
-
function estimateLogSizeInBytes(
|
|
38244
|
+
function estimateLogSizeInBytes(log18) {
|
|
38245
38245
|
let weight = 0;
|
|
38246
|
-
if (
|
|
38247
|
-
weight +=
|
|
38246
|
+
if (log18.message) {
|
|
38247
|
+
weight += log18.message.length * 2;
|
|
38248
38248
|
}
|
|
38249
|
-
return weight + estimateAttributesSizeInBytes(
|
|
38249
|
+
return weight + estimateAttributesSizeInBytes(log18.attributes);
|
|
38250
38250
|
}
|
|
38251
38251
|
function estimateAttributesSizeInBytes(attributes) {
|
|
38252
38252
|
if (!attributes) {
|
|
@@ -38647,7 +38647,7 @@ var require_offline = __commonJS((exports) => {
|
|
|
38647
38647
|
var START_DELAY = 5000;
|
|
38648
38648
|
var MAX_DELAY = 3600000;
|
|
38649
38649
|
function makeOfflineTransport(createTransport) {
|
|
38650
|
-
function
|
|
38650
|
+
function log18(...args) {
|
|
38651
38651
|
debugBuild.DEBUG_BUILD && debugLogger.debug.log("[Offline]:", ...args);
|
|
38652
38652
|
}
|
|
38653
38653
|
return (options3) => {
|
|
@@ -38675,10 +38675,10 @@ var require_offline = __commonJS((exports) => {
|
|
|
38675
38675
|
flushTimer = undefined;
|
|
38676
38676
|
const found = await store.shift();
|
|
38677
38677
|
if (found) {
|
|
38678
|
-
|
|
38678
|
+
log18("Attempting to send previously queued event");
|
|
38679
38679
|
found[0].sent_at = new Date().toISOString();
|
|
38680
38680
|
send(found, true).catch((e) => {
|
|
38681
|
-
|
|
38681
|
+
log18("Failed to retry sending", e);
|
|
38682
38682
|
});
|
|
38683
38683
|
}
|
|
38684
38684
|
}, delay));
|
|
@@ -38722,7 +38722,7 @@ var require_offline = __commonJS((exports) => {
|
|
|
38722
38722
|
await store.push(envelope$1);
|
|
38723
38723
|
}
|
|
38724
38724
|
flushWithBackOff();
|
|
38725
|
-
|
|
38725
|
+
log18("Error sending. Event queued.", e);
|
|
38726
38726
|
return {};
|
|
38727
38727
|
} else {
|
|
38728
38728
|
throw e;
|
|
@@ -40063,8 +40063,8 @@ var require_console = __commonJS((exports) => {
|
|
|
40063
40063
|
return function(...args) {
|
|
40064
40064
|
const handlerData = { args, level };
|
|
40065
40065
|
handlers.triggerHandlers("console", handlerData);
|
|
40066
|
-
const
|
|
40067
|
-
|
|
40066
|
+
const log18 = debugLogger.originalConsoleMethods[level];
|
|
40067
|
+
log18?.apply(worldwide.GLOBAL_OBJ.console, args);
|
|
40068
40068
|
};
|
|
40069
40069
|
});
|
|
40070
40070
|
});
|
|
@@ -54273,7 +54273,7 @@ var require_local_variables_async = __commonJS((exports) => {
|
|
|
54273
54273
|
var debug = require_debug();
|
|
54274
54274
|
var common2 = require_common2();
|
|
54275
54275
|
var base64WorkerScript = "LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjQzLjAgKDNmYjgxMDIpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyBlfWZyb20ibm9kZTppbnNwZWN0b3IvcHJvbWlzZXMiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIHR9ZnJvbSJub2RlOndvcmtlcl90aHJlYWRzIjtjb25zdCBuPWdsb2JhbFRoaXMsaT17fTtjb25zdCBvPSJfX1NFTlRSWV9FUlJPUl9MT0NBTF9WQVJJQUJMRVNfXyI7Y29uc3QgYT10O2Z1bmN0aW9uIHMoLi4uZSl7YS5kZWJ1ZyYmZnVuY3Rpb24oZSl7aWYoISgiY29uc29sZSJpbiBuKSlyZXR1cm4gZSgpO2NvbnN0IHQ9bi5jb25zb2xlLG89e30sYT1PYmplY3Qua2V5cyhpKTthLmZvckVhY2goZT0+e2NvbnN0IG49aVtlXTtvW2VdPXRbZV0sdFtlXT1ufSk7dHJ5e3JldHVybiBlKCl9ZmluYWxseXthLmZvckVhY2goZT0+e3RbZV09b1tlXX0pfX0oKCk9PmNvbnNvbGUubG9nKCJbTG9jYWxWYXJpYWJsZXMgV29ya2VyXSIsLi4uZSkpfWFzeW5jIGZ1bmN0aW9uIGMoZSx0LG4saSl7Y29uc3Qgbz1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pO2lbbl09by5yZXN1bHQuZmlsdGVyKGU9PiJsZW5ndGgiIT09ZS5uYW1lJiYhaXNOYU4ocGFyc2VJbnQoZS5uYW1lLDEwKSkpLnNvcnQoKGUsdCk9PnBhcnNlSW50KGUubmFtZSwxMCktcGFyc2VJbnQodC5uYW1lLDEwKSkubWFwKGU9PmUudmFsdWU/LnZhbHVlKX1hc3luYyBmdW5jdGlvbiByKGUsdCxuLGkpe2NvbnN0IG89YXdhaXQgZS5wb3N0KCJSdW50aW1lLmdldFByb3BlcnRpZXMiLHtvYmplY3RJZDp0LG93blByb3BlcnRpZXM6ITB9KTtpW25dPW8ucmVzdWx0Lm1hcChlPT5bZS5uYW1lLGUudmFsdWU/LnZhbHVlXSkucmVkdWNlKChlLFt0LG5dKT0+KGVbdF09bixlKSx7fSl9ZnVuY3Rpb24gdShlLHQpe2UudmFsdWUmJigidmFsdWUiaW4gZS52YWx1ZT92b2lkIDA9PT1lLnZhbHVlLnZhbHVlfHxudWxsPT09ZS52YWx1ZS52YWx1ZT90W2UubmFtZV09YDwke2UudmFsdWUudmFsdWV9PmA6dFtlLm5hbWVdPWUudmFsdWUudmFsdWU6ImRlc2NyaXB0aW9uImluIGUudmFsdWUmJiJmdW5jdGlvbiIhPT1lLnZhbHVlLnR5cGU/dFtlLm5hbWVdPWA8JHtlLnZhbHVlLmRlc2NyaXB0aW9ufT5gOiJ1bmRlZmluZWQiPT09ZS52YWx1ZS50eXBlJiYodFtlLm5hbWVdPSI8dW5kZWZpbmVkPiIpKX1hc3luYyBmdW5jdGlvbiBsKGUsdCl7Y29uc3Qgbj1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuZ2V0UHJvcGVydGllcyIse29iamVjdElkOnQsb3duUHJvcGVydGllczohMH0pLGk9e307Zm9yKGNvbnN0IHQgb2Ygbi5yZXN1bHQpaWYodC52YWx1ZT8ub2JqZWN0SWQmJiJBcnJheSI9PT10LnZhbHVlLmNsYXNzTmFtZSl7Y29uc3Qgbj10LnZhbHVlLm9iamVjdElkO2F3YWl0IGMoZSxuLHQubmFtZSxpKX1lbHNlIGlmKHQudmFsdWU/Lm9iamVjdElkJiYiT2JqZWN0Ij09PXQudmFsdWUuY2xhc3NOYW1lKXtjb25zdCBuPXQudmFsdWUub2JqZWN0SWQ7YXdhaXQgcihlLG4sdC5uYW1lLGkpfWVsc2UgdC52YWx1ZSYmdSh0LGkpO3JldHVybiBpfWxldCBmOyhhc3luYyBmdW5jdGlvbigpe2NvbnN0IHQ9bmV3IGU7dC5jb25uZWN0VG9NYWluVGhyZWFkKCkscygiQ29ubmVjdGVkIHRvIG1haW4gdGhyZWFkIik7bGV0IG49ITE7dC5vbigiRGVidWdnZXIucmVzdW1lZCIsKCk9PntuPSExfSksdC5vbigiRGVidWdnZXIucGF1c2VkIixlPT57bj0hMCxhc3luYyBmdW5jdGlvbihlLHtyZWFzb246dCxkYXRhOntvYmplY3RJZDpufSxjYWxsRnJhbWVzOml9KXtpZigiZXhjZXB0aW9uIiE9PXQmJiJwcm9taXNlUmVqZWN0aW9uIiE9PXQpcmV0dXJuO2lmKGY/LigpLG51bGw9PW4pcmV0dXJuO2NvbnN0IGE9W107Zm9yKGxldCB0PTA7dDxpLmxlbmd0aDt0Kyspe2NvbnN0e3Njb3BlQ2hhaW46bixmdW5jdGlvbk5hbWU6byx0aGlzOnN9PWlbdF0sYz1uLmZpbmQoZT0+ImxvY2FsIj09PWUudHlwZSkscj0iZ2xvYmFsIiE9PXMuY2xhc3NOYW1lJiZzLmNsYXNzTmFtZT9gJHtzLmNsYXNzTmFtZX0uJHtvfWA6bztpZih2b2lkIDA9PT1jPy5vYmplY3Qub2JqZWN0SWQpYVt0XT17ZnVuY3Rpb246cn07ZWxzZXtjb25zdCBuPWF3YWl0IGwoZSxjLm9iamVjdC5vYmplY3RJZCk7YVt0XT17ZnVuY3Rpb246cix2YXJzOm59fX1hd2FpdCBlLnBvc3QoIlJ1bnRpbWUuY2FsbEZ1bmN0aW9uT24iLHtmdW5jdGlvbkRlY2xhcmF0aW9uOmBmdW5jdGlvbigpIHsgdGhpcy4ke299ID0gdGhpcy4ke299IHx8ICR7SlNPTi5zdHJpbmdpZnkoYSl9OyB9YCxzaWxlbnQ6ITAsb2JqZWN0SWQ6bn0pLGF3YWl0IGUucG9zdCgiUnVudGltZS5yZWxlYXNlT2JqZWN0Iix7b2JqZWN0SWQ6bn0pfSh0LGUucGFyYW1zKS50aGVuKGFzeW5jKCk9PntuJiZhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpfSxhc3luYyBlPT57biYmYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKX0pfSksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5lbmFibGUiKTtjb25zdCBpPSExIT09YS5jYXB0dXJlQWxsRXhjZXB0aW9ucztpZihhd2FpdCB0LnBvc3QoIkRlYnVnZ2VyLnNldFBhdXNlT25FeGNlcHRpb25zIix7c3RhdGU6aT8iYWxsIjoidW5jYXVnaHQifSksaSl7Y29uc3QgZT1hLm1heEV4Y2VwdGlvbnNQZXJTZWNvbmR8fDUwO2Y9ZnVuY3Rpb24oZSx0LG4pe2xldCBpPTAsbz01LGE9MDtyZXR1cm4gc2V0SW50ZXJ2YWwoKCk9PnswPT09YT9pPmUmJihvKj0yLG4obyksbz44NjQwMCYmKG89ODY0MDApLGE9byk6KGEtPTEsMD09PWEmJnQoKSksaT0wfSwxZTMpLnVucmVmKCksKCk9PntpKz0xfX0oZSxhc3luYygpPT57cygiUmF0ZS1saW1pdCBsaWZ0ZWQuIiksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJhbGwifSl9LGFzeW5jIGU9PntzKGBSYXRlLWxpbWl0IGV4Y2VlZGVkLiBEaXNhYmxpbmcgY2FwdHVyaW5nIG9mIGNhdWdodCBleGNlcHRpb25zIGZvciAke2V9IHNlY29uZHMuYCksYXdhaXQgdC5wb3N0KCJEZWJ1Z2dlci5zZXRQYXVzZU9uRXhjZXB0aW9ucyIse3N0YXRlOiJ1bmNhdWdodCJ9KX0pfX0pKCkuY2F0Y2goZT0+e3MoIkZhaWxlZCB0byBzdGFydCBkZWJ1Z2dlciIsZSl9KSxzZXRJbnRlcnZhbCgoKT0+e30sMWU0KTs=";
|
|
54276
|
-
function
|
|
54276
|
+
function log18(...args) {
|
|
54277
54277
|
core.debug.log("[LocalVariables]", ...args);
|
|
54278
54278
|
}
|
|
54279
54279
|
var localVariablesAsyncIntegration = core.defineIntegration((integrationOptions = {}) => {
|
|
@@ -54317,10 +54317,10 @@ var require_local_variables_async = __commonJS((exports) => {
|
|
|
54317
54317
|
worker.terminate();
|
|
54318
54318
|
});
|
|
54319
54319
|
worker.once("error", (err) => {
|
|
54320
|
-
|
|
54320
|
+
log18("Worker error", err);
|
|
54321
54321
|
});
|
|
54322
54322
|
worker.once("exit", (code) => {
|
|
54323
|
-
|
|
54323
|
+
log18("Worker exit", code);
|
|
54324
54324
|
});
|
|
54325
54325
|
worker.unref();
|
|
54326
54326
|
}
|
|
@@ -55330,10 +55330,10 @@ var require_proxy = __commonJS((exports) => {
|
|
|
55330
55330
|
const proxyResponsePromise = parseProxyResponse.parseProxyResponse(socket);
|
|
55331
55331
|
socket.write(`${payload}\r
|
|
55332
55332
|
`);
|
|
55333
|
-
const { connect:
|
|
55334
|
-
req.emit("proxyConnect",
|
|
55335
|
-
this.emit("proxyConnect",
|
|
55336
|
-
if (
|
|
55333
|
+
const { connect: connect3, buffered } = await proxyResponsePromise;
|
|
55334
|
+
req.emit("proxyConnect", connect3);
|
|
55335
|
+
this.emit("proxyConnect", connect3, req);
|
|
55336
|
+
if (connect3.statusCode === 200) {
|
|
55337
55337
|
req.once("socket", resume);
|
|
55338
55338
|
if (opts.secureEndpoint) {
|
|
55339
55339
|
debugLog("Upgrading socket connection to TLS");
|
|
@@ -55574,9 +55574,9 @@ var require_client2 = __commonJS((exports) => {
|
|
|
55574
55574
|
core._INTERNAL_flushLogsBuffer(this);
|
|
55575
55575
|
};
|
|
55576
55576
|
if (serverName) {
|
|
55577
|
-
this.on("beforeCaptureLog", (
|
|
55578
|
-
|
|
55579
|
-
...
|
|
55577
|
+
this.on("beforeCaptureLog", (log18) => {
|
|
55578
|
+
log18.attributes = {
|
|
55579
|
+
...log18.attributes,
|
|
55580
55580
|
"server.address": serverName
|
|
55581
55581
|
};
|
|
55582
55582
|
});
|
|
@@ -55901,7 +55901,7 @@ var require_anr2 = __commonJS((exports) => {
|
|
|
55901
55901
|
var base64WorkerScript = "LyohIEBzZW50cnkvbm9kZS1jb3JlIDEwLjQzLjAgKDNmYjgxMDIpIHwgaHR0cHM6Ly9naXRodWIuY29tL2dldHNlbnRyeS9zZW50cnktamF2YXNjcmlwdCAqLwppbXBvcnR7U2Vzc2lvbiBhcyB0fWZyb20ibm9kZTppbnNwZWN0b3IiO2ltcG9ydHt3b3JrZXJEYXRhIGFzIG4scGFyZW50UG9ydCBhcyBlfWZyb20ibm9kZTp3b3JrZXJfdGhyZWFkcyI7aW1wb3J0e3Bvc2l4IGFzIHIsc2VwIGFzIG99ZnJvbSJub2RlOnBhdGgiO2ltcG9ydCphcyBpIGZyb20ibm9kZTpodHRwIjtpbXBvcnQqYXMgcyBmcm9tIm5vZGU6aHR0cHMiO2ltcG9ydHtSZWFkYWJsZSBhcyBjfWZyb20ibm9kZTpzdHJlYW0iO2ltcG9ydHtjcmVhdGVHemlwIGFzIHV9ZnJvbSJub2RlOnpsaWIiO2ltcG9ydCphcyBhIGZyb20ibm9kZTpuZXQiO2ltcG9ydCphcyBmIGZyb20ibm9kZTp0bHMiO2NvbnN0IGg9InVuZGVmaW5lZCI9PXR5cGVvZiBfX1NFTlRSWV9ERUJVR19ffHxfX1NFTlRSWV9ERUJVR19fLHA9Z2xvYmFsVGhpcyxkPSIxMC40My4wIjtmdW5jdGlvbiBsKCl7cmV0dXJuIG0ocCkscH1mdW5jdGlvbiBtKHQpe2NvbnN0IG49dC5fX1NFTlRSWV9fPXQuX19TRU5UUllfX3x8e307cmV0dXJuIG4udmVyc2lvbj1uLnZlcnNpb258fGQsbltkXT1uW2RdfHx7fX1mdW5jdGlvbiBnKHQsbixlPXApe2NvbnN0IHI9ZS5fX1NFTlRSWV9fPWUuX19TRU5UUllfX3x8e30sbz1yW2RdPXJbZF18fHt9O3JldHVybiBvW3RdfHwob1t0XT1uKCkpfWNvbnN0IHk9e307ZnVuY3Rpb24gYih0KXtpZighKCJjb25zb2xlImluIHApKXJldHVybiB0KCk7Y29uc3Qgbj1wLmNvbnNvbGUsZT17fSxyPU9iamVjdC5rZXlzKHkpO3IuZm9yRWFjaCh0PT57Y29uc3Qgcj15W3RdO2VbdF09blt0XSxuW3RdPXJ9KTt0cnl7cmV0dXJuIHQoKX1maW5hbGx5e3IuZm9yRWFjaCh0PT57blt0XT1lW3RdfSl9fWZ1bmN0aW9uIHYoKXtyZXR1cm4gdygpLmVuYWJsZWR9ZnVuY3Rpb24gXyh0LC4uLm4pe2gmJnYoKSYmYigoKT0+e3AuY29uc29sZVt0XShgU2VudHJ5IExvZ2dlciBbJHt0fV06YCwuLi5uKX0pfWZ1bmN0aW9uIHcoKXtyZXR1cm4gaD9nKCJsb2dnZXJTZXR0aW5ncyIsKCk9Pih7ZW5hYmxlZDohMX0pKTp7ZW5hYmxlZDohMX19Y29uc3QgUz17ZW5hYmxlOmZ1bmN0aW9uKCl7dygpLmVuYWJsZWQ9ITB9LGRpc2FibGU6ZnVuY3Rpb24oKXt3KCkuZW5hYmxlZD0hMX0saXNFbmFibGVkOnYsbG9nOmZ1bmN0aW9uKC4uLnQpe18oImxvZyIsLi4udCl9LHdhcm46ZnVuY3Rpb24oLi4udCl7Xygid2FybiIsLi4udCl9LGVycm9yOmZ1bmN0aW9uKC4uLnQpe18oImVycm9yIiwuLi50KX19LCQ9L2NhcHR1cmVNZXNzYWdlfGNhcHR1cmVFeGNlcHRpb24vO2Z1bmN0aW9uIEUodCl7cmV0dXJuIHRbdC5sZW5ndGgtMV18fHt9fWNvbnN0IHg9Ijxhbm9ueW1vdXM+Ijtjb25zdCBOPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmc7ZnVuY3Rpb24gQyh0LG4pe3JldHVybiBOLmNhbGwodCk9PT1gW29iamVjdCAke259XWB9ZnVuY3Rpb24gQSh0KXtyZXR1cm4gQyh0LCJTdHJpbmciKX1mdW5jdGlvbiBqKHQpe3JldHVybiBDKHQsIk9iamVjdCIpfWZ1bmN0aW9uIGsodCl7cmV0dXJuIEJvb2xlYW4odD8udGhlbiYmImZ1bmN0aW9uIj09dHlwZW9mIHQudGhlbil9ZnVuY3Rpb24gVCh0LG4pe3RyeXtyZXR1cm4gdCBpbnN0YW5jZW9mIG59Y2F0Y2h7cmV0dXJuITF9fWNvbnN0IEk9cDtmdW5jdGlvbiBSKHQsbil7Y29uc3QgZT10LHI9W107aWYoIWU/LnRhZ05hbWUpcmV0dXJuIiI7aWYoSS5IVE1MRWxlbWVudCYmZSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50JiZlLmRhdGFzZXQpe2lmKGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlDb21wb25lbnQ7aWYoZS5kYXRhc2V0LnNlbnRyeUVsZW1lbnQpcmV0dXJuIGUuZGF0YXNldC5zZW50cnlFbGVtZW50fXIucHVzaChlLnRhZ05hbWUudG9Mb3dlckNhc2UoKSk7Y29uc3Qgbz1uPy5sZW5ndGg/bi5maWx0ZXIodD0+ZS5nZXRBdHRyaWJ1dGUodCkpLm1hcCh0PT5bdCxlLmdldEF0dHJpYnV0ZSh0KV0pOm51bGw7aWYobz8ubGVuZ3RoKW8uZm9yRWFjaCh0PT57ci5wdXNoKGBbJHt0WzBdfT0iJHt0WzFdfSJdYCl9KTtlbHNle2UuaWQmJnIucHVzaChgIyR7ZS5pZH1gKTtjb25zdCB0PWUuY2xhc3NOYW1lO2lmKHQmJkEodCkpe2NvbnN0IG49dC5zcGxpdCgvXHMrLyk7Zm9yKGNvbnN0IHQgb2YgbilyLnB1c2goYC4ke3R9YCl9fWNvbnN0IGk9WyJhcmlhLWxhYmVsIiwidHlwZSIsIm5hbWUiLCJ0aXRsZSIsImFsdCJdO2Zvcihjb25zdCB0IG9mIGkpe2NvbnN0IG49ZS5nZXRBdHRyaWJ1dGUodCk7biYmci5wdXNoKGBbJHt0fT0iJHtufSJdYCl9cmV0dXJuIHIuam9pbigiIil9ZnVuY3Rpb24gTyh0KXtpZihmdW5jdGlvbih0KXtzd2l0Y2goTi5jYWxsKHQpKXtjYXNlIltvYmplY3QgRXJyb3JdIjpjYXNlIltvYmplY3QgRXhjZXB0aW9uXSI6Y2FzZSJbb2JqZWN0IERPTUV4Y2VwdGlvbl0iOmNhc2UiW29iamVjdCBXZWJBc3NlbWJseS5FeGNlcHRpb25dIjpyZXR1cm4hMDtkZWZhdWx0OnJldHVybiBUKHQsRXJyb3IpfX0odCkpcmV0dXJue21lc3NhZ2U6dC5tZXNzYWdlLG5hbWU6dC5uYW1lLHN0YWNrOnQuc3RhY2ssLi4uRCh0KX07aWYobj10LCJ1bmRlZmluZWQiIT10eXBlb2YgRXZlbnQmJlQobixFdmVudCkpe2NvbnN0IG49e3R5cGU6dC50eXBlLHRhcmdldDpQKHQudGFyZ2V0KSxjdXJyZW50VGFyZ2V0OlAodC5jdXJyZW50VGFyZ2V0KSwuLi5EKHQpfTtyZXR1cm4idW5kZWZpbmVkIiE9dHlwZW9mIEN1c3RvbUV2ZW50JiZUKHQsQ3VzdG9tRXZlbnQpJiYobi5kZXRhaWw9dC5kZXRhaWwpLG59cmV0dXJuIHQ7dmFyIG59ZnVuY3Rpb24gUCh0KXt0cnl7cmV0dXJuIG49dCwidW5kZWZpbmVkIiE9dHlwZW9mIEVsZW1lbnQmJlQobixFbGVtZW50KT9mdW5jdGlvbih0LG49e30pe2lmKCF0KXJldHVybiI8dW5rbm93bj4iO3RyeXtsZXQgZT10O2NvbnN0IHI9NSxvPVtdO2xldCBpPTAscz0wO2NvbnN0IGM9IiA+ICIsdT1jLmxlbmd0aDtsZXQgYTtjb25zdCBmPUFycmF5LmlzQXJyYXkobik/bjpuLmtleUF0dHJzLGg9IUFycmF5LmlzQXJyYXkobikmJm4ubWF4U3RyaW5nTGVuZ3RofHw4MDtmb3IoO2UmJmkrKzxyJiYoYT1SKGUsZiksISgiaHRtbCI9PT1hfHxpPjEmJnMrby5sZW5ndGgqdSthLmxlbmd0aD49aCkpOylvLnB1c2goYSkscys9YS5sZW5ndGgsZT1lLnBhcmVudE5vZGU7cmV0dXJuIG8ucmV2ZXJzZSgpLmpvaW4oYyl9Y2F0Y2h7cmV0dXJuIjx1bmtub3duPiJ9fSh0KTpPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodCl9Y2F0Y2h7cmV0dXJuIjx1bmtub3duPiJ9dmFyIG59ZnVuY3Rpb24gRCh0KXtpZigib2JqZWN0Ij09dHlwZW9mIHQmJm51bGwhPT10KXtjb25zdCBuPXt9O2Zvcihjb25zdCBlIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQsZSkmJihuW2VdPXRbZV0pO3JldHVybiBufXJldHVybnt9fWxldCBVLE07ZnVuY3Rpb24gTCh0KXtpZih2b2lkIDAhPT1VKXJldHVybiBVP1UodCk6dCgpO2NvbnN0IG49U3ltYm9sLmZvcigiX19TRU5UUllfU0FGRV9SQU5ET01fSURfV1JBUFBFUl9fIiksZT1wO3JldHVybiBuIGluIGUmJiJmdW5jdGlvbiI9PXR5cGVvZiBlW25dPyhVPWVbbl0sVSh0KSk6KFU9bnVsbCx0KCkpfWZ1bmN0aW9uIEIoKXtyZXR1cm4gTCgoKT0+TWF0aC5yYW5kb20oKSl9ZnVuY3Rpb24gVygpe3JldHVybiBMKCgpPT5EYXRlLm5vdygpKX1mdW5jdGlvbiB6KHQsbj0wKXtyZXR1cm4ic3RyaW5nIiE9dHlwZW9mIHR8fDA9PT1ufHx0Lmxlbmd0aDw9bj90OmAke3Quc2xpY2UoMCxuKX0uLi5gfWZ1bmN0aW9uIEYodD1mdW5jdGlvbigpe2NvbnN0IHQ9cDtyZXR1cm4gdC5jcnlwdG98fHQubXNDcnlwdG99KCkpe3RyeXtpZih0Py5yYW5kb21VVUlEKXJldHVybiBMKCgpPT50LnJhbmRvbVVVSUQoKSkucmVwbGFjZSgvLS9nLCIiKX1jYXRjaHt9cmV0dXJuIE18fChNPVsxZTddKzFlMys0ZTMrOGUzKzFlMTEpLE0ucmVwbGFjZSgvWzAxOF0vZyx0PT4odF4oMTYqQigpJjE1KT4+dC80KS50b1N0cmluZygxNikpfWZ1bmN0aW9uIEcoKXtyZXR1cm4gVygpLzFlM31sZXQgSDtmdW5jdGlvbiBKKCl7cmV0dXJuKEg/PyhIPWZ1bmN0aW9uKCl7Y29uc3R7cGVyZm9ybWFuY2U6dH09cDtpZighdD8ubm93fHwhdC50aW1lT3JpZ2luKXJldHVybiBHO2NvbnN0IG49dC50aW1lT3JpZ2luO3JldHVybigpPT4obitMKCgpPT50Lm5vdygpKSkvMWUzfSgpKSkoKX1mdW5jdGlvbiBZKHQpe2NvbnN0IG49SigpLGU9e3NpZDpGKCksaW5pdDohMCx0aW1lc3RhbXA6bixzdGFydGVkOm4sZHVyYXRpb246MCxzdGF0dXM6Im9rIixlcnJvcnM6MCxpZ25vcmVEdXJhdGlvbjohMSx0b0pTT046KCk9PmZ1bmN0aW9uKHQpe3JldHVybntzaWQ6YCR7dC5zaWR9YCxpbml0OnQuaW5pdCxzdGFydGVkOm5ldyBEYXRlKDFlMyp0LnN0YXJ0ZWQpLnRvSVNPU3RyaW5nKCksdGltZXN0YW1wOm5ldyBEYXRlKDFlMyp0LnRpbWVzdGFtcCkudG9JU09TdHJpbmcoKSxzdGF0dXM6dC5zdGF0dXMsZXJyb3JzOnQuZXJyb3JzLGRpZDoibnVtYmVyIj09dHlwZW9mIHQuZGlkfHwic3RyaW5nIj09dHlwZW9mIHQuZGlkP2Ake3QuZGlkfWA6dm9pZCAwLGR1cmF0aW9uOnQuZHVyYXRpb24sYWJub3JtYWxfbWVjaGFuaXNtOnQuYWJub3JtYWxfbWVjaGFuaXNtLGF0dHJzOntyZWxlYXNlOnQucmVsZWFzZSxlbnZpcm9ubWVudDp0LmVudmlyb25tZW50LGlwX2FkZHJlc3M6dC5pcEFkZHJlc3MsdXNlcl9hZ2VudDp0LnVzZXJBZ2VudH19fShlKX07cmV0dXJuIHQmJlYoZSx0KSxlfWZ1bmN0aW9uIFYodCxuPXt9KXtpZihuLnVzZXImJighdC5pcEFkZHJlc3MmJm4udXNlci5pcF9hZGRyZXNzJiYodC5pcEFkZHJlc3M9bi51c2VyLmlwX2FkZHJlc3MpLHQuZGlkfHxuLmRpZHx8KHQuZGlkPW4udXNlci5pZHx8bi51c2VyLmVtYWlsfHxuLnVzZXIudXNlcm5hbWUpKSx0LnRpbWVzdGFtcD1uLnRpbWVzdGFtcHx8SigpLG4uYWJub3JtYWxfbWVjaGFuaXNtJiYodC5hYm5vcm1hbF9tZWNoYW5pc209bi5hYm5vcm1hbF9tZWNoYW5pc20pLG4uaWdub3JlRHVyYXRpb24mJih0Lmlnbm9yZUR1cmF0aW9uPW4uaWdub3JlRHVyYXRpb24pLG4uc2lkJiYodC5zaWQ9MzI9PT1uLnNpZC5sZW5ndGg/bi5zaWQ6RigpKSx2b2lkIDAhPT1uLmluaXQmJih0LmluaXQ9bi5pbml0KSwhdC5kaWQmJm4uZGlkJiYodC5kaWQ9YCR7bi5kaWR9YCksIm51bWJlciI9PXR5cGVvZiBuLnN0YXJ0ZWQmJih0LnN0YXJ0ZWQ9bi5zdGFydGVkKSx0Lmlnbm9yZUR1cmF0aW9uKXQuZHVyYXRpb249dm9pZCAwO2Vsc2UgaWYoIm51bWJlciI9PXR5cGVvZiBuLmR1cmF0aW9uKXQuZHVyYXRpb249bi5kdXJhdGlvbjtlbHNle2NvbnN0IG49dC50aW1lc3RhbXAtdC5zdGFydGVkO3QuZHVyYXRpb249bj49MD9uOjB9bi5yZWxlYXNlJiYodC5yZWxlYXNlPW4ucmVsZWFzZSksbi5lbnZpcm9ubWVudCYmKHQuZW52aXJvbm1lbnQ9bi5lbnZpcm9ubWVudCksIXQuaXBBZGRyZXNzJiZuLmlwQWRkcmVzcyYmKHQuaXBBZGRyZXNzPW4uaXBBZGRyZXNzKSwhdC51c2VyQWdlbnQmJm4udXNlckFnZW50JiYodC51c2VyQWdlbnQ9bi51c2VyQWdlbnQpLCJudW1iZXIiPT10eXBlb2Ygbi5lcnJvcnMmJih0LmVycm9ycz1uLmVycm9ycyksbi5zdGF0dXMmJih0LnN0YXR1cz1uLnN0YXR1cyl9ZnVuY3Rpb24gSyh0LG4sZT0yKXtpZighbnx8Im9iamVjdCIhPXR5cGVvZiBufHxlPD0wKXJldHVybiBuO2lmKHQmJjA9PT1PYmplY3Qua2V5cyhuKS5sZW5ndGgpcmV0dXJuIHQ7Y29uc3Qgcj17Li4udH07Zm9yKGNvbnN0IHQgaW4gbilPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobix0KSYmKHJbdF09SyhyW3RdLG5bdF0sZS0xKSk7cmV0dXJuIHJ9ZnVuY3Rpb24gWigpe3JldHVybiBGKCl9ZnVuY3Rpb24gcSgpe3JldHVybiBGKCkuc3Vic3RyaW5nKDE2KX1jb25zdCBRPSJfc2VudHJ5U3BhbiI7ZnVuY3Rpb24gWCh0LG4pe24/ZnVuY3Rpb24odCxuLGUpe3RyeXtPYmplY3QuZGVmaW5lUHJvcGVydHkodCxuLHt2YWx1ZTplLHdyaXRhYmxlOiEwLGNvbmZpZ3VyYWJsZTohMH0pfWNhdGNoe2gmJlMubG9nKGBGYWlsZWQgdG8gYWRkIG5vbi1lbnVtZXJhYmxlIHByb3BlcnR5ICIke259IiB0byBvYmplY3RgLHQpfX0odCxRLG4pOmRlbGV0ZSB0W1FdfWZ1bmN0aW9uIHR0KHQpe3JldHVybiB0W1FdfWNsYXNzIG50e2NvbnN0cnVjdG9yKCl7dGhpcy50PSExLHRoaXMubz1bXSx0aGlzLmk9W10sdGhpcy51PVtdLHRoaXMuaD1bXSx0aGlzLnA9e30sdGhpcy5sPXt9LHRoaXMubT17fSx0aGlzLnY9e30sdGhpcy5fPXt9LHRoaXMuUz17fSx0aGlzLk49e3RyYWNlSWQ6WigpLHNhbXBsZVJhbmQ6QigpfX1jbG9uZSgpe2NvbnN0IHQ9bmV3IG50O3JldHVybiB0LnU9Wy4uLnRoaXMudV0sdC5sPXsuLi50aGlzLmx9LHQubT17Li4udGhpcy5tfSx0LnY9ey4uLnRoaXMudn0sdC5fPXsuLi50aGlzLl99LHRoaXMuXy5mbGFncyYmKHQuXy5mbGFncz17dmFsdWVzOlsuLi50aGlzLl8uZmxhZ3MudmFsdWVzXX0pLHQucD10aGlzLnAsdC5DPXRoaXMuQyx0LkE9dGhpcy5BLHQuaj10aGlzLmosdC5rPXRoaXMuayx0Lmk9Wy4uLnRoaXMuaV0sdC5oPVsuLi50aGlzLmhdLHQuUz17Li4udGhpcy5TfSx0Lk49ey4uLnRoaXMuTn0sdC5UPXRoaXMuVCx0Lkk9dGhpcy5JLHQuUj10aGlzLlIsWCh0LHR0KHRoaXMpKSx0fXNldENsaWVudCh0KXt0aGlzLlQ9dH1zZXRMYXN0RXZlbnRJZCh0KXt0aGlzLkk9dH1nZXRDbGllbnQoKXtyZXR1cm4gdGhpcy5UfWxhc3RFdmVudElkKCl7cmV0dXJuIHRoaXMuSX1hZGRTY29wZUxpc3RlbmVyKHQpe3RoaXMuby5wdXNoKHQpfWFkZEV2ZW50UHJvY2Vzc29yKHQpe3JldHVybiB0aGlzLmkucHVzaCh0KSx0aGlzfXNldFVzZXIodCl7cmV0dXJuIHRoaXMucD10fHx7ZW1haWw6dm9pZCAwLGlkOnZvaWQgMCxpcF9hZGRyZXNzOnZvaWQgMCx1c2VybmFtZTp2b2lkIDB9LHRoaXMuQSYmVih0aGlzLkEse3VzZXI6dH0pLHRoaXMuTygpLHRoaXN9Z2V0VXNlcigpe3JldHVybiB0aGlzLnB9c2V0Q29udmVyc2F0aW9uSWQodCl7cmV0dXJuIHRoaXMuUj10fHx2b2lkIDAsdGhpcy5PKCksdGhpc31zZXRUYWdzKHQpe3JldHVybiB0aGlzLmw9ey4uLnRoaXMubCwuLi50fSx0aGlzLk8oKSx0aGlzfXNldFRhZyh0LG4pe3JldHVybiB0aGlzLnNldFRhZ3Moe1t0XTpufSl9c2V0QXR0cmlidXRlcyh0KXtyZXR1cm4gdGhpcy5tPXsuLi50aGlzLm0sLi4udH0sdGhpcy5PKCksdGhpc31zZXRBdHRyaWJ1dGUodCxuKXtyZXR1cm4gdGhpcy5zZXRBdHRyaWJ1dGVzKHtbdF06bn0pfXJlbW92ZUF0dHJpYnV0ZSh0KXtyZXR1cm4gdCBpbiB0aGlzLm0mJihkZWxldGUgdGhpcy5tW3RdLHRoaXMuTygpKSx0aGlzfXNldEV4dHJhcyh0KXtyZXR1cm4gdGhpcy52PXsuLi50aGlzLnYsLi4udH0sdGhpcy5PKCksdGhpc31zZXRFeHRyYSh0LG4pe3JldHVybiB0aGlzLnY9ey4uLnRoaXMudixbdF06bn0sdGhpcy5PKCksdGhpc31zZXRGaW5nZXJwcmludCh0KXtyZXR1cm4gdGhpcy5rPXQsdGhpcy5PKCksdGhpc31zZXRMZXZlbCh0KXtyZXR1cm4gdGhpcy5DPXQsdGhpcy5PKCksdGhpc31zZXRUcmFuc2FjdGlvbk5hbWUodCl7cmV0dXJuIHRoaXMuaj10LHRoaXMuTygpLHRoaXN9c2V0Q29udGV4dCh0LG4pe3JldHVybiBudWxsPT09bj9kZWxldGUgdGhpcy5fW3RdOnRoaXMuX1t0XT1uLHRoaXMuTygpLHRoaXN9c2V0U2Vzc2lvbih0KXtyZXR1cm4gdD90aGlzLkE9dDpkZWxldGUgdGhpcy5BLHRoaXMuTygpLHRoaXN9Z2V0U2Vzc2lvbigpe3JldHVybiB0aGlzLkF9dXBkYXRlKHQpe2lmKCF0KXJldHVybiB0aGlzO2NvbnN0IG49ImZ1bmN0aW9uIj09dHlwZW9mIHQ/dCh0aGlzKTp0LGU9biBpbnN0YW5jZW9mIG50P24uZ2V0U2NvcGVEYXRhKCk6aihuKT90OnZvaWQgMCx7dGFnczpyLGF0dHJpYnV0ZXM6byxleHRyYTppLHVzZXI6cyxjb250ZXh0czpjLGxldmVsOnUsZmluZ2VycHJpbnQ6YT1bXSxwcm9wYWdhdGlvbkNvbnRleHQ6Zixjb252ZXJzYXRpb25JZDpofT1lfHx7fTtyZXR1cm4gdGhpcy5sPXsuLi50aGlzLmwsLi4ucn0sdGhpcy5tPXsuLi50aGlzLm0sLi4ub30sdGhpcy52PXsuLi50aGlzLnYsLi4uaX0sdGhpcy5fPXsuLi50aGlzLl8sLi4uY30scyYmT2JqZWN0LmtleXMocykubGVuZ3RoJiYodGhpcy5wPXMpLHUmJih0aGlzLkM9dSksYS5sZW5ndGgmJih0aGlzLms9YSksZiYmKHRoaXMuTj1mKSxoJiYodGhpcy5SPWgpLHRoaXN9Y2xlYXIoKXtyZXR1cm4gdGhpcy51PVtdLHRoaXMubD17fSx0aGlzLm09e30sdGhpcy52PXt9LHRoaXMucD17fSx0aGlzLl89e30sdGhpcy5DPXZvaWQgMCx0aGlzLmo9dm9pZCAwLHRoaXMuaz12b2lkIDAsdGhpcy5BPXZvaWQgMCx0aGlzLlI9dm9pZCAwLFgodGhpcyx2b2lkIDApLHRoaXMuaD1bXSx0aGlzLnNldFByb3BhZ2F0aW9uQ29udGV4dCh7dHJhY2VJZDpaKCksc2FtcGxlUmFuZDpCKCl9KSx0aGlzLk8oKSx0aGlzfWFkZEJyZWFkY3J1bWIodCxuKXtjb25zdCBlPSJudW1iZXIiPT10eXBlb2Ygbj9uOjEwMDtpZihlPD0wKXJldHVybiB0aGlzO2NvbnN0IHI9e3RpbWVzdGFtcDpHKCksLi4udCxtZXNzYWdlOnQubWVzc2FnZT96KHQubWVzc2FnZSwyMDQ4KTp0Lm1lc3NhZ2V9O3JldHVybiB0aGlzLnUucHVzaChyKSx0aGlzLnUubGVuZ3RoPmUmJih0aGlzLnU9dGhpcy51LnNsaWNlKC1lKSx0aGlzLlQ/LnJlY29yZERyb3BwZWRFdmVudCgiYnVmZmVyX292ZXJmbG93IiwibG9nX2l0ZW0iKSksdGhpcy5PKCksdGhpc31nZXRMYXN0QnJlYWRjcnVtYigpe3JldHVybiB0aGlzLnVbdGhpcy51Lmxlbmd0aC0xXX1jbGVhckJyZWFkY3J1bWJzKCl7cmV0dXJuIHRoaXMudT1bXSx0aGlzLk8oKSx0aGlzfWFkZEF0dGFjaG1lbnQodCl7cmV0dXJuIHRoaXMuaC5wdXNoKHQpLHRoaXN9Y2xlYXJBdHRhY2htZW50cygpe3JldHVybiB0aGlzLmg9W10sdGhpc31nZXRTY29wZURhdGEoKXtyZXR1cm57YnJlYWRjcnVtYnM6dGhpcy51LGF0dGFjaG1lbnRzOnRoaXMuaCxjb250ZXh0czp0aGlzLl8sdGFnczp0aGlzLmwsYXR0cmlidXRlczp0aGlzLm0sZXh0cmE6dGhpcy52LHVzZXI6dGhpcy5wLGxldmVsOnRoaXMuQyxmaW5nZXJwcmludDp0aGlzLmt8fFtdLGV2ZW50UHJvY2Vzc29yczp0aGlzLmkscHJvcGFnYXRpb25Db250ZXh0OnRoaXMuTixzZGtQcm9jZXNzaW5nTWV0YWRhdGE6dGhpcy5TLHRyYW5zYWN0aW9uTmFtZTp0aGlzLmosc3Bhbjp0dCh0aGlzKSxjb252ZXJzYXRpb25JZDp0aGlzLlJ9fXNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh0KXtyZXR1cm4gdGhpcy5TPUsodGhpcy5TLHQsMiksdGhpc31zZXRQcm9wYWdhdGlvbkNvbnRleHQodCl7cmV0dXJuIHRoaXMuTj10LHRoaXN9Z2V0UHJvcGFnYXRpb25Db250ZXh0KCl7cmV0dXJuIHRoaXMuTn1jYXB0dXJlRXhjZXB0aW9uKHQsbil7Y29uc3QgZT1uPy5ldmVudF9pZHx8RigpO2lmKCF0aGlzLlQpcmV0dXJuIGgmJlMud2FybigiTm8gY2xpZW50IGNvbmZpZ3VyZWQgb24gc2NvcGUgLSB3aWxsIG5vdCBjYXB0dXJlIGV4Y2VwdGlvbiEiKSxlO2NvbnN0IHI9bmV3IEVycm9yKCJTZW50cnkgc3ludGhldGljRXhjZXB0aW9uIik7cmV0dXJuIHRoaXMuVC5jYXB0dXJlRXhjZXB0aW9uKHQse29yaWdpbmFsRXhjZXB0aW9uOnQsc3ludGhldGljRXhjZXB0aW9uOnIsLi4ubixldmVudF9pZDplfSx0aGlzKSxlfWNhcHR1cmVNZXNzYWdlKHQsbixlKXtjb25zdCByPWU/LmV2ZW50X2lkfHxGKCk7aWYoIXRoaXMuVClyZXR1cm4gaCYmUy53YXJuKCJObyBjbGllbnQgY29uZmlndXJlZCBvbiBzY29wZSAtIHdpbGwgbm90IGNhcHR1cmUgbWVzc2FnZSEiKSxyO2NvbnN0IG89ZT8uc3ludGhldGljRXhjZXB0aW9uPz9uZXcgRXJyb3IodCk7cmV0dXJuIHRoaXMuVC5jYXB0dXJlTWVzc2FnZSh0LG4se29yaWdpbmFsRXhjZXB0aW9uOnQsc3ludGhldGljRXhjZXB0aW9uOm8sLi4uZSxldmVudF9pZDpyfSx0aGlzKSxyfWNhcHR1cmVFdmVudCh0LG4pe2NvbnN0IGU9dC5ldmVudF9pZHx8bj8uZXZlbnRfaWR8fEYoKTtyZXR1cm4gdGhpcy5UPyh0aGlzLlQuY2FwdHVyZUV2ZW50KHQsey4uLm4sZXZlbnRfaWQ6ZX0sdGhpcyksZSk6KGgmJlMud2FybigiTm8gY2xpZW50IGNvbmZpZ3VyZWQgb24gc2NvcGUgLSB3aWxsIG5vdCBjYXB0dXJlIGV2ZW50ISIpLGUpfU8oKXt0aGlzLnR8fCh0aGlzLnQ9ITAsdGhpcy5vLmZvckVhY2godD0+e3QodGhpcyl9KSx0aGlzLnQ9ITEpfX1jbGFzcyBldHtjb25zdHJ1Y3Rvcih0LG4pe2xldCBlLHI7ZT10fHxuZXcgbnQscj1ufHxuZXcgbnQsdGhpcy5QPVt7c2NvcGU6ZX1dLHRoaXMuRD1yfXdpdGhTY29wZSh0KXtjb25zdCBuPXRoaXMuVSgpO2xldCBlO3RyeXtlPXQobil9Y2F0Y2godCl7dGhyb3cgdGhpcy5NKCksdH1yZXR1cm4gayhlKT9lLnRoZW4odD0+KHRoaXMuTSgpLHQpLHQ9Pnt0aHJvdyB0aGlzLk0oKSx0fSk6KHRoaXMuTSgpLGUpfWdldENsaWVudCgpe3JldHVybiB0aGlzLmdldFN0YWNrVG9wKCkuY2xpZW50fWdldFNjb3BlKCl7cmV0dXJuIHRoaXMuZ2V0U3RhY2tUb3AoKS5zY29wZX1nZXRJc29sYXRpb25TY29wZSgpe3JldHVybiB0aGlzLkR9Z2V0U3RhY2tUb3AoKXtyZXR1cm4gdGhpcy5QW3RoaXMuUC5sZW5ndGgtMV19VSgpe2NvbnN0IHQ9dGhpcy5nZXRTY29wZSgpLmNsb25lKCk7cmV0dXJuIHRoaXMuUC5wdXNoKHtjbGllbnQ6dGhpcy5nZXRDbGllbnQoKSxzY29wZTp0fSksdH1NKCl7cmV0dXJuISh0aGlzLlAubGVuZ3RoPD0xKSYmISF0aGlzLlAucG9wKCl9fWZ1bmN0aW9uIHJ0KCl7Y29uc3QgdD1tKGwoKSk7cmV0dXJuIHQuc3RhY2s9dC5zdGFja3x8bmV3IGV0KGcoImRlZmF1bHRDdXJyZW50U2NvcGUiLCgpPT5uZXcgbnQpLGcoImRlZmF1bHRJc29sYXRpb25TY29wZSIsKCk9Pm5ldyBudCkpfWZ1bmN0aW9uIG90KHQpe3JldHVybiBydCgpLndpdGhTY29wZSh0KX1mdW5jdGlvbiBpdCh0LG4pe2NvbnN0IGU9cnQoKTtyZXR1cm4gZS53aXRoU2NvcGUoKCk9PihlLmdldFN0YWNrVG9wKCkuc2NvcGU9dCxuKHQpKSl9ZnVuY3Rpb24gc3QodCl7cmV0dXJuIHJ0KCkud2l0aFNjb3BlKCgpPT50KHJ0KCkuZ2V0SXNvbGF0aW9uU2NvcGUoKSkpfWZ1bmN0aW9uIGN0KHQpe2NvbnN0IG49bSh0KTtyZXR1cm4gbi5hY3M/bi5hY3M6e3dpdGhJc29sYXRpb25TY29wZTpzdCx3aXRoU2NvcGU6b3Qsd2l0aFNldFNjb3BlOml0LHdpdGhTZXRJc29sYXRpb25TY29wZToodCxuKT0+c3QobiksZ2V0Q3VycmVudFNjb3BlOigpPT5ydCgpLmdldFNjb3BlKCksZ2V0SXNvbGF0aW9uU2NvcGU6KCk9PnJ0KCkuZ2V0SXNvbGF0aW9uU2NvcGUoKX19ZnVuY3Rpb24gdXQoKXtyZXR1cm4gY3QobCgpKS5nZXRDdXJyZW50U2NvcGUoKS5nZXRDbGllbnQoKX1mdW5jdGlvbiBhdCh0KXtpZih0KXtpZigib2JqZWN0Ij09dHlwZW9mIHQmJiJkZXJlZiJpbiB0JiYiZnVuY3Rpb24iPT10eXBlb2YgdC5kZXJlZil0cnl7cmV0dXJuIHQuZGVyZWYoKX1jYXRjaHtyZXR1cm59cmV0dXJuIHR9fWZ1bmN0aW9uIGZ0KHQpe2NvbnN0IG49dDtyZXR1cm57c2NvcGU6bi5fc2VudHJ5U2NvcGUsaXNvbGF0aW9uU2NvcGU6YXQobi5fc2VudHJ5SXNvbGF0aW9uU2NvcGUpfX1jb25zdCBodD0vXnNlbnRyeS0vO2Z1bmN0aW9uIHB0KHQpe2NvbnN0IG49ZnVuY3Rpb24odCl7aWYoIXR8fCFBKHQpJiYhQXJyYXkuaXNBcnJheSh0KSlyZXR1cm47aWYoQXJyYXkuaXNBcnJheSh0KSlyZXR1cm4gdC5yZWR1Y2UoKHQsbik9Pntjb25zdCBlPWR0KG4pO3JldHVybiBPYmplY3QuZW50cmllcyhlKS5mb3JFYWNoKChbbixlXSk9Pnt0W25dPWV9KSx0fSx7fSk7cmV0dXJuIGR0KHQpfSh0KTtpZighbilyZXR1cm47Y29uc3QgZT1PYmplY3QuZW50cmllcyhuKS5yZWR1Y2UoKHQsW24sZV0pPT57aWYobi5tYXRjaChodCkpe3Rbbi5zbGljZSg3KV09ZX1yZXR1cm4gdH0se30pO3JldHVybiBPYmplY3Qua2V5cyhlKS5sZW5ndGg+MD9lOnZvaWQgMH1mdW5jdGlvbiBkdCh0KXtyZXR1cm4gdC5zcGxpdCgiLCIpLm1hcCh0PT57Y29uc3Qgbj10LmluZGV4T2YoIj0iKTtpZigtMT09PW4pcmV0dXJuW107cmV0dXJuW3Quc2xpY2UoMCxuKSx0LnNsaWNlKG4rMSldLm1hcCh0PT57dHJ5e3JldHVybiBkZWNvZGVVUklDb21wb25lbnQodC50cmltKCkpfWNhdGNoe3JldHVybn19KX0pLnJlZHVjZSgodCxbbixlXSk9PihuJiZlJiYodFtuXT1lKSx0KSx7fSl9Y29uc3QgbHQ9L15vKFxkKylcLi87ZnVuY3Rpb24gbXQodCxuPSExKXtjb25zdHtob3N0OmUscGF0aDpyLHBhc3M6byxwb3J0OmkscHJvamVjdElkOnMscHJvdG9jb2w6YyxwdWJsaWNLZXk6dX09dDtyZXR1cm5gJHtjfTovLyR7dX0ke24mJm8/YDoke299YDoiIn1AJHtlfSR7aT9gOiR7aX1gOiIifS8ke3I/YCR7cn0vYDpyfSR7c31gfWZ1bmN0aW9uIGd0KHQpe2NvbnN0IG49dC5nZXRPcHRpb25zKCkse2hvc3Q6ZX09dC5nZXREc24oKXx8e307bGV0IHI7cmV0dXJuIG4ub3JnSWQ/cj1TdHJpbmcobi5vcmdJZCk6ZSYmKHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj10Lm1hdGNoKGx0KTtyZXR1cm4gbj8uWzFdfShlKSkscn1mdW5jdGlvbiB5dCh0KXtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmUsaXNSZW1vdGU6cn09dC5zcGFuQ29udGV4dCgpLG89cj9uOnd0KHQpLnBhcmVudF9zcGFuX2lkLGk9ZnQodCkuc2NvcGU7cmV0dXJue3BhcmVudF9zcGFuX2lkOm8sc3Bhbl9pZDpyP2k/LmdldFByb3BhZ2F0aW9uQ29udGV4dCgpLnByb3BhZ2F0aW9uU3BhbklkfHxxKCk6bix0cmFjZV9pZDplfX1mdW5jdGlvbiBidCh0KXtyZXR1cm4gdCYmdC5sZW5ndGg+MD90Lm1hcCgoe2NvbnRleHQ6e3NwYW5JZDp0LHRyYWNlSWQ6bix0cmFjZUZsYWdzOmUsLi4ucn0sYXR0cmlidXRlczpvfSk9Pih7c3Bhbl9pZDp0LHRyYWNlX2lkOm4sc2FtcGxlZDoxPT09ZSxhdHRyaWJ1dGVzOm8sLi4ucn0pKTp2b2lkIDB9ZnVuY3Rpb24gdnQodCl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiB0P190KHQpOkFycmF5LmlzQXJyYXkodCk/dFswXSt0WzFdLzFlOTp0IGluc3RhbmNlb2YgRGF0ZT9fdCh0LmdldFRpbWUoKSk6SigpfWZ1bmN0aW9uIF90KHQpe3JldHVybiB0Pjk5OTk5OTk5OTk/dC8xZTM6dH1mdW5jdGlvbiB3dCh0KXtpZihmdW5jdGlvbih0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgdC5nZXRTcGFuSlNPTn0odCkpcmV0dXJuIHQuZ2V0U3BhbkpTT04oKTtjb25zdHtzcGFuSWQ6bix0cmFjZUlkOmV9PXQuc3BhbkNvbnRleHQoKTtpZihmdW5jdGlvbih0KXtjb25zdCBuPXQ7cmV0dXJuISEobi5hdHRyaWJ1dGVzJiZuLnN0YXJ0VGltZSYmbi5uYW1lJiZuLmVuZFRpbWUmJm4uc3RhdHVzKX0odCkpe2NvbnN0e2F0dHJpYnV0ZXM6cixzdGFydFRpbWU6byxuYW1lOmksZW5kVGltZTpzLHN0YXR1czpjLGxpbmtzOnV9PXQ7cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLGRhdGE6cixkZXNjcmlwdGlvbjppLHBhcmVudF9zcGFuX2lkOiJwYXJlbnRTcGFuSWQiaW4gdD90LnBhcmVudFNwYW5JZDoicGFyZW50U3BhbkNvbnRleHQiaW4gdD90LnBhcmVudFNwYW5Db250ZXh0Py5zcGFuSWQ6dm9pZCAwLHN0YXJ0X3RpbWVzdGFtcDp2dChvKSx0aW1lc3RhbXA6dnQocyl8fHZvaWQgMCxzdGF0dXM6U3QoYyksb3A6clsic2VudHJ5Lm9wIl0sb3JpZ2luOnJbInNlbnRyeS5vcmlnaW4iXSxsaW5rczpidCh1KX19cmV0dXJue3NwYW5faWQ6bix0cmFjZV9pZDplLHN0YXJ0X3RpbWVzdGFtcDowLGRhdGE6e319fWZ1bmN0aW9uIFN0KHQpe2lmKHQmJjAhPT10LmNvZGUpcmV0dXJuIDE9PT10LmNvZGU/Im9rIjp0Lm1lc3NhZ2V8fCJpbnRlcm5hbF9lcnJvciJ9ZnVuY3Rpb24gJHQodCl7cmV0dXJuIHQuX3NlbnRyeVJvb3RTcGFufHx0fWZ1bmN0aW9uIEV0KHQpe2NvbnN0IG49dXQoKTtpZighbilyZXR1cm57fTtjb25zdCBlPSR0KHQpLHI9d3QoZSksbz1yLmRhdGEsaT1lLnNwYW5Db250ZXh0KCkudHJhY2VTdGF0ZSxzPWk/LmdldCgic2VudHJ5LnNhbXBsZV9yYXRlIik/P29bInNlbnRyeS5zYW1wbGVfcmF0ZSJdPz9vWyJzZW50cnkucHJldmlvdXNfdHJhY2Vfc2FtcGxlX3JhdGUiXTtmdW5jdGlvbiBjKHQpe3JldHVybiJudW1iZXIiIT10eXBlb2YgcyYmInN0cmluZyIhPXR5cGVvZiBzfHwodC5zYW1wbGVfcmF0ZT1gJHtzfWApLHR9Y29uc3QgdT1lLl9mcm96ZW5Ec2M7aWYodSlyZXR1cm4gYyh1KTtjb25zdCBhPWk/LmdldCgic2VudHJ5LmRzYyIpLGY9YSYmcHQoYSk7aWYoZilyZXR1cm4gYyhmKTtjb25zdCBoPWZ1bmN0aW9uKHQsbil7Y29uc3QgZT1uLmdldE9wdGlvbnMoKSx7cHVibGljS2V5OnJ9PW4uZ2V0RHNuKCl8fHt9LG89e2Vudmlyb25tZW50OmUuZW52aXJvbm1lbnR8fCJwcm9kdWN0aW9uIixyZWxlYXNlOmUucmVsZWFzZSxwdWJsaWNfa2V5OnIsdHJhY2VfaWQ6dCxvcmdfaWQ6Z3Qobil9O3JldHVybiBuLmVtaXQoImNyZWF0ZURzYyIsbyksb30odC5zcGFuQ29udGV4dCgpLnRyYWNlSWQsbikscD1vWyJzZW50cnkuc291cmNlIl0sZD1yLmRlc2NyaXB0aW9uO3JldHVybiJ1cmwiIT09cCYmZCYmKGgudHJhbnNhY3Rpb249ZCksZnVuY3Rpb24oKXtpZigiYm9vbGVhbiI9PXR5cGVvZiBfX1NFTlRSWV9UUkFDSU5HX18mJiFfX1NFTlRSWV9UUkFDSU5HX18pcmV0dXJuITE7Y29uc3QgdD11dCgpPy5nZXRPcHRpb25zKCk7cmV0dXJuISghdHx8bnVsbD09dC50cmFjZXNTYW1wbGVSYXRlJiYhdC50cmFjZXNTYW1wbGVyKX0oKSYmKGguc2FtcGxlZD1TdHJpbmcoZnVuY3Rpb24odCl7Y29uc3R7dHJhY2VGbGFnczpufT10LnNwYW5Db250ZXh0KCk7cmV0dXJuIDE9PT1ufShlKSksaC5zYW1wbGVfcmFuZD1pPy5nZXQoInNlbnRyeS5zYW1wbGVfcmFuZCIpPz9mdChlKS5zY29wZT8uZ2V0UHJvcGFnYXRpb25Db250ZXh0KCkuc2FtcGxlUmFuZC50b1N0cmluZygpKSxjKGgpLG4uZW1pdCgiY3JlYXRlRHNjIixoLGUpLGh9ZnVuY3Rpb24geHQodCxuPTEwMCxlPTEvMCl7dHJ5e3JldHVybiBOdCgiIix0LG4sZSl9Y2F0Y2godCl7cmV0dXJue0VSUk9SOmAqKm5vbi1zZXJpYWxpemFibGUqKiAoJHt0fSlgfX19ZnVuY3Rpb24gTnQodCxuLGU9MS8wLHI9MS8wLG89ZnVuY3Rpb24oKXtjb25zdCB0PW5ldyBXZWFrU2V0O2Z1bmN0aW9uIG4obil7cmV0dXJuISF0LmhhcyhuKXx8KHQuYWRkKG4pLCExKX1mdW5jdGlvbiBlKG4pe3QuZGVsZXRlKG4pfXJldHVybltuLGVdfSgpKXtjb25zdFtpLHNdPW87aWYobnVsbD09bnx8WyJib29sZWFuIiwic3RyaW5nIl0uaW5jbHVkZXModHlwZW9mIG4pfHwibnVtYmVyIj09dHlwZW9mIG4mJk51bWJlci5pc0Zpbml0ZShuKSlyZXR1cm4gbjtjb25zdCBjPWZ1bmN0aW9uKHQsbil7dHJ5e2lmKCJkb21haW4iPT09dCYmbiYmIm9iamVjdCI9PXR5cGVvZiBuJiZuLkwpcmV0dXJuIltEb21haW5dIjtpZigiZG9tYWluRW1pdHRlciI9PT10KXJldHVybiJbRG9tYWluRW1pdHRlcl0iO2lmKCJ1bmRlZmluZWQiIT10eXBlb2YgZ2xvYmFsJiZuPT09Z2xvYmFsKXJldHVybiJbR2xvYmFsXSI7aWYoInVuZGVmaW5lZCIhPXR5cGVvZiB3aW5kb3cmJm49PT13aW5kb3cpcmV0dXJuIltXaW5kb3ddIjtpZigidW5kZWZpbmVkIiE9dHlwZW9mIGRvY3VtZW50JiZuPT09ZG9jdW1lbnQpcmV0dXJuIltEb2N1bWVudF0iO2lmKCJvYmplY3QiPT10eXBlb2YoZT1uKSYmbnVsbCE9PWUmJihlLl9faXNWdWV8fGUuQnx8ZS5fX3ZfaXNWTm9kZSkpcmV0dXJuIGZ1bmN0aW9uKHQpe3JldHVybiJfX3ZfaXNWTm9kZSJpbiB0JiZ0Ll9fdl9pc1ZOb2RlPyJbVnVlVk5vZGVdIjoiW1Z1ZVZpZXdNb2RlbF0ifShuKTtpZihmdW5jdGlvbih0KXtyZXR1cm4gaih0KSYmIm5hdGl2ZUV2ZW50ImluIHQmJiJwcmV2ZW50RGVmYXVsdCJpbiB0JiYic3RvcFByb3BhZ2F0aW9uImluIHR9KG4pKXJldHVybiJbU3ludGhldGljRXZlbnRdIjtpZigibnVtYmVyIj09dHlwZW9mIG4mJiFOdW1iZXIuaXNGaW5pdGUobikpcmV0dXJuYFske259XWA7aWYoImZ1bmN0aW9uIj09dHlwZW9mIG4pcmV0dXJuYFtGdW5jdGlvbjogJHtmdW5jdGlvbih0KXt0cnl7cmV0dXJuIHQmJiJmdW5jdGlvbiI9PXR5cGVvZiB0JiZ0Lm5hbWV8fHh9Y2F0Y2h7cmV0dXJuIHh9fShuKX1dYDtpZigic3ltYm9sIj09dHlwZW9mIG4pcmV0dXJuYFske1N0cmluZyhuKX1dYDtpZigiYmlnaW50Ij09dHlwZW9mIG4pcmV0dXJuYFtCaWdJbnQ6ICR7U3RyaW5nKG4pfV1gO2NvbnN0IHI9ZnVuY3Rpb24odCl7Y29uc3Qgbj1PYmplY3QuZ2V0UHJvdG90eXBlT2YodCk7cmV0dXJuIG4/LmNvbnN0cnVjdG9yP24uY29uc3RydWN0b3IubmFtZToibnVsbCBwcm90b3R5cGUifShuKTtyZXR1cm4vXkhUTUwoXHcqKUVsZW1lbnQkLy50ZXN0KHIpP2BbSFRNTEVsZW1lbnQ6ICR7cn1dYDpgW29iamVjdCAke3J9XWB9Y2F0Y2godCl7cmV0dXJuYCoqbm9uLXNlcmlhbGl6YWJsZSoqICgke3R9KWB9dmFyIGV9KHQsbik7aWYoIWMuc3RhcnRzV2l0aCgiW29iamVjdCAiKSlyZXR1cm4gYztpZihuLl9fc2VudHJ5X3NraXBfbm9ybWFsaXphdGlvbl9fKXJldHVybiBuO2NvbnN0IHU9Im51bWJlciI9PXR5cGVvZiBuLl9fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXz9uLl9fc2VudHJ5X292ZXJyaWRlX25vcm1hbGl6YXRpb25fZGVwdGhfXzplO2lmKDA9PT11KXJldHVybiBjLnJlcGxhY2UoIm9iamVjdCAiLCIiKTtpZihpKG4pKXJldHVybiJbQ2lyY3VsYXIgfl0iO2NvbnN0IGE9bjtpZihhJiYiZnVuY3Rpb24iPT10eXBlb2YgYS50b0pTT04pdHJ5e3JldHVybiBOdCgiIixhLnRvSlNPTigpLHUtMSxyLG8pfWNhdGNoe31jb25zdCBmPUFycmF5LmlzQXJyYXkobik/W106e307bGV0IGg9MDtjb25zdCBwPU8obik7Zm9yKGNvbnN0IHQgaW4gcCl7aWYoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChwLHQpKWNvbnRpbnVlO2lmKGg+PXIpe2ZbdF09IltNYXhQcm9wZXJ0aWVzIH5dIjticmVha31jb25zdCBuPXBbdF07Zlt0XT1OdCh0LG4sdS0xLHIsbyksaCsrfXJldHVybiBzKG4pLGZ9ZnVuY3Rpb24gQ3QodCxuKXtjb25zdCBlPW4ucmVwbGFjZSgvXFwvZywiLyIpLnJlcGxhY2UoL1t8XFx7fSgpW1xdXiQrKj8uXS9nLCJcXCQmIik7bGV0IHI9dDt0cnl7cj1kZWNvZGVVUkkodCl9Y2F0Y2h7fXJldHVybiByLnJlcGxhY2UoL1xcL2csIi8iKS5yZXBsYWNlKC93ZWJwYWNrOlwvPy9nLCIiKS5yZXBsYWNlKG5ldyBSZWdFeHAoYChmaWxlOi8vKT8vKiR7ZX0vKmAsImlnIiksImFwcDovLy8iKX1mdW5jdGlvbiBBdCh0LG49W10pe3JldHVyblt0LG5dfWZ1bmN0aW9uIGp0KHQsbil7Y29uc3QgZT10WzFdO2Zvcihjb25zdCB0IG9mIGUpe2lmKG4odCx0WzBdLnR5cGUpKXJldHVybiEwfXJldHVybiExfWZ1bmN0aW9uIGt0KHQpe2NvbnN0IG49bShwKTtyZXR1cm4gbi5lbmNvZGVQb2x5ZmlsbD9uLmVuY29kZVBvbHlmaWxsKHQpOihuZXcgVGV4dEVuY29kZXIpLmVuY29kZSh0KX1mdW5jdGlvbiBUdCh0KXtjb25zdFtuLGVdPXQ7bGV0IHI9SlNPTi5zdHJpbmdpZnkobik7ZnVuY3Rpb24gbyh0KXsic3RyaW5nIj09dHlwZW9mIHI/cj0ic3RyaW5nIj09dHlwZW9mIHQ/cit0OltrdChyKSx0XTpyLnB1c2goInN0cmluZyI9PXR5cGVvZiB0P2t0KHQpOnQpfWZvcihjb25zdCB0IG9mIGUpe2NvbnN0W24sZV09dDtpZihvKGBcbiR7SlNPTi5zdHJpbmdpZnkobil9XG5gKSwic3RyaW5nIj09dHlwZW9mIGV8fGUgaW5zdGFuY2VvZiBVaW50OEFycmF5KW8oZSk7ZWxzZXtsZXQgdDt0cnl7dD1KU09OLnN0cmluZ2lmeShlKX1jYXRjaHt0PUpTT04uc3RyaW5naWZ5KHh0KGUpKX1vKHQpfX1yZXR1cm4ic3RyaW5nIj09dHlwZW9mIHI/cjpmdW5jdGlvbih0KXtjb25zdCBuPXQucmVkdWNlKCh0LG4pPT50K24ubGVuZ3RoLDApLGU9bmV3IFVpbnQ4QXJyYXkobik7bGV0IHI9MDtmb3IoY29uc3QgbiBvZiB0KWUuc2V0KG4scikscis9bi5sZW5ndGg7cmV0dXJuIGV9KHIpfWNvbnN0IEl0PXtzZXNzaW9uOiJzZXNzaW9uIixzZXNzaW9uczoic2Vzc2lvbiIsYXR0YWNobWVudDoiYXR0YWNobWVudCIsdHJhbnNhY3Rpb246InRyYW5zYWN0aW9uIixldmVudDoiZXJyb3IiLGNsaWVudF9yZXBvcnQ6ImludGVybmFsIix1c2VyX3JlcG9ydDoiZGVmYXVsdCIscHJvZmlsZToicHJvZmlsZSIscHJvZmlsZV9jaHVuazoicHJvZmlsZSIscmVwbGF5X2V2ZW50OiJyZXBsYXkiLHJlcGxheV9yZWNvcmRpbmc6InJlcGxheSIsY2hlY2tfaW46Im1vbml0b3IiLGZlZWRiYWNrOiJmZWVkYmFjayIsc3Bhbjoic3BhbiIscmF3X3NlY3VyaXR5OiJzZWN1cml0eSIsbG9nOiJsb2dfaXRlbSIsbWV0cmljOiJtZXRyaWMiLHRyYWNlX21ldHJpYzoibWV0cmljIn07ZnVuY3Rpb24gUnQodCl7aWYoIXQ/LnNkaylyZXR1cm47Y29uc3R7bmFtZTpuLHZlcnNpb246ZX09dC5zZGs7cmV0dXJue25hbWU6bix2ZXJzaW9uOmV9fWZ1bmN0aW9uIE90KHQsbixlLHIpe2NvbnN0IG89UnQoZSksaT10LnR5cGUmJiJyZXBsYXlfZXZlbnQiIT09dC50eXBlP3QudHlwZToiZXZlbnQiOyFmdW5jdGlvbih0LG4pe2lmKCFuKXJldHVybiB0O2NvbnN0IGU9dC5zZGt8fHt9O3Quc2RrPXsuLi5lLG5hbWU6ZS5uYW1lfHxuLm5hbWUsdmVyc2lvbjplLnZlcnNpb258fG4udmVyc2lvbixpbnRlZ3JhdGlvbnM6Wy4uLnQuc2RrPy5pbnRlZ3JhdGlvbnN8fFtdLC4uLm4uaW50ZWdyYXRpb25zfHxbXV0scGFja2FnZXM6Wy4uLnQuc2RrPy5wYWNrYWdlc3x8W10sLi4ubi5wYWNrYWdlc3x8W11dLHNldHRpbmdzOnQuc2RrPy5zZXR0aW5nc3x8bi5zZXR0aW5ncz97Li4udC5zZGs/LnNldHRpbmdzLC4uLm4uc2V0dGluZ3N9OnZvaWQgMH19KHQsZT8uc2RrKTtjb25zdCBzPWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE/LmR5bmFtaWNTYW1wbGluZ0NvbnRleHQ7cmV0dXJue2V2ZW50X2lkOnQuZXZlbnRfaWQsc2VudF9hdDoobmV3IERhdGUpLnRvSVNPU3RyaW5nKCksLi4ubiYme3NkazpufSwuLi4hIWUmJnImJntkc246bXQocil9LC4uLm8mJnt0cmFjZTpvfX19KHQsbyxyLG4pO2RlbGV0ZSB0LnNka1Byb2Nlc3NpbmdNZXRhZGF0YTtyZXR1cm4gQXQocyxbW3t0eXBlOml9LHRdXSl9Y29uc3QgUHQ9Il9fU0VOVFJZX1NVUFBSRVNTX1RSQUNJTkdfXyI7ZnVuY3Rpb24gRHQodCl7Y29uc3Qgbj1jdChsKCkpO3JldHVybiBuLnN1cHByZXNzVHJhY2luZz9uLnN1cHByZXNzVHJhY2luZyh0KTpmdW5jdGlvbiguLi50KXtjb25zdCBuPWN0KGwoKSk7aWYoMj09PXQubGVuZ3RoKXtjb25zdFtlLHJdPXQ7cmV0dXJuIGU/bi53aXRoU2V0U2NvcGUoZSxyKTpuLndpdGhTY29wZShyKX1yZXR1cm4gbi53aXRoU2NvcGUodFswXSl9KG49PntuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1B0XTohMH0pO2NvbnN0IGU9dCgpO3JldHVybiBuLnNldFNES1Byb2Nlc3NpbmdNZXRhZGF0YSh7W1B0XTp2b2lkIDB9KSxlfSl9Y2xhc3MgVXR7Y29uc3RydWN0b3IodCl7dGhpcy5XPTAsdGhpcy5GPVtdLHRoaXMuRyh0KX10aGVuKHQsbil7cmV0dXJuIG5ldyBVdCgoZSxyKT0+e3RoaXMuRi5wdXNoKFshMSxuPT57aWYodCl0cnl7ZSh0KG4pKX1jYXRjaCh0KXtyKHQpfWVsc2UgZShuKX0sdD0+e2lmKG4pdHJ5e2Uobih0KSl9Y2F0Y2godCl7cih0KX1lbHNlIHIodCl9XSksdGhpcy5IKCl9KX1jYXRjaCh0KXtyZXR1cm4gdGhpcy50aGVuKHQ9PnQsdCl9ZmluYWxseSh0KXtyZXR1cm4gbmV3IFV0KChuLGUpPT57bGV0IHIsbztyZXR1cm4gdGhpcy50aGVuKG49PntvPSExLHI9bix0JiZ0KCl9LG49PntvPSEwLHI9bix0JiZ0KCl9KS50aGVuKCgpPT57bz9lKHIpOm4ocil9KX0pfUgoKXtpZigwPT09dGhpcy5XKXJldHVybjtjb25zdCB0PXRoaXMuRi5zbGljZSgpO3RoaXMuRj1bXSx0LmZvckVhY2godD0+e3RbMF18fCgxPT09dGhpcy5XJiZ0WzFdKHRoaXMuSiksMj09PXRoaXMuVyYmdFsyXSh0aGlzLkopLHRbMF09ITApfSl9Ryh0KXtjb25zdCBuPSh0LG4pPT57MD09PXRoaXMuVyYmKGsobik/bi50aGVuKGUscik6KHRoaXMuVz10LHRoaXMuSj1uLHRoaXMuSCgpKSl9LGU9dD0+e24oMSx0KX0scj10PT57bigyLHQpfTt0cnl7dChlLHIpfWNhdGNoKHQpe3IodCl9fX1mdW5jdGlvbiBNdCh0LG4pe2NvbnN0e2ZpbmdlcnByaW50OmUsc3BhbjpyLGJyZWFkY3J1bWJzOm8sc2RrUHJvY2Vzc2luZ01ldGFkYXRhOml9PW47IWZ1bmN0aW9uKHQsbil7Y29uc3R7ZXh0cmE6ZSx0YWdzOnIsdXNlcjpvLGNvbnRleHRzOmksbGV2ZWw6cyx0cmFuc2FjdGlvbk5hbWU6Y309bjtPYmplY3Qua2V5cyhlKS5sZW5ndGgmJih0LmV4dHJhPXsuLi5lLC4uLnQuZXh0cmF9KTtPYmplY3Qua2V5cyhyKS5sZW5ndGgmJih0LnRhZ3M9ey4uLnIsLi4udC50YWdzfSk7T2JqZWN0LmtleXMobykubGVuZ3RoJiYodC51c2VyPXsuLi5vLC4uLnQudXNlcn0pO09iamVjdC5rZXlzKGkpLmxlbmd0aCYmKHQuY29udGV4dHM9ey4uLmksLi4udC5jb250ZXh0c30pO3MmJih0LmxldmVsPXMpO2MmJiJ0cmFuc2FjdGlvbiIhPT10LnR5cGUmJih0LnRyYW5zYWN0aW9uPWMpfSh0LG4pLHImJmZ1bmN0aW9uKHQsbil7dC5jb250ZXh0cz17dHJhY2U6eXQobiksLi4udC5jb250ZXh0c30sdC5zZGtQcm9jZXNzaW5nTWV0YWRhdGE9e2R5bmFtaWNTYW1wbGluZ0NvbnRleHQ6RXQobiksLi4udC5zZGtQcm9jZXNzaW5nTWV0YWRhdGF9O2NvbnN0IGU9JHQobikscj13dChlKS5kZXNjcmlwdGlvbjtyJiYhdC50cmFuc2FjdGlvbiYmInRyYW5zYWN0aW9uIj09PXQudHlwZSYmKHQudHJhbnNhY3Rpb249cil9KHQsciksZnVuY3Rpb24odCxuKXt0LmZpbmdlcnByaW50PXQuZmluZ2VycHJpbnQ/QXJyYXkuaXNBcnJheSh0LmZpbmdlcnByaW50KT90LmZpbmdlcnByaW50Olt0LmZpbmdlcnByaW50XTpbXSxuJiYodC5maW5nZXJwcmludD10LmZpbmdlcnByaW50LmNvbmNhdChuKSk7dC5maW5nZXJwcmludC5sZW5ndGh8fGRlbGV0ZSB0LmZpbmdlcnByaW50fSh0LGUpLGZ1bmN0aW9uKHQsbil7Y29uc3QgZT1bLi4udC5icmVhZGNydW1ic3x8W10sLi4ubl07dC5icmVhZGNydW1icz1lLmxlbmd0aD9lOnZvaWQgMH0odCxvKSxmdW5jdGlvbih0LG4pe3Quc2RrUHJvY2Vzc2luZ01ldGFkYXRhPXsuLi50LnNka1Byb2Nlc3NpbmdNZXRhZGF0YSwuLi5ufX0odCxpKX1jb25zdCBMdD1TeW1ib2wuZm9yKCJTZW50cnlCdWZmZXJGdWxsRXJyb3IiKTtmdW5jdGlvbiBCdCh0PTEwMCl7Y29uc3Qgbj1uZXcgU2V0O2Z1bmN0aW9uIGUodCl7bi5kZWxldGUodCl9cmV0dXJue2dldCAkKCl7cmV0dXJuIEFycmF5LmZyb20obil9LGFkZDpmdW5jdGlvbihyKXtpZighKG4uc2l6ZTx0KSlyZXR1cm4gbz1MdCxuZXcgVXQoKHQsbik9PntuKG8pfSk7dmFyIG87Y29uc3QgaT1yKCk7cmV0dXJuIG4uYWRkKGkpLGkudGhlbigoKT0+ZShpKSwoKT0+ZShpKSksaX0sZHJhaW46ZnVuY3Rpb24odCl7aWYoIW4uc2l6ZSlyZXR1cm4gZT0hMCxuZXcgVXQodD0+e3QoZSl9KTt2YXIgZTtjb25zdCByPVByb21pc2UuYWxsU2V0dGxlZChBcnJheS5mcm9tKG4pKS50aGVuKCgpPT4hMCk7aWYoIXQpcmV0dXJuIHI7Y29uc3Qgbz1bcixuZXcgUHJvbWlzZShuPT57cmV0dXJuIm9iamVjdCI9PXR5cGVvZihlPXNldFRpbWVvdXQoKCk9Pm4oITEpLHQpKSYmImZ1bmN0aW9uIj09dHlwZW9mIGUudW5yZWYmJmUudW5yZWYoKSxlO3ZhciBlfSldO3JldHVybiBQcm9taXNlLnJhY2Uobyl9fX1mdW5jdGlvbiBXdCh0LHtzdGF0dXNDb2RlOm4saGVhZGVyczplfSxyPVcoKSl7Y29uc3Qgbz17Li4udH0saT1lPy5bIngtc2VudHJ5LXJhdGUtbGltaXRzIl0scz1lPy5bInJldHJ5LWFmdGVyIl07aWYoaSlmb3IoY29uc3QgdCBvZiBpLnRyaW0oKS5zcGxpdCgiLCIpKXtjb25zdFtuLGUsLCxpXT10LnNwbGl0KCI6Iiw1KSxzPXBhcnNlSW50KG4sMTApLGM9MWUzKihpc05hTihzKT82MDpzKTtpZihlKWZvcihjb25zdCB0IG9mIGUuc3BsaXQoIjsiKSkibWV0cmljX2J1Y2tldCI9PT10JiZpJiYhaS5zcGxpdCgiOyIpLmluY2x1ZGVzKCJjdXN0b20iKXx8KG9bdF09citjKTtlbHNlIG8uYWxsPXIrY31lbHNlIHM/by5hbGw9citmdW5jdGlvbih0LG49VygpKXtjb25zdCBlPXBhcnNlSW50KGAke3R9YCwxMCk7aWYoIWlzTmFOKGUpKXJldHVybiAxZTMqZTtjb25zdCByPURhdGUucGFyc2UoYCR7dH1gKTtyZXR1cm4gaXNOYU4ocik/NmU0OnItbn0ocyxyKTo0Mjk9PT1uJiYoby5hbGw9cis2ZTQpO3JldHVybiBvfWZ1bmN0aW9uIHp0KHQsbixlPUJ0KHQuYnVmZmVyU2l6ZXx8NjQpKXtsZXQgcj17fTtyZXR1cm57c2VuZDpmdW5jdGlvbih0KXtjb25zdCBvPVtdO2lmKGp0KHQsKHQsbik9Pntjb25zdCBlPWZ1bmN0aW9uKHQpe3JldHVybiBJdFt0XX0obik7KGZ1bmN0aW9uKHQsbixlPVcoKSl7cmV0dXJuIGZ1bmN0aW9uKHQsbil7cmV0dXJuIHRbbl18fHQuYWxsfHwwfSh0LG4pPmV9KShyLGUpfHxvLnB1c2godCl9KSwwPT09by5sZW5ndGgpcmV0dXJuIFByb21pc2UucmVzb2x2ZSh7fSk7Y29uc3QgaT1BdCh0WzBdLG8pLHM9dD0+eyFmdW5jdGlvbih0LG4pe3JldHVybiBqdCh0LCh0LGUpPT5uLmluY2x1ZGVzKGUpKX0oaSxbImNsaWVudF9yZXBvcnQiXSk/anQoaSwodCxuKT0+e30pOmgmJlMud2FybihgRHJvcHBpbmcgY2xpZW50IHJlcG9ydC4gV2lsbCBub3Qgc2VuZCBvdXRjb21lcyAocmVhc29uOiAke3R9KS5gKX07cmV0dXJuIGUuYWRkKCgpPT5uKHtib2R5OlR0KGkpfSkudGhlbih0PT40MTM9PT10LnN0YXR1c0NvZGU/KGgmJlMuZXJyb3IoIlNlbnRyeSByZXNwb25kZWQgd2l0aCBzdGF0dXMgY29kZSA0MTMuIEVudmVsb3BlIHdhcyBkaXNjYXJkZWQgZHVlIHRvIGV4Y2VlZGluZyBzaXplIGxpbWl0cy4iKSxzKCJzZW5kX2Vycm9yIiksdCk6KGgmJnZvaWQgMCE9PXQuc3RhdHVzQ29kZSYmKHQuc3RhdHVzQ29kZTwyMDB8fHQuc3RhdHVzQ29kZT49MzAwKSYmUy53YXJuKGBTZW50cnkgcmVzcG9uZGVkIHdpdGggc3RhdHVzIGNvZGUgJHt0LnN0YXR1c0NvZGV9IHRvIHNlbnQgZXZlbnQuYCkscj1XdChyLHQpLHQpLHQ9Pnt0aHJvdyBzKCJuZXR3b3JrX2Vycm9yIiksaCYmUy5lcnJvcigiRW5jb3VudGVyZWQgZXJyb3IgcnVubmluZyB0cmFuc3BvcnQgcmVxdWVzdDoiLHQpLHR9KSkudGhlbih0PT50LHQ9PntpZih0PT09THQpcmV0dXJuIGgmJlMuZXJyb3IoIlNraXBwZWQgc2VuZGluZyBldmVudCBiZWNhdXNlIGJ1ZmZlciBpcyBmdWxsLiIpLHMoInF1ZXVlX292ZXJmbG93IiksUHJvbWlzZS5yZXNvbHZlKHt9KTt0aHJvdyB0fSl9LGZsdXNoOnQ9PmUuZHJhaW4odCl9fWNvbnN0IEZ0PS9eKFxTKzpcXHxcLz8pKFtcc1xTXSo/KSgoPzpcLnsxLDJ9fFteL1xcXSs/fCkoXC5bXi4vXFxdKnwpKSg/OlsvXFxdKikkLztmdW5jdGlvbiBHdCh0KXtjb25zdCBuPWZ1bmN0aW9uKHQpe2NvbnN0IG49dC5sZW5ndGg+MTAyND9gPHRydW5jYXRlZD4ke3Quc2xpY2UoLTEwMjQpfWA6dCxlPUZ0LmV4ZWMobik7cmV0dXJuIGU/ZS5zbGljZSgxKTpbXX0odCksZT1uWzBdfHwiIjtsZXQgcj1uWzFdO3JldHVybiBlfHxyPyhyJiYocj1yLnNsaWNlKDAsci5sZW5ndGgtMSkpLGUrcik6Ii4ifWZ1bmN0aW9uIEh0KHQsbj0hMSl7cmV0dXJuIShufHx0JiYhdC5zdGFydHNXaXRoKCIvIikmJiF0Lm1hdGNoKC9eW0EtWl06LykmJiF0LnN0YXJ0c1dpdGgoIi4iKSYmIXQubWF0Y2goL15bYS16QS1aXShbYS16QS1aMC05LlwtK10pKjpcL1wvLykpJiZ2b2lkIDAhPT10JiYhdC5pbmNsdWRlcygibm9kZV9tb2R1bGVzLyIpfWNvbnN0IEp0PVN5bWJvbCgiQWdlbnRCYXNlSW50ZXJuYWxTdGF0ZSIpO2NsYXNzIFl0IGV4dGVuZHMgaS5BZ2VudHtjb25zdHJ1Y3Rvcih0KXtzdXBlcih0KSx0aGlzW0p0XT17fX1pc1NlY3VyZUVuZHBvaW50KHQpe2lmKHQpe2lmKCJib29sZWFuIj09dHlwZW9mIHQuc2VjdXJlRW5kcG9pbnQpcmV0dXJuIHQuc2VjdXJlRW5kcG9pbnQ7aWYoInN0cmluZyI9PXR5cGVvZiB0LnByb3RvY29sKXJldHVybiJodHRwczoiPT09dC5wcm90b2NvbH1jb25zdHtzdGFjazpufT1uZXcgRXJyb3I7cmV0dXJuInN0cmluZyI9PXR5cGVvZiBuJiZuLnNwbGl0KCJcbiIpLnNvbWUodD0+LTEhPT10LmluZGV4T2YoIihodHRwcy5qczoiKXx8LTEhPT10LmluZGV4T2YoIm5vZGU6aHR0cHM6IikpfWNyZWF0ZVNvY2tldCh0LG4sZSl7Y29uc3Qgcj17Li4ubixzZWN1cmVFbmRwb2ludDp0aGlzLmlzU2VjdXJlRW5kcG9pbnQobil9O1Byb21pc2UucmVzb2x2ZSgpLnRoZW4oKCk9PnRoaXMuY29ubmVjdCh0LHIpKS50aGVuKG89PntpZihvIGluc3RhbmNlb2YgaS5BZ2VudClyZXR1cm4gby5hZGRSZXF1ZXN0KHQscik7dGhpc1tKdF0uY3VycmVudFNvY2tldD1vLHN1cGVyLmNyZWF0ZVNvY2tldCh0LG4sZSl9LGUpfWNyZWF0ZUNvbm5lY3Rpb24oKXtjb25zdCB0PXRoaXNbSnRdLmN1cnJlbnRTb2NrZXQ7aWYodGhpc1tKdF0uY3VycmVudFNvY2tldD12b2lkIDAsIXQpdGhyb3cgbmV3IEVycm9yKCJObyBzb2NrZXQgd2FzIHJldHVybmVkIGluIHRoZSBgY29ubmVjdCgpYCBmdW5jdGlvbiIpO3JldHVybiB0fWdldCBkZWZhdWx0UG9ydCgpe3JldHVybiB0aGlzW0p0XS5kZWZhdWx0UG9ydD8/KCJodHRwczoiPT09dGhpcy5wcm90b2NvbD80NDM6ODApfXNldCBkZWZhdWx0UG9ydCh0KXt0aGlzW0p0XSYmKHRoaXNbSnRdLmRlZmF1bHRQb3J0PXQpfWdldCBwcm90b2NvbCgpe3JldHVybiB0aGlzW0p0XS5wcm90b2NvbD8/KHRoaXMuaXNTZWN1cmVFbmRwb2ludCgpPyJodHRwczoiOiJodHRwOiIpfXNldCBwcm90b2NvbCh0KXt0aGlzW0p0XSYmKHRoaXNbSnRdLnByb3RvY29sPXQpfX1mdW5jdGlvbiBWdCguLi50KXtTLmxvZygiW2h0dHBzLXByb3h5LWFnZW50OnBhcnNlLXByb3h5LXJlc3BvbnNlXSIsLi4udCl9ZnVuY3Rpb24gS3QodCl7cmV0dXJuIG5ldyBQcm9taXNlKChuLGUpPT57bGV0IHI9MDtjb25zdCBvPVtdO2Z1bmN0aW9uIGkoKXtjb25zdCBjPXQucmVhZCgpO2M/ZnVuY3Rpb24oYyl7by5wdXNoKGMpLHIrPWMubGVuZ3RoO2NvbnN0IHU9QnVmZmVyLmNvbmNhdChvLHIpLGE9dS5pbmRleE9mKCJcclxuXHJcbiIpO2lmKC0xPT09YSlyZXR1cm4gVnQoImhhdmUgbm90IHJlY2VpdmVkIGVuZCBvZiBIVFRQIGhlYWRlcnMgeWV0Li4uIiksdm9pZCBpKCk7Y29uc3QgZj11LnN1YmFycmF5KDAsYSkudG9TdHJpbmcoImFzY2lpIikuc3BsaXQoIlxyXG4iKSxoPWYuc2hpZnQoKTtpZighaClyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoIk5vIGhlYWRlciByZWNlaXZlZCBmcm9tIHByb3h5IENPTk5FQ1QgcmVzcG9uc2UiKSk7Y29uc3QgcD1oLnNwbGl0KCIgIiksZD0rKHBbMV18fDApLGw9cC5zbGljZSgyKS5qb2luKCIgIiksbT17fTtmb3IoY29uc3QgbiBvZiBmKXtpZighbiljb250aW51ZTtjb25zdCByPW4uaW5kZXhPZigiOiIpO2lmKC0xPT09cilyZXR1cm4gdC5kZXN0cm95KCksZShuZXcgRXJyb3IoYEludmFsaWQgaGVhZGVyIGZyb20gcHJveHkgQ09OTkVDVCByZXNwb25zZTogIiR7bn0iYCkpO2NvbnN0IG89bi5zbGljZSgwLHIpLnRvTG93ZXJDYXNlKCksaT1uLnNsaWNlKHIrMSkudHJpbVN0YXJ0KCkscz1tW29dOyJzdHJpbmciPT10eXBlb2Ygcz9tW29dPVtzLGldOkFycmF5LmlzQXJyYXkocyk/cy5wdXNoKGkpOm1bb109aX1WdCgiZ290IHByb3h5IHNlcnZlciByZXNwb25zZTogJW8gJW8iLGgsbSkscygpLG4oe2Nvbm5lY3Q6e3N0YXR1c0NvZGU6ZCxzdGF0dXNUZXh0OmwsaGVhZGVyczptfSxidWZmZXJlZDp1fSl9KGMpOnQub25jZSgicmVhZGFibGUiLGkpfWZ1bmN0aW9uIHMoKXt0LnJlbW92ZUxpc3RlbmVyKCJlbmQiLGMpLHQucmVtb3ZlTGlzdGVuZXIoImVycm9yIix1KSx0LnJlbW92ZUxpc3RlbmVyKCJyZWFkYWJsZSIsaSl9ZnVuY3Rpb24gYygpe3MoKSxWdCgib25lbmQiKSxlKG5ldyBFcnJvcigiUHJveHkgY29ubmVjdGlvbiBlbmRlZCBiZWZvcmUgcmVjZWl2aW5nIENPTk5FQ1QgcmVzcG9uc2UiKSl9ZnVuY3Rpb24gdSh0KXtzKCksVnQoIm9uZXJyb3IgJW8iLHQpLGUodCl9dC5vbigiZXJyb3IiLHUpLHQub24oImVuZCIsYyksaSgpfSl9ZnVuY3Rpb24gWnQoLi4udCl7Uy5sb2coIltodHRwcy1wcm94eS1hZ2VudF0iLC4uLnQpfWNsYXNzIHF0IGV4dGVuZHMgWXR7c3RhdGljIF9faW5pdFN0YXRpYygpe3RoaXMucHJvdG9jb2xzPVsiaHR0cCIsImh0dHBzIl19Y29uc3RydWN0b3IodCxuKXtzdXBlcihuKSx0aGlzLm9wdGlvbnM9e30sdGhpcy5wcm94eT0ic3RyaW5nIj09dHlwZW9mIHQ/bmV3IFVSTCh0KTp0LHRoaXMucHJveHlIZWFkZXJzPW4/LmhlYWRlcnM/P3t9LFp0KCJDcmVhdGluZyBuZXcgSHR0cHNQcm94eUFnZW50IGluc3RhbmNlOiAlbyIsdGhpcy5wcm94eS5ocmVmKTtjb25zdCBlPSh0aGlzLnByb3h5Lmhvc3RuYW1lfHx0aGlzLnByb3h5Lmhvc3QpLnJlcGxhY2UoL15cW3xcXSQvZywiIikscj10aGlzLnByb3h5LnBvcnQ/cGFyc2VJbnQodGhpcy5wcm94eS5wb3J0LDEwKToiaHR0cHM6Ij09PXRoaXMucHJveHkucHJvdG9jb2w/NDQzOjgwO3RoaXMuY29ubmVjdE9wdHM9e0FMUE5Qcm90b2NvbHM6WyJodHRwLzEuMSJdLC4uLm4/WHQobiwiaGVhZGVycyIpOm51bGwsaG9zdDplLHBvcnQ6cn19YXN5bmMgY29ubmVjdCh0LG4pe2NvbnN0e3Byb3h5OmV9PXRoaXM7aWYoIW4uaG9zdCl0aHJvdyBuZXcgVHlwZUVycm9yKCdObyAiaG9zdCIgcHJvdmlkZWQnKTtsZXQgcjtpZigiaHR0cHM6Ij09PWUucHJvdG9jb2wpe1p0KCJDcmVhdGluZyBgdGxzLlNvY2tldGA6ICVvIix0aGlzLmNvbm5lY3RPcHRzKTtjb25zdCB0PXRoaXMuY29ubmVjdE9wdHMuc2VydmVybmFtZXx8dGhpcy5jb25uZWN0T3B0cy5ob3N0O3I9Zi5jb25uZWN0KHsuLi50aGlzLmNvbm5lY3RPcHRzLHNlcnZlcm5hbWU6dCYmYS5pc0lQKHQpP3ZvaWQgMDp0fSl9ZWxzZSBadCgiQ3JlYXRpbmcgYG5ldC5Tb2NrZXRgOiAlbyIsdGhpcy5jb25uZWN0T3B0cykscj1hLmNvbm5lY3QodGhpcy5jb25uZWN0T3B0cyk7Y29uc3Qgbz0iZnVuY3Rpb24iPT10eXBlb2YgdGhpcy5wcm94eUhlYWRlcnM/dGhpcy5wcm94eUhlYWRlcnMoKTp7Li4udGhpcy5wcm94eUhlYWRlcnN9LGk9YS5pc0lQdjYobi5ob3N0KT9gWyR7bi5ob3N0fV1gOm4uaG9zdDtsZXQgcz1gQ09OTkVDVCAke2l9OiR7bi5wb3J0fSBIVFRQLzEuMVxyXG5gO2lmKGUudXNlcm5hbWV8fGUucGFzc3dvcmQpe2NvbnN0IHQ9YCR7ZGVjb2RlVVJJQ29tcG9uZW50KGUudXNlcm5hbWUpfToke2RlY29kZVVSSUNvbXBvbmVudChlLnBhc3N3b3JkKX1gO29bIlByb3h5LUF1dGhvcml6YXRpb24iXT1gQmFzaWMgJHtCdWZmZXIuZnJvbSh0KS50b1N0cmluZygiYmFzZTY0Iil9YH1vLkhvc3Q9YCR7aX06JHtuLnBvcnR9YCxvWyJQcm94eS1Db25uZWN0aW9uIl18fChvWyJQcm94eS1Db25uZWN0aW9uIl09dGhpcy5rZWVwQWxpdmU/IktlZXAtQWxpdmUiOiJjbG9zZSIpO2Zvcihjb25zdCB0IG9mIE9iamVjdC5rZXlzKG8pKXMrPWAke3R9OiAke29bdF19XHJcbmA7Y29uc3QgYz1LdChyKTtyLndyaXRlKGAke3N9XHJcbmApO2NvbnN0e2Nvbm5lY3Q6dSxidWZmZXJlZDpofT1hd2FpdCBjO2lmKHQuZW1pdCgicHJveHlDb25uZWN0Iix1KSx0aGlzLmVtaXQoInByb3h5Q29ubmVjdCIsdSx0KSwyMDA9PT11LnN0YXR1c0NvZGUpe2lmKHQub25jZSgic29ja2V0IixRdCksbi5zZWN1cmVFbmRwb2ludCl7WnQoIlVwZ3JhZGluZyBzb2NrZXQgY29ubmVjdGlvbiB0byBUTFMiKTtjb25zdCB0PW4uc2VydmVybmFtZXx8bi5ob3N0O3JldHVybiBmLmNvbm5lY3Qoey4uLlh0KG4sImhvc3QiLCJwYXRoIiwicG9ydCIpLHNvY2tldDpyLHNlcnZlcm5hbWU6YS5pc0lQKHQpP3ZvaWQgMDp0fSl9cmV0dXJuIHJ9ci5kZXN0cm95KCk7Y29uc3QgcD1uZXcgYS5Tb2NrZXQoe3dyaXRhYmxlOiExfSk7cmV0dXJuIHAucmVhZGFibGU9ITAsdC5vbmNlKCJzb2NrZXQiLHQ9PntadCgiUmVwbGF5aW5nIHByb3h5IGJ1ZmZlciBmb3IgZmFpbGVkIHJlcXVlc3QiKSx0LnB1c2goaCksdC5wdXNoKG51bGwpfSkscH19ZnVuY3Rpb24gUXQodCl7dC5yZXN1bWUoKX1mdW5jdGlvbiBYdCh0LC4uLm4pe2NvbnN0IGU9e307bGV0IHI7Zm9yKHIgaW4gdCluLmluY2x1ZGVzKHIpfHwoZVtyXT10W3JdKTtyZXR1cm4gZX1xdC5fX2luaXRTdGF0aWMoKTtmdW5jdGlvbiB0bih0KXtyZXR1cm4gdC5yZXBsYWNlKC9eW0EtWl06LywiIikucmVwbGFjZSgvXFwvZywiLyIpfWNvbnN0IG5uPW47bGV0IGVuLHJuPTAsb249e307ZnVuY3Rpb24gc24odCl7bm4uZGVidWcmJmNvbnNvbGUubG9nKGBbQU5SIFdvcmtlcl0gJHt0fWApfXZhciBjbix1bixhbjtjb25zdCBmbj1mdW5jdGlvbih0KXtsZXQgbjt0cnl7bj1uZXcgVVJMKHQudXJsKX1jYXRjaChuKXtyZXR1cm4gYigoKT0+e2NvbnNvbGUud2FybigiW0BzZW50cnkvbm9kZV06IEludmFsaWQgZHNuIG9yIHR1bm5lbCBvcHRpb24sIHdpbGwgbm90IHNlbmQgYW55IGV2ZW50cy4gVGhlIHR1bm5lbCBvcHRpb24gbXVzdCBiZSBhIGZ1bGwgVVJMIHdoZW4gdXNlZC4iKX0pLHp0KHQsKCk9PlByb21pc2UucmVzb2x2ZSh7fSkpfWNvbnN0IGU9Imh0dHBzOiI9PT1uLnByb3RvY29sLHI9ZnVuY3Rpb24odCxuKXtjb25zdHtub19wcm94eTplfT1wcm9jZXNzLmVudixyPWU/LnNwbGl0KCIsIikuc29tZShuPT50Lmhvc3QuZW5kc1dpdGgobil8fHQuaG9zdG5hbWUuZW5kc1dpdGgobikpO3JldHVybiByP3ZvaWQgMDpufShuLHQucHJveHl8fChlP3Byb2Nlc3MuZW52Lmh0dHBzX3Byb3h5OnZvaWQgMCl8fHByb2Nlc3MuZW52Lmh0dHBfcHJveHkpLG89ZT9zOmksYT12b2lkIDAhPT10LmtlZXBBbGl2ZSYmdC5rZWVwQWxpdmUsZj1yP25ldyBxdChyKTpuZXcgby5BZ2VudCh7a2VlcEFsaXZlOmEsbWF4U29ja2V0czozMCx0aW1lb3V0OjJlM30pLGg9ZnVuY3Rpb24odCxuLGUpe2NvbnN0e2hvc3RuYW1lOnIscGF0aG5hbWU6byxwb3J0OmkscHJvdG9jb2w6cyxzZWFyY2g6YX09bmV3IFVSTCh0LnVybCk7cmV0dXJuIGZ1bmN0aW9uKGYpe3JldHVybiBuZXcgUHJvbWlzZSgoaCxwKT0+e0R0KCgpPT57bGV0IGQ9ZnVuY3Rpb24odCl7cmV0dXJuIG5ldyBjKHtyZWFkKCl7dGhpcy5wdXNoKHQpLHRoaXMucHVzaChudWxsKX19KX0oZi5ib2R5KTtjb25zdCBsPXsuLi50LmhlYWRlcnN9O2YuYm9keS5sZW5ndGg+MzI3NjgmJihsWyJjb250ZW50LWVuY29kaW5nIl09Imd6aXAiLGQ9ZC5waXBlKHUoKSkpO2NvbnN0IG09ci5zdGFydHNXaXRoKCJbIiksZz1uLnJlcXVlc3Qoe21ldGhvZDoiUE9TVCIsYWdlbnQ6ZSxoZWFkZXJzOmwsaG9zdG5hbWU6bT9yLnNsaWNlKDEsLTEpOnIscGF0aDpgJHtvfSR7YX1gLHBvcnQ6aSxwcm90b2NvbDpzLGNhOnQuY2FDZXJ0c30sdD0+e3Qub24oImRhdGEiLCgpPT57fSksdC5vbigiZW5kIiwoKT0+e30pLHQuc2V0RW5jb2RpbmcoInV0ZjgiKTtjb25zdCBuPXQuaGVhZGVyc1sicmV0cnktYWZ0ZXIiXT8/bnVsbCxlPXQuaGVhZGVyc1sieC1zZW50cnktcmF0ZS1saW1pdHMiXT8/bnVsbDtoKHtzdGF0dXNDb2RlOnQuc3RhdHVzQ29kZSxoZWFkZXJzOnsicmV0cnktYWZ0ZXIiOm4sIngtc2VudHJ5LXJhdGUtbGltaXRzIjpBcnJheS5pc0FycmF5KGUpP2VbMF18fG51bGw6ZX19KX0pO2cub24oImVycm9yIixwKSxkLnBpcGUoZyl9KX0pfX0odCx0Lmh0dHBNb2R1bGU/P28sZik7cmV0dXJuIHp0KHQsaCl9KHt1cmw6KGNuPW5uLmRzbix1bj1ubi50dW5uZWwsYW49bm4uc2RrTWV0YWRhdGEuc2RrLHVufHxgJHtmdW5jdGlvbih0KXtyZXR1cm5gJHtmdW5jdGlvbih0KXtjb25zdCBuPXQucHJvdG9jb2w/YCR7dC5wcm90b2NvbH06YDoiIixlPXQucG9ydD9gOiR7dC5wb3J0fWA6IiI7cmV0dXJuYCR7bn0vLyR7dC5ob3N0fSR7ZX0ke3QucGF0aD9gLyR7dC5wYXRofWA6IiJ9L2FwaS9gfSh0KX0ke3QucHJvamVjdElkfS9lbnZlbG9wZS9gfShjbil9PyR7ZnVuY3Rpb24odCxuKXtjb25zdCBlPXtzZW50cnlfdmVyc2lvbjoiNyJ9O3JldHVybiB0LnB1YmxpY0tleSYmKGUuc2VudHJ5X2tleT10LnB1YmxpY0tleSksbiYmKGUuc2VudHJ5X2NsaWVudD1gJHtuLm5hbWV9LyR7bi52ZXJzaW9ufWApLG5ldyBVUkxTZWFyY2hQYXJhbXMoZSkudG9TdHJpbmcoKX0oY24sYW4pfWApfSk7YXN5bmMgZnVuY3Rpb24gaG4oKXtpZihlbil7c24oIlNlbmRpbmcgYWJub3JtYWwgc2Vzc2lvbiIpLFYoZW4se3N0YXR1czoiYWJub3JtYWwiLGFibm9ybWFsX21lY2hhbmlzbToiYW5yX2ZvcmVncm91bmQiLHJlbGVhc2U6bm4ucmVsZWFzZSxlbnZpcm9ubWVudDpubi5lbnZpcm9ubWVudH0pO2NvbnN0IHQ9ZnVuY3Rpb24odCxuLGUscil7Y29uc3Qgbz1SdChlKTtyZXR1cm4gQXQoe3NlbnRfYXQ6KG5ldyBEYXRlKS50b0lTT1N0cmluZygpLC4uLm8mJntzZGs6b30sLi4uISFyJiZuJiZ7ZHNuOm10KG4pfX0sWyJhZ2dyZWdhdGVzImluIHQ/W3t0eXBlOiJzZXNzaW9ucyJ9LHRdOlt7dHlwZToic2Vzc2lvbiJ9LHQudG9KU09OKCldXSl9KGVuLG5uLmRzbixubi5zZGtNZXRhZGF0YSxubi50dW5uZWwpO3NuKEpTT04uc3RyaW5naWZ5KHQpKSxhd2FpdCBmbi5zZW5kKHQpO3RyeXtlPy5wb3N0TWVzc2FnZSgic2Vzc2lvbi1lbmRlZCIpfWNhdGNoe319fWZ1bmN0aW9uIHBuKHQpe2lmKCF0KXJldHVybjtjb25zdCBuPWZ1bmN0aW9uKHQpe2lmKCF0Lmxlbmd0aClyZXR1cm5bXTtjb25zdCBuPUFycmF5LmZyb20odCk7cmV0dXJuL3NlbnRyeVdyYXBwZWQvLnRlc3QoRShuKS5mdW5jdGlvbnx8IiIpJiZuLnBvcCgpLG4ucmV2ZXJzZSgpLCQudGVzdChFKG4pLmZ1bmN0aW9ufHwiIikmJihuLnBvcCgpLCQudGVzdChFKG4pLmZ1bmN0aW9ufHwiIikmJm4ucG9wKCkpLG4uc2xpY2UoMCw1MCkubWFwKHQ9Pih7Li4udCxmaWxlbmFtZTp0LmZpbGVuYW1lfHxFKG4pLmZpbGVuYW1lLGZ1bmN0aW9uOnQuZnVuY3Rpb258fCI/In0pKX0odCk7aWYobm4uYXBwUm9vdFBhdGgpZm9yKGNvbnN0IHQgb2Ygbil0LmZpbGVuYW1lJiYodC5maWxlbmFtZT1DdCh0LmZpbGVuYW1lLG5uLmFwcFJvb3RQYXRoKSk7cmV0dXJuIG59YXN5bmMgZnVuY3Rpb24gZG4odCxuKXtpZihybj49bm4ubWF4QW5yRXZlbnRzKXJldHVybjtybis9MSxhd2FpdCBobigpLHNuKCJTZW5kaW5nIGV2ZW50Iik7Y29uc3QgZT17ZXZlbnRfaWQ6RigpLGNvbnRleHRzOm5uLmNvbnRleHRzLHJlbGVhc2U6bm4ucmVsZWFzZSxlbnZpcm9ubWVudDpubi5lbnZpcm9ubWVudCxkaXN0Om5uLmRpc3QscGxhdGZvcm06Im5vZGUiLGxldmVsOiJlcnJvciIsZXhjZXB0aW9uOnt2YWx1ZXM6W3t0eXBlOiJBcHBsaWNhdGlvbk5vdFJlc3BvbmRpbmciLHZhbHVlOmBBcHBsaWNhdGlvbiBOb3QgUmVzcG9uZGluZyBmb3IgYXQgbGVhc3QgJHtubi5hbnJUaHJlc2hvbGR9IG1zYCxzdGFja3RyYWNlOntmcmFtZXM6cG4odCl9LG1lY2hhbmlzbTp7dHlwZToiQU5SIn19XX0sdGFnczpubi5zdGF0aWNUYWdzfTtuJiZmdW5jdGlvbih0LG4pe2lmKE10KHQsbiksIXQuY29udGV4dHM/LnRyYWNlKXtjb25zdHt0cmFjZUlkOmUscGFyZW50U3BhbklkOnIscHJvcGFnYXRpb25TcGFuSWQ6b309bi5wcm9wYWdhdGlvbkNvbnRleHQ7dC5jb250ZXh0cz17dHJhY2U6e3RyYWNlX2lkOmUsc3Bhbl9pZDpvfHxxKCkscGFyZW50X3NwYW5faWQ6cn0sLi4udC5jb250ZXh0c319fShlLG4pLGZ1bmN0aW9uKHQpe2lmKDA9PT1PYmplY3Qua2V5cyhvbikubGVuZ3RoKXJldHVybjtjb25zdCBuPW5uLmFwcFJvb3RQYXRoP3t9Om9uO2lmKG5uLmFwcFJvb3RQYXRoKWZvcihjb25zdFt0LGVdb2YgT2JqZWN0LmVudHJpZXMob24pKW5bQ3QodCxubi5hcHBSb290UGF0aCldPWU7Y29uc3QgZT1uZXcgTWFwO2Zvcihjb25zdCByIG9mIHQuZXhjZXB0aW9uPy52YWx1ZXN8fFtdKWZvcihjb25zdCB0IG9mIHIuc3RhY2t0cmFjZT8uZnJhbWVzfHxbXSl7Y29uc3Qgcj10LmFic19wYXRofHx0LmZpbGVuYW1lO3ImJm5bcl0mJmUuc2V0KHIsbltyXSl9aWYoZS5zaXplPjApe2NvbnN0IG49W107Zm9yKGNvbnN0W3Qscl1vZiBlLmVudHJpZXMoKSluLnB1c2goe3R5cGU6InNvdXJjZW1hcCIsY29kZV9maWxlOnQsZGVidWdfaWQ6cn0pO3QuZGVidWdfbWV0YT17aW1hZ2VzOm59fX0oZSk7Y29uc3Qgcj1PdChlLG5uLmRzbixubi5zZGtNZXRhZGF0YSxubi50dW5uZWwpO3NuKEpTT04uc3RyaW5naWZ5KHIpKSxhd2FpdCBmbi5zZW5kKHIpLGF3YWl0IGZuLmZsdXNoKDJlMykscm4+PW5uLm1heEFuckV2ZW50cyYmc2V0VGltZW91dCgoKT0+e3Byb2Nlc3MuZXhpdCgwKX0sNWUzKX1sZXQgbG47aWYoc24oIlN0YXJ0ZWQiKSxubi5jYXB0dXJlU3RhY2tUcmFjZSl7c24oIkNvbm5lY3RpbmcgdG8gZGVidWdnZXIiKTtjb25zdCBuPW5ldyB0O24uY29ubmVjdFRvTWFpblRocmVhZCgpLHNuKCJDb25uZWN0ZWQgdG8gZGVidWdnZXIiKTtjb25zdCBlPW5ldyBNYXA7bi5vbigiRGVidWdnZXIuc2NyaXB0UGFyc2VkIix0PT57ZS5zZXQodC5wYXJhbXMuc2NyaXB0SWQsdC5wYXJhbXMudXJsKX0pLG4ub24oIkRlYnVnZ2VyLnBhdXNlZCIsdD0+e2lmKCJvdGhlciI9PT10LnBhcmFtcy5yZWFzb24pdHJ5e3NuKCJEZWJ1Z2dlciBwYXVzZWQiKTtjb25zdCBpPVsuLi50LnBhcmFtcy5jYWxsRnJhbWVzXSxzPW5uLmFwcFJvb3RQYXRoP2Z1bmN0aW9uKHQ9KHByb2Nlc3MuYXJndlsxXT9HdChwcm9jZXNzLmFyZ3ZbMV0pOnByb2Nlc3MuY3dkKCkpLG49IlxcIj09PW8pe2NvbnN0IGU9bj90bih0KTp0O3JldHVybiB0PT57aWYoIXQpcmV0dXJuO2NvbnN0IG89bj90bih0KTp0O2xldHtkaXI6aSxiYXNlOnMsZXh0OmN9PXIucGFyc2Uobyk7Ii5qcyIhPT1jJiYiLm1qcyIhPT1jJiYiLmNqcyIhPT1jfHwocz1zLnNsaWNlKDAsLTEqYy5sZW5ndGgpKTtjb25zdCB1PWRlY29kZVVSSUNvbXBvbmVudChzKTtpfHwoaT0iLiIpO2NvbnN0IGE9aS5sYXN0SW5kZXhPZigiL25vZGVfbW9kdWxlcyIpO2lmKGE+LTEpcmV0dXJuYCR7aS5zbGljZShhKzE0KS5yZXBsYWNlKC9cLy9nLCIuIil9OiR7dX1gO2lmKGkuc3RhcnRzV2l0aChlKSl7Y29uc3QgdD1pLnNsaWNlKGUubGVuZ3RoKzEpLnJlcGxhY2UoL1wvL2csIi4iKTtyZXR1cm4gdD9gJHt0fToke3V9YDp1fXJldHVybiB1fX0obm4uYXBwUm9vdFBhdGgpOigpPT57fSxjPWkubWFwKHQ9PmZ1bmN0aW9uKHQsbixlKXtjb25zdCByPW4/bi5yZXBsYWNlKC9eZmlsZTpcL1wvLywiIik6dm9pZCAwLG89dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXI/dC5sb2NhdGlvbi5jb2x1bW5OdW1iZXIrMTp2b2lkIDAsaT10LmxvY2F0aW9uLmxpbmVOdW1iZXI/dC5sb2NhdGlvbi5saW5lTnVtYmVyKzE6dm9pZCAwO3JldHVybntmaWxlbmFtZTpyLG1vZHVsZTplKHIpLGZ1bmN0aW9uOnQuZnVuY3Rpb25OYW1lfHwiPyIsY29sbm86byxsaW5lbm86aSxpbl9hcHA6cj9IdChyKTp2b2lkIDB9fSh0LGUuZ2V0KHQubG9jYXRpb24uc2NyaXB0SWQpLHMpKSx1PXNldFRpbWVvdXQoKCk9PntkbihjKS50aGVuKG51bGwsKCk9PntzbigiU2VuZGluZyBBTlIgZXZlbnQgZmFpbGVkLiIpfSl9LDVlMyk7bi5wb3N0KCJSdW50aW1lLmV2YWx1YXRlIix7ZXhwcmVzc2lvbjoiZ2xvYmFsLl9fU0VOVFJZX0dFVF9TQ09QRVNfXygpOyIsc2lsZW50OiEwLHJldHVybkJ5VmFsdWU6ITB9LCh0LGUpPT57dCYmc24oYEVycm9yIGV4ZWN1dGluZyBzY3JpcHQ6ICcke3QubWVzc2FnZX0nYCksY2xlYXJUaW1lb3V0KHUpO2NvbnN0IHI9ZT8ucmVzdWx0P2UucmVzdWx0LnZhbHVlOnZvaWQgMDtuLnBvc3QoIkRlYnVnZ2VyLnJlc3VtZSIpLG4ucG9zdCgiRGVidWdnZXIuZGlzYWJsZSIpLGRuKGMscikudGhlbihudWxsLCgpPT57c24oIlNlbmRpbmcgQU5SIGV2ZW50IGZhaWxlZC4iKX0pfSl9Y2F0Y2godCl7dGhyb3cgbi5wb3N0KCJEZWJ1Z2dlci5yZXN1bWUiKSxuLnBvc3QoIkRlYnVnZ2VyLmRpc2FibGUiKSx0fX0pLGxuPSgpPT57dHJ5e24ucG9zdCgiRGVidWdnZXIuZW5hYmxlIiwoKT0+e24ucG9zdCgiRGVidWdnZXIucGF1c2UiKX0pfWNhdGNoe319fWNvbnN0e3BvbGw6bW59PWZ1bmN0aW9uKHQsbixlLHIpe2NvbnN0IG89dCgpO2xldCBpPSExLHM9ITA7cmV0dXJuIHNldEludGVydmFsKCgpPT57Y29uc3QgdD1vLmdldFRpbWVNcygpOyExPT09aSYmdD5uK2UmJihpPSEwLHMmJnIoKSksdDxuK2UmJihpPSExKX0sMjApLHtwb2xsOigpPT57by5yZXNldCgpfSxlbmFibGVkOnQ9PntzPXR9fX0oZnVuY3Rpb24oKXtsZXQgdD1wcm9jZXNzLmhydGltZSgpO3JldHVybntnZXRUaW1lTXM6KCk9Pntjb25zdFtuLGVdPXByb2Nlc3MuaHJ0aW1lKHQpO3JldHVybiBNYXRoLmZsb29yKDFlMypuK2UvMWU2KX0scmVzZXQ6KCk9Pnt0PXByb2Nlc3MuaHJ0aW1lKCl9fX0sbm4ucG9sbEludGVydmFsLG5uLmFuclRocmVzaG9sZCxmdW5jdGlvbigpe3NuKCJXYXRjaGRvZyB0aW1lb3V0IiksbG4/KHNuKCJQYXVzaW5nIGRlYnVnZ2VyIHRvIGNhcHR1cmUgc3RhY2sgdHJhY2UiKSxsbigpKTooc24oIkNhcHR1cmluZyBldmVudCB3aXRob3V0IGEgc3RhY2sgdHJhY2UiKSxkbigpLnRoZW4obnVsbCwoKT0+e3NuKCJTZW5kaW5nIEFOUiBldmVudCBmYWlsZWQgb24gd2F0Y2hkb2cgdGltZW91dC4iKX0pKX0pO2U/Lm9uKCJtZXNzYWdlIix0PT57dC5zZXNzaW9uJiYoZW49WSh0LnNlc3Npb24pKSx0LmRlYnVnSW1hZ2VzJiYob249dC5kZWJ1Z0ltYWdlcyksbW4oKX0pOw==";
|
|
55902
55902
|
var DEFAULT_INTERVAL = 50;
|
|
55903
55903
|
var DEFAULT_HANG_THRESHOLD = 5000;
|
|
55904
|
-
function
|
|
55904
|
+
function log18(message2, ...args) {
|
|
55905
55905
|
core.debug.log(`[ANR] ${message2}`, ...args);
|
|
55906
55906
|
}
|
|
55907
55907
|
function globalWithScopeFetchFn() {
|
|
@@ -56014,17 +56014,17 @@ var require_anr2 = __commonJS((exports) => {
|
|
|
56014
56014
|
timer.unref();
|
|
56015
56015
|
worker.on("message", (msg) => {
|
|
56016
56016
|
if (msg === "session-ended") {
|
|
56017
|
-
|
|
56017
|
+
log18("ANR event sent from ANR worker. Clearing session in this thread.");
|
|
56018
56018
|
core.getIsolationScope().setSession(undefined);
|
|
56019
56019
|
}
|
|
56020
56020
|
});
|
|
56021
56021
|
worker.once("error", (err) => {
|
|
56022
56022
|
clearInterval(timer);
|
|
56023
|
-
|
|
56023
|
+
log18("ANR worker error", err);
|
|
56024
56024
|
});
|
|
56025
56025
|
worker.once("exit", (code) => {
|
|
56026
56026
|
clearInterval(timer);
|
|
56027
|
-
|
|
56027
|
+
log18("ANR worker exit", code);
|
|
56028
56028
|
});
|
|
56029
56029
|
worker.unref();
|
|
56030
56030
|
return () => {
|
|
@@ -61142,10 +61142,10 @@ globstar while`, file, fr, pattern, pr, swallowee);
|
|
|
61142
61142
|
}
|
|
61143
61143
|
return filtered.join("/");
|
|
61144
61144
|
}).join("|");
|
|
61145
|
-
const [
|
|
61146
|
-
re = "^" +
|
|
61145
|
+
const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
|
|
61146
|
+
re = "^" + open + re + close + "$";
|
|
61147
61147
|
if (this.partial) {
|
|
61148
|
-
re = "^(?:\\/|" +
|
|
61148
|
+
re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
|
|
61149
61149
|
}
|
|
61150
61150
|
if (this.negate)
|
|
61151
61151
|
re = "^(?!" + re + ").+$";
|
|
@@ -66880,7 +66880,7 @@ var require_instrumentation17 = __commonJS((exports) => {
|
|
|
66880
66880
|
_getClientConnectPatch() {
|
|
66881
66881
|
const plugin = this;
|
|
66882
66882
|
return (original) => {
|
|
66883
|
-
return function
|
|
66883
|
+
return function connect3(callback) {
|
|
66884
66884
|
const config2 = plugin.getConfig();
|
|
66885
66885
|
if (utils.shouldSkipInstrumentation(config2) || config2.ignoreConnectSpans) {
|
|
66886
66886
|
return original.call(this, callback);
|
|
@@ -67067,7 +67067,7 @@ var require_instrumentation17 = __commonJS((exports) => {
|
|
|
67067
67067
|
_getPoolConnectPatch() {
|
|
67068
67068
|
const plugin = this;
|
|
67069
67069
|
return (originalConnect) => {
|
|
67070
|
-
return function
|
|
67070
|
+
return function connect3(callback) {
|
|
67071
67071
|
const config2 = plugin.getConfig();
|
|
67072
67072
|
if (utils.shouldSkipInstrumentation(config2)) {
|
|
67073
67073
|
return originalConnect.call(this, callback);
|
|
@@ -73240,7 +73240,7 @@ var require_tracing2 = __commonJS((exports) => {
|
|
|
73240
73240
|
var http = require_http4();
|
|
73241
73241
|
var amqplib = require_amqplib2();
|
|
73242
73242
|
var index$6 = require_anthropic_ai2();
|
|
73243
|
-
var
|
|
73243
|
+
var connect3 = require_connect2();
|
|
73244
73244
|
var express = require_express();
|
|
73245
73245
|
var index = require_fastify();
|
|
73246
73246
|
var firebase = require_firebase();
|
|
@@ -73280,7 +73280,7 @@ var require_tracing2 = __commonJS((exports) => {
|
|
|
73280
73280
|
prisma.prismaIntegration(),
|
|
73281
73281
|
index$1.hapiIntegration(),
|
|
73282
73282
|
koa.koaIntegration(),
|
|
73283
|
-
|
|
73283
|
+
connect3.connectIntegration(),
|
|
73284
73284
|
tedious.tediousIntegration(),
|
|
73285
73285
|
genericPool.genericPoolIntegration(),
|
|
73286
73286
|
kafka.kafkaIntegration(),
|
|
@@ -73301,7 +73301,7 @@ var require_tracing2 = __commonJS((exports) => {
|
|
|
73301
73301
|
http.instrumentSentryHttp,
|
|
73302
73302
|
http.instrumentOtelHttp,
|
|
73303
73303
|
express.instrumentExpress,
|
|
73304
|
-
|
|
73304
|
+
connect3.instrumentConnect,
|
|
73305
73305
|
index.instrumentFastify,
|
|
73306
73306
|
index.instrumentFastifyV3,
|
|
73307
73307
|
index$1.instrumentHapi,
|
|
@@ -73486,7 +73486,7 @@ var require_cjs4 = __commonJS((exports) => {
|
|
|
73486
73486
|
var index$5 = require_hapi();
|
|
73487
73487
|
var index$6 = require_hono();
|
|
73488
73488
|
var koa = require_koa();
|
|
73489
|
-
var
|
|
73489
|
+
var connect3 = require_connect2();
|
|
73490
73490
|
var knex = require_knex();
|
|
73491
73491
|
var tedious = require_tedious();
|
|
73492
73492
|
var genericPool = require_genericPool();
|
|
@@ -73535,8 +73535,8 @@ var require_cjs4 = __commonJS((exports) => {
|
|
|
73535
73535
|
exports.setupHonoErrorHandler = index$6.setupHonoErrorHandler;
|
|
73536
73536
|
exports.koaIntegration = koa.koaIntegration;
|
|
73537
73537
|
exports.setupKoaErrorHandler = koa.setupKoaErrorHandler;
|
|
73538
|
-
exports.connectIntegration =
|
|
73539
|
-
exports.setupConnectErrorHandler =
|
|
73538
|
+
exports.connectIntegration = connect3.connectIntegration;
|
|
73539
|
+
exports.setupConnectErrorHandler = connect3.setupConnectErrorHandler;
|
|
73540
73540
|
exports.knexIntegration = knex.knexIntegration;
|
|
73541
73541
|
exports.tediousIntegration = tedious.tediousIntegration;
|
|
73542
73542
|
exports.genericPoolIntegration = genericPool.genericPoolIntegration;
|
|
@@ -75335,6 +75335,13 @@ function createOmniClient(config) {
|
|
|
75335
75335
|
throw OmniApiError.from(json, resp.status);
|
|
75336
75336
|
return { items: json?.items ?? [], meta: json?.meta ?? { totalFetched: 0, hasMore: false } };
|
|
75337
75337
|
},
|
|
75338
|
+
async listGroupMembers(id, groupJid) {
|
|
75339
|
+
const resp = await apiFetch(`${baseUrl}/api/v2/instances/${id}/groups/${encodeURIComponent(groupJid)}/members`, {});
|
|
75340
|
+
const json = await resp.json();
|
|
75341
|
+
if (!resp.ok)
|
|
75342
|
+
throw OmniApiError.from(json, resp.status);
|
|
75343
|
+
return { members: json?.members ?? [] };
|
|
75344
|
+
},
|
|
75338
75345
|
async getUserProfile(id, userId) {
|
|
75339
75346
|
const resp = await apiFetch(`${baseUrl}/api/v2/instances/${id}/users/${userId}/profile`, {});
|
|
75340
75347
|
const json = await resp.json();
|
|
@@ -76643,8 +76650,8 @@ import { fileURLToPath } from "url";
|
|
|
76643
76650
|
// package.json
|
|
76644
76651
|
var package_default = {
|
|
76645
76652
|
name: "@automagik/omni",
|
|
76646
|
-
version: "2.
|
|
76647
|
-
description: "LLM-optimized CLI for Omni
|
|
76653
|
+
version: "2.260331.1",
|
|
76654
|
+
description: "LLM-optimized CLI for Omni",
|
|
76648
76655
|
type: "module",
|
|
76649
76656
|
bin: {
|
|
76650
76657
|
omni: "./bin/omni"
|
|
@@ -78314,6 +78321,221 @@ function createBatchCommand() {
|
|
|
78314
78321
|
return batch;
|
|
78315
78322
|
}
|
|
78316
78323
|
|
|
78324
|
+
// src/commands/channels.ts
|
|
78325
|
+
import * as readline from "readline";
|
|
78326
|
+
init_config();
|
|
78327
|
+
async function apiCall(path, method = "GET", body) {
|
|
78328
|
+
const config = loadConfig();
|
|
78329
|
+
const baseUrl = (config.apiUrl ?? "http://localhost:8882").replace(/\/$/, "");
|
|
78330
|
+
const apiKey = config.apiKey ?? "";
|
|
78331
|
+
const headers = { "x-api-key": apiKey };
|
|
78332
|
+
if (body)
|
|
78333
|
+
headers["Content-Type"] = "application/json";
|
|
78334
|
+
const resp = await fetch(`${baseUrl}/api/v2/${path}`, {
|
|
78335
|
+
method,
|
|
78336
|
+
headers,
|
|
78337
|
+
body: body ? JSON.stringify(body) : undefined
|
|
78338
|
+
});
|
|
78339
|
+
if (!resp.ok) {
|
|
78340
|
+
const err = await resp.json();
|
|
78341
|
+
throw new Error(err?.error?.message ?? `API error: ${resp.status}`);
|
|
78342
|
+
}
|
|
78343
|
+
return resp.json();
|
|
78344
|
+
}
|
|
78345
|
+
function promptUser(question) {
|
|
78346
|
+
const rl = readline.createInterface({
|
|
78347
|
+
input: process.stdin,
|
|
78348
|
+
output: process.stderr
|
|
78349
|
+
});
|
|
78350
|
+
return new Promise((resolve) => {
|
|
78351
|
+
rl.question(question, (answer) => {
|
|
78352
|
+
rl.close();
|
|
78353
|
+
resolve(answer.trim());
|
|
78354
|
+
});
|
|
78355
|
+
});
|
|
78356
|
+
}
|
|
78357
|
+
function buildStatusItems(instances) {
|
|
78358
|
+
return instances.map((inst) => ({
|
|
78359
|
+
channel: inst.channel,
|
|
78360
|
+
name: inst.name,
|
|
78361
|
+
active: inst.isActive ? "yes" : "no",
|
|
78362
|
+
profile: inst.profileName ?? "-"
|
|
78363
|
+
}));
|
|
78364
|
+
}
|
|
78365
|
+
function createChannelsCommand() {
|
|
78366
|
+
const channels = new Command("channels").description("Channel management \u2014 list types, add instances, check status");
|
|
78367
|
+
channels.command("list").description("Show available channel types and instance counts").action(async () => {
|
|
78368
|
+
const client = getClient();
|
|
78369
|
+
try {
|
|
78370
|
+
const supported = await apiCall("instances/supported-channels");
|
|
78371
|
+
const instanceResult = await client.instances.list({ limit: 100 });
|
|
78372
|
+
const instancesByChannel = new Map;
|
|
78373
|
+
const connectedByChannel = new Map;
|
|
78374
|
+
for (const inst of instanceResult.items) {
|
|
78375
|
+
const ch = inst.channel;
|
|
78376
|
+
instancesByChannel.set(ch, (instancesByChannel.get(ch) ?? 0) + 1);
|
|
78377
|
+
if (inst.isActive) {
|
|
78378
|
+
connectedByChannel.set(ch, (connectedByChannel.get(ch) ?? 0) + 1);
|
|
78379
|
+
}
|
|
78380
|
+
}
|
|
78381
|
+
const items = supported.items.map((ch) => {
|
|
78382
|
+
const total = instancesByChannel.get(ch.id) ?? 0;
|
|
78383
|
+
const connected = connectedByChannel.get(ch.id) ?? 0;
|
|
78384
|
+
return {
|
|
78385
|
+
channel: ch.id,
|
|
78386
|
+
name: ch.name,
|
|
78387
|
+
status: ch.loaded ? "loaded" : "not loaded",
|
|
78388
|
+
instances: total > 0 ? `${total} (${connected} active)` : "0"
|
|
78389
|
+
};
|
|
78390
|
+
});
|
|
78391
|
+
list(items, { emptyMessage: "No channels available." });
|
|
78392
|
+
} catch (err) {
|
|
78393
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
78394
|
+
error(`Failed to list channels: ${message}`);
|
|
78395
|
+
}
|
|
78396
|
+
});
|
|
78397
|
+
channels.command("add <type>").description("Add a new channel instance (interactive setup)").option("--token <token>", "Bot token (for Telegram, Discord, Slack)").option("--name <name>", "Instance name (auto-generated if not provided)").action(async (type, options) => {
|
|
78398
|
+
const validTypes = ["telegram", "discord", "slack", "whatsapp-baileys", "whatsapp-cloud"];
|
|
78399
|
+
if (!validTypes.includes(type)) {
|
|
78400
|
+
error(`Unknown channel type: ${type}. Available: ${validTypes.join(", ")}`);
|
|
78401
|
+
}
|
|
78402
|
+
try {
|
|
78403
|
+
if (type === "telegram") {
|
|
78404
|
+
await addTelegram(options);
|
|
78405
|
+
} else if (type === "discord") {
|
|
78406
|
+
await addTokenChannel("discord", "Discord", options, "Get one from https://discord.com/developers");
|
|
78407
|
+
} else if (type === "slack") {
|
|
78408
|
+
await addTokenChannel("slack", "Slack", options, "Get one from https://api.slack.com/apps");
|
|
78409
|
+
} else if (type.startsWith("whatsapp")) {
|
|
78410
|
+
await addWhatsApp(type, options);
|
|
78411
|
+
} else {
|
|
78412
|
+
error(`Channel type "${type}" is not yet supported for interactive setup. Use: omni instances create --channel ${type} --name <name>`);
|
|
78413
|
+
}
|
|
78414
|
+
} catch (err) {
|
|
78415
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
78416
|
+
error(`Failed to add ${type} channel: ${message}`);
|
|
78417
|
+
}
|
|
78418
|
+
});
|
|
78419
|
+
channels.command("status").description("Overview of all channels and their connection states").action(async () => {
|
|
78420
|
+
const client = getClient();
|
|
78421
|
+
try {
|
|
78422
|
+
const result = await client.instances.list({ limit: 100 });
|
|
78423
|
+
if (result.items.length === 0) {
|
|
78424
|
+
info("No instances configured. Run: omni channels add <type>");
|
|
78425
|
+
return;
|
|
78426
|
+
}
|
|
78427
|
+
const items = buildStatusItems(result.items);
|
|
78428
|
+
list(items, { emptyMessage: "No instances found." });
|
|
78429
|
+
} catch (err) {
|
|
78430
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
78431
|
+
error(`Failed to get channel status: ${message}`);
|
|
78432
|
+
}
|
|
78433
|
+
});
|
|
78434
|
+
return channels;
|
|
78435
|
+
}
|
|
78436
|
+
async function addTelegram(options) {
|
|
78437
|
+
let token = options.token;
|
|
78438
|
+
if (!token) {
|
|
78439
|
+
if (!process.stdin.isTTY) {
|
|
78440
|
+
error(`Token required. Use: omni channels add telegram --token <token>
|
|
78441
|
+
Get one from @BotFather: https://t.me/BotFather`);
|
|
78442
|
+
}
|
|
78443
|
+
info(`Get a bot token from @BotFather: https://t.me/BotFather
|
|
78444
|
+
`);
|
|
78445
|
+
token = await promptUser("Paste your Telegram bot token: ");
|
|
78446
|
+
if (!token) {
|
|
78447
|
+
error("No token provided. Aborting.");
|
|
78448
|
+
}
|
|
78449
|
+
}
|
|
78450
|
+
if (!token.includes(":")) {
|
|
78451
|
+
error(`Invalid token format. Telegram bot tokens look like: 123456789:ABCDefGhIjKlMnOpQrStUvWxYz
|
|
78452
|
+
Get one from @BotFather: https://t.me/BotFather`);
|
|
78453
|
+
}
|
|
78454
|
+
const name = options.name ?? `telegram-${Date.now().toString(36)}`;
|
|
78455
|
+
info(`Creating Telegram instance "${name}"...`);
|
|
78456
|
+
const client = getClient();
|
|
78457
|
+
const instance = await client.instances.create({
|
|
78458
|
+
name,
|
|
78459
|
+
channel: "telegram",
|
|
78460
|
+
token
|
|
78461
|
+
});
|
|
78462
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
78463
|
+
try {
|
|
78464
|
+
const status = await client.instances.status(instance.id);
|
|
78465
|
+
if (status.isConnected) {
|
|
78466
|
+
const botUsername = status.ownerIdentifier ?? status.profileName ?? "unknown";
|
|
78467
|
+
const botName = status.profileName ?? "unknown";
|
|
78468
|
+
success(`Connected as ${botUsername} (${botName})`, {
|
|
78469
|
+
instanceId: instance.id,
|
|
78470
|
+
name: instance.name,
|
|
78471
|
+
channel: "telegram",
|
|
78472
|
+
botUsername,
|
|
78473
|
+
botName
|
|
78474
|
+
});
|
|
78475
|
+
} else {
|
|
78476
|
+
success(`Instance created: ${instance.id}`, {
|
|
78477
|
+
instanceId: instance.id,
|
|
78478
|
+
name: instance.name,
|
|
78479
|
+
channel: "telegram",
|
|
78480
|
+
state: status.state,
|
|
78481
|
+
message: status.message ?? "Connecting..."
|
|
78482
|
+
});
|
|
78483
|
+
info(`Connection in progress. Check: omni instances status ${instance.id}`);
|
|
78484
|
+
}
|
|
78485
|
+
} catch {
|
|
78486
|
+
success(`Instance created: ${instance.id}`, {
|
|
78487
|
+
instanceId: instance.id,
|
|
78488
|
+
name: instance.name,
|
|
78489
|
+
channel: "telegram"
|
|
78490
|
+
});
|
|
78491
|
+
info(`Check status: omni instances status ${instance.id}`);
|
|
78492
|
+
}
|
|
78493
|
+
}
|
|
78494
|
+
async function addTokenChannel(type, displayName, options, tokenHelp) {
|
|
78495
|
+
let token = options.token;
|
|
78496
|
+
if (!token) {
|
|
78497
|
+
if (!process.stdin.isTTY) {
|
|
78498
|
+
error(`Token required. Use: omni channels add ${type} --token <token>
|
|
78499
|
+
${tokenHelp}`);
|
|
78500
|
+
}
|
|
78501
|
+
info(`${tokenHelp}
|
|
78502
|
+
`);
|
|
78503
|
+
token = await promptUser(`Paste your ${displayName} bot token: `);
|
|
78504
|
+
if (!token) {
|
|
78505
|
+
error("No token provided. Aborting.");
|
|
78506
|
+
}
|
|
78507
|
+
}
|
|
78508
|
+
const name = options.name ?? `${type}-${Date.now().toString(36)}`;
|
|
78509
|
+
info(`Creating ${displayName} instance "${name}"...`);
|
|
78510
|
+
const client = getClient();
|
|
78511
|
+
const instance = await client.instances.create({
|
|
78512
|
+
name,
|
|
78513
|
+
channel: type,
|
|
78514
|
+
token
|
|
78515
|
+
});
|
|
78516
|
+
success(`Instance created: ${instance.id}`, {
|
|
78517
|
+
instanceId: instance.id,
|
|
78518
|
+
name: instance.name,
|
|
78519
|
+
channel: type
|
|
78520
|
+
});
|
|
78521
|
+
info(`Check status: omni instances status ${instance.id}`);
|
|
78522
|
+
}
|
|
78523
|
+
async function addWhatsApp(channel, options) {
|
|
78524
|
+
const name = options.name ?? `whatsapp-${Date.now().toString(36)}`;
|
|
78525
|
+
info(`Creating WhatsApp instance "${name}"...`);
|
|
78526
|
+
const client = getClient();
|
|
78527
|
+
const instance = await client.instances.create({
|
|
78528
|
+
name,
|
|
78529
|
+
channel
|
|
78530
|
+
});
|
|
78531
|
+
success(`Instance created: ${instance.id}`, {
|
|
78532
|
+
instanceId: instance.id,
|
|
78533
|
+
name: instance.name,
|
|
78534
|
+
channel
|
|
78535
|
+
});
|
|
78536
|
+
info(`Scan QR code: omni instances qr ${instance.id} --watch`);
|
|
78537
|
+
}
|
|
78538
|
+
|
|
78317
78539
|
// src/commands/chats.ts
|
|
78318
78540
|
var VALID_CHANNELS = ["whatsapp-baileys", "whatsapp-cloud", "discord", "slack", "telegram"];
|
|
78319
78541
|
async function buildInstanceNameMap(client) {
|
|
@@ -79396,6 +79618,105 @@ function createConfigCommand() {
|
|
|
79396
79618
|
return config;
|
|
79397
79619
|
}
|
|
79398
79620
|
|
|
79621
|
+
// src/commands/connect.ts
|
|
79622
|
+
import { execFileSync } from "child_process";
|
|
79623
|
+
async function findOrCreateProvider(client, agentName, agentEntry, natsUrl) {
|
|
79624
|
+
try {
|
|
79625
|
+
const provider = await client.providers.create({
|
|
79626
|
+
name: `nats-genie-${agentName}`,
|
|
79627
|
+
schema: "nats-genie",
|
|
79628
|
+
baseUrl: `nats://${natsUrl}`,
|
|
79629
|
+
schemaConfig: { agentName: agentEntry.name, agentDir: agentEntry.dir, natsUrl }
|
|
79630
|
+
});
|
|
79631
|
+
return provider.id;
|
|
79632
|
+
} catch {
|
|
79633
|
+
try {
|
|
79634
|
+
const providers = await client.providers.list();
|
|
79635
|
+
const existing = providers.find((p) => p.name === `nats-genie-${agentName}` && p.schema === "nats-genie");
|
|
79636
|
+
if (existing) {
|
|
79637
|
+
info(`Using existing provider: ${existing.id}`);
|
|
79638
|
+
return existing.id;
|
|
79639
|
+
}
|
|
79640
|
+
} catch {}
|
|
79641
|
+
error("Failed to create provider. Check API connection.");
|
|
79642
|
+
return null;
|
|
79643
|
+
}
|
|
79644
|
+
}
|
|
79645
|
+
async function findOrCreateAgent(client, agentName, providerId) {
|
|
79646
|
+
try {
|
|
79647
|
+
const agent = await client.agents.create({
|
|
79648
|
+
name: agentName,
|
|
79649
|
+
agentProviderId: providerId,
|
|
79650
|
+
agentType: "assistant",
|
|
79651
|
+
provider: "custom",
|
|
79652
|
+
capabilities: [],
|
|
79653
|
+
isInternal: false,
|
|
79654
|
+
isActive: true
|
|
79655
|
+
});
|
|
79656
|
+
return agent.id;
|
|
79657
|
+
} catch {
|
|
79658
|
+
try {
|
|
79659
|
+
const { items } = await client.agents.list();
|
|
79660
|
+
const existing = items.find((a) => a.name === agentName && a.agentProviderId === providerId);
|
|
79661
|
+
if (existing) {
|
|
79662
|
+
info(`Using existing agent: ${existing.id}`);
|
|
79663
|
+
return existing.id;
|
|
79664
|
+
}
|
|
79665
|
+
} catch {}
|
|
79666
|
+
error("Failed to create agent record. Check API connection.");
|
|
79667
|
+
return null;
|
|
79668
|
+
}
|
|
79669
|
+
}
|
|
79670
|
+
function createConnectCommand() {
|
|
79671
|
+
return new Command("connect").description("Connect an Omni instance to a Genie agent via NATS").argument("<instance-id>", "Omni instance ID").argument("<agent-name>", "Genie agent name (from genie directory)").option("--nats-url <url>", "NATS server URL", "localhost:4222").action(async (instanceId, agentName, options) => {
|
|
79672
|
+
const client = getClient();
|
|
79673
|
+
info(`Discovering agent "${agentName}" from genie directory...`);
|
|
79674
|
+
let agentEntry;
|
|
79675
|
+
try {
|
|
79676
|
+
const stdout = execFileSync("genie", ["dir", "get", agentName, "--json"], {
|
|
79677
|
+
encoding: "utf-8",
|
|
79678
|
+
env: process.env,
|
|
79679
|
+
timeout: 1e4
|
|
79680
|
+
});
|
|
79681
|
+
agentEntry = JSON.parse(stdout.trim());
|
|
79682
|
+
} catch {
|
|
79683
|
+
error(`Failed to discover agent "${agentName}" from genie directory.
|
|
79684
|
+
Make sure genie is installed and the agent is registered:
|
|
79685
|
+
genie dir add ${agentName} --dir /path/to/agent`);
|
|
79686
|
+
return;
|
|
79687
|
+
}
|
|
79688
|
+
info(`Found agent: ${agentEntry.name} (dir: ${agentEntry.dir})`);
|
|
79689
|
+
try {
|
|
79690
|
+
await client.instances.get(instanceId);
|
|
79691
|
+
} catch {
|
|
79692
|
+
error(`Instance "${instanceId}" not found. Run: omni instances list`);
|
|
79693
|
+
return;
|
|
79694
|
+
}
|
|
79695
|
+
info("Creating NATS Genie provider...");
|
|
79696
|
+
const providerId = await findOrCreateProvider(client, agentName, agentEntry, options.natsUrl);
|
|
79697
|
+
if (!providerId)
|
|
79698
|
+
return;
|
|
79699
|
+
info("Creating agent record...");
|
|
79700
|
+
const agentId = await findOrCreateAgent(client, agentName, providerId);
|
|
79701
|
+
if (!agentId)
|
|
79702
|
+
return;
|
|
79703
|
+
info("Updating instance agent assignment...");
|
|
79704
|
+
try {
|
|
79705
|
+
await client.instances.update(instanceId, { agentProviderId: providerId });
|
|
79706
|
+
} catch {
|
|
79707
|
+
warn("Could not update instance agent assignment automatically.");
|
|
79708
|
+
info(`Set manually: omni instances update ${instanceId} --agent-provider-id ${providerId}`);
|
|
79709
|
+
}
|
|
79710
|
+
success(`Connected instance "${instanceId}" to genie agent "${agentName}".
|
|
79711
|
+
NATS topics:
|
|
79712
|
+
Inbound: omni.message.${instanceId}.*
|
|
79713
|
+
Outbound: omni.reply.${instanceId}.*
|
|
79714
|
+
|
|
79715
|
+
Next: Start the genie bridge:
|
|
79716
|
+
genie omni start`);
|
|
79717
|
+
});
|
|
79718
|
+
}
|
|
79719
|
+
|
|
79399
79720
|
// src/commands/dead-letters.ts
|
|
79400
79721
|
function createDeadLettersCommand() {
|
|
79401
79722
|
const deadLetters = new Command("dead-letters").description("Manage failed events (dead letters)");
|
|
@@ -79726,7 +80047,7 @@ function createEventsCommand() {
|
|
|
79726
80047
|
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
79727
80048
|
import { homedir as homedir2 } from "os";
|
|
79728
80049
|
import { join as join4 } from "path";
|
|
79729
|
-
import { createInterface } from "readline";
|
|
80050
|
+
import { createInterface as createInterface2 } from "readline";
|
|
79730
80051
|
|
|
79731
80052
|
// ../../node_modules/.bun/ora@8.2.0/node_modules/ora/index.js
|
|
79732
80053
|
import process9 from "process";
|
|
@@ -80779,7 +81100,7 @@ async function downloadNats() {
|
|
|
80779
81100
|
}
|
|
80780
81101
|
}
|
|
80781
81102
|
async function promptLine(question, defaultValue = "") {
|
|
80782
|
-
const rl =
|
|
81103
|
+
const rl = createInterface2({
|
|
80783
81104
|
input: process.stdin,
|
|
80784
81105
|
output: process.stdout
|
|
80785
81106
|
});
|
|
@@ -80792,7 +81113,7 @@ async function promptLine(question, defaultValue = "") {
|
|
|
80792
81113
|
});
|
|
80793
81114
|
}
|
|
80794
81115
|
async function promptYesNo(question, defaultYes = true) {
|
|
80795
|
-
const rl =
|
|
81116
|
+
const rl = createInterface2({
|
|
80796
81117
|
input: process.stdin,
|
|
80797
81118
|
output: process.stdout
|
|
80798
81119
|
});
|
|
@@ -81193,7 +81514,7 @@ async function resolveBase64Image(options) {
|
|
|
81193
81514
|
const buffer = await resp.arrayBuffer();
|
|
81194
81515
|
return Buffer.from(buffer).toString("base64");
|
|
81195
81516
|
}
|
|
81196
|
-
async function
|
|
81517
|
+
async function apiCall2(path, method = "GET", body) {
|
|
81197
81518
|
const config = (await Promise.resolve().then(() => (init_config(), exports_config))).loadConfig();
|
|
81198
81519
|
const baseUrl = config.apiUrl ?? "http://localhost:8882";
|
|
81199
81520
|
const apiKey = config.apiKey ?? "";
|
|
@@ -81266,7 +81587,7 @@ function createInstancesCommand() {
|
|
|
81266
81587
|
body.name = options.name;
|
|
81267
81588
|
body.channel = channel;
|
|
81268
81589
|
setBool(body, "isDefault", options.isDefault);
|
|
81269
|
-
const response = await
|
|
81590
|
+
const response = await apiCall2("instances", "POST", body);
|
|
81270
81591
|
const instance = response.data ?? response;
|
|
81271
81592
|
success(`Instance created: ${instance.id}`, {
|
|
81272
81593
|
id: instance.id,
|
|
@@ -81598,6 +81919,21 @@ function createInstancesCommand() {
|
|
|
81598
81919
|
error(`Failed to list groups: ${message}`);
|
|
81599
81920
|
}
|
|
81600
81921
|
});
|
|
81922
|
+
instances.command("group-members <id> <jid>").description("List members of a group").action(async (rawId, jid) => {
|
|
81923
|
+
try {
|
|
81924
|
+
const id = await resolveInstanceId(rawId);
|
|
81925
|
+
const result = await apiCall2(`instances/${id}/groups/${encodeURIComponent(jid)}/members`);
|
|
81926
|
+
const items = result.members.map((m) => ({
|
|
81927
|
+
id: m.id,
|
|
81928
|
+
name: m.name ?? "-",
|
|
81929
|
+
role: m.role ?? "member"
|
|
81930
|
+
}));
|
|
81931
|
+
list(items, { emptyMessage: "No members found.", rawData: result.members });
|
|
81932
|
+
} catch (err) {
|
|
81933
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
81934
|
+
error(`Failed to list group members: ${message}`);
|
|
81935
|
+
}
|
|
81936
|
+
});
|
|
81601
81937
|
instances.command("profile <id> <userId>").description("Get user profile from the channel").action(async (rawId, userId) => {
|
|
81602
81938
|
const client = getClient();
|
|
81603
81939
|
try {
|
|
@@ -81735,7 +82071,7 @@ function createInstancesCommand() {
|
|
|
81735
82071
|
try {
|
|
81736
82072
|
const id = await resolveInstanceId(rawId);
|
|
81737
82073
|
const base64Data = await resolveBase64Image(options);
|
|
81738
|
-
await
|
|
82074
|
+
await apiCall2(`instances/${id}/profile/picture`, "PUT", { base64: base64Data });
|
|
81739
82075
|
success("Profile picture updated");
|
|
81740
82076
|
} catch (err) {
|
|
81741
82077
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -81745,7 +82081,7 @@ function createInstancesCommand() {
|
|
|
81745
82081
|
instances.command("remove-picture <id>").description("Remove instance profile picture").action(async (rawId) => {
|
|
81746
82082
|
try {
|
|
81747
82083
|
const id = await resolveInstanceId(rawId);
|
|
81748
|
-
await
|
|
82084
|
+
await apiCall2(`instances/${id}/profile/picture`, "DELETE");
|
|
81749
82085
|
success("Profile picture removed");
|
|
81750
82086
|
} catch (err) {
|
|
81751
82087
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -81760,7 +82096,7 @@ function createInstancesCommand() {
|
|
|
81760
82096
|
try {
|
|
81761
82097
|
const id = await resolveInstanceId(rawId);
|
|
81762
82098
|
const base64Data = await resolveBase64Image(options);
|
|
81763
|
-
await
|
|
82099
|
+
await apiCall2(`instances/${id}/groups/${options.group}/picture`, "PUT", { base64: base64Data });
|
|
81764
82100
|
success(`Group picture updated for ${options.group}`);
|
|
81765
82101
|
} catch (err) {
|
|
81766
82102
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -81770,7 +82106,7 @@ function createInstancesCommand() {
|
|
|
81770
82106
|
instances.command("group-create <id>").description("Create a new WhatsApp group").requiredOption("--subject <name>", "Group name/subject").requiredOption("--participants <phones...>", "Phone numbers or JIDs to add (space-separated)").action(async (rawId, opts) => {
|
|
81771
82107
|
try {
|
|
81772
82108
|
const id = await resolveInstanceId(rawId);
|
|
81773
|
-
const result = await
|
|
82109
|
+
const result = await apiCall2(`instances/${id}/groups`, "POST", {
|
|
81774
82110
|
subject: opts.subject,
|
|
81775
82111
|
participants: opts.participants
|
|
81776
82112
|
});
|
|
@@ -81783,7 +82119,7 @@ function createInstancesCommand() {
|
|
|
81783
82119
|
instances.command("group-invite <id> <groupJid>").description("Get group invite link").action(async (rawId, groupJid) => {
|
|
81784
82120
|
try {
|
|
81785
82121
|
const id = await resolveInstanceId(rawId);
|
|
81786
|
-
const result = await
|
|
82122
|
+
const result = await apiCall2(`instances/${id}/groups/${encodeURIComponent(groupJid)}/invite`);
|
|
81787
82123
|
data(result.data);
|
|
81788
82124
|
} catch (err) {
|
|
81789
82125
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -81793,7 +82129,7 @@ function createInstancesCommand() {
|
|
|
81793
82129
|
instances.command("group-revoke-invite <id> <groupJid>").description("Revoke group invite link and generate new one").action(async (rawId, groupJid) => {
|
|
81794
82130
|
try {
|
|
81795
82131
|
const id = await resolveInstanceId(rawId);
|
|
81796
|
-
const result = await
|
|
82132
|
+
const result = await apiCall2(`instances/${id}/groups/${encodeURIComponent(groupJid)}/invite/revoke`, "POST");
|
|
81797
82133
|
success("Invite link revoked", result.data);
|
|
81798
82134
|
} catch (err) {
|
|
81799
82135
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -81803,7 +82139,7 @@ function createInstancesCommand() {
|
|
|
81803
82139
|
instances.command("group-join <id> <code>").description("Join a group via invite code").action(async (rawId, code) => {
|
|
81804
82140
|
try {
|
|
81805
82141
|
const id = await resolveInstanceId(rawId);
|
|
81806
|
-
const result = await
|
|
82142
|
+
const result = await apiCall2(`instances/${id}/groups/join`, "POST", { code });
|
|
81807
82143
|
success(`Joined group: ${result.data.groupJid}`, result.data);
|
|
81808
82144
|
} catch (err) {
|
|
81809
82145
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -81813,7 +82149,7 @@ function createInstancesCommand() {
|
|
|
81813
82149
|
instances.command("privacy <id>").description("Fetch privacy settings").action(async (rawId) => {
|
|
81814
82150
|
try {
|
|
81815
82151
|
const id = await resolveInstanceId(rawId);
|
|
81816
|
-
const result = await
|
|
82152
|
+
const result = await apiCall2(`instances/${id}/privacy`);
|
|
81817
82153
|
data(result.data);
|
|
81818
82154
|
} catch (err) {
|
|
81819
82155
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -81823,7 +82159,7 @@ function createInstancesCommand() {
|
|
|
81823
82159
|
instances.command("reject-call <id>").description("Reject an incoming call").requiredOption("--call-id <callId>", "Call ID from the call event").requiredOption("--from <jid>", "Caller JID").action(async (rawId, options) => {
|
|
81824
82160
|
try {
|
|
81825
82161
|
const id = await resolveInstanceId(rawId);
|
|
81826
|
-
await
|
|
82162
|
+
await apiCall2(`instances/${id}/calls/reject`, "POST", {
|
|
81827
82163
|
callId: options.callId,
|
|
81828
82164
|
callFrom: options.from
|
|
81829
82165
|
});
|
|
@@ -82284,7 +82620,7 @@ function parseErrorMessage(err) {
|
|
|
82284
82620
|
return err.error?.message;
|
|
82285
82621
|
return;
|
|
82286
82622
|
}
|
|
82287
|
-
async function
|
|
82623
|
+
async function apiCall3(path, method = "GET", body, query) {
|
|
82288
82624
|
const config = loadConfig();
|
|
82289
82625
|
const baseUrl = (config.apiUrl ?? "http://localhost:8882").replace(/\/$/, "");
|
|
82290
82626
|
const apiKey = config.apiKey ?? "";
|
|
@@ -82430,7 +82766,7 @@ async function handleLs(options) {
|
|
|
82430
82766
|
query.since = options.since;
|
|
82431
82767
|
if (options.until)
|
|
82432
82768
|
query.until = options.until;
|
|
82433
|
-
const result = await
|
|
82769
|
+
const result = await apiCall3("messages", "GET", undefined, query);
|
|
82434
82770
|
const items = applyFilters(result.items ?? [], options);
|
|
82435
82771
|
if (items.length === 0) {
|
|
82436
82772
|
info("No media items found.");
|
|
@@ -82488,7 +82824,7 @@ async function handleDownload(options) {
|
|
|
82488
82824
|
chat: chatId,
|
|
82489
82825
|
external: options.external
|
|
82490
82826
|
});
|
|
82491
|
-
const response = await
|
|
82827
|
+
const response = await apiCall3("messages/media/download", "POST", body);
|
|
82492
82828
|
const result = response.data;
|
|
82493
82829
|
const config = loadConfig();
|
|
82494
82830
|
const baseUrl = (config.apiUrl ?? "http://localhost:8882").replace(/\/$/, "");
|
|
@@ -83125,6 +83461,7 @@ var CORE_EVENT_TYPES = [
|
|
|
83125
83461
|
"access.allowed",
|
|
83126
83462
|
"access.denied",
|
|
83127
83463
|
"access.pairing_requested",
|
|
83464
|
+
"access.pairing_approved",
|
|
83128
83465
|
"presence.typing",
|
|
83129
83466
|
"presence.online",
|
|
83130
83467
|
"presence.offline",
|
|
@@ -88307,7 +88644,7 @@ var AUTO_RETRY_DELAYS_MS = [
|
|
|
88307
88644
|
24 * 60 * 60 * 1000
|
|
88308
88645
|
];
|
|
88309
88646
|
// ../core/src/types/agent.ts
|
|
88310
|
-
var PROVIDER_SCHEMAS = ["agno", "webhook", "openclaw", "ag-ui", "claude-code", "a2a", "genie"];
|
|
88647
|
+
var PROVIDER_SCHEMAS = ["agno", "webhook", "openclaw", "ag-ui", "claude-code", "a2a", "nats-genie"];
|
|
88311
88648
|
|
|
88312
88649
|
// ../core/src/schemas/common.ts
|
|
88313
88650
|
var UuidSchema = exports_external.string().uuid();
|
|
@@ -90634,334 +90971,14 @@ class InMemorySessionActivityStore {
|
|
|
90634
90971
|
this.activities.clear();
|
|
90635
90972
|
}
|
|
90636
90973
|
}
|
|
90637
|
-
// ../core/src/providers/types.ts
|
|
90638
|
-
class ProviderError extends Error {
|
|
90639
|
-
code;
|
|
90640
|
-
statusCode;
|
|
90641
|
-
details;
|
|
90642
|
-
constructor(message2, code, statusCode, details) {
|
|
90643
|
-
super(message2);
|
|
90644
|
-
this.code = code;
|
|
90645
|
-
this.statusCode = statusCode;
|
|
90646
|
-
this.details = details;
|
|
90647
|
-
this.name = "ProviderError";
|
|
90648
|
-
}
|
|
90649
|
-
}
|
|
90650
90974
|
// ../core/src/providers/claude-code-client.ts
|
|
90651
90975
|
var log6 = createLogger("provider:claude-code");
|
|
90652
|
-
// ../core/src/providers/genie-client.ts
|
|
90653
|
-
import { execFile } from "child_process";
|
|
90654
|
-
import { mkdir, open, readFile, rename, stat, unlink, writeFile } from "fs/promises";
|
|
90655
|
-
import { homedir as homedir3 } from "os";
|
|
90656
|
-
import { join as join5 } from "path";
|
|
90657
|
-
var log7 = createLogger("providers:genie-client");
|
|
90658
|
-
var CACHE_TTL_MS = 5 * 60 * 1000;
|
|
90659
|
-
var CACHE_MAX_SIZE = 100;
|
|
90660
|
-
function sanitize(value) {
|
|
90661
|
-
return value.replace(/[^a-zA-Z0-9_-]/g, "").replace(/-+$/, "");
|
|
90662
|
-
}
|
|
90663
|
-
function execFilePromise(file, args) {
|
|
90664
|
-
return new Promise((resolve2, reject) => {
|
|
90665
|
-
execFile(file, args, {}, (error2, stdout) => {
|
|
90666
|
-
if (error2)
|
|
90667
|
-
reject(error2);
|
|
90668
|
-
else
|
|
90669
|
-
resolve2(String(stdout));
|
|
90670
|
-
});
|
|
90671
|
-
});
|
|
90672
|
-
}
|
|
90673
|
-
function interpolateTemplate(template, vars) {
|
|
90674
|
-
return template.replace(/\{(\w+)\}/g, (_match, key) => {
|
|
90675
|
-
const value = vars[key];
|
|
90676
|
-
return value ?? "";
|
|
90677
|
-
});
|
|
90678
|
-
}
|
|
90679
|
-
function extractTemplateVars(request) {
|
|
90680
|
-
return {
|
|
90681
|
-
thread_id: request.chat?.threadId,
|
|
90682
|
-
chat_id: request.chat?.id,
|
|
90683
|
-
sender_id: request.userId,
|
|
90684
|
-
channel: request.platform?.channel,
|
|
90685
|
-
instance_id: request.platform?.instanceId
|
|
90686
|
-
};
|
|
90687
|
-
}
|
|
90688
|
-
|
|
90689
|
-
class GenieClient {
|
|
90690
|
-
teamNameTemplate;
|
|
90691
|
-
agentNameTemplate;
|
|
90692
|
-
targetAgentTemplate;
|
|
90693
|
-
hasTemplates;
|
|
90694
|
-
autoSpawn;
|
|
90695
|
-
autoSpawnDir;
|
|
90696
|
-
sessionName;
|
|
90697
|
-
agentRole;
|
|
90698
|
-
knownTeams = new Map;
|
|
90699
|
-
pendingTeams = new Set;
|
|
90700
|
-
constructor(config2) {
|
|
90701
|
-
this.teamNameTemplate = config2.teamName ?? "genie";
|
|
90702
|
-
this.agentNameTemplate = config2.agentName;
|
|
90703
|
-
this.targetAgentTemplate = config2.targetAgent;
|
|
90704
|
-
this.agentRole = config2.agentRole;
|
|
90705
|
-
this.autoSpawn = config2.autoSpawn ?? true;
|
|
90706
|
-
this.autoSpawnDir = config2.autoSpawnDir ?? "";
|
|
90707
|
-
this.sessionName = config2.sessionName ?? "";
|
|
90708
|
-
this.hasTemplates = /\{\w+\}/.test(this.teamNameTemplate) || /\{\w+\}/.test(this.agentNameTemplate) || /\{\w+\}/.test(this.targetAgentTemplate);
|
|
90709
|
-
}
|
|
90710
|
-
isTeamKnown(teamName) {
|
|
90711
|
-
const cachedAt = this.knownTeams.get(teamName);
|
|
90712
|
-
if (cachedAt === undefined)
|
|
90713
|
-
return false;
|
|
90714
|
-
if (Date.now() - cachedAt > CACHE_TTL_MS) {
|
|
90715
|
-
this.knownTeams.delete(teamName);
|
|
90716
|
-
return false;
|
|
90717
|
-
}
|
|
90718
|
-
return true;
|
|
90719
|
-
}
|
|
90720
|
-
markTeamKnown(teamName) {
|
|
90721
|
-
if (this.knownTeams.size >= CACHE_MAX_SIZE && !this.knownTeams.has(teamName)) {
|
|
90722
|
-
const oldest = this.knownTeams.keys().next().value;
|
|
90723
|
-
if (oldest !== undefined)
|
|
90724
|
-
this.knownTeams.delete(oldest);
|
|
90725
|
-
}
|
|
90726
|
-
this.knownTeams.set(teamName, Date.now());
|
|
90727
|
-
}
|
|
90728
|
-
resolveConfig(request) {
|
|
90729
|
-
let teamName;
|
|
90730
|
-
let agentName;
|
|
90731
|
-
let targetAgent;
|
|
90732
|
-
if (this.hasTemplates) {
|
|
90733
|
-
const vars = extractTemplateVars(request);
|
|
90734
|
-
teamName = sanitize(interpolateTemplate(this.teamNameTemplate, vars));
|
|
90735
|
-
agentName = sanitize(interpolateTemplate(this.agentNameTemplate, vars));
|
|
90736
|
-
targetAgent = sanitize(interpolateTemplate(this.targetAgentTemplate, vars));
|
|
90737
|
-
} else {
|
|
90738
|
-
teamName = sanitize(this.teamNameTemplate);
|
|
90739
|
-
agentName = sanitize(this.agentNameTemplate);
|
|
90740
|
-
targetAgent = sanitize(this.targetAgentTemplate);
|
|
90741
|
-
}
|
|
90742
|
-
if (!teamName) {
|
|
90743
|
-
throw new ProviderError(`Team name resolved to empty string (template: "${this.teamNameTemplate}"). Ensure the template variable (e.g. {thread_id}) is populated in the request.`, "INVALID_RESPONSE");
|
|
90744
|
-
}
|
|
90745
|
-
if (!agentName) {
|
|
90746
|
-
throw new ProviderError(`Agent name resolved to empty string (template: "${this.agentNameTemplate}"). Ensure the template variable is populated in the request.`, "INVALID_RESPONSE");
|
|
90747
|
-
}
|
|
90748
|
-
if (!targetAgent) {
|
|
90749
|
-
throw new ProviderError(`Target agent resolved to empty string (template: "${this.targetAgentTemplate}"). Ensure the template variable is populated in the request.`, "INVALID_RESPONSE");
|
|
90750
|
-
}
|
|
90751
|
-
const inboxDir = join5(homedir3(), ".claude", "teams", teamName, "inboxes");
|
|
90752
|
-
const inboxPath = join5(inboxDir, `${targetAgent}.json`);
|
|
90753
|
-
return { teamName, agentName, targetAgent, inboxDir, inboxPath };
|
|
90754
|
-
}
|
|
90755
|
-
ensureTeamExists(teamName) {
|
|
90756
|
-
if (!this.autoSpawn)
|
|
90757
|
-
return;
|
|
90758
|
-
if (this.isTeamKnown(teamName))
|
|
90759
|
-
return;
|
|
90760
|
-
if (this.pendingTeams.has(teamName))
|
|
90761
|
-
return;
|
|
90762
|
-
this.pendingTeams.add(teamName);
|
|
90763
|
-
this.checkAndSpawnTeam(teamName).catch((error2) => {
|
|
90764
|
-
log7.warn("Auto-spawn failed", {
|
|
90765
|
-
teamName,
|
|
90766
|
-
error: error2 instanceof Error ? error2.message : String(error2)
|
|
90767
|
-
});
|
|
90768
|
-
this.pendingTeams.delete(teamName);
|
|
90769
|
-
});
|
|
90770
|
-
}
|
|
90771
|
-
async checkAndSpawnTeam(teamName) {
|
|
90772
|
-
await this.spawnAgentSession(teamName);
|
|
90773
|
-
}
|
|
90774
|
-
async spawnAgentSession(teamName) {
|
|
90775
|
-
const args = ["spawn", this.agentRole, "--team", teamName];
|
|
90776
|
-
if (this.autoSpawnDir) {
|
|
90777
|
-
args.push("--cwd", this.autoSpawnDir);
|
|
90778
|
-
}
|
|
90779
|
-
if (this.sessionName) {
|
|
90780
|
-
args.push("--session", this.sessionName);
|
|
90781
|
-
}
|
|
90782
|
-
log7.info("Auto-spawning agent session", { teamName, agentRole: this.agentRole, args });
|
|
90783
|
-
try {
|
|
90784
|
-
await execFilePromise("genie", args);
|
|
90785
|
-
log7.info("Agent session spawned successfully", { teamName });
|
|
90786
|
-
this.markTeamKnown(teamName);
|
|
90787
|
-
} catch (error2) {
|
|
90788
|
-
log7.warn("Agent session spawn failed", {
|
|
90789
|
-
teamName,
|
|
90790
|
-
error: error2 instanceof Error ? error2.message : String(error2)
|
|
90791
|
-
});
|
|
90792
|
-
} finally {
|
|
90793
|
-
this.pendingTeams.delete(teamName);
|
|
90794
|
-
}
|
|
90795
|
-
}
|
|
90796
|
-
buildMetadataHeader(request) {
|
|
90797
|
-
const tags = [];
|
|
90798
|
-
if (request.platform) {
|
|
90799
|
-
tags.push(`channel:${request.platform.channel}`);
|
|
90800
|
-
tags.push(`instance:${request.platform.instanceId}`);
|
|
90801
|
-
}
|
|
90802
|
-
if (request.chat?.id)
|
|
90803
|
-
tags.push(`chat:${request.chat.id}`);
|
|
90804
|
-
if (request.chat?.threadId)
|
|
90805
|
-
tags.push(`thread:${request.chat.threadId}`);
|
|
90806
|
-
if (request.messageId)
|
|
90807
|
-
tags.push(`msg:${request.messageId}`);
|
|
90808
|
-
if (request.sender?.displayName)
|
|
90809
|
-
tags.push(`from:${request.sender.displayName}`);
|
|
90810
|
-
if (request.chat?.type)
|
|
90811
|
-
tags.push(`type:${request.chat.type}`);
|
|
90812
|
-
if (request.replyToMessageId)
|
|
90813
|
-
tags.push(`replyTo:${request.replyToMessageId}`);
|
|
90814
|
-
return tags.length > 0 ? `[${tags.join(" ")}]` : "";
|
|
90815
|
-
}
|
|
90816
|
-
async acquireLock(lockPath) {
|
|
90817
|
-
try {
|
|
90818
|
-
return await open(lockPath, "wx");
|
|
90819
|
-
} catch {
|
|
90820
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
90821
|
-
}
|
|
90822
|
-
try {
|
|
90823
|
-
return await open(lockPath, "wx");
|
|
90824
|
-
} catch {}
|
|
90825
|
-
try {
|
|
90826
|
-
const lockStat = await stat(lockPath);
|
|
90827
|
-
const ageMs = Date.now() - lockStat.mtimeMs;
|
|
90828
|
-
if (ageMs > 30000) {
|
|
90829
|
-
log7.warn("Removing stale lock file", { lockPath, ageMs });
|
|
90830
|
-
await unlink(lockPath);
|
|
90831
|
-
return await open(lockPath, "wx");
|
|
90832
|
-
}
|
|
90833
|
-
log7.error("Lock file is fresh, another process is active", { lockPath, ageMs });
|
|
90834
|
-
} catch (error2) {
|
|
90835
|
-
if (error2 instanceof ProviderError)
|
|
90836
|
-
throw error2;
|
|
90837
|
-
log7.error("Could not recover from lock contention", { lockPath, error: error2 });
|
|
90838
|
-
}
|
|
90839
|
-
throw new ProviderError("Failed to acquire lock on team inbox, message not delivered", "SERVER_ERROR");
|
|
90840
|
-
}
|
|
90841
|
-
async run(request) {
|
|
90842
|
-
const startMs = Date.now();
|
|
90843
|
-
const { teamName, agentName, targetAgent, inboxDir, inboxPath } = this.resolveConfig(request);
|
|
90844
|
-
const header2 = this.buildMetadataHeader(request);
|
|
90845
|
-
const replyInstruction = `\u26A1 REPLY NOW via SendMessage to "${agentName}". Include the routing header from the first line of this message in your reply. Do not proceed with other tasks until you respond.`;
|
|
90846
|
-
const text = header2 ? `${header2}
|
|
90847
|
-
${request.message}
|
|
90848
|
-
${replyInstruction}` : `${request.message}
|
|
90849
|
-
${replyInstruction}`;
|
|
90850
|
-
const summary = request.message.length > 50 ? `${request.message.substring(0, 50)}...` : request.message;
|
|
90851
|
-
const inboxMessage = {
|
|
90852
|
-
from: agentName,
|
|
90853
|
-
text,
|
|
90854
|
-
summary,
|
|
90855
|
-
timestamp: new Date().toISOString(),
|
|
90856
|
-
read: false
|
|
90857
|
-
};
|
|
90858
|
-
await mkdir(inboxDir, { recursive: true });
|
|
90859
|
-
const lockPath = `${inboxPath}.lock`;
|
|
90860
|
-
const lockFd = await this.acquireLock(lockPath);
|
|
90861
|
-
try {
|
|
90862
|
-
let inbox = [];
|
|
90863
|
-
try {
|
|
90864
|
-
const data2 = await readFile(inboxPath, "utf-8");
|
|
90865
|
-
inbox = JSON.parse(data2);
|
|
90866
|
-
if (!Array.isArray(inbox))
|
|
90867
|
-
inbox = [];
|
|
90868
|
-
} catch {
|
|
90869
|
-
inbox = [];
|
|
90870
|
-
}
|
|
90871
|
-
inbox.push(inboxMessage);
|
|
90872
|
-
const tmpPath = `${inboxPath}.tmp`;
|
|
90873
|
-
await writeFile(tmpPath, JSON.stringify(inbox, null, 2), "utf-8");
|
|
90874
|
-
await rename(tmpPath, inboxPath);
|
|
90875
|
-
} finally {
|
|
90876
|
-
await lockFd.close();
|
|
90877
|
-
try {
|
|
90878
|
-
await unlink(lockPath);
|
|
90879
|
-
} catch {}
|
|
90880
|
-
}
|
|
90881
|
-
this.ensureTeamExists(teamName);
|
|
90882
|
-
const durationMs = Date.now() - startMs;
|
|
90883
|
-
log7.info("Message delivered to team inbox", {
|
|
90884
|
-
agent: agentName,
|
|
90885
|
-
team: teamName,
|
|
90886
|
-
target: targetAgent,
|
|
90887
|
-
messageLength: text.length,
|
|
90888
|
-
durationMs,
|
|
90889
|
-
hasTemplates: this.hasTemplates
|
|
90890
|
-
});
|
|
90891
|
-
return {
|
|
90892
|
-
content: "",
|
|
90893
|
-
runId: `genie-${agentName}-${Date.now()}`,
|
|
90894
|
-
sessionId: request.sessionId ?? "",
|
|
90895
|
-
status: "completed",
|
|
90896
|
-
metrics: {
|
|
90897
|
-
inputTokens: 0,
|
|
90898
|
-
outputTokens: 0,
|
|
90899
|
-
durationMs
|
|
90900
|
-
}
|
|
90901
|
-
};
|
|
90902
|
-
}
|
|
90903
|
-
async* stream(_request) {
|
|
90904
|
-
throw new ProviderError("Genie client does not support streaming", "STREAM_ERROR");
|
|
90905
|
-
}
|
|
90906
|
-
async checkHealth() {
|
|
90907
|
-
const startMs = Date.now();
|
|
90908
|
-
try {
|
|
90909
|
-
if (this.hasTemplates) {
|
|
90910
|
-
const teamsRoot = join5(homedir3(), ".claude", "teams");
|
|
90911
|
-
try {
|
|
90912
|
-
await stat(teamsRoot);
|
|
90913
|
-
} catch {
|
|
90914
|
-
return {
|
|
90915
|
-
healthy: false,
|
|
90916
|
-
latencyMs: Date.now() - startMs,
|
|
90917
|
-
error: `Teams root directory does not exist: ${teamsRoot}`
|
|
90918
|
-
};
|
|
90919
|
-
}
|
|
90920
|
-
return {
|
|
90921
|
-
healthy: true,
|
|
90922
|
-
latencyMs: Date.now() - startMs
|
|
90923
|
-
};
|
|
90924
|
-
}
|
|
90925
|
-
const teamName = sanitize(this.teamNameTemplate);
|
|
90926
|
-
const teamDir = join5(homedir3(), ".claude", "teams", teamName);
|
|
90927
|
-
const inboxDir = join5(teamDir, "inboxes");
|
|
90928
|
-
try {
|
|
90929
|
-
await stat(teamDir);
|
|
90930
|
-
} catch {
|
|
90931
|
-
return {
|
|
90932
|
-
healthy: false,
|
|
90933
|
-
latencyMs: Date.now() - startMs,
|
|
90934
|
-
error: `Team directory does not exist: ${teamDir}`
|
|
90935
|
-
};
|
|
90936
|
-
}
|
|
90937
|
-
try {
|
|
90938
|
-
await stat(inboxDir);
|
|
90939
|
-
} catch {
|
|
90940
|
-
return {
|
|
90941
|
-
healthy: false,
|
|
90942
|
-
latencyMs: Date.now() - startMs,
|
|
90943
|
-
error: `Inbox directory does not exist: ${inboxDir}`
|
|
90944
|
-
};
|
|
90945
|
-
}
|
|
90946
|
-
return {
|
|
90947
|
-
healthy: true,
|
|
90948
|
-
latencyMs: Date.now() - startMs
|
|
90949
|
-
};
|
|
90950
|
-
} catch (error2) {
|
|
90951
|
-
return {
|
|
90952
|
-
healthy: false,
|
|
90953
|
-
latencyMs: Date.now() - startMs,
|
|
90954
|
-
error: error2 instanceof Error ? error2.message : String(error2)
|
|
90955
|
-
};
|
|
90956
|
-
}
|
|
90957
|
-
}
|
|
90958
|
-
}
|
|
90959
90976
|
// ../core/src/providers/agno-provider.ts
|
|
90960
|
-
var
|
|
90977
|
+
var log7 = createLogger("provider:agno");
|
|
90961
90978
|
// ../core/src/providers/claude-code-provider.ts
|
|
90962
|
-
var
|
|
90979
|
+
var log8 = createLogger("provider:claude-code");
|
|
90963
90980
|
// ../core/src/providers/webhook-provider.ts
|
|
90964
|
-
var
|
|
90981
|
+
var log9 = createLogger("provider:webhook");
|
|
90965
90982
|
// ../core/src/providers/openclaw/client.ts
|
|
90966
90983
|
import * as nodeCrypto from "crypto";
|
|
90967
90984
|
|
|
@@ -90985,7 +91002,7 @@ function generateDeviceKeypair() {
|
|
|
90985
91002
|
}
|
|
90986
91003
|
|
|
90987
91004
|
// ../core/src/providers/openclaw/client.ts
|
|
90988
|
-
var
|
|
91005
|
+
var log10 = createLogger("openclaw:client");
|
|
90989
91006
|
|
|
90990
91007
|
class OpenClawClient {
|
|
90991
91008
|
ws = null;
|
|
@@ -91007,7 +91024,7 @@ class OpenClawClient {
|
|
|
91007
91024
|
const urlObj = new URL(config2.url);
|
|
91008
91025
|
const host = urlObj.hostname;
|
|
91009
91026
|
if (host !== "localhost" && host !== "127.0.0.1" && host !== "::1") {
|
|
91010
|
-
|
|
91027
|
+
log10.warn("Connecting over unencrypted WebSocket to non-localhost", {
|
|
91011
91028
|
providerId: config2.providerId,
|
|
91012
91029
|
host
|
|
91013
91030
|
});
|
|
@@ -91046,7 +91063,7 @@ class OpenClawClient {
|
|
|
91046
91063
|
this.flushAccumulations(new Error("Client stopped"));
|
|
91047
91064
|
this.flushAgentAccumulations(new Error("Client stopped"));
|
|
91048
91065
|
this.setState("disconnected");
|
|
91049
|
-
|
|
91066
|
+
log10.info("Client stopped", {
|
|
91050
91067
|
providerId: this.config.providerId,
|
|
91051
91068
|
pendingRequests: this.pending.size
|
|
91052
91069
|
});
|
|
@@ -91133,7 +91150,7 @@ class OpenClawClient {
|
|
|
91133
91150
|
try {
|
|
91134
91151
|
this.ws = new WebSocket(this.config.url);
|
|
91135
91152
|
} catch (err) {
|
|
91136
|
-
|
|
91153
|
+
log10.error("WebSocket construction failed", {
|
|
91137
91154
|
providerId: this.config.providerId,
|
|
91138
91155
|
error: String(err)
|
|
91139
91156
|
});
|
|
@@ -91190,7 +91207,7 @@ class OpenClawClient {
|
|
|
91190
91207
|
try {
|
|
91191
91208
|
listener(event);
|
|
91192
91209
|
} catch (err) {
|
|
91193
|
-
|
|
91210
|
+
log10.error("Event listener error", {
|
|
91194
91211
|
providerId: this.config.providerId,
|
|
91195
91212
|
event: event.event,
|
|
91196
91213
|
error: String(err)
|
|
@@ -91207,7 +91224,7 @@ class OpenClawClient {
|
|
|
91207
91224
|
try {
|
|
91208
91225
|
callback(chatEvent);
|
|
91209
91226
|
} catch (err) {
|
|
91210
|
-
|
|
91227
|
+
log10.error("Accumulation callback error", {
|
|
91211
91228
|
providerId: this.config.providerId,
|
|
91212
91229
|
runId: chatEvent.runId,
|
|
91213
91230
|
error: String(err)
|
|
@@ -91224,7 +91241,7 @@ class OpenClawClient {
|
|
|
91224
91241
|
try {
|
|
91225
91242
|
callback(payload);
|
|
91226
91243
|
} catch (err) {
|
|
91227
|
-
|
|
91244
|
+
log10.error("Agent accumulation callback error", {
|
|
91228
91245
|
providerId: this.config.providerId,
|
|
91229
91246
|
runId,
|
|
91230
91247
|
error: String(err)
|
|
@@ -91280,7 +91297,7 @@ class OpenClawClient {
|
|
|
91280
91297
|
nonce
|
|
91281
91298
|
};
|
|
91282
91299
|
authToken = dev.token;
|
|
91283
|
-
|
|
91300
|
+
log10.debug("Using device keypair for operator scopes", {
|
|
91284
91301
|
providerId: this.config.providerId,
|
|
91285
91302
|
deviceId: dev.id,
|
|
91286
91303
|
scopes
|
|
@@ -91303,7 +91320,7 @@ class OpenClawClient {
|
|
|
91303
91320
|
userAgent: "omni-v2/1.0.0",
|
|
91304
91321
|
device: deviceField
|
|
91305
91322
|
};
|
|
91306
|
-
|
|
91323
|
+
log10.debug("Sending connect handshake", {
|
|
91307
91324
|
providerId: this.config.providerId,
|
|
91308
91325
|
auth: params.auth ? { token: "[REDACTED]" } : undefined,
|
|
91309
91326
|
scopes: params.scopes,
|
|
@@ -91316,13 +91333,13 @@ class OpenClawClient {
|
|
|
91316
91333
|
this.reconnectAttempt = 0;
|
|
91317
91334
|
this.setState("connected");
|
|
91318
91335
|
this.startHealthPing();
|
|
91319
|
-
|
|
91336
|
+
log10.info("Connected to OpenClaw gateway", {
|
|
91320
91337
|
providerId: this.config.providerId,
|
|
91321
91338
|
protocol: payload.protocol,
|
|
91322
91339
|
defaultAgentId: payload.snapshot?.sessionDefaults?.defaultAgentId
|
|
91323
91340
|
});
|
|
91324
91341
|
} catch (err) {
|
|
91325
|
-
|
|
91342
|
+
log10.error("Connect handshake failed", {
|
|
91326
91343
|
providerId: this.config.providerId,
|
|
91327
91344
|
error: String(err)
|
|
91328
91345
|
});
|
|
@@ -91350,13 +91367,13 @@ class OpenClawClient {
|
|
|
91350
91367
|
backoffMs: Math.round(this.backoffMs)
|
|
91351
91368
|
};
|
|
91352
91369
|
if (this.reconnectAttempt < 3) {
|
|
91353
|
-
|
|
91370
|
+
log10.warn("Connection lost, reconnecting", ctx);
|
|
91354
91371
|
} else if (this.reconnectAttempt % 10 === 0) {
|
|
91355
|
-
|
|
91372
|
+
log10.warn("Still reconnecting (periodic)", ctx);
|
|
91356
91373
|
} else if (this.reconnectAttempt < 10) {
|
|
91357
|
-
|
|
91374
|
+
log10.info("Reconnecting", ctx);
|
|
91358
91375
|
} else {
|
|
91359
|
-
|
|
91376
|
+
log10.debug("Reconnecting", ctx);
|
|
91360
91377
|
}
|
|
91361
91378
|
}
|
|
91362
91379
|
startHealthPing() {
|
|
@@ -91364,7 +91381,7 @@ class OpenClawClient {
|
|
|
91364
91381
|
this.healthPingTimer = setInterval(() => {
|
|
91365
91382
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
91366
91383
|
this.request("health").then(() => {}).catch(() => {
|
|
91367
|
-
|
|
91384
|
+
log10.warn("Health ping failed, connection may be stale", {
|
|
91368
91385
|
providerId: this.config.providerId
|
|
91369
91386
|
});
|
|
91370
91387
|
});
|
|
@@ -91418,22 +91435,193 @@ class OpenClawClient {
|
|
|
91418
91435
|
}
|
|
91419
91436
|
}
|
|
91420
91437
|
// ../core/src/providers/openclaw/provider.ts
|
|
91421
|
-
var
|
|
91438
|
+
var log11 = createLogger("openclaw:provider");
|
|
91422
91439
|
var MAX_MESSAGE_BYTES = 100 * 1024;
|
|
91423
91440
|
var MAX_ACCUMULATION_BYTES = 1 * 1024 * 1024;
|
|
91424
91441
|
var MAX_DELTA_CONTENT_BYTES = 256 * 1024;
|
|
91425
91442
|
// ../core/src/providers/ag-ui-client.ts
|
|
91426
|
-
var
|
|
91443
|
+
var log12 = createLogger("providers:ag-ui-client");
|
|
91427
91444
|
|
|
91428
91445
|
// ../core/src/providers/ag-ui-provider.ts
|
|
91429
|
-
var
|
|
91446
|
+
var log13 = createLogger("provider:ag-ui");
|
|
91430
91447
|
// ../core/src/providers/a2a-client.ts
|
|
91431
|
-
var
|
|
91448
|
+
var log14 = createLogger("providers:a2a-client");
|
|
91432
91449
|
|
|
91433
91450
|
// ../core/src/providers/a2a-provider.ts
|
|
91434
|
-
var
|
|
91435
|
-
// ../core/src/providers/genie-provider.ts
|
|
91436
|
-
var
|
|
91451
|
+
var log15 = createLogger("provider:a2a");
|
|
91452
|
+
// ../core/src/providers/nats-genie-provider.ts
|
|
91453
|
+
var import_nats4 = __toESM(require_mod4(), 1);
|
|
91454
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
91455
|
+
import { homedir as homedir3 } from "os";
|
|
91456
|
+
import { join as join5 } from "path";
|
|
91457
|
+
var log16 = createLogger("provider:nats-genie");
|
|
91458
|
+
|
|
91459
|
+
class NatsGenieProvider {
|
|
91460
|
+
id;
|
|
91461
|
+
name;
|
|
91462
|
+
config;
|
|
91463
|
+
schema = "nats-genie";
|
|
91464
|
+
mode = "fire-and-forget";
|
|
91465
|
+
nc = null;
|
|
91466
|
+
sc = import_nats4.StringCodec();
|
|
91467
|
+
replySubscription = null;
|
|
91468
|
+
constructor(id, name, config2) {
|
|
91469
|
+
this.id = id;
|
|
91470
|
+
this.name = name;
|
|
91471
|
+
this.config = config2;
|
|
91472
|
+
}
|
|
91473
|
+
canHandle(_trigger) {
|
|
91474
|
+
return true;
|
|
91475
|
+
}
|
|
91476
|
+
async trigger(context) {
|
|
91477
|
+
const startTime = Date.now();
|
|
91478
|
+
const message2 = this.buildMessage(context);
|
|
91479
|
+
if (!message2) {
|
|
91480
|
+
log16.debug("No content to send", { traceId: context.traceId });
|
|
91481
|
+
return {
|
|
91482
|
+
parts: [],
|
|
91483
|
+
metadata: { runId: "", providerId: this.id, durationMs: Date.now() - startTime }
|
|
91484
|
+
};
|
|
91485
|
+
}
|
|
91486
|
+
const topic = `omni.message.${this.config.instanceId}.${context.source.chatId}`;
|
|
91487
|
+
const payload = {
|
|
91488
|
+
content: message2,
|
|
91489
|
+
sender: context.sender.displayName ?? context.sender.platformUserId,
|
|
91490
|
+
instanceId: this.config.instanceId,
|
|
91491
|
+
chatId: context.source.chatId,
|
|
91492
|
+
agent: this.config.agentName,
|
|
91493
|
+
timestamp: new Date().toISOString(),
|
|
91494
|
+
traceId: context.traceId,
|
|
91495
|
+
messageId: context.source.messageId,
|
|
91496
|
+
files: context.content.files
|
|
91497
|
+
};
|
|
91498
|
+
try {
|
|
91499
|
+
await this.ensureConnected();
|
|
91500
|
+
this.nc?.publish(topic, this.sc.encode(JSON.stringify(payload)));
|
|
91501
|
+
log16.info("Published to NATS", {
|
|
91502
|
+
topic,
|
|
91503
|
+
agent: this.config.agentName,
|
|
91504
|
+
chatId: context.source.chatId,
|
|
91505
|
+
traceId: context.traceId
|
|
91506
|
+
});
|
|
91507
|
+
} catch (error2) {
|
|
91508
|
+
log16.error("Failed to publish to NATS, writing to dead-letter queue", {
|
|
91509
|
+
topic,
|
|
91510
|
+
error: error2 instanceof Error ? error2.message : String(error2),
|
|
91511
|
+
traceId: context.traceId
|
|
91512
|
+
});
|
|
91513
|
+
await this.writeDeadLetter(payload, error2);
|
|
91514
|
+
}
|
|
91515
|
+
const durationMs = Date.now() - startTime;
|
|
91516
|
+
return {
|
|
91517
|
+
parts: [],
|
|
91518
|
+
metadata: {
|
|
91519
|
+
runId: `nats-genie-${Date.now()}`,
|
|
91520
|
+
providerId: this.id,
|
|
91521
|
+
durationMs
|
|
91522
|
+
}
|
|
91523
|
+
};
|
|
91524
|
+
}
|
|
91525
|
+
async startReplySubscription() {
|
|
91526
|
+
if (!this.config.onReply)
|
|
91527
|
+
return;
|
|
91528
|
+
if (this.replySubscription)
|
|
91529
|
+
return;
|
|
91530
|
+
await this.ensureConnected();
|
|
91531
|
+
if (!this.nc)
|
|
91532
|
+
return;
|
|
91533
|
+
const topic = `omni.reply.${this.config.instanceId}.*`;
|
|
91534
|
+
const sub = this.nc.subscribe(topic);
|
|
91535
|
+
this.replySubscription = sub;
|
|
91536
|
+
log16.info("Subscribed to agent replies", { topic });
|
|
91537
|
+
(async () => {
|
|
91538
|
+
for await (const msg of sub) {
|
|
91539
|
+
try {
|
|
91540
|
+
const data2 = JSON.parse(this.sc.decode(msg.data));
|
|
91541
|
+
const chatId = data2.chat_id || msg.subject.split(".").pop() || "";
|
|
91542
|
+
if (data2.content && this.config.onReply) {
|
|
91543
|
+
await this.config.onReply(chatId, data2.content, {
|
|
91544
|
+
agent: data2.agent,
|
|
91545
|
+
auto_reply: data2.auto_reply,
|
|
91546
|
+
timestamp: data2.timestamp
|
|
91547
|
+
});
|
|
91548
|
+
}
|
|
91549
|
+
} catch (error2) {
|
|
91550
|
+
log16.error("Error processing reply", {
|
|
91551
|
+
subject: msg.subject,
|
|
91552
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
91553
|
+
});
|
|
91554
|
+
}
|
|
91555
|
+
}
|
|
91556
|
+
})();
|
|
91557
|
+
}
|
|
91558
|
+
async checkHealth() {
|
|
91559
|
+
const startMs = Date.now();
|
|
91560
|
+
try {
|
|
91561
|
+
await this.ensureConnected();
|
|
91562
|
+
return { healthy: this.nc !== null, latencyMs: Date.now() - startMs };
|
|
91563
|
+
} catch (error2) {
|
|
91564
|
+
return {
|
|
91565
|
+
healthy: false,
|
|
91566
|
+
latencyMs: Date.now() - startMs,
|
|
91567
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
91568
|
+
};
|
|
91569
|
+
}
|
|
91570
|
+
}
|
|
91571
|
+
async dispose() {
|
|
91572
|
+
if (this.replySubscription) {
|
|
91573
|
+
this.replySubscription.unsubscribe();
|
|
91574
|
+
this.replySubscription = null;
|
|
91575
|
+
}
|
|
91576
|
+
if (this.nc) {
|
|
91577
|
+
try {
|
|
91578
|
+
await this.nc.drain();
|
|
91579
|
+
} catch {}
|
|
91580
|
+
this.nc = null;
|
|
91581
|
+
}
|
|
91582
|
+
}
|
|
91583
|
+
async ensureConnected() {
|
|
91584
|
+
if (this.nc)
|
|
91585
|
+
return;
|
|
91586
|
+
this.nc = await import_nats4.connect({
|
|
91587
|
+
servers: this.config.natsUrl,
|
|
91588
|
+
name: `omni-nats-genie-${this.config.instanceId}`,
|
|
91589
|
+
reconnect: true,
|
|
91590
|
+
maxReconnectAttempts: -1,
|
|
91591
|
+
reconnectTimeWait: 2000
|
|
91592
|
+
});
|
|
91593
|
+
log16.info("Connected to NATS", { url: this.config.natsUrl });
|
|
91594
|
+
}
|
|
91595
|
+
buildMessage(context) {
|
|
91596
|
+
let message2 = "";
|
|
91597
|
+
if (context.content.text) {
|
|
91598
|
+
message2 = context.content.text;
|
|
91599
|
+
} else if (context.content.emoji) {
|
|
91600
|
+
message2 = `[Reaction: ${context.content.emoji} on message ${context.content.referencedMessageId ?? context.source.messageId}]`;
|
|
91601
|
+
}
|
|
91602
|
+
if (message2 && this.config.prefixSenderName !== false && context.sender.displayName) {
|
|
91603
|
+
message2 = `[${context.sender.displayName}]: ${message2}`;
|
|
91604
|
+
}
|
|
91605
|
+
return message2;
|
|
91606
|
+
}
|
|
91607
|
+
async writeDeadLetter(payload, error2) {
|
|
91608
|
+
try {
|
|
91609
|
+
const dlDir = join5(homedir3(), ".omni", "dead-letters");
|
|
91610
|
+
await mkdir(dlDir, { recursive: true });
|
|
91611
|
+
const filename = `nats-genie-${Date.now()}-${payload.chatId}.json`;
|
|
91612
|
+
await writeFile(join5(dlDir, filename), JSON.stringify({
|
|
91613
|
+
payload,
|
|
91614
|
+
error: error2 instanceof Error ? error2.message : String(error2),
|
|
91615
|
+
timestamp: new Date().toISOString()
|
|
91616
|
+
}, null, 2));
|
|
91617
|
+
log16.warn("Dead letter written", { filename });
|
|
91618
|
+
} catch (dlError) {
|
|
91619
|
+
log16.error("Failed to write dead letter", {
|
|
91620
|
+
error: dlError instanceof Error ? dlError.message : String(dlError)
|
|
91621
|
+
});
|
|
91622
|
+
}
|
|
91623
|
+
}
|
|
91624
|
+
}
|
|
91437
91625
|
// ../core/src/tracing/journey-tracker.ts
|
|
91438
91626
|
var LATENCY_PAIRS = [
|
|
91439
91627
|
{ key: "channelProcessing", from: "T0", to: "T1" },
|
|
@@ -91762,17 +91950,17 @@ class HookRegistry {
|
|
|
91762
91950
|
}
|
|
91763
91951
|
}
|
|
91764
91952
|
// ../core/src/hooks/executor.ts
|
|
91765
|
-
var
|
|
91953
|
+
var log17 = createLogger("hooks:executor");
|
|
91766
91954
|
// src/commands/providers-setup.ts
|
|
91767
|
-
import { execFileSync, execSync } from "child_process";
|
|
91955
|
+
import { execFileSync as execFileSync2, execSync } from "child_process";
|
|
91768
91956
|
import * as nodeCrypto2 from "crypto";
|
|
91769
91957
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
91770
91958
|
import { homedir as homedir4 } from "os";
|
|
91771
91959
|
import { dirname as dirname4, resolve as resolve2 } from "path";
|
|
91772
|
-
import { createInterface as
|
|
91960
|
+
import { createInterface as createInterface3 } from "readline";
|
|
91773
91961
|
init_config();
|
|
91774
91962
|
async function promptLine2(question, defaultValue = "") {
|
|
91775
|
-
const rl =
|
|
91963
|
+
const rl = createInterface3({
|
|
91776
91964
|
input: process.stdin,
|
|
91777
91965
|
output: process.stdout
|
|
91778
91966
|
});
|
|
@@ -92045,7 +92233,7 @@ function resolvePluginPath(explicit) {
|
|
|
92045
92233
|
function registerPlugin(config2, pluginPath, configPath) {
|
|
92046
92234
|
if (hasOpenClawCli()) {
|
|
92047
92235
|
try {
|
|
92048
|
-
|
|
92236
|
+
execFileSync2("openclaw", ["plugins", "install", "--link", pluginPath], { stdio: "ignore" });
|
|
92049
92237
|
try {
|
|
92050
92238
|
return { config: readOpenClawConfig(configPath), registered: true };
|
|
92051
92239
|
} catch {
|
|
@@ -92211,136 +92399,6 @@ async function runOpenClawSetup(opts) {
|
|
|
92211
92399
|
error(`OpenClaw provider setup failed: ${message2}`);
|
|
92212
92400
|
}
|
|
92213
92401
|
}
|
|
92214
|
-
var GENIE_TEMPLATE_VARS = `
|
|
92215
|
-
Template variables (resolved per-message at runtime):
|
|
92216
|
-
{chat_id} - Conversation ID (1 session per chat)
|
|
92217
|
-
{thread_id} - Thread ID (1 session per thread)
|
|
92218
|
-
{sender_id} - Sender user ID (1 session per person)
|
|
92219
|
-
{channel} - Channel type (telegram, whatsapp, etc.)
|
|
92220
|
-
{instance_id} - Omni instance ID
|
|
92221
|
-
Examples:
|
|
92222
|
-
"workspace" \u2192 Single shared session
|
|
92223
|
-
"genie-{chat_id}" \u2192 One session per conversation
|
|
92224
|
-
"genie-{chat_id}-{thread_id}" \u2192 Per-thread isolation`;
|
|
92225
|
-
async function collectGenieOptions(options) {
|
|
92226
|
-
const agentName = options.agentName ?? await promptLine2('Agent name (your identity / "from" field): ');
|
|
92227
|
-
const targetAgent = options.targetAgent ?? await promptLine2("Target agent (inbox to deliver to): ");
|
|
92228
|
-
const agentRole = options.agentRole ?? await promptLine2('Agent role (registered genie dir agent, e.g. "omni-pm") [team-lead]: ', "team-lead");
|
|
92229
|
-
if (!options.teamName) {
|
|
92230
|
-
info(GENIE_TEMPLATE_VARS);
|
|
92231
|
-
}
|
|
92232
|
-
const teamName = options.teamName ?? await promptLine2(`Team name template [${agentName}-{chat_id}]: `, `${agentName}-{chat_id}`);
|
|
92233
|
-
const defaultName = `genie-${agentName}`;
|
|
92234
|
-
const name = options.name ?? await promptLine2(`Provider name [${defaultName}]: `, defaultName);
|
|
92235
|
-
const baseUrl = options.baseUrl ?? await promptLine2("Base URL [file:///home/genie/.claude/teams]: ", "file:///home/genie/.claude/teams");
|
|
92236
|
-
let instanceId = options.instanceId;
|
|
92237
|
-
if (!instanceId) {
|
|
92238
|
-
instanceId = await promptLine2("Instance ID (Omni instance UUID, leave blank to skip): ") || undefined;
|
|
92239
|
-
}
|
|
92240
|
-
return {
|
|
92241
|
-
agentName,
|
|
92242
|
-
targetAgent,
|
|
92243
|
-
teamName,
|
|
92244
|
-
agentRole,
|
|
92245
|
-
name,
|
|
92246
|
-
baseUrl,
|
|
92247
|
-
instanceId,
|
|
92248
|
-
nonInteractive: options.nonInteractive
|
|
92249
|
-
};
|
|
92250
|
-
}
|
|
92251
|
-
async function runGenieSetup(opts) {
|
|
92252
|
-
const spinner = ora();
|
|
92253
|
-
try {
|
|
92254
|
-
spinner.start("Creating Genie provider...");
|
|
92255
|
-
const client = getClient();
|
|
92256
|
-
const provider = await client.providers.create({
|
|
92257
|
-
name: opts.name,
|
|
92258
|
-
schema: "genie",
|
|
92259
|
-
baseUrl: opts.baseUrl,
|
|
92260
|
-
schemaConfig: {
|
|
92261
|
-
agentName: opts.agentName,
|
|
92262
|
-
targetAgent: opts.targetAgent,
|
|
92263
|
-
teamName: opts.teamName,
|
|
92264
|
-
agentRole: opts.agentRole
|
|
92265
|
-
}
|
|
92266
|
-
});
|
|
92267
|
-
spinner.succeed(`Provider created: ${provider.id}`);
|
|
92268
|
-
spinner.start("Testing provider health...");
|
|
92269
|
-
const health = await client.providers.checkHealth(provider.id);
|
|
92270
|
-
if (health.healthy) {
|
|
92271
|
-
spinner.succeed(`Provider is healthy (latency: ${health.latency}ms)`);
|
|
92272
|
-
} else {
|
|
92273
|
-
spinner.warn(`Provider created but health check failed: ${health.error ?? "unknown"}`);
|
|
92274
|
-
info(" The provider was created. Re-test later with:");
|
|
92275
|
-
info(` omni providers test ${provider.id}`);
|
|
92276
|
-
}
|
|
92277
|
-
info("");
|
|
92278
|
-
success("Genie provider setup complete");
|
|
92279
|
-
info(` Provider ID: ${provider.id}`);
|
|
92280
|
-
info(` Provider name: ${opts.name}`);
|
|
92281
|
-
info(` Agent name: ${opts.agentName}`);
|
|
92282
|
-
info(` Target agent: ${opts.targetAgent}`);
|
|
92283
|
-
info(` Agent role: ${opts.agentRole}`);
|
|
92284
|
-
info(` Team template: ${opts.teamName}`);
|
|
92285
|
-
info(` Base URL: ${opts.baseUrl}`);
|
|
92286
|
-
info("");
|
|
92287
|
-
info("How it works:");
|
|
92288
|
-
info(" 1. Incoming messages are written to ~/.claude/teams/<team>/inboxes/<target>.json");
|
|
92289
|
-
info(" 2. Claude Code agent picks up messages natively from its team inbox");
|
|
92290
|
-
info(" 3. Agent replies via `omni send` (fire-and-forget, no polling)");
|
|
92291
|
-
info("");
|
|
92292
|
-
info("Next steps:");
|
|
92293
|
-
if (opts.instanceId) {
|
|
92294
|
-
info(` 1. Assign to instance: omni instances update ${opts.instanceId} --agent-provider ${provider.id}`);
|
|
92295
|
-
} else {
|
|
92296
|
-
info(` 1. Assign to instance: omni instances update <instance-id> --agent-provider ${provider.id}`);
|
|
92297
|
-
}
|
|
92298
|
-
info(` 2. Test connectivity: omni providers test ${provider.id}`);
|
|
92299
|
-
info(` 3. Update template: omni providers update ${provider.id} --team-name "new-{chat_id}"`);
|
|
92300
|
-
} catch (err) {
|
|
92301
|
-
spinner.fail("Setup failed");
|
|
92302
|
-
const message2 = err instanceof Error ? err.message : "Unknown error";
|
|
92303
|
-
error(`Genie provider setup failed: ${message2}`);
|
|
92304
|
-
}
|
|
92305
|
-
}
|
|
92306
|
-
function resolveGenieNonInteractive(options) {
|
|
92307
|
-
if (!options.agentName)
|
|
92308
|
-
return "Missing required flag: --agent-name";
|
|
92309
|
-
if (!options.targetAgent)
|
|
92310
|
-
return "Missing required flag: --target-agent";
|
|
92311
|
-
return {
|
|
92312
|
-
agentName: options.agentName,
|
|
92313
|
-
targetAgent: options.targetAgent,
|
|
92314
|
-
teamName: options.teamName ?? `${options.agentName}-{chat_id}`,
|
|
92315
|
-
agentRole: options.agentRole ?? "team-lead",
|
|
92316
|
-
name: options.name ?? `genie-${options.agentName}`,
|
|
92317
|
-
baseUrl: options.baseUrl ?? "file:///home/genie/.claude/teams",
|
|
92318
|
-
instanceId: options.instanceId,
|
|
92319
|
-
nonInteractive: true
|
|
92320
|
-
};
|
|
92321
|
-
}
|
|
92322
|
-
async function handleGenieSetup(options) {
|
|
92323
|
-
if (options.instanceId && !isValidUuid(options.instanceId)) {
|
|
92324
|
-
error(`Invalid --instance-id: must be a valid UUID. Got: ${options.instanceId}`);
|
|
92325
|
-
return;
|
|
92326
|
-
}
|
|
92327
|
-
let resolved;
|
|
92328
|
-
if (options.nonInteractive) {
|
|
92329
|
-
const result = resolveGenieNonInteractive(options);
|
|
92330
|
-
if (typeof result === "string") {
|
|
92331
|
-
error(result);
|
|
92332
|
-
return;
|
|
92333
|
-
}
|
|
92334
|
-
resolved = result;
|
|
92335
|
-
} else {
|
|
92336
|
-
resolved = await collectGenieOptions(options);
|
|
92337
|
-
}
|
|
92338
|
-
if (resolved.instanceId && !isValidUuid(resolved.instanceId)) {
|
|
92339
|
-
error(`Invalid instance ID: must be a valid UUID. Got: ${resolved.instanceId}`);
|
|
92340
|
-
return;
|
|
92341
|
-
}
|
|
92342
|
-
await runGenieSetup(resolved);
|
|
92343
|
-
}
|
|
92344
92402
|
function createSetupCommand() {
|
|
92345
92403
|
const setup = new Command("setup").description("Interactive setup wizards for providers");
|
|
92346
92404
|
setup.command("openclaw").description("Set up an OpenClaw provider (keypair generation + device pairing + provider creation)").option("--gateway-url <url>", "Gateway WebSocket URL (ws:// or wss://)").option("--gateway-token <token>", "Gateway authentication token").option("--agent-id <agentId>", "Default agent ID").option("--name <name>", "Provider name (default: openclaw-<agent-id>)").option("--instance-id <uuid>", "Omni instance UUID for the openclaw channel account").option("--account-name <name>", "Account name in openclaw.json (default: agent-id)").option("--plugin-path <path>", "Path to omni.ts plugin entry (auto-detected from CWD)").option("--skip-openclaw-config", "Skip openclaw.json updates entirely").option("--non-interactive", "Error on missing required flags instead of prompting").action(async (options) => {
|
|
@@ -92372,7 +92430,9 @@ function createSetupCommand() {
|
|
|
92372
92430
|
}
|
|
92373
92431
|
await runOpenClawSetup(resolved);
|
|
92374
92432
|
});
|
|
92375
|
-
setup.command("genie").description("
|
|
92433
|
+
setup.command("genie").description("[DEPRECATED] Use `omni connect <instance-id> <agent-name>` instead").allowUnknownOption().action(() => {
|
|
92434
|
+
error("The `providers setup genie` command has been replaced.\nUse `omni connect <instance-id> <agent-name>` for NATS-based Genie integration.");
|
|
92435
|
+
});
|
|
92376
92436
|
return setup;
|
|
92377
92437
|
}
|
|
92378
92438
|
|
|
@@ -92427,18 +92487,17 @@ function buildClaudeCodeConfig(options) {
|
|
|
92427
92487
|
config2.systemPrompt = options.systemPrompt;
|
|
92428
92488
|
return config2;
|
|
92429
92489
|
}
|
|
92430
|
-
function
|
|
92431
|
-
|
|
92432
|
-
|
|
92433
|
-
|
|
92434
|
-
|
|
92435
|
-
};
|
|
92490
|
+
function buildNatsGenieConfig(options) {
|
|
92491
|
+
const config2 = {};
|
|
92492
|
+
if (options.agentName)
|
|
92493
|
+
config2.agentName = options.agentName;
|
|
92494
|
+
return config2;
|
|
92436
92495
|
}
|
|
92437
92496
|
function buildSchemaConfig(options) {
|
|
92438
92497
|
const builders = {
|
|
92439
92498
|
openclaw: buildOpenClawConfig,
|
|
92440
92499
|
"claude-code": buildClaudeCodeConfig,
|
|
92441
|
-
genie:
|
|
92500
|
+
"nats-genie": buildNatsGenieConfig
|
|
92442
92501
|
};
|
|
92443
92502
|
const builder = builders[options.schema];
|
|
92444
92503
|
if (builder) {
|
|
@@ -93649,7 +93708,7 @@ function createTtsCommand() {
|
|
|
93649
93708
|
}
|
|
93650
93709
|
|
|
93651
93710
|
// src/commands/update.ts
|
|
93652
|
-
import { createInterface as
|
|
93711
|
+
import { createInterface as createInterface4 } from "readline";
|
|
93653
93712
|
init_config();
|
|
93654
93713
|
var PACKAGE_NAME = "@automagik/omni";
|
|
93655
93714
|
var UPDATE_HEALTH_TIMEOUT_MS = 1e4;
|
|
@@ -93742,7 +93801,7 @@ async function restartServicesAndVerify(servicesToRestart, latest) {
|
|
|
93742
93801
|
process.exit(1);
|
|
93743
93802
|
}
|
|
93744
93803
|
async function promptConfirm(question) {
|
|
93745
|
-
const rl =
|
|
93804
|
+
const rl = createInterface4({
|
|
93746
93805
|
input: process.stdin,
|
|
93747
93806
|
output: process.stdout
|
|
93748
93807
|
});
|
|
@@ -94108,6 +94167,12 @@ var COMMANDS = [
|
|
|
94108
94167
|
helpGroup: "Core",
|
|
94109
94168
|
helpDescription: "Text-to-speech operations"
|
|
94110
94169
|
},
|
|
94170
|
+
{
|
|
94171
|
+
create: createChannelsCommand,
|
|
94172
|
+
category: "core",
|
|
94173
|
+
helpGroup: "Management",
|
|
94174
|
+
helpDescription: "Channel types, add instances, status overview"
|
|
94175
|
+
},
|
|
94111
94176
|
{
|
|
94112
94177
|
create: createInstancesCommand,
|
|
94113
94178
|
category: "core",
|
|
@@ -94133,6 +94198,12 @@ var COMMANDS = [
|
|
|
94133
94198
|
helpGroup: "Management",
|
|
94134
94199
|
helpDescription: "AI/LLM providers configuration"
|
|
94135
94200
|
},
|
|
94201
|
+
{
|
|
94202
|
+
create: createConnectCommand,
|
|
94203
|
+
category: "core",
|
|
94204
|
+
helpGroup: "Management",
|
|
94205
|
+
helpDescription: "Connect instance to genie agent via NATS"
|
|
94206
|
+
},
|
|
94136
94207
|
{
|
|
94137
94208
|
create: createRoutesCommand,
|
|
94138
94209
|
category: "standard",
|
|
@@ -94245,7 +94316,7 @@ function getHiddenCount() {
|
|
|
94245
94316
|
return COMMANDS.filter((cmd) => !cmd.helpDescription).length;
|
|
94246
94317
|
}
|
|
94247
94318
|
var program2 = new Command;
|
|
94248
|
-
program2.name("omni").description("CLI for Omni
|
|
94319
|
+
program2.name("omni").description("CLI for Omni - Universal Omnichannel Platform").version(VERSION, "-V, --version", "output the version number").enablePositionalOptions().passThroughOptions().option("--no-color", "Disable colored output").option("--all", "Show all commands including debug commands").hook("preAction", (_thisCommand, actionCommand) => {
|
|
94249
94320
|
const opts = actionCommand.optsWithGlobals();
|
|
94250
94321
|
if (opts.color === false) {
|
|
94251
94322
|
disableColors();
|