ummaya 0.2.9 → 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.
- 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/ExistingWorkflowStep.tsx +1 -1
- package/tui/src/commands/install-github-app/InstallAppStep.tsx +1 -1
- package/tui/src/commands/install-github-app/install-github-app.tsx +2 -2
- package/tui/src/commands/install-github-app/setupGitHubActions.ts +2 -2
- 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 +1 -1
- package/tui/src/components/messages/AssistantTextMessage.tsx +1 -2
- package/tui/src/constants/github-app.ts +19 -26
- 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 -5
- 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
|
@@ -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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
|
|
@@ -28,12 +28,12 @@ function isDevMode(): boolean {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Builds a deep link URL for
|
|
32
|
-
* Format:
|
|
33
|
-
* In dev mode:
|
|
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() ? '
|
|
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
|
|
45
|
-
* On macOS, checks for /Applications/
|
|
46
|
-
* On Linux, checks if xdg-open can handle
|
|
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
|
|
60
|
-
return pathExists('/Applications/
|
|
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
|
|
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/
|
|
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\\
|
|
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
|
|
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/
|
|
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, '
|
|
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
|
|
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
|
-
'
|
|
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
|
|
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://
|
|
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.
|
package/tui/src/utils/http.ts
CHANGED
|
@@ -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 `
|
|
53
|
+
return `UMMAYA-User (${getClaudeCodeUserAgent()}; +https://ummaya-docs.pages.dev/)`
|
|
58
54
|
}
|
|
59
55
|
|
|
60
56
|
export type AuthHeaders = {
|
package/tui/src/utils/ide.ts
CHANGED
|
@@ -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
|
-
|
|
1423
|
-
|
|
1424
|
-
'
|
|
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
|
-
|
|
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
|
|
24
|
+
return false
|
|
29
25
|
}
|
|
30
26
|
try {
|
|
31
27
|
const host = new URL(baseUrl).host
|
|
32
|
-
const allowedHosts =
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
18
|
+
process.env.UMMAYA_NATIVE_DOWNLOAD_BASE_URL?.trim()
|
|
27
19
|
export const ARTIFACTORY_REGISTRY_URL =
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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()
|