@xortex/xcode 3.0.5 → 3.1.0

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.
Files changed (75) hide show
  1. package/INSTALLATION.md +285 -0
  2. package/QUICKSTART.md +151 -0
  3. package/SYSTEM_PROMPT.md +583 -0
  4. package/SYSTEM_PROMPT_EXTRACTED.md +1 -0
  5. package/Untitled +1 -0
  6. package/bin/xcode +69 -120
  7. package/bootstrap/state.ts +1758 -0
  8. package/bun-bundle-hook.js +38 -17
  9. package/bun-bundle-shim.ts +12 -0
  10. package/bun.lock +645 -0
  11. package/context/QueuedMessageContext.tsx +63 -0
  12. package/context/fpsMetrics.tsx +30 -0
  13. package/context/mailbox.tsx +38 -0
  14. package/context/modalContext.tsx +58 -0
  15. package/context/notifications.tsx +240 -0
  16. package/context/overlayContext.tsx +151 -0
  17. package/context/promptOverlayContext.tsx +125 -0
  18. package/context/stats.tsx +220 -0
  19. package/context/voice.tsx +88 -0
  20. package/coordinator/coordinatorMode.ts +369 -0
  21. package/costHook.ts +22 -0
  22. package/dialogLaunchers.tsx +133 -0
  23. package/entrypoints/cli.tsx +1 -1
  24. package/extract_prompt.ts +304 -0
  25. package/ink.ts +85 -0
  26. package/install.sh +221 -0
  27. package/interactiveHelpers.tsx +366 -0
  28. package/macro.ts +1 -1
  29. package/memdir/findRelevantMemories.ts +141 -0
  30. package/memdir/memdir.ts +511 -0
  31. package/memdir/memoryAge.ts +53 -0
  32. package/memdir/memoryScan.ts +94 -0
  33. package/memdir/memoryTypes.ts +271 -0
  34. package/memdir/paths.ts +291 -0
  35. package/memdir/teamMemPaths.ts +292 -0
  36. package/memdir/teamMemPrompts.ts +100 -0
  37. package/moreright/useMoreRight.tsx +26 -0
  38. package/native-ts/color-diff/index.ts +999 -0
  39. package/native-ts/file-index/index.ts +370 -0
  40. package/native-ts/yoga-layout/enums.ts +134 -0
  41. package/native-ts/yoga-layout/index.ts +2578 -0
  42. package/outputStyles/loadOutputStylesDir.ts +98 -0
  43. package/package.json +5 -42
  44. package/plugins/builtinPlugins.ts +159 -0
  45. package/plugins/bundled/index.ts +23 -0
  46. package/projectOnboardingState.ts +83 -0
  47. package/public/claude-files.png +0 -0
  48. package/public/leak-tweet.png +0 -0
  49. package/query/config.ts +46 -0
  50. package/query/deps.ts +40 -0
  51. package/query/stopHooks.ts +470 -0
  52. package/query/tokenBudget.ts +93 -0
  53. package/replLauncher.tsx +27 -0
  54. package/schemas/hooks.ts +222 -0
  55. package/screens/Doctor.tsx +575 -0
  56. package/screens/REPL.tsx +7107 -0
  57. package/screens/ResumeConversation.tsx +399 -0
  58. package/scripts/postinstall.js +90 -0
  59. package/server/createDirectConnectSession.ts +88 -0
  60. package/server/directConnectManager.ts +213 -0
  61. package/server/types.ts +57 -0
  62. package/setup.ts +477 -0
  63. package/stub_types.sh +13 -0
  64. package/tasks.ts +39 -0
  65. package/tools.ts +396 -0
  66. package/tsconfig.json +16 -0
  67. package/upstreamproxy/relay.ts +455 -0
  68. package/upstreamproxy/upstreamproxy.ts +285 -0
  69. package/vim/motions.ts +82 -0
  70. package/vim/operators.ts +556 -0
  71. package/vim/textObjects.ts +186 -0
  72. package/vim/transitions.ts +490 -0
  73. package/vim/types.ts +199 -0
  74. package/voice/voiceModeEnabled.ts +54 -0
  75. package/utils/bunBundleCompat.ts +0 -44
