🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

otterkit

Package Overview
Dependencies
Maintainers
1
Versions
40
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

otterkit - npm Package Compare versions

Comparing version
0.37.0
to
0.39.0
+20
dist/domains.d.ts
export interface DomainStatus {
domain: {
hostname: string;
status: string;
sslStatus: string | null;
} | null;
cnameTarget: string;
}
export declare class DomainError extends Error {
code: string;
constructor(code: string, message?: string);
}
/** The endpoint's custom domain, if any (polls certificate issuance). */
export declare function getDomain(subdomain: string): Promise<DomainStatus>;
/** Attach a hostname (one per endpoint). Returns pending until the CNAME lands. */
export declare function attachDomain(subdomain: string, hostname: string): Promise<DomainStatus>;
/** Detach the endpoint's custom domain. */
export declare function removeDomain(subdomain: string): Promise<{
removed: boolean;
}>;
/**
* Custom domains for webhook endpoints, over the token-authed /api/me
* surface - the same machinery as the console's domain card: attach a
* hostname you own, add the CNAME, and the certificate issues on its own.
*/
import { getToken } from './credentials.js';
const API_SERVER = process.env.OTTERKIT_API_URL || 'https://api.otterkit.com';
export class DomainError extends Error {
code;
constructor(code, message) {
super(message ?? code);
this.code = code;
this.name = 'DomainError';
}
}
async function domainFetch(subdomain, init) {
const token = getToken();
if (!token)
throw new DomainError('not_logged_in', 'Run `otterkit login` first.');
const resp = await fetch(`${API_SERVER}/api/me/webhooks/${encodeURIComponent(subdomain)}/domain`, {
...init,
headers: {
Authorization: `Bearer ${token}`,
...(init.body ? { 'Content-Type': 'application/json' } : {}),
},
});
const json = (await resp.json().catch(() => ({})));
if (resp.status === 404) {
throw new DomainError('not_found', `No webhook endpoint "${subdomain}" on this account.`);
}
if (!resp.ok || !json.data) {
throw new DomainError(json.error ?? `http_${resp.status}`, json.error);
}
return json.data;
}
/** The endpoint's custom domain, if any (polls certificate issuance). */
export async function getDomain(subdomain) {
return (await domainFetch(subdomain, { method: 'GET' }));
}
/** Attach a hostname (one per endpoint). Returns pending until the CNAME lands. */
export async function attachDomain(subdomain, hostname) {
return (await domainFetch(subdomain, {
method: 'POST',
body: JSON.stringify({ hostname }),
}));
}
/** Detach the endpoint's custom domain. */
export async function removeDomain(subdomain) {
return (await domainFetch(subdomain, { method: 'DELETE' }));
}
+15
-0

