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.
Files changed (65) hide show
  1. package/npm-shrinkwrap.json +2 -2
  2. package/package.json +1 -1
  3. package/pyproject.toml +2 -2
  4. package/tui/package.json +1 -1
  5. package/tui/src/bridge/bridgeEnabled.ts +1 -1
  6. package/tui/src/commands/chrome/chrome.tsx +3 -4
  7. package/tui/src/commands/createMovedToPluginCommand.ts +1 -1
  8. package/tui/src/commands/extra-usage/extra-usage-core.ts +2 -4
  9. package/tui/src/commands/insights.ts +5 -4
  10. package/tui/src/commands/install-github-app/ApiKeyStep.tsx +1 -2
  11. package/tui/src/commands/install-github-app/CheckExistingSecretStep.tsx +3 -3
  12. package/tui/src/commands/install-github-app/ExistingWorkflowStep.tsx +3 -3
  13. package/tui/src/commands/install-github-app/InstallAppStep.tsx +1 -1
  14. package/tui/src/commands/install-github-app/SuccessStep.tsx +2 -10
  15. package/tui/src/commands/install-github-app/install-github-app.tsx +21 -22
  16. package/tui/src/commands/install-github-app/setupGitHubActions.ts +21 -27
  17. package/tui/src/commands/install-slack-app/install-slack-app.ts +1 -1
  18. package/tui/src/commands/mobile/mobile.tsx +3 -3
  19. package/tui/src/commands/stickers/stickers.ts +1 -1
  20. package/tui/src/commands/upgrade/upgrade.tsx +3 -4
  21. package/tui/src/components/ClaudeInChromeOnboarding.tsx +2 -3
  22. package/tui/src/components/Feedback.tsx +8 -2
  23. package/tui/src/components/FeedbackSurvey/submitTranscriptShare.ts +7 -2
  24. package/tui/src/components/Passes/Passes.tsx +1 -1
  25. package/tui/src/components/WorkflowMultiselectDialog.tsx +3 -3
  26. package/tui/src/components/messages/AssistantTextMessage.tsx +1 -2
  27. package/tui/src/constants/github-app.ts +26 -38
  28. package/tui/src/constants/oauth.ts +28 -59
  29. package/tui/src/constants/product.ts +6 -6
  30. package/tui/src/entrypoints/sdk/coreSchemas.ts +1 -1
  31. package/tui/src/services/analytics/firstPartyEventLogger.ts +0 -3
  32. package/tui/src/services/analytics/firstPartyEventLoggingExporter.ts +34 -12
  33. package/tui/src/services/analytics/growthbook.ts +17 -16
  34. package/tui/src/services/api/filesApi.ts +4 -14
  35. package/tui/src/services/api/metricsOptOut.ts +20 -1
  36. package/tui/src/services/mcp/channelNotification.ts +1 -1
  37. package/tui/src/services/mcp/officialRegistry.ts +5 -1
  38. package/tui/src/services/mcp/useManageMCPConnections.ts +1 -1
  39. package/tui/src/services/mcp/utils.ts +2 -2
  40. package/tui/src/services/tokenEstimation.ts +0 -1
  41. package/tui/src/tools/AgentTool/built-in/claudeCodeGuideAgent.ts +6 -6
  42. package/tui/src/tools/McpAuthTool/McpAuthTool.ts +1 -1
  43. package/tui/src/tools/RemoteTriggerTool/RemoteTriggerTool.ts +1 -1
  44. package/tui/src/tools/RemoteTriggerTool/prompt.ts +2 -2
  45. package/tui/src/tools/WebFetchTool/preapproved.ts +0 -4
  46. package/tui/src/tools/WebFetchTool/utils.ts +15 -11
  47. package/tui/src/upstreamproxy/upstreamproxy.ts +9 -15
  48. package/tui/src/utils/autoUpdater.ts +4 -1
  49. package/tui/src/utils/claudeInChrome/mcpServer.ts +1 -1
  50. package/tui/src/utils/claudeInChrome/setup.ts +1 -1
  51. package/tui/src/utils/claudeInChrome/toolRendering.tsx +1 -2
  52. package/tui/src/utils/desktopDeepLink.ts +18 -18
  53. package/tui/src/utils/fastMode.ts +1 -1
  54. package/tui/src/utils/http.ts +1 -6
  55. package/tui/src/utils/ide.ts +6 -5
  56. package/tui/src/utils/model/providers.ts +6 -10
  57. package/tui/src/utils/modelCost.ts +0 -2
  58. package/tui/src/utils/nativeInstaller/download.ts +15 -17
  59. package/tui/src/utils/plugins/installCounts.ts +4 -11
  60. package/tui/src/utils/plugins/officialMarketplaceGcs.ts +5 -18
  61. package/tui/src/utils/releaseNotes.ts +2 -2
  62. package/tui/src/utils/settings/types.ts +1 -1
  63. package/tui/src/utils/statusNoticeDefinitions.tsx +3 -3
  64. package/tui/src/utils/telemetry/bigqueryExporter.ts +20 -13
  65. 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
