iobroker.ai-usage 0.2.0 → 0.4.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.
- package/README.md +135 -24
- package/admin/ai-usage.svg +12 -17
- package/admin/custom/assets/Components-Bq1fGAd7.js +1 -0
- package/admin/custom/assets/ConfigPanel-DGMF4_qT.js +1 -0
- package/admin/custom/assets/{_virtual_mf-localSharedImportMap___mfe_internal__AiUsageComponentSet__mf_owner__1-CXmnAfNY.js → _virtual_mf-localSharedImportMap___mfe_internal__AiUsageComponentSet__mf_owner__1-DKcsNxEw.js} +1 -1
- package/admin/custom/assets/{_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DwHNIjSR.js → _virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DzPBJWNQ.js} +1 -1
- package/admin/custom/assets/{_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-qu1s07FY.js → _virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-DEdJInQG.js} +3 -3
- package/admin/custom/assets/{_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-C8-GjKo4.js → _virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-CFn_lBXc.js} +1 -1
- package/admin/custom/assets/{_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__prebuild___mf_0_iobroker_mf_1_gui_mf_2_components__prebuild__-CQxWwR2r.js → _virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__prebuild___mf_0_iobroker_mf_1_gui_mf_2_components__prebuild__-DeLUTSLT.js} +1 -1
- package/admin/custom/assets/{hostInit-RHMRa462.js → hostInit-DoGxgbHj.js} +1 -1
- package/admin/custom/assets/index-CKngiVu3.js +1 -0
- package/admin/custom/assets/{lib-ByF76TCN.js → lib-MY71LxhX.js} +1 -1
- package/admin/custom/assets/{mf-entry-bootstrap-0-e070fa3e.js → mf-entry-bootstrap-0-eec08a33.js} +2 -2
- package/admin/custom/assets/virtualExposes-Dc9myzsk.js +1 -0
- package/admin/custom/assets/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__AiUsageComponentSet__customComponents_js-l7jtElkM.js → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__AiUsageComponentSet__customComponents_js-CP4YA3u3.js} +1 -1
- package/admin/custom/assets/virtual_mf-exposes-ssr___mfe_internal__AiUsageComponentSet__customComponents_js-DlNU_Z38.js +2 -0
- package/admin/custom/assets/{virtual_mf-exposes___mfe_internal__AiUsageComponentSet__customComponents_js-BZ-CVpVo.js → virtual_mf-exposes___mfe_internal__AiUsageComponentSet__customComponents_js-CEfffc0D.js} +2 -2
- package/admin/custom/customComponents.js +1 -1
- package/admin/custom/i18n/de.json +24 -15
- package/admin/custom/i18n/en.json +25 -16
- package/admin/custom/i18n/es.json +25 -16
- package/admin/custom/i18n/fr.json +25 -16
- package/admin/custom/i18n/it.json +25 -16
- package/admin/custom/i18n/nl.json +25 -16
- package/admin/custom/i18n/pl.json +25 -16
- package/admin/custom/i18n/pt.json +25 -16
- package/admin/custom/i18n/ru.json +25 -16
- package/admin/custom/i18n/uk.json +25 -16
- package/admin/custom/i18n/zh-cn.json +25 -16
- package/admin/custom/mf-manifest.json +1 -1
- package/build/lib/http.js +33 -4
- package/build/lib/http.js.map +2 -2
- package/build/lib/jwt.js +59 -0
- package/build/lib/jwt.js.map +7 -0
- package/build/lib/poll-engine.js +86 -6
- package/build/lib/poll-engine.js.map +2 -2
- package/build/lib/provider.js.map +2 -2
- package/build/lib/providers/chatgpt-auth.js +125 -0
- package/build/lib/providers/chatgpt-auth.js.map +7 -0
- package/build/lib/providers/chatgpt-sub.js +122 -0
- package/build/lib/providers/chatgpt-sub.js.map +7 -0
- package/build/lib/providers/claude-auth.js.map +2 -2
- package/build/lib/providers/claude-sub.js +9 -5
- package/build/lib/providers/claude-sub.js.map +2 -2
- package/build/lib/providers/gemini-auth.js +135 -0
- package/build/lib/providers/gemini-auth.js.map +7 -0
- package/build/lib/providers/gemini-sub.js +147 -0
- package/build/lib/providers/gemini-sub.js.map +7 -0
- package/build/lib/pure-helpers.js +38 -6
- package/build/lib/pure-helpers.js.map +2 -2
- package/build/lib/sign-in.js +38 -0
- package/build/lib/sign-in.js.map +7 -0
- package/build/lib/snapshot-tree.js +21 -9
- package/build/lib/snapshot-tree.js.map +2 -2
- package/build/main.js +283 -151
- package/build/main.js.map +2 -2
- package/io-package.json +46 -12
- package/package.json +1 -1
- package/admin/custom/assets/Components-Bd545388.js +0 -1
- package/admin/custom/assets/ConfigPanel-qivVprTj.js +0 -1
- package/admin/custom/assets/index-B0J1NbOp.js +0 -1
- package/admin/custom/assets/virtualExposes-BZWQ_oUV.js +0 -1
- package/admin/custom/assets/virtual_mf-exposes-ssr___mfe_internal__AiUsageComponentSet__customComponents_js-CRK-nAdE.js +0 -2
package/README.md
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
**Release:** [](https://www.npmjs.com/package/iobroker.ai-usage)   [](https://www.npmjs.com/package/iobroker.ai-usage)
|
|
4
4
|
|
|
5
|
-
**Build:** [](https://github.com/krobipd/ioBroker.ai-usage/actions/workflows/test-and-release.yml)   [](LICENSE)
|
|
5
|
+
**Build:** [](https://github.com/krobipd/ioBroker.ai-usage/actions/workflows/test-and-release.yml)   [](LICENSE) [](https://github.com/ioBroker/plugin-sentry#plugin-sentry)
|
|
6
6
|
|
|
7
7
|
**Support:** [](https://ko-fi.com/krobipd) [](https://paypal.me/krobipd)
|
|
8
8
|
|
|
9
|
-
Monitors your AI accounts in ioBroker: usage windows, limits, credits and costs of
|
|
10
|
-
Claude,
|
|
11
|
-
dashboards, history and automations ("send a message at
|
|
9
|
+
Monitors your AI accounts in ioBroker: usage windows, limits, credits and costs of your
|
|
10
|
+
Claude, ChatGPT and Google subscriptions plus OpenRouter, DeepSeek, OpenAI and Anthropic API
|
|
11
|
+
accounts — as clean datapoints for dashboards, history and automations ("send a message at
|
|
12
|
+
90 % of the weekly limit").
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
@@ -17,12 +18,22 @@ dashboards, history and automations ("send a message at 90 % of the weekly limit
|
|
|
17
18
|
- **Per-account monitoring** — one device node per AI account with harmonised datapoints: limit windows (percent + reset time), credits, costs and tokens
|
|
18
19
|
- **Totals** — summed costs, the highest limit utilisation across all accounts, and one `limitReached` trigger for automations
|
|
19
20
|
- **Warn threshold per account** — a single notification when an account crosses its threshold
|
|
21
|
+
- **Three subscriptions** — Claude, ChatGPT and Google are signed in with your own account; the settings page guides each sign-in step by step
|
|
20
22
|
- **Central credentials** — API keys live in the admin's central credential storage and are shared with the admin AI assistant
|
|
21
23
|
- **Read-only by design** — the adapter only reads usage data; it never calls, configures or writes to any AI service
|
|
24
|
+
- **Service status per account** — one indicator that tells apart "the AI service is down", "your sign-in is rejected" and "this host has no connection"
|
|
22
25
|
- **Throttle-safe polling** — a hard minimum interval and automatic backoff protect your accounts from provider rate-limit lockouts
|
|
23
26
|
|
|
24
27
|
---
|
|
25
28
|
|
|
29
|
+
## Sentry / Error reporting
|
|
30
|
+
|
|
31
|
+
**This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** Reporting only happens if you have enabled error reporting in the ioBroker diagnostics (**System settings → Diagnostics and error reporting**). Only an anonymous installation ID is transmitted — no name, e-mail address or IP address.
|
|
32
|
+
|
|
33
|
+
For details and how to disable it, see the [Sentry plugin documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry). Error reporting requires js-controller 3.0 or newer.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
26
37
|
## Requirements
|
|
27
38
|
|
|
28
39
|
- Node.js >= 22
|
|
@@ -33,16 +44,54 @@ dashboards, history and automations ("send a message at 90 % of the weekly limit
|
|
|
33
44
|
|
|
34
45
|
## Configuration
|
|
35
46
|
|
|
36
|
-
The instance settings show
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
The instance settings show one list of AI accounts. Switch on what you want to monitor:
|
|
48
|
+
|
|
49
|
+
| Account | How it is connected |
|
|
50
|
+
|---------|---------------------|
|
|
51
|
+
| **Claude subscription** | Sign in once with your Claude account (guided, in the settings) |
|
|
52
|
+
| **ChatGPT subscription** | Sign in once with your ChatGPT account (guided, in the settings) |
|
|
53
|
+
| **Google/Gemini subscription** | Sign in once with your Google account (guided, in the settings) |
|
|
54
|
+
| **OpenRouter, DeepSeek** | Pick the stored key from the admin's central credential storage |
|
|
55
|
+
| **OpenAI, Anthropic (organisation)** | Needs an **admin key** of your organisation — see below |
|
|
56
|
+
|
|
57
|
+
### Signing in to a subscription
|
|
58
|
+
|
|
59
|
+
Each provider needs a different last step, and the settings page tells you which one:
|
|
60
|
+
|
|
61
|
+
- **Claude** — open the sign-in page, log in, copy the code shown there and paste it back.
|
|
62
|
+
- **ChatGPT** — the adapter shows a short code; type it on the OpenAI page it links to. The
|
|
63
|
+
settings page notices by itself once you confirmed. The adapter signs in separately from the
|
|
64
|
+
Codex CLI and does not disturb its session.
|
|
65
|
+
- **Google/Gemini** — open the sign-in page and log in. Google then sends the result to
|
|
66
|
+
`localhost`, so **your browser shows an error page ("site cannot be reached"). That is expected**
|
|
67
|
+
— the address bar still carries the sign-in result. Copy the **whole address** and paste it back.
|
|
68
|
+
|
|
69
|
+
> The three subscription endpoints are **not officially documented**. They are the same ones the
|
|
70
|
+
> providers' own tools use, but they can change without notice. If a subscription stops delivering
|
|
71
|
+
> data, that is the likely reason.
|
|
72
|
+
>
|
|
73
|
+
> **Verification status:** the Claude sign-in and read-out were tested against a live subscription.
|
|
74
|
+
> ChatGPT and Google/Gemini were built from the requests those providers' own tools make, but no
|
|
75
|
+
> live subscription of either was available to test them on — please open an issue if a value looks
|
|
76
|
+
> wrong or a sign-in step behaves differently than described.
|
|
77
|
+
|
|
78
|
+
### API accounts with a key
|
|
79
|
+
|
|
80
|
+
Keys come from the admin's central credential storage (Settings → Credentials) and are shared with
|
|
81
|
+
the admin AI assistant. Two of them need a **different** key than the assistant uses:
|
|
82
|
+
|
|
83
|
+
- **OpenAI** — the usage and cost reports are organisation endpoints and need an **admin key**
|
|
84
|
+
created in the organisation settings.
|
|
85
|
+
- **Anthropic** — same, an **admin key** (`sk-ant-admin…`), which only an organisation
|
|
86
|
+
administrator can create. Personal accounts without an organisation cannot deliver these reports
|
|
87
|
+
at all; use the Claude subscription row instead.
|
|
88
|
+
|
|
89
|
+
Add such a key as its own entry in the credential storage (template "Key") and pick it in the row.
|
|
41
90
|
|
|
42
91
|
| Option | Description | Default |
|
|
43
92
|
|--------|-------------|---------|
|
|
44
|
-
| **Warn at %** | Per account: one notification when a limit window crosses this utilisation | 80 |
|
|
45
|
-
| **Poll interval** | How often each account is queried
|
|
93
|
+
| **Warn at %** | Per account: one notification when a plan-wide limit window crosses this utilisation | 80 |
|
|
94
|
+
| **Poll interval** | How often each account is queried, 60–3600 seconds. The floor is deliberate — asking too often gets the account throttled by the provider | 300 |
|
|
46
95
|
| **Notifications** | One notification on threshold crossing or broken credentials | on |
|
|
47
96
|
|
|
48
97
|
---
|
|
@@ -51,29 +100,76 @@ assistant.
|
|
|
51
100
|
|
|
52
101
|
```
|
|
53
102
|
ai-usage.0.
|
|
54
|
-
├── info.connection — at least one account
|
|
103
|
+
├── info.connection — at least one account is delivering data (bool)
|
|
55
104
|
├── total. — totals across all accounts
|
|
56
|
-
│ ├── costs
|
|
57
|
-
│ ├── maxLimitPercent — highest
|
|
105
|
+
│ ├── costs.today/month/… — summed real money (same currency only)
|
|
106
|
+
│ ├── maxLimitPercent — highest plan-wide utilisation of any account
|
|
58
107
|
│ ├── warningsActive — number of accounts above their threshold
|
|
59
|
-
│
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
108
|
+
│ ├── limitReached — a plan-wide window is full (automation trigger)
|
|
109
|
+
│ ├── accountsReachable — accounts currently delivering data
|
|
110
|
+
│ └── accounts — configured accounts
|
|
111
|
+
├── claude / chatgpt / gemini — one node per subscription
|
|
112
|
+
│ ├── warning — this account is above its warn threshold (bool)
|
|
113
|
+
│ ├── limitReached — a plan-wide window of this account is full (bool)
|
|
114
|
+
│ ├── info.serviceOnline — the AI service itself answered (bool)
|
|
115
|
+
│ ├── info.state — ok · unauthorized · rate-limited · service-down · no-connection
|
|
116
|
+
│ ├── info.reachable — usage data was read successfully (bool)
|
|
117
|
+
│ ├── info.signedIn — the sign-in is in place (bool)
|
|
118
|
+
│ ├── info.provider — which provider this node speaks to
|
|
119
|
+
│ ├── info.lastUpdate — time of the last successful read
|
|
120
|
+
│ ├── limits.<window>.* — percent + reset time (session, week, per model, …)
|
|
121
|
+
│ └── credits.* — where the provider reports a balance
|
|
122
|
+
└── <name>-api — one node per key-based account
|
|
123
|
+
├── warning / limitReached — same triggers as above
|
|
124
|
+
├── info.* — same status states as above (no signedIn — it uses a key)
|
|
125
|
+
├── credits.* / costs.* — granted budget and real money
|
|
126
|
+
└── tokens.* — token counters
|
|
66
127
|
```
|
|
67
128
|
|
|
68
129
|
Only what an account's source actually delivers is created.
|
|
69
130
|
|
|
131
|
+
### Is it online?
|
|
132
|
+
|
|
133
|
+
`info.serviceOnline` answers "is the AI service itself up", `info.state` says why not:
|
|
134
|
+
|
|
135
|
+
| State | What happened |
|
|
136
|
+
|-------|---------------|
|
|
137
|
+
| `ok` | Data was read |
|
|
138
|
+
| `unauthorized` | The service answered, but rejects the sign-in or key — the service is **up** |
|
|
139
|
+
| `rate-limited` | The service answered with a throttle; the adapter backs off and keeps the last values |
|
|
140
|
+
| `service-down` | The service answered with a fault of its own (reported immediately) |
|
|
141
|
+
| `no-connection` | The service was not reachable at all — reported after three attempts in a row, so a single hiccup does not flip the indicator |
|
|
142
|
+
|
|
143
|
+
### Which limit raises the warning
|
|
144
|
+
|
|
145
|
+
Only **plan-wide** windows — your session and your week — decide `warning`, `limitReached` and
|
|
146
|
+
`total.maxLimitPercent`, and the warning always names the window it came from. A window that
|
|
147
|
+
belongs to a single model keeps its own datapoints but stays out of it: a model you never use can
|
|
148
|
+
sit at 100 % forever, and an alarm that never clears is worse than none. Google is the exception,
|
|
149
|
+
because there every bucket belongs to a model — those are your quota, so they do count.
|
|
150
|
+
|
|
151
|
+
Want a specific model watched anyway? Build the automation on its own datapoint,
|
|
152
|
+
`limits.<window>.percent`.
|
|
153
|
+
|
|
70
154
|
---
|
|
71
155
|
|
|
72
156
|
## Troubleshooting
|
|
73
157
|
|
|
74
|
-
### An account
|
|
75
|
-
|
|
76
|
-
|
|
158
|
+
### An account delivers no data
|
|
159
|
+
Read `info.state` first, it names the cause: `unauthorized` means the sign-in expired (sign in
|
|
160
|
+
again in the settings) or the key is not an admin key (see Configuration); `service-down` and
|
|
161
|
+
`no-connection` are outside your instance and clear up by themselves. The adapter log states the
|
|
162
|
+
same reason once and raises a single notification.
|
|
163
|
+
|
|
164
|
+
### A subscription says "not signed in" although you just signed in
|
|
165
|
+
Save the settings first, then sign in — the row needs a saved account to attach the sign-in to.
|
|
166
|
+
After a successful sign-in the adapter queries the account immediately, so values appear within
|
|
167
|
+
seconds.
|
|
168
|
+
|
|
169
|
+
### The object tree changed after updating to 0.3.0
|
|
170
|
+
Accounts now live under fixed ids (`claude`, `chatgpt`, `gemini`, `<name>-api`) and the separate
|
|
171
|
+
`auth` branch is gone — the sign-in state moved into `<account>.info.signedIn`. Old nodes are
|
|
172
|
+
removed automatically, and an existing Claude sign-in is carried over.
|
|
77
173
|
|
|
78
174
|
---
|
|
79
175
|
|
|
@@ -83,6 +179,21 @@ and the adapter log. Authentication errors raise one notification with the reaso
|
|
|
83
179
|
Placeholder for the next version (at the beginning of the line):
|
|
84
180
|
### **WORK IN PROGRESS**
|
|
85
181
|
-->
|
|
182
|
+
### 0.4.0 (2026-08-26)
|
|
183
|
+
|
|
184
|
+
- Fixed: A limit that belongs to a single model no longer reports the whole account as full, and the warning names the window it came from instead of just "usage"
|
|
185
|
+
- New: Each account shows whether the AI service itself is online, telling a service outage apart from a rejected sign-in or a missing internet connection
|
|
186
|
+
- New: Error reporting via Sentry — crashes reach the developer automatically, but only if you enabled diagnostics and error reporting in the ioBroker system settings
|
|
187
|
+
- Changed: New adapter icon — a network of nodes instead of the dark tile, so it reads as AI at a glance and sits cleanly in both the light and the dark admin
|
|
188
|
+
|
|
189
|
+
### 0.3.0 (2026-08-26)
|
|
190
|
+
|
|
191
|
+
- New: ChatGPT and Google/Gemini subscriptions can now be monitored like the Claude one — each with its own guided sign-in that the settings page walks you through step by step
|
|
192
|
+
- New: The ChatGPT and Gemini readouts use the same endpoints the providers' own tools use, but no live subscription was available to test them on — please report anything that looks wrong
|
|
193
|
+
- Changed: Each account now owns exactly one node in the object tree (`claude`, `chatgpt`, `gemini`, `<name>-api`); the separate sign-in branch is gone and old nodes are cleaned up automatically
|
|
194
|
+
- Fixed: After signing in, the account is queried immediately instead of waiting for the next poll — no restart needed
|
|
195
|
+
- Fixed: OpenAI and Anthropic rows now state that they need an organisation admin key, instead of failing with an unexplained rejection
|
|
196
|
+
|
|
86
197
|
### 0.2.0 (2026-08-26)
|
|
87
198
|
|
|
88
199
|
- Changed: Completely new settings page — your stored AI credentials appear as simple on/off switches instead of a table, and new keys are picked up straight from the admin credential storage
|
package/admin/ai-usage.svg
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
|
2
|
-
<!-- Theme-fixed colors
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
<!-- Needle -->
|
|
15
|
-
<line x1="64" y1="86" x2="88" y2="50" stroke="#eceff1" stroke-width="6" stroke-linecap="round"/>
|
|
16
|
-
<circle cx="64" cy="86" r="7" fill="#eceff1"/>
|
|
17
|
-
<!-- Spark: the AI -->
|
|
18
|
-
<path d="M 64 100 l 4 8 l 8 4 l -8 4 l -4 8 l -4 -8 l -8 -4 l 8 -4 z" fill="#4fc3f7" transform="translate(0,-4) scale(0.9)" transform-origin="64 112"/>
|
|
2
|
+
<!-- Theme-fixed colors, transparent background — never currentColor (renders black as <img>). -->
|
|
3
|
+
<g stroke="#8fa0ad" stroke-width="7" stroke-linecap="round" opacity="0.75">
|
|
4
|
+
<line x1="28" y1="64" x2="64" y2="32"/>
|
|
5
|
+
<line x1="28" y1="64" x2="64" y2="96"/>
|
|
6
|
+
<line x1="64" y1="32" x2="100" y2="64"/>
|
|
7
|
+
<line x1="64" y1="96" x2="100" y2="64"/>
|
|
8
|
+
<line x1="64" y1="32" x2="64" y2="96"/>
|
|
9
|
+
</g>
|
|
10
|
+
<circle cx="28" cy="64" r="14" fill="#38bdf8"/>
|
|
11
|
+
<circle cx="64" cy="32" r="14" fill="#38bdf8"/>
|
|
12
|
+
<circle cx="64" cy="96" r="14" fill="#0ea5e9"/>
|
|
13
|
+
<circle cx="100" cy="64" r="14" fill="#f59e0b"/>
|
|
19
14
|
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./ConfigPanel-DGMF4_qT.js";var t={ConfigPanel:e};export{t as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-DEdJInQG.js";import{Br as t,Dr as n,Er as r,Fi as i,Ha as a,Hi as o,Or as s,Ti as c,Tr as l,Va as u,Xr as d,_o as f,ai as p,ci as m,ei as h,ga as g,ii as _,in as v,kr as y,li as b,ni as x,oi as S}from"./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DzPBJWNQ.js";f();var C=[{provider:`claude-sub`,label:`Claude`,captionKey:`aiu_capClaude`},{provider:`chatgpt-sub`,label:`ChatGPT`,captionKey:`aiu_capChatgpt`},{provider:`gemini-sub`,label:`Gemini`,captionKey:`aiu_capGemini`}];function w(e,t){let n=`${e} ${t}`.toLowerCase();return n.includes(`gemini`)?null:n.includes(`anthropic`)||n.includes(`claude`)?{provider:`anthropic-api`,label:`Anthropic`,needsAdminKey:!0}:n.includes(`chatgpt`)||n.includes(`openai`)?{provider:`openai`,label:`OpenAI`,needsAdminKey:!0}:n.includes(`deepseek`)?{provider:`deepseek`,label:`DeepSeek`,needsAdminKey:!1}:n.includes(`openrouter`)||n.includes(`router`)?{provider:`openrouter`,label:`OpenRouter`,needsAdminKey:!1}:null}var T=[{provider:`openrouter`,label:`OpenRouter`,needsAdminKey:!1},{provider:`deepseek`,label:`DeepSeek`,needsAdminKey:!1},{provider:`openai`,label:`OpenAI`,needsAdminKey:!0},{provider:`anthropic-api`,label:`Anthropic`,needsAdminKey:!0}];function E(e,t){return e.find(e=>e.provider===t)}function D(e,t,n,r){let i=e.filter(e=>e.provider!==t);return n?[...i,{name:r,provider:t,credentialId:``,warnThreshold:80,enabled:!0}]:i}function O(e,t,n,r){let i=e.filter(e=>e.credentialId!==t.id);return!r||!n?i:[...i,{name:t.name,provider:n,credentialId:t.id,warnThreshold:80,enabled:!0}]}function k(e,t,n){let r=Math.min(100,Math.max(10,Math.round(Number(n))||80));return e.map(e=>(t.credentialId?e.credentialId===t.credentialId:e.provider===t.provider)?{...e,warnThreshold:r}:e)}var A=class extends e{constructor(e){super(e),g(this,`timer`,null),this.state={...this.state,credentials:[],credentialsLoaded:!1,signIn:{},drafts:{},providerChoice:{},busy:``}}async componentDidMount(){var e;(e=super.componentDidMount)==null||e.call(this),await this.loadCredentials(),await this.refreshSignIn(),this.timer=setInterval(()=>void this.refreshSignIn(),4e3)}componentWillUnmount(){var e;this.timer&&=(clearInterval(this.timer),null),(e=super.componentWillUnmount)==null||e.call(this)}async loadCredentials(){try{let e=await this.props.oContext.socket.getObjectViewSystem(`config`,`system.credentials.`,`system.credentials.香`),t=Object.values(e||{}).filter(e=>{var t;return!!e&&((t=e.native)==null?void 0:t.type)===`ai`}).map(e=>{var t,n;let r=e._id.substring(19),i=(t=e.common)==null?void 0:t.name,a=typeof i==`string`?i:(i==null?void 0:i.en)||Object.values(i||{})[0]||r;return{id:e._id,suffix:r,name:a,icon:typeof((n=e.common)==null?void 0:n.icon)==`string`?e.common.icon:void 0}}).sort((e,t)=>e.name.localeCompare(t.name));this.setState({credentials:t,credentialsLoaded:!0})}catch{this.setState({credentialsLoaded:!0})}}async refreshSignIn(){if(!this.props.alive)return;let e={};for(let t of C){if(!E(this.accounts(),t.provider))continue;let n=await this.ask(`signInStatus`,t.provider);n&&(e[t.provider]=n)}this.setState({signIn:e})}async ask(e,t,n){let r=this.props.oContext;try{let i=await r.socket.sendTo(`${r.adapterName}.${r.instance}`,e,{provider:t,value:n});return!i||i.error?{status:`failed`,reason:(i==null?void 0:i.error)||v.t(`aiu_noAnswer`)}:i}catch{return null}}async run(e,t,n){this.setState({busy:t});let r=await this.ask(e,t,n);this.setState(e=>({busy:``,signIn:r?{...e.signIn,[t]:r}:e.signIn,drafts:{...e.drafts,[t]:``}}))}accounts(){let t=e.getValue(this.props.data,`accounts`);return Array.isArray(t)?t:[]}commit(e){this.onChange(`accounts`,e)}renderThreshold(e,t,n){return u(d,{size:`small`,type:`number`,label:v.t(`aiu_warnAt`),defaultValue:t.warnThreshold||80,onBlur:e=>this.commit(k(this.accounts(),n,e.target.value)),slotProps:{htmlInput:{min:10,max:100,style:{width:60}},inputLabel:{shrink:!0}},sx:{ml:`auto`}},e)}renderSignIn(e){let t=this.state.signIn[e],i=this.state.busy===e;if(!this.props.alive)return u(c,{severity:`warning`,children:v.t(`aiu_instanceNotRunning`)});if(this.props.changed)return u(c,{severity:`info`,children:v.t(`aiu_saveFirst`)});if(!t)return u(b,{size:20});if(t.status===`signed-in`)return a(x,{sx:{display:`flex`,alignItems:`center`,gap:1,flexWrap:`wrap`},children:[u(y,{color:`success`}),u(h,{sx:{color:`success.main`},children:v.t(`aiu_signedIn`)}),u(_,{size:`small`,startIcon:u(r,{}),disabled:i,onClick:()=>void this.run(`signOut`,e),children:v.t(`aiu_signOut`)})]});if(t.status===`awaiting-device`)return a(x,{sx:{display:`flex`,flexDirection:`column`,gap:1.5},children:[u(h,{variant:`body2`,children:v.t(`aiu_deviceStep1`)}),a(x,{sx:{display:`flex`,gap:1,alignItems:`center`,flexWrap:`wrap`},children:[u(m,{label:t.userCode,sx:{fontSize:20,fontFamily:`monospace`,py:2.5,px:1}}),u(_,{size:`small`,startIcon:u(s,{}),onClick:()=>{var e;(e=navigator.clipboard)==null||e.writeText(t.userCode)},children:v.t(`aiu_copyCode`)}),u(_,{variant:`contained`,startIcon:u(n,{}),href:t.verificationUrl,target:`_blank`,rel:`noopener noreferrer`,children:v.t(`aiu_openPage`)})]}),u(c,{severity:`info`,children:v.t(`aiu_deviceWaiting`)})]});if(t.status===`awaiting-paste`){let r=t.flow===`paste-url`;return a(x,{sx:{display:`flex`,flexDirection:`column`,gap:1.5},children:[u(h,{variant:`body2`,children:v.t(r?`aiu_googleStep1`:`aiu_claudeStep1`)}),a(x,{sx:{display:`flex`,gap:1,flexWrap:`wrap`},children:[u(_,{variant:`contained`,startIcon:u(n,{}),href:t.url,target:`_blank`,rel:`noopener noreferrer`,children:v.t(`aiu_openSignIn`)}),u(_,{variant:`outlined`,startIcon:u(s,{}),onClick:()=>{var e;(e=navigator.clipboard)==null||e.writeText(t.url)},children:v.t(`aiu_copyLink`)})]}),r?u(c,{severity:`warning`,children:v.t(`aiu_googleErrorPageHint`)}):null,u(h,{variant:`body2`,children:v.t(r?`aiu_googleStep2`:`aiu_claudeStep2`)}),a(x,{sx:{display:`flex`,gap:1,alignItems:`center`,flexWrap:`wrap`},children:[u(d,{size:`small`,label:v.t(r?`aiu_addressLabel`:`aiu_codeLabel`),value:this.state.drafts[e]??``,onChange:t=>this.setState(n=>({drafts:{...n.drafts,[e]:t.target.value}})),sx:{minWidth:340,flexGrow:1}}),u(_,{variant:`contained`,disabled:i||!(this.state.drafts[e]??``).trim(),onClick:()=>void this.run(`signInSubmit`,e,this.state.drafts[e]),children:i?u(b,{size:20}):v.t(`aiu_redeem`)})]})]})}return a(x,{sx:{display:`flex`,flexDirection:`column`,gap:1},children:[t.status===`failed`?u(c,{severity:`error`,children:t.reason}):null,u(x,{children:u(_,{variant:`contained`,startIcon:u(n,{}),disabled:i,onClick:()=>void this.run(`signInStart`,e),children:i?u(b,{size:20}):v.t(`aiu_startSignIn`)})})]})}renderSubscriptionRow(e){let n=E(this.accounts(),e.provider);return a(x,{children:[a(x,{sx:{display:`flex`,alignItems:`center`,gap:1.5,py:1,borderBottom:1,borderColor:`divider`},children:[u(o,{sx:{width:28,height:28,bgcolor:`transparent`},children:u(l,{fontSize:`small`,color:`primary`})}),a(x,{sx:{minWidth:180},children:[u(h,{children:e.label}),u(h,{variant:`caption`,sx:{opacity:.7},children:v.t(e.captionKey)})]}),n?this.renderThreshold(`${e.provider}-t`,n,{provider:e.provider}):null,u(t,{checked:!!n,onChange:t=>this.commit(D(this.accounts(),e.provider,t.target.checked,e.label)),sx:{ml:n?0:`auto`}})]}),n?u(x,{sx:{pl:6,py:1.5,borderBottom:1,borderColor:`divider`},children:this.renderSignIn(e.provider)}):null]},e.provider)}renderCredentialRow(e){var n;let r=this.accounts(),s=r.find(t=>t.credentialId===e.id),f=w(e.suffix,e.name),p=(s==null?void 0:s.provider)||(f==null?void 0:f.provider)||this.state.providerChoice[e.id]||``,m=((n=T.find(e=>e.provider===p))==null?void 0:n.needsAdminKey)??(f==null?void 0:f.needsAdminKey)??!1,g=!f&&!this.state.providerChoice[e.id]&&!s;return a(x,{sx:{borderBottom:1,borderColor:`divider`},children:[a(x,{sx:{display:`flex`,alignItems:`center`,gap:1.5,py:1},children:[u(o,{src:e.icon,sx:{width:28,height:28,bgcolor:`transparent`},children:u(l,{fontSize:`small`})}),a(x,{sx:{minWidth:180},children:[u(h,{children:e.name}),u(h,{variant:`caption`,sx:{opacity:.7},children:v.t(`aiu_storedKey`)})]}),g?u(d,{select:!0,size:`small`,label:v.t(`aiu_provider`),value:this.state.providerChoice[e.id]||``,onChange:t=>this.setState(n=>({providerChoice:{...n.providerChoice,[e.id]:t.target.value}})),sx:{minWidth:170},children:T.map(e=>u(i,{value:e.provider,children:e.label},e.provider))}):null,s?this.renderThreshold(`${e.id}-t`,s,{credentialId:e.id}):null,u(t,{checked:!!s,disabled:!p,onChange:t=>this.commit(O(r,e,p,t.target.checked)),sx:{ml:s?0:`auto`}})]}),s&&m?u(c,{severity:`info`,sx:{mb:1},children:v.t(`aiu_adminKeyHint`)}):null]},e.id)}renderItem(){return u(x,{"data-testid":`aiu-config`,sx:{maxWidth:760},children:u(p,{variant:`outlined`,children:a(S,{children:[u(h,{variant:`h6`,sx:{mb:.5},children:v.t(`aiu_storedTitle`)}),u(h,{variant:`body2`,sx:{opacity:.8,mb:1},children:v.t(`aiu_storedHint`)}),C.map(e=>this.renderSubscriptionRow(e)),this.state.credentialsLoaded?this.state.credentials.map(e=>this.renderCredentialRow(e)):u(b,{size:24,sx:{mt:1}}),this.state.credentialsLoaded&&this.state.credentials.length===0?u(c,{severity:`info`,sx:{mt:1},children:v.t(`aiu_noCredentials`)}):null]})})})}};export{A as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./vite-preload-helper-B7qeedMF.js";var t={react:async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-
|
|
1
|
+
import{t as e}from"./vite-preload-helper-B7qeedMF.js";var t={react:async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DzPBJWNQ.js`).then(e=>(e.yo(),e.vo)),[],import.meta.url),"react/jsx-runtime":async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DzPBJWNQ.js`).then(e=>e.Ua),[],import.meta.url),"react-dom":async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DzPBJWNQ.js`).then(e=>(e.Xa(),e.Ya)),[],import.meta.url),"react-dom/client":async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-CFn_lBXc.js`).then(e=>e.n),[],import.meta.url),"@mui/material":async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DzPBJWNQ.js`).then(e=>e.Wi),[],import.meta.url),"@mui/icons-material":async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DzPBJWNQ.js`).then(e=>e.wr),[],import.meta.url),"@iobroker/gui-components":async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__prebuild___mf_0_iobroker_mf_1_gui_mf_2_components__prebuild__-DeLUTSLT.js`),[],import.meta.url),"@iobroker/json-config":async()=>await e(()=>import(`./_virtual_mf___mfe_internal__AiUsageComponentSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-DEdJInQG.js`).then(e=>e.n),[],import.meta.url)},n={react:{name:`react`,version:`19.2.8`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!0,async get(){n.react.loaded=!0;let{react:e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}},"react/jsx-runtime":{name:`react/jsx-runtime`,version:`19.2.8`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!0,async get(){n[`react/jsx-runtime`].loaded=!0;let{"react/jsx-runtime":e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}},"react-dom":{name:`react-dom`,version:`19.2.8`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!0,async get(){n[`react-dom`].loaded=!0;let{"react-dom":e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}},"react-dom/client":{name:`react-dom/client`,version:`19.2.8`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!0,async get(){n[`react-dom/client`].loaded=!0;let{"react-dom/client":e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}},"@mui/material":{name:`@mui/material`,version:`9.3.1`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!0,async get(){n[`@mui/material`].loaded=!0;let{"@mui/material":e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}},"@mui/icons-material":{name:`@mui/icons-material`,version:`9.3.1`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!0,async get(){n[`@mui/icons-material`].loaded=!0;let{"@mui/icons-material":e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}},"@iobroker/gui-components":{name:`@iobroker/gui-components`,version:`10.1.0`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!1,async get(){n[`@iobroker/gui-components`].loaded=!0;let{"@iobroker/gui-components":e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}},"@iobroker/json-config":{name:`@iobroker/json-config`,version:`9.0.22`,scope:[`default`],loaded:!1,eager:!1,from:`AiUsageComponentSet`,canLiveRebind:!0,async get(){n[`@iobroker/json-config`].loaded=!0;let{"@iobroker/json-config":e}=t,r={...await e()};return r.__esModule!==!0&&Object.defineProperty(r,"__esModule",{value:!0,enumerable:!1}),function(){return r}},shareConfig:{singleton:!0,requiredVersion:`*`,strictVersion:!1,eager:!1}}},r=[];export{r as usedRemotes,n as usedShared};
|