@@ -38,2 +38,17 @@ export interface StoredRequest {

}>;
/**
* Re-deliver a server-stored capture to a target URL, with optional
* method/headers/body overrides. Cloud endpoints only (they hold the
* history); local-log captures replay with `otterkit replay`.
*/
export declare function replayStored(subdomain: string, requestId: string, opts: {
targetUrl: string;
method?: string;
headers?: Record<string, string>;
bodyText?: string;
}): Promise<{
delivered: boolean;
status: number;
durationMs: number;
}>;
export interface SchemaInferResult {

@@ -40,0 +55,0 @@ subdomain: string;

@@ -52,2 +52,22 @@ /**

/**
* Re-deliver a server-stored capture to a target URL, with optional
* method/headers/body overrides. Cloud endpoints only (they hold the
* history); local-log captures replay with `otterkit replay`.
*/
export async function replayStored(subdomain, requestId, opts) {
const token = getToken();
if (!token)
throw new HistoryError('not_logged_in', 'Run `otterkit login` first.');
const resp = await fetch(`${API_SERVER}/api/me/webhooks/${encodeURIComponent(subdomain)}/requests/${encodeURIComponent(requestId)}/replay`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify(opts),
});
const json = (await resp.json().catch(() => ({})));
if (!resp.ok || !json.data) {
throw new HistoryError(json.error ?? `replay_failed_${resp.status}`, json.message);
}
return json.data;
}
/**
* Infer JSON Schema + TypeScript types from a session's stored request

@@ -54,0 +74,0 @@ * bodies. Same token-authed surface and error mapping as fetchHistory; the

+84
-2

@@ -26,6 +26,7 @@ /**

import { sendCore, verifyCore, SendError } from './send.js';
import { createPulse } from './pulse.js';
import { createPulse, getPulseConfig, updatePulse } from './pulse.js';
import { attachDomain, getDomain, removeDomain } from './domains.js';
import { listEvents } from './providers.js';
import { createWait, listWaits, cancelWait, sessionToken } from './waits.js';
import { fetchHistory, fetchSchema } from './history.js';
import { fetchHistory, fetchSchema, replayStored } from './history.js';
const { version } = createRequire(import.meta.url)('../package.json');

@@ -504,2 +505,83 @@ const API_SERVER = process.env.OTTERKIT_API_URL || 'https://api.otterkit.com';

});
server.registerTool('pulse_update', {
title: 'Edit a live pulse in place',
description: "Change an owned pulse's schedule without recreating it: interval/window, mode, and for active mode the target URL, method, and body. Run history, the pulse URL, and the billing window all survive; the next run (or check-in window) is measured from the update. Omitted fields keep their current values.",
inputSchema: {
subdomain: z.string().describe('The pulse to edit'),
mode: z.enum(['active', 'expect']).optional().describe('Switch mode (default: keep)'),
interval: z
.string()
.optional()
.describe('New interval/window e.g. 30s, 10m, 4h, 1d (min 30s; default: keep)'),
url: z.string().optional().describe('Active mode: new target URL (default: keep)'),
method: z.string().optional().describe('Active mode: new HTTP method (default: keep)'),
body: z.string().optional().describe('Active mode: new request body (default: keep)'),
},
}, async (args) => {
try {
const current = await getPulseConfig(args.subdomain);
const mode = args.mode ?? current.mode;
const r = await updatePulse(args.subdomain, {
mode,
interval: args.interval ?? `${Math.round(current.intervalMs / 1000)}s`,
url: mode === 'active' ? (args.url ?? current.url ?? undefined) : undefined,
method: mode === 'active' ? (args.method ?? current.method ?? 'GET') : undefined,
body: mode === 'active' ? (args.body ?? current.bodyText ?? undefined) : undefined,
});
return ok({ updated: true, nextRunAt: r.nextRunAt });
}
catch (e) {
return mapError(e);
}
});
server.registerTool('webhook_domain', {
title: 'Custom domain on a webhook endpoint',
description: "Serve an owned webhook endpoint on a domain the user controls (e.g. hooks.their-company.com) - the certificate is issued and renewed automatically once they add the returned CNAME record. Actions: status (poll issuance), attach {hostname}, remove. One hostname per endpoint.",
inputSchema: {
subdomain: z.string().describe('Webhook endpoint to configure'),
action: z.enum(['status', 'attach', 'remove']),
hostname: z.string().optional().describe('attach: the hostname to serve the endpoint on'),
},
}, async (args) => {
try {
if (args.action === 'remove')
return ok(await removeDomain(args.subdomain));
if (args.action === 'attach') {
if (!args.hostname)
return err('hostname_required', {});
return ok(await attachDomain(args.subdomain, args.hostname));
}
return ok(await getDomain(args.subdomain));
}
catch (e) {
return mapError(e);
}
});
server.registerTool('history_replay', {
title: 'Replay a server-stored capture to a URL',
description: 'Re-deliver a request from server-side stored history (webhook_history returns the ids) to any target URL - original method, headers (transport noise stripped), and body, plus an X-OtterKit-Replay marker. Optional overrides edit the method, headers, or body first. Cloud endpoints only; local-log captures replay with request_replay.',
inputSchema: {
subdomain: z.string().describe('Endpoint whose stored history holds the request'),
requestId: z.string().describe('Stored request id (from webhook_history)'),
targetUrl: z.string().describe('Where to deliver the copy'),
method: z.string().optional().describe('Override the HTTP method'),
headers: z
.record(z.string(), z.string())
.optional()
.describe('Replace the header set entirely'),
bodyText: z.string().optional().describe('Replace the body'),
},
}, async (args) => {
try {
return ok(await replayStored(args.subdomain, args.requestId, {
targetUrl: args.targetUrl,
method: args.method,
headers: args.headers,
bodyText: args.bodyText,
}));
}
catch (e) {
return mapError(e);
}
});
server.registerTool('webhook_history', {

@@ -506,0 +588,0 @@ title: 'Server-side stored capture history',

@@ -35,1 +35,23 @@ export interface PulseOptions {

export declare function stopPulse(subdomain: string, stopToken: string): Promise<void>;
export interface PulseConfig {
mode: 'active' | 'expect';
intervalMs: number;
url: string | null;
method: string | null;
bodyText: string | null;
}
/** The live schedule config of an owned pulse (url/method/body included). */
export declare function getPulseConfig(subdomain: string): Promise<PulseConfig>;
/**
* Edit a live pulse's schedule in place: run history, the URL, and the
* billing window all stay; the next run/window is measured from now.
*/
export declare function updatePulse(subdomain: string, opts: {
mode: 'active' | 'expect';
interval: string;
url?: string;
method?: string;
body?: string;
}): Promise<{
nextRunAt: string;
}>;

@@ -47,1 +47,34 @@ /**

}
/* ── edit (token-authed /api/me surface - works from any machine) ────── */
const API_SERVER = process.env.OTTERKIT_API_URL || 'https://api.otterkit.com';
/** The live schedule config of an owned pulse (url/method/body included). */
export async function getPulseConfig(subdomain) {
const token = getToken();
if (!token)
throw new PulseError('not_logged_in', 'Run `otterkit login` first.');
const resp = await fetch(`${API_SERVER}/api/me/pulses/${encodeURIComponent(subdomain)}/config`, { headers: { Authorization: `Bearer ${token}` } });
const json = (await resp.json().catch(() => ({})));
if (!resp.ok || !json.data) {
throw new PulseError(json.error ?? `http_${resp.status}`, json.error);
}
return json.data.config;
}
/**
* Edit a live pulse's schedule in place: run history, the URL, and the
* billing window all stay; the next run/window is measured from now.
*/
export async function updatePulse(subdomain, opts) {
const token = getToken();
if (!token)
throw new PulseError('not_logged_in', 'Run `otterkit login` first.');
const resp = await fetch(`${API_SERVER}/api/me/pulses/${encodeURIComponent(subdomain)}`, {
method: 'PATCH',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify(opts),
});
const json = (await resp.json().catch(() => ({})));
if (!resp.ok || !json.data) {
throw new PulseError(json.error ?? `http_${resp.status}`, json.error);
}
return { nextRunAt: json.data.nextRunAt };
}
+1
-1
{
"name": "otterkit",
"version": "0.37.0",
"version": "0.39.0",
"description": "OtterKit CLI - provision and connect tunnels for AI agents",

@@ -5,0 +5,0 @@ "mcpName": "io.github.useotterkit/otterkit",

Sorry, the diff of this file is too big to display