- // Default: prod, except when ANTHROPIC_BASE_URL is explicitly staging.
113
- // Overridable via tengu_1p_event_batch_config.baseUrl.
114
- const baseUrl =
115
- options.baseUrl ||
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
- // Retry any failed events from previous runs of this session (in background)
138
- void this.retryPreviousBatches()
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 a non-Anthropic proxy.
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
- if (host === 'api.anthropic.com') return undefined
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
- 'https://api.anthropic.com'
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 (default: https://api.anthropic.com) */
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 Anthropic Public Files API
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 = `https://api.anthropic.com/api/claude_code/organizations/metrics_enabled`
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,
@@ -223,7 +223,7 @@ export function gateChannelServer(
223
223
  return {
224
224
  action: 'skip',
225
225
  kind: 'auth',
226
- reason: 'channels requires claude.ai authentication (run /login)',
226
+ reason: 'channels requires UMMAYA remote authentication (run /login)',
227
227
  }
228
228
  }
229
229
 
@@ -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
- 'https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial',
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 claude.ai authentication · run /login'
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 'claude.ai'
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 'claude.ai config'
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 CLAUDE_CODE_DOCS_MAP_URL =
17
+ const UMMAYA_DOCS_MAP_URL =
18
18
  'https://ummaya-docs.pages.dev/llms.txt'
19
- const CDP_DOCS_MAP_URL = 'https://platform.claude.com/llms.txt'
19
+ const FRIENDLIAI_DOCS_MAP_URL = 'https://ummaya-docs.pages.dev/llms.txt'
20
20
 
21
- export const CLAUDE_CODE_GUIDE_AGENT_TYPE = 'claude-code-guide'
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** (${CLAUDE_CODE_DOCS_MAP_URL}): Fetch this for questions about the UMMAYA CLI tool, including:
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** (${CDP_DOCS_MAP_URL}): Fetch this for questions about building agents with the SDK, including:
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** (${CDP_DOCS_MAP_URL}): Fetch this for questions about the provider API, including:
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 claude.ai MCP connector. Ask the user to run /mcp and select "${serverName}" to authenticate.`,
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 claude.ai account. Run /login and try again.',
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 Claude Code agents (triggers) via the claude.ai CCR API. Auth is handled in-process the token never reaches the shell.'
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 claude.ai remote-trigger API. Use this instead of curl the OAuth token is added automatically in-process and never exposed.
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 claude.ai.`,
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 response = await axios.get(
184
- `https://api.anthropic.com/api/web/domain_info?domain=${encodeURIComponent(domain)}`,
185
- { timeout: DOMAIN_CHECK_TIMEOUT_MS },
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.ANTHROPIC_BASE_URL ??
121
- 'https://api.anthropic.com'
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
- 'https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases'
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/anthropics/claude-code/issues/new?labels=bug,claude-in-chrome'
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://clau.de/chrome/reconnect'
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://clau.de/chrome/tab/';
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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