iobroker.ai-usage 0.9.1 → 0.9.2
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 +4 -6
- package/build/main.js +32 -0
- package/build/main.js.map +2 -2
- package/io-package.json +14 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,6 +126,10 @@ After a successful sign-in the account is queried immediately, so values appear
|
|
|
126
126
|
Placeholder for the next version (at the beginning of the line):
|
|
127
127
|
### **WORK IN PROGRESS**
|
|
128
128
|
-->
|
|
129
|
+
### 0.9.2 (2026-08-27)
|
|
130
|
+
|
|
131
|
+
- Fixed: Stopping the instance now marks the accounts as offline on installations that were updated too, not only on fresh ones — the previous version left them showing as online
|
|
132
|
+
|
|
129
133
|
### 0.9.1 (2026-08-27)
|
|
130
134
|
|
|
131
135
|
- Changed: While an account has nothing to report — the adapter switched off, or started and not asked yet — the reason now reads "Unknown" instead of a sentence about the adapter
|
|
@@ -151,12 +155,6 @@ After a successful sign-in the account is queried immediately, so values appear
|
|
|
151
155
|
|
|
152
156
|
- Fixed: Restarting the adapter no longer writes one unchanged value into every status datapoint, so a recorded history stays free of restart noise
|
|
153
157
|
|
|
154
|
-
### 0.7.0 (2026-08-27)
|
|
155
|
-
|
|
156
|
-
- New: One log line after a change tells you how many datapoints the object tree gained and lost, instead of leaving you to click through the tree
|
|
157
|
-
- Fixed: A started sign-in that sat unused for a quarter of an hour now says so plainly instead of failing later with the provider's own cryptic answer
|
|
158
|
-
- Fixed: Datapoints that only repeat their previous value are no longer rewritten every cycle, which kept flooding the history of anyone recording them
|
|
159
|
-
|
|
160
158
|
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
|
161
159
|
|
|
162
160
|
## Support
|
package/build/main.js
CHANGED
|
@@ -466,8 +466,40 @@ class AiUsageAdapter extends utils.Adapter {
|
|
|
466
466
|
}
|
|
467
467
|
// ------------------------------------------------------------- life cycle
|
|
468
468
|
/** Validate the configuration, clean up stale objects and start the engine. */
|
|
469
|
+
/**
|
|
470
|
+
* Clear a leftover `supportedMessages.stopInstance` from THIS instance's object.
|
|
471
|
+
*
|
|
472
|
+
* The entry lives in two places: in the adapter's manifest, and as a copy in the
|
|
473
|
+
* instance object in the database. An update merges the manifest into that copy —
|
|
474
|
+
* it never removes a field. So on every installation that ran a version carrying
|
|
475
|
+
* the entry, the host keeps killing the process outright and `onUnload` still never
|
|
476
|
+
* runs: the update alone changes nothing (found by a second pair of eyes on the
|
|
477
|
+
* live server 2026-08-27, after my own test had been contaminated by a value I had
|
|
478
|
+
* set by hand).
|
|
479
|
+
*
|
|
480
|
+
* Writing the instance object makes the host restart this instance once. That is
|
|
481
|
+
* the price, it happens on the first start after the update and never again —
|
|
482
|
+
* afterwards the condition below is false. public-holidays corrects its own run
|
|
483
|
+
* mode the same way.
|
|
484
|
+
*/
|
|
485
|
+
async clearStopInstanceFlag() {
|
|
486
|
+
var _a;
|
|
487
|
+
const id = `system.adapter.${this.namespace}`;
|
|
488
|
+
try {
|
|
489
|
+
const obj = await this.getForeignObjectAsync(id);
|
|
490
|
+
const supported = (_a = obj == null ? void 0 : obj.common) == null ? void 0 : _a.supportedMessages;
|
|
491
|
+
if (!(supported == null ? void 0 : supported.stopInstance)) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
this.log.info("Correcting a leftover setting from an earlier version \u2014 this instance restarts once");
|
|
495
|
+
await this.extendForeignObjectAsync(id, { common: { supportedMessages: { stopInstance: false } } });
|
|
496
|
+
} catch (e) {
|
|
497
|
+
this.log.debug(`Could not check the instance object: ${e instanceof Error ? e.message : String(e)}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
469
500
|
async onReady() {
|
|
470
501
|
try {
|
|
502
|
+
await this.clearStopInstanceFlag();
|
|
471
503
|
const accounts = (0, import_pure_helpers.parseAccounts)(this.config.accounts);
|
|
472
504
|
const interval = (0, import_pure_helpers.clampPollInterval)(this.config.pollInterval);
|
|
473
505
|
await this.migrateTokenFiles();
|
package/build/main.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { Credentials } from \"@iobroker/adapter-core\";\nimport { mkdir, readFile, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { postForm, postJson } from \"./lib/http\";\nimport { PollEngine } from \"./lib/poll-engine\";\nimport {\n clampPollInterval,\n datapointBalanceLine,\n parseAccounts,\n SUBSCRIPTION_IDS,\n type AccountConfig,\n} from \"./lib/pure-helpers\";\nimport type { TokenSet, TokenStore, UsageProvider } from \"./lib/provider\";\nimport { SIGN_IN_FLOWS, SIGN_IN_LABELS, attemptExpired, type SignInState } from \"./lib/sign-in\";\nimport { buildAuthorizeUrl, exchangeCode, generatePkce, type PkcePair } from \"./lib/providers/claude-auth\";\nimport { claudeSubProvider } from \"./lib/providers/claude-sub\";\nimport {\n CHATGPT_OAUTH,\n exchangeDeviceCode,\n pollDeviceCode,\n startDeviceCode,\n type DeviceCodeStart,\n} from \"./lib/providers/chatgpt-auth\";\nimport { chatgptSubProvider } from \"./lib/providers/chatgpt-sub\";\nimport {\n buildGeminiAuthorizeUrl,\n exchangeGeminiCode,\n extractGeminiCode,\n generateGeminiPkce,\n type GeminiPkce,\n} from \"./lib/providers/gemini-auth\";\nimport { geminiSubProvider } from \"./lib/providers/gemini-sub\";\nimport { anthropicApiProvider } from \"./lib/providers/anthropic-api\";\nimport { deepSeekProvider } from \"./lib/providers/deepseek\";\nimport { openAiProvider } from \"./lib/providers/openai\";\nimport { openRouterProvider } from \"./lib/providers/openrouter\";\n\n/** A cancellable handle: interval or timeout \u2014 the engine treats them uniformly. */\ntype TimerHandle =\n | { kind: \"interval\"; handle: ioBroker.Interval | undefined }\n | { kind: \"timeout\"; handle: ioBroker.Timeout | undefined };\n\n/** One running sign-in attempt (secrets live in memory only, never on disk). */\ntype Attempt =\n | { flow: \"paste-code\"; pkce: PkcePair; url: string; expiresAt: number }\n | { flow: \"paste-url\"; pkce: GeminiPkce; url: string; expiresAt: number }\n | { flow: \"device-code\"; start: DeviceCodeStart };\n\n/**\n * AI Usage adapter \u2014 reads usage windows, credits and costs of AI accounts into\n * read-only states. Three subscriptions sign in with the user's own account\n * (Claude, ChatGPT, Google), the other accounts use a key from the admin's central\n * credential storage. Orchestration lives in the unit-tested {@link PollEngine};\n * this class wires ioBroker IO, the sign-in flows and the token files to it.\n */\nexport class AiUsageAdapter extends utils.Adapter {\n private engine: PollEngine | null = null;\n /** Running sign-in attempts, keyed by provider kind. */\n private readonly attempts = new Map<string, Attempt>();\n /** Last failure reason per provider, shown in the admin row. */\n private readonly signInErrors = new Map<string, string>();\n /** Device-code pollers, so they can be stopped on unload. */\n private readonly devicePollers = new Map<string, TimerHandle>();\n /** One token store per subscription \u2014 see {@link tokenStore} for why it is shared. */\n private readonly tokenStores = new Map<string, TokenStore>();\n\n /**\n * Every state id that already existed when this process started.\n *\n * The create path runs `extendObject` for every state once per process \u2014 also for\n * states that were already in the database \u2014 so \"the create path touched it\" would\n * report every datapoint as new after each restart. Only what is missing from this\n * snapshot is a real addition (beszel pattern).\n */\n private knownStateIds = new Set<string>();\n /** Datapoints created since the snapshot. */\n private createdStates = 0;\n /** Datapoints removed since the snapshot \u2014 excluding the one-shot migration, which reports itself. */\n private removedStates = 0;\n /** Whether the startup balance was already logged. */\n private balanceLogged = false;\n\n /**\n * Read every existing state id once, before anything creates or deletes.\n *\n * @returns nothing; fills {@link knownStateIds}\n */\n private async snapshotExistingStates(): Promise<void> {\n try {\n const view = await this.getObjectViewAsync(\"system\", \"state\", {\n startkey: `${this.namespace}.`,\n endkey: `${this.namespace}.\\uFFFF`,\n });\n for (const row of view?.rows ?? []) {\n this.knownStateIds.add(row.id.substring(this.namespace.length + 1));\n }\n } catch (e) {\n this.log.debug(`Could not snapshot existing states: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Report what the object tree gained and lost in this startup \u2014 one line, both\n * sides, silent when nothing changed. A normal restart must stay quiet.\n */\n private logDatapointBalance(): void {\n if (this.balanceLogged) {\n return;\n }\n this.balanceLogged = true;\n const line = datapointBalanceLine(this.createdStates, this.removedStates);\n if (line) {\n this.log.info(line);\n }\n }\n\n /**\n * @param options the adapter options\n */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({ ...options, name: \"ai-usage\" });\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"message\", this.onMessage.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n }\n\n // ---------------------------------------------------------------- sign-in\n\n /**\n * Handle admin messages: the three sign-in flows plus their status.\n *\n * @param obj the message\n */\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n try {\n const provider = this.providerFrom(obj.message);\n switch (obj.command) {\n case \"signInStart\":\n this.respond(obj, provider ? await this.startSignIn(provider) : { error: \"unknown provider\" });\n return;\n case \"signInSubmit\":\n this.respond(obj, provider ? await this.submitSignIn(provider, obj.message) : { error: \"unknown provider\" });\n return;\n case \"signInStatus\":\n this.respond(obj, provider ? await this.signInState(provider) : { error: \"unknown provider\" });\n return;\n case \"signOut\":\n this.respond(obj, provider ? await this.signOut(provider) : { error: \"unknown provider\" });\n return;\n default:\n // Always answer, or the caller's callback dangles until timeout.\n this.respond(obj, { error: `Unknown command: ${obj.command}` });\n }\n } catch (e) {\n this.log.error(`onMessage failed: ${e instanceof Error ? e.message : String(e)}`);\n this.respond(obj, { error: \"internal error \u2014 see log\" });\n }\n }\n\n /**\n * Send a message response, when the caller expects one.\n *\n * @param obj the request message\n * @param response the response payload\n */\n private respond(obj: ioBroker.Message, response: unknown): void {\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, response, obj.callback);\n }\n }\n\n /**\n * The provider kind named in a message, if it is a subscription we know.\n *\n * @param message the message payload ({ provider })\n * @returns the provider kind, or undefined\n */\n private providerFrom(message: unknown): string | undefined {\n const value =\n typeof (message as { provider?: unknown })?.provider === \"string\"\n ? (message as { provider: string }).provider\n : \"\";\n return SIGN_IN_FLOWS[value] ? value : undefined;\n }\n\n /**\n * Begin a sign-in: build the link (Claude/Google) or fetch a device code (ChatGPT).\n *\n * @param provider the subscription kind\n * @returns what the admin panel has to show\n */\n private async startSignIn(provider: string): Promise<SignInState | { error: string }> {\n this.signInErrors.delete(provider);\n this.stopDevicePoller(provider);\n const flow = SIGN_IN_FLOWS[provider];\n const now = Date.now();\n try {\n if (flow === \"paste-code\") {\n const pkce = generatePkce();\n const url = buildAuthorizeUrl(pkce);\n this.attempts.set(provider, { flow, pkce, url, expiresAt: now + 15 * 60_000 });\n return { status: \"awaiting-paste\", url, flow };\n }\n if (flow === \"paste-url\") {\n const pkce = generateGeminiPkce();\n const url = buildGeminiAuthorizeUrl(pkce);\n this.attempts.set(provider, { flow, pkce, url, expiresAt: now + 15 * 60_000 });\n return { status: \"awaiting-paste\", url, flow };\n }\n const start = await startDeviceCode(postJson, now);\n this.attempts.set(provider, { flow: \"device-code\", start });\n this.armDevicePoller(provider, start);\n return {\n status: \"awaiting-device\",\n userCode: start.userCode,\n verificationUrl: CHATGPT_OAUTH.verificationUrl,\n expiresAt: start.expiresAt,\n };\n } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n this.signInErrors.set(provider, reason);\n return { status: \"failed\", reason };\n }\n }\n\n /**\n * Finish a paste-based sign-in (Claude code, Google address).\n *\n * @param provider the subscription kind\n * @param message the message payload ({ value })\n * @returns the resulting state\n */\n private async submitSignIn(provider: string, message: unknown): Promise<SignInState> {\n const attempt = this.attempts.get(provider);\n const value =\n typeof (message as { value?: unknown })?.value === \"string\" ? (message as { value: string }).value.trim() : \"\";\n if (!attempt || attempt.flow === \"device-code\") {\n return { status: \"failed\", reason: \"start the sign-in first\" };\n }\n // The 15-minute window was stored but never enforced \u2014 a stale attempt used to\n // fail with the provider's own cryptic answer instead of a clear instruction.\n if (attemptExpired(attempt.expiresAt, Date.now())) {\n this.attempts.delete(provider);\n return { status: \"failed\", reason: \"the sign-in window expired \u2014 start the sign-in again\" };\n }\n if (!value) {\n return { status: \"failed\", reason: \"nothing pasted\" };\n }\n try {\n const now = Date.now();\n const tokens =\n attempt.flow === \"paste-code\"\n ? await exchangeCode(value, attempt.pkce, postJson, now)\n : await exchangeGeminiCode(extractGeminiCode(value, attempt.pkce.state), attempt.pkce, postForm, now);\n await this.finishSignIn(provider, tokens);\n return { status: \"signed-in\" };\n } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n this.signInErrors.set(provider, reason);\n return { status: \"failed\", reason };\n }\n }\n\n /**\n * Store fresh tokens, mark the account signed in and poll it right away \u2014 waiting\n * up to a full interval after a successful sign-in reads as \"it did not work\".\n *\n * @param provider the subscription kind\n * @param tokens the token set\n */\n private async finishSignIn(provider: string, tokens: TokenSet): Promise<void> {\n await this.tokenStore(provider).save(tokens);\n this.attempts.delete(provider);\n this.signInErrors.delete(provider);\n this.stopDevicePoller(provider);\n const id = SUBSCRIPTION_IDS[provider];\n if (id) {\n // Started, not awaited: the settings page must confirm the sign-in at once,\n // and a first query that has to wait for a provider can take up to the full\n // request timeout. The values arrive a moment later on their own.\n void this.engine?.pollNow(id).catch(e => {\n this.log.debug(`First query after sign-in failed: ${e instanceof Error ? e.message : String(e)}`);\n });\n }\n this.log.info(`${SIGN_IN_LABELS[provider] ?? provider}: signed in`);\n }\n\n /**\n * Poll the device-code endpoint until the user confirmed, the window closed or\n * the adapter stops. The handle lives in memory only \u2014 a restart mid-flow just\n * means the user starts again, which is cheaper than persisting a 15-minute secret.\n *\n * @param provider the subscription kind\n * @param start the device-code handle\n */\n private armDevicePoller(provider: string, start: DeviceCodeStart): void {\n const tick = async (): Promise<void> => {\n try {\n if (attemptExpired(start.expiresAt, Date.now())) {\n this.stopDevicePoller(provider);\n this.attempts.delete(provider);\n this.signInErrors.set(provider, \"the code expired \u2014 start the sign-in again\");\n return;\n }\n const result = await pollDeviceCode(start, postJson);\n if (result.status === \"ready\") {\n this.stopDevicePoller(provider);\n const tokens = await exchangeDeviceCode(result.code, result.codeVerifier, postForm, Date.now());\n await this.finishSignIn(provider, tokens);\n }\n } catch (e) {\n this.stopDevicePoller(provider);\n this.attempts.delete(provider);\n this.signInErrors.set(provider, e instanceof Error ? e.message : String(e));\n }\n };\n this.devicePollers.set(provider, {\n kind: \"interval\",\n handle: this.setInterval(() => void tick(), Math.max(start.intervalSec, 1) * 1000),\n });\n }\n\n /**\n * Stop a running device-code poller.\n *\n * @param provider the subscription kind\n */\n private stopDevicePoller(provider: string): void {\n const handle = this.devicePollers.get(provider);\n if (handle?.kind === \"interval\") {\n this.clearInterval(handle.handle);\n }\n this.devicePollers.delete(provider);\n }\n\n /**\n * The current sign-in state of one subscription, for the admin row.\n *\n * @param provider the subscription kind\n * @returns the state\n */\n private async signInState(provider: string): Promise<SignInState> {\n const failure = this.signInErrors.get(provider);\n const attempt = this.attempts.get(provider);\n if (attempt?.flow === \"device-code\") {\n return {\n status: \"awaiting-device\",\n userCode: attempt.start.userCode,\n verificationUrl: CHATGPT_OAUTH.verificationUrl,\n expiresAt: attempt.start.expiresAt,\n };\n }\n if (attempt) {\n if (attemptExpired(attempt.expiresAt, Date.now())) {\n this.attempts.delete(provider);\n return { status: \"failed\", reason: \"the sign-in window expired \u2014 start the sign-in again\" };\n }\n return { status: \"awaiting-paste\", url: attempt.url, flow: attempt.flow };\n }\n if (failure) {\n return { status: \"failed\", reason: failure };\n }\n return (await this.tokenStore(provider).load()) ? { status: \"signed-in\" } : { status: \"signed-out\" };\n }\n\n /**\n * Forget the tokens of one subscription.\n *\n * @param provider the subscription kind\n * @returns the resulting state\n */\n private async signOut(provider: string): Promise<SignInState> {\n await this.tokenStore(provider).clear();\n this.attempts.delete(provider);\n this.signInErrors.delete(provider);\n this.stopDevicePoller(provider);\n return { status: \"signed-out\" };\n }\n\n // ------------------------------------------------------------ token files\n\n /**\n * The token store of one subscription \u2014 created ONCE per provider.\n *\n * The identity matters: the store holds the in-memory copy of the tokens, so\n * signing out really takes effect. When each provider module kept its own copy,\n * a sign-out deleted the file while the adapter kept polling with what it still\n * had \u2014 and the next token refresh wrote the deleted file back.\n *\n * @param provider the subscription kind\n * @returns the store\n */\n private tokenStore(provider: string): TokenStore {\n let store = this.tokenStores.get(provider);\n if (!store) {\n store = this.makeTokenStore(provider);\n this.tokenStores.set(provider, store);\n }\n return store;\n }\n\n /**\n * Build the store for one subscription: an encrypted file in the instance data\n * directory, named after the PROVIDER. Keying by provider (not by account name)\n * keeps a sign-in alive when an account is renamed \u2014 the previous scheme lost it.\n *\n * @param provider the subscription kind\n * @returns the store\n */\n private makeTokenStore(provider: string): TokenStore {\n const dir = utils.getAbsoluteInstanceDataDir(this);\n const file = join(dir, `tokens-${provider}.json`);\n let cached: TokenSet | null = null;\n let read = false;\n return {\n load: async (): Promise<TokenSet | null> => {\n if (!read) {\n cached = await this.readTokenFile(file, provider);\n read = true;\n }\n return cached;\n },\n save: async (tokens: TokenSet): Promise<void> => {\n await mkdir(dir, { recursive: true });\n await writeFile(file, this.encrypt(JSON.stringify(tokens)), \"utf8\");\n cached = tokens;\n read = true;\n },\n clear: async (): Promise<void> => {\n cached = null;\n read = true;\n await unlink(file).catch(() => {\n /* already gone */\n });\n },\n };\n }\n\n /**\n * Read one token file.\n *\n * A missing file means \"never signed in\" and is silent. A file that is there but\n * cannot be read \u2014 damaged, or encrypted with a different instance secret \u2014 is\n * NOT the same thing: without a word in the log, signing in would look like it\n * simply does nothing.\n *\n * @param file the file path\n * @param provider the subscription kind (for the log line)\n * @returns the tokens, or null\n */\n private async readTokenFile(file: string, provider: string): Promise<TokenSet | null> {\n let raw: string;\n try {\n raw = await readFile(file, \"utf8\");\n } catch (e) {\n if ((e as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n this.log.warn(`${SIGN_IN_LABELS[provider] ?? provider}: cannot open the stored sign-in (${String(e)})`);\n }\n return null; // never signed in \u2014 the provider reports auth-required\n }\n try {\n const parsed = JSON.parse(this.decrypt(raw)) as Partial<TokenSet>;\n if (typeof parsed.accessToken !== \"string\" || typeof parsed.refreshToken !== \"string\") {\n throw new Error(\"the file carries no tokens\");\n }\n return {\n accessToken: parsed.accessToken,\n refreshToken: parsed.refreshToken,\n expiresAt: Number(parsed.expiresAt) || 0,\n accountRef: typeof parsed.accountRef === \"string\" ? parsed.accountRef : undefined,\n };\n } catch (e) {\n this.log.warn(\n `${SIGN_IN_LABELS[provider] ?? provider}: the stored sign-in cannot be read (${\n e instanceof Error ? e.message : String(e)\n }) \u2014 sign in again in the instance settings`,\n );\n return null;\n }\n }\n\n /**\n * Carry a sign-in from the pre-0.3.0 layout over: tokens used to be stored per\n * ACCOUNT NAME (`claude-tokens-<name>.json`). Without this the rename to fixed\n * account ids would silently sign the user out.\n */\n private async migrateTokenFiles(): Promise<void> {\n const dir = utils.getAbsoluteInstanceDataDir(this);\n const target = join(dir, \"tokens-claude-sub.json\");\n try {\n await readFile(target, \"utf8\");\n return; // already migrated\n } catch {\n // not there yet \u2014 look for an old file\n }\n for (const legacy of [\"claude-tokens-Claude.json\", \"claude-tokens-claude.json\"]) {\n try {\n await rename(join(dir, legacy), target);\n this.log.info(\"Carried the existing Claude sign-in over to the new layout\");\n return;\n } catch {\n // try the next candidate\n }\n }\n }\n\n // ------------------------------------------------------------- life cycle\n\n /** Validate the configuration, clean up stale objects and start the engine. */\n private async onReady(): Promise<void> {\n try {\n const accounts = parseAccounts(this.config.accounts);\n const interval = clampPollInterval(this.config.pollInterval);\n await this.migrateTokenFiles();\n // Baseline first: the cleanup deletes and the engine creates, both are counted\n // against this snapshot.\n await this.snapshotExistingStates();\n await this.cleanupStaleObjects();\n const retired = await this.removeRetiredStates(accounts);\n if (retired > 0) {\n this.log.info(`Object tree updated: removed ${retired} obsolete status datapoint(s)`);\n }\n if (accounts.length === 0) {\n this.log.info(\"No AI accounts configured \u2014 add accounts in the instance settings\");\n await this.setState(\"info.connection\", { val: false, ack: true });\n return;\n }\n const providers = new Map<string, UsageProvider>();\n for (const account of accounts) {\n const provider = await this.makeProvider(account);\n if (provider) {\n providers.set(account.id, provider);\n }\n }\n this.engine = new PollEngine(accounts, providers, interval, {\n upsertObject: async def => {\n await this.extendObject(def.id, { type: def.type, common: def.common as ioBroker.ObjectCommon, native: {} });\n if (def.type === \"state\" && !this.knownStateIds.has(def.id)) {\n this.knownStateIds.add(def.id);\n this.createdStates++;\n }\n },\n setState: (id, value) => {\n void this.setState(id, { val: value, ack: true }).catch(() => {\n /* states DB going down \u2014 never crash the poll loop */\n });\n },\n setStateChanged: async (id, value) => {\n // Awaited only by the shutdown path; the poll loop drops the promise.\n await this.setStateChangedAsync(id, { val: value, ack: true }).catch(() => {\n /* states DB going down \u2014 never crash the poll loop */\n });\n },\n deleteObject: async id => {\n try {\n await this.delObjectAsync(id, { recursive: true });\n if (this.knownStateIds.delete(id)) {\n this.removedStates++;\n }\n } catch (e) {\n this.log.debug(`Could not remove ${id}: ${e instanceof Error ? e.message : String(e)}`);\n }\n },\n listStateIds: async prefix => {\n const start = `${this.namespace}.${prefix}.`;\n const view = await this.getObjectViewAsync(\"system\", \"state\", {\n startkey: start,\n endkey: `${start}\uFFFF`,\n });\n return (view?.rows ?? []).map(row => row.id.substring(this.namespace.length + 1));\n },\n schedule: (cb, ms): TimerHandle => ({ kind: \"interval\", handle: this.setInterval(cb, ms) }),\n scheduleOnce: (cb, ms): TimerHandle => ({ kind: \"timeout\", handle: this.setTimeout(cb, ms) }),\n cancel: handle => {\n const timer = handle as TimerHandle;\n if (timer.kind === \"interval\") {\n this.clearInterval(timer.handle);\n } else {\n this.clearTimeout(timer.handle);\n }\n },\n now: () => Date.now(),\n log: {\n debug: m => this.log.debug(m),\n info: m => this.log.info(m),\n warn: m => this.log.warn(m),\n error: m => this.log.error(m),\n },\n afterFirstRound: () => this.logDatapointBalance(),\n notify: this.config.notifications\n ? (_account, message) =>\n void this.registerNotification(\"ai-usage\", \"userActionRequired\", message).catch(e =>\n this.log.debug(`Could not raise notification: ${e instanceof Error ? e.message : String(e)}`),\n )\n : undefined,\n });\n await this.engine.start();\n this.log.info(`Monitoring ${providers.size} of ${accounts.length} AI account(s), polling every ${interval} s`);\n } catch (e) {\n this.log.error(`Startup failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Build the provider for one account.\n *\n * @param account the validated account config\n * @returns the provider, or undefined to skip the account\n */\n private async makeProvider(account: AccountConfig): Promise<UsageProvider | undefined> {\n switch (account.provider) {\n case \"claude-sub\":\n return claudeSubProvider(this.tokenStore(account.provider), postJson);\n case \"chatgpt-sub\":\n return chatgptSubProvider(this.tokenStore(account.provider), postJson);\n case \"gemini-sub\":\n return geminiSubProvider(this.tokenStore(account.provider), postJson, postForm);\n case \"openrouter\": {\n const key = await this.resolveKey(account);\n return key ? openRouterProvider(key) : undefined;\n }\n case \"deepseek\": {\n const key = await this.resolveKey(account);\n return key ? deepSeekProvider(key) : undefined;\n }\n case \"openai\": {\n const key = await this.resolveKey(account);\n return key ? openAiProvider(key) : undefined;\n }\n case \"anthropic-api\": {\n const key = await this.resolveKey(account);\n return key ? anthropicApiProvider(key) : undefined;\n }\n default:\n return undefined;\n }\n }\n\n /**\n * Read and decrypt a key-form credential from the central credential storage.\n *\n * @param account the account whose credential to resolve\n * @returns the key, or undefined (with a log line) when it cannot be read\n */\n private async resolveKey(account: AccountConfig): Promise<string | undefined> {\n if (!account.credentialId) {\n this.log.warn(`${account.name}: no credential selected \u2014 pick one in the instance settings`);\n return undefined;\n }\n try {\n const credential = await Credentials.getCredentials(this, account.credentialId);\n const values = credential.values as { key?: unknown };\n const key = typeof values.key === \"string\" && values.key ? values.key : undefined;\n if (!key) {\n this.log.warn(`${account.name}: credential ${account.credentialId} carries no API key`);\n }\n return key;\n } catch (e) {\n this.log.warn(\n `${account.name}: cannot read credential ${account.credentialId} (${e instanceof Error ? e.message : String(e)})`,\n );\n return undefined;\n }\n }\n\n /**\n * Status states retired in 0.5.0 \u2014 `provider` (visible in the node name anyway),\n * `reachable`/`serviceOnline`/`state` (three spellings of one fact) and `signedIn`\n * (the settings page asks the adapter directly).\n *\n * ioBroker never garbage-collects an object whose id the adapter stopped writing:\n * it would sit there frozen on its last value and keep lying. So the old ids are\n * deleted from a fixed list \u2014 no state reading, no heuristics.\n */\n private static readonly RETIRED_INFO_STATES = [\n \"info.provider\",\n \"info.reachable\",\n \"info.serviceOnline\",\n \"info.state\",\n \"info.signedIn\",\n ];\n\n /**\n * Remove the retired status states of every configured account.\n *\n * Which of them still exist is answered by the startup snapshot that was read a\n * moment earlier \u2014 asking the database for every id on every start would keep\n * costing 35 lookups forever for a migration that is done after the first one.\n *\n * @param accounts the configured accounts\n * @returns how many objects were actually deleted\n */\n private async removeRetiredStates(accounts: readonly AccountConfig[]): Promise<number> {\n let removed = 0;\n for (const account of accounts) {\n for (const suffix of AiUsageAdapter.RETIRED_INFO_STATES) {\n const id = `${account.id}.${suffix}`;\n if (!this.knownStateIds.has(id)) {\n continue;\n }\n try {\n await this.delObjectAsync(id);\n removed++;\n // Deliberately NOT counted in the startup balance: this one-shot migration\n // reports its own sum, and the same deletion must not appear in two lines\n // that mean different things.\n this.knownStateIds.delete(id);\n } catch (e) {\n this.log.debug(`Could not remove ${id}: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n return removed;\n }\n\n /**\n * Delete object trees that no longer belong to a configured account, plus the\n * `auth` branch of the pre-0.3.0 layout. An EMPTY table deletes nothing \u2014 the\n * guard against wiping everything through an accidental clear.\n */\n private async cleanupStaleObjects(): Promise<void> {\n const keepIds = parseAccounts(this.config.accounts).map(account => account.id);\n if (keepIds.length === 0) {\n return;\n }\n const keep = new Set([...keepIds, \"info\", \"total\"]);\n try {\n const objects = await this.getAdapterObjectsAsync();\n const roots = new Set<string>();\n for (const id of Object.keys(objects)) {\n const root = id.substring(this.namespace.length + 1).split(\".\")[0];\n if (root && !keep.has(root)) {\n roots.add(root);\n }\n }\n for (const root of roots) {\n this.log.info(\n root === \"auth\"\n ? \"Removing the old sign-in branch \u2014 the sign-in state now lives inside each account\"\n : `Removing objects of no longer configured account \"${root}\"`,\n );\n // Count the datapoints BEFORE they are gone \u2014 afterwards there is nothing to count.\n for (const id of [...this.knownStateIds]) {\n if (id === root || id.startsWith(`${root}.`)) {\n this.knownStateIds.delete(id);\n this.removedStates++;\n }\n }\n await this.delObjectAsync(root, { recursive: true });\n }\n } catch (e) {\n this.log.warn(`Cleanup of stale objects failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Tear down: cancel everything, then say that nothing is delivering any more.\n *\n * The final writes are AWAITED before `callback()` \u2014 measured on the live server\n * (2026-08-27), a fire-and-forget write followed by an immediate callback never\n * reached the database, so a switched-off instance kept showing every account as\n * online. The whole thing takes about 100 ms.\n *\n * Deliberately WITHOUT a time limit of its own: the adapter's timer API refuses to\n * arm once shutdown has begun, and the host already applies the only deadline that\n * matters \u2014 it ends the process a second after asking. A states database that\n * hangs would swallow the writes either way, so a second guard adds code, not\n * safety.\n *\n * None of this runs while `common.supportedMessages.stopInstance` sits in the\n * manifest: with it the host kills the process outright instead of asking, and\n * every state written here is dead code. A test pins that the entry stays out.\n *\n * @param callback invoked when cleanup is done\n */\n private onUnload(callback: () => void): void {\n try {\n for (const provider of [...this.devicePollers.keys()]) {\n this.stopDevicePoller(provider);\n }\n const engine = this.engine;\n this.engine?.stop();\n this.engine = null;\n void (engine?.markAllOffline() ?? this.setState(\"info.connection\", { val: false, ack: true }))\n .then(() => this.log.debug(\"Shutdown: final states written\"))\n .catch(e => this.log.debug(`Shutdown: final states rejected \u2014 ${e instanceof Error ? e.message : String(e)}`))\n .finally(callback);\n } catch {\n callback();\n }\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new AiUsageAdapter(options);\n} else {\n (() => new AiUsageAdapter())();\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAAuB;AACvB,0BAA4B;AAC5B,sBAA2D;AAC3D,uBAAqB;AACrB,kBAAmC;AACnC,yBAA2B;AAC3B,0BAMO;AAEP,qBAAgF;AAChF,yBAA6E;AAC7E,wBAAkC;AAClC,0BAMO;AACP,yBAAmC;AACnC,yBAMO;AACP,wBAAkC;AAClC,2BAAqC;AACrC,sBAAiC;AACjC,oBAA+B;AAC/B,wBAAmC;AAoB5B,MAAM,uBAAuB,MAAM,QAAQ;AAAA,EACxC,SAA4B;AAAA;AAAA,EAEnB,WAAW,oBAAI,IAAqB;AAAA;AAAA,EAEpC,eAAe,oBAAI,IAAoB;AAAA;AAAA,EAEvC,gBAAgB,oBAAI,IAAyB;AAAA;AAAA,EAE7C,cAAc,oBAAI,IAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUnD,gBAAgB,oBAAI,IAAY;AAAA;AAAA,EAEhC,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOxB,MAAc,yBAAwC;AAxFxD;AAyFI,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,mBAAmB,UAAU,SAAS;AAAA,QAC5D,UAAU,GAAG,KAAK,SAAS;AAAA,QAC3B,QAAQ,GAAG,KAAK,SAAS;AAAA,MAC3B,CAAC;AACD,iBAAW,QAAO,kCAAM,SAAN,YAAc,CAAC,GAAG;AAClC,aAAK,cAAc,IAAI,IAAI,GAAG,UAAU,KAAK,UAAU,SAAS,CAAC,CAAC;AAAA,MACpE;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,uCAAuC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,IACpG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAA4B;AAClC,QAAI,KAAK,eAAe;AACtB;AAAA,IACF;AACA,SAAK,gBAAgB;AACrB,UAAM,WAAO,0CAAqB,KAAK,eAAe,KAAK,aAAa;AACxE,QAAI,MAAM;AACR,WAAK,IAAI,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,YAAY,UAAyC,CAAC,GAAG;AAC9D,UAAM,EAAE,GAAG,SAAS,MAAM,WAAW,CAAC;AACtC,SAAK,GAAG,SAAS,KAAK,QAAQ,KAAK,IAAI,CAAC;AACxC,SAAK,GAAG,WAAW,KAAK,UAAU,KAAK,IAAI,CAAC;AAC5C,SAAK,GAAG,UAAU,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,UAAU,KAAsC;AAC5D,QAAI;AACF,YAAM,WAAW,KAAK,aAAa,IAAI,OAAO;AAC9C,cAAQ,IAAI,SAAS;AAAA,QACnB,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,YAAY,QAAQ,IAAI,EAAE,OAAO,mBAAmB,CAAC;AAC7F;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,aAAa,UAAU,IAAI,OAAO,IAAI,EAAE,OAAO,mBAAmB,CAAC;AAC3G;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,YAAY,QAAQ,IAAI,EAAE,OAAO,mBAAmB,CAAC;AAC7F;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,QAAQ,QAAQ,IAAI,EAAE,OAAO,mBAAmB,CAAC;AACzF;AAAA,QACF;AAEE,eAAK,QAAQ,KAAK,EAAE,OAAO,oBAAoB,IAAI,OAAO,GAAG,CAAC;AAAA,MAClE;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,qBAAqB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAChF,WAAK,QAAQ,KAAK,EAAE,OAAO,gCAA2B,CAAC;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,QAAQ,KAAuB,UAAyB;AAC9D,QAAI,IAAI,UAAU;AAChB,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,UAAU,IAAI,QAAQ;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,aAAa,SAAsC;AACzD,UAAM,QACJ,QAAQ,mCAAoC,cAAa,WACpD,QAAiC,WAClC;AACN,WAAO,6BAAc,KAAK,IAAI,QAAQ;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAY,UAA4D;AACpF,SAAK,aAAa,OAAO,QAAQ;AACjC,SAAK,iBAAiB,QAAQ;AAC9B,UAAM,OAAO,6BAAc,QAAQ;AACnC,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI;AACF,UAAI,SAAS,cAAc;AACzB,cAAM,WAAO,iCAAa;AAC1B,cAAM,UAAM,sCAAkB,IAAI;AAClC,aAAK,SAAS,IAAI,UAAU,EAAE,MAAM,MAAM,KAAK,WAAW,MAAM,KAAK,IAAO,CAAC;AAC7E,eAAO,EAAE,QAAQ,kBAAkB,KAAK,KAAK;AAAA,MAC/C;AACA,UAAI,SAAS,aAAa;AACxB,cAAM,WAAO,uCAAmB;AAChC,cAAM,UAAM,4CAAwB,IAAI;AACxC,aAAK,SAAS,IAAI,UAAU,EAAE,MAAM,MAAM,KAAK,WAAW,MAAM,KAAK,IAAO,CAAC;AAC7E,eAAO,EAAE,QAAQ,kBAAkB,KAAK,KAAK;AAAA,MAC/C;AACA,YAAM,QAAQ,UAAM,qCAAgB,sBAAU,GAAG;AACjD,WAAK,SAAS,IAAI,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;AAC1D,WAAK,gBAAgB,UAAU,KAAK;AACpC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,UAAU,MAAM;AAAA,QAChB,iBAAiB,kCAAc;AAAA,QAC/B,WAAW,MAAM;AAAA,MACnB;AAAA,IACF,SAAS,GAAG;AACV,YAAM,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACxD,WAAK,aAAa,IAAI,UAAU,MAAM;AACtC,aAAO,EAAE,QAAQ,UAAU,OAAO;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,aAAa,UAAkB,SAAwC;AACnF,UAAM,UAAU,KAAK,SAAS,IAAI,QAAQ;AAC1C,UAAM,QACJ,QAAQ,mCAAiC,WAAU,WAAY,QAA8B,MAAM,KAAK,IAAI;AAC9G,QAAI,CAAC,WAAW,QAAQ,SAAS,eAAe;AAC9C,aAAO,EAAE,QAAQ,UAAU,QAAQ,0BAA0B;AAAA,IAC/D;AAGA,YAAI,+BAAe,QAAQ,WAAW,KAAK,IAAI,CAAC,GAAG;AACjD,WAAK,SAAS,OAAO,QAAQ;AAC7B,aAAO,EAAE,QAAQ,UAAU,QAAQ,4DAAuD;AAAA,IAC5F;AACA,QAAI,CAAC,OAAO;AACV,aAAO,EAAE,QAAQ,UAAU,QAAQ,iBAAiB;AAAA,IACtD;AACA,QAAI;AACF,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SACJ,QAAQ,SAAS,eACb,UAAM,iCAAa,OAAO,QAAQ,MAAM,sBAAU,GAAG,IACrD,UAAM,2CAAmB,sCAAkB,OAAO,QAAQ,KAAK,KAAK,GAAG,QAAQ,MAAM,sBAAU,GAAG;AACxG,YAAM,KAAK,aAAa,UAAU,MAAM;AACxC,aAAO,EAAE,QAAQ,YAAY;AAAA,IAC/B,SAAS,GAAG;AACV,YAAM,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACxD,WAAK,aAAa,IAAI,UAAU,MAAM;AACtC,aAAO,EAAE,QAAQ,UAAU,OAAO;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,aAAa,UAAkB,QAAiC;AA/QhF;AAgRI,UAAM,KAAK,WAAW,QAAQ,EAAE,KAAK,MAAM;AAC3C,SAAK,SAAS,OAAO,QAAQ;AAC7B,SAAK,aAAa,OAAO,QAAQ;AACjC,SAAK,iBAAiB,QAAQ;AAC9B,UAAM,KAAK,qCAAiB,QAAQ;AACpC,QAAI,IAAI;AAIN,aAAK,UAAK,WAAL,mBAAa,QAAQ,IAAI,MAAM,OAAK;AACvC,aAAK,IAAI,MAAM,qCAAqC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,MAClG;AAAA,IACF;AACA,SAAK,IAAI,KAAK,IAAG,mCAAe,QAAQ,MAAvB,YAA4B,QAAQ,aAAa;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,gBAAgB,UAAkB,OAA8B;AACtE,UAAM,OAAO,YAA2B;AACtC,UAAI;AACF,gBAAI,+BAAe,MAAM,WAAW,KAAK,IAAI,CAAC,GAAG;AAC/C,eAAK,iBAAiB,QAAQ;AAC9B,eAAK,SAAS,OAAO,QAAQ;AAC7B,eAAK,aAAa,IAAI,UAAU,iDAA4C;AAC5E;AAAA,QACF;AACA,cAAM,SAAS,UAAM,oCAAe,OAAO,oBAAQ;AACnD,YAAI,OAAO,WAAW,SAAS;AAC7B,eAAK,iBAAiB,QAAQ;AAC9B,gBAAM,SAAS,UAAM,wCAAmB,OAAO,MAAM,OAAO,cAAc,sBAAU,KAAK,IAAI,CAAC;AAC9F,gBAAM,KAAK,aAAa,UAAU,MAAM;AAAA,QAC1C;AAAA,MACF,SAAS,GAAG;AACV,aAAK,iBAAiB,QAAQ;AAC9B,aAAK,SAAS,OAAO,QAAQ;AAC7B,aAAK,aAAa,IAAI,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,MAC5E;AAAA,IACF;AACA,SAAK,cAAc,IAAI,UAAU;AAAA,MAC/B,MAAM;AAAA,MACN,QAAQ,KAAK,YAAY,MAAM,KAAK,KAAK,GAAG,KAAK,IAAI,MAAM,aAAa,CAAC,IAAI,GAAI;AAAA,IACnF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAiB,UAAwB;AAC/C,UAAM,SAAS,KAAK,cAAc,IAAI,QAAQ;AAC9C,SAAI,iCAAQ,UAAS,YAAY;AAC/B,WAAK,cAAc,OAAO,MAAM;AAAA,IAClC;AACA,SAAK,cAAc,OAAO,QAAQ;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAY,UAAwC;AAChE,UAAM,UAAU,KAAK,aAAa,IAAI,QAAQ;AAC9C,UAAM,UAAU,KAAK,SAAS,IAAI,QAAQ;AAC1C,SAAI,mCAAS,UAAS,eAAe;AACnC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,UAAU,QAAQ,MAAM;AAAA,QACxB,iBAAiB,kCAAc;AAAA,QAC/B,WAAW,QAAQ,MAAM;AAAA,MAC3B;AAAA,IACF;AACA,QAAI,SAAS;AACX,cAAI,+BAAe,QAAQ,WAAW,KAAK,IAAI,CAAC,GAAG;AACjD,aAAK,SAAS,OAAO,QAAQ;AAC7B,eAAO,EAAE,QAAQ,UAAU,QAAQ,4DAAuD;AAAA,MAC5F;AACA,aAAO,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,KAAK,MAAM,QAAQ,KAAK;AAAA,IAC1E;AACA,QAAI,SAAS;AACX,aAAO,EAAE,QAAQ,UAAU,QAAQ,QAAQ;AAAA,IAC7C;AACA,WAAQ,MAAM,KAAK,WAAW,QAAQ,EAAE,KAAK,IAAK,EAAE,QAAQ,YAAY,IAAI,EAAE,QAAQ,aAAa;AAAA,EACrG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,QAAQ,UAAwC;AAC5D,UAAM,KAAK,WAAW,QAAQ,EAAE,MAAM;AACtC,SAAK,SAAS,OAAO,QAAQ;AAC7B,SAAK,aAAa,OAAO,QAAQ;AACjC,SAAK,iBAAiB,QAAQ;AAC9B,WAAO,EAAE,QAAQ,aAAa;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,WAAW,UAA8B;AAC/C,QAAI,QAAQ,KAAK,YAAY,IAAI,QAAQ;AACzC,QAAI,CAAC,OAAO;AACV,cAAQ,KAAK,eAAe,QAAQ;AACpC,WAAK,YAAY,IAAI,UAAU,KAAK;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,eAAe,UAA8B;AACnD,UAAM,MAAM,MAAM,2BAA2B,IAAI;AACjD,UAAM,WAAO,uBAAK,KAAK,UAAU,QAAQ,OAAO;AAChD,QAAI,SAA0B;AAC9B,QAAI,OAAO;AACX,WAAO;AAAA,MACL,MAAM,YAAsC;AAC1C,YAAI,CAAC,MAAM;AACT,mBAAS,MAAM,KAAK,cAAc,MAAM,QAAQ;AAChD,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAAA,MACA,MAAM,OAAO,WAAoC;AAC/C,kBAAM,uBAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,kBAAM,2BAAU,MAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,CAAC,GAAG,MAAM;AAClE,iBAAS;AACT,eAAO;AAAA,MACT;AAAA,MACA,OAAO,YAA2B;AAChC,iBAAS;AACT,eAAO;AACP,kBAAM,wBAAO,IAAI,EAAE,MAAM,MAAM;AAAA,QAE/B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAc,cAAc,MAAc,UAA4C;AAncxF;AAocI,QAAI;AACJ,QAAI;AACF,YAAM,UAAM,0BAAS,MAAM,MAAM;AAAA,IACnC,SAAS,GAAG;AACV,WAAK,uBAA6B,UAAS,UAAU;AACnD,aAAK,IAAI,KAAK,IAAG,mCAAe,QAAQ,MAAvB,YAA4B,QAAQ,qCAAqC,OAAO,CAAC,CAAC,GAAG;AAAA,MACxG;AACA,aAAO;AAAA,IACT;AACA,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK,QAAQ,GAAG,CAAC;AAC3C,UAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,OAAO,iBAAiB,UAAU;AACrF,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAC9C;AACA,aAAO;AAAA,QACL,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,WAAW,OAAO,OAAO,SAAS,KAAK;AAAA,QACvC,YAAY,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;AAAA,MAC1E;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI;AAAA,QACP,IAAG,mCAAe,QAAQ,MAAvB,YAA4B,QAAQ,wCACrC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAC3C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,oBAAmC;AAC/C,UAAM,MAAM,MAAM,2BAA2B,IAAI;AACjD,UAAM,aAAS,uBAAK,KAAK,wBAAwB;AACjD,QAAI;AACF,gBAAM,0BAAS,QAAQ,MAAM;AAC7B;AAAA,IACF,QAAQ;AAAA,IAER;AACA,eAAW,UAAU,CAAC,6BAA6B,2BAA2B,GAAG;AAC/E,UAAI;AACF,kBAAM,4BAAO,uBAAK,KAAK,MAAM,GAAG,MAAM;AACtC,aAAK,IAAI,KAAK,4DAA4D;AAC1E;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,MAAc,UAAyB;AACrC,QAAI;AACF,YAAM,eAAW,mCAAc,KAAK,OAAO,QAAQ;AACnD,YAAM,eAAW,uCAAkB,KAAK,OAAO,YAAY;AAC3D,YAAM,KAAK,kBAAkB;AAG7B,YAAM,KAAK,uBAAuB;AAClC,YAAM,KAAK,oBAAoB;AAC/B,YAAM,UAAU,MAAM,KAAK,oBAAoB,QAAQ;AACvD,UAAI,UAAU,GAAG;AACf,aAAK,IAAI,KAAK,gCAAgC,OAAO,+BAA+B;AAAA,MACtF;AACA,UAAI,SAAS,WAAW,GAAG;AACzB,aAAK,IAAI,KAAK,wEAAmE;AACjF,cAAM,KAAK,SAAS,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAChE;AAAA,MACF;AACA,YAAM,YAAY,oBAAI,IAA2B;AACjD,iBAAW,WAAW,UAAU;AAC9B,cAAM,WAAW,MAAM,KAAK,aAAa,OAAO;AAChD,YAAI,UAAU;AACZ,oBAAU,IAAI,QAAQ,IAAI,QAAQ;AAAA,QACpC;AAAA,MACF;AACA,WAAK,SAAS,IAAI,8BAAW,UAAU,WAAW,UAAU;AAAA,QAC1D,cAAc,OAAM,QAAO;AACzB,gBAAM,KAAK,aAAa,IAAI,IAAI,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAiC,QAAQ,CAAC,EAAE,CAAC;AAC3G,cAAI,IAAI,SAAS,WAAW,CAAC,KAAK,cAAc,IAAI,IAAI,EAAE,GAAG;AAC3D,iBAAK,cAAc,IAAI,IAAI,EAAE;AAC7B,iBAAK;AAAA,UACP;AAAA,QACF;AAAA,QACA,UAAU,CAAC,IAAI,UAAU;AACvB,eAAK,KAAK,SAAS,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,UAE9D,CAAC;AAAA,QACH;AAAA,QACA,iBAAiB,OAAO,IAAI,UAAU;AAEpC,gBAAM,KAAK,qBAAqB,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,UAE3E,CAAC;AAAA,QACH;AAAA,QACA,cAAc,OAAM,OAAM;AACxB,cAAI;AACF,kBAAM,KAAK,eAAe,IAAI,EAAE,WAAW,KAAK,CAAC;AACjD,gBAAI,KAAK,cAAc,OAAO,EAAE,GAAG;AACjC,mBAAK;AAAA,YACP;AAAA,UACF,SAAS,GAAG;AACV,iBAAK,IAAI,MAAM,oBAAoB,EAAE,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,UACxF;AAAA,QACF;AAAA,QACA,cAAc,OAAM,WAAU;AApjBtC;AAqjBU,gBAAM,QAAQ,GAAG,KAAK,SAAS,IAAI,MAAM;AACzC,gBAAM,OAAO,MAAM,KAAK,mBAAmB,UAAU,SAAS;AAAA,YAC5D,UAAU;AAAA,YACV,QAAQ,GAAG,KAAK;AAAA,UAClB,CAAC;AACD,mBAAQ,kCAAM,SAAN,YAAc,CAAC,GAAG,IAAI,SAAO,IAAI,GAAG,UAAU,KAAK,UAAU,SAAS,CAAC,CAAC;AAAA,QAClF;AAAA,QACA,UAAU,CAAC,IAAI,QAAqB,EAAE,MAAM,YAAY,QAAQ,KAAK,YAAY,IAAI,EAAE,EAAE;AAAA,QACzF,cAAc,CAAC,IAAI,QAAqB,EAAE,MAAM,WAAW,QAAQ,KAAK,WAAW,IAAI,EAAE,EAAE;AAAA,QAC3F,QAAQ,YAAU;AAChB,gBAAM,QAAQ;AACd,cAAI,MAAM,SAAS,YAAY;AAC7B,iBAAK,cAAc,MAAM,MAAM;AAAA,UACjC,OAAO;AACL,iBAAK,aAAa,MAAM,MAAM;AAAA,UAChC;AAAA,QACF;AAAA,QACA,KAAK,MAAM,KAAK,IAAI;AAAA,QACpB,KAAK;AAAA,UACH,OAAO,OAAK,KAAK,IAAI,MAAM,CAAC;AAAA,UAC5B,MAAM,OAAK,KAAK,IAAI,KAAK,CAAC;AAAA,UAC1B,MAAM,OAAK,KAAK,IAAI,KAAK,CAAC;AAAA,UAC1B,OAAO,OAAK,KAAK,IAAI,MAAM,CAAC;AAAA,QAC9B;AAAA,QACA,iBAAiB,MAAM,KAAK,oBAAoB;AAAA,QAChD,QAAQ,KAAK,OAAO,gBAChB,CAAC,UAAU,YACT,KAAK,KAAK,qBAAqB,YAAY,sBAAsB,OAAO,EAAE;AAAA,UAAM,OAC9E,KAAK,IAAI,MAAM,iCAAiC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,QAC9F,IACF;AAAA,MACN,CAAC;AACD,YAAM,KAAK,OAAO,MAAM;AACxB,WAAK,IAAI,KAAK,cAAc,UAAU,IAAI,OAAO,SAAS,MAAM,iCAAiC,QAAQ,IAAI;AAAA,IAC/G,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,mBAAmB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,IAChF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,aAAa,SAA4D;AACrF,YAAQ,QAAQ,UAAU;AAAA,MACxB,KAAK;AACH,mBAAO,qCAAkB,KAAK,WAAW,QAAQ,QAAQ,GAAG,oBAAQ;AAAA,MACtE,KAAK;AACH,mBAAO,uCAAmB,KAAK,WAAW,QAAQ,QAAQ,GAAG,oBAAQ;AAAA,MACvE,KAAK;AACH,mBAAO,qCAAkB,KAAK,WAAW,QAAQ,QAAQ,GAAG,sBAAU,oBAAQ;AAAA,MAChF,KAAK,cAAc;AACjB,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,sCAAmB,GAAG,IAAI;AAAA,MACzC;AAAA,MACA,KAAK,YAAY;AACf,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,kCAAiB,GAAG,IAAI;AAAA,MACvC;AAAA,MACA,KAAK,UAAU;AACb,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,8BAAe,GAAG,IAAI;AAAA,MACrC;AAAA,MACA,KAAK,iBAAiB;AACpB,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,2CAAqB,GAAG,IAAI;AAAA,MAC3C;AAAA,MACA;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,WAAW,SAAqD;AAC5E,QAAI,CAAC,QAAQ,cAAc;AACzB,WAAK,IAAI,KAAK,GAAG,QAAQ,IAAI,mEAA8D;AAC3F,aAAO;AAAA,IACT;AACA,QAAI;AACF,YAAM,aAAa,MAAM,gCAAY,eAAe,MAAM,QAAQ,YAAY;AAC9E,YAAM,SAAS,WAAW;AAC1B,YAAM,MAAM,OAAO,OAAO,QAAQ,YAAY,OAAO,MAAM,OAAO,MAAM;AACxE,UAAI,CAAC,KAAK;AACR,aAAK,IAAI,KAAK,GAAG,QAAQ,IAAI,gBAAgB,QAAQ,YAAY,qBAAqB;AAAA,MACxF;AACA,aAAO;AAAA,IACT,SAAS,GAAG;AACV,WAAK,IAAI;AAAA,QACP,GAAG,QAAQ,IAAI,4BAA4B,QAAQ,YAAY,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,MAChH;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAwB,sBAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,oBAAoB,UAAqD;AACrF,QAAI,UAAU;AACd,eAAW,WAAW,UAAU;AAC9B,iBAAW,UAAU,eAAe,qBAAqB;AACvD,cAAM,KAAK,GAAG,QAAQ,EAAE,IAAI,MAAM;AAClC,YAAI,CAAC,KAAK,cAAc,IAAI,EAAE,GAAG;AAC/B;AAAA,QACF;AACA,YAAI;AACF,gBAAM,KAAK,eAAe,EAAE;AAC5B;AAIA,eAAK,cAAc,OAAO,EAAE;AAAA,QAC9B,SAAS,GAAG;AACV,eAAK,IAAI,MAAM,oBAAoB,EAAE,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,sBAAqC;AACjD,UAAM,cAAU,mCAAc,KAAK,OAAO,QAAQ,EAAE,IAAI,aAAW,QAAQ,EAAE;AAC7E,QAAI,QAAQ,WAAW,GAAG;AACxB;AAAA,IACF;AACA,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,SAAS,QAAQ,OAAO,CAAC;AAClD,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,uBAAuB;AAClD,YAAM,QAAQ,oBAAI,IAAY;AAC9B,iBAAW,MAAM,OAAO,KAAK,OAAO,GAAG;AACrC,cAAM,OAAO,GAAG,UAAU,KAAK,UAAU,SAAS,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE,YAAI,QAAQ,CAAC,KAAK,IAAI,IAAI,GAAG;AAC3B,gBAAM,IAAI,IAAI;AAAA,QAChB;AAAA,MACF;AACA,iBAAW,QAAQ,OAAO;AACxB,aAAK,IAAI;AAAA,UACP,SAAS,SACL,2FACA,qDAAqD,IAAI;AAAA,QAC/D;AAEA,mBAAW,MAAM,CAAC,GAAG,KAAK,aAAa,GAAG;AACxC,cAAI,OAAO,QAAQ,GAAG,WAAW,GAAG,IAAI,GAAG,GAAG;AAC5C,iBAAK,cAAc,OAAO,EAAE;AAC5B,iBAAK;AAAA,UACP;AAAA,QACF;AACA,cAAM,KAAK,eAAe,MAAM,EAAE,WAAW,KAAK,CAAC;AAAA,MACrD;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,KAAK,oCAAoC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,IAChG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBQ,SAAS,UAA4B;AAxwB/C;AAywBI,QAAI;AACF,iBAAW,YAAY,CAAC,GAAG,KAAK,cAAc,KAAK,CAAC,GAAG;AACrD,aAAK,iBAAiB,QAAQ;AAAA,MAChC;AACA,YAAM,SAAS,KAAK;AACpB,iBAAK,WAAL,mBAAa;AACb,WAAK,SAAS;AACd,aAAM,sCAAQ,qBAAR,YAA4B,KAAK,SAAS,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,GACzF,KAAK,MAAM,KAAK,IAAI,MAAM,gCAAgC,CAAC,EAC3D,MAAM,OAAK,KAAK,IAAI,MAAM,0CAAqC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE,CAAC,EAC5G,QAAQ,QAAQ;AAAA,IACrB,QAAQ;AACN,eAAS;AAAA,IACX;AAAA,EACF;AACF;AAEA,IAAI,QAAQ,SAAS,QAAQ;AAE3B,SAAO,UAAU,CAAC,YAAuD,IAAI,eAAe,OAAO;AACrG,OAAO;AACL,GAAC,MAAM,IAAI,eAAe,GAAG;AAC/B;",
|
|
4
|
+
"sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { Credentials } from \"@iobroker/adapter-core\";\nimport { mkdir, readFile, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { postForm, postJson } from \"./lib/http\";\nimport { PollEngine } from \"./lib/poll-engine\";\nimport {\n clampPollInterval,\n datapointBalanceLine,\n parseAccounts,\n SUBSCRIPTION_IDS,\n type AccountConfig,\n} from \"./lib/pure-helpers\";\nimport type { TokenSet, TokenStore, UsageProvider } from \"./lib/provider\";\nimport { SIGN_IN_FLOWS, SIGN_IN_LABELS, attemptExpired, type SignInState } from \"./lib/sign-in\";\nimport { buildAuthorizeUrl, exchangeCode, generatePkce, type PkcePair } from \"./lib/providers/claude-auth\";\nimport { claudeSubProvider } from \"./lib/providers/claude-sub\";\nimport {\n CHATGPT_OAUTH,\n exchangeDeviceCode,\n pollDeviceCode,\n startDeviceCode,\n type DeviceCodeStart,\n} from \"./lib/providers/chatgpt-auth\";\nimport { chatgptSubProvider } from \"./lib/providers/chatgpt-sub\";\nimport {\n buildGeminiAuthorizeUrl,\n exchangeGeminiCode,\n extractGeminiCode,\n generateGeminiPkce,\n type GeminiPkce,\n} from \"./lib/providers/gemini-auth\";\nimport { geminiSubProvider } from \"./lib/providers/gemini-sub\";\nimport { anthropicApiProvider } from \"./lib/providers/anthropic-api\";\nimport { deepSeekProvider } from \"./lib/providers/deepseek\";\nimport { openAiProvider } from \"./lib/providers/openai\";\nimport { openRouterProvider } from \"./lib/providers/openrouter\";\n\n/** A cancellable handle: interval or timeout \u2014 the engine treats them uniformly. */\ntype TimerHandle =\n | { kind: \"interval\"; handle: ioBroker.Interval | undefined }\n | { kind: \"timeout\"; handle: ioBroker.Timeout | undefined };\n\n/** One running sign-in attempt (secrets live in memory only, never on disk). */\ntype Attempt =\n | { flow: \"paste-code\"; pkce: PkcePair; url: string; expiresAt: number }\n | { flow: \"paste-url\"; pkce: GeminiPkce; url: string; expiresAt: number }\n | { flow: \"device-code\"; start: DeviceCodeStart };\n\n/**\n * AI Usage adapter \u2014 reads usage windows, credits and costs of AI accounts into\n * read-only states. Three subscriptions sign in with the user's own account\n * (Claude, ChatGPT, Google), the other accounts use a key from the admin's central\n * credential storage. Orchestration lives in the unit-tested {@link PollEngine};\n * this class wires ioBroker IO, the sign-in flows and the token files to it.\n */\nexport class AiUsageAdapter extends utils.Adapter {\n private engine: PollEngine | null = null;\n /** Running sign-in attempts, keyed by provider kind. */\n private readonly attempts = new Map<string, Attempt>();\n /** Last failure reason per provider, shown in the admin row. */\n private readonly signInErrors = new Map<string, string>();\n /** Device-code pollers, so they can be stopped on unload. */\n private readonly devicePollers = new Map<string, TimerHandle>();\n /** One token store per subscription \u2014 see {@link tokenStore} for why it is shared. */\n private readonly tokenStores = new Map<string, TokenStore>();\n\n /**\n * Every state id that already existed when this process started.\n *\n * The create path runs `extendObject` for every state once per process \u2014 also for\n * states that were already in the database \u2014 so \"the create path touched it\" would\n * report every datapoint as new after each restart. Only what is missing from this\n * snapshot is a real addition (beszel pattern).\n */\n private knownStateIds = new Set<string>();\n /** Datapoints created since the snapshot. */\n private createdStates = 0;\n /** Datapoints removed since the snapshot \u2014 excluding the one-shot migration, which reports itself. */\n private removedStates = 0;\n /** Whether the startup balance was already logged. */\n private balanceLogged = false;\n\n /**\n * Read every existing state id once, before anything creates or deletes.\n *\n * @returns nothing; fills {@link knownStateIds}\n */\n private async snapshotExistingStates(): Promise<void> {\n try {\n const view = await this.getObjectViewAsync(\"system\", \"state\", {\n startkey: `${this.namespace}.`,\n endkey: `${this.namespace}.\\uFFFF`,\n });\n for (const row of view?.rows ?? []) {\n this.knownStateIds.add(row.id.substring(this.namespace.length + 1));\n }\n } catch (e) {\n this.log.debug(`Could not snapshot existing states: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Report what the object tree gained and lost in this startup \u2014 one line, both\n * sides, silent when nothing changed. A normal restart must stay quiet.\n */\n private logDatapointBalance(): void {\n if (this.balanceLogged) {\n return;\n }\n this.balanceLogged = true;\n const line = datapointBalanceLine(this.createdStates, this.removedStates);\n if (line) {\n this.log.info(line);\n }\n }\n\n /**\n * @param options the adapter options\n */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({ ...options, name: \"ai-usage\" });\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"message\", this.onMessage.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n }\n\n // ---------------------------------------------------------------- sign-in\n\n /**\n * Handle admin messages: the three sign-in flows plus their status.\n *\n * @param obj the message\n */\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n try {\n const provider = this.providerFrom(obj.message);\n switch (obj.command) {\n case \"signInStart\":\n this.respond(obj, provider ? await this.startSignIn(provider) : { error: \"unknown provider\" });\n return;\n case \"signInSubmit\":\n this.respond(obj, provider ? await this.submitSignIn(provider, obj.message) : { error: \"unknown provider\" });\n return;\n case \"signInStatus\":\n this.respond(obj, provider ? await this.signInState(provider) : { error: \"unknown provider\" });\n return;\n case \"signOut\":\n this.respond(obj, provider ? await this.signOut(provider) : { error: \"unknown provider\" });\n return;\n default:\n // Always answer, or the caller's callback dangles until timeout.\n this.respond(obj, { error: `Unknown command: ${obj.command}` });\n }\n } catch (e) {\n this.log.error(`onMessage failed: ${e instanceof Error ? e.message : String(e)}`);\n this.respond(obj, { error: \"internal error \u2014 see log\" });\n }\n }\n\n /**\n * Send a message response, when the caller expects one.\n *\n * @param obj the request message\n * @param response the response payload\n */\n private respond(obj: ioBroker.Message, response: unknown): void {\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, response, obj.callback);\n }\n }\n\n /**\n * The provider kind named in a message, if it is a subscription we know.\n *\n * @param message the message payload ({ provider })\n * @returns the provider kind, or undefined\n */\n private providerFrom(message: unknown): string | undefined {\n const value =\n typeof (message as { provider?: unknown })?.provider === \"string\"\n ? (message as { provider: string }).provider\n : \"\";\n return SIGN_IN_FLOWS[value] ? value : undefined;\n }\n\n /**\n * Begin a sign-in: build the link (Claude/Google) or fetch a device code (ChatGPT).\n *\n * @param provider the subscription kind\n * @returns what the admin panel has to show\n */\n private async startSignIn(provider: string): Promise<SignInState | { error: string }> {\n this.signInErrors.delete(provider);\n this.stopDevicePoller(provider);\n const flow = SIGN_IN_FLOWS[provider];\n const now = Date.now();\n try {\n if (flow === \"paste-code\") {\n const pkce = generatePkce();\n const url = buildAuthorizeUrl(pkce);\n this.attempts.set(provider, { flow, pkce, url, expiresAt: now + 15 * 60_000 });\n return { status: \"awaiting-paste\", url, flow };\n }\n if (flow === \"paste-url\") {\n const pkce = generateGeminiPkce();\n const url = buildGeminiAuthorizeUrl(pkce);\n this.attempts.set(provider, { flow, pkce, url, expiresAt: now + 15 * 60_000 });\n return { status: \"awaiting-paste\", url, flow };\n }\n const start = await startDeviceCode(postJson, now);\n this.attempts.set(provider, { flow: \"device-code\", start });\n this.armDevicePoller(provider, start);\n return {\n status: \"awaiting-device\",\n userCode: start.userCode,\n verificationUrl: CHATGPT_OAUTH.verificationUrl,\n expiresAt: start.expiresAt,\n };\n } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n this.signInErrors.set(provider, reason);\n return { status: \"failed\", reason };\n }\n }\n\n /**\n * Finish a paste-based sign-in (Claude code, Google address).\n *\n * @param provider the subscription kind\n * @param message the message payload ({ value })\n * @returns the resulting state\n */\n private async submitSignIn(provider: string, message: unknown): Promise<SignInState> {\n const attempt = this.attempts.get(provider);\n const value =\n typeof (message as { value?: unknown })?.value === \"string\" ? (message as { value: string }).value.trim() : \"\";\n if (!attempt || attempt.flow === \"device-code\") {\n return { status: \"failed\", reason: \"start the sign-in first\" };\n }\n // The 15-minute window was stored but never enforced \u2014 a stale attempt used to\n // fail with the provider's own cryptic answer instead of a clear instruction.\n if (attemptExpired(attempt.expiresAt, Date.now())) {\n this.attempts.delete(provider);\n return { status: \"failed\", reason: \"the sign-in window expired \u2014 start the sign-in again\" };\n }\n if (!value) {\n return { status: \"failed\", reason: \"nothing pasted\" };\n }\n try {\n const now = Date.now();\n const tokens =\n attempt.flow === \"paste-code\"\n ? await exchangeCode(value, attempt.pkce, postJson, now)\n : await exchangeGeminiCode(extractGeminiCode(value, attempt.pkce.state), attempt.pkce, postForm, now);\n await this.finishSignIn(provider, tokens);\n return { status: \"signed-in\" };\n } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n this.signInErrors.set(provider, reason);\n return { status: \"failed\", reason };\n }\n }\n\n /**\n * Store fresh tokens, mark the account signed in and poll it right away \u2014 waiting\n * up to a full interval after a successful sign-in reads as \"it did not work\".\n *\n * @param provider the subscription kind\n * @param tokens the token set\n */\n private async finishSignIn(provider: string, tokens: TokenSet): Promise<void> {\n await this.tokenStore(provider).save(tokens);\n this.attempts.delete(provider);\n this.signInErrors.delete(provider);\n this.stopDevicePoller(provider);\n const id = SUBSCRIPTION_IDS[provider];\n if (id) {\n // Started, not awaited: the settings page must confirm the sign-in at once,\n // and a first query that has to wait for a provider can take up to the full\n // request timeout. The values arrive a moment later on their own.\n void this.engine?.pollNow(id).catch(e => {\n this.log.debug(`First query after sign-in failed: ${e instanceof Error ? e.message : String(e)}`);\n });\n }\n this.log.info(`${SIGN_IN_LABELS[provider] ?? provider}: signed in`);\n }\n\n /**\n * Poll the device-code endpoint until the user confirmed, the window closed or\n * the adapter stops. The handle lives in memory only \u2014 a restart mid-flow just\n * means the user starts again, which is cheaper than persisting a 15-minute secret.\n *\n * @param provider the subscription kind\n * @param start the device-code handle\n */\n private armDevicePoller(provider: string, start: DeviceCodeStart): void {\n const tick = async (): Promise<void> => {\n try {\n if (attemptExpired(start.expiresAt, Date.now())) {\n this.stopDevicePoller(provider);\n this.attempts.delete(provider);\n this.signInErrors.set(provider, \"the code expired \u2014 start the sign-in again\");\n return;\n }\n const result = await pollDeviceCode(start, postJson);\n if (result.status === \"ready\") {\n this.stopDevicePoller(provider);\n const tokens = await exchangeDeviceCode(result.code, result.codeVerifier, postForm, Date.now());\n await this.finishSignIn(provider, tokens);\n }\n } catch (e) {\n this.stopDevicePoller(provider);\n this.attempts.delete(provider);\n this.signInErrors.set(provider, e instanceof Error ? e.message : String(e));\n }\n };\n this.devicePollers.set(provider, {\n kind: \"interval\",\n handle: this.setInterval(() => void tick(), Math.max(start.intervalSec, 1) * 1000),\n });\n }\n\n /**\n * Stop a running device-code poller.\n *\n * @param provider the subscription kind\n */\n private stopDevicePoller(provider: string): void {\n const handle = this.devicePollers.get(provider);\n if (handle?.kind === \"interval\") {\n this.clearInterval(handle.handle);\n }\n this.devicePollers.delete(provider);\n }\n\n /**\n * The current sign-in state of one subscription, for the admin row.\n *\n * @param provider the subscription kind\n * @returns the state\n */\n private async signInState(provider: string): Promise<SignInState> {\n const failure = this.signInErrors.get(provider);\n const attempt = this.attempts.get(provider);\n if (attempt?.flow === \"device-code\") {\n return {\n status: \"awaiting-device\",\n userCode: attempt.start.userCode,\n verificationUrl: CHATGPT_OAUTH.verificationUrl,\n expiresAt: attempt.start.expiresAt,\n };\n }\n if (attempt) {\n if (attemptExpired(attempt.expiresAt, Date.now())) {\n this.attempts.delete(provider);\n return { status: \"failed\", reason: \"the sign-in window expired \u2014 start the sign-in again\" };\n }\n return { status: \"awaiting-paste\", url: attempt.url, flow: attempt.flow };\n }\n if (failure) {\n return { status: \"failed\", reason: failure };\n }\n return (await this.tokenStore(provider).load()) ? { status: \"signed-in\" } : { status: \"signed-out\" };\n }\n\n /**\n * Forget the tokens of one subscription.\n *\n * @param provider the subscription kind\n * @returns the resulting state\n */\n private async signOut(provider: string): Promise<SignInState> {\n await this.tokenStore(provider).clear();\n this.attempts.delete(provider);\n this.signInErrors.delete(provider);\n this.stopDevicePoller(provider);\n return { status: \"signed-out\" };\n }\n\n // ------------------------------------------------------------ token files\n\n /**\n * The token store of one subscription \u2014 created ONCE per provider.\n *\n * The identity matters: the store holds the in-memory copy of the tokens, so\n * signing out really takes effect. When each provider module kept its own copy,\n * a sign-out deleted the file while the adapter kept polling with what it still\n * had \u2014 and the next token refresh wrote the deleted file back.\n *\n * @param provider the subscription kind\n * @returns the store\n */\n private tokenStore(provider: string): TokenStore {\n let store = this.tokenStores.get(provider);\n if (!store) {\n store = this.makeTokenStore(provider);\n this.tokenStores.set(provider, store);\n }\n return store;\n }\n\n /**\n * Build the store for one subscription: an encrypted file in the instance data\n * directory, named after the PROVIDER. Keying by provider (not by account name)\n * keeps a sign-in alive when an account is renamed \u2014 the previous scheme lost it.\n *\n * @param provider the subscription kind\n * @returns the store\n */\n private makeTokenStore(provider: string): TokenStore {\n const dir = utils.getAbsoluteInstanceDataDir(this);\n const file = join(dir, `tokens-${provider}.json`);\n let cached: TokenSet | null = null;\n let read = false;\n return {\n load: async (): Promise<TokenSet | null> => {\n if (!read) {\n cached = await this.readTokenFile(file, provider);\n read = true;\n }\n return cached;\n },\n save: async (tokens: TokenSet): Promise<void> => {\n await mkdir(dir, { recursive: true });\n await writeFile(file, this.encrypt(JSON.stringify(tokens)), \"utf8\");\n cached = tokens;\n read = true;\n },\n clear: async (): Promise<void> => {\n cached = null;\n read = true;\n await unlink(file).catch(() => {\n /* already gone */\n });\n },\n };\n }\n\n /**\n * Read one token file.\n *\n * A missing file means \"never signed in\" and is silent. A file that is there but\n * cannot be read \u2014 damaged, or encrypted with a different instance secret \u2014 is\n * NOT the same thing: without a word in the log, signing in would look like it\n * simply does nothing.\n *\n * @param file the file path\n * @param provider the subscription kind (for the log line)\n * @returns the tokens, or null\n */\n private async readTokenFile(file: string, provider: string): Promise<TokenSet | null> {\n let raw: string;\n try {\n raw = await readFile(file, \"utf8\");\n } catch (e) {\n if ((e as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n this.log.warn(`${SIGN_IN_LABELS[provider] ?? provider}: cannot open the stored sign-in (${String(e)})`);\n }\n return null; // never signed in \u2014 the provider reports auth-required\n }\n try {\n const parsed = JSON.parse(this.decrypt(raw)) as Partial<TokenSet>;\n if (typeof parsed.accessToken !== \"string\" || typeof parsed.refreshToken !== \"string\") {\n throw new Error(\"the file carries no tokens\");\n }\n return {\n accessToken: parsed.accessToken,\n refreshToken: parsed.refreshToken,\n expiresAt: Number(parsed.expiresAt) || 0,\n accountRef: typeof parsed.accountRef === \"string\" ? parsed.accountRef : undefined,\n };\n } catch (e) {\n this.log.warn(\n `${SIGN_IN_LABELS[provider] ?? provider}: the stored sign-in cannot be read (${\n e instanceof Error ? e.message : String(e)\n }) \u2014 sign in again in the instance settings`,\n );\n return null;\n }\n }\n\n /**\n * Carry a sign-in from the pre-0.3.0 layout over: tokens used to be stored per\n * ACCOUNT NAME (`claude-tokens-<name>.json`). Without this the rename to fixed\n * account ids would silently sign the user out.\n */\n private async migrateTokenFiles(): Promise<void> {\n const dir = utils.getAbsoluteInstanceDataDir(this);\n const target = join(dir, \"tokens-claude-sub.json\");\n try {\n await readFile(target, \"utf8\");\n return; // already migrated\n } catch {\n // not there yet \u2014 look for an old file\n }\n for (const legacy of [\"claude-tokens-Claude.json\", \"claude-tokens-claude.json\"]) {\n try {\n await rename(join(dir, legacy), target);\n this.log.info(\"Carried the existing Claude sign-in over to the new layout\");\n return;\n } catch {\n // try the next candidate\n }\n }\n }\n\n // ------------------------------------------------------------- life cycle\n\n /** Validate the configuration, clean up stale objects and start the engine. */\n /**\n * Clear a leftover `supportedMessages.stopInstance` from THIS instance's object.\n *\n * The entry lives in two places: in the adapter's manifest, and as a copy in the\n * instance object in the database. An update merges the manifest into that copy \u2014\n * it never removes a field. So on every installation that ran a version carrying\n * the entry, the host keeps killing the process outright and `onUnload` still never\n * runs: the update alone changes nothing (found by a second pair of eyes on the\n * live server 2026-08-27, after my own test had been contaminated by a value I had\n * set by hand).\n *\n * Writing the instance object makes the host restart this instance once. That is\n * the price, it happens on the first start after the update and never again \u2014\n * afterwards the condition below is false. public-holidays corrects its own run\n * mode the same way.\n */\n private async clearStopInstanceFlag(): Promise<void> {\n const id = `system.adapter.${this.namespace}`;\n try {\n const obj = await this.getForeignObjectAsync(id);\n const supported = obj?.common?.supportedMessages as { stopInstance?: unknown } | undefined;\n if (!supported?.stopInstance) {\n return;\n }\n this.log.info(\"Correcting a leftover setting from an earlier version \u2014 this instance restarts once\");\n await this.extendForeignObjectAsync(id, { common: { supportedMessages: { stopInstance: false } } });\n } catch (e) {\n this.log.debug(`Could not check the instance object: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n private async onReady(): Promise<void> {\n try {\n // First: without this the whole shutdown path stays dead on an updated install.\n await this.clearStopInstanceFlag();\n const accounts = parseAccounts(this.config.accounts);\n const interval = clampPollInterval(this.config.pollInterval);\n await this.migrateTokenFiles();\n // Baseline first: the cleanup deletes and the engine creates, both are counted\n // against this snapshot.\n await this.snapshotExistingStates();\n await this.cleanupStaleObjects();\n const retired = await this.removeRetiredStates(accounts);\n if (retired > 0) {\n this.log.info(`Object tree updated: removed ${retired} obsolete status datapoint(s)`);\n }\n if (accounts.length === 0) {\n this.log.info(\"No AI accounts configured \u2014 add accounts in the instance settings\");\n await this.setState(\"info.connection\", { val: false, ack: true });\n return;\n }\n const providers = new Map<string, UsageProvider>();\n for (const account of accounts) {\n const provider = await this.makeProvider(account);\n if (provider) {\n providers.set(account.id, provider);\n }\n }\n this.engine = new PollEngine(accounts, providers, interval, {\n upsertObject: async def => {\n await this.extendObject(def.id, { type: def.type, common: def.common as ioBroker.ObjectCommon, native: {} });\n if (def.type === \"state\" && !this.knownStateIds.has(def.id)) {\n this.knownStateIds.add(def.id);\n this.createdStates++;\n }\n },\n setState: (id, value) => {\n void this.setState(id, { val: value, ack: true }).catch(() => {\n /* states DB going down \u2014 never crash the poll loop */\n });\n },\n setStateChanged: async (id, value) => {\n // Awaited only by the shutdown path; the poll loop drops the promise.\n await this.setStateChangedAsync(id, { val: value, ack: true }).catch(() => {\n /* states DB going down \u2014 never crash the poll loop */\n });\n },\n deleteObject: async id => {\n try {\n await this.delObjectAsync(id, { recursive: true });\n if (this.knownStateIds.delete(id)) {\n this.removedStates++;\n }\n } catch (e) {\n this.log.debug(`Could not remove ${id}: ${e instanceof Error ? e.message : String(e)}`);\n }\n },\n listStateIds: async prefix => {\n const start = `${this.namespace}.${prefix}.`;\n const view = await this.getObjectViewAsync(\"system\", \"state\", {\n startkey: start,\n endkey: `${start}\uFFFF`,\n });\n return (view?.rows ?? []).map(row => row.id.substring(this.namespace.length + 1));\n },\n schedule: (cb, ms): TimerHandle => ({ kind: \"interval\", handle: this.setInterval(cb, ms) }),\n scheduleOnce: (cb, ms): TimerHandle => ({ kind: \"timeout\", handle: this.setTimeout(cb, ms) }),\n cancel: handle => {\n const timer = handle as TimerHandle;\n if (timer.kind === \"interval\") {\n this.clearInterval(timer.handle);\n } else {\n this.clearTimeout(timer.handle);\n }\n },\n now: () => Date.now(),\n log: {\n debug: m => this.log.debug(m),\n info: m => this.log.info(m),\n warn: m => this.log.warn(m),\n error: m => this.log.error(m),\n },\n afterFirstRound: () => this.logDatapointBalance(),\n notify: this.config.notifications\n ? (_account, message) =>\n void this.registerNotification(\"ai-usage\", \"userActionRequired\", message).catch(e =>\n this.log.debug(`Could not raise notification: ${e instanceof Error ? e.message : String(e)}`),\n )\n : undefined,\n });\n await this.engine.start();\n this.log.info(`Monitoring ${providers.size} of ${accounts.length} AI account(s), polling every ${interval} s`);\n } catch (e) {\n this.log.error(`Startup failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Build the provider for one account.\n *\n * @param account the validated account config\n * @returns the provider, or undefined to skip the account\n */\n private async makeProvider(account: AccountConfig): Promise<UsageProvider | undefined> {\n switch (account.provider) {\n case \"claude-sub\":\n return claudeSubProvider(this.tokenStore(account.provider), postJson);\n case \"chatgpt-sub\":\n return chatgptSubProvider(this.tokenStore(account.provider), postJson);\n case \"gemini-sub\":\n return geminiSubProvider(this.tokenStore(account.provider), postJson, postForm);\n case \"openrouter\": {\n const key = await this.resolveKey(account);\n return key ? openRouterProvider(key) : undefined;\n }\n case \"deepseek\": {\n const key = await this.resolveKey(account);\n return key ? deepSeekProvider(key) : undefined;\n }\n case \"openai\": {\n const key = await this.resolveKey(account);\n return key ? openAiProvider(key) : undefined;\n }\n case \"anthropic-api\": {\n const key = await this.resolveKey(account);\n return key ? anthropicApiProvider(key) : undefined;\n }\n default:\n return undefined;\n }\n }\n\n /**\n * Read and decrypt a key-form credential from the central credential storage.\n *\n * @param account the account whose credential to resolve\n * @returns the key, or undefined (with a log line) when it cannot be read\n */\n private async resolveKey(account: AccountConfig): Promise<string | undefined> {\n if (!account.credentialId) {\n this.log.warn(`${account.name}: no credential selected \u2014 pick one in the instance settings`);\n return undefined;\n }\n try {\n const credential = await Credentials.getCredentials(this, account.credentialId);\n const values = credential.values as { key?: unknown };\n const key = typeof values.key === \"string\" && values.key ? values.key : undefined;\n if (!key) {\n this.log.warn(`${account.name}: credential ${account.credentialId} carries no API key`);\n }\n return key;\n } catch (e) {\n this.log.warn(\n `${account.name}: cannot read credential ${account.credentialId} (${e instanceof Error ? e.message : String(e)})`,\n );\n return undefined;\n }\n }\n\n /**\n * Status states retired in 0.5.0 \u2014 `provider` (visible in the node name anyway),\n * `reachable`/`serviceOnline`/`state` (three spellings of one fact) and `signedIn`\n * (the settings page asks the adapter directly).\n *\n * ioBroker never garbage-collects an object whose id the adapter stopped writing:\n * it would sit there frozen on its last value and keep lying. So the old ids are\n * deleted from a fixed list \u2014 no state reading, no heuristics.\n */\n private static readonly RETIRED_INFO_STATES = [\n \"info.provider\",\n \"info.reachable\",\n \"info.serviceOnline\",\n \"info.state\",\n \"info.signedIn\",\n ];\n\n /**\n * Remove the retired status states of every configured account.\n *\n * Which of them still exist is answered by the startup snapshot that was read a\n * moment earlier \u2014 asking the database for every id on every start would keep\n * costing 35 lookups forever for a migration that is done after the first one.\n *\n * @param accounts the configured accounts\n * @returns how many objects were actually deleted\n */\n private async removeRetiredStates(accounts: readonly AccountConfig[]): Promise<number> {\n let removed = 0;\n for (const account of accounts) {\n for (const suffix of AiUsageAdapter.RETIRED_INFO_STATES) {\n const id = `${account.id}.${suffix}`;\n if (!this.knownStateIds.has(id)) {\n continue;\n }\n try {\n await this.delObjectAsync(id);\n removed++;\n // Deliberately NOT counted in the startup balance: this one-shot migration\n // reports its own sum, and the same deletion must not appear in two lines\n // that mean different things.\n this.knownStateIds.delete(id);\n } catch (e) {\n this.log.debug(`Could not remove ${id}: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n }\n return removed;\n }\n\n /**\n * Delete object trees that no longer belong to a configured account, plus the\n * `auth` branch of the pre-0.3.0 layout. An EMPTY table deletes nothing \u2014 the\n * guard against wiping everything through an accidental clear.\n */\n private async cleanupStaleObjects(): Promise<void> {\n const keepIds = parseAccounts(this.config.accounts).map(account => account.id);\n if (keepIds.length === 0) {\n return;\n }\n const keep = new Set([...keepIds, \"info\", \"total\"]);\n try {\n const objects = await this.getAdapterObjectsAsync();\n const roots = new Set<string>();\n for (const id of Object.keys(objects)) {\n const root = id.substring(this.namespace.length + 1).split(\".\")[0];\n if (root && !keep.has(root)) {\n roots.add(root);\n }\n }\n for (const root of roots) {\n this.log.info(\n root === \"auth\"\n ? \"Removing the old sign-in branch \u2014 the sign-in state now lives inside each account\"\n : `Removing objects of no longer configured account \"${root}\"`,\n );\n // Count the datapoints BEFORE they are gone \u2014 afterwards there is nothing to count.\n for (const id of [...this.knownStateIds]) {\n if (id === root || id.startsWith(`${root}.`)) {\n this.knownStateIds.delete(id);\n this.removedStates++;\n }\n }\n await this.delObjectAsync(root, { recursive: true });\n }\n } catch (e) {\n this.log.warn(`Cleanup of stale objects failed: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Tear down: cancel everything, then say that nothing is delivering any more.\n *\n * The final writes are AWAITED before `callback()` \u2014 measured on the live server\n * (2026-08-27), a fire-and-forget write followed by an immediate callback never\n * reached the database, so a switched-off instance kept showing every account as\n * online. The whole thing takes about 100 ms.\n *\n * Deliberately WITHOUT a time limit of its own: the adapter's timer API refuses to\n * arm once shutdown has begun, and the host already applies the only deadline that\n * matters \u2014 it ends the process a second after asking. A states database that\n * hangs would swallow the writes either way, so a second guard adds code, not\n * safety.\n *\n * None of this runs while `common.supportedMessages.stopInstance` sits in the\n * manifest: with it the host kills the process outright instead of asking, and\n * every state written here is dead code. A test pins that the entry stays out.\n *\n * @param callback invoked when cleanup is done\n */\n private onUnload(callback: () => void): void {\n try {\n for (const provider of [...this.devicePollers.keys()]) {\n this.stopDevicePoller(provider);\n }\n const engine = this.engine;\n this.engine?.stop();\n this.engine = null;\n void (engine?.markAllOffline() ?? this.setState(\"info.connection\", { val: false, ack: true }))\n .then(() => this.log.debug(\"Shutdown: final states written\"))\n .catch(e => this.log.debug(`Shutdown: final states rejected \u2014 ${e instanceof Error ? e.message : String(e)}`))\n .finally(callback);\n } catch {\n callback();\n }\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new AiUsageAdapter(options);\n} else {\n (() => new AiUsageAdapter())();\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAAuB;AACvB,0BAA4B;AAC5B,sBAA2D;AAC3D,uBAAqB;AACrB,kBAAmC;AACnC,yBAA2B;AAC3B,0BAMO;AAEP,qBAAgF;AAChF,yBAA6E;AAC7E,wBAAkC;AAClC,0BAMO;AACP,yBAAmC;AACnC,yBAMO;AACP,wBAAkC;AAClC,2BAAqC;AACrC,sBAAiC;AACjC,oBAA+B;AAC/B,wBAAmC;AAoB5B,MAAM,uBAAuB,MAAM,QAAQ;AAAA,EACxC,SAA4B;AAAA;AAAA,EAEnB,WAAW,oBAAI,IAAqB;AAAA;AAAA,EAEpC,eAAe,oBAAI,IAAoB;AAAA;AAAA,EAEvC,gBAAgB,oBAAI,IAAyB;AAAA;AAAA,EAE7C,cAAc,oBAAI,IAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUnD,gBAAgB,oBAAI,IAAY;AAAA;AAAA,EAEhC,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOxB,MAAc,yBAAwC;AAxFxD;AAyFI,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,mBAAmB,UAAU,SAAS;AAAA,QAC5D,UAAU,GAAG,KAAK,SAAS;AAAA,QAC3B,QAAQ,GAAG,KAAK,SAAS;AAAA,MAC3B,CAAC;AACD,iBAAW,QAAO,kCAAM,SAAN,YAAc,CAAC,GAAG;AAClC,aAAK,cAAc,IAAI,IAAI,GAAG,UAAU,KAAK,UAAU,SAAS,CAAC,CAAC;AAAA,MACpE;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,uCAAuC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,IACpG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAA4B;AAClC,QAAI,KAAK,eAAe;AACtB;AAAA,IACF;AACA,SAAK,gBAAgB;AACrB,UAAM,WAAO,0CAAqB,KAAK,eAAe,KAAK,aAAa;AACxE,QAAI,MAAM;AACR,WAAK,IAAI,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,YAAY,UAAyC,CAAC,GAAG;AAC9D,UAAM,EAAE,GAAG,SAAS,MAAM,WAAW,CAAC;AACtC,SAAK,GAAG,SAAS,KAAK,QAAQ,KAAK,IAAI,CAAC;AACxC,SAAK,GAAG,WAAW,KAAK,UAAU,KAAK,IAAI,CAAC;AAC5C,SAAK,GAAG,UAAU,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,UAAU,KAAsC;AAC5D,QAAI;AACF,YAAM,WAAW,KAAK,aAAa,IAAI,OAAO;AAC9C,cAAQ,IAAI,SAAS;AAAA,QACnB,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,YAAY,QAAQ,IAAI,EAAE,OAAO,mBAAmB,CAAC;AAC7F;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,aAAa,UAAU,IAAI,OAAO,IAAI,EAAE,OAAO,mBAAmB,CAAC;AAC3G;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,YAAY,QAAQ,IAAI,EAAE,OAAO,mBAAmB,CAAC;AAC7F;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,KAAK,WAAW,MAAM,KAAK,QAAQ,QAAQ,IAAI,EAAE,OAAO,mBAAmB,CAAC;AACzF;AAAA,QACF;AAEE,eAAK,QAAQ,KAAK,EAAE,OAAO,oBAAoB,IAAI,OAAO,GAAG,CAAC;AAAA,MAClE;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,qBAAqB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAChF,WAAK,QAAQ,KAAK,EAAE,OAAO,gCAA2B,CAAC;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,QAAQ,KAAuB,UAAyB;AAC9D,QAAI,IAAI,UAAU;AAChB,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,UAAU,IAAI,QAAQ;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,aAAa,SAAsC;AACzD,UAAM,QACJ,QAAQ,mCAAoC,cAAa,WACpD,QAAiC,WAClC;AACN,WAAO,6BAAc,KAAK,IAAI,QAAQ;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAY,UAA4D;AACpF,SAAK,aAAa,OAAO,QAAQ;AACjC,SAAK,iBAAiB,QAAQ;AAC9B,UAAM,OAAO,6BAAc,QAAQ;AACnC,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI;AACF,UAAI,SAAS,cAAc;AACzB,cAAM,WAAO,iCAAa;AAC1B,cAAM,UAAM,sCAAkB,IAAI;AAClC,aAAK,SAAS,IAAI,UAAU,EAAE,MAAM,MAAM,KAAK,WAAW,MAAM,KAAK,IAAO,CAAC;AAC7E,eAAO,EAAE,QAAQ,kBAAkB,KAAK,KAAK;AAAA,MAC/C;AACA,UAAI,SAAS,aAAa;AACxB,cAAM,WAAO,uCAAmB;AAChC,cAAM,UAAM,4CAAwB,IAAI;AACxC,aAAK,SAAS,IAAI,UAAU,EAAE,MAAM,MAAM,KAAK,WAAW,MAAM,KAAK,IAAO,CAAC;AAC7E,eAAO,EAAE,QAAQ,kBAAkB,KAAK,KAAK;AAAA,MAC/C;AACA,YAAM,QAAQ,UAAM,qCAAgB,sBAAU,GAAG;AACjD,WAAK,SAAS,IAAI,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;AAC1D,WAAK,gBAAgB,UAAU,KAAK;AACpC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,UAAU,MAAM;AAAA,QAChB,iBAAiB,kCAAc;AAAA,QAC/B,WAAW,MAAM;AAAA,MACnB;AAAA,IACF,SAAS,GAAG;AACV,YAAM,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACxD,WAAK,aAAa,IAAI,UAAU,MAAM;AACtC,aAAO,EAAE,QAAQ,UAAU,OAAO;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,aAAa,UAAkB,SAAwC;AACnF,UAAM,UAAU,KAAK,SAAS,IAAI,QAAQ;AAC1C,UAAM,QACJ,QAAQ,mCAAiC,WAAU,WAAY,QAA8B,MAAM,KAAK,IAAI;AAC9G,QAAI,CAAC,WAAW,QAAQ,SAAS,eAAe;AAC9C,aAAO,EAAE,QAAQ,UAAU,QAAQ,0BAA0B;AAAA,IAC/D;AAGA,YAAI,+BAAe,QAAQ,WAAW,KAAK,IAAI,CAAC,GAAG;AACjD,WAAK,SAAS,OAAO,QAAQ;AAC7B,aAAO,EAAE,QAAQ,UAAU,QAAQ,4DAAuD;AAAA,IAC5F;AACA,QAAI,CAAC,OAAO;AACV,aAAO,EAAE,QAAQ,UAAU,QAAQ,iBAAiB;AAAA,IACtD;AACA,QAAI;AACF,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,SACJ,QAAQ,SAAS,eACb,UAAM,iCAAa,OAAO,QAAQ,MAAM,sBAAU,GAAG,IACrD,UAAM,2CAAmB,sCAAkB,OAAO,QAAQ,KAAK,KAAK,GAAG,QAAQ,MAAM,sBAAU,GAAG;AACxG,YAAM,KAAK,aAAa,UAAU,MAAM;AACxC,aAAO,EAAE,QAAQ,YAAY;AAAA,IAC/B,SAAS,GAAG;AACV,YAAM,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACxD,WAAK,aAAa,IAAI,UAAU,MAAM;AACtC,aAAO,EAAE,QAAQ,UAAU,OAAO;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,aAAa,UAAkB,QAAiC;AA/QhF;AAgRI,UAAM,KAAK,WAAW,QAAQ,EAAE,KAAK,MAAM;AAC3C,SAAK,SAAS,OAAO,QAAQ;AAC7B,SAAK,aAAa,OAAO,QAAQ;AACjC,SAAK,iBAAiB,QAAQ;AAC9B,UAAM,KAAK,qCAAiB,QAAQ;AACpC,QAAI,IAAI;AAIN,aAAK,UAAK,WAAL,mBAAa,QAAQ,IAAI,MAAM,OAAK;AACvC,aAAK,IAAI,MAAM,qCAAqC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,MAClG;AAAA,IACF;AACA,SAAK,IAAI,KAAK,IAAG,mCAAe,QAAQ,MAAvB,YAA4B,QAAQ,aAAa;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,gBAAgB,UAAkB,OAA8B;AACtE,UAAM,OAAO,YAA2B;AACtC,UAAI;AACF,gBAAI,+BAAe,MAAM,WAAW,KAAK,IAAI,CAAC,GAAG;AAC/C,eAAK,iBAAiB,QAAQ;AAC9B,eAAK,SAAS,OAAO,QAAQ;AAC7B,eAAK,aAAa,IAAI,UAAU,iDAA4C;AAC5E;AAAA,QACF;AACA,cAAM,SAAS,UAAM,oCAAe,OAAO,oBAAQ;AACnD,YAAI,OAAO,WAAW,SAAS;AAC7B,eAAK,iBAAiB,QAAQ;AAC9B,gBAAM,SAAS,UAAM,wCAAmB,OAAO,MAAM,OAAO,cAAc,sBAAU,KAAK,IAAI,CAAC;AAC9F,gBAAM,KAAK,aAAa,UAAU,MAAM;AAAA,QAC1C;AAAA,MACF,SAAS,GAAG;AACV,aAAK,iBAAiB,QAAQ;AAC9B,aAAK,SAAS,OAAO,QAAQ;AAC7B,aAAK,aAAa,IAAI,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,MAC5E;AAAA,IACF;AACA,SAAK,cAAc,IAAI,UAAU;AAAA,MAC/B,MAAM;AAAA,MACN,QAAQ,KAAK,YAAY,MAAM,KAAK,KAAK,GAAG,KAAK,IAAI,MAAM,aAAa,CAAC,IAAI,GAAI;AAAA,IACnF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAiB,UAAwB;AAC/C,UAAM,SAAS,KAAK,cAAc,IAAI,QAAQ;AAC9C,SAAI,iCAAQ,UAAS,YAAY;AAC/B,WAAK,cAAc,OAAO,MAAM;AAAA,IAClC;AACA,SAAK,cAAc,OAAO,QAAQ;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAY,UAAwC;AAChE,UAAM,UAAU,KAAK,aAAa,IAAI,QAAQ;AAC9C,UAAM,UAAU,KAAK,SAAS,IAAI,QAAQ;AAC1C,SAAI,mCAAS,UAAS,eAAe;AACnC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,UAAU,QAAQ,MAAM;AAAA,QACxB,iBAAiB,kCAAc;AAAA,QAC/B,WAAW,QAAQ,MAAM;AAAA,MAC3B;AAAA,IACF;AACA,QAAI,SAAS;AACX,cAAI,+BAAe,QAAQ,WAAW,KAAK,IAAI,CAAC,GAAG;AACjD,aAAK,SAAS,OAAO,QAAQ;AAC7B,eAAO,EAAE,QAAQ,UAAU,QAAQ,4DAAuD;AAAA,MAC5F;AACA,aAAO,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,KAAK,MAAM,QAAQ,KAAK;AAAA,IAC1E;AACA,QAAI,SAAS;AACX,aAAO,EAAE,QAAQ,UAAU,QAAQ,QAAQ;AAAA,IAC7C;AACA,WAAQ,MAAM,KAAK,WAAW,QAAQ,EAAE,KAAK,IAAK,EAAE,QAAQ,YAAY,IAAI,EAAE,QAAQ,aAAa;AAAA,EACrG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,QAAQ,UAAwC;AAC5D,UAAM,KAAK,WAAW,QAAQ,EAAE,MAAM;AACtC,SAAK,SAAS,OAAO,QAAQ;AAC7B,SAAK,aAAa,OAAO,QAAQ;AACjC,SAAK,iBAAiB,QAAQ;AAC9B,WAAO,EAAE,QAAQ,aAAa;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,WAAW,UAA8B;AAC/C,QAAI,QAAQ,KAAK,YAAY,IAAI,QAAQ;AACzC,QAAI,CAAC,OAAO;AACV,cAAQ,KAAK,eAAe,QAAQ;AACpC,WAAK,YAAY,IAAI,UAAU,KAAK;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,eAAe,UAA8B;AACnD,UAAM,MAAM,MAAM,2BAA2B,IAAI;AACjD,UAAM,WAAO,uBAAK,KAAK,UAAU,QAAQ,OAAO;AAChD,QAAI,SAA0B;AAC9B,QAAI,OAAO;AACX,WAAO;AAAA,MACL,MAAM,YAAsC;AAC1C,YAAI,CAAC,MAAM;AACT,mBAAS,MAAM,KAAK,cAAc,MAAM,QAAQ;AAChD,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAAA,MACA,MAAM,OAAO,WAAoC;AAC/C,kBAAM,uBAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,kBAAM,2BAAU,MAAM,KAAK,QAAQ,KAAK,UAAU,MAAM,CAAC,GAAG,MAAM;AAClE,iBAAS;AACT,eAAO;AAAA,MACT;AAAA,MACA,OAAO,YAA2B;AAChC,iBAAS;AACT,eAAO;AACP,kBAAM,wBAAO,IAAI,EAAE,MAAM,MAAM;AAAA,QAE/B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAc,cAAc,MAAc,UAA4C;AAncxF;AAocI,QAAI;AACJ,QAAI;AACF,YAAM,UAAM,0BAAS,MAAM,MAAM;AAAA,IACnC,SAAS,GAAG;AACV,WAAK,uBAA6B,UAAS,UAAU;AACnD,aAAK,IAAI,KAAK,IAAG,mCAAe,QAAQ,MAAvB,YAA4B,QAAQ,qCAAqC,OAAO,CAAC,CAAC,GAAG;AAAA,MACxG;AACA,aAAO;AAAA,IACT;AACA,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK,QAAQ,GAAG,CAAC;AAC3C,UAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,OAAO,iBAAiB,UAAU;AACrF,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAC9C;AACA,aAAO;AAAA,QACL,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,WAAW,OAAO,OAAO,SAAS,KAAK;AAAA,QACvC,YAAY,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;AAAA,MAC1E;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI;AAAA,QACP,IAAG,mCAAe,QAAQ,MAAvB,YAA4B,QAAQ,wCACrC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAC3C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,oBAAmC;AAC/C,UAAM,MAAM,MAAM,2BAA2B,IAAI;AACjD,UAAM,aAAS,uBAAK,KAAK,wBAAwB;AACjD,QAAI;AACF,gBAAM,0BAAS,QAAQ,MAAM;AAC7B;AAAA,IACF,QAAQ;AAAA,IAER;AACA,eAAW,UAAU,CAAC,6BAA6B,2BAA2B,GAAG;AAC/E,UAAI;AACF,kBAAM,4BAAO,uBAAK,KAAK,MAAM,GAAG,MAAM;AACtC,aAAK,IAAI,KAAK,4DAA4D;AAC1E;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAc,wBAAuC;AA9gBvD;AA+gBI,UAAM,KAAK,kBAAkB,KAAK,SAAS;AAC3C,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,sBAAsB,EAAE;AAC/C,YAAM,aAAY,gCAAK,WAAL,mBAAa;AAC/B,UAAI,EAAC,uCAAW,eAAc;AAC5B;AAAA,MACF;AACA,WAAK,IAAI,KAAK,0FAAqF;AACnG,YAAM,KAAK,yBAAyB,IAAI,EAAE,QAAQ,EAAE,mBAAmB,EAAE,cAAc,MAAM,EAAE,EAAE,CAAC;AAAA,IACpG,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,wCAAwC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,IACrG;AAAA,EACF;AAAA,EAEA,MAAc,UAAyB;AACrC,QAAI;AAEF,YAAM,KAAK,sBAAsB;AACjC,YAAM,eAAW,mCAAc,KAAK,OAAO,QAAQ;AACnD,YAAM,eAAW,uCAAkB,KAAK,OAAO,YAAY;AAC3D,YAAM,KAAK,kBAAkB;AAG7B,YAAM,KAAK,uBAAuB;AAClC,YAAM,KAAK,oBAAoB;AAC/B,YAAM,UAAU,MAAM,KAAK,oBAAoB,QAAQ;AACvD,UAAI,UAAU,GAAG;AACf,aAAK,IAAI,KAAK,gCAAgC,OAAO,+BAA+B;AAAA,MACtF;AACA,UAAI,SAAS,WAAW,GAAG;AACzB,aAAK,IAAI,KAAK,wEAAmE;AACjF,cAAM,KAAK,SAAS,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAChE;AAAA,MACF;AACA,YAAM,YAAY,oBAAI,IAA2B;AACjD,iBAAW,WAAW,UAAU;AAC9B,cAAM,WAAW,MAAM,KAAK,aAAa,OAAO;AAChD,YAAI,UAAU;AACZ,oBAAU,IAAI,QAAQ,IAAI,QAAQ;AAAA,QACpC;AAAA,MACF;AACA,WAAK,SAAS,IAAI,8BAAW,UAAU,WAAW,UAAU;AAAA,QAC1D,cAAc,OAAM,QAAO;AACzB,gBAAM,KAAK,aAAa,IAAI,IAAI,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAiC,QAAQ,CAAC,EAAE,CAAC;AAC3G,cAAI,IAAI,SAAS,WAAW,CAAC,KAAK,cAAc,IAAI,IAAI,EAAE,GAAG;AAC3D,iBAAK,cAAc,IAAI,IAAI,EAAE;AAC7B,iBAAK;AAAA,UACP;AAAA,QACF;AAAA,QACA,UAAU,CAAC,IAAI,UAAU;AACvB,eAAK,KAAK,SAAS,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,UAE9D,CAAC;AAAA,QACH;AAAA,QACA,iBAAiB,OAAO,IAAI,UAAU;AAEpC,gBAAM,KAAK,qBAAqB,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,UAE3E,CAAC;AAAA,QACH;AAAA,QACA,cAAc,OAAM,OAAM;AACxB,cAAI;AACF,kBAAM,KAAK,eAAe,IAAI,EAAE,WAAW,KAAK,CAAC;AACjD,gBAAI,KAAK,cAAc,OAAO,EAAE,GAAG;AACjC,mBAAK;AAAA,YACP;AAAA,UACF,SAAS,GAAG;AACV,iBAAK,IAAI,MAAM,oBAAoB,EAAE,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,UACxF;AAAA,QACF;AAAA,QACA,cAAc,OAAM,WAAU;AArlBtC;AAslBU,gBAAM,QAAQ,GAAG,KAAK,SAAS,IAAI,MAAM;AACzC,gBAAM,OAAO,MAAM,KAAK,mBAAmB,UAAU,SAAS;AAAA,YAC5D,UAAU;AAAA,YACV,QAAQ,GAAG,KAAK;AAAA,UAClB,CAAC;AACD,mBAAQ,kCAAM,SAAN,YAAc,CAAC,GAAG,IAAI,SAAO,IAAI,GAAG,UAAU,KAAK,UAAU,SAAS,CAAC,CAAC;AAAA,QAClF;AAAA,QACA,UAAU,CAAC,IAAI,QAAqB,EAAE,MAAM,YAAY,QAAQ,KAAK,YAAY,IAAI,EAAE,EAAE;AAAA,QACzF,cAAc,CAAC,IAAI,QAAqB,EAAE,MAAM,WAAW,QAAQ,KAAK,WAAW,IAAI,EAAE,EAAE;AAAA,QAC3F,QAAQ,YAAU;AAChB,gBAAM,QAAQ;AACd,cAAI,MAAM,SAAS,YAAY;AAC7B,iBAAK,cAAc,MAAM,MAAM;AAAA,UACjC,OAAO;AACL,iBAAK,aAAa,MAAM,MAAM;AAAA,UAChC;AAAA,QACF;AAAA,QACA,KAAK,MAAM,KAAK,IAAI;AAAA,QACpB,KAAK;AAAA,UACH,OAAO,OAAK,KAAK,IAAI,MAAM,CAAC;AAAA,UAC5B,MAAM,OAAK,KAAK,IAAI,KAAK,CAAC;AAAA,UAC1B,MAAM,OAAK,KAAK,IAAI,KAAK,CAAC;AAAA,UAC1B,OAAO,OAAK,KAAK,IAAI,MAAM,CAAC;AAAA,QAC9B;AAAA,QACA,iBAAiB,MAAM,KAAK,oBAAoB;AAAA,QAChD,QAAQ,KAAK,OAAO,gBAChB,CAAC,UAAU,YACT,KAAK,KAAK,qBAAqB,YAAY,sBAAsB,OAAO,EAAE;AAAA,UAAM,OAC9E,KAAK,IAAI,MAAM,iCAAiC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,QAC9F,IACF;AAAA,MACN,CAAC;AACD,YAAM,KAAK,OAAO,MAAM;AACxB,WAAK,IAAI,KAAK,cAAc,UAAU,IAAI,OAAO,SAAS,MAAM,iCAAiC,QAAQ,IAAI;AAAA,IAC/G,SAAS,GAAG;AACV,WAAK,IAAI,MAAM,mBAAmB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,IAChF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,aAAa,SAA4D;AACrF,YAAQ,QAAQ,UAAU;AAAA,MACxB,KAAK;AACH,mBAAO,qCAAkB,KAAK,WAAW,QAAQ,QAAQ,GAAG,oBAAQ;AAAA,MACtE,KAAK;AACH,mBAAO,uCAAmB,KAAK,WAAW,QAAQ,QAAQ,GAAG,oBAAQ;AAAA,MACvE,KAAK;AACH,mBAAO,qCAAkB,KAAK,WAAW,QAAQ,QAAQ,GAAG,sBAAU,oBAAQ;AAAA,MAChF,KAAK,cAAc;AACjB,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,sCAAmB,GAAG,IAAI;AAAA,MACzC;AAAA,MACA,KAAK,YAAY;AACf,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,kCAAiB,GAAG,IAAI;AAAA,MACvC;AAAA,MACA,KAAK,UAAU;AACb,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,8BAAe,GAAG,IAAI;AAAA,MACrC;AAAA,MACA,KAAK,iBAAiB;AACpB,cAAM,MAAM,MAAM,KAAK,WAAW,OAAO;AACzC,eAAO,UAAM,2CAAqB,GAAG,IAAI;AAAA,MAC3C;AAAA,MACA;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,WAAW,SAAqD;AAC5E,QAAI,CAAC,QAAQ,cAAc;AACzB,WAAK,IAAI,KAAK,GAAG,QAAQ,IAAI,mEAA8D;AAC3F,aAAO;AAAA,IACT;AACA,QAAI;AACF,YAAM,aAAa,MAAM,gCAAY,eAAe,MAAM,QAAQ,YAAY;AAC9E,YAAM,SAAS,WAAW;AAC1B,YAAM,MAAM,OAAO,OAAO,QAAQ,YAAY,OAAO,MAAM,OAAO,MAAM;AACxE,UAAI,CAAC,KAAK;AACR,aAAK,IAAI,KAAK,GAAG,QAAQ,IAAI,gBAAgB,QAAQ,YAAY,qBAAqB;AAAA,MACxF;AACA,aAAO;AAAA,IACT,SAAS,GAAG;AACV,WAAK,IAAI;AAAA,QACP,GAAG,QAAQ,IAAI,4BAA4B,QAAQ,YAAY,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,MAChH;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAwB,sBAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,oBAAoB,UAAqD;AACrF,QAAI,UAAU;AACd,eAAW,WAAW,UAAU;AAC9B,iBAAW,UAAU,eAAe,qBAAqB;AACvD,cAAM,KAAK,GAAG,QAAQ,EAAE,IAAI,MAAM;AAClC,YAAI,CAAC,KAAK,cAAc,IAAI,EAAE,GAAG;AAC/B;AAAA,QACF;AACA,YAAI;AACF,gBAAM,KAAK,eAAe,EAAE;AAC5B;AAIA,eAAK,cAAc,OAAO,EAAE;AAAA,QAC9B,SAAS,GAAG;AACV,eAAK,IAAI,MAAM,oBAAoB,EAAE,KAAK,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,sBAAqC;AACjD,UAAM,cAAU,mCAAc,KAAK,OAAO,QAAQ,EAAE,IAAI,aAAW,QAAQ,EAAE;AAC7E,QAAI,QAAQ,WAAW,GAAG;AACxB;AAAA,IACF;AACA,UAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,SAAS,QAAQ,OAAO,CAAC;AAClD,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,uBAAuB;AAClD,YAAM,QAAQ,oBAAI,IAAY;AAC9B,iBAAW,MAAM,OAAO,KAAK,OAAO,GAAG;AACrC,cAAM,OAAO,GAAG,UAAU,KAAK,UAAU,SAAS,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE,YAAI,QAAQ,CAAC,KAAK,IAAI,IAAI,GAAG;AAC3B,gBAAM,IAAI,IAAI;AAAA,QAChB;AAAA,MACF;AACA,iBAAW,QAAQ,OAAO;AACxB,aAAK,IAAI;AAAA,UACP,SAAS,SACL,2FACA,qDAAqD,IAAI;AAAA,QAC/D;AAEA,mBAAW,MAAM,CAAC,GAAG,KAAK,aAAa,GAAG;AACxC,cAAI,OAAO,QAAQ,GAAG,WAAW,GAAG,IAAI,GAAG,GAAG;AAC5C,iBAAK,cAAc,OAAO,EAAE;AAC5B,iBAAK;AAAA,UACP;AAAA,QACF;AACA,cAAM,KAAK,eAAe,MAAM,EAAE,WAAW,KAAK,CAAC;AAAA,MACrD;AAAA,IACF,SAAS,GAAG;AACV,WAAK,IAAI,KAAK,oCAAoC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE;AAAA,IAChG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBQ,SAAS,UAA4B;AAzyB/C;AA0yBI,QAAI;AACF,iBAAW,YAAY,CAAC,GAAG,KAAK,cAAc,KAAK,CAAC,GAAG;AACrD,aAAK,iBAAiB,QAAQ;AAAA,MAChC;AACA,YAAM,SAAS,KAAK;AACpB,iBAAK,WAAL,mBAAa;AACb,WAAK,SAAS;AACd,aAAM,sCAAQ,qBAAR,YAA4B,KAAK,SAAS,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,GACzF,KAAK,MAAM,KAAK,IAAI,MAAM,gCAAgC,CAAC,EAC3D,MAAM,OAAK,KAAK,IAAI,MAAM,0CAAqC,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC,EAAE,CAAC,EAC5G,QAAQ,QAAQ;AAAA,IACrB,QAAQ;AACN,eAAS;AAAA,IACX;AAAA,EACF;AACF;AAEA,IAAI,QAAQ,SAAS,QAAQ;AAE3B,SAAO,UAAU,CAAC,YAAuD,IAAI,eAAe,OAAO;AACrG,OAAO;AACL,GAAC,MAAM,IAAI,eAAe,GAAG;AAC/B;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "ai-usage",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.2",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.9.2": {
|
|
7
|
+
"en": "Stopping the instance now marks the accounts as offline on updated installations too, not only on fresh ones — before, they kept showing as online",
|
|
8
|
+
"de": "Das Stoppen der Instanz kennzeichnet die Konten jetzt auch auf aktualisierten Installationen als offline, nicht nur auf frischen — vorher blieben sie auf online stehen",
|
|
9
|
+
"ru": "Остановка экземпляра теперь помечает аккаунты офлайн и на обновлённых установках, а не только на новых — раньше они оставались онлайн",
|
|
10
|
+
"pt": "Parar a instância passa a marcar as contas como offline também em instalações atualizadas, não só em novas — antes continuavam a aparecer online",
|
|
11
|
+
"nl": "Het stoppen van de instantie markeert de accounts nu ook op bijgewerkte installaties als offline, niet alleen op nieuwe — eerder bleven ze online staan",
|
|
12
|
+
"fr": "L'arrêt de l'instance marque désormais les comptes hors ligne sur les installations mises à jour aussi, pas seulement sur les neuves — auparavant ils restaient en ligne",
|
|
13
|
+
"it": "L'arresto dell'istanza contrassegna ora gli account come offline anche sulle installazioni aggiornate, non solo su quelle nuove — prima restavano online",
|
|
14
|
+
"es": "Detener la instancia ahora marca las cuentas sin conexión también en instalaciones actualizadas, no solo en nuevas — antes seguían apareciendo en línea",
|
|
15
|
+
"pl": "Zatrzymanie instancji oznacza teraz konta jako offline także na zaktualizowanych instalacjach, nie tylko na nowych — wcześniej pozostawały online",
|
|
16
|
+
"uk": "Зупинка екземпляра тепер позначає облікові записи офлайн і на оновлених інсталяціях, а не лише на нових — раніше вони лишалися онлайн",
|
|
17
|
+
"zh-cn": "停止实例现在在已更新的安装上也会把账户标记为离线,而不只是全新安装——此前它们仍显示为在线"
|
|
18
|
+
},
|
|
6
19
|
"0.9.1": {
|
|
7
20
|
"en": "While an account has nothing to report — adapter off, or started and not asked yet — the reason now reads \"Unknown\" instead of a sentence about the adapter",
|
|
8
21
|
"de": "Solange ein Konto nichts zu melden hat — Adapter aus oder gestartet und noch nichts gefragt — steht als Grund jetzt „Unknown\" statt eines Satzes über den Adapter",
|
|
@@ -80,19 +93,6 @@
|
|
|
80
93
|
"pl": "Każde konto pokazuje teraz ikonę połączenia w drzewie obiektów — zieloną, dopóki dostarcza dane, przekreśloną gdy nie, jak każde urządzenie ioBroker",
|
|
81
94
|
"uk": "Кожен акаунт тепер має піктограму з’єднання в дереві об’єктів — зелену, поки дані надходять, перекреслену, коли ні, як у будь-якого пристрою ioBroker",
|
|
82
95
|
"zh-cn": "每个账户现在都会在对象树中显示连接图标——有数据时为绿色,无数据时带斜杠,与其他 ioBroker 设备一致"
|
|
83
|
-
},
|
|
84
|
-
"0.5.0": {
|
|
85
|
-
"en": "Two status datapoints per account instead of six: an offline marker ioBroker actually shows plus the reason in plain text; the retired ones are removed automatically",
|
|
86
|
-
"de": "Zwei Status-Datenpunkte je Konto statt sechs: ein Offline-Kennzeichen, das ioBroker wirklich anzeigt, plus den Grund im Klartext; die alten werden automatisch entfernt",
|
|
87
|
-
"ru": "Два статусных состояния на аккаунт вместо шести: признак «не в сети», который ioBroker действительно показывает, и причина текстом; старые удаляются автоматически",
|
|
88
|
-
"pt": "Dois pontos de estado por conta em vez de seis: um marcador offline que o ioBroker mostra mesmo, mais o motivo em texto; os antigos são removidos automaticamente",
|
|
89
|
-
"nl": "Twee statusdatapunten per account in plaats van zes: een offline-markering die ioBroker echt toont, plus de reden in gewone taal; de oude worden automatisch verwijderd",
|
|
90
|
-
"fr": "Deux points d'état par compte au lieu de six : un marqueur hors ligne qu'ioBroker affiche vraiment, plus la raison en clair ; les anciens sont supprimés automatiquement",
|
|
91
|
-
"it": "Due punti di stato per account invece di sei: un contrassegno offline che ioBroker mostra davvero e il motivo in chiaro; quelli superati vengono rimossi automaticamente",
|
|
92
|
-
"es": "Dos puntos de estado por cuenta en lugar de seis: un marcador de desconexión que ioBroker sí muestra y el motivo en texto claro; los antiguos se eliminan automáticamente",
|
|
93
|
-
"pl": "Dwa punkty stanu na konto zamiast sześciu: znacznik offline, który ioBroker naprawdę pokazuje, oraz powód tekstem; stare są usuwane automatycznie",
|
|
94
|
-
"uk": "Дві точки стану на акаунт замість шести: позначка «не в мережі», яку ioBroker справді показує, і причина текстом; застарілі видаляються автоматично",
|
|
95
|
-
"zh-cn": "每个账户从六个状态数据点减到两个:ioBroker 真正会显示的离线标记,加上纯文本的原因;旧的会被自动删除"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"plugins": {
|