@alisaitteke/photoshop-mcp 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +162 -19
  2. package/dist/analytics/beta-telemetry.d.ts +11 -0
  3. package/dist/analytics/beta-telemetry.d.ts.map +1 -0
  4. package/dist/analytics/beta-telemetry.js +47 -0
  5. package/dist/analytics/beta-telemetry.js.map +1 -0
  6. package/dist/analytics/config.d.ts +6 -0
  7. package/dist/analytics/config.d.ts.map +1 -0
  8. package/dist/analytics/config.js +23 -0
  9. package/dist/analytics/config.js.map +1 -0
  10. package/dist/analytics/context.d.ts +2 -0
  11. package/dist/analytics/context.d.ts.map +1 -0
  12. package/dist/analytics/context.js +13 -0
  13. package/dist/analytics/context.js.map +1 -0
  14. package/dist/analytics/events.d.ts +3 -0
  15. package/dist/analytics/events.d.ts.map +1 -0
  16. package/dist/analytics/events.js +75 -0
  17. package/dist/analytics/events.js.map +1 -0
  18. package/dist/analytics/identity.d.ts +9 -0
  19. package/dist/analytics/identity.d.ts.map +1 -0
  20. package/dist/analytics/identity.js +166 -0
  21. package/dist/analytics/identity.js.map +1 -0
  22. package/dist/analytics/index.d.ts +10 -0
  23. package/dist/analytics/index.d.ts.map +1 -0
  24. package/dist/analytics/index.js +31 -0
  25. package/dist/analytics/index.js.map +1 -0
  26. package/dist/analytics/launch-method.d.ts +4 -0
  27. package/dist/analytics/launch-method.d.ts.map +1 -0
  28. package/dist/analytics/launch-method.js +33 -0
  29. package/dist/analytics/launch-method.js.map +1 -0
  30. package/dist/analytics/noop.d.ts +6 -0
  31. package/dist/analytics/noop.d.ts.map +1 -0
  32. package/dist/analytics/noop.js +9 -0
  33. package/dist/analytics/noop.js.map +1 -0
  34. package/dist/analytics/posthog-node.d.ts +9 -0
  35. package/dist/analytics/posthog-node.d.ts.map +1 -0
  36. package/dist/analytics/posthog-node.js +36 -0
  37. package/dist/analytics/posthog-node.js.map +1 -0
  38. package/dist/analytics/provider.d.ts +5 -0
  39. package/dist/analytics/provider.d.ts.map +1 -0
  40. package/dist/analytics/provider.js +27 -0
  41. package/dist/analytics/provider.js.map +1 -0
  42. package/dist/analytics/types.d.ts +23 -0
  43. package/dist/analytics/types.d.ts.map +1 -0
  44. package/dist/analytics/types.js +2 -0
  45. package/dist/analytics/types.js.map +1 -0
  46. package/dist/core/server.d.ts +1 -0
  47. package/dist/core/server.d.ts.map +1 -1
  48. package/dist/core/server.js +3 -0
  49. package/dist/core/server.js.map +1 -1
  50. package/dist/index.js +31 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/platform/macos-detector.js +4 -4
  53. package/dist/platform/macos-detector.js.map +1 -1
  54. package/dist/platform/macos-executor.js +1 -1
  55. package/dist/platform/macos-executor.js.map +1 -1
  56. package/dist/platform/windows-detector.js +4 -4
  57. package/dist/platform/windows-detector.js.map +1 -1
  58. package/dist/platform/windows-executor.js +2 -2
  59. package/dist/platform/windows-executor.js.map +1 -1
  60. package/dist/ui/agent/action-plan.d.ts +19 -0
  61. package/dist/ui/agent/action-plan.d.ts.map +1 -0
  62. package/dist/ui/agent/action-plan.js +552 -0
  63. package/dist/ui/agent/action-plan.js.map +1 -0
  64. package/dist/ui/agent/api-key.d.ts +19 -0
  65. package/dist/ui/agent/api-key.d.ts.map +1 -0
  66. package/dist/ui/agent/api-key.js +131 -0
  67. package/dist/ui/agent/api-key.js.map +1 -0
  68. package/dist/ui/agent/claude-account.d.ts +16 -0
  69. package/dist/ui/agent/claude-account.d.ts.map +1 -0
  70. package/dist/ui/agent/claude-account.js +164 -0
  71. package/dist/ui/agent/claude-account.js.map +1 -0
  72. package/dist/ui/agent/gemini-account.d.ts +15 -0
  73. package/dist/ui/agent/gemini-account.d.ts.map +1 -0
  74. package/dist/ui/agent/gemini-account.js +222 -0
  75. package/dist/ui/agent/gemini-account.js.map +1 -0
  76. package/dist/ui/agent/mcp-transport.d.ts +9 -0
  77. package/dist/ui/agent/mcp-transport.d.ts.map +1 -0
  78. package/dist/ui/agent/mcp-transport.js +34 -0
  79. package/dist/ui/agent/mcp-transport.js.map +1 -0
  80. package/dist/ui/agent/shared.d.ts +55 -0
  81. package/dist/ui/agent/shared.d.ts.map +1 -0
  82. package/dist/ui/agent/shared.js +121 -0
  83. package/dist/ui/agent/shared.js.map +1 -0
  84. package/dist/ui/agent.d.ts +11 -26
  85. package/dist/ui/agent.d.ts.map +1 -1
  86. package/dist/ui/agent.js +84 -169
  87. package/dist/ui/agent.js.map +1 -1
  88. package/dist/ui/cli.js +10 -1
  89. package/dist/ui/cli.js.map +1 -1
  90. package/dist/ui/config.d.ts +12 -0
  91. package/dist/ui/config.d.ts.map +1 -1
  92. package/dist/ui/config.js +11 -0
  93. package/dist/ui/config.js.map +1 -1
  94. package/dist/ui/providers/anthropic.d.ts.map +1 -1
  95. package/dist/ui/providers/anthropic.js +24 -0
  96. package/dist/ui/providers/anthropic.js.map +1 -1
  97. package/dist/ui/providers/cli-utils.d.ts +12 -0
  98. package/dist/ui/providers/cli-utils.d.ts.map +1 -0
  99. package/dist/ui/providers/cli-utils.js +46 -0
  100. package/dist/ui/providers/cli-utils.js.map +1 -0
  101. package/dist/ui/providers/google.d.ts.map +1 -1
  102. package/dist/ui/providers/google.js +18 -0
  103. package/dist/ui/providers/google.js.map +1 -1
  104. package/dist/ui/providers/openai.d.ts.map +1 -1
  105. package/dist/ui/providers/openai.js +1 -0
  106. package/dist/ui/providers/openai.js.map +1 -1
  107. package/dist/ui/providers/openrouter.d.ts.map +1 -1
  108. package/dist/ui/providers/openrouter.js +1 -0
  109. package/dist/ui/providers/openrouter.js.map +1 -1
  110. package/dist/ui/providers/registry.d.ts +1 -1
  111. package/dist/ui/providers/registry.d.ts.map +1 -1
  112. package/dist/ui/providers/types.d.ts +11 -0
  113. package/dist/ui/providers/types.d.ts.map +1 -1
  114. package/dist/ui/server.d.ts.map +1 -1
  115. package/dist/ui/server.js +167 -10
  116. package/dist/ui/server.js.map +1 -1
  117. package/dist/ui/store/chats.d.ts +11 -0
  118. package/dist/ui/store/chats.d.ts.map +1 -1
  119. package/dist/ui/store/chats.js.map +1 -1
  120. package/package.json +7 -3
  121. package/web/dist/assets/index-BgiXf1Jy.css +1 -0
  122. package/web/dist/assets/index-BuJJLllx.js +249 -0
  123. package/web/dist/index.html +9 -3
  124. package/web/dist/assets/index-4AnFityQ.css +0 -1
  125. package/web/dist/assets/index-CFTrtoOu.js +0 -138
