From 479548ee53ffb73db73841e77acbeee5a78dbbd5 Mon Sep 17 00:00:00 2001 From: SimpleX Chat Date: Sat, 3 Oct 2026 07:36:00 +0000 Subject: [PATCH 01/11] 7.0.4: android 384, desktop 166, ios 361 --- apps/ios/SimpleX.xcodeproj/project.pbxproj | 40 +++++++++---------- apps/multiplatform/gradle.properties | 8 ++-- packages/simplex-chat-nodejs/package.json | 2 +- .../simplex-chat-nodejs/src/download-libs.js | 2 +- .../src/simplex_chat/_version.py | 4 +- .../flatpak/chat.simplex.simplex.metainfo.xml | 17 ++++++++ 6 files changed, 45 insertions(+), 28 deletions(-) diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 66b884fc7e..720aa305dd 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -2085,7 +2085,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2110,7 +2110,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES_THIN; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; @@ -2135,7 +2135,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2160,7 +2160,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; @@ -2177,11 +2177,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2197,11 +2197,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2222,7 +2222,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = s; @@ -2237,7 +2237,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2259,7 +2259,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_CODE_COVERAGE = NO; @@ -2274,7 +2274,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2296,7 +2296,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2322,7 +2322,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2347,7 +2347,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2374,7 +2374,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2401,7 +2401,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2416,7 +2416,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2435,7 +2435,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 359; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2450,7 +2450,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 7.0.3; + MARKETING_VERSION = 7.0.4; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; diff --git a/apps/multiplatform/gradle.properties b/apps/multiplatform/gradle.properties index 2cdcfe90bf..76e2da4719 100644 --- a/apps/multiplatform/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -24,11 +24,11 @@ android.nonTransitiveRClass=true kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.jvm.target=11 -android.version_name=7.0.3 -android.version_code=381 +android.version_name=7.0.4 +android.version_code=384 -desktop.version_name=7.0.3 -desktop.version_code=164 +desktop.version_name=7.0.4 +desktop.version_code=166 kotlin.version=2.1.20 gradle.plugin.version=8.7.0 diff --git a/packages/simplex-chat-nodejs/package.json b/packages/simplex-chat-nodejs/package.json index 9526f080b2..02194ffac2 100644 --- a/packages/simplex-chat-nodejs/package.json +++ b/packages/simplex-chat-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "simplex-chat", - "version": "7.0.3", + "version": "7.0.4", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ diff --git a/packages/simplex-chat-nodejs/src/download-libs.js b/packages/simplex-chat-nodejs/src/download-libs.js index cc6b5b4cbd..438451587f 100644 --- a/packages/simplex-chat-nodejs/src/download-libs.js +++ b/packages/simplex-chat-nodejs/src/download-libs.js @@ -4,7 +4,7 @@ const path = require('path'); const extract = require('extract-zip'); const GITHUB_REPO = 'simplex-chat/simplex-chat-libs'; -const RELEASE_TAG = 'v7.0.3'; +const RELEASE_TAG = 'v7.0.4'; const BACKEND = (process.env.SIMPLEX_BACKEND || process.env.npm_config_simplex_backend || 'sqlite').toLowerCase(); if (BACKEND !== 'sqlite' && BACKEND !== 'postgres') { diff --git a/packages/simplex-chat-python/src/simplex_chat/_version.py b/packages/simplex-chat-python/src/simplex_chat/_version.py index 8337c0102f..a816a5d751 100644 --- a/packages/simplex-chat-python/src/simplex_chat/_version.py +++ b/packages/simplex-chat-python/src/simplex_chat/_version.py @@ -5,5 +5,5 @@ Bump both together for normal releases. For wrapper-only fixes use a PEP 440 post-release: __version__ = "6.5.2.post1", LIBS_VERSION unchanged. """ -__version__ = "7.0.3" # PEP 440 — read by hatchling for wheel metadata -LIBS_VERSION = "7.0.3" # simplex-chat-libs release tag (no 'v' prefix) +__version__ = "7.0.4" # PEP 440 — read by hatchling for wheel metadata +LIBS_VERSION = "7.0.4" # simplex-chat-libs release tag (no 'v' prefix) diff --git a/scripts/flatpak/chat.simplex.simplex.metainfo.xml b/scripts/flatpak/chat.simplex.simplex.metainfo.xml index 71192f34e2..51bd7b33ae 100644 --- a/scripts/flatpak/chat.simplex.simplex.metainfo.xml +++ b/scripts/flatpak/chat.simplex.simplex.metainfo.xml @@ -38,6 +38,23 @@ + + https://simplex.chat/blog/20260722-simplex-public-names.html + +

New in v7.0-v7.0.4.

+

SimpleX public names for channels and businesses (BETA).

+

Better channels:

+
    +
  • Promote subscribers to contributors.
  • +
  • Publish your channel on your website.
  • +
  • Verify security code with contributors.
  • +
  • Wider messages, easier to read.
  • +
  • Host your own chat relays.
  • +
+

Add a longer description to your profile.

+

Simplified app settings.

+
+
https://simplex.chat/blog/20260722-simplex-public-names.html From e41ee3a851f82d1eb90d55307ab23f2b50404684 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 19 Oct 2026 13:49:56 +0100 Subject: [PATCH 02/11] docs: web discovery and LLM agent protocols (#7769) * docs: web discovery and LLM agent protocols * update * update * update --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- docs/protocol/agent-protocols.md | 469 +++++++++++++++++++++++++++++++ docs/protocol/web-discovery.md | 249 ++++++++++++++++ 2 files changed, 718 insertions(+) create mode 100644 docs/protocol/agent-protocols.md create mode 100644 docs/protocol/web-discovery.md diff --git a/docs/protocol/agent-protocols.md b/docs/protocol/agent-protocols.md new file mode 100644 index 0000000000..77c15ed1fa --- /dev/null +++ b/docs/protocol/agent-protocols.md @@ -0,0 +1,469 @@ +Revision 1, 2026-10-19, draft for comments + +Evgeny Poberezkin + +# Agent protocols over SimpleX + +## Table of contents + +- [Abstract](#abstract) +- [Conventions](#conventions) +- [Overview](#overview) +- [Addresses in llms.txt](#addresses-in-llmstxt) +- [Profile declarations](#profile-declarations) + - [protocols](#protocols) + - [Channel audience and update frequency](#channel-audience-and-update-frequency) + - [Contact address peers](#contact-address-peers) + - [Protocol names](#protocol-names) +- [Chat transport](#chat-transport) + - [Commands](#commands) + - [Replies](#replies) + - [Message size](#message-size) +- [Service RPC transport](#service-rpc-transport) + - [Exchange](#exchange) + - [Request](#request) + - [Response](#response) + - [Signed requests](#signed-requests) +- [MCP binding](#mcp-binding) + - [Messages](#messages) + - [Versions and capabilities](#versions-and-capabilities) + - [Examples](#examples) +- [Security considerations](#security-considerations) +- [Privacy considerations](#privacy-considerations) +- [Implementations](#implementations) +- [References](#references) + +## Abstract + +This document defines how software agents find SimpleX addresses and call the tools of other agents and bots over SimpleX: + +- an `llms.txt` section with communication addresses, beside the link relation types and the well-known document of [Publishing SimpleX addresses on websites](./web-discovery.md); +- declarations: the protocols a profile serves and their transports, the peer types a channel or a contact address is made for, and the update frequency of a channel; +- the chat transport: the command `/_call` in a conversation; +- the Service RPC transport: one request and one response, without a conversation, in a generic envelope; +- the binding of the Model Context Protocol (MCP) [MCP] to both transports. + +## Conventions + +The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT" and "MAY" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals. + +JSON schemas use JSON Type Definition [RFC8927]. Whitespace in JSON examples is for readability. + +**Bot**: a SimpleX profile operated by software: a bot of the bot library, or an AI agent through `@simplex-chat/mcp`. + +## Overview + +A website owner publishes channels for updates and contact addresses for requests [WEB]. An agent: + +1. reads the addresses from the website: link relations, `/.well-known/simplex.json` or `llms.txt`; +2. retrieves the link data with the link, without connecting; +3. reads the declarations; +4. subscribes to a channel, connects to a contact address, or sends a Service RPC request to it. + +```mermaid +sequenceDiagram + participant A as Agent + participant W as Website + participant R as SMP router + participant B as Bot + + A->>W: GET /.well-known/simplex.json + W-->>A: channels and contacts + A->>R: retrieve link data + R-->>A: link data with declarations + alt chat transport + A->>B: connect, then /_call messages + B-->>A: replies + else Service RPC transport + A->>B: request in the connection request + B-->>A: response to the reply queue + end +``` + +Two transports carry protocol messages: + +- **chat**: a conversation in a direct chat or a group. The caller connects once and sends commands as messages. The conversation persists between calls. +- **rpc**: Service RPC. The request is sent in the connection request to a contact address, and the response to the reply queue of that request. Both sides then delete the connection. A call needs one round trip, and each call uses a new connection. + +## Addresses in llms.txt + +An `llms.txt` file [LLMSTXT] lists communication addresses in a section with the heading `## Communications`. The section is a file list: each item is a Markdown link, then a colon and notes. + +The notes begin with the network and the type of the address, then the audience and, for updates, the update frequency or, for contacts, the protocols. Free text follows: + +``` + updates for [, ]. + contact for [, ]*. +``` + +- ``: the name of the network, `SimpleX` for SimpleX links. +- `updates`: an address that publishes updates. `contact`: an address that accepts conversations and requests. +- ``: `people`, `agents`, or `people and agents`. +- ``: a `changefreq` value of the Sitemaps protocol [SITEMAPS]. +- ``: the name of a protocol the address serves ([protocol names](#protocol-names)), in any letter case. +- ``: what the address is about. + +The network is named in the notes because the host in a SimpleX link is the router that hosts the address, and that router can have any domain name. + +For SimpleX entries, the type `updates` requires a channel link, and the type `contact` a contact address link [WEB]. The link text is the title of the address. + +``` +## Communications + +- [Ridgeline updates](https://smp12.simplex.im/c#…): SimpleX updates for people and agents, weekly. Trail conditions and closures. +- [Ask Ridgeline](https://smp12.simplex.im/a#…): SimpleX contact for agents, MCP. Bookings and availability. +- [Ridgeline support](mailto:help@ridgeline.example): Email contact for people. +``` + +## Profile declarations + +A profile or a group declares the protocols it serves in its preferences. A channel declares its audience and its update frequency in its public group record. A contact address declares the peers it serves in its link data. + +### protocols + +`protocols` is a key of the preferences: `preferences` of a user or contact profile, and `groupPreferences` of a group profile [CHAT]. Clients store the preferences of other profiles as received, so a client that does not know the key decodes it after an upgrade. + +JSON Type Definition schema [RFC8927] of the `protocols` key: + +```json +{ + "values": { + "properties": { + "versions": {"elements": {"type": "string"}}, + "transports": { + "optionalProperties": { + "chat": {"type": "boolean"}, + "rpc": {"type": "boolean"} + }, + "additionalProperties": true + } + }, + "additionalProperties": true + } +} +``` + +- Keys: protocol names. +- `versions`: required. The protocol versions the bot serves, in the bot's order of preference. +- `transports`: required. The transports on which the bot serves the protocol. +- `transports.chat`: the bot answers `/_call` for the protocol in a conversation. +- `transports.rpc`: the bot answers Service RPC requests for the protocol on its contact address. It is valid in `preferences` only. +- An absent transport is false. +- Clients MUST ignore unknown keys. + +A declaration in `preferences` applies to direct chats with the profile and to Service RPC requests to its contact address. A declaration in `groupPreferences` states that the group's owners serve the protocol in the group chat. Other members can serve a protocol in a group without a declaration, because the core removes preferences from member profiles in groups. In a business chat, `protocols` of the business profile MUST be copied into the chat's `groupPreferences`, as `commands` are. + +`commands` remains the command menu for people. `/_call` is not added to it. The underscore marks a command for software, as in the API of the core, and leaves `/call` free for bots' commands for people. + +Example, the `preferences` of a contact profile: + +```json +{ + "protocols": { + "mcp": { + "versions": ["2026-07-28"], + "transports": {"chat": true, "rpc": true} + } + } +} +``` + +### Channel audience and update frequency + +Two fields of the channel's public group record, `publicGroup` of its group profile [CHAT]. JSON Type Definition schema [RFC8927]: + +```json +{ + "optionalProperties": { + "audience": {"elements": {"type": "string"}}, + "changeFrequency": {"enum": ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]} + } +} +``` + +- `audience`: the peer types the channel is made for. The values are the values of a profile's `peerType`: `human`, `bot`, `business`, `agent`. An absent `audience` is `["human"]`. Agents also read content made for people, so a channel declares `agent` when it is made for agents, or has dedicated support for them. +- `changeFrequency`: how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. + +### Contact address peers + +One field of the data published with a contact address link, beside `business`. JSON Type Definition schema [RFC8927]: + +```json +{ + "optionalProperties": { + "serves": {"elements": {"type": "string"}} + } +} +``` + +- `serves`: the peer types the address is made for, with the values and the default of `audience`. + +Clients MUST ignore unknown values of `audience` and `serves`. The values of the channel and the address take precedence over the values on a website [WEB]. + +### Protocol names + +A protocol name consists of lowercase ASCII letters, digits and hyphens. + +A binding defines the protocol's request and response messages, including how, and whether, a peer discovers the protocol's capabilities. Both transports carry these messages unchanged. + +| Name | Binding | +|---|---| +| `mcp` | [MCP binding](#mcp-binding) | + +## Chat transport + +### Commands + +``` +/_call +/_call @ +``` + +- ``: the name of a protocol the recipient serves. +- ``: the protocol's request message, a JSON object. +- `@`: optional, in a group: a mention of the called member [CHAT]. +- In a group, a command with a mention is answered by the mentioned member. A command without a mention is a broadcast: each member that serves the protocol answers it. +- A command is the text of a text message. One space separates its parts. +- A bot that declares `transports.chat` for a protocol MUST answer each `/_call` for it that mentions the bot or mentions nobody. + +### Replies + +- A bot sends one reply to each command it answers. The reply quotes the command, with a truncated quote. Its text is the protocol's response message, a JSON object. +- A caller matches each reply to its command by the quoted message. Replies can arrive in any order. +- In a group, a broadcast can receive several replies, one from each member that serves the protocol. A caller collects them all, each with its sender. +- In a direct chat, a caller accepts replies only from the recipient. In a group, a caller of a command with a mention accepts only the mentioned member's reply. +- Commands and replies are ordinary messages: people see them in the conversation. + +### Message size + +A command and a reply are one message each. The core refuses a message whose encoded JSON exceeds 15,602 bytes [CHAT]. The command or the reply is a string inside that JSON, so its escape characters count towards the limit. A bot whose reply exceeds the limit replies with the binding's error instead. + +## Service RPC transport + +### Exchange + +The requester's core sends the request with: + +``` +/_service_request [timeout=] [sign_key=] +``` + +- ``: a contact address link, full or short, or the SimpleX name of a contact address [NAMES]. +- `timeout`: how long the requester waits for the response. The default is 30 seconds. +- `sign_key`: the requester's Ed25519 private key, for a [signed request](#signed-requests). +- ``: the [request](#request), a JSON object. +- The command returns `serviceResponse` with `responseData`, the [response](#response). + +The responder's core reports the request as the event `serviceRequest`, with `requestId`, `signerKey` and `requestData`. The responder sends the response with: + +``` +/_service_response +``` + +Requirements: + +- The responder's core MUST run with service requests enabled, the option `service_requests=on` of `/_start`. Otherwise the core drops each request, and the requester waits until its timeout. +- The responder's contact address MUST have ratchet keys. The core adds them to an address created or updated with the option `pq_ratchet=on` or `pq_ratchet=off` (`/_address`, `/_short_link_address`, `/_address_settings`). A request to an address without ratchet keys fails at once. +- The core accepts a response within 180 seconds of receiving the request. A requester that expects a slow response sets `timeout` accordingly. +- Before encryption, the agent pads a request or a response, its own framing included, to 11,106 bytes, and a longer one fails. A request and a response SHOULD NOT exceed 10,000 bytes of JSON. + +### Request + +JSON Type Definition schema [RFC8927] of the request: + +```json +{ + "properties": { + "protocol": {"type": "string"}, + "request": {} + }, + "optionalProperties": { + "version": {}, + "requestKey": {"type": "string"} + }, + "additionalProperties": true +} +``` + +- `protocol`: the protocol name. +- `version`: the protocol version, for a protocol whose messages do not include one. Its type is defined by the binding. +- `requestKey`: the requester's Ed25519 public key, in the encoding of the core's `signerKey`: base64url of the X.509 DER encoding of the key. A request with `requestKey` MUST be signed with that key. +- `request`: the protocol's request message, the same as after `/_call ` in a conversation. The fields of the envelope are never added to it. + +### Response + +JSON Type Definition schema [RFC8927] of the response: + +```json +{ + "optionalProperties": { + "response": {}, + "error": { + "properties": { + "code": {"type": "string"} + }, + "optionalProperties": { + "message": {"type": "string"}, + "retryAfter": {"type": "uint32"} + }, + "additionalProperties": true + } + }, + "additionalProperties": true +} +``` + +- A response has exactly one of `response` and `error`. +- `response`: the protocol's response message, the same as the reply in a conversation. +- `error`: the request was not passed to the protocol. +- `message`: a description for the requester's logs. +- `retryAfter`: the number of seconds after which a repeated request can succeed. + +Error codes: + +- `unsupported_protocol`: the responder does not serve the protocol over Service RPC. +- `unsupported_version`: the responder does not serve the requested version. +- `bad_request`: the envelope is invalid, or `requestKey` differs from the verified signer key. +- `conversation_required`: the request needs a conversation. The requester connects to the address and uses the chat transport. +- `too_large`: the response exceeds the size limit. +- `rate_limited`: the requester repeats the request after `retryAfter`. +- `internal`: the responder failed. + +A requester treats an unknown code as final, unless the error has `retryAfter`. + +### Signed requests + +- A signed request has the requester's Ed25519 signature over the request payload, bound to the request's ratchet session: the agent signs `SHA3-256("SimpleXService" ‖ AD) ‖ payload`, where AD is the ratchet's associated data. The responder's agent verifies the signature, and the core reports the key as `signerKey`. A signature copied into another session fails verification. +- A responder MUST answer `bad_request` to a request whose `requestKey` differs from `signerKey`, and to an unsigned request with `requestKey`. +- A requester signs when the responder needs a persistent identity, for example an account or a purchase. All requests signed with one key are linkable to each other. A requester SHOULD use a separate key for each responder. +- An unsigned request has only the payload: the responder cannot link it to other requests by a key. + +## MCP binding + +Protocol name: `mcp`. + +### Messages + +- The request is one JSON-RPC request of MCP revision 2026-07-28 or later, with its `_meta` envelope [MCP]. In a conversation, it follows `/_call mcp`. Over Service RPC, it is the envelope's `request`. +- The response is the JSON-RPC response to the request: a result or a JSON-RPC error. In a conversation, it is the text of the reply. Over Service RPC, it is the envelope's `response`. +- In a conversation, a caller matches a reply to its request by the quoted message. The response has the request's `id`, as JSON-RPC requires. +- A bot serves the methods answered with one response, for example `server/discover`, `tools/list` and `tools/call`. +- A multi-round-trip result (`resultType: "input_required"`) is answered with a new request, as MCP defines. +- A response above the size limit is replaced with a JSON-RPC error with code -32603 in a conversation, and with the envelope error `too_large` over Service RPC. +- Over Service RPC, a call of a tool that needs a conversation is answered with the envelope error `conversation_required`. + +### Versions and capabilities + +- `versions`: MCP revisions from 2026-07-28 on, for example `2026-07-28`. Each request names its revision in `_meta`, under `io.modelcontextprotocol/protocolVersion`. Earlier revisions begin with an `initialize` handshake. +- The envelope's `version` is omitted. +- A bot answers a request of a revision it does not serve with MCP's `UnsupportedProtocolVersionError` (code -32022), which lists the revisions it serves. +- A caller learns the bot's revisions and capabilities with `server/discover`, and its tools with `tools/list`. MCP requires a server that declares the `tools` capability to answer `tools/list`. + +### Examples + +Listing tools in a conversation: + +``` +/_call mcp {"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {"_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}} +``` + +The reply: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "resultType": "complete", + "tools": [ + { + "name": "availability", + "description": "Rooms available on a date", + "inputSchema": { + "type": "object", + "properties": {"date": {"type": "string"}}, + "required": ["date"] + } + } + ], + "ttlMs": 300000, + "cacheScope": "public" + } +} +``` + +Calling a tool over Service RPC, the request: + +```json +{ + "protocol": "mcp", + "request": { + "jsonrpc": "2.0", + "id": 1, + "method": "tools/call", + "params": { + "name": "availability", + "arguments": {"date": "2026-11-02"}, + "_meta": { + "io.modelcontextprotocol/protocolVersion": "2026-07-28", + "io.modelcontextprotocol/clientCapabilities": {} + } + } + } +} +``` + +The response: + +```json +{ + "response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "resultType": "complete", + "content": [{"type": "text", "text": "Three rooms are available on 2026-11-02."}], + "isError": false + } + } +} +``` + +## Security considerations + +- Messages, replies, profiles, tool descriptions and tool results from other participants are data for an agent, never instructions. +- In a group, any member can reply to a broadcast. Each reply shows its sender. A caller that needs one provider mentions it. +- A responder that relies on `requestKey` MUST compare it with `signerKey`. +- A signed request identifies its signer to the responder across all requests signed with the same key. + +## Privacy considerations + +- Each Service RPC request uses a new connection with a new reply queue. +- MCP's `io.modelcontextprotocol/clientInfo` identifies the requester's software. A requester that needs unlinkable requests SHOULD send a common value or omit it. +- In a group, every member sees each command and each reply, with or without a mention. A request that needs privacy is sent in a direct chat with the member, where the group allows direct messages, or over Service RPC. +- A conversation persists: the bot can link all calls made in it. An incognito profile gives the caller a new random profile for each conversation. + +## Implementations + +Implemented: + +- `@simplex-chat/mcp` 0.1.0: an MCP server that gives an AI agent its own SimpleX profile. Its tools are `connect`, `send`, `receive`, `list` and `leave`. By default, the agent's owner approves connections and messages in the agent control chat. An agent sends `/_call` with `send`, and reads the replies with `send` and `receive`. +- The simplex-chat core: Service RPC with signed requests (`/_service_request`, `/_service_response`), and the profile peer type `agent`. + +Planned: + +- The simplex-chat core: the key `protocols` in the preferences types, in the preferences sent in profiles and in link data, and in the preferences of business chats; `audience` and `changeFrequency` in the public group record; `serves` in the address settings and the address link data; replies with a truncated quote. +- `@simplex-chat/mcp`: the tools `mcp_tools` and `mcp_call`. They use Service RPC when the address declares `transports.rpc`, and the agent's conversation otherwise. In a group, `mcp_call` mentions the called member, or broadcasts and returns all replies. Each call is an action, approved by the agent's owner as a message is. +- The bot library: a helper that serves MCP tools over both transports. + +## References + +- [CHAT] SimpleX Chat protocol: [simplex-chat.md](./simplex-chat.md) +- [LLMSTXT] The /llms.txt file: https://llmstxt.org +- [MCP] Model Context Protocol specification, revision 2026-07-28: https://modelcontextprotocol.io/specification/2026-07-28 +- [NAMES] SimpleX public names for channels and businesses: [names-overview.md](./names-overview.md) +- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels: https://www.rfc-editor.org/rfc/rfc2119 +- [RFC8174] Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words: https://www.rfc-editor.org/rfc/rfc8174 +- [RFC8927] JSON Type Definition: https://www.rfc-editor.org/rfc/rfc8927 +- [SITEMAPS] Sitemaps XML format: https://www.sitemaps.org/protocol.html +- [WEB] Publishing SimpleX addresses on websites: [web-discovery.md](./web-discovery.md) diff --git a/docs/protocol/web-discovery.md b/docs/protocol/web-discovery.md new file mode 100644 index 0000000000..95bda8c2fb --- /dev/null +++ b/docs/protocol/web-discovery.md @@ -0,0 +1,249 @@ +Revision 1, 2026-10-19, draft + +Evgeny Poberezkin + +# Publishing SimpleX addresses on websites + +## Table of contents + +- [Abstract](#abstract) +- [Conventions](#conventions) +- [Terms](#terms) +- [Link relation types](#link-relation-types) + - [simplex-channel](#simplex-channel) + - [simplex-contact](#simplex-contact) + - [Target attributes](#target-attributes) + - [HTML](#html) + - [HTTP](#http) +- [Well-known URI](#well-known-uri) + - [Retrieval](#retrieval) + - [Format](#format) + - [Example](#example) +- [Processing](#processing) +- [Security considerations](#security-considerations) +- [Privacy considerations](#privacy-considerations) +- [IANA considerations](#iana-considerations) + - [Link relation types registration](#link-relation-types-registration) + - [Well-known URI registration](#well-known-uri-registration) +- [HTML link type extensions](#html-link-type-extensions) +- [References](#references) + +## Abstract + +This document defines how a website publishes the SimpleX channels and contact addresses of its owner: + +- the link relation types `simplex-channel` and `simplex-contact`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field; +- the well-known URI `simplex.json`: a JSON document with the owner's channels and contact addresses, their audience, the update frequency of each channel and the protocols each contact address serves. + +People and software agents subscribe to a channel to receive updates, and connect to a contact address to send requests. [Agent protocols over SimpleX](./agent-protocols.md) defines the requests an agent sends to a contact address, and an `llms.txt` section with the same addresses. + +## Conventions + +The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT" and "MAY" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals. + +JSON schemas use JSON Type Definition [RFC8927]. + +## Terms + +- **SimpleX link**: a connection link of the SimpleX Agent protocol, in the full or the short syntax, with the `https` or the `simplex` scheme [AGENT]. +- **Channel link**: a short SimpleX link of type `c`, for example `https://smp12.simplex.im/c#`. +- **Contact address link**: a short SimpleX link of type `a`, for example `https://smp12.simplex.im/a#`, or a full SimpleX link of type `contact`. +- **SimpleX name**: a name in the SimpleX name service [NAMES]. `#example.simplex` is the name of a channel, `@example.simplex` the name of a contact address. +- **Owner**: the person or organization that publishes the website. + +## Link relation types + +### simplex-channel + +The target of a `simplex-channel` link is a SimpleX channel in which the owner of the link context publishes updates. The target MUST be a channel link. + +### simplex-contact + +The target of a `simplex-contact` link is a SimpleX contact address at which the owner of the link context accepts conversations and requests. The target MUST be a contact address link. + +### Target attributes + +- `title`: the name of the channel or the address, for display. In HTTP, a title with non-ASCII characters is sent as `title*` [RFC8187]. +- `hreflang`: the language of the channel's posts or of the conversations [BCP47]. + +A link context MAY have several links of one relation type. Their order is the owner's order of preference: document order in HTML, and the order of link values in HTTP. + +### HTML + +In the document head: + +```html + + +``` + +In the document body, the relation types are used on visible links: + +```html +Chat with us +``` + +### HTTP + +``` +Link: ; rel="simplex-channel"; hreflang="en"; title="Ridgeline updates" +Link: ; rel="simplex-contact"; hreflang="en"; title="Ask Ridgeline" +``` + +## Well-known URI + +### Retrieval + +The document is located at the path `/.well-known/simplex.json` of an origin [RFC8615]. A client retrieves it with an HTTPS `GET` request. A server responds with status 200, and SHOULD send the media type `application/json`. A client parses the response body as JSON, whatever media type the server sends. + +The entries of the document are the channels and contact addresses of the origin's owner. A site that controls only a path of its origin publishes link relations instead. + +### Format + +The document MUST conform to this JSON Type Definition schema [RFC8927]: + +```json +{ + "optionalProperties": { + "channels": {"elements": {"ref": "address"}}, + "contacts": {"elements": {"ref": "address"}} + }, + "additionalProperties": true, + "definitions": { + "address": { + "properties": { + "link": {"type": "string"} + }, + "optionalProperties": { + "name": {"type": "string"}, + "title": {"type": "string"}, + "lang": {"type": "string"}, + "audience": {"elements": {"type": "string"}}, + "changeFrequency": {"enum": ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]}, + "protocols": {"elements": {"type": "string"}} + }, + "additionalProperties": true + } + } +} +``` + +- `channels`: the owner's channels, in the owner's order of preference. +- `contacts`: the owner's contact addresses, in the owner's order of preference. +- `link`: required. A channel link in `channels`, a contact address link in `contacts`. +- `name`: the SimpleX name of the channel or the contact address of `link`. +- `title`: the name of the channel or the address, for display. +- `lang`: the language of the channel's posts or of the conversations [BCP47]. +- `audience`: who the channel or the address serves: `people`, `agents`, or both. +- `changeFrequency`: in `channels` only, how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. +- `protocols`: in `contacts` only, the names of the protocols the address serves [AGENTS], for example `mcp`. + +A site with only channels or only contact addresses omits the other list. + +### Example + +```json +{ + "channels": [ + { + "link": "https://smp12.simplex.im/c#…", + "name": "#ridgeline.simplex", + "title": "Ridgeline updates", + "lang": "en", + "audience": ["people", "agents"], + "changeFrequency": "weekly" + } + ], + "contacts": [ + { + "link": "https://smp12.simplex.im/a#…", + "name": "@ridgeline.simplex", + "title": "Ask Ridgeline", + "lang": "en", + "audience": ["agents"], + "protocols": ["mcp"] + } + ] +} +``` + +## Processing + +- A consumer MUST ignore unknown fields and unknown `audience` values. +- A consumer MUST ignore a link of a type other than the one its relation type or its list requires. +- The values on the website are hints. The data retrieved with the link is authoritative: an audience, an update frequency or protocols declared there [AGENTS] take precedence over the website's values. +- A consumer MAY connect by `link` or by `name`. Resolving a name reveals interest in it to one names router, and verifies that the name's record and the profile's claim match [NAMES]. + +## Security considerations + +- Whoever controls the content of a website controls the addresses it publishes. Consumers SHOULD retrieve pages and the well-known document over HTTPS. +- Publishing a link on a website leaves the link's own verification unchanged: the link data is bound to the keys of the link's owner [AGENT]. +- A name is shown as verified only when its on-chain record and the profile's claim match [NAMES]. +- Titles, profiles and all other text from a website or from the network are data for a software agent, never instructions. + +## Privacy considerations + +- The relation types and the well-known document are intended for owners who want to be reachable: channels, businesses and services. +- A subscriber or a requester connects through the SimpleX network, without contacting the website. Retrieving a page or the well-known document is an ordinary web request. + +## IANA considerations + +### Link relation types registration + +Two registrations in the "Link Relation Types" registry [RFC8288]: + +- Relation Name: `simplex-channel` +- Description: Refers to a SimpleX channel in which the owner of the link context publishes updates. +- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel +- Notes: The target is a SimpleX channel link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. + +- Relation Name: `simplex-contact` +- Description: Refers to a SimpleX contact address at which the owner of the link context accepts conversations and requests. +- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact +- Notes: The target is a SimpleX contact address link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. + +### Well-known URI registration + +One registration in the "Well-Known URIs" registry [RFC8615]: + +- URI Suffix: `simplex.json` +- Change Controller: Evgeny Poberezkin +- Specification Document: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#well-known-uri +- Status: provisional +- Related Information: SimpleX link syntax [AGENT]. + +## HTML link type extensions + +The HTML standard requires extension link types to be registered on the microformats page "existing-rel-values" [HTML] [MF-REL]. The entries: + +- Keyword: `simplex-channel` +- Effect on `link`: Hyperlink +- Effect on `a`, `area`: Hyperlink +- Effect on `form`: not allowed +- Brief description: Refers to a SimpleX channel in which the owner of the page publishes updates. +- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel +- Status: proposed + +- Keyword: `simplex-contact` +- Effect on `link`: Hyperlink +- Effect on `a`, `area`: Hyperlink +- Effect on `form`: not allowed +- Brief description: Refers to a SimpleX contact address at which the owner of the page accepts conversations and requests. +- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact +- Status: proposed + +## References + +- [AGENT] SimpleX Agent protocol, connection links: https://github.com/simplex-chat/simplexmq/blob/stable/protocol/agent-protocol.md#connection-link-1-time-invitation-and-contact-address +- [AGENTS] Agent protocols over SimpleX: [agent-protocols.md](./agent-protocols.md) +- [BCP47] Tags for Identifying Languages: https://www.rfc-editor.org/info/bcp47 +- [HTML] HTML Living Standard, other link types: https://html.spec.whatwg.org/multipage/links.html#other-link-types +- [MF-REL] Microformats, existing rel values: https://microformats.org/wiki/existing-rel-values +- [NAMES] SimpleX public names for channels and businesses: [names-overview.md](./names-overview.md) +- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels: https://www.rfc-editor.org/rfc/rfc2119 +- [RFC8174] Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words: https://www.rfc-editor.org/rfc/rfc8174 +- [RFC8187] Indicating Character Encoding and Language for HTTP Header Field Parameters: https://www.rfc-editor.org/rfc/rfc8187 +- [RFC8288] Web Linking: https://www.rfc-editor.org/rfc/rfc8288 +- [RFC8615] Well-Known Uniform Resource Identifiers (URIs): https://www.rfc-editor.org/rfc/rfc8615 +- [RFC8927] JSON Type Definition: https://www.rfc-editor.org/rfc/rfc8927 +- [SITEMAPS] Sitemaps XML format: https://www.sitemaps.org/protocol.html From 85cae84809b43c30a3a523cc8ef08683bf60097b Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 19 Oct 2026 13:49:56 +0100 Subject: [PATCH 03/11] docs: web discovery and LLM agent protocols (#7769) * docs: web discovery and LLM agent protocols * update * update * update --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- docs/protocol/agent-protocols.md | 469 +++++++++++++++++++++++++++++++ docs/protocol/web-discovery.md | 249 ++++++++++++++++ 2 files changed, 718 insertions(+) create mode 100644 docs/protocol/agent-protocols.md create mode 100644 docs/protocol/web-discovery.md diff --git a/docs/protocol/agent-protocols.md b/docs/protocol/agent-protocols.md new file mode 100644 index 0000000000..77c15ed1fa --- /dev/null +++ b/docs/protocol/agent-protocols.md @@ -0,0 +1,469 @@ +Revision 1, 2026-10-19, draft for comments + +Evgeny Poberezkin + +# Agent protocols over SimpleX + +## Table of contents + +- [Abstract](#abstract) +- [Conventions](#conventions) +- [Overview](#overview) +- [Addresses in llms.txt](#addresses-in-llmstxt) +- [Profile declarations](#profile-declarations) + - [protocols](#protocols) + - [Channel audience and update frequency](#channel-audience-and-update-frequency) + - [Contact address peers](#contact-address-peers) + - [Protocol names](#protocol-names) +- [Chat transport](#chat-transport) + - [Commands](#commands) + - [Replies](#replies) + - [Message size](#message-size) +- [Service RPC transport](#service-rpc-transport) + - [Exchange](#exchange) + - [Request](#request) + - [Response](#response) + - [Signed requests](#signed-requests) +- [MCP binding](#mcp-binding) + - [Messages](#messages) + - [Versions and capabilities](#versions-and-capabilities) + - [Examples](#examples) +- [Security considerations](#security-considerations) +- [Privacy considerations](#privacy-considerations) +- [Implementations](#implementations) +- [References](#references) + +## Abstract + +This document defines how software agents find SimpleX addresses and call the tools of other agents and bots over SimpleX: + +- an `llms.txt` section with communication addresses, beside the link relation types and the well-known document of [Publishing SimpleX addresses on websites](./web-discovery.md); +- declarations: the protocols a profile serves and their transports, the peer types a channel or a contact address is made for, and the update frequency of a channel; +- the chat transport: the command `/_call` in a conversation; +- the Service RPC transport: one request and one response, without a conversation, in a generic envelope; +- the binding of the Model Context Protocol (MCP) [MCP] to both transports. + +## Conventions + +The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT" and "MAY" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals. + +JSON schemas use JSON Type Definition [RFC8927]. Whitespace in JSON examples is for readability. + +**Bot**: a SimpleX profile operated by software: a bot of the bot library, or an AI agent through `@simplex-chat/mcp`. + +## Overview + +A website owner publishes channels for updates and contact addresses for requests [WEB]. An agent: + +1. reads the addresses from the website: link relations, `/.well-known/simplex.json` or `llms.txt`; +2. retrieves the link data with the link, without connecting; +3. reads the declarations; +4. subscribes to a channel, connects to a contact address, or sends a Service RPC request to it. + +```mermaid +sequenceDiagram + participant A as Agent + participant W as Website + participant R as SMP router + participant B as Bot + + A->>W: GET /.well-known/simplex.json + W-->>A: channels and contacts + A->>R: retrieve link data + R-->>A: link data with declarations + alt chat transport + A->>B: connect, then /_call messages + B-->>A: replies + else Service RPC transport + A->>B: request in the connection request + B-->>A: response to the reply queue + end +``` + +Two transports carry protocol messages: + +- **chat**: a conversation in a direct chat or a group. The caller connects once and sends commands as messages. The conversation persists between calls. +- **rpc**: Service RPC. The request is sent in the connection request to a contact address, and the response to the reply queue of that request. Both sides then delete the connection. A call needs one round trip, and each call uses a new connection. + +## Addresses in llms.txt + +An `llms.txt` file [LLMSTXT] lists communication addresses in a section with the heading `## Communications`. The section is a file list: each item is a Markdown link, then a colon and notes. + +The notes begin with the network and the type of the address, then the audience and, for updates, the update frequency or, for contacts, the protocols. Free text follows: + +``` + updates for [, ]. + contact for [, ]*. +``` + +- ``: the name of the network, `SimpleX` for SimpleX links. +- `updates`: an address that publishes updates. `contact`: an address that accepts conversations and requests. +- ``: `people`, `agents`, or `people and agents`. +- ``: a `changefreq` value of the Sitemaps protocol [SITEMAPS]. +- ``: the name of a protocol the address serves ([protocol names](#protocol-names)), in any letter case. +- ``: what the address is about. + +The network is named in the notes because the host in a SimpleX link is the router that hosts the address, and that router can have any domain name. + +For SimpleX entries, the type `updates` requires a channel link, and the type `contact` a contact address link [WEB]. The link text is the title of the address. + +``` +## Communications + +- [Ridgeline updates](https://smp12.simplex.im/c#…): SimpleX updates for people and agents, weekly. Trail conditions and closures. +- [Ask Ridgeline](https://smp12.simplex.im/a#…): SimpleX contact for agents, MCP. Bookings and availability. +- [Ridgeline support](mailto:help@ridgeline.example): Email contact for people. +``` + +## Profile declarations + +A profile or a group declares the protocols it serves in its preferences. A channel declares its audience and its update frequency in its public group record. A contact address declares the peers it serves in its link data. + +### protocols + +`protocols` is a key of the preferences: `preferences` of a user or contact profile, and `groupPreferences` of a group profile [CHAT]. Clients store the preferences of other profiles as received, so a client that does not know the key decodes it after an upgrade. + +JSON Type Definition schema [RFC8927] of the `protocols` key: + +```json +{ + "values": { + "properties": { + "versions": {"elements": {"type": "string"}}, + "transports": { + "optionalProperties": { + "chat": {"type": "boolean"}, + "rpc": {"type": "boolean"} + }, + "additionalProperties": true + } + }, + "additionalProperties": true + } +} +``` + +- Keys: protocol names. +- `versions`: required. The protocol versions the bot serves, in the bot's order of preference. +- `transports`: required. The transports on which the bot serves the protocol. +- `transports.chat`: the bot answers `/_call` for the protocol in a conversation. +- `transports.rpc`: the bot answers Service RPC requests for the protocol on its contact address. It is valid in `preferences` only. +- An absent transport is false. +- Clients MUST ignore unknown keys. + +A declaration in `preferences` applies to direct chats with the profile and to Service RPC requests to its contact address. A declaration in `groupPreferences` states that the group's owners serve the protocol in the group chat. Other members can serve a protocol in a group without a declaration, because the core removes preferences from member profiles in groups. In a business chat, `protocols` of the business profile MUST be copied into the chat's `groupPreferences`, as `commands` are. + +`commands` remains the command menu for people. `/_call` is not added to it. The underscore marks a command for software, as in the API of the core, and leaves `/call` free for bots' commands for people. + +Example, the `preferences` of a contact profile: + +```json +{ + "protocols": { + "mcp": { + "versions": ["2026-07-28"], + "transports": {"chat": true, "rpc": true} + } + } +} +``` + +### Channel audience and update frequency + +Two fields of the channel's public group record, `publicGroup` of its group profile [CHAT]. JSON Type Definition schema [RFC8927]: + +```json +{ + "optionalProperties": { + "audience": {"elements": {"type": "string"}}, + "changeFrequency": {"enum": ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]} + } +} +``` + +- `audience`: the peer types the channel is made for. The values are the values of a profile's `peerType`: `human`, `bot`, `business`, `agent`. An absent `audience` is `["human"]`. Agents also read content made for people, so a channel declares `agent` when it is made for agents, or has dedicated support for them. +- `changeFrequency`: how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. + +### Contact address peers + +One field of the data published with a contact address link, beside `business`. JSON Type Definition schema [RFC8927]: + +```json +{ + "optionalProperties": { + "serves": {"elements": {"type": "string"}} + } +} +``` + +- `serves`: the peer types the address is made for, with the values and the default of `audience`. + +Clients MUST ignore unknown values of `audience` and `serves`. The values of the channel and the address take precedence over the values on a website [WEB]. + +### Protocol names + +A protocol name consists of lowercase ASCII letters, digits and hyphens. + +A binding defines the protocol's request and response messages, including how, and whether, a peer discovers the protocol's capabilities. Both transports carry these messages unchanged. + +| Name | Binding | +|---|---| +| `mcp` | [MCP binding](#mcp-binding) | + +## Chat transport + +### Commands + +``` +/_call +/_call @ +``` + +- ``: the name of a protocol the recipient serves. +- ``: the protocol's request message, a JSON object. +- `@`: optional, in a group: a mention of the called member [CHAT]. +- In a group, a command with a mention is answered by the mentioned member. A command without a mention is a broadcast: each member that serves the protocol answers it. +- A command is the text of a text message. One space separates its parts. +- A bot that declares `transports.chat` for a protocol MUST answer each `/_call` for it that mentions the bot or mentions nobody. + +### Replies + +- A bot sends one reply to each command it answers. The reply quotes the command, with a truncated quote. Its text is the protocol's response message, a JSON object. +- A caller matches each reply to its command by the quoted message. Replies can arrive in any order. +- In a group, a broadcast can receive several replies, one from each member that serves the protocol. A caller collects them all, each with its sender. +- In a direct chat, a caller accepts replies only from the recipient. In a group, a caller of a command with a mention accepts only the mentioned member's reply. +- Commands and replies are ordinary messages: people see them in the conversation. + +### Message size + +A command and a reply are one message each. The core refuses a message whose encoded JSON exceeds 15,602 bytes [CHAT]. The command or the reply is a string inside that JSON, so its escape characters count towards the limit. A bot whose reply exceeds the limit replies with the binding's error instead. + +## Service RPC transport + +### Exchange + +The requester's core sends the request with: + +``` +/_service_request [timeout=] [sign_key=] +``` + +- ``: a contact address link, full or short, or the SimpleX name of a contact address [NAMES]. +- `timeout`: how long the requester waits for the response. The default is 30 seconds. +- `sign_key`: the requester's Ed25519 private key, for a [signed request](#signed-requests). +- ``: the [request](#request), a JSON object. +- The command returns `serviceResponse` with `responseData`, the [response](#response). + +The responder's core reports the request as the event `serviceRequest`, with `requestId`, `signerKey` and `requestData`. The responder sends the response with: + +``` +/_service_response +``` + +Requirements: + +- The responder's core MUST run with service requests enabled, the option `service_requests=on` of `/_start`. Otherwise the core drops each request, and the requester waits until its timeout. +- The responder's contact address MUST have ratchet keys. The core adds them to an address created or updated with the option `pq_ratchet=on` or `pq_ratchet=off` (`/_address`, `/_short_link_address`, `/_address_settings`). A request to an address without ratchet keys fails at once. +- The core accepts a response within 180 seconds of receiving the request. A requester that expects a slow response sets `timeout` accordingly. +- Before encryption, the agent pads a request or a response, its own framing included, to 11,106 bytes, and a longer one fails. A request and a response SHOULD NOT exceed 10,000 bytes of JSON. + +### Request + +JSON Type Definition schema [RFC8927] of the request: + +```json +{ + "properties": { + "protocol": {"type": "string"}, + "request": {} + }, + "optionalProperties": { + "version": {}, + "requestKey": {"type": "string"} + }, + "additionalProperties": true +} +``` + +- `protocol`: the protocol name. +- `version`: the protocol version, for a protocol whose messages do not include one. Its type is defined by the binding. +- `requestKey`: the requester's Ed25519 public key, in the encoding of the core's `signerKey`: base64url of the X.509 DER encoding of the key. A request with `requestKey` MUST be signed with that key. +- `request`: the protocol's request message, the same as after `/_call ` in a conversation. The fields of the envelope are never added to it. + +### Response + +JSON Type Definition schema [RFC8927] of the response: + +```json +{ + "optionalProperties": { + "response": {}, + "error": { + "properties": { + "code": {"type": "string"} + }, + "optionalProperties": { + "message": {"type": "string"}, + "retryAfter": {"type": "uint32"} + }, + "additionalProperties": true + } + }, + "additionalProperties": true +} +``` + +- A response has exactly one of `response` and `error`. +- `response`: the protocol's response message, the same as the reply in a conversation. +- `error`: the request was not passed to the protocol. +- `message`: a description for the requester's logs. +- `retryAfter`: the number of seconds after which a repeated request can succeed. + +Error codes: + +- `unsupported_protocol`: the responder does not serve the protocol over Service RPC. +- `unsupported_version`: the responder does not serve the requested version. +- `bad_request`: the envelope is invalid, or `requestKey` differs from the verified signer key. +- `conversation_required`: the request needs a conversation. The requester connects to the address and uses the chat transport. +- `too_large`: the response exceeds the size limit. +- `rate_limited`: the requester repeats the request after `retryAfter`. +- `internal`: the responder failed. + +A requester treats an unknown code as final, unless the error has `retryAfter`. + +### Signed requests + +- A signed request has the requester's Ed25519 signature over the request payload, bound to the request's ratchet session: the agent signs `SHA3-256("SimpleXService" ‖ AD) ‖ payload`, where AD is the ratchet's associated data. The responder's agent verifies the signature, and the core reports the key as `signerKey`. A signature copied into another session fails verification. +- A responder MUST answer `bad_request` to a request whose `requestKey` differs from `signerKey`, and to an unsigned request with `requestKey`. +- A requester signs when the responder needs a persistent identity, for example an account or a purchase. All requests signed with one key are linkable to each other. A requester SHOULD use a separate key for each responder. +- An unsigned request has only the payload: the responder cannot link it to other requests by a key. + +## MCP binding + +Protocol name: `mcp`. + +### Messages + +- The request is one JSON-RPC request of MCP revision 2026-07-28 or later, with its `_meta` envelope [MCP]. In a conversation, it follows `/_call mcp`. Over Service RPC, it is the envelope's `request`. +- The response is the JSON-RPC response to the request: a result or a JSON-RPC error. In a conversation, it is the text of the reply. Over Service RPC, it is the envelope's `response`. +- In a conversation, a caller matches a reply to its request by the quoted message. The response has the request's `id`, as JSON-RPC requires. +- A bot serves the methods answered with one response, for example `server/discover`, `tools/list` and `tools/call`. +- A multi-round-trip result (`resultType: "input_required"`) is answered with a new request, as MCP defines. +- A response above the size limit is replaced with a JSON-RPC error with code -32603 in a conversation, and with the envelope error `too_large` over Service RPC. +- Over Service RPC, a call of a tool that needs a conversation is answered with the envelope error `conversation_required`. + +### Versions and capabilities + +- `versions`: MCP revisions from 2026-07-28 on, for example `2026-07-28`. Each request names its revision in `_meta`, under `io.modelcontextprotocol/protocolVersion`. Earlier revisions begin with an `initialize` handshake. +- The envelope's `version` is omitted. +- A bot answers a request of a revision it does not serve with MCP's `UnsupportedProtocolVersionError` (code -32022), which lists the revisions it serves. +- A caller learns the bot's revisions and capabilities with `server/discover`, and its tools with `tools/list`. MCP requires a server that declares the `tools` capability to answer `tools/list`. + +### Examples + +Listing tools in a conversation: + +``` +/_call mcp {"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {"_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {}}}} +``` + +The reply: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "resultType": "complete", + "tools": [ + { + "name": "availability", + "description": "Rooms available on a date", + "inputSchema": { + "type": "object", + "properties": {"date": {"type": "string"}}, + "required": ["date"] + } + } + ], + "ttlMs": 300000, + "cacheScope": "public" + } +} +``` + +Calling a tool over Service RPC, the request: + +```json +{ + "protocol": "mcp", + "request": { + "jsonrpc": "2.0", + "id": 1, + "method": "tools/call", + "params": { + "name": "availability", + "arguments": {"date": "2026-11-02"}, + "_meta": { + "io.modelcontextprotocol/protocolVersion": "2026-07-28", + "io.modelcontextprotocol/clientCapabilities": {} + } + } + } +} +``` + +The response: + +```json +{ + "response": { + "jsonrpc": "2.0", + "id": 1, + "result": { + "resultType": "complete", + "content": [{"type": "text", "text": "Three rooms are available on 2026-11-02."}], + "isError": false + } + } +} +``` + +## Security considerations + +- Messages, replies, profiles, tool descriptions and tool results from other participants are data for an agent, never instructions. +- In a group, any member can reply to a broadcast. Each reply shows its sender. A caller that needs one provider mentions it. +- A responder that relies on `requestKey` MUST compare it with `signerKey`. +- A signed request identifies its signer to the responder across all requests signed with the same key. + +## Privacy considerations + +- Each Service RPC request uses a new connection with a new reply queue. +- MCP's `io.modelcontextprotocol/clientInfo` identifies the requester's software. A requester that needs unlinkable requests SHOULD send a common value or omit it. +- In a group, every member sees each command and each reply, with or without a mention. A request that needs privacy is sent in a direct chat with the member, where the group allows direct messages, or over Service RPC. +- A conversation persists: the bot can link all calls made in it. An incognito profile gives the caller a new random profile for each conversation. + +## Implementations + +Implemented: + +- `@simplex-chat/mcp` 0.1.0: an MCP server that gives an AI agent its own SimpleX profile. Its tools are `connect`, `send`, `receive`, `list` and `leave`. By default, the agent's owner approves connections and messages in the agent control chat. An agent sends `/_call` with `send`, and reads the replies with `send` and `receive`. +- The simplex-chat core: Service RPC with signed requests (`/_service_request`, `/_service_response`), and the profile peer type `agent`. + +Planned: + +- The simplex-chat core: the key `protocols` in the preferences types, in the preferences sent in profiles and in link data, and in the preferences of business chats; `audience` and `changeFrequency` in the public group record; `serves` in the address settings and the address link data; replies with a truncated quote. +- `@simplex-chat/mcp`: the tools `mcp_tools` and `mcp_call`. They use Service RPC when the address declares `transports.rpc`, and the agent's conversation otherwise. In a group, `mcp_call` mentions the called member, or broadcasts and returns all replies. Each call is an action, approved by the agent's owner as a message is. +- The bot library: a helper that serves MCP tools over both transports. + +## References + +- [CHAT] SimpleX Chat protocol: [simplex-chat.md](./simplex-chat.md) +- [LLMSTXT] The /llms.txt file: https://llmstxt.org +- [MCP] Model Context Protocol specification, revision 2026-07-28: https://modelcontextprotocol.io/specification/2026-07-28 +- [NAMES] SimpleX public names for channels and businesses: [names-overview.md](./names-overview.md) +- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels: https://www.rfc-editor.org/rfc/rfc2119 +- [RFC8174] Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words: https://www.rfc-editor.org/rfc/rfc8174 +- [RFC8927] JSON Type Definition: https://www.rfc-editor.org/rfc/rfc8927 +- [SITEMAPS] Sitemaps XML format: https://www.sitemaps.org/protocol.html +- [WEB] Publishing SimpleX addresses on websites: [web-discovery.md](./web-discovery.md) diff --git a/docs/protocol/web-discovery.md b/docs/protocol/web-discovery.md new file mode 100644 index 0000000000..95bda8c2fb --- /dev/null +++ b/docs/protocol/web-discovery.md @@ -0,0 +1,249 @@ +Revision 1, 2026-10-19, draft + +Evgeny Poberezkin + +# Publishing SimpleX addresses on websites + +## Table of contents + +- [Abstract](#abstract) +- [Conventions](#conventions) +- [Terms](#terms) +- [Link relation types](#link-relation-types) + - [simplex-channel](#simplex-channel) + - [simplex-contact](#simplex-contact) + - [Target attributes](#target-attributes) + - [HTML](#html) + - [HTTP](#http) +- [Well-known URI](#well-known-uri) + - [Retrieval](#retrieval) + - [Format](#format) + - [Example](#example) +- [Processing](#processing) +- [Security considerations](#security-considerations) +- [Privacy considerations](#privacy-considerations) +- [IANA considerations](#iana-considerations) + - [Link relation types registration](#link-relation-types-registration) + - [Well-known URI registration](#well-known-uri-registration) +- [HTML link type extensions](#html-link-type-extensions) +- [References](#references) + +## Abstract + +This document defines how a website publishes the SimpleX channels and contact addresses of its owner: + +- the link relation types `simplex-channel` and `simplex-contact`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field; +- the well-known URI `simplex.json`: a JSON document with the owner's channels and contact addresses, their audience, the update frequency of each channel and the protocols each contact address serves. + +People and software agents subscribe to a channel to receive updates, and connect to a contact address to send requests. [Agent protocols over SimpleX](./agent-protocols.md) defines the requests an agent sends to a contact address, and an `llms.txt` section with the same addresses. + +## Conventions + +The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT" and "MAY" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals. + +JSON schemas use JSON Type Definition [RFC8927]. + +## Terms + +- **SimpleX link**: a connection link of the SimpleX Agent protocol, in the full or the short syntax, with the `https` or the `simplex` scheme [AGENT]. +- **Channel link**: a short SimpleX link of type `c`, for example `https://smp12.simplex.im/c#`. +- **Contact address link**: a short SimpleX link of type `a`, for example `https://smp12.simplex.im/a#`, or a full SimpleX link of type `contact`. +- **SimpleX name**: a name in the SimpleX name service [NAMES]. `#example.simplex` is the name of a channel, `@example.simplex` the name of a contact address. +- **Owner**: the person or organization that publishes the website. + +## Link relation types + +### simplex-channel + +The target of a `simplex-channel` link is a SimpleX channel in which the owner of the link context publishes updates. The target MUST be a channel link. + +### simplex-contact + +The target of a `simplex-contact` link is a SimpleX contact address at which the owner of the link context accepts conversations and requests. The target MUST be a contact address link. + +### Target attributes + +- `title`: the name of the channel or the address, for display. In HTTP, a title with non-ASCII characters is sent as `title*` [RFC8187]. +- `hreflang`: the language of the channel's posts or of the conversations [BCP47]. + +A link context MAY have several links of one relation type. Their order is the owner's order of preference: document order in HTML, and the order of link values in HTTP. + +### HTML + +In the document head: + +```html + + +``` + +In the document body, the relation types are used on visible links: + +```html +Chat with us +``` + +### HTTP + +``` +Link: ; rel="simplex-channel"; hreflang="en"; title="Ridgeline updates" +Link: ; rel="simplex-contact"; hreflang="en"; title="Ask Ridgeline" +``` + +## Well-known URI + +### Retrieval + +The document is located at the path `/.well-known/simplex.json` of an origin [RFC8615]. A client retrieves it with an HTTPS `GET` request. A server responds with status 200, and SHOULD send the media type `application/json`. A client parses the response body as JSON, whatever media type the server sends. + +The entries of the document are the channels and contact addresses of the origin's owner. A site that controls only a path of its origin publishes link relations instead. + +### Format + +The document MUST conform to this JSON Type Definition schema [RFC8927]: + +```json +{ + "optionalProperties": { + "channels": {"elements": {"ref": "address"}}, + "contacts": {"elements": {"ref": "address"}} + }, + "additionalProperties": true, + "definitions": { + "address": { + "properties": { + "link": {"type": "string"} + }, + "optionalProperties": { + "name": {"type": "string"}, + "title": {"type": "string"}, + "lang": {"type": "string"}, + "audience": {"elements": {"type": "string"}}, + "changeFrequency": {"enum": ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]}, + "protocols": {"elements": {"type": "string"}} + }, + "additionalProperties": true + } + } +} +``` + +- `channels`: the owner's channels, in the owner's order of preference. +- `contacts`: the owner's contact addresses, in the owner's order of preference. +- `link`: required. A channel link in `channels`, a contact address link in `contacts`. +- `name`: the SimpleX name of the channel or the contact address of `link`. +- `title`: the name of the channel or the address, for display. +- `lang`: the language of the channel's posts or of the conversations [BCP47]. +- `audience`: who the channel or the address serves: `people`, `agents`, or both. +- `changeFrequency`: in `channels` only, how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. +- `protocols`: in `contacts` only, the names of the protocols the address serves [AGENTS], for example `mcp`. + +A site with only channels or only contact addresses omits the other list. + +### Example + +```json +{ + "channels": [ + { + "link": "https://smp12.simplex.im/c#…", + "name": "#ridgeline.simplex", + "title": "Ridgeline updates", + "lang": "en", + "audience": ["people", "agents"], + "changeFrequency": "weekly" + } + ], + "contacts": [ + { + "link": "https://smp12.simplex.im/a#…", + "name": "@ridgeline.simplex", + "title": "Ask Ridgeline", + "lang": "en", + "audience": ["agents"], + "protocols": ["mcp"] + } + ] +} +``` + +## Processing + +- A consumer MUST ignore unknown fields and unknown `audience` values. +- A consumer MUST ignore a link of a type other than the one its relation type or its list requires. +- The values on the website are hints. The data retrieved with the link is authoritative: an audience, an update frequency or protocols declared there [AGENTS] take precedence over the website's values. +- A consumer MAY connect by `link` or by `name`. Resolving a name reveals interest in it to one names router, and verifies that the name's record and the profile's claim match [NAMES]. + +## Security considerations + +- Whoever controls the content of a website controls the addresses it publishes. Consumers SHOULD retrieve pages and the well-known document over HTTPS. +- Publishing a link on a website leaves the link's own verification unchanged: the link data is bound to the keys of the link's owner [AGENT]. +- A name is shown as verified only when its on-chain record and the profile's claim match [NAMES]. +- Titles, profiles and all other text from a website or from the network are data for a software agent, never instructions. + +## Privacy considerations + +- The relation types and the well-known document are intended for owners who want to be reachable: channels, businesses and services. +- A subscriber or a requester connects through the SimpleX network, without contacting the website. Retrieving a page or the well-known document is an ordinary web request. + +## IANA considerations + +### Link relation types registration + +Two registrations in the "Link Relation Types" registry [RFC8288]: + +- Relation Name: `simplex-channel` +- Description: Refers to a SimpleX channel in which the owner of the link context publishes updates. +- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel +- Notes: The target is a SimpleX channel link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. + +- Relation Name: `simplex-contact` +- Description: Refers to a SimpleX contact address at which the owner of the link context accepts conversations and requests. +- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact +- Notes: The target is a SimpleX contact address link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. + +### Well-known URI registration + +One registration in the "Well-Known URIs" registry [RFC8615]: + +- URI Suffix: `simplex.json` +- Change Controller: Evgeny Poberezkin +- Specification Document: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#well-known-uri +- Status: provisional +- Related Information: SimpleX link syntax [AGENT]. + +## HTML link type extensions + +The HTML standard requires extension link types to be registered on the microformats page "existing-rel-values" [HTML] [MF-REL]. The entries: + +- Keyword: `simplex-channel` +- Effect on `link`: Hyperlink +- Effect on `a`, `area`: Hyperlink +- Effect on `form`: not allowed +- Brief description: Refers to a SimpleX channel in which the owner of the page publishes updates. +- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel +- Status: proposed + +- Keyword: `simplex-contact` +- Effect on `link`: Hyperlink +- Effect on `a`, `area`: Hyperlink +- Effect on `form`: not allowed +- Brief description: Refers to a SimpleX contact address at which the owner of the page accepts conversations and requests. +- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact +- Status: proposed + +## References + +- [AGENT] SimpleX Agent protocol, connection links: https://github.com/simplex-chat/simplexmq/blob/stable/protocol/agent-protocol.md#connection-link-1-time-invitation-and-contact-address +- [AGENTS] Agent protocols over SimpleX: [agent-protocols.md](./agent-protocols.md) +- [BCP47] Tags for Identifying Languages: https://www.rfc-editor.org/info/bcp47 +- [HTML] HTML Living Standard, other link types: https://html.spec.whatwg.org/multipage/links.html#other-link-types +- [MF-REL] Microformats, existing rel values: https://microformats.org/wiki/existing-rel-values +- [NAMES] SimpleX public names for channels and businesses: [names-overview.md](./names-overview.md) +- [RFC2119] Key words for use in RFCs to Indicate Requirement Levels: https://www.rfc-editor.org/rfc/rfc2119 +- [RFC8174] Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words: https://www.rfc-editor.org/rfc/rfc8174 +- [RFC8187] Indicating Character Encoding and Language for HTTP Header Field Parameters: https://www.rfc-editor.org/rfc/rfc8187 +- [RFC8288] Web Linking: https://www.rfc-editor.org/rfc/rfc8288 +- [RFC8615] Well-Known Uniform Resource Identifiers (URIs): https://www.rfc-editor.org/rfc/rfc8615 +- [RFC8927] JSON Type Definition: https://www.rfc-editor.org/rfc/rfc8927 +- [SITEMAPS] Sitemaps XML format: https://www.sitemaps.org/protocol.html From 03d789aed791bdeecde4fae788ceb55f3f1bd067 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 19 Oct 2026 14:28:35 +0100 Subject: [PATCH 04/11] website: add link relations, update spec (#7773) * website: add link relations, update spec * wellknown --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- docs/protocol/agent-protocols.md | 27 +++--- docs/protocol/web-discovery.md | 84 ++++++++++++++----- website/src/.well-known/simplex.json | 33 ++++++++ website/src/_includes/markdown-alternate.html | 5 ++ website/src/crowdfunding-news.html | 2 +- website/src/directory.html | 2 +- website/src/index.html | 2 +- website/src/js/directory.jsc | 2 +- website/src/llms/llms-txt.njk | 8 ++ website/src/news.html | 2 +- 10 files changed, 127 insertions(+), 40 deletions(-) create mode 100644 website/src/.well-known/simplex.json diff --git a/docs/protocol/agent-protocols.md b/docs/protocol/agent-protocols.md index 77c15ed1fa..86286bf54d 100644 --- a/docs/protocol/agent-protocols.md +++ b/docs/protocol/agent-protocols.md @@ -12,7 +12,7 @@ Evgeny Poberezkin - [Addresses in llms.txt](#addresses-in-llmstxt) - [Profile declarations](#profile-declarations) - [protocols](#protocols) - - [Channel audience and update frequency](#channel-audience-and-update-frequency) + - [Public group audience and update frequency](#public-group-audience-and-update-frequency) - [Contact address peers](#contact-address-peers) - [Protocol names](#protocol-names) - [Chat transport](#chat-transport) @@ -93,11 +93,12 @@ The notes begin with the network and the type of the address, then the audience ``` updates for [, ]. - contact for [, ]*. + group for . + [business ]contact for [, ]*. ``` - ``: the name of the network, `SimpleX` for SimpleX links. -- `updates`: an address that publishes updates. `contact`: an address that accepts conversations and requests. +- `updates`: an address that publishes updates. `group`: an address of a group, where members converse. `contact`: an address that accepts conversations and requests. `business contact`: a business address [BUSINESS]. - ``: `people`, `agents`, or `people and agents`. - ``: a `changefreq` value of the Sitemaps protocol [SITEMAPS]. - ``: the name of a protocol the address serves ([protocol names](#protocol-names)), in any letter case. @@ -105,19 +106,20 @@ The notes begin with the network and the type of the address, then the audience The network is named in the notes because the host in a SimpleX link is the router that hosts the address, and that router can have any domain name. -For SimpleX entries, the type `updates` requires a channel link, and the type `contact` a contact address link [WEB]. The link text is the title of the address. +For SimpleX entries, the type `updates` requires a channel link, the type `group` a group link, and the types `contact` and `business contact` a contact address link [WEB]. The link text is the title of the address. ``` ## Communications - [Ridgeline updates](https://smp12.simplex.im/c#…): SimpleX updates for people and agents, weekly. Trail conditions and closures. -- [Ask Ridgeline](https://smp12.simplex.im/a#…): SimpleX contact for agents, MCP. Bookings and availability. +- [Ridgeline hikers](https://smp12.simplex.im/g#…): SimpleX group for people. Routes and meetups. +- [Ask Ridgeline](https://smp12.simplex.im/a#…): SimpleX business contact for agents, MCP. Bookings and availability. - [Ridgeline support](mailto:help@ridgeline.example): Email contact for people. ``` ## Profile declarations -A profile or a group declares the protocols it serves in its preferences. A channel declares its audience and its update frequency in its public group record. A contact address declares the peers it serves in its link data. +A profile or a group declares the protocols it serves in its preferences. A channel or a public group declares its audience in its public group record, and a channel also its update frequency. A contact address declares the peers it serves in its link data. ### protocols @@ -168,9 +170,9 @@ Example, the `preferences` of a contact profile: } ``` -### Channel audience and update frequency +### Public group audience and update frequency -Two fields of the channel's public group record, `publicGroup` of its group profile [CHAT]. JSON Type Definition schema [RFC8927]: +Two fields of the public group record, `publicGroup` of the group profile of a channel or a public group [CHAT]. JSON Type Definition schema [RFC8927]: ```json { @@ -181,12 +183,12 @@ Two fields of the channel's public group record, `publicGroup` of its group prof } ``` -- `audience`: the peer types the channel is made for. The values are the values of a profile's `peerType`: `human`, `bot`, `business`, `agent`. An absent `audience` is `["human"]`. Agents also read content made for people, so a channel declares `agent` when it is made for agents, or has dedicated support for them. -- `changeFrequency`: how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. +- `audience`: the peer types the channel or the group is made for. The values are the values of a profile's `peerType`: `human`, `bot`, `business`, `agent`. An absent `audience` is `["human"]`. Agents also read content made for people, so a channel or a group declares `agent` when it is made for agents, or has dedicated support for them. +- `changeFrequency`: in a channel only, how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. ### Contact address peers -One field of the data published with a contact address link, beside `business`. JSON Type Definition schema [RFC8927]: +One field of the data published with a contact address link, beside `business`, which marks a business address [BUSINESS]. JSON Type Definition schema [RFC8927]: ```json { @@ -198,7 +200,7 @@ One field of the data published with a contact address link, beside `business`. - `serves`: the peer types the address is made for, with the values and the default of `audience`. -Clients MUST ignore unknown values of `audience` and `serves`. The values of the channel and the address take precedence over the values on a website [WEB]. +Clients MUST ignore unknown values of `audience` and `serves`. The values in the public group record and in the address link data take precedence over the values on a website [WEB]. ### Protocol names @@ -458,6 +460,7 @@ Planned: ## References +- [BUSINESS] Using SimpleX Chat in business: [BUSINESS.md](../BUSINESS.md) - [CHAT] SimpleX Chat protocol: [simplex-chat.md](./simplex-chat.md) - [LLMSTXT] The /llms.txt file: https://llmstxt.org - [MCP] Model Context Protocol specification, revision 2026-07-28: https://modelcontextprotocol.io/specification/2026-07-28 diff --git a/docs/protocol/web-discovery.md b/docs/protocol/web-discovery.md index 95bda8c2fb..58e85e6d22 100644 --- a/docs/protocol/web-discovery.md +++ b/docs/protocol/web-discovery.md @@ -11,7 +11,9 @@ Evgeny Poberezkin - [Terms](#terms) - [Link relation types](#link-relation-types) - [simplex-channel](#simplex-channel) + - [simplex-group](#simplex-group) - [simplex-contact](#simplex-contact) + - [Owner's and described addresses](#owners-and-described-addresses) - [Target attributes](#target-attributes) - [HTML](#html) - [HTTP](#http) @@ -30,12 +32,12 @@ Evgeny Poberezkin ## Abstract -This document defines how a website publishes the SimpleX channels and contact addresses of its owner: +This document defines how a website publishes SimpleX channels, groups and contact addresses: -- the link relation types `simplex-channel` and `simplex-contact`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field; -- the well-known URI `simplex.json`: a JSON document with the owner's channels and contact addresses, their audience, the update frequency of each channel and the protocols each contact address serves. +- the link relation types `simplex-channel`, `simplex-group` and `simplex-contact`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field. Their targets are the addresses of the website's owner, or the addresses a page describes, such as the entries of a directory; +- the well-known URI `simplex.json`: a JSON document with the owner's channels, groups and contact addresses, their audience, the update frequency of each channel, and the kind and the protocols of each contact address. -People and software agents subscribe to a channel to receive updates, and connect to a contact address to send requests. [Agent protocols over SimpleX](./agent-protocols.md) defines the requests an agent sends to a contact address, and an `llms.txt` section with the same addresses. +People and software agents subscribe to a channel to receive updates, join a group to take part in its conversations, and connect to a contact address to send requests. [Agent protocols over SimpleX](./agent-protocols.md) defines the requests an agent sends to a contact address, and an `llms.txt` section with the same addresses. ## Conventions @@ -47,6 +49,7 @@ JSON schemas use JSON Type Definition [RFC8927]. - **SimpleX link**: a connection link of the SimpleX Agent protocol, in the full or the short syntax, with the `https` or the `simplex` scheme [AGENT]. - **Channel link**: a short SimpleX link of type `c`, for example `https://smp12.simplex.im/c#`. +- **Group link**: a short SimpleX link of type `g`, for example `https://smp12.simplex.im/g#`, or a full SimpleX link of a group. - **Contact address link**: a short SimpleX link of type `a`, for example `https://smp12.simplex.im/a#`, or a full SimpleX link of type `contact`. - **SimpleX name**: a name in the SimpleX name service [NAMES]. `#example.simplex` is the name of a channel, `@example.simplex` the name of a contact address. - **Owner**: the person or organization that publishes the website. @@ -55,16 +58,24 @@ JSON schemas use JSON Type Definition [RFC8927]. ### simplex-channel -The target of a `simplex-channel` link is a SimpleX channel in which the owner of the link context publishes updates. The target MUST be a channel link. +The target of a `simplex-channel` link is a SimpleX channel of the owner of the link context, or a SimpleX channel described by the link context. The target MUST be a channel link. + +### simplex-group + +The target of a `simplex-group` link is a SimpleX group of the owner of the link context, or a SimpleX group described by the link context. The target MUST be a group link. ### simplex-contact -The target of a `simplex-contact` link is a SimpleX contact address at which the owner of the link context accepts conversations and requests. The target MUST be a contact address link. +The target of a `simplex-contact` link is a SimpleX contact address of the owner of the link context, or a SimpleX contact address described by the link context. The target MUST be a contact address link. + +### Owner's and described addresses + +A page that describes the addresses of others, such as a directory or a catalogue, uses the relation types on its entries. The owner's own addresses are listed in the [well-known document](#well-known-uri). ### Target attributes -- `title`: the name of the channel or the address, for display. In HTTP, a title with non-ASCII characters is sent as `title*` [RFC8187]. -- `hreflang`: the language of the channel's posts or of the conversations [BCP47]. +- `title`: the name of the channel, the group or the address, for display. In HTTP, a title with non-ASCII characters is sent as `title*` [RFC8187]. +- `hreflang`: the language of the channel's posts, or of the conversations [BCP47]. A link context MAY have several links of one relation type. Their order is the owner's order of preference: document order in HTML, and the order of link values in HTTP. @@ -77,10 +88,11 @@ In the document head: ``` -In the document body, the relation types are used on visible links: +In the document body, the relation types are used on visible links, for example on the entries of a directory: ```html Chat with us +Ridgeline hikers ``` ### HTTP @@ -96,7 +108,7 @@ Link: ; rel="simplex-contact"; hreflang="en"; ti The document is located at the path `/.well-known/simplex.json` of an origin [RFC8615]. A client retrieves it with an HTTPS `GET` request. A server responds with status 200, and SHOULD send the media type `application/json`. A client parses the response body as JSON, whatever media type the server sends. -The entries of the document are the channels and contact addresses of the origin's owner. A site that controls only a path of its origin publishes link relations instead. +The entries of the document are the channels, groups and contact addresses of the origin's owner. A site that controls only a path of its origin publishes link relations instead. ### Format @@ -106,6 +118,7 @@ The document MUST conform to this JSON Type Definition schema [RFC8927]: { "optionalProperties": { "channels": {"elements": {"ref": "address"}}, + "groups": {"elements": {"ref": "address"}}, "contacts": {"elements": {"ref": "address"}} }, "additionalProperties": true, @@ -120,6 +133,7 @@ The document MUST conform to this JSON Type Definition schema [RFC8927]: "lang": {"type": "string"}, "audience": {"elements": {"type": "string"}}, "changeFrequency": {"enum": ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]}, + "business": {"type": "boolean"}, "protocols": {"elements": {"type": "string"}} }, "additionalProperties": true @@ -129,16 +143,18 @@ The document MUST conform to this JSON Type Definition schema [RFC8927]: ``` - `channels`: the owner's channels, in the owner's order of preference. +- `groups`: the owner's groups, in the owner's order of preference. - `contacts`: the owner's contact addresses, in the owner's order of preference. -- `link`: required. A channel link in `channels`, a contact address link in `contacts`. -- `name`: the SimpleX name of the channel or the contact address of `link`. -- `title`: the name of the channel or the address, for display. -- `lang`: the language of the channel's posts or of the conversations [BCP47]. -- `audience`: who the channel or the address serves: `people`, `agents`, or both. +- `link`: required. A channel link in `channels`, a group link in `groups`, a contact address link in `contacts`. +- `name`: the SimpleX name of the channel, the group or the contact address of `link`. +- `title`: the name of the channel, the group or the address, for display. +- `lang`: the language of the channel's posts, or of the conversations [BCP47]. +- `audience`: who the channel, the group or the address serves: `people`, `agents`, or both. - `changeFrequency`: in `channels` only, how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. +- `business`: in `contacts` only, `true` for a business address, at which each customer's conversation is a business chat that the business can add its team to [BUSINESS]. - `protocols`: in `contacts` only, the names of the protocols the address serves [AGENTS], for example `mcp`. -A site with only channels or only contact addresses omits the other list. +A site omits the lists it does not use. ### Example @@ -154,6 +170,13 @@ A site with only channels or only contact addresses omits the other list. "changeFrequency": "weekly" } ], + "groups": [ + { + "link": "https://smp12.simplex.im/g#…", + "title": "Ridgeline hikers", + "lang": "en" + } + ], "contacts": [ { "link": "https://smp12.simplex.im/a#…", @@ -161,6 +184,7 @@ A site with only channels or only contact addresses omits the other list. "title": "Ask Ridgeline", "lang": "en", "audience": ["agents"], + "business": true, "protocols": ["mcp"] } ] @@ -171,7 +195,7 @@ A site with only channels or only contact addresses omits the other list. - A consumer MUST ignore unknown fields and unknown `audience` values. - A consumer MUST ignore a link of a type other than the one its relation type or its list requires. -- The values on the website are hints. The data retrieved with the link is authoritative: an audience, an update frequency or protocols declared there [AGENTS] take precedence over the website's values. +- The values on the website are hints. The data retrieved with the link is authoritative: an audience, an update frequency, the business flag or protocols declared there [AGENTS] take precedence over the website's values. - A consumer MAY connect by `link` or by `name`. Resolving a name reveals interest in it to one names router, and verifies that the name's record and the profile's claim match [NAMES]. ## Security considerations @@ -183,22 +207,27 @@ A site with only channels or only contact addresses omits the other list. ## Privacy considerations -- The relation types and the well-known document are intended for owners who want to be reachable: channels, businesses and services. +- The relation types and the well-known document are intended for owners who want to be reachable, such as channels, communities, businesses and services, and for pages that describe them, such as directories. - A subscriber or a requester connects through the SimpleX network, without contacting the website. Retrieving a page or the well-known document is an ordinary web request. ## IANA considerations ### Link relation types registration -Two registrations in the "Link Relation Types" registry [RFC8288]: +Three registrations in the "Link Relation Types" registry [RFC8288]: - Relation Name: `simplex-channel` -- Description: Refers to a SimpleX channel in which the owner of the link context publishes updates. +- Description: Refers to a SimpleX channel of the owner of the link context, or described by the link context. - Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel - Notes: The target is a SimpleX channel link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. +- Relation Name: `simplex-group` +- Description: Refers to a SimpleX group of the owner of the link context, or described by the link context. +- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-group +- Notes: The target is a SimpleX group link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. + - Relation Name: `simplex-contact` -- Description: Refers to a SimpleX contact address at which the owner of the link context accepts conversations and requests. +- Description: Refers to a SimpleX contact address of the owner of the link context, or described by the link context. - Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact - Notes: The target is a SimpleX contact address link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. @@ -220,15 +249,23 @@ The HTML standard requires extension link types to be registered on the microfor - Effect on `link`: Hyperlink - Effect on `a`, `area`: Hyperlink - Effect on `form`: not allowed -- Brief description: Refers to a SimpleX channel in which the owner of the page publishes updates. +- Brief description: Refers to a SimpleX channel of the page's owner, or described by the page. - Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel - Status: proposed +- Keyword: `simplex-group` +- Effect on `link`: Hyperlink +- Effect on `a`, `area`: Hyperlink +- Effect on `form`: not allowed +- Brief description: Refers to a SimpleX group of the page's owner, or described by the page. +- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-group +- Status: proposed + - Keyword: `simplex-contact` - Effect on `link`: Hyperlink - Effect on `a`, `area`: Hyperlink - Effect on `form`: not allowed -- Brief description: Refers to a SimpleX contact address at which the owner of the page accepts conversations and requests. +- Brief description: Refers to a SimpleX contact address of the page's owner, or described by the page. - Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact - Status: proposed @@ -237,6 +274,7 @@ The HTML standard requires extension link types to be registered on the microfor - [AGENT] SimpleX Agent protocol, connection links: https://github.com/simplex-chat/simplexmq/blob/stable/protocol/agent-protocol.md#connection-link-1-time-invitation-and-contact-address - [AGENTS] Agent protocols over SimpleX: [agent-protocols.md](./agent-protocols.md) - [BCP47] Tags for Identifying Languages: https://www.rfc-editor.org/info/bcp47 +- [BUSINESS] Using SimpleX Chat in business: [BUSINESS.md](../BUSINESS.md) - [HTML] HTML Living Standard, other link types: https://html.spec.whatwg.org/multipage/links.html#other-link-types - [MF-REL] Microformats, existing rel values: https://microformats.org/wiki/existing-rel-values - [NAMES] SimpleX public names for channels and businesses: [names-overview.md](./names-overview.md) diff --git a/website/src/.well-known/simplex.json b/website/src/.well-known/simplex.json new file mode 100644 index 0000000000..b7a1ba102b --- /dev/null +++ b/website/src/.well-known/simplex.json @@ -0,0 +1,33 @@ +{ + "channels": [ + { + "link": "https://smp18.simplex.im/c#grcfG3ulVI4Sh6ow33qBsmSk7uEy3gRSl2KkJ5ER6tA", + "title": "SimpleX Network News", + "changeFrequency": "weekly" + }, + { + "link": "https://smp10.simplex.im/c#q09nMBmWFGz1m2TvgfZFaEOG5D2a7Ma9mSkl6pHXEsg", + "title": "SimpleX Crowdfunding News", + "changeFrequency": "weekly" + } + ], + "groups": [ + { + "link": "https://smp4.simplex.im/g#hr4lvFeBmndWMKTwqiodPz3VBo_6UmdGWocXd1SupsM", + "title": "SimpleX users group" + } + ], + "contacts": [ + { + "link": "https://smp6.simplex.im/a#lrdvu2d8A1GumSmoKb2krQmtKhWXq-tyGpHuM7aMwsw", + "title": "Ask SimpleX Team", + "audience": ["people", "agents"], + "business": true + }, + { + "link": "https://smp4.simplex.im/a#lXUjJW5vHYQzoLYgmi8GbxkGP41_kjefFvBrdwg-0Ok", + "title": "SimpleX Directory", + "audience": ["people", "agents"] + } + ] +} diff --git a/website/src/_includes/markdown-alternate.html b/website/src/_includes/markdown-alternate.html index ad8301359c..cb4da7cb92 100644 --- a/website/src/_includes/markdown-alternate.html +++ b/website/src/_includes/markdown-alternate.html @@ -1,3 +1,8 @@ {% set markdownAlternateUrl = page | markdownAlternate %} {% if markdownAlternateUrl %}{% endif %} + + + +{% if active_directory %} +{% endif %} diff --git a/website/src/crowdfunding-news.html b/website/src/crowdfunding-news.html index 40294ee881..c703e03892 100644 --- a/website/src/crowdfunding-news.html +++ b/website/src/crowdfunding-news.html @@ -11,4 +11,4 @@ channel: - relay3.simplex.chat --- -

The news about SimpleX Chat equity crowdfunding on Wefunder, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Crowdfunding News. The crowdfunding memo: simplex.chat/wefunder.md.

+

The news about SimpleX Chat equity crowdfunding on Wefunder, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Crowdfunding News. The crowdfunding memo: simplex.chat/wefunder.md.

diff --git a/website/src/directory.html b/website/src/directory.html index 41dea04862..7725930769 100644 --- a/website/src/directory.html +++ b/website/src/directory.html @@ -318,7 +318,7 @@ active_directory: true

SimpleX Directory

Welcome to the selected users' communities that you can join via SimpleX Chat app.

-

SimpleX Directory is also available as a SimpleX chat bot.

+

SimpleX Directory is also available as a SimpleX chat bot.

Read about how to add your community.

diff --git a/website/src/index.html b/website/src/index.html index 31e6e086f8..bdb00f785d 100644 --- a/website/src/index.html +++ b/website/src/index.html @@ -273,7 +273,7 @@ active_home: true - + diff --git a/website/src/js/directory.jsc b/website/src/js/directory.jsc index dd07735260..61b19c4ab5 100644 --- a/website/src/js/directory.jsc +++ b/website/src/js/directory.jsc @@ -339,7 +339,7 @@ function displayEntries(entries) { imgLinkElement.href = groupLinkUri; } imgLinkElement.target = "_blank"; - imgLinkElement.rel = "noopener"; + imgLinkElement.rel = entryType?.groupType === 'channel' ? "noopener simplex-channel" : "noopener simplex-group"; imgLinkElement.title = `Join ${displayName}`; const imgElement = document.createElement('img'); diff --git a/website/src/llms/llms-txt.njk b/website/src/llms/llms-txt.njk index 9004a88675..acb87df202 100644 --- a/website/src/llms/llms-txt.njk +++ b/website/src/llms/llms-txt.njk @@ -57,6 +57,14 @@ Every page of simplex.chat is also available as Markdown: replace `.html` with ` - [SimpleX for business](https://simplex.chat/docs/business.md) - [Host your own messaging server](https://simplex.chat/docs/server.md) and [file server](https://simplex.chat/docs/xftp-server.md) +## Communications + +- [SimpleX Network News](https://smp18.simplex.im/c#grcfG3ulVI4Sh6ow33qBsmSk7uEy3gRSl2KkJ5ER6tA): SimpleX updates for people, weekly. Network technology, governance and development. +- [SimpleX Crowdfunding News](https://smp10.simplex.im/c#q09nMBmWFGz1m2TvgfZFaEOG5D2a7Ma9mSkl6pHXEsg): SimpleX updates for people, weekly. The equity crowdfunding on Wefunder. +- [SimpleX users group](https://smp4.simplex.im/g#hr4lvFeBmndWMKTwqiodPz3VBo_6UmdGWocXd1SupsM): SimpleX group for people. Users of SimpleX Chat. +- [Ask SimpleX Team](https://smp6.simplex.im/a#lrdvu2d8A1GumSmoKb2krQmtKhWXq-tyGpHuM7aMwsw): SimpleX business contact for people and agents. Questions to the SimpleX Chat team. +- [SimpleX Directory](https://smp4.simplex.im/a#lXUjJW5vHYQzoLYgmi8GbxkGP41_kjefFvBrdwg-0Ok): SimpleX contact for people and agents. Search for public groups and channels. + ## For AI agents and developers - [SimpleX for AI agents and developers](https://simplex.chat/llms/agents.md): a bot in five minutes, the API, building from source, OpenClaw, asking the team diff --git a/website/src/news.html b/website/src/news.html index 1c0558ff81..ef53b04c8d 100644 --- a/website/src/news.html +++ b/website/src/news.html @@ -12,4 +12,4 @@ channel: - relay3.simplex.chat --- -

The news about SimpleX Network technology, governance and anything related to its development, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Network News.

+

The news about SimpleX Network technology, governance and anything related to its development, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Network News.

From 7361a029ace8f9c397493ff62cf4227f1c4cd484 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 19 Oct 2026 14:31:21 +0100 Subject: [PATCH 05/11] docs: update web discovery --- docs/protocol/agent-protocols.md | 27 +++++----- docs/protocol/web-discovery.md | 84 +++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 35 deletions(-) diff --git a/docs/protocol/agent-protocols.md b/docs/protocol/agent-protocols.md index 77c15ed1fa..86286bf54d 100644 --- a/docs/protocol/agent-protocols.md +++ b/docs/protocol/agent-protocols.md @@ -12,7 +12,7 @@ Evgeny Poberezkin - [Addresses in llms.txt](#addresses-in-llmstxt) - [Profile declarations](#profile-declarations) - [protocols](#protocols) - - [Channel audience and update frequency](#channel-audience-and-update-frequency) + - [Public group audience and update frequency](#public-group-audience-and-update-frequency) - [Contact address peers](#contact-address-peers) - [Protocol names](#protocol-names) - [Chat transport](#chat-transport) @@ -93,11 +93,12 @@ The notes begin with the network and the type of the address, then the audience ``` updates for [, ]. - contact for [, ]*. + group for . + [business ]contact for [, ]*. ``` - ``: the name of the network, `SimpleX` for SimpleX links. -- `updates`: an address that publishes updates. `contact`: an address that accepts conversations and requests. +- `updates`: an address that publishes updates. `group`: an address of a group, where members converse. `contact`: an address that accepts conversations and requests. `business contact`: a business address [BUSINESS]. - ``: `people`, `agents`, or `people and agents`. - ``: a `changefreq` value of the Sitemaps protocol [SITEMAPS]. - ``: the name of a protocol the address serves ([protocol names](#protocol-names)), in any letter case. @@ -105,19 +106,20 @@ The notes begin with the network and the type of the address, then the audience The network is named in the notes because the host in a SimpleX link is the router that hosts the address, and that router can have any domain name. -For SimpleX entries, the type `updates` requires a channel link, and the type `contact` a contact address link [WEB]. The link text is the title of the address. +For SimpleX entries, the type `updates` requires a channel link, the type `group` a group link, and the types `contact` and `business contact` a contact address link [WEB]. The link text is the title of the address. ``` ## Communications - [Ridgeline updates](https://smp12.simplex.im/c#…): SimpleX updates for people and agents, weekly. Trail conditions and closures. -- [Ask Ridgeline](https://smp12.simplex.im/a#…): SimpleX contact for agents, MCP. Bookings and availability. +- [Ridgeline hikers](https://smp12.simplex.im/g#…): SimpleX group for people. Routes and meetups. +- [Ask Ridgeline](https://smp12.simplex.im/a#…): SimpleX business contact for agents, MCP. Bookings and availability. - [Ridgeline support](mailto:help@ridgeline.example): Email contact for people. ``` ## Profile declarations -A profile or a group declares the protocols it serves in its preferences. A channel declares its audience and its update frequency in its public group record. A contact address declares the peers it serves in its link data. +A profile or a group declares the protocols it serves in its preferences. A channel or a public group declares its audience in its public group record, and a channel also its update frequency. A contact address declares the peers it serves in its link data. ### protocols @@ -168,9 +170,9 @@ Example, the `preferences` of a contact profile: } ``` -### Channel audience and update frequency +### Public group audience and update frequency -Two fields of the channel's public group record, `publicGroup` of its group profile [CHAT]. JSON Type Definition schema [RFC8927]: +Two fields of the public group record, `publicGroup` of the group profile of a channel or a public group [CHAT]. JSON Type Definition schema [RFC8927]: ```json { @@ -181,12 +183,12 @@ Two fields of the channel's public group record, `publicGroup` of its group prof } ``` -- `audience`: the peer types the channel is made for. The values are the values of a profile's `peerType`: `human`, `bot`, `business`, `agent`. An absent `audience` is `["human"]`. Agents also read content made for people, so a channel declares `agent` when it is made for agents, or has dedicated support for them. -- `changeFrequency`: how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. +- `audience`: the peer types the channel or the group is made for. The values are the values of a profile's `peerType`: `human`, `bot`, `business`, `agent`. An absent `audience` is `["human"]`. Agents also read content made for people, so a channel or a group declares `agent` when it is made for agents, or has dedicated support for them. +- `changeFrequency`: in a channel only, how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. ### Contact address peers -One field of the data published with a contact address link, beside `business`. JSON Type Definition schema [RFC8927]: +One field of the data published with a contact address link, beside `business`, which marks a business address [BUSINESS]. JSON Type Definition schema [RFC8927]: ```json { @@ -198,7 +200,7 @@ One field of the data published with a contact address link, beside `business`. - `serves`: the peer types the address is made for, with the values and the default of `audience`. -Clients MUST ignore unknown values of `audience` and `serves`. The values of the channel and the address take precedence over the values on a website [WEB]. +Clients MUST ignore unknown values of `audience` and `serves`. The values in the public group record and in the address link data take precedence over the values on a website [WEB]. ### Protocol names @@ -458,6 +460,7 @@ Planned: ## References +- [BUSINESS] Using SimpleX Chat in business: [BUSINESS.md](../BUSINESS.md) - [CHAT] SimpleX Chat protocol: [simplex-chat.md](./simplex-chat.md) - [LLMSTXT] The /llms.txt file: https://llmstxt.org - [MCP] Model Context Protocol specification, revision 2026-07-28: https://modelcontextprotocol.io/specification/2026-07-28 diff --git a/docs/protocol/web-discovery.md b/docs/protocol/web-discovery.md index 95bda8c2fb..58e85e6d22 100644 --- a/docs/protocol/web-discovery.md +++ b/docs/protocol/web-discovery.md @@ -11,7 +11,9 @@ Evgeny Poberezkin - [Terms](#terms) - [Link relation types](#link-relation-types) - [simplex-channel](#simplex-channel) + - [simplex-group](#simplex-group) - [simplex-contact](#simplex-contact) + - [Owner's and described addresses](#owners-and-described-addresses) - [Target attributes](#target-attributes) - [HTML](#html) - [HTTP](#http) @@ -30,12 +32,12 @@ Evgeny Poberezkin ## Abstract -This document defines how a website publishes the SimpleX channels and contact addresses of its owner: +This document defines how a website publishes SimpleX channels, groups and contact addresses: -- the link relation types `simplex-channel` and `simplex-contact`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field; -- the well-known URI `simplex.json`: a JSON document with the owner's channels and contact addresses, their audience, the update frequency of each channel and the protocols each contact address serves. +- the link relation types `simplex-channel`, `simplex-group` and `simplex-contact`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field. Their targets are the addresses of the website's owner, or the addresses a page describes, such as the entries of a directory; +- the well-known URI `simplex.json`: a JSON document with the owner's channels, groups and contact addresses, their audience, the update frequency of each channel, and the kind and the protocols of each contact address. -People and software agents subscribe to a channel to receive updates, and connect to a contact address to send requests. [Agent protocols over SimpleX](./agent-protocols.md) defines the requests an agent sends to a contact address, and an `llms.txt` section with the same addresses. +People and software agents subscribe to a channel to receive updates, join a group to take part in its conversations, and connect to a contact address to send requests. [Agent protocols over SimpleX](./agent-protocols.md) defines the requests an agent sends to a contact address, and an `llms.txt` section with the same addresses. ## Conventions @@ -47,6 +49,7 @@ JSON schemas use JSON Type Definition [RFC8927]. - **SimpleX link**: a connection link of the SimpleX Agent protocol, in the full or the short syntax, with the `https` or the `simplex` scheme [AGENT]. - **Channel link**: a short SimpleX link of type `c`, for example `https://smp12.simplex.im/c#`. +- **Group link**: a short SimpleX link of type `g`, for example `https://smp12.simplex.im/g#`, or a full SimpleX link of a group. - **Contact address link**: a short SimpleX link of type `a`, for example `https://smp12.simplex.im/a#`, or a full SimpleX link of type `contact`. - **SimpleX name**: a name in the SimpleX name service [NAMES]. `#example.simplex` is the name of a channel, `@example.simplex` the name of a contact address. - **Owner**: the person or organization that publishes the website. @@ -55,16 +58,24 @@ JSON schemas use JSON Type Definition [RFC8927]. ### simplex-channel -The target of a `simplex-channel` link is a SimpleX channel in which the owner of the link context publishes updates. The target MUST be a channel link. +The target of a `simplex-channel` link is a SimpleX channel of the owner of the link context, or a SimpleX channel described by the link context. The target MUST be a channel link. + +### simplex-group + +The target of a `simplex-group` link is a SimpleX group of the owner of the link context, or a SimpleX group described by the link context. The target MUST be a group link. ### simplex-contact -The target of a `simplex-contact` link is a SimpleX contact address at which the owner of the link context accepts conversations and requests. The target MUST be a contact address link. +The target of a `simplex-contact` link is a SimpleX contact address of the owner of the link context, or a SimpleX contact address described by the link context. The target MUST be a contact address link. + +### Owner's and described addresses + +A page that describes the addresses of others, such as a directory or a catalogue, uses the relation types on its entries. The owner's own addresses are listed in the [well-known document](#well-known-uri). ### Target attributes -- `title`: the name of the channel or the address, for display. In HTTP, a title with non-ASCII characters is sent as `title*` [RFC8187]. -- `hreflang`: the language of the channel's posts or of the conversations [BCP47]. +- `title`: the name of the channel, the group or the address, for display. In HTTP, a title with non-ASCII characters is sent as `title*` [RFC8187]. +- `hreflang`: the language of the channel's posts, or of the conversations [BCP47]. A link context MAY have several links of one relation type. Their order is the owner's order of preference: document order in HTML, and the order of link values in HTTP. @@ -77,10 +88,11 @@ In the document head: ``` -In the document body, the relation types are used on visible links: +In the document body, the relation types are used on visible links, for example on the entries of a directory: ```html Chat with us +Ridgeline hikers ``` ### HTTP @@ -96,7 +108,7 @@ Link: ; rel="simplex-contact"; hreflang="en"; ti The document is located at the path `/.well-known/simplex.json` of an origin [RFC8615]. A client retrieves it with an HTTPS `GET` request. A server responds with status 200, and SHOULD send the media type `application/json`. A client parses the response body as JSON, whatever media type the server sends. -The entries of the document are the channels and contact addresses of the origin's owner. A site that controls only a path of its origin publishes link relations instead. +The entries of the document are the channels, groups and contact addresses of the origin's owner. A site that controls only a path of its origin publishes link relations instead. ### Format @@ -106,6 +118,7 @@ The document MUST conform to this JSON Type Definition schema [RFC8927]: { "optionalProperties": { "channels": {"elements": {"ref": "address"}}, + "groups": {"elements": {"ref": "address"}}, "contacts": {"elements": {"ref": "address"}} }, "additionalProperties": true, @@ -120,6 +133,7 @@ The document MUST conform to this JSON Type Definition schema [RFC8927]: "lang": {"type": "string"}, "audience": {"elements": {"type": "string"}}, "changeFrequency": {"enum": ["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]}, + "business": {"type": "boolean"}, "protocols": {"elements": {"type": "string"}} }, "additionalProperties": true @@ -129,16 +143,18 @@ The document MUST conform to this JSON Type Definition schema [RFC8927]: ``` - `channels`: the owner's channels, in the owner's order of preference. +- `groups`: the owner's groups, in the owner's order of preference. - `contacts`: the owner's contact addresses, in the owner's order of preference. -- `link`: required. A channel link in `channels`, a contact address link in `contacts`. -- `name`: the SimpleX name of the channel or the contact address of `link`. -- `title`: the name of the channel or the address, for display. -- `lang`: the language of the channel's posts or of the conversations [BCP47]. -- `audience`: who the channel or the address serves: `people`, `agents`, or both. +- `link`: required. A channel link in `channels`, a group link in `groups`, a contact address link in `contacts`. +- `name`: the SimpleX name of the channel, the group or the contact address of `link`. +- `title`: the name of the channel, the group or the address, for display. +- `lang`: the language of the channel's posts, or of the conversations [BCP47]. +- `audience`: who the channel, the group or the address serves: `people`, `agents`, or both. - `changeFrequency`: in `channels` only, how often the channel is updated. The values are the `changefreq` values of the Sitemaps protocol [SITEMAPS]: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`. +- `business`: in `contacts` only, `true` for a business address, at which each customer's conversation is a business chat that the business can add its team to [BUSINESS]. - `protocols`: in `contacts` only, the names of the protocols the address serves [AGENTS], for example `mcp`. -A site with only channels or only contact addresses omits the other list. +A site omits the lists it does not use. ### Example @@ -154,6 +170,13 @@ A site with only channels or only contact addresses omits the other list. "changeFrequency": "weekly" } ], + "groups": [ + { + "link": "https://smp12.simplex.im/g#…", + "title": "Ridgeline hikers", + "lang": "en" + } + ], "contacts": [ { "link": "https://smp12.simplex.im/a#…", @@ -161,6 +184,7 @@ A site with only channels or only contact addresses omits the other list. "title": "Ask Ridgeline", "lang": "en", "audience": ["agents"], + "business": true, "protocols": ["mcp"] } ] @@ -171,7 +195,7 @@ A site with only channels or only contact addresses omits the other list. - A consumer MUST ignore unknown fields and unknown `audience` values. - A consumer MUST ignore a link of a type other than the one its relation type or its list requires. -- The values on the website are hints. The data retrieved with the link is authoritative: an audience, an update frequency or protocols declared there [AGENTS] take precedence over the website's values. +- The values on the website are hints. The data retrieved with the link is authoritative: an audience, an update frequency, the business flag or protocols declared there [AGENTS] take precedence over the website's values. - A consumer MAY connect by `link` or by `name`. Resolving a name reveals interest in it to one names router, and verifies that the name's record and the profile's claim match [NAMES]. ## Security considerations @@ -183,22 +207,27 @@ A site with only channels or only contact addresses omits the other list. ## Privacy considerations -- The relation types and the well-known document are intended for owners who want to be reachable: channels, businesses and services. +- The relation types and the well-known document are intended for owners who want to be reachable, such as channels, communities, businesses and services, and for pages that describe them, such as directories. - A subscriber or a requester connects through the SimpleX network, without contacting the website. Retrieving a page or the well-known document is an ordinary web request. ## IANA considerations ### Link relation types registration -Two registrations in the "Link Relation Types" registry [RFC8288]: +Three registrations in the "Link Relation Types" registry [RFC8288]: - Relation Name: `simplex-channel` -- Description: Refers to a SimpleX channel in which the owner of the link context publishes updates. +- Description: Refers to a SimpleX channel of the owner of the link context, or described by the link context. - Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel - Notes: The target is a SimpleX channel link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. +- Relation Name: `simplex-group` +- Description: Refers to a SimpleX group of the owner of the link context, or described by the link context. +- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-group +- Notes: The target is a SimpleX group link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. + - Relation Name: `simplex-contact` -- Description: Refers to a SimpleX contact address at which the owner of the link context accepts conversations and requests. +- Description: Refers to a SimpleX contact address of the owner of the link context, or described by the link context. - Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact - Notes: The target is a SimpleX contact address link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. @@ -220,15 +249,23 @@ The HTML standard requires extension link types to be registered on the microfor - Effect on `link`: Hyperlink - Effect on `a`, `area`: Hyperlink - Effect on `form`: not allowed -- Brief description: Refers to a SimpleX channel in which the owner of the page publishes updates. +- Brief description: Refers to a SimpleX channel of the page's owner, or described by the page. - Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel - Status: proposed +- Keyword: `simplex-group` +- Effect on `link`: Hyperlink +- Effect on `a`, `area`: Hyperlink +- Effect on `form`: not allowed +- Brief description: Refers to a SimpleX group of the page's owner, or described by the page. +- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-group +- Status: proposed + - Keyword: `simplex-contact` - Effect on `link`: Hyperlink - Effect on `a`, `area`: Hyperlink - Effect on `form`: not allowed -- Brief description: Refers to a SimpleX contact address at which the owner of the page accepts conversations and requests. +- Brief description: Refers to a SimpleX contact address of the page's owner, or described by the page. - Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact - Status: proposed @@ -237,6 +274,7 @@ The HTML standard requires extension link types to be registered on the microfor - [AGENT] SimpleX Agent protocol, connection links: https://github.com/simplex-chat/simplexmq/blob/stable/protocol/agent-protocol.md#connection-link-1-time-invitation-and-contact-address - [AGENTS] Agent protocols over SimpleX: [agent-protocols.md](./agent-protocols.md) - [BCP47] Tags for Identifying Languages: https://www.rfc-editor.org/info/bcp47 +- [BUSINESS] Using SimpleX Chat in business: [BUSINESS.md](../BUSINESS.md) - [HTML] HTML Living Standard, other link types: https://html.spec.whatwg.org/multipage/links.html#other-link-types - [MF-REL] Microformats, existing rel values: https://microformats.org/wiki/existing-rel-values - [NAMES] SimpleX public names for channels and businesses: [names-overview.md](./names-overview.md) From 1ad402d06ba9adad89590d573ba50b7fb3f3d28f Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 19 Oct 2026 15:51:03 +0100 Subject: [PATCH 06/11] docs: update discovery spec (#7774) * docs: update discovery spec * rename --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- docs/protocol/agent-protocols.md | 22 +++--- docs/protocol/web-discovery.md | 112 +++++++++++-------------------- 2 files changed, 49 insertions(+), 85 deletions(-) diff --git a/docs/protocol/agent-protocols.md b/docs/protocol/agent-protocols.md index 86286bf54d..d4e828d677 100644 --- a/docs/protocol/agent-protocols.md +++ b/docs/protocol/agent-protocols.md @@ -37,7 +37,7 @@ Evgeny Poberezkin This document defines how software agents find SimpleX addresses and call the tools of other agents and bots over SimpleX: -- an `llms.txt` section with communication addresses, beside the link relation types and the well-known document of [Publishing SimpleX addresses on websites](./web-discovery.md); +- an `llms.txt` section with communication addresses, beside the link relation type and the well-known document of [Publishing SimpleX addresses on websites](./web-discovery.md); - declarations: the protocols a profile serves and their transports, the peer types a channel or a contact address is made for, and the update frequency of a channel; - the chat transport: the command `/_call` in a conversation; - the Service RPC transport: one request and one response, without a conversation, in a generic envelope; @@ -111,10 +111,10 @@ For SimpleX entries, the type `updates` requires a channel link, the type `group ``` ## Communications -- [Ridgeline updates](https://smp12.simplex.im/c#…): SimpleX updates for people and agents, weekly. Trail conditions and closures. -- [Ridgeline hikers](https://smp12.simplex.im/g#…): SimpleX group for people. Routes and meetups. -- [Ask Ridgeline](https://smp12.simplex.im/a#…): SimpleX business contact for agents, MCP. Bookings and availability. -- [Ridgeline support](mailto:help@ridgeline.example): Email contact for people. +- [Acme news](https://smp12.simplex.im/c#…): SimpleX updates for people and agents, weekly. Product news and offers. +- [Acme customers](https://smp12.simplex.im/g#…): SimpleX group for people. Questions and tips from customers. +- [Ask Acme](https://smp12.simplex.im/a#…): SimpleX business contact for agents, MCP. Orders and availability. +- [Acme support](mailto:support@acme.example): Email contact for people. ``` ## Profile declarations @@ -250,7 +250,7 @@ The requester's core sends the request with: /_service_request [timeout=] [sign_key=] ``` -- ``: a contact address link, full or short, or the SimpleX name of a contact address [NAMES]. +- ``: a contact address link, or the SimpleX name of a contact address [NAMES]. - `timeout`: how long the requester waits for the response. The default is 30 seconds. - `sign_key`: the requester's Ed25519 private key, for a [signed request](#signed-requests). - ``: the [request](#request), a JSON object. @@ -380,11 +380,11 @@ The reply: "tools": [ { "name": "availability", - "description": "Rooms available on a date", + "description": "Items in stock", "inputSchema": { "type": "object", - "properties": {"date": {"type": "string"}}, - "required": ["date"] + "properties": {"item": {"type": "string"}}, + "required": ["item"] } } ], @@ -405,7 +405,7 @@ Calling a tool over Service RPC, the request: "method": "tools/call", "params": { "name": "availability", - "arguments": {"date": "2026-11-02"}, + "arguments": {"item": "anvil"}, "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/clientCapabilities": {} @@ -424,7 +424,7 @@ The response: "id": 1, "result": { "resultType": "complete", - "content": [{"type": "text", "text": "Three rooms are available on 2026-11-02."}], + "content": [{"type": "text", "text": "12 anvils are in stock."}], "isError": false } } diff --git a/docs/protocol/web-discovery.md b/docs/protocol/web-discovery.md index 58e85e6d22..cc35a73ddc 100644 --- a/docs/protocol/web-discovery.md +++ b/docs/protocol/web-discovery.md @@ -9,10 +9,8 @@ Evgeny Poberezkin - [Abstract](#abstract) - [Conventions](#conventions) - [Terms](#terms) -- [Link relation types](#link-relation-types) - - [simplex-channel](#simplex-channel) - - [simplex-group](#simplex-group) - - [simplex-contact](#simplex-contact) +- [Link relation type](#link-relation-type) + - [simplex](#simplex) - [Owner's and described addresses](#owners-and-described-addresses) - [Target attributes](#target-attributes) - [HTML](#html) @@ -25,7 +23,7 @@ Evgeny Poberezkin - [Security considerations](#security-considerations) - [Privacy considerations](#privacy-considerations) - [IANA considerations](#iana-considerations) - - [Link relation types registration](#link-relation-types-registration) + - [Link relation type registration](#link-relation-type-registration) - [Well-known URI registration](#well-known-uri-registration) - [HTML link type extensions](#html-link-type-extensions) - [References](#references) @@ -34,7 +32,7 @@ Evgeny Poberezkin This document defines how a website publishes SimpleX channels, groups and contact addresses: -- the link relation types `simplex-channel`, `simplex-group` and `simplex-contact`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field. Their targets are the addresses of the website's owner, or the addresses a page describes, such as the entries of a directory; +- the link relation type `simplex`, for the HTML elements `link`, `a` and `area`, and for the HTTP `Link` header field. Its targets are the addresses of the website's owner, or the addresses a page describes, such as the entries of a directory; - the well-known URI `simplex.json`: a JSON document with the owner's channels, groups and contact addresses, their audience, the update frequency of each channel, and the kind and the protocols of each contact address. People and software agents subscribe to a channel to receive updates, join a group to take part in its conversations, and connect to a contact address to send requests. [Agent protocols over SimpleX](./agent-protocols.md) defines the requests an agent sends to a contact address, and an `llms.txt` section with the same addresses. @@ -47,59 +45,51 @@ JSON schemas use JSON Type Definition [RFC8927]. ## Terms -- **SimpleX link**: a connection link of the SimpleX Agent protocol, in the full or the short syntax, with the `https` or the `simplex` scheme [AGENT]. -- **Channel link**: a short SimpleX link of type `c`, for example `https://smp12.simplex.im/c#`. -- **Group link**: a short SimpleX link of type `g`, for example `https://smp12.simplex.im/g#`, or a full SimpleX link of a group. -- **Contact address link**: a short SimpleX link of type `a`, for example `https://smp12.simplex.im/a#`, or a full SimpleX link of type `contact`. +- **SimpleX link**: a short connection link of the SimpleX Agent protocol, with the `https` or the `simplex` scheme [AGENT]. The type in the link is the kind of the address. +- **Channel link**: a SimpleX link of type `c`, for example `https://smp12.simplex.im/c#`. +- **Group link**: a SimpleX link of type `g`, for example `https://smp12.simplex.im/g#`. +- **Contact address link**: a SimpleX link of type `a`, for example `https://smp12.simplex.im/a#`. - **SimpleX name**: a name in the SimpleX name service [NAMES]. `#example.simplex` is the name of a channel, `@example.simplex` the name of a contact address. - **Owner**: the person or organization that publishes the website. -## Link relation types +## Link relation type -### simplex-channel +### simplex -The target of a `simplex-channel` link is a SimpleX channel of the owner of the link context, or a SimpleX channel described by the link context. The target MUST be a channel link. - -### simplex-group - -The target of a `simplex-group` link is a SimpleX group of the owner of the link context, or a SimpleX group described by the link context. The target MUST be a group link. - -### simplex-contact - -The target of a `simplex-contact` link is a SimpleX contact address of the owner of the link context, or a SimpleX contact address described by the link context. The target MUST be a contact address link. +The target of a `simplex` link is a SimpleX channel, group or contact address of the owner of the link context, or described by the link context. The target MUST be a channel link, a group link or a contact address link. The type in the link tells which. ### Owner's and described addresses -A page that describes the addresses of others, such as a directory or a catalogue, uses the relation types on its entries. The owner's own addresses are listed in the [well-known document](#well-known-uri). +A page that describes the addresses of others, such as a directory or a catalogue, uses the relation type on its entries. The owner's own addresses are listed in the [well-known document](#well-known-uri). ### Target attributes - `title`: the name of the channel, the group or the address, for display. In HTTP, a title with non-ASCII characters is sent as `title*` [RFC8187]. - `hreflang`: the language of the channel's posts, or of the conversations [BCP47]. -A link context MAY have several links of one relation type. Their order is the owner's order of preference: document order in HTML, and the order of link values in HTTP. +A link context MAY have several `simplex` links of one kind. Their order is the owner's order of preference: document order in HTML, and the order of link values in HTTP. ### HTML In the document head: ```html - - + + ``` -In the document body, the relation types are used on visible links, for example on the entries of a directory: +In the document body, the relation type is used on visible links, for example on the entries of a directory: ```html -Chat with us -Ridgeline hikers +Chat with us +Acme customers ``` ### HTTP ``` -Link: ; rel="simplex-channel"; hreflang="en"; title="Ridgeline updates" -Link: ; rel="simplex-contact"; hreflang="en"; title="Ask Ridgeline" +Link: ; rel="simplex"; hreflang="en"; title="Acme news" +Link: ; rel="simplex"; hreflang="en"; title="Ask Acme" ``` ## Well-known URI @@ -163,8 +153,8 @@ A site omits the lists it does not use. "channels": [ { "link": "https://smp12.simplex.im/c#…", - "name": "#ridgeline.simplex", - "title": "Ridgeline updates", + "name": "#acme.simplex", + "title": "Acme news", "lang": "en", "audience": ["people", "agents"], "changeFrequency": "weekly" @@ -173,15 +163,15 @@ A site omits the lists it does not use. "groups": [ { "link": "https://smp12.simplex.im/g#…", - "title": "Ridgeline hikers", + "title": "Acme customers", "lang": "en" } ], "contacts": [ { "link": "https://smp12.simplex.im/a#…", - "name": "@ridgeline.simplex", - "title": "Ask Ridgeline", + "name": "@acme.simplex", + "title": "Ask Acme", "lang": "en", "audience": ["agents"], "business": true, @@ -194,7 +184,7 @@ A site omits the lists it does not use. ## Processing - A consumer MUST ignore unknown fields and unknown `audience` values. -- A consumer MUST ignore a link of a type other than the one its relation type or its list requires. +- A consumer MUST ignore a `simplex` link that is not a channel, group or contact address link, and an entry of `simplex.json` whose link is of a type other than its list requires. - The values on the website are hints. The data retrieved with the link is authoritative: an audience, an update frequency, the business flag or protocols declared there [AGENTS] take precedence over the website's values. - A consumer MAY connect by `link` or by `name`. Resolving a name reveals interest in it to one names router, and verifies that the name's record and the profile's claim match [NAMES]. @@ -207,29 +197,19 @@ A site omits the lists it does not use. ## Privacy considerations -- The relation types and the well-known document are intended for owners who want to be reachable, such as channels, communities, businesses and services, and for pages that describe them, such as directories. +- The relation type and the well-known document are intended for owners who want to be reachable, such as channels, communities, businesses and services, and for pages that describe them, such as directories. - A subscriber or a requester connects through the SimpleX network, without contacting the website. Retrieving a page or the well-known document is an ordinary web request. ## IANA considerations -### Link relation types registration +### Link relation type registration -Three registrations in the "Link Relation Types" registry [RFC8288]: +One registration in the "Link Relation Types" registry [RFC8288]: -- Relation Name: `simplex-channel` -- Description: Refers to a SimpleX channel of the owner of the link context, or described by the link context. -- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel -- Notes: The target is a SimpleX channel link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. - -- Relation Name: `simplex-group` -- Description: Refers to a SimpleX group of the owner of the link context, or described by the link context. -- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-group -- Notes: The target is a SimpleX group link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. - -- Relation Name: `simplex-contact` -- Description: Refers to a SimpleX contact address of the owner of the link context, or described by the link context. -- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact -- Notes: The target is a SimpleX contact address link [AGENT], with the `https` scheme or the provisionally registered `simplex` scheme. +- Relation Name: `simplex` +- Description: Refers to a SimpleX channel, group or contact address of the owner of the link context, or described by the link context. +- Reference: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex +- Notes: The target is a SimpleX short link [AGENT] of a channel, a group or a contact address, with the `https` scheme or the provisionally registered `simplex` scheme. ### Well-known URI registration @@ -243,35 +223,19 @@ One registration in the "Well-Known URIs" registry [RFC8615]: ## HTML link type extensions -The HTML standard requires extension link types to be registered on the microformats page "existing-rel-values" [HTML] [MF-REL]. The entries: +The HTML standard requires extension link types to be registered on the microformats page "existing-rel-values" [HTML] [MF-REL]. The entry: -- Keyword: `simplex-channel` +- Keyword: `simplex` - Effect on `link`: Hyperlink - Effect on `a`, `area`: Hyperlink - Effect on `form`: not allowed -- Brief description: Refers to a SimpleX channel of the page's owner, or described by the page. -- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-channel -- Status: proposed - -- Keyword: `simplex-group` -- Effect on `link`: Hyperlink -- Effect on `a`, `area`: Hyperlink -- Effect on `form`: not allowed -- Brief description: Refers to a SimpleX group of the page's owner, or described by the page. -- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-group -- Status: proposed - -- Keyword: `simplex-contact` -- Effect on `link`: Hyperlink -- Effect on `a`, `area`: Hyperlink -- Effect on `form`: not allowed -- Brief description: Refers to a SimpleX contact address of the page's owner, or described by the page. -- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex-contact +- Brief description: Refers to a SimpleX channel, group or contact address of the page's owner, or described by the page. +- Specification: https://github.com/simplex-chat/simplex-chat/blob/stable/docs/protocol/web-discovery.md#simplex - Status: proposed ## References -- [AGENT] SimpleX Agent protocol, connection links: https://github.com/simplex-chat/simplexmq/blob/stable/protocol/agent-protocol.md#connection-link-1-time-invitation-and-contact-address +- [AGENT] SimpleX Agent protocol, short connection links: https://github.com/simplex-chat/simplexmq/blob/stable/protocol/agent-protocol.md#short-connection-link-syntax - [AGENTS] Agent protocols over SimpleX: [agent-protocols.md](./agent-protocols.md) - [BCP47] Tags for Identifying Languages: https://www.rfc-editor.org/info/bcp47 - [BUSINESS] Using SimpleX Chat in business: [BUSINESS.md](../BUSINESS.md) From 82d44a951f82ed40c8bafd9dacd8e1a4c50eebff Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 19 Oct 2026 16:13:15 +0100 Subject: [PATCH 07/11] libs: 7.1.0-beta.6.5 --- packages/simplex-chat-nodejs/package.json | 4 ++-- packages/simplex-chat-nodejs/src/release.ts | 14 +++++++------- .../src/simplex_chat/_version.py | 16 ++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/simplex-chat-nodejs/package.json b/packages/simplex-chat-nodejs/package.json index eaf9de68fb..4771a480ea 100644 --- a/packages/simplex-chat-nodejs/package.json +++ b/packages/simplex-chat-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "simplex-chat", - "version": "7.1.0-beta.6.4", + "version": "7.1.0-beta.6.5", "main": "dist/index.js", "types": "dist/index.d.ts", "gypfile": false, @@ -24,7 +24,7 @@ "docs": "typedoc" }, "dependencies": { - "@simplex-chat/types": "^7.1.0-beta.6.1", + "@simplex-chat/types": "^7.1.0-beta.6.2", "extract-zip": "^2.0.1", "fast-deep-equal": "^3.1.3" }, diff --git a/packages/simplex-chat-nodejs/src/release.ts b/packages/simplex-chat-nodejs/src/release.ts index ad9dfa4c78..cc96e5b9f6 100644 --- a/packages/simplex-chat-nodejs/src/release.ts +++ b/packages/simplex-chat-nodejs/src/release.ts @@ -1,16 +1,16 @@ // Written by scripts/ci/packages_release.py from the simplex-chat-libs releases. -export const LIBS_RELEASE = "core-7.1.0.12" +export const LIBS_RELEASE = "core-7.1.0.14" export const ADDON_RELEASE: string | undefined = "nodejs-addon-308c0960c707" export const SHA256: {readonly [asset: string]: string} = { - "simplex-chat-libs-linux-aarch64.zip": "924b8e14eaba5e309fa584c4bfe598421cf64983d63790c1c45247f592f60055", - "simplex-chat-libs-linux-x86_64-postgres.zip": "323f908b45dde751d6082f8885d07d81fba8a5ab8b2df7d3b8ab3a87e56299e5", - "simplex-chat-libs-linux-x86_64.zip": "995409e963db408bcf896361b669944484f845b6744fefe479aebb0ca2e7c5cd", - "simplex-chat-libs-macos-aarch64.zip": "64464aa48b996e5a96b4aa651bc57bc69e412d3c3804b589c67bd9dc52faddbf", - "simplex-chat-libs-macos-x86_64.zip": "94664f96ff7a2cd5a419096e7e843928faa31cb3addb8869fa1e4b5a0d0b3629", - "simplex-chat-libs-windows-x86_64.zip": "2e10844e5c119921139c373ee2971341aceef7dc4198429d3be6b1570a009d15", + "simplex-chat-libs-linux-aarch64.zip": "375a6021f0c2e5d79b2cd192e17c6c22c8eae73f46e3758fba24ee5c26a671aa", + "simplex-chat-libs-linux-x86_64-postgres.zip": "c3e8a54d37801e685c6361c4e7e53e1f5163d3aa9195dcfe686b6cf90e668ef3", + "simplex-chat-libs-linux-x86_64.zip": "cc41ab9049d61be3b386d799fa9394f2870e8ae3b2b9b8745dd3f53ba99f211e", + "simplex-chat-libs-macos-aarch64.zip": "ac3a46004f25976f71b3ead8a20b9a01c170fe12f125a0a6799ae97ea990f234", + "simplex-chat-libs-macos-x86_64.zip": "0d85ffcccc3a6dfe3e0da18d0b7be9ef36ad27381f047ef66cfde67ee4a7e882", + "simplex-chat-libs-windows-x86_64.zip": "f1d1c7efc333a5ec5328a5843c9d3c9ef038fb2886c0b60ac12e87a5c1e85bbd", "simplex-chat-nodejs-linux-aarch64.node": "3636a82c56c48229e0326fa03d6ceb5c16dd3afbcc1e86337783dc53a7328d67", "simplex-chat-nodejs-linux-x86_64.node": "55f3479b03fdb57591179ed730da744f1aa362e7529df55221b60028415746e9", "simplex-chat-nodejs-macos-aarch64.node": "a3472872fb0816f435f52ee5471088e6545ebffb95e89fbf8e1ab82275f2d56e", diff --git a/packages/simplex-chat-python/src/simplex_chat/_version.py b/packages/simplex-chat-python/src/simplex_chat/_version.py index e5075096d4..0c2e45a340 100644 --- a/packages/simplex-chat-python/src/simplex_chat/_version.py +++ b/packages/simplex-chat-python/src/simplex_chat/_version.py @@ -4,13 +4,13 @@ Written by scripts/ci/packages_release.py prepare: it bumps the version and pins new library releases with the SHA-256 of their assets. """ -__version__ = "7.1.0b6.post1" # PEP 440 — read by hatchling for wheel metadata -LIBS_RELEASE = "core-7.1.0.12" # simplex-chat-libs release tag +__version__ = "7.1.0b6.post2" # PEP 440 — read by hatchling for wheel metadata +LIBS_RELEASE = "core-7.1.0.14" # simplex-chat-libs release tag LIBS_SHA256 = { - "simplex-chat-libs-linux-aarch64.zip": "924b8e14eaba5e309fa584c4bfe598421cf64983d63790c1c45247f592f60055", - "simplex-chat-libs-linux-x86_64-postgres.zip": "323f908b45dde751d6082f8885d07d81fba8a5ab8b2df7d3b8ab3a87e56299e5", - "simplex-chat-libs-linux-x86_64.zip": "995409e963db408bcf896361b669944484f845b6744fefe479aebb0ca2e7c5cd", - "simplex-chat-libs-macos-aarch64.zip": "64464aa48b996e5a96b4aa651bc57bc69e412d3c3804b589c67bd9dc52faddbf", - "simplex-chat-libs-macos-x86_64.zip": "94664f96ff7a2cd5a419096e7e843928faa31cb3addb8869fa1e4b5a0d0b3629", - "simplex-chat-libs-windows-x86_64.zip": "2e10844e5c119921139c373ee2971341aceef7dc4198429d3be6b1570a009d15", + "simplex-chat-libs-linux-aarch64.zip": "375a6021f0c2e5d79b2cd192e17c6c22c8eae73f46e3758fba24ee5c26a671aa", + "simplex-chat-libs-linux-x86_64-postgres.zip": "c3e8a54d37801e685c6361c4e7e53e1f5163d3aa9195dcfe686b6cf90e668ef3", + "simplex-chat-libs-linux-x86_64.zip": "cc41ab9049d61be3b386d799fa9394f2870e8ae3b2b9b8745dd3f53ba99f211e", + "simplex-chat-libs-macos-aarch64.zip": "ac3a46004f25976f71b3ead8a20b9a01c170fe12f125a0a6799ae97ea990f234", + "simplex-chat-libs-macos-x86_64.zip": "0d85ffcccc3a6dfe3e0da18d0b7be9ef36ad27381f047ef66cfde67ee4a7e882", + "simplex-chat-libs-windows-x86_64.zip": "f1d1c7efc333a5ec5328a5843c9d3c9ef038fb2886c0b60ac12e87a5c1e85bbd", } From fc3f3abd0e855e5bfb0c71281a6839d9687c09e9 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 19 Oct 2026 16:15:06 +0100 Subject: [PATCH 08/11] website: update link relations (#7775) Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- website/src/_includes/markdown-alternate.html | 10 +++++----- website/src/crowdfunding-news.html | 2 +- website/src/directory.html | 2 +- website/src/index.html | 2 +- website/src/js/directory.jsc | 2 +- website/src/news.html | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/website/src/_includes/markdown-alternate.html b/website/src/_includes/markdown-alternate.html index cb4da7cb92..f8367dd2ac 100644 --- a/website/src/_includes/markdown-alternate.html +++ b/website/src/_includes/markdown-alternate.html @@ -1,8 +1,8 @@ {% set markdownAlternateUrl = page | markdownAlternate %} {% if markdownAlternateUrl %}{% endif %} - - - -{% if active_directory %} -{% endif %} + + + +{% if active_directory %} +{% endif %} diff --git a/website/src/crowdfunding-news.html b/website/src/crowdfunding-news.html index c703e03892..dffcc9c5b3 100644 --- a/website/src/crowdfunding-news.html +++ b/website/src/crowdfunding-news.html @@ -11,4 +11,4 @@ channel: - relay3.simplex.chat --- -

The news about SimpleX Chat equity crowdfunding on Wefunder, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Crowdfunding News. The crowdfunding memo: simplex.chat/wefunder.md.

+

The news about SimpleX Chat equity crowdfunding on Wefunder, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Crowdfunding News. The crowdfunding memo: simplex.chat/wefunder.md.

diff --git a/website/src/directory.html b/website/src/directory.html index 7725930769..8ab3166dcd 100644 --- a/website/src/directory.html +++ b/website/src/directory.html @@ -318,7 +318,7 @@ active_directory: true

SimpleX Directory

Welcome to the selected users' communities that you can join via SimpleX Chat app.

-

SimpleX Directory is also available as a SimpleX chat bot.

+

SimpleX Directory is also available as a SimpleX chat bot.

Read about how to add your community.

diff --git a/website/src/index.html b/website/src/index.html index bdb00f785d..2925915a64 100644 --- a/website/src/index.html +++ b/website/src/index.html @@ -273,7 +273,7 @@ active_home: true - + diff --git a/website/src/js/directory.jsc b/website/src/js/directory.jsc index 61b19c4ab5..f12f1108d9 100644 --- a/website/src/js/directory.jsc +++ b/website/src/js/directory.jsc @@ -339,7 +339,7 @@ function displayEntries(entries) { imgLinkElement.href = groupLinkUri; } imgLinkElement.target = "_blank"; - imgLinkElement.rel = entryType?.groupType === 'channel' ? "noopener simplex-channel" : "noopener simplex-group"; + imgLinkElement.rel = groupLink.connShortLink ? "noopener simplex" : "noopener"; imgLinkElement.title = `Join ${displayName}`; const imgElement = document.createElement('img'); diff --git a/website/src/news.html b/website/src/news.html index ef53b04c8d..3c5bd5fdfb 100644 --- a/website/src/news.html +++ b/website/src/news.html @@ -12,4 +12,4 @@ channel: - relay3.simplex.chat --- -

The news about SimpleX Network technology, governance and anything related to its development, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Network News.

+

The news about SimpleX Network technology, governance and anything related to its development, published as a SimpleX channel. Join it in the SimpleX Chat app: SimpleX Network News.

From ba9269a3add4b552a2f81d0c604f343157633fb3 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 19 Oct 2026 17:54:57 +0100 Subject: [PATCH 09/11] desktop: fix race condition when searching for address (#7776) Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- .../chat/simplex/common/views/chatlist/ChatListView.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index 0615f6e5dd..52cb2b338b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -874,10 +874,10 @@ private fun ChatListSearchBar(listState: LazyListState, searchText: MutableState } else { val candidate = nameSearchCandidate(it.trim()) connectNameCandidate.value = candidate - // clear the previous match immediately so the list falls back to text search during the debounce, - // instead of showing a stale filtered chat while the new search runs - searchChatFilteredBySimplexLink.value = emptySet() if (candidate != null) { + // clear the previous match immediately so the list falls back to text search during the debounce, + // instead of showing a stale filtered chat while the new search runs + searchChatFilteredBySimplexLink.value = emptySet() // resolve the name locally on each keystroke, debounced; collectLatest cancels the in-flight // search when the next keystroke arrives. A bare name can be a contact or a channel, so search // both and filter every known chat found; drop the row only when both types are already known. From 46b2cf9f8505470077fc4c76728362ac7e488d11 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 19 Oct 2026 19:49:44 +0100 Subject: [PATCH 10/11] nodejs: add tests to library (#7694) * nodejs: add tests to library * new tests * remove message splitting * fix race * docs * replace extract-zip with yauzl that it wraps (high severity issue) * fix ci tests * skip server tests on macos and win unless SMP_SERVER is set * fix win test * fix win test --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- packages/simplex-chat-nodejs/README.md | 44 +++++ .../simplex-chat-nodejs/docs/Namespace.api.md | 1 + .../docs/api.Class.ChatApi.md | 161 +++++++++--------- .../docs/api.Class.ChatCommandError.md | 18 +- .../docs/api.Class.LiveMessage.md | 98 +++++++++++ .../docs/api.Enumeration.ConnReqType.md | 6 +- .../docs/api.Interface.BotAddressSettings.md | 8 +- .../docs/api.TypeAlias.DbConfig.md | 2 +- .../docs/api.TypeAlias.EventSubscriberFunc.md | 2 +- .../docs/api.TypeAlias.EventSubscribers.md | 2 +- .../api.Variable.defaultBotAddressSettings.md | 2 +- .../docs/bot.Function.run.md | 2 +- .../docs/bot.Function.subscribeChatItems.md | 4 +- .../docs/bot.Interface.BotConfig.md | 16 +- .../docs/bot.Interface.BotOptions.md | 20 +-- .../docs/bot.TypeAlias.BotDbOpts.md | 2 +- .../docs/core.Class.ChatAPIError.md | 20 +-- .../docs/core.Class.ChatInitError.md | 18 +- ...MigrationError.Interface.ErrorMigration.md | 8 +- ...rationError.Interface.ErrorNotADatabase.md | 6 +- ...ore.DBMigrationError.Interface.ErrorSQL.md | 8 +- ...tionError.Interface.InvalidConfirmation.md | 4 +- ...grationError.Interface.InvalidQueueSize.md | 4 +- .../core.DBMigrationError.TypeAlias.Tag.md | 2 +- .../core.Enumeration.MigrationConfirmation.md | 10 +- .../docs/core.Function.chatCloseStore.md | 2 +- .../docs/core.Function.chatDecryptFile.md | 2 +- .../docs/core.Function.chatEncryptFile.md | 2 +- .../docs/core.Function.chatMigrateInit.md | 2 +- .../docs/core.Function.chatReadFile.md | 2 +- .../docs/core.Function.chatRecvMsgWait.md | 2 +- .../docs/core.Function.chatSendCmd.md | 2 +- .../docs/core.Function.chatWriteFile.md | 2 +- .../docs/core.Function.loadLibrary.md | 2 +- .../docs/core.Interface.APIResult.md | 6 +- .../docs/core.Interface.CryptoArgs.md | 6 +- .../docs/core.Interface.UpMigration.md | 6 +- .../core.MTRError.Interface.MTREDifferent.md | 6 +- .../core.MTRError.Interface.MTRENoDown.md | 6 +- .../docs/core.MTRError.TypeAlias.Tag.md | 2 +- ...re.MigrationError.Interface.MEDowngrade.md | 6 +- ...core.MigrationError.Interface.MEUpgrade.md | 6 +- ...MigrationError.Interface.MigrationError.md | 6 +- .../docs/core.MigrationError.TypeAlias.Tag.md | 2 +- .../docs/core.TypeAlias.Backend.md | 2 +- .../docs/core.TypeAlias.DBMigrationError.md | 2 +- .../docs/core.TypeAlias.MTRError.md | 2 +- .../docs/core.TypeAlias.MigrationError.md | 2 +- .../docs/util.Function.botAddressSettings.md | 2 +- .../docs/util.Function.chatInfoName.md | 2 +- .../docs/util.Function.chatInfoRef.md | 2 +- .../docs/util.Function.ciBotCommand.md | 2 +- .../docs/util.Function.ciContentText.md | 2 +- .../docs/util.Function.contactAddressStr.md | 2 +- .../docs/util.Function.fromLocalProfile.md | 2 +- .../docs/util.Function.reactionText.md | 2 +- .../docs/util.Function.senderName.md | 2 +- .../docs/util.Interface.BotCommand.md | 6 +- packages/simplex-chat-nodejs/package.json | 9 +- packages/simplex-chat-nodejs/src/api.ts | 94 +++++++++- packages/simplex-chat-nodejs/src/libs.ts | 44 ++++- .../simplex-chat-nodejs/tests/api.test.ts | 66 ++++--- .../tests/api.unit.test.ts | 127 ++++++++++++++ .../simplex-chat-nodejs/tests/bot.test.ts | 32 +++- .../simplex-chat-nodejs/tests/core.test.ts | 26 ++- .../tests/fixtures/tls/ca.crt | 12 ++ .../tests/fixtures/tls/server.crt | 12 ++ .../tests/fixtures/tls/server.key | 4 + .../simplex-chat-nodejs/tests/libs.test.ts | 29 ++++ .../simplex-chat-nodejs/tests/live.test.ts | 68 ++++++++ .../simplex-chat-nodejs/tests/smpServer.ts | 118 +++++++++++++ packages/simplex-chat-nodejs/tests/zip.ts | 9 +- scripts/ci/package-tests.sh | 2 +- 73 files changed, 957 insertions(+), 265 deletions(-) create mode 100644 packages/simplex-chat-nodejs/docs/api.Class.LiveMessage.md create mode 100644 packages/simplex-chat-nodejs/tests/fixtures/tls/ca.crt create mode 100644 packages/simplex-chat-nodejs/tests/fixtures/tls/server.crt create mode 100644 packages/simplex-chat-nodejs/tests/fixtures/tls/server.key create mode 100644 packages/simplex-chat-nodejs/tests/live.test.ts create mode 100644 packages/simplex-chat-nodejs/tests/smpServer.ts diff --git a/packages/simplex-chat-nodejs/README.md b/packages/simplex-chat-nodejs/README.md index 6ccd26fc05..31338c5142 100644 --- a/packages/simplex-chat-nodejs/README.md +++ b/packages/simplex-chat-nodejs/README.md @@ -66,6 +66,33 @@ You can run it with: `npx ts-node ./examples/squaring-bot.ts` A larger example, with a business address and commands as keys: [SimpleX Calculator](../../apps/simplex-calculator-bot/). +## Live messages + +Live messages are shown to recipients while they are written, e.g. a streamed reply of a language model. + +`onMessage` handler for `bot.run` that streams a reply: + +```javascript +const {api, util} = require("simplex-chat") + +async function onMessage(ci, content, chat) { + const {chatType, chatId} = util.chatInfoRef(ci.chatInfo) + const live = new api.LiveMessage(chat, chatType, chatId) + let text = "" + for await (const delta of streamReply(content.text)) { + text += delta + live.update(text) + } + await live.end() +} +``` + +`streamReply` stands for your function that returns text fragments as an async iterable. + +`update` sends the first text at once, and then updates the message at most once per 3 seconds (`interval` parameter). `end` finalizes the message; a message without text is deleted. + +The underlying commands: `apiSendMessages` with `liveMessage = true` sends a live message, `apiUpdateChatItem` with `liveMessage = true` updates it, and with `liveMessage = false` finalizes it. + ## Native library `libsimplex` and the prebuilt Node.js add-on are downloaded on first use into the user cache (`$XDG_CACHE_HOME/simplex-chat` or `~/.cache/simplex-chat` on Linux, `~/Library/Caches/simplex-chat` on macOS, `%LOCALAPPDATA%\simplex-chat` on Windows), shared with the Python library. To download them ahead of time, for example in a Dockerfile, run as the user that runs the app: @@ -121,10 +148,27 @@ Library provides types and functions to: - create and accept invitations or connect with the contacts. - create and manage long-term user address, accepting connection requests automatically. - send, receive, delete and update messages, and add message reactions. +- send live messages, e.g. streamed replies. - create, join and manage group. - send and receive files. - etc. +## Tests + +```sh +npm install +npm run build +npm test +``` + +To run one test file: `npm test -- tests/core.test.ts`. + +A local SMP server is started for the API and bot tests. On Linux x86-64 and arm64, `smp-server` is downloaded from [simplexmq releases](https://github.com/simplex-chat/simplexmq/releases) to `node_modules/.cache/smp-server`. To use another build, or on other systems, set `SMP_SERVER` to the path of `smp-server`: + +```sh +SMP_SERVER=/path/to/smp-server npm test +``` + ## License [AGPL v3](./LICENSE) diff --git a/packages/simplex-chat-nodejs/docs/Namespace.api.md b/packages/simplex-chat-nodejs/docs/Namespace.api.md index 5771b8450e..14689c9279 100644 --- a/packages/simplex-chat-nodejs/docs/Namespace.api.md +++ b/packages/simplex-chat-nodejs/docs/Namespace.api.md @@ -17,6 +17,7 @@ You need to use it in bot event handlers, and for any other use cases. - [ChatApi](api.Class.ChatApi.md) - [ChatCommandError](api.Class.ChatCommandError.md) +- [LiveMessage](api.Class.LiveMessage.md) ## Interfaces diff --git a/packages/simplex-chat-nodejs/docs/api.Class.ChatApi.md b/packages/simplex-chat-nodejs/docs/api.Class.ChatApi.md index cf293f55d9..bfd2034d94 100644 --- a/packages/simplex-chat-nodejs/docs/api.Class.ChatApi.md +++ b/packages/simplex-chat-nodejs/docs/api.Class.ChatApi.md @@ -6,7 +6,7 @@ # Class: ChatApi -Defined in: [src/api.ts:95](../src/api.ts#L95) +Defined in: [src/api.ts:100](../api.ts#L100) Main API class for interacting with the chat core library. @@ -16,7 +16,7 @@ Main API class for interacting with the chat core library. > `protected` **ctrl\_**: `bigint` \| `undefined` -Defined in: [src/api.ts:101](../src/api.ts#L101) +Defined in: [src/api.ts:106](../api.ts#L106) ## Accessors @@ -26,7 +26,7 @@ Defined in: [src/api.ts:101](../src/api.ts#L101) > **get** **ctrl**(): `bigint` -Defined in: [src/api.ts:343](../src/api.ts#L343) +Defined in: [src/api.ts:345](../api.ts#L345) Chat controller reference @@ -42,7 +42,7 @@ Chat controller reference > **get** **initialized**(): `boolean` -Defined in: [src/api.ts:329](../src/api.ts#L329) +Defined in: [src/api.ts:331](../api.ts#L331) Chat controller is initialized @@ -58,7 +58,7 @@ Chat controller is initialized > **get** **started**(): `boolean` -Defined in: [src/api.ts:336](../src/api.ts#L336) +Defined in: [src/api.ts:338](../api.ts#L338) Chat controller is started @@ -72,7 +72,7 @@ Chat controller is started > **apiAcceptContactRequest**(`contactReqId`): `Promise`\<`Contact`\> -Defined in: [src/api.ts:770](../src/api.ts#L770) +Defined in: [src/api.ts:773](../api.ts#L773) Accept contact request. Network usage: interactive. @@ -93,7 +93,7 @@ Network usage: interactive. > **apiAcceptMember**(`groupId`, `groupMemberId`, `memberRole`): `Promise`\<`GroupMember`\> -Defined in: [src/api.ts:590](../src/api.ts#L590) +Defined in: [src/api.ts:593](../api.ts#L593) Accept group member. Requires Admin role. Network usage: background. @@ -122,7 +122,7 @@ Network usage: background. > **apiAddMember**(`groupId`, `contactId`, `memberRole`): `Promise`\<`GroupMember`\> -Defined in: [src/api.ts:560](../src/api.ts#L560) +Defined in: [src/api.ts:563](../api.ts#L563) Add contact to group. Requires bot to have Admin role. Network usage: interactive. @@ -151,7 +151,7 @@ Network usage: interactive. > **apiAddMemberViaGroup**(`groupId`, `viaGroupId`, `groupMemberId`, `memberRole`): `Promise`\<`GroupMember`\> -Defined in: [src/api.ts:570](../src/api.ts#L570) +Defined in: [src/api.ts:573](../api.ts#L573) Add member of another group to group. Requires bot to have Admin role in the group, and direct messages allowed for its role in another group. Network usage: interactive. @@ -184,7 +184,7 @@ Network usage: interactive. > **apiBlockMembersForAll**(`groupId`, `groupMemberIds`, `blocked`): `Promise`\<`void`\> -Defined in: [src/api.ts:610](../src/api.ts#L610) +Defined in: [src/api.ts:613](../api.ts#L613) Block members. Requires Moderator role. Network usage: background. @@ -213,7 +213,7 @@ Network usage: background. > **apiCancelFile**(`fileId`): `Promise`\<`void`\> -Defined in: [src/api.ts:550](../src/api.ts#L550) +Defined in: [src/api.ts:553](../api.ts#L553) Cancel file. Network usage: background. @@ -234,7 +234,7 @@ Network usage: background. > **apiChatItemReaction**(`chatType`, `chatId`, `chatItemId`, `add`, `reaction`): `Promise`\<`ACIReaction`\> -Defined in: [src/api.ts:523](../src/api.ts#L523) +Defined in: [src/api.ts:526](../api.ts#L526) Add/remove message reaction. Network usage: background. @@ -271,7 +271,7 @@ Network usage: background. > **apiConnect**(`userId`, `incognito`, `preparedLink`): `Promise`\<[`ConnReqType`](api.Enumeration.ConnReqType.md)\> -Defined in: [src/api.ts:739](../src/api.ts#L739) +Defined in: [src/api.ts:742](../api.ts#L742) Connect via prepared SimpleX link. The link can be 1-time invitation link, contact address or group link Network usage: interactive. @@ -300,7 +300,7 @@ Network usage: interactive. > **apiConnectActiveUser**(`connLink`): `Promise`\<[`ConnReqType`](api.Enumeration.ConnReqType.md)\> -Defined in: [src/api.ts:748](../src/api.ts#L748) +Defined in: [src/api.ts:751](../api.ts#L751) Connect via SimpleX link as string in the active user profile. Network usage: interactive. @@ -321,7 +321,7 @@ Network usage: interactive. > **apiConnectPlan**(`userId`, `connectionLink`): `Promise`\<\[`ConnectionPlan`, `CreatedConnLink`\]\> -Defined in: [src/api.ts:729](../src/api.ts#L729) +Defined in: [src/api.ts:732](../api.ts#L732) Determine SimpleX link type and if the bot is already connected via this link. Network usage: interactive. @@ -346,7 +346,7 @@ Network usage: interactive. > **apiCreateActiveUser**(`profile?`): `Promise`\<`User`\> -Defined in: [src/api.ts:907](../src/api.ts#L907) +Defined in: [src/api.ts:910](../api.ts#L910) Create new user profile Network usage: no. @@ -367,7 +367,7 @@ Network usage: no. > **apiCreateGroupLink**(`groupId`, `memberRole`): `Promise`\<`string`\> -Defined in: [src/api.ts:670](../src/api.ts#L670) +Defined in: [src/api.ts:673](../api.ts#L673) Create group link. Network usage: interactive. @@ -392,7 +392,7 @@ Network usage: interactive. > **apiCreateLink**(`userId`): `Promise`\<`string`\> -Defined in: [src/api.ts:716](../src/api.ts#L716) +Defined in: [src/api.ts:719](../api.ts#L719) Create 1-time invitation link. Network usage: interactive. @@ -413,7 +413,7 @@ Network usage: interactive. > **apiCreateMemberContact**(`groupId`, `groupMemberId`): `Promise`\<`Contact`\> -Defined in: [src/api.ts:973](../src/api.ts#L973) +Defined in: [src/api.ts:976](../api.ts#L976) Create a direct message contact with a group member. Returns the created contact. @@ -439,7 +439,7 @@ Network usage: interactive. > **apiCreateUserAddress**(`userId`): `Promise`\<`CreatedConnLink`\> -Defined in: [src/api.ts:360](../src/api.ts#L360) +Defined in: [src/api.ts:362](../api.ts#L362) Create bot address. Network usage: interactive. @@ -460,7 +460,7 @@ Network usage: interactive. > **apiDeleteChat**(`chatType`, `chatId`, `deleteMode?`): `Promise`\<`void`\> -Defined in: [src/api.ts:829](../src/api.ts#L829) +Defined in: [src/api.ts:832](../api.ts#L832) Delete chat. Network usage: background. @@ -489,7 +489,7 @@ Network usage: background. > **apiDeleteChatItems**(`chatType`, `chatId`, `chatItemIds`, `deleteMode`): `Promise`\<`ChatItemDeletion`[]\> -Defined in: [src/api.ts:498](../src/api.ts#L498) +Defined in: [src/api.ts:501](../api.ts#L501) Delete message. Network usage: background. @@ -522,7 +522,7 @@ Network usage: background. > **apiDeleteGroupLink**(`groupId`): `Promise`\<`void`\> -Defined in: [src/api.ts:692](../src/api.ts#L692) +Defined in: [src/api.ts:695](../api.ts#L695) Delete group link. Network usage: background. @@ -543,7 +543,7 @@ Network usage: background. > **apiDeleteMemberChatItem**(`groupId`, `chatItemIds`): `Promise`\<`ChatItemDeletion`[]\> -Defined in: [src/api.ts:513](../src/api.ts#L513) +Defined in: [src/api.ts:516](../api.ts#L516) Moderate message. Requires Moderator role (and higher than message author's). Network usage: background. @@ -568,7 +568,7 @@ Network usage: background. > **apiDeleteUser**(`userId`, `delSMPQueues`, `viewPwd?`): `Promise`\<`void`\> -Defined in: [src/api.ts:937](../src/api.ts#L937) +Defined in: [src/api.ts:940](../api.ts#L940) Delete user profile. Network usage: background. @@ -597,7 +597,7 @@ Network usage: background. > **apiDeleteUserAddress**(`userId`): `Promise`\<`void`\> -Defined in: [src/api.ts:370](../src/api.ts#L370) +Defined in: [src/api.ts:372](../api.ts#L372) Deletes a user address. Network usage: background. @@ -618,7 +618,7 @@ Network usage: background. > **apiGetActiveUser**(): `Promise`\<`User` \| `undefined`\> -Defined in: [src/api.ts:887](../src/api.ts#L887) +Defined in: [src/api.ts:890](../api.ts#L890) Get active user profile Network usage: no. @@ -633,7 +633,7 @@ Network usage: no. > **apiGetChat**(`chatType`, `chatId`, `count`): `Promise`\<`any`\> -Defined in: [src/api.ts:877](../src/api.ts#L877) +Defined in: [src/api.ts:880](../api.ts#L880) Get chat items. Network usage: no. @@ -662,7 +662,7 @@ Network usage: no. > **apiGetChats**(`userId`, `pagination`, `query?`, `pendingConnections?`): `Promise`\<`AChat`[]\> -Defined in: [src/api.ts:814](../src/api.ts#L814) +Defined in: [src/api.ts:817](../api.ts#L817) Get chat previews (paginated). Network usage: no. @@ -699,7 +699,7 @@ on large databases. > **apiGetGroupLink**(`groupId`): `Promise`\<`GroupLink`\> -Defined in: [src/api.ts:701](../src/api.ts#L701) +Defined in: [src/api.ts:704](../api.ts#L704) Get group link. Network usage: no. @@ -720,7 +720,7 @@ Network usage: no. > **apiGetGroupLinkStr**(`groupId`): `Promise`\<`string`\> -Defined in: [src/api.ts:707](../src/api.ts#L707) +Defined in: [src/api.ts:710](../api.ts#L710) #### Parameters @@ -738,7 +738,7 @@ Defined in: [src/api.ts:707](../src/api.ts#L707) > **apiGetUserAddress**(`userId`): `Promise`\<`UserContactLink` \| `undefined`\> -Defined in: [src/api.ts:380](../src/api.ts#L380) +Defined in: [src/api.ts:382](../api.ts#L382) Get bot address and settings. Network usage: no. @@ -759,7 +759,7 @@ Network usage: no. > **apiJoinGroup**(`groupId`): `Promise`\<`GroupInfo`\> -Defined in: [src/api.ts:580](../src/api.ts#L580) +Defined in: [src/api.ts:583](../api.ts#L583) Join group. Network usage: interactive. @@ -780,7 +780,7 @@ Network usage: interactive. > **apiLeaveGroup**(`groupId`): `Promise`\<`GroupInfo`\> -Defined in: [src/api.ts:630](../src/api.ts#L630) +Defined in: [src/api.ts:633](../api.ts#L633) Leave group. Network usage: background. @@ -801,7 +801,7 @@ Network usage: background. > **apiListContacts**(`userId`): `Promise`\<`Contact`[]\> -Defined in: [src/api.ts:790](../src/api.ts#L790) +Defined in: [src/api.ts:793](../api.ts#L793) Get contacts. Network usage: no. @@ -822,7 +822,7 @@ Network usage: no. > **apiListGroups**(`userId`, `contactId?`, `search?`): `Promise`\<`GroupInfo`[]\> -Defined in: [src/api.ts:800](../src/api.ts#L800) +Defined in: [src/api.ts:803](../api.ts#L803) Get groups. Network usage: no. @@ -851,7 +851,7 @@ Network usage: no. > **apiListMembers**(`groupId`): `Promise`\<`GroupMember`[]\> -Defined in: [src/api.ts:640](../src/api.ts#L640) +Defined in: [src/api.ts:643](../api.ts#L643) Get group members. Network usage: no. @@ -872,7 +872,7 @@ Network usage: no. > **apiListUsers**(): `Promise`\<`UserInfo`[]\> -Defined in: [src/api.ts:917](../src/api.ts#L917) +Defined in: [src/api.ts:920](../api.ts#L920) Get all user profiles Network usage: no. @@ -887,7 +887,7 @@ Network usage: no. > **apiNewGroup**(`userId`, `groupProfile`): `Promise`\<`GroupInfo`\> -Defined in: [src/api.ts:650](../src/api.ts#L650) +Defined in: [src/api.ts:653](../api.ts#L653) Create group. Network usage: no. @@ -912,7 +912,7 @@ Network usage: no. > **apiReceiveFile**(`fileId`): `Promise`\<`AChatItem`\> -Defined in: [src/api.ts:539](../src/api.ts#L539) +Defined in: [src/api.ts:542](../api.ts#L542) Receive file. Network usage: no. @@ -933,7 +933,7 @@ Network usage: no. > **apiRejectContactRequest**(`contactReqId`): `Promise`\<`void`\> -Defined in: [src/api.ts:780](../src/api.ts#L780) +Defined in: [src/api.ts:783](../api.ts#L783) Reject contact request. The user who sent the request is **not notified**. Network usage: no. @@ -954,7 +954,7 @@ Network usage: no. > **apiRemoveMembers**(`groupId`, `memberIds`, `withMessages?`): `Promise`\<`GroupMember`[]\> -Defined in: [src/api.ts:620](../src/api.ts#L620) +Defined in: [src/api.ts:623](../api.ts#L623) Remove members. Requires Admin role. Network usage: background. @@ -983,7 +983,7 @@ Network usage: background. > **apiSendMemberContactInvitation**(`contactId`, `message?`): `Promise`\<`Contact`\> -Defined in: [src/api.ts:984](../src/api.ts#L984) +Defined in: [src/api.ts:987](../api.ts#L987) Send a direct message invitation to a group member contact. The contact must have been created with [apiCreateMemberContact](#apicreatemembercontact). @@ -1009,7 +1009,7 @@ Network usage: interactive. > **apiSendMessages**(`chat`, `messages`, `liveMessage?`): `Promise`\<`AChatItem`[]\> -Defined in: [src/api.ts:442](../src/api.ts#L442) +Defined in: [src/api.ts:444](../api.ts#L444) Send messages. Network usage: background. @@ -1038,7 +1038,7 @@ Network usage: background. > **apiSendTextMessage**(`chat`, `text`, `inReplyTo?`): `Promise`\<`AChatItem`[]\> -Defined in: [src/api.ts:465](../src/api.ts#L465) +Defined in: [src/api.ts:467](../api.ts#L467) Send text message. Network usage: background. @@ -1067,7 +1067,7 @@ Network usage: background. > **apiSendTextReply**(`chatItem`, `text`): `Promise`\<`AChatItem`[]\> -Defined in: [src/api.ts:473](../src/api.ts#L473) +Defined in: [src/api.ts:475](../api.ts#L475) Send text message in reply to received message. Network usage: background. @@ -1092,7 +1092,7 @@ Network usage: background. > **apiSetActiveUser**(`userId`, `viewPwd?`): `Promise`\<`User`\> -Defined in: [src/api.ts:927](../src/api.ts#L927) +Defined in: [src/api.ts:930](../api.ts#L930) Set active user profile Network usage: no. @@ -1117,7 +1117,7 @@ Network usage: no. > **apiSetAddressSettings**(`userId`, `__namedParameters`): `Promise`\<`void`\> -Defined in: [src/api.ts:414](../src/api.ts#L414) +Defined in: [src/api.ts:416](../api.ts#L416) Set bot address settings. Network usage: interactive. @@ -1142,7 +1142,7 @@ Network usage: interactive. > **apiSetAutoAcceptMemberContacts**(`userId`, `onOff`): `Promise`\<`void`\> -Defined in: [src/api.ts:866](../src/api.ts#L866) +Defined in: [src/api.ts:869](../api.ts#L869) Set auto-accept member contacts. Network usage: no. @@ -1167,7 +1167,7 @@ Network usage: no. > **apiSetContactCustomData**(`contactId`, `customData?`): `Promise`\<`void`\> -Defined in: [src/api.ts:856](../src/api.ts#L856) +Defined in: [src/api.ts:859](../api.ts#L859) Set contact custom data. Network usage: no. @@ -1192,7 +1192,7 @@ Network usage: no. > **apiSetContactPrefs**(`contactId`, `preferences`): `Promise`\<`void`\> -Defined in: [src/api.ts:963](../src/api.ts#L963) +Defined in: [src/api.ts:966](../api.ts#L966) Configure chat preference overrides for the contact. Network usage: background. @@ -1217,7 +1217,7 @@ Network usage: background. > **apiSetGroupCustomData**(`groupId`, `customData?`): `Promise`\<`void`\> -Defined in: [src/api.ts:846](../src/api.ts#L846) +Defined in: [src/api.ts:849](../api.ts#L849) Set group custom data. Network usage: no. @@ -1242,7 +1242,7 @@ Network usage: no. > **apiSetGroupLinkMemberRole**(`groupId`, `memberRole`): `Promise`\<`void`\> -Defined in: [src/api.ts:683](../src/api.ts#L683) +Defined in: [src/api.ts:686](../api.ts#L686) Set member role for group link. Network usage: no. @@ -1267,7 +1267,7 @@ Network usage: no. > **apiSetMembersRole**(`groupId`, `groupMemberIds`, `memberRole`): `Promise`\<`void`\> -Defined in: [src/api.ts:600](../src/api.ts#L600) +Defined in: [src/api.ts:603](../api.ts#L603) Set members role. Requires Admin role. Network usage: background. @@ -1296,7 +1296,7 @@ Network usage: background. > **apiSetProfileAddress**(`userId`, `enable`): `Promise`\<`UserProfileUpdateSummary`\> -Defined in: [src/api.ts:398](../src/api.ts#L398) +Defined in: [src/api.ts:400](../api.ts#L400) Add address to bot profile. Network usage: interactive. @@ -1321,7 +1321,7 @@ Network usage: interactive. > **apiSetUserDomain**(`userId`, `simplexDomain?`): `Promise`\<`User`\> -Defined in: [src/api.ts:427](../src/api.ts#L427) +Defined in: [src/api.ts:429](../api.ts#L429) #### Parameters @@ -1341,11 +1341,12 @@ Defined in: [src/api.ts:427](../src/api.ts#L427) ### apiUpdateChatItem() -> **apiUpdateChatItem**(`chatType`, `chatId`, `chatItemId`, `msgContent`, `liveMessage`): `Promise`\<`ChatItem`\> +> **apiUpdateChatItem**(`chatType`, `chatId`, `chatItemId`, `msgContent`, `liveMessage?`): `Promise`\<`ChatItem`\> -Defined in: [src/api.ts:481](../src/api.ts#L481) +Defined in: [src/api.ts:484](../api.ts#L484) Update message. +A live message stays live with `liveMessage = true` and is finalized with `liveMessage = false`. Network usage: background. #### Parameters @@ -1366,9 +1367,9 @@ Network usage: background. `MsgContent` -##### liveMessage +##### liveMessage? -`false` +`boolean` = `false` #### Returns @@ -1380,7 +1381,7 @@ Network usage: background. > **apiUpdateGroupProfile**(`groupId`, `groupProfile`): `Promise`\<`GroupInfo`\> -Defined in: [src/api.ts:660](../src/api.ts#L660) +Defined in: [src/api.ts:663](../api.ts#L663) Update group profile. Network usage: background. @@ -1405,7 +1406,7 @@ Network usage: background. > **apiUpdateProfile**(`userId`, `profile`): `Promise`\<`UserProfileUpdateSummary` \| `undefined`\> -Defined in: [src/api.ts:947](../src/api.ts#L947) +Defined in: [src/api.ts:950](../api.ts#L950) Update user profile. Network usage: background. @@ -1430,7 +1431,7 @@ Network usage: background. > **close**(): `Promise`\<`void`\> -Defined in: [src/api.ts:157](../src/api.ts#L157) +Defined in: [src/api.ts:162](../api.ts#L162) Stop chat controller and close chat database. The database is not closed if stopping fails. @@ -1446,7 +1447,7 @@ Usually doesn't need to be called in chat bots. > **off**\<`K`\>(`event`, `subscriber?`): `void` -Defined in: [src/api.ts:301](../src/api.ts#L301) +Defined in: [src/api.ts:303](../api.ts#L303) Unsubscribe all or a specific handler from a specific event. @@ -1466,7 +1467,7 @@ The event type to unsubscribe from. ##### subscriber? -[`EventSubscriberFunc`](api.TypeAlias.EventSubscriberFunc.md)\<`K`\> \| `undefined` +[`EventSubscriberFunc`](api.TypeAlias.EventSubscriberFunc.md)\<`K`\> \| `undefined` = `undefined` An optional subscriber function for the event. @@ -1480,7 +1481,7 @@ An optional subscriber function for the event. > **offAny**(`receiver?`): `void` -Defined in: [src/api.ts:317](../src/api.ts#L317) +Defined in: [src/api.ts:319](../api.ts#L319) Unsubscribe all or a specific handler from any events. @@ -1488,7 +1489,7 @@ Unsubscribe all or a specific handler from any events. ##### receiver? -[`EventSubscriberFunc`](api.TypeAlias.EventSubscriberFunc.md)\<`Tag`\> \| `undefined` +[`EventSubscriberFunc`](api.TypeAlias.EventSubscriberFunc.md)\<`Tag`\> \| `undefined` = `undefined` An optional subscriber function for the event. @@ -1504,7 +1505,7 @@ An optional subscriber function for the event. > **on**\<`K`\>(`subscribers`): `void` -Defined in: [src/api.ts:211](../src/api.ts#L211) +Defined in: [src/api.ts:211](../api.ts#L211) Subscribe multiple event handlers at once. @@ -1534,7 +1535,7 @@ If the same function is subscribed to event. > **on**\<`K`\>(`event`, `subscriber`): `void` -Defined in: [src/api.ts:219](../src/api.ts#L219) +Defined in: [src/api.ts:219](../api.ts#L219) Subscribe a handler to a specific event. @@ -1572,7 +1573,7 @@ If the same function is subscribed to event. > **onAny**(`receiver`): `void` -Defined in: [src/api.ts:242](../src/api.ts#L242) +Defined in: [src/api.ts:242](../api.ts#L242) Subscribe a handler to any event. @@ -1598,7 +1599,7 @@ If the same function is subscribed to event. > **once**\<`K`\>(`event`, `subscriber`): `void` -Defined in: [src/api.ts:253](../src/api.ts#L253) +Defined in: [src/api.ts:253](../api.ts#L253) Subscribe a handler to a specific event to be delivered one time. @@ -1636,7 +1637,7 @@ If the same function is subscribed to event. > **recvChatEvent**(`wait?`): `Promise`\<`ChatEvent` \| `undefined`\> -Defined in: [src/api.ts:352](../src/api.ts#L352) +Defined in: [src/api.ts:354](../api.ts#L354) #### Parameters @@ -1654,7 +1655,7 @@ Defined in: [src/api.ts:352](../src/api.ts#L352) > **sendChatCmd**(`cmd`): `Promise`\<`ChatResponse`\> -Defined in: [src/api.ts:348](../src/api.ts#L348) +Defined in: [src/api.ts:350](../api.ts#L350) #### Parameters @@ -1672,7 +1673,7 @@ Defined in: [src/api.ts:348](../src/api.ts#L348) > **startChat**(): `Promise`\<`void`\> -Defined in: [src/api.ts:123](../src/api.ts#L123) +Defined in: [src/api.ts:128](../api.ts#L128) Start chat controller. Must be called with the existing user profile. @@ -1686,7 +1687,7 @@ Start chat controller. Must be called with the existing user profile. > **stopChat**(): `Promise`\<`void`\> -Defined in: [src/api.ts:146](../src/api.ts#L146) +Defined in: [src/api.ts:151](../api.ts#L151) Stop chat controller. `close` calls it before closing the database. @@ -1704,7 +1705,7 @@ Usually doesn't need to be called in chat bots. > **wait**\<`K`\>(`event`): `Promise`\<`ChatEvent` & `object`\> -Defined in: [src/api.ts:261](../src/api.ts#L261) +Defined in: [src/api.ts:261](../api.ts#L261) Waits for specific event, with an optional predicate. Returns `undefined` on timeout if specified. @@ -1729,7 +1730,7 @@ Returns `undefined` on timeout if specified. > **wait**\<`K`\>(`event`, `predicate`): `Promise`\<`ChatEvent` & `object`\> -Defined in: [src/api.ts:262](../src/api.ts#L262) +Defined in: [src/api.ts:262](../api.ts#L262) Waits for specific event, with an optional predicate. Returns `undefined` on timeout if specified. @@ -1758,7 +1759,7 @@ Returns `undefined` on timeout if specified. > **wait**\<`K`\>(`event`, `timeout`): `Promise`\ -Defined in: [src/api.ts:263](../src/api.ts#L263) +Defined in: [src/api.ts:263](../api.ts#L263) Waits for specific event, with an optional predicate. Returns `undefined` on timeout if specified. @@ -1787,7 +1788,7 @@ Returns `undefined` on timeout if specified. > **wait**\<`K`\>(`event`, `predicate`, `timeout`): `Promise`\ -Defined in: [src/api.ts:264](../src/api.ts#L264) +Defined in: [src/api.ts:264](../api.ts#L264) Waits for specific event, with an optional predicate. Returns `undefined` on timeout if specified. @@ -1822,7 +1823,7 @@ Returns `undefined` on timeout if specified. > `static` **init**(`db`, `confirm?`, `queueSize?`): `Promise`\<`ChatApi`\> -Defined in: [src/api.ts:109](../src/api.ts#L109) +Defined in: [src/api.ts:114](../api.ts#L114) Initializes the ChatApi, loading libsimplex for `db.type` (downloaded on first use). diff --git a/packages/simplex-chat-nodejs/docs/api.Class.ChatCommandError.md b/packages/simplex-chat-nodejs/docs/api.Class.ChatCommandError.md index 5ec1d40540..5d6e0a50fb 100644 --- a/packages/simplex-chat-nodejs/docs/api.Class.ChatCommandError.md +++ b/packages/simplex-chat-nodejs/docs/api.Class.ChatCommandError.md @@ -6,7 +6,7 @@ # Class: ChatCommandError -Defined in: [src/api.ts:5](../src/api.ts#L5) +Defined in: [src/api.ts:5](../api.ts#L5) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/api.ts:5](../src/api.ts#L5) > **new ChatCommandError**(`message`, `response`): `ChatCommandError` -Defined in: [src/api.ts:6](../src/api.ts#L6) +Defined in: [src/api.ts:6](../api.ts#L6) #### Parameters @@ -44,7 +44,7 @@ Defined in: [src/api.ts:6](../src/api.ts#L6) > **message**: `string` -Defined in: [src/api.ts:6](../src/api.ts#L6) +Defined in: [src/api.ts:6](../api.ts#L6) #### Inherited from @@ -56,7 +56,7 @@ Defined in: [src/api.ts:6](../src/api.ts#L6) > **name**: `string` -Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../node_modules/typescript/lib/lib.es5.d.ts#L1076) +Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../../node_modules/typescript/lib/lib.es5.d.ts#L1076) #### Inherited from @@ -68,7 +68,7 @@ Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../node_modules/typ > **response**: `ChatResponse` -Defined in: [src/api.ts:6](../src/api.ts#L6) +Defined in: [src/api.ts:6](../api.ts#L6) *** @@ -76,7 +76,7 @@ Defined in: [src/api.ts:6](../src/api.ts#L6) > `optional` **stack?**: `string` -Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../node_modules/typescript/lib/lib.es5.d.ts#L1078) +Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../../node_modules/typescript/lib/lib.es5.d.ts#L1078) #### Inherited from @@ -88,7 +88,7 @@ Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../node_modules/typ > `static` **stackTraceLimit**: `number` -Defined in: [node\_modules/@types/node/globals.d.ts:67](../node_modules/@types/node/globals.d.ts#L67) +Defined in: [node\_modules/@types/node/globals.d.ts:67](../../node_modules/@types/node/globals.d.ts#L67) The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or @@ -110,7 +110,7 @@ not capture any frames. > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` -Defined in: [node\_modules/@types/node/globals.d.ts:51](../node_modules/@types/node/globals.d.ts#L51) +Defined in: [node\_modules/@types/node/globals.d.ts:51](../../node_modules/@types/node/globals.d.ts#L51) Creates a `.stack` property on `targetObject`, which when accessed returns a string representing the location in the code at which @@ -180,7 +180,7 @@ a(); > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` -Defined in: [node\_modules/@types/node/globals.d.ts:55](../node_modules/@types/node/globals.d.ts#L55) +Defined in: [node\_modules/@types/node/globals.d.ts:55](../../node_modules/@types/node/globals.d.ts#L55) #### Parameters diff --git a/packages/simplex-chat-nodejs/docs/api.Class.LiveMessage.md b/packages/simplex-chat-nodejs/docs/api.Class.LiveMessage.md new file mode 100644 index 0000000000..9f7f57dd5c --- /dev/null +++ b/packages/simplex-chat-nodejs/docs/api.Class.LiveMessage.md @@ -0,0 +1,98 @@ +[**simplex-chat**](README.md) + +*** + +[simplex-chat](README.md) / [api](Namespace.api.md) / LiveMessage + +# Class: LiveMessage + +Defined in: [src/api.ts:1005](../api.ts#L1005) + +Text shown as a live message while it is written, e.g. a streamed reply. + +## Constructors + +### Constructor + +> **new LiveMessage**(`chat`, `chatType`, `chatId`, `interval?`, `onError?`): `LiveMessage` + +Defined in: [src/api.ts:1019](../api.ts#L1019) + +#### Parameters + +##### chat + +`Pick`\<[`ChatApi`](api.Class.ChatApi.md), `"apiSendMessages"` \| `"apiUpdateChatItem"` \| `"apiDeleteChatItems"`\> + +Chat API, or an object with its methods to send, update and delete messages. + +##### chatType + +`ChatType` + +Chat type. + +##### chatId + +`number` + +Contact or group ID. + +##### interval? + +`number` = `3000` + +Minimum interval between updates of the live message, in milliseconds. + +##### onError? + +(`error`) => `void` + +Error handler for sending, updating and deleting messages. Errors are logged when it is omitted. + +#### Returns + +`LiveMessage` + +## Methods + +### end() + +> **end**(`text?`): `Promise`\<`void`\> + +Defined in: [src/api.ts:1049](../api.ts#L1049) + +Finalize the live message with the text, or with the last text when omitted. +A message without text is deleted. The next update starts a new live message. +The returned promise resolves after all queued commands complete. + +#### Parameters + +##### text? + +`string` = `...` + +#### Returns + +`Promise`\<`void`\> + +*** + +### update() + +> **update**(`text`): `void` + +Defined in: [src/api.ts:1031](../api.ts#L1031) + +Set the text of the live message. +The first text is sent at once; later texts update the message at most once per interval. + +#### Parameters + +##### text + +`string` + +#### Returns + +`void` diff --git a/packages/simplex-chat-nodejs/docs/api.Enumeration.ConnReqType.md b/packages/simplex-chat-nodejs/docs/api.Enumeration.ConnReqType.md index dcabb85b67..dc5fb0d63e 100644 --- a/packages/simplex-chat-nodejs/docs/api.Enumeration.ConnReqType.md +++ b/packages/simplex-chat-nodejs/docs/api.Enumeration.ConnReqType.md @@ -6,7 +6,7 @@ # Enumeration: ConnReqType -Defined in: [src/api.ts:15](../src/api.ts#L15) +Defined in: [src/api.ts:15](../api.ts#L15) Connection request types. @@ -16,7 +16,7 @@ Connection request types. > **Contact**: `"contact"` -Defined in: [src/api.ts:17](../src/api.ts#L17) +Defined in: [src/api.ts:17](../api.ts#L17) *** @@ -24,4 +24,4 @@ Defined in: [src/api.ts:17](../src/api.ts#L17) > **Invitation**: `"invitation"` -Defined in: [src/api.ts:16](../src/api.ts#L16) +Defined in: [src/api.ts:16](../api.ts#L16) diff --git a/packages/simplex-chat-nodejs/docs/api.Interface.BotAddressSettings.md b/packages/simplex-chat-nodejs/docs/api.Interface.BotAddressSettings.md index 23c5e35326..f0c9521c6b 100644 --- a/packages/simplex-chat-nodejs/docs/api.Interface.BotAddressSettings.md +++ b/packages/simplex-chat-nodejs/docs/api.Interface.BotAddressSettings.md @@ -6,7 +6,7 @@ # Interface: BotAddressSettings -Defined in: [src/api.ts:23](../src/api.ts#L23) +Defined in: [src/api.ts:23](../api.ts#L23) Bot address settings. @@ -16,7 +16,7 @@ Bot address settings. > `optional` **autoAccept?**: `boolean` -Defined in: [src/api.ts:28](../src/api.ts#L28) +Defined in: [src/api.ts:28](../api.ts#L28) Automatically accept contact requests. @@ -32,7 +32,7 @@ true > `optional` **businessAddress?**: `boolean` -Defined in: [src/api.ts:41](../src/api.ts#L41) +Defined in: [src/api.ts:41](../api.ts#L41) Business contact address. For all requests business chats will be created where other participants can be added. @@ -49,7 +49,7 @@ false > `optional` **welcomeMessage?**: `string` \| `MsgContent` -Defined in: [src/api.ts:34](../src/api.ts#L34) +Defined in: [src/api.ts:34](../api.ts#L34) Optional welcome message to show before connection to the users. diff --git a/packages/simplex-chat-nodejs/docs/api.TypeAlias.DbConfig.md b/packages/simplex-chat-nodejs/docs/api.TypeAlias.DbConfig.md index 26934a9c6b..114392d8ba 100644 --- a/packages/simplex-chat-nodejs/docs/api.TypeAlias.DbConfig.md +++ b/packages/simplex-chat-nodejs/docs/api.TypeAlias.DbConfig.md @@ -8,7 +8,7 @@ > **DbConfig** = \{ `encryptionKey?`: `string`; `filePrefix`: `string`; `type`: `"sqlite"`; \} \| \{ `connectionString`: `string`; `schemaPrefix?`: `string`; `type`: `"postgres"`; \} -Defined in: [src/api.ts:63](../src/api.ts#L63) +Defined in: [src/api.ts:63](../api.ts#L63) Database configuration. `type` selects the libsimplex backend loaded by `ChatApi.init`; one backend per process. diff --git a/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscriberFunc.md b/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscriberFunc.md index 13efcec7dd..103ee471e0 100644 --- a/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscriberFunc.md +++ b/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscriberFunc.md @@ -8,7 +8,7 @@ > **EventSubscriberFunc**\<`K`\> = (`event`, `chat`) => `void` \| `Promise`\<`void`\> -Defined in: [src/api.ts:50](../src/api.ts#L50) +Defined in: [src/api.ts:50](../api.ts#L50) ## Type Parameters diff --git a/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscribers.md b/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscribers.md index 3b63d11bd4..6f694c79aa 100644 --- a/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscribers.md +++ b/packages/simplex-chat-nodejs/docs/api.TypeAlias.EventSubscribers.md @@ -8,4 +8,4 @@ > **EventSubscribers** = `{ [K in CEvt.Tag]?: EventSubscriberFunc }` -Defined in: [src/api.ts:52](../src/api.ts#L52) +Defined in: [src/api.ts:52](../api.ts#L52) diff --git a/packages/simplex-chat-nodejs/docs/api.Variable.defaultBotAddressSettings.md b/packages/simplex-chat-nodejs/docs/api.Variable.defaultBotAddressSettings.md index f076ee0fad..29352adcfc 100644 --- a/packages/simplex-chat-nodejs/docs/api.Variable.defaultBotAddressSettings.md +++ b/packages/simplex-chat-nodejs/docs/api.Variable.defaultBotAddressSettings.md @@ -8,4 +8,4 @@ > `const` **defaultBotAddressSettings**: [`BotAddressSettings`](api.Interface.BotAddressSettings.md) -Defined in: [src/api.ts:44](../src/api.ts#L44) +Defined in: [src/api.ts:44](../api.ts#L44) diff --git a/packages/simplex-chat-nodejs/docs/bot.Function.run.md b/packages/simplex-chat-nodejs/docs/bot.Function.run.md index bc31ad01a8..3a9ab5feb4 100644 --- a/packages/simplex-chat-nodejs/docs/bot.Function.run.md +++ b/packages/simplex-chat-nodejs/docs/bot.Function.run.md @@ -8,7 +8,7 @@ > **run**(`__namedParameters`): `Promise`\<\[[`ChatApi`](api.Class.ChatApi.md), `User`, `UserContactLink` \| `undefined`\]\> -Defined in: [src/bot.ts:49](../src/bot.ts#L49) +Defined in: [src/bot.ts:49](../bot.ts#L49) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/bot.Function.subscribeChatItems.md b/packages/simplex-chat-nodejs/docs/bot.Function.subscribeChatItems.md index 0bcc49cbc6..c66aa388ac 100644 --- a/packages/simplex-chat-nodejs/docs/bot.Function.subscribeChatItems.md +++ b/packages/simplex-chat-nodejs/docs/bot.Function.subscribeChatItems.md @@ -8,7 +8,7 @@ > **subscribeChatItems**(`bot`, `onMessage`, `commands`): `void` -Defined in: [src/bot.ts:110](../src/bot.ts#L110) +Defined in: [src/bot.ts:110](../bot.ts#L110) ## Parameters @@ -22,6 +22,8 @@ Defined in: [src/bot.ts:110](../src/bot.ts#L110) ### commands +\{\[`key`: `string`\]: ((`chatItem`, `command`, `chat`) => `void` \| `Promise`\<`void`\>) \| `undefined`; \} + ## Returns `void` diff --git a/packages/simplex-chat-nodejs/docs/bot.Interface.BotConfig.md b/packages/simplex-chat-nodejs/docs/bot.Interface.BotConfig.md index 036afe64d4..ddb2106627 100644 --- a/packages/simplex-chat-nodejs/docs/bot.Interface.BotConfig.md +++ b/packages/simplex-chat-nodejs/docs/bot.Interface.BotConfig.md @@ -6,7 +6,7 @@ # Interface: BotConfig -Defined in: [src/bot.ts:36](../src/bot.ts#L36) +Defined in: [src/bot.ts:36](../bot.ts#L36) ## Properties @@ -14,7 +14,7 @@ Defined in: [src/bot.ts:36](../src/bot.ts#L36) > **dbOpts**: [`BotDbOpts`](bot.TypeAlias.BotDbOpts.md) -Defined in: [src/bot.ts:39](../src/bot.ts#L39) +Defined in: [src/bot.ts:39](../bot.ts#L39) *** @@ -22,7 +22,7 @@ Defined in: [src/bot.ts:39](../src/bot.ts#L39) > `optional` **events?**: [`EventSubscribers`](api.TypeAlias.EventSubscribers.md) -Defined in: [src/bot.ts:46](../src/bot.ts#L46) +Defined in: [src/bot.ts:46](../bot.ts#L46) *** @@ -30,7 +30,7 @@ Defined in: [src/bot.ts:46](../src/bot.ts#L46) > `optional` **onCommands?**: `object` -Defined in: [src/bot.ts:43](../src/bot.ts#L43) +Defined in: [src/bot.ts:43](../bot.ts#L43) #### Index Signature @@ -42,7 +42,7 @@ Defined in: [src/bot.ts:43](../src/bot.ts#L43) > `optional` **onMessage?**: (`chatItem`, `content`, `chat`) => `void` \| `Promise`\<`void`\> -Defined in: [src/bot.ts:41](../src/bot.ts#L41) +Defined in: [src/bot.ts:41](../bot.ts#L41) #### Parameters @@ -68,7 +68,7 @@ Defined in: [src/bot.ts:41](../src/bot.ts#L41) > **options**: [`BotOptions`](bot.Interface.BotOptions.md) -Defined in: [src/bot.ts:40](../src/bot.ts#L40) +Defined in: [src/bot.ts:40](../bot.ts#L40) *** @@ -76,7 +76,7 @@ Defined in: [src/bot.ts:40](../src/bot.ts#L40) > **profile**: `Profile` -Defined in: [src/bot.ts:37](../src/bot.ts#L37) +Defined in: [src/bot.ts:37](../bot.ts#L37) *** @@ -84,4 +84,4 @@ Defined in: [src/bot.ts:37](../src/bot.ts#L37) > `optional` **simplexDomain?**: `string` \| `null` -Defined in: [src/bot.ts:38](../src/bot.ts#L38) +Defined in: [src/bot.ts:38](../bot.ts#L38) diff --git a/packages/simplex-chat-nodejs/docs/bot.Interface.BotOptions.md b/packages/simplex-chat-nodejs/docs/bot.Interface.BotOptions.md index c5d5df5193..74e5fd6295 100644 --- a/packages/simplex-chat-nodejs/docs/bot.Interface.BotOptions.md +++ b/packages/simplex-chat-nodejs/docs/bot.Interface.BotOptions.md @@ -6,7 +6,7 @@ # Interface: BotOptions -Defined in: [src/bot.ts:12](../src/bot.ts#L12) +Defined in: [src/bot.ts:12](../bot.ts#L12) ## Properties @@ -14,7 +14,7 @@ Defined in: [src/bot.ts:12](../src/bot.ts#L12) > `optional` **addressSettings?**: [`BotAddressSettings`](api.Interface.BotAddressSettings.md) -Defined in: [src/bot.ts:16](../src/bot.ts#L16) +Defined in: [src/bot.ts:16](../bot.ts#L16) *** @@ -22,7 +22,7 @@ Defined in: [src/bot.ts:16](../src/bot.ts#L16) > `optional` **allowFiles?**: `boolean` -Defined in: [src/bot.ts:17](../src/bot.ts#L17) +Defined in: [src/bot.ts:17](../bot.ts#L17) *** @@ -30,7 +30,7 @@ Defined in: [src/bot.ts:17](../src/bot.ts#L17) > `optional` **commands?**: `ChatBotCommand`[] -Defined in: [src/bot.ts:18](../src/bot.ts#L18) +Defined in: [src/bot.ts:18](../bot.ts#L18) *** @@ -38,7 +38,7 @@ Defined in: [src/bot.ts:18](../src/bot.ts#L18) > `optional` **createAddress?**: `boolean` -Defined in: [src/bot.ts:13](../src/bot.ts#L13) +Defined in: [src/bot.ts:13](../bot.ts#L13) *** @@ -46,7 +46,7 @@ Defined in: [src/bot.ts:13](../src/bot.ts#L13) > `optional` **logContacts?**: `boolean` -Defined in: [src/bot.ts:20](../src/bot.ts#L20) +Defined in: [src/bot.ts:20](../bot.ts#L20) *** @@ -54,7 +54,7 @@ Defined in: [src/bot.ts:20](../src/bot.ts#L20) > `optional` **logNetwork?**: `boolean` -Defined in: [src/bot.ts:21](../src/bot.ts#L21) +Defined in: [src/bot.ts:21](../bot.ts#L21) *** @@ -62,7 +62,7 @@ Defined in: [src/bot.ts:21](../src/bot.ts#L21) > `optional` **updateAddress?**: `boolean` -Defined in: [src/bot.ts:14](../src/bot.ts#L14) +Defined in: [src/bot.ts:14](../bot.ts#L14) *** @@ -70,7 +70,7 @@ Defined in: [src/bot.ts:14](../src/bot.ts#L14) > `optional` **updateProfile?**: `boolean` -Defined in: [src/bot.ts:15](../src/bot.ts#L15) +Defined in: [src/bot.ts:15](../bot.ts#L15) *** @@ -78,4 +78,4 @@ Defined in: [src/bot.ts:15](../src/bot.ts#L15) > `optional` **useBotProfile?**: `boolean` -Defined in: [src/bot.ts:19](../src/bot.ts#L19) +Defined in: [src/bot.ts:19](../bot.ts#L19) diff --git a/packages/simplex-chat-nodejs/docs/bot.TypeAlias.BotDbOpts.md b/packages/simplex-chat-nodejs/docs/bot.TypeAlias.BotDbOpts.md index 5aeb298a09..d616c5a0f2 100644 --- a/packages/simplex-chat-nodejs/docs/bot.TypeAlias.BotDbOpts.md +++ b/packages/simplex-chat-nodejs/docs/bot.TypeAlias.BotDbOpts.md @@ -8,7 +8,7 @@ > **BotDbOpts** = [`DbConfig`](api.TypeAlias.DbConfig.md) & `object` -Defined in: [src/bot.ts:7](../src/bot.ts#L7) +Defined in: [src/bot.ts:7](../bot.ts#L7) ## Type Declaration diff --git a/packages/simplex-chat-nodejs/docs/core.Class.ChatAPIError.md b/packages/simplex-chat-nodejs/docs/core.Class.ChatAPIError.md index 64e3e764df..19e6343198 100644 --- a/packages/simplex-chat-nodejs/docs/core.Class.ChatAPIError.md +++ b/packages/simplex-chat-nodejs/docs/core.Class.ChatAPIError.md @@ -6,7 +6,7 @@ # Class: ChatAPIError -Defined in: [src/core.ts:117](../src/core.ts#L117) +Defined in: [src/core.ts:117](../core.ts#L117) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:117](../src/core.ts#L117) > **new ChatAPIError**(`message`, `chatError?`): `ChatAPIError` -Defined in: [src/core.ts:118](../src/core.ts#L118) +Defined in: [src/core.ts:118](../core.ts#L118) #### Parameters @@ -28,7 +28,7 @@ Defined in: [src/core.ts:118](../src/core.ts#L118) ##### chatError? -`ChatError` \| `undefined` +`ChatError` \| `undefined` = `undefined` #### Returns @@ -44,7 +44,7 @@ Defined in: [src/core.ts:118](../src/core.ts#L118) > **chatError**: `ChatError` \| `undefined` = `undefined` -Defined in: [src/core.ts:118](../src/core.ts#L118) +Defined in: [src/core.ts:118](../core.ts#L118) *** @@ -52,7 +52,7 @@ Defined in: [src/core.ts:118](../src/core.ts#L118) > **message**: `string` -Defined in: [src/core.ts:118](../src/core.ts#L118) +Defined in: [src/core.ts:118](../core.ts#L118) #### Inherited from @@ -64,7 +64,7 @@ Defined in: [src/core.ts:118](../src/core.ts#L118) > **name**: `string` -Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../node_modules/typescript/lib/lib.es5.d.ts#L1076) +Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../../node_modules/typescript/lib/lib.es5.d.ts#L1076) #### Inherited from @@ -76,7 +76,7 @@ Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../node_modules/typ > `optional` **stack?**: `string` -Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../node_modules/typescript/lib/lib.es5.d.ts#L1078) +Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../../node_modules/typescript/lib/lib.es5.d.ts#L1078) #### Inherited from @@ -88,7 +88,7 @@ Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../node_modules/typ > `static` **stackTraceLimit**: `number` -Defined in: [node\_modules/@types/node/globals.d.ts:67](../node_modules/@types/node/globals.d.ts#L67) +Defined in: [node\_modules/@types/node/globals.d.ts:67](../../node_modules/@types/node/globals.d.ts#L67) The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or @@ -110,7 +110,7 @@ not capture any frames. > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` -Defined in: [node\_modules/@types/node/globals.d.ts:51](../node_modules/@types/node/globals.d.ts#L51) +Defined in: [node\_modules/@types/node/globals.d.ts:51](../../node_modules/@types/node/globals.d.ts#L51) Creates a `.stack` property on `targetObject`, which when accessed returns a string representing the location in the code at which @@ -180,7 +180,7 @@ a(); > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` -Defined in: [node\_modules/@types/node/globals.d.ts:55](../node_modules/@types/node/globals.d.ts#L55) +Defined in: [node\_modules/@types/node/globals.d.ts:55](../../node_modules/@types/node/globals.d.ts#L55) #### Parameters diff --git a/packages/simplex-chat-nodejs/docs/core.Class.ChatInitError.md b/packages/simplex-chat-nodejs/docs/core.Class.ChatInitError.md index aefc4b9073..22fc7607cb 100644 --- a/packages/simplex-chat-nodejs/docs/core.Class.ChatInitError.md +++ b/packages/simplex-chat-nodejs/docs/core.Class.ChatInitError.md @@ -6,7 +6,7 @@ # Class: ChatInitError -Defined in: [src/core.ts:141](../src/core.ts#L141) +Defined in: [src/core.ts:141](../core.ts#L141) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:141](../src/core.ts#L141) > **new ChatInitError**(`message`, `dbMigrationError`): `ChatInitError` -Defined in: [src/core.ts:142](../src/core.ts#L142) +Defined in: [src/core.ts:142](../core.ts#L142) #### Parameters @@ -44,7 +44,7 @@ Defined in: [src/core.ts:142](../src/core.ts#L142) > **dbMigrationError**: [`DBMigrationError`](core.TypeAlias.DBMigrationError.md) -Defined in: [src/core.ts:142](../src/core.ts#L142) +Defined in: [src/core.ts:142](../core.ts#L142) *** @@ -52,7 +52,7 @@ Defined in: [src/core.ts:142](../src/core.ts#L142) > **message**: `string` -Defined in: [src/core.ts:142](../src/core.ts#L142) +Defined in: [src/core.ts:142](../core.ts#L142) #### Inherited from @@ -64,7 +64,7 @@ Defined in: [src/core.ts:142](../src/core.ts#L142) > **name**: `string` -Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../node_modules/typescript/lib/lib.es5.d.ts#L1076) +Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../../node_modules/typescript/lib/lib.es5.d.ts#L1076) #### Inherited from @@ -76,7 +76,7 @@ Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1076](../node_modules/typ > `optional` **stack?**: `string` -Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../node_modules/typescript/lib/lib.es5.d.ts#L1078) +Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../../node_modules/typescript/lib/lib.es5.d.ts#L1078) #### Inherited from @@ -88,7 +88,7 @@ Defined in: [node\_modules/typescript/lib/lib.es5.d.ts:1078](../node_modules/typ > `static` **stackTraceLimit**: `number` -Defined in: [node\_modules/@types/node/globals.d.ts:67](../node_modules/@types/node/globals.d.ts#L67) +Defined in: [node\_modules/@types/node/globals.d.ts:67](../../node_modules/@types/node/globals.d.ts#L67) The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or @@ -110,7 +110,7 @@ not capture any frames. > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` -Defined in: [node\_modules/@types/node/globals.d.ts:51](../node_modules/@types/node/globals.d.ts#L51) +Defined in: [node\_modules/@types/node/globals.d.ts:51](../../node_modules/@types/node/globals.d.ts#L51) Creates a `.stack` property on `targetObject`, which when accessed returns a string representing the location in the code at which @@ -180,7 +180,7 @@ a(); > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` -Defined in: [node\_modules/@types/node/globals.d.ts:55](../node_modules/@types/node/globals.d.ts#L55) +Defined in: [node\_modules/@types/node/globals.d.ts:55](../../node_modules/@types/node/globals.d.ts#L55) #### Parameters diff --git a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorMigration.md b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorMigration.md index 9b6ad3c099..d894205865 100644 --- a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorMigration.md +++ b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorMigration.md @@ -6,7 +6,7 @@ # Interface: ErrorMigration -Defined in: [src/core.ts:174](../src/core.ts#L174) +Defined in: [src/core.ts:174](../core.ts#L174) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:174](../src/core.ts#L174) > **dbFile**: `string` -Defined in: [src/core.ts:176](../src/core.ts#L176) +Defined in: [src/core.ts:176](../core.ts#L176) *** @@ -26,7 +26,7 @@ Defined in: [src/core.ts:176](../src/core.ts#L176) > **migrationError**: [`MigrationError`](core.TypeAlias.MigrationError.md) -Defined in: [src/core.ts:177](../src/core.ts#L177) +Defined in: [src/core.ts:177](../core.ts#L177) *** @@ -34,7 +34,7 @@ Defined in: [src/core.ts:177](../src/core.ts#L177) > **type**: `"errorMigration"` -Defined in: [src/core.ts:175](../src/core.ts#L175) +Defined in: [src/core.ts:175](../core.ts#L175) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md index d54f59003f..9944389b3b 100644 --- a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md +++ b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorNotADatabase.md @@ -6,7 +6,7 @@ # Interface: ErrorNotADatabase -Defined in: [src/core.ts:169](../src/core.ts#L169) +Defined in: [src/core.ts:169](../core.ts#L169) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:169](../src/core.ts#L169) > **dbFile**: `string` -Defined in: [src/core.ts:171](../src/core.ts#L171) +Defined in: [src/core.ts:171](../core.ts#L171) *** @@ -26,7 +26,7 @@ Defined in: [src/core.ts:171](../src/core.ts#L171) > **type**: `"errorNotADatabase"` -Defined in: [src/core.ts:170](../src/core.ts#L170) +Defined in: [src/core.ts:170](../core.ts#L170) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorSQL.md b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorSQL.md index e76de932f6..108e5e226a 100644 --- a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorSQL.md +++ b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.ErrorSQL.md @@ -6,7 +6,7 @@ # Interface: ErrorSQL -Defined in: [src/core.ts:180](../src/core.ts#L180) +Defined in: [src/core.ts:180](../core.ts#L180) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:180](../src/core.ts#L180) > **dbFile**: `string` -Defined in: [src/core.ts:182](../src/core.ts#L182) +Defined in: [src/core.ts:182](../core.ts#L182) *** @@ -26,7 +26,7 @@ Defined in: [src/core.ts:182](../src/core.ts#L182) > **migrationSQLError**: `string` -Defined in: [src/core.ts:183](../src/core.ts#L183) +Defined in: [src/core.ts:183](../core.ts#L183) *** @@ -34,7 +34,7 @@ Defined in: [src/core.ts:183](../src/core.ts#L183) > **type**: `"errorSQL"` -Defined in: [src/core.ts:181](../src/core.ts#L181) +Defined in: [src/core.ts:181](../core.ts#L181) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidConfirmation.md b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidConfirmation.md index 30157c808d..a1abcdf993 100644 --- a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidConfirmation.md +++ b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidConfirmation.md @@ -6,7 +6,7 @@ # Interface: InvalidConfirmation -Defined in: [src/core.ts:161](../src/core.ts#L161) +Defined in: [src/core.ts:161](../core.ts#L161) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:161](../src/core.ts#L161) > **type**: `"invalidConfirmation"` -Defined in: [src/core.ts:162](../src/core.ts#L162) +Defined in: [src/core.ts:162](../core.ts#L162) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidQueueSize.md b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidQueueSize.md index 5264e4977f..f59284b129 100644 --- a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidQueueSize.md +++ b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.Interface.InvalidQueueSize.md @@ -6,7 +6,7 @@ # Interface: InvalidQueueSize -Defined in: [src/core.ts:165](../src/core.ts#L165) +Defined in: [src/core.ts:165](../core.ts#L165) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:165](../src/core.ts#L165) > **type**: `"invalidQueueSize"` -Defined in: [src/core.ts:166](../src/core.ts#L166) +Defined in: [src/core.ts:166](../core.ts#L166) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.TypeAlias.Tag.md b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.TypeAlias.Tag.md index d94d5893ad..c83f29a130 100644 --- a/packages/simplex-chat-nodejs/docs/core.DBMigrationError.TypeAlias.Tag.md +++ b/packages/simplex-chat-nodejs/docs/core.DBMigrationError.TypeAlias.Tag.md @@ -8,4 +8,4 @@ > **Tag** = `"invalidConfirmation"` \| `"invalidQueueSize"` \| `"errorNotADatabase"` \| `"errorMigration"` \| `"errorSQL"` -Defined in: [src/core.ts:155](../src/core.ts#L155) +Defined in: [src/core.ts:155](../core.ts#L155) diff --git a/packages/simplex-chat-nodejs/docs/core.Enumeration.MigrationConfirmation.md b/packages/simplex-chat-nodejs/docs/core.Enumeration.MigrationConfirmation.md index cc2dea3901..a0df879c4c 100644 --- a/packages/simplex-chat-nodejs/docs/core.Enumeration.MigrationConfirmation.md +++ b/packages/simplex-chat-nodejs/docs/core.Enumeration.MigrationConfirmation.md @@ -6,7 +6,7 @@ # Enumeration: MigrationConfirmation -Defined in: [src/core.ts:126](../src/core.ts#L126) +Defined in: [src/core.ts:126](../core.ts#L126) Migration confirmation mode @@ -16,7 +16,7 @@ Migration confirmation mode > **Console**: `"console"` -Defined in: [src/core.ts:129](../src/core.ts#L129) +Defined in: [src/core.ts:129](../core.ts#L129) *** @@ -24,7 +24,7 @@ Defined in: [src/core.ts:129](../src/core.ts#L129) > **Error**: `"error"` -Defined in: [src/core.ts:130](../src/core.ts#L130) +Defined in: [src/core.ts:130](../core.ts#L130) *** @@ -32,7 +32,7 @@ Defined in: [src/core.ts:130](../src/core.ts#L130) > **YesUp**: `"yesUp"` -Defined in: [src/core.ts:127](../src/core.ts#L127) +Defined in: [src/core.ts:127](../core.ts#L127) *** @@ -40,4 +40,4 @@ Defined in: [src/core.ts:127](../src/core.ts#L127) > **YesUpDown**: `"yesUpDown"` -Defined in: [src/core.ts:128](../src/core.ts#L128) +Defined in: [src/core.ts:128](../core.ts#L128) diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatCloseStore.md b/packages/simplex-chat-nodejs/docs/core.Function.chatCloseStore.md index 69be845ed3..8902ba5653 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatCloseStore.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatCloseStore.md @@ -8,7 +8,7 @@ > **chatCloseStore**(`ctrl`): `Promise`\<`void`\> -Defined in: [src/core.ts:42](../src/core.ts#L42) +Defined in: [src/core.ts:42](../core.ts#L42) Close chat store diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatDecryptFile.md b/packages/simplex-chat-nodejs/docs/core.Function.chatDecryptFile.md index a2a901ddc5..6d1a43d5a6 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatDecryptFile.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatDecryptFile.md @@ -8,7 +8,7 @@ > **chatDecryptFile**(`fromPath`, `__namedParameters`, `toPath`): `Promise`\<`void`\> -Defined in: [src/core.ts:98](../src/core.ts#L98) +Defined in: [src/core.ts:98](../core.ts#L98) Decrypt file diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatEncryptFile.md b/packages/simplex-chat-nodejs/docs/core.Function.chatEncryptFile.md index 8f4c7815c4..b99ff277e0 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatEncryptFile.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatEncryptFile.md @@ -8,7 +8,7 @@ > **chatEncryptFile**(`ctrl`, `fromPath`, `toPath`): `Promise`\<[`CryptoArgs`](core.Interface.CryptoArgs.md)\> -Defined in: [src/core.ts:90](../src/core.ts#L90) +Defined in: [src/core.ts:90](../core.ts#L90) Encrypt file diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatMigrateInit.md b/packages/simplex-chat-nodejs/docs/core.Function.chatMigrateInit.md index c6ad146aa1..1a12285c7a 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatMigrateInit.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatMigrateInit.md @@ -8,7 +8,7 @@ > **chatMigrateInit**(`dbPath`, `dbKey`, `confirm`, `queueSize?`): `Promise`\<`bigint`\> -Defined in: [src/core.ts:30](../src/core.ts#L30) +Defined in: [src/core.ts:30](../core.ts#L30) Initialize chat controller diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatReadFile.md b/packages/simplex-chat-nodejs/docs/core.Function.chatReadFile.md index f9413c4d74..b94cb8b3f9 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatReadFile.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatReadFile.md @@ -8,7 +8,7 @@ > **chatReadFile**(`path`, `__namedParameters`): `Promise`\<`Buffer`\<`ArrayBufferLike`\>\> -Defined in: [src/core.ts:83](../src/core.ts#L83) +Defined in: [src/core.ts:83](../core.ts#L83) Read buffer from encrypted file diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatRecvMsgWait.md b/packages/simplex-chat-nodejs/docs/core.Function.chatRecvMsgWait.md index 8aaa2e1590..40cfb9d182 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatRecvMsgWait.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatRecvMsgWait.md @@ -8,7 +8,7 @@ > **chatRecvMsgWait**(`ctrl`, `wait`): `Promise`\<`ChatEvent` \| `undefined`\> -Defined in: [src/core.ts:62](../src/core.ts#L62) +Defined in: [src/core.ts:62](../core.ts#L62) Receive chat event diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatSendCmd.md b/packages/simplex-chat-nodejs/docs/core.Function.chatSendCmd.md index 9f8c34be0f..a86cdd23d5 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatSendCmd.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatSendCmd.md @@ -8,7 +8,7 @@ > **chatSendCmd**(`ctrl`, `cmd`): `Promise`\<`ChatResponse`\> -Defined in: [src/core.ts:50](../src/core.ts#L50) +Defined in: [src/core.ts:50](../core.ts#L50) Send chat command as string diff --git a/packages/simplex-chat-nodejs/docs/core.Function.chatWriteFile.md b/packages/simplex-chat-nodejs/docs/core.Function.chatWriteFile.md index bd22e9d2c6..327f605347 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.chatWriteFile.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.chatWriteFile.md @@ -8,7 +8,7 @@ > **chatWriteFile**(`ctrl`, `path`, `buffer`): `Promise`\<[`CryptoArgs`](core.Interface.CryptoArgs.md)\> -Defined in: [src/core.ts:75](../src/core.ts#L75) +Defined in: [src/core.ts:75](../core.ts#L75) Write buffer to encrypted file diff --git a/packages/simplex-chat-nodejs/docs/core.Function.loadLibrary.md b/packages/simplex-chat-nodejs/docs/core.Function.loadLibrary.md index a2034b953d..557bb0c80e 100644 --- a/packages/simplex-chat-nodejs/docs/core.Function.loadLibrary.md +++ b/packages/simplex-chat-nodejs/docs/core.Function.loadLibrary.md @@ -8,7 +8,7 @@ > **loadLibrary**(`backend`): `Promise`\<`void`\> -Defined in: [src/core.ts:13](../src/core.ts#L13) +Defined in: [src/core.ts:13](../core.ts#L13) Resolve (downloading on first use) and load libsimplex for the backend. One libsimplex per process: the Haskell runtime is initialized once, and another backend is rejected. diff --git a/packages/simplex-chat-nodejs/docs/core.Interface.APIResult.md b/packages/simplex-chat-nodejs/docs/core.Interface.APIResult.md index 63b0caba85..3c03930c57 100644 --- a/packages/simplex-chat-nodejs/docs/core.Interface.APIResult.md +++ b/packages/simplex-chat-nodejs/docs/core.Interface.APIResult.md @@ -6,7 +6,7 @@ # Interface: APIResult\ -Defined in: [src/core.ts:112](../src/core.ts#L112) +Defined in: [src/core.ts:112](../core.ts#L112) ## Type Parameters @@ -20,7 +20,7 @@ Defined in: [src/core.ts:112](../src/core.ts#L112) > `optional` **error?**: `ChatError` -Defined in: [src/core.ts:114](../src/core.ts#L114) +Defined in: [src/core.ts:114](../core.ts#L114) *** @@ -28,4 +28,4 @@ Defined in: [src/core.ts:114](../src/core.ts#L114) > `optional` **result?**: `R` -Defined in: [src/core.ts:113](../src/core.ts#L113) +Defined in: [src/core.ts:113](../core.ts#L113) diff --git a/packages/simplex-chat-nodejs/docs/core.Interface.CryptoArgs.md b/packages/simplex-chat-nodejs/docs/core.Interface.CryptoArgs.md index 939b3f7430..3531bad332 100644 --- a/packages/simplex-chat-nodejs/docs/core.Interface.CryptoArgs.md +++ b/packages/simplex-chat-nodejs/docs/core.Interface.CryptoArgs.md @@ -6,7 +6,7 @@ # Interface: CryptoArgs -Defined in: [src/core.ts:136](../src/core.ts#L136) +Defined in: [src/core.ts:136](../core.ts#L136) File encryption key and nonce @@ -16,7 +16,7 @@ File encryption key and nonce > **fileKey**: `string` -Defined in: [src/core.ts:137](../src/core.ts#L137) +Defined in: [src/core.ts:137](../core.ts#L137) *** @@ -24,4 +24,4 @@ Defined in: [src/core.ts:137](../src/core.ts#L137) > **fileNonce**: `string` -Defined in: [src/core.ts:138](../src/core.ts#L138) +Defined in: [src/core.ts:138](../core.ts#L138) diff --git a/packages/simplex-chat-nodejs/docs/core.Interface.UpMigration.md b/packages/simplex-chat-nodejs/docs/core.Interface.UpMigration.md index 30a7f0b060..061cb346ac 100644 --- a/packages/simplex-chat-nodejs/docs/core.Interface.UpMigration.md +++ b/packages/simplex-chat-nodejs/docs/core.Interface.UpMigration.md @@ -6,7 +6,7 @@ # Interface: UpMigration -Defined in: [src/core.ts:215](../src/core.ts#L215) +Defined in: [src/core.ts:215](../core.ts#L215) ## Properties @@ -14,7 +14,7 @@ Defined in: [src/core.ts:215](../src/core.ts#L215) > **upName**: `string` -Defined in: [src/core.ts:216](../src/core.ts#L216) +Defined in: [src/core.ts:216](../core.ts#L216) *** @@ -22,4 +22,4 @@ Defined in: [src/core.ts:216](../src/core.ts#L216) > **withDown**: `boolean` -Defined in: [src/core.ts:217](../src/core.ts#L217) +Defined in: [src/core.ts:217](../core.ts#L217) diff --git a/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTREDifferent.md b/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTREDifferent.md index aaaa506f1e..406e9a054b 100644 --- a/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTREDifferent.md +++ b/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTREDifferent.md @@ -6,7 +6,7 @@ # Interface: MTREDifferent -Defined in: [src/core.ts:236](../src/core.ts#L236) +Defined in: [src/core.ts:236](../core.ts#L236) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:236](../src/core.ts#L236) > **downMigrations**: `string`[] -Defined in: [src/core.ts:238](../src/core.ts#L238) +Defined in: [src/core.ts:238](../core.ts#L238) *** @@ -26,7 +26,7 @@ Defined in: [src/core.ts:238](../src/core.ts#L238) > **type**: `"different"` -Defined in: [src/core.ts:237](../src/core.ts#L237) +Defined in: [src/core.ts:237](../core.ts#L237) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTRENoDown.md b/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTRENoDown.md index e088536be9..8cdb1141ce 100644 --- a/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTRENoDown.md +++ b/packages/simplex-chat-nodejs/docs/core.MTRError.Interface.MTRENoDown.md @@ -6,7 +6,7 @@ # Interface: MTRENoDown -Defined in: [src/core.ts:231](../src/core.ts#L231) +Defined in: [src/core.ts:231](../core.ts#L231) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:231](../src/core.ts#L231) > **dbMigrations**: `string`[] -Defined in: [src/core.ts:233](../src/core.ts#L233) +Defined in: [src/core.ts:233](../core.ts#L233) *** @@ -26,7 +26,7 @@ Defined in: [src/core.ts:233](../src/core.ts#L233) > **type**: `"noDown"` -Defined in: [src/core.ts:232](../src/core.ts#L232) +Defined in: [src/core.ts:232](../core.ts#L232) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.MTRError.TypeAlias.Tag.md b/packages/simplex-chat-nodejs/docs/core.MTRError.TypeAlias.Tag.md index 058c64d396..5d92b7dc67 100644 --- a/packages/simplex-chat-nodejs/docs/core.MTRError.TypeAlias.Tag.md +++ b/packages/simplex-chat-nodejs/docs/core.MTRError.TypeAlias.Tag.md @@ -8,4 +8,4 @@ > **Tag** = `"noDown"` \| `"different"` -Defined in: [src/core.ts:225](../src/core.ts#L225) +Defined in: [src/core.ts:225](../core.ts#L225) diff --git a/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEDowngrade.md b/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEDowngrade.md index 523fe8fe1e..d8dadcf2da 100644 --- a/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEDowngrade.md +++ b/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEDowngrade.md @@ -6,7 +6,7 @@ # Interface: MEDowngrade -Defined in: [src/core.ts:204](../src/core.ts#L204) +Defined in: [src/core.ts:204](../core.ts#L204) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:204](../src/core.ts#L204) > **downMigrations**: `string`[] -Defined in: [src/core.ts:206](../src/core.ts#L206) +Defined in: [src/core.ts:206](../core.ts#L206) *** @@ -26,7 +26,7 @@ Defined in: [src/core.ts:206](../src/core.ts#L206) > **type**: `"downgrade"` -Defined in: [src/core.ts:205](../src/core.ts#L205) +Defined in: [src/core.ts:205](../core.ts#L205) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEUpgrade.md b/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEUpgrade.md index c5856f1943..7414f4309d 100644 --- a/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEUpgrade.md +++ b/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MEUpgrade.md @@ -6,7 +6,7 @@ # Interface: MEUpgrade -Defined in: [src/core.ts:199](../src/core.ts#L199) +Defined in: [src/core.ts:199](../core.ts#L199) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:199](../src/core.ts#L199) > **type**: `"upgrade"` -Defined in: [src/core.ts:200](../src/core.ts#L200) +Defined in: [src/core.ts:200](../core.ts#L200) #### Overrides @@ -30,4 +30,4 @@ Defined in: [src/core.ts:200](../src/core.ts#L200) > **upMigrations**: [`UpMigration`](core.Interface.UpMigration.md)[] -Defined in: [src/core.ts:201](../src/core.ts#L201) +Defined in: [src/core.ts:201](../core.ts#L201) diff --git a/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MigrationError.md b/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MigrationError.md index f3f7d9bc1f..b27f2a2f68 100644 --- a/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MigrationError.md +++ b/packages/simplex-chat-nodejs/docs/core.MigrationError.Interface.MigrationError.md @@ -6,7 +6,7 @@ # Interface: MigrationError -Defined in: [src/core.ts:209](../src/core.ts#L209) +Defined in: [src/core.ts:209](../core.ts#L209) ## Extends @@ -18,7 +18,7 @@ Defined in: [src/core.ts:209](../src/core.ts#L209) > **mtrError**: [`MTRError`](core.TypeAlias.MTRError.md) -Defined in: [src/core.ts:211](../src/core.ts#L211) +Defined in: [src/core.ts:211](../core.ts#L211) *** @@ -26,7 +26,7 @@ Defined in: [src/core.ts:211](../src/core.ts#L211) > **type**: `"migrationError"` -Defined in: [src/core.ts:210](../src/core.ts#L210) +Defined in: [src/core.ts:210](../core.ts#L210) #### Overrides diff --git a/packages/simplex-chat-nodejs/docs/core.MigrationError.TypeAlias.Tag.md b/packages/simplex-chat-nodejs/docs/core.MigrationError.TypeAlias.Tag.md index 92f649e615..a7028e908f 100644 --- a/packages/simplex-chat-nodejs/docs/core.MigrationError.TypeAlias.Tag.md +++ b/packages/simplex-chat-nodejs/docs/core.MigrationError.TypeAlias.Tag.md @@ -8,4 +8,4 @@ > **Tag** = `"upgrade"` \| `"downgrade"` \| `"migrationError"` -Defined in: [src/core.ts:193](../src/core.ts#L193) +Defined in: [src/core.ts:193](../core.ts#L193) diff --git a/packages/simplex-chat-nodejs/docs/core.TypeAlias.Backend.md b/packages/simplex-chat-nodejs/docs/core.TypeAlias.Backend.md index 9b32c20d5b..5f95c3a0b5 100644 --- a/packages/simplex-chat-nodejs/docs/core.TypeAlias.Backend.md +++ b/packages/simplex-chat-nodejs/docs/core.TypeAlias.Backend.md @@ -8,4 +8,4 @@ > **Backend** = `"sqlite"` \| `"postgres"` -Defined in: [src/core.ts:5](../src/core.ts#L5) +Defined in: [src/core.ts:5](../core.ts#L5) diff --git a/packages/simplex-chat-nodejs/docs/core.TypeAlias.DBMigrationError.md b/packages/simplex-chat-nodejs/docs/core.TypeAlias.DBMigrationError.md index 159ce87391..58cefef41f 100644 --- a/packages/simplex-chat-nodejs/docs/core.TypeAlias.DBMigrationError.md +++ b/packages/simplex-chat-nodejs/docs/core.TypeAlias.DBMigrationError.md @@ -8,4 +8,4 @@ > **DBMigrationError** = [`InvalidConfirmation`](core.DBMigrationError.Interface.InvalidConfirmation.md) \| [`InvalidQueueSize`](core.DBMigrationError.Interface.InvalidQueueSize.md) \| [`ErrorNotADatabase`](core.DBMigrationError.Interface.ErrorNotADatabase.md) \| [`ErrorMigration`](core.DBMigrationError.Interface.ErrorMigration.md) \| [`ErrorSQL`](core.DBMigrationError.Interface.ErrorSQL.md) -Defined in: [src/core.ts:147](../src/core.ts#L147) +Defined in: [src/core.ts:147](../core.ts#L147) diff --git a/packages/simplex-chat-nodejs/docs/core.TypeAlias.MTRError.md b/packages/simplex-chat-nodejs/docs/core.TypeAlias.MTRError.md index 1c4ceb16e9..bb6e1b1650 100644 --- a/packages/simplex-chat-nodejs/docs/core.TypeAlias.MTRError.md +++ b/packages/simplex-chat-nodejs/docs/core.TypeAlias.MTRError.md @@ -8,4 +8,4 @@ > **MTRError** = [`MTRENoDown`](core.MTRError.Interface.MTRENoDown.md) \| [`MTREDifferent`](core.MTRError.Interface.MTREDifferent.md) -Defined in: [src/core.ts:220](../src/core.ts#L220) +Defined in: [src/core.ts:220](../core.ts#L220) diff --git a/packages/simplex-chat-nodejs/docs/core.TypeAlias.MigrationError.md b/packages/simplex-chat-nodejs/docs/core.TypeAlias.MigrationError.md index 485311f0de..1945816f6f 100644 --- a/packages/simplex-chat-nodejs/docs/core.TypeAlias.MigrationError.md +++ b/packages/simplex-chat-nodejs/docs/core.TypeAlias.MigrationError.md @@ -8,4 +8,4 @@ > **MigrationError** = [`MEUpgrade`](core.MigrationError.Interface.MEUpgrade.md) \| [`MEDowngrade`](core.MigrationError.Interface.MEDowngrade.md) \| [`MigrationError`](core.MigrationError.Interface.MigrationError.md) -Defined in: [src/core.ts:187](../src/core.ts#L187) +Defined in: [src/core.ts:187](../core.ts#L187) diff --git a/packages/simplex-chat-nodejs/docs/util.Function.botAddressSettings.md b/packages/simplex-chat-nodejs/docs/util.Function.botAddressSettings.md index 7fa5d81b7a..9c7e1c22ef 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.botAddressSettings.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.botAddressSettings.md @@ -8,7 +8,7 @@ > **botAddressSettings**(`__namedParameters`): [`BotAddressSettings`](api.Interface.BotAddressSettings.md) -Defined in: [src/util.ts:48](../src/util.ts#L48) +Defined in: [src/util.ts:48](../util.ts#L48) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.chatInfoName.md b/packages/simplex-chat-nodejs/docs/util.Function.chatInfoName.md index dd68403c40..614e164974 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.chatInfoName.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.chatInfoName.md @@ -8,7 +8,7 @@ > **chatInfoName**(`cInfo`): `string` -Defined in: [src/util.ts:18](../src/util.ts#L18) +Defined in: [src/util.ts:18](../util.ts#L18) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.chatInfoRef.md b/packages/simplex-chat-nodejs/docs/util.Function.chatInfoRef.md index dccb2a9d29..11e5272392 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.chatInfoRef.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.chatInfoRef.md @@ -8,7 +8,7 @@ > **chatInfoRef**(`cInfo`): `ChatRef` \| `undefined` -Defined in: [src/util.ts:4](../src/util.ts#L4) +Defined in: [src/util.ts:4](../util.ts#L4) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.ciBotCommand.md b/packages/simplex-chat-nodejs/docs/util.Function.ciBotCommand.md index bc7a66f163..4e880ea329 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.ciBotCommand.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.ciBotCommand.md @@ -8,7 +8,7 @@ > **ciBotCommand**(`chatItem`): [`BotCommand`](util.Interface.BotCommand.md) \| `undefined` -Defined in: [src/util.ts:78](../src/util.ts#L78) +Defined in: [src/util.ts:78](../util.ts#L78) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.ciContentText.md b/packages/simplex-chat-nodejs/docs/util.Function.ciContentText.md index 7ab0bc540c..624f88f45a 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.ciContentText.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.ciContentText.md @@ -8,7 +8,7 @@ > **ciContentText**(`__namedParameters`): `string` \| `undefined` -Defined in: [src/util.ts:64](../src/util.ts#L64) +Defined in: [src/util.ts:64](../util.ts#L64) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.contactAddressStr.md b/packages/simplex-chat-nodejs/docs/util.Function.contactAddressStr.md index 3f6c7b9562..4776fd7262 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.contactAddressStr.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.contactAddressStr.md @@ -8,7 +8,7 @@ > **contactAddressStr**(`link`): `string` -Defined in: [src/util.ts:44](../src/util.ts#L44) +Defined in: [src/util.ts:44](../util.ts#L44) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.fromLocalProfile.md b/packages/simplex-chat-nodejs/docs/util.Function.fromLocalProfile.md index c32081b6cf..ed9640b927 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.fromLocalProfile.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.fromLocalProfile.md @@ -8,7 +8,7 @@ > **fromLocalProfile**(`__namedParameters`): `Profile` -Defined in: [src/util.ts:56](../src/util.ts#L56) +Defined in: [src/util.ts:56](../util.ts#L56) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.reactionText.md b/packages/simplex-chat-nodejs/docs/util.Function.reactionText.md index 896dc74a12..48090064c3 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.reactionText.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.reactionText.md @@ -8,7 +8,7 @@ > **reactionText**(`reaction`): `string` -Defined in: [src/util.ts:89](../src/util.ts#L89) +Defined in: [src/util.ts:89](../util.ts#L89) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Function.senderName.md b/packages/simplex-chat-nodejs/docs/util.Function.senderName.md index 6bacad97f6..891ec75991 100644 --- a/packages/simplex-chat-nodejs/docs/util.Function.senderName.md +++ b/packages/simplex-chat-nodejs/docs/util.Function.senderName.md @@ -8,7 +8,7 @@ > **senderName**(`cInfo`, `chatDir`): `string` -Defined in: [src/util.ts:37](../src/util.ts#L37) +Defined in: [src/util.ts:37](../util.ts#L37) ## Parameters diff --git a/packages/simplex-chat-nodejs/docs/util.Interface.BotCommand.md b/packages/simplex-chat-nodejs/docs/util.Interface.BotCommand.md index 21c3ad72ba..f50e88a1d1 100644 --- a/packages/simplex-chat-nodejs/docs/util.Interface.BotCommand.md +++ b/packages/simplex-chat-nodejs/docs/util.Interface.BotCommand.md @@ -6,7 +6,7 @@ # Interface: BotCommand -Defined in: [src/util.ts:72](../src/util.ts#L72) +Defined in: [src/util.ts:72](../util.ts#L72) ## Properties @@ -14,7 +14,7 @@ Defined in: [src/util.ts:72](../src/util.ts#L72) > **keyword**: `string` -Defined in: [src/util.ts:73](../src/util.ts#L73) +Defined in: [src/util.ts:73](../util.ts#L73) *** @@ -22,4 +22,4 @@ Defined in: [src/util.ts:73](../src/util.ts#L73) > **params**: `string` -Defined in: [src/util.ts:74](../src/util.ts#L74) +Defined in: [src/util.ts:74](../util.ts#L74) diff --git a/packages/simplex-chat-nodejs/package.json b/packages/simplex-chat-nodejs/package.json index 4771a480ea..008d74a28b 100644 --- a/packages/simplex-chat-nodejs/package.json +++ b/packages/simplex-chat-nodejs/package.json @@ -25,15 +25,16 @@ }, "dependencies": { "@simplex-chat/types": "^7.1.0-beta.6.2", - "extract-zip": "^2.0.1", - "fast-deep-equal": "^3.1.3" + "fast-deep-equal": "^3.1.3", + "yauzl": "^3.4.0" }, "devDependencies": { "@types/jest": "^30.0.0", "@types/node": "^25.0.5", - "jest": "^30.2.0", + "@types/yauzl": "^3.4.0", + "jest": "^30.5.2", "node-addon-api": "^8.5.0", - "ts-jest": "^29.4.6", + "ts-jest": "^29.4.15", "typedoc": "^0.28.15", "typedoc-plugin-markdown": "^4.9.0", "typescript": "^5.9.3" diff --git a/packages/simplex-chat-nodejs/src/api.ts b/packages/simplex-chat-nodejs/src/api.ts index 4ee4e4b3b9..fc090d037c 100644 --- a/packages/simplex-chat-nodejs/src/api.ts +++ b/packages/simplex-chat-nodejs/src/api.ts @@ -273,9 +273,11 @@ export class ChatApi { } return new Promise((resolve, reject) => { let done = false + let timer: NodeJS.Timeout | undefined = undefined const cleanup = () => { done = true - this.off(event, subscriber) + clearTimeout(timer) + this.off(event, subscriber) } const subscriber: EventSubscriberFunc = async (evt: ChatEvent & {type: K}) => { if (done) return @@ -288,7 +290,7 @@ export class ChatApi { } this.on(event, subscriber) if (timeout > 0) { - setTimeout(() => { if (!done) { cleanup(); resolve(undefined) } }, timeout) + timer = setTimeout(() => { if (!done) { cleanup(); resolve(undefined) } }, timeout) } }) } @@ -476,9 +478,10 @@ export class ChatApi { /** * Update message. + * A live message stays live with `liveMessage = true` and is finalized with `liveMessage = false`. * Network usage: background. */ - async apiUpdateChatItem(chatType: T.ChatType, chatId: number, chatItemId: number, msgContent: T.MsgContent, liveMessage: false): Promise { + async apiUpdateChatItem(chatType: T.ChatType, chatId: number, chatItemId: number, msgContent: T.MsgContent, liveMessage = false): Promise { const r = await this.sendChatCmd( CC.APIUpdateChatItem.cmdString({ chatRef: {chatType, chatId}, @@ -995,3 +998,88 @@ export class ChatApi { throw new ChatCommandError("error sending member contact invitation", r) } } + +/** + * Text shown as a live message while it is written, e.g. a streamed reply. + */ +export class LiveMessage { + private tasks: Promise = Promise.resolve() + private text = "" + private writing = false + private timer: NodeJS.Timeout | undefined = undefined + private item: {itemId: number, text: string} | undefined = undefined + + /** + * @param chat - Chat API, or an object with its methods to send, update and delete messages. + * @param {T.ChatType} chatType - Chat type. + * @param {number} chatId - Contact or group ID. + * @param {number} [interval=3000] - Minimum interval between updates of the live message, in milliseconds. + * @param onError - Error handler for sending, updating and deleting messages. Errors are logged when it is omitted. + */ + constructor( + private readonly chat: Pick, + private readonly chatType: T.ChatType, + private readonly chatId: number, + private readonly interval = 3000, + private readonly onError: (error: unknown) => void = e => console.log("live message error", e) + ) {} + + /** + * Set the text of the live message. + * The first text is sent at once; later texts update the message at most once per interval. + */ + update(text: string): void { + this.text = text + if (!this.writing) { + this.writing = true + this.queueText(text, true) + } else if (this.timer === undefined) { + this.timer = setTimeout(() => { + this.timer = undefined + this.queueText(this.text, true) + }, this.interval) + } + } + + /** + * Finalize the live message with the text, or with the last text when omitted. + * A message without text is deleted. The next update starts a new live message. + * The returned promise resolves after all queued commands complete. + */ + end(text = this.text): Promise { + clearTimeout(this.timer) + this.timer = undefined + this.text = "" + this.writing = false + this.queueText(text, false) + return this.tasks + } + + private queueText(text: string, live: boolean): void { + this.tasks = this.tasks.then(() => this.showText(text, live)).catch(this.onError) + } + + private async showText(text: string, live: boolean): Promise { + try { + await this.showMessage(text, live) + } finally { + if (!live) this.item = undefined + } + } + + private async showMessage(text: string, live: boolean): Promise { + const item = this.item + if (item === undefined) { + if (live || text !== "") { + const [{chatItem}] = await this.chat.apiSendMessages([this.chatType, this.chatId], [{msgContent: {type: "text", text}, mentions: {}}], live) + this.item = live ? {itemId: chatItem.meta.itemId, text} : undefined + } + } else if (!live && text === "") { + await this.chat.apiDeleteChatItems(this.chatType, this.chatId, [item.itemId], T.CIDeleteMode.Broadcast) + this.item = undefined + } else if (!live || text !== item.text) { + await this.chat.apiUpdateChatItem(this.chatType, this.chatId, item.itemId, {type: "text", text}, live) + this.item = live ? {itemId: item.itemId, text} : undefined + } + } +} diff --git a/packages/simplex-chat-nodejs/src/libs.ts b/packages/simplex-chat-nodejs/src/libs.ts index 013e49f5d0..ce0063f183 100644 --- a/packages/simplex-chat-nodejs/src/libs.ts +++ b/packages/simplex-chat-nodejs/src/libs.ts @@ -5,7 +5,7 @@ import * as https from "https" import * as os from "os" import * as path from "path" import {pipeline} from "stream/promises" -import extract = require("extract-zip") +import * as yauzl from "yauzl" import type {Backend} from "./core" import {ADDON_RELEASE, LIBS_RELEASE, SHA256} from "./release" @@ -19,6 +19,8 @@ const PLATFORMS: {[platform: string]: {name: string, lib: string} | undefined} = } const ARCHS: {[arch: string]: string | undefined} = {x64: "x86_64", arm64: "aarch64"} const SUPPORTED = ["linux-x86_64", "linux-aarch64", "macos-x86_64", "macos-aarch64", "windows-x86_64"] +const S_IFMT = 0o170000 +const S_IFLNK = 0o120000 function unsupported(platform: string, arch: string): Error { return new Error(`Unsupported platform: ${platform}/${arch}; supported: ${SUPPORTED.join(", ")}`) @@ -115,7 +117,7 @@ export function installLibs(url: string, sha256: string, target: string, lib: st const zipPath = path.join(tmp, "libs.zip") await download(url, zipPath, timeoutMs) await verifySha256(zipPath, sha256, url) - await extract(zipPath, {dir: tmp}) + await unzip(zipPath, tmp) const extracted = path.join(tmp, "libs") if (!fs.existsSync(path.join(extracted, lib))) throw new Error(`libs/${lib} missing from ${url}`) await renameInto(extracted, target, path.join(target, lib)) @@ -132,6 +134,44 @@ export function installAddon(url: string, sha256: string, target: string, timeou }) } +function unzip(zipPath: string, dir: string): Promise { + const root = path.resolve(dir) + return new Promise((resolve, reject) => { + yauzl.open(zipPath, {lazyEntries: true}, (err, zip) => { + if (err || !zip) return reject(err ?? new Error(`cannot open ${zipPath}`)) + let failed = false + const fail = (e: unknown) => { + if (failed) return + failed = true + zip.close() + reject(e) + } + zip.on("error", e => { + failed = true + reject(e) + }) + zip.on("end", () => resolve()) + zip.on("entry", (entry: yauzl.Entry) => { + const dest = path.resolve(root, entry.fileName) + if (!dest.startsWith(root + path.sep)) return fail(new Error(`zip entry outside the target folder: ${entry.fileName}`)) + const mode = entry.externalFileAttributes >>> 16 + if ((mode & S_IFMT) === S_IFLNK) return fail(new Error(`zip entry is a symlink: ${entry.fileName}`)) + if (entry.fileName.endsWith("/")) { + fs.promises.mkdir(dest, {recursive: true}).then(() => zip.readEntry(), fail) + return + } + zip.openReadStream(entry, (e, stream) => { + if (e || !stream) return fail(e ?? new Error(`cannot read ${entry.fileName}`)) + fs.promises.mkdir(path.dirname(dest), {recursive: true}) + .then(() => pipeline(stream, fs.createWriteStream(dest, {mode: (mode & 0o777) || 0o644}))) + .then(() => zip.readEntry(), fail) + }) + }) + zip.readEntry() + }) + }) +} + async function verifySha256(file: string, expected: string, url: string): Promise { const hash = crypto.createHash("sha256") for await (const chunk of fs.createReadStream(file)) hash.update(chunk) diff --git a/packages/simplex-chat-nodejs/tests/api.test.ts b/packages/simplex-chat-nodejs/tests/api.test.ts index e3ddca2440..5ff298d33f 100644 --- a/packages/simplex-chat-nodejs/tests/api.test.ts +++ b/packages/simplex-chat-nodejs/tests/api.test.ts @@ -2,17 +2,25 @@ import * as path from "path" import * as fs from "fs" import {CEvt, T} from "@simplex-chat/types" import {api} from ".." +import {SmpServer, describeIfSmpServerAvailable, startSmpServer} from "./smpServer" const CT = T.ChatType -describe("API tests (use preset servers)", () => { +describeIfSmpServerAvailable("API tests (use local SMP server)", () => { const tmpDir = "./tests/tmp" const alicePath = path.join(tmpDir, "alice") const bobPath = path.join(tmpDir, "bob") + let smpServer: SmpServer + beforeAll(async () => { smpServer = await startSmpServer() }, 120000) + afterAll(() => smpServer?.stop()) beforeEach(() => fs.mkdirSync(tmpDir, {recursive: true})) afterEach(() => fs.rmSync(tmpDir, {recursive: true, force: true})) + async function useSmpServer(chat: api.ChatApi): Promise { + expect(await chat.sendChatCmd(`/smp ${smpServer.address}`)).toMatchObject({type: "cmdOk"}) + } + it("should send/receive message", async () => { // create users and start chat controllers const alice = await api.ChatApi.init({type: "sqlite", filePrefix: alicePath}) @@ -27,32 +35,37 @@ describe("API tests (use preset servers)", () => { await bob.apiCreateActiveUser({displayName: "bob", fullName: ""}) await alice.startChat() await bob.startChat() + await useSmpServer(alice) + await useSmpServer(bob) // connect via link const link = await alice.apiCreateLink(aliceUser.userId) + const connected = Promise.all([alice.wait("contactConnected"), bob.wait("contactConnected")]) await expect(bob.apiConnectActiveUser(link)).resolves.toBe(api.ConnReqType.Invitation) - const [bobContact, aliceContact] = await Promise.all([ - (await alice.wait("contactConnected")).contact, - (await bob.wait("contactConnected")).contact - ]) + const [{contact: bobContact}, {contact: aliceContact}] = await connected expect(bobContact).toMatchObject({profile: {displayName: "bob"}}) expect(aliceContact).toMatchObject({profile: {displayName: "alice"}}) // exchange messages const isMessage = ({contactId}: T.Contact, msg: string) => (evt: CEvt.NewChatItems) => evt.chatItems.some(ci => ci.chatInfo.type === CT.Direct && ci.chatInfo.contact.contactId === contactId && ci.chatItem.meta.itemText === msg) + const hello = bob.wait("newChatItems", isMessage(aliceContact, "hello")) await alice.apiSendTextMessage([CT.Direct, bobContact.contactId], "hello") - await bob.wait("newChatItems", isMessage(aliceContact, "hello")) + await hello + const helloToo = alice.wait("newChatItems", isMessage(bobContact, "hello too"), 10000) await bob.apiSendTextMessage([CT.Direct, aliceContact.contactId], "hello too") - await alice.wait("newChatItems", isMessage(bobContact, "hello too"), 10000) + await expect(helloToo).resolves.toBeDefined() + const howAreYou = bob.wait("newChatItems", isMessage(aliceContact, "how are you?")) await alice.apiSendTextMessage([CT.Direct, bobContact.contactId], "how are you?") - await bob.wait("newChatItems", isMessage(aliceContact, "how are you?")) + await howAreYou + const okAndYou = alice.wait("newChatItems", isMessage(bobContact, "ok, and you?"), 10000) await bob.apiSendTextMessage([CT.Direct, aliceContact.contactId], "ok, and you?") - await alice.wait("newChatItems", isMessage(bobContact, "ok, and you?"), 10000) + await expect(okAndYou).resolves.toBeDefined() // no more messages await expect(alice.wait("newChatItems", 500)).resolves.toBeUndefined() await expect(bob.wait("newChatItems", 500)).resolves.toBeUndefined() // delete contacts, stop chat controllers and close databases + const deleted = bob.wait("contactDeletedByContact") await alice.apiDeleteChat(CT.Direct, bobContact.contactId) - await bob.wait("contactDeletedByContact") + await deleted await bob.apiDeleteChat(CT.Direct, aliceContact.contactId) await alice.stopChat() await bob.stopChat() @@ -60,8 +73,7 @@ describe("API tests (use preset servers)", () => { await bob.close() await expect(alice.startChat()).rejects.toThrow("chat api controller not initialized") await expect(bob.startChat()).rejects.toThrow("chat api controller not initialized") - expect(servers.length).toBe(2) - expect(servers[0] !== servers[1]).toBe(true) + expect([...new Set(servers)]).toEqual(["localhost"]) expect(eventCount > 0).toBe(true) }, 30000) @@ -77,20 +89,19 @@ describe("API tests (use preset servers)", () => { await alice.startChat() await bob.startChat() await carol.startChat() + await useSmpServer(alice) + await useSmpServer(bob) + await useSmpServer(carol) // connect alice <-> bob const aliceLink1 = await alice.apiCreateLink(aliceUser.userId) + const bobConnected = Promise.all([alice.wait("contactConnected"), bob.wait("contactConnected")]) await expect(bob.apiConnectActiveUser(aliceLink1)).resolves.toBe(api.ConnReqType.Invitation) - const [bobContact] = await Promise.all([ - (await alice.wait("contactConnected")).contact, - (await bob.wait("contactConnected")).contact - ]) + const [{contact: bobContact}] = await bobConnected // connect alice <-> carol const aliceLink2 = await alice.apiCreateLink(aliceUser.userId) + const carolConnected = Promise.all([alice.wait("contactConnected"), carol.wait("contactConnected")]) await expect(carol.apiConnectActiveUser(aliceLink2)).resolves.toBe(api.ConnReqType.Invitation) - const [carolContact] = await Promise.all([ - (await alice.wait("contactConnected")).contact, - (await carol.wait("contactConnected")).contact - ]) + const [{contact: carolContact}] = await carolConnected // create group with direct messages enabled const group = await alice.apiNewGroup(aliceUser.userId, { displayName: "test-group", @@ -105,10 +116,10 @@ describe("API tests (use preset servers)", () => { await alice.apiAddMember(groupId, bobContact.contactId, T.GroupMemberRole.Member) const bobInvEvt = await bobInvP expect(bobInvEvt).toBeDefined() - const aliceBobConnP = alice.wait("connectedToGroupMember", 15000) - const bobAliceConnP = bob.wait("connectedToGroupMember", 15000) + const aliceBobConnP = alice.wait("joinedGroupMember", 15000) + const bobAliceConnP = bob.wait("userJoinedGroup", 15000) await bob.apiJoinGroup(bobInvEvt!.groupInfo.groupId) - await Promise.all([aliceBobConnP, bobAliceConnP]) + expect(await Promise.all([aliceBobConnP, bobAliceConnP])).not.toContain(undefined) // add carol to the group const carolInvP = carol.wait("receivedGroupInvitation", 30000) await alice.apiAddMember(groupId, carolContact.contactId, T.GroupMemberRole.Member) @@ -117,14 +128,13 @@ describe("API tests (use preset servers)", () => { // wait for carol to connect to both alice and bob (and vice versa) const bobCarolConnP = bob.wait("connectedToGroupMember", (evt: CEvt.ConnectedToGroupMember) => evt.member.memberProfile.displayName === "carol", 30000) - const carolAliceConnP = carol.wait("connectedToGroupMember", - (evt: CEvt.ConnectedToGroupMember) => evt.member.memberProfile.displayName === "alice", 30000) + const carolAliceConnP = carol.wait("userJoinedGroup", 30000) const carolBobConnP = carol.wait("connectedToGroupMember", (evt: CEvt.ConnectedToGroupMember) => evt.member.memberProfile.displayName === "bob", 30000) - const aliceCarolConnP = alice.wait("connectedToGroupMember", - (evt: CEvt.ConnectedToGroupMember) => evt.member.memberProfile.displayName === "carol", 30000) + const aliceCarolConnP = alice.wait("joinedGroupMember", + (evt: CEvt.JoinedGroupMember) => evt.member.memberProfile.displayName === "carol", 30000) await carol.apiJoinGroup(carolInvEvt!.groupInfo.groupId) - await Promise.all([bobCarolConnP, carolAliceConnP, carolBobConnP, aliceCarolConnP]) + expect(await Promise.all([bobCarolConnP, carolAliceConnP, carolBobConnP, aliceCarolConnP])).not.toContain(undefined) // find carol's memberId from bob's perspective const members = await bob.apiListMembers(groupId) const carolMember = members.find(m => m.memberProfile.displayName === "carol") diff --git a/packages/simplex-chat-nodejs/tests/api.unit.test.ts b/packages/simplex-chat-nodejs/tests/api.unit.test.ts index b4ae456af9..14be90f5bf 100644 --- a/packages/simplex-chat-nodejs/tests/api.unit.test.ts +++ b/packages/simplex-chat-nodejs/tests/api.unit.test.ts @@ -26,6 +26,17 @@ describe("documented success responses", () => { await expect(chat.apiUpdateChatItem(T.ChatType.Direct, 1, 2, {type: "text", text: "same"}, false)).resolves.toEqual(chatItem) }) + it("apiUpdateChatItem updates and finalizes a live message", async () => { + const chatItem = {meta: {itemId: 2}} + const chat = await chatWithResponse({type: "chatItemUpdated", user, chatItem: {chatItem}}) + await expect(chat.apiUpdateChatItem(T.ChatType.Group, 1, 2, {type: "text", text: "partial"}, true)).resolves.toEqual(chatItem) + await expect(chat.apiUpdateChatItem(T.ChatType.Group, 1, 2, {type: "text", text: "final"})).resolves.toEqual(chatItem) + expect(jest.mocked(core.chatSendCmd).mock.calls.map(([, cmd]) => cmd)).toEqual([ + '/_update item #1 2 live=on json {"msgContent":{"type":"text","text":"partial"},"mentions":{}}', + '/_update item #1 2 json {"msgContent":{"type":"text","text":"final"},"mentions":{}}', + ]) + }) + it("apiSetProfileAddress accepts userProfileNoChange", async () => { const chat = await chatWithResponse({type: "userProfileNoChange", user}) await expect(chat.apiSetProfileAddress(1, true)).resolves.toEqual({updateSuccesses: 0, updateFailures: 0, changedContacts: []}) @@ -129,6 +140,122 @@ describe("startChat lifecycle", () => { }) }) +describe("LiveMessage", () => { + const interval = 50 + const json = JSON.stringify + + function recordingChat() { + const calls: string[] = [] + let itemId = 0 + const ref = (chatType: T.ChatType, chatId: number) => T.ChatType.cmdString(chatType) + chatId + const chat = { + apiSendMessages: async ([chatType, chatId]: [T.ChatType, number], [{msgContent}]: T.ComposedMessage[], live: boolean) => { + calls.push(`send ${ref(chatType, chatId)} ${json(msgContent.text)} live=${live}`) + return [{chatItem: {meta: {itemId: ++itemId}}}] as unknown as T.AChatItem[] + }, + apiUpdateChatItem: async (chatType: T.ChatType, chatId: number, chatItemId: number, msgContent: T.MsgContent, live: boolean) => { + calls.push(`update ${ref(chatType, chatId)} ${chatItemId} ${json(msgContent.text)} live=${live}`) + return {} as T.ChatItem + }, + apiDeleteChatItems: async (chatType: T.ChatType, chatId: number, chatItemIds: number[], deleteMode: T.CIDeleteMode) => { + calls.push(`delete ${ref(chatType, chatId)} ${chatItemIds} ${deleteMode}`) + return [] as T.ChatItemDeletion[] + }, + } + return {chat, calls} + } + + function recordingLiveMessage(): {live: api.LiveMessage, calls: string[]} { + const {chat, calls} = recordingChat() + return {live: new api.LiveMessage(chat, T.ChatType.Group, 5, interval), calls} + } + + function wait(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)) + } + + it("sends the first text at once, updates at most once per interval and finalizes", async () => { + const {live, calls} = recordingLiveMessage() + live.update("") + live.update("Hel") + live.update("Hello") + await wait(interval / 5) + expect(calls).toEqual(['send #5 "" live=true']) + await wait(interval * 2) + expect(calls).toEqual(['send #5 "" live=true', 'update #5 1 "Hello" live=true']) + await live.end("Hello world") + expect(calls).toEqual(['send #5 "" live=true', 'update #5 1 "Hello" live=true', 'update #5 1 "Hello world" live=false']) + }) + + it("does not repeat unchanged text", async () => { + const {live, calls} = recordingLiveMessage() + live.update("Hello") + live.update("Hello") + await wait(interval * 2) + await live.end() + expect(calls).toEqual(['send #5 "Hello" live=true', 'update #5 1 "Hello" live=false']) + }) + + it("deletes a live message without text", async () => { + const {live, calls} = recordingLiveMessage() + live.update("") + await live.end() + expect(calls).toEqual(['send #5 "" live=true', "delete #5 1 broadcast"]) + }) + + it("sends the final text without a live message", async () => { + const {live, calls} = recordingLiveMessage() + await live.end() + await live.end("Done") + expect(calls).toEqual(['send #5 "Done" live=false']) + }) + + it("finalizes the last text", async () => { + const {live, calls} = recordingLiveMessage() + live.update("") + live.update("partial") + await live.end() + expect(calls).toEqual(['send #5 "" live=true', 'update #5 1 "partial" live=false']) + }) + + it("starts the next live message after the end", async () => { + const {live, calls} = recordingLiveMessage() + live.update("") + void live.end("first") + live.update("") + await live.end("second") + expect(calls).toEqual([ + 'send #5 "" live=true', + 'update #5 1 "first" live=false', + 'send #5 "" live=true', + 'update #5 2 "second" live=false', + ]) + }) + + it("passes errors to the handler and repeats the change with the next text", async () => { + const {chat, calls} = recordingChat() + const error = new Error("update failed") + chat.apiUpdateChatItem = jest.fn(chat.apiUpdateChatItem).mockRejectedValueOnce(error) + const errors: unknown[] = [] + const live = new api.LiveMessage(chat, T.ChatType.Group, 5, interval, e => errors.push(e)) + live.update("") + live.update("Hello") + await wait(interval * 2) + await live.end("Hello world") + expect(errors).toEqual([error]) + expect(calls).toEqual(['send #5 "" live=true', 'update #5 1 "Hello world" live=false']) + }) + + it("logs errors by default", async () => { + const {chat} = recordingChat() + const error = new Error("send failed") + chat.apiSendMessages = jest.fn(chat.apiSendMessages).mockRejectedValueOnce(error) + const log = jest.spyOn(console, "log").mockImplementation(() => {}) + await new api.LiveMessage(chat, T.ChatType.Group, 5).end("Hello") + expect(log).toHaveBeenCalledWith("live message error", error) + }) +}) + describe("ChatApi.init", () => { it("loads the library for the configured backend before opening the database", async () => { let loaded!: () => void diff --git a/packages/simplex-chat-nodejs/tests/bot.test.ts b/packages/simplex-chat-nodejs/tests/bot.test.ts index 5a7faa663f..41d3f054c5 100644 --- a/packages/simplex-chat-nodejs/tests/bot.test.ts +++ b/packages/simplex-chat-nodejs/tests/bot.test.ts @@ -3,18 +3,35 @@ import * as fs from "fs" import * as assert from "assert" import {CEvt, T} from "@simplex-chat/types" import {api, bot, util} from ".." +import {SmpServer, describeIfSmpServerAvailable, startSmpServer} from "./smpServer" const CT = T.ChatType -describe("Bot tests (use preset servers)", () => { +describeIfSmpServerAvailable("Bot tests (use local SMP server)", () => { const tmpDir = "./tests/tmp" const botPath = path.join(tmpDir, "bot") const alicePath = path.join(tmpDir, "alice") + let smpServer: SmpServer + beforeAll(async () => { smpServer = await startSmpServer() }, 120000) + afterAll(() => smpServer?.stop()) beforeEach(() => fs.mkdirSync(tmpDir, {recursive: true})) afterEach(() => fs.rmSync(tmpDir, {recursive: true, force: true})) + async function useSmpServer(chat: api.ChatApi): Promise { + expect(await chat.sendChatCmd(`/smp ${smpServer.address}`)).toMatchObject({type: "cmdOk"}) + } + + async function prepareBotDatabase(): Promise { + const chat = await api.ChatApi.init({type: "sqlite", filePrefix: botPath}) + await chat.apiCreateActiveUser({displayName: "Squaring bot", fullName: ""}) + await chat.startChat() + await useSmpServer(chat) + await chat.close() + } + it("should reply to messages", async () => { + await prepareBotDatabase() // run bot const [chat, botUser, botAddress] = await bot.run({ profile: {displayName: "Squaring bot", fullName: ""}, @@ -33,24 +50,25 @@ describe("Bot tests (use preset servers)", () => { const alice = await api.ChatApi.init({type: "sqlite", filePrefix: alicePath}) const aliceUser = await alice.apiCreateActiveUser({displayName: "alice", fullName: ""}) await alice.startChat() + await useSmpServer(alice) // connect to bot const [plan, link] = await alice.apiConnectPlan(aliceUser.userId, util.contactAddressStr(botAddress.connLinkContact)) assert(plan.type === "contactAddress") + const connected = Promise.all([alice.wait("contactConnected"), chat.wait("contactConnected")]) await expect(alice.apiConnect(aliceUser.userId, false, link)).resolves.toBe(api.ConnReqType.Contact) - const [botContact, aliceContact] = await Promise.all([ - (await alice.wait("contactConnected")).contact, - (await chat.wait("contactConnected")).contact - ]) + const [{contact: botContact}, {contact: aliceContact}] = await connected expect(botContact.profile.displayName).toBe("Squaring bot") // send message to bot const isMessage = ({contactId}: T.Contact, msg: string) => (evt: CEvt.NewChatItems) => evt.chatItems.some(ci => ci.chatInfo.type === CT.Direct && ci.chatInfo.contact.contactId === contactId && ci.chatItem.meta.itemText === msg) + const reply = alice.wait("newChatItems", isMessage(botContact, "2 * 2 = 4"), 5000) await alice.apiSendTextMessage([CT.Direct, botContact.contactId], "2") console.log("after sending message") - await alice.wait("newChatItems", isMessage(botContact, "2 * 2 = 4"), 5000) + await expect(reply).resolves.toBeDefined() // cleanup + const deleted = chat.wait("contactDeletedByContact", ({contact}) => contact.contactId === aliceContact.contactId) await alice.apiDeleteChat(CT.Direct, botContact.contactId) - await chat.wait("contactDeletedByContact", ({contact}) => contact.contactId === aliceContact.contactId) + await deleted await chat.apiDeleteUserAddress(botUser.userId) await chat.stopChat() await chat.close() diff --git a/packages/simplex-chat-nodejs/tests/core.test.ts b/packages/simplex-chat-nodejs/tests/core.test.ts index 608c3939cd..d8a87c82f6 100644 --- a/packages/simplex-chat-nodejs/tests/core.test.ts +++ b/packages/simplex-chat-nodejs/tests/core.test.ts @@ -17,8 +17,18 @@ describe("Core tests", () => { }, 300000); const tmpDir = "./tests/tmp"; const dbPath = path.join(tmpDir, "simplex_v1"); + const templateDir = "./tests/template"; + const templateDbPath = path.join(templateDir, "simplex_v1"); - beforeEach(() => fs.mkdirSync(tmpDir, {recursive: true})); + beforeAll(async () => { + fs.mkdirSync(templateDir, {recursive: true}); + await stopAndClose(await core.chatMigrateInit(templateDbPath, "key", core.MigrationConfirmation.YesUp)); + }, 30000); + afterAll(() => fs.rmSync(templateDir, {recursive: true, force: true})); + beforeEach(() => { + fs.mkdirSync(tmpDir, {recursive: true}); + copyTemplateDb(dbPath); + }); afterEach(() => fs.rmSync(tmpDir, {recursive: true, force: true})); async function stopAndClose(ctrl: bigint): Promise { @@ -26,6 +36,10 @@ describe("Core tests", () => { await core.chatCloseStore(ctrl); } + function copyTemplateDb(dbFilePrefix: string): void { + for (const suffix of ["_chat.db", "_agent.db"]) fs.copyFileSync(templateDbPath + suffix, dbFilePrefix + suffix); + } + it("should initialize chat controller", async () => { const ctrl = await core.chatMigrateInit(dbPath, "key", core.MigrationConfirmation.YesUp); expect(typeof ctrl).toBe("bigint"); @@ -132,7 +146,7 @@ describe("Core tests", () => { it("should not block the libuv pool while receiving", async () => { const ctrl = await core.chatMigrateInit(dbPath, "key", core.MigrationConfirmation.YesUp); - const receives = [1, 2, 3, 4].map(() => core.chatRecvMsgWait(ctrl, 2_000_000)); + const receives = [1, 2, 3, 4].map(() => core.chatRecvMsgWait(ctrl, 500_000)); const start = Date.now(); await fs.promises.stat(tmpDir); expect(Date.now() - start).toBeLessThan(200); @@ -159,8 +173,10 @@ describe("Core tests", () => { }, 30000); it("should receive on two controllers concurrently", async () => { - const ctrlA = await core.chatMigrateInit(path.join(tmpDir, "simplex_a"), "key", core.MigrationConfirmation.YesUp); - const ctrlB = await core.chatMigrateInit(path.join(tmpDir, "simplex_b"), "key", core.MigrationConfirmation.YesUp); + const dbPathB = path.join(tmpDir, "simplex_b"); + copyTemplateDb(dbPathB); + const ctrlA = await core.chatMigrateInit(dbPath, "key", core.MigrationConfirmation.YesUp); + const ctrlB = await core.chatMigrateInit(dbPathB, "key", core.MigrationConfirmation.YesUp); const start = Date.now(); await expect(Promise.all([core.chatRecvMsgWait(ctrlA, 1_000_000), core.chatRecvMsgWait(ctrlB, 1_000_000)])) .resolves.toEqual([undefined, undefined]); @@ -235,7 +251,7 @@ describe("Core tests", () => { }, 35000); it("should let the process exit while a receiver is idle", () => { - const childDbPath = path.resolve(tmpDir, "simplex_child"); + const childDbPath = path.resolve(dbPath); const script = ` const simplex = require("./build/Release/simplex.node"); simplex.load(${JSON.stringify(libPath)}); diff --git a/packages/simplex-chat-nodejs/tests/fixtures/tls/ca.crt b/packages/simplex-chat-nodejs/tests/fixtures/tls/ca.crt new file mode 100644 index 0000000000..e9902529bc --- /dev/null +++ b/packages/simplex-chat-nodejs/tests/fixtures/tls/ca.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBtjCCATagAwIBAgIUe2PryrWo0xXX9vcA3WfbCzcdmgAwBQYDK2VxMCoxFjAU +BgNVBAMMDVNNUCBzZXJ2ZXIgQ0ExEDAOBgNVBAoMB1NpbXBsZVgwIBcNMjIwMTEx +MTExNjM5WhgPNDc1OTEyMDgxMTE2MzlaMCoxFjAUBgNVBAMMDVNNUCBzZXJ2ZXIg +Q0ExEDAOBgNVBAoMB1NpbXBsZVgwQzAFBgMrZXEDOgCAcvFwVicR+RLZpiEWPFNR +XYTbf+mFcX1NHIyPQDugFwOCgqJAW1fsjYgFhtQJSMH/lc1N7clfm4CjUzBRMB0G +A1UdDgQWBBQcUJvR7mm26yxMQfCsWgbnwMmJVDAfBgNVHSMEGDAWgBQcUJvR7mm2 +6yxMQfCsWgbnwMmJVDAPBgNVHRMBAf8EBTADAQH/MAUGAytlcQNzAAAP/hMPNxyW +fyJi+iJViodU+C/aklnvHtjh5P3AbiVCSUfY6+PEdvkC8Ov0pBAYpYi5ukSNNVXl +ABVRlipB+vOcLQStNyaZ7kXzQ2IO/0btmIidh+G6SP8I4aytYIYYcV5pEUZpG1L1 +57g8P29SDv81AA== +-----END CERTIFICATE----- diff --git a/packages/simplex-chat-nodejs/tests/fixtures/tls/server.crt b/packages/simplex-chat-nodejs/tests/fixtures/tls/server.crt new file mode 100644 index 0000000000..68234a19a8 --- /dev/null +++ b/packages/simplex-chat-nodejs/tests/fixtures/tls/server.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBvDCCATygAwIBAgIUUgqht3ibEJ9jp5JuroML5Baxa/IwBQYDK2VxMCoxFjAU +BgNVBAMMDVNNUCBzZXJ2ZXIgQ0ExEDAOBgNVBAoMB1NpbXBsZVgwIBcNMjIwMTEx +MTYzOTQ2WhgPNDc1OTEyMDgxNjM5NDZaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDBD +MAUGAytlcQM6AFj+fmjmgOMBFgQ7lXfICSlSaB5sYp1wzhM2IaqgmVhWbDs3Tw7W +oUXFJsWpKku1kFjLH2yhqIInAKNvMG0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA8gw +EwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFMjbHbxESDI8YDPjJ8kXWjkg +jHxmMB8GA1UdIwQYMBaAFBxQm9HuabbrLExB8KxaBufAyYlUMAUGAytlcQNzAP98 +RpFacmjxsc07GwC07uAxUxNgxX4R2nA+0St866uFbdyxApiyn44giG+m+YXCXLjh +1ffKEhhqAPdlpzByLarx8EWgmOsisBCRWrmraksOV15FKxjPqm7OYKp4f9GvieAQ +o/SKOyZVVdcZX7NcuDUxAA== +-----END CERTIFICATE----- diff --git a/packages/simplex-chat-nodejs/tests/fixtures/tls/server.key b/packages/simplex-chat-nodejs/tests/fixtures/tls/server.key new file mode 100644 index 0000000000..27e0b7caaa --- /dev/null +++ b/packages/simplex-chat-nodejs/tests/fixtures/tls/server.key @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOZcXSjKQ3FQ9EBfqHmFIidOu4LecpUgi5NVPMhx7mS2d +QNQ0lFasp+EfvLErbs1Sd9xLVxA1hnZJng== +-----END PRIVATE KEY----- diff --git a/packages/simplex-chat-nodejs/tests/libs.test.ts b/packages/simplex-chat-nodejs/tests/libs.test.ts index c8a65f6bdd..175eadb880 100644 --- a/packages/simplex-chat-nodejs/tests/libs.test.ts +++ b/packages/simplex-chat-nodejs/tests/libs.test.ts @@ -249,6 +249,35 @@ describe("installLibs", () => { expect(leftovers()).toEqual([]) }) + it("rejects a symlink entry and cleans up", async () => { + const zip = storedZip({[`libs/${LIB}`]: "lib", "libs/link": {content: "../../outside", mode: 0o120777}}) + routes["/symlink.zip"] = res => res.writeHead(200).end(zip) + const target = path.join(tmp, "sqlite") + await expect(installLibs(`${base}/symlink.zip`, sha256(zip), target, LIB)).rejects.toThrow("zip entry is a symlink: libs/link") + expect(leftovers()).toEqual([]) + expect(fs.existsSync(target)).toBe(false) + }) + + it("rejects an entry outside the target folder and cleans up", async () => { + const zip = storedZip({[`libs/${LIB}`]: "lib", "libs/../../outside": "x"}) + routes["/outside.zip"] = res => res.writeHead(200).end(zip) + await expect(installLibs(`${base}/outside.zip`, sha256(zip), path.join(tmp, "sqlite"), LIB)).rejects.toThrow("libs/../../outside") + expect(leftovers()).toEqual([]) + expect(fs.existsSync(path.join(tmp, "outside"))).toBe(false) + expect(fs.existsSync(path.join(path.dirname(tmp), "outside"))).toBe(false) + }) + + const itOnPosix = process.platform === "win32" ? it.skip : it + + itOnPosix("keeps the file mode of an entry", async () => { + const zip = storedZip({[`libs/${LIB}`]: {content: "lib", mode: 0o100755}, "libs/data": {content: "x", mode: 0o100644}}) + routes["/modes.zip"] = res => res.writeHead(200).end(zip) + const target = path.join(tmp, "sqlite") + await installLibs(`${base}/modes.zip`, sha256(zip), target, LIB) + expect(fs.statSync(path.join(target, LIB)).mode & 0o777).toBe(0o755) + expect(fs.statSync(path.join(target, "data")).mode & 0o777).toBe(0o644) + }) + it("times out a request without a response", async () => { await expect(installLibs(`${base}/hang.zip`, GOOD_SHA, path.join(tmp, "sqlite"), LIB, 200)).rejects.toThrow("timeout") expect(leftovers()).toEqual([]) diff --git a/packages/simplex-chat-nodejs/tests/live.test.ts b/packages/simplex-chat-nodejs/tests/live.test.ts new file mode 100644 index 0000000000..aef27e3026 --- /dev/null +++ b/packages/simplex-chat-nodejs/tests/live.test.ts @@ -0,0 +1,68 @@ +import * as path from "path" +import * as fs from "fs" +import {T} from "@simplex-chat/types" +import {api} from ".." +import {SmpServer, describeIfSmpServerAvailable, startSmpServer} from "./smpServer" + +const CT = T.ChatType + +describeIfSmpServerAvailable("Live message tests (use local SMP server)", () => { + const tmpDir = "./tests/tmp" + const alicePath = path.join(tmpDir, "alice") + const bobPath = path.join(tmpDir, "bob") + let smpServer: SmpServer + + beforeAll(async () => { smpServer = await startSmpServer() }, 120000) + afterAll(() => smpServer?.stop()) + beforeEach(() => fs.mkdirSync(tmpDir, {recursive: true})) + afterEach(() => fs.rmSync(tmpDir, {recursive: true, force: true})) + + async function useSmpServer(chat: api.ChatApi): Promise { + expect(await chat.sendChatCmd(`/smp ${smpServer.address}`)).toMatchObject({type: "cmdOk"}) + } + + it("should show, update, finalize and delete live messages", async () => { + const alice = await api.ChatApi.init({type: "sqlite", filePrefix: alicePath}) + const bob = await api.ChatApi.init({type: "sqlite", filePrefix: bobPath}) + const aliceUser = await alice.apiCreateActiveUser({displayName: "alice", fullName: ""}) + await bob.apiCreateActiveUser({displayName: "bob", fullName: ""}) + await alice.startChat() + await bob.startChat() + await useSmpServer(alice) + await useSmpServer(bob) + const aliceConnected = alice.wait("contactConnected", 30000) + const bobConnected = bob.wait("contactConnected", 30000) + await bob.apiConnectActiveUser(await alice.apiCreateLink(aliceUser.userId)) + const bobContact = (await aliceConnected)!.contact + expect(await bobConnected).toBeDefined() + + const timeout = 10000 + const received = (check: (meta: T.CIMeta) => boolean) => + bob.wait("newChatItems", ({chatItems}) => chatItems.some(({chatItem: {meta}}) => check(meta)), timeout) + const updated = (check: (meta: T.CIMeta) => boolean) => + bob.wait("chatItemUpdated", ({chatItem: {chatItem: {meta}}}) => check(meta), timeout) + const live = new api.LiveMessage(alice, CT.Direct, bobContact.contactId, 500) + + const started = received(meta => meta.itemLive === true && meta.itemText === "") + live.update("") + const itemId = (await started)!.chatItems[0].chatItem.meta.itemId + const updatedLive = updated(meta => meta.itemId === itemId && meta.itemLive === true && meta.itemText === "Hello") + live.update("Hel") + live.update("Hello") + expect(await updatedLive).toBeDefined() + const finalized = updated(meta => meta.itemId === itemId && meta.itemLive === false && meta.itemText === "Hello world") + await live.end("Hello world") + expect((await finalized)?.chatItem.chatItem.meta.itemEdited).toBe(false) + + const emptyStarted = received(meta => meta.itemLive === true && meta.itemText === "") + live.update("") + const emptyItemId = (await emptyStarted)!.chatItems[0].chatItem.meta.itemId + const deleted = bob.wait("chatItemsDeleted", ({chatItemDeletions}) => + chatItemDeletions.some(({deletedChatItem}) => deletedChatItem.chatItem.meta.itemId === emptyItemId), timeout) + await live.end() + expect(await deleted).toBeDefined() + + await alice.close() + await bob.close() + }, 90000) +}) diff --git a/packages/simplex-chat-nodejs/tests/smpServer.ts b/packages/simplex-chat-nodejs/tests/smpServer.ts new file mode 100644 index 0000000000..dccd2e4ea6 --- /dev/null +++ b/packages/simplex-chat-nodejs/tests/smpServer.ts @@ -0,0 +1,118 @@ +import {ChildProcess, spawn} from "child_process" +import {createHash} from "crypto" +import {once} from "events" +import {copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync} from "fs" +import {AddressInfo, createConnection, createServer} from "net" +import {tmpdir} from "os" +import {dirname, join} from "path" + +const tlsFixtures = join(__dirname, "fixtures/tls") +const smpServerRelease = "v7.1.0-beta.3" +const releaseArch: Record = {x64: "x86-64", arm64: "aarch64"} +const downloadArch = process.platform === "linux" ? releaseArch[process.arch] : undefined +const downloadAttempts = 4 + +export const describeIfSmpServerAvailable = process.env.SMP_SERVER || downloadArch ? describe : describe.skip + +export interface SmpServer { + address: string + stop: () => Promise +} + +export async function startSmpServer(): Promise { + const port = await freePort() + const dir = mkdtempSync(join(tmpdir(), "smp-server-")) + const configDir = join(dir, "config") + mkdirSync(configDir) + for (const file of ["ca.crt", "server.crt", "server.key"]) copyFileSync(join(tlsFixtures, file), join(configDir, file)) + const fingerprint = caFingerprint(join(configDir, "ca.crt")) + writeFileSync(join(configDir, "fingerprint"), `${fingerprint}\n`) + writeFileSync(join(configDir, "smp-server.ini"), `[STORE_LOG]\nenable: off\n\n[TRANSPORT]\nhost: localhost\nport: ${port}\nwebsockets: off\n`) + const server = spawn(await smpServerExecutable(), ["start"], { + env: {...process.env, SMP_SERVER_CFG_PATH: configDir, SMP_SERVER_LOG_PATH: join(dir, "logs")}, + stdio: ["ignore", "ignore", "inherit"], + }) + const stop = async () => { + if (server.exitCode === null) { + const exited = once(server, "exit") + server.kill() + await exited + } + rmSync(dir, {recursive: true, force: true}) + } + try { + await once(server, "spawn") + await waitForServer(server, port) + } catch (e) { + await stop() + throw e + } + return {address: `smp://${fingerprint}@localhost:${port}`, stop} +} + +async function smpServerExecutable(): Promise { + if (process.env.SMP_SERVER) return process.env.SMP_SERVER + const path = join(__dirname, "../node_modules/.cache/smp-server", `smp-server-${smpServerRelease}`) + if (!existsSync(path)) await downloadSmpServer(path) + return path +} + +async function downloadSmpServer(path: string): Promise { + if (!downloadArch) { + throw new Error(`smp-server release binaries are not available for ${process.platform} ${process.arch}, set SMP_SERVER to the smp-server path`) + } + const url = `https://github.com/simplex-chat/simplexmq/releases/download/${smpServerRelease}/smp-server-ubuntu-22_04-${downloadArch}` + const data = await downloadWithRetries(url) + mkdirSync(dirname(path), {recursive: true}) + writeFileSync(`${path}.download`, data, {mode: 0o755}) + renameSync(`${path}.download`, path) +} + +async function downloadWithRetries(url: string): Promise { + for (let attempt = 1; attempt < downloadAttempts; attempt++) { + const data = await download(url).catch(() => undefined) + if (data) return data + await new Promise(resolve => setTimeout(resolve, attempt * 5000)) + } + return download(url) +} + +async function download(url: string): Promise { + const response = await fetch(url) + if (!response.ok) throw new Error(`error downloading ${url}: ${response.status}`) + return Buffer.from(await response.arrayBuffer()) +} + +function caFingerprint(caFile: string): string { + const der = Buffer.from(readFileSync(caFile, "utf-8").replace(/-----[^-]+-----|\s/g, ""), "base64") + return createHash("sha256").update(der).digest("base64").replace(/\+/g, "-").replace(/\//g, "_") +} + +function freePort(): Promise { + return new Promise((resolve, reject) => { + const probe = createServer() + probe.on("error", reject) + probe.listen(0, "127.0.0.1", () => { + const {port} = probe.address() as AddressInfo + probe.close(() => resolve(port)) + }) + }) +} + +async function waitForServer(server: ChildProcess, port: number): Promise { + const deadline = Date.now() + 15_000 + while (!(await canConnect(port))) { + if (server.exitCode !== null || Date.now() > deadline) throw new Error(`smp-server did not start on port ${port}`) + await new Promise(resolve => setTimeout(resolve, 100)) + } +} + +function canConnect(port: number): Promise { + return new Promise(resolve => { + const socket = createConnection({host: "localhost", port}, () => { + socket.destroy() + resolve(true) + }) + socket.on("error", () => resolve(false)) + }) +} diff --git a/packages/simplex-chat-nodejs/tests/zip.ts b/packages/simplex-chat-nodejs/tests/zip.ts index 547c8c211c..49b5c22747 100644 --- a/packages/simplex-chat-nodejs/tests/zip.ts +++ b/packages/simplex-chat-nodejs/tests/zip.ts @@ -4,15 +4,17 @@ const LOCAL_FILE_HEADER = 0x04034b50 const CENTRAL_DIRECTORY_HEADER = 0x02014b50 const END_OF_CENTRAL_DIRECTORY = 0x06054b50 const ZIP_VERSION = 20 +const MADE_BY_UNIX = 3 << 8 const LOCAL_HEADER_SIZE = 30 const CENTRAL_ENTRY_SIZE = 46 const END_RECORD_SIZE = 22 -export function storedZip(files: {[name: string]: string}): Buffer { +export function storedZip(files: {[name: string]: string | {content: string, mode: number}}): Buffer { const local: Buffer[] = [] const central: Buffer[] = [] let offset = 0 - for (const [name, content] of Object.entries(files)) { + for (const [name, file] of Object.entries(files)) { + const {content, mode} = typeof file === "string" ? {content: file, mode: 0} : file const nameBuf = Buffer.from(name) const data = Buffer.from(content) const crc = zlib.crc32(data) @@ -26,12 +28,13 @@ export function storedZip(files: {[name: string]: string}): Buffer { local.push(header, nameBuf, data) const entry = Buffer.alloc(CENTRAL_ENTRY_SIZE) entry.writeUInt32LE(CENTRAL_DIRECTORY_HEADER, 0) - entry.writeUInt16LE(ZIP_VERSION, 4) + entry.writeUInt16LE(mode ? MADE_BY_UNIX | ZIP_VERSION : ZIP_VERSION, 4) entry.writeUInt16LE(ZIP_VERSION, 6) entry.writeUInt32LE(crc, 16) entry.writeUInt32LE(data.length, 20) entry.writeUInt32LE(data.length, 24) entry.writeUInt16LE(nameBuf.length, 28) + entry.writeUInt32LE(mode * 0x10000, 38) entry.writeUInt32LE(offset, 42) central.push(entry, nameBuf) offset += header.length + nameBuf.length + data.length diff --git a/scripts/ci/package-tests.sh b/scripts/ci/package-tests.sh index 33d83c4986..48fa9a889c 100755 --- a/scripts/ci/package-tests.sh +++ b/scripts/ci/package-tests.sh @@ -26,7 +26,7 @@ build_package() { } test_nodejs() { - (cd "$nodejs" && npx jest --forceExit --testNamePattern '^(?!.*use preset servers)') + (cd "$nodejs" && npx jest --forceExit) } test_python() { From 61a57d3835bff53160946225d1f191c931fff807 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 19 Oct 2026 19:54:16 +0100 Subject: [PATCH 11/11] libs: nodejs 7.1.0-beta.6.6 --- packages/simplex-chat-nodejs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/simplex-chat-nodejs/package.json b/packages/simplex-chat-nodejs/package.json index 008d74a28b..68c4c63469 100644 --- a/packages/simplex-chat-nodejs/package.json +++ b/packages/simplex-chat-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "simplex-chat", - "version": "7.1.0-beta.6.5", + "version": "7.1.0-beta.6.6", "main": "dist/index.js", "types": "dist/index.d.ts", "gypfile": false,