@pouchy_ai/companion-sdk 0.30.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -1
- package/README.md +51 -2
- package/dist/adapter-core.d.ts +58 -0
- package/dist/adapter-core.d.ts.map +1 -0
- package/dist/adapter-core.js +158 -0
- package/dist/adapter-core.js.map +1 -0
- package/dist/call.d.ts +1 -0
- package/dist/call.d.ts.map +1 -0
- package/dist/call.js +1 -0
- package/dist/call.js.map +1 -0
- package/dist/client.d.ts +48 -1
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +56 -3
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +1 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +1 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +1 -0
- package/dist/protocol.js.map +1 -0
- package/dist/scopes.d.ts +1 -0
- package/dist/scopes.d.ts.map +1 -0
- package/dist/scopes.js +1 -0
- package/dist/scopes.js.map +1 -0
- package/dist/sse.d.ts +1 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +1 -0
- package/dist/sse.js.map +1 -0
- package/dist/svelte.d.ts +16 -0
- package/dist/svelte.d.ts.map +1 -0
- package/dist/svelte.js +25 -0
- package/dist/svelte.js.map +1 -0
- package/dist/vue.d.ts +15 -0
- package/dist/vue.d.ts.map +1 -0
- package/dist/vue.js +27 -0
- package/dist/vue.js.map +1 -0
- package/dist/ws-transport.d.ts +1 -0
- package/dist/ws-transport.d.ts.map +1 -0
- package/dist/ws-transport.js +1 -0
- package/dist/ws-transport.js.map +1 -0
- package/package.json +24 -6
- package/src/adapter-core.ts +227 -0
- package/src/call.ts +699 -0
- package/src/client.ts +1960 -0
- package/src/errors.ts +77 -0
- package/src/index.ts +105 -0
- package/src/protocol.ts +316 -0
- package/src/scopes.ts +94 -0
- package/src/sse.ts +41 -0
- package/src/svelte.ts +47 -0
- package/src/vue.ts +46 -0
- package/src/ws-transport.ts +70 -0
package/src/vue.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Vue 3 adapter (0.31.0) — `@pouchy_ai/companion-sdk/vue`.
|
|
2
|
+
// Thin binding of adapter-core onto a shallowRef; behavior lives (and is
|
|
3
|
+
// tested) in adapter-core.ts.
|
|
4
|
+
//
|
|
5
|
+
// const { snapshot, sendText } = useCompanion(client);
|
|
6
|
+
// snapshot.value.transcript / snapshot.value.streamState
|
|
7
|
+
//
|
|
8
|
+
// Inside a component/effect scope the subscription is torn down automatically
|
|
9
|
+
// (onScopeDispose); outside any scope, call dispose() yourself.
|
|
10
|
+
|
|
11
|
+
import { getCurrentScope, onScopeDispose, shallowRef, type ShallowRef } from 'vue';
|
|
12
|
+
import type { CompanionClient } from './client';
|
|
13
|
+
import {
|
|
14
|
+
createCompanionView,
|
|
15
|
+
type CompanionView,
|
|
16
|
+
type CompanionViewOptions,
|
|
17
|
+
type CompanionViewSnapshot
|
|
18
|
+
} from './adapter-core';
|
|
19
|
+
|
|
20
|
+
export interface UseCompanionReturn {
|
|
21
|
+
/** Reactive, immutable view snapshot (replaced wholesale per change). */
|
|
22
|
+
snapshot: ShallowRef<CompanionViewSnapshot>;
|
|
23
|
+
sendText: CompanionView['sendText'];
|
|
24
|
+
confirmAction: CompanionView['confirmAction'];
|
|
25
|
+
/** Manual teardown for use outside an effect scope. */
|
|
26
|
+
dispose(): void;
|
|
27
|
+
client: CompanionClient;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Reactive companion view state for a host-owned client. */
|
|
31
|
+
export function useCompanion(
|
|
32
|
+
client: CompanionClient,
|
|
33
|
+
opts?: CompanionViewOptions
|
|
34
|
+
): UseCompanionReturn {
|
|
35
|
+
const view = createCompanionView(client, opts);
|
|
36
|
+
const snapshot = shallowRef(view.getSnapshot());
|
|
37
|
+
const unsub = view.subscribe(() => {
|
|
38
|
+
snapshot.value = view.getSnapshot();
|
|
39
|
+
});
|
|
40
|
+
const dispose = () => {
|
|
41
|
+
unsub();
|
|
42
|
+
view.dispose();
|
|
43
|
+
};
|
|
44
|
+
if (getCurrentScope()) onScopeDispose(dispose);
|
|
45
|
+
return { snapshot, sendText: view.sendText, confirmAction: view.confirmAction, dispose, client };
|
|
46
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Companion SDK — WebSocket transport helpers (pure core).
|
|
2
|
+
//
|
|
3
|
+
// The SDK streams replies over Server-Sent Events today; the client comment
|
|
4
|
+
// always anticipated "when the WebSocket plane lands it slots in behind this
|
|
5
|
+
// same interface". This module is that slot: pure, dependency-free helpers for
|
|
6
|
+
// deriving the ws(s) URL and framing/parsing messages, so the live WS loop in
|
|
7
|
+
// client.ts stays a thin wrapper and the framing logic is unit-testable.
|
|
8
|
+
//
|
|
9
|
+
// IMPORTANT — server support: a persistent WebSocket endpoint needs a
|
|
10
|
+
// WS-capable host (adapter-node, or a standalone WS gateway). The default
|
|
11
|
+
// serverless deploy (adapter-auto → Vercel/Netlify functions) can't hold an
|
|
12
|
+
// open socket, so the client keeps SSE as the default and FALLS BACK to it if
|
|
13
|
+
// the socket can't open. Self-hosters who run a WS gateway can opt in with
|
|
14
|
+
// `stream: 'websocket'`. This file ships the deployment-agnostic client side.
|
|
15
|
+
|
|
16
|
+
/** Build the WebSocket stream URL from the HTTP base. http→ws, https→wss. The
|
|
17
|
+
* token rides the query string because browsers can't set an Authorization
|
|
18
|
+
* header on a WebSocket handshake; the server authenticates it the same way it
|
|
19
|
+
* authenticates the SSE `?cursor=` stream. `cursor` resumes after a reconnect,
|
|
20
|
+
* mirroring the SSE plane. */
|
|
21
|
+
export function deriveWsStreamUrl(
|
|
22
|
+
baseUrl: string,
|
|
23
|
+
sessionId: string,
|
|
24
|
+
token: string,
|
|
25
|
+
cursor = 0
|
|
26
|
+
): string {
|
|
27
|
+
const origin = baseUrl.replace(/\/+$/, '').replace(/^http(s?):\/\//i, (_m, s) => `ws${s}://`);
|
|
28
|
+
const id = encodeURIComponent(sessionId);
|
|
29
|
+
const q = new URLSearchParams({ cursor: String(cursor), token });
|
|
30
|
+
return `${origin}/api/companion/session/${id}/ws?${q.toString()}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Normalize an incoming WS message into the `{ event, data }` shape the SSE
|
|
34
|
+
* frame handler already consumes, so both transports converge on one code
|
|
35
|
+
* path. Accepts two wire forms:
|
|
36
|
+
* 1. a framed message: { "event": "...", "data": "<json string>" }
|
|
37
|
+
* 2. a bare envelope: { "type": "...", ... } → event defaults to "message"
|
|
38
|
+
* Returns null for anything unparseable (the caller drops it). */
|
|
39
|
+
export function decodeWsMessage(raw: string): { event: string; data: string } | null {
|
|
40
|
+
let parsed: unknown;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(raw);
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (!parsed || typeof parsed !== 'object') return null;
|
|
47
|
+
const obj = parsed as Record<string, unknown>;
|
|
48
|
+
|
|
49
|
+
// Form 1 — explicit frame.
|
|
50
|
+
if (typeof obj.event === 'string') {
|
|
51
|
+
const data = typeof obj.data === 'string' ? obj.data : JSON.stringify(obj.data ?? {});
|
|
52
|
+
return { event: obj.event, data };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Form 2 — bare envelope. Re-serialize so handleFrame can JSON.parse it back
|
|
56
|
+
// into a CompanionEnvelope exactly as the SSE path does.
|
|
57
|
+
if (typeof obj.type === 'string') {
|
|
58
|
+
return { event: 'message', data: raw };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Frame an outbound user-input message for the WS plane (for hosts that also
|
|
65
|
+
* accept input over the socket). Mirrors the REST `/input` body. */
|
|
66
|
+
export function encodeWsInput(text: string, opts?: { image?: string }): string {
|
|
67
|
+
const payload: Record<string, unknown> = { type: 'input', text };
|
|
68
|
+
if (opts?.image) payload.image = opts.image;
|
|
69
|
+
return JSON.stringify(payload);
|
|
70
|
+
}
|