NativeAutoUpdater.tsx
components/NativeAutoUpdater.tsx
193
Lines
26514
Bytes
1
Exports
14
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with ui-flow. It contains 193 lines, 14 detected imports, and 1 detected exports.
Important relationships
Detected exports
NativeAutoUpdater
Keywords
texterrormessageincludesversionautoupdaterresultisupdatingcurrentutilserrortypelogevent
Detected imports
reactreactsrc/services/analytics/index.jssrc/utils/debug.jssrc/utils/log.jsusehooks-ts../hooks/useUpdateNotification.js../ink.js../utils/autoUpdater.js../utils/autoUpdater.js../utils/config.js../utils/nativeInstaller/index.js../utils/semver.js../utils/settings/settings.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
import * as React from 'react';
import { useEffect, useRef, useState } from 'react';
import { logEvent } from 'src/services/analytics/index.js';
import { logForDebugging } from 'src/utils/debug.js';
import { logError } from 'src/utils/log.js';
import { useInterval } from 'usehooks-ts';
import { useUpdateNotification } from '../hooks/useUpdateNotification.js';
import { Box, Text } from '../ink.js';
import type { AutoUpdaterResult } from '../utils/autoUpdater.js';
import { getMaxVersion, getMaxVersionMessage } from '../utils/autoUpdater.js';
import { isAutoUpdaterDisabled } from '../utils/config.js';
import { installLatest } from '../utils/nativeInstaller/index.js';
import { gt } from '../utils/semver.js';
import { getInitialSettings } from '../utils/settings/settings.js';
/**
* Categorize error messages for analytics
*/
function getErrorType(errorMessage: string): string {
if (errorMessage.includes('timeout')) {
return 'timeout';
}
if (errorMessage.includes('Checksum mismatch')) {
return 'checksum_mismatch';
}
if (errorMessage.includes('ENOENT') || errorMessage.includes('not found')) {
return 'not_found';
}
if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) {
return 'permission_denied';
}
if (errorMessage.includes('ENOSPC')) {
return 'disk_full';
}
if (errorMessage.includes('npm')) {
return 'npm_error';
}
if (errorMessage.includes('network') || errorMessage.includes('ECONNREFUSED') || errorMessage.includes('ENOTFOUND')) {
return 'network_error';
}
return 'unknown';
}
type Props = {
isUpdating: boolean;
onChangeIsUpdating: (isUpdating: boolean) => void;
onAutoUpdaterResult: (autoUpdaterResult: AutoUpdaterResult) => void;
autoUpdaterResult: AutoUpdaterResult | null;
showSuccessMessage: boolean;
verbose: boolean;
};
export function NativeAutoUpdater({
isUpdating,
onChangeIsUpdating,
onAutoUpdaterResult,
autoUpdaterResult,
showSuccessMessage,
verbose
}: Props): React.ReactNode {
const [versions, setVersions] = useState<{
current?: string | null;
latest?: string | null;
}>({});
const [maxVersionIssue, setMaxVersionIssue] = useState<string | null>(null);
const updateSemver = useUpdateNotification(autoUpdaterResult?.version);
const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest';
// Track latest isUpdating value in a ref so the memoized checkForUpdates
// callback always sees the current value without changing callback identity
// (which would re-trigger the initial-check useEffect below and cause
// repeated downloads on remount — the upstream trigger for #22413).
const isUpdatingRef = useRef(isUpdating);
isUpdatingRef.current = isUpdating;
const checkForUpdates = React.useCallback(async () => {
if (isUpdatingRef.current) {
return;
}
if ("production" === 'test' || "production" === 'development') {
logForDebugging('NativeAutoUpdater: Skipping update check in test/dev environment');
return;
}
if (isAutoUpdaterDisabled()) {
return;
}
onChangeIsUpdating(true);
const startTime = Date.now();
// Log the start of an auto-update check for funnel analysis
logEvent('tengu_native_auto_updater_start', {});
try {
// Check if current version is above the max allowed version
const maxVersion = await getMaxVersion();
if (maxVersion && gt(MACRO.VERSION, maxVersion)) {
const msg = await getMaxVersionMessage();
setMaxVersionIssue(msg ?? 'affects your version');
}
const result = await installLatest(channel);
const currentVersion = MACRO.VERSION;
const latencyMs = Date.now() - startTime;
// Handle lock contention gracefully - just return without treating as error
if (result.lockFailed) {
logEvent('tengu_native_auto_updater_lock_contention', {
latency_ms: latencyMs
});
return; // Silently skip this update check, will try again later
}
// Update versions for display
setVersions({
current: currentVersion,
latest: result.latestVersion
});
if (result.wasUpdated) {
logEvent('tengu_native_auto_updater_success', {
latency_ms: latencyMs
});
onAutoUpdaterResult({
version: result.latestVersion,
status: 'success'
});
} else {
// Already up to date
logEvent('tengu_native_auto_updater_up_to_date', {
latency_ms: latencyMs
});
}
} catch (error) {
const latencyMs = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
logError(error);
const errorType = getErrorType(errorMessage);
logEvent('tengu_native_auto_updater_fail', {
latency_ms: latencyMs,
error_timeout: errorType === 'timeout',
error_checksum: errorType === 'checksum_mismatch',
error_not_found: errorType === 'not_found',
error_permission: errorType === 'permission_denied',
error_disk_full: errorType === 'disk_full',
error_npm: errorType === 'npm_error',
error_network: errorType === 'network_error'
});
onAutoUpdaterResult({
version: null,
status: 'install_failed'
});
} finally {
onChangeIsUpdating(false);
}
// isUpdating intentionally omitted from deps; we read isUpdatingRef
// instead so the guard is always current without changing callback
// identity (which would re-trigger the initial-check useEffect below).
// eslint-disable-next-line react-hooks/exhaustive-deps
// biome-ignore lint/correctness/useExhaustiveDependencies: isUpdating read via ref
}, [onAutoUpdaterResult, channel]);
// Initial check
useEffect(() => {
void checkForUpdates();
}, [checkForUpdates]);
// Check every 30 minutes
useInterval(checkForUpdates, 30 * 60 * 1000);
const hasUpdateResult = !!autoUpdaterResult?.version;
const hasVersionInfo = !!versions.current && !!versions.latest;
// Show the component when:
// - warning banner needed (above max version), or
// - there's an update result to display (success/error), or
// - actively checking and we have version info to show
const shouldRender = !!maxVersionIssue || hasUpdateResult || isUpdating && hasVersionInfo;
if (!shouldRender) {
return null;
}
return <Box flexDirection="row" gap={1}>
{verbose && <Text dimColor wrap="truncate">
current: {versions.current} · {channel}: {versions.latest}
</Text>}
{isUpdating ? <Box>
<Text dimColor wrap="truncate">
Checking for updates
</Text>
</Box> : autoUpdaterResult?.status === 'success' && showSuccessMessage && updateSemver && <Text color="success" wrap="truncate">
✓ Update installed · Restart to update
</Text>}
{autoUpdaterResult?.status === 'install_failed' && <Text color="error" wrap="truncate">
✗ Auto-update failed · Try <Text bold>/status</Text>
</Text>}
{maxVersionIssue && "external" === 'ant' && <Text color="warning">
⚠ Known issue: {maxVersionIssue} · Run{' '}
<Text bold>claude rollback --safe</Text> to downgrade
</Text>}
</Box>;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwibG9nRXZlbnQiLCJsb2dGb3JEZWJ1Z2dpbmciLCJsb2dFcnJvciIsInVzZUludGVydmFsIiwidXNlVXBkYXRlTm90aWZpY2F0aW9uIiwiQm94IiwiVGV4dCIsIkF1dG9VcGRhdGVyUmVzdWx0IiwiZ2V0TWF4VmVyc2lvbiIsImdldE1heFZlcnNpb25NZXNzYWdlIiwiaXNBdXRvVXBkYXRlckRpc2FibGVkIiwiaW5zdGFsbExhdGVzdCIsImd0IiwiZ2V0SW5pdGlhbFNldHRpbmdzIiwiZ2V0RXJyb3JUeXBlIiwiZXJyb3JNZXNzYWdlIiwiaW5jbHVkZXMiLCJQcm9wcyIsImlzVXBkYXRpbmciLCJvbkNoYW5nZUlzVXBkYXRpbmciLCJvbkF1dG9VcGRhdGVyUmVzdWx0IiwiYXV0b1VwZGF0ZXJSZXN1bHQiLCJzaG93U3VjY2Vzc01lc3NhZ2UiLCJ2ZXJib3NlIiwiTmF0aXZlQXV0b1VwZGF0ZXIiLCJSZWFjdE5vZGUiLCJ2ZXJzaW9ucyIsInNldFZlcnNpb25zIiwiY3VycmVudCIsImxhdGVzdCIsIm1heFZlcnNpb25Jc3N1ZSIsInNldE1heFZlcnNpb25Jc3N1ZSIsInVwZGF0ZVNlbXZlciIsInZlcnNpb24iLCJjaGFubmVsIiwiYXV0b1VwZGF0ZXNDaGFubmVsIiwiaXNVcGRhdGluZ1JlZiIsImNoZWNrRm9yVXBkYXRlcyIsInVzZUNhbGxiYWNrIiwic3RhcnRUaW1lIiwiRGF0ZSIsIm5vdyIsIm1heFZlcnNpb24iLCJNQUNSTyIsIlZFUlNJT04iLCJtc2ciLCJyZXN1bHQiLCJjdXJyZW50VmVyc2lvbiIsImxhdGVuY3lNcyIsImxvY2tGYWlsZWQiLCJsYXRlbmN5X21zIiwibGF0ZXN0VmVyc2lvbiIsIndhc1VwZGF0ZWQiLCJzdGF0dXMiLCJlcnJvciIsIkVycm9yIiwibWVzc2FnZSIsIlN0cmluZyIsImVycm9yVHlwZSIsImVycm9yX3RpbWVvdXQiLCJlcnJvcl9jaGVja3N1bSIsImVycm9yX25vdF9mb3VuZCIsImVycm9yX3Blcm1pc3Npb24iLCJlcnJvcl9kaXNrX2Z1bGwiLCJlcnJvcl9ucG0iLCJlcnJvcl9uZXR3b3JrIiwiaGFzVXBkYXRlUmVzdWx0IiwiaGFzVmVyc2lvbkluZm8iLCJzaG91bGRSZW5kZXIiXSwic291cmNlcyI6WyJOYXRpdmVBdXRvVXBkYXRlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJ3NyYy91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnc3JjL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IHVzZUludGVydmFsIH0gZnJvbSAndXNlaG9va3MtdHMnXG5pbXBvcnQgeyB1c2VVcGRhdGVOb3RpZmljYXRpb24gfSBmcm9tICcuLi9ob29rcy91c2VVcGRhdGVOb3RpZmljYXRpb24uanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IEF1dG9VcGRhdGVyUmVzdWx0IH0gZnJvbSAnLi4vdXRpbHMvYXV0b1VwZGF0ZXIuanMnXG5pbXBvcnQgeyBnZXRNYXhWZXJzaW9uLCBnZXRNYXhWZXJzaW9uTWVzc2FnZSB9IGZyb20gJy4uL3V0aWxzL2F1dG9VcGRhdGVyLmpzJ1xuaW1wb3J0IHsgaXNBdXRvVXBkYXRlckRpc2FibGVkIH0gZnJvbSAnLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgaW5zdGFsbExhdGVzdCB9IGZyb20gJy4uL3V0aWxzL25hdGl2ZUluc3RhbGxlci9pbmRleC5qcydcbmltcG9ydCB7IGd0IH0gZnJvbSAnLi4vdXRpbHMvc2VtdmVyLmpzJ1xuaW1wb3J0IHsgZ2V0SW5pdGlhbFNldHRpbmdzIH0gZnJvbSAnLi4vdXRpbHMvc2V0dGluZ3Mvc2V0dGluZ3MuanMnXG5cbi8qKlxuICogQ2F0ZWdvcml6ZSBlcnJvciBtZXNzYWdlcyBmb3IgYW5hbHl0aWNzXG4gKi9cbmZ1bmN0aW9uIGdldEVycm9yVHlwZShlcnJvck1lc3NhZ2U6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmIChlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ3RpbWVvdXQnKSkge1xuICAgIHJldHVybiAndGltZW91dCdcbiAgfVxuICBpZiAoZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdDaGVja3N1bSBtaXNtYXRjaCcpKSB7XG4gICAgcmV0dXJuICdjaGVja3N1bV9taXNtYXRjaCdcbiAgfVxuICBpZiAoZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdFTk9FTlQnKSB8fCBlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ25vdCBmb3VuZCcpKSB7XG4gICAgcmV0dXJuICdub3RfZm91bmQnXG4gIH1cbiAgaWYgKGVycm9yTWVzc2FnZS5pbmNsdWRlcygnRUFDQ0VTJykgfHwgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdwZXJtaXNzaW9uJykpIHtcbiAgICByZXR1cm4gJ3Blcm1pc3Npb25fZGVuaWVkJ1xuICB9XG4gIGlmIChlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ0VOT1NQQycpKSB7XG4gICAgcmV0dXJuICdkaXNrX2Z1bGwnXG4gIH1cbiAgaWYgKGVycm9yTWVzc2FnZS5pbmNsdWRlcygnbnBtJykpIHtcbiAgICByZXR1cm4gJ25wbV9lcnJvcidcbiAgfVxuICBpZiAoXG4gICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCduZXR3b3JrJykgfHxcbiAgICBlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ0VDT05OUkVGVVNFRCcpIHx8XG4gICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdFTk9URk9VTkQnKVxuICApIHtcbiAgICByZXR1cm4gJ25ldHdvcmtfZXJyb3InXG4gIH1cbiAgcmV0dXJuICd1bmtub3duJ1xufVxuXG50eXBlIFByb3BzID0ge1xuICBpc1VwZGF0aW5nOiBib29sZWFuXG4gIG9uQ2hhbmdlSXNVcGRhdGluZzogKGlzVXBkYXRpbmc6IGJvb2xlYW4pID0+IHZvaWRcbiAgb25BdXRvVXBkYXRlclJlc3VsdDogKGF1dG9VcGRhdGVyUmVzdWx0OiBBdXRvVXBkYXRlclJlc3VsdCkgPT4gdm9pZFxuICBhdXRvVXBkYXRlclJlc3VsdDogQXV0b1VwZGF0ZXJSZXN1bHQgfCBudWxsXG4gIHNob3dTdWNjZXNzTWVzc2FnZTogYm9vbGVhblxuICB2ZXJib3NlOiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBOYXRpdmVBdXRvVXBkYXRlcih7XG4gIGlzVXBkYXRpbmcsXG4gIG9uQ2hhbmdlSXNVcGRhdGluZyxcbiAgb25BdXRvVXBkYXRlclJlc3VsdCxcbiAgYXV0b1VwZGF0ZXJSZXN1bHQsXG4gIHNob3dTdWNjZXNzTWVzc2FnZSxcbiAgdmVyYm9zZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3ZlcnNpb25zLCBzZXRWZXJzaW9uc10gPSB1c2VTdGF0ZTx7XG4gICAgY3VycmVudD86IHN0cmluZyB8IG51bGxcbiAgICBsYXRlc3Q/OiBzdHJpbmcgfCBudWxsXG4gIH0+KHt9KVxuICBjb25zdCBbbWF4VmVyc2lvbklzc3VlLCBzZXRNYXhWZXJzaW9uSXNzdWVdID0gdXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4obnVsbClcbiAgY29uc3QgdXBkYXRlU2VtdmVyID0gdXNlVXBkYXRlTm90aWZpY2F0aW9uKGF1dG9VcGRhdGVyUmVzdWx0Py52ZXJzaW9uKVxuICBjb25zdCBjaGFubmVsID0gZ2V0SW5pdGlhbFNldHRpbmdzKCk/LmF1dG9VcGRhdGVzQ2hhbm5lbCA/PyAnbGF0ZXN0J1xuXG4gIC8vIFRyYWNrIGxhdGVzdCBpc1VwZGF0aW5nIHZhbHVlIGluIGEgcmVmIHNvIHRoZSBtZW1vaXplZCBjaGVja0ZvclVwZGF0ZXNcbiAgLy8gY2FsbGJhY2sgYWx3YXlzIHNlZXMgdGhlIGN1cnJlbnQgdmFsdWUgd2l0aG91dCBjaGFuZ2luZyBjYWxsYmFjayBpZGVudGl0eVxuICAvLyAod2hpY2ggd291bGQgcmUtdHJpZ2dlciB0aGUgaW5pdGlhbC1jaGVjayB1c2VFZmZlY3QgYmVsb3cgYW5kIGNhdXNlXG4gIC8vIHJlcGVhdGVkIGRvd25sb2FkcyBvbiByZW1vdW50IOKAlCB0aGUgdXBzdHJlYW0gdHJpZ2dlciBmb3IgIzIyNDEzKS5cbiAgY29uc3QgaXNVcGRhdGluZ1JlZiA9IHVzZVJlZihpc1VwZGF0aW5nKVxuICBpc1VwZGF0aW5nUmVmLmN1cnJlbnQgPSBpc1VwZGF0aW5nXG5cbiAgY29uc3QgY2hlY2tGb3JVcGRhdGVzID0gUmVhY3QudXNlQ2FsbGJhY2soYXN5bmMgKCkgPT4ge1xuICAgIGlmIChpc1VwZGF0aW5nUmVmLmN1cnJlbnQpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChcbiAgICAgIFwicHJvZHVjdGlvblwiID09PSAndGVzdCcgfHxcbiAgICAgIFwicHJvZHVjdGlvblwiID09PSAnZGV2ZWxvcG1lbnQnXG4gICAgKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICdOYXRpdmVBdXRvVXBkYXRlcjogU2tpcHBpbmcgdXBkYXRlIGNoZWNrIGluIHRlc3QvZGV2IGVudmlyb25tZW50JyxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChpc0F1dG9VcGRhdGVyRGlzYWJsZWQoKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgb25DaGFuZ2VJc1VwZGF0aW5nKHRydWUpXG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKVxuXG4gICAgLy8gTG9nIHRoZSBzdGFydCBvZiBhbiBhdXRvLXVwZGF0ZSBjaGVjayBmb3IgZnVubmVsIGFuYWx5c2lzXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X25hdGl2ZV9hdXRvX3VwZGF0ZXJfc3RhcnQnLCB7fSlcblxuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayBpZiBjdXJyZW50IHZlcnNpb24gaXMgYWJvdmUgdGhlIG1heCBhbGxvd2VkIHZlcnNpb25cbiAgICAgIGNvbnN0IG1heFZlcnNpb24gPSBhd2FpdCBnZXRNYXhWZXJzaW9uKClcbiAgICAgIGlmIChtYXhWZXJzaW9uICYmIGd0KE1BQ1JPLlZFUlNJT04sIG1heFZlcnNpb24pKSB7XG4gICAgICAgIGNvbnN0IG1zZyA9IGF3YWl0IGdldE1heFZlcnNpb25NZXNzYWdlKClcbiAgICAgICAgc2V0TWF4VmVyc2lvbklzc3VlKG1zZyA/PyAnYWZmZWN0cyB5b3VyIHZlcnNpb24nKVxuICAgICAgfVxuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBpbnN0YWxsTGF0ZXN0KGNoYW5uZWwpXG4gICAgICBjb25zdCBjdXJyZW50VmVyc2lvbiA9IE1BQ1JPLlZFUlNJT05cbiAgICAgIGNvbnN0IGxhdGVuY3lNcyA9IERhdGUubm93KCkgLSBzdGFydFRpbWVcblxuICAgICAgLy8gSGFuZGxlIGxvY2sgY29udGVudGlvbiBncmFjZWZ1bGx5IC0ganVzdCByZXR1cm4gd2l0aG91dCB0cmVhdGluZyBhcyBlcnJvclxuICAgICAgaWYgKHJlc3VsdC5sb2NrRmFpbGVkKSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9uYXRpdmVfYXV0b191cGRhdGVyX2xvY2tfY29udGVudGlvbicsIHtcbiAgICAgICAgICBsYXRlbmN5X21zOiBsYXRlbmN5TXMsXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVybiAvLyBTaWxlbnRseSBza2lwIHRoaXMgdXBkYXRlIGNoZWNrLCB3aWxsIHRyeSBhZ2FpbiBsYXRlclxuICAgICAgfVxuXG4gICAgICAvLyBVcGRhdGUgdmVyc2lvbnMgZm9yIGRpc3BsYXlcbiAgICAgIHNldFZlcnNpb25zKHsgY3VycmVudDogY3VycmVudFZlcnNpb24sIGxhdGVzdDogcmVzdWx0LmxhdGVzdFZlcnNpb24gfSlcblxuICAgICAgaWYgKHJlc3VsdC53YXNVcGRhdGVkKSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9uYXRpdmVfYXV0b191cGRhdGVyX3N1Y2Nlc3MnLCB7XG4gICAgICAgICAgbGF0ZW5jeV9tczogbGF0ZW5jeU1zLFxuICAgICAgICB9KVxuXG4gICAgICAgIG9uQXV0b1VwZGF0ZXJSZXN1bHQoe1xuICAgICAgICAgIHZlcnNpb246IHJlc3VsdC5sYXRlc3RWZXJzaW9uLFxuICAgICAgICAgIHN0YXR1czogJ3N1Y2Nlc3MnLFxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gQWxyZWFkeSB1cCB0byBkYXRlXG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9uYXRpdmVfYXV0b191cGRhdGVyX3VwX3RvX2RhdGUnLCB7XG4gICAgICAgICAgbGF0ZW5jeV9tczogbGF0ZW5jeU1zLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zdCBsYXRlbmN5TXMgPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lXG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPVxuICAgICAgICBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcilcbiAgICAgIGxvZ0Vycm9yKGVycm9yKVxuXG4gICAgICBjb25zdCBlcnJvclR5cGUgPSBnZXRFcnJvclR5cGUoZXJyb3JNZXNzYWdlKVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X25hdGl2ZV9hdXRvX3VwZGF0ZXJfZmFpbCcsIHtcbiAgICAgICAgbGF0ZW5jeV9tczogbGF0ZW5jeU1zLFxuICAgICAgICBlcnJvcl90aW1lb3V0OiBlcnJvclR5cGUgPT09ICd0aW1lb3V0JyxcbiAgICAgICAgZXJyb3JfY2hlY2tzdW06IGVycm9yVHlwZSA9PT0gJ2NoZWNrc3VtX21pc21hdGNoJyxcbiAgICAgICAgZXJyb3Jfbm90X2ZvdW5kOiBlcnJvclR5cGUgPT09ICdub3RfZm91bmQnLFxuICAgICAgICBlcnJvcl9wZXJtaXNzaW9uOiBlcnJvclR5cGUgPT09ICdwZXJtaXNzaW9uX2RlbmllZCcsXG4gICAgICAgIGVycm9yX2Rpc2tfZnVsbDogZXJyb3JUeXBlID09PSAnZGlza19mdWxsJyxcbiAgICAgICAgZXJyb3JfbnBtOiBlcnJvclR5cGUgPT09ICducG1fZXJyb3InLFxuICAgICAgICBlcnJvcl9uZXR3b3JrOiBlcnJvclR5cGUgPT09ICduZXR3b3JrX2Vycm9yJyxcbiAgICAgIH0pXG5cbiAgICAgIG9uQXV0b1VwZGF0ZXJSZXN1bHQoe1xuICAgICAgICB2ZXJzaW9uOiBudWxsLFxuICAgICAgICBzdGF0dXM6ICdpbnN0YWxsX2ZhaWxlZCcsXG4gICAgICB9KVxuICAgIH0gZmluYWxseSB7XG4gICAgICBvbkNoYW5nZUlzVXBkYXRpbmcoZmFsc2UpXG4gICAgfVxuICAgIC8vIGlzVXBkYXRpbmcgaW50ZW50aW9uYWxseSBvbWl0dGVkIGZyb20gZGVwczsgd2UgcmVhZCBpc1VwZGF0aW5nUmVmXG4gICAgLy8gaW5zdGVhZCBzbyB0aGUgZ3VhcmQgaXMgYWx3YXlzIGN1cnJlbnQgd2l0aG91dCBjaGFuZ2luZyBjYWxsYmFja1xuICAgIC8vIGlkZW50aXR5ICh3aGljaCB3b3VsZCByZS10cmlnZ2VyIHRoZSBpbml0aWFsLWNoZWNrIHVzZUVmZmVjdCBiZWxvdykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwc1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUV4aGF1c3RpdmVEZXBlbmRlbmNpZXM6IGlzVXBkYXRpbmcgcmVhZCB2aWEgcmVmXG4gIH0sIFtvbkF1dG9VcGRhdGVyUmVzdWx0LCBjaGFubmVsXSlcblxuICAvLyBJbml0aWFsIGNoZWNrXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgdm9pZCBjaGVja0ZvclVwZGF0ZXMoKVxuICB9LCBbY2hlY2tGb3JVcGRhdGVzXSlcblxuICAvLyBDaGVjayBldmVyeSAzMCBtaW51dGVzXG4gIHVzZUludGVydmFsKGNoZWNrRm9yVXBkYXRlcywgMzAgKiA2MCAqIDEwMDApXG5cbiAgY29uc3QgaGFzVXBkYXRlUmVzdWx0ID0gISFhdXRvVXBkYXRlclJlc3VsdD8udmVyc2lvblxuICBjb25zdCBoYXNWZXJzaW9uSW5mbyA9ICEhdmVyc2lvbnMuY3VycmVudCAmJiAhIXZlcnNpb25zLmxhdGVzdFxuICAvLyBTaG93IHRoZSBjb21wb25lbnQgd2hlbjpcbiAgLy8gLSB3YXJuaW5nIGJhbm5lciBuZWVkZWQgKGFib3ZlIG1heCB2ZXJzaW9uKSwgb3JcbiAgLy8gLSB0aGVyZSdzIGFuIHVwZGF0ZSByZXN1bHQgdG8gZGlzcGxheSAoc3VjY2Vzcy9lcnJvciksIG9yXG4gIC8vIC0gYWN0aXZlbHkgY2hlY2tpbmcgYW5kIHdlIGhhdmUgdmVyc2lvbiBpbmZvIHRvIHNob3dcbiAgY29uc3Qgc2hvdWxkUmVuZGVyID1cbiAgICAhIW1heFZlcnNpb25Jc3N1ZSB8fCBoYXNVcGRhdGVSZXN1bHQgfHwgKGlzVXBkYXRpbmcgJiYgaGFzVmVyc2lvbkluZm8pXG5cbiAgaWYgKCFzaG91bGRSZW5kZXIpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAge3ZlcmJvc2UgJiYgKFxuICAgICAgICA8VGV4dCBkaW1Db2xvciB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICBjdXJyZW50OiB7dmVyc2lvbnMuY3VycmVudH0gJm1pZGRvdDsge2NoYW5uZWx9OiB7dmVyc2lvbnMubGF0ZXN0fVxuICAgICAgICA8L1RleHQ+XG4gICAgICApfVxuICAgICAge2lzVXBkYXRpbmcgPyAoXG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3Igd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICBDaGVja2luZyBmb3IgdXBkYXRlc1xuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApIDogKFxuICAgICAgICBhdXRvVXBkYXRlclJlc3VsdD8uc3RhdHVzID09PSAnc3VjY2VzcycgJiZcbiAgICAgICAgc2hvd1N1Y2Nlc3NNZXNzYWdlICYmXG4gICAgICAgIHVwZGF0ZVNlbXZlciAmJiAoXG4gICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCIgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICDinJMgVXBkYXRlIGluc3RhbGxlZCDCtyBSZXN0YXJ0IHRvIHVwZGF0ZVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKVxuICAgICAgKX1cbiAgICAgIHthdXRvVXBkYXRlclJlc3VsdD8uc3RhdHVzID09PSAnaW5zdGFsbF9mYWlsZWQnICYmIChcbiAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiIHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgIOKclyBBdXRvLXVwZGF0ZSBmYWlsZWQgJm1pZGRvdDsgVHJ5IDxUZXh0IGJvbGQ+L3N0YXR1czwvVGV4dD5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKX1cbiAgICAgIHttYXhWZXJzaW9uSXNzdWUgJiYgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJiAoXG4gICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPlxuICAgICAgICAgIOKaoCBLbm93biBpc3N1ZToge21heFZlcnNpb25Jc3N1ZX0gJm1pZGRvdDsgUnVueycgJ31cbiAgICAgICAgICA8VGV4dCBib2xkPmNsYXVkZSByb2xsYmFjayAtLXNhZmU8L1RleHQ+IHRvIGRvd25ncmFkZVxuICAgICAgICA8L1RleHQ+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ25ELFNBQVNDLFFBQVEsUUFBUSxpQ0FBaUM7QUFDMUQsU0FBU0MsZUFBZSxRQUFRLG9CQUFvQjtBQUNwRCxTQUFTQyxRQUFRLFFB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