ummaya 0.2.8 → 0.2.10

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 (63) 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/ExistingWorkflowStep.tsx +1 -1
  12. package/tui/src/commands/install-github-app/InstallAppStep.tsx +1 -1
  13. package/tui/src/commands/install-github-app/install-github-app.tsx +2 -2
  14. package/tui/src/commands/install-github-app/setupGitHubActions.ts +2 -2
  15. package/tui/src/commands/install-slack-app/install-slack-app.ts +1 -1
  16. package/tui/src/commands/mobile/mobile.tsx +3 -3
  17. package/tui/src/commands/stickers/stickers.ts +1 -1
  18. package/tui/src/commands/upgrade/upgrade.tsx +3 -4
  19. package/tui/src/components/ClaudeInChromeOnboarding.tsx +2 -3
  20. package/tui/src/components/Feedback.tsx +8 -2
  21. package/tui/src/components/FeedbackSurvey/submitTranscriptShare.ts +7 -2
  22. package/tui/src/components/Passes/Passes.tsx +1 -1
  23. package/tui/src/components/WorkflowMultiselectDialog.tsx +1 -1
  24. package/tui/src/components/messages/AssistantTextMessage.tsx +1 -2
  25. package/tui/src/constants/github-app.ts +19 -26
  26. package/tui/src/constants/oauth.ts +28 -59
  27. package/tui/src/constants/product.ts +6 -6
  28. package/tui/src/entrypoints/sdk/coreSchemas.ts +1 -1
  29. package/tui/src/services/analytics/firstPartyEventLogger.ts +0 -3
  30. package/tui/src/services/analytics/firstPartyEventLoggingExporter.ts +34 -12
  31. package/tui/src/services/analytics/growthbook.ts +17 -16
  32. package/tui/src/services/api/filesApi.ts +4 -14
  33. package/tui/src/services/api/metricsOptOut.ts +20 -1
  34. package/tui/src/services/mcp/channelNotification.ts +1 -1
  35. package/tui/src/services/mcp/officialRegistry.ts +5 -1
  36. package/tui/src/services/mcp/useManageMCPConnections.ts +1 -1
  37. package/tui/src/services/mcp/utils.ts +2 -2
  38. package/tui/src/services/tokenEstimation.ts +0 -1
  39. package/tui/src/tools/AgentTool/built-in/claudeCodeGuideAgent.ts +6 -6
  40. package/tui/src/tools/McpAuthTool/McpAuthTool.ts +1 -1
  41. package/tui/src/tools/RemoteTriggerTool/RemoteTriggerTool.ts +1 -1
  42. package/tui/src/tools/RemoteTriggerTool/prompt.ts +2 -2
  43. package/tui/src/tools/WebFetchTool/preapproved.ts +0 -4
  44. package/tui/src/tools/WebFetchTool/utils.ts +15 -11
  45. package/tui/src/upstreamproxy/upstreamproxy.ts +9 -15
  46. package/tui/src/utils/autoUpdater.ts +4 -1
  47. package/tui/src/utils/claudeInChrome/mcpServer.ts +1 -1
  48. package/tui/src/utils/claudeInChrome/setup.ts +1 -1
  49. package/tui/src/utils/claudeInChrome/toolRendering.tsx +1 -2
  50. package/tui/src/utils/desktopDeepLink.ts +18 -18
  51. package/tui/src/utils/fastMode.ts +1 -1
  52. package/tui/src/utils/http.ts +1 -5
  53. package/tui/src/utils/ide.ts +6 -5
  54. package/tui/src/utils/model/providers.ts +6 -10
  55. package/tui/src/utils/modelCost.ts +0 -2
  56. package/tui/src/utils/nativeInstaller/download.ts +15 -17
  57. package/tui/src/utils/plugins/installCounts.ts +4 -11
  58. package/tui/src/utils/plugins/officialMarketplaceGcs.ts +5 -18
  59. package/tui/src/utils/releaseNotes.ts +2 -2
  60. package/tui/src/utils/settings/types.ts +1 -1
  61. package/tui/src/utils/statusNoticeDefinitions.tsx +3 -3
  62. package/tui/src/utils/telemetry/bigqueryExporter.ts +20 -13
  63. package/uv.lock +1 -1
