Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ This file is the canonical live tracker for the Infomaniak SDK and OpenClaw inte
- Tightened local ignore rules for credential files and generated build leftovers.
- Restricted CI workflow permissions to read-only contents access and disabled checkout credential persistence.
- Added `SECURITY.md` and Dependabot updates for npm and GitHub Actions.
- `feat: add kchat websocket client`
- Added reviewed `client.kchat.websocket` helpers for hosted Infomaniak Echo/Pusher sockets and plain Mattermost `/api/v4/websocket` sockets.
- Added injectable `fetch` and `WebSocket` transports, team/user discovery for Echo subscriptions, private-channel `/broadcasting/auth`, Mattermost authentication challenge frames, normalized `posted` events, sender/channel filters, abort handling, and no-secret error messages.
- Added mocked unit coverage for Echo subscription/auth flows, Mattermost auth/post flows, filtering, malformed frames, binary payloads, socket API variants, and failure cases without live network calls.

## Next Task Queue

Expand Down Expand Up @@ -142,6 +146,7 @@ This file is the canonical live tracker for the Infomaniak SDK and OpenClaw inte
- npm publication: `liquid-potassium@0.1.0` is published to `https://registry.npmjs.org/` with `latest` pointing to `0.1.0`.
- Post-publish install verification: a fresh temporary project installed `liquid-potassium@0.1.0` from npm with 0 vulnerabilities and successfully imported the main SDK, OpenClaw plugin, and OpenClaw tools subpaths.
- Public GitHub security preflight: passing `npm run ci`, `npm audit`, `npm audit --omit=dev`, `npm pack --dry-run --json`, `git diff --check`, and high-confidence secret scans across current tracked files and git history.
- kChat WebSocket client: passing `npm run typecheck` and `npm run test:coverage` with 100% statements, branches, functions, and lines; all tests use mocked `fetch` and WebSocket implementations.

## Blockers And Risks

Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This is an unofficial community project and is not affiliated with Infomaniak.
- Searchable operation catalog with docs-enriched metadata from a committed Infomaniak Developer Portal snapshot.
- Resource discovery helpers for common opaque IDs such as kDrive drive IDs.
- Reviewed Mail application helpers for mailbox consumption routes that are not present in the public OpenAPI spec.
- Reviewed kChat WebSocket helpers for hosted Infomaniak Echo/Pusher events and plain Mattermost WebSocket events.
- Curated domain workflow actions for migration-friendly tasks such as mailbox reads, kDrive browsing/uploads, kChat posts, and resource discovery.
- Native OpenClaw plugin with six compact tools.
- No real network calls in tests; runtime HTTP goes through injected `fetch`.
Expand Down Expand Up @@ -147,6 +148,29 @@ const message = await client.mail.application.getMessage(

`mailApplicationBaseUrl` defaults to `https://mail.infomaniak.com`. Route provenance and maintenance rules are documented in [`docs/mail-application-api.md`](docs/mail-application-api.md).

## kChat WebSocket API

Hosted Infomaniak kChat exposes live events through an Echo/Pusher-compatible socket at `websocket.kchat.infomaniak.com`, with private subscription auth through the kChat team host. The reviewed SDK surface lives under `client.kchat.websocket` and keeps all network dependencies injectable:

```ts
const client = createInfomaniakClient({
token: process.env.INFOMANIAK_TOKEN,
fetch,
});

const abort = new AbortController();
await client.kchat.websocket.runConnection({
teamName: "my-team",
channelIds: ["channel-id-to-accept"],
signal: abort.signal,
onPost(event) {
console.log(event.text, event.channelId, event.userName);
},
});
```

By default the client resolves `private-team.<team_id>` and `presence-teamUser.<user_id>` from `teamName`, authenticates them through `/broadcasting/auth`, and emits normalized `posted` events. For a plain Mattermost server, set `protocol: "mattermost"` and provide `apiBaseUrl` or a full `url`.

## OpenClaw Plugin

The native plugin entry is exported at:
Expand Down
18 changes: 17 additions & 1 deletion src/client/create-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createDomainWorkflowClient, type DomainWorkflowClient } from "../workfl
import type { OperationMetadata } from "../generated/catalog/types.js";
import type { GeneratedOperationClient, OperationRequest } from "./generated-operation-client.js";
import { InfomaniakError, InfomaniakOperationError } from "./errors.js";
import { createKchatWebSocketClient, type KchatWebSocketClient, type KchatWebSocketConstructor } from "./kchat-websocket.js";
import { createMailApplicationClient, type MailApplicationClient } from "./mail-application.js";

export interface InfomaniakClientConfig {
Expand All @@ -14,6 +15,7 @@ export interface InfomaniakClientConfig {
fetch?: typeof fetch;
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
userAgent?: string;
webSocket?: KchatWebSocketConstructor;
}

export interface RawRequestOptions extends OperationRequest {
Expand All @@ -34,10 +36,14 @@ export type DomainClients = ReturnType<typeof createDomainOperations>;
export type MailDomainClient = DomainClients["mail"] & {
application: MailApplicationClient;
};
export type KchatDomainClient = DomainClients["kchat"] & {
websocket: KchatWebSocketClient;
};

export type InfomaniakClient = GeneratedOperationClient &
Omit<DomainClients, "mail"> & {
Omit<DomainClients, "kchat" | "mail"> & {
discovery: ResourceDiscovery;
kchat: KchatDomainClient;
mail: MailDomainClient;
raw: RawClient;
workflows: DomainWorkflowClient;
Expand Down Expand Up @@ -81,6 +87,12 @@ export function createInfomaniakClient(config: InfomaniakClientConfig = {}): Inf
DELETE: (path, options) => requestPath(fetchImpl, config, path, { ...options, method: "DELETE" }),
};
const domainClients = createDomainOperations(generatedClient);
const kchatWebSocketOptions = {
fetch: fetchImpl,
...(config.token === undefined ? {} : { token: config.token }),
...(config.webSocket === undefined ? {} : { webSocket: config.webSocket }),
};
const kchatWebSocket = createKchatWebSocketClient(kchatWebSocketOptions);
const mailApplicationBaseUrl = config.mailApplicationBaseUrl ?? defaultMailApplicationBaseUrl;
const mailApplication = createMailApplicationClient({
apiRequest: (path, options) => requestPath(fetchImpl, config, path, options as RawRequestOptions | undefined),
Expand All @@ -98,6 +110,10 @@ export function createInfomaniakClient(config: InfomaniakClientConfig = {}): Inf
...generatedClient,
...domainClients,
discovery,
kchat: {
...domainClients.kchat,
websocket: kchatWebSocket,
},
mail: {
...domainClients.mail,
application: mailApplication,
Expand Down
Loading
Loading