package/README.md CHANGED
@@ -1,21 +1,24 @@
1
1
  # Photoshop MCP Server
2
2
 
3
- *v1.1+ — recipe workflows, fewer round-trips, snappier sessions.*
3
+ *v1.1+ — recipe workflows, fewer round-trips, snappier sessions. Standalone UI ships **Action Plan (beta)** for plan-then-execute runs.*
4
4
 
5
5
  > **Note:** This is an unofficial, community-maintained project and is not affiliated with or endorsed by Adobe Inc.
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@alisaitteke/photoshop-mcp.svg)](https://www.npmjs.com/package/@alisaitteke/photoshop-mcp)
8
+ [![Action Plan](https://img.shields.io/badge/Action%20Plan-beta-amber.svg)](#action-plan-beta)
8
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
10
11
  [![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20macOS-lightgrey.svg)]()
11
12
 
12
- A Model Context Protocol (MCP) server that enables AI assistants like Claude and Cursor to control Adobe Photoshop programmatically. This allows you to create designs, manipulate images, and automate Photoshop workflows through natural language commands while working in your IDE.
13
+ A Model Context Protocol (MCP) server that enables AI assistants like Claude and Cursor to control Adobe Photoshop programmatically. This allows you to create designs, manipulate images, and automate Photoshop workflows through natural language commands while working in your IDE — or through the bundled **standalone web UI**, which supports both API keys and CLI subscription accounts (Claude Code / Gemini CLI). The UI also offers an opt-in **Action Plan (beta)** mode that plans every Photoshop step in one LLM call, then runs them in a single pass.
13
14
 
14
15
  ## 🖥️ Standalone UI (no IDE required)
15
16
 
16
17
  Don't want to wire this into Claude Desktop or Cursor? The same package ships a
17
18
  fully local web UI that lets you chat with an AI model and drive Photoshop
18
- through this MCP server underneath.
19
+ through this MCP server underneath. Connect with a provider API key **or**, for
20
+ Anthropic and Google, reuse the OAuth session from **Claude Code** or **Gemini
21
+ CLI** — no separate API key required.
19
22
 
20
23
  ![Standalone UI Screenshot](./images/frame_generic_light.png)
21
24
 
@@ -28,26 +31,77 @@ default browser opens the chat UI automatically.
28
31
 
29
32
  ### Supported providers
30
33
 
31
- Pick any of the following on first launch — bring your own API key:
34
+ Pick any of the following on first launch — use an API key **or** your existing
35
+ CLI subscription account (Anthropic and Google):
32
36
 
33
- | Provider | Models | Get a key |
34
- |---|---|---|
35
- | **Anthropic** | Claude Sonnet / Opus / Haiku | [console.anthropic.com](https://console.anthropic.com/settings/keys) |
36
- | **OpenAI** | GPT-5, GPT-4.1, o-series | [platform.openai.com](https://platform.openai.com/api-keys) |
37
- | **Google** | Gemini 2.5 Pro / Flash / Flash-Lite | [aistudio.google.com](https://aistudio.google.com/apikey) |
38
- | **OpenRouter** | 100+ models from any provider | [openrouter.ai](https://openrouter.ai/keys) |
37
+ | Provider | Models | API key | CLI account |
38
+ |---|---|---|---|
39
+ | **Anthropic** | Claude Sonnet / Opus / Haiku | [console.anthropic.com](https://console.anthropic.com/settings/keys) | `npm i -g @anthropic-ai/claude-code` → `claude auth login` |
40
+ | **OpenAI** | GPT-5, GPT-4.1, o-series | [platform.openai.com](https://platform.openai.com/api-keys) | — |
41
+ | **Google** | Gemini 2.5 Pro / Flash / Flash-Lite | [aistudio.google.com](https://aistudio.google.com/apikey) | `npm i -g @google/gemini-cli` → `gemini auth login` |
42
+ | **OpenRouter** | 100+ models from any provider | [openrouter.ai](https://openrouter.ai/keys) | — |
43
+
44
+ ### Authentication modes
45
+
46
+ - **`api_key` (default)** — Vercel AI SDK + your provider API key. Usage is billed
47
+ per token at API rates; the UI shows estimated cost per chat.
48
+ - **`cli_account`** — Uses your local Claude Code or Gemini CLI OAuth session.
49
+ No API key is stored; the UI probes `claude auth status` / `gemini` headless
50
+ to verify login. Usage counts against your **subscription quota**, not API
51
+ billing — the status bar shows "Included in subscription".
52
+
53
+ You can switch auth method per provider in Settings without losing the other
54
+ credential (e.g. keep an API key while trying CLI account, then switch back).
55
+
56
+ ### Action Plan (beta)
57
+
58
+ An optional execution mode in the standalone web UI for **API key auth only**
59
+ (`cli_account` always uses the default agentic flow). Turn it on with the
60
+ **Action Plan** toggle next to the model selector in the composer.
61
+
62
+ Instead of a per-step ReAct loop (model → tool → model → tool …), Action Plan:
63
+
64
+ 1. Makes **one** planning LLM call that outputs an ordered todo list of
65
+ Photoshop MCP tool calls with parameters.
66
+ 2. Executes those tools **directly** in sequence — no extra model round-trips
67
+ between steps.
68
+ 3. On a failed step or unresolved dependency, runs a bounded **repair** loop
69
+ (re-plans only the remaining steps, up to 3 times).
70
+
71
+ The plan appears as a live todo list above the tool-call cards, with per-step
72
+ status (`pending` → `running` → `done` / `error`). Plans are persisted in chat
73
+ history so they survive reload. The toggle is off by default; the existing
74
+ agentic flow is unchanged when Action Plan is disabled.
75
+
76
+ Good for multi-step prompts such as *"remove the background and export for web"*
77
+ where you want fewer model calls and faster end-to-end execution.
39
78
 
40
79
  ### What happens on first launch
41
80
 
42
- 1. Pick a provider and paste your API key.
43
- 2. The key is validated against the provider, then stored locally at
44
- `~/.photoshop-mcp/data.db` (SQLite, `chmod 600`). It never leaves your
45
- machine.
81
+ 1. Pick a provider and choose **API key** or **Uses your account**.
82
+ 2. Validate the key or check the CLI connection. Config is stored locally at
83
+ `~/.photoshop-mcp/data.db` (SQLite, `chmod 600`). API keys never leave your
84
+ machine; CLI mode inherits OAuth from `~/.claude/` or `~/.gemini/`.
46
85
  3. Type natural-language prompts. The UI streams the model's reply, runs
47
86
  Photoshop tool calls in real time, and renders each tool call as an
48
87
  inspectable card (input + result).
49
- 4. Switch provider or model anytime from the model selector — chats, costs and
50
- tool history are persisted across sessions.
88
+ 4. Switch provider, auth method, or model anytime from Settings / model selector
89
+ — chats, costs and tool history are persisted across sessions.
90
+
91
+ ### Switching auth method later
92
+
93
+ Open **Settings** from the sidebar at any time:
94
+
95
+ | Action | API key mode | CLI account mode |
96
+ |---|---|---|
97
+ | Set up | Paste key → **Save** | Install CLI → `auth login` → **Check connection** |
98
+ | Switch away | Choose **API key** — stored key is kept | Choose **Uses your account** — key is not deleted |
99
+ | Custom binary | — | Optional **CLI path** if `claude` / `gemini` is not on `PATH` |
100
+ | Cost display | Per-token estimate in status bar | **Included in subscription** badge |
101
+
102
+ Auth method is stored per provider in `~/.photoshop-mcp/data.db` (`authMethod`:
103
+ `api_key` or `cli_account`). Existing configs without `authMethod` default to
104
+ `api_key` and keep working unchanged.
51
105
 
52
106
  ### CLI flags
53
107
 
@@ -60,9 +114,15 @@ photoshop-mcp-ui [--port 5174] [--host 127.0.0.1] [--no-open]
60
114
  - The agent is restricted to Photoshop MCP tools only — built-in shell, file
61
115
  and web tools are disabled.
62
116
  - Tech stack: Vue 3 + Tailwind v4 + [shadcn-vue](https://www.shadcn-vue.com/)
63
- on the frontend; [Hono](https://hono.dev/) + the [Vercel AI SDK](https://sdk.vercel.ai/)
64
- on the backend. The agent loop talks to this same Photoshop MCP server over
65
- STDIO — same code path as the IDE integration.
117
+ on the frontend; [Hono](https://hono.dev/) on the backend. API-key mode uses
118
+ the [Vercel AI SDK](https://sdk.vercel.ai/); CLI account mode uses the
119
+ [Claude Agent SDK](https://code.claude.com/docs/en/agent-sdk/mcp) (Anthropic)
120
+ or Gemini CLI headless `stream-json` (Google). All paths talk to this same
121
+ Photoshop MCP server over STDIO.
122
+ - **CLI account limitations:** Gemini headless may open a new session each turn
123
+ (history is prepended to the prompt). Anthropic CLI account consumes
124
+ subscription quota. OAuth login is macOS-first (`claude auth login` /
125
+ `gemini auth login` in Terminal).
66
126
 
67
127
  ---
68
128
 
@@ -388,6 +448,10 @@ Never guess — read get_state after a failure and propose the next single step.
388
448
 
389
449
  ## Features
390
450
 
451
+ - **Standalone web UI** — local chat interface (`photoshop-mcp-ui`); API key or CLI
452
+ subscription auth per provider (Anthropic, Google)
453
+ - **Action Plan (beta)** — opt-in plan-then-execute mode in the web UI (API key
454
+ only): one planning call, direct tool execution, bounded repair on failure
391
455
  - **Works on both Windows and macOS**
392
456
  - **Supports Photoshop 2012-2025+**
393
457
  - **ExtendScript API**: Universal compatibility via AppleScript/COM automation
@@ -461,6 +525,10 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
461
525
 
462
526
  - `PHOTOSHOP_PATH`: (Optional) Specify custom Photoshop installation path
463
527
  - `LOG_LEVEL`: Logging level (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR)
528
+ - `POSTHOG_DISABLED`: Set to `1` or `true` to disable anonymous usage analytics entirely
529
+ - `POSTHOG_KEY`: (Optional) Override the PostHog project key used for analytics
530
+ - `POSTHOG_API_HOST`: (Optional) PostHog ingest host (default: `https://a.alisait.com`)
531
+ - `POSTHOG_UI_HOST`: (Optional) PostHog UI host for dashboard links (default: `https://eu.posthog.com`)
464
532
 
465
533
  ## Available Tools
466
534
 
@@ -531,6 +599,9 @@ This context helps AI assistants remember what document and layer they're workin
531
599
  - Uses AppleScript/OSA for Photoshop communication
532
600
  - Spotlight-based auto-detection
533
601
  - Supports multiple Photoshop versions installed simultaneously
602
+ - **CLI account auth** (standalone UI) is macOS-first: run `claude auth login` /
603
+ `gemini auth login` in Terminal; credentials live under `~/.claude/` and
604
+ `~/.gemini/`
534
605
 
535
606
  ## Supported Photoshop Versions
536
607
 
@@ -543,6 +614,16 @@ This context helps AI assistants remember what document and layer they're workin
543
614
  Common connection, scripting, and logging issues:
544
615
  [`docs/troubleshooting.md`](docs/troubleshooting.md).
545
616
 
617
+ ### Standalone UI — CLI account auth
618
+
619
+ | Symptom | Likely cause | Fix |
620
+ |---|---|---|
621
+ | `cli_not_found` | Claude Code / Gemini CLI not installed | `npm i -g @anthropic-ai/claude-code` or `npm i -g @google/gemini-cli` |
622
+ | `not_authenticated` | No OAuth session | Run `claude auth login` or `gemini auth login` in Terminal |
623
+ | `claude` / `gemini` not on `PATH` | Custom install location | Settings → **CLI path** → **Check connection** |
624
+ | Chat works in IDE but not UI (CLI mode) | OAuth tokens are CLI-only | Use **CLI account** in UI; API keys and CLI sessions are separate |
625
+ | Gemini multi-turn feels forgetful | Headless CLI may start a fresh session each turn | Known limitation; history is prepended to the prompt (MVP) |
626
+
546
627
  ## Development
547
628
 
548
629
  From-source setup, build, lint, integration tests (with latest results), and usage examples:
@@ -561,6 +642,68 @@ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) before
561
642
 
562
643
  MIT
563
644
 
645
+ ## Anonymous Usage Analytics
646
+
647
+ This project collects **anonymous, aggregated usage events** to understand how the
648
+ MCP server and standalone UI are used and to improve the product. Analytics are
649
+ **enabled by default** and can be turned off at any time.
650
+
651
+ ### What we collect
652
+
653
+ - App version, operating system, and Node.js version
654
+ - Startup signals (MCP server or UI server started, Photoshop detected or not)
655
+ - Setup funnel events (provider chosen, auth method, validation success/failure
656
+ codes — not credentials)
657
+ - Generic UI events (app loaded, onboarding completed)
658
+
659
+ Events use a random anonymous identifier stored locally at
660
+ `~/.photoshop-mcp/` (SQLite `kv` table and/or `analytics-store.json`). We do
661
+ **not** call `identify()` — no person profiles are created for anonymous usage
662
+ (`person_profiles: identified_only`).
663
+
664
+ ### What we do **not** collect (unless you opt into beta team sharing)
665
+
666
+ - API keys or OAuth tokens
667
+ - Chat messages, prompts, or model responses **by default**
668
+ - Photoshop document or layer names, file paths, or image content
669
+ - CLI account labels, email addresses, or other account identifiers
670
+ - Tool call arguments or results
671
+
672
+ ### Beta team content sharing (opt-in)
673
+
674
+ On first launch of the standalone UI, you are asked whether you want to **join the
675
+ beta team**. This is separate from anonymous usage analytics above.
676
+
677
+ If you accept:
678
+
679
+ - Your **prompts**, **assistant responses**, **reasoning text**, and **tool names**
680
+ (not arguments or results) may be sent to PostHog after each chat turn
681
+ - Content is truncated for very long messages
682
+ - Requires anonymous analytics to remain enabled
683
+
684
+ If you decline, no chat content is logged. You can change this later in
685
+ **Settings → General → Privacy → Beta team content sharing**.
686
+
687
+ Existing installs that have not answered yet are prompted once on the next launch.
688
+
689
+ ### Processor and hosting
690
+
691
+ Analytics are processed by [PostHog](https://posthog.com/). Events are sent to a
692
+ managed reverse proxy at `https://a.alisait.com`; the PostHog project UI is
693
+ hosted in the EU (`https://eu.posthog.com`). See the
694
+ [PostHog privacy policy](https://posthog.com/privacy) for how PostHog handles
695
+ data on their side.
696
+
697
+ ### How to opt out
698
+
699
+ 1. **Standalone UI:** Settings → General → Privacy → set **Anonymous usage
700
+ analytics** to **Off** (also disables beta content sharing).
701
+ 2. **Beta content only:** Settings → General → Privacy → set **Beta team content
702
+ sharing** to **Off** (anonymous analytics can stay on).
703
+ 3. **Environment variable:** set `POSTHOG_DISABLED=1` before starting
704
+ `photoshop-mcp` or `photoshop-mcp-ui` (disables all analytics for that
705
+ process and persists opt-out in local storage).
706
+
564
707
  ## Acknowledgments
565
708
 
566
709
  - Built with the [Model Context Protocol SDK](https://github.com/modelcontextprotocol/sdk)
@@ -0,0 +1,11 @@
1
+ export interface BetaChatTurnInput {
2
+ providerId: string;
3
+ model: string;
4
+ authMethod: string;
5
+ userPrompt: string;
6
+ assistantText: string;
7
+ assistantReasoning?: string;
8
+ toolNames: string[];
9
+ }
10
+ export declare function captureBetaChatTurn(input: BetaChatTurnInput): void;
11
+ //# sourceMappingURL=beta-telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beta-telemetry.d.ts","sourceRoot":"","sources":["../../src/analytics/beta-telemetry.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAYD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAgClE"}
@@ -0,0 +1,47 @@
1
+ import { isAnalyticsEnabled, isBetaTelemetryOptIn } from './identity.js';
2
+ import { getLaunchMethod } from './launch-method.js';
3
+ import { getAnalytics } from './provider.js';
4
+ const MAX_TEXT_LENGTH = 16_000;
5
+ const TRUNCATION_SUFFIX = '…[truncated]';
6
+ function truncateText(value) {
7
+ if (!value)
8
+ return undefined;
9
+ if (value.length <= MAX_TEXT_LENGTH)
10
+ return value;
11
+ return `${value.slice(0, MAX_TEXT_LENGTH)}${TRUNCATION_SUFFIX}`;
12
+ }
13
+ function uniqueToolNames(names) {
14
+ return [...new Set(names.map((name) => name.trim()).filter(Boolean))];
15
+ }
16
+ export function captureBetaChatTurn(input) {
17
+ if (!isAnalyticsEnabled() || !isBetaTelemetryOptIn())
18
+ return;
19
+ const assistantText = input.assistantText.trim();
20
+ const assistantReasoning = input.assistantReasoning?.trim() ?? '';
21
+ const hasAssistantContent = assistantText.length > 0 ||
22
+ assistantReasoning.length > 0 ||
23
+ input.toolNames.length > 0;
24
+ if (!hasAssistantContent)
25
+ return;
26
+ const toolNames = uniqueToolNames(input.toolNames);
27
+ getAnalytics().capture({
28
+ name: 'beta_chat_turn',
29
+ properties: {
30
+ os: process.platform,
31
+ arch: process.arch,
32
+ node_version: process.version,
33
+ launch_method: getLaunchMethod(),
34
+ event_source: 'server',
35
+ provider_id: input.providerId,
36
+ auth_method: input.authMethod,
37
+ model: input.model,
38
+ user_prompt: truncateText(input.userPrompt.trim()) ?? '',
39
+ assistant_text: truncateText(assistantText) ?? '',
40
+ ...(assistantReasoning
41
+ ? { assistant_reasoning: truncateText(assistantReasoning) }
42
+ : {}),
43
+ ...(toolNames.length > 0 ? { tool_names: toolNames.join(',') } : {}),
44
+ },
45
+ });
46
+ }
47
+ //# sourceMappingURL=beta-telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beta-telemetry.js","sourceRoot":"","sources":["../../src/analytics/beta-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAYzC,SAAS,YAAY,CAAC,KAAyB;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,IAAI,eAAe;QAAE,OAAO,KAAK,CAAC;IAClD,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,iBAAiB,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,eAAe,CAAC,KAAe;IACtC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAwB;IAC1D,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,oBAAoB,EAAE;QAAE,OAAO;IAE7D,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;IACjD,MAAM,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAClE,MAAM,mBAAmB,GACvB,aAAa,CAAC,MAAM,GAAG,CAAC;QACxB,kBAAkB,CAAC,MAAM,GAAG,CAAC;QAC7B,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,mBAAmB;QAAE,OAAO;IAEjC,MAAM,SAAS,GAAG,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAEnD,YAAY,EAAE,CAAC,OAAO,CAAC;QACrB,IAAI,EAAE,gBAAgB;QACtB,UAAU,EAAE;YACV,EAAE,EAAE,OAAO,CAAC,QAAQ;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,YAAY,EAAE,OAAO,CAAC,OAAO;YAC7B,aAAa,EAAE,eAAe,EAAE;YAChC,YAAY,EAAE,QAAQ;YACtB,WAAW,EAAE,KAAK,CAAC,UAAU;YAC7B,WAAW,EAAE,KAAK,CAAC,UAAU;YAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,WAAW,EAAE,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;YACxD,cAAc,EAAE,YAAY,CAAC,aAAa,CAAC,IAAI,EAAE;YACjD,GAAG,CAAC,kBAAkB;gBACpB,CAAC,CAAC,EAAE,mBAAmB,EAAE,YAAY,CAAC,kBAAkB,CAAC,EAAE;gBAC3D,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACrE;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function isPostHogDisabledByEnv(): boolean;
2
+ export declare function resolvePostHogKey(): string;
3
+ export declare function resolvePostHogApiHost(): string;
4
+ export declare function resolvePostHogUiHost(): string;
5
+ export declare function hasPostHogKey(): boolean;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/analytics/config.ts"],"names":[],"mappings":"AASA,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC"}
@@ -0,0 +1,23 @@
1
+ const DEFAULT_POSTHOG_KEY = 'phc_mejq4ZZ8jTNZPiusjh7vHyPzWYinzsDwVJW43SM5FEcg';
2
+ const DEFAULT_API_HOST = 'https://a.alisait.com';
3
+ const DEFAULT_UI_HOST = 'https://eu.posthog.com';
4
+ function envTruthy(name) {
5
+ const value = process.env[name]?.trim().toLowerCase();
6
+ return value === '1' || value === 'true' || value === 'yes';
7
+ }
8
+ export function isPostHogDisabledByEnv() {
9
+ return envTruthy('POSTHOG_DISABLED');
10
+ }
11
+ export function resolvePostHogKey() {
12
+ return process.env.POSTHOG_KEY?.trim() || DEFAULT_POSTHOG_KEY;
13
+ }
14
+ export function resolvePostHogApiHost() {
15
+ return process.env.POSTHOG_API_HOST?.trim() || DEFAULT_API_HOST;
16
+ }
17
+ export function resolvePostHogUiHost() {
18
+ return process.env.POSTHOG_UI_HOST?.trim() || DEFAULT_UI_HOST;
19
+ }
20
+ export function hasPostHogKey() {
21
+ return resolvePostHogKey().length > 0;
22
+ }
23
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/analytics/config.ts"],"names":[],"mappings":"AAAA,MAAM,mBAAmB,GAAG,kDAAkD,CAAC;AAC/E,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AACjD,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACtD,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,KAAK,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,SAAS,CAAC,kBAAkB,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,mBAAmB,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,gBAAgB,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,eAAe,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,iBAAiB,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function getServerAnalyticsContext(): Record<string, boolean>;
2
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/analytics/context.ts"],"names":[],"mappings":"AAGA,wBAAgB,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASnE"}
@@ -0,0 +1,13 @@
1
+ import { loadConfig } from '../ui/config.js';
2
+ import { isAnalyticsEnabled, isBetaTelemetryOptIn } from './identity.js';
3
+ export function getServerAnalyticsContext() {
4
+ const config = loadConfig();
5
+ const analyticsEnabled = isAnalyticsEnabled();
6
+ return {
7
+ privacy_mode: !analyticsEnabled,
8
+ analytics_enabled: analyticsEnabled,
9
+ beta_telemetry_opt_in: isBetaTelemetryOptIn(),
10
+ action_plan_enabled: Boolean(config.actionPlanBeta),
11
+ };
12
+ }
13
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/analytics/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEzE,MAAM,UAAU,yBAAyB;IACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,gBAAgB,GAAG,kBAAkB,EAAE,CAAC;IAC9C,OAAO;QACL,YAAY,EAAE,CAAC,gBAAgB;QAC/B,iBAAiB,EAAE,gBAAgB;QACnC,qBAAqB,EAAE,oBAAoB,EAAE;QAC7C,mBAAmB,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;KACpD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function sanitizeAnalyticsProperties(properties: Record<string, unknown> | undefined): Record<string, string | number | boolean>;
2
+ export declare function buildRuntimeProperties(properties: Record<string, unknown> | undefined): Record<string, string | number | boolean>;
3
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/analytics/events.ts"],"names":[],"mappings":"AAiDA,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC9C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAc3C;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC9C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAS3C"}
@@ -0,0 +1,75 @@
1
+ import { getServerAnalyticsContext } from './context.js';
2
+ import { getLaunchMethod } from './launch-method.js';
3
+ const BLOCKED_PROPERTY_KEYS = new Set([
4
+ 'api_key',
5
+ 'apikey',
6
+ 'key',
7
+ 'token',
8
+ 'secret',
9
+ 'password',
10
+ 'email',
11
+ 'account_label',
12
+ 'cli_path',
13
+ 'path',
14
+ 'file_path',
15
+ 'filepath',
16
+ 'message',
17
+ 'content',
18
+ 'prompt',
19
+ 'chat_id',
20
+ 'session_id',
21
+ ]);
22
+ const ALLOWED_PROPERTY_KEYS = new Set([
23
+ 'app_version',
24
+ 'os',
25
+ 'arch',
26
+ 'node_version',
27
+ 'provider_id',
28
+ 'auth_method',
29
+ 'ok',
30
+ 'error_code',
31
+ 'photoshop_detected',
32
+ 'action_plan_enabled',
33
+ 'event_source',
34
+ 'port',
35
+ 'host',
36
+ 'no_open',
37
+ 'has_auth',
38
+ 'has_custom_path',
39
+ 'opted_out',
40
+ 'opted_in',
41
+ 'privacy_mode',
42
+ 'analytics_enabled',
43
+ 'beta_telemetry_opt_in',
44
+ 'theme',
45
+ 'launch_method',
46
+ ]);
47
+ export function sanitizeAnalyticsProperties(properties) {
48
+ if (!properties)
49
+ return {};
50
+ const out = {};
51
+ for (const [key, value] of Object.entries(properties)) {
52
+ const normalized = key.toLowerCase();
53
+ if (BLOCKED_PROPERTY_KEYS.has(normalized))
54
+ continue;
55
+ if (!ALLOWED_PROPERTY_KEYS.has(normalized))
56
+ continue;
57
+ if (value === null || value === undefined)
58
+ continue;
59
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
60
+ out[normalized] = value;
61
+ }
62
+ }
63
+ return out;
64
+ }
65
+ export function buildRuntimeProperties(properties) {
66
+ return {
67
+ os: process.platform,
68
+ arch: process.arch,
69
+ node_version: process.version,
70
+ launch_method: getLaunchMethod(),
71
+ ...getServerAnalyticsContext(),
72
+ ...sanitizeAnalyticsProperties(properties),
73
+ };
74
+ }
75
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/analytics/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,SAAS;IACT,QAAQ;IACR,KAAK;IACL,OAAO;IACP,QAAQ;IACR,UAAU;IACV,OAAO;IACP,eAAe;IACf,UAAU;IACV,MAAM;IACN,WAAW;IACX,UAAU;IACV,SAAS;IACT,SAAS;IACT,QAAQ;IACR,SAAS;IACT,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,aAAa;IACb,IAAI;IACJ,MAAM;IACN,cAAc;IACd,aAAa;IACb,aAAa;IACb,IAAI;IACJ,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,cAAc;IACd,MAAM;IACN,MAAM;IACN,SAAS;IACT,UAAU;IACV,iBAAiB;IACjB,WAAW;IACX,UAAU;IACV,cAAc;IACd,mBAAmB;IACnB,uBAAuB;IACvB,OAAO;IACP,eAAe;CAChB,CAAC,CAAC;AAEH,MAAM,UAAU,2BAA2B,CACzC,UAA+C;IAE/C,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAE3B,MAAM,GAAG,GAA8C,EAAE,CAAC;IAC1D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACtD,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,qBAAqB,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,SAAS;QACpD,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,SAAS;QACrD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;YACzF,GAAG,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,UAA+C;IAE/C,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,QAAQ;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,OAAO,CAAC,OAAO;QAC7B,aAAa,EAAE,eAAe,EAAE;QAChC,GAAG,yBAAyB,EAAE;QAC9B,GAAG,2BAA2B,CAAC,UAAU,CAAC;KAC3C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { BetaTelemetryState } from './types.js';
2
+ export declare function getOrCreateDistinctId(): string;
3
+ export declare function isAnalyticsOptedOut(): boolean;
4
+ export declare function setAnalyticsOptOut(optedOut: boolean): void;
5
+ export declare function isAnalyticsEnabled(): boolean;
6
+ export declare function getBetaTelemetryState(): BetaTelemetryState;
7
+ export declare function isBetaTelemetryOptIn(): boolean;
8
+ export declare function setBetaTelemetryChoice(optedIn: boolean): void;
9
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/analytics/identity.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAwFrD,wBAAgB,qBAAqB,IAAI,MAAM,CAsB9C;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAO7C;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAI1D;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAqBD,wBAAgB,qBAAqB,IAAI,kBAAkB,CAW1D;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAS7D"}
@@ -0,0 +1,166 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { getPhotoshopMcpHomeDir } from '../lib/export-paths.js';
5
+ import { kvGet, kvSet } from '../ui/store/kv.js';
6
+ import { isPostHogDisabledByEnv } from './config.js';
7
+ const DISTINCT_ID_KEY = 'analytics_distinct_id';
8
+ const OPT_OUT_KEY = 'analytics_opt_out';
9
+ const BETA_TELEMETRY_OPT_IN_KEY = 'beta_telemetry_opt_in';
10
+ const BETA_TELEMETRY_PROMPT_ANSWERED_KEY = 'beta_telemetry_prompt_answered';
11
+ const STORE_FILE = 'analytics-store.json';
12
+ let memoryDistinctId = null;
13
+ let dbAvailable = null;
14
+ function getStorePath() {
15
+ return join(getPhotoshopMcpHomeDir(), STORE_FILE);
16
+ }
17
+ function readFileStore() {
18
+ try {
19
+ const raw = readFileSync(getStorePath(), 'utf8');
20
+ return JSON.parse(raw);
21
+ }
22
+ catch {
23
+ return {};
24
+ }
25
+ }
26
+ function writeFileStore(store) {
27
+ const dir = getPhotoshopMcpHomeDir();
28
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
29
+ writeFileSync(getStorePath(), JSON.stringify(store), { mode: 0o600 });
30
+ }
31
+ function canUseKv() {
32
+ if (dbAvailable === false)
33
+ return false;
34
+ try {
35
+ kvGet('__analytics_probe__');
36
+ dbAvailable = true;
37
+ return true;
38
+ }
39
+ catch {
40
+ dbAvailable = false;
41
+ return false;
42
+ }
43
+ }
44
+ function readDistinctIdFromKv() {
45
+ if (!canUseKv())
46
+ return undefined;
47
+ try {
48
+ return kvGet(DISTINCT_ID_KEY);
49
+ }
50
+ catch {
51
+ dbAvailable = false;
52
+ return undefined;
53
+ }
54
+ }
55
+ function writeDistinctIdToKv(id) {
56
+ if (!canUseKv())
57
+ return;
58
+ try {
59
+ kvSet(DISTINCT_ID_KEY, id);
60
+ }
61
+ catch {
62
+ dbAvailable = false;
63
+ }
64
+ }
65
+ function readOptOutFromKv() {
66
+ if (!canUseKv())
67
+ return undefined;
68
+ try {
69
+ return kvGet(OPT_OUT_KEY);
70
+ }
71
+ catch {
72
+ dbAvailable = false;
73
+ return undefined;
74
+ }
75
+ }
76
+ function writeOptOutToKv(optedOut) {
77
+ if (!canUseKv())
78
+ return;
79
+ try {
80
+ kvSet(OPT_OUT_KEY, optedOut);
81
+ }
82
+ catch {
83
+ dbAvailable = false;
84
+ }
85
+ }
86
+ export function getOrCreateDistinctId() {
87
+ if (memoryDistinctId)
88
+ return memoryDistinctId;
89
+ const fromKv = readDistinctIdFromKv();
90
+ if (fromKv) {
91
+ memoryDistinctId = fromKv;
92
+ return fromKv;
93
+ }
94
+ const fromFile = readFileStore().distinctId;
95
+ if (fromFile) {
96
+ memoryDistinctId = fromFile;
97
+ writeDistinctIdToKv(fromFile);
98
+ return fromFile;
99
+ }
100
+ const id = randomUUID();
101
+ memoryDistinctId = id;
102
+ writeDistinctIdToKv(id);
103
+ const store = readFileStore();
104
+ writeFileStore({ ...store, distinctId: id });
105
+ return id;
106
+ }
107
+ export function isAnalyticsOptedOut() {
108
+ if (isPostHogDisabledByEnv())
109
+ return true;
110
+ const fromKv = readOptOutFromKv();
111
+ if (fromKv !== undefined)
112
+ return fromKv;
113
+ return readFileStore().optedOut === true;
114
+ }
115
+ export function setAnalyticsOptOut(optedOut) {
116
+ writeOptOutToKv(optedOut);
117
+ const store = readFileStore();
118
+ writeFileStore({ ...store, optedOut });
119
+ }
120
+ export function isAnalyticsEnabled() {
121
+ return !isAnalyticsOptedOut();
122
+ }
123
+ function readBooleanFromKv(key) {
124
+ if (!canUseKv())
125
+ return undefined;
126
+ try {
127
+ return kvGet(key);
128
+ }
129
+ catch {
130
+ dbAvailable = false;
131
+ return undefined;
132
+ }
133
+ }
134
+ function writeBooleanToKv(key, value) {
135
+ if (!canUseKv())
136
+ return;
137
+ try {
138
+ kvSet(key, value);
139
+ }
140
+ catch {
141
+ dbAvailable = false;
142
+ }
143
+ }
144
+ export function getBetaTelemetryState() {
145
+ const fromKvOptIn = readBooleanFromKv(BETA_TELEMETRY_OPT_IN_KEY);
146
+ const fromKvAnswered = readBooleanFromKv(BETA_TELEMETRY_PROMPT_ANSWERED_KEY);
147
+ const fileStore = readFileStore();
148
+ return {
149
+ betaTelemetryOptIn: fromKvOptIn ?? fileStore.betaTelemetryOptIn ?? false,
150
+ betaTelemetryPromptAnswered: fromKvAnswered ?? fileStore.betaTelemetryPromptAnswered ?? false,
151
+ };
152
+ }
153
+ export function isBetaTelemetryOptIn() {
154
+ return getBetaTelemetryState().betaTelemetryOptIn;
155
+ }
156
+ export function setBetaTelemetryChoice(optedIn) {
157
+ writeBooleanToKv(BETA_TELEMETRY_OPT_IN_KEY, optedIn);
158
+ writeBooleanToKv(BETA_TELEMETRY_PROMPT_ANSWERED_KEY, true);
159
+ const store = readFileStore();
160
+ writeFileStore({
161
+ ...store,
162
+ betaTelemetryOptIn: optedIn,
163
+ betaTelemetryPromptAnswered: true,
164
+ });
165
+ }
166
+ //# sourceMappingURL=identity.js.map