aimeat 2.4.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 +186 -20
- package/dist/locales/fi.json +186 -20
- 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/css/views/transparency.css +5 -0
- 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/landing.js +4 -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/ai-transparency-card.js +22 -17
- 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/public/views/transparency.js +50 -0
- 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 +182 -0
- package/dist/scripts/check-ai-disclosure.js.map +1 -1
- package/dist/src/cli/connect/ai-provenance-carry.d.ts +247 -0
- package/dist/src/cli/connect/ai-provenance-carry.d.ts.map +1 -0
- package/dist/src/cli/connect/ai-provenance-carry.js +334 -0
- package/dist/src/cli/connect/ai-provenance-carry.js.map +1 -0
- 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/agent-messages.d.ts +2 -0
- package/dist/src/cli/connect/mcp/tools/agent-messages.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/agent-messages.js +5 -2
- package/dist/src/cli/connect/mcp/tools/agent-messages.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/agent-tasks.d.ts +2 -0
- package/dist/src/cli/connect/mcp/tools/agent-tasks.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/agent-tasks.js +5 -2
- package/dist/src/cli/connect/mcp/tools/agent-tasks.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/apps.d.ts +3 -0
- package/dist/src/cli/connect/mcp/tools/apps.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/apps.js +13 -5
- package/dist/src/cli/connect/mcp/tools/apps.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/boards.d.ts +3 -0
- package/dist/src/cli/connect/mcp/tools/boards.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/boards.js +5 -2
- package/dist/src/cli/connect/mcp/tools/boards.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/core.d.ts +5 -0
- package/dist/src/cli/connect/mcp/tools/core.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/core.js +25 -5
- package/dist/src/cli/connect/mcp/tools/core.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts +2 -0
- package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/dm-messages.js +11 -6
- package/dist/src/cli/connect/mcp/tools/dm-messages.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/exchange.d.ts +2 -0
- package/dist/src/cli/connect/mcp/tools/exchange.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/exchange.js +8 -2
- package/dist/src/cli/connect/mcp/tools/exchange.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/knowledge.d.ts +3 -0
- package/dist/src/cli/connect/mcp/tools/knowledge.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/knowledge.js +7 -3
- package/dist/src/cli/connect/mcp/tools/knowledge.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/organisms.d.ts +2 -0
- package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/organisms.js +7 -2
- package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
- package/dist/src/cli/connect/mcp/tools/workspaces.d.ts +3 -0
- package/dist/src/cli/connect/mcp/tools/workspaces.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/workspaces.js +8 -2
- package/dist/src/cli/connect/mcp/tools/workspaces.js.map +1 -1
- package/dist/src/cli/connect/tool-call-defs-core.d.ts +3 -0
- package/dist/src/cli/connect/tool-call-defs-core.d.ts.map +1 -1
- package/dist/src/cli/connect/tool-call-defs-core.js +7 -0
- package/dist/src/cli/connect/tool-call-defs-core.js.map +1 -1
- package/dist/src/cli/connect/tool-call.d.ts +3 -0
- package/dist/src/cli/connect/tool-call.d.ts.map +1 -1
- package/dist/src/cli/connect/tool-call.js +11 -1
- package/dist/src/cli/connect/tool-call.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/data/public-pages.d.ts +12 -0
- package/dist/src/data/public-pages.d.ts.map +1 -1
- package/dist/src/data/public-pages.js +12 -0
- package/dist/src/data/public-pages.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +969 -85
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/mcp/ai-provenance-input.d.ts +45 -2
- package/dist/src/mcp/ai-provenance-input.d.ts.map +1 -1
- package/dist/src/mcp/ai-provenance-input.js +48 -2
- 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-transparency.d.ts +6 -1
- package/dist/src/routes/ai-transparency.d.ts.map +1 -1
- package/dist/src/routes/ai-transparency.js +17 -3
- package/dist/src/routes/ai-transparency.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/markdown-negotiation.d.ts +17 -0
- package/dist/src/services/markdown-negotiation.d.ts.map +1 -1
- package/dist/src/services/markdown-negotiation.js +15 -0
- package/dist/src/services/markdown-negotiation.js.map +1 -1
- 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
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file publish.ts
|
|
3
|
+
* @description The recipes that actually put something on a provider (TARGET-057 phase 7b). One
|
|
4
|
+
* function per provider, behind one interface, driven by the gate in publish-gate.ts.
|
|
5
|
+
*
|
|
6
|
+
* WHERE THIS LIVES, AND A DELIBERATE DEVIATION FROM THE SPEC. doc-t057-connect-spec puts recipes
|
|
7
|
+
* in an extension so a provider's endpoints can change without a node deploy. That reason is
|
|
8
|
+
* real and it still holds. It is not done yet, because the extension path needs two things that
|
|
9
|
+
* do not exist — a `ctx.connection()` binding into the sandbox and a streaming upload that keeps
|
|
10
|
+
* the bytes out of QuickJS — and building both before a single publish has ever succeeded would
|
|
11
|
+
* be designing the seam before knowing its shape. The house rule applies: a primitive is
|
|
12
|
+
* extracted when it has a second consumer, not before. `PublishRecipe` below is the seam, kept
|
|
13
|
+
* deliberately narrow so the move is a relocation rather than a redesign.
|
|
14
|
+
*
|
|
15
|
+
* PERMANENT AND TEMPORARY ARE DIFFERENT ANSWERS. A provider refusing the CONTENT is final and
|
|
16
|
+
* must never be retried; a provider being unreachable is not. Collapsing the two is how a
|
|
17
|
+
* rejected video gets resubmitted forever, and how an outage looks like a rejection.
|
|
18
|
+
* @structure PublishRecipe · publishToProvider · mastodonPublish · blueskyPost · youtubeUpload
|
|
19
|
+
* @usage import { publishToProvider } from './publish.js';
|
|
20
|
+
* @version-history
|
|
21
|
+
* v1.0.0 — 2026-08-02 — TARGET-057 phase 7b. Mastodon video, Bluesky text, YouTube resumable.
|
|
22
|
+
*/
|
|
23
|
+
import { safeFetch } from '../../utils/url-validator.js';
|
|
24
|
+
import { logger } from '../../utils/logger.js';
|
|
25
|
+
/** Read a param the app or the delegation supplied, as a string. */
|
|
26
|
+
function str(params, key, fallback = '') {
|
|
27
|
+
const v = params[key];
|
|
28
|
+
return typeof v === 'string' && v.length > 0 ? v : fallback;
|
|
29
|
+
}
|
|
30
|
+
/** A provider's own words, trimmed to something loggable and showable without dumping a page. */
|
|
31
|
+
async function providerSaid(res) {
|
|
32
|
+
const body = await res.text().catch((err) => {
|
|
33
|
+
// An unreadable body and an empty one would otherwise produce the same message, and the first
|
|
34
|
+
// is a transport fault while the second is just a terse provider.
|
|
35
|
+
logger.warn('connections: could not read a provider error body', { status: res.status, error: String(err) });
|
|
36
|
+
return '';
|
|
37
|
+
});
|
|
38
|
+
return body.slice(0, 400).replace(/\s+/g, ' ').trim();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Mastodon: upload the media, WAIT for it to finish processing, then post the status.
|
|
42
|
+
*
|
|
43
|
+
* The wait is the part that looks optional and is not. `POST /api/v2/media` answers 202 while the
|
|
44
|
+
* server is still transcoding, and a status posted with that id lands with no video attached — a
|
|
45
|
+
* post that looks successful to everyone including us, and is empty to a reader.
|
|
46
|
+
*/
|
|
47
|
+
const mastodonPublish = async (input) => {
|
|
48
|
+
const base = input.connection.instance;
|
|
49
|
+
if (!base)
|
|
50
|
+
return { ok: false, permanent: true, reason: 'this Mastodon connection has no instance' };
|
|
51
|
+
if (!input.file)
|
|
52
|
+
return { ok: false, permanent: true, reason: 'Mastodon needs a file to attach' };
|
|
53
|
+
const auth = { Authorization: `Bearer ${input.credential.accessToken}` };
|
|
54
|
+
let mediaId;
|
|
55
|
+
try {
|
|
56
|
+
const form = new FormData();
|
|
57
|
+
form.append('file', new Blob([new Uint8Array(input.file.bytes)], { type: input.file.mimeType }), input.file.name);
|
|
58
|
+
const alt = str(input.params, 'alt');
|
|
59
|
+
if (alt)
|
|
60
|
+
form.append('description', alt);
|
|
61
|
+
const res = await safeFetch(`${base}/api/v2/media`, {
|
|
62
|
+
method: 'POST', headers: auth, body: form, signal: AbortSignal.timeout(300_000),
|
|
63
|
+
});
|
|
64
|
+
// 422 is "we will not take this file" — wrong codec, too long, too large. Final.
|
|
65
|
+
if (res.status === 422)
|
|
66
|
+
return { ok: false, permanent: true, reason: `the instance refused the file: ${await providerSaid(res)}` };
|
|
67
|
+
if (!res.ok)
|
|
68
|
+
return { ok: false, permanent: res.status === 401 || res.status === 403, reason: `media upload failed (HTTP ${res.status})` };
|
|
69
|
+
const j = await res.json();
|
|
70
|
+
mediaId = typeof j.id === 'string' ? j.id : '';
|
|
71
|
+
if (!mediaId)
|
|
72
|
+
return { ok: false, permanent: false, reason: 'the instance returned no media id' };
|
|
73
|
+
if (res.status === 202) {
|
|
74
|
+
// Still transcoding. GET answers 206 while it works and 200 when it is ready.
|
|
75
|
+
const deadline = Date.now() + 300_000;
|
|
76
|
+
for (;;) {
|
|
77
|
+
if (Date.now() > deadline) {
|
|
78
|
+
return { ok: false, permanent: false, reason: 'the instance was still processing the video after five minutes' };
|
|
79
|
+
}
|
|
80
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
81
|
+
const poll = await safeFetch(`${base}/api/v1/media/${mediaId}`, { headers: auth, signal: AbortSignal.timeout(30_000) });
|
|
82
|
+
if (poll.status === 200)
|
|
83
|
+
break;
|
|
84
|
+
if (poll.status !== 206) {
|
|
85
|
+
return { ok: false, permanent: false, reason: `processing check failed (HTTP ${poll.status})` };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return { ok: false, permanent: false, reason: `could not reach the instance: ${err.message}` };
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const body = new URLSearchParams({
|
|
95
|
+
status: input.caption,
|
|
96
|
+
// The delegation's fixed value wins; `unlisted` is the safer default for something posted by
|
|
97
|
+
// machinery rather than by a person sitting there.
|
|
98
|
+
visibility: str(input.params, 'visibility', 'unlisted'),
|
|
99
|
+
});
|
|
100
|
+
body.append('media_ids[]', mediaId);
|
|
101
|
+
const res = await safeFetch(`${base}/api/v1/statuses`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: { ...auth, 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
104
|
+
body: body.toString(),
|
|
105
|
+
signal: AbortSignal.timeout(60_000),
|
|
106
|
+
});
|
|
107
|
+
if (!res.ok) {
|
|
108
|
+
return { ok: false, permanent: res.status === 422 || res.status === 401, reason: `posting failed (HTTP ${res.status}): ${await providerSaid(res)}` };
|
|
109
|
+
}
|
|
110
|
+
const j = await res.json();
|
|
111
|
+
return { ok: true, externalRef: typeof j.url === 'string' ? j.url : String(j.id ?? mediaId) };
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
// The MEDIA is uploaded at this point and the status is not. Temporary, so a retry re-posts —
|
|
115
|
+
// and the idempotency key is what stops that retry becoming a second post.
|
|
116
|
+
return { ok: false, permanent: false, reason: `could not post the status: ${err.message}` };
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Bluesky: a text post.
|
|
121
|
+
*
|
|
122
|
+
* Text only, on purpose. Video on Bluesky goes through a separate video service with its own job
|
|
123
|
+
* queue and its own limits, and until that is built and proven, advertising `publish-video` here
|
|
124
|
+
* would be the same "a button that cannot work" mistake the attach route was just fixed for. The
|
|
125
|
+
* provider's capability list says publish-post alone for exactly that reason.
|
|
126
|
+
*/
|
|
127
|
+
const blueskyPost = async (input) => {
|
|
128
|
+
const pds = input.credential.extra?.pds ?? 'https://bsky.social';
|
|
129
|
+
const did = input.credential.extra?.did ?? '';
|
|
130
|
+
if (!did)
|
|
131
|
+
return { ok: false, permanent: true, reason: 'this Bluesky connection has no account id' };
|
|
132
|
+
try {
|
|
133
|
+
const res = await safeFetch(`${pds}/xrpc/com.atproto.repo.createRecord`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
headers: { Authorization: `Bearer ${input.credential.accessToken}`, 'Content-Type': 'application/json' },
|
|
136
|
+
body: JSON.stringify({
|
|
137
|
+
repo: did,
|
|
138
|
+
collection: 'app.bsky.feed.post',
|
|
139
|
+
record: {
|
|
140
|
+
$type: 'app.bsky.feed.post',
|
|
141
|
+
text: input.caption,
|
|
142
|
+
createdAt: new Date().toISOString(),
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
signal: AbortSignal.timeout(60_000),
|
|
146
|
+
});
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
return { ok: false, permanent: res.status === 400 || res.status === 401, reason: `Bluesky refused the post (HTTP ${res.status}): ${await providerSaid(res)}` };
|
|
149
|
+
}
|
|
150
|
+
const j = await res.json();
|
|
151
|
+
return { ok: true, externalRef: typeof j.uri === 'string' ? j.uri : '' };
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
return { ok: false, permanent: false, reason: `could not reach Bluesky: ${err.message}` };
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/** Chunk size for the resumable upload. A multiple of 256 KiB, which the protocol requires. */
|
|
158
|
+
const RESUMABLE_CHUNK = 8 * 256 * 1024;
|
|
159
|
+
/**
|
|
160
|
+
* YouTube: a resumable upload, in chunks, that resumes from where the SERVER says it stopped.
|
|
161
|
+
*
|
|
162
|
+
* The resume is the whole reason this protocol exists and the reason it is implemented properly
|
|
163
|
+
* rather than as one big PUT: on an interruption the client does not know how much arrived, so it
|
|
164
|
+
* asks. Guessing "start again from zero" wastes an upload out of a daily allowance of six, and
|
|
165
|
+
* guessing "continue from what I sent" corrupts the file.
|
|
166
|
+
*/
|
|
167
|
+
const youtubeUpload = async (input) => {
|
|
168
|
+
if (!input.file)
|
|
169
|
+
return { ok: false, permanent: true, reason: 'YouTube needs a file' };
|
|
170
|
+
const auth = { Authorization: `Bearer ${input.credential.accessToken}` };
|
|
171
|
+
const total = input.file.bytes.length;
|
|
172
|
+
let sessionUrl;
|
|
173
|
+
try {
|
|
174
|
+
const meta = {
|
|
175
|
+
snippet: {
|
|
176
|
+
title: str(input.params, 'title', input.caption.slice(0, 100) || 'Untitled'),
|
|
177
|
+
description: str(input.params, 'description', input.caption),
|
|
178
|
+
...(Array.isArray(input.params.tags) ? { tags: input.params.tags } : {}),
|
|
179
|
+
},
|
|
180
|
+
status: {
|
|
181
|
+
// The delegation's fixed value wins. `unlisted` rather than `private`: a private video
|
|
182
|
+
// renders no thumbnail and its link 404s for everyone else, so it cannot be checked.
|
|
183
|
+
privacyStatus: str(input.params, 'privacyStatus', 'unlisted'),
|
|
184
|
+
selfDeclaredMadeForKids: false,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
const res = await safeFetch('https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status', {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
headers: {
|
|
190
|
+
...auth,
|
|
191
|
+
'Content-Type': 'application/json',
|
|
192
|
+
'X-Upload-Content-Length': String(total),
|
|
193
|
+
'X-Upload-Content-Type': input.file.mimeType,
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify(meta),
|
|
196
|
+
signal: AbortSignal.timeout(60_000),
|
|
197
|
+
});
|
|
198
|
+
if (!res.ok) {
|
|
199
|
+
// 403 here is usually the quota: six uploads a day for the whole project, and it is spent.
|
|
200
|
+
const permanent = res.status === 400 || res.status === 401;
|
|
201
|
+
return { ok: false, permanent, reason: `YouTube would not start the upload (HTTP ${res.status}): ${await providerSaid(res)}` };
|
|
202
|
+
}
|
|
203
|
+
sessionUrl = res.headers.get('location') ?? '';
|
|
204
|
+
if (!sessionUrl)
|
|
205
|
+
return { ok: false, permanent: false, reason: 'YouTube returned no upload session' };
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
return { ok: false, permanent: false, reason: `could not reach YouTube: ${err.message}` };
|
|
209
|
+
}
|
|
210
|
+
let offset = 0;
|
|
211
|
+
let consecutiveFailures = 0;
|
|
212
|
+
while (offset < total) {
|
|
213
|
+
const end = Math.min(offset + RESUMABLE_CHUNK, total) - 1;
|
|
214
|
+
try {
|
|
215
|
+
const res = await safeFetch(sessionUrl, {
|
|
216
|
+
method: 'PUT',
|
|
217
|
+
headers: {
|
|
218
|
+
'Content-Length': String(end - offset + 1),
|
|
219
|
+
'Content-Range': `bytes ${offset}-${end}/${total}`,
|
|
220
|
+
},
|
|
221
|
+
body: new Uint8Array(input.file.bytes.subarray(offset, end + 1)),
|
|
222
|
+
signal: AbortSignal.timeout(300_000),
|
|
223
|
+
});
|
|
224
|
+
if (res.status === 200 || res.status === 201) {
|
|
225
|
+
const j = await res.json().catch((err) => {
|
|
226
|
+
logger.warn('connections: YouTube upload finished with an unreadable body', { error: String(err) });
|
|
227
|
+
return null;
|
|
228
|
+
});
|
|
229
|
+
const id = typeof j?.id === 'string' ? j.id : '';
|
|
230
|
+
return id
|
|
231
|
+
? { ok: true, externalRef: `https://youtu.be/${id}` }
|
|
232
|
+
: { ok: false, permanent: false, reason: 'YouTube accepted the upload but named no video' };
|
|
233
|
+
}
|
|
234
|
+
// 308 = keep going. The Range header is the SERVER's account of what arrived, and it is the
|
|
235
|
+
// only trustworthy one — a client that assumes its own bytes landed will corrupt the file.
|
|
236
|
+
if (res.status === 308) {
|
|
237
|
+
const range = res.headers.get('range');
|
|
238
|
+
const received = range ? Number(range.split('-')[1]) : NaN;
|
|
239
|
+
offset = Number.isFinite(received) ? received + 1 : end + 1;
|
|
240
|
+
consecutiveFailures = 0;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (res.status === 404)
|
|
244
|
+
return { ok: false, permanent: true, reason: 'the upload session expired; start again' };
|
|
245
|
+
if (res.status >= 400 && res.status < 500) {
|
|
246
|
+
return { ok: false, permanent: true, reason: `YouTube rejected the upload (HTTP ${res.status}): ${await providerSaid(res)}` };
|
|
247
|
+
}
|
|
248
|
+
// 5xx: ask the server where it got to rather than resending blindly.
|
|
249
|
+
consecutiveFailures++;
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
consecutiveFailures++;
|
|
253
|
+
logger.warn('connections: a resumable chunk failed; asking YouTube where it got to', {
|
|
254
|
+
offset, total, error: String(err),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (consecutiveFailures > 5) {
|
|
258
|
+
return { ok: false, permanent: false, reason: 'the upload kept failing; the bytes already sent are still held by YouTube' };
|
|
259
|
+
}
|
|
260
|
+
// THE RESUME QUERY. An empty PUT with a range of `*` makes the server state how much it has,
|
|
261
|
+
// which is the question a client cannot answer for itself after an interruption.
|
|
262
|
+
try {
|
|
263
|
+
await new Promise((r) => setTimeout(r, 1000 * consecutiveFailures));
|
|
264
|
+
const probe = await safeFetch(sessionUrl, {
|
|
265
|
+
method: 'PUT',
|
|
266
|
+
headers: { 'Content-Length': '0', 'Content-Range': `bytes */${total}` },
|
|
267
|
+
signal: AbortSignal.timeout(60_000),
|
|
268
|
+
});
|
|
269
|
+
if (probe.status === 200 || probe.status === 201) {
|
|
270
|
+
const j = await probe.json().catch((err) => {
|
|
271
|
+
logger.warn('connections: the resume probe answered 200 with an unreadable body', { error: String(err) });
|
|
272
|
+
return null;
|
|
273
|
+
});
|
|
274
|
+
const id = typeof j?.id === 'string' ? j.id : '';
|
|
275
|
+
if (id)
|
|
276
|
+
return { ok: true, externalRef: `https://youtu.be/${id}` };
|
|
277
|
+
}
|
|
278
|
+
if (probe.status === 308) {
|
|
279
|
+
const range = probe.headers.get('range');
|
|
280
|
+
const received = range ? Number(range.split('-')[1]) : NaN;
|
|
281
|
+
offset = Number.isFinite(received) ? received + 1 : offset;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
logger.warn('connections: the resume probe itself failed', { error: String(err) });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return { ok: false, permanent: false, reason: 'the upload ended without YouTube confirming a video' };
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* The test provider's recipe. Reached only when the test provider is configured, which nothing but
|
|
292
|
+
* the E2E environment does. It exists so the ROUTE -- gate, credential, recipe, outcome, attempt
|
|
293
|
+
* update -- can be proven end to end without posting to anyone's real account, and so the
|
|
294
|
+
* permanent-versus-temporary distinction has a test rather than only a comment.
|
|
295
|
+
*/
|
|
296
|
+
const fakePublish = async (input) => {
|
|
297
|
+
const base = input.provider.endpoints(null)?.token.replace(/\/token$/, '') ?? '';
|
|
298
|
+
if (!base)
|
|
299
|
+
return { ok: false, permanent: true, reason: 'the test provider has no base URL' };
|
|
300
|
+
try {
|
|
301
|
+
const res = await safeFetch(`${base}/publish`, {
|
|
302
|
+
method: 'POST',
|
|
303
|
+
headers: { Authorization: `Bearer ${input.credential.accessToken}`, 'Content-Type': 'application/octet-stream' },
|
|
304
|
+
body: input.file ? new Uint8Array(input.file.bytes) : new Uint8Array(0),
|
|
305
|
+
signal: AbortSignal.timeout(60_000),
|
|
306
|
+
});
|
|
307
|
+
// 422 is the provider refusing the CONTENT: final, and a retry would only repeat it.
|
|
308
|
+
if (res.status === 422)
|
|
309
|
+
return { ok: false, permanent: true, reason: `refused: ${await providerSaid(res)}` };
|
|
310
|
+
if (!res.ok)
|
|
311
|
+
return { ok: false, permanent: false, reason: `publish failed (HTTP ${res.status})` };
|
|
312
|
+
const j = await res.json();
|
|
313
|
+
return { ok: true, externalRef: typeof j.url === 'string' ? j.url : '' };
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
return { ok: false, permanent: false, reason: `could not reach the test provider: ${err.message}` };
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
const RECIPES = {
|
|
320
|
+
mastodon: mastodonPublish,
|
|
321
|
+
bluesky: blueskyPost,
|
|
322
|
+
youtube: youtubeUpload,
|
|
323
|
+
fake: fakePublish,
|
|
324
|
+
'fake-static': fakePublish,
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Run the recipe for a connection's provider.
|
|
328
|
+
*
|
|
329
|
+
* A provider with no recipe is a refusal rather than a silent no-op: the gate has already recorded
|
|
330
|
+
* an attempt by the time this is called, and leaving it in flight forever would be worse than
|
|
331
|
+
* saying so.
|
|
332
|
+
*/
|
|
333
|
+
export async function publishToProvider(input) {
|
|
334
|
+
const recipe = RECIPES[input.provider.id];
|
|
335
|
+
if (!recipe) {
|
|
336
|
+
return { ok: false, permanent: true, reason: `no publish recipe for ${input.connection.provider}` };
|
|
337
|
+
}
|
|
338
|
+
return recipe(input);
|
|
339
|
+
}
|
|
340
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../../../src/services/connections/publish.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file refresh.ts
|
|
3
|
+
* @description Keeping a connection's credential usable, and taking it away (TARGET-057).
|
|
4
|
+
*
|
|
5
|
+
* SINGLE FLIGHT, AND WHY. Several providers invalidate the old refresh token as part of issuing a
|
|
6
|
+
* new one. Two concurrent publishes on one connection therefore refresh twice, and the second
|
|
7
|
+
* exchange presents a token the provider has already retired: one publish works, the other parks
|
|
8
|
+
* the connection in `needs_reauth`, and the owner is told to reconnect an account that was never
|
|
9
|
+
* broken. The claim in the store makes exactly one caller refresh; the others wait and re-read.
|
|
10
|
+
*
|
|
11
|
+
* REFRESH BEFORE EXPIRY, NOT AFTER. A token that expires mid-upload fails a request that has
|
|
12
|
+
* already moved bytes. The skew below is what turns "it expired" into "it was replaced".
|
|
13
|
+
*
|
|
14
|
+
* REVOKING MEANS REVOKING. Deleting our row while the provider still honours the token is not a
|
|
15
|
+
* revocation, it is forgetting. The provider is told first, wherever it offers a way to be told.
|
|
16
|
+
* @structure ensureFreshCredential · revokeConnection
|
|
17
|
+
* @usage import { ensureFreshCredential } from './refresh.js';
|
|
18
|
+
* @version-history
|
|
19
|
+
* v1.0.0 — 2026-08-02 — TARGET-057 Phase 1c.
|
|
20
|
+
*/
|
|
21
|
+
import type { ConnectContext } from './oauth.js';
|
|
22
|
+
import type { ConnectionCredential, ConnectionRecord } from '../../models/connection-schemas.js';
|
|
23
|
+
export type FreshResult = {
|
|
24
|
+
ok: true;
|
|
25
|
+
credential: ConnectionCredential;
|
|
26
|
+
connection: ConnectionRecord;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
code: string;
|
|
30
|
+
reason: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* A credential that will still be valid when it is used.
|
|
34
|
+
*
|
|
35
|
+
* Returns a failure rather than throwing for every expected outcome — a dead refresh token, a
|
|
36
|
+
* missing key, a provider that is gone — because every caller has to show the owner something, and
|
|
37
|
+
* an exception at this depth surfaces as a 500 in whatever background job touched it first.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ensureFreshCredential(ctx: ConnectContext, connectionId: string): Promise<FreshResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Revoke: tell the provider first where it offers a way to be told, then drop the credential.
|
|
42
|
+
*
|
|
43
|
+
* The provider call is best-effort and its failure does NOT stop the local removal. An owner who
|
|
44
|
+
* asked to disconnect an account must end up disconnected even when the provider is down; the
|
|
45
|
+
* alternative is a button that sometimes silently does nothing.
|
|
46
|
+
*/
|
|
47
|
+
export declare function revokeConnection(ctx: ConnectContext, connectionId: string): Promise<{
|
|
48
|
+
ok: true;
|
|
49
|
+
toldProvider: boolean;
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
code: string;
|
|
53
|
+
reason: string;
|
|
54
|
+
}>;
|
|
55
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../../../src/services/connections/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAYjG,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,oBAAoB,CAAC;IAAC,UAAU,EAAE,gBAAgB,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAWhD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,GACxC,OAAO,CAAC,WAAW,CAAC,CAiDtB;AAwKD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,GACxC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA4C5F"}
|