@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
package/dist/chat.js
ADDED
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
// Copyright (c) 2026 Komaa DigiTech
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/**
|
|
4
|
+
* The messages lane: Microsoft Teams chat, without a bot credential.
|
|
5
|
+
*
|
|
6
|
+
* Managed connections only. StandIn owns the Microsoft Teams bot, authenticates the
|
|
7
|
+
* activity, resolves it to your connection and strips the bot @mention. Your
|
|
8
|
+
* handler returns text and StandIn performs the Microsoft Teams send, so your agent never
|
|
9
|
+
* holds a Bot Framework credential.
|
|
10
|
+
*
|
|
11
|
+
* Same shape as the call lane: the worker dials OUT and StandIn pushes messages
|
|
12
|
+
* down that socket, so there is no listener, no port to expose and no tunnel.
|
|
13
|
+
*
|
|
14
|
+
* Microsoft Teams message
|
|
15
|
+
* |
|
|
16
|
+
* v
|
|
17
|
+
* StandIn gateway (authenticates, normalizes, signs)
|
|
18
|
+
* | pushed down the worker's outbound socket
|
|
19
|
+
* v
|
|
20
|
+
* ChatChannel (this class)
|
|
21
|
+
* | your async handler returns reply text
|
|
22
|
+
* v
|
|
23
|
+
* back up the same socket; StandIn sends it to Microsoft Teams
|
|
24
|
+
*
|
|
25
|
+
* The twin of the Python SDK's `standin/sdk/chat.py`, method for method, with
|
|
26
|
+
* this language's casing. The shared conformance vectors assert both parse and
|
|
27
|
+
* build identical wire payloads.
|
|
28
|
+
*/
|
|
29
|
+
import { WebSocket } from "ws";
|
|
30
|
+
import { StandInError } from "./errors.js";
|
|
31
|
+
import { SIGNATURE_HEADER, TIMESTAMP_HEADER, nowMs, signHandshake, } from "./hmac.js";
|
|
32
|
+
import { logger } from "./log.js";
|
|
33
|
+
/**
|
|
34
|
+
* chat-schema.yaml SCHEMA_VERSION. A MAJOR version: additive evolution does not
|
|
35
|
+
* bump it, because the schema already requires receivers to ignore unknown
|
|
36
|
+
* fields. An integer above ours therefore means incompatible semantics.
|
|
37
|
+
*/
|
|
38
|
+
export const SCHEMA_VERSION = 1;
|
|
39
|
+
export const DEFAULT_CHAT_URL = "wss://teams.standin.komaa.com/api/chat/channel";
|
|
40
|
+
/**
|
|
41
|
+
* What may be rendered inline in a reply. Raster only, and the decoded bytes
|
|
42
|
+
* must actually carry the type's signature.
|
|
43
|
+
*
|
|
44
|
+
* `image/svg+xml` is deliberately absent and is not a gap to fill later. SVG is
|
|
45
|
+
* scriptable XML, which is precisely what "an image" must not be.
|
|
46
|
+
*/
|
|
47
|
+
export const OUTBOUND_IMAGE_CONTENT_TYPES = [
|
|
48
|
+
"image/png",
|
|
49
|
+
"image/jpeg",
|
|
50
|
+
"image/gif",
|
|
51
|
+
"image/webp",
|
|
52
|
+
];
|
|
53
|
+
/** What an inline image may weigh, decoded. */
|
|
54
|
+
export const OUTBOUND_IMAGE_MAX_BYTES = 1024 * 1024;
|
|
55
|
+
/**
|
|
56
|
+
* The first bytes each allowed type must begin with. A declared type is a claim;
|
|
57
|
+
* this is the check. Without it an HTML page or an SVG labelled image/png is
|
|
58
|
+
* posted under this bot's name.
|
|
59
|
+
*/
|
|
60
|
+
const IMAGE_MAGIC = {
|
|
61
|
+
"image/png": [Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])],
|
|
62
|
+
"image/jpeg": [Buffer.from([0xff, 0xd8, 0xff])],
|
|
63
|
+
"image/gif": [Buffer.from("GIF87a"), Buffer.from("GIF89a")],
|
|
64
|
+
"image/webp": [Buffer.from("RIFF")],
|
|
65
|
+
};
|
|
66
|
+
/** A filename reaches a chat as a download. One path-free segment, bounded. */
|
|
67
|
+
const MAX_IMAGE_NAME_CHARS = 200;
|
|
68
|
+
/**
|
|
69
|
+
* One path-free segment, bounded, or undefined.
|
|
70
|
+
*
|
|
71
|
+
* A filename reaches a chat as a download under this bot's identity, and the
|
|
72
|
+
* model that chose it is being steered by whoever is in the conversation.
|
|
73
|
+
*/
|
|
74
|
+
export function sanitizeImageName(name) {
|
|
75
|
+
if (!name)
|
|
76
|
+
return undefined;
|
|
77
|
+
let segment = name.replace(/\\/g, "/").split("/").pop() ?? "";
|
|
78
|
+
segment = [...segment]
|
|
79
|
+
.filter((ch) => ch >= " " && !'<>:"|?*'.includes(ch))
|
|
80
|
+
.join("")
|
|
81
|
+
.trim();
|
|
82
|
+
if (segment === "" || segment === "." || segment === "..")
|
|
83
|
+
return undefined;
|
|
84
|
+
if (segment.length > MAX_IMAGE_NAME_CHARS) {
|
|
85
|
+
const dot = segment.lastIndexOf(".");
|
|
86
|
+
const extension = dot > 0 ? segment.slice(dot + 1) : "";
|
|
87
|
+
const keep = MAX_IMAGE_NAME_CHARS - extension.length - 1;
|
|
88
|
+
segment =
|
|
89
|
+
extension !== "" && keep > 0
|
|
90
|
+
? `${segment.slice(0, keep)}.${extension}`
|
|
91
|
+
: segment.slice(0, MAX_IMAGE_NAME_CHARS);
|
|
92
|
+
}
|
|
93
|
+
return segment;
|
|
94
|
+
}
|
|
95
|
+
/** What these bytes actually are, or undefined. */
|
|
96
|
+
export function sniffImageType(data) {
|
|
97
|
+
for (const [contentType, signatures] of Object.entries(IMAGE_MAGIC)) {
|
|
98
|
+
if (!signatures.some((sig) => data.subarray(0, sig.length).equals(sig)))
|
|
99
|
+
continue;
|
|
100
|
+
// A RIFF container is only a webp when it says so.
|
|
101
|
+
if (contentType === "image/webp" &&
|
|
102
|
+
data.subarray(8, 12).toString("ascii") !== "WEBP")
|
|
103
|
+
continue;
|
|
104
|
+
return contentType;
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check a picture and build the wire form. Throws on anything it will not send.
|
|
110
|
+
*
|
|
111
|
+
* Three checks, and the second is the one that matters. A declared type is a
|
|
112
|
+
* claim made by whatever produced the bytes; the signature is what they are.
|
|
113
|
+
* Without it an HTML document or an SVG labelled `image/png` is posted into
|
|
114
|
+
* somebody's chat under this bot's name.
|
|
115
|
+
*/
|
|
116
|
+
export function outboundImage(data, contentType, name) {
|
|
117
|
+
const raw = typeof data === "string" ? Buffer.from(data, "base64") : data;
|
|
118
|
+
let declared = contentType.trim().toLowerCase();
|
|
119
|
+
// A common spelling that is not a media type.
|
|
120
|
+
if (declared === "image/jpg")
|
|
121
|
+
declared = "image/jpeg";
|
|
122
|
+
if (!OUTBOUND_IMAGE_CONTENT_TYPES.includes(declared)) {
|
|
123
|
+
throw new Error(`${JSON.stringify(contentType)} cannot be sent inline; it must be one of ` +
|
|
124
|
+
OUTBOUND_IMAGE_CONTENT_TYPES.join(", "));
|
|
125
|
+
}
|
|
126
|
+
if (raw.length > OUTBOUND_IMAGE_MAX_BYTES) {
|
|
127
|
+
throw new Error(`that image is ${raw.length} bytes, over the ${OUTBOUND_IMAGE_MAX_BYTES} limit`);
|
|
128
|
+
}
|
|
129
|
+
const actual = sniffImageType(raw);
|
|
130
|
+
if (actual !== declared) {
|
|
131
|
+
throw new Error(`those bytes are not ${declared}: they look like ${actual ?? "something else"}`);
|
|
132
|
+
}
|
|
133
|
+
const safe = sanitizeImageName(name);
|
|
134
|
+
return {
|
|
135
|
+
contentType: declared,
|
|
136
|
+
contentBase64: raw.toString("base64"),
|
|
137
|
+
...(safe === undefined ? {} : { name: safe }),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function imageToWire(image) {
|
|
141
|
+
const wire = {
|
|
142
|
+
contentType: image.contentType,
|
|
143
|
+
contentBase64: image.contentBase64,
|
|
144
|
+
};
|
|
145
|
+
if (image.name)
|
|
146
|
+
wire.name = image.name;
|
|
147
|
+
return wire;
|
|
148
|
+
}
|
|
149
|
+
/** How long one turn may run before it is abandoned. Serialization means a hung
|
|
150
|
+
* turn would wedge its conversation forever, so every turn is bounded. Generous:
|
|
151
|
+
* agent turns legitimately run long. */
|
|
152
|
+
const TURN_TIMEOUT_MS = 300_000;
|
|
153
|
+
/** 2 MB bounds a single inbound message, matching the call lane. */
|
|
154
|
+
const MAX_PAYLOAD_BYTES = 2 * 1024 * 1024;
|
|
155
|
+
/** True when the message came from a 1:1 chat rather than a group or channel. */
|
|
156
|
+
export function isPersonal(message) {
|
|
157
|
+
return message.scope === "personal";
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* How long a remembered 1:1 chat stays usable as a delivery target: 12 hours.
|
|
161
|
+
*
|
|
162
|
+
* Long enough to cover a working day, short enough that a conversation from
|
|
163
|
+
* last week is not treated as evidence of who is on the phone today.
|
|
164
|
+
*/
|
|
165
|
+
export const CHAT_FALLBACK_WINDOW_MS = 12 * 60 * 60 * 1000;
|
|
166
|
+
/** How many people are remembered at once, oldest first out. */
|
|
167
|
+
const MAX_REMEMBERED_CHATS = 512;
|
|
168
|
+
/**
|
|
169
|
+
* The 1:1 chats this bot has been messaged in, so a call can be answered in one.
|
|
170
|
+
*
|
|
171
|
+
* A 1:1 CALL carries no thread id and no conversation to post into, so the only
|
|
172
|
+
* honest source of the caller's own chat is a message they sent the bot from
|
|
173
|
+
* it. Feed this from the chat lane and read it from the call lane:
|
|
174
|
+
*
|
|
175
|
+
* ```ts
|
|
176
|
+
* const chats = new PersonalChats();
|
|
177
|
+
* const channel = new ChatChannel({ respond, chats });
|
|
178
|
+
* // later, on a call:
|
|
179
|
+
* const mine = chats.forCaller({ callerAadId, tenantId });
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* Both lanes have to be in one process for that to work, or the memory has to
|
|
183
|
+
* be shared some other way. Until it is, a 1:1 recap can only be delivered to
|
|
184
|
+
* somebody who has messaged this bot inside {@link CHAT_FALLBACK_WINDOW_MS}.
|
|
185
|
+
*
|
|
186
|
+
* Nothing is remembered from a group or a channel. That is decided by the
|
|
187
|
+
* message's SCOPE and by nothing else: an @mention in a team channel would
|
|
188
|
+
* otherwise make that channel the caller's "chat" and put a private escalation
|
|
189
|
+
* in front of their whole team.
|
|
190
|
+
*/
|
|
191
|
+
export class PersonalChats {
|
|
192
|
+
/** Keyed by tenant and person, in order of last seen, and bounded. */
|
|
193
|
+
#chats = new Map();
|
|
194
|
+
#windowMs;
|
|
195
|
+
constructor(windowMs = CHAT_FALLBACK_WINDOW_MS) {
|
|
196
|
+
this.#windowMs = windowMs;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Record a 1:1 chat. Anything else is ignored, including a message whose
|
|
200
|
+
* conversation id merely looks personal.
|
|
201
|
+
*/
|
|
202
|
+
remember(message, atMs = nowMs()) {
|
|
203
|
+
// Scope, and only scope. A bot's personal chat is addressed "a:1..." while
|
|
204
|
+
// "19:..." is precisely the group and channel shape this excludes, so an
|
|
205
|
+
// id-prefix test rejects every real personal chat and admits nothing.
|
|
206
|
+
if (!isPersonal(message))
|
|
207
|
+
return;
|
|
208
|
+
const conversationId = (message.conversationId ?? "").trim();
|
|
209
|
+
const tenantId = (message.tenantId ?? "").trim();
|
|
210
|
+
if (conversationId === "" || tenantId === "")
|
|
211
|
+
return;
|
|
212
|
+
const aadId = (message.senderAadId ?? "").trim();
|
|
213
|
+
const key = chatKey(tenantId, aadId);
|
|
214
|
+
// Re-inserted rather than overwritten: a Map keeps insertion order, and
|
|
215
|
+
// that order is what decides who is forgotten first.
|
|
216
|
+
this.#chats.delete(key);
|
|
217
|
+
this.#chats.set(key, {
|
|
218
|
+
conversationId,
|
|
219
|
+
tenantId,
|
|
220
|
+
aadId,
|
|
221
|
+
displayName: message.senderName ?? "",
|
|
222
|
+
atMs,
|
|
223
|
+
});
|
|
224
|
+
if (this.#chats.size > MAX_REMEMBERED_CHATS) {
|
|
225
|
+
const oldest = this.#chats.keys().next();
|
|
226
|
+
if (!oldest.done)
|
|
227
|
+
this.#chats.delete(oldest.value);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* The chat a call may be answered in, or undefined when there is not one.
|
|
232
|
+
*
|
|
233
|
+
* Four things have to hold, and posting call content into the wrong
|
|
234
|
+
* conversation is the failure all four exist to prevent: the chat was a 1:1
|
|
235
|
+
* chat, it is in this tenant, it was seen inside
|
|
236
|
+
* {@link CHAT_FALLBACK_WINDOW_MS}, and the call names a caller whose AAD id
|
|
237
|
+
* is the one that sent it. Without that last rule every anonymous caller
|
|
238
|
+
* collapses onto whoever chatted last.
|
|
239
|
+
*/
|
|
240
|
+
forCaller(options) {
|
|
241
|
+
const tenantId = (options.tenantId ?? "").trim();
|
|
242
|
+
if (tenantId === "")
|
|
243
|
+
return undefined;
|
|
244
|
+
const now = options.nowMs ?? nowMs();
|
|
245
|
+
const callerAadId = (options.callerAadId ?? "").trim();
|
|
246
|
+
if (callerAadId !== "") {
|
|
247
|
+
const chat = this.#chats.get(chatKey(tenantId, callerAadId));
|
|
248
|
+
return chat !== undefined && this.#fresh(chat, now) ? chat : undefined;
|
|
249
|
+
}
|
|
250
|
+
if (options.allowUnidentified !== true)
|
|
251
|
+
return undefined;
|
|
252
|
+
logger.warn("standin: allowUnidentified is on, so this call is being matched to whoever " +
|
|
253
|
+
"last messaged this bot in the tenant rather than to an identified caller");
|
|
254
|
+
let best;
|
|
255
|
+
for (const chat of this.#chats.values()) {
|
|
256
|
+
if (chat.tenantId !== tenantId || !this.#fresh(chat, now))
|
|
257
|
+
continue;
|
|
258
|
+
if (best === undefined || chat.atMs >= best.atMs)
|
|
259
|
+
best = chat;
|
|
260
|
+
}
|
|
261
|
+
return best;
|
|
262
|
+
}
|
|
263
|
+
#fresh(chat, now) {
|
|
264
|
+
return now - chat.atMs <= this.#windowMs;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/** A tenant and a person. The space cannot appear in either half. */
|
|
268
|
+
function chatKey(tenantId, aadId) {
|
|
269
|
+
return `${tenantId} ${aadId}`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Parse and validate an inbound message. Throws naming the problem; the caller
|
|
273
|
+
* maps that to HTTP 400.
|
|
274
|
+
*/
|
|
275
|
+
export function parseInbound(body) {
|
|
276
|
+
let raw;
|
|
277
|
+
try {
|
|
278
|
+
raw = JSON.parse(body);
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
throw new StandInError("malformed json");
|
|
282
|
+
}
|
|
283
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
284
|
+
throw new StandInError("body must be an object");
|
|
285
|
+
}
|
|
286
|
+
const rec = raw;
|
|
287
|
+
for (const key of ["tenantId", "conversationId", "activityId"]) {
|
|
288
|
+
if (typeof rec[key] !== "string" || rec[key] === "") {
|
|
289
|
+
throw new StandInError(`${key} is required`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const version = rec.schemaVersion ?? SCHEMA_VERSION;
|
|
293
|
+
if (typeof version === "number" &&
|
|
294
|
+
Number.isInteger(version) &&
|
|
295
|
+
version > SCHEMA_VERSION) {
|
|
296
|
+
throw new StandInError(`unsupported schemaVersion ${version} (this plugin speaks ${SCHEMA_VERSION})`);
|
|
297
|
+
}
|
|
298
|
+
const rawSender = rec.sender;
|
|
299
|
+
const sender = typeof rawSender === "object" && rawSender !== null
|
|
300
|
+
? rawSender
|
|
301
|
+
: {};
|
|
302
|
+
const scope = rec.scope;
|
|
303
|
+
const str = (v) => typeof v === "string" ? v : undefined;
|
|
304
|
+
return {
|
|
305
|
+
tenantId: rec.tenantId,
|
|
306
|
+
conversationId: rec.conversationId,
|
|
307
|
+
activityId: rec.activityId,
|
|
308
|
+
// ChatScope is an OPEN enum: an unknown value relays as a group chat rather
|
|
309
|
+
// than being rejected.
|
|
310
|
+
scope: typeof scope === "string" && scope ? scope : "personal",
|
|
311
|
+
text: typeof rec.text === "string" ? rec.text : "",
|
|
312
|
+
senderName: str(sender.displayName),
|
|
313
|
+
senderAadId: str(sender.aadObjectId),
|
|
314
|
+
senderIsGuest: Boolean(sender.isGuest ?? false),
|
|
315
|
+
senderIsLinkedOwner: Boolean(sender.isLinkedOwner ?? false),
|
|
316
|
+
attachments: Array.isArray(rec.attachments)
|
|
317
|
+
? rec.attachments
|
|
318
|
+
: [],
|
|
319
|
+
mentions: Array.isArray(rec.mentions) ? rec.mentions : [],
|
|
320
|
+
locale: str(rec.locale),
|
|
321
|
+
cardAction: typeof rec.cardAction === "object" &&
|
|
322
|
+
rec.cardAction !== null &&
|
|
323
|
+
!Array.isArray(rec.cardAction)
|
|
324
|
+
? rec.cardAction
|
|
325
|
+
: undefined,
|
|
326
|
+
bindingId: str(rec.bindingId),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The gateway-bound reply. tenantId and conversationId echo the inbound
|
|
331
|
+
* EXACTLY: the gateway rejects a mismatch, and that check is the cross-tenant
|
|
332
|
+
* leak guard the whole relay rests on.
|
|
333
|
+
*/
|
|
334
|
+
export function buildReply(message, text, kind = "message", image) {
|
|
335
|
+
const reply = {
|
|
336
|
+
schemaVersion: SCHEMA_VERSION,
|
|
337
|
+
tenantId: message.tenantId,
|
|
338
|
+
conversationId: message.conversationId,
|
|
339
|
+
replyToId: message.activityId,
|
|
340
|
+
kind,
|
|
341
|
+
idempotencyKey: `${message.activityId}:${kind}`,
|
|
342
|
+
};
|
|
343
|
+
if (message.bindingId) {
|
|
344
|
+
// Which connection this reply is FROM. One tenant can have several, so the
|
|
345
|
+
// tenant alone no longer identifies the sender.
|
|
346
|
+
reply.bindingId = message.bindingId;
|
|
347
|
+
}
|
|
348
|
+
if (kind !== "typing") {
|
|
349
|
+
reply.text = text;
|
|
350
|
+
// A typing indicator carries neither: it is a state, not a message.
|
|
351
|
+
if (image !== undefined)
|
|
352
|
+
reply.image = imageToWire(image);
|
|
353
|
+
}
|
|
354
|
+
return reply;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* At-least-once dedupe on the schema's activityId idempotency key. Bounded LRU:
|
|
358
|
+
* an aged-out redelivery running again is acceptable at-least-once behaviour, a
|
|
359
|
+
* fresh double-run is not.
|
|
360
|
+
*/
|
|
361
|
+
class Seen {
|
|
362
|
+
#capacity;
|
|
363
|
+
#seen = new Set();
|
|
364
|
+
constructor(capacity = 2048) {
|
|
365
|
+
this.#capacity = capacity;
|
|
366
|
+
}
|
|
367
|
+
markFirst(key) {
|
|
368
|
+
if (this.#seen.has(key))
|
|
369
|
+
return false;
|
|
370
|
+
this.#seen.add(key);
|
|
371
|
+
if (this.#seen.size > this.#capacity) {
|
|
372
|
+
// Set preserves insertion order, so the first entry is the oldest.
|
|
373
|
+
const oldest = this.#seen.values().next();
|
|
374
|
+
if (!oldest.done)
|
|
375
|
+
this.#seen.delete(oldest.value);
|
|
376
|
+
}
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Answer Microsoft Teams messages with your agent.
|
|
382
|
+
*
|
|
383
|
+
* Dialed out from the worker, like the call lane, so nothing listens and there
|
|
384
|
+
* is nothing to expose. Managed connections only, and that needs no flag: the
|
|
385
|
+
* socket authenticates with your connection secret, so if it opens at all you
|
|
386
|
+
* are managed.
|
|
387
|
+
*
|
|
388
|
+
* ```ts
|
|
389
|
+
* const chat = new ChatChannel({
|
|
390
|
+
* respond: async (msg) => `You said: ${msg.text}`,
|
|
391
|
+
* });
|
|
392
|
+
* await chat.start();
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
export class ChatChannel {
|
|
396
|
+
#respond;
|
|
397
|
+
#secret;
|
|
398
|
+
#url;
|
|
399
|
+
#seen = new Seen();
|
|
400
|
+
#chats;
|
|
401
|
+
#ws;
|
|
402
|
+
#closed = false;
|
|
403
|
+
/**
|
|
404
|
+
* Per-conversation chains. The schema promises per-conversation ORDERING;
|
|
405
|
+
* independent tasks would let replies overtake each other.
|
|
406
|
+
*/
|
|
407
|
+
#chains = new Map();
|
|
408
|
+
constructor(options) {
|
|
409
|
+
this.#secret =
|
|
410
|
+
options.secret ??
|
|
411
|
+
process.env.STANDIN_CHAT_SECRET ??
|
|
412
|
+
process.env.STANDIN_SECRET ??
|
|
413
|
+
"";
|
|
414
|
+
if (!this.#secret) {
|
|
415
|
+
throw new StandInError("a StandIn connection secret is required: pass secret or set STANDIN_SECRET");
|
|
416
|
+
}
|
|
417
|
+
this.#respond = options.respond;
|
|
418
|
+
this.#url = options.url ?? process.env.STANDIN_CHAT_URL ?? DEFAULT_CHAT_URL;
|
|
419
|
+
this.#chats = options.chats;
|
|
420
|
+
}
|
|
421
|
+
/** Dial StandIn and begin taking messages. */
|
|
422
|
+
async start() {
|
|
423
|
+
const timestamp = nowMs();
|
|
424
|
+
const ws = new WebSocket(this.#url, {
|
|
425
|
+
headers: {
|
|
426
|
+
[TIMESTAMP_HEADER]: String(timestamp),
|
|
427
|
+
// The channel NAME is what the handshake signs here, not the body. The
|
|
428
|
+
// POST relay lane signs the body instead; the two are different lanes
|
|
429
|
+
// and both are correct. Do not "fix" one into the other.
|
|
430
|
+
[SIGNATURE_HEADER]: signHandshake(this.#secret, timestamp, "chat"),
|
|
431
|
+
},
|
|
432
|
+
maxPayload: MAX_PAYLOAD_BYTES,
|
|
433
|
+
});
|
|
434
|
+
try {
|
|
435
|
+
await new Promise((resolve, reject) => {
|
|
436
|
+
ws.once("open", () => resolve());
|
|
437
|
+
ws.once("error", reject);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
ws.terminate();
|
|
442
|
+
throw err;
|
|
443
|
+
}
|
|
444
|
+
this.#ws = ws;
|
|
445
|
+
ws.on("message", (data, isBinary) => {
|
|
446
|
+
if (isBinary)
|
|
447
|
+
return;
|
|
448
|
+
this.#onMessage(data.toString("utf8"));
|
|
449
|
+
});
|
|
450
|
+
ws.on("error", (err) => {
|
|
451
|
+
logger.error(`standin: chat channel failed: ${String(err)}`);
|
|
452
|
+
});
|
|
453
|
+
logger.info("standin: chat channel open");
|
|
454
|
+
}
|
|
455
|
+
/** Stop taking messages and close the socket. */
|
|
456
|
+
async aclose() {
|
|
457
|
+
this.#closed = true;
|
|
458
|
+
// Let in-flight turns settle so a reply already handed to the agent is not
|
|
459
|
+
// silently dropped on shutdown.
|
|
460
|
+
await Promise.allSettled([...this.#chains.values()]);
|
|
461
|
+
this.#chains.clear();
|
|
462
|
+
const ws = this.#ws;
|
|
463
|
+
this.#ws = undefined;
|
|
464
|
+
if (ws !== undefined && ws.readyState === WebSocket.OPEN) {
|
|
465
|
+
try {
|
|
466
|
+
ws.close();
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
ws.terminate();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
#onMessage(body) {
|
|
474
|
+
if (this.#closed)
|
|
475
|
+
return;
|
|
476
|
+
let inbound;
|
|
477
|
+
try {
|
|
478
|
+
inbound = parseInbound(body);
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
logger.warn(`standin: dropping malformed chat message: ${String(err)}`);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
// Before the dedupe: a redelivery is still evidence that this person has a
|
|
485
|
+
// chat with this bot, and remembering it twice changes nothing.
|
|
486
|
+
this.#chats?.remember(inbound);
|
|
487
|
+
// The turn, though, runs once: StandIn is at-least-once, and a redelivery
|
|
488
|
+
// must not start a second turn for the same activity.
|
|
489
|
+
const key = `${inbound.tenantId}:${inbound.conversationId}:${inbound.activityId}`;
|
|
490
|
+
if (this.#seen.markFirst(key))
|
|
491
|
+
this.#enqueue(inbound);
|
|
492
|
+
}
|
|
493
|
+
#enqueue(message) {
|
|
494
|
+
const chainKey = `${message.tenantId}:${message.conversationId}`;
|
|
495
|
+
const previous = this.#chains.get(chainKey) ?? Promise.resolve();
|
|
496
|
+
// A failed turn must not dam the chain, so the previous rejection is
|
|
497
|
+
// swallowed HERE rather than propagated into this turn.
|
|
498
|
+
const next = previous
|
|
499
|
+
.catch(() => undefined)
|
|
500
|
+
.then(() => this.#process(message));
|
|
501
|
+
this.#chains.set(chainKey, next);
|
|
502
|
+
void next.finally(() => {
|
|
503
|
+
if (this.#chains.get(chainKey) === next)
|
|
504
|
+
this.#chains.delete(chainKey);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
async #process(message) {
|
|
508
|
+
// Typing is a courtesy, so it must not sit in FRONT of the turn. Send it and
|
|
509
|
+
// let the agent think; the indicator still lands first.
|
|
510
|
+
this.#send(buildReply(message, "", "typing"));
|
|
511
|
+
try {
|
|
512
|
+
const text = await withTimeout(this.#respond(message), TURN_TIMEOUT_MS);
|
|
513
|
+
if (text && text.trim()) {
|
|
514
|
+
this.#send(buildReply(message, text));
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
// After a typing indicator, silence looks exactly like a hang.
|
|
518
|
+
logger.warn("standin: chat handler returned an empty answer");
|
|
519
|
+
this.#send(buildReply(message, "I couldn't come up with an answer to that - try rephrasing, or ask something else.", "error"));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
logger.error(`standin: chat turn failed: ${String(err)}`);
|
|
524
|
+
this.#send(buildReply(message, "Something went wrong answering that - please try again.", "error"));
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Post into a Microsoft Teams conversation with no inbound message to answer.
|
|
529
|
+
*
|
|
530
|
+
* Useful from inside a call. Best-effort: returns false rather than throwing,
|
|
531
|
+
* because a failed post must never break a live call.
|
|
532
|
+
*/
|
|
533
|
+
async send(options) {
|
|
534
|
+
const payload = {
|
|
535
|
+
schemaVersion: SCHEMA_VERSION,
|
|
536
|
+
tenantId: options.tenantId,
|
|
537
|
+
conversationId: options.conversationId,
|
|
538
|
+
kind: "message",
|
|
539
|
+
text: options.text,
|
|
540
|
+
};
|
|
541
|
+
if (options.image !== undefined)
|
|
542
|
+
payload.image = imageToWire(options.image);
|
|
543
|
+
if (options.bindingId)
|
|
544
|
+
payload.bindingId = options.bindingId;
|
|
545
|
+
if (options.idempotencyKey)
|
|
546
|
+
payload.idempotencyKey = options.idempotencyKey;
|
|
547
|
+
return this.#send(payload);
|
|
548
|
+
}
|
|
549
|
+
#send(reply) {
|
|
550
|
+
const ws = this.#ws;
|
|
551
|
+
if (ws === undefined || ws.readyState !== WebSocket.OPEN) {
|
|
552
|
+
logger.warn("standin: chat channel is not open; dropping a reply");
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
ws.send(JSON.stringify(reply));
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
catch (err) {
|
|
560
|
+
logger.warn(`standin: chat reply failed: ${String(err)}`);
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
/** Reject after `ms`, so one hung turn cannot wedge its conversation forever. */
|
|
566
|
+
function withTimeout(promise, ms) {
|
|
567
|
+
let timer;
|
|
568
|
+
const expiry = new Promise((_, reject) => {
|
|
569
|
+
timer = setTimeout(() => reject(new StandInError("chat turn timed out")), ms);
|
|
570
|
+
timer.unref?.();
|
|
571
|
+
});
|
|
572
|
+
return Promise.race([promise, expiry]).finally(() => clearTimeout(timer));
|
|
573
|
+
}
|
|
574
|
+
//# sourceMappingURL=chat.js.map
|
package/dist/chat.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a variable that must be set, or throw naming it.
|
|
3
|
+
*
|
|
4
|
+
* `purpose` completes the sentence "X is required to ...", so write it as a verb
|
|
5
|
+
* phrase: `"answer calls with ElevenLabs"`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function required(name: string, purpose?: string): string;
|
|
8
|
+
/** Read a variable that may be set. Blank reads as absent. */
|
|
9
|
+
export declare function optional(name: string, fallback?: string): string | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Read a boolean. Only `true` is true, so a typo is off rather than on.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately strict. A setting that turns a guard OFF must not be turned off
|
|
14
|
+
* by `TRUE`, `1` or `yes` landing in a config file by accident.
|
|
15
|
+
*/
|
|
16
|
+
export declare function flag(name: string, fallback?: boolean): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Read a host and refuse one that is not the vendor's.
|
|
19
|
+
*
|
|
20
|
+
* Your API key travels to this host. A mistyped or injected value is credential
|
|
21
|
+
* exfiltration rather than a failed call, which is why this throws instead of
|
|
22
|
+
* warning.
|
|
23
|
+
*/
|
|
24
|
+
export declare function vendorHost(name: string, fallback: string, suffix: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Read a JSON object of strings, or throw saying it must be one.
|
|
27
|
+
*
|
|
28
|
+
* Used for header maps, which carry YOUR credentials to somebody else's
|
|
29
|
+
* endpoint, so the value is never logged on the failure path.
|
|
30
|
+
*/
|
|
31
|
+
export declare function jsonObject(name: string): Record<string, string>;
|
|
32
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAoBA;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,MAAM,CAO3D;AAED,8DAA8D;AAC9D,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAI5D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,CAOR;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkB/D"}
|