@@ -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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
@@ -28,12 +28,12 @@ function isDevMode(): boolean {
28
28
  }
29
29
 
30
30
  /**
31
- * Builds a deep link URL for Claude Desktop to resume a CLI session.
32
- * Format: claude://resume?session={sessionId}&cwd={cwd}
33
- * In dev mode: claude-dev://resume?session={sessionId}&cwd={cwd}
31
+ * Builds a deep link URL for UMMAYA Desktop to resume a CLI session.
32
+ * Format: ummaya://resume?session={sessionId}&cwd={cwd}
33
+ * In dev mode: ummaya-dev://resume?session={sessionId}&cwd={cwd}
34
34
  */
35
35
  function buildDesktopDeepLink(sessionId: string): string {
36
- const protocol = isDevMode() ? 'claude-dev' : 'claude'
36
+ const protocol = isDevMode() ? 'ummaya-dev' : 'ummaya'
37
37
  const url = new URL(`${protocol}://resume`)
38
38
  url.searchParams.set('session', sessionId)
39
39
  url.searchParams.set('cwd', getCwd())
@@ -41,9 +41,9 @@ function buildDesktopDeepLink(sessionId: string): string {
41
41
  }
42
42
 
43
43
  /**
44
- * Check if Claude Desktop app is installed.
45
- * On macOS, checks for /Applications/Claude.app.
46
- * On Linux, checks if xdg-open can handle claude:// protocol.
44
+ * Check if UMMAYA Desktop app is installed.
45
+ * On macOS, checks for /Applications/UMMAYA.app.
46
+ * On Linux, checks if xdg-open can handle ummaya:// protocol.
47
47
  * On Windows, checks if the protocol handler exists.
48
48
  * In dev mode, always returns true (assumes dev Desktop is running).
49
49
  */
@@ -56,22 +56,22 @@ async function isDesktopInstalled(): Promise<boolean> {
56
56
  const platform = process.platform
57
57
 
58
58
  if (platform === 'darwin') {
59
- // Check for Claude.app in /Applications
60
- return pathExists('/Applications/Claude.app')
59
+ // Check for UMMAYA.app in /Applications
60
+ return pathExists('/Applications/UMMAYA.app')
61
61
  } else if (platform === 'linux') {
62
- // Check if xdg-mime can find a handler for claude://
62
+ // Check if xdg-mime can find a handler for ummaya://
63
63
  // Note: xdg-mime returns exit code 0 even with no handler, so check stdout too
64
64
  const { code, stdout } = await execFileNoThrow('xdg-mime', [
65
65
  'query',
66
66
  'default',
67
- 'x-scheme-handler/claude',
67
+ 'x-scheme-handler/ummaya',
68
68
  ])
69
69
  return code === 0 && stdout.trim().length > 0
70
70
  } else if (platform === 'win32') {
71
71
  // On Windows, try to query the registry for the protocol handler
72
72
  const { code } = await execFileNoThrow('reg', [
73
73
  'query',
74
- 'HKEY_CLASSES_ROOT\\claude',
74
+ 'HKEY_CLASSES_ROOT\\ummaya',
75
75
  '/ve',
76
76
  ])
77
77
  return code === 0
@@ -81,7 +81,7 @@ async function isDesktopInstalled(): Promise<boolean> {
81
81
  }
82
82
 
83
83
  /**
84
- * Detect the installed Claude Desktop version.
84
+ * Detect the installed UMMAYA Desktop version.
85
85
  * On macOS, reads CFBundleShortVersionString from the app plist.
86
86
  * On Windows, finds the highest app-X.Y.Z directory in the Squirrel install.
87
87
  * Returns null if version cannot be determined.
@@ -92,7 +92,7 @@ async function getDesktopVersion(): Promise<string | null> {
92
92
  if (platform === 'darwin') {
93
93
  const { code, stdout } = await execFileNoThrow('defaults', [
94
94
  'read',
95
- '/Applications/Claude.app/Contents/Info.plist',
95
+ '/Applications/UMMAYA.app/Contents/Info.plist',
96
96
  'CFBundleShortVersionString',
97
97
  ])
98
98
  if (code !== 0) {
@@ -105,7 +105,7 @@ async function getDesktopVersion(): Promise<string | null> {
105
105
  if (!localAppData) {
106
106
  return null
107
107
  }
108
- const installDir = join(localAppData, 'AnthropicClaude')
108
+ const installDir = join(localAppData, 'UMMAYA')
109
109
  try {
110
110
  const entries = await readdir(installDir)
111
111
  const versions = entries
@@ -200,7 +200,7 @@ async function openDeepLink(deepLinkUrl: string): Promise<boolean> {
200
200
  }
201
201
 
202
202
  /**
203
- * Build and open a deep link to resume the current session in Claude Desktop.
203
+ * Build and open a deep link to resume the current session in UMMAYA Desktop.
204
204
  * Returns an object with success status and any error message.
205
205
  */
206
206
  export async function openCurrentSessionInDesktop(): Promise<{
@@ -216,7 +216,7 @@ export async function openCurrentSessionInDesktop(): Promise<{
216
216
  return {
217
217
  success: false,
218
218
  error:
219
- 'Claude Desktop is not installed. Install it from https://claude.ai/download',
219
+ 'UMMAYA Desktop is not installed. See https://ummaya-docs.pages.dev/ for setup instructions.',
220
220
  }
221
221
  }
222
222
 
@@ -227,7 +227,7 @@ export async function openCurrentSessionInDesktop(): Promise<{
227
227
  if (!opened) {
228
228
  return {
229
229
  success: false,
230
- error: 'Failed to open Claude Desktop. Please try opening it manually.',
230
+ error: 'Failed to open UMMAYA Desktop. Please try opening it manually.',
231
231
  deepLinkUrl,
232
232
  }
233
233
  }
@@ -90,7 +90,7 @@ export function getFastModeUnavailableReason(): string | null {
90
90
  !isInBundledMode() &&
91
91
  getFeatureValue_CACHED_MAY_BE_STALE('tengu_marble_sandcastle', false)
92
92
  ) {
93
- return 'Fast mode requires the native binary · Install from: https://claude.com/product/claude-code'
93
+ return 'Fast mode requires the native UMMAYA binary · Install from: https://ummaya-docs.pages.dev/'
94
94
  }
95
95
 
96
96
  // Not available in the SDK unless explicitly opted in via --settings.
@@ -49,12 +49,8 @@ export function getMCPUserAgent(): string {
49
49
  return `claude-code/${MACRO.VERSION}${suffix}`
50
50
  }
51
51
 
52
- // User-Agent for WebFetch requests to arbitrary sites. `Claude-User` is
53
- // Anthropic's publicly documented agent for user-initiated fetches (what site
54
- // operators match in robots.txt); the claude-code suffix lets them distinguish
55
- // local CLI traffic from claude.ai server-side fetches.
56
52
  export function getWebFetchUserAgent(): string {
57
- return `Claude-User (${getClaudeCodeUserAgent()}; +https://support.anthropic.com/)`
53
+ return `UMMAYA-User (${getClaudeCodeUserAgent()}; +https://ummaya-docs.pages.dev/)`
58
54
  }
59
55
 
60
56
  export type AuthHeaders = {
@@ -1419,9 +1419,11 @@ async function installFromArtifactory(command: string): Promise<string> {
1419
1419
  throw new Error('No artifactory auth token found in ~/.npmrc')
1420
1420
  }
1421
1421
 
1422
- // Fetch the version from artifactory
1423
- const versionUrl =
1424
- 'https://artifactory.infra.ant.dev/artifactory/armorcode-claude-code-internal/claude-vscode-releases/stable'
1422
+ const vsixBaseUrl = process.env.UMMAYA_VSCODE_EXTENSION_BASE_URL?.trim()
1423
+ if (!vsixBaseUrl) {
1424
+ throw new Error('UMMAYA_VSCODE_EXTENSION_BASE_URL is not configured')
1425
+ }
1426
+ const versionUrl = `${vsixBaseUrl}/stable`
1425
1427
 
1426
1428
  try {
1427
1429
  const versionResponse = await axios.get(versionUrl, {
@@ -1435,8 +1437,7 @@ async function installFromArtifactory(command: string): Promise<string> {
1435
1437
  throw new Error('No version found in artifactory response')
1436
1438
  }
1437
1439
 
1438
- // Download the .vsix file from artifactory
1439
- const vsixUrl = `https://artifactory.infra.ant.dev/artifactory/armorcode-claude-code-internal/claude-vscode-releases/${version}/claude-code.vsix`
1440
+ const vsixUrl = `${vsixBaseUrl}/${version}/ummaya.vsix`
1440
1441
  const tempVsixPath = join(
1441
1442
  os.tmpdir(),
1442
1443
  `claude-code-${version}-${Date.now()}.vsix`,
@@ -17,22 +17,18 @@ export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS
17
17
  return getAPIProvider() as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
18
18
  }
19
19
 
20
- /**
21
- * Check if ANTHROPIC_BASE_URL is a first-party Anthropic API URL.
22
- * Returns true if not set (default API) or points to api.anthropic.com
23
- * (or api-staging.anthropic.com for ant users).
24
- */
20
+ /** Check if ANTHROPIC_BASE_URL points to an operator-approved first-party API. */
25
21
  export function isFirstPartyAnthropicBaseUrl(): boolean {
26
22
  const baseUrl = process.env.ANTHROPIC_BASE_URL
27
23
  if (!baseUrl) {
28
- return true
24
+ return false
29
25
  }
30
26
  try {
31
27
  const host = new URL(baseUrl).host
32
- const allowedHosts = ['api.anthropic.com']
33
- if (process.env.USER_TYPE === 'ant') {
34
- allowedHosts.push('api-staging.anthropic.com')
35
- }
28
+ const allowedHosts = (process.env.UMMAYA_FIRST_PARTY_API_HOSTS ?? '')
29
+ .split(',')
30
+ .map(value => value.trim())
31
+ .filter(Boolean)
36
32
  return allowedHosts.includes(host)
37
33
  } catch {
38
34
  return false
@@ -23,7 +23,6 @@ import {
23
23
  type ModelShortName,
24
24
  } from './model/model.js'
25
25
 
26
- // @see https://platform.claude.com/docs/en/about-claude/pricing
27
26
  export type ModelCosts = {
28
27
  inputTokens: number
29
28
  outputTokens: number
@@ -99,7 +98,6 @@ export function getOpus46CostTier(fastMode: boolean): ModelCosts {
99
98
  }
100
99
 
101
100
  // @[MODEL LAUNCH]: Add a pricing entry for the new model below.
102
- // Costs from https://platform.claude.com/docs/en/about-claude/pricing
103
101
  // Web search cost: $10 per 1000 requests = $0.01 per request
104
102
  export const MODEL_COSTS: Record<ModelShortName, ModelCosts> = {
105
103
  [firstPartyNameToCanonical(CLAUDE_3_5_HAIKU_CONFIG.firstParty)]:
@@ -1,11 +1,3 @@
1
- /**
2
- * Download functionality for native installer
3
- *
4
- * Handles downloading Claude binaries from various sources:
5
- * - Artifactory NPM packages
6
- * - GCS bucket
7
- */
8
-
9
1
  import { feature } from 'bun:bundle'
10
2
  import axios from 'axios'
11
3
  import { createHash } from 'crypto'
@@ -23,9 +15,10 @@ import { jsonStringify, writeFileSync_DEPRECATED } from '../slowOperations.js'
23
15
  import { getBinaryName, getPlatform } from './installer.js'
24
16
 
25
17
  const GCS_BUCKET_URL =
26
- 'https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases'
18
+ process.env.UMMAYA_NATIVE_DOWNLOAD_BASE_URL?.trim()
27
19
  export const ARTIFACTORY_REGISTRY_URL =
28
- 'https://artifactory.infra.ant.dev/artifactory/api/npm/npm-all/'
20
+ process.env.UMMAYA_NATIVE_NPM_REGISTRY_URL?.trim() ??
21
+ 'https://registry.npmjs.org/'
29
22
 
30
23
  export async function getLatestVersionFromArtifactory(
31
24
  tag: string = 'latest',
@@ -144,7 +137,9 @@ export async function getLatestVersion(
144
137
  return getLatestVersionFromArtifactory(npmTag)
145
138
  }
146
139
 
147
- // Use GCS for external users
140
+ if (!GCS_BUCKET_URL) {
141
+ throw new Error('UMMAYA_NATIVE_DOWNLOAD_BASE_URL is not configured')
142
+ }
148
143
  return getLatestVersionFromBinaryRepo(channel, GCS_BUCKET_URL)
149
144
  }
150
145
 
@@ -488,11 +483,12 @@ export async function downloadVersion(
488
483
  version: string,
489
484
  stagingPath: string,
490
485
  ): Promise<'npm' | 'binary'> {
491
- // Test-fixture versions route to the private sentinel bucket. DCE'd in all
492
- // shipped builds — the string 'claude-code-ci-sentinel' and the gcloud call
493
- // never exist in compiled binaries. Same gcloud-token pattern as
494
- // remoteSkillLoader.ts:175-195.
495
486
  if (feature('ALLOW_TEST_VERSIONS') && /^99\.99\./.test(version)) {
487
+ const testDownloadBaseUrl =
488
+ process.env.UMMAYA_NATIVE_TEST_DOWNLOAD_BASE_URL?.trim()
489
+ if (!testDownloadBaseUrl) {
490
+ throw new Error('UMMAYA_NATIVE_TEST_DOWNLOAD_BASE_URL is not configured')
491
+ }
496
492
  const { stdout } = await execFileNoThrowWithCwd('gcloud', [
497
493
  'auth',
498
494
  'print-access-token',
@@ -500,7 +496,7 @@ export async function downloadVersion(
500
496
  await downloadVersionFromBinaryRepo(
501
497
  version,
502
498
  stagingPath,
503
- 'https://storage.googleapis.com/claude-code-ci-sentinel',
499
+ testDownloadBaseUrl,
504
500
  { headers: { Authorization: `Bearer ${stdout.trim()}` } },
505
501
  )
506
502
  return 'binary'
@@ -512,7 +508,9 @@ export async function downloadVersion(
512
508
  return 'npm'
513
509
  }
514
510
 
515
- // Use GCS for external users
511
+ if (!GCS_BUCKET_URL) {
512
+ throw new Error('UMMAYA_NATIVE_DOWNLOAD_BASE_URL is not configured')
513
+ }
516
514
  await downloadVersionFromBinaryRepo(version, stagingPath, GCS_BUCKET_URL)
517
515
  return 'binary'
518
516
  }
@@ -1,13 +1,3 @@
1
- /**
2
- * Plugin install counts data layer
3
- *
4
- * This module fetches and caches plugin install counts from the official
5
- * Claude plugins statistics repository. The cache is refreshed if older
6
- * than 24 hours.
7
- *
8
- * Cache location: ~/.claude/plugins/install-counts-cache.json
9
- */
10
-
11
1
  import axios from 'axios'
12
2
  import { randomBytes } from 'crypto'
13
3
  import { readFile, rename, unlink, writeFile } from 'fs/promises'
@@ -23,7 +13,7 @@ import { getPluginsDirectory } from './pluginDirectories.js'
23
13
  const INSTALL_COUNTS_CACHE_VERSION = 1
24
14
  const INSTALL_COUNTS_CACHE_FILENAME = 'install-counts-cache.json'
25
15
  const INSTALL_COUNTS_URL =
26
- 'https://raw.githubusercontent.com/anthropics/claude-plugins-official/refs/heads/stats/stats/plugin-installs.json'
16
+ process.env.UMMAYA_PLUGIN_INSTALL_COUNTS_URL?.trim()
27
17
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours in milliseconds
28
18
 
29
19
  /**
@@ -184,6 +174,9 @@ async function saveInstallCountsCache(
184
174
  async function fetchInstallCountsFromGitHub(): Promise<
185
175
  Array<{ plugin: string; unique_installs: number }>
186
176
  > {
177
+ if (!INSTALL_COUNTS_URL) {
178
+ return []
179
+ }
187
180
  logForDebugging(`Fetching install counts from ${INSTALL_COUNTS_URL}`)
188
181
 
189
182
  const started = performance.now()