@komaa/standin-sdk 0.1.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/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/ambient.d.ts +80 -0
- package/dist/ambient.d.ts.map +1 -0
- package/dist/ambient.js +281 -0
- package/dist/ambient.js.map +1 -0
- package/dist/attachments.d.ts +149 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/attachments.js +451 -0
- package/dist/attachments.js.map +1 -0
- package/dist/audio.d.ts +80 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +170 -0
- package/dist/audio.js.map +1 -0
- package/dist/avatar.d.ts +111 -0
- package/dist/avatar.d.ts.map +1 -0
- package/dist/avatar.js +201 -0
- package/dist/avatar.js.map +1 -0
- package/dist/callServer.d.ts +151 -0
- package/dist/callServer.d.ts.map +1 -0
- package/dist/callServer.js +1097 -0
- package/dist/callServer.js.map +1 -0
- package/dist/callTools.d.ts +154 -0
- package/dist/callTools.d.ts.map +1 -0
- package/dist/callTools.js +255 -0
- package/dist/callTools.js.map +1 -0
- package/dist/chat.d.ts +226 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +574 -0
- package/dist/chat.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/consult.d.ts +147 -0
- package/dist/consult.d.ts.map +1 -0
- package/dist/consult.js +388 -0
- package/dist/consult.js.map +1 -0
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.d.ts.map +1 -0
- package/dist/delivery.js +187 -0
- package/dist/delivery.js.map +1 -0
- package/dist/echoGuard.d.ts +56 -0
- package/dist/echoGuard.d.ts.map +1 -0
- package/dist/echoGuard.js +57 -0
- package/dist/echoGuard.js.map +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -0
- package/dist/fetch.d.ts +40 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +240 -0
- package/dist/fetch.js.map +1 -0
- package/dist/gate.d.ts +80 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +249 -0
- package/dist/gate.js.map +1 -0
- package/dist/handler.d.ts +283 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +4 -0
- package/dist/handler.js.map +1 -0
- package/dist/hmac.d.ts +32 -0
- package/dist/hmac.d.ts.map +1 -0
- package/dist/hmac.js +108 -0
- package/dist/hmac.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/lane.d.ts +92 -0
- package/dist/lane.d.ts.map +1 -0
- package/dist/lane.js +269 -0
- package/dist/lane.js.map +1 -0
- package/dist/lipsync.d.ts +136 -0
- package/dist/lipsync.d.ts.map +1 -0
- package/dist/lipsync.js +310 -0
- package/dist/lipsync.js.map +1 -0
- package/dist/log.d.ts +18 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +20 -0
- package/dist/log.js.map +1 -0
- package/dist/media.d.ts +43 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +190 -0
- package/dist/media.js.map +1 -0
- package/dist/minutes.d.ts +398 -0
- package/dist/minutes.d.ts.map +1 -0
- package/dist/minutes.js +805 -0
- package/dist/minutes.js.map +1 -0
- package/dist/outbound.d.ts +388 -0
- package/dist/outbound.d.ts.map +1 -0
- package/dist/outbound.js +933 -0
- package/dist/outbound.js.map +1 -0
- package/dist/plugins/cartesia/agent.d.ts +58 -0
- package/dist/plugins/cartesia/agent.d.ts.map +1 -0
- package/dist/plugins/cartesia/agent.js +254 -0
- package/dist/plugins/cartesia/agent.js.map +1 -0
- package/dist/plugins/cartesia/cli.d.ts +3 -0
- package/dist/plugins/cartesia/cli.d.ts.map +1 -0
- package/dist/plugins/cartesia/cli.js +10 -0
- package/dist/plugins/cartesia/cli.js.map +1 -0
- package/dist/plugins/cartesia/config.d.ts +30 -0
- package/dist/plugins/cartesia/config.d.ts.map +1 -0
- package/dist/plugins/cartesia/config.js +29 -0
- package/dist/plugins/cartesia/config.js.map +1 -0
- package/dist/plugins/cartesia/handler.d.ts +40 -0
- package/dist/plugins/cartesia/handler.d.ts.map +1 -0
- package/dist/plugins/cartesia/handler.js +142 -0
- package/dist/plugins/cartesia/handler.js.map +1 -0
- package/dist/plugins/cartesia/index.d.ts +6 -0
- package/dist/plugins/cartesia/index.d.ts.map +1 -0
- package/dist/plugins/cartesia/index.js +54 -0
- package/dist/plugins/cartesia/index.js.map +1 -0
- package/dist/plugins/deepgram/agent.d.ts +60 -0
- package/dist/plugins/deepgram/agent.d.ts.map +1 -0
- package/dist/plugins/deepgram/agent.js +302 -0
- package/dist/plugins/deepgram/agent.js.map +1 -0
- package/dist/plugins/deepgram/cli.d.ts +3 -0
- package/dist/plugins/deepgram/cli.d.ts.map +1 -0
- package/dist/plugins/deepgram/cli.js +10 -0
- package/dist/plugins/deepgram/cli.js.map +1 -0
- package/dist/plugins/deepgram/config.d.ts +39 -0
- package/dist/plugins/deepgram/config.d.ts.map +1 -0
- package/dist/plugins/deepgram/config.js +66 -0
- package/dist/plugins/deepgram/config.js.map +1 -0
- package/dist/plugins/deepgram/handler.d.ts +87 -0
- package/dist/plugins/deepgram/handler.d.ts.map +1 -0
- package/dist/plugins/deepgram/handler.js +268 -0
- package/dist/plugins/deepgram/handler.js.map +1 -0
- package/dist/plugins/deepgram/index.d.ts +11 -0
- package/dist/plugins/deepgram/index.d.ts.map +1 -0
- package/dist/plugins/deepgram/index.js +76 -0
- package/dist/plugins/deepgram/index.js.map +1 -0
- package/dist/plugins/echo/cli.d.ts +3 -0
- package/dist/plugins/echo/cli.d.ts.map +1 -0
- package/dist/plugins/echo/cli.js +10 -0
- package/dist/plugins/echo/cli.js.map +1 -0
- package/dist/plugins/echo/index.d.ts +37 -0
- package/dist/plugins/echo/index.d.ts.map +1 -0
- package/dist/plugins/echo/index.js +61 -0
- package/dist/plugins/echo/index.js.map +1 -0
- package/dist/plugins/elevenlabs/agent.d.ts +72 -0
- package/dist/plugins/elevenlabs/agent.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/agent.js +305 -0
- package/dist/plugins/elevenlabs/agent.js.map +1 -0
- package/dist/plugins/elevenlabs/cli.d.ts +3 -0
- package/dist/plugins/elevenlabs/cli.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/cli.js +10 -0
- package/dist/plugins/elevenlabs/cli.js.map +1 -0
- package/dist/plugins/elevenlabs/config.d.ts +27 -0
- package/dist/plugins/elevenlabs/config.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/config.js +42 -0
- package/dist/plugins/elevenlabs/config.js.map +1 -0
- package/dist/plugins/elevenlabs/handler.d.ts +67 -0
- package/dist/plugins/elevenlabs/handler.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/handler.js +419 -0
- package/dist/plugins/elevenlabs/handler.js.map +1 -0
- package/dist/plugins/elevenlabs/index.d.ts +11 -0
- package/dist/plugins/elevenlabs/index.d.ts.map +1 -0
- package/dist/plugins/elevenlabs/index.js +72 -0
- package/dist/plugins/elevenlabs/index.js.map +1 -0
- package/dist/plugins/livekit/cli.d.ts +3 -0
- package/dist/plugins/livekit/cli.d.ts.map +1 -0
- package/dist/plugins/livekit/cli.js +10 -0
- package/dist/plugins/livekit/cli.js.map +1 -0
- package/dist/plugins/livekit/config.d.ts +47 -0
- package/dist/plugins/livekit/config.d.ts.map +1 -0
- package/dist/plugins/livekit/config.js +46 -0
- package/dist/plugins/livekit/config.js.map +1 -0
- package/dist/plugins/livekit/handler.d.ts +59 -0
- package/dist/plugins/livekit/handler.d.ts.map +1 -0
- package/dist/plugins/livekit/handler.js +211 -0
- package/dist/plugins/livekit/handler.js.map +1 -0
- package/dist/plugins/livekit/index.d.ts +6 -0
- package/dist/plugins/livekit/index.d.ts.map +1 -0
- package/dist/plugins/livekit/index.js +68 -0
- package/dist/plugins/livekit/index.js.map +1 -0
- package/dist/plugins/livekit/room.d.ts +117 -0
- package/dist/plugins/livekit/room.d.ts.map +1 -0
- package/dist/plugins/livekit/room.js +449 -0
- package/dist/plugins/livekit/room.js.map +1 -0
- package/dist/plugins/openai/agent.d.ts +74 -0
- package/dist/plugins/openai/agent.d.ts.map +1 -0
- package/dist/plugins/openai/agent.js +264 -0
- package/dist/plugins/openai/agent.js.map +1 -0
- package/dist/plugins/openai/cli.d.ts +3 -0
- package/dist/plugins/openai/cli.d.ts.map +1 -0
- package/dist/plugins/openai/cli.js +10 -0
- package/dist/plugins/openai/cli.js.map +1 -0
- package/dist/plugins/openai/config.d.ts +29 -0
- package/dist/plugins/openai/config.d.ts.map +1 -0
- package/dist/plugins/openai/config.js +37 -0
- package/dist/plugins/openai/config.js.map +1 -0
- package/dist/plugins/openai/handler.d.ts +65 -0
- package/dist/plugins/openai/handler.d.ts.map +1 -0
- package/dist/plugins/openai/handler.js +297 -0
- package/dist/plugins/openai/handler.js.map +1 -0
- package/dist/plugins/openai/index.d.ts +6 -0
- package/dist/plugins/openai/index.d.ts.map +1 -0
- package/dist/plugins/openai/index.js +72 -0
- package/dist/plugins/openai/index.js.map +1 -0
- package/dist/plugins/openclaw/allowlist.d.ts +9 -0
- package/dist/plugins/openclaw/allowlist.d.ts.map +1 -0
- package/dist/plugins/openclaw/allowlist.js +11 -0
- package/dist/plugins/openclaw/allowlist.js.map +1 -0
- package/dist/plugins/openclaw/config.d.ts +50 -0
- package/dist/plugins/openclaw/config.d.ts.map +1 -0
- package/dist/plugins/openclaw/config.js +53 -0
- package/dist/plugins/openclaw/config.js.map +1 -0
- package/dist/plugins/openclaw/handler.d.ts +90 -0
- package/dist/plugins/openclaw/handler.d.ts.map +1 -0
- package/dist/plugins/openclaw/handler.js +146 -0
- package/dist/plugins/openclaw/handler.js.map +1 -0
- package/dist/plugins/openclaw/index.d.ts +55 -0
- package/dist/plugins/openclaw/index.d.ts.map +1 -0
- package/dist/plugins/openclaw/index.js +89 -0
- package/dist/plugins/openclaw/index.js.map +1 -0
- package/dist/plugins/openclaw/openclaw.plugin.json +74 -0
- package/dist/plugins/openclaw/package.json +14 -0
- package/dist/plugins/openclaw/realtime.d.ts +82 -0
- package/dist/plugins/openclaw/realtime.d.ts.map +1 -0
- package/dist/plugins/openclaw/realtime.js +238 -0
- package/dist/plugins/openclaw/realtime.js.map +1 -0
- package/dist/plugins/openclaw/runtime.d.ts +19 -0
- package/dist/plugins/openclaw/runtime.d.ts.map +1 -0
- package/dist/plugins/openclaw/runtime.js +160 -0
- package/dist/plugins/openclaw/runtime.js.map +1 -0
- package/dist/policy.d.ts +38 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +95 -0
- package/dist/policy.js.map +1 -0
- package/dist/protocol.d.ts +44 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +77 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocolRuntime.d.ts +19 -0
- package/dist/protocolRuntime.d.ts.map +1 -0
- package/dist/protocolRuntime.js +78 -0
- package/dist/protocolRuntime.js.map +1 -0
- package/dist/smoke.d.ts +65 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +293 -0
- package/dist/smoke.js.map +1 -0
- package/dist/startup.d.ts +74 -0
- package/dist/startup.d.ts.map +1 -0
- package/dist/startup.js +112 -0
- package/dist/startup.js.map +1 -0
- package/dist/tile.d.ts +101 -0
- package/dist/tile.d.ts.map +1 -0
- package/dist/tile.js +195 -0
- package/dist/tile.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/dist/vision.d.ts +160 -0
- package/dist/vision.d.ts.map +1 -0
- package/dist/vision.js +301 -0
- package/dist/vision.js.map +1 -0
- package/dist/visionTools.d.ts +237 -0
- package/dist/visionTools.d.ts.map +1 -0
- package/dist/visionTools.js +578 -0
- package/dist/visionTools.js.map +1 -0
- package/dist/voice.d.ts +143 -0
- package/dist/voice.d.ts.map +1 -0
- package/dist/voice.js +403 -0
- package/dist/voice.js.map +1 -0
- package/package.json +129 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { type InboundMessage } from "./chat.js";
|
|
2
|
+
/**
|
|
3
|
+
* What one image may weigh. Matches the per-attachment ceiling the relay itself
|
|
4
|
+
* applies, so a larger local number could never be reached anyway.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MAX_IMAGE_BYTES: number;
|
|
7
|
+
/** Images kept from one message. Each becomes a base64 blob in front of a model. */
|
|
8
|
+
export declare const MAX_IMAGES = 4;
|
|
9
|
+
/** How long one image has to arrive. */
|
|
10
|
+
export declare const IMAGE_FETCH_TIMEOUT_MS = 10000;
|
|
11
|
+
/**
|
|
12
|
+
* How many images may be REQUESTED, whatever the outcome. The accept cap counts
|
|
13
|
+
* only successes, so a message naming fifty attachments that all time out still
|
|
14
|
+
* costs fifty timeouts and blows the turn budget. This makes the worst case
|
|
15
|
+
* arithmetic: eight tries at ten seconds.
|
|
16
|
+
*/
|
|
17
|
+
export declare const IMAGE_FETCH_ATTEMPTS = 8;
|
|
18
|
+
/** A voice note is minutes of audio, so it gets a bigger budget than a picture. */
|
|
19
|
+
export declare const MAX_CLIP_BYTES: number;
|
|
20
|
+
export declare const MAX_CLIPS = 2;
|
|
21
|
+
export declare const CLIP_FETCH_TIMEOUT_MS = 20000;
|
|
22
|
+
export declare const CLIP_FETCH_ATTEMPTS = 4;
|
|
23
|
+
/** A card's submit payload is model input, bounded because it reaches a model. */
|
|
24
|
+
export declare const CARD_PAYLOAD_MAX_CHARS = 4096;
|
|
25
|
+
/** Lines in the "what was attached" note. */
|
|
26
|
+
export declare const ATTACHMENT_NOTE_MAX_LINES = 10;
|
|
27
|
+
/** One picture from a message, ready for a vision model. */
|
|
28
|
+
export interface ChatImage {
|
|
29
|
+
readonly dataBase64: string;
|
|
30
|
+
readonly mime: string;
|
|
31
|
+
readonly name?: string;
|
|
32
|
+
readonly sizeBytes: number;
|
|
33
|
+
}
|
|
34
|
+
/** One voice note, as bytes, before anything has transcribed it. */
|
|
35
|
+
export interface ChatAudio {
|
|
36
|
+
readonly data: Buffer;
|
|
37
|
+
readonly mime: string;
|
|
38
|
+
readonly name: string;
|
|
39
|
+
}
|
|
40
|
+
/** One inbound message, assembled into what an agent is actually asked. */
|
|
41
|
+
export interface ChatTurn {
|
|
42
|
+
/** The text to put in front of the model, including every note below. */
|
|
43
|
+
readonly query: string;
|
|
44
|
+
readonly images: ChatImage[];
|
|
45
|
+
readonly voiceNote: string;
|
|
46
|
+
readonly attachmentNote: string;
|
|
47
|
+
}
|
|
48
|
+
/** The bytes of an image, since an interface carries no accessors. */
|
|
49
|
+
export declare function chatImageData(image: ChatImage): Buffer;
|
|
50
|
+
/** The `data:` form most vision APIs take directly. */
|
|
51
|
+
export declare function chatImageDataUrl(image: ChatImage): string;
|
|
52
|
+
/**
|
|
53
|
+
* Turn a voice note into words. Supplied by the handler or a speech plugin: the
|
|
54
|
+
* core ships none and reads no provider key.
|
|
55
|
+
*/
|
|
56
|
+
export type Transcriber = (data: Buffer, mime: string) => Promise<string>;
|
|
57
|
+
/** Options shared by the fetchers. */
|
|
58
|
+
export interface FetchOptions {
|
|
59
|
+
origin: string | undefined;
|
|
60
|
+
maxBytes?: number;
|
|
61
|
+
timeoutMs?: number;
|
|
62
|
+
fetchFn?: typeof fetch;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The one origin attachments may be fetched from, or undefined.
|
|
66
|
+
*
|
|
67
|
+
* Derived from the chat channel's own URL rather than configured separately, so
|
|
68
|
+
* it is right by construction for a self-hosted or local gateway and there is no
|
|
69
|
+
* second setting to get wrong.
|
|
70
|
+
*
|
|
71
|
+
* undefined means fetch nothing. That is the safe direction: an unset origin
|
|
72
|
+
* read as "anywhere" turns a configuration typo into a fetcher that a message
|
|
73
|
+
* can point at any address it likes.
|
|
74
|
+
*/
|
|
75
|
+
export declare function chatAttachmentOrigin(url?: string): string | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Pictures from a message, ready to put in front of a vision model.
|
|
78
|
+
*
|
|
79
|
+
* Best-effort per attachment: one that will not load costs that attachment,
|
|
80
|
+
* never the answer.
|
|
81
|
+
*/
|
|
82
|
+
export declare function fetchChatImages(attachments: readonly Record<string, unknown>[], options: FetchOptions & {
|
|
83
|
+
maxImages?: number;
|
|
84
|
+
}): Promise<ChatImage[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Voice notes from a message, as bytes.
|
|
87
|
+
*
|
|
88
|
+
* `video/` is accepted as well as `audio/`: some clients label a voice or video
|
|
89
|
+
* note with a container type that speech-to-text reads perfectly well.
|
|
90
|
+
*/
|
|
91
|
+
export declare function fetchChatAudio(attachments: readonly Record<string, unknown>[], options: FetchOptions & {
|
|
92
|
+
maxClips?: number;
|
|
93
|
+
}): Promise<ChatAudio[]>;
|
|
94
|
+
/**
|
|
95
|
+
* What the voice notes said, as one block of text.
|
|
96
|
+
*
|
|
97
|
+
* Empty when there are none, when no transcriber was supplied, or when every one
|
|
98
|
+
* failed. A transcriber that throws costs that clip and nothing else.
|
|
99
|
+
*/
|
|
100
|
+
export declare function transcribeVoiceMessages(attachments: readonly Record<string, unknown>[], options: FetchOptions & {
|
|
101
|
+
transcribe?: Transcriber;
|
|
102
|
+
maxClips?: number;
|
|
103
|
+
}): Promise<string>;
|
|
104
|
+
/**
|
|
105
|
+
* A plain sentence naming what came with the message.
|
|
106
|
+
*
|
|
107
|
+
* Worth saying even when nothing could be read: a model that is told a picture
|
|
108
|
+
* was attached and could not be opened says something useful, and a model told
|
|
109
|
+
* nothing answers as if the message were empty.
|
|
110
|
+
*/
|
|
111
|
+
export declare function attachmentsNote(attachments: readonly Record<string, unknown>[], status?: Map<number, string>): string;
|
|
112
|
+
/**
|
|
113
|
+
* What a button press on one of this agent's own cards submitted.
|
|
114
|
+
*
|
|
115
|
+
* A card message arrives with EMPTY text, so without this the agent is asked
|
|
116
|
+
* nothing at all and answers as though the person said nothing.
|
|
117
|
+
*/
|
|
118
|
+
export declare function cardActionNote(cardAction: Record<string, unknown> | undefined): string;
|
|
119
|
+
/** Options for {@link buildChatTurn}. */
|
|
120
|
+
export interface ChatTurnOptions {
|
|
121
|
+
origin?: string;
|
|
122
|
+
images?: boolean;
|
|
123
|
+
transcribe?: Transcriber;
|
|
124
|
+
fetchFn?: typeof fetch;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* One inbound message, assembled into what to ask an agent.
|
|
128
|
+
*
|
|
129
|
+
* The order is fixed, and it is the order a person would say it in: what they
|
|
130
|
+
* typed, what they pressed, what they said out loud, then what they attached.
|
|
131
|
+
*
|
|
132
|
+
* ```ts
|
|
133
|
+
* const turn = await buildChatTurn(message, { transcribe: myStt });
|
|
134
|
+
* const answer = await agent.ask(turn.query, turn.images.map(chatImageDataUrl));
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* Never throws. Anything that will not load is named in the note rather than
|
|
138
|
+
* failing the turn.
|
|
139
|
+
*/
|
|
140
|
+
export declare function buildChatTurn(message: InboundMessage, options?: ChatTurnOptions): Promise<ChatTurn>;
|
|
141
|
+
/**
|
|
142
|
+
* Put a voice note on disk for an engine that only takes a path.
|
|
143
|
+
*
|
|
144
|
+
* Removed on the way out, on every path. A transcription engine is handed
|
|
145
|
+
* somebody's voice, and leaving it in a temporary directory is a copy nobody
|
|
146
|
+
* decided to keep.
|
|
147
|
+
*/
|
|
148
|
+
export declare function spoolClip<T>(clip: ChatAudio, use: (path: string) => Promise<T>): Promise<T>;
|
|
149
|
+
//# sourceMappingURL=attachments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/**
|
|
4
|
+
* What somebody attached to a chat message, turned into something an agent can
|
|
5
|
+
* use.
|
|
6
|
+
*
|
|
7
|
+
* A Microsoft Teams message can carry a pasted screenshot, a dragged-in file, or
|
|
8
|
+
* a voice note. Without this the handler gets `InboundMessage.attachments` as
|
|
9
|
+
* raw objects, so the best it can do is read a JSON blob to a model, and the
|
|
10
|
+
* worst is answer a message about a picture as though nothing had been sent.
|
|
11
|
+
*
|
|
12
|
+
* {@link buildChatTurn} is the whole thing in one call: the text, the images
|
|
13
|
+
* fetched and ready to hand to a vision model, a voice note transcribed, and a
|
|
14
|
+
* plain sentence naming anything that could not be read.
|
|
15
|
+
*
|
|
16
|
+
* This is deliberately NOT in `chat.ts`. That module owns the socket, the
|
|
17
|
+
* duplicate check and the per-conversation ordering, and none of it changes
|
|
18
|
+
* here. Fetching is optional work that must never be able to wedge the
|
|
19
|
+
* transport, so it lives beside it: a handler that fetches nothing pays nothing.
|
|
20
|
+
*
|
|
21
|
+
* Every fetch is pinned to the one origin the messages themselves arrived from,
|
|
22
|
+
* and fails CLOSED. An attachment URL is signed, but not by us and not for us:
|
|
23
|
+
* it arrives inside a message somebody else wrote, and the pin is the only thing
|
|
24
|
+
* bounding where this worker can be told to go.
|
|
25
|
+
*
|
|
26
|
+
* Identical in shape to the Python SDK's `standin.attachments`.
|
|
27
|
+
*/
|
|
28
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
29
|
+
import { tmpdir } from "node:os";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
import { DEFAULT_CHAT_URL } from "./chat.js";
|
|
32
|
+
import { logger } from "./log.js";
|
|
33
|
+
/**
|
|
34
|
+
* What one image may weigh. Matches the per-attachment ceiling the relay itself
|
|
35
|
+
* applies, so a larger local number could never be reached anyway.
|
|
36
|
+
*/
|
|
37
|
+
export const MAX_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
38
|
+
/** Images kept from one message. Each becomes a base64 blob in front of a model. */
|
|
39
|
+
export const MAX_IMAGES = 4;
|
|
40
|
+
/** How long one image has to arrive. */
|
|
41
|
+
export const IMAGE_FETCH_TIMEOUT_MS = 10_000;
|
|
42
|
+
/**
|
|
43
|
+
* How many images may be REQUESTED, whatever the outcome. The accept cap counts
|
|
44
|
+
* only successes, so a message naming fifty attachments that all time out still
|
|
45
|
+
* costs fifty timeouts and blows the turn budget. This makes the worst case
|
|
46
|
+
* arithmetic: eight tries at ten seconds.
|
|
47
|
+
*/
|
|
48
|
+
export const IMAGE_FETCH_ATTEMPTS = 8;
|
|
49
|
+
/** A voice note is minutes of audio, so it gets a bigger budget than a picture. */
|
|
50
|
+
export const MAX_CLIP_BYTES = 16 * 1024 * 1024;
|
|
51
|
+
export const MAX_CLIPS = 2;
|
|
52
|
+
export const CLIP_FETCH_TIMEOUT_MS = 20_000;
|
|
53
|
+
export const CLIP_FETCH_ATTEMPTS = 4;
|
|
54
|
+
/** A card's submit payload is model input, bounded because it reaches a model. */
|
|
55
|
+
export const CARD_PAYLOAD_MAX_CHARS = 4096;
|
|
56
|
+
/** Lines in the "what was attached" note. */
|
|
57
|
+
export const ATTACHMENT_NOTE_MAX_LINES = 10;
|
|
58
|
+
/**
|
|
59
|
+
* Extensions that make a relayed FILE worth trying as an image. A pasted
|
|
60
|
+
* screenshot arrives as an image; the same file dragged in from disk arrives as
|
|
61
|
+
* a file whose declared type is a bare extension, so gating on the kind alone
|
|
62
|
+
* makes an attached picture invisible while the note says one was sent.
|
|
63
|
+
*/
|
|
64
|
+
const IMAGE_EXTENSIONS = [
|
|
65
|
+
"png",
|
|
66
|
+
"jpg",
|
|
67
|
+
"jpeg",
|
|
68
|
+
"gif",
|
|
69
|
+
"webp",
|
|
70
|
+
"bmp",
|
|
71
|
+
"heic",
|
|
72
|
+
"heif",
|
|
73
|
+
];
|
|
74
|
+
/** The same for a voice note, which also arrives as a file in practice. */
|
|
75
|
+
const AUDIO_EXTENSIONS = [
|
|
76
|
+
"wav",
|
|
77
|
+
"mp3",
|
|
78
|
+
"m4a",
|
|
79
|
+
"mp4",
|
|
80
|
+
"ogg",
|
|
81
|
+
"oga",
|
|
82
|
+
"opus",
|
|
83
|
+
"aac",
|
|
84
|
+
"amr",
|
|
85
|
+
"webm",
|
|
86
|
+
"mov",
|
|
87
|
+
"3gp",
|
|
88
|
+
];
|
|
89
|
+
const SCHEME_FOR_FETCH = {
|
|
90
|
+
"ws:": "http",
|
|
91
|
+
"wss:": "https",
|
|
92
|
+
"http:": "http",
|
|
93
|
+
"https:": "https",
|
|
94
|
+
};
|
|
95
|
+
const DEFAULT_PORT = { http: 80, https: 443 };
|
|
96
|
+
/** The bytes of an image, since an interface carries no accessors. */
|
|
97
|
+
export function chatImageData(image) {
|
|
98
|
+
return Buffer.from(image.dataBase64, "base64");
|
|
99
|
+
}
|
|
100
|
+
/** The `data:` form most vision APIs take directly. */
|
|
101
|
+
export function chatImageDataUrl(image) {
|
|
102
|
+
return `data:${image.mime};base64,${image.dataBase64}`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The one origin attachments may be fetched from, or undefined.
|
|
106
|
+
*
|
|
107
|
+
* Derived from the chat channel's own URL rather than configured separately, so
|
|
108
|
+
* it is right by construction for a self-hosted or local gateway and there is no
|
|
109
|
+
* second setting to get wrong.
|
|
110
|
+
*
|
|
111
|
+
* undefined means fetch nothing. That is the safe direction: an unset origin
|
|
112
|
+
* read as "anywhere" turns a configuration typo into a fetcher that a message
|
|
113
|
+
* can point at any address it likes.
|
|
114
|
+
*/
|
|
115
|
+
export function chatAttachmentOrigin(url) {
|
|
116
|
+
const raw = (url ?? process.env.STANDIN_CHAT_URL ?? DEFAULT_CHAT_URL).trim();
|
|
117
|
+
let parts;
|
|
118
|
+
try {
|
|
119
|
+
parts = new URL(raw);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
const scheme = SCHEME_FOR_FETCH[parts.protocol];
|
|
125
|
+
if (scheme === undefined || parts.hostname === "")
|
|
126
|
+
return undefined;
|
|
127
|
+
const port = parts.port === "" ? DEFAULT_PORT[scheme] : Number(parts.port);
|
|
128
|
+
const host = parts.hostname.toLowerCase();
|
|
129
|
+
return port === DEFAULT_PORT[scheme]
|
|
130
|
+
? `${scheme}://${host}`
|
|
131
|
+
: `${scheme}://${host}:${port}`;
|
|
132
|
+
}
|
|
133
|
+
/** Whether this URL is the origin we were told about. Fails closed. */
|
|
134
|
+
function sameOrigin(url, origin) {
|
|
135
|
+
if (!origin)
|
|
136
|
+
return false;
|
|
137
|
+
let got;
|
|
138
|
+
let want;
|
|
139
|
+
try {
|
|
140
|
+
got = new URL(url);
|
|
141
|
+
want = new URL(origin);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (got.protocol !== "http:" && got.protocol !== "https:")
|
|
147
|
+
return false;
|
|
148
|
+
const key = (u) => {
|
|
149
|
+
const scheme = u.protocol.replace(":", "");
|
|
150
|
+
const port = u.port === "" ? DEFAULT_PORT[scheme] : Number(u.port);
|
|
151
|
+
return `${scheme}|${u.hostname.toLowerCase()}|${port}`;
|
|
152
|
+
};
|
|
153
|
+
return key(got) === key(want);
|
|
154
|
+
}
|
|
155
|
+
function looksLike(name, contentType, extensions) {
|
|
156
|
+
if (extensions.includes(contentType.trim().toLowerCase()))
|
|
157
|
+
return true;
|
|
158
|
+
const suffix = name.includes(".")
|
|
159
|
+
? name.split(".").pop().trim().toLowerCase()
|
|
160
|
+
: "";
|
|
161
|
+
return extensions.includes(suffix);
|
|
162
|
+
}
|
|
163
|
+
/** Attachments worth a request, in wire order. */
|
|
164
|
+
function candidates(attachments, kind, extensions) {
|
|
165
|
+
const out = [];
|
|
166
|
+
attachments.forEach((item, index) => {
|
|
167
|
+
if (typeof item !== "object" || item === null)
|
|
168
|
+
return;
|
|
169
|
+
if (item.relayable === false)
|
|
170
|
+
return;
|
|
171
|
+
if (typeof item.url !== "string" || item.url === "")
|
|
172
|
+
return;
|
|
173
|
+
const itemKind = String(item.kind ?? "");
|
|
174
|
+
if (itemKind === kind ||
|
|
175
|
+
(itemKind === "file" &&
|
|
176
|
+
looksLike(String(item.name ?? ""), String(item.contentType ?? ""), extensions))) {
|
|
177
|
+
out.push({ index, item });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The real media type.
|
|
184
|
+
*
|
|
185
|
+
* What the RESPONSE said wins. The declared value arrived inside the message,
|
|
186
|
+
* which somebody else wrote, so letting it decide would let a message claim
|
|
187
|
+
* `image/png` for a page of HTML and have it read as a picture.
|
|
188
|
+
*
|
|
189
|
+
* The declared value is the fallback, and only when it IS a media type: a
|
|
190
|
+
* relayed file declares a bare extension, and taking that literally fails every
|
|
191
|
+
* `audio/` check.
|
|
192
|
+
*/
|
|
193
|
+
function resolveMime(declared, header) {
|
|
194
|
+
for (const candidate of [header, declared]) {
|
|
195
|
+
const value = String(candidate ?? "")
|
|
196
|
+
.split(";")[0]
|
|
197
|
+
.trim()
|
|
198
|
+
.toLowerCase();
|
|
199
|
+
if (value.includes("/"))
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The body, or undefined when it is too big.
|
|
206
|
+
*
|
|
207
|
+
* Checked while reading, not after. A content-length that lies, or is simply
|
|
208
|
+
* absent, otherwise gets to allocate whatever it likes before a later check
|
|
209
|
+
* objects.
|
|
210
|
+
*/
|
|
211
|
+
async function readCapped(response, maxBytes) {
|
|
212
|
+
const declared = response.headers.get("content-length");
|
|
213
|
+
if (declared !== null &&
|
|
214
|
+
/^\d+$/.test(declared) &&
|
|
215
|
+
Number(declared) > maxBytes)
|
|
216
|
+
return undefined;
|
|
217
|
+
const body = response.body;
|
|
218
|
+
if (body === null)
|
|
219
|
+
return undefined;
|
|
220
|
+
const reader = body.getReader();
|
|
221
|
+
const chunks = [];
|
|
222
|
+
let total = 0;
|
|
223
|
+
try {
|
|
224
|
+
for (;;) {
|
|
225
|
+
const { done, value } = await reader.read();
|
|
226
|
+
if (done)
|
|
227
|
+
break;
|
|
228
|
+
total += value.length;
|
|
229
|
+
if (total > maxBytes) {
|
|
230
|
+
await reader.cancel().catch(() => undefined);
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
chunks.push(Buffer.from(value));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
reader.releaseLock();
|
|
238
|
+
}
|
|
239
|
+
return Buffer.concat(chunks);
|
|
240
|
+
}
|
|
241
|
+
/** The shared fetch loop. Never throws, and returns what it got. */
|
|
242
|
+
async function fetchAll(attachments, spec, options) {
|
|
243
|
+
const call = options.fetchFn ?? fetch;
|
|
244
|
+
const maxBytes = options.maxBytes ?? spec.defaultMaxBytes;
|
|
245
|
+
const timeoutMs = options.timeoutMs ?? spec.defaultTimeoutMs;
|
|
246
|
+
const taken = [];
|
|
247
|
+
let attempts = 0;
|
|
248
|
+
for (const { index, item } of candidates(attachments, spec.kind, spec.extensions)) {
|
|
249
|
+
if (taken.length >= spec.maxItems || attempts >= spec.maxAttempts)
|
|
250
|
+
break;
|
|
251
|
+
const url = String(item.url);
|
|
252
|
+
if (!sameOrigin(url, options.origin)) {
|
|
253
|
+
logger.warn("standin: refusing an attachment from another origin");
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
attempts += 1;
|
|
257
|
+
try {
|
|
258
|
+
// No redirects. The URL is same-origin and signed, so a redirect off it is
|
|
259
|
+
// already anomalous, and following one reopens the door the origin pin
|
|
260
|
+
// just closed: the pin is checked on the URL in the message, not on
|
|
261
|
+
// wherever a 302 points.
|
|
262
|
+
const response = await call(url, {
|
|
263
|
+
redirect: "error",
|
|
264
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
265
|
+
});
|
|
266
|
+
if (!response.ok)
|
|
267
|
+
continue;
|
|
268
|
+
const mime = resolveMime(String(item.contentType ?? ""), response.headers.get("content-type") ?? "");
|
|
269
|
+
// Judged BEFORE a byte is read: an error page would otherwise be base64'd
|
|
270
|
+
// in front of a model as though it were a picture.
|
|
271
|
+
if (!spec.prefixes.some((prefix) => mime.startsWith(prefix)))
|
|
272
|
+
continue;
|
|
273
|
+
const body = await readCapped(response, maxBytes);
|
|
274
|
+
if (body !== undefined && body.length > 0) {
|
|
275
|
+
taken.push({ index, body, mime, name: String(item.name ?? "") });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
logger.warn(`standin: could not fetch an attachment: ${String(err)}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return taken;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Pictures from a message, ready to put in front of a vision model.
|
|
286
|
+
*
|
|
287
|
+
* Best-effort per attachment: one that will not load costs that attachment,
|
|
288
|
+
* never the answer.
|
|
289
|
+
*/
|
|
290
|
+
export async function fetchChatImages(attachments, options) {
|
|
291
|
+
const got = await fetchAll(attachments, {
|
|
292
|
+
kind: "image",
|
|
293
|
+
extensions: IMAGE_EXTENSIONS,
|
|
294
|
+
prefixes: ["image/"],
|
|
295
|
+
maxItems: options.maxImages ?? MAX_IMAGES,
|
|
296
|
+
maxAttempts: IMAGE_FETCH_ATTEMPTS,
|
|
297
|
+
defaultTimeoutMs: IMAGE_FETCH_TIMEOUT_MS,
|
|
298
|
+
defaultMaxBytes: MAX_IMAGE_BYTES,
|
|
299
|
+
}, options);
|
|
300
|
+
return got.map((t) => ({
|
|
301
|
+
dataBase64: t.body.toString("base64"),
|
|
302
|
+
mime: t.mime,
|
|
303
|
+
name: t.name || undefined,
|
|
304
|
+
sizeBytes: t.body.length,
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Voice notes from a message, as bytes.
|
|
309
|
+
*
|
|
310
|
+
* `video/` is accepted as well as `audio/`: some clients label a voice or video
|
|
311
|
+
* note with a container type that speech-to-text reads perfectly well.
|
|
312
|
+
*/
|
|
313
|
+
export async function fetchChatAudio(attachments, options) {
|
|
314
|
+
const got = await fetchAll(attachments, {
|
|
315
|
+
kind: "audio",
|
|
316
|
+
extensions: AUDIO_EXTENSIONS,
|
|
317
|
+
prefixes: ["audio/", "video/"],
|
|
318
|
+
maxItems: options.maxClips ?? MAX_CLIPS,
|
|
319
|
+
maxAttempts: CLIP_FETCH_ATTEMPTS,
|
|
320
|
+
defaultTimeoutMs: CLIP_FETCH_TIMEOUT_MS,
|
|
321
|
+
defaultMaxBytes: MAX_CLIP_BYTES,
|
|
322
|
+
}, options);
|
|
323
|
+
return got.map((t) => ({ data: t.body, mime: t.mime, name: t.name }));
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* What the voice notes said, as one block of text.
|
|
327
|
+
*
|
|
328
|
+
* Empty when there are none, when no transcriber was supplied, or when every one
|
|
329
|
+
* failed. A transcriber that throws costs that clip and nothing else.
|
|
330
|
+
*/
|
|
331
|
+
export async function transcribeVoiceMessages(attachments, options) {
|
|
332
|
+
if (options.transcribe === undefined)
|
|
333
|
+
return "";
|
|
334
|
+
const said = [];
|
|
335
|
+
for (const clip of await fetchChatAudio(attachments, options)) {
|
|
336
|
+
try {
|
|
337
|
+
const text = (await options.transcribe(clip.data, clip.mime)).trim();
|
|
338
|
+
if (text !== "")
|
|
339
|
+
said.push(text);
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
logger.warn(`standin: could not transcribe a voice message: ${String(err)}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return said.join("\n");
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* A plain sentence naming what came with the message.
|
|
349
|
+
*
|
|
350
|
+
* Worth saying even when nothing could be read: a model that is told a picture
|
|
351
|
+
* was attached and could not be opened says something useful, and a model told
|
|
352
|
+
* nothing answers as if the message were empty.
|
|
353
|
+
*/
|
|
354
|
+
export function attachmentsNote(attachments, status = new Map()) {
|
|
355
|
+
if (attachments.length === 0)
|
|
356
|
+
return "";
|
|
357
|
+
const lines = [];
|
|
358
|
+
for (const [index, item] of attachments.entries()) {
|
|
359
|
+
if (lines.length >= ATTACHMENT_NOTE_MAX_LINES) {
|
|
360
|
+
lines.push(`and ${attachments.length - lines.length} more`);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
if (typeof item !== "object" || item === null)
|
|
364
|
+
continue;
|
|
365
|
+
const name = String(item.name ?? "").trim() || "an unnamed file";
|
|
366
|
+
const kind = String(item.kind ?? "file").trim() || "file";
|
|
367
|
+
const mark = status.get(index);
|
|
368
|
+
lines.push(`- ${name} [${kind}]${mark ? ` (${mark})` : ""}`);
|
|
369
|
+
}
|
|
370
|
+
if (lines.length === 0)
|
|
371
|
+
return "";
|
|
372
|
+
return `[Attached to this message]\n${lines.join("\n")}`;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* What a button press on one of this agent's own cards submitted.
|
|
376
|
+
*
|
|
377
|
+
* A card message arrives with EMPTY text, so without this the agent is asked
|
|
378
|
+
* nothing at all and answers as though the person said nothing.
|
|
379
|
+
*/
|
|
380
|
+
export function cardActionNote(cardAction) {
|
|
381
|
+
if (cardAction === undefined || cardAction === null)
|
|
382
|
+
return "";
|
|
383
|
+
let payload;
|
|
384
|
+
try {
|
|
385
|
+
payload = JSON.stringify(cardAction, Object.keys(cardAction).sort());
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
payload = String(cardAction);
|
|
389
|
+
}
|
|
390
|
+
return `[The person used a button on your card]\n${payload.slice(0, CARD_PAYLOAD_MAX_CHARS)}`;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* One inbound message, assembled into what to ask an agent.
|
|
394
|
+
*
|
|
395
|
+
* The order is fixed, and it is the order a person would say it in: what they
|
|
396
|
+
* typed, what they pressed, what they said out loud, then what they attached.
|
|
397
|
+
*
|
|
398
|
+
* ```ts
|
|
399
|
+
* const turn = await buildChatTurn(message, { transcribe: myStt });
|
|
400
|
+
* const answer = await agent.ask(turn.query, turn.images.map(chatImageDataUrl));
|
|
401
|
+
* ```
|
|
402
|
+
*
|
|
403
|
+
* Never throws. Anything that will not load is named in the note rather than
|
|
404
|
+
* failing the turn.
|
|
405
|
+
*/
|
|
406
|
+
export async function buildChatTurn(message, options = {}) {
|
|
407
|
+
const origin = options.origin !== undefined ? options.origin : chatAttachmentOrigin();
|
|
408
|
+
const attachments = (message.attachments ?? []);
|
|
409
|
+
const shared = { origin, fetchFn: options.fetchFn };
|
|
410
|
+
const images = options.images === false ? [] : await fetchChatImages(attachments, shared);
|
|
411
|
+
const voiceNote = await transcribeVoiceMessages(attachments, {
|
|
412
|
+
...shared,
|
|
413
|
+
transcribe: options.transcribe,
|
|
414
|
+
});
|
|
415
|
+
// Which ones actually made it, so the note can say so rather than implying the
|
|
416
|
+
// model has seen something it has not.
|
|
417
|
+
const readable = new Set(images.map((i) => i.name).filter(Boolean));
|
|
418
|
+
const status = new Map();
|
|
419
|
+
attachments.forEach((item, index) => {
|
|
420
|
+
if (typeof item !== "object" || item === null)
|
|
421
|
+
return;
|
|
422
|
+
status.set(index, readable.has(String(item.name ?? "")) ? "attached" : "unreadable");
|
|
423
|
+
});
|
|
424
|
+
const card = cardActionNote(message.cardAction);
|
|
425
|
+
const attachmentNote = attachmentsNote(attachments, status);
|
|
426
|
+
const spoken = voiceNote ? `[They sent a voice message]\n${voiceNote}` : "";
|
|
427
|
+
const query = [message.text.trim(), card, spoken, attachmentNote]
|
|
428
|
+
.filter(Boolean)
|
|
429
|
+
.join("\n\n");
|
|
430
|
+
return { query, images, voiceNote, attachmentNote };
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Put a voice note on disk for an engine that only takes a path.
|
|
434
|
+
*
|
|
435
|
+
* Removed on the way out, on every path. A transcription engine is handed
|
|
436
|
+
* somebody's voice, and leaving it in a temporary directory is a copy nobody
|
|
437
|
+
* decided to keep.
|
|
438
|
+
*/
|
|
439
|
+
export async function spoolClip(clip, use) {
|
|
440
|
+
const dir = mkdtempSync(join(tmpdir(), "standin-clip-"));
|
|
441
|
+
const suffix = clip.mime.split("/").pop() || "bin";
|
|
442
|
+
const path = join(dir, `clip.${suffix}`);
|
|
443
|
+
try {
|
|
444
|
+
writeFileSync(path, clip.data);
|
|
445
|
+
return await use(path);
|
|
446
|
+
}
|
|
447
|
+
finally {
|
|
448
|
+
rmSync(dir, { recursive: true, force: true });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
//# sourceMappingURL=attachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}
|