@@ -0,0 +1,366 @@
1
+ import { feature } from 'bun:bundle';
2
+ import { appendFileSync } from 'fs';
3
+ import React from 'react';
4
+ import { logEvent } from 'src/services/analytics/index.js';
5
+ import { gracefulShutdown, gracefulShutdownSync } from 'src/utils/gracefulShutdown.js';
6
+ import { type ChannelEntry, getAllowedChannels, setAllowedChannels, setHasDevChannels, setSessionTrustAccepted, setStatsStore } from './bootstrap/state.js';
7
+ import type { Command } from './commands.js';
8
+ import { createStatsStore, type StatsStore } from './context/stats.js';
9
+ import { getSystemContext } from './context.js';
10
+ import { initializeTelemetryAfterTrust } from './entrypoints/init.js';
11
+ import { isSynchronizedOutputSupported } from './ink/terminal.js';
12
+ import type { RenderOptions, Root, TextProps } from './ink.js';
13
+ import { KeybindingSetup } from './keybindings/KeybindingProviderSetup.js';
14
+ import { startDeferredPrefetches } from './main.js';
15
+ import { checkGate_CACHED_OR_BLOCKING, initializeGrowthBook, resetGrowthBook } from './services/analytics/growthbook.js';
16
+ import { isQualifiedForGrove } from './services/api/grove.js';
17
+ import { handleMcpjsonServerApprovals } from './services/mcpServerApproval.js';
18
+ import { AppStateProvider } from './state/AppState.js';
19
+ import { onChangeAppState } from './state/onChangeAppState.js';
20
+ import { normalizeApiKeyForConfig } from './utils/authPortable.js';
21
+ import { getExternalClaudeMdIncludes, getMemoryFiles, shouldShowClaudeMdExternalIncludesWarning } from './utils/claudemd.js';
22
+ import { checkHasTrustDialogAccepted, getCustomApiKeyStatus, getGlobalConfig, saveGlobalConfig } from './utils/config.js';
23
+ import { updateDeepLinkTerminalPreference } from './utils/deepLink/terminalPreference.js';
24
+ import { isEnvTruthy, isRunningOnHomespace } from './utils/envUtils.js';
25
+ import { type FpsMetrics, FpsTracker } from './utils/fpsTracker.js';
26
+ import { updateGithubRepoPathMapping } from './utils/githubRepoPathMapping.js';
27
+ import { applyConfigEnvironmentVariables } from './utils/managedEnv.js';
28
+ import type { PermissionMode } from './utils/permissions/PermissionMode.js';
29
+ import { getBaseRenderOptions } from './utils/renderOptions.js';
30
+ import { getSettingsWithAllErrors } from './utils/settings/allErrors.js';
31
+ import { hasAutoModeOptIn, hasSkipDangerousModePermissionPrompt } from './utils/settings/settings.js';
32
+ export function completeOnboarding(): void {
33
+ saveGlobalConfig(current => ({
34
+ ...current,
35
+ hasCompletedOnboarding: true,
36
+ lastOnboardingVersion: MACRO.VERSION
37
+ }));
38
+ }
39
+ export function showDialog<T = void>(root: Root, renderer: (done: (result: T) => void) => React.ReactNode): Promise<T> {
40
+ return new Promise<T>(resolve => {
41
+ const done = (result: T): void => void resolve(result);
42
+ root.render(renderer(done));
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Render an error message through Ink, then unmount and exit.
48
+ * Use this for fatal errors after the Ink root has been created —
49
+ * console.error is swallowed by Ink's patchConsole, so we render
50
+ * through the React tree instead.
51
+ */
52
+ export async function exitWithError(root: Root, message: string, beforeExit?: () => Promise<void>): Promise<never> {
53
+ return exitWithMessage(root, message, {
54
+ color: 'error',
55
+ beforeExit
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Render a message through Ink, then unmount and exit.
61
+ * Use this for messages after the Ink root has been created —
62
+ * console output is swallowed by Ink's patchConsole, so we render
63
+ * through the React tree instead.
64
+ */
65
+ export async function exitWithMessage(root: Root, message: string, options?: {
66
+ color?: TextProps['color'];
67
+ exitCode?: number;
68
+ beforeExit?: () => Promise<void>;
69
+ }): Promise<never> {
70
+ const {
71
+ Text
72
+ } = await import('./ink.js');
73
+ const color = options?.color;
74
+ const exitCode = options?.exitCode ?? 1;
75
+ root.render(color ? <Text color={color}>{message}</Text> : <Text>{message}</Text>);
76
+ root.unmount();
77
+ await options?.beforeExit?.();
78
+ // eslint-disable-next-line custom-rules/no-process-exit -- exit after Ink unmount
79
+ process.exit(exitCode);
80
+ }
81
+
82
+ /**
83
+ * Show a setup dialog wrapped in AppStateProvider + KeybindingSetup.
84
+ * Reduces boilerplate in showSetupScreens() where every dialog needs these wrappers.
85
+ */
86
+ export function showSetupDialog<T = void>(root: Root, renderer: (done: (result: T) => void) => React.ReactNode, options?: {
87
+ onChangeAppState?: typeof onChangeAppState;
88
+ }): Promise<T> {
89
+ return showDialog<T>(root, done => <AppStateProvider onChangeAppState={options?.onChangeAppState}>
90
+ <KeybindingSetup>{renderer(done)}</KeybindingSetup>
91
+ </AppStateProvider>);
92
+ }
93
+
94
+ /**
95
+ * Render the main UI into the root and wait for it to exit.
96
+ * Handles the common epilogue: start deferred prefetches, wait for exit, graceful shutdown.
97
+ */
98
+ export async function renderAndRun(root: Root, element: React.ReactNode): Promise<void> {
99
+ root.render(element);
100
+ startDeferredPrefetches();
101
+ await root.waitUntilExit();
102
+ await gracefulShutdown(0);
103
+ }
104
+ export async function showSetupScreens(root: Root, permissionMode: PermissionMode, allowDangerouslySkipPermissions: boolean, commands?: Command[], claudeInChrome?: boolean, devChannels?: ChannelEntry[]): Promise<boolean> {
105
+ if ("production" === 'test' || isEnvTruthy(false) || process.env.IS_DEMO // Skip onboarding in demo mode
106
+ ) {
107
+ return false;
108
+ }
109
+ const config = getGlobalConfig();
110
+ let onboardingShown = false;
111
+ if (!config.theme || !config.hasCompletedOnboarding // always show onboarding at least once
112
+ ) {
113
+ onboardingShown = true;
114
+ const {
115
+ Onboarding
116
+ } = await import('./components/Onboarding.js');
117
+ await showSetupDialog(root, done => <Onboarding onDone={() => {
118
+ completeOnboarding();
119
+ void done();
120
+ }} />, {
121
+ onChangeAppState
122
+ });
123
+ }
124
+
125
+ // Always show the trust dialog in interactive sessions, regardless of permission mode.
126
+ // The trust dialog is the workspace trust boundary — it warns about untrusted repos
127
+ // and checks CLAUDE.md external includes. bypassPermissions mode
128
+ // only affects tool execution permissions, not workspace trust.
129
+ // Note: non-interactive sessions (CI/CD with -p) never reach showSetupScreens at all.
130
+ // Skip permission checks in claubbit
131
+ if (!isEnvTruthy(process.env.CLAUBBIT)) {
132
+ // Fast-path: skip TrustDialog import+render when CWD is already trusted.
133
+ // If it returns true, the TrustDialog would auto-resolve regardless of
134
+ // security features, so we can skip the dynamic import and render cycle.
135
+ if (!checkHasTrustDialogAccepted()) {
136
+ const {
137
+ TrustDialog
138
+ } = await import('./components/TrustDialog/TrustDialog.js');
139
+ await showSetupDialog(root, done => <TrustDialog commands={commands} onDone={done} />);
140
+ }
141
+
142
+ // Signal that trust has been verified for this session.
143
+ // GrowthBook checks this to decide whether to include auth headers.
144
+ setSessionTrustAccepted(true);
145
+
146
+ // Reset and reinitialize GrowthBook after trust is established.
147
+ // Defense for login/logout: clears any prior client so the next init
148
+ // picks up fresh auth headers.
149
+ resetGrowthBook();
150
+ void initializeGrowthBook();
151
+
152
+ // Now that trust is established, prefetch system context if it wasn't already
153
+ void getSystemContext();
154
+
155
+ // If settings are valid, check for any mcp.json servers that need approval
156
+ const {
157
+ errors: allErrors
158
+ } = getSettingsWithAllErrors();
159
+ if (allErrors.length === 0) {
160
+ await handleMcpjsonServerApprovals(root);
161
+ }
162
+
163
+ // Check for claude.md includes that need approval
164
+ if (await shouldShowClaudeMdExternalIncludesWarning()) {
165
+ const externalIncludes = getExternalClaudeMdIncludes(await getMemoryFiles(true));
166
+ const {
167
+ ClaudeMdExternalIncludesDialog
168
+ } = await import('./components/ClaudeMdExternalIncludesDialog.js');
169
+ await showSetupDialog(root, done => <ClaudeMdExternalIncludesDialog onDone={done} isStandaloneDialog externalIncludes={externalIncludes} />);
170
+ }
171
+ }
172
+
173
+ // Track current repo path for teleport directory switching (fire-and-forget)
174
+ // This must happen AFTER trust to prevent untrusted directories from poisoning the mapping
175
+ void updateGithubRepoPathMapping();
176
+ if (feature('LODESTONE')) {
177
+ updateDeepLinkTerminalPreference();
178
+ }
179
+
180
+ // Apply full environment variables after trust dialog is accepted OR in bypass mode
181
+ // In bypass mode (CI/CD, automation), we trust the environment so apply all variables
182
+ // In normal mode, this happens after the trust dialog is accepted
183
+ // This includes potentially dangerous environment variables from untrusted sources
184
+ applyConfigEnvironmentVariables();
185
+
186
+ // Initialize telemetry after env vars are applied so OTEL endpoint env vars and
187
+ // otelHeadersHelper (which requires trust to execute) are available.
188
+ // Defer to next tick so the OTel dynamic import resolves after first render
189
+ // instead of during the pre-render microtask queue.
190
+ setImmediate(() => initializeTelemetryAfterTrust());
191
+ if (await isQualifiedForGrove()) {
192
+ const {
193
+ GroveDialog
194
+ } = await import('src/components/grove/Grove.js');
195
+ const decision = await showSetupDialog<string>(root, done => <GroveDialog showIfAlreadyViewed={false} location={onboardingShown ? 'onboarding' : 'policy_update_modal'} onDone={done} />);
196
+ if (decision === 'escape') {
197
+ logEvent('tengu_grove_policy_exited', {});
198
+ gracefulShutdownSync(0);
199
+ return false;
200
+ }
201
+ }
202
+
203
+ // Check for custom API key
204
+ // On homespace, ANTHROPIC_API_KEY is preserved in process.env for child
205
+ // processes but ignored by Claude Code itself (see auth.ts).
206
+ if (process.env.ANTHROPIC_API_KEY && !isRunningOnHomespace()) {
207
+ const customApiKeyTruncated = normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY);
208
+ const keyStatus = getCustomApiKeyStatus(customApiKeyTruncated);
209
+ if (keyStatus === 'new') {
210
+ const {
211
+ ApproveApiKey
212
+ } = await import('./components/ApproveApiKey.js');
213
+ await showSetupDialog<boolean>(root, done => <ApproveApiKey customApiKeyTruncated={customApiKeyTruncated} onDone={done} />, {
214
+ onChangeAppState
215
+ });
216
+ }
217
+ }
218
+ if ((permissionMode === 'bypassPermissions' || allowDangerouslySkipPermissions) && !hasSkipDangerousModePermissionPrompt()) {
219
+ const {
220
+ BypassPermissionsModeDialog
221
+ } = await import('./components/BypassPermissionsModeDialog.js');
222
+ await showSetupDialog(root, done => <BypassPermissionsModeDialog onAccept={done} />);
223
+ }
224
+ if (feature('TRANSCRIPT_CLASSIFIER')) {
225
+ // Only show the opt-in dialog if auto mode actually resolved — if the
226
+ // gate denied it (org not allowlisted, settings disabled), showing
227
+ // consent for an unavailable feature is pointless. The
228
+ // verifyAutoModeGateAccess notification will explain why instead.
229
+ if (permissionMode === 'auto' && !hasAutoModeOptIn()) {
230
+ const {
231
+ AutoModeOptInDialog
232
+ } = await import('./components/AutoModeOptInDialog.js');
233
+ await showSetupDialog(root, done => <AutoModeOptInDialog onAccept={done} onDecline={() => gracefulShutdownSync(1)} declineExits />);
234
+ }
235
+ }
236
+
237
+ // --dangerously-load-development-channels confirmation. On accept, append
238
+ // dev channels to any --channels list already set in main.tsx. Org policy
239
+ // is NOT bypassed — gateChannelServer() still runs; this flag only exists
240
+ // to sidestep the --channels approved-server allowlist.
241
+ if (feature('KAIROS') || feature('KAIROS_CHANNELS')) {
242
+ // gateChannelServer and ChannelsNotice read tengu_harbor after this
243
+ // function returns. A cold disk cache (fresh install, or first run after
244
+ // the flag was added server-side) defaults to false and silently drops
245
+ // channel notifications for the whole session — gh#37026.
246
+ // checkGate_CACHED_OR_BLOCKING returns immediately if disk already says
247
+ // true; only blocks on a cold/stale-false cache (awaits the same memoized
248
+ // initializeGrowthBook promise fired earlier). Also warms the
249
+ // isChannelsEnabled() check in the dev-channels dialog below.
250
+ if (getAllowedChannels().length > 0 || (devChannels?.length ?? 0) > 0) {
251
+ await checkGate_CACHED_OR_BLOCKING('tengu_harbor');
252
+ }
253
+ if (devChannels && devChannels.length > 0) {
254
+ const [{
255
+ isChannelsEnabled
256
+ }, {
257
+ getClaudeAIOAuthTokens
258
+ }] = await Promise.all([import('./services/mcp/channelAllowlist.js'), import('./utils/auth.js')]);
259
+ // Skip the dialog when channels are blocked (tengu_harbor off or no
260
+ // OAuth) — accepting then immediately seeing "not available" in
261
+ // ChannelsNotice is worse than no dialog. Append entries anyway so
262
+ // ChannelsNotice renders the blocked branch with the dev entries
263
+ // named. dev:true here is for the flag label in ChannelsNotice
264
+ // (hasNonDev check); the allowlist bypass it also grants is moot
265
+ // since the gate blocks upstream.
266
+ if (!isChannelsEnabled() || !getClaudeAIOAuthTokens()?.accessToken) {
267
+ setAllowedChannels([...getAllowedChannels(), ...devChannels.map(c => ({
268
+ ...c,
269
+ dev: true
270
+ }))]);
271
+ setHasDevChannels(true);
272
+ } else {
273
+ const {
274
+ DevChannelsDialog
275
+ } = await import('./components/DevChannelsDialog.js');
276
+ await showSetupDialog(root, done => <DevChannelsDialog channels={devChannels} onAccept={() => {
277
+ // Mark dev entries per-entry so the allowlist bypass doesn't leak
278
+ // to --channels entries when both flags are passed.
279
+ setAllowedChannels([...getAllowedChannels(), ...devChannels.map(c => ({
280
+ ...c,
281
+ dev: true
282
+ }))]);
283
+ setHasDevChannels(true);
284
+ void done();
285
+ }} />);
286
+ }
287
+ }
288
+ }
289
+
290
+ // Show Chrome onboarding for first-time Claude in Chrome users
291
+ if (claudeInChrome && !getGlobalConfig().hasCompletedClaudeInChromeOnboarding) {
292
+ const {
293
+ ClaudeInChromeOnboarding
294
+ } = await import('./components/ClaudeInChromeOnboarding.js');
295
+ await showSetupDialog(root, done => <ClaudeInChromeOnboarding onDone={done} />);
296
+ }
297
+ return onboardingShown;
298
+ }
299
+ export function getRenderContext(exitOnCtrlC: boolean): {
300
+ renderOptions: RenderOptions;
301
+ getFpsMetrics: () => FpsMetrics | undefined;
302
+ stats: StatsStore;
303
+ } {
304
+ let lastFlickerTime = 0;
305
+ const baseOptions = getBaseRenderOptions(exitOnCtrlC);
306
+
307
+ // Log analytics event when stdin override is active
308
+ if (baseOptions.stdin) {
309
+ logEvent('tengu_stdin_interactive', {});
310
+ }
311
+ const fpsTracker = new FpsTracker();
312
+ const stats = createStatsStore();
313
+ setStatsStore(stats);
314
+
315
+ // Bench mode: when set, append per-frame phase timings as JSONL for
316
+ // offline analysis by bench/repl-scroll.ts. Captures the full TUI
317
+ // render pipeline (yoga → screen buffer → diff → optimize → stdout)
318
+ // so perf work on any phase can be validated against real user flows.
319
+ const frameTimingLogPath = process.env.CLAUDE_CODE_FRAME_TIMING_LOG;
320
+ return {
321
+ getFpsMetrics: () => fpsTracker.getMetrics(),
322
+ stats,
323
+ renderOptions: {
324
+ ...baseOptions,
325
+ onFrame: event => {
326
+ fpsTracker.record(event.durationMs);
327
+ stats.observe('frame_duration_ms', event.durationMs);
328
+ if (frameTimingLogPath && event.phases) {
329
+ // Bench-only env-var-gated path: sync write so no frames dropped
330
+ // on abrupt exit. ~100 bytes at ≤60fps is negligible. rss/cpu are
331
+ // single syscalls; cpu is cumulative — bench side computes delta.
332
+ const line =
333
+ // eslint-disable-next-line custom-rules/no-direct-json-operations -- tiny object, hot bench path
334
+ JSON.stringify({
335
+ total: event.durationMs,
336
+ ...event.phases,
337
+ rss: process.memoryUsage.rss(),
338
+ cpu: process.cpuUsage()
339
+ }) + '\n';
340
+ // eslint-disable-next-line custom-rules/no-sync-fs -- bench-only, sync so no frames dropped on exit
341
+ appendFileSync(frameTimingLogPath, line);
342
+ }
343
+ // Skip flicker reporting for terminals with synchronized output —
344
+ // DEC 2026 buffers between BSU/ESU so clear+redraw is atomic.
345
+ if (isSynchronizedOutputSupported()) {
346
+ return;
347
+ }
348
+ for (const flicker of event.flickers) {
349
+ if (flicker.reason === 'resize') {
350
+ continue;
351
+ }
352
+ const now = Date.now();
353
+ if (now - lastFlickerTime < 1000) {
354
+ logEvent('tengu_flicker', {
355
+ desiredHeight: flicker.desiredHeight,
356
+ actualHeight: flicker.availableHeight,
357
+ reason: flicker.reason
358
+ } as unknown as Record<string, boolean | number | undefined>);
359
+ }
360
+ lastFlickerTime = now;
361
+ }
362
+ }
363
+ }
364
+ };
365
+ }
366
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiYXBwZW5kRmlsZVN5bmMiLCJSZWFjdCIsImxvZ0V2ZW50IiwiZ3JhY2VmdWxTaHV0ZG93biIsImdyYWNlZnVsU2h1dGRvd25TeW5jIiwiQ2hhbm5lbEVudHJ5IiwiZ2V0QWxsb3dlZENoYW5uZWxzIiwic2V0QWxsb3dlZENoYW5uZWxzIiwic2V0SGFzRGV2Q2hhbm5lbHMiLCJzZXRTZXNzaW9uVHJ1c3RBY2NlcHRlZCIsInNldFN0YXRzU3RvcmUiLCJDb21tYW5kIiwiY3JlYXRlU3RhdHNTdG9yZSIsIlN0YXRzU3RvcmUiLCJnZXRTeXN0ZW1Db250ZXh0IiwiaW5pdGlhbGl6ZVRlbGVtZXRyeUFmdGVyVHJ1c3QiLCJpc1N5bmNocm9uaXplZE91dHB1dFN1cHBvcnRlZCIsIlJlbmRlck9wdGlvbnMiLCJSb290IiwiVGV4dFByb3BzIiwiS2V5YmluZGluZ1NldHVwIiwic3RhcnREZWZlcnJlZFByZWZldGNoZXMiLCJjaGVja0dhdGVfQ0FDSEVEX09SX0JMT0NLSU5HIiwiaW5pdGlhbGl6ZUdyb3d0aEJvb2siLCJyZXNldEdyb3d0aEJvb2siLCJpc1F1YWxpZmllZEZvckdyb3ZlIiwiaGFuZGxlTWNwanNvblNlcnZlckFwcHJvdmFscyIsIkFwcFN0YXRlUHJvdmlkZXIiLCJvbkNoYW5nZUFwcFN0YXRlIiwibm9ybWFsaXplQXBpS2V5Rm9yQ29uZmlnIiwiZ2V0RXh0ZXJuYWxDbGF1ZGVNZEluY2x1ZGVzIiwiZ2V0TWVtb3J5RmlsZXMiLCJzaG91bGRTaG93Q2xhdWRlTWRFeHRlcm5hbEluY2x1ZGVzV2FybmluZyIsImNoZWNrSGFzVHJ1c3REaWFsb2dBY2NlcHRlZCIsImdldEN1c3RvbUFwaUtleVN0YXR1cyIsImdldEdsb2JhbENvbmZpZyIsInNhdmVHbG9iYWxDb25maWciLCJ1cGRhdGVEZWVwTGlua1Rlcm1pbmFsUHJlZmVyZW5jZSIsImlzRW52VHJ1dGh5IiwiaXNSdW5uaW5nT25Ib21lc3BhY2UiLCJGcHNNZXRyaWNzIiwiRnBzVHJhY2tlciIsInVwZGF0ZUdpdGh1YlJlcG9QYXRoTWFwcGluZyIsImFwcGx5Q29uZmlnRW52aXJvbm1lbnRWYXJpYWJsZXMiLCJQZXJtaXNzaW9uTW9kZSIsImdldEJhc2VSZW5kZXJPcHRpb25zIiwiZ2V0U2V0dGluZ3NXaXRoQWxsRXJyb3JzIiwiaGFzQXV0b01vZGVPcHRJbiIsImhhc1NraXBEYW5nZXJvdXNNb2RlUGVybWlzc2lvblByb21wdCIsImNvbXBsZXRlT25ib2FyZGluZyIsImN1cnJlbnQiLCJoYXNDb21wbGV0ZWRPbmJvYXJkaW5nIiwibGFzdE9uYm9hcmRpbmdWZXJzaW9uIiwiTUFDUk8iLCJWRVJTSU9OIiwic2hvd0RpYWxvZyIsInJvb3QiLCJyZW5kZXJlciIsImRvbmUiLCJyZXN1bHQiLCJUIiwiUmVhY3ROb2RlIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZW5kZXIiLCJleGl0V2l0aEVycm9yIiwibWVzc2FnZSIsImJlZm9yZUV4aXQiLCJleGl0V2l0aE1lc3NhZ2UiLCJjb2xvciIsIm9wdGlvbnMiLCJleGl0Q29kZSIsIlRleHQiLCJ1bm1vdW50IiwicHJvY2VzcyIsImV4aXQiLCJzaG93U2V0dXBEaWFsb2ciLCJyZW5kZXJBbmRSdW4iLCJlbGVtZW50Iiwid2FpdFVudGlsRXhpdCIsInNob3dTZXR1cFNjcmVlbnMiLCJwZXJtaXNzaW9uTW9kZSIsImFsbG93RGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnMiLCJjb21tYW5kcyIsImNsYXVkZUluQ2hyb21lIiwiZGV2Q2hhbm5lbHMiLCJlbnYiLCJJU19ERU1PIiwiY29uZmlnIiwib25ib2FyZGluZ1Nob3duIiwidGhlbWUiLCJPbmJvYXJkaW5nIiwiQ0xBVUJCSVQiLCJUcnVzdERpYWxvZyIsImVycm9ycyIsImFsbEVycm9ycyIsImxlbmd0aCIsImV4dGVybmFsSW5jbHVkZXMiLCJDbGF1ZGVNZEV4dGVybmFsSW5jbHVkZXNEaWFsb2ciLCJzZXRJbW1lZGlhdGUiLCJHcm92ZURpYWxvZyIsImRlY2lzaW9uIiwiQU5USFJPUElDX0FQSV9LRVkiLCJjdXN0b21BcGlLZXlUcnVuY2F0ZWQiLCJrZXlTdGF0dXMiLCJBcHByb3ZlQXBpS2V5IiwiQnlwYXNzUGVybWlzc2lvbnNNb2RlRGlhbG9nIiwiQXV0b01vZGVPcHRJbkRpYWxvZyIsImlzQ2hhbm5lbHNFbmFibGVkIiwiZ2V0Q2xhdWRlQUlPQXV0aFRva2VucyIsImFsbCIsImFjY2Vzc1Rva2VuIiwibWFwIiwiYyIsImRldiIsIkRldkNoYW5uZWxzRGlhbG9nIiwiaGFzQ29tcGxldGVkQ2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nIiwiQ2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nIiwiZ2V0UmVuZGVyQ29udGV4dCIsImV4aXRPbkN0cmxDIiwicmVuZGVyT3B0aW9ucyIsImdldEZwc01ldHJpY3MiLCJzdGF0cyIsImxhc3RGbGlja2VyVGltZSIsImJhc2VPcHRpb25zIiwic3RkaW4iLCJmcHNUcmFja2VyIiwiZnJhbWVUaW1pbmdMb2dQYXRoIiwiQ0xBVURFX0NPREVfRlJBTUVfVElNSU5HX0xPRyIsImdldE1ldHJpY3MiLCJvbkZyYW1lIiwiZXZlbnQiLCJyZWNvcmQiLCJkdXJhdGlvbk1zIiwib2JzZXJ2ZSIsInBoYXNlcyIsImxpbmUiLCJKU09OIiwic3RyaW5naWZ5IiwidG90YWwiLCJyc3MiLCJtZW1vcnlVc2FnZSIsImNwdSIsImNwdVVzYWdlIiwiZmxpY2tlciIsImZsaWNrZXJzIiwicmVhc29uIiwibm93IiwiRGF0ZSIsImRlc2lyZWRIZWlnaHQiLCJhY3R1YWxIZWlnaHQiLCJhdmFpbGFibGVIZWlnaHQiLCJSZWNvcmQiXSwic291cmNlcyI6WyJpbnRlcmFjdGl2ZUhlbHBlcnMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IHsgYXBwZW5kRmlsZVN5bmMgfSBmcm9tICdmcydcbmltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7XG4gIGdyYWNlZnVsU2h1dGRvd24sXG4gIGdyYWNlZnVsU2h1dGRvd25TeW5jLFxufSBmcm9tICdzcmMvdXRpbHMvZ3JhY2VmdWxTaHV0ZG93bi5qcydcbmltcG9ydCB7XG4gIHR5cGUgQ2hhbm5lbEVudHJ5LFxuICBnZXRBbGxvd2VkQ2hhbm5lbHMsXG4gIHNldEFsbG93ZWRDaGFubmVscyxcbiAgc2V0SGFzRGV2Q2hhbm5lbHMsXG4gIHNldFNlc3Npb25UcnVzdEFjY2VwdGVkLFxuICBzZXRTdGF0c1N0b3JlLFxufSBmcm9tICcuL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZCB9IGZyb20gJy4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyBjcmVhdGVTdGF0c1N0b3JlLCB0eXBlIFN0YXRzU3RvcmUgfSBmcm9tICcuL2NvbnRleHQvc3RhdHMuanMnXG5pbXBvcnQgeyBnZXRTeXN0ZW1Db250ZXh0IH0gZnJvbSAnLi9jb250ZXh0LmpzJ1xuaW1wb3J0IHsgaW5pdGlhbGl6ZVRlbGVtZXRyeUFmdGVyVHJ1c3QgfSBmcm9tICcuL2VudHJ5cG9pbnRzL2luaXQuanMnXG5pbXBvcnQgeyBpc1N5bmNocm9uaXplZE91dHB1dFN1cHBvcnRlZCB9IGZyb20gJy4vaW5rL3Rlcm1pbmFsLmpzJ1xuaW1wb3J0IHR5cGUgeyBSZW5kZXJPcHRpb25zLCBSb290LCBUZXh0UHJvcHMgfSBmcm9tICcuL2luay5qcydcbmltcG9ydCB7IEtleWJpbmRpbmdTZXR1cCB9IGZyb20gJy4va2V5YmluZGluZ3MvS2V5YmluZGluZ1Byb3ZpZGVyU2V0dXAuanMnXG5pbXBvcnQgeyBzdGFydERlZmVycmVkUHJlZmV0Y2hlcyB9IGZyb20gJy4vbWFpbi5qcydcbmltcG9ydCB7XG4gIGNoZWNrR2F0ZV9DQUNIRURfT1JfQkxPQ0tJTkcsXG4gIGluaXRpYWxpemVHcm93dGhCb29rLFxuICByZXNldEdyb3d0aEJvb2ssXG59IGZyb20gJy4vc2VydmljZXMvYW5hbHl0aWNzL2dyb3d0aGJvb2suanMnXG5pbXBvcnQgeyBpc1F1YWxpZmllZEZvckdyb3ZlIH0gZnJvbSAnLi9zZXJ2aWNlcy9hcGkvZ3JvdmUuanMnXG5pbXBvcnQgeyBoYW5kbGVNY3Bqc29uU2VydmVyQXBwcm92YWxzIH0gZnJvbSAnLi9zZXJ2aWNlcy9tY3BTZXJ2ZXJBcHByb3ZhbC5qcydcbmltcG9ydCB7IEFwcFN0YXRlUHJvdmlkZXIgfSBmcm9tICcuL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHsgb25DaGFuZ2VBcHBTdGF0ZSB9IGZyb20gJy4vc3RhdGUvb25DaGFuZ2VBcHBTdGF0ZS5qcydcbmltcG9ydCB7IG5vcm1hbGl6ZUFwaUtleUZvckNvbmZpZyB9IGZyb20gJy4vdXRpbHMvYXV0aFBvcnRhYmxlLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0RXh0ZXJuYWxDbGF1ZGVNZEluY2x1ZGVzLFxuICBnZXRNZW1vcnlGaWxlcyxcbiAgc2hvdWxkU2hvd0NsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc1dhcm5pbmcsXG59IGZyb20gJy4vdXRpbHMvY2xhdWRlbWQuanMnXG5pbXBvcnQge1xuICBjaGVja0hhc1RydXN0RGlhbG9nQWNjZXB0ZWQsXG4gIGdldEN1c3RvbUFwaUtleVN0YXR1cyxcbiAgZ2V0R2xvYmFsQ29uZmlnLFxuICBzYXZlR2xvYmFsQ29uZmlnLFxufSBmcm9tICcuL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IHVwZGF0ZURlZXBMaW5rVGVybWluYWxQcmVmZXJlbmNlIH0gZnJvbSAnLi91dGlscy9kZWVwTGluay90ZXJtaW5hbFByZWZlcmVuY2UuanMnXG5pbXBvcnQgeyBpc0VudlRydXRoeSwgaXNSdW5uaW5nT25Ib21lc3BhY2UgfSBmcm9tICcuL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHsgdHlwZSBGcHNNZXRyaWNzLCBGcHNUcmFja2VyIH0gZnJvbSAnLi91dGlscy9mcHNUcmFja2VyLmpzJ1xuaW1wb3J0IHsgdXBkYXRlR2l0aHViUmVwb1BhdGhNYXBwaW5nIH0gZnJvbSAnLi91dGlscy9naXRodWJSZXBvUGF0aE1hcHBpbmcuanMnXG5pbXBvcnQgeyBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzIH0gZnJvbSAnLi91dGlscy9tYW5hZ2VkRW52LmpzJ1xuaW1wb3J0IHR5cGUgeyBQZXJtaXNzaW9uTW9kZSB9IGZyb20gJy4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvbk1vZGUuanMnXG5pbXBvcnQgeyBnZXRCYXNlUmVuZGVyT3B0aW9ucyB9IGZyb20gJy4vdXRpbHMvcmVuZGVyT3B0aW9ucy5qcydcbmltcG9ydCB7IGdldFNldHRpbmdzV2l0aEFsbEVycm9ycyB9IGZyb20gJy4vdXRpbHMvc2V0dGluZ3MvYWxsRXJyb3JzLmpzJ1xuaW1wb3J0IHtcbiAgaGFzQXV0b01vZGVPcHRJbixcbiAgaGFzU2tpcERhbmdlcm91c01vZGVQZXJtaXNzaW9uUHJvbXB0LFxufSBmcm9tICcuL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gY29tcGxldGVPbmJvYXJkaW5nKCk6IHZvaWQge1xuICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAuLi5jdXJyZW50LFxuICAgIGhhc0NvbXBsZXRlZE9uYm9hcmRpbmc6IHRydWUsXG4gICAgbGFzdE9uYm9hcmRpbmdWZXJzaW9uOiBNQUNSTy5WRVJTSU9OLFxuICB9KSlcbn1cbmV4cG9ydCBmdW5jdGlvbiBzaG93RGlhbG9nPFQgPSB2b2lkPihcbiAgcm9vdDogUm9vdCxcbiAgcmVuZGVyZXI6IChkb25lOiAocmVzdWx0OiBUKSA9PiB2b2lkKSA9PiBSZWFjdC5SZWFjdE5vZGUsXG4pOiBQcm9taXNlPFQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlPFQ+KHJlc29sdmUgPT4ge1xuICAgIGNvbnN0IGRvbmUgPSAocmVzdWx0OiBUKTogdm9pZCA9PiB2b2lkIHJlc29sdmUocmVzdWx0KVxuICAgIHJvb3QucmVuZGVyKHJlbmRlcmVyKGRvbmUpKVxuICB9KVxufVxuXG4vKipcbiAqIFJlbmRlciBhbiBlcnJvciBtZXNzYWdlIHRocm91Z2ggSW5rLCB0aGVuIHVubW91bnQgYW5kIGV4aXQuXG4gKiBVc2UgdGhpcyBmb3IgZmF0YWwgZXJyb3JzIGFmdGVyIHRoZSBJbmsgcm9vdCBoYXMgYmVlbiBjcmVhdGVkIOKAlFxuICogY29uc29sZS5lcnJvciBpcyBzd2FsbG93ZWQgYnkgSW5rJ3MgcGF0Y2hDb25zb2xlLCBzbyB3ZSByZW5kZXJcbiAqIHRocm91Z2ggdGhlIFJlYWN0IHRyZWUgaW5zdGVhZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4aXRXaXRoRXJyb3IoXG4gIHJvb3Q6IFJvb3QsXG4gIG1lc3NhZ2U6IHN0cmluZyxcbiAgYmVmb3JlRXhpdD86ICgpID0+IFByb21pc2U8dm9pZD4sXG4pOiBQcm9taXNlPG5ldmVyPiB7XG4gIHJldHVybiBleGl0V2l0aE1lc3NhZ2Uocm9vdCwgbWVzc2FnZSwgeyBjb2xvcjogJ2Vycm9yJywgYmVmb3JlRXhpdCB9KVxufVxuXG4vKipcbiAqIFJlbmRlciBhIG1lc3NhZ2UgdGhyb3VnaCBJbmssIHRoZW4gdW5tb3VudCBhbmQgZXhpdC5cbiAqIFVzZSB0aGlzIGZvciBtZXNzYWdlcyBhZnRlciB0aGUgSW5rIHJvb3QgaGFzIGJlZW4gY3JlYXRlZCDigJRcbiAqIGNvbnNvbGUgb3V0cHV0IGlzIHN3YWxsb3dlZCBieSBJbmsncyBwYXRjaENvbnNvbGUsIHNvIHdlIHJlbmRlclxuICogdGhyb3VnaCB0aGUgUmVhY3QgdHJlZSBpbnN0ZWFkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhpdFdpdGhNZXNzYWdlKFxuICByb290OiBSb290LFxuICBtZXNzYWdlOiBzdHJpbmcsXG4gIG9wdGlvbnM/OiB7XG4gICAgY29sb3I/OiBUZXh0UHJvcHNbJ2NvbG9yJ11cbiAgICBleGl0Q29kZT86IG51bWJlclxuICAgIGJlZm9yZUV4aXQ/OiAoKSA9PiBQcm9taXNlPHZvaWQ+XG4gIH0sXG4pOiBQcm9taXNlPG5ldmVyPiB7XG4gIGNvbnN0IHsgVGV4dCB9ID0gYXdhaXQgaW1wb3J0KCcuL2luay5qcycpXG4gIGNvbnN0IGNvbG9yID0gb3B0aW9ucz8uY29sb3JcbiAgY29uc3QgZXhpdENvZGUgPSBvcHRpb25zPy5leGl0Q29kZSA/PyAxXG4gIHJvb3QucmVuZGVyKFxuICAgIGNvbG9yID8gPFRleHQgY29sb3I9e2NvbG9yfT57bWVzc2FnZX08L1RleHQ+IDogPFRleHQ+e21lc3NhZ2V9PC9UZXh0PixcbiAgKVxuICByb290LnVubW91bnQoKVxuICBhd2FpdCBvcHRpb25zPy5iZWZvcmVFeGl0Py4oKVxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL25vLXByb2Nlc3MtZXhpdCAtLSBleGl0IGFmdGVyIEluayB1bm1vdW50XG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSlcbn1cblxuLyoqXG4gKiBTaG93IGEgc2V0dXAgZGlhbG9nIHdyYXBwZWQgaW4gQXBwU3RhdGVQcm92aWRlciArIEtleWJpbmRpbmdTZXR1cC5cbiAqIFJlZHVjZXMgYm9pbGVycGxhdGUgaW4gc2hvd1NldHVwU2NyZWVucygpIHdoZXJlIGV2ZXJ5IGRpYWxvZyBuZWVkcyB0aGVzZSB3cmFwcGVycy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNob3dTZXR1cERpYWxvZzxUID0gdm9pZD4oXG4gIHJvb3Q6IFJvb3QsXG4gIHJlbmRlcmVyOiAoZG9uZTogKHJlc3VsdDogVCkgPT4gdm9pZCkgPT4gUmVhY3QuUmVhY3ROb2RlLFxuICBvcHRpb25zPzogeyBvbkNoYW5nZUFwcFN0YXRlPzogdHlwZW9mIG9uQ2hhbmdlQXBwU3RhdGUgfSxcbik6IFByb21pc2U8VD4ge1xuICByZXR1cm4gc2hvd0RpYWxvZzxUPihyb290LCBkb25lID0+IChcbiAgICA8QXBwU3RhdGVQcm92aWRlciBvbkNoYW5nZUFwcFN0YXRlPXtvcHRpb25zPy5vbkNoYW5nZUFwcFN0YXRlfT5cbiAgICAgIDxLZXliaW5kaW5nU2V0dXA+e3JlbmRlcmVyKGRvbmUpfTwvS2V5YmluZGluZ1NldHVwPlxuICAgIDwvQXBwU3RhdGVQcm92aWRlcj5cbiAgKSlcbn1cblxuLyoqXG4gKiBSZW5kZXIgdGhlIG1haW4gVUkgaW50byB0aGUgcm9vdCBhbmQgd2FpdCBmb3IgaXQgdG8gZXhpdC5cbiAqIEhhbmRsZXMgdGhlIGNvbW1vbiBlcGlsb2d1ZTogc3RhcnQgZGVmZXJyZWQgcHJlZmV0Y2hlcywgd2FpdCBmb3IgZXhpdCwgZ3JhY2VmdWwgc2h1dGRvd24uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW5kZXJBbmRSdW4oXG4gIHJvb3Q6IFJvb3QsXG4gIGVsZW1lbnQ6IFJlYWN0LlJlYWN0Tm9kZSxcbik6IFByb21pc2U8dm9pZD4ge1xuICByb290LnJlbmRlcihlbGVtZW50KVxuICBzdGFydERlZmVycmVkUHJlZmV0Y2hlcygpXG4gIGF3YWl0IHJvb3Qud2FpdFVudGlsRXhpdCgpXG4gIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMClcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNob3dTZXR1cFNjcmVlbnMoXG4gIHJvb3Q6IFJvb3QsXG4gIHBlcm1pc3Npb25Nb2RlOiBQZXJtaXNzaW9uTW9kZSxcbiAgYWxsb3dEYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uczogYm9vbGVhbixcbiAgY29tbWFuZHM/OiBDb21tYW5kW10sXG4gIGNsYXVkZUluQ2hyb21lPzogYm9vbGVhbixcbiAgZGV2Q2hhbm5lbHM/OiBDaGFubmVsRW50cnlbXSxcbik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBpZiAoXG4gICAgXCJwcm9kdWN0aW9uXCIgPT09ICd0ZXN0JyB8fFxuICAgIGlzRW52VHJ1dGh5KGZhbHNlKSB8fFxuICAgIHByb2Nlc3MuZW52LklTX0RFTU8gLy8gU2tpcCBvbmJvYXJkaW5nIGluIGRlbW8gbW9kZVxuICApIHtcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIGNvbnN0IGNvbmZpZyA9IGdldEdsb2JhbENvbmZpZygpXG4gIGxldCBvbmJvYXJkaW5nU2hvd24gPSBmYWxzZVxuICBpZiAoXG4gICAgIWNvbmZpZy50aGVtZSB8fFxuICAgICFjb25maWcuaGFzQ29tcGxldGVkT25ib2FyZGluZyAvLyBhbHdheXMgc2hvdyBvbmJvYXJkaW5nIGF0IGxlYXN0IG9uY2VcbiAgKSB7XG4gICAgb25ib2FyZGluZ1Nob3duID0gdHJ1ZVxuICAgIGNvbnN0IHsgT25ib2FyZGluZyB9ID0gYXdhaXQgaW1wb3J0KCcuL2NvbXBvbmVudHMvT25ib2FyZGluZy5qcycpXG4gICAgYXdhaXQgc2hvd1NldHVwRGlhbG9nKFxuICAgICAgcm9vdCxcbiAgICAgIGRvbmUgPT4gKFxuICAgICAgICA8T25ib2FyZGluZ1xuICAgICAgICAgIG9uRG9uZT17KCkgPT4ge1xuICAgICAgICAgICAgY29tcGxldGVPbmJvYXJkaW5nKClcbiAgICAgICAgICAgIHZvaWQgZG9uZSgpXG4gICAgICAgICAgfX1cbiAgICAgICAgLz5cbiAgICAgICksXG4gICAgICB7IG9uQ2hhbmdlQXBwU3RhdGUgfSxcbiAgICApXG4gIH1cblxuICAvLyBBbHdheXMgc2hvdyB0aGUgdHJ1c3QgZGlhbG9nIGluIGludGVyYWN0aXZlIHNlc3Npb25zLCByZWdhcmRsZXNzIG9mIHBlcm1pc3Npb24gbW9kZS5cbiAgLy8gVGhlIHRydXN0IGRpYWxvZyBpcyB0aGUgd29ya3NwYWNlIHRydXN0IGJvdW5kYXJ5IOKAlCBpdCB3YXJucyBhYm91dCB1bnRydXN0ZWQgcmVwb3NcbiAgLy8gYW5kIGNoZWNrcyBDTEFVREUubWQgZXh0ZXJuYWwgaW5jbHVkZXMuIGJ5cGFzc1Blcm1pc3Npb25zIG1vZGVcbiAgLy8gb25seSBhZmZlY3RzIHRvb2wgZXhlY3V0aW9uIHBlcm1pc3Npb25zLCBub3Qgd29ya3NwYWNlIHRydXN0LlxuICAvLyBOb3RlOiBub24taW50ZXJhY3RpdmUgc2Vzc2lvbnMgKENJL0NEIHdpdGggLXApIG5ldmVyIHJlYWNoIHNob3dTZXR1cFNjcmVlbnMgYXQgYWxsLlxuICAvLyBTa2lwIHBlcm1pc3Npb24gY2hlY2tzIGluIGNsYXViYml0XG4gIGlmICghaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVUJCSVQpKSB7XG4gICAgLy8gRmFzdC1wYXRoOiBza2lwIFRydXN0RGlhbG9nIGltcG9ydCtyZW5kZXIgd2hlbiBDV0QgaXMgYWxyZWFkeSB0cnVzdGVkLlxuICAgIC8vIElmIGl0IHJldHVybnMgdHJ1ZSwgdGhlIFRydXN0RGlhbG9nIHdvdWxkIGF1dG8tcmVzb2x2ZSByZWdhcmRsZXNzIG9mXG4gICAgLy8gc2VjdXJpdHkgZmVhdHVyZXMsIHNvIHdlIGNhbiBza2lwIHRoZSBkeW5hbWljIGltcG9ydCBhbmQgcmVuZGVyIGN5Y2xlLlxuICAgIGlmICghY2hlY2tIYXNUcnVzdERpYWxvZ0FjY2VwdGVkKCkpIHtcbiAgICAgIGNvbnN0IHsgVHJ1c3REaWFsb2cgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgJy4vY29tcG9uZW50cy9UcnVzdERpYWxvZy9UcnVzdERpYWxvZy5qcydcbiAgICAgIClcbiAgICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgICAgPFRydXN0RGlhbG9nIGNvbW1hbmRzPXtjb21tYW5kc30gb25Eb25lPXtkb25lfSAvPlxuICAgICAgKSlcbiAgICB9XG5cbiAgICAvLyBTaWduYWwgdGhhdCB0cnVzdCBoYXMgYmVlbiB2ZXJpZmllZCBmb3IgdGhpcyBzZXNzaW9uLlxuICAgIC8vIEdyb3d0aEJvb2sgY2hlY2tzIHRoaXMgdG8gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSBhdXRoIGhlYWRlcnMuXG4gICAgc2V0U2Vzc2lvblRydXN0QWNjZXB0ZWQodHJ1ZSlcblxuICAgIC8vIFJlc2V0IGFuZCByZWluaXRpYWxpemUgR3Jvd3RoQm9vayBhZnRlciB0cnVzdCBpcyBlc3RhYmxpc2hlZC5cbiAgICAvLyBEZWZlbnNlIGZvciBsb2dpbi9sb2dvdXQ6IGNsZWFycyBhbnkgcHJpb3IgY2xpZW50IHNvIHRoZSBuZXh0IGluaXRcbiAgICAvLyBwaWNrcyB1cCBmcmVzaCBhdXRoIGhlYWRlcnMuXG4gICAgcmVzZXRHcm93dGhCb29rKClcbiAgICB2b2lkIGluaXRpYWxpemVHcm93dGhCb29rKClcblxuICAgIC8vIE5vdyB0aGF0IHRydXN0IGlzIGVzdGFibGlzaGVkLCBwcmVmZXRjaCBzeXN0ZW0gY29udGV4dCBpZiBpdCB3YXNuJ3QgYWxyZWFkeVxuICAgIHZvaWQgZ2V0U3lzdGVtQ29udGV4dCgpXG5cbiAgICAvLyBJZiBzZXR0aW5ncyBhcmUgdmFsaWQsIGNoZWNrIGZvciBhbnkgbWNwLmpzb24gc2VydmVycyB0aGF0IG5lZWQgYXBwcm92YWxcbiAgICBjb25zdCB7IGVycm9yczogYWxsRXJyb3JzIH0gPSBnZXRTZXR0aW5nc1dpdGhBbGxFcnJvcnMoKVxuICAgIGlmIChhbGxFcnJvcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICBhd2FpdCBoYW5kbGVNY3Bqc29uU2VydmVyQXBwcm92YWxzKHJvb3QpXG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIGNsYXVkZS5tZCBpbmNsdWRlcyB0aGF0IG5lZWQgYXBwcm92YWxcbiAgICBpZiAoYXdhaXQgc2hvdWxkU2hvd0NsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc1dhcm5pbmcoKSkge1xuICAgICAgY29uc3QgZXh0ZXJuYWxJbmNsdWRlcyA9IGdldEV4dGVybmFsQ2xhdWRlTWRJbmNsdWRlcyhcbiAgICAgICAgYXdhaXQgZ2V0TWVtb3J5RmlsZXModHJ1ZSksXG4gICAgICApXG4gICAgICBjb25zdCB7IENsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc0RpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAnLi9jb21wb25lbnRzL0NsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc0RpYWxvZy5qcydcbiAgICAgIClcbiAgICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgICAgPENsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc0RpYWxvZ1xuICAgICAgICAgIG9uRG9uZT17ZG9uZX1cbiAgICAgICAgICBpc1N0YW5kYWxvbmVEaWFsb2dcbiAgICAgICAgICBleHRlcm5hbEluY2x1ZGVzPXtleHRlcm5hbEluY2x1ZGVzfVxuICAgICAgICAvPlxuICAgICAgKSlcbiAgICB9XG4gIH1cblxuICAvLyBUcmFjayBjdXJyZW50IHJlcG8gcGF0aCBmb3IgdGVsZXBvcnQgZGlyZWN0b3J5IHN3aXRjaGluZyAoZmlyZS1hbmQtZm9yZ2V0KVxuICAvLyBUaGlzIG11c3QgaGFwcGVuIEFGVEVSIHRydXN0IHRvIHByZXZlbnQgdW50cnVzdGVkIGRpcmVjdG9yaWVzIGZyb20gcG9pc29uaW5nIHRoZSBtYXBwaW5nXG4gIHZvaWQgdXBkYXRlR2l0aHViUmVwb1BhdGhNYXBwaW5nKClcbiAgaWYgKGZlYXR1cmUoJ0xPREVTVE9ORScpKSB7XG4gICAgdXBkYXRlRGVlcExpbmtUZXJtaW5hbFByZWZlcmVuY2UoKVxuICB9XG5cbiAgLy8gQXBwbHkgZnVsbCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYWZ0ZXIgdHJ1c3QgZGlhbG9nIGlzIGFjY2VwdGVkIE9SIGluIGJ5cGFzcyBtb2RlXG4gIC8vIEluIGJ5cGFzcyBtb2RlIChDSS9DRCwgYXV0b21hdGlvbiksIHdlIHRydXN0IHRoZSBlbnZpcm9ubWVudCBzbyBhcHBseSBhbGwgdmFyaWFibGVzXG4gIC8vIEluIG5vcm1hbCBtb2RlLCB0aGlzIGhhcHBlbnMgYWZ0ZXIgdGhlIHRydXN0IGRpYWxvZyBpcyBhY2NlcHRlZFxuICAvLyBUaGlzIGluY2x1ZGVzIHBvdGVudGlhbGx5IGRhbmdlcm91cyBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZnJvbSB1bnRydXN0ZWQgc291cmNlc1xuICBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzKClcblxuICAvLyBJbml0aWFsaXplIHRlbGVtZXRyeSBhZnRlciBlbnYgdmFycyBhcmUgYXBwbGllZCBzbyBPVEVMIGVuZHBvaW50IGVudiB2YXJzIGFuZFxuICAvLyBvdGVsSGVhZGVyc0hlbHBlciAod2hpY2ggcmVxdWlyZXMgdHJ1c3QgdG8gZXhlY3V0ZSkgYXJlIGF2YWlsYWJsZS5cbiAgLy8gRGVmZXIgdG8gbmV4dCB0aWNrIHNvIHRoZSBPVGVsIGR5bmFtaWMgaW1wb3J0IHJlc29sdmVzIGFmdGVyIGZpcnN0IHJlbmRlclxuICAvLyBpbnN0ZWFkIG9mIGR1cmluZyB0aGUgcHJlLXJlbmRlciBtaWNyb3Rhc2sgcXVldWUuXG4gIHNldEltbWVkaWF0ZSgoKSA9PiBpbml0aWFsaXplVGVsZW1ldHJ5QWZ0ZXJUcnVzdCgpKVxuXG4gIGlmIChhd2FpdCBpc1F1YWxpZmllZEZvckdyb3ZlKCkpIHtcbiAgICBjb25zdCB7IEdyb3ZlRGlhbG9nIH0gPSBhd2FpdCBpbXBvcnQoJ3NyYy9jb21wb25lbnRzL2dyb3ZlL0dyb3ZlLmpzJylcbiAgICBjb25zdCBkZWNpc2lvbiA9IGF3YWl0IHNob3dTZXR1cERpYWxvZzxzdHJpbmc+KHJvb3QsIGRvbmUgPT4gKFxuICAgICAgPEdyb3ZlRGlhbG9nXG4gICAgICAgIHNob3dJZkFscmVhZHlWaWV3ZWQ9e2ZhbHNlfVxuICAgICAgICBsb2NhdGlvbj17b25ib2FyZGluZ1Nob3duID8gJ29uYm9hcmRpbmcnIDogJ3BvbGljeV91cGRhdGVfbW9kYWwnfVxuICAgICAgICBvbkRvbmU9e2RvbmV9XG4gICAgICAvPlxuICAgICkpXG4gICAgaWYgKGRlY2lzaW9uID09PSAnZXNjYXBlJykge1xuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2dyb3ZlX3BvbGljeV9leGl0ZWQnLCB7fSlcbiAgICAgIGdyYWNlZnVsU2h1dGRvd25TeW5jKDApXG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG4gIH1cblxuICAvLyBDaGVjayBmb3IgY3VzdG9tIEFQSSBrZXlcbiAgLy8gT24gaG9tZXNwYWNlLCBBTlRIUk9QSUNfQVBJX0tFWSBpcyBwcmVzZXJ2ZWQgaW4gcHJvY2Vzcy5lbnYgZm9yIGNoaWxkXG4gIC8vIHByb2Nlc3NlcyBidXQgaWdub3JlZCBieSBDbGF1ZGUgQ29kZSBpdHNlbGYgKHNlZSBhdXRoLnRzKS5cbiAgaWYgKHByb2Nlc3MuZW52LkFOVEhST1BJQ19BUElfS0VZICYmICFpc1J1bm5pbmdPbkhvbWVzcGFjZSgpKSB7XG4gICAgY29uc3QgY3VzdG9tQXBpS2V5VHJ1bmNhdGVkID0gbm9ybWFsaXplQXBpS2V5Rm9yQ29uZmlnKFxuICAgICAgcHJvY2Vzcy5lbnYuQU5USFJPUElDX0FQSV9LRVksXG4gICAgKVxuICAgIGNvbnN0IGtleVN0YXR1cyA9IGdldEN1c3RvbUFwaUtleVN0YXR1cyhjdXN0b21BcGlLZXlUcnVuY2F0ZWQpXG4gICAgaWYgKGtleVN0YXR1cyA9PT0gJ25ldycpIHtcbiAgICAgIGNvbnN0IHsgQXBwcm92ZUFwaUtleSB9ID0gYXdhaXQgaW1wb3J0KCcuL2NvbXBvbmVudHMvQXBwcm92ZUFwaUtleS5qcycpXG4gICAgICBhd2FpdCBzaG93U2V0dXBEaWFsb2c8Ym9vbGVhbj4oXG4gICAgICAgIHJvb3QsXG4gICAgICAgIGRvbmUgPT4gKFxuICAgICAgICAgIDxBcHByb3ZlQXBpS2V5XG4gICAgICAgICAgICBjdXN0b21BcGlLZXlUcnVuY2F0ZWQ9e2N1c3RvbUFwaUtleVRydW5jYXRlZH1cbiAgICAgICAgICAgIG9uRG9uZT17ZG9uZX1cbiAgICAgICAgICAvPlxuICAgICAgICApLFxuICAgICAgICB7IG9uQ2hhbmdlQXBwU3RhdGUgfSxcbiAgICAgIClcbiAgICB9XG4gIH1cblxuICBpZiAoXG4gICAgKHBlcm1pc3Npb25Nb2RlID09PSAnYnlwYXNzUGVybWlzc2lvbnMnIHx8XG4gICAgICBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zKSAmJlxuICAgICFoYXNTa2lwRGFuZ2Vyb3VzTW9kZVBlcm1pc3Npb25Qcm9tcHQoKVxuICApIHtcbiAgICBjb25zdCB7IEJ5cGFzc1Blcm1pc3Npb25zTW9kZURpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgJy4vY29tcG9uZW50cy9CeXBhc3NQZXJtaXNzaW9uc01vZGVEaWFsb2cuanMnXG4gICAgKVxuICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgIDxCeXBhc3NQZXJtaXNzaW9uc01vZGVEaWFsb2cgb25BY2NlcHQ9e2RvbmV9IC8+XG4gICAgKSlcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgIC8vIE9ubHkgc2hvdyB0aGUgb3B0LWluIGRpYWxvZyBpZiBhdXRvIG1vZGUgYWN0dWFsbHkgcmVzb2x2ZWQg4oCUIGlmIHRoZVxuICAgIC8vIGdhdGUgZGVuaWVkIGl0IChvcmcgbm90IGFsbG93bGlzdGVkLCBzZXR0aW5ncyBkaXNhYmxlZCksIHNob3dpbmdcbiAgICAvLyBjb25zZW50IGZvciBhbiB1bmF2YWlsYWJsZSBmZWF0dXJlIGlzIHBvaW50bGVzcy4gVGhlXG4gICAgLy8gdmVyaWZ5QXV0b01vZGVHYXRlQWNjZXNzIG5vdGlmaWNhdGlvbiB3aWxsIGV4cGxhaW4gd2h5IGluc3RlYWQuXG4gICAgaWYgKHBlcm1pc3Npb25Nb2RlID09PSAnYXV0bycgJiYgIWhhc0F1dG9Nb2RlT3B0SW4oKSkge1xuICAgICAgY29uc3QgeyBBdXRvTW9kZU9wdEluRGlhbG9nIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICcuL2NvbXBvbmVudHMvQXV0b01vZGVPcHRJbkRpYWxvZy5qcydcbiAgICAgIClcbiAgICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgICAgPEF1dG9Nb2RlT3B0SW5EaWFsb2dcbiAgICAgICAgICBvbkFjY2VwdD17ZG9uZX1cbiAgICAgICAgICBvbkRlY2xpbmU9eygpID0+IGdyYWNlZnVsU2h1dGRvd25TeW5jKDEpfVxuICAgICAgICAgIGRlY2xpbmVFeGl0c1xuICAgICAgICAvPlxuICAgICAgKSlcbiAgICB9XG4gIH1cblxuICAvLyAtLWRhbmdlcm91c2x5LWxvYWQtZGV2ZWxvcG1lbnQtY2hhbm5lbHMgY29uZmlybWF0aW9uLiBPbiBhY2NlcHQsIGFwcGVuZFxuICAvLyBkZXYgY2hhbm5lbHMgdG8gYW55IC0tY2hhbm5lbHMgbGlzdCBhbHJlYWR5IHNldCBpbiBtYWluLnRzeC4gT3JnIHBvbGljeVxuICAvLyBpcyBOT1QgYnlwYXNzZWQg4oCUIGdhdGVDaGFubmVsU2VydmVyKCkgc3RpbGwgcnVuczsgdGhpcyBmbGFnIG9ubHkgZXhpc3RzXG4gIC8vIHRvIHNpZGVzdGVwIHRoZSAtLWNoYW5uZWxzIGFwcHJvdmVkLXNlcnZlciBhbGxvd2xpc3QuXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQ0hBTk5FTFMnKSkge1xuICAgIC8vIGdhdGVDaGFubmVsU2VydmVyIGFuZCBDaGFubmVsc05vdGljZSByZWFkIHRlbmd1X2hhcmJvciBhZnRlciB0aGlzXG4gICAgLy8gZnVuY3Rpb24gcmV0dXJucy4gQSBjb2xkIGRpc2sgY2FjaGUgKGZyZXNoIGluc3RhbGwsIG9yIGZpcnN0IHJ1biBhZnRlclxuICAgIC8vIHRoZSBmbGFnIHdhcyBhZGRlZCBzZXJ2ZXItc2lkZSkgZGVmYXVsdHMgdG8gZmFsc2UgYW5kIHNpbGVudGx5IGRyb3BzXG4gICAgLy8gY2hhbm5lbCBub3RpZmljYXRpb25zIGZvciB0aGUgd2hvbGUgc2Vzc2lvbiDigJQgZ2gjMzcwMjYuXG4gICAgLy8gY2hlY2tHYXRlX0NBQ0hFRF9PUl9CTE9DS0lORyByZXR1cm5zIGltbWVkaWF0ZWx5IGlmIGRpc2sgYWxyZWFkeSBzYXlzXG4gICAgLy8gdHJ1ZTsgb25seSBibG9ja3Mgb24gYSBjb2xkL3N0YWxlLWZhbHNlIGNhY2hlIChhd2FpdHMgdGhlIHNhbWUgbWVtb2l6ZWRcbiAgICAvLyBpbml0aWFsaXplR3Jvd3RoQm9vayBwcm9taXNlIGZpcmVkIGVhcmxpZXIpLiBBbHNvIHdhcm1zIHRoZVxuICAgIC8vIGlzQ2hhbm5lbHNFbmFibGVkKCkgY2hlY2sgaW4gdGhlIGRldi1jaGFubmVscyBkaWFsb2cgYmVsb3cuXG4gICAgaWYgKGdldEFsbG93ZWRDaGFubmVscygpLmxlbmd0aCA+IDAgfHwgKGRldkNoYW5uZWxzPy5sZW5ndGggPz8gMCkgPiAwKSB7XG4gICAgICBhd2FpdCBjaGVja0dhdGVfQ0FDSEVEX09SX0JMT0NLSU5HKCd0ZW5ndV9oYXJib3InKVxuICAgIH1cblxuICAgIGlmIChkZXZDaGFubmVscyAmJiBkZXZDaGFubmVscy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBbeyBpc0NoYW5uZWxzRW5hYmxlZCB9LCB7IGdldENsYXVkZUFJT0F1dGhUb2tlbnMgfV0gPVxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAgICAgaW1wb3J0KCcuL3NlcnZpY2VzL21jcC9jaGFubmVsQWxsb3dsaXN0LmpzJyksXG4gICAgICAgICAgaW1wb3J0KCcuL3V0aWxzL2F1dGguanMnKSxcbiAgICAgICAgXSlcbiAgICAgIC8vIFNraXAgdGhlIGRpYWxvZyB3aGVuIGNoYW5uZWxzIGFyZSBibG9ja2VkICh0ZW5ndV9oYXJib3Igb2ZmIG9yIG5vXG4gICAgICAvLyBPQXV0aCkg4oCUIGFjY2VwdGluZyB0aGVuIGltbWVkaWF0ZWx5IHNlZWluZyBcIm5vdCBhdmFpbGFibGVcIiBpblxuICAgICAgLy8gQ2hhbm5lbHNOb3RpY2UgaXMgd29yc2UgdGhhbiBubyBkaWFsb2cuIEFwcGVuZCBlbnRyaWVzIGFueXdheSBzb1xuICAgICAgLy8gQ2hhbm5lbHNOb3RpY2UgcmVuZGVycyB0aGUgYmxvY2tlZCBicmFuY2ggd2l0aCB0aGUgZGV2IGVudHJpZXNcbiAgICAgIC8vIG5hbWVkLiBkZXY6dHJ1ZSBoZXJlIGlzIGZvciB0aGUgZmxhZyBsYWJlbCBpbiBDaGFubmVsc05vdGljZVxuICAgICAgLy8gKGhhc05vbkRldiBjaGVjayk7IHRoZSBhbGxvd2xpc3QgYnlwYXNzIGl0IGFsc28gZ3JhbnRzIGlzIG1vb3RcbiAgICAgIC8vIHNpbmNlIHRoZSBnYXRlIGJsb2NrcyB1cHN0cmVhbS5cbiAgICAgIGlmICghaXNDaGFubmVsc0VuYWJsZWQoKSB8fCAhZ2V0Q2xhdWRlQUlPQXV0aFRva2VucygpPy5hY2Nlc3NUb2tlbikge1xuICAgICAgICBzZXRBbGxvd2VkQ2hhbm5lbHMoW1xuICAgICAgICAgIC4uLmdldEFsbG93ZWRDaGFubmVscygpLFxuICAgICAgICAgIC4uLmRldkNoYW5uZWxzLm1hcChjID0+ICh7IC4uLmMsIGRldjogdHJ1ZSB9KSksXG4gICAgICAgIF0pXG4gICAgICAgIHNldEhhc0RldkNoYW5uZWxzKHRydWUpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCB7IERldkNoYW5uZWxzRGlhbG9nIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICAgJy4vY29tcG9uZW50cy9EZXZDaGFubmVsc0RpYWxvZy5qcydcbiAgICAgICAgKVxuICAgICAgICBhd2FpdCBzaG93U2V0dXBEaWFsb2cocm9vdCwgZG9uZSA9PiAoXG4gICAgICAgICAgPERldkNoYW5uZWxzRGlhbG9nXG4gICAgICAgICAgICBjaGFubmVscz17ZGV2Q2hhbm5lbHN9XG4gICAgICAgICAgICBvbkFjY2VwdD17KCkgPT4ge1xuICAgICAgICAgICAgICAvLyBNYXJrIGRldiBlbnRyaWVzIHBlci1lbnRyeSBzbyB0aGUgYWxsb3dsaXN0IGJ5cGFzcyBkb2Vzbid0IGxlYWtcbiAgICAgICAgICAgICAgLy8gdG8gLS1jaGFubmVscyBlbnRyaWVzIHdoZW4gYm90aCBmbGFncyBhcmUgcGFzc2VkLlxuICAgICAgICAgICAgICBzZXRBbGxvd2VkQ2hhbm5lbHMoW1xuICAgICAgICAgICAgICAgIC4uLmdldEFsbG93ZWRDaGFubmVscygpLFxuICAgICAgICAgICAgICAgIC4uLmRldkNoYW5uZWxzLm1hcChjID0+ICh7IC4uLmMsIGRldjogdHJ1ZSB9KSksXG4gICAgICAgICAgICAgIF0pXG4gICAgICAgICAgICAgIHNldEhhc0RldkNoYW5uZWxzKHRydWUpXG4gICAgICAgICAgICAgIHZvaWQgZG9uZSgpXG4gICAgICAgICAgICB9fVxuICAgICAgICAgIC8+XG4gICAgICAgICkpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gU2hvdyBDaHJvbWUgb25ib2FyZGluZyBmb3IgZmlyc3QtdGltZSBDbGF1ZGUgaW4gQ2hyb21lIHVzZXJzXG4gIGlmIChcbiAgICBjbGF1ZGVJbkNocm9tZSAmJlxuICAgICFnZXRHbG9iYWxDb25maWcoKS5oYXNDb21wbGV0ZWRDbGF1ZGVJbkNocm9tZU9uYm9hcmRpbmdcbiAgKSB7XG4gICAgY29uc3QgeyBDbGF1ZGVJbkNocm9tZU9uYm9hcmRpbmcgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICcuL2NvbXBvbmVudHMvQ2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nLmpzJ1xuICAgIClcbiAgICBhd2FpdCBzaG93U2V0dXBEaWFsb2cocm9vdCwgZG9uZSA9PiAoXG4gICAgICA8Q2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nIG9uRG9uZT17ZG9uZX0gLz5cbiAgICApKVxuICB9XG5cbiAgcmV0dXJuIG9uYm9hcmRpbmdTaG93blxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UmVuZGVyQ29udGV4dChleGl0T25DdHJsQzogYm9vbGVhbik6IHtcbiAgcmVuZGVyT3B0aW9uczogUmVuZGVyT3B0aW9uc1xuICBnZXRGcHNNZXRyaWNzOiAoKSA9PiBGcHNNZXRyaWNzIHwgdW5kZWZpbmVkXG4gIHN0YXRzOiBTdGF0c1N0b3JlXG59IHtcbiAgbGV0IGxhc3RGbGlja2VyVGltZSA9IDBcbiAgY29uc3QgYmFzZU9wdGlvbnMgPSBnZXRCYXNlUmVuZGVyT3B0aW9ucyhleGl0T25DdHJsQylcblxuICAvLyBMb2cgYW5hbHl0aWNzIGV2ZW50IHdoZW4gc3RkaW4gb3ZlcnJpZGUgaXMgYWN0aXZlXG4gIGlmIChiYXNlT3B0aW9ucy5zdGRpbikge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV9zdGRpbl9pbnRlcmFjdGl2ZScsIHt9KVxuICB9XG5cbiAgY29uc3QgZnBzVHJhY2tlciA9IG5ldyBGcHNUcmFja2VyKClcbiAgY29uc3Qgc3RhdHMgPSBjcmVhdGVTdGF0c1N0b3JlKClcbiAgc2V0U3RhdHNTdG9yZShzdGF0cylcblxuICAvLyBCZW5jaCBtb2RlOiB3aGVuIHNldCwgYXBwZW5kIHBlci1mcmFtZSBwaGFzZSB0aW1pbmdzIGFzIEpTT05MIGZvclxuICAvLyBvZmZsaW5lIGFuYWx5c2lzIGJ5IGJlbmNoL3JlcGwtc2Nyb2xsLnRzLiBDYXB0dXJlcyB0aGUgZnVsbCBUVUlcbiAgLy8gcmVuZGVyIHBpcGVsaW5lICh5b2dhIOKGkiBzY3JlZW4gYnVmZmVyIOKGkiBkaWZmIOKGkiBvcHRpbWl6ZSDihpIgc3Rkb3V0KVxuICAvLyBzbyBwZXJmIHdvcmsgb24gYW55IHBoYXNlIGNhbiBiZSB2YWxpZGF0ZWQgYWdhaW5zdCByZWFsIHVzZXIgZmxvd3MuXG4gIGNvbnN0IGZyYW1lVGltaW5nTG9nUGF0aCA9IHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0ZSQU1FX1RJTUlOR19MT0dcbiAgcmV0dXJuIHtcbiAgICBnZXRGcHNNZXRyaWNzOiAoKSA9PiBmcHNUcmFja2VyLmdldE1ldHJpY3MoKSxcbiAgICBzdGF0cyxcbiAgICByZW5kZXJPcHRpb25zOiB7XG4gICAgICAuLi5iYXNlT3B0aW9ucyxcbiAgICAgIG9uRnJhbWU6IGV2ZW50ID0+IHtcbiAgICAgICAgZnBzVHJhY2tlci5yZWNvcmQoZXZlbnQuZHVyYXRpb25NcylcbiAgICAgICAgc3RhdHMub2JzZXJ2ZSgnZnJhbWVfZHVyYXRpb25fbXMnLCBldmVudC5kdXJhdGlvbk1zKVxuICAgICAgICBpZiAoZnJhbWVUaW1pbmdMb2dQYXRoICYmIGV2ZW50LnBoYXNlcykge1xuICAgICAgICAgIC8vIEJlbmNoLW9ubHkgZW52LXZhci1nYXRlZCBwYXRoOiBzeW5jIHdyaXRlIHNvIG5vIGZyYW1lcyBkcm9wcGVkXG4gICAgICAgICAgLy8gb24gYWJydXB0IGV4aXQuIH4xMDAgYnl0ZXMgYXQg4omkNjBmcHMgaXMgbmVnbGlnaWJsZS4gcnNzL2NwdSBhcmVcbiAgICAgICAgICAvLyBzaW5nbGUgc3lzY2FsbHM7IGNwdSBpcyBjdW11bGF0aXZlIOKAlCBiZW5jaCBzaWRlIGNvbXB1dGVzIGRlbHRhLlxuICAgICAgICAgIGNvbnN0IGxpbmUgPVxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9uby1kaXJlY3QtanNvbi1vcGVyYXRpb25zIC0tIHRpbnkgb2JqZWN0LCBob3QgYmVuY2ggcGF0aFxuICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgICAgICB0b3RhbDogZXZlbnQuZHVyYXRpb25NcyxcbiAgICAgICAgICAgICAgLi4uZXZlbnQucGhhc2VzLFxuICAgICAgICAgICAgICByc3M6IHByb2Nlc3MubWVtb3J5VXNhZ2UucnNzKCksXG4gICAgICAgICAgICAgIGNwdTogcHJvY2Vzcy5jcHVVc2FnZSgpLFxuICAgICAgICAgICAgfSkgKyAnXFxuJ1xuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tc3luYy1mcyAtLSBiZW5jaC1vbmx5LCBzeW5jIHNvIG5vIGZyYW1lcyBkcm9wcGVkIG9uIGV4aXRcbiAgICAgICAgICBhcHBlbmRGaWxlU3luYyhmcmFtZVRpbWluZ0xvZ1BhdGgsIGxpbmUpXG4gICAgICAgIH1cbiAgICAgICAgLy8gU2tpcCBmbGlja2VyIHJlcG9ydGluZyBmb3IgdGVybWluYWxzIHdpdGggc3luY2hyb25pemVkIG91dHB1dCDigJRcbiAgICAgICAgLy8gREVDIDIwMjYgYnVmZmVycyBiZXR3ZWVuIEJTVS9FU1Ugc28gY2xlYXIrcmVkcmF3IGlzIGF0b21pYy5cbiAgICAgICAgaWYgKGlzU3luY2hyb25pemVkT3V0cHV0U3VwcG9ydGVkKCkpIHtcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBmb3IgKGNvbnN0IGZsaWNrZXIgb2YgZXZlbnQuZmxpY2tlcnMpIHtcbiAgICAgICAgICBpZiAoZmxpY2tlci5yZWFzb24gPT09ICdyZXNpemUnKSB7XG4gICAgICAgICAgICBjb250aW51ZVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpXG4gICAgICAgICAgaWYgKG5vdyAtIGxhc3RGbGlja2VyVGltZSA8IDEwMDApIHtcbiAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9mbGlja2VyJywge1xuICAgICAgICAgICAgICBkZXNpcmVkSGVpZ2h0OiBmbGlja2VyLmRlc2lyZWRIZWlnaHQsXG4gICAgICAgICAgICAgIGFjdHVhbEhlaWdodDogZmxpY2tlci5hdmFpbGFibGVIZWlnaHQsXG4gICAgICAgICAgICAgIHJlYXNvbjogZmxpY2tlci5yZWFzb24sXG4gICAgICAgICAgICB9IGFzIHVua25vd24gYXMgUmVjb3JkPHN0cmluZywgYm9vbGVhbiB8IG51bWJlciB8IHVuZGVmaW5lZD4pXG4gICAgICAgICAgfVxuICAgICAgICAgIGxhc3RGbGlja2VyVGltZSA9IG5vd1xuICAgICAgICB9XG4gICAgICB9LFxuICAgIH0sXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsU0FBU0MsY0FBYyxRQUFRLElBQUk7QUFDbkMsT0FBT0MsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsUUFBUSxRQUFRLGlDQUFpQztBQUMxRCxTQUNFQyxnQkFBZ0IsRUFDaEJDLG9CQUFvQixRQUNmLCtCQUErQjtBQUN0QyxTQUNFLEtBQUtDLFlBQVksRUFDakJDLGtCQUFrQixFQUNsQkMsa0JBQWtCLEVBQ2xCQyxpQkFBaUIsRUFDakJDLHVCQUF1QixFQUN2QkMsYUFBYSxRQUNSLHNCQUFzQjtBQUM3QixjQUFjQyxPQUFPLFFBQVEsZUFBZTtBQUM1QyxTQUFTQyxnQkFBZ0IsRUFBRSxLQUFLQyxVQUFVLFFBQVEsb0JBQW9CO0FBQ3RFLFNBQVNDLGdCQUFnQixRQUFRLGNBQWM7QUFDL0MsU0FBU0MsNkJBQTZCLFFBQVEsdUJBQXVCO0FBQ3JFLFNBQVNDLDZCQUE2QixRQUFRLG1CQUFtQjtBQUNqRSxjQUFjQyxhQUFhLEVBQUVDLElBQUksRUFBRUMsU0FBUyxRQUFR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
package/macro.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // Build-time macros - stubbed for development
2
2
 
3
3
  export const MACRO = {
4
- VERSION: '3.0.5',
4
+ VERSION: '3.1.0',
5
5
  VERSION_CHANGELOG: '',
6
6
  NATIVE_PACKAGE_URL: '@anthropic-ai/claude-code-native',
7
7
  CLI_BIN_PATH: './cli.tsx',
@@ -0,0 +1,141 @@
1
+ import { feature } from 'bun:bundle'
2
+ import { logForDebugging } from '../utils/debug.js'
3
+ import { errorMessage } from '../utils/errors.js'
4
+ import { getDefaultSonnetModel } from '../utils/model/model.js'
5
+ import { sideQuery } from '../utils/sideQuery.js'
6
+ import { jsonParse } from '../utils/slowOperations.js'
7
+ import {
8
+ formatMemoryManifest,
9
+ type MemoryHeader,
10
+ scanMemoryFiles,
11
+ } from './memoryScan.js'
12
+
13
+ export type RelevantMemory = {
14
+ path: string
15
+ mtimeMs: number
16
+ }
17
+
18
+ const SELECT_MEMORIES_SYSTEM_PROMPT = `You are selecting memories that will be useful to Claude Code as it processes a user's query. You will be given the user's query and a list of available memory files with their filenames and descriptions.
19
+
20
+ Return a list of filenames for the memories that will clearly be useful to Claude Code as it processes the user's query (up to 5). Only include memories that you are certain will be helpful based on their name and description.
21
+ - If you are unsure if a memory will be useful in processing the user's query, then do not include it in your list. Be selective and discerning.
22
+ - If there are no memories in the list that would clearly be useful, feel free to return an empty list.
23
+ - If a list of recently-used tools is provided, do not select memories that are usage reference or API documentation for those tools (Claude Code is already exercising them). DO still select memories containing warnings, gotchas, or known issues about those tools — active use is exactly when those matter.
24
+ `
25
+
26
+ /**
27
+ * Find memory files relevant to a query by scanning memory file headers
28
+ * and asking Sonnet to select the most relevant ones.
29
+ *
30
+ * Returns absolute file paths + mtime of the most relevant memories
31
+ * (up to 5). Excludes MEMORY.md (already loaded in system prompt).
32
+ * mtime is threaded through so callers can surface freshness to the
33
+ * main model without a second stat.
34
+ *
35
+ * `alreadySurfaced` filters paths shown in prior turns before the
36
+ * Sonnet call, so the selector spends its 5-slot budget on fresh
37
+ * candidates instead of re-picking files the caller will discard.
38
+ */
39
+ export async function findRelevantMemories(
40
+ query: string,
41
+ memoryDir: string,
42
+ signal: AbortSignal,
43
+ recentTools: readonly string[] = [],
44
+ alreadySurfaced: ReadonlySet<string> = new Set(),
45
+ ): Promise<RelevantMemory[]> {
46
+ const memories = (await scanMemoryFiles(memoryDir, signal)).filter(
47
+ m => !alreadySurfaced.has(m.filePath),
48
+ )
49
+ if (memories.length === 0) {
50
+ return []
51
+ }
52
+
53
+ const selectedFilenames = await selectRelevantMemories(
54
+ query,
55
+ memories,
56
+ signal,
57
+ recentTools,
58
+ )
59
+ const byFilename = new Map(memories.map(m => [m.filename, m]))
60
+ const selected = selectedFilenames
61
+ .map(filename => byFilename.get(filename))
62
+ .filter((m): m is MemoryHeader => m !== undefined)
63
+
64
+ // Fires even on empty selection: selection-rate needs the denominator,
65
+ // and -1 ages distinguish "ran, picked nothing" from "never ran".
66
+ if (feature('MEMORY_SHAPE_TELEMETRY')) {
67
+ /* eslint-disable @typescript-eslint/no-require-imports */
68
+ const { logMemoryRecallShape } =
69
+ require('./memoryShapeTelemetry.js') as typeof import('./memoryShapeTelemetry.js')
70
+ /* eslint-enable @typescript-eslint/no-require-imports */
71
+ logMemoryRecallShape(memories, selected)
72
+ }
73
+
74
+ return selected.map(m => ({ path: m.filePath, mtimeMs: m.mtimeMs }))
75
+ }
76
+
77
+ async function selectRelevantMemories(
78
+ query: string,
79
+ memories: MemoryHeader[],
80
+ signal: AbortSignal,
81
+ recentTools: readonly string[],
82
+ ): Promise<string[]> {
83
+ const validFilenames = new Set(memories.map(m => m.filename))
84
+
85
+ const manifest = formatMemoryManifest(memories)
86
+
87
+ // When Claude Code is actively using a tool (e.g. mcp__X__spawn),
88
+ // surfacing that tool's reference docs is noise — the conversation
89
+ // already contains working usage. The selector otherwise matches
90
+ // on keyword overlap ("spawn" in query + "spawn" in a memory
91
+ // description → false positive).
92
+ const toolsSection =
93
+ recentTools.length > 0
94
+ ? `\n\nRecently used tools: ${recentTools.join(', ')}`
95
+ : ''
96
+
97
+ try {
98
+ const result = await sideQuery({
99
+ model: getDefaultSonnetModel(),
100
+ system: SELECT_MEMORIES_SYSTEM_PROMPT,
101
+ skipSystemPromptPrefix: true,
102
+ messages: [
103
+ {
104
+ role: 'user',
105
+ content: `Query: ${query}\n\nAvailable memories:\n${manifest}${toolsSection}`,
106
+ },
107
+ ],
108
+ max_tokens: 256,
109
+ output_format: {
110
+ type: 'json_schema',
111
+ schema: {
112
+ type: 'object',
113
+ properties: {
114
+ selected_memories: { type: 'array', items: { type: 'string' } },
115
+ },
116
+ required: ['selected_memories'],
117
+ additionalProperties: false,
118
+ },
119
+ },
120
+ signal,
121
+ querySource: 'memdir_relevance',
122
+ })
123
+
124
+ const textBlock = result.content.find(block => block.type === 'text')
125
+ if (!textBlock || textBlock.type !== 'text') {
126
+ return []
127
+ }
128
+
129
+ const parsed: { selected_memories: string[] } = jsonParse(textBlock.text)
130
+ return parsed.selected_memories.filter(f => validFilenames.has(f))
131
+ } catch (e) {
132
+ if (signal.aborted) {
133
+ return []
134
+ }
135
+ logForDebugging(
136
+ `[memdir] selectRelevantMemories failed: ${errorMessage(e)}`,
137
+ { level: 'warn' },
138
+ )
139
+ return []
140
+ }
141
+ }