aimeat 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.env.example +33 -0
- package/dist/build-stamp.json +13 -0
- package/dist/locales/en.json +166 -3
- package/dist/locales/fi.json +166 -3
- package/dist/public/components/VoiceRecorder.js +116 -0
- package/dist/public/connection-done.html +53 -0
- package/dist/public/css/components/voice-recorder.css +66 -0
- package/dist/public/css/views/inbox.css +41 -0
- package/dist/public/css/views/profile.css +171 -95
- package/dist/public/js/services/audio-recorder.js +171 -0
- package/dist/public/js/services/messages.js +21 -2
- package/dist/public/spa.html +3 -0
- package/dist/public/views/profile/access-tab/connections.js +264 -0
- package/dist/public/views/profile/access-tab.js +2 -0
- package/dist/public/views/profile/inbox-tab/components.js +60 -10
- package/dist/public/views/profile/inbox-tab/helpers.js +33 -0
- package/dist/public/views/profile/inbox-tab/panels.js +6 -1
- package/dist/public/views/profile/inbox-tab/use-voice.js +50 -0
- package/dist/public/views/profile/inbox-tab/voice-parts.js +111 -0
- package/dist/public/views/profile/inbox-tab.js +18 -24
- package/dist/public/views/profile/openrouter/budget-panel.js +224 -0
- package/dist/public/views/profile/openrouter/model-picker.js +150 -0
- package/dist/public/views/profile/openrouter/pricing.js +139 -0
- package/dist/public/views/profile/openrouter-settings.js +425 -488
- package/dist/scripts/build-sdk-libs.d.ts.map +1 -1
- package/dist/scripts/build-sdk-libs.js +1 -0
- package/dist/scripts/build-sdk-libs.js.map +1 -1
- package/dist/scripts/check-ai-disclosure.d.ts.map +1 -1
- package/dist/scripts/check-ai-disclosure.js +65 -0
- package/dist/scripts/check-ai-disclosure.js.map +1 -1
- package/dist/src/cli/connect/ai-provenance-carry.d.ts +54 -3
- package/dist/src/cli/connect/ai-provenance-carry.d.ts.map +1 -1
- package/dist/src/cli/connect/ai-provenance-carry.js +84 -12
- package/dist/src/cli/connect/ai-provenance-carry.js.map +1 -1
- package/dist/src/cli/connect/api-client.d.ts +11 -0
- package/dist/src/cli/connect/api-client.d.ts.map +1 -1
- package/dist/src/cli/connect/api-client.js +2 -0
- package/dist/src/cli/connect/api-client.js.map +1 -1
- package/dist/src/cli/connect/mcp/local-server.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/local-server.js +11 -0
- package/dist/src/cli/connect/mcp/local-server.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/apps.d.ts +1 -0
- package/dist/src/cli/connect/mcp/tools/apps.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/apps.js +3 -2
- package/dist/src/cli/connect/mcp/tools/apps.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/core.d.ts +2 -0
- package/dist/src/cli/connect/mcp/tools/core.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/core.js +5 -2
- package/dist/src/cli/connect/mcp/tools/core.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/knowledge.d.ts +1 -0
- package/dist/src/cli/connect/mcp/tools/knowledge.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/knowledge.js +3 -2
- package/dist/src/cli/connect/mcp/tools/knowledge.js.map +1 -1
- package/dist/src/config-types.d.ts +14 -0
- package/dist/src/config-types.d.ts.map +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +10 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +969 -4
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/mcp/ai-provenance-input.d.ts +33 -0
- package/dist/src/mcp/ai-provenance-input.d.ts.map +1 -1
- package/dist/src/mcp/ai-provenance-input.js +36 -0
- package/dist/src/mcp/ai-provenance-input.js.map +1 -1
- package/dist/src/mcp/apps.d.ts +7 -0
- package/dist/src/mcp/apps.d.ts.map +1 -1
- package/dist/src/mcp/apps.js +20 -2
- package/dist/src/mcp/apps.js.map +1 -1
- package/dist/src/models/ai-provenance-schemas.d.ts +7 -1
- package/dist/src/models/ai-provenance-schemas.d.ts.map +1 -1
- package/dist/src/models/ai-provenance-schemas.js +11 -1
- package/dist/src/models/ai-provenance-schemas.js.map +1 -1
- package/dist/src/models/connection-schemas.d.ts +203 -0
- package/dist/src/models/connection-schemas.d.ts.map +1 -0
- package/dist/src/models/connection-schemas.js +33 -0
- package/dist/src/models/connection-schemas.js.map +1 -0
- package/dist/src/models/message-schemas.d.ts +40 -0
- package/dist/src/models/message-schemas.d.ts.map +1 -1
- package/dist/src/models/message-schemas.js +23 -0
- package/dist/src/models/message-schemas.js.map +1 -1
- package/dist/src/routes/ai-provenance.d.ts +6 -0
- package/dist/src/routes/ai-provenance.d.ts.map +1 -1
- package/dist/src/routes/ai-provenance.js +34 -1
- package/dist/src/routes/ai-provenance.js.map +1 -1
- package/dist/src/routes/ai.d.ts +5 -0
- package/dist/src/routes/ai.d.ts.map +1 -1
- package/dist/src/routes/ai.js +83 -0
- package/dist/src/routes/ai.js.map +1 -1
- package/dist/src/routes/app-grants.d.ts.map +1 -1
- package/dist/src/routes/app-grants.js +4 -0
- package/dist/src/routes/app-grants.js.map +1 -1
- package/dist/src/routes/apps/drafts.d.ts +3 -0
- package/dist/src/routes/apps/drafts.d.ts.map +1 -1
- package/dist/src/routes/apps/drafts.js +13 -0
- package/dist/src/routes/apps/drafts.js.map +1 -1
- package/dist/src/routes/apps/publish.d.ts +5 -0
- package/dist/src/routes/apps/publish.d.ts.map +1 -1
- package/dist/src/routes/apps/publish.js +21 -5
- package/dist/src/routes/apps/publish.js.map +1 -1
- package/dist/src/routes/connections.d.ts +35 -0
- package/dist/src/routes/connections.d.ts.map +1 -0
- package/dist/src/routes/connections.js +406 -0
- package/dist/src/routes/connections.js.map +1 -0
- package/dist/src/routes/libs.js +1 -1
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/messages.d.ts +5 -0
- package/dist/src/routes/messages.d.ts.map +1 -1
- package/dist/src/routes/messages.js +92 -0
- package/dist/src/routes/messages.js.map +1 -1
- package/dist/src/routes/openrouter.d.ts +6 -1
- package/dist/src/routes/openrouter.d.ts.map +1 -1
- package/dist/src/routes/openrouter.js +37 -7
- package/dist/src/routes/openrouter.js.map +1 -1
- package/dist/src/routes/upload.d.ts +5 -0
- package/dist/src/routes/upload.d.ts.map +1 -1
- package/dist/src/routes/upload.js +32 -0
- package/dist/src/routes/upload.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +2 -0
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/services/ai-completion.d.ts +48 -0
- package/dist/src/services/ai-completion.d.ts.map +1 -1
- package/dist/src/services/ai-completion.js +112 -72
- package/dist/src/services/ai-completion.js.map +1 -1
- package/dist/src/services/ai-disclosure.d.ts +6 -0
- package/dist/src/services/ai-disclosure.d.ts.map +1 -1
- package/dist/src/services/ai-disclosure.js +20 -2
- package/dist/src/services/ai-disclosure.js.map +1 -1
- package/dist/src/services/ai-provenance-page.d.ts +59 -0
- package/dist/src/services/ai-provenance-page.d.ts.map +1 -0
- package/dist/src/services/ai-provenance-page.js +185 -0
- package/dist/src/services/ai-provenance-page.js.map +1 -0
- package/dist/src/services/ai-provenance.d.ts +6 -0
- package/dist/src/services/ai-provenance.d.ts.map +1 -1
- package/dist/src/services/ai-provenance.js +5 -0
- package/dist/src/services/ai-provenance.js.map +1 -1
- package/dist/src/services/ai-transcription.d.ts +66 -0
- package/dist/src/services/ai-transcription.d.ts.map +1 -0
- package/dist/src/services/ai-transcription.js +90 -0
- package/dist/src/services/ai-transcription.js.map +1 -0
- package/dist/src/services/app-publish.d.ts.map +1 -1
- package/dist/src/services/app-publish.js +16 -1
- package/dist/src/services/app-publish.js.map +1 -1
- package/dist/src/services/build-app-prompt.d.ts.map +1 -1
- package/dist/src/services/build-app-prompt.js +2 -1
- package/dist/src/services/build-app-prompt.js.map +1 -1
- package/dist/src/services/connections/attach.d.ts +62 -0
- package/dist/src/services/connections/attach.d.ts.map +1 -0
- package/dist/src/services/connections/attach.js +168 -0
- package/dist/src/services/connections/attach.js.map +1 -0
- package/dist/src/services/connections/credential.d.ts +39 -0
- package/dist/src/services/connections/credential.d.ts.map +1 -0
- package/dist/src/services/connections/credential.js +66 -0
- package/dist/src/services/connections/credential.js.map +1 -0
- package/dist/src/services/connections/instance.d.ts +65 -0
- package/dist/src/services/connections/instance.d.ts.map +1 -0
- package/dist/src/services/connections/instance.js +144 -0
- package/dist/src/services/connections/instance.js.map +1 -0
- package/dist/src/services/connections/oauth.d.ts +77 -0
- package/dist/src/services/connections/oauth.d.ts.map +1 -0
- package/dist/src/services/connections/oauth.js +290 -0
- package/dist/src/services/connections/oauth.js.map +1 -0
- package/dist/src/services/connections/providers.d.ts +118 -0
- package/dist/src/services/connections/providers.d.ts.map +1 -0
- package/dist/src/services/connections/providers.js +260 -0
- package/dist/src/services/connections/providers.js.map +1 -0
- package/dist/src/services/connections/publish-gate.d.ts +103 -0
- package/dist/src/services/connections/publish-gate.d.ts.map +1 -0
- package/dist/src/services/connections/publish-gate.js +0 -0
- package/dist/src/services/connections/publish-gate.js.map +1 -0
- package/dist/src/services/connections/publish.d.ts +60 -0
- package/dist/src/services/connections/publish.d.ts.map +1 -0
- package/dist/src/services/connections/publish.js +340 -0
- package/dist/src/services/connections/publish.js.map +1 -0
- package/dist/src/services/connections/refresh.d.ts +55 -0
- package/dist/src/services/connections/refresh.d.ts.map +1 -0
- package/dist/src/services/connections/refresh.js +307 -0
- package/dist/src/services/connections/refresh.js.map +1 -0
- package/dist/src/services/message-send.d.ts.map +1 -1
- package/dist/src/services/message-send.js +16 -2
- package/dist/src/services/message-send.js.map +1 -1
- package/dist/src/services/openrouter.d.ts +67 -1
- package/dist/src/services/openrouter.d.ts.map +1 -1
- package/dist/src/services/openrouter.js +102 -11
- package/dist/src/services/openrouter.js.map +1 -1
- package/dist/src/services/upload-token.d.ts +5 -1
- package/dist/src/services/upload-token.d.ts.map +1 -1
- package/dist/src/services/upload-token.js +13 -1
- package/dist/src/services/upload-token.js.map +1 -1
- package/dist/src/services/workflow/engine-util.d.ts +18 -0
- package/dist/src/services/workflow/engine-util.d.ts.map +1 -1
- package/dist/src/services/workflow/engine-util.js +27 -0
- package/dist/src/services/workflow/engine-util.js.map +1 -1
- package/dist/src/services/workflow/engine.d.ts.map +1 -1
- package/dist/src/services/workflow/engine.js +4 -2
- package/dist/src/services/workflow/engine.js.map +1 -1
- package/dist/src/storage/interface.d.ts +2 -1
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/db-types.d.ts +65 -0
- package/dist/src/storage/providers/postgres-kysely/db-types.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/index.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/index.js +2 -1
- package/dist/src/storage/providers/postgres-kysely/index.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/connections.d.ts +44 -0
- package/dist/src/storage/providers/postgres-kysely/methods/connections.d.ts.map +1 -0
- package/dist/src/storage/providers/postgres-kysely/methods/connections.js +308 -0
- package/dist/src/storage/providers/postgres-kysely/methods/connections.js.map +1 -0
- package/dist/src/storage/providers/postgres-kysely/methods/node-infra.d.ts.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/methods/node-infra.js +4 -2
- package/dist/src/storage/providers/postgres-kysely/methods/node-infra.js.map +1 -1
- package/dist/src/storage/providers/postgres-kysely/migrations/0021_connections.sql +116 -0
- package/dist/src/storage/providers/postgres-kysely/migrations/0022_provider_clients.sql +36 -0
- package/dist/src/storage/providers/postgres-kysely/migrations/0023_nonce_payload.sql +13 -0
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +2 -1
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/community.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/community.js +2 -1
- package/dist/src/storage/providers/sqlite/methods/community.js.map +1 -1
- package/dist/src/storage/providers/sqlite/methods/connections.d.ts +60 -0
- package/dist/src/storage/providers/sqlite/methods/connections.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/methods/connections.js +287 -0
- package/dist/src/storage/providers/sqlite/methods/connections.js.map +1 -0
- package/dist/src/storage/providers/sqlite/schema-tables-1.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema-tables-1.js +4 -0
- package/dist/src/storage/providers/sqlite/schema-tables-1.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema-tables-3.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema-tables-3.js +106 -0
- package/dist/src/storage/providers/sqlite/schema-tables-3.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +2 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/connection.repository.d.ts +124 -0
- package/dist/src/storage/repositories/connection.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/connection.repository.js +2 -0
- package/dist/src/storage/repositories/connection.repository.js.map +1 -0
- package/dist/src/storage/types/agents-messaging.d.ts +19 -0
- package/dist/src/storage/types/agents-messaging.d.ts.map +1 -1
- package/dist/src/storage/types/agents-messaging.js.map +1 -1
- package/dist/src/storage/types/auth.d.ts +8 -1
- package/dist/src/storage/types/auth.d.ts.map +1 -1
- package/dist/src/utils/build-stamp.d.ts +56 -0
- package/dist/src/utils/build-stamp.d.ts.map +1 -0
- package/dist/src/utils/build-stamp.js +142 -0
- package/dist/src/utils/build-stamp.js.map +1 -0
- package/dist/src/utils/messaging.d.ts +14 -0
- package/dist/src/utils/messaging.d.ts.map +1 -1
- package/dist/src/utils/messaging.js +0 -0
- package/dist/src/utils/messaging.js.map +1 -1
- package/dist/static/sdk-libs/connect/index.js +199 -0
- package/dist/static/sdk-libs/connect/notes.js +47 -0
- package/dist/static/sdk-libs/connect/panel.js +238 -0
- package/dist/static/sdk-libs/data/index.js +63 -11
- package/dist/static/sdk-libs/dist/aimeat-connect.js +357 -0
- package/dist/static/sdk-libs/dist/aimeat-data.js +40 -9
- package/package.json +2 -2
package/dist/.env.example
CHANGED
|
@@ -273,6 +273,16 @@ AIMEAT_PORT=40050
|
|
|
273
273
|
# AIMEAT_STORAGE_QUOTA_MB=100
|
|
274
274
|
# AIMEAT_STORAGE_MAX_FILE_SIZE_MB=10
|
|
275
275
|
# AIMEAT_STORAGE_MAX_CHUNKED_FILE_SIZE_GB=5
|
|
276
|
+
# ── Speech-to-text (POST /v1/ai/transcribe, voice messages) ──
|
|
277
|
+
# Largest audio file accepted for transcription. 25 MB is the provider-side multipart ceiling, so
|
|
278
|
+
# raising this past it turns a clear local refusal into an opaque upstream one.
|
|
279
|
+
# AIMEAT_STT_MAX_MB=25
|
|
280
|
+
# Duration guideline in seconds. Checked after the call (only the response knows the duration) and
|
|
281
|
+
# logged; a transcription the owner has already paid for is never withheld. 0 disables the warning.
|
|
282
|
+
# AIMEAT_STT_MAX_SECONDS=600
|
|
283
|
+
# Hard cap on one browser voice-message recording, in seconds. Served to the UI so the recorder stops
|
|
284
|
+
# at this node's number. Raise it for long-form dictation; every second still costs the owner money.
|
|
285
|
+
# AIMEAT_VOICE_MSG_MAX_SECONDS=300
|
|
276
286
|
# AIMEAT_MICRO_MEMORY_QUOTA_KB=500
|
|
277
287
|
# AIMEAT_MICRO_MEMORY_MAX_SETS=50
|
|
278
288
|
# AIMEAT_MICRO_MEMORY_MAX_KEYS_PER_SET=100
|
|
@@ -469,6 +479,29 @@ AIMEAT_PORT=40050
|
|
|
469
479
|
# AIMEAT_ENTRA_OAUTH_CLIENT_SECRET="" # Client secret value from the app registration
|
|
470
480
|
# AIMEAT_ENTRA_OAUTH_REDIRECT_URI="" # default: <baseUrl>/v1/ghii/login/entra/callback
|
|
471
481
|
|
|
482
|
+
# ── Outbound connections (TARGET-057, aimeat-connect) ────────
|
|
483
|
+
# Lets a principal connect THEIR OWN account at an external service (Mastodon, YouTube, Bluesky) so
|
|
484
|
+
# an app can publish there on their behalf. The opposite direction from the sign-in blocks above,
|
|
485
|
+
# and deliberately its own client: signing IN with Google and PUBLISHING to a Google account are
|
|
486
|
+
# different consent and must be separately revocable.
|
|
487
|
+
#
|
|
488
|
+
# WHICH SECRET GOES WHERE — the distinction that catches everyone:
|
|
489
|
+
# • The values below identify the APPLICATION. One per node, the SAME for every user. If they were
|
|
490
|
+
# per-user, every user would have to create their own Google Cloud project.
|
|
491
|
+
# • The USER's access/refresh token identifies THE ACCOUNT. It is never here: it lives encrypted in
|
|
492
|
+
# the Connection table, keyed by the principal who connected it.
|
|
493
|
+
# • Mastodon has nothing here at all. Its client credentials are issued PER INSTANCE and there are
|
|
494
|
+
# hundreds of instances, so the node registers itself on first contact and remembers the result.
|
|
495
|
+
#
|
|
496
|
+
# Off by default. An unconfigured provider is absent from discovery and refused at start with a
|
|
497
|
+
# reason; nothing here fails open.
|
|
498
|
+
# AIMEAT_CONNECTIONS_ENABLED=false
|
|
499
|
+
# AIMEAT_CONNECT_GOOGLE_CLIENT_ID="" # Google Cloud → Clients → Web application
|
|
500
|
+
# AIMEAT_CONNECT_GOOGLE_CLIENT_SECRET="" # from the same client; NOT the sign-in client
|
|
501
|
+
# AIMEAT_CONNECT_REDIRECT_URI="" # default: <baseUrl>/v1/connections/callback
|
|
502
|
+
# The redirect URI must match the provider's registration EXACTLY — scheme, port, no trailing
|
|
503
|
+
# slash. A mismatch surfaces only at the provider's consent screen, as redirect_uri_mismatch.
|
|
504
|
+
|
|
472
505
|
# ── Consul (optional fleet management) ───────────────────────
|
|
473
506
|
# Connect to HashiCorp Consul for centralized config management across nodes.
|
|
474
507
|
# Values in Consul KV take precedence over file/.env but below database overrides.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"version": "2.6.0",
|
|
4
|
+
"commit": "9cca6f004888be424d1b031da77a2a04693b856f",
|
|
5
|
+
"dirty": false,
|
|
6
|
+
"built_at": "2026-08-02T01:53:22.702Z",
|
|
7
|
+
"newest_source_mtime": 1785635326931.713,
|
|
8
|
+
"source_dirs": [
|
|
9
|
+
"src",
|
|
10
|
+
"bin",
|
|
11
|
+
"scripts"
|
|
12
|
+
]
|
|
13
|
+
}
|
package/dist/locales/en.json
CHANGED
|
@@ -2985,7 +2985,27 @@
|
|
|
2985
2985
|
"agCapSaved": "Spending limit saved",
|
|
2986
2986
|
"agCapResetBtn": "Clear counter",
|
|
2987
2987
|
"agCapReset": "Counter cleared",
|
|
2988
|
-
"agCapFailed": "Could not change the limit"
|
|
2988
|
+
"agCapFailed": "Could not change the limit",
|
|
2989
|
+
"cxTitle": "Connected accounts",
|
|
2990
|
+
"cxIntro": "Accounts you have connected at other services. The credential stays on this node — an app is only ever told which account it may use, never the account itself.",
|
|
2991
|
+
"cxEmpty": "No connected accounts yet.",
|
|
2992
|
+
"cxConnect": "Connect",
|
|
2993
|
+
"cxConnecting": "Connecting…",
|
|
2994
|
+
"cxDisconnect": "Disconnect",
|
|
2995
|
+
"cxReconnect": "Reconnect",
|
|
2996
|
+
"cxNeedsReauth": "needs reconnecting",
|
|
2997
|
+
"cxInstance": "instance address, e.g. mastodon.social",
|
|
2998
|
+
"cxConfirm": "Disconnect {account}? Apps that publish to it will stop being able to.",
|
|
2999
|
+
"cxRevokedTold": "Disconnected, and the service was told.",
|
|
3000
|
+
"cxRevokedLocal": "Disconnected here. The service could not be reached to be told, so check it there too.",
|
|
3001
|
+
"cxRevokeFailed": "Could not disconnect that account",
|
|
3002
|
+
"cxConnectFailed": "Could not start connecting that account",
|
|
3003
|
+
"cxNoteMastodonNeeds": "The address of your instance, for example mastodon.social.",
|
|
3004
|
+
"cxNoteMastodonBefore": "Your account lives at one instance, and the same name at another instance is a different account.",
|
|
3005
|
+
"cxNoteYoutubeBefore": "Sign in with the Google account that owns the channel. A Google account with no YouTube channel will connect and then have nowhere to publish.",
|
|
3006
|
+
"cxNoteBlueskyNeeds": "An app password. This is not your Bluesky password.",
|
|
3007
|
+
"cxNoteBlueskyWhere": "Bluesky → Settings → Privacy and security → App passwords.",
|
|
3008
|
+
"cxNoteBlueskyBefore": "This is the one service that asks you to copy a secret to us instead of approving on their own site. You can revoke it from Bluesky at any time, and it cannot be used to sign in as you."
|
|
2989
3009
|
},
|
|
2990
3010
|
"nodes": {
|
|
2991
3011
|
"title": "Personal Nodes",
|
|
@@ -3817,7 +3837,133 @@
|
|
|
3817
3837
|
"pinRemove": "Unpin",
|
|
3818
3838
|
"pinLimit": "Max 5 pinned items"
|
|
3819
3839
|
},
|
|
3820
|
-
"mcpConnected": "MCP connected"
|
|
3840
|
+
"mcpConnected": "MCP connected",
|
|
3841
|
+
"openrouter": {
|
|
3842
|
+
"title": "AI provider",
|
|
3843
|
+
"statusReady": "Key saved",
|
|
3844
|
+
"statusNoKey": "No key",
|
|
3845
|
+
"section": {
|
|
3846
|
+
"connection": "Connection",
|
|
3847
|
+
"models": "Models",
|
|
3848
|
+
"params": "Model parameters",
|
|
3849
|
+
"budget": "Budget and app spending"
|
|
3850
|
+
},
|
|
3851
|
+
"provider": "Provider",
|
|
3852
|
+
"provider_openrouter": "OpenRouter",
|
|
3853
|
+
"provider_lmstudio": "LM Studio",
|
|
3854
|
+
"provider_custom": "Custom (OpenAI-compatible)",
|
|
3855
|
+
"baseUrl": "API base URL",
|
|
3856
|
+
"baseUrl_hint": "https://…/v1",
|
|
3857
|
+
"apiKey": "API key",
|
|
3858
|
+
"apiKeyPlaceholder": "sk-or-v1-…",
|
|
3859
|
+
"apiKeyMasked": "●●●●●●●●",
|
|
3860
|
+
"saveKey": "Save key",
|
|
3861
|
+
"keySaved": "Key saved.",
|
|
3862
|
+
"providerSaved": "Provider saved.",
|
|
3863
|
+
"saveFailed": "Save failed.",
|
|
3864
|
+
"save": "Save",
|
|
3865
|
+
"cancel": "Cancel",
|
|
3866
|
+
"saveModels": "Save models",
|
|
3867
|
+
"modelsSaved": "Models saved.",
|
|
3868
|
+
"saveParams": "Save parameters",
|
|
3869
|
+
"paramsSaved": "Parameters saved.",
|
|
3870
|
+
"testConnection": "Test",
|
|
3871
|
+
"testOk": "Connection works ({model}, {ms} ms).",
|
|
3872
|
+
"testFail": "Connection failed",
|
|
3873
|
+
"testBudgetSpent": "Daily AI budget is spent — the key itself is fine",
|
|
3874
|
+
"delete": "Remove key",
|
|
3875
|
+
"deleted": "Key and settings removed.",
|
|
3876
|
+
"deleteConfirm": "Remove your API key and AI settings from this node?",
|
|
3877
|
+
"getKeyLink": "Get an API key",
|
|
3878
|
+
"creditsLink": "Check your credit balance",
|
|
3879
|
+
"modelsRefresh": "Refresh models",
|
|
3880
|
+
"modelsHint": "Fetched from the provider after you save the key.",
|
|
3881
|
+
"modelsError": "Could not load models. Check the base URL and key, save, then refresh.",
|
|
3882
|
+
"modelsEmpty": "The provider returned no models. Check the base URL.",
|
|
3883
|
+
"paramDefault": "default",
|
|
3884
|
+
"autoRetry": "Retry automatically on a validation error",
|
|
3885
|
+
"maxRetries": "Max retries",
|
|
3886
|
+
"temperature": "Temperature",
|
|
3887
|
+
"temperature_hint": "0 = deterministic, 1 = default, 2 = most creative",
|
|
3888
|
+
"topP": "Top P",
|
|
3889
|
+
"topP_hint": "Nucleus sampling: 0.1 = focused, 1.0 = full range",
|
|
3890
|
+
"maxTokens": "Max tokens",
|
|
3891
|
+
"maxTokens_hint": "Longest answer. Empty = the model's own default.",
|
|
3892
|
+
"model": {
|
|
3893
|
+
"default": "Default model",
|
|
3894
|
+
"reasoning": "Reasoning model",
|
|
3895
|
+
"reasoning_hint": "Used for planning and analysis when a caller asks for the reasoning role.",
|
|
3896
|
+
"execution": "Execution model",
|
|
3897
|
+
"execution_hint": "Used for code, tests and assembly when a caller asks for the execution role.",
|
|
3898
|
+
"rolesSummary": "Separate models for reasoning and execution",
|
|
3899
|
+
"vision": "Vision model (images)",
|
|
3900
|
+
"vision_hint": "Reads images you attach. Your default model is often text-only.",
|
|
3901
|
+
"visionNone": "Not in use — do not read images",
|
|
3902
|
+
"stt": "Speech-to-text model",
|
|
3903
|
+
"stt_hint": "Turns voice messages into text. Without one, transcription stays off.",
|
|
3904
|
+
"sttNone": "Not in use — no transcription",
|
|
3905
|
+
"none": "Not selected",
|
|
3906
|
+
"clear": "Clear",
|
|
3907
|
+
"notInList": "not in the current list",
|
|
3908
|
+
"searchPlaceholder": "Search {n} models…",
|
|
3909
|
+
"recommended": "Recommended",
|
|
3910
|
+
"showAll": "Show all {n}",
|
|
3911
|
+
"noMatch": "No model matches that search.",
|
|
3912
|
+
"openPage": "Open the model page on OpenRouter",
|
|
3913
|
+
"customIdPlaceholder": "model id, e.g. whisper-1",
|
|
3914
|
+
"customIdUse": "Use",
|
|
3915
|
+
"customIdHint": "A self-hosted endpoint may serve models its catalogue does not list."
|
|
3916
|
+
},
|
|
3917
|
+
"price": {
|
|
3918
|
+
"perMillionIn": "{usd} / M in",
|
|
3919
|
+
"perMillionOut": "{usd} / M out",
|
|
3920
|
+
"free": "free",
|
|
3921
|
+
"context": "{n} context",
|
|
3922
|
+
"audioReported": "price {value}"
|
|
3923
|
+
},
|
|
3924
|
+
"stt": {
|
|
3925
|
+
"language": "Language",
|
|
3926
|
+
"languageAuto": "Detect automatically",
|
|
3927
|
+
"language_hint": "A hint helps a single known language; leave it automatic for mixed speech.",
|
|
3928
|
+
"lang_fi": "Finnish",
|
|
3929
|
+
"lang_en": "English",
|
|
3930
|
+
"lang_sv": "Swedish",
|
|
3931
|
+
"lang_de": "German",
|
|
3932
|
+
"lang_fr": "French",
|
|
3933
|
+
"lang_es": "Spanish",
|
|
3934
|
+
"lang_et": "Estonian",
|
|
3935
|
+
"listEmpty": "This provider offers no transcription models.",
|
|
3936
|
+
"test": "Test transcription",
|
|
3937
|
+
"testing": "Transcribing…",
|
|
3938
|
+
"testNeedsModel": "Choose a model first.",
|
|
3939
|
+
"testFailed": "Test failed.",
|
|
3940
|
+
"testSilent": "(nothing recognised)",
|
|
3941
|
+
"measured": "Measured: {seconds} s of audio, ${cost}.",
|
|
3942
|
+
"costNotReported": "The provider reported no price."
|
|
3943
|
+
},
|
|
3944
|
+
"budget": {
|
|
3945
|
+
"title": "Daily AI budget",
|
|
3946
|
+
"desc": "Everything that spends your key shares this budget: apps calling AIMEAT.ai.complete(), and transcription. Once it is used up, further calls are refused.",
|
|
3947
|
+
"change": "Change",
|
|
3948
|
+
"budgetSaved": "Budget saved.",
|
|
3949
|
+
"budgetRange": "The budget must be a number between 0 and 1000.",
|
|
3950
|
+
"capRange": "The cap for \"{app}\" must be a number between 0 and 1000.",
|
|
3951
|
+
"capsSaved": "Per-app limits saved.",
|
|
3952
|
+
"saveCaps": "Save per-app limits",
|
|
3953
|
+
"saveFailed": "Save failed.",
|
|
3954
|
+
"perApp": "Per-app limits and spending ({n})",
|
|
3955
|
+
"perAppHint": "Every app may spend up to the daily budget. Set a cap to hold one below it; leave it empty to allow the full budget.",
|
|
3956
|
+
"colApp": "App",
|
|
3957
|
+
"colSpent": "Spent today",
|
|
3958
|
+
"colCap": "Daily cap ($)",
|
|
3959
|
+
"colCalls": "Calls",
|
|
3960
|
+
"chartTitle": "Usage over 30 days",
|
|
3961
|
+
"metricCost": "Cost",
|
|
3962
|
+
"metricTokens": "Tokens",
|
|
3963
|
+
"metricSeconds": "Seconds",
|
|
3964
|
+
"footnote": "Cost is estimated when the provider does not report one (LM Studio, custom). Your provider's own dashboard has the authoritative bill."
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3821
3967
|
},
|
|
3822
3968
|
"dev": {
|
|
3823
3969
|
"title": "Connect your AI to this node",
|
|
@@ -8445,5 +8591,22 @@
|
|
|
8445
8591
|
"biz.transTitle": "AI transparency your compliance officer can read",
|
|
8446
8592
|
"biz.trans1": "Article 50 of the EU AI Act has applied since 2 August 2026. What you get here is an EU-built, self-hostable platform where AI provenance is a primitive of the layer every app sits on: content a model produced carries a record of how it was made, a person reading it on the node’s own surfaces sees a label, and the operator’s legal identity and supervisory authority sit at a fixed machine-readable address your compliance officer can fetch without asking us. Content the node served can be checked by anyone, byte for byte, with no account.",
|
|
8447
8593
|
"biz.trans2": "What stays yours: the obligations that come with what you publish. Running on AIMEAT does not discharge them, and we would rather say so here than have you discover it later. What the platform does is make marking, labelling and the record the default path instead of a project.",
|
|
8448
|
-
"biz.transCta": "How this node marks AI content →"
|
|
8594
|
+
"biz.transCta": "How this node marks AI content →",
|
|
8595
|
+
"voice.record": "Record a voice message",
|
|
8596
|
+
"voice.recording": "Recording",
|
|
8597
|
+
"voice.stop": "Stop",
|
|
8598
|
+
"voice.cancel": "Discard recording",
|
|
8599
|
+
"voice.capped": "Reached the {n} second limit",
|
|
8600
|
+
"voice.errDenied": "Microphone access was denied. Allow it in your browser's site settings.",
|
|
8601
|
+
"voice.errNoMic": "No microphone found.",
|
|
8602
|
+
"voice.errUnsupported": "This browser cannot record audio.",
|
|
8603
|
+
"voice.errFailed": "Recording failed.",
|
|
8604
|
+
"inbox.transcript": "Transcript",
|
|
8605
|
+
"inbox.transcriptBySender": "written by the sender",
|
|
8606
|
+
"inbox.transcriptByYou": "transcribed by you ({model})",
|
|
8607
|
+
"inbox.transcribe": "Transcribe",
|
|
8608
|
+
"inbox.transcribing": "Transcribing…",
|
|
8609
|
+
"inbox.transcribeFailed": "Transcription failed.",
|
|
8610
|
+
"inbox.transcribeNoModel": "Choose a transcription model in Settings to turn speech into text.",
|
|
8611
|
+
"inbox.voiceMessage": "Voice message"
|
|
8449
8612
|
}
|
package/dist/locales/fi.json
CHANGED
|
@@ -2985,7 +2985,27 @@
|
|
|
2985
2985
|
"agCapSaved": "Kulutusraja tallennettu",
|
|
2986
2986
|
"agCapResetBtn": "Nollaa laskuri",
|
|
2987
2987
|
"agCapReset": "Laskuri nollattu",
|
|
2988
|
-
"agCapFailed": "Rajan muuttaminen ei onnistunut"
|
|
2988
|
+
"agCapFailed": "Rajan muuttaminen ei onnistunut",
|
|
2989
|
+
"cxTitle": "Liitetyt tilit",
|
|
2990
|
+
"cxIntro": "Tilit jotka olet liittänyt muihin palveluihin. Tunnus pysyy tällä nodella — appi saa tietää vain minkä tilin se saa käyttöönsä, ei itse tiliä.",
|
|
2991
|
+
"cxEmpty": "Et ole vielä liittänyt yhtään tiliä.",
|
|
2992
|
+
"cxConnect": "Liitä",
|
|
2993
|
+
"cxConnecting": "Liitetään…",
|
|
2994
|
+
"cxDisconnect": "Katkaise",
|
|
2995
|
+
"cxReconnect": "Valtuuta uudelleen",
|
|
2996
|
+
"cxNeedsReauth": "vaatii uuden valtuutuksen",
|
|
2997
|
+
"cxInstance": "instanssin osoite, esim. mastodon.social",
|
|
2998
|
+
"cxConfirm": "Katkaistaanko {account}? Sinne julkaisevat apit eivät enää pysty siihen.",
|
|
2999
|
+
"cxRevokedTold": "Katkaistu, ja palvelulle kerrottiin.",
|
|
3000
|
+
"cxRevokedLocal": "Katkaistu täällä. Palveluun ei saatu yhteyttä, joten tarkista tilanne myös sieltä.",
|
|
3001
|
+
"cxRevokeFailed": "Tilin katkaisu ei onnistunut",
|
|
3002
|
+
"cxConnectFailed": "Tilin liittämistä ei saatu käyntiin",
|
|
3003
|
+
"cxNoteMastodonNeeds": "Instanssisi osoite, esimerkiksi mastodon.social.",
|
|
3004
|
+
"cxNoteMastodonBefore": "Tilisi asuu yhdellä instanssilla, ja sama nimi toisella instanssilla on eri tili.",
|
|
3005
|
+
"cxNoteYoutubeBefore": "Kirjaudu sillä Google-tilillä joka omistaa kanavan. Tili jolla ei ole YouTube-kanavaa liittyy kyllä, mutta sillä ei ole mihin julkaista.",
|
|
3006
|
+
"cxNoteBlueskyNeeds": "App password. Se ei ole Bluesky-salasanasi.",
|
|
3007
|
+
"cxNoteBlueskyWhere": "Bluesky → Settings → Privacy and security → App passwords.",
|
|
3008
|
+
"cxNoteBlueskyBefore": "Tämä on ainoa palvelu joka pyytää kopioimaan salaisuuden meille sen sijaan että hyväksyisit sen heidän omalla sivullaan. Voit perua sen Blueskyssä milloin tahansa, eikä sillä voi kirjautua sinuna."
|
|
2989
3009
|
},
|
|
2990
3010
|
"nodes": {
|
|
2991
3011
|
"title": "Henkilökohtaiset solmut",
|
|
@@ -3817,7 +3837,133 @@
|
|
|
3817
3837
|
"pinRemove": "Irrota",
|
|
3818
3838
|
"pinLimit": "Enintään 5 kiinnitettyä"
|
|
3819
3839
|
},
|
|
3820
|
-
"mcpConnected": "MCP kytketty"
|
|
3840
|
+
"mcpConnected": "MCP kytketty",
|
|
3841
|
+
"openrouter": {
|
|
3842
|
+
"title": "Tekoälypalvelu",
|
|
3843
|
+
"statusReady": "Avain tallennettu",
|
|
3844
|
+
"statusNoKey": "Ei avainta",
|
|
3845
|
+
"section": {
|
|
3846
|
+
"connection": "Yhteys",
|
|
3847
|
+
"models": "Mallit",
|
|
3848
|
+
"params": "Mallin parametrit",
|
|
3849
|
+
"budget": "Budjetti ja sovellusten kulutus"
|
|
3850
|
+
},
|
|
3851
|
+
"provider": "Palvelu",
|
|
3852
|
+
"provider_openrouter": "OpenRouter",
|
|
3853
|
+
"provider_lmstudio": "LM Studio",
|
|
3854
|
+
"provider_custom": "Oma (OpenAI-yhteensopiva)",
|
|
3855
|
+
"baseUrl": "API-osoite",
|
|
3856
|
+
"baseUrl_hint": "https://…/v1",
|
|
3857
|
+
"apiKey": "API-avain",
|
|
3858
|
+
"apiKeyPlaceholder": "sk-or-v1-…",
|
|
3859
|
+
"apiKeyMasked": "●●●●●●●●",
|
|
3860
|
+
"saveKey": "Tallenna avain",
|
|
3861
|
+
"keySaved": "Avain tallennettu.",
|
|
3862
|
+
"providerSaved": "Palvelu tallennettu.",
|
|
3863
|
+
"saveFailed": "Tallennus epäonnistui.",
|
|
3864
|
+
"save": "Tallenna",
|
|
3865
|
+
"cancel": "Peruuta",
|
|
3866
|
+
"saveModels": "Tallenna mallit",
|
|
3867
|
+
"modelsSaved": "Mallit tallennettu.",
|
|
3868
|
+
"saveParams": "Tallenna parametrit",
|
|
3869
|
+
"paramsSaved": "Parametrit tallennettu.",
|
|
3870
|
+
"testConnection": "Testaa",
|
|
3871
|
+
"testOk": "Yhteys toimii ({model}, {ms} ms).",
|
|
3872
|
+
"testFail": "Yhteys ei toimi",
|
|
3873
|
+
"testBudgetSpent": "Päivän tekoälybudjetti on käytetty — avaimessa ei ole vikaa",
|
|
3874
|
+
"delete": "Poista avain",
|
|
3875
|
+
"deleted": "Avain ja asetukset poistettu.",
|
|
3876
|
+
"deleteConfirm": "Poistetaanko API-avain ja tekoälyasetukset tältä solmulta?",
|
|
3877
|
+
"getKeyLink": "Hanki API-avain",
|
|
3878
|
+
"creditsLink": "Tarkista saldo",
|
|
3879
|
+
"modelsRefresh": "Päivitä mallit",
|
|
3880
|
+
"modelsHint": "Haetaan palvelusta, kun avain on tallennettu.",
|
|
3881
|
+
"modelsError": "Malleja ei saatu. Tarkista osoite ja avain, tallenna ja päivitä.",
|
|
3882
|
+
"modelsEmpty": "Palvelu ei palauttanut yhtään mallia. Tarkista osoite.",
|
|
3883
|
+
"paramDefault": "oletus",
|
|
3884
|
+
"autoRetry": "Yritä automaattisesti uudelleen validointivirheessä",
|
|
3885
|
+
"maxRetries": "Yritysten enimmäismäärä",
|
|
3886
|
+
"temperature": "Lämpötila",
|
|
3887
|
+
"temperature_hint": "0 = toistettava, 1 = oletus, 2 = luovin",
|
|
3888
|
+
"topP": "Top P",
|
|
3889
|
+
"topP_hint": "Nucleus-otanta: 0,1 = kapea, 1,0 = koko kirjo",
|
|
3890
|
+
"maxTokens": "Enimmäistokenit",
|
|
3891
|
+
"maxTokens_hint": "Vastauksen enimmäispituus. Tyhjä = mallin oma oletus.",
|
|
3892
|
+
"model": {
|
|
3893
|
+
"default": "Oletusmalli",
|
|
3894
|
+
"reasoning": "Päättelymalli",
|
|
3895
|
+
"reasoning_hint": "Käytetään suunnitteluun ja analyysiin, kun kutsuja pyytää päättelyroolia.",
|
|
3896
|
+
"execution": "Suoritusmalli",
|
|
3897
|
+
"execution_hint": "Käytetään koodiin, testeihin ja kokoamiseen, kun kutsuja pyytää suoritusroolia.",
|
|
3898
|
+
"rolesSummary": "Erilliset mallit päättelylle ja suoritukselle",
|
|
3899
|
+
"vision": "Kuvamalli",
|
|
3900
|
+
"vision_hint": "Lukee liittämäsi kuvat. Oletusmalli on usein pelkkä tekstimalli.",
|
|
3901
|
+
"visionNone": "Ei käytössä — älä lue kuvia",
|
|
3902
|
+
"stt": "Puheentunnistusmalli",
|
|
3903
|
+
"stt_hint": "Muuttaa ääniviestit tekstiksi. Ilman mallia litterointi on pois käytöstä.",
|
|
3904
|
+
"sttNone": "Ei käytössä — ei litterointia",
|
|
3905
|
+
"none": "Ei valittu",
|
|
3906
|
+
"clear": "Tyhjennä",
|
|
3907
|
+
"notInList": "ei nykyisessä listassa",
|
|
3908
|
+
"searchPlaceholder": "Hae {n} mallista…",
|
|
3909
|
+
"recommended": "Suositellut",
|
|
3910
|
+
"showAll": "Näytä kaikki {n}",
|
|
3911
|
+
"noMatch": "Haku ei osunut yhteenkään malliin.",
|
|
3912
|
+
"openPage": "Avaa mallin sivu OpenRouterissa",
|
|
3913
|
+
"customIdPlaceholder": "mallin tunnus, esim. whisper-1",
|
|
3914
|
+
"customIdUse": "Käytä",
|
|
3915
|
+
"customIdHint": "Oma palvelin voi tarjota malleja, joita sen katalogi ei listaa."
|
|
3916
|
+
},
|
|
3917
|
+
"price": {
|
|
3918
|
+
"perMillionIn": "{usd} / M sisään",
|
|
3919
|
+
"perMillionOut": "{usd} / M ulos",
|
|
3920
|
+
"free": "ilmainen",
|
|
3921
|
+
"context": "konteksti {n}",
|
|
3922
|
+
"audioReported": "hinta {value}"
|
|
3923
|
+
},
|
|
3924
|
+
"stt": {
|
|
3925
|
+
"language": "Kieli",
|
|
3926
|
+
"languageAuto": "Tunnista automaattisesti",
|
|
3927
|
+
"language_hint": "Vihje auttaa yhdessä tunnetussa kielessä; jätä automaattiseksi sekakieliselle puheelle.",
|
|
3928
|
+
"lang_fi": "suomi",
|
|
3929
|
+
"lang_en": "englanti",
|
|
3930
|
+
"lang_sv": "ruotsi",
|
|
3931
|
+
"lang_de": "saksa",
|
|
3932
|
+
"lang_fr": "ranska",
|
|
3933
|
+
"lang_es": "espanja",
|
|
3934
|
+
"lang_et": "viro",
|
|
3935
|
+
"listEmpty": "Tämä palvelu ei tarjoa litterointimalleja.",
|
|
3936
|
+
"test": "Testaa litterointi",
|
|
3937
|
+
"testing": "Litteroidaan…",
|
|
3938
|
+
"testNeedsModel": "Valitse ensin malli.",
|
|
3939
|
+
"testFailed": "Testi epäonnistui.",
|
|
3940
|
+
"testSilent": "(ei tunnistettu mitään)",
|
|
3941
|
+
"measured": "Mitattu: {seconds} s ääntä, {cost} $.",
|
|
3942
|
+
"costNotReported": "Palvelu ei ilmoittanut hintaa."
|
|
3943
|
+
},
|
|
3944
|
+
"budget": {
|
|
3945
|
+
"title": "Päivittäinen tekoälybudjetti",
|
|
3946
|
+
"desc": "Kaikki avaintasi kuluttava jakaa tämän budjetin: AIMEAT.ai.complete() -kutsut sovelluksista ja litterointi. Kun budjetti on käytetty, seuraavat kutsut hylätään.",
|
|
3947
|
+
"change": "Muuta",
|
|
3948
|
+
"budgetSaved": "Budjetti tallennettu.",
|
|
3949
|
+
"budgetRange": "Budjetin on oltava luku väliltä 0–1000.",
|
|
3950
|
+
"capRange": "Sovelluksen \"{app}\" katon on oltava luku väliltä 0–1000.",
|
|
3951
|
+
"capsSaved": "Sovelluskohtaiset rajat tallennettu.",
|
|
3952
|
+
"saveCaps": "Tallenna sovelluskohtaiset rajat",
|
|
3953
|
+
"saveFailed": "Tallennus epäonnistui.",
|
|
3954
|
+
"perApp": "Sovelluskohtaiset rajat ja kulutus ({n})",
|
|
3955
|
+
"perAppHint": "Jokainen sovellus saa kuluttaa koko päiväbudjetin. Aseta katto pitääksesi yhden sen alla; tyhjä = koko budjetti.",
|
|
3956
|
+
"colApp": "Sovellus",
|
|
3957
|
+
"colSpent": "Kulutettu tänään",
|
|
3958
|
+
"colCap": "Päiväkatto ($)",
|
|
3959
|
+
"colCalls": "Kutsuja",
|
|
3960
|
+
"chartTitle": "Käyttö 30 päivän ajalta",
|
|
3961
|
+
"metricCost": "Kulu",
|
|
3962
|
+
"metricTokens": "Tokenit",
|
|
3963
|
+
"metricSeconds": "Sekunnit",
|
|
3964
|
+
"footnote": "Hinta on arvio, kun palvelu ei ilmoita sitä (LM Studio, oma palvelin). Palveluntarjoajan oma näkymä on lasku, joka pätee."
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3821
3967
|
},
|
|
3822
3968
|
"dev": {
|
|
3823
3969
|
"title": "Yhdistä tekoälysi tähän nodeen",
|
|
@@ -8445,5 +8591,22 @@
|
|
|
8445
8591
|
"biz.transTitle": "Tekoälyn läpinäkyvyys, jonka vastuuhenkilösi voi lukea",
|
|
8446
8592
|
"biz.trans1": "EU:n tekoälyasetuksen 50 artiklaa on sovellettu 2.8.2026 alkaen. Täällä saat eurooppalaisen, itse ylläpidettävän alustan, jossa tekoälyn alkuperätieto kuuluu siihen kerrokseen jonka päällä jokainen appi toimii: mallin tuottama sisältö kantaa mukanaan tiedon siitä miten se syntyi, noden omilla pinnoilla lukija näkee merkinnän, ja ylläpitäjän oikeushenkilö sekä valvova viranomainen ovat kiinteässä koneluettavassa osoitteessa, jonka vastuuhenkilösi voi hakea kysymättä meiltä. Noden lähettämän sisällön voi kuka tahansa tarkistaa tavu tavulta ilman tiliä.",
|
|
8447
8593
|
"biz.trans2": "Sinulle jää vastuu siitä mitä itse julkaiset. AIMEATin päällä ajaminen ei poista sitä, ja sanomme sen mieluummin tässä kuin annamme sinun huomata sen myöhemmin. Alusta tekee sen, että merkitseminen, tallentaminen ja näyttäminen on oletustie eikä projekti.",
|
|
8448
|
-
"biz.transCta": "Näin tämä node merkitsee tekoälysisällön →"
|
|
8594
|
+
"biz.transCta": "Näin tämä node merkitsee tekoälysisällön →",
|
|
8595
|
+
"voice.record": "Nauhoita ääniviesti",
|
|
8596
|
+
"voice.recording": "Nauhoitetaan",
|
|
8597
|
+
"voice.stop": "Lopeta",
|
|
8598
|
+
"voice.cancel": "Hylkää nauhoitus",
|
|
8599
|
+
"voice.capped": "Saavutti {n} sekunnin rajan",
|
|
8600
|
+
"voice.errDenied": "Mikrofonin käyttö estettiin. Salli se selaimen sivustoasetuksista.",
|
|
8601
|
+
"voice.errNoMic": "Mikrofonia ei löytynyt.",
|
|
8602
|
+
"voice.errUnsupported": "Tämä selain ei osaa nauhoittaa ääntä.",
|
|
8603
|
+
"voice.errFailed": "Nauhoitus epäonnistui.",
|
|
8604
|
+
"inbox.transcript": "Tekstitys",
|
|
8605
|
+
"inbox.transcriptBySender": "lähettäjän kirjoittama",
|
|
8606
|
+
"inbox.transcriptByYou": "sinun litteroimasi ({model})",
|
|
8607
|
+
"inbox.transcribe": "Litteroi",
|
|
8608
|
+
"inbox.transcribing": "Litteroidaan…",
|
|
8609
|
+
"inbox.transcribeFailed": "Litterointi epäonnistui.",
|
|
8610
|
+
"inbox.transcribeNoModel": "Valitse asetuksista puheentunnistusmalli, niin saat puheen tekstiksi.",
|
|
8611
|
+
"inbox.voiceMessage": "Ääniviesti"
|
|
8449
8612
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file VoiceRecorder.js
|
|
3
|
+
* @description Record-a-voice-clip control, shared by the inbox composer and the OpenRouter
|
|
4
|
+
* settings' transcription test. Wraps /js/services/audio-recorder.js in a Preact button that
|
|
5
|
+
* shows what is happening: elapsed time, a live input level so a dead microphone is obvious
|
|
6
|
+
* immediately, and separate stop-and-keep versus cancel-and-discard actions.
|
|
7
|
+
*
|
|
8
|
+
* Press to start, press again to stop. Not press-and-hold: on a touch screen that loses the
|
|
9
|
+
* recording to any accidental lift, and it makes a long message physically tiring.
|
|
10
|
+
* @structure VoiceRecorder (control) · useRecorder (state machine)
|
|
11
|
+
* @usage <${VoiceRecorder} maxSeconds=${300} onRecorded=${(file, seconds) => …} />
|
|
12
|
+
* @version-history
|
|
13
|
+
* v1.0.0 — 2026-08-01 — Initial version (voice messages + STT settings test).
|
|
14
|
+
*/
|
|
15
|
+
import { h } from 'preact';
|
|
16
|
+
import { useState, useRef, useEffect } from 'preact/hooks';
|
|
17
|
+
import htm from 'htm';
|
|
18
|
+
const html = htm.bind(h);
|
|
19
|
+
import { t } from '/js/i18n.js';
|
|
20
|
+
import { startRecording, isRecordingSupported, fmtDuration } from '/js/services/audio-recorder.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} props
|
|
24
|
+
* @param {(file: File, durationSeconds: number) => void} props.onRecorded
|
|
25
|
+
* @param {number} [props.maxSeconds] hard stop (the node's AIMEAT_VOICE_MSG_MAX_SECONDS)
|
|
26
|
+
* @param {boolean} [props.disabled]
|
|
27
|
+
* @param {string} [props.label] text next to the icon; omitted = icon only
|
|
28
|
+
* @param {string} [props.className] extra class on the idle button (host styling)
|
|
29
|
+
*/
|
|
30
|
+
export function VoiceRecorder({ onRecorded, maxSeconds = 300, disabled = false, label, className = '' }) {
|
|
31
|
+
const [state, setState] = useState('idle'); // idle | starting | recording | finishing
|
|
32
|
+
const [seconds, setSeconds] = useState(0);
|
|
33
|
+
const [level, setLevel] = useState(0);
|
|
34
|
+
const [error, setError] = useState(null);
|
|
35
|
+
const [capped, setCapped] = useState(false);
|
|
36
|
+
const recRef = useRef(null);
|
|
37
|
+
|
|
38
|
+
// Leaving the view mid-recording must release the microphone. Without this the browser's recording
|
|
39
|
+
// indicator stays lit after the user has navigated away, which reads as an app still listening.
|
|
40
|
+
useEffect(() => () => { try { recRef.current?.cancel(); } catch { /* gone already */ } }, []); // eslint-disable-line aimeat/no-silent-catch -- teardown of an already-torn-down recorder
|
|
41
|
+
|
|
42
|
+
if (!isRecordingSupported()) return null;
|
|
43
|
+
|
|
44
|
+
async function begin() {
|
|
45
|
+
setError(null); setCapped(false); setSeconds(0); setLevel(0); setState('starting');
|
|
46
|
+
try {
|
|
47
|
+
recRef.current = await startRecording({
|
|
48
|
+
maxSeconds,
|
|
49
|
+
onTick: setSeconds,
|
|
50
|
+
onLevel: setLevel,
|
|
51
|
+
onAutoStop: () => { setCapped(true); finish(); },
|
|
52
|
+
});
|
|
53
|
+
setState('recording');
|
|
54
|
+
} catch (e) {
|
|
55
|
+
setState('idle');
|
|
56
|
+
// Say which of the three it was: a denied permission, no microphone, or a browser that cannot.
|
|
57
|
+
const name = e?.name || '';
|
|
58
|
+
setError(
|
|
59
|
+
name === 'NotAllowedError' || name === 'SecurityError' ? t('voice.errDenied')
|
|
60
|
+
: name === 'NotFoundError' ? t('voice.errNoMic')
|
|
61
|
+
: e?.message === 'unsupported' ? t('voice.errUnsupported')
|
|
62
|
+
: (e?.message || t('voice.errFailed')),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function finish() {
|
|
68
|
+
const rec = recRef.current;
|
|
69
|
+
if (!rec) return;
|
|
70
|
+
setState('finishing');
|
|
71
|
+
try {
|
|
72
|
+
const { file, durationSeconds } = await rec.stop();
|
|
73
|
+
recRef.current = null;
|
|
74
|
+
setState('idle'); setSeconds(0); setLevel(0);
|
|
75
|
+
onRecorded(file, durationSeconds);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
recRef.current = null;
|
|
78
|
+
setState('idle');
|
|
79
|
+
setError(e?.message || t('voice.errFailed'));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function discard() {
|
|
84
|
+
try { recRef.current?.cancel(); } catch { /* already stopped */ } // eslint-disable-line aimeat/no-silent-catch -- cancel is best-effort; the state reset below is what matters
|
|
85
|
+
recRef.current = null;
|
|
86
|
+
setState('idle'); setSeconds(0); setLevel(0); setCapped(false);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (state === 'recording' || state === 'finishing') {
|
|
90
|
+
const near = maxSeconds > 0 && seconds >= maxSeconds - 10;
|
|
91
|
+
return html`
|
|
92
|
+
<span class="vr-active" role="group" aria-label=${t('voice.recording')}>
|
|
93
|
+
<span class="vr-dot" aria-hidden="true"></span>
|
|
94
|
+
<span class=${`vr-time${near ? ' vr-time--near' : ''}`}>${fmtDuration(seconds)}</span>
|
|
95
|
+
<span class="vr-meter" aria-hidden="true">
|
|
96
|
+
<span class="vr-meter-fill" style=${`width:${Math.round(Math.min(1, level * 1.6) * 100)}%`}></span>
|
|
97
|
+
</span>
|
|
98
|
+
<button type="button" class="btn-primary btn-sm vr-stop" disabled=${state === 'finishing'}
|
|
99
|
+
onClick=${finish} title=${t('voice.stop')} aria-label=${t('voice.stop')}>
|
|
100
|
+
${state === 'finishing' ? '…' : t('voice.stop')}
|
|
101
|
+
</button>
|
|
102
|
+
<button type="button" class="btn-ghost btn-sm vr-cancel" disabled=${state === 'finishing'}
|
|
103
|
+
onClick=${discard} title=${t('voice.cancel')} aria-label=${t('voice.cancel')}>✕</button>
|
|
104
|
+
${capped ? html`<span class="vr-note">${t('voice.capped', { n: maxSeconds })}</span>` : null}
|
|
105
|
+
</span>`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return html`
|
|
109
|
+
<span class="vr-wrap">
|
|
110
|
+
<button type="button" class=${`vr-btn ${className}`} disabled=${disabled || state === 'starting'}
|
|
111
|
+
onClick=${begin} title=${t('voice.record')} aria-label=${t('voice.record')}>
|
|
112
|
+
<span class="vr-ico" aria-hidden="true">🎤</span>${label ? html`<span class="vr-label">${label}</span>` : null}
|
|
113
|
+
</button>
|
|
114
|
+
${error ? html`<span class="vr-error">${error}</span>` : null}
|
|
115
|
+
</span>`;
|
|
116
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Account connected</title>
|
|
7
|
+
<!--
|
|
8
|
+
Where the provider's redirect lands after a connection is finished (TARGET-057).
|
|
9
|
+
|
|
10
|
+
It is a STATIC file rather than something the node renders, because the backend is protocol-only
|
|
11
|
+
and does not build pages.
|
|
12
|
+
|
|
13
|
+
IT ANNOUNCES OVER BroadcastChannel, not window.opener. This node sets a Cross-Origin-Opener-Policy,
|
|
14
|
+
which severs the opener relationship the moment the pop-up navigates to the provider — so
|
|
15
|
+
window.opener is null here and the page that opened this one cannot read window.closed either.
|
|
16
|
+
The first version relied on exactly that and produced ten "COOP would block the window.closed
|
|
17
|
+
call" errors per connection while appearing to work.
|
|
18
|
+
|
|
19
|
+
BroadcastChannel is same-origin and unaffected by COOP, so this reaches the opener directly. The
|
|
20
|
+
opener also polls the server as a fallback, because a user who closes the pop-up early never
|
|
21
|
+
reaches this page at all.
|
|
22
|
+
-->
|
|
23
|
+
<style>
|
|
24
|
+
html { color-scheme: light dark; }
|
|
25
|
+
body {
|
|
26
|
+
margin: 0; min-height: 100vh; display: grid; place-items: center;
|
|
27
|
+
font: 1rem/1.5 system-ui, sans-serif; padding: 1.5rem; text-align: center;
|
|
28
|
+
}
|
|
29
|
+
p { max-width: 28rem; margin: 0; }
|
|
30
|
+
</style>
|
|
31
|
+
</head>
|
|
32
|
+
<body>
|
|
33
|
+
<p id="msg">Account connected. You can close this window.</p>
|
|
34
|
+
<script>
|
|
35
|
+
try {
|
|
36
|
+
const ch = new BroadcastChannel('aimeat-connect');
|
|
37
|
+
ch.postMessage({ type: 'connected' });
|
|
38
|
+
ch.close();
|
|
39
|
+
} catch (err) {
|
|
40
|
+
// Reported rather than swallowed: without this message the opener falls back to polling, which
|
|
41
|
+
// is slower but still correct, and an operator should be able to tell the two paths apart.
|
|
42
|
+
console.warn('[aimeat-connect] could not announce over BroadcastChannel', err);
|
|
43
|
+
}
|
|
44
|
+
// Attempted, not assumed. A window the script did not open cannot close itself in most
|
|
45
|
+
// browsers, which is what happens when someone reaches this URL directly — and a blank page
|
|
46
|
+
// would then read as a failure when the connection has in fact already succeeded.
|
|
47
|
+
try { window.close(); } catch (err) {
|
|
48
|
+
document.getElementById('msg').textContent =
|
|
49
|
+
'Account connected. You can close this window. (' + err.message + ')';
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|