within-sdk 1.0.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 +1 -0
- package/README.md +77 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +243 -0
- package/dist/middleware.d.ts +40 -0
- package/dist/middleware.js +562 -0
- package/dist/modules/compatibility.d.ts +18 -0
- package/dist/modules/compatibility.js +99 -0
- package/dist/modules/constants.d.ts +12 -0
- package/dist/modules/constants.js +12 -0
- package/dist/modules/context-parameters.d.ts +13 -0
- package/dist/modules/context-parameters.js +74 -0
- package/dist/modules/diagnostics.d.ts +7 -0
- package/dist/modules/diagnostics.js +12 -0
- package/dist/modules/eventQueue.d.ts +32 -0
- package/dist/modules/eventQueue.js +234 -0
- package/dist/modules/exceptions.d.ts +13 -0
- package/dist/modules/exceptions.js +730 -0
- package/dist/modules/exporters/datadog.d.ts +17 -0
- package/dist/modules/exporters/datadog.js +166 -0
- package/dist/modules/exporters/otlp.d.ts +13 -0
- package/dist/modules/exporters/otlp.js +140 -0
- package/dist/modules/exporters/posthog.d.ts +32 -0
- package/dist/modules/exporters/posthog.js +272 -0
- package/dist/modules/exporters/sentry.d.ts +27 -0
- package/dist/modules/exporters/sentry.js +386 -0
- package/dist/modules/exporters/trace-context.d.ts +8 -0
- package/dist/modules/exporters/trace-context.js +27 -0
- package/dist/modules/index.d.ts +8 -0
- package/dist/modules/index.js +8 -0
- package/dist/modules/internal.d.ts +33 -0
- package/dist/modules/internal.js +195 -0
- package/dist/modules/logging.d.ts +2 -0
- package/dist/modules/logging.js +74 -0
- package/dist/modules/mcp-sdk-compat.d.ts +32 -0
- package/dist/modules/mcp-sdk-compat.js +111 -0
- package/dist/modules/privacy.d.ts +15 -0
- package/dist/modules/privacy.js +179 -0
- package/dist/modules/redaction.d.ts +11 -0
- package/dist/modules/redaction.js +81 -0
- package/dist/modules/sanitization.d.ts +9 -0
- package/dist/modules/sanitization.js +111 -0
- package/dist/modules/session.d.ts +22 -0
- package/dist/modules/session.js +109 -0
- package/dist/modules/telemetry.d.ts +8 -0
- package/dist/modules/telemetry.js +53 -0
- package/dist/modules/tools.d.ts +25 -0
- package/dist/modules/tools.js +119 -0
- package/dist/modules/tracing.d.ts +4 -0
- package/dist/modules/tracing.js +263 -0
- package/dist/modules/tracingV2.d.ts +2 -0
- package/dist/modules/tracingV2.js +300 -0
- package/dist/modules/truncation.d.ts +24 -0
- package/dist/modules/truncation.js +303 -0
- package/dist/modules/validation.d.ts +6 -0
- package/dist/modules/validation.js +51 -0
- package/dist/network.d.ts +83 -0
- package/dist/network.js +411 -0
- package/dist/proxy.d.ts +31 -0
- package/dist/proxy.js +158 -0
- package/dist/thirdparty/ksuid/base-convert-int-array.js +49 -0
- package/dist/thirdparty/ksuid/base62.js +24 -0
- package/dist/thirdparty/ksuid/index.d.ts +30 -0
- package/dist/thirdparty/ksuid/index.js +201 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.js +5 -0
- package/dist/validate.d.ts +53 -0
- package/dist/validate.js +139 -0
- package/package.json +43 -0
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote MCP tool-call proxy — Streamable HTTP.
|
|
3
|
+
*
|
|
4
|
+
* Used by within__use to invoke a tool on another vendor's MCP server.
|
|
5
|
+
*
|
|
6
|
+
* The MCP Streamable HTTP transport requires a handshake before tool calls:
|
|
7
|
+
* 1. POST /mcp { method: "initialize", ... } → 200 with mcp-session-id header
|
|
8
|
+
* 2. POST /mcp { method: "notifications/initialized" } (with that session id)
|
|
9
|
+
* 3. POST /mcp { method: "tools/call", ... } (with that session id) → result
|
|
10
|
+
*
|
|
11
|
+
* Skipping steps 1+2 yields "Server not initialized" on the target server,
|
|
12
|
+
* which is what killed within__use against the real Beacon CRM deployment.
|
|
13
|
+
*
|
|
14
|
+
* Response bodies come back as either application/json (a single JSON-RPC
|
|
15
|
+
* response) or text/event-stream (SSE with one or more `event: message \n
|
|
16
|
+
* data: {...}` blocks). We accept both and extract the first JSON-RPC
|
|
17
|
+
* response message we see.
|
|
18
|
+
*
|
|
19
|
+
* Session reuse: we cache the session id per (url, token) so a subsequent
|
|
20
|
+
* within__use to the same target doesn't re-handshake. Cache keyed on access
|
|
21
|
+
* token which itself has a short TTL, so there's no long-lived drift risk.
|
|
22
|
+
*/
|
|
23
|
+
const sessionCache = new Map();
|
|
24
|
+
const SESSION_TTL_MS = 9 * 60_000; // generous buffer under the 10-min access-token TTL
|
|
25
|
+
const PROTOCOL_VERSION = '2025-11-25';
|
|
26
|
+
export async function callRemoteMcpTool(mcpServerUrl, accessToken, toolName, toolArguments, options) {
|
|
27
|
+
const timeoutMs = options?.timeoutMs ?? 30_000;
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
30
|
+
try {
|
|
31
|
+
const sessionId = await ensureSession(mcpServerUrl, accessToken, controller.signal);
|
|
32
|
+
const res = await fetch(mcpServerUrl, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
Accept: 'application/json, text/event-stream',
|
|
37
|
+
Authorization: `Bearer ${accessToken}`,
|
|
38
|
+
'mcp-session-id': sessionId,
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
jsonrpc: '2.0',
|
|
42
|
+
id: options?.requestId ?? 1,
|
|
43
|
+
method: 'tools/call',
|
|
44
|
+
params: { name: toolName, arguments: toolArguments },
|
|
45
|
+
}),
|
|
46
|
+
signal: controller.signal,
|
|
47
|
+
});
|
|
48
|
+
const text = await res.text();
|
|
49
|
+
const parsed = parseJsonRpcResponse(text, res.headers.get('content-type') ?? '');
|
|
50
|
+
// If the target says our session has vanished (container recycled,
|
|
51
|
+
// memory eviction, etc.) drop our cache entry and retry exactly once.
|
|
52
|
+
if (!res.ok && looksLikeStaleSession(text)) {
|
|
53
|
+
sessionCache.delete(cacheKey(mcpServerUrl, accessToken));
|
|
54
|
+
return callRemoteMcpTool(mcpServerUrl, accessToken, toolName, toolArguments, options);
|
|
55
|
+
}
|
|
56
|
+
return { ok: res.ok, status: res.status, body: parsed ?? text };
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function cacheKey(url, token) {
|
|
63
|
+
return `${url}::${token}`;
|
|
64
|
+
}
|
|
65
|
+
function looksLikeStaleSession(body) {
|
|
66
|
+
return /session\s+(expired|not\s+found|invalid)/i.test(body) || body.includes('Session expired');
|
|
67
|
+
}
|
|
68
|
+
async function ensureSession(url, accessToken, signal) {
|
|
69
|
+
const key = cacheKey(url, accessToken);
|
|
70
|
+
const cached = sessionCache.get(key);
|
|
71
|
+
if (cached && Date.now() - cached.createdAt < SESSION_TTL_MS) {
|
|
72
|
+
return cached.sessionId;
|
|
73
|
+
}
|
|
74
|
+
const initRes = await fetch(url, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: {
|
|
77
|
+
'Content-Type': 'application/json',
|
|
78
|
+
Accept: 'application/json, text/event-stream',
|
|
79
|
+
Authorization: `Bearer ${accessToken}`,
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
jsonrpc: '2.0',
|
|
83
|
+
id: 0,
|
|
84
|
+
method: 'initialize',
|
|
85
|
+
params: {
|
|
86
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
87
|
+
capabilities: {},
|
|
88
|
+
clientInfo: { name: 'within-mcp-auth/proxy', version: '0.1.0' },
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
signal,
|
|
92
|
+
});
|
|
93
|
+
if (!initRes.ok) {
|
|
94
|
+
const errText = await initRes.text();
|
|
95
|
+
throw new Error(`Remote MCP initialize failed (${initRes.status}): ${errText.slice(0, 240)}`);
|
|
96
|
+
}
|
|
97
|
+
const sessionId = initRes.headers.get('mcp-session-id');
|
|
98
|
+
if (!sessionId) {
|
|
99
|
+
throw new Error('Remote MCP server did not return an mcp-session-id header on initialize');
|
|
100
|
+
}
|
|
101
|
+
// Consume init response body to free the socket before the next POST.
|
|
102
|
+
await initRes.text().catch(() => undefined);
|
|
103
|
+
// Fire-and-forget the initialized notification. The spec wants it, and
|
|
104
|
+
// some servers block tools/call until they've seen it.
|
|
105
|
+
const notifRes = await fetch(url, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: {
|
|
108
|
+
'Content-Type': 'application/json',
|
|
109
|
+
Accept: 'application/json, text/event-stream',
|
|
110
|
+
Authorization: `Bearer ${accessToken}`,
|
|
111
|
+
'mcp-session-id': sessionId,
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({
|
|
114
|
+
jsonrpc: '2.0',
|
|
115
|
+
method: 'notifications/initialized',
|
|
116
|
+
}),
|
|
117
|
+
signal,
|
|
118
|
+
});
|
|
119
|
+
// 202 Accepted is the expected response for notifications; anything else
|
|
120
|
+
// we tolerate (target may acknowledge with 200 or drop it). We do NOT
|
|
121
|
+
// throw here because some implementations don't require this step.
|
|
122
|
+
await notifRes.text().catch(() => undefined);
|
|
123
|
+
sessionCache.set(key, { sessionId, createdAt: Date.now() });
|
|
124
|
+
return sessionId;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The MCP StreamableHTTP transport can respond with either raw JSON or SSE.
|
|
128
|
+
* This function peels off the SSE framing (if present) and returns the
|
|
129
|
+
* first JSON-RPC response object it finds, or null if the body is neither.
|
|
130
|
+
*/
|
|
131
|
+
function parseJsonRpcResponse(text, contentType) {
|
|
132
|
+
const trimmed = text.trim();
|
|
133
|
+
if (!trimmed)
|
|
134
|
+
return null;
|
|
135
|
+
if (contentType.includes('text/event-stream') || trimmed.startsWith('event:')) {
|
|
136
|
+
// SSE frame format: event: message\ndata: {...}\n\n
|
|
137
|
+
for (const line of trimmed.split(/\r?\n/)) {
|
|
138
|
+
if (line.startsWith('data:')) {
|
|
139
|
+
const payload = line.slice(5).trim();
|
|
140
|
+
if (!payload)
|
|
141
|
+
continue;
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(payload);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Not JSON, try next line
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
return JSON.parse(trimmed);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const maxLength = (array, from, to) =>
|
|
4
|
+
Math.ceil((array.length * Math.log2(from)) / Math.log2(to));
|
|
5
|
+
|
|
6
|
+
function baseConvertIntArray(array, { from, to, fixedLength = null }) {
|
|
7
|
+
const length =
|
|
8
|
+
fixedLength === null ? maxLength(array, from, to) : fixedLength;
|
|
9
|
+
const result = new Array(length);
|
|
10
|
+
|
|
11
|
+
// Each iteration prepends the resulting value, so start the offset at the end.
|
|
12
|
+
let offset = length;
|
|
13
|
+
let input = array;
|
|
14
|
+
while (input.length > 0) {
|
|
15
|
+
if (offset === 0) {
|
|
16
|
+
throw new RangeError(
|
|
17
|
+
`Fixed length of ${fixedLength} is too small, expected at least ${maxLength(array, from, to)}`,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const quotients = [];
|
|
22
|
+
let remainder = 0;
|
|
23
|
+
|
|
24
|
+
for (const digit of input) {
|
|
25
|
+
const acc = digit + remainder * from;
|
|
26
|
+
const q = Math.floor(acc / to);
|
|
27
|
+
remainder = acc % to;
|
|
28
|
+
|
|
29
|
+
if (quotients.length > 0 || q > 0) {
|
|
30
|
+
quotients.push(q);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
result[--offset] = remainder;
|
|
35
|
+
input = quotients;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Trim leading padding, unless length is fixed.
|
|
39
|
+
if (fixedLength === null) {
|
|
40
|
+
return offset > 0 ? result.slice(offset) : result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Fill in any holes in the result array.
|
|
44
|
+
while (offset > 0) {
|
|
45
|
+
result[--offset] = 0;
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
export default baseConvertIntArray;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import baseConvertIntArray from "./base-convert-int-array.js";
|
|
3
|
+
|
|
4
|
+
const CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
5
|
+
|
|
6
|
+
function encode(buffer, fixedLength) {
|
|
7
|
+
return baseConvertIntArray(buffer, { from: 256, to: 62, fixedLength })
|
|
8
|
+
.map((value) => CHARS[value])
|
|
9
|
+
.join("");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function decode(string, fixedLength) {
|
|
13
|
+
// Optimization from https://github.com/andrew/base62.js/pull/31.
|
|
14
|
+
const input = Array.from(string, (char) => {
|
|
15
|
+
const charCode = char.charCodeAt(0);
|
|
16
|
+
if (charCode < 58) return charCode - 48;
|
|
17
|
+
if (charCode < 91) return charCode - 55;
|
|
18
|
+
return charCode - 61;
|
|
19
|
+
});
|
|
20
|
+
return Buffer.from(
|
|
21
|
+
baseConvertIntArray(input, { from: 62, to: 256, fixedLength }),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
export { encode, decode };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
declare class KSUID {
|
|
3
|
+
constructor(buffer: Buffer);
|
|
4
|
+
readonly raw: Buffer;
|
|
5
|
+
readonly date: Date;
|
|
6
|
+
readonly timestamp: number;
|
|
7
|
+
readonly payload: Buffer;
|
|
8
|
+
readonly string: string;
|
|
9
|
+
compare(other: KSUID): number;
|
|
10
|
+
equals(other: KSUID): boolean;
|
|
11
|
+
toString(): string;
|
|
12
|
+
toJSON(): string;
|
|
13
|
+
static random(): Promise<KSUID>;
|
|
14
|
+
static random(timeInMs: number): Promise<KSUID>;
|
|
15
|
+
static random(date: Date): Promise<KSUID>;
|
|
16
|
+
static randomSync(): KSUID;
|
|
17
|
+
static randomSync(timeInMs: number): KSUID;
|
|
18
|
+
static randomSync(date: Date): KSUID;
|
|
19
|
+
static fromParts(timeInMs: number, payload: Buffer): KSUID;
|
|
20
|
+
static isValid(buffer: Buffer): boolean;
|
|
21
|
+
static parse(str: string): KSUID;
|
|
22
|
+
static withPrefix(prefix: string): {
|
|
23
|
+
random(timeInMs?: number): Promise<string>;
|
|
24
|
+
random(date?: Date): Promise<string>;
|
|
25
|
+
randomSync(timeInMs?: number): string;
|
|
26
|
+
randomSync(date?: Date): string;
|
|
27
|
+
fromParts(timeInMs: number, payload: Buffer): string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export default KSUID;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { inspect } from "node:util";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import * as base62 from "./base62.js";
|
|
6
|
+
|
|
7
|
+
const customInspectSymbol = inspect.custom;
|
|
8
|
+
|
|
9
|
+
const asyncRandomBytes = promisify(randomBytes);
|
|
10
|
+
|
|
11
|
+
// KSUID's epoch starts more recently so that the 32-bit number space gives a
|
|
12
|
+
// significantly higher useful lifetime of around 136 years from March 2014.
|
|
13
|
+
// This number (14e11) was picked to be easy to remember.
|
|
14
|
+
const EPOCH_IN_MS = 14e11;
|
|
15
|
+
|
|
16
|
+
const MAX_TIME_IN_MS = 1e3 * (2 ** 32 - 1) + EPOCH_IN_MS;
|
|
17
|
+
|
|
18
|
+
// Timestamp is a uint32
|
|
19
|
+
const TIMESTAMP_BYTE_LENGTH = 4;
|
|
20
|
+
|
|
21
|
+
// Payload is 16-bytes
|
|
22
|
+
const PAYLOAD_BYTE_LENGTH = 16;
|
|
23
|
+
|
|
24
|
+
// KSUIDs are 20 bytes when binary encoded
|
|
25
|
+
const BYTE_LENGTH = TIMESTAMP_BYTE_LENGTH + PAYLOAD_BYTE_LENGTH;
|
|
26
|
+
|
|
27
|
+
// The length of a KSUID when string (base62) encoded
|
|
28
|
+
const STRING_ENCODED_LENGTH = 27;
|
|
29
|
+
|
|
30
|
+
const TIME_IN_MS_ASSERTION =
|
|
31
|
+
`Valid KSUID timestamps must be in milliseconds since ${new Date(0).toISOString()},
|
|
32
|
+
no earlier than ${new Date(EPOCH_IN_MS).toISOString()} and no later than ${new Date(MAX_TIME_IN_MS).toISOString()}
|
|
33
|
+
`
|
|
34
|
+
.trim()
|
|
35
|
+
.replace(/(\n|\s)+/g, " ")
|
|
36
|
+
.replace(/\.000Z/g, "Z");
|
|
37
|
+
|
|
38
|
+
const VALID_ENCODING_ASSERTION = `Valid encoded KSUIDs are ${STRING_ENCODED_LENGTH} characters`;
|
|
39
|
+
|
|
40
|
+
const VALID_BUFFER_ASSERTION = `Valid KSUID buffers are ${BYTE_LENGTH} bytes`;
|
|
41
|
+
|
|
42
|
+
const VALID_PAYLOAD_ASSERTION = `Valid KSUID payloads are ${PAYLOAD_BYTE_LENGTH} bytes`;
|
|
43
|
+
|
|
44
|
+
function fromParts(timeInMs, payload) {
|
|
45
|
+
const timestamp = Math.floor((timeInMs - EPOCH_IN_MS) / 1e3);
|
|
46
|
+
const timestampBuffer = Buffer.allocUnsafe(TIMESTAMP_BYTE_LENGTH);
|
|
47
|
+
timestampBuffer.writeUInt32BE(timestamp, 0);
|
|
48
|
+
|
|
49
|
+
return Buffer.concat([timestampBuffer, payload], BYTE_LENGTH);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const bufferLookup = new WeakMap();
|
|
53
|
+
|
|
54
|
+
class KSUID {
|
|
55
|
+
constructor(buffer) {
|
|
56
|
+
if (!KSUID.isValid(buffer)) {
|
|
57
|
+
throw new TypeError(VALID_BUFFER_ASSERTION);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
bufferLookup.set(this, buffer);
|
|
61
|
+
Object.defineProperty(this, "buffer", {
|
|
62
|
+
enumerable: true,
|
|
63
|
+
get() {
|
|
64
|
+
return Buffer.from(buffer);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get raw() {
|
|
70
|
+
return Buffer.from(bufferLookup.get(this).slice(0));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get date() {
|
|
74
|
+
return new Date(1e3 * this.timestamp + EPOCH_IN_MS);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get timestamp() {
|
|
78
|
+
return bufferLookup.get(this).readUInt32BE(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
get payload() {
|
|
82
|
+
const payload = bufferLookup
|
|
83
|
+
.get(this)
|
|
84
|
+
.slice(TIMESTAMP_BYTE_LENGTH, BYTE_LENGTH);
|
|
85
|
+
return Buffer.from(payload);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get string() {
|
|
89
|
+
const encoded = base62.encode(
|
|
90
|
+
bufferLookup.get(this),
|
|
91
|
+
STRING_ENCODED_LENGTH,
|
|
92
|
+
);
|
|
93
|
+
return encoded.padStart(STRING_ENCODED_LENGTH, "0");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
compare(other) {
|
|
97
|
+
if (!bufferLookup.has(other)) {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return bufferLookup
|
|
102
|
+
.get(this)
|
|
103
|
+
.compare(bufferLookup.get(other), 0, BYTE_LENGTH);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
equals(other) {
|
|
107
|
+
return (
|
|
108
|
+
this === other || (bufferLookup.has(other) && this.compare(other) === 0)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
toString() {
|
|
113
|
+
return `${this[Symbol.toStringTag]} { ${this.string} }`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
toJSON() {
|
|
117
|
+
return this.string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
[customInspectSymbol]() {
|
|
121
|
+
return this.toString();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static async random(time = Date.now()) {
|
|
125
|
+
const payload = await asyncRandomBytes(PAYLOAD_BYTE_LENGTH);
|
|
126
|
+
return new KSUID(fromParts(Number(time), payload));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static randomSync(time = Date.now()) {
|
|
130
|
+
const payload = randomBytes(PAYLOAD_BYTE_LENGTH);
|
|
131
|
+
return new KSUID(fromParts(Number(time), payload));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static fromParts(timeInMs, payload) {
|
|
135
|
+
if (
|
|
136
|
+
!Number.isInteger(timeInMs) ||
|
|
137
|
+
timeInMs < EPOCH_IN_MS ||
|
|
138
|
+
timeInMs > MAX_TIME_IN_MS
|
|
139
|
+
) {
|
|
140
|
+
throw new TypeError(TIME_IN_MS_ASSERTION);
|
|
141
|
+
}
|
|
142
|
+
if (
|
|
143
|
+
!Buffer.isBuffer(payload) ||
|
|
144
|
+
payload.byteLength !== PAYLOAD_BYTE_LENGTH
|
|
145
|
+
) {
|
|
146
|
+
throw new TypeError(VALID_PAYLOAD_ASSERTION);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return new KSUID(fromParts(timeInMs, payload));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static isValid(buffer) {
|
|
153
|
+
return Buffer.isBuffer(buffer) && buffer.byteLength === BYTE_LENGTH;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static parse(string) {
|
|
157
|
+
if (string.length !== STRING_ENCODED_LENGTH) {
|
|
158
|
+
throw new TypeError(VALID_ENCODING_ASSERTION);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const decoded = base62.decode(string, BYTE_LENGTH);
|
|
162
|
+
if (decoded.byteLength === BYTE_LENGTH) {
|
|
163
|
+
return new KSUID(decoded);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const buffer = Buffer.allocUnsafe(BYTE_LENGTH);
|
|
167
|
+
const padEnd = BYTE_LENGTH - decoded.byteLength;
|
|
168
|
+
buffer.fill(0, 0, padEnd);
|
|
169
|
+
decoded.copy(buffer, padEnd);
|
|
170
|
+
return new KSUID(buffer);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
Object.defineProperty(KSUID.prototype, Symbol.toStringTag, { value: "KSUID" });
|
|
174
|
+
// A string-encoded maximum value for a KSUID
|
|
175
|
+
Object.defineProperty(KSUID, "MAX_STRING_ENCODED", {
|
|
176
|
+
value: "aWgEPTl1tmebfsQzFP4bxwgy80V",
|
|
177
|
+
});
|
|
178
|
+
// A string-encoded minimum value for a KSUID
|
|
179
|
+
Object.defineProperty(KSUID, "MIN_STRING_ENCODED", {
|
|
180
|
+
value: "000000000000000000000000000",
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Add prefix functionality
|
|
184
|
+
KSUID.withPrefix = function (prefix) {
|
|
185
|
+
return {
|
|
186
|
+
random: async (time = Date.now()) => {
|
|
187
|
+
const ksuid = await KSUID.random(time);
|
|
188
|
+
return `${prefix}_${ksuid.string}`;
|
|
189
|
+
},
|
|
190
|
+
randomSync: (time = Date.now()) => {
|
|
191
|
+
const ksuid = KSUID.randomSync(time);
|
|
192
|
+
return `${prefix}_${ksuid.string}`;
|
|
193
|
+
},
|
|
194
|
+
fromParts: (timeInMs, payload) => {
|
|
195
|
+
const ksuid = KSUID.fromParts(timeInMs, payload);
|
|
196
|
+
return `${prefix}_${ksuid.string}`;
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export default KSUID;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export interface WithinOptions {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
apiBaseUrl?: string;
|
|
5
|
+
fetch?: typeof fetch;
|
|
6
|
+
log?: (line: string) => void;
|
|
7
|
+
enableReportMissing?: boolean;
|
|
8
|
+
enableTracing?: boolean;
|
|
9
|
+
enableToolCallContext?: boolean;
|
|
10
|
+
customContextDescription?: string;
|
|
11
|
+
identify?: (request: any, extra?: CompatibleRequestHandlerExtra) => Promise<UserIdentity | null>;
|
|
12
|
+
redactSensitiveInformation?: RedactFunction;
|
|
13
|
+
exporters?: Record<string, ExporterConfig>;
|
|
14
|
+
disableDiagnostics?: boolean;
|
|
15
|
+
eventTags?: (request: any, extra?: CompatibleRequestHandlerExtra) => Record<string, string> | null | Promise<Record<string, string> | null>;
|
|
16
|
+
eventProperties?: (request: any, extra?: CompatibleRequestHandlerExtra) => Record<string, any> | null | Promise<Record<string, any> | null>;
|
|
17
|
+
privacy?: {
|
|
18
|
+
maxFieldBytes?: number;
|
|
19
|
+
maxEventBytes?: number;
|
|
20
|
+
redactKeys?: string[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export type ToolCallback = ((args: any, extra: CompatibleRequestHandlerExtra) => CallToolResult | Promise<CallToolResult>) | ((extra: CompatibleRequestHandlerExtra) => CallToolResult | Promise<CallToolResult>);
|
|
24
|
+
export type RegisteredTool = {
|
|
25
|
+
description?: string;
|
|
26
|
+
inputSchema?: any;
|
|
27
|
+
update?: (...args: any[]) => any;
|
|
28
|
+
} & ({
|
|
29
|
+
callback: ToolCallback;
|
|
30
|
+
handler?: never;
|
|
31
|
+
} | {
|
|
32
|
+
handler: ToolCallback;
|
|
33
|
+
callback?: never;
|
|
34
|
+
});
|
|
35
|
+
export type RedactFunction = (text: string) => Promise<string> | string;
|
|
36
|
+
export interface ExporterConfig {
|
|
37
|
+
type: string;
|
|
38
|
+
[key: string]: any;
|
|
39
|
+
}
|
|
40
|
+
export interface Exporter {
|
|
41
|
+
export(event: Event): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export declare enum WithinIDPrefixes {
|
|
44
|
+
Session = "ses",
|
|
45
|
+
Event = "evt"
|
|
46
|
+
}
|
|
47
|
+
export interface Event {
|
|
48
|
+
id: string;
|
|
49
|
+
sessionId: string;
|
|
50
|
+
projectId?: string;
|
|
51
|
+
vendorSlug?: string;
|
|
52
|
+
apiKey?: string;
|
|
53
|
+
ingestBaseUrl?: string;
|
|
54
|
+
fetchImpl?: typeof fetch;
|
|
55
|
+
eventType: string;
|
|
56
|
+
timestamp: Date;
|
|
57
|
+
duration?: number;
|
|
58
|
+
ipAddress?: string;
|
|
59
|
+
sdkLanguage?: string;
|
|
60
|
+
sdkVersion?: string;
|
|
61
|
+
serverName?: string;
|
|
62
|
+
serverVersion?: string;
|
|
63
|
+
clientName?: string;
|
|
64
|
+
clientVersion?: string;
|
|
65
|
+
identifyActorGivenId?: string;
|
|
66
|
+
identifyActorName?: string;
|
|
67
|
+
identifyActorData?: object;
|
|
68
|
+
resourceName?: string;
|
|
69
|
+
parameters?: any;
|
|
70
|
+
response?: any;
|
|
71
|
+
userIntent?: string;
|
|
72
|
+
isError?: boolean;
|
|
73
|
+
error?: ErrorData;
|
|
74
|
+
tags?: Record<string, string> | null;
|
|
75
|
+
properties?: Record<string, any> | null;
|
|
76
|
+
actorId?: string;
|
|
77
|
+
eventId?: string;
|
|
78
|
+
identifyData?: object;
|
|
79
|
+
}
|
|
80
|
+
export interface UnredactedEvent extends Partial<Event> {
|
|
81
|
+
redactionFn?: RedactFunction;
|
|
82
|
+
}
|
|
83
|
+
export interface CompatibleRequestHandlerExtra {
|
|
84
|
+
sessionId?: string;
|
|
85
|
+
headers?: Record<string, string | string[]>;
|
|
86
|
+
[key: string]: any;
|
|
87
|
+
}
|
|
88
|
+
export interface ServerClientInfoLike {
|
|
89
|
+
name?: string;
|
|
90
|
+
version?: string;
|
|
91
|
+
}
|
|
92
|
+
export interface HighLevelMCPServerLike {
|
|
93
|
+
_registeredTools: {
|
|
94
|
+
[name: string]: RegisteredTool;
|
|
95
|
+
};
|
|
96
|
+
server: MCPServerLike;
|
|
97
|
+
tool?(name: string, cb: ToolCallback): void;
|
|
98
|
+
tool?(name: string, description: string, cb: ToolCallback): void;
|
|
99
|
+
tool?(name: string, paramsSchema: any, cb: ToolCallback): void;
|
|
100
|
+
tool?(name: string, description: string, paramsSchema: any, cb: ToolCallback): void;
|
|
101
|
+
registerTool?(name: string, config: {
|
|
102
|
+
description?: string;
|
|
103
|
+
inputSchema?: any;
|
|
104
|
+
}, handler: ToolCallback): void;
|
|
105
|
+
}
|
|
106
|
+
export interface MCPServerLike {
|
|
107
|
+
setRequestHandler(schema: any, handler: (request: any, extra?: CompatibleRequestHandlerExtra) => Promise<any>): void;
|
|
108
|
+
_requestHandlers: Map<string, (request: any, extra?: CompatibleRequestHandlerExtra) => Promise<any>>;
|
|
109
|
+
_serverInfo?: ServerClientInfoLike;
|
|
110
|
+
getClientVersion(): ServerClientInfoLike | undefined;
|
|
111
|
+
}
|
|
112
|
+
export interface UserIdentity {
|
|
113
|
+
userId: string;
|
|
114
|
+
userName?: string;
|
|
115
|
+
userData?: Record<string, any>;
|
|
116
|
+
}
|
|
117
|
+
export interface SessionInfo {
|
|
118
|
+
ipAddress?: string;
|
|
119
|
+
sdkLanguage?: string;
|
|
120
|
+
sdkVersion?: string;
|
|
121
|
+
serverName?: string;
|
|
122
|
+
serverVersion?: string;
|
|
123
|
+
clientName?: string;
|
|
124
|
+
clientVersion?: string;
|
|
125
|
+
identifyActorGivenId?: string;
|
|
126
|
+
identifyActorName?: string;
|
|
127
|
+
identifyActorData?: object;
|
|
128
|
+
}
|
|
129
|
+
export interface WithinTrackingData {
|
|
130
|
+
projectId: string;
|
|
131
|
+
vendorSlug: string;
|
|
132
|
+
apiKey?: string;
|
|
133
|
+
ingestBaseUrl: string;
|
|
134
|
+
fetchImpl: typeof fetch;
|
|
135
|
+
sessionId: string;
|
|
136
|
+
lastActivity: Date;
|
|
137
|
+
identifiedSessions: Map<string, UserIdentity>;
|
|
138
|
+
sessionInfo: SessionInfo;
|
|
139
|
+
options: WithinOptions;
|
|
140
|
+
lastMcpSessionId?: string;
|
|
141
|
+
sessionSource: "mcp" | "within";
|
|
142
|
+
}
|
|
143
|
+
export interface StackFrame {
|
|
144
|
+
filename: string;
|
|
145
|
+
function: string;
|
|
146
|
+
lineno?: number;
|
|
147
|
+
colno?: number;
|
|
148
|
+
in_app: boolean;
|
|
149
|
+
abs_path?: string;
|
|
150
|
+
context_line?: string;
|
|
151
|
+
}
|
|
152
|
+
export interface ChainedErrorData {
|
|
153
|
+
message: string;
|
|
154
|
+
type?: string;
|
|
155
|
+
stack?: string;
|
|
156
|
+
frames?: StackFrame[];
|
|
157
|
+
}
|
|
158
|
+
export interface ErrorData {
|
|
159
|
+
message: string;
|
|
160
|
+
type?: string;
|
|
161
|
+
stack?: string;
|
|
162
|
+
frames?: StackFrame[];
|
|
163
|
+
chained_errors?: ChainedErrorData[];
|
|
164
|
+
platform?: string;
|
|
165
|
+
}
|
|
166
|
+
export interface CustomEventData {
|
|
167
|
+
resourceName?: string;
|
|
168
|
+
parameters?: any;
|
|
169
|
+
response?: any;
|
|
170
|
+
message?: string;
|
|
171
|
+
duration?: number;
|
|
172
|
+
isError?: boolean;
|
|
173
|
+
error?: any;
|
|
174
|
+
tags?: Record<string, string>;
|
|
175
|
+
properties?: Record<string, any>;
|
|
176
|
+
apiKey?: string;
|
|
177
|
+
apiBaseUrl?: string;
|
|
178
|
+
fetch?: typeof fetch;
|
|
179
|
+
}
|
|
180
|
+
export interface WithinConversionPlan {
|
|
181
|
+
id?: string;
|
|
182
|
+
name?: string;
|
|
183
|
+
interval?: string;
|
|
184
|
+
}
|
|
185
|
+
export interface WithinConversionInput {
|
|
186
|
+
subject: string;
|
|
187
|
+
convertedAt?: string | Date;
|
|
188
|
+
plan?: WithinConversionPlan;
|
|
189
|
+
metadata?: Record<string, unknown>;
|
|
190
|
+
}
|
|
191
|
+
export interface WithinConversionResult {
|
|
192
|
+
ok: boolean;
|
|
193
|
+
inserted: boolean;
|
|
194
|
+
status: "subscriber";
|
|
195
|
+
identityKey: string;
|
|
196
|
+
conversionUtcDate: string;
|
|
197
|
+
}
|