@pingroom/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 +185 -0
- package/dist/client.d.ts +106 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +301 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/http.d.ts +38 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +118 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/async.d.ts +5 -0
- package/dist/internal/async.d.ts.map +1 -0
- package/dist/internal/async.js +47 -0
- package/dist/internal/async.js.map +1 -0
- package/dist/internal/guards.d.ts +7 -0
- package/dist/internal/guards.d.ts.map +1 -0
- package/dist/internal/guards.js +41 -0
- package/dist/internal/guards.js.map +1 -0
- package/dist/internal/url.d.ts +8 -0
- package/dist/internal/url.d.ts.map +1 -0
- package/dist/internal/url.js +28 -0
- package/dist/internal/url.js.map +1 -0
- package/dist/mcp.d.ts +40 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +46 -0
- package/dist/mcp.js.map +1 -0
- package/dist/types.d.ts +262 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/webhooks.d.ts +56 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +133 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +53 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error types. PingRoomError carries the HTTP status and the API's machine
|
|
3
|
+
* `code` (e.g. `pings_closed`, `cooldown`, `rate_limited`) so callers can branch
|
|
4
|
+
* without string-matching messages. The agent token is NEVER stored on or
|
|
5
|
+
* serialized through an error.
|
|
6
|
+
*/
|
|
7
|
+
export interface ApiErrorBody {
|
|
8
|
+
code?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
message?: string;
|
|
11
|
+
retry_after?: number;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface PingRoomErrorInit {
|
|
15
|
+
status?: number;
|
|
16
|
+
code?: string | null;
|
|
17
|
+
retryAfter?: number | null;
|
|
18
|
+
body?: unknown;
|
|
19
|
+
}
|
|
20
|
+
export declare class PingRoomError extends Error {
|
|
21
|
+
/** HTTP status, or 0 for client-side/network errors. */
|
|
22
|
+
readonly status: number;
|
|
23
|
+
/** Machine-readable code from the API body (`code` or `error`), or a client code. */
|
|
24
|
+
readonly code: string | null;
|
|
25
|
+
/** Seconds to wait before retrying, when the API or a Retry-After header provides it. */
|
|
26
|
+
readonly retryAfter: number | null;
|
|
27
|
+
/** The parsed response body (or raw text), for inspection. Contains no credentials. */
|
|
28
|
+
readonly body: unknown;
|
|
29
|
+
constructor(message: string, init?: PingRoomErrorInit);
|
|
30
|
+
static fromResponse(status: number, body: unknown, headers?: Headers): PingRoomError;
|
|
31
|
+
}
|
|
32
|
+
/** Thrown when a request exceeds its timeout. */
|
|
33
|
+
export declare class PingRoomTimeoutError extends PingRoomError {
|
|
34
|
+
constructor(message?: string);
|
|
35
|
+
}
|
|
36
|
+
/** Thrown when the underlying fetch fails (DNS, connection, TLS, etc.). */
|
|
37
|
+
export declare class PingRoomNetworkError extends PingRoomError {
|
|
38
|
+
constructor(message: string, cause?: unknown);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qFAAqF;IACrF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB;IAUzD,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,aAAa;CAoBrF;AAED,iDAAiD;AACjD,qBAAa,oBAAqB,SAAQ,aAAa;gBACzC,OAAO,SAAsB;CAI1C;AAED,2EAA2E;AAC3E,qBAAa,oBAAqB,SAAQ,aAAa;gBACzC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAO7C"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error types. PingRoomError carries the HTTP status and the API's machine
|
|
3
|
+
* `code` (e.g. `pings_closed`, `cooldown`, `rate_limited`) so callers can branch
|
|
4
|
+
* without string-matching messages. The agent token is NEVER stored on or
|
|
5
|
+
* serialized through an error.
|
|
6
|
+
*/
|
|
7
|
+
export class PingRoomError extends Error {
|
|
8
|
+
/** HTTP status, or 0 for client-side/network errors. */
|
|
9
|
+
status;
|
|
10
|
+
/** Machine-readable code from the API body (`code` or `error`), or a client code. */
|
|
11
|
+
code;
|
|
12
|
+
/** Seconds to wait before retrying, when the API or a Retry-After header provides it. */
|
|
13
|
+
retryAfter;
|
|
14
|
+
/** The parsed response body (or raw text), for inspection. Contains no credentials. */
|
|
15
|
+
body;
|
|
16
|
+
constructor(message, init = {}) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'PingRoomError';
|
|
19
|
+
this.status = init.status ?? 0;
|
|
20
|
+
this.code = init.code ?? null;
|
|
21
|
+
this.retryAfter = init.retryAfter ?? null;
|
|
22
|
+
this.body = init.body ?? null;
|
|
23
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
24
|
+
}
|
|
25
|
+
static fromResponse(status, body, headers) {
|
|
26
|
+
const b = body && typeof body === 'object' ? body : {};
|
|
27
|
+
const code = (typeof b.code === 'string' && b.code) ||
|
|
28
|
+
(typeof b.error === 'string' && b.error) ||
|
|
29
|
+
null;
|
|
30
|
+
const message = (typeof b.message === 'string' && b.message) ||
|
|
31
|
+
(code ? code : `HTTP ${status}`);
|
|
32
|
+
let retryAfter = typeof b.retry_after === 'number' ? b.retry_after : null;
|
|
33
|
+
if (retryAfter === null && headers) {
|
|
34
|
+
const h = headers.get('retry-after');
|
|
35
|
+
if (h && /^\d+$/.test(h.trim())) {
|
|
36
|
+
retryAfter = Number(h.trim());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return new PingRoomError(message, { status, code, retryAfter, body });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Thrown when a request exceeds its timeout. */
|
|
43
|
+
export class PingRoomTimeoutError extends PingRoomError {
|
|
44
|
+
constructor(message = 'Request timed out') {
|
|
45
|
+
super(message, { code: 'timeout' });
|
|
46
|
+
this.name = 'PingRoomTimeoutError';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Thrown when the underlying fetch fails (DNS, connection, TLS, etc.). */
|
|
50
|
+
export class PingRoomNetworkError extends PingRoomError {
|
|
51
|
+
constructor(message, cause) {
|
|
52
|
+
super(message, { code: 'network_error' });
|
|
53
|
+
this.name = 'PingRoomNetworkError';
|
|
54
|
+
if (cause !== undefined) {
|
|
55
|
+
this.cause = cause;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,wDAAwD;IAC/C,MAAM,CAAS;IACxB,qFAAqF;IAC5E,IAAI,CAAgB;IAC7B,yFAAyF;IAChF,UAAU,CAAgB;IACnC,uFAAuF;IAC9E,IAAI,CAAU;IAEvB,YAAY,OAAe,EAAE,OAA0B,EAAE;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAC9B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,MAAc,EAAE,IAAa,EAAE,OAAiB;QAClE,MAAM,CAAC,GAAiB,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,IAAI,GACR,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC;YACtC,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC;YACxC,IAAI,CAAC;QACP,MAAM,OAAO,GACX,CAAC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC;YAC5C,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC;QAEnC,IAAI,UAAU,GAAkB,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;QACzF,IAAI,UAAU,KAAK,IAAI,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACrC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAChC,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAED,OAAO,IAAI,aAAa,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;CACF;AAED,iDAAiD;AACjD,MAAM,OAAO,oBAAqB,SAAQ,aAAa;IACrD,YAAY,OAAO,GAAG,mBAAmB;QACvC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,2EAA2E;AAC3E,MAAM,OAAO,oBAAqB,SAAQ,aAAa;IACrD,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACvB,IAA4B,CAAC,KAAK,GAAG,KAAK,CAAC;QAC9C,CAAC;IACH,CAAC;CACF"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FetchLike } from './types.js';
|
|
2
|
+
export interface HttpClientOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
token?: string | null;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
fetch?: FetchLike;
|
|
7
|
+
userAgent?: string;
|
|
8
|
+
allowInsecure?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface RequestOptions {
|
|
11
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
12
|
+
body?: unknown;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
/** Send the Authorization header (default true). Public endpoints pass false. */
|
|
15
|
+
auth?: boolean;
|
|
16
|
+
/** Per-call timeout override in ms. */
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Thin authenticated fetch wrapper. Holds the token privately, enforces https
|
|
22
|
+
* for credentialed traffic, applies a timeout, and maps non-2xx responses to a
|
|
23
|
+
* PingRoomError. The token is never written into thrown errors or logs.
|
|
24
|
+
*/
|
|
25
|
+
export declare class HttpClient {
|
|
26
|
+
private token;
|
|
27
|
+
private readonly baseUrl;
|
|
28
|
+
private readonly timeoutMs;
|
|
29
|
+
private readonly fetchImpl;
|
|
30
|
+
private readonly userAgent;
|
|
31
|
+
constructor(options: HttpClientOptions);
|
|
32
|
+
setToken(token: string | null): void;
|
|
33
|
+
getToken(): string | null;
|
|
34
|
+
request<T>(method: string, path: string, opts?: RequestOptions): Promise<T>;
|
|
35
|
+
private parse;
|
|
36
|
+
private buildUrl;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iFAAiF;IACjF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAID;;;;GAIG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,iBAAiB;IAkBtC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIpC,QAAQ,IAAI,MAAM,GAAG,IAAI;IAInB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;YAiDvE,KAAK;IAmBnB,OAAO,CAAC,QAAQ;CAejB"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { PingRoomError, PingRoomNetworkError, PingRoomTimeoutError } from './errors.js';
|
|
2
|
+
import { combineSignals } from './internal/async.js';
|
|
3
|
+
import { assertSecureUrl } from './internal/url.js';
|
|
4
|
+
import { VERSION } from './version.js';
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
6
|
+
/**
|
|
7
|
+
* Thin authenticated fetch wrapper. Holds the token privately, enforces https
|
|
8
|
+
* for credentialed traffic, applies a timeout, and maps non-2xx responses to a
|
|
9
|
+
* PingRoomError. The token is never written into thrown errors or logs.
|
|
10
|
+
*/
|
|
11
|
+
export class HttpClient {
|
|
12
|
+
token;
|
|
13
|
+
baseUrl;
|
|
14
|
+
timeoutMs;
|
|
15
|
+
fetchImpl;
|
|
16
|
+
userAgent;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
const base = assertSecureUrl(options.baseUrl, options.allowInsecure ?? false);
|
|
19
|
+
// Normalise: keep origin + any path prefix, drop a trailing slash.
|
|
20
|
+
this.baseUrl = (base.origin + base.pathname).replace(/\/$/, '');
|
|
21
|
+
this.token = options.token ?? null;
|
|
22
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
23
|
+
const f = options.fetch ?? globalThis.fetch;
|
|
24
|
+
if (!f) {
|
|
25
|
+
throw new PingRoomError('No fetch implementation available. Use Node >= 18, a modern browser, or pass `fetch`.', { code: 'no_fetch' });
|
|
26
|
+
}
|
|
27
|
+
this.fetchImpl = f;
|
|
28
|
+
this.userAgent = options.userAgent ?? `pingroom-sdk/${VERSION}`;
|
|
29
|
+
}
|
|
30
|
+
setToken(token) {
|
|
31
|
+
this.token = token;
|
|
32
|
+
}
|
|
33
|
+
getToken() {
|
|
34
|
+
return this.token;
|
|
35
|
+
}
|
|
36
|
+
async request(method, path, opts = {}) {
|
|
37
|
+
const url = this.buildUrl(path, opts.query);
|
|
38
|
+
const headers = {
|
|
39
|
+
Accept: 'application/json',
|
|
40
|
+
'User-Agent': this.userAgent,
|
|
41
|
+
...opts.headers,
|
|
42
|
+
};
|
|
43
|
+
if (opts.auth !== false) {
|
|
44
|
+
if (!this.token) {
|
|
45
|
+
throw new PingRoomError('This call requires an agent token. Pass `token` to the client or call setToken().', { code: 'no_token' });
|
|
46
|
+
}
|
|
47
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
48
|
+
}
|
|
49
|
+
let bodyText;
|
|
50
|
+
if (opts.body !== undefined) {
|
|
51
|
+
headers['Content-Type'] = 'application/json';
|
|
52
|
+
bodyText = JSON.stringify(opts.body);
|
|
53
|
+
}
|
|
54
|
+
const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
const timer = setTimeout(() => controller.abort(new PingRoomTimeoutError()), timeoutMs);
|
|
57
|
+
const signal = combineSignals(controller.signal, opts.signal);
|
|
58
|
+
let res;
|
|
59
|
+
try {
|
|
60
|
+
res = await this.fetchImpl(url, { method, headers, body: bodyText, signal });
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
// Our own timeout reason propagates as the rejection.
|
|
64
|
+
if (err instanceof PingRoomError) {
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
if (isAbortError(err) || opts.signal?.aborted) {
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
throw new PingRoomNetworkError(`Network request failed: ${errMessage(err)}`, err);
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
}
|
|
75
|
+
return this.parse(res);
|
|
76
|
+
}
|
|
77
|
+
async parse(res) {
|
|
78
|
+
if (res.status === 204) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const text = await res.text();
|
|
82
|
+
let json = null;
|
|
83
|
+
if (text) {
|
|
84
|
+
try {
|
|
85
|
+
json = JSON.parse(text);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
json = text;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
throw PingRoomError.fromResponse(res.status, json, res.headers);
|
|
93
|
+
}
|
|
94
|
+
return json;
|
|
95
|
+
}
|
|
96
|
+
buildUrl(path, query) {
|
|
97
|
+
const full = /^https?:\/\//i.test(path)
|
|
98
|
+
? path
|
|
99
|
+
: this.baseUrl + (path.startsWith('/') ? path : `/${path}`);
|
|
100
|
+
if (!query) {
|
|
101
|
+
return full;
|
|
102
|
+
}
|
|
103
|
+
const url = new URL(full);
|
|
104
|
+
for (const [k, v] of Object.entries(query)) {
|
|
105
|
+
if (v !== undefined) {
|
|
106
|
+
url.searchParams.set(k, String(v));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return url.toString();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function isAbortError(err) {
|
|
113
|
+
return Boolean(err) && typeof err === 'object' && err.name === 'AbortError';
|
|
114
|
+
}
|
|
115
|
+
function errMessage(err) {
|
|
116
|
+
return err instanceof Error ? err.message : String(err);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pingroom/sdk — official TypeScript/JavaScript SDK for the PingRoom agent API.
|
|
3
|
+
*
|
|
4
|
+
* import { PingRoom } from '@pingroom/sdk';
|
|
5
|
+
* const pr = new PingRoom({ token: process.env.PINGROOM_TOKEN });
|
|
6
|
+
* await pr.broadcast('ab12cd', { message: 'Deploy shipped ✅' });
|
|
7
|
+
*/
|
|
8
|
+
import { PingRoom } from './client.js';
|
|
9
|
+
export { PingRoom };
|
|
10
|
+
/** Alias for {@link PingRoom}. */
|
|
11
|
+
export { PingRoom as PingRoomClient } from './client.js';
|
|
12
|
+
export { McpClient } from './mcp.js';
|
|
13
|
+
export type { JsonRpcError, McpTool, McpToolResult } from './mcp.js';
|
|
14
|
+
export { PingRoomError, PingRoomTimeoutError, PingRoomNetworkError } from './errors.js';
|
|
15
|
+
export type { ApiErrorBody, PingRoomErrorInit } from './errors.js';
|
|
16
|
+
export { verifyWebhookSignature, sendIncomingWebhook, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_EVENT_HEADER, } from './webhooks.js';
|
|
17
|
+
export type { VerifyWebhookSignatureOptions, IncomingWebhookPayload, SendIncomingWebhookOptions, IncomingWebhookResult, } from './webhooks.js';
|
|
18
|
+
export { VERSION } from './version.js';
|
|
19
|
+
export type * from './types.js';
|
|
20
|
+
export default PingRoom;
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,kCAAkC;AAClC,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAErE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,mBAAmB,YAAY,CAAC;AAEhC,eAAe,QAAQ,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pingroom/sdk — official TypeScript/JavaScript SDK for the PingRoom agent API.
|
|
3
|
+
*
|
|
4
|
+
* import { PingRoom } from '@pingroom/sdk';
|
|
5
|
+
* const pr = new PingRoom({ token: process.env.PINGROOM_TOKEN });
|
|
6
|
+
* await pr.broadcast('ab12cd', { message: 'Deploy shipped ✅' });
|
|
7
|
+
*/
|
|
8
|
+
import { PingRoom } from './client.js';
|
|
9
|
+
export { PingRoom };
|
|
10
|
+
/** Alias for {@link PingRoom}. */
|
|
11
|
+
export { PingRoom as PingRoomClient } from './client.js';
|
|
12
|
+
export { McpClient } from './mcp.js';
|
|
13
|
+
export { PingRoomError, PingRoomTimeoutError, PingRoomNetworkError } from './errors.js';
|
|
14
|
+
export { verifyWebhookSignature, sendIncomingWebhook, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_EVENT_HEADER, } from './webhooks.js';
|
|
15
|
+
export { VERSION } from './version.js';
|
|
16
|
+
export default PingRoom;
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,kCAAkC;AAClC,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAGrC,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxF,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAQvB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIvC,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Merge two abort signals into one (uses AbortSignal.any when available). */
|
|
2
|
+
export declare function combineSignals(primary: AbortSignal, extra?: AbortSignal): AbortSignal;
|
|
3
|
+
/** Resolve after `ms`, or early (without rejecting) if `signal` aborts. */
|
|
4
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=async.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.d.ts","sourceRoot":"","sources":["../../src/internal/async.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,CAsBrF;AAED,2EAA2E;AAC3E,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBrE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Merge two abort signals into one (uses AbortSignal.any when available). */
|
|
2
|
+
export function combineSignals(primary, extra) {
|
|
3
|
+
if (!extra) {
|
|
4
|
+
return primary;
|
|
5
|
+
}
|
|
6
|
+
const anyFn = AbortSignal.any;
|
|
7
|
+
if (typeof anyFn === 'function') {
|
|
8
|
+
return anyFn([primary, extra]);
|
|
9
|
+
}
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const forward = (s) => {
|
|
12
|
+
if (!controller.signal.aborted) {
|
|
13
|
+
controller.abort(s.reason);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
for (const s of [primary, extra]) {
|
|
17
|
+
if (s.aborted) {
|
|
18
|
+
forward(s);
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
s.addEventListener('abort', () => forward(s), { once: true });
|
|
22
|
+
}
|
|
23
|
+
return controller.signal;
|
|
24
|
+
}
|
|
25
|
+
/** Resolve after `ms`, or early (without rejecting) if `signal` aborts. */
|
|
26
|
+
export function sleep(ms, signal) {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
if (signal?.aborted) {
|
|
29
|
+
resolve();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
cleanup();
|
|
34
|
+
resolve();
|
|
35
|
+
}, ms);
|
|
36
|
+
const onAbort = () => {
|
|
37
|
+
cleanup();
|
|
38
|
+
resolve();
|
|
39
|
+
};
|
|
40
|
+
const cleanup = () => {
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
signal?.removeEventListener('abort', onAbort);
|
|
43
|
+
};
|
|
44
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/internal/async.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,OAAoB,EAAE,KAAmB;IACtE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAI,WAA4E,CAAC,GAAG,CAAC;IAChG,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,CAAc,EAAQ,EAAE;QACvC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,CAAC,CAAC,CAAC;YACX,MAAM;QACR,CAAC;QACD,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,UAAU,CAAC,MAAM,CAAC;AAC3B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Mirrors the server's structured-`data` limits so oversized payloads fail fast, locally. */
|
|
2
|
+
export declare const MAX_DATA_BYTES: number;
|
|
3
|
+
export declare const MAX_DATA_KEYS = 25;
|
|
4
|
+
export declare function assertStructuredData(data: unknown): void;
|
|
5
|
+
export declare function assertActionNumber(n: unknown): void;
|
|
6
|
+
export declare function requireNonEmpty(value: unknown, field: string): void;
|
|
7
|
+
//# sourceMappingURL=guards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../src/internal/guards.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,eAAO,MAAM,cAAc,QAAW,CAAC;AACvC,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAqBxD;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAOnD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAInE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PingRoomError } from '../errors.js';
|
|
2
|
+
/** Mirrors the server's structured-`data` limits so oversized payloads fail fast, locally. */
|
|
3
|
+
export const MAX_DATA_BYTES = 8 * 1024;
|
|
4
|
+
export const MAX_DATA_KEYS = 25;
|
|
5
|
+
export function assertStructuredData(data) {
|
|
6
|
+
if (data === undefined || data === null) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (typeof data !== 'object' || Array.isArray(data)) {
|
|
10
|
+
throw new PingRoomError('`data` must be a JSON object (not an array or scalar).', { code: 'invalid_data' });
|
|
11
|
+
}
|
|
12
|
+
const keys = Object.keys(data);
|
|
13
|
+
if (keys.length > MAX_DATA_KEYS) {
|
|
14
|
+
throw new PingRoomError(`\`data\` may have at most ${MAX_DATA_KEYS} keys (got ${keys.length}).`, { code: 'invalid_data' });
|
|
15
|
+
}
|
|
16
|
+
let encoded;
|
|
17
|
+
try {
|
|
18
|
+
encoded = JSON.stringify(data);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new PingRoomError('`data` must be JSON-serializable.', { code: 'invalid_data' });
|
|
22
|
+
}
|
|
23
|
+
const bytes = new TextEncoder().encode(encoded).length;
|
|
24
|
+
if (bytes > MAX_DATA_BYTES) {
|
|
25
|
+
throw new PingRoomError(`\`data\` is too large (${bytes} bytes, max ${MAX_DATA_BYTES}).`, { code: 'invalid_data' });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function assertActionNumber(n) {
|
|
29
|
+
if (n === undefined || n === null) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (typeof n !== 'number' || !Number.isInteger(n) || n < 1 || n > 4) {
|
|
33
|
+
throw new PingRoomError('action_number must be an integer 1–4.', { code: 'invalid_request' });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function requireNonEmpty(value, field) {
|
|
37
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
38
|
+
throw new PingRoomError(`\`${field}\` is required.`, { code: 'invalid_request' });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/internal/guards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,8FAA8F;AAC9F,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAG,IAAI,CAAC;AACvC,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEhC,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QACxC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,aAAa,CAAC,wDAAwD,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC;IACzC,IAAI,IAAI,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;QAChC,MAAM,IAAI,aAAa,CAAC,6BAA6B,aAAa,cAAc,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;IAC7H,CAAC;IACD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,aAAa,CAAC,mCAAmC,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACvD,IAAI,KAAK,GAAG,cAAc,EAAE,CAAC;QAC3B,MAAM,IAAI,aAAa,CAAC,0BAA0B,KAAK,eAAe,cAAc,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;IACtH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,CAAU;IAC3C,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAClC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,aAAa,CAAC,uCAAuC,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAChG,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,KAAa;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,aAAa,CAAC,KAAK,KAAK,iBAAiB,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACpF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function isLoopbackHost(hostname: string): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* Refuse to carry a bearer credential over plain http to a non-loopback host —
|
|
4
|
+
* a token on the wire over http is a token leaked. https always passes; http is
|
|
5
|
+
* allowed only to loopback dev hosts, or anywhere when `allowInsecure` is set.
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertSecureUrl(rawUrl: string, allowInsecure: boolean): URL;
|
|
8
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,GAAG,CAkB3E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PingRoomError } from '../errors.js';
|
|
2
|
+
export function isLoopbackHost(hostname) {
|
|
3
|
+
const h = hostname.toLowerCase().replace(/^\[/, '').replace(/\]$/, '');
|
|
4
|
+
return h === 'localhost' || h === '127.0.0.1' || h === '::1' || h.endsWith('.localhost');
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Refuse to carry a bearer credential over plain http to a non-loopback host —
|
|
8
|
+
* a token on the wire over http is a token leaked. https always passes; http is
|
|
9
|
+
* allowed only to loopback dev hosts, or anywhere when `allowInsecure` is set.
|
|
10
|
+
*/
|
|
11
|
+
export function assertSecureUrl(rawUrl, allowInsecure) {
|
|
12
|
+
let url;
|
|
13
|
+
try {
|
|
14
|
+
url = new URL(rawUrl);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new PingRoomError(`Invalid URL: ${rawUrl}`, { code: 'invalid_url' });
|
|
18
|
+
}
|
|
19
|
+
if (url.protocol === 'https:') {
|
|
20
|
+
return url;
|
|
21
|
+
}
|
|
22
|
+
if (url.protocol === 'http:' && (allowInsecure || isLoopbackHost(url.hostname))) {
|
|
23
|
+
return url;
|
|
24
|
+
}
|
|
25
|
+
throw new PingRoomError(`Refusing to send credentials over an insecure URL (${url.protocol}//${url.host}). ` +
|
|
26
|
+
'Use https, or set allowInsecure: true for trusted local development.', { code: 'insecure_url' });
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvE,OAAO,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC3F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,aAAsB;IACpE,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,aAAa,CAAC,gBAAgB,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,aAAa,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,IAAI,aAAa,CACrB,sDAAsD,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,KAAK;QAClF,sEAAsE,EACxE,EAAE,IAAI,EAAE,cAAc,EAAE,CACzB,CAAC;AACJ,CAAC"}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { HttpClient } from './http.js';
|
|
2
|
+
export interface JsonRpcError {
|
|
3
|
+
code: number;
|
|
4
|
+
message: string;
|
|
5
|
+
data?: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface McpTool {
|
|
8
|
+
name: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
inputSchema?: Record<string, unknown>;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
export interface McpToolResult {
|
|
14
|
+
content: Array<{
|
|
15
|
+
type: string;
|
|
16
|
+
text?: string;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}>;
|
|
19
|
+
isError?: boolean;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Thin client for the PingRoom MCP endpoint (`POST /api/agent/mcp`,
|
|
24
|
+
* JSON-RPC 2.0). Tools are scope-filtered server-side, so `listTools()` returns
|
|
25
|
+
* only what the credential can call.
|
|
26
|
+
*/
|
|
27
|
+
export declare class McpClient {
|
|
28
|
+
private readonly http;
|
|
29
|
+
private readonly path;
|
|
30
|
+
private nextId;
|
|
31
|
+
constructor(http: HttpClient, path?: string);
|
|
32
|
+
/** Raw JSON-RPC call. Resolves the `result`; throws PingRoomError on a JSON-RPC `error`. */
|
|
33
|
+
call<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
|
|
34
|
+
initialize(params?: Record<string, unknown>): Promise<unknown>;
|
|
35
|
+
listTools(): Promise<{
|
|
36
|
+
tools: McpTool[];
|
|
37
|
+
}>;
|
|
38
|
+
callTool(name: string, args?: Record<string, unknown>): Promise<McpToolResult>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG5C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,SAAS;IAIlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJvB,OAAO,CAAC,MAAM,CAAK;gBAGA,IAAI,EAAE,UAAU,EAChB,IAAI,SAAmB;IAG1C,4FAA4F;IACtF,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAgBrF,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAW9D,SAAS,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAI1C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC;CAGnF"}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { PingRoomError } from './errors.js';
|
|
2
|
+
import { VERSION } from './version.js';
|
|
3
|
+
/**
|
|
4
|
+
* Thin client for the PingRoom MCP endpoint (`POST /api/agent/mcp`,
|
|
5
|
+
* JSON-RPC 2.0). Tools are scope-filtered server-side, so `listTools()` returns
|
|
6
|
+
* only what the credential can call.
|
|
7
|
+
*/
|
|
8
|
+
export class McpClient {
|
|
9
|
+
http;
|
|
10
|
+
path;
|
|
11
|
+
nextId = 1;
|
|
12
|
+
constructor(http, path = '/api/agent/mcp') {
|
|
13
|
+
this.http = http;
|
|
14
|
+
this.path = path;
|
|
15
|
+
}
|
|
16
|
+
/** Raw JSON-RPC call. Resolves the `result`; throws PingRoomError on a JSON-RPC `error`. */
|
|
17
|
+
async call(method, params) {
|
|
18
|
+
const id = this.nextId++;
|
|
19
|
+
const envelope = { jsonrpc: '2.0', id, method, ...(params ? { params } : {}) };
|
|
20
|
+
const res = await this.http.request('POST', this.path, {
|
|
21
|
+
body: envelope,
|
|
22
|
+
headers: { Accept: 'application/json, text/event-stream' },
|
|
23
|
+
});
|
|
24
|
+
if (res && res.error) {
|
|
25
|
+
throw new PingRoomError(`MCP error ${res.error.code}: ${res.error.message}`, {
|
|
26
|
+
code: `mcp_${res.error.code}`,
|
|
27
|
+
body: res.error,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return res?.result;
|
|
31
|
+
}
|
|
32
|
+
initialize(params) {
|
|
33
|
+
return this.call('initialize', params ?? {
|
|
34
|
+
protocolVersion: '2025-06-18',
|
|
35
|
+
capabilities: {},
|
|
36
|
+
clientInfo: { name: 'pingroom-sdk', version: VERSION },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
listTools() {
|
|
40
|
+
return this.call('tools/list');
|
|
41
|
+
}
|
|
42
|
+
callTool(name, args = {}) {
|
|
43
|
+
return this.call('tools/call', { name, arguments: args });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAqBvC;;;;GAIG;AACH,MAAM,OAAO,SAAS;IAID;IACA;IAJX,MAAM,GAAG,CAAC,CAAC;IAEnB,YACmB,IAAgB,EAChB,OAAO,gBAAgB;QADvB,SAAI,GAAJ,IAAI,CAAY;QAChB,SAAI,GAAJ,IAAI,CAAmB;IACvC,CAAC;IAEJ,4FAA4F;IAC5F,KAAK,CAAC,IAAI,CAAc,MAAc,EAAE,MAAgC;QACtE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC/E,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAuC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE;YAC3F,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,EAAE,MAAM,EAAE,qCAAqC,EAAE;SAC3D,CAAC,CAAC;QACH,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,IAAI,aAAa,CAAC,aAAa,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE;gBAC3E,IAAI,EAAE,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE;gBAC7B,IAAI,EAAE,GAAG,CAAC,KAAK;aAChB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,GAAG,EAAE,MAAW,CAAC;IAC1B,CAAC;IAED,UAAU,CAAC,MAAgC;QACzC,OAAO,IAAI,CAAC,IAAI,CACd,YAAY,EACZ,MAAM,IAAI;YACR,eAAe,EAAE,YAAY;YAC7B,YAAY,EAAE,EAAE;YAChB,UAAU,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE;SACvD,CACF,CAAC;IACJ,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,IAAI,CAAuB,YAAY,CAAC,CAAC;IACvD,CAAC;IAED,QAAQ,CAAC,IAAY,EAAE,OAAgC,EAAE;QACvD,OAAO,IAAI,CAAC,IAAI,CAAgB,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;CACF"}
|