ummaya 0.2.9 → 0.2.11
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/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/pyproject.toml +2 -2
- package/tui/package.json +1 -1
- package/tui/src/bridge/bridgeEnabled.ts +1 -1
- package/tui/src/commands/chrome/chrome.tsx +3 -4
- package/tui/src/commands/createMovedToPluginCommand.ts +1 -1
- package/tui/src/commands/extra-usage/extra-usage-core.ts +2 -4
- package/tui/src/commands/insights.ts +5 -4
- package/tui/src/commands/install-github-app/ApiKeyStep.tsx +1 -2
- package/tui/src/commands/install-github-app/CheckExistingSecretStep.tsx +3 -3
- package/tui/src/commands/install-github-app/ExistingWorkflowStep.tsx +3 -3
- package/tui/src/commands/install-github-app/InstallAppStep.tsx +1 -1
- package/tui/src/commands/install-github-app/SuccessStep.tsx +2 -10
- package/tui/src/commands/install-github-app/install-github-app.tsx +21 -22
- package/tui/src/commands/install-github-app/setupGitHubActions.ts +21 -27
- package/tui/src/commands/install-slack-app/install-slack-app.ts +1 -1
- package/tui/src/commands/mobile/mobile.tsx +3 -3
- package/tui/src/commands/stickers/stickers.ts +1 -1
- package/tui/src/commands/upgrade/upgrade.tsx +3 -4
- package/tui/src/components/ClaudeInChromeOnboarding.tsx +2 -3
- package/tui/src/components/Feedback.tsx +8 -2
- package/tui/src/components/FeedbackSurvey/submitTranscriptShare.ts +7 -2
- package/tui/src/components/Passes/Passes.tsx +1 -1
- package/tui/src/components/WorkflowMultiselectDialog.tsx +3 -3
- package/tui/src/components/messages/AssistantTextMessage.tsx +1 -2
- package/tui/src/constants/github-app.ts +26 -38
- package/tui/src/constants/oauth.ts +28 -59
- package/tui/src/constants/product.ts +6 -6
- package/tui/src/entrypoints/sdk/coreSchemas.ts +1 -1
- package/tui/src/services/analytics/firstPartyEventLogger.ts +0 -3
- package/tui/src/services/analytics/firstPartyEventLoggingExporter.ts +34 -12
- package/tui/src/services/analytics/growthbook.ts +17 -16
- package/tui/src/services/api/filesApi.ts +4 -14
- package/tui/src/services/api/metricsOptOut.ts +20 -1
- package/tui/src/services/mcp/channelNotification.ts +1 -1
- package/tui/src/services/mcp/officialRegistry.ts +5 -1
- package/tui/src/services/mcp/useManageMCPConnections.ts +1 -1
- package/tui/src/services/mcp/utils.ts +2 -2
- package/tui/src/services/tokenEstimation.ts +0 -1
- package/tui/src/tools/AgentTool/built-in/claudeCodeGuideAgent.ts +6 -6
- package/tui/src/tools/McpAuthTool/McpAuthTool.ts +1 -1
- package/tui/src/tools/RemoteTriggerTool/RemoteTriggerTool.ts +1 -1
- package/tui/src/tools/RemoteTriggerTool/prompt.ts +2 -2
- package/tui/src/tools/WebFetchTool/preapproved.ts +0 -4
- package/tui/src/tools/WebFetchTool/utils.ts +15 -11
- package/tui/src/upstreamproxy/upstreamproxy.ts +9 -15
- package/tui/src/utils/autoUpdater.ts +4 -1
- package/tui/src/utils/claudeInChrome/mcpServer.ts +1 -1
- package/tui/src/utils/claudeInChrome/setup.ts +1 -1
- package/tui/src/utils/claudeInChrome/toolRendering.tsx +1 -2
- package/tui/src/utils/desktopDeepLink.ts +18 -18
- package/tui/src/utils/fastMode.ts +1 -1
- package/tui/src/utils/http.ts +1 -6
- package/tui/src/utils/ide.ts +6 -5
- package/tui/src/utils/model/providers.ts +6 -10
- package/tui/src/utils/modelCost.ts +0 -2
- package/tui/src/utils/nativeInstaller/download.ts +15 -17
- package/tui/src/utils/plugins/installCounts.ts +4 -11
- package/tui/src/utils/plugins/officialMarketplaceGcs.ts +5 -18
- package/tui/src/utils/releaseNotes.ts +2 -2
- package/tui/src/utils/settings/types.ts +1 -1
- package/tui/src/utils/statusNoticeDefinitions.tsx +3 -3
- package/tui/src/utils/telemetry/bigqueryExporter.ts +20 -13
- package/uv.lock +1 -1
|
@@ -239,9 +239,6 @@ export type GrowthBookExperimentData = {
|
|
|
239
239
|
experimentMetadata?: Record<string, unknown>
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
// api.anthropic.com only serves the "production" GrowthBook environment
|
|
243
|
-
// (see starling/starling/cli/cli.py DEFAULT_ENVIRONMENTS). Staging and
|
|
244
|
-
// development environments are not exported to the prod API.
|
|
245
242
|
function getEnvironmentForGrowthBook(): string {
|
|
246
243
|
return 'production'
|
|
247
244
|
}
|
|
@@ -55,6 +55,17 @@ type FirstPartyEventLoggingPayload = {
|
|
|
55
55
|
events: FirstPartyEventLoggingEvent[]
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
function getFirstPartyEventBaseUrl(
|
|
59
|
+
configuredBaseUrl: string | undefined,
|
|
60
|
+
): string | undefined {
|
|
61
|
+
const baseUrl =
|
|
62
|
+
configuredBaseUrl ??
|
|
63
|
+
process.env.UMMAYA_FIRST_PARTY_EVENT_BASE_URL ??
|
|
64
|
+
process.env.ANTHROPIC_BASE_URL
|
|
65
|
+
const trimmed = baseUrl?.trim()
|
|
66
|
+
return trimmed ? trimmed.replace(/\/$/, '') : undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
/**
|
|
59
70
|
* Exporter for 1st-party event logging to /api/event_logging/batch.
|
|
60
71
|
*
|
|
@@ -71,7 +82,7 @@ type FirstPartyEventLoggingPayload = {
|
|
|
71
82
|
* - Auth fallback: retries without auth on 401 errors
|
|
72
83
|
*/
|
|
73
84
|
export class FirstPartyEventLoggingExporter implements LogRecordExporter {
|
|
74
|
-
private readonly endpoint: string
|
|
85
|
+
private readonly endpoint: string | undefined
|
|
75
86
|
private readonly timeout: number
|
|
76
87
|
private readonly maxBatchSize: number
|
|
77
88
|
private readonly skipAuth: boolean
|
|
@@ -109,15 +120,10 @@ export class FirstPartyEventLoggingExporter implements LogRecordExporter {
|
|
|
109
120
|
schedule?: (fn: () => Promise<void>, delayMs: number) => () => void
|
|
110
121
|
} = {},
|
|
111
122
|
) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
(process.env.ANTHROPIC_BASE_URL === 'https://api-staging.anthropic.com'
|
|
117
|
-
? 'https://api-staging.anthropic.com'
|
|
118
|
-
: 'https://api.anthropic.com')
|
|
119
|
-
|
|
120
|
-
this.endpoint = `${baseUrl}${options.path || '/api/event_logging/batch'}`
|
|
123
|
+
const baseUrl = getFirstPartyEventBaseUrl(options.baseUrl)
|
|
124
|
+
this.endpoint = baseUrl
|
|
125
|
+
? `${baseUrl}${options.path || '/api/event_logging/batch'}`
|
|
126
|
+
: undefined
|
|
121
127
|
|
|
122
128
|
this.timeout = options.timeout || 10000
|
|
123
129
|
this.maxBatchSize = options.maxBatchSize || 200
|
|
@@ -134,8 +140,10 @@ export class FirstPartyEventLoggingExporter implements LogRecordExporter {
|
|
|
134
140
|
return () => clearTimeout(t)
|
|
135
141
|
})
|
|
136
142
|
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
if (this.endpoint) {
|
|
144
|
+
// Retry any failed events from previous runs of this session (in background)
|
|
145
|
+
void this.retryPreviousBatches()
|
|
146
|
+
}
|
|
139
147
|
}
|
|
140
148
|
|
|
141
149
|
// Expose for testing
|
|
@@ -327,6 +335,11 @@ export class FirstPartyEventLoggingExporter implements LogRecordExporter {
|
|
|
327
335
|
return
|
|
328
336
|
}
|
|
329
337
|
|
|
338
|
+
if (!this.endpoint) {
|
|
339
|
+
resultCallback({ code: ExportResultCode.SUCCESS })
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
330
343
|
if (this.attempts >= this.maxAttempts) {
|
|
331
344
|
resultCallback({
|
|
332
345
|
code: ExportResultCode.FAILED,
|
|
@@ -467,6 +480,11 @@ export class FirstPartyEventLoggingExporter implements LogRecordExporter {
|
|
|
467
480
|
}
|
|
468
481
|
|
|
469
482
|
private async retryFailedEvents(): Promise<void> {
|
|
483
|
+
if (!this.endpoint) {
|
|
484
|
+
this.resetBackoff()
|
|
485
|
+
return
|
|
486
|
+
}
|
|
487
|
+
|
|
470
488
|
const filePath = this.getCurrentBatchFilePath()
|
|
471
489
|
|
|
472
490
|
// Keep retrying while there are events and endpoint is healthy
|
|
@@ -527,6 +545,10 @@ export class FirstPartyEventLoggingExporter implements LogRecordExporter {
|
|
|
527
545
|
private async sendBatchWithRetry(
|
|
528
546
|
payload: FirstPartyEventLoggingPayload,
|
|
529
547
|
): Promise<void> {
|
|
548
|
+
if (!this.endpoint) {
|
|
549
|
+
return
|
|
550
|
+
}
|
|
551
|
+
|
|
530
552
|
if (this.isKilled()) {
|
|
531
553
|
// Throw so the caller short-circuits remaining batches and queues
|
|
532
554
|
// everything to disk. Zero network traffic while killed; the backoff
|
|
@@ -421,27 +421,31 @@ function syncRemoteEvalToDisk(): void {
|
|
|
421
421
|
*/
|
|
422
422
|
function isGrowthBookEnabled(): boolean {
|
|
423
423
|
// GrowthBook depends on 1P event logging.
|
|
424
|
-
return is1PEventLoggingEnabled()
|
|
424
|
+
return is1PEventLoggingEnabled() && getGrowthBookApiHost() !== undefined
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function getGrowthBookApiHost(): string | undefined {
|
|
428
|
+
const baseUrl =
|
|
429
|
+
process.env.USER_TYPE === 'ant'
|
|
430
|
+
? process.env.CLAUDE_CODE_GB_BASE_URL
|
|
431
|
+
: process.env.UMMAYA_GROWTHBOOK_BASE_URL
|
|
432
|
+
const trimmed = baseUrl?.trim()
|
|
433
|
+
return trimmed ? trimmed : undefined
|
|
425
434
|
}
|
|
426
435
|
|
|
427
436
|
/**
|
|
428
|
-
* Hostname of ANTHROPIC_BASE_URL when it points at
|
|
429
|
-
*
|
|
430
|
-
* Enterprise-proxy deployments (Epic, Marble, etc.) typically use
|
|
431
|
-
* apiKeyHelper auth, which means isAnthropicAuthEnabled() returns false and
|
|
432
|
-
* organizationUUID/accountUUID/email are all absent from GrowthBook
|
|
433
|
-
* attributes. Without this, there's no stable attribute to target them on
|
|
434
|
-
* — only per-device IDs. See src/utils/auth.ts isAnthropicAuthEnabled().
|
|
435
|
-
*
|
|
436
|
-
* Returns undefined for unset/default (api.anthropic.com) so the attribute
|
|
437
|
-
* is absent for direct-API users. Hostname only — no path/query/creds.
|
|
437
|
+
* Hostname of ANTHROPIC_BASE_URL when it points at an operator-approved proxy.
|
|
438
438
|
*/
|
|
439
439
|
export function getApiBaseUrlHost(): string | undefined {
|
|
440
440
|
const baseUrl = process.env.ANTHROPIC_BASE_URL
|
|
441
441
|
if (!baseUrl) return undefined
|
|
442
442
|
try {
|
|
443
443
|
const host = new URL(baseUrl).host
|
|
444
|
-
|
|
444
|
+
const firstPartyHosts = (process.env.UMMAYA_FIRST_PARTY_API_HOSTS ?? '')
|
|
445
|
+
.split(',')
|
|
446
|
+
.map(value => value.trim())
|
|
447
|
+
.filter(Boolean)
|
|
448
|
+
if (firstPartyHosts.includes(host)) return undefined
|
|
445
449
|
return host
|
|
446
450
|
} catch {
|
|
447
451
|
return undefined
|
|
@@ -500,10 +504,7 @@ const getGrowthBookClient = memoize(
|
|
|
500
504
|
`GrowthBook: Creating client with clientKey=${clientKey}, attributes: ${jsonStringify(attributes)}`,
|
|
501
505
|
)
|
|
502
506
|
}
|
|
503
|
-
const baseUrl =
|
|
504
|
-
process.env.USER_TYPE === 'ant'
|
|
505
|
-
? process.env.CLAUDE_CODE_GB_BASE_URL || 'https://api.anthropic.com/'
|
|
506
|
-
: 'https://api.anthropic.com/'
|
|
507
|
+
const baseUrl = getGrowthBookApiHost() ?? 'http://127.0.0.1:9'
|
|
507
508
|
|
|
508
509
|
// Skip auth if trust hasn't been established yet
|
|
509
510
|
// This prevents executing apiKeyHelper commands before the trust dialog
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Files API client for managing files
|
|
3
|
-
*
|
|
4
|
-
* This module provides functionality to download and upload files to Anthropic Public Files API.
|
|
5
|
-
* Used by the Claude Code agent to download file attachments at session startup.
|
|
6
|
-
*
|
|
7
|
-
* API Reference: https://docs.anthropic.com/en/api/files-content
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import axios from 'axios'
|
|
11
2
|
import { randomUUID } from 'crypto'
|
|
12
3
|
import * as fs from 'fs/promises'
|
|
@@ -27,13 +18,12 @@ import {
|
|
|
27
18
|
const FILES_API_BETA_HEADER = 'files-api-2025-04-14,oauth-2025-04-20'
|
|
28
19
|
const ANTHROPIC_VERSION = '2023-06-01'
|
|
29
20
|
|
|
30
|
-
// API base URL - uses ANTHROPIC_BASE_URL set by env-manager for the appropriate environment
|
|
31
|
-
// Falls back to public API for standalone usage
|
|
32
21
|
function getDefaultApiBaseUrl(): string {
|
|
33
22
|
return (
|
|
23
|
+
process.env.UMMAYA_FILES_API_BASE_URL ||
|
|
34
24
|
process.env.ANTHROPIC_BASE_URL ||
|
|
35
25
|
process.env.CLAUDE_CODE_API_BASE_URL ||
|
|
36
|
-
'
|
|
26
|
+
'http://127.0.0.1:9/ummaya-disabled-files-api'
|
|
37
27
|
)
|
|
38
28
|
}
|
|
39
29
|
|
|
@@ -60,7 +50,7 @@ export type File = {
|
|
|
60
50
|
export type FilesApiConfig = {
|
|
61
51
|
/** OAuth token for authentication (from session JWT) */
|
|
62
52
|
oauthToken: string
|
|
63
|
-
/** Base URL for the API
|
|
53
|
+
/** Base URL for the API. Defaults to a disabled local endpoint. */
|
|
64
54
|
baseUrl?: string
|
|
65
55
|
/** Session ID for creating session-specific directories */
|
|
66
56
|
sessionId: string
|
|
@@ -123,7 +113,7 @@ async function retryWithBackoff<T>(
|
|
|
123
113
|
}
|
|
124
114
|
|
|
125
115
|
/**
|
|
126
|
-
* Downloads a single file from the
|
|
116
|
+
* Downloads a single file from the configured runtime Files API.
|
|
127
117
|
*
|
|
128
118
|
* @param fileId - The file ID (e.g., "file_011CNha8iCJcU1wXNR6q4V8w")
|
|
129
119
|
* @param config - Files API configuration
|
|
@@ -26,6 +26,18 @@ const CACHE_TTL_MS = 60 * 60 * 1000
|
|
|
26
26
|
// N `claude -p` invocations into ~1 API call/day.
|
|
27
27
|
const DISK_CACHE_TTL_MS = 24 * 60 * 60 * 1000
|
|
28
28
|
|
|
29
|
+
function getMetricsEnabledEndpoint(): string | undefined {
|
|
30
|
+
const baseUrl =
|
|
31
|
+
process.env.UMMAYA_METRICS_BASE_URL ??
|
|
32
|
+
process.env.ANT_CLAUDE_CODE_METRICS_ENDPOINT ??
|
|
33
|
+
process.env.ANTHROPIC_BASE_URL ??
|
|
34
|
+
process.env.CLAUDE_CODE_API_BASE_URL
|
|
35
|
+
const trimmed = baseUrl?.trim()
|
|
36
|
+
return trimmed
|
|
37
|
+
? `${trimmed.replace(/\/$/, '')}/api/claude_code/organizations/metrics_enabled`
|
|
38
|
+
: undefined
|
|
39
|
+
}
|
|
40
|
+
|
|
29
41
|
/**
|
|
30
42
|
* Internal function to call the API and check if metrics are enabled
|
|
31
43
|
* This is wrapped by memoizeWithTTLAsync to add caching behavior
|
|
@@ -42,7 +54,10 @@ async function _fetchMetricsEnabled(): Promise<MetricsEnabledResponse> {
|
|
|
42
54
|
...authResult.headers,
|
|
43
55
|
}
|
|
44
56
|
|
|
45
|
-
const endpoint =
|
|
57
|
+
const endpoint = getMetricsEnabledEndpoint()
|
|
58
|
+
if (!endpoint) {
|
|
59
|
+
throw new Error('Metrics opt-out endpoint is not configured for UMMAYA')
|
|
60
|
+
}
|
|
46
61
|
const response = await axios.get<MetricsEnabledResponse>(endpoint, {
|
|
47
62
|
headers,
|
|
48
63
|
timeout: 5000,
|
|
@@ -58,6 +73,10 @@ async function _checkMetricsEnabledAPI(): Promise<MetricsStatus> {
|
|
|
58
73
|
return { enabled: false, hasError: false }
|
|
59
74
|
}
|
|
60
75
|
|
|
76
|
+
if (!getMetricsEnabledEndpoint()) {
|
|
77
|
+
return { enabled: false, hasError: false }
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
try {
|
|
62
81
|
const data = await withOAuth401Retry(_fetchMetricsEnabled, {
|
|
63
82
|
also403Revoked: true,
|
|
@@ -34,10 +34,14 @@ export async function prefetchOfficialMcpUrls(): Promise<void> {
|
|
|
34
34
|
if (process.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
|
|
35
35
|
return
|
|
36
36
|
}
|
|
37
|
+
const registryUrl = process.env.UMMAYA_MCP_REGISTRY_URL?.trim()
|
|
38
|
+
if (!registryUrl) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
37
41
|
|
|
38
42
|
try {
|
|
39
43
|
const response = await axios.get<RegistryResponse>(
|
|
40
|
-
|
|
44
|
+
registryUrl,
|
|
41
45
|
{ timeout: 5000 },
|
|
42
46
|
)
|
|
43
47
|
|
|
@@ -597,7 +597,7 @@ export function useManageMCPConnections(
|
|
|
597
597
|
gate.kind === 'disabled'
|
|
598
598
|
? 'Channels are not currently available'
|
|
599
599
|
: gate.kind === 'auth'
|
|
600
|
-
? 'Channels require
|
|
600
|
+
? 'Channels require UMMAYA remote authentication · run /login'
|
|
601
601
|
: gate.kind === 'policy'
|
|
602
602
|
? 'Channels are not enabled for your org · have an administrator set channelsEnabled: true in managed settings'
|
|
603
603
|
: gate.reason
|
|
@@ -273,7 +273,7 @@ export function describeMcpConfigFilePath(scope: ConfigScope): string {
|
|
|
273
273
|
case 'enterprise':
|
|
274
274
|
return getEnterpriseMcpFilePath()
|
|
275
275
|
case 'claudeai':
|
|
276
|
-
return '
|
|
276
|
+
return 'UMMAYA remote'
|
|
277
277
|
default:
|
|
278
278
|
return scope
|
|
279
279
|
}
|
|
@@ -292,7 +292,7 @@ export function getScopeLabel(scope: ConfigScope): string {
|
|
|
292
292
|
case 'enterprise':
|
|
293
293
|
return 'Enterprise config (managed by your organization)'
|
|
294
294
|
case 'claudeai':
|
|
295
|
-
return '
|
|
295
|
+
return 'UMMAYA remote config'
|
|
296
296
|
default:
|
|
297
297
|
return scope
|
|
298
298
|
}
|
|
@@ -398,7 +398,6 @@ function roughTokenCountEstimationForBlock(
|
|
|
398
398
|
return roughTokenCountEstimation(block.text)
|
|
399
399
|
}
|
|
400
400
|
if (block.type === 'image' || block.type === 'document') {
|
|
401
|
-
// https://platform.claude.com/docs/en/build-with-claude/vision#calculate-image-costs
|
|
402
401
|
// tokens = (width px * height px)/750
|
|
403
402
|
// Images are resized to max 2000x2000 (5333 tokens). Use a conservative
|
|
404
403
|
// estimate that matches microCompact's IMAGE_MAX_TOKEN_SIZE to avoid
|
|
@@ -14,11 +14,11 @@ import type {
|
|
|
14
14
|
BuiltInAgentDefinition,
|
|
15
15
|
} from '../loadAgentsDir.js'
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const UMMAYA_DOCS_MAP_URL =
|
|
18
18
|
'https://ummaya-docs.pages.dev/llms.txt'
|
|
19
|
-
const
|
|
19
|
+
const FRIENDLIAI_DOCS_MAP_URL = 'https://ummaya-docs.pages.dev/llms.txt'
|
|
20
20
|
|
|
21
|
-
export const CLAUDE_CODE_GUIDE_AGENT_TYPE = '
|
|
21
|
+
export const CLAUDE_CODE_GUIDE_AGENT_TYPE = 'ummaya-guide'
|
|
22
22
|
|
|
23
23
|
function getClaudeCodeGuideBasePrompt(): string {
|
|
24
24
|
// Ant-native builds alias find/grep to embedded bfs/ugrep and remove the
|
|
@@ -39,7 +39,7 @@ function getClaudeCodeGuideBasePrompt(): string {
|
|
|
39
39
|
|
|
40
40
|
**Documentation sources:**
|
|
41
41
|
|
|
42
|
-
- **UMMAYA docs** (${
|
|
42
|
+
- **UMMAYA docs** (${UMMAYA_DOCS_MAP_URL}): Fetch this for questions about the UMMAYA CLI tool, including:
|
|
43
43
|
- Installation, setup, and getting started
|
|
44
44
|
- Hooks (pre/post command execution)
|
|
45
45
|
- Custom skills
|
|
@@ -50,7 +50,7 @@ function getClaudeCodeGuideBasePrompt(): string {
|
|
|
50
50
|
- Subagents and plugins
|
|
51
51
|
- Sandboxing and security
|
|
52
52
|
|
|
53
|
-
- **UMMAYA Agent SDK surface docs** (${
|
|
53
|
+
- **UMMAYA Agent SDK surface docs** (${FRIENDLIAI_DOCS_MAP_URL}): Fetch this for questions about building agents with the SDK, including:
|
|
54
54
|
- SDK overview and getting started (Python and TypeScript)
|
|
55
55
|
- Agent configuration + custom tools
|
|
56
56
|
- Session management and permissions
|
|
@@ -59,7 +59,7 @@ function getClaudeCodeGuideBasePrompt(): string {
|
|
|
59
59
|
- Cost tracking and context management
|
|
60
60
|
Note: Agent SDK docs are part of the UMMAYA provider documentation at the same URL.
|
|
61
61
|
|
|
62
|
-
- **FriendliAI/K-EXAONE API docs** (${
|
|
62
|
+
- **FriendliAI/K-EXAONE API docs** (${FRIENDLIAI_DOCS_MAP_URL}): Fetch this for questions about the provider API, including:
|
|
63
63
|
- Messages API and streaming
|
|
64
64
|
- Tool use (function calling), computer use, code execution, web search, text editing, bash, programmatic tool calling, tool search, context editing, Files API, and structured outputs
|
|
65
65
|
- Vision, PDF support, and citations
|
|
@@ -105,7 +105,7 @@ export function createMcpAuthTool(
|
|
|
105
105
|
return {
|
|
106
106
|
data: {
|
|
107
107
|
status: 'unsupported' as const,
|
|
108
|
-
message: `This is a
|
|
108
|
+
message: `This is a UMMAYA remote MCP connector. Ask the user to run /mcp and select "${serverName}" to authenticate.`,
|
|
109
109
|
},
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -80,7 +80,7 @@ export const RemoteTriggerTool = buildTool({
|
|
|
80
80
|
const accessToken = getClaudeAIOAuthTokens()?.accessToken
|
|
81
81
|
if (!accessToken) {
|
|
82
82
|
throw new Error(
|
|
83
|
-
'Not authenticated with a
|
|
83
|
+
'Not authenticated with a UMMAYA remote account. Run /login and try again.',
|
|
84
84
|
)
|
|
85
85
|
}
|
|
86
86
|
const orgUUID = await getOrganizationUUID()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const REMOTE_TRIGGER_TOOL_NAME = 'RemoteTrigger'
|
|
2
2
|
|
|
3
3
|
export const DESCRIPTION =
|
|
4
|
-
'Manage scheduled remote
|
|
4
|
+
'Manage scheduled UMMAYA remote agents (triggers) via the configured remote API. Auth is handled in-process; the token never reaches the shell.'
|
|
5
5
|
|
|
6
|
-
export const PROMPT = `Call the
|
|
6
|
+
export const PROMPT = `Call the configured UMMAYA remote-trigger API. Use this instead of curl; the OAuth token is added automatically in-process and never exposed.
|
|
7
7
|
|
|
8
8
|
Actions:
|
|
9
9
|
- list: GET /v1/code/triggers
|
|
@@ -12,11 +12,7 @@
|
|
|
12
12
|
// that sandbox network restrictions require explicit user permission rules.
|
|
13
13
|
|
|
14
14
|
export const PREAPPROVED_HOSTS = new Set([
|
|
15
|
-
// Anthropic
|
|
16
|
-
'platform.claude.com',
|
|
17
|
-
'code.claude.com',
|
|
18
15
|
'modelcontextprotocol.io',
|
|
19
|
-
'github.com/anthropics',
|
|
20
16
|
'agentskills.io',
|
|
21
17
|
|
|
22
18
|
// Top Programming Languages
|
|
@@ -28,7 +28,7 @@ class DomainBlockedError extends Error {
|
|
|
28
28
|
class DomainCheckFailedError extends Error {
|
|
29
29
|
constructor(domain: string) {
|
|
30
30
|
super(
|
|
31
|
-
`Unable to verify if domain ${domain} is safe to fetch. This may be due to network restrictions or enterprise security policies blocking
|
|
31
|
+
`Unable to verify if domain ${domain} is safe to fetch. This may be due to network restrictions or enterprise security policies blocking the configured UMMAYA domain-check endpoint.`,
|
|
32
32
|
)
|
|
33
33
|
this.name = 'DomainCheckFailedError'
|
|
34
34
|
}
|
|
@@ -68,10 +68,6 @@ const URL_CACHE = new LRUCache<string, CacheEntry>({
|
|
|
68
68
|
ttl: CACHE_TTL_MS,
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
// Separate cache for preflight domain checks. URL_CACHE is URL-keyed, so
|
|
72
|
-
// fetching two paths on the same domain triggers two identical preflight
|
|
73
|
-
// HTTP round-trips to api.anthropic.com. This hostname-keyed cache avoids
|
|
74
|
-
// that. Only 'allowed' is cached — blocked/failed re-check on next attempt.
|
|
75
71
|
const DOMAIN_CHECK_CACHE = new LRUCache<string, true>({
|
|
76
72
|
max: 128,
|
|
77
73
|
ttl: 5 * 60 * 1000, // 5 minutes — shorter than URL_CACHE TTL
|
|
@@ -180,10 +176,21 @@ export async function checkDomainBlocklist(
|
|
|
180
176
|
return { status: 'allowed' }
|
|
181
177
|
}
|
|
182
178
|
try {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
{
|
|
179
|
+
const baseUrl = process.env.UMMAYA_DOMAIN_CHECK_BASE_URL?.trim()
|
|
180
|
+
if (!baseUrl) {
|
|
181
|
+
return {
|
|
182
|
+
status: 'check_failed',
|
|
183
|
+
error: new Error('UMMAYA_DOMAIN_CHECK_BASE_URL is not configured'),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const endpoint = new URL(
|
|
187
|
+
'/api/web/domain_info',
|
|
188
|
+
baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`,
|
|
186
189
|
)
|
|
190
|
+
endpoint.searchParams.set('domain', domain)
|
|
191
|
+
const response = await axios.get(endpoint.toString(), {
|
|
192
|
+
timeout: DOMAIN_CHECK_TIMEOUT_MS,
|
|
193
|
+
})
|
|
187
194
|
if (response.status === 200) {
|
|
188
195
|
if (response.data.can_fetch === true) {
|
|
189
196
|
DOMAIN_CHECK_CACHE.set(domain, true)
|
|
@@ -380,9 +387,6 @@ export async function getURLMarkdownContent(
|
|
|
380
387
|
|
|
381
388
|
const hostname = parsedUrl.hostname
|
|
382
389
|
|
|
383
|
-
// Check if the user has opted to skip the blocklist check
|
|
384
|
-
// This is for enterprise customers with restrictive security policies
|
|
385
|
-
// that prevent outbound connections to claude.ai
|
|
386
390
|
const settings = getSettings_DEPRECATED()
|
|
387
391
|
if (!settings.skipWebFetchPreflight) {
|
|
388
392
|
const checkResult = await checkDomainBlocklist(hostname)
|
|
@@ -42,15 +42,6 @@ const NO_PROXY_LIST = [
|
|
|
42
42
|
'10.0.0.0/8',
|
|
43
43
|
'172.16.0.0/12',
|
|
44
44
|
'192.168.0.0/16',
|
|
45
|
-
// Anthropic API: no upstream route will ever match, and the MITM breaks
|
|
46
|
-
// non-Bun runtimes (Python httpx/certifi doesn't trust the forged CA).
|
|
47
|
-
// Three forms because NO_PROXY parsing differs across runtimes:
|
|
48
|
-
// *.anthropic.com — Bun, curl, Go (glob match)
|
|
49
|
-
// .anthropic.com — Python urllib/httpx (suffix match, strips leading dot)
|
|
50
|
-
// anthropic.com — apex domain fallback
|
|
51
|
-
'anthropic.com',
|
|
52
|
-
'.anthropic.com',
|
|
53
|
-
'*.anthropic.com',
|
|
54
45
|
'github.com',
|
|
55
46
|
'api.github.com',
|
|
56
47
|
'*.github.com',
|
|
@@ -111,14 +102,17 @@ export async function initUpstreamProxy(opts?: {
|
|
|
111
102
|
|
|
112
103
|
setNonDumpable()
|
|
113
104
|
|
|
114
|
-
// CCR injects ANTHROPIC_BASE_URL via StartupContext (sessionExecutor.ts /
|
|
115
|
-
// sessionHandler.ts). getOauthConfig() is wrong here: it keys off
|
|
116
|
-
// USER_TYPE + USE_{LOCAL,STAGING}_OAUTH, none of which the container sets,
|
|
117
|
-
// so it always returned the prod URL and the CA fetch 404'd.
|
|
118
105
|
const baseUrl =
|
|
119
106
|
opts?.ccrBaseUrl ??
|
|
120
|
-
process.env.
|
|
121
|
-
|
|
107
|
+
process.env.UMMAYA_UPSTREAM_PROXY_BASE_URL ??
|
|
108
|
+
process.env.ANTHROPIC_BASE_URL
|
|
109
|
+
if (!baseUrl) {
|
|
110
|
+
logForDebugging(
|
|
111
|
+
'[upstreamproxy] UMMAYA_UPSTREAM_PROXY_BASE_URL unset; proxy disabled',
|
|
112
|
+
{ level: 'warn' },
|
|
113
|
+
)
|
|
114
|
+
return state
|
|
115
|
+
}
|
|
122
116
|
const caBundlePath =
|
|
123
117
|
opts?.caBundlePath ?? join(homedir(), '.ccr', 'ca-bundle.crt')
|
|
124
118
|
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import { jsonParse } from './slowOperations.js'
|
|
29
29
|
|
|
30
30
|
const GCS_BUCKET_URL =
|
|
31
|
-
|
|
31
|
+
process.env.UMMAYA_NATIVE_DOWNLOAD_BASE_URL?.trim()
|
|
32
32
|
|
|
33
33
|
class AutoUpdaterError extends ClaudeError {}
|
|
34
34
|
|
|
@@ -376,6 +376,9 @@ export async function getNpmDistTags(): Promise<NpmDistTags> {
|
|
|
376
376
|
export async function getLatestVersionFromGcs(
|
|
377
377
|
channel: ReleaseChannel,
|
|
378
378
|
): Promise<string | null> {
|
|
379
|
+
if (!GCS_BUCKET_URL) {
|
|
380
|
+
return null
|
|
381
|
+
}
|
|
379
382
|
try {
|
|
380
383
|
const response = await axios.get(`${GCS_BUCKET_URL}/${channel}`, {
|
|
381
384
|
timeout: 5000,
|
|
@@ -23,7 +23,7 @@ import { getAllSocketPaths, getSecureSocketPath } from './common.js'
|
|
|
23
23
|
|
|
24
24
|
const EXTENSION_DOWNLOAD_URL = 'https://ummaya-docs.pages.dev/en/'
|
|
25
25
|
const BUG_REPORT_URL =
|
|
26
|
-
'https://github.com/
|
|
26
|
+
'https://github.com/umyunsang/UMMAYA/issues/new?labels=bug,chrome'
|
|
27
27
|
|
|
28
28
|
// String metadata keys safe to forward to analytics. Keys like error_message
|
|
29
29
|
// are excluded because they could contain page content or user data.
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import { getChromeSystemPrompt } from './prompt.js'
|
|
32
32
|
import { isChromeExtensionInstalledPortable } from './setupPortable.js'
|
|
33
33
|
|
|
34
|
-
const CHROME_EXTENSION_RECONNECT_URL = 'https://
|
|
34
|
+
const CHROME_EXTENSION_RECONNECT_URL = 'https://ummaya-docs.pages.dev/chrome/reconnect'
|
|
35
35
|
|
|
36
36
|
const NATIVE_HOST_IDENTIFIER = 'com.anthropic.claude_code_browser_extension'
|
|
37
37
|
const NATIVE_HOST_MANIFEST_NAME = `${NATIVE_HOST_IDENTIFIER}.json`
|
|
@@ -13,7 +13,7 @@ export type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
|
13
13
|
* Keep in sync with the package's BROWSER_TOOLS array.
|
|
14
14
|
*/
|
|
15
15
|
export type ChromeToolName = 'javascript_tool' | 'read_page' | 'find' | 'form_input' | 'computer' | 'navigate' | 'resize_window' | 'gif_creator' | 'upload_image' | 'get_page_text' | 'tabs_context_mcp' | 'tabs_create_mcp' | 'update_plan' | 'read_console_messages' | 'read_network_requests' | 'shortcuts_list' | 'shortcuts_execute';
|
|
16
|
-
const CHROME_EXTENSION_FOCUS_TAB_URL_BASE = 'https://
|
|
16
|
+
const CHROME_EXTENSION_FOCUS_TAB_URL_BASE = 'https://ummaya-docs.pages.dev/chrome/tab/';
|
|
17
17
|
function renderChromeToolUseMessage(input: Record<string, unknown>, toolName: ChromeToolName, verbose: boolean): React.ReactNode {
|
|
18
18
|
const tabId = input.tabId;
|
|
19
19
|
if (typeof tabId === 'number') {
|
|
@@ -259,4 +259,3 @@ export function getClaudeInChromeMCPToolOverrides(toolName: string): {
|
|
|
259
259
|
function isMCPToolResult(output: string | MCPToolResult): output is MCPToolResult {
|
|
260
260
|
return typeof output === 'object' && output !== null;
|
|
261
261
|
}
|
|
262
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIk1lc3NhZ2VSZXNwb25zZSIsInN1cHBvcnRzSHlwZXJsaW5rcyIsIkxpbmsiLCJUZXh0IiwicmVuZGVyVG9vbFJlc3VsdE1lc3NhZ2UiLCJyZW5kZXJEZWZhdWx0TUNQVG9vbFJlc3VsdE1lc3NhZ2UiLCJNQ1BUb29sUmVzdWx0IiwidHJ1bmNhdGVUb1dpZHRoIiwidHJhY2tDbGF1ZGVJbkNocm9tZVRhYklkIiwiVG9vbCIsIkNocm9tZVRvb2xOYW1lIiwiQ0hST01FX0VYVEVOU0lPTl9GT0NVU19UQUJfVVJMX0JBU0UiLCJyZW5kZXJDaHJvbWVUb29sVXNlTWVzc2FnZSIsImlucHV0IiwiUmVjb3JkIiwidG9vbE5hbWUiLCJ2ZXJib3NlIiwiUmVhY3ROb2RlIiwidGFiSWQiLCJzZWNvbmRhcnlJbmZvIiwidXJsIiwiVVJMIiwicHVzaCIsImhvc3RuYW1lIiwicXVlcnkiLCJhY3Rpb24iLCJyZWYiLCJBcnJheSIsImlzQXJyYXkiLCJjb29yZGluYXRlIiwiam9pbiIsInRleHQiLCJzY3JvbGxfZGlyZWN0aW9uIiwiZHVyYXRpb24iLCJ3aWR0aCIsImhlaWdodCIsInBhdHRlcm4iLCJvbmx5RXJyb3JzIiwidXJsUGF0dGVybiIsInNob3J0Y3V0SWQiLCJyZW5kZXJDaHJvbWVWaWV3VGFiTGluayIsInBhcnNlSW50IiwiTmFOIiwiaXNOYU4iLCJsaW5rVXJsIiwicmVuZGVyQ2hyb21lVG9vbFJlc3VsdE1lc3NhZ2UiLCJvdXRwdXQiLCJzdW1tYXJ5IiwiZ2V0Q2xhdWRlSW5DaHJvbWVNQ1BUb29sT3ZlcnJpZGVzIiwidXNlckZhY2luZ05hbWUiLCJyZW5kZXJUb29sVXNlTWVzc2FnZSIsIm9wdGlvbnMiLCJyZW5kZXJUb29sVXNlVGFnIiwiUGFydGlhbCIsInByb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlIiwiX2lucHV0IiwiZGlzcGxheU5hbWUiLCJyZXBsYWNlIiwiX3Byb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlIiwiaXNNQ1BUb29sUmVzdWx0Il0sInNvdXJjZXMiOlsidG9vbFJlbmRlcmluZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBNZXNzYWdlUmVzcG9uc2UgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL01lc3NhZ2VSZXNwb25zZS5qcydcbmltcG9ydCB7IHN1cHBvcnRzSHlwZXJsaW5rcyB9IGZyb20gJy4uLy4uL2luay9zdXBwb3J0cy1oeXBlcmxpbmtzLmpzJ1xuaW1wb3J0IHsgTGluaywgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHJlbmRlclRvb2xSZXN1bHRNZXNzYWdlIGFzIHJlbmRlckRlZmF1bHRNQ1BUb29sUmVzdWx0TWVzc2FnZSB9IGZyb20gJy4uLy4uL3Rvb2xzL01DUFRvb2wvVUkuanMnXG5pbXBvcnQgdHlwZSB7IE1DUFRvb2xSZXN1bHQgfSBmcm9tICcuLi8uLi91dGlscy9tY3BWYWxpZGF0aW9uLmpzJ1xuaW1wb3J0IHsgdHJ1bmNhdGVUb1dpZHRoIH0gZnJvbSAnLi4vZm9ybWF0LmpzJ1xuaW1wb3J0IHsgdHJhY2tDbGF1ZGVJbkNocm9tZVRhYklkIH0gZnJvbSAnLi9jb21tb24uanMnXG5cbmV4cG9ydCB0eXBlIHsgVG9vbCB9IGZyb20gJ0Btb2RlbGNvbnRleHRwcm90b2NvbC9zZGsvdHlwZXMuanMnXG5cbi8qKlxuICogQWxsIHRvb2wgbmFtZXMgZnJvbSBCUk9XU0VSX1RPT0xTIGluIEBhbnQvY2xhdWRlLWZvci1jaHJvbWUtbWNwLlxuICogS2VlcCBpbiBzeW5jIHdpdGggdGhlIHBhY2thZ2UncyBCUk9XU0VSX1RPT0xTIGFycmF5LlxuICovXG5leHBvcnQgdHlwZSBDaHJvbWVUb29sTmFtZSA9XG4gIHwgJ2phdmFzY3JpcHRfdG9vbCdcbiAgfCAncmVhZF9wYWdlJ1xuICB8ICdmaW5kJ1xuICB8ICdmb3JtX2lucHV0J1xuICB8ICdjb21wdXRlcidcbiAgfCAnbmF2aWdhdGUnXG4gIHwgJ3Jlc2l6ZV93aW5kb3cnXG4gIHwgJ2dpZl9jcmVhdG9yJ1xuICB8ICd1cGxvYWRfaW1hZ2UnXG4gIHwgJ2dldF9wYWdlX3RleHQnXG4gIHwgJ3RhYnNfY29udGV4dF9tY3AnXG4gIHwgJ3RhYnNfY3JlYXRlX21jcCdcbiAgfCAndXBkYXRlX3BsYW4nXG4gIHwgJ3JlYWRfY29uc29sZV9tZXNzYWdlcydcbiAgfCAncmVhZF9uZXR3b3JrX3JlcXVlc3RzJ1xuICB8ICdzaG9ydGN1dHNfbGlzdCdcbiAgfCAnc2hvcnRjdXRzX2V4ZWN1dGUnXG5cbmNvbnN0IENIUk9NRV9FWFRFTlNJT05fRk9DVVNfVEFCX1VSTF9CQVNFID0gJ2h0dHBzOi8vY2xhdS5kZS9jaHJvbWUvdGFiLydcblxuZnVuY3Rpb24gcmVuZGVyQ2hyb21lVG9vbFVzZU1lc3NhZ2UoXG4gIGlucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgdG9vbE5hbWU6IENocm9tZVRvb2xOYW1lLFxuICB2ZXJib3NlOiBib29sZWFuLFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdGFiSWQgPSBpbnB1dC50YWJJZFxuICBpZiAodHlwZW9mIHRhYklkID09PSAnbnVtYmVyJykge1xuICAgIHRyYWNrQ2xhdWRlSW5DaHJvbWVUYWJJZCh0YWJJZClcbiAgfVxuXG4gIC8vIEJ1aWxkIHNlY29uZGFyeSBpbmZvIGJhc2VkIG9uIHRvb2wgdHlwZSBhbmQgaW5wdXRcbiAgY29uc3Qgc2Vjb25kYXJ5SW5mbzogc3RyaW5nW10gPSBbXVxuXG4gIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICBjYXNlICduYXZpZ2F0ZSc6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LnVybCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCB1cmwgPSBuZXcgVVJMKGlucHV0LnVybClcbiAgICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2godXJsLmhvc3RuYW1lKVxuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2godHJ1bmNhdGVUb1dpZHRoKGlucHV0LnVybCwgMzApKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBicmVha1xuXG4gICAgY2FzZSAnZmluZCc6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LnF1ZXJ5ID09PSAnc3RyaW5nJykge1xuICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goYHBhdHRlcm46ICR7dHJ1bmNhdGVUb1dpZHRoKGlucHV0LnF1ZXJ5LCAzMCl9YClcbiAgICAgIH1cbiAgICAgIGJyZWFrXG5cbiAgICBjYXNlICdjb21wdXRlcic6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LmFjdGlvbiA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgY29uc3QgYWN0aW9uID0gaW5wdXQuYWN0aW9uXG4gICAgICAgIGlmIChcbiAgICAgICAgICBhY3Rpb24gPT09ICdsZWZ0X2NsaWNrJyB8fFxuICAgICAgICAgIGFjdGlvbiA9PT0gJ3JpZ2h0X2NsaWNrJyB8fFxuICAgICAgICAgIGFjdGlvbiA9PT0gJ2RvdWJsZV9jbGljaycgfHxcbiAgICAgICAgICBhY3Rpb24gPT09ICdtaWRkbGVfY2xpY2snXG4gICAgICAgICkge1xuICAgICAgICAgIGlmICh0eXBlb2YgaW5wdXQucmVmID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGAke2FjdGlvbn0gb24gJHtpbnB1dC5yZWZ9YClcbiAgICAgICAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoaW5wdXQuY29vcmRpbmF0ZSkpIHtcbiAgICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChgJHthY3Rpb259IGF0ICgke2lucHV0LmNvb3JkaW5hdGUuam9pbignLCAnKX0pYClcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGFjdGlvbilcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoYWN0aW9uID09PSAndHlwZScgJiYgdHlwZW9mIGlucHV0LnRleHQgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGB0eXBlIFwiJHt0cnVuY2F0ZVRvV2lkdGgoaW5wdXQudGV4dCwgMTUpfVwiYClcbiAgICAgICAgfSBlbHNlIGlmIChhY3Rpb24gPT09ICdrZXknICYmIHR5cGVvZiBpbnB1dC50ZXh0ID09PSAnc3RyaW5nJykge1xuICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChga2V5ICR7aW5wdXQudGV4dH1gKVxuICAgICAgICB9IGVsc2UgaWYgKFxuICAgICAgICAgIGFjdGlvbiA9PT0gJ3Njcm9sbCcgJiZcbiAgICAgICAgICB0eXBlb2YgaW5wdXQuc2Nyb2xsX2RpcmVjdGlvbiA9PT0gJ3N0cmluZydcbiAgICAgICAgKSB7XG4gICAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGBzY3JvbGwgJHtpbnB1dC5zY3JvbGxfZGlyZWN0aW9ufWApXG4gICAgICAgIH0gZWxzZSBpZiAoYWN0aW9uID09PSAnd2FpdCcgJiYgdHlwZW9mIGlucHV0LmR1cmF0aW9uID09PSAnbnVtYmVyJykge1xuICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChgd2FpdCAke2lucHV0LmR1cmF0aW9ufXNgKVxuICAgICAgICB9IGVsc2UgaWYgKGFjdGlvbiA9PT0gJ2xlZnRfY2xpY2tfZHJhZycpIHtcbiAgICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goJ2RyYWcnKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChhY3Rpb24pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGJyZWFrXG5cbiAgICBjYXNlICdnaWZfY3JlYXRvcic6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LmFjdGlvbiA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGAke2lucHV0LmFjdGlvbn1gKVxuICAgICAgfVxuICAgICAgYnJlYWtcblxuICAgIGNhc2UgJ3Jlc2l6ZV93aW5kb3cnOlxuICAgICAgaWYgKHR5cGVvZiBpbnB1dC53aWR0aCA9PT0gJ251bWJlcicgJiYgdHlwZW9mIGlucHV0LmhlaWdodCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGAke2lucHV0LndpZHRofXgke2lucHV0LmhlaWdodH1gKVxuICAgICAgfVxuICAgICAgYnJlYWtcblxuICAgIGNhc2UgJ3JlYWRfY29uc29sZV9tZXNzYWdlcyc6XG4gICAgICBpZiAodHlwZW9mIGlucHV0LnBhdHRlcm4gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHNlY29uZGFyeUluZm8ucHVzaChgcGF0dGVybjogJHt0cnVuY2F0ZVRvV2lkdGgoaW5wdXQucGF0dGVybiwgMjApfWApXG4gICAgICB9XG4gICAgICBpZiAoaW5wdXQub25seUVycm9ycyA9PT0gdHJ1ZSkge1xuICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goJ2Vycm9ycyBvbmx5JylcbiAgICAgIH1cbiAgICAgIGJyZWFrXG5cbiAgICBjYXNlICdyZWFkX25ldHdvcmtfcmVxdWVzdHMnOlxuICAgICAgaWYgKHR5cGVvZiBpbnB1dC51cmxQYXR0ZXJuID09PSAnc3RyaW5nJykge1xuICAgICAgICBzZWNvbmRhcnlJbmZvLnB1c2goYHBhdHRlcm46ICR7dHJ1bmNhdGVUb1dpZHRoKGlucHV0LnVybFBhdHRlcm4sIDIwKX1gKVxuICAgICAgfVxuICAgICAgYnJlYWtcblxuICAgIGNhc2UgJ3Nob3J0Y3V0c19leGVjdXRlJzpcbiAgICAgIGlmICh0eXBlb2YgaW5wdXQuc2hvcnRjdXRJZCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgc2Vjb25kYXJ5SW5mby5wdXNoKGBzaG9ydGN1dF9pZDogJHtpbnB1dC5zaG9ydGN1dElkfWApXG4gICAgICB9XG4gICAgICBicmVha1xuXG4gICAgY2FzZSAnamF2YXNjcmlwdF90b29sJzpcbiAgICAgIC8vIEluIHZlcmJvc2UgbW9kZSwgc2hvdyB0aGUgZnVsbCBjb2RlXG4gICAgICBpZiAodmVyYm9zZSAmJiB0eXBlb2YgaW5wdXQudGV4dCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgcmV0dXJuIGlucHV0LnRleHRcbiAgICAgIH1cbiAgICAgIC8vIEluIG5vbi12ZXJib3NlIG1vZGUsIHJldHVybiBlbXB0eSBzdHJpbmcgdG8gcHJlc2VydmUgVmlldyBUYWIgbGF5b3V0XG4gICAgICByZXR1cm4gJydcblxuICAgIGNhc2UgJ3RhYnNfY3JlYXRlX21jcCc6XG4gICAgY2FzZSAndGFic19jb250ZXh0X21jcCc6XG4gICAgY2FzZSAnZm9ybV9pbnB1dCc6XG4gICAgY2FzZSAnc2hvcnRjdXRzX2xpc3QnOlxuICAgIGNhc2UgJ3JlYWRfcGFnZSc6XG4gICAgY2FzZSAndXBsb2FkX2ltYWdlJzpcbiAgICBjYXNlICdnZXRfcGFnZV90ZXh0JzpcbiAgICBjYXNlICd1cGRhdGVfcGxhbic6XG4gICAgICAvLyBUaGVzZSB0b29scyBkb24ndCBoYXZlIG1lYW5pbmdmdWwgc2Vjb25kYXJ5IGluZm8gdG8gc2hvdyBpbmxpbmUuXG4gICAgICAvLyBSZXR1cm4gZW1wdHkgc3RyaW5nIChub3QgbnVsbCkgdG8gZW5zdXJlIHRvb2wgaGVhZGVyIHN0aWxsIHJlbmRlcnMuXG4gICAgICByZXR1cm4gJydcbiAgfVxuXG4gIHJldHVybiBzZWNvbmRhcnlJbmZvLmpvaW4oJywgJykgfHwgbnVsbFxufVxuXG4vKipcbiAqIFJlbmRlcnMgYSBjbGlja2FibGUgXCJWaWV3IFRhYlwiIGxpbmsgZm9yIENsYXVkZSBpbiBDaHJvbWUgTUNQIHRvb2xzLlxuICogUmV0dXJucyBudWxsIGlmOlxuICogLSBUaGUgdG9vbCBpcyBub3QgYSBDbGF1ZGUgaW4gQ2hyb21lIE1DUCB0b29sXG4gKiAtIFRoZSBpbnB1dCBkb2Vzbid0IGhhdmUgYSB2YWxpZCB0YWJJZFxuICogLSBIeXBlcmxpbmtzIGFyZSBub3Qgc3VwcG9ydGVkXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckNocm9tZVZpZXdUYWJMaW5rKGlucHV0OiB1bmtub3duKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKCFzdXBwb3J0c0h5cGVybGlua3MoKSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgaWYgKHR5cGVvZiBpbnB1dCAhPT0gJ29iamVjdCcgfHwgaW5wdXQgPT09IG51bGwgfHwgISgndGFiSWQnIGluIGlucHV0KSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgY29uc3QgdGFiSWQgPVxuICAgIHR5cGVvZiBpbnB1dC50YWJJZCA9PT0gJ251bWJlcidcbiAgICAgID8gaW5wdXQudGFiSWRcbiAgICAgIDogdHlwZW9mIGlucHV0LnRhYklkID09PSAnc3RyaW5nJ1xuICAgICAgICA/IHBhcnNlSW50KGlucHV0LnRhYklkLCAxMClcbiAgICAgICAgOiBOYU5cbiAgaWYgKGlzTmFOKHRhYklkKSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cbiAgY29uc3QgbGlua1VybCA9IGAke0NIUk9NRV9FWFRFTlNJT05fRk9DVVNfVEFCX1VSTF9CQVNFfSR7dGFiSWR9YFxuICByZXR1cm4gKFxuICAgIDxUZXh0PlxuICAgICAgeycgJ31cbiAgICAgIDxMaW5rIHVybD17bGlua1VybH0+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+W1ZpZXcgVGFiXTwvVGV4dD5cbiAgICAgIDwvTGluaz5cbiAgICA8L1RleHQ+XG4gIClcbn1cblxuLyoqXG4gKiBDdXN0b20gdG9vbCByZXN1bHQgbWVzc2FnZSByZW5kZXJpbmcgZm9yIGNsYXVkZS1pbi1jaHJvbWUgdG9vbHMuXG4gKiBTaG93cyBhIGJyaWVmIHN1bW1hcnkgZm9yIHN1Y2Nlc3NmdWwgcmVzdWx0cy4gRXJyb3JzIGFyZSBoYW5kbGVkIGJ5XG4gKiB0aGUgZGVmYXVsdCByZW5kZXJUb29sVXNlRXJyb3JNZXNzYWdlIHdoZW4gaXNfZXJyb3IgaXMgc2V0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyQ2hyb21lVG9vbFJlc3VsdE1lc3NhZ2UoXG4gIG91dHB1dDogTUNQVG9vbFJlc3VsdCxcbiAgdG9vbE5hbWU6IENocm9tZVRvb2xOYW1lLFxuICB2ZXJib3NlOiBib29sZWFuLFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKHZlcmJvc2UpIHtcbiAgICByZXR1cm4gcmVuZGVyRGVmYXVsdE1DUFRvb2xSZXN1bHRNZXNzYWdlKG91dHB1dCwgW10sIHsgdmVyYm9zZSB9KVxuICB9XG5cbiAgbGV0IHN1bW1hcnk6IHN0cmluZyB8IG51bGwgPSBudWxsXG4gIHN3aXRjaCAodG9vbE5hbWUpIHtcbiAgICBjYXNlICduYXZpZ2F0ZSc6XG4gICAgICBzdW1tYXJ5ID0gJ05hdmlnYXRpb24gY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICd0YWJzX2NyZWF0ZV9tY3AnOlxuICAgICAgc3VtbWFyeSA9ICdUYWIgY3JlYXRlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAndGFic19jb250ZXh0X21jcCc6XG4gICAgICBzdW1tYXJ5ID0gJ1RhYnMgcmVhZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnZm9ybV9pbnB1dCc6XG4gICAgICBzdW1tYXJ5ID0gJ0lucHV0IGNvbXBsZXRlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnY29tcHV0ZXInOlxuICAgICAgc3VtbWFyeSA9ICdBY3Rpb24gY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdyZXNpemVfd2luZG93JzpcbiAgICAgIHN1bW1hcnkgPSAnV2luZG93IHJlc2l6ZWQnXG4gICAgICBicmVha1xuICAgIGNhc2UgJ2ZpbmQnOlxuICAgICAgc3VtbWFyeSA9ICdTZWFyY2ggY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdnaWZfY3JlYXRvcic6XG4gICAgICBzdW1tYXJ5ID0gJ0dJRiBhY3Rpb24gY29tcGxldGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdyZWFkX2NvbnNvbGVfbWVzc2FnZXMnOlxuICAgICAgc3VtbWFyeSA9ICdDb25zb2xlIG1lc3NhZ2VzIHJldHJpZXZlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAncmVhZF9uZXR3b3JrX3JlcXVlc3RzJzpcbiAgICAgIHN1bW1hcnkgPSAnTmV0d29yayByZXF1ZXN0cyByZXRyaWV2ZWQnXG4gICAgICBicmVha1xuICAgIGNhc2UgJ3Nob3J0Y3V0c19saXN0JzpcbiAgICAgIHN1bW1hcnkgPSAnU2hvcnRjdXRzIHJldHJpZXZlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnc2hvcnRjdXRzX2V4ZWN1dGUnOlxuICAgICAgc3VtbWFyeSA9ICdTaG9ydGN1dCBleGVjdXRlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnamF2YXNjcmlwdF90b29sJzpcbiAgICAgIHN1bW1hcnkgPSAnU2NyaXB0IGV4ZWN1dGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdyZWFkX3BhZ2UnOlxuICAgICAgc3VtbWFyeSA9ICdQYWdlIHJlYWQnXG4gICAgICBicmVha1xuICAgIGNhc2UgJ3VwbG9hZF9pbWFnZSc6XG4gICAgICBzdW1tYXJ5ID0gJ0ltYWdlIHVwbG9hZGVkJ1xuICAgICAgYnJlYWtcbiAgICBjYXNlICdnZXRfcGFnZV90ZXh0JzpcbiAgICAgIHN1bW1hcnkgPSAnUGFnZSB0ZXh0IHJldHJpZXZlZCdcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAndXBkYXRlX3BsYW4nOlxuICAgICAgc3VtbWFyeSA9ICdQbGFuIHVwZGF0ZWQnXG4gICAgICBicmVha1xuICB9XG5cbiAgaWYgKHN1bW1hcnkpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPE1lc3NhZ2VSZXNwb25zZSBoZWlnaHQ9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj57c3VtbWFyeX08L1RleHQ+XG4gICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICApXG4gIH1cblxuICByZXR1cm4gbnVsbFxufVxuXG4vKipcbiAqIFJldHVybnMgdG9vbCBtZXRob2Qgb3ZlcnJpZGVzIGZvciBDbGF1ZGUgaW4gQ2hyb21lIE1DUCB0b29scy4gVXNlIHRoaXMgdG8gY3VzdG9taXplXG4gKiByZW5kZXJpbmcgZm9yIGNocm9tZSB0b29scyBpbiBhIHNpbmdsZSBzcHJlYWQgb3BlcmF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2xhdWRlSW5DaHJvbWVNQ1BUb29sT3ZlcnJpZGVzKHRvb2xOYW1lOiBzdHJpbmcpOiB7XG4gIHVzZXJGYWNpbmdOYW1lOiAoaW5wdXQ/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gc3RyaW5nXG4gIHJlbmRlclRvb2xVc2VNZXNzYWdlOiAoXG4gICAgaW5wdXQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICAgIG9wdGlvbnM6IHsgdmVyYm9zZTogYm9vbGVhbiB9LFxuICApID0+IFJlYWN0LlJlYWN0Tm9kZVxuICByZW5kZXJUb29sVXNlVGFnOiAoaW5wdXQ6IFBhcnRpYWw8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+KSA9PiBSZWFjdC5SZWFjdE5vZGVcbiAgcmVuZGVyVG9vbFJlc3VsdE1lc3NhZ2U6IChcbiAgICBvdXRwdXQ6IHN0cmluZyB8IE1DUFRvb2xSZXN1bHQsXG4gICAgcHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2U6IHVua25vd25bXSxcbiAgICBvcHRpb25zOiB7IHZlcmJvc2U6IGJvb2xlYW4gfSxcbiAgKSA9PiBSZWFjdC5SZWFjdE5vZGVcbn0ge1xuICByZXR1cm4ge1xuICAgIHVzZXJGYWNpbmdOYW1lKF9pbnB1dD86IFJlY29yZDxzdHJpbmcsIHVua25vd24+KSB7XG4gICAgICAvLyBUcmltIHRoZSBfbWNwIHBvc3RmaXggdGhhdCBzaG93IHVwIGluIHNvbWUgb2YgdGhlIHRvb2wgbmFtZXNcbiAgICAgIGNvbnN0IGRpc3BsYXlOYW1lID0gdG9vbE5hbWUucmVwbGFjZSgvX21jcCQvLCAnJylcbiAgICAgIHJldHVybiBgQ2xhdWRlIGluIENocm9tZVske2Rpc3BsYXlOYW1lfV1gXG4gICAgfSxcbiAgICByZW5kZXJUb29sVXNlTWVzc2FnZShcbiAgICAgIGlucHV0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICAgIHsgdmVyYm9zZSB9OiB7IHZlcmJvc2U6IGJvb2xlYW4gfSxcbiAgICApOiBSZWFjdC5SZWFjdE5vZGUge1xuICAgICAgcmV0dXJuIHJlbmRlckNocm9tZVRvb2xVc2VNZXNzYWdlKFxuICAgICAgICBpbnB1dCxcbiAgICAgICAgdG9vbE5hbWUgYXMgQ2hyb21lVG9vbE5hbWUsXG4gICAgICAgIHZlcmJvc2UsXG4gICAgICApXG4gICAgfSxcbiAgICByZW5kZXJUb29sVXNlVGFnKGlucHV0OiBQYXJ0aWFsPFJlY29yZDxzdHJpbmcsIHVua25vd24+Pik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gICAgICByZXR1cm4gcmVuZGVyQ2hyb21lVmlld1RhYkxpbmsoaW5wdXQpXG4gICAgfSxcbiAgICByZW5kZXJUb29sUmVzdWx0TWVzc2FnZShcbiAgICAgIG91dHB1dDogc3RyaW5nIHwgTUNQVG9vbFJlc3VsdCxcbiAgICAgIF9wcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZTogdW5rbm93bltdLFxuICAgICAgeyB2ZXJib3NlIH06IHsgdmVyYm9zZTogYm9vbGVhbiB9LFxuICAgICk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gICAgICBpZiAoIWlzTUNQVG9vbFJlc3VsdChvdXRwdXQpKSB7XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgICByZXR1cm4gcmVuZGVyQ2hyb21lVG9vbFJlc3VsdE1lc3NhZ2UoXG4gICAgICAgIG91dHB1dCxcbiAgICAgICAgdG9vbE5hbWUgYXMgQ2hyb21lVG9vbE5hbWUsXG4gICAgICAgIHZlcmJvc2UsXG4gICAgICApXG4gICAgfSxcbiAgfVxufVxuXG5mdW5jdGlvbiBpc01DUFRvb2xSZXN1bHQoXG4gIG91dHB1dDogc3RyaW5nIHwgTUNQVG9vbFJlc3VsdCxcbik6IG91dHB1dCBpcyBNQ1BUb29sUmVzdWx0IHtcbiAgcmV0dXJuIHR5cGVvZiBvdXRwdXQgPT09ICdvYmplY3QnICYmIG91dHB1dCAhPT0gbnVsbFxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFP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
|