@pouchy_ai/companion-sdk 0.10.0
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 +86 -0
- package/LICENSE +41 -0
- package/README.md +136 -0
- package/dist/call.d.ts +59 -0
- package/dist/call.js +478 -0
- package/dist/client.d.ts +366 -0
- package/dist/client.js +716 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +17 -0
- package/dist/protocol.d.ts +124 -0
- package/dist/protocol.js +35 -0
- package/dist/sse.d.ts +12 -0
- package/dist/sse.js +38 -0
- package/dist/ws-transport.d.ts +21 -0
- package/dist/ws-transport.js +62 -0
- package/package.json +47 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CompanionClient, type CompanionClientOptions } from './client.js';
|
|
2
|
+
export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js';
|
|
3
|
+
export type { CompanionClientOptions, HelloAck, RecalledMemory, CallCredentials, CompanionToolDecl, WorldStateInput, CompanionAvatar, BrandIconSize } from './client.js';
|
|
4
|
+
export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES } from './call.js';
|
|
5
|
+
export type { CompanionCall, CompanionCallOptions, VoiceToolBridge } from './call.js';
|
|
6
|
+
export type { CompanionEnvelope, OutboundType, InboundType, WorldStateEvent, RenderInterfacePayload, InterfaceUpdatePayload, SocialMessagePayload, ConfirmRequestPayload, AudioClipPayload, ExpressionPayload, UsagePayload } from './protocol.js';
|
|
7
|
+
/** Create a companion client. */
|
|
8
|
+
export declare function createCompanion(opts: CompanionClientOptions): CompanionClient;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Companion SDK — public entry. Embed the Pouchy companion in a third-party
|
|
2
|
+
// surface (web / app / game / hardware) over the REST/SSE plane.
|
|
3
|
+
//
|
|
4
|
+
// import { createCompanion } from '$lib/companion-sdk';
|
|
5
|
+
// const c = createCompanion({ baseUrl: 'https://www.pouchy.ai', token: PAT });
|
|
6
|
+
// await c.connect();
|
|
7
|
+
// c.onMessage((text) => console.log(text));
|
|
8
|
+
// c.start();
|
|
9
|
+
// c.sendWorldState({ type: 'game.scene', data: 'tavern', retained: true });
|
|
10
|
+
// await c.sendText('what should I do?');
|
|
11
|
+
import { CompanionClient } from './client.js';
|
|
12
|
+
export { CompanionClient, CompanionError, pouchyBrandIconUrl } from './client.js';
|
|
13
|
+
export { openCompanionCall, HOST_CONTROL_TOOLS, HOST_CONTROL_TOOL_NAMES } from './call.js';
|
|
14
|
+
/** Create a companion client. */
|
|
15
|
+
export function createCompanion(opts) {
|
|
16
|
+
return new CompanionClient(opts);
|
|
17
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export declare const PROTOCOL_VERSION: 1;
|
|
2
|
+
/** App → Pouchy (control/data plane). */
|
|
3
|
+
export declare const INBOUND_TYPES: readonly ["hello", "input.text", "context.event", "context.snapshot", "tool.result", "control.start_call", "control.end_call", "control.set_modalities", "control.ping"];
|
|
4
|
+
export type InboundType = (typeof INBOUND_TYPES)[number];
|
|
5
|
+
/** Pouchy → app (control/data plane). */
|
|
6
|
+
export declare const OUTBOUND_TYPES: readonly ["hello.ack", "companion.message", "companion.audio", "companion.tool_call", "companion.ui_action", "companion.ui_update", "companion.expression", "companion.voice_inject", "companion.confirm_request", "companion.social_message", "control.call_ready", "control.error", "control.usage"];
|
|
7
|
+
export type OutboundType = (typeof OUTBOUND_TYPES)[number];
|
|
8
|
+
export type MessageType = InboundType | OutboundType;
|
|
9
|
+
/** The single envelope every control/data-plane message shares. */
|
|
10
|
+
export interface CompanionEnvelope<T = unknown> {
|
|
11
|
+
v: typeof PROTOCOL_VERSION;
|
|
12
|
+
id: string;
|
|
13
|
+
session?: string;
|
|
14
|
+
ts: number;
|
|
15
|
+
type: MessageType;
|
|
16
|
+
payload: T;
|
|
17
|
+
}
|
|
18
|
+
/** Payload of a `companion.ui_action` event — an Instant UI surface for the host
|
|
19
|
+
* to render. `interface` is the platform-neutral genui schema: a tree of UI
|
|
20
|
+
* `nodes` (Text/Slider/Button/Chart/…), an optional `state` bag input atoms
|
|
21
|
+
* read+write, and optional `computed` derived values. The SAME shape is drawn by
|
|
22
|
+
* a web embed, a native iOS/Android renderer, or a CLI/TUI — only the renderer
|
|
23
|
+
* differs per platform, never the contract. Kept structurally loose here (the
|
|
24
|
+
* SDK package carries no $lib import); the authoritative schema lives in
|
|
25
|
+
* src/lib/genui/schema.ts and the renderer-contract doc. When the panel sets
|
|
26
|
+
* `reportChanges`, the user's edits stream back as a normal text turn. */
|
|
27
|
+
export interface RenderInterfacePayload {
|
|
28
|
+
interface: {
|
|
29
|
+
title?: string;
|
|
30
|
+
nodes: Array<{
|
|
31
|
+
type: string;
|
|
32
|
+
[k: string]: unknown;
|
|
33
|
+
}>;
|
|
34
|
+
state?: Record<string, string | number | boolean>;
|
|
35
|
+
computed?: Record<string, unknown>;
|
|
36
|
+
reportChanges?: boolean;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Payload of a `companion.confirm_request` event — the companion is asking the
|
|
40
|
+
* user to approve a sensitive op (a payment, a skill run, a friend message).
|
|
41
|
+
* An embed may OBSERVE this to surface the pending approval in its own UI, but
|
|
42
|
+
* it CANNOT approve: approval is authed as the first-party Pouchy user (their
|
|
43
|
+
* app / a hosted confirm page), so a third-party DOM can never confirm a spend.
|
|
44
|
+
* A first-party native host can gate that approval behind a biometric (Face ID
|
|
45
|
+
* / passkey) — see docs/companion-wallet-noncustodial.md. */
|
|
46
|
+
export interface ConfirmRequestPayload {
|
|
47
|
+
/** Opaque id the first-party surface passes to the confirm endpoint. */
|
|
48
|
+
confirmId: string;
|
|
49
|
+
/** The scope of the op needing approval (e.g. `wallet.spend`). */
|
|
50
|
+
scope: string;
|
|
51
|
+
/** A human, user-facing summary of what approving will do. */
|
|
52
|
+
summary: string;
|
|
53
|
+
/** Advisory: when true, the first-party approval surface should require a
|
|
54
|
+
* stronger user gesture — a biometric / passkey (Face ID, Touch ID) — before
|
|
55
|
+
* approving, not just a tap. Set for irreversible money ops (wallet.spend).
|
|
56
|
+
* Cryptographic enforcement is server-side follow-up; this is the host's cue. */
|
|
57
|
+
stepUp?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Payload of a `companion.ui_update` event — a live change to an
|
|
60
|
+
* already-rendered Instant UI panel: write each `{ key, value }` into the
|
|
61
|
+
* panel's state bag (re-evaluating templates / progress / `when`), without
|
|
62
|
+
* rebuilding. `panelId` targets one of several on-screen panels when present. */
|
|
63
|
+
export interface InterfaceUpdatePayload {
|
|
64
|
+
update: {
|
|
65
|
+
panelId?: string;
|
|
66
|
+
updates: Array<{
|
|
67
|
+
key: string;
|
|
68
|
+
value: string | number | boolean;
|
|
69
|
+
}>;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Payload of a `companion.audio` event — a TTS clip for the assistant's reply on
|
|
73
|
+
* a non-call modality. Either a playable `url` or an opaque `ref` the host
|
|
74
|
+
* resolves. */
|
|
75
|
+
export interface AudioClipPayload {
|
|
76
|
+
url?: string;
|
|
77
|
+
ref?: string;
|
|
78
|
+
}
|
|
79
|
+
/** Payload of a `companion.expression` event — avatar cues for an embed rendering
|
|
80
|
+
* the companion's body (e.g. a VRM): a lip-sync `viseme`, an `expression`, or a
|
|
81
|
+
* `gesture`. A pure-text/audio host can ignore it. */
|
|
82
|
+
export interface ExpressionPayload {
|
|
83
|
+
viseme?: string;
|
|
84
|
+
expression?: string;
|
|
85
|
+
gesture?: string;
|
|
86
|
+
}
|
|
87
|
+
/** Payload of a `control.usage` event — a per-token metering echo the host can
|
|
88
|
+
* surface in a usage/billing view. */
|
|
89
|
+
export interface UsagePayload {
|
|
90
|
+
chatTurns?: number;
|
|
91
|
+
voiceSeconds?: number;
|
|
92
|
+
memoryOps?: number;
|
|
93
|
+
}
|
|
94
|
+
/** Payload of a `companion.social_message` event — an inbound A2A message from
|
|
95
|
+
* one of the user's paired friends, delivered cross-app to any live embed whose
|
|
96
|
+
* token holds `social.message`. Lets a native/web host surface friend messages
|
|
97
|
+
* in real time without the first-party Pouchy app. Only plain human messages are
|
|
98
|
+
* delivered (Pouchy-internal structured envelopes are not). */
|
|
99
|
+
export interface SocialMessagePayload {
|
|
100
|
+
/** The sender's Pouchy uid. */
|
|
101
|
+
fromUid: string;
|
|
102
|
+
/** The sender's display name. */
|
|
103
|
+
fromName: string;
|
|
104
|
+
/** The message text. */
|
|
105
|
+
content: string;
|
|
106
|
+
/** ISO timestamp of delivery. */
|
|
107
|
+
createdAt: string;
|
|
108
|
+
}
|
|
109
|
+
/** A live world-state event the app streams in. `retained:true` = latest-value
|
|
110
|
+
* state (coalesced); `retained:false` = a transient moment carrying `salience`
|
|
111
|
+
* and optional `voiceRelevant`. */
|
|
112
|
+
export interface WorldStateEvent<D = unknown> {
|
|
113
|
+
specversion: '1.0';
|
|
114
|
+
id: string;
|
|
115
|
+
source: string;
|
|
116
|
+
/** Namespaced dotted kind, e.g. `game.player.hp`, `game.event.boss_spawned`. */
|
|
117
|
+
type: string;
|
|
118
|
+
time?: string;
|
|
119
|
+
data: D;
|
|
120
|
+
retained?: boolean;
|
|
121
|
+
ttl?: number;
|
|
122
|
+
salience?: number;
|
|
123
|
+
voiceRelevant?: boolean;
|
|
124
|
+
}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The wire contract, as the SDK sees it — a self-contained copy so the package
|
|
2
|
+
// has ZERO repo-internal ($lib) imports and publishes / builds with plain tsc.
|
|
3
|
+
//
|
|
4
|
+
// This mirrors the server's source of truth (src/lib/types/companion-protocol.ts);
|
|
5
|
+
// a drift test (protocol.drift.test.ts) keeps PROTOCOL_VERSION + the type unions
|
|
6
|
+
// in sync. Adding an outbound type is non-breaking (receivers ignore unknowns).
|
|
7
|
+
export const PROTOCOL_VERSION = 1;
|
|
8
|
+
/** App → Pouchy (control/data plane). */
|
|
9
|
+
export const INBOUND_TYPES = [
|
|
10
|
+
'hello',
|
|
11
|
+
'input.text',
|
|
12
|
+
'context.event',
|
|
13
|
+
'context.snapshot',
|
|
14
|
+
'tool.result',
|
|
15
|
+
'control.start_call',
|
|
16
|
+
'control.end_call',
|
|
17
|
+
'control.set_modalities',
|
|
18
|
+
'control.ping'
|
|
19
|
+
];
|
|
20
|
+
/** Pouchy → app (control/data plane). */
|
|
21
|
+
export const OUTBOUND_TYPES = [
|
|
22
|
+
'hello.ack',
|
|
23
|
+
'companion.message',
|
|
24
|
+
'companion.audio',
|
|
25
|
+
'companion.tool_call',
|
|
26
|
+
'companion.ui_action',
|
|
27
|
+
'companion.ui_update',
|
|
28
|
+
'companion.expression',
|
|
29
|
+
'companion.voice_inject',
|
|
30
|
+
'companion.confirm_request',
|
|
31
|
+
'companion.social_message',
|
|
32
|
+
'control.call_ready',
|
|
33
|
+
'control.error',
|
|
34
|
+
'control.usage'
|
|
35
|
+
];
|
package/dist/sse.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SseEvent {
|
|
2
|
+
/** The `event:` field; defaults to "message" per the SSE spec. */
|
|
3
|
+
event: string;
|
|
4
|
+
/** The joined `data:` field(s). */
|
|
5
|
+
data: string;
|
|
6
|
+
}
|
|
7
|
+
/** Parse all COMPLETE frames out of an accumulated buffer. Returns the parsed
|
|
8
|
+
* events plus the unparsed remainder (an incomplete trailing frame). */
|
|
9
|
+
export declare function parseSse(buffer: string): {
|
|
10
|
+
events: SseEvent[];
|
|
11
|
+
rest: string;
|
|
12
|
+
};
|
package/dist/sse.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Minimal, dependency-free SSE frame parser for the companion SDK. The browser's
|
|
2
|
+
// EventSource can't set an Authorization header, so the SDK consumes the stream
|
|
3
|
+
// over fetch() instead and parses frames itself — this is that parser, kept pure
|
|
4
|
+
// so it unit-tests in isolation.
|
|
5
|
+
//
|
|
6
|
+
// An SSE frame is a block of `field: value` lines terminated by a blank line.
|
|
7
|
+
// Comment lines (starting ":") are keep-alives and ignored. A chunk off the wire
|
|
8
|
+
// may end mid-frame, so the parser returns the leftover for the next call.
|
|
9
|
+
/** Parse all COMPLETE frames out of an accumulated buffer. Returns the parsed
|
|
10
|
+
* events plus the unparsed remainder (an incomplete trailing frame). */
|
|
11
|
+
export function parseSse(buffer) {
|
|
12
|
+
const events = [];
|
|
13
|
+
let working = buffer.replace(/\r\n/g, '\n');
|
|
14
|
+
let sep = working.indexOf('\n\n');
|
|
15
|
+
while (sep !== -1) {
|
|
16
|
+
const block = working.slice(0, sep);
|
|
17
|
+
working = working.slice(sep + 2);
|
|
18
|
+
let event = 'message';
|
|
19
|
+
const dataLines = [];
|
|
20
|
+
for (const line of block.split('\n')) {
|
|
21
|
+
if (!line || line.startsWith(':'))
|
|
22
|
+
continue; // blank or comment (keep-alive)
|
|
23
|
+
const ci = line.indexOf(':');
|
|
24
|
+
const field = ci === -1 ? line : line.slice(0, ci);
|
|
25
|
+
let value = ci === -1 ? '' : line.slice(ci + 1);
|
|
26
|
+
if (value.startsWith(' '))
|
|
27
|
+
value = value.slice(1); // single leading space stripped
|
|
28
|
+
if (field === 'event')
|
|
29
|
+
event = value;
|
|
30
|
+
else if (field === 'data')
|
|
31
|
+
dataLines.push(value);
|
|
32
|
+
}
|
|
33
|
+
if (dataLines.length)
|
|
34
|
+
events.push({ event, data: dataLines.join('\n') });
|
|
35
|
+
sep = working.indexOf('\n\n');
|
|
36
|
+
}
|
|
37
|
+
return { events, rest: working };
|
|
38
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Build the WebSocket stream URL from the HTTP base. http→ws, https→wss. The
|
|
2
|
+
* token rides the query string because browsers can't set an Authorization
|
|
3
|
+
* header on a WebSocket handshake; the server authenticates it the same way it
|
|
4
|
+
* authenticates the SSE `?cursor=` stream. `cursor` resumes after a reconnect,
|
|
5
|
+
* mirroring the SSE plane. */
|
|
6
|
+
export declare function deriveWsStreamUrl(baseUrl: string, sessionId: string, token: string, cursor?: number): string;
|
|
7
|
+
/** Normalize an incoming WS message into the `{ event, data }` shape the SSE
|
|
8
|
+
* frame handler already consumes, so both transports converge on one code
|
|
9
|
+
* path. Accepts two wire forms:
|
|
10
|
+
* 1. a framed message: { "event": "...", "data": "<json string>" }
|
|
11
|
+
* 2. a bare envelope: { "type": "...", ... } → event defaults to "message"
|
|
12
|
+
* Returns null for anything unparseable (the caller drops it). */
|
|
13
|
+
export declare function decodeWsMessage(raw: string): {
|
|
14
|
+
event: string;
|
|
15
|
+
data: string;
|
|
16
|
+
} | null;
|
|
17
|
+
/** Frame an outbound user-input message for the WS plane (for hosts that also
|
|
18
|
+
* accept input over the socket). Mirrors the REST `/input` body. */
|
|
19
|
+
export declare function encodeWsInput(text: string, opts?: {
|
|
20
|
+
image?: string;
|
|
21
|
+
}): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
/** Build the WebSocket stream URL from the HTTP base. http→ws, https→wss. The
|
|
16
|
+
* token rides the query string because browsers can't set an Authorization
|
|
17
|
+
* header on a WebSocket handshake; the server authenticates it the same way it
|
|
18
|
+
* authenticates the SSE `?cursor=` stream. `cursor` resumes after a reconnect,
|
|
19
|
+
* mirroring the SSE plane. */
|
|
20
|
+
export function deriveWsStreamUrl(baseUrl, sessionId, token, cursor = 0) {
|
|
21
|
+
const origin = baseUrl.replace(/\/+$/, '').replace(/^http(s?):\/\//i, (_m, s) => `ws${s}://`);
|
|
22
|
+
const id = encodeURIComponent(sessionId);
|
|
23
|
+
const q = new URLSearchParams({ cursor: String(cursor), token });
|
|
24
|
+
return `${origin}/api/companion/session/${id}/ws?${q.toString()}`;
|
|
25
|
+
}
|
|
26
|
+
/** Normalize an incoming WS message into the `{ event, data }` shape the SSE
|
|
27
|
+
* frame handler already consumes, so both transports converge on one code
|
|
28
|
+
* path. Accepts two wire forms:
|
|
29
|
+
* 1. a framed message: { "event": "...", "data": "<json string>" }
|
|
30
|
+
* 2. a bare envelope: { "type": "...", ... } → event defaults to "message"
|
|
31
|
+
* Returns null for anything unparseable (the caller drops it). */
|
|
32
|
+
export function decodeWsMessage(raw) {
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = JSON.parse(raw);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (!parsed || typeof parsed !== 'object')
|
|
41
|
+
return null;
|
|
42
|
+
const obj = parsed;
|
|
43
|
+
// Form 1 — explicit frame.
|
|
44
|
+
if (typeof obj.event === 'string') {
|
|
45
|
+
const data = typeof obj.data === 'string' ? obj.data : JSON.stringify(obj.data ?? {});
|
|
46
|
+
return { event: obj.event, data };
|
|
47
|
+
}
|
|
48
|
+
// Form 2 — bare envelope. Re-serialize so handleFrame can JSON.parse it back
|
|
49
|
+
// into a CompanionEnvelope exactly as the SSE path does.
|
|
50
|
+
if (typeof obj.type === 'string') {
|
|
51
|
+
return { event: 'message', data: raw };
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/** Frame an outbound user-input message for the WS plane (for hosts that also
|
|
56
|
+
* accept input over the socket). Mirrors the REST `/input` body. */
|
|
57
|
+
export function encodeWsInput(text, opts) {
|
|
58
|
+
const payload = { type: 'input', text };
|
|
59
|
+
if (opts?.image)
|
|
60
|
+
payload.image = opts.image;
|
|
61
|
+
return JSON.stringify(payload);
|
|
62
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pouchy_ai/companion-sdk",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Embed the Pouchy companion — chat, voice, tools, memory, live world-state, instant UI, and agent-to-agent messaging — in any app, game, or site.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"homepage": "https://www.pouchy.ai",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/oviswang/Pouchy.git",
|
|
11
|
+
"directory": "packages/companion-sdk"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"email": "support@pouchy.ai"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"files": ["dist", "README.md", "CHANGELOG.md", "LICENSE"],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -p tsconfig.json && node scripts/fix-esm-extensions.mjs",
|
|
28
|
+
"build:cdn": "esbuild ../../src/lib/companion-sdk/index.ts --bundle --format=esm --platform=browser --external:@elevenlabs/client --legal-comments=none --outfile=cdn/companion-sdk.js && node scripts/cdn-banner.mjs",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@elevenlabs/client": "^1.9.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"@elevenlabs/client": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"esbuild": "^0.24.0",
|
|
41
|
+
"typescript": "^5.5.0"
|
|
42
|
+
},
|
|
43
|
+
"keywords": ["pouchy", "ai-companion", "voice", "agent", "sdk"],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
}
|
|
47
|
+
}
|