diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3397a0a..59fc92f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -75,7 +75,7 @@ jobs:
URL: https://pkg.stainless.com/s?subpackage=mcp-server
AUTH: ${{ steps.github-oidc.outputs.github_token }}
SHA: ${{ github.sha }}
- BUILD_PATH: packages/mcp-server/dist
+ BASE_PATH: packages/mcp-server
run: ./scripts/utils/upload-artifact.sh
test:
timeout-minutes: 10
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
index 6ab8894..b9eedaa 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -16,6 +16,8 @@ jobs:
publish:
name: publish
runs-on: ubuntu-latest
+ permissions:
+ contents: write
steps:
- uses: actions/checkout@v4
@@ -39,3 +41,10 @@ jobs:
yarn tsn scripts/publish-packages.ts "{ \"paths_released\": \"$PATHS_RELEASED\" }"
env:
NPM_TOKEN: ${{ secrets.BEEPER_DESKTOP_NPM_TOKEN || secrets.NPM_TOKEN }}
+
+ - name: Upload MCP Server DXT GitHub release asset
+ run: |
+ gh release upload ${{ github.event.release.tag_name }} \
+ packages/mcp-server/beeper_desktop_api_api.mcpb
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index d98d51a..74cba89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,5 @@ dist
dist-deno
/*.tgz
.idea/
-
+dist-bundle
+*.mcpb
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 88f7c73..18e45d5 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.4"
+ ".": "0.1.5"
}
diff --git a/.stats.yml b/.stats.yml
index 6f44d46..fc8ce6a 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 11
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper%2Fbeeper-desktop-api-097cbf5de83619d0b40ba59377494db32816f2a5369d4e986a94568f3d79c7c8.yml
-openapi_spec_hash: cbb791aa1e4b75740911af7a0d93529d
-config_hash: 35aeee58f2bef9a9f5354e4c8504e26e
+configured_endpoints: 14
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper%2Fbeeper-desktop-api-8c712fe19f280b0b89ecc8a3ce61e9f6b165cee97ce33f66c66a7a5db339c755.yml
+openapi_spec_hash: 1ea71129cc1a1ccc3dc8a99566082311
+config_hash: 061b75b88f80bb43b4121e5e7c1255e2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8008f60..9667165 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,40 @@
# Changelog
+## 0.1.5 (2025-09-19)
+
+Full Changelog: [v0.1.4...v0.1.5](https://github.com/beeper/desktop-api-js/compare/v0.1.4...v0.1.5)
+
+### Features
+
+* **api:** fix typo in bearerAuth ([76ac2e1](https://github.com/beeper/desktop-api-js/commit/76ac2e16ba6422053a24de98270016f90abe77a7))
+* **api:** manual updates ([3c711d9](https://github.com/beeper/desktop-api-js/commit/3c711d9190aed783a3c80c994bdc1a44107cd1be))
+* **api:** manual updates ([6f43810](https://github.com/beeper/desktop-api-js/commit/6f43810bf528676498df6ee81380fdf181ef5363))
+* **api:** manual updates ([2bc9e1b](https://github.com/beeper/desktop-api-js/commit/2bc9e1b7b6006cf9341b0bbf7ad5fba6ae482822))
+* **api:** manual updates ([49f039b](https://github.com/beeper/desktop-api-js/commit/49f039bbfb9080e425ea0756bdab767107de711d))
+* **api:** small tweaks ([ab7267b](https://github.com/beeper/desktop-api-js/commit/ab7267b120d2f009050bf8944cb43f6a27631de5))
+* **mcp:** add docs search tool ([d90bd86](https://github.com/beeper/desktop-api-js/commit/d90bd86e672737b397e15d388f4e9ad1a8bd43e3))
+* **mcp:** allow setting logging level ([6f2169f](https://github.com/beeper/desktop-api-js/commit/6f2169fd4bb1aaf12dbc7765c304fc9b722b0c32))
+* **mcp:** expose client options in `streamableHTTPApp` ([9afd751](https://github.com/beeper/desktop-api-js/commit/9afd7510b1c96fe5624d1f37f84521c1be1a6353))
+
+
+### Bug Fixes
+
+* **ci:** set permissions for DXT publish action ([5a6b63d](https://github.com/beeper/desktop-api-js/commit/5a6b63d7d7ba2514901ff5524890294feeaa981c))
+* coerce nullable values to undefined ([f43ac68](https://github.com/beeper/desktop-api-js/commit/f43ac6875858be2d27c31cbe7acc3d1fbac76c40))
+* **mcp:** fix query options parsing ([4f00241](https://github.com/beeper/desktop-api-js/commit/4f00241e35309ff7441fd842ba787a87caf17cae))
+* **mcp:** fix uploading dxt release assets ([1189099](https://github.com/beeper/desktop-api-js/commit/118909952860f420b4bc3f63b3de7e31d486c967))
+
+
+### Chores
+
+* ci build action ([b63fda2](https://github.com/beeper/desktop-api-js/commit/b63fda25086cfcc8758df9b5fcb32b49a78df28c))
+* **internal:** codegen related update ([b244e26](https://github.com/beeper/desktop-api-js/commit/b244e2647d3fd792e3764127ebdd8dc0c0f6df7f))
+* **internal:** codegen related update ([b961076](https://github.com/beeper/desktop-api-js/commit/b961076bf7533638f00eebb1b2a81508db35ad7b))
+* **internal:** codegen related update ([dd44c80](https://github.com/beeper/desktop-api-js/commit/dd44c803559a85fe7bc1f34fe7a5c4a356ec3460))
+* **internal:** gitignore .mcpb files ([5a8ade3](https://github.com/beeper/desktop-api-js/commit/5a8ade3b8c2fff3764893114e5eb862604fc5c6b))
+* **mcp:** rename dxt to mcpb ([343d030](https://github.com/beeper/desktop-api-js/commit/343d0303c26c103a763be49e7319bee88aa71b7e))
+* **mcp:** upload dxt as release asset ([55f6005](https://github.com/beeper/desktop-api-js/commit/55f6005d92b7f26a46c49ba76f17f7ea34b8c4b9))
+
## 0.1.4 (2025-09-01)
Full Changelog: [v0.1.3...v0.1.4](https://github.com/beeper/desktop-api-js/compare/v0.1.3...v0.1.4)
diff --git a/api.md b/api.md
index 825f9d1..54b94fd 100644
--- a/api.md
+++ b/api.md
@@ -24,20 +24,36 @@ Methods:
Types:
+- AppDownloadAssetResponse
- AppOpenResponse
+- AppSearchResponse
Methods:
+- client.app.downloadAsset({ ...params }) -> AppDownloadAssetResponse
- client.app.open({ ...params }) -> AppOpenResponse
+- client.app.search({ ...params }) -> AppSearchResponse
+
+# Contacts
+
+Types:
+
+- ContactSearchResponse
+
+Methods:
+
+- client.contacts.search({ ...params }) -> ContactSearchResponse
# Chats
Types:
- Chat
+- ChatCreateResponse
Methods:
+- client.chats.create({ ...params }) -> ChatCreateResponse
- client.chats.retrieve({ ...params }) -> Chat
- client.chats.archive({ ...params }) -> BaseResponse
- client.chats.search({ ...params }) -> ChatsCursor
@@ -53,22 +69,12 @@ Methods:
Types:
-- MessageSendResponse
-
-Methods:
-
-- client.messages.search({ ...params }) -> MessagesCursor
-- client.messages.send({ ...params }) -> MessageSendResponse
-
-## Attachments
-
-Types:
-
-- AttachmentDownloadResponse
+- MessageSendResponse
Methods:
-- client.messages.attachments.download({ ...params }) -> AttachmentDownloadResponse
+- client.messages.search({ ...params }) -> MessagesCursor
+- client.messages.send({ ...params }) -> MessageSendResponse
# Token
diff --git a/package.json b/package.json
index c5faeaf..bf401d4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@beeper/desktop-api",
- "version": "0.1.4",
+ "version": "0.1.5",
"description": "The official TypeScript library for the Beeper Desktop API",
"author": "Beeper Desktop ",
"types": "dist/index.d.ts",
diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md
index 7d51cbc..2ab4e18 100644
--- a/packages/mcp-server/README.md
+++ b/packages/mcp-server/README.md
@@ -218,13 +218,14 @@ The following tools are available in this MCP server.
### Resource `app`:
-- `open_in_app` (`write`) tags: [app]: Open Beeper, optionally focusing a chat or message, or pre-filling a draft.
+- `open_in_app` (`write`) tags: [app]: Open Beeper Desktop and optionally navigate to a specific chat, message, or pre-fill draft text and attachment.
+- `search` (`read`) tags: [app]: Search for chats, participant name matches in groups, and the first page of messages in one call. Use this when the user asks for a specific chat, group, or person.
### Resource `chats`:
- `get_chat` (`read`) tags: [chats]: Get chat details: metadata, participants (limited), last activity.
- `archive_chat` (`write`) tags: [chats]: Archive or unarchive a chat.
-- `search_chats` (`read`) tags: [chats]: Search chats by inbox, type, unread status, or text. Paginates.
+- `search_chats` (`read`) tags: [chats]: Search chats by title/network or participants using Beeper Desktop's renderer algorithm. Optional 'scope'.
### Resource `chats.reminders`:
@@ -239,17 +240,11 @@ The following tools are available in this MCP server.
• ✅ RIGHT: query="dinner" or query="sick" or query="error" (single words users type)
• ❌ WRONG: query="dinner plans tonight" or query="health issues" (phrases/concepts)
• The query matches ALL words provided (in any order). Example: query="flight booking" finds messages with both "flight" AND "booking".
- - Media filters: Use onlyWithMedia for any media, or specific filters like onlyWithVideo, onlyWithImage, onlyWithLink, onlyWithFile for specific types.
- - Pagination: use 'oldestCursor' + direction='before' for older; 'newestCursor' + direction='after' for newer.
- Performance: provide chatIDs/accountIDs when known. Omitted 'query' returns results based on filters only. Partial matches enabled; 'excludeLowPriority' defaults to true.
- Workflow tip: To search messages in specific conversations: 1) Use find-chats to get chatIDs, 2) Use search-messages with those chatIDs.
- IMPORTANT: Chat names vary widely. ASK the user for clarification:
• "Which chat do you mean by family?" (could be "The Smiths", "Mom Dad Kids", etc.)
• "What's the name of your work chat?" (could be "Team", company name, project name)
- • "Who are the participants?" (use participantQuery in find-chats)
+ • "Who are the participants?" (use scope="participants" in search-chats)
Returns: matching messages and referenced chats.
- `send_message` (`write`) tags: [messages]: Send a text message to a specific chat. Supports replying to existing messages. Returns the sent message ID and a deeplink to the chat
-
-### Resource `messages.attachments`:
-
-- `download_attachment` (`write`) tags: [messages]: Download a message attachment and return the local file path.
diff --git a/packages/mcp-server/build b/packages/mcp-server/build
index ce3047d..b94538a 100644
--- a/packages/mcp-server/build
+++ b/packages/mcp-server/build
@@ -30,3 +30,27 @@ cp tsconfig.dist-src.json dist/src/tsconfig.json
chmod +x dist/index.js
DIST_PATH=./dist PKG_IMPORT_PATH=@beeper/desktop-mcp/ node ../../scripts/utils/postprocess-files.cjs
+
+# mcp bundle
+rm -rf dist-bundle beeper_desktop_api_api.mcpb; mkdir dist-bundle
+
+# copy package.json
+PKG_JSON_PATH=../../packages/mcp-server/package.json node ../../scripts/utils/make-dist-package-json.cjs > dist-bundle/package.json
+
+# copy files
+node scripts/copy-bundle-files.cjs
+
+# install runtime deps
+cd dist-bundle
+npm install
+cd ..
+
+# pack bundle
+cp manifest.json dist-bundle
+
+npx mcpb pack dist-bundle beeper_desktop_api_api.mcpb
+
+npx mcpb sign beeper_desktop_api_api.mcpb --self-signed
+
+# clean up
+rm -rf dist-bundle
diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json
new file mode 100644
index 0000000..09047c8
--- /dev/null
+++ b/packages/mcp-server/manifest.json
@@ -0,0 +1,43 @@
+{
+ "dxt_version": "0.2",
+ "name": "@beeper/desktop-mcp",
+ "version": "0.1.4",
+ "description": "The official MCP Server for the Beeper Desktop API",
+ "author": {
+ "name": "Beeper Desktop",
+ "email": "help@beeper.com"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/beeper/desktop-api-js.git"
+ },
+ "homepage": "https://github.com/beeper/desktop-api-js/tree/main/packages/mcp-server#readme",
+ "documentation": "https://developers.beeper.com/desktop-api/",
+ "server": {
+ "type": "node",
+ "entry_point": "index.js",
+ "mcp_config": {
+ "command": "node",
+ "args": ["${__dirname}/index.js"],
+ "env": {
+ "BEEPER_ACCESS_TOKEN": "${user_config.BEEPER_ACCESS_TOKEN}"
+ }
+ }
+ },
+ "user_config": {
+ "BEEPER_ACCESS_TOKEN": {
+ "title": "access_token",
+ "description": "Bearer access token obtained via OAuth2 PKCE flow or created in-app. Required for all API operations.",
+ "required": true,
+ "type": "string"
+ }
+ },
+ "tools": [],
+ "tools_generated": true,
+ "compatibility": {
+ "runtimes": {
+ "node": ">=18.0.0"
+ }
+ },
+ "keywords": ["api"]
+}
diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json
index ac00c70..b9510db 100644
--- a/packages/mcp-server/package.json
+++ b/packages/mcp-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@beeper/desktop-mcp",
- "version": "0.1.4",
+ "version": "0.1.5",
"description": "The official MCP Server for the Beeper Desktop API",
"author": "Beeper Desktop ",
"types": "dist/index.d.ts",
@@ -47,6 +47,7 @@
"mcp-server": "dist/index.js"
},
"devDependencies": {
+ "@anthropic-ai/mcpb": "^1.1.0",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/jest": "^29.4.0",
diff --git a/packages/mcp-server/scripts/copy-bundle-files.cjs b/packages/mcp-server/scripts/copy-bundle-files.cjs
new file mode 100644
index 0000000..8a1188b
--- /dev/null
+++ b/packages/mcp-server/scripts/copy-bundle-files.cjs
@@ -0,0 +1,36 @@
+const fs = require('fs');
+const path = require('path');
+const pkgJson = require('../dist-bundle/package.json');
+
+const distDir = path.resolve(__dirname, '..', 'dist');
+const distBundleDir = path.resolve(__dirname, '..', 'dist-bundle');
+const distBundlePkgJson = path.join(distBundleDir, 'package.json');
+
+async function* walk(dir) {
+ for await (const d of await fs.promises.opendir(dir)) {
+ const entry = path.join(dir, d.name);
+ if (d.isDirectory()) yield* walk(entry);
+ else if (d.isFile()) yield entry;
+ }
+}
+
+async function copyFiles() {
+ // copy runtime files
+ for await (const file of walk(distDir)) {
+ if (!/[cm]?js$/.test(file)) continue;
+ const dest = path.join(distBundleDir, path.relative(distDir, file));
+ await fs.promises.mkdir(path.dirname(dest), { recursive: true });
+ await fs.promises.copyFile(file, dest);
+ }
+
+ // replace package.json reference with local reference
+ for (const dep in pkgJson.dependencies) {
+ if (dep === '@beeper/desktop-api') {
+ pkgJson.dependencies[dep] = 'file:../../../dist/';
+ }
+ }
+
+ await fs.promises.writeFile(distBundlePkgJson, JSON.stringify(pkgJson, null, 2));
+}
+
+copyFiles();
diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts
index baa106d..8339908 100644
--- a/packages/mcp-server/src/code-tool.ts
+++ b/packages/mcp-server/src/code-tool.ts
@@ -7,9 +7,7 @@ import { Endpoint, ContentBlock, Metadata } from './tools/types';
import { Tool } from '@modelcontextprotocol/sdk/types.js';
-import { newDenoHTTPWorker } from '@valtown/deno-http-worker';
import { WorkerInput, WorkerError, WorkerSuccess } from './code-tool-types';
-import { workerPath } from './code-tool-paths.cjs';
/**
* A tool that runs code against a copy of the SDK.
@@ -20,7 +18,7 @@ import { workerPath } from './code-tool-paths.cjs';
*
* @param endpoints - The endpoints to include in the list.
*/
-export function codeTool(): Endpoint {
+export async function codeTool(): Promise {
const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] };
const tool: Tool = {
name: 'execute',
@@ -29,6 +27,10 @@ export function codeTool(): Endpoint {
inputSchema: { type: 'object', properties: { code: { type: 'string' } } },
};
+ // Import dynamically to avoid failing at import time in cases where the environment is not well-supported.
+ const { newDenoHTTPWorker } = await import('@valtown/deno-http-worker');
+ const { workerPath } = await import('./code-tool-paths.cjs');
+
const handler = async (client: BeeperDesktop, args: unknown) => {
const baseURLHostname = new URL(client.baseURL).hostname;
const { code } = args as { code: string };
diff --git a/packages/mcp-server/src/http.ts b/packages/mcp-server/src/http.ts
index 3165f91..91f0c84 100644
--- a/packages/mcp-server/src/http.ts
+++ b/packages/mcp-server/src/http.ts
@@ -7,7 +7,7 @@ import cors from 'cors';
import express from 'express';
import { fromError } from 'zod-validation-error/v3';
import { McpOptions, parseQueryOptions } from './options';
-import { initMcpServer, newMcpServer } from './server';
+import { ClientOptions, initMcpServer, newMcpServer } from './server';
import { parseAuthHeaders } from './headers';
const oauthResourceIdentifier = (req: express.Request): string => {
@@ -15,11 +15,17 @@ const oauthResourceIdentifier = (req: express.Request): string => {
return `${protocol}://${req.get('host')}/`;
};
-const newServer = (
- defaultMcpOptions: McpOptions,
- req: express.Request,
- res: express.Response,
-): McpServer | null => {
+const newServer = ({
+ clientOptions,
+ mcpOptions: defaultMcpOptions,
+ req,
+ res,
+}: {
+ clientOptions: ClientOptions;
+ mcpOptions: McpOptions;
+ req: express.Request;
+ res: express.Response;
+}): McpServer | null => {
const server = newMcpServer();
let mcpOptions: McpOptions;
@@ -41,10 +47,8 @@ const newServer = (
initMcpServer({
server: server,
clientOptions: {
+ ...clientOptions,
...authOptions,
- defaultHeaders: {
- 'X-Stainless-MCP': 'true',
- },
},
mcpOptions,
});
@@ -67,17 +71,19 @@ const newServer = (
return server;
};
-const post = (defaultOptions: McpOptions) => async (req: express.Request, res: express.Response) => {
- const server = newServer(defaultOptions, req, res);
- // If we return null, we already set the authorization error.
- if (server === null) return;
- const transport = new StreamableHTTPServerTransport({
- // Stateless server
- sessionIdGenerator: undefined,
- });
- await server.connect(transport);
- await transport.handleRequest(req, res, req.body);
-};
+const post =
+ (options: { clientOptions: ClientOptions; mcpOptions: McpOptions }) =>
+ async (req: express.Request, res: express.Response) => {
+ const server = newServer({ ...options, req, res });
+ // If we return null, we already set the authorization error.
+ if (server === null) return;
+ const transport = new StreamableHTTPServerTransport({
+ // Stateless server
+ sessionIdGenerator: undefined,
+ });
+ await server.connect(transport);
+ await transport.handleRequest(req, res, req.body);
+ };
const get = async (req: express.Request, res: express.Response) => {
res.status(405).json({
@@ -109,21 +115,27 @@ const oauthMetadata = (req: express.Request, res: express.Response) => {
});
};
-export const streamableHTTPApp = (options: McpOptions): express.Express => {
+export const streamableHTTPApp = ({
+ clientOptions = {},
+ mcpOptions = {},
+}: {
+ clientOptions?: ClientOptions;
+ mcpOptions?: McpOptions;
+}): express.Express => {
const app = express();
app.set('query parser', 'extended');
app.use(express.json());
app.get('/.well-known/oauth-protected-resource', cors(), oauthMetadata);
app.get('/', get);
- app.post('/', post(options));
+ app.post('/', post({ clientOptions, mcpOptions }));
app.delete('/', del);
return app;
};
export const launchStreamableHTTPServer = async (options: McpOptions, port: number | string | undefined) => {
- const app = streamableHTTPApp(options);
+ const app = streamableHTTPApp({ mcpOptions: options });
const server = app.listen(port);
const address = server.address();
diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts
index c450e4b..4850a0e 100644
--- a/packages/mcp-server/src/index.ts
+++ b/packages/mcp-server/src/index.ts
@@ -14,7 +14,7 @@ async function main() {
return;
}
- const selectedTools = selectToolsOrError(endpoints, options);
+ const selectedTools = await selectToolsOrError(endpoints, options);
console.error(
`MCP Server starting with ${selectedTools.length} tools:`,
@@ -47,9 +47,9 @@ function parseOptionsOrError() {
}
}
-function selectToolsOrError(endpoints: Endpoint[], options: McpOptions): Endpoint[] {
+async function selectToolsOrError(endpoints: Endpoint[], options: McpOptions): Promise {
try {
- const includedTools = selectTools(endpoints, options);
+ const includedTools = await selectTools(endpoints, options);
if (includedTools.length === 0) {
console.error('No tools match the provided filters.');
process.exit(1);
diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts
index 2100cf5..ecc9f10 100644
--- a/packages/mcp-server/src/options.ts
+++ b/packages/mcp-server/src/options.ts
@@ -367,12 +367,12 @@ export function parseQueryOptions(defaultOptions: McpOptions, query: unknown): M
}
let dynamicTools: boolean | undefined =
- queryOptions.no_tools && !queryOptions.no_tools?.includes('dynamic') ? false
+ queryOptions.no_tools && queryOptions.no_tools?.includes('dynamic') ? false
: queryOptions.tools?.includes('dynamic') ? true
: defaultOptions.includeDynamicTools;
let allTools: boolean | undefined =
- queryOptions.no_tools && !queryOptions.no_tools?.includes('all') ? false
+ queryOptions.no_tools && queryOptions.no_tools?.includes('all') ? false
: queryOptions.tools?.includes('all') ? true
: defaultOptions.includeAllTools;
diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts
index 54ffb6c..3f193eb 100644
--- a/packages/mcp-server/src/server.ts
+++ b/packages/mcp-server/src/server.ts
@@ -5,8 +5,9 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { Endpoint, endpoints, HandlerFunction, query } from './tools';
import {
CallToolRequestSchema,
- Implementation,
ListToolsRequestSchema,
+ SetLevelRequestSchema,
+ Implementation,
Tool,
} from '@modelcontextprotocol/sdk/types.js';
import { ClientOptions } from '@beeper/desktop-api';
@@ -32,12 +33,12 @@ export const newMcpServer = () =>
new McpServer(
{
name: 'beeper_desktop_api_api',
- version: '0.1.4',
+ version: '0.1.5',
},
{
capabilities: { tools: {}, logging: {} },
instructions:
- 'This MCP server provides access to your Beeper Desktop messages and chats. Use the search and find tools to locate messages and conversations, then use send_message to respond.',
+ 'Access to all chats and messages across networks using Beeper Desktop. Can be used to find, get, send, and manage messages and chats.',
},
);
@@ -59,7 +60,7 @@ export function initMcpServer(params: {
let providedEndpoints: Endpoint[] | null = null;
let endpointMap: Record | null = null;
- const initTools = (implementation?: Implementation) => {
+ const initTools = async (implementation?: Implementation) => {
if (implementation && (!mcpOptions.client || mcpOptions.client === 'infer')) {
mcpOptions.client =
implementation.name.toLowerCase().includes('claude') ? 'claude'
@@ -70,8 +71,8 @@ export function initMcpServer(params: {
...mcpOptions.capabilities,
};
}
- providedEndpoints = selectTools(endpoints, mcpOptions);
- endpointMap = Object.fromEntries(providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint]));
+ providedEndpoints ??= await selectTools(endpoints, mcpOptions);
+ endpointMap ??= Object.fromEntries(providedEndpoints.map((endpoint) => [endpoint.tool.name, endpoint]));
};
const logAtLevel =
@@ -89,7 +90,7 @@ export function initMcpServer(params: {
error: logAtLevel('error'),
};
- const client = new BeeperDesktop({
+ let client = new BeeperDesktop({
logger,
...params.clientOptions,
defaultHeaders: {
@@ -100,7 +101,7 @@ export function initMcpServer(params: {
server.setRequestHandler(ListToolsRequestSchema, async () => {
if (providedEndpoints === null) {
- initTools(server.getClientVersion());
+ await initTools(server.getClientVersion());
}
return {
tools: providedEndpoints!.map((endpoint) => endpoint.tool),
@@ -109,7 +110,7 @@ export function initMcpServer(params: {
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (endpointMap === null) {
- initTools(server.getClientVersion());
+ await initTools(server.getClientVersion());
}
const { name, arguments: args } = request.params;
const endpoint = endpointMap![name];
@@ -119,12 +120,35 @@ export function initMcpServer(params: {
return executeHandler(endpoint.tool, endpoint.handler, client, args, mcpOptions.capabilities);
});
+
+ server.setRequestHandler(SetLevelRequestSchema, async (request) => {
+ const { level } = request.params;
+ switch (level) {
+ case 'debug':
+ client = client.withOptions({ logLevel: 'debug' });
+ break;
+ case 'info':
+ client = client.withOptions({ logLevel: 'info' });
+ break;
+ case 'notice':
+ case 'warning':
+ client = client.withOptions({ logLevel: 'warn' });
+ break;
+ case 'error':
+ client = client.withOptions({ logLevel: 'error' });
+ break;
+ default:
+ client = client.withOptions({ logLevel: 'off' });
+ break;
+ }
+ return {};
+ });
}
/**
* Selects the tools to include in the MCP Server based on the provided options.
*/
-export function selectTools(endpoints: Endpoint[], options?: McpOptions): Endpoint[] {
+export async function selectTools(endpoints: Endpoint[], options?: McpOptions): Promise {
const filteredEndpoints = query(options?.filters ?? [], endpoints);
let includedTools = filteredEndpoints;
@@ -139,7 +163,7 @@ export function selectTools(endpoints: Endpoint[], options?: McpOptions): Endpoi
} else if (options?.includeDynamicTools) {
includedTools = dynamicTools(endpoints);
} else if (options?.includeCodeTools) {
- includedTools = [codeTool()];
+ includedTools = [await codeTool()];
} else {
includedTools = endpoints;
}
diff --git a/packages/mcp-server/src/tools/app/open-in-app.ts b/packages/mcp-server/src/tools/app/open-in-app.ts
index 6f4cefb..bf8fbb1 100644
--- a/packages/mcp-server/src/tools/app/open-in-app.ts
+++ b/packages/mcp-server/src/tools/app/open-in-app.ts
@@ -16,7 +16,8 @@ export const metadata: Metadata = {
export const tool: Tool = {
name: 'open_in_app',
- description: 'Open Beeper, optionally focusing a chat or message, or pre-filling a draft.',
+ description:
+ 'Open Beeper Desktop and optionally navigate to a specific chat, message, or pre-fill draft text and attachment.',
inputSchema: {
type: 'object',
properties: {
@@ -25,13 +26,17 @@ export const tool: Tool = {
description:
'Optional Beeper chat ID (or local chat ID) to focus after opening the app. If omitted, only opens/focuses the app.',
},
+ draftAttachmentPath: {
+ type: 'string',
+ description: 'Optional draft attachment path to populate in the message input field.',
+ },
draftText: {
type: 'string',
description: 'Optional draft text to populate in the message input field.',
},
- messageSortKey: {
+ messageID: {
type: 'string',
- description: 'Optional message sort key. Jumps to that message in the chat when opening.',
+ description: 'Optional message ID. Jumps to that message in the chat when opening.',
},
},
required: [],
diff --git a/packages/mcp-server/src/tools/app/search.ts b/packages/mcp-server/src/tools/app/search.ts
new file mode 100644
index 0000000..a911592
--- /dev/null
+++ b/packages/mcp-server/src/tools/app/search.ts
@@ -0,0 +1,41 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { Metadata, asTextContentResult } from '@beeper/desktop-mcp/tools/types';
+
+import { Tool } from '@modelcontextprotocol/sdk/types.js';
+import BeeperDesktop from '@beeper/desktop-api';
+
+export const metadata: Metadata = {
+ resource: 'app',
+ operation: 'read',
+ tags: ['app'],
+ httpMethod: 'get',
+ httpPath: '/v0/search',
+ operationId: 'search',
+};
+
+export const tool: Tool = {
+ name: 'search',
+ description:
+ 'Search for chats, participant name matches in groups, and the first page of messages in one call. Use this when the user asks for a specific chat, group, or person.',
+ inputSchema: {
+ type: 'object',
+ properties: {
+ query: {
+ type: 'string',
+ description: 'User-typed search text. Literal word matching (NOT semantic).',
+ },
+ },
+ required: ['query'],
+ },
+ annotations: {
+ readOnlyHint: true,
+ },
+};
+
+export const handler = async (client: BeeperDesktop, args: Record | undefined) => {
+ const body = args as any;
+ return asTextContentResult(await client.app.search(body));
+};
+
+export default { metadata, tool, handler };
diff --git a/packages/mcp-server/src/tools/chats/search-chats.ts b/packages/mcp-server/src/tools/chats/search-chats.ts
index f621c81..bc746d9 100644
--- a/packages/mcp-server/src/tools/chats/search-chats.ts
+++ b/packages/mcp-server/src/tools/chats/search-chats.ts
@@ -16,7 +16,8 @@ export const metadata: Metadata = {
export const tool: Tool = {
name: 'search_chats',
- description: 'Search chats by inbox, type, unread status, or text. Paginates.',
+ description:
+ "Search chats by title/network or participants using Beeper Desktop's renderer algorithm. Optional 'scope'.",
inputSchema: {
type: 'object',
properties: {
@@ -65,21 +66,22 @@ export const tool: Tool = {
type: 'integer',
description: 'Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50',
},
- participantQuery: {
+ query: {
type: 'string',
description:
- 'Search string to filter chats by participant names. When multiple words provided, ALL words must match. Searches in username, displayName, and fullName fields.',
+ 'Literal token search (non-semantic). Use single words users type (e.g., "dinner"). When multiple words provided, ALL must match. Case-insensitive.',
},
- query: {
+ scope: {
type: 'string',
description:
- 'Search string to filter chats by title. When multiple words provided, ALL words must match. Matches are case-insensitive substrings.',
+ "Search scope: 'titles' matches title + network; 'participants' matches participant names.",
+ enum: ['titles', 'participants'],
},
type: {
type: 'string',
description:
- 'Specify the type of chats to retrieve: use "single" for direct messages, "group" for group chats, "channel" for channels, or "any" to get all types',
- enum: ['single', 'group', 'channel', 'any'],
+ 'Specify the type of chats to retrieve: use "single" for direct messages, "group" for group chats, or "any" to get all types',
+ enum: ['single', 'group', 'any'],
},
unreadOnly: {
type: 'boolean',
diff --git a/packages/mcp-server/src/tools/index.ts b/packages/mcp-server/src/tools/index.ts
index ad0dc8c..e3ad0a9 100644
--- a/packages/mcp-server/src/tools/index.ts
+++ b/packages/mcp-server/src/tools/index.ts
@@ -6,6 +6,7 @@ export { Metadata, Endpoint, HandlerFunction };
import get_accounts from './accounts/get-accounts';
import open_in_app from './app/open-in-app';
+import search from './app/search';
import get_chat from './chats/get-chat';
import archive_chat from './chats/archive-chat';
import search_chats from './chats/search-chats';
@@ -13,7 +14,6 @@ import set_chat_reminder from './chats/reminders/set-chat-reminder';
import clear_chat_reminder from './chats/reminders/clear-chat-reminder';
import search_messages from './messages/search-messages';
import send_message from './messages/send-message';
-import download_attachment from './messages/attachments/download-attachment';
export const endpoints: Endpoint[] = [];
@@ -23,6 +23,7 @@ function addEndpoint(endpoint: Endpoint) {
addEndpoint(get_accounts);
addEndpoint(open_in_app);
+addEndpoint(search);
addEndpoint(get_chat);
addEndpoint(archive_chat);
addEndpoint(search_chats);
@@ -30,7 +31,6 @@ addEndpoint(set_chat_reminder);
addEndpoint(clear_chat_reminder);
addEndpoint(search_messages);
addEndpoint(send_message);
-addEndpoint(download_attachment);
export type Filter = {
type: 'resource' | 'operation' | 'tag' | 'tool';
diff --git a/packages/mcp-server/src/tools/messages/attachments/download-attachment.ts b/packages/mcp-server/src/tools/messages/attachments/download-attachment.ts
deleted file mode 100644
index 3d31dcc..0000000
--- a/packages/mcp-server/src/tools/messages/attachments/download-attachment.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import { Metadata, asTextContentResult } from '@beeper/desktop-mcp/tools/types';
-
-import { Tool } from '@modelcontextprotocol/sdk/types.js';
-import BeeperDesktop from '@beeper/desktop-api';
-
-export const metadata: Metadata = {
- resource: 'messages.attachments',
- operation: 'write',
- tags: ['messages'],
- httpMethod: 'post',
- httpPath: '/v0/download-attachment',
- operationId: 'download_attachment',
-};
-
-export const tool: Tool = {
- name: 'download_attachment',
- description: 'Download a message attachment and return the local file path.',
- inputSchema: {
- type: 'object',
- properties: {
- chatID: {
- type: 'string',
- description: 'Unique identifier of the chat (supports both chatID and localChatID).',
- },
- messageID: {
- type: 'string',
- description: 'The message ID (eventID) containing the attachment.',
- },
- },
- required: ['chatID', 'messageID'],
- },
- annotations: {},
-};
-
-export const handler = async (client: BeeperDesktop, args: Record | undefined) => {
- const body = args as any;
- return asTextContentResult(await client.messages.attachments.download(body));
-};
-
-export default { metadata, tool, handler };
diff --git a/packages/mcp-server/src/tools/messages/search-messages.ts b/packages/mcp-server/src/tools/messages/search-messages.ts
index a3fc774..f9ca9c7 100644
--- a/packages/mcp-server/src/tools/messages/search-messages.ts
+++ b/packages/mcp-server/src/tools/messages/search-messages.ts
@@ -17,7 +17,7 @@ export const metadata: Metadata = {
export const tool: Tool = {
name: 'search_messages',
description:
- 'Search messages across chats using Beeper\'s message index.\n- When to use: find messages by text and/or filters (chatIDs, accountIDs, chatType, media type filters, sender, date ranges).\n- CRITICAL: Query is LITERAL WORD MATCHING, NOT semantic search! Only finds messages containing these EXACT words.\n • ✅ RIGHT: query="dinner" or query="sick" or query="error" (single words users type)\n • ❌ WRONG: query="dinner plans tonight" or query="health issues" (phrases/concepts)\n • The query matches ALL words provided (in any order). Example: query="flight booking" finds messages with both "flight" AND "booking".\n- Media filters: Use onlyWithMedia for any media, or specific filters like onlyWithVideo, onlyWithImage, onlyWithLink, onlyWithFile for specific types.\n- Pagination: use \'oldestCursor\' + direction=\'before\' for older; \'newestCursor\' + direction=\'after\' for newer.\n- Performance: provide chatIDs/accountIDs when known. Omitted \'query\' returns results based on filters only. Partial matches enabled; \'excludeLowPriority\' defaults to true.\n- Workflow tip: To search messages in specific conversations: 1) Use find-chats to get chatIDs, 2) Use search-messages with those chatIDs.\n- IMPORTANT: Chat names vary widely. ASK the user for clarification:\n • "Which chat do you mean by family?" (could be "The Smiths", "Mom Dad Kids", etc.)\n • "What\'s the name of your work chat?" (could be "Team", company name, project name)\n • "Who are the participants?" (use participantQuery in find-chats)\nReturns: matching messages and referenced chats.',
+ 'Search messages across chats using Beeper\'s message index.\n- When to use: find messages by text and/or filters (chatIDs, accountIDs, chatType, media type filters, sender, date ranges).\n- CRITICAL: Query is LITERAL WORD MATCHING, NOT semantic search! Only finds messages containing these EXACT words.\n • ✅ RIGHT: query="dinner" or query="sick" or query="error" (single words users type)\n • ❌ WRONG: query="dinner plans tonight" or query="health issues" (phrases/concepts)\n • The query matches ALL words provided (in any order). Example: query="flight booking" finds messages with both "flight" AND "booking".\n- Performance: provide chatIDs/accountIDs when known. Omitted \'query\' returns results based on filters only. Partial matches enabled; \'excludeLowPriority\' defaults to true.\n- Workflow tip: To search messages in specific conversations: 1) Use find-chats to get chatIDs, 2) Use search-messages with those chatIDs.\n- IMPORTANT: Chat names vary widely. ASK the user for clarification:\n • "Which chat do you mean by family?" (could be "The Smiths", "Mom Dad Kids", etc.)\n • "What\'s the name of your work chat?" (could be "Team", company name, project name)\n • "Who are the participants?" (use scope="participants" in search-chats)\nReturns: matching messages and referenced chats.',
inputSchema: {
type: 'object',
properties: {
@@ -75,27 +75,17 @@ export const tool: Tool = {
},
limit: {
type: 'integer',
- description: 'Maximum number of messages to return (1–500). Defaults to 50.',
- },
- onlyWithFile: {
- type: 'boolean',
- description: 'Only return messages that contain file attachments.',
- },
- onlyWithImage: {
- type: 'boolean',
- description: 'Only return messages that contain image attachments.',
- },
- onlyWithLink: {
- type: 'boolean',
- description: 'Only return messages that contain link attachments.',
- },
- onlyWithMedia: {
- type: 'boolean',
- description: 'Only return messages that contain any type of media attachment.',
+ description:
+ 'Maximum number of messages to return (1–500). Defaults to 20. The current implementation caps each page at 20 items even if a higher limit is requested.',
},
- onlyWithVideo: {
- type: 'boolean',
- description: 'Only return messages that contain video attachments.',
+ mediaTypes: {
+ type: 'array',
+ description:
+ "Filter messages by media types. Use ['any'] for any media type, or specify exact types like ['video', 'image']. Omit for no media filtering.",
+ items: {
+ type: 'string',
+ enum: ['any', 'video', 'image', 'link', 'file'],
+ },
},
query: {
type: 'string',
diff --git a/packages/mcp-server/src/tools/messages/send-message.ts b/packages/mcp-server/src/tools/messages/send-message.ts
index 2a25670..93a7e8a 100644
--- a/packages/mcp-server/src/tools/messages/send-message.ts
+++ b/packages/mcp-server/src/tools/messages/send-message.ts
@@ -23,8 +23,7 @@ export const tool: Tool = {
properties: {
chatID: {
type: 'string',
- description:
- 'The identifier of the chat where the message will send (accepts both chatID and local chat ID)',
+ description: 'Unique identifier of the chat (a.k.a. room or thread).',
},
replyToMessageID: {
type: 'string',
diff --git a/packages/mcp-server/yarn.lock b/packages/mcp-server/yarn.lock
index 707a2de..ad81983 100644
--- a/packages/mcp-server/yarn.lock
+++ b/packages/mcp-server/yarn.lock
@@ -10,6 +10,20 @@
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
+"@anthropic-ai/dxt@^0.2.6":
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/@anthropic-ai/dxt/-/dxt-0.2.6.tgz#636197c3d083c9136ac3b5a11d2ba82477fdc2c6"
+ integrity sha512-5VSqKRpkytTYh5UJz9jOaI8zLXNCe4Gc+ArKGFV6IeWnEPP0Qnd0k+V3pO8cYzp92Puf/+Cgo0xc4haE0azTXg==
+ dependencies:
+ "@inquirer/prompts" "^6.0.1"
+ commander "^13.1.0"
+ fflate "^0.8.2"
+ galactus "^1.0.0"
+ ignore "^7.0.5"
+ node-forge "^1.3.1"
+ pretty-bytes "^5.6.0"
+ zod "^3.25.67"
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
@@ -336,6 +350,144 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
+"@inquirer/checkbox@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-3.0.1.tgz#0a57f704265f78c36e17f07e421b98efb4b9867b"
+ integrity sha512-0hm2nrToWUdD6/UHnel/UKGdk1//ke5zGUpHIvk5ZWmaKezlGxZkOJXNSWsdxO/rEqTkbB3lNC2J6nBElV2aAQ==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/figures" "^1.0.6"
+ "@inquirer/type" "^2.0.0"
+ ansi-escapes "^4.3.2"
+ yoctocolors-cjs "^2.1.2"
+
+"@inquirer/confirm@^4.0.1":
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-4.0.1.tgz#9106d6bffa0b2fdd0e4f60319b6f04f2e06e6e25"
+ integrity sha512-46yL28o2NJ9doViqOy0VDcoTzng7rAb6yPQKU7VDLqkmbCaH4JqK4yk4XqlzNWy9PVC5pG1ZUXPBQv+VqnYs2w==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/type" "^2.0.0"
+
+"@inquirer/core@^9.2.1":
+ version "9.2.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-9.2.1.tgz#677c49dee399c9063f31e0c93f0f37bddc67add1"
+ integrity sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==
+ dependencies:
+ "@inquirer/figures" "^1.0.6"
+ "@inquirer/type" "^2.0.0"
+ "@types/mute-stream" "^0.0.4"
+ "@types/node" "^22.5.5"
+ "@types/wrap-ansi" "^3.0.0"
+ ansi-escapes "^4.3.2"
+ cli-width "^4.1.0"
+ mute-stream "^1.0.0"
+ signal-exit "^4.1.0"
+ strip-ansi "^6.0.1"
+ wrap-ansi "^6.2.0"
+ yoctocolors-cjs "^2.1.2"
+
+"@inquirer/editor@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-3.0.1.tgz#d109f21e050af6b960725388cb1c04214ed7c7bc"
+ integrity sha512-VA96GPFaSOVudjKFraokEEmUQg/Lub6OXvbIEZU1SDCmBzRkHGhxoFAVaF30nyiB4m5cEbDgiI2QRacXZ2hw9Q==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/type" "^2.0.0"
+ external-editor "^3.1.0"
+
+"@inquirer/expand@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-3.0.1.tgz#aed9183cac4d12811be47a4a895ea8e82a17e22c"
+ integrity sha512-ToG8d6RIbnVpbdPdiN7BCxZGiHOTomOX94C2FaT5KOHupV40tKEDozp12res6cMIfRKrXLJyexAZhWVHgbALSQ==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/type" "^2.0.0"
+ yoctocolors-cjs "^2.1.2"
+
+"@inquirer/figures@^1.0.6":
+ version "1.0.13"
+ resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.13.tgz#ad0afd62baab1c23175115a9b62f511b6a751e45"
+ integrity sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==
+
+"@inquirer/input@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-3.0.1.tgz#de63d49e516487388508d42049deb70f2cb5f28e"
+ integrity sha512-BDuPBmpvi8eMCxqC5iacloWqv+5tQSJlUafYWUe31ow1BVXjW2a5qe3dh4X/Z25Wp22RwvcaLCc2siHobEOfzg==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/type" "^2.0.0"
+
+"@inquirer/number@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-2.0.1.tgz#b9863080d02ab7dc2e56e16433d83abea0f2a980"
+ integrity sha512-QpR8jPhRjSmlr/mD2cw3IR8HRO7lSVOnqUvQa8scv1Lsr3xoAMMworcYW3J13z3ppjBFBD2ef1Ci6AE5Qn8goQ==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/type" "^2.0.0"
+
+"@inquirer/password@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-3.0.1.tgz#2a9a9143591088336bbd573bcb05d5bf080dbf87"
+ integrity sha512-haoeEPUisD1NeE2IanLOiFr4wcTXGWrBOyAyPZi1FfLJuXOzNmxCJPgUrGYKVh+Y8hfGJenIfz5Wb/DkE9KkMQ==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/type" "^2.0.0"
+ ansi-escapes "^4.3.2"
+
+"@inquirer/prompts@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-6.0.1.tgz#43f5c0ed35c5ebfe52f1d43d46da2d363d950071"
+ integrity sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==
+ dependencies:
+ "@inquirer/checkbox" "^3.0.1"
+ "@inquirer/confirm" "^4.0.1"
+ "@inquirer/editor" "^3.0.1"
+ "@inquirer/expand" "^3.0.1"
+ "@inquirer/input" "^3.0.1"
+ "@inquirer/number" "^2.0.1"
+ "@inquirer/password" "^3.0.1"
+ "@inquirer/rawlist" "^3.0.1"
+ "@inquirer/search" "^2.0.1"
+ "@inquirer/select" "^3.0.1"
+
+"@inquirer/rawlist@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-3.0.1.tgz#729def358419cc929045f264131878ed379e0af3"
+ integrity sha512-VgRtFIwZInUzTiPLSfDXK5jLrnpkuSOh1ctfaoygKAdPqjcjKYmGh6sCY1pb0aGnCGsmhUxoqLDUAU0ud+lGXQ==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/type" "^2.0.0"
+ yoctocolors-cjs "^2.1.2"
+
+"@inquirer/search@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-2.0.1.tgz#69b774a0a826de2e27b48981d01bc5ad81e73721"
+ integrity sha512-r5hBKZk3g5MkIzLVoSgE4evypGqtOannnB3PKTG9NRZxyFRKcfzrdxXXPcoJQsxJPzvdSU2Rn7pB7lw0GCmGAg==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/figures" "^1.0.6"
+ "@inquirer/type" "^2.0.0"
+ yoctocolors-cjs "^2.1.2"
+
+"@inquirer/select@^3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-3.0.1.tgz#1df9ed27fb85a5f526d559ac5ce7cc4e9dc4e7ec"
+ integrity sha512-lUDGUxPhdWMkN/fHy1Lk7pF3nK1fh/gqeyWXmctefhxLYxlDsc7vsPBEpxrfVGDsVdyYJsiJoD4bJ1b623cV1Q==
+ dependencies:
+ "@inquirer/core" "^9.2.1"
+ "@inquirer/figures" "^1.0.6"
+ "@inquirer/type" "^2.0.0"
+ ansi-escapes "^4.3.2"
+ yoctocolors-cjs "^2.1.2"
+
+"@inquirer/type@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-2.0.0.tgz#08fa513dca2cb6264fe1b0a2fabade051444e3f6"
+ integrity sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==
+ dependencies:
+ mute-stream "^1.0.0"
+
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@@ -725,6 +877,13 @@
dependencies:
"@types/node" "*"
+"@types/cors@^2.8.19":
+ version "2.8.19"
+ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.19.tgz#d93ea2673fd8c9f697367f5eeefc2bbfa94f0342"
+ integrity sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==
+ dependencies:
+ "@types/node" "*"
+
"@types/express-serve-static-core@^5.0.0":
version "5.0.7"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz#2fa94879c9d46b11a5df4c74ac75befd6b283de6"
@@ -788,6 +947,13 @@
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
+"@types/mute-stream@^0.0.4":
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478"
+ integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==
+ dependencies:
+ "@types/node" "*"
+
"@types/node@*":
version "22.15.17"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.17.tgz#355ccec95f705b664e4332bb64a7f07db30b7055"
@@ -795,7 +961,14 @@
dependencies:
undici-types "~6.21.0"
-"@types/qs@*":
+"@types/node@^22.5.5":
+ version "22.18.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.0.tgz#9e4709be4f104e3568f7dd1c71e2949bf147a47b"
+ integrity sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==
+ dependencies:
+ undici-types "~6.21.0"
+
+"@types/qs@*", "@types/qs@^6.14.0":
version "6.14.0"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5"
integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==
@@ -827,6 +1000,11 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
+"@types/wrap-ansi@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd"
+ integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==
+
"@types/yargs-parser@*":
version "21.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
@@ -925,6 +1103,11 @@
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8"
integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
+"@valtown/deno-http-worker@^0.0.21":
+ version "0.0.21"
+ resolved "https://registry.yarnpkg.com/@valtown/deno-http-worker/-/deno-http-worker-0.0.21.tgz#9ce3b5c1d0db211fe7ea8297881fe551838474ad"
+ integrity sha512-16kFuUykann75lNytnXXIQlmpzreZjzdyT27ebT3yNGCS3kKaS1iZYWHc3Si9An54Cphwr4qEcviChQkEeJBlA==
+
accepts@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895"
@@ -968,7 +1151,7 @@ ajv@^6.12.4, ajv@^6.12.6:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
-ansi-escapes@^4.2.1:
+ansi-escapes@^4.2.1, ansi-escapes@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
@@ -1210,6 +1393,11 @@ char-regex@^1.0.2:
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
+chardet@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+ integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+
ci-info@^3.2.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
@@ -1225,6 +1413,11 @@ clean-stack@^2.0.0:
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
+cli-width@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5"
+ integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==
+
cliui@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
@@ -1261,6 +1454,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+commander@^13.1.0:
+ version "13.1.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46"
+ integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==
+
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -1673,6 +1871,15 @@ express@^5.0.1, express@^5.1.0:
type-is "^2.0.1"
vary "^1.1.2"
+external-editor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+ integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+ dependencies:
+ chardet "^0.7.0"
+ iconv-lite "^0.4.24"
+ tmp "^0.0.33"
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -1718,6 +1925,11 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
+fflate@^0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
+ integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
+
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -1781,6 +1993,14 @@ flatted@^3.2.9:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358"
integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
+flora-colossus@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-2.0.0.tgz#af1e85db0a8256ef05f3fb531c1235236c97220a"
+ integrity sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==
+ dependencies:
+ debug "^4.3.4"
+ fs-extra "^10.1.0"
+
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -1791,6 +2011,15 @@ fresh@^2.0.0:
resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4"
integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==
+fs-extra@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
+ integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -1806,6 +2035,15 @@ function-bind@^1.1.2:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+galactus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/galactus/-/galactus-1.0.0.tgz#c2615182afa0c6d0859b92e56ae36d052827db7e"
+ integrity sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==
+ dependencies:
+ debug "^4.3.4"
+ flora-colossus "^2.0.0"
+ fs-extra "^10.1.0"
+
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -1898,7 +2136,7 @@ gopd@^1.2.0:
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
-graceful-fs@^4.2.9:
+graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -1953,11 +2191,23 @@ iconv-lite@0.6.3, iconv-lite@^0.6.3:
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"
+iconv-lite@^0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
ignore@^5.2.0, ignore@^5.3.1:
version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+ignore@^7.0.5:
+ version "7.0.5"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9"
+ integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
+
import-fresh@^3.2.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
@@ -2536,6 +2786,15 @@ json5@^2.2.2, json5@^2.2.3:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+jsonfile@^6.0.1:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62"
+ integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==
+ dependencies:
+ universalify "^2.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
keyv@^4.5.3:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
@@ -2709,6 +2968,11 @@ ms@^2.1.3:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+mute-stream@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e"
+ integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==
+
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -2719,6 +2983,11 @@ negotiator@^1.0.0:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a"
integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==
+node-forge@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
+ integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
+
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -2784,6 +3053,11 @@ optionator@^0.9.3:
type-check "^0.4.0"
word-wrap "^1.2.5"
+os-tmpdir@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
+
p-all@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-all/-/p-all-3.0.0.tgz#077c023c37e75e760193badab2bad3ccd5782bfb"
@@ -2927,6 +3201,11 @@ prettier@^3.0.0:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5"
integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==
+pretty-bytes@^5.6.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
+ integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
+
pretty-format@^29.0.0, pretty-format@^29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
@@ -3074,7 +3353,7 @@ safe-buffer@5.2.1, safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-"safer-buffer@>= 2.1.2 < 3.0.0":
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -3178,6 +3457,11 @@ signal-exit@^3.0.3, signal-exit@^3.0.7:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+signal-exit@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+ integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@@ -3322,6 +3606,13 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+tmp@^0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ dependencies:
+ os-tmpdir "~1.0.2"
+
tmpl@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
@@ -3387,9 +3678,9 @@ ts-node@^10.5.0:
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
-"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz":
- version "1.1.8"
- resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.8/tsc-multi.tgz#f544b359b8f05e607771ffacc280e58201476b04"
+"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz":
+ version "1.1.9"
+ resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz#777f6f5d9e26bf0e94e5170990dd3a841d6707cd"
dependencies:
debug "^4.3.7"
fast-glob "^3.3.2"
@@ -3462,6 +3753,11 @@ undici-types@~6.21.0:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
+universalify@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+ integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
unpipe@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@@ -3525,6 +3821,15 @@ word-wrap@^1.2.5:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+wrap-ansi@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -3585,6 +3890,11 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+yoctocolors-cjs@^2.1.2:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa"
+ integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==
+
zod-to-json-schema@^3.24.1, zod-to-json-schema@^3.24.5:
version "3.24.5"
resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3"
@@ -3600,7 +3910,7 @@ zod@^3.23.8:
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.4.tgz#e2e2cca5faaa012d76e527d0d36622e0a90c315f"
integrity sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==
-zod@^3.25.20:
+zod@^3.25.20, zod@^3.25.67:
version "3.25.76"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34"
integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
index 54c2860..35e2b8f 100755
--- a/scripts/utils/upload-artifact.sh
+++ b/scripts/utils/upload-artifact.sh
@@ -12,7 +12,7 @@ if [[ "$SIGNED_URL" == "null" ]]; then
exit 1
fi
-UPLOAD_RESPONSE=$(tar -cz "${BUILD_PATH:-dist}" | curl -v -X PUT \
+UPLOAD_RESPONSE=$(tar "${BASE_PATH:+-C$BASE_PATH}" -cz "${ARTIFACT_PATH:-dist}" | curl -v -X PUT \
-H "Content-Type: application/gzip" \
--data-binary @- "$SIGNED_URL" 2>&1)
diff --git a/src/client.ts b/src/client.ts
index f5ad48f..d9bc8cf 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -20,22 +20,28 @@ import * as Uploads from './core/uploads';
import * as API from './resources/index';
import { APIPromise } from './core/api-promise';
import { Account, AccountListResponse, Accounts } from './resources/accounts';
-import { App, AppOpenParams, AppOpenResponse } from './resources/app';
+import {
+ App,
+ AppDownloadAssetParams,
+ AppDownloadAssetResponse,
+ AppOpenParams,
+ AppOpenResponse,
+ AppSearchParams,
+ AppSearchResponse,
+} from './resources/app';
+import { ContactSearchParams, ContactSearchResponse, Contacts } from './resources/contacts';
+import { MessageSearchParams, MessageSendParams, MessageSendResponse, Messages } from './resources/messages';
import { RevokeRequest, Token, UserInfo } from './resources/token';
import {
Chat,
ChatArchiveParams,
+ ChatCreateParams,
+ ChatCreateResponse,
ChatRetrieveParams,
ChatSearchParams,
Chats,
ChatsCursor,
} from './resources/chats/chats';
-import {
- MessageSearchParams,
- MessageSendParams,
- MessageSendResponse,
- Messages,
-} from './resources/messages/messages';
import { type Fetch } from './internal/builtin-types';
import { isRunningInBrowser } from './internal/detect-platform';
import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers';
@@ -766,6 +772,10 @@ export class BeeperDesktop {
* App operations
*/
app: API.App = new API.App(this);
+ /**
+ * Contacts operations
+ */
+ contacts: API.Contacts = new API.Contacts(this);
/**
* Chats operations
*/
@@ -782,6 +792,7 @@ export class BeeperDesktop {
BeeperDesktop.Accounts = Accounts;
BeeperDesktop.App = App;
+BeeperDesktop.Contacts = Contacts;
BeeperDesktop.Chats = Chats;
BeeperDesktop.Messages = Messages;
BeeperDesktop.Token = Token;
@@ -794,12 +805,28 @@ export declare namespace BeeperDesktop {
export { Accounts as Accounts, type Account as Account, type AccountListResponse as AccountListResponse };
- export { App as App, type AppOpenResponse as AppOpenResponse, type AppOpenParams as AppOpenParams };
+ export {
+ App as App,
+ type AppDownloadAssetResponse as AppDownloadAssetResponse,
+ type AppOpenResponse as AppOpenResponse,
+ type AppSearchResponse as AppSearchResponse,
+ type AppDownloadAssetParams as AppDownloadAssetParams,
+ type AppOpenParams as AppOpenParams,
+ type AppSearchParams as AppSearchParams,
+ };
+
+ export {
+ Contacts as Contacts,
+ type ContactSearchResponse as ContactSearchResponse,
+ type ContactSearchParams as ContactSearchParams,
+ };
export {
Chats as Chats,
type Chat as Chat,
+ type ChatCreateResponse as ChatCreateResponse,
type ChatsCursor as ChatsCursor,
+ type ChatCreateParams as ChatCreateParams,
type ChatRetrieveParams as ChatRetrieveParams,
type ChatArchiveParams as ChatArchiveParams,
type ChatSearchParams as ChatSearchParams,
diff --git a/src/internal/utils/values.ts b/src/internal/utils/values.ts
index 2889032..67775aa 100644
--- a/src/internal/utils/values.ts
+++ b/src/internal/utils/values.ts
@@ -76,21 +76,21 @@ export const coerceBoolean = (value: unknown): boolean => {
};
export const maybeCoerceInteger = (value: unknown): number | undefined => {
- if (value === undefined) {
+ if (value == null) {
return undefined;
}
return coerceInteger(value);
};
export const maybeCoerceFloat = (value: unknown): number | undefined => {
- if (value === undefined) {
+ if (value == null) {
return undefined;
}
return coerceFloat(value);
};
export const maybeCoerceBoolean = (value: unknown): boolean | undefined => {
- if (value === undefined) {
+ if (value == null) {
return undefined;
}
return coerceBoolean(value);
diff --git a/src/resources/app.ts b/src/resources/app.ts
index 42c4dfe..a7991cf 100644
--- a/src/resources/app.ts
+++ b/src/resources/app.ts
@@ -1,6 +1,8 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import { APIResource } from '../core/resource';
+import * as Shared from './shared';
+import * as ChatsAPI from './chats/chats';
import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
@@ -8,9 +10,27 @@ import { RequestOptions } from '../internal/request-options';
* App operations
*/
export class App extends APIResource {
+ /**
+ * Download a Matrix asset using its mxc:// or localmxc:// URL and return the local
+ * file URL.
+ *
+ * @example
+ * ```ts
+ * const response = await client.app.downloadAsset({
+ * url: 'x',
+ * });
+ * ```
+ */
+ downloadAsset(
+ body: AppDownloadAssetParams,
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.post('/v0/download-asset', { body, ...options });
+ }
+
/**
* Open Beeper Desktop and optionally navigate to a specific chat, message, or
- * pre-fill draft text
+ * pre-fill draft text and attachment.
*
* @example
* ```ts
@@ -20,6 +40,32 @@ export class App extends APIResource {
open(body: AppOpenParams | null | undefined = {}, options?: RequestOptions): APIPromise {
return this._client.post('/v0/open-app', { body, ...options });
}
+
+ /**
+ * Returns matching chats, participant name matches in groups, and the first page
+ * of messages in one call. Paginate messages via search-messages. Paginate chats
+ * via search-chats. Uses the same sorting as the chat search in the app.
+ *
+ * @example
+ * ```ts
+ * const response = await client.app.search({ query: 'x' });
+ * ```
+ */
+ search(query: AppSearchParams, options?: RequestOptions): APIPromise {
+ return this._client.get('/v0/search', { query, ...options });
+ }
+}
+
+export interface AppDownloadAssetResponse {
+ /**
+ * Error message if the download failed.
+ */
+ error?: string;
+
+ /**
+ * Local file URL to the downloaded asset.
+ */
+ srcURL?: string;
}
/**
@@ -32,6 +78,64 @@ export interface AppOpenResponse {
success: boolean;
}
+export interface AppSearchResponse {
+ results: AppSearchResponse.Results;
+}
+
+export namespace AppSearchResponse {
+ export interface Results {
+ /**
+ * Top chat results.
+ */
+ chats: Array;
+
+ /**
+ * Top group results by participant matches.
+ */
+ in_groups: Array;
+
+ messages: Results.Messages;
+ }
+
+ export namespace Results {
+ export interface Messages {
+ /**
+ * Map of chatID -> chat details for chats referenced in items.
+ */
+ chats: { [key: string]: ChatsAPI.Chat };
+
+ /**
+ * True if additional results can be fetched using the provided cursors.
+ */
+ hasMore: boolean;
+
+ /**
+ * Messages matching the query and filters.
+ */
+ items: Array;
+
+ /**
+ * Cursor for fetching newer results (use with direction='after'). Opaque string;
+ * do not inspect.
+ */
+ newestCursor: string | null;
+
+ /**
+ * Cursor for fetching older results (use with direction='before'). Opaque string;
+ * do not inspect.
+ */
+ oldestCursor: string | null;
+ }
+ }
+}
+
+export interface AppDownloadAssetParams {
+ /**
+ * Matrix content URL (mxc:// or localmxc://) for the asset to download.
+ */
+ url: string;
+}
+
export interface AppOpenParams {
/**
* Optional Beeper chat ID (or local chat ID) to focus after opening the app. If
@@ -39,17 +143,36 @@ export interface AppOpenParams {
*/
chatID?: string;
+ /**
+ * Optional draft attachment path to populate in the message input field.
+ */
+ draftAttachmentPath?: string;
+
/**
* Optional draft text to populate in the message input field.
*/
draftText?: string;
/**
- * Optional message sort key. Jumps to that message in the chat when opening.
+ * Optional message ID. Jumps to that message in the chat when opening.
+ */
+ messageID?: string;
+}
+
+export interface AppSearchParams {
+ /**
+ * User-typed search text. Literal word matching (NOT semantic).
*/
- messageSortKey?: string;
+ query: string;
}
export declare namespace App {
- export { type AppOpenResponse as AppOpenResponse, type AppOpenParams as AppOpenParams };
+ export {
+ type AppDownloadAssetResponse as AppDownloadAssetResponse,
+ type AppOpenResponse as AppOpenResponse,
+ type AppSearchResponse as AppSearchResponse,
+ type AppDownloadAssetParams as AppDownloadAssetParams,
+ type AppOpenParams as AppOpenParams,
+ type AppSearchParams as AppSearchParams,
+ };
}
diff --git a/src/resources/chats/chats.ts b/src/resources/chats/chats.ts
index 062665d..0154f56 100644
--- a/src/resources/chats/chats.ts
+++ b/src/resources/chats/chats.ts
@@ -14,6 +14,24 @@ import { RequestOptions } from '../../internal/request-options';
export class Chats extends APIResource {
reminders: RemindersAPI.Reminders = new RemindersAPI.Reminders(this._client);
+ /**
+ * Create a single or group chat on a specific account using participant IDs and
+ * optional title.
+ *
+ * @example
+ * ```ts
+ * const chat = await client.chats.create({
+ * accountID:
+ * 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
+ * participantIDs: ['string'],
+ * type: 'single',
+ * });
+ * ```
+ */
+ create(body: ChatCreateParams, options?: RequestOptions): APIPromise {
+ return this._client.post('/v0/create-chat', { body, ...options });
+ }
+
/**
* Retrieve chat details including metadata, participants, and latest message
*
@@ -44,7 +62,8 @@ export class Chats extends APIResource {
}
/**
- * Search and filter conversations across all messaging accounts
+ * Search chats by title/network or participants using Beeper Desktop's renderer
+ * algorithm.
*
* @example
* ```ts
@@ -155,6 +174,41 @@ export namespace Chat {
}
}
+export interface ChatCreateResponse extends Shared.BaseResponse {
+ /**
+ * Newly created chat if available.
+ */
+ chatID?: string;
+}
+
+export interface ChatCreateParams {
+ /**
+ * Account to create the chat on.
+ */
+ accountID: string;
+
+ /**
+ * User IDs to include in the new chat.
+ */
+ participantIDs: Array;
+
+ /**
+ * Chat type to create: 'single' requires exactly one participantID; 'group'
+ * supports multiple participants and optional title.
+ */
+ type: 'single' | 'group';
+
+ /**
+ * Optional first message content if the platform requires it to create the chat.
+ */
+ messageText?: string;
+
+ /**
+ * Optional title for group chats; ignored for single chats on most platforms.
+ */
+ title?: string;
+}
+
export interface ChatRetrieveParams {
/**
* Unique identifier of the chat to retrieve. Not available for iMessage chats.
@@ -199,7 +253,7 @@ export interface ChatSearchParams extends CursorParams {
* Include chats marked as Muted by the user, which are usually less important.
* Default: true. Set to false if the user wants a more refined search.
*/
- includeMuted?: boolean;
+ includeMuted?: boolean | null;
/**
* Provide an ISO datetime string to only retrieve chats with last activity after
@@ -214,28 +268,27 @@ export interface ChatSearchParams extends CursorParams {
lastActivityBefore?: string;
/**
- * Search string to filter chats by participant names. When multiple words
- * provided, ALL words must match. Searches in username, displayName, and fullName
- * fields.
+ * Literal token search (non-semantic). Use single words users type (e.g.,
+ * "dinner"). When multiple words provided, ALL must match. Case-insensitive.
*/
- participantQuery?: string;
+ query?: string;
/**
- * Search string to filter chats by title. When multiple words provided, ALL words
- * must match. Matches are case-insensitive substrings.
+ * Search scope: 'titles' matches title + network; 'participants' matches
+ * participant names.
*/
- query?: string;
+ scope?: 'titles' | 'participants';
/**
* Specify the type of chats to retrieve: use "single" for direct messages, "group"
- * for group chats, "channel" for channels, or "any" to get all types
+ * for group chats, or "any" to get all types
*/
- type?: 'single' | 'group' | 'channel' | 'any';
+ type?: 'single' | 'group' | 'any';
/**
* Set to true to only retrieve chats that have unread messages
*/
- unreadOnly?: boolean;
+ unreadOnly?: boolean | null;
}
Chats.Reminders = Reminders;
@@ -243,7 +296,9 @@ Chats.Reminders = Reminders;
export declare namespace Chats {
export {
type Chat as Chat,
+ type ChatCreateResponse as ChatCreateResponse,
type ChatsCursor as ChatsCursor,
+ type ChatCreateParams as ChatCreateParams,
type ChatRetrieveParams as ChatRetrieveParams,
type ChatArchiveParams as ChatArchiveParams,
type ChatSearchParams as ChatSearchParams,
diff --git a/src/resources/chats/index.ts b/src/resources/chats/index.ts
index 11ac515..ab2b155 100644
--- a/src/resources/chats/index.ts
+++ b/src/resources/chats/index.ts
@@ -3,6 +3,8 @@
export {
Chats,
type Chat,
+ type ChatCreateResponse,
+ type ChatCreateParams,
type ChatRetrieveParams,
type ChatArchiveParams,
type ChatSearchParams,
diff --git a/src/resources/contacts.ts b/src/resources/contacts.ts
new file mode 100644
index 0000000..b7b0f4b
--- /dev/null
+++ b/src/resources/contacts.ts
@@ -0,0 +1,42 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import * as Shared from './shared';
+import { APIPromise } from '../core/api-promise';
+import { RequestOptions } from '../internal/request-options';
+
+/**
+ * Contacts operations
+ */
+export class Contacts extends APIResource {
+ /**
+ * Search users across on a specific account using the network's search API. Only
+ * use for creating new chats.
+ */
+ search(query: ContactSearchParams, options?: RequestOptions): APIPromise {
+ return this._client.get('/v0/search-users', { query, ...options });
+ }
+}
+
+export interface ContactSearchResponse {
+ items: Array;
+}
+
+export interface ContactSearchParams {
+ /**
+ * Beeper account ID this resource belongs to.
+ */
+ accountID: string;
+
+ /**
+ * Text to search users by. Network-specific behavior.
+ */
+ query: string;
+}
+
+export declare namespace Contacts {
+ export {
+ type ContactSearchResponse as ContactSearchResponse,
+ type ContactSearchParams as ContactSearchParams,
+ };
+}
diff --git a/src/resources/index.ts b/src/resources/index.ts
index f5869db..4d7984c 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -2,19 +2,30 @@
export * from './shared';
export { Accounts, type Account, type AccountListResponse } from './accounts';
-export { App, type AppOpenResponse, type AppOpenParams } from './app';
+export {
+ App,
+ type AppDownloadAssetResponse,
+ type AppOpenResponse,
+ type AppSearchResponse,
+ type AppDownloadAssetParams,
+ type AppOpenParams,
+ type AppSearchParams,
+} from './app';
export {
Chats,
type Chat,
+ type ChatCreateResponse,
+ type ChatCreateParams,
type ChatRetrieveParams,
type ChatArchiveParams,
type ChatSearchParams,
type ChatsCursor,
} from './chats/chats';
+export { Contacts, type ContactSearchResponse, type ContactSearchParams } from './contacts';
export {
Messages,
type MessageSendResponse,
type MessageSearchParams,
type MessageSendParams,
-} from './messages/messages';
+} from './messages';
export { Token, type RevokeRequest, type UserInfo } from './token';
diff --git a/src/resources/messages.ts b/src/resources/messages.ts
index eb17523..32dff27 100644
--- a/src/resources/messages.ts
+++ b/src/resources/messages.ts
@@ -1,3 +1,146 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-export * from './messages/index';
+import { APIResource } from '../core/resource';
+import * as Shared from './shared';
+import { MessagesCursor } from './shared';
+import { APIPromise } from '../core/api-promise';
+import { Cursor, type CursorParams, PagePromise } from '../core/pagination';
+import { RequestOptions } from '../internal/request-options';
+
+/**
+ * Messages operations
+ */
+export class Messages extends APIResource {
+ /**
+ * Search messages across chats using Beeper's message index
+ *
+ * @example
+ * ```ts
+ * // Automatically fetches more pages as needed.
+ * for await (const message of client.messages.search()) {
+ * // ...
+ * }
+ * ```
+ */
+ search(
+ query: MessageSearchParams | null | undefined = {},
+ options?: RequestOptions,
+ ): PagePromise {
+ return this._client.getAPIList('/v0/search-messages', Cursor, { query, ...options });
+ }
+
+ /**
+ * Send a text message to a specific chat. Supports replying to existing messages.
+ * Returns the sent message ID.
+ *
+ * @example
+ * ```ts
+ * const response = await client.messages.send({
+ * chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * });
+ * ```
+ */
+ send(body: MessageSendParams, options?: RequestOptions): APIPromise {
+ return this._client.post('/v0/send-message', { body, ...options });
+ }
+}
+
+export interface MessageSendResponse extends Shared.BaseResponse {
+ /**
+ * Unique identifier of the chat (a.k.a. room or thread).
+ */
+ chatID: string;
+
+ /**
+ * Pending message ID
+ */
+ pendingMessageID: string;
+}
+
+export interface MessageSearchParams extends CursorParams {
+ /**
+ * Limit search to specific Beeper account IDs (bridge instances).
+ */
+ accountIDs?: Array;
+
+ /**
+ * Limit search to specific Beeper chat IDs.
+ */
+ chatIDs?: Array;
+
+ /**
+ * Filter by chat type: 'group' for group chats, 'single' for 1:1 chats.
+ */
+ chatType?: 'group' | 'single';
+
+ /**
+ * Only include messages with timestamp strictly after this ISO 8601 datetime
+ * (e.g., '2024-07-01T00:00:00Z' or '2024-07-01T00:00:00+02:00').
+ */
+ dateAfter?: string;
+
+ /**
+ * Only include messages with timestamp strictly before this ISO 8601 datetime
+ * (e.g., '2024-07-31T23:59:59Z' or '2024-07-31T23:59:59+02:00').
+ */
+ dateBefore?: string;
+
+ /**
+ * Exclude messages marked Low Priority by the user. Default: true. Set to false to
+ * include all.
+ */
+ excludeLowPriority?: boolean | null;
+
+ /**
+ * Include messages in chats marked as Muted by the user, which are usually less
+ * important. Default: true. Set to false if the user wants a more refined search.
+ */
+ includeMuted?: boolean | null;
+
+ /**
+ * Filter messages by media types. Use ['any'] for any media type, or specify exact
+ * types like ['video', 'image']. Omit for no media filtering.
+ */
+ mediaTypes?: Array<'any' | 'video' | 'image' | 'link' | 'file'>;
+
+ /**
+ * Literal word search (NOT semantic). Finds messages containing these EXACT words
+ * in any order. Use single words users actually type, not concepts or phrases.
+ * Example: use "dinner" not "dinner plans", use "sick" not "health issues". If
+ * omitted, returns results filtered only by other parameters.
+ */
+ query?: string;
+
+ /**
+ * Filter by sender: 'me' (messages sent by the authenticated user), 'others'
+ * (messages sent by others), or a specific user ID string (user.id).
+ */
+ sender?: 'me' | 'others' | (string & {});
+}
+
+export interface MessageSendParams {
+ /**
+ * Unique identifier of the chat (a.k.a. room or thread).
+ */
+ chatID: string;
+
+ /**
+ * Provide a message ID to send this as a reply to an existing message
+ */
+ replyToMessageID?: string;
+
+ /**
+ * Text content of the message you want to send. You may use markdown.
+ */
+ text?: string;
+}
+
+export declare namespace Messages {
+ export {
+ type MessageSendResponse as MessageSendResponse,
+ type MessageSearchParams as MessageSearchParams,
+ type MessageSendParams as MessageSendParams,
+ };
+}
+
+export { type MessagesCursor };
diff --git a/src/resources/messages/attachments.ts b/src/resources/messages/attachments.ts
deleted file mode 100644
index 1b425b6..0000000
--- a/src/resources/messages/attachments.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import { APIResource } from '../../core/resource';
-import { APIPromise } from '../../core/api-promise';
-import { RequestOptions } from '../../internal/request-options';
-
-/**
- * Attachments operations
- */
-export class Attachments extends APIResource {
- /**
- * Download an attachment from a message and return the local file path
- *
- * @example
- * ```ts
- * const response = await client.messages.attachments.download(
- * {
- * chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
- * messageID: 'messageID',
- * },
- * );
- * ```
- */
- download(body: AttachmentDownloadParams, options?: RequestOptions): APIPromise {
- return this._client.post('/v0/download-attachment', { body, ...options });
- }
-}
-
-export interface AttachmentDownloadResponse {
- /**
- * Whether the attachment was successfully downloaded.
- */
- success: boolean;
-
- /**
- * Error message if the download failed.
- */
- error?: string;
-
- /**
- * Local file system path to the downloaded attachment.
- */
- filePath?: string;
-}
-
-export interface AttachmentDownloadParams {
- /**
- * Unique identifier of the chat (supports both chatID and localChatID).
- */
- chatID: string;
-
- /**
- * The message ID (eventID) containing the attachment.
- */
- messageID: string;
-}
-
-export declare namespace Attachments {
- export {
- type AttachmentDownloadResponse as AttachmentDownloadResponse,
- type AttachmentDownloadParams as AttachmentDownloadParams,
- };
-}
diff --git a/src/resources/messages/index.ts b/src/resources/messages/index.ts
deleted file mode 100644
index 6ff60fc..0000000
--- a/src/resources/messages/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-export { Attachments, type AttachmentDownloadResponse, type AttachmentDownloadParams } from './attachments';
-export {
- Messages,
- type MessageSendResponse,
- type MessageSearchParams,
- type MessageSendParams,
-} from './messages';
diff --git a/src/resources/messages/messages.ts b/src/resources/messages/messages.ts
deleted file mode 100644
index f99677e..0000000
--- a/src/resources/messages/messages.ts
+++ /dev/null
@@ -1,173 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-import { APIResource } from '../../core/resource';
-import * as Shared from '../shared';
-import { MessagesCursor } from '../shared';
-import * as AttachmentsAPI from './attachments';
-import { AttachmentDownloadParams, AttachmentDownloadResponse, Attachments } from './attachments';
-import { APIPromise } from '../../core/api-promise';
-import { Cursor, type CursorParams, PagePromise } from '../../core/pagination';
-import { RequestOptions } from '../../internal/request-options';
-
-/**
- * Messages operations
- */
-export class Messages extends APIResource {
- attachments: AttachmentsAPI.Attachments = new AttachmentsAPI.Attachments(this._client);
-
- /**
- * Search messages across chats using Beeper's message index
- *
- * @example
- * ```ts
- * // Automatically fetches more pages as needed.
- * for await (const message of client.messages.search()) {
- * // ...
- * }
- * ```
- */
- search(
- query: MessageSearchParams | null | undefined = {},
- options?: RequestOptions,
- ): PagePromise {
- return this._client.getAPIList('/v0/search-messages', Cursor, { query, ...options });
- }
-
- /**
- * Send a text message to a specific chat. Supports replying to existing messages.
- * Returns the sent message ID.
- *
- * @example
- * ```ts
- * const response = await client.messages.send({
- * chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
- * });
- * ```
- */
- send(body: MessageSendParams, options?: RequestOptions): APIPromise {
- return this._client.post('/v0/send-message', { body, ...options });
- }
-}
-
-export interface MessageSendResponse extends Shared.BaseResponse {
- /**
- * Stable message ID.
- */
- messageID: string;
-}
-
-export interface MessageSearchParams extends CursorParams {
- /**
- * Limit search to specific Beeper account IDs (bridge instances).
- */
- accountIDs?: Array;
-
- /**
- * Limit search to specific Beeper chat IDs.
- */
- chatIDs?: Array;
-
- /**
- * Filter by chat type: 'group' for group chats, 'single' for 1:1 chats.
- */
- chatType?: 'group' | 'single';
-
- /**
- * Only include messages with timestamp strictly after this ISO 8601 datetime
- * (e.g., '2024-07-01T00:00:00Z' or '2024-07-01T00:00:00+02:00').
- */
- dateAfter?: string;
-
- /**
- * Only include messages with timestamp strictly before this ISO 8601 datetime
- * (e.g., '2024-07-31T23:59:59Z' or '2024-07-31T23:59:59+02:00').
- */
- dateBefore?: string;
-
- /**
- * Exclude messages marked Low Priority by the user. Default: true. Set to false to
- * include all.
- */
- excludeLowPriority?: boolean;
-
- /**
- * Include messages in chats marked as Muted by the user, which are usually less
- * important. Default: true. Set to false if the user wants a more refined search.
- */
- includeMuted?: boolean;
-
- /**
- * Only return messages that contain file attachments.
- */
- onlyWithFile?: boolean;
-
- /**
- * Only return messages that contain image attachments.
- */
- onlyWithImage?: boolean;
-
- /**
- * Only return messages that contain link attachments.
- */
- onlyWithLink?: boolean;
-
- /**
- * Only return messages that contain any type of media attachment.
- */
- onlyWithMedia?: boolean;
-
- /**
- * Only return messages that contain video attachments.
- */
- onlyWithVideo?: boolean;
-
- /**
- * Literal word search (NOT semantic). Finds messages containing these EXACT words
- * in any order. Use single words users actually type, not concepts or phrases.
- * Example: use "dinner" not "dinner plans", use "sick" not "health issues". If
- * omitted, returns results filtered only by other parameters.
- */
- query?: string;
-
- /**
- * Filter by sender: 'me' (messages sent by the authenticated user), 'others'
- * (messages sent by others), or a specific user ID string (user.id).
- */
- sender?: 'me' | 'others' | (string & {});
-}
-
-export interface MessageSendParams {
- /**
- * The identifier of the chat where the message will send (accepts both chatID and
- * local chat ID)
- */
- chatID: string;
-
- /**
- * Provide a message ID to send this as a reply to an existing message
- */
- replyToMessageID?: string;
-
- /**
- * Text content of the message you want to send. You may use markdown.
- */
- text?: string;
-}
-
-Messages.Attachments = Attachments;
-
-export declare namespace Messages {
- export {
- type MessageSendResponse as MessageSendResponse,
- type MessageSearchParams as MessageSearchParams,
- type MessageSendParams as MessageSendParams,
- };
-
- export {
- Attachments as Attachments,
- type AttachmentDownloadResponse as AttachmentDownloadResponse,
- type AttachmentDownloadParams as AttachmentDownloadParams,
- };
-}
-
-export { type MessagesCursor };
diff --git a/src/version.ts b/src/version.ts
index 28b999e..44ea521 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '0.1.4'; // x-release-please-version
+export const VERSION = '0.1.5'; // x-release-please-version
diff --git a/tests/api-resources/app.test.ts b/tests/api-resources/app.test.ts
index 30a1eb4..0e0a118 100644
--- a/tests/api-resources/app.test.ts
+++ b/tests/api-resources/app.test.ts
@@ -8,6 +8,21 @@ const client = new BeeperDesktop({
});
describe('resource app', () => {
+ test('downloadAsset: only required params', async () => {
+ const responsePromise = client.app.downloadAsset({ url: 'x' });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('downloadAsset: required and optional params', async () => {
+ const response = await client.app.downloadAsset({ url: 'x' });
+ });
+
test('open', async () => {
const responsePromise = client.app.open();
const rawResponse = await responsePromise.asResponse();
@@ -25,11 +40,27 @@ describe('resource app', () => {
client.app.open(
{
chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ draftAttachmentPath: 'draftAttachmentPath',
draftText: 'draftText',
- messageSortKey: 'messageSortKey',
+ messageID: 'messageID',
},
{ path: '/_stainless_unknown_path' },
),
).rejects.toThrow(BeeperDesktop.NotFoundError);
});
+
+ test('search: only required params', async () => {
+ const responsePromise = client.app.search({ query: 'x' });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('search: required and optional params', async () => {
+ const response = await client.app.search({ query: 'x' });
+ });
});
diff --git a/tests/api-resources/chats/chats.test.ts b/tests/api-resources/chats/chats.test.ts
index cbfa64e..162c241 100644
--- a/tests/api-resources/chats/chats.test.ts
+++ b/tests/api-resources/chats/chats.test.ts
@@ -8,6 +8,31 @@ const client = new BeeperDesktop({
});
describe('resource chats', () => {
+ test('create: only required params', async () => {
+ const responsePromise = client.chats.create({
+ accountID: 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
+ participantIDs: ['string'],
+ type: 'single',
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('create: required and optional params', async () => {
+ const response = await client.chats.create({
+ accountID: 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
+ participantIDs: ['string'],
+ type: 'single',
+ messageText: 'messageText',
+ title: 'title',
+ });
+ });
+
test('retrieve: only required params', async () => {
const responsePromise = client.chats.retrieve({ chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com' });
const rawResponse = await responsePromise.asResponse();
@@ -68,8 +93,8 @@ describe('resource chats', () => {
lastActivityAfter: '2019-12-27T18:11:19.117Z',
lastActivityBefore: '2019-12-27T18:11:19.117Z',
limit: 1,
- participantQuery: 'participantQuery',
- query: 'query',
+ query: 'x',
+ scope: 'titles',
type: 'single',
unreadOnly: true,
},
diff --git a/tests/api-resources/messages/attachments.test.ts b/tests/api-resources/contacts.test.ts
similarity index 60%
rename from tests/api-resources/messages/attachments.test.ts
rename to tests/api-resources/contacts.test.ts
index 42125f4..b21cda7 100644
--- a/tests/api-resources/messages/attachments.test.ts
+++ b/tests/api-resources/contacts.test.ts
@@ -7,11 +7,11 @@ const client = new BeeperDesktop({
baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
});
-describe('resource attachments', () => {
- test('download: only required params', async () => {
- const responsePromise = client.messages.attachments.download({
- chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
- messageID: 'messageID',
+describe('resource contacts', () => {
+ test('search: only required params', async () => {
+ const responsePromise = client.contacts.search({
+ accountID: 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
+ query: 'x',
});
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
@@ -22,10 +22,10 @@ describe('resource attachments', () => {
expect(dataAndResponse.response).toBe(rawResponse);
});
- test('download: required and optional params', async () => {
- const response = await client.messages.attachments.download({
- chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
- messageID: 'messageID',
+ test('search: required and optional params', async () => {
+ const response = await client.contacts.search({
+ accountID: 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
+ query: 'x',
});
});
});
diff --git a/tests/api-resources/messages/messages.test.ts b/tests/api-resources/messages.test.ts
similarity index 91%
rename from tests/api-resources/messages/messages.test.ts
rename to tests/api-resources/messages.test.ts
index f62cbd1..21fafff 100644
--- a/tests/api-resources/messages/messages.test.ts
+++ b/tests/api-resources/messages.test.ts
@@ -30,18 +30,14 @@ describe('resource messages', () => {
],
chatIDs: ['!NCdzlIaMjZUmvmvyHU:beeper.com', '1231073'],
chatType: 'group',
- cursor: 'eyJvZmZzZXQiOjE3MTk5OTk5OTl9',
+ cursor: '1725489123456|c29tZUltc2dQYWdl',
dateAfter: '2025-08-01T00:00:00Z',
dateBefore: '2025-08-31T23:59:59Z',
direction: 'before',
excludeLowPriority: true,
includeMuted: true,
- limit: 50,
- onlyWithFile: true,
- onlyWithImage: true,
- onlyWithLink: true,
- onlyWithMedia: true,
- onlyWithVideo: true,
+ limit: 20,
+ mediaTypes: ['any'],
query: 'dinner',
sender: 'me',
},