@burmese/client 3.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenPets
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,23 @@
1
+ export interface OpenPetsDiscoveryFile {
2
+ readonly protocolVersion: 1;
3
+ readonly protocol: "openpets-ipc";
4
+ readonly endpoint: string;
5
+ readonly token: string;
6
+ readonly appVersion: string;
7
+ readonly pid: number;
8
+ readonly platform: NodeJS.Platform;
9
+ }
10
+ export type ParsedIpcEndpoint = {
11
+ readonly kind: "tcp";
12
+ readonly host: string;
13
+ readonly port: number;
14
+ } | {
15
+ readonly kind: "path";
16
+ readonly path: string;
17
+ };
18
+ export declare function getDiscoveryFilePath(): string;
19
+ export declare function readDiscoveryFile(path?: string): OpenPetsDiscoveryFile;
20
+ export declare function validateDiscovery(value: unknown): OpenPetsDiscoveryFile;
21
+ export declare function validateEndpoint(endpoint: string): void;
22
+ export declare function parseIpcEndpoint(endpoint: string): ParsedIpcEndpoint;
23
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CACpC;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,wBAAgB,oBAAoB,IAAI,MAAM,CAmB7C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,SAAyB,GAAG,qBAAqB,CAsBtF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,CAwBvE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAgCpE"}
@@ -0,0 +1,203 @@
1
+ import { existsSync, lstatSync, readFileSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { basename, dirname, join } from "node:path";
4
+ import { isRecord, maxIpcMessageBytes, openPetsIpcProtocol, openPetsIpcVersion, OpenPetsClientError } from "./protocol.js";
5
+ export function getDiscoveryFilePath() {
6
+ if (process.env.OPENPETS_DISCOVERY_FILE) {
7
+ return process.env.OPENPETS_DISCOVERY_FILE;
8
+ }
9
+ if (process.platform === "darwin") {
10
+ return join(homedir(), "Library", "Application Support", "OpenPets", "runtime", "ipc.json");
11
+ }
12
+ if (process.platform === "win32") {
13
+ return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "OpenPets", "runtime", "ipc.json");
14
+ }
15
+ const xdg = getSecureXdgRuntimeDir();
16
+ if (xdg) {
17
+ return join(xdg, "openpets", "ipc.json");
18
+ }
19
+ return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "OpenPets", "runtime", "ipc.json");
20
+ }
21
+ export function readDiscoveryFile(path = getDiscoveryFilePath()) {
22
+ let raw;
23
+ try {
24
+ const stat = statSync(path);
25
+ if (!stat.isFile())
26
+ throw new OpenPetsClientError("invalid_discovery", "OpenPets discovery path is not a file.");
27
+ if (stat.size > maxIpcMessageBytes)
28
+ throw new OpenPetsClientError("invalid_discovery", "OpenPets discovery file is too large.");
29
+ raw = readFileSync(path, "utf8");
30
+ }
31
+ catch (error) {
32
+ if (error instanceof OpenPetsClientError)
33
+ throw error;
34
+ throw new OpenPetsClientError("unavailable", `OpenPets discovery file is unavailable: ${error instanceof Error ? error.message : "unknown error"}`);
35
+ }
36
+ if (Buffer.byteLength(raw, "utf8") > maxIpcMessageBytes) {
37
+ throw new OpenPetsClientError("invalid_discovery", "OpenPets discovery file is too large.");
38
+ }
39
+ try {
40
+ return validateDiscovery(JSON.parse(raw));
41
+ }
42
+ catch (error) {
43
+ if (error instanceof OpenPetsClientError)
44
+ throw error;
45
+ throw new OpenPetsClientError("invalid_discovery", "OpenPets discovery file is malformed JSON.");
46
+ }
47
+ }
48
+ export function validateDiscovery(value) {
49
+ if (!isRecord(value))
50
+ throw new OpenPetsClientError("invalid_discovery", "Discovery must be an object.");
51
+ if (value.protocol !== openPetsIpcProtocol)
52
+ throw new OpenPetsClientError("invalid_discovery", "Discovery protocol is invalid.");
53
+ if (value.protocolVersion !== openPetsIpcVersion)
54
+ throw new OpenPetsClientError("invalid_discovery", "Discovery protocol version is invalid.");
55
+ if (typeof value.endpoint !== "string")
56
+ throw new OpenPetsClientError("invalid_discovery", "Discovery endpoint is invalid.");
57
+ if (typeof value.token !== "string" || value.token.length < 16 || value.token.length > 256)
58
+ throw new OpenPetsClientError("invalid_discovery", "Discovery token is invalid.");
59
+ if (typeof value.appVersion !== "string")
60
+ throw new OpenPetsClientError("invalid_discovery", "Discovery app version is invalid.");
61
+ if (typeof value.pid !== "number" || !Number.isInteger(value.pid) || value.pid <= 0)
62
+ throw new OpenPetsClientError("invalid_discovery", "Discovery pid is invalid.");
63
+ if (value.platform !== "darwin" && value.platform !== "linux" && value.platform !== "win32")
64
+ throw new OpenPetsClientError("invalid_discovery", "Discovery platform is invalid.");
65
+ const endpoint = parseIpcEndpoint(value.endpoint);
66
+ if (value.platform !== process.platform && !allowsCrossPlatformDiscovery(value.platform, endpoint)) {
67
+ throw new OpenPetsClientError("invalid_discovery", "Discovery platform does not match this client.");
68
+ }
69
+ return {
70
+ protocolVersion: openPetsIpcVersion,
71
+ protocol: openPetsIpcProtocol,
72
+ endpoint: value.endpoint,
73
+ token: value.token,
74
+ appVersion: value.appVersion,
75
+ pid: value.pid,
76
+ platform: value.platform,
77
+ };
78
+ }
79
+ export function validateEndpoint(endpoint) {
80
+ parseIpcEndpoint(endpoint);
81
+ }
82
+ export function parseIpcEndpoint(endpoint) {
83
+ if (endpoint.length < 1 || endpoint.length > 240)
84
+ throw new OpenPetsClientError("invalid_discovery", "Discovery endpoint length is invalid.");
85
+ if (endpoint.includes("\0"))
86
+ throw new OpenPetsClientError("invalid_discovery", "Discovery endpoint contains NUL.");
87
+ if (endpoint.startsWith("tcp://")) {
88
+ return parseTcpEndpoint(endpoint);
89
+ }
90
+ if (process.platform === "win32") {
91
+ if (!endpoint.startsWith("\\\\.\\pipe\\openpets-") || endpoint.includes("/")) {
92
+ throw new OpenPetsClientError("invalid_discovery", "Discovery endpoint is not an OpenPets named pipe.");
93
+ }
94
+ return { kind: "path", path: endpoint };
95
+ }
96
+ if (!endpoint.startsWith("/") || endpoint.includes("://") || endpoint.includes("..")) {
97
+ throw new OpenPetsClientError("invalid_discovery", "Discovery endpoint is not an absolute Unix socket path.");
98
+ }
99
+ if (!basename(endpoint).startsWith("openpets-") || !basename(endpoint).endsWith(".sock")) {
100
+ throw new OpenPetsClientError("invalid_discovery", "Discovery endpoint filename is not an OpenPets socket.");
101
+ }
102
+ const parent = dirname(endpoint);
103
+ const parentName = basename(parent);
104
+ const isTmpRuntime = parent.startsWith("/tmp/") && parentName.startsWith("openpets-");
105
+ const isXdgRuntime = parentName === "openpets";
106
+ if (!isTmpRuntime && !isXdgRuntime) {
107
+ throw new OpenPetsClientError("invalid_discovery", "Discovery endpoint is outside an expected OpenPets runtime directory.");
108
+ }
109
+ return { kind: "path", path: endpoint };
110
+ }
111
+ function parseTcpEndpoint(endpoint) {
112
+ let url;
113
+ try {
114
+ url = new URL(endpoint);
115
+ }
116
+ catch {
117
+ throw new OpenPetsClientError("invalid_discovery", "Discovery TCP endpoint is invalid.");
118
+ }
119
+ if (url.protocol !== "tcp:" || url.username || url.password || (url.pathname !== "" && url.pathname !== "/") || url.search || url.hash) {
120
+ throw new OpenPetsClientError("invalid_discovery", "Discovery TCP endpoint must be tcp://<host>:<port> with no credentials, path, query, or fragment.");
121
+ }
122
+ const host = url.hostname;
123
+ // Reject 0.0.0.0 - not a valid target address
124
+ if (host === "0.0.0.0") {
125
+ throw new OpenPetsClientError("invalid_discovery", "Discovery TCP endpoint cannot use 0.0.0.0 as target host.");
126
+ }
127
+ // Validate IPv4 format
128
+ if (!isValidIpv4(host)) {
129
+ throw new OpenPetsClientError("invalid_discovery", "Discovery TCP endpoint host must be a valid IPv4 address.");
130
+ }
131
+ // Reject hostnames (contain letters)
132
+ if (/[a-zA-Z]/.test(host)) {
133
+ throw new OpenPetsClientError("invalid_discovery", "Discovery TCP endpoint host must be an IPv4 address, not a hostname.");
134
+ }
135
+ // Validate that it's a private/local address (loopback, private, or link-local)
136
+ if (!isPrivateOrLocalIpv4(host)) {
137
+ throw new OpenPetsClientError("invalid_discovery", `Discovery TCP endpoint host ${host} is not a private/local IPv4 address. Only loopback (127.0.0.1), private (10.x.x.x, 172.16-31.x.x, 192.168.x.x), or link-local (169.254.x.x) addresses are allowed.`);
138
+ }
139
+ const port = Number(url.port);
140
+ if (!Number.isInteger(port) || port < 1 || port > 65_535 || String(port) !== url.port) {
141
+ throw new OpenPetsClientError("invalid_discovery", "Discovery TCP endpoint port is invalid.");
142
+ }
143
+ return { kind: "tcp", host, port };
144
+ }
145
+ function isValidIpv4(host) {
146
+ const parts = host.split(".");
147
+ if (parts.length !== 4)
148
+ return false;
149
+ return parts.every((part) => {
150
+ const num = Number(part);
151
+ return String(num) === part && num >= 0 && num <= 255;
152
+ });
153
+ }
154
+ function isPrivateOrLocalIpv4(host) {
155
+ const parts = host.split(".").map(Number);
156
+ if (parts.length !== 4)
157
+ return false;
158
+ // Loopback: 127.0.0.0/8
159
+ if (parts[0] === 127)
160
+ return true;
161
+ // Private: 10.0.0.0/8
162
+ if (parts[0] === 10)
163
+ return true;
164
+ // Private: 172.16.0.0/12
165
+ if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31)
166
+ return true;
167
+ // Private: 192.168.0.0/16
168
+ if (parts[0] === 192 && parts[1] === 168)
169
+ return true;
170
+ // Link-local: 169.254.0.0/16
171
+ if (parts[0] === 169 && parts[1] === 254)
172
+ return true;
173
+ return false;
174
+ }
175
+ function allowsCrossPlatformDiscovery(platform, endpoint) {
176
+ // Allow cross-platform discovery for TCP endpoints when:
177
+ // - Desktop is Windows (win32) and client is Linux (WSL)
178
+ // - The endpoint is a private/local IPv4 address (not just loopback)
179
+ if (endpoint.kind !== "tcp" || platform !== "win32" || process.platform !== "linux") {
180
+ return false;
181
+ }
182
+ // Additional validation: ensure the host is a valid private/local IPv4
183
+ return isPrivateOrLocalIpv4(endpoint.host);
184
+ }
185
+ function getSecureXdgRuntimeDir() {
186
+ const dir = process.env.XDG_RUNTIME_DIR;
187
+ if (!dir || !existsSync(dir))
188
+ return null;
189
+ try {
190
+ const stat = lstatSync(dir);
191
+ if (!stat.isDirectory() || stat.isSymbolicLink())
192
+ return null;
193
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid())
194
+ return null;
195
+ if ((stat.mode & 0o777) !== 0o700)
196
+ return null;
197
+ return dir;
198
+ }
199
+ catch {
200
+ return null;
201
+ }
202
+ }
203
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { type OpenPetsDiscoveryFile } from "./discovery.js";
2
+ import { type OpenPetsIpcMethod, type OpenPetsReaction } from "./protocol.js";
3
+ export { getDiscoveryFilePath, parseIpcEndpoint, readDiscoveryFile, validateDiscovery, validateEndpoint, type OpenPetsDiscoveryFile, type ParsedIpcEndpoint } from "./discovery.js";
4
+ export { allowedReactions, OpenPetsClientError, type OpenPetsReaction } from "./protocol.js";
5
+ /** Which transport an update arrived through, so the desktop can join a session's sources. */
6
+ export type OpenPetsSource = "mcp" | "hook" | "plugin" | "cli" | "ipc";
7
+ export interface OpenPetsClientOptions {
8
+ readonly discoveryPath?: string;
9
+ readonly connectTimeoutMs?: number;
10
+ readonly responseTimeoutMs?: number;
11
+ readonly clientLabel?: string;
12
+ /** Default transport for this client's react/say (per-call `source` overrides). */
13
+ readonly source?: OpenPetsSource;
14
+ }
15
+ export interface OpenPetsStatusResult {
16
+ readonly ok: boolean;
17
+ readonly appRunning: boolean;
18
+ readonly unavailableReason?: string;
19
+ readonly [key: string]: unknown;
20
+ }
21
+ export interface OpenPetsLeaseResult {
22
+ readonly leaseId: string;
23
+ readonly requestedPetId?: string;
24
+ readonly targetKind: "default" | "explicit";
25
+ readonly actualTargetPetId: string;
26
+ readonly actualTargetPetName: string;
27
+ readonly usingDefaultPet: boolean;
28
+ readonly fallbackReason?: string;
29
+ readonly expiresAt: number;
30
+ readonly leaseActive: boolean;
31
+ }
32
+ export interface OpenPetsPetListResult {
33
+ readonly ok: true;
34
+ readonly pets: readonly OpenPetsPetListItem[];
35
+ readonly defaultPetId: string;
36
+ }
37
+ export interface OpenPetsPetInstallResult {
38
+ readonly ok: true;
39
+ readonly petId: string;
40
+ readonly displayName: string;
41
+ readonly installed: true;
42
+ }
43
+ export interface OpenPetsPetListItem {
44
+ readonly id: string;
45
+ readonly displayName: string;
46
+ readonly builtIn: boolean;
47
+ readonly broken: boolean;
48
+ }
49
+ export interface OpenPetsClient {
50
+ hello(): Promise<unknown>;
51
+ status(options?: {
52
+ readonly leaseId?: string;
53
+ }): Promise<OpenPetsStatusResult>;
54
+ listPets(): Promise<OpenPetsPetListResult>;
55
+ installPet(petId: string): Promise<OpenPetsPetInstallResult>;
56
+ acquireLease(options?: {
57
+ readonly requestedPetId?: string;
58
+ }): Promise<OpenPetsLeaseResult>;
59
+ heartbeatLease(leaseId: string): Promise<{
60
+ readonly leaseId: string;
61
+ readonly expiresAt: number;
62
+ }>;
63
+ releaseLease(leaseId: string): Promise<{
64
+ readonly released: boolean;
65
+ }>;
66
+ react(reaction: OpenPetsReaction, options?: {
67
+ readonly leaseId?: string;
68
+ readonly clientLabel?: string;
69
+ readonly source?: OpenPetsSource;
70
+ readonly externalSessionId?: string;
71
+ }): Promise<unknown>;
72
+ say(message: string, options?: {
73
+ readonly reaction?: OpenPetsReaction;
74
+ readonly leaseId?: string;
75
+ readonly clientLabel?: string;
76
+ readonly source?: OpenPetsSource;
77
+ readonly externalSessionId?: string;
78
+ }): Promise<unknown>;
79
+ }
80
+ export declare function createOpenPetsClient(options?: OpenPetsClientOptions): OpenPetsClient;
81
+ export declare function parsePetInstallResult(value: unknown): OpenPetsPetInstallResult;
82
+ export declare function parsePetListResult(value: unknown): OpenPetsPetListResult;
83
+ export declare function sendRequest<T>(discovery: OpenPetsDiscoveryFile, method: OpenPetsIpcMethod, params: unknown, options?: OpenPetsClientOptions): Promise<T>;
84
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,136 @@
1
+ import net from "node:net";
2
+ import { randomUUID } from "node:crypto";
3
+ import { parseIpcEndpoint, readDiscoveryFile } from "./discovery.js";
4
+ import { connectTimeoutMs, maxIpcMessageBytes, openPetsIpcVersion, parseIpcResponse, responseTimeoutMs, validateReaction, OpenPetsClientError } from "./protocol.js";
5
+ export { getDiscoveryFilePath, parseIpcEndpoint, readDiscoveryFile, validateDiscovery, validateEndpoint } from "./discovery.js";
6
+ export { allowedReactions, OpenPetsClientError } from "./protocol.js";
7
+ /**
8
+ * Stable per-process session nonce, generated once at module load.
9
+ * Sent alongside clientPid in lease.acquire so the desktop can distinguish
10
+ * a genuine re-acquire (same process, same nonce) from an OS PID-reuse
11
+ * collision (same PID, different nonce → fresh acquire, no stale-lease reuse).
12
+ */
13
+ const SESSION_NONCE = randomUUID();
14
+ export function createOpenPetsClient(options = {}) {
15
+ return {
16
+ hello: () => sendDiscoveredRequest("hello", {}, options),
17
+ status: async (statusOptions) => {
18
+ try {
19
+ return await sendDiscoveredRequest("status", { leaseId: statusOptions?.leaseId }, options);
20
+ }
21
+ catch (error) {
22
+ return {
23
+ ok: false,
24
+ appRunning: false,
25
+ unavailableReason: error instanceof Error ? error.message : "OpenPets is unavailable.",
26
+ };
27
+ }
28
+ },
29
+ listPets: async () => parsePetListResult(await sendDiscoveredRequest("pets.list", {}, options)),
30
+ installPet: async (petId) => parsePetInstallResult(await sendDiscoveredRequest("pets.install", { petId: validatePetId(petId) }, { ...options, responseTimeoutMs: options.responseTimeoutMs ?? 60_000 })),
31
+ acquireLease: (leaseOptions) => sendDiscoveredRequest("lease.acquire", { requestedPetId: leaseOptions?.requestedPetId, clientPid: process.pid, sessionNonce: SESSION_NONCE }, options),
32
+ heartbeatLease: (leaseId) => sendDiscoveredRequest("lease.heartbeat", { leaseId }, options),
33
+ releaseLease: (leaseId) => sendDiscoveredRequest("lease.release", { leaseId }, options),
34
+ react: (reaction, reactOptions) => sendDiscoveredRequest("pet.react", { reaction: validateReaction(reaction), leaseId: reactOptions?.leaseId, clientLabel: reactOptions?.clientLabel ?? options.clientLabel, clientSessionId: SESSION_NONCE, source: reactOptions?.source ?? options.source, externalSessionId: reactOptions?.externalSessionId }, options),
35
+ say: (message, sayOptions) => sendDiscoveredRequest("pet.say", { message, reaction: sayOptions?.reaction, leaseId: sayOptions?.leaseId, clientLabel: sayOptions?.clientLabel ?? options.clientLabel, clientSessionId: SESSION_NONCE, source: sayOptions?.source ?? options.source, externalSessionId: sayOptions?.externalSessionId }, options),
36
+ };
37
+ }
38
+ export function parsePetInstallResult(value) {
39
+ if (!isRecord(value) || value.ok !== true || typeof value.petId !== "string" || typeof value.displayName !== "string" || value.installed !== true) {
40
+ throw new OpenPetsClientError("invalid_response", "OpenPets pet install response is invalid.");
41
+ }
42
+ return { ok: true, petId: value.petId, displayName: value.displayName, installed: true };
43
+ }
44
+ function validatePetId(value) {
45
+ if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(value) || value === "builtin") {
46
+ throw new OpenPetsClientError("invalid_pet_id", "Invalid OpenPets pet id.");
47
+ }
48
+ return value;
49
+ }
50
+ export function parsePetListResult(value) {
51
+ if (!isRecord(value) || value.ok !== true || !Array.isArray(value.pets) || typeof value.defaultPetId !== "string") {
52
+ throw new OpenPetsClientError("invalid_response", "OpenPets pet list response is invalid.");
53
+ }
54
+ return {
55
+ ok: true,
56
+ defaultPetId: value.defaultPetId,
57
+ pets: value.pets.map(parsePetListItem),
58
+ };
59
+ }
60
+ function parsePetListItem(value) {
61
+ if (!isRecord(value) || typeof value.id !== "string" || typeof value.displayName !== "string" || typeof value.builtIn !== "boolean" || typeof value.broken !== "boolean") {
62
+ throw new OpenPetsClientError("invalid_response", "OpenPets pet list item is invalid.");
63
+ }
64
+ return { id: value.id, displayName: value.displayName, builtIn: value.builtIn, broken: value.broken };
65
+ }
66
+ function isRecord(value) {
67
+ return typeof value === "object" && value !== null;
68
+ }
69
+ async function sendDiscoveredRequest(method, params, options) {
70
+ const discovery = readDiscoveryFile(options.discoveryPath);
71
+ return sendRequest(discovery, method, params, options);
72
+ }
73
+ export function sendRequest(discovery, method, params, options = {}) {
74
+ const request = {
75
+ id: randomUUID(),
76
+ version: openPetsIpcVersion,
77
+ token: discovery.token,
78
+ method,
79
+ params,
80
+ };
81
+ const requestLine = `${JSON.stringify(request)}\n`;
82
+ if (Buffer.byteLength(requestLine, "utf8") > maxIpcMessageBytes) {
83
+ return Promise.reject(new OpenPetsClientError("request_too_large", "OpenPets IPC request is too large."));
84
+ }
85
+ return new Promise((resolve, reject) => {
86
+ const endpoint = parseIpcEndpoint(discovery.endpoint);
87
+ const socket = endpoint.kind === "tcp" ? net.createConnection({ host: endpoint.host, port: endpoint.port }) : net.createConnection(endpoint.path);
88
+ let buffer = "";
89
+ let settled = false;
90
+ const connectTimer = setTimeout(() => finish(new OpenPetsClientError("connect_timeout", "Timed out connecting to OpenPets.")), options.connectTimeoutMs ?? connectTimeoutMs);
91
+ const responseTimer = setTimeout(() => finish(new OpenPetsClientError("response_timeout", "Timed out waiting for OpenPets response.")), options.responseTimeoutMs ?? responseTimeoutMs);
92
+ const finish = (error, result) => {
93
+ if (settled)
94
+ return;
95
+ settled = true;
96
+ clearTimeout(connectTimer);
97
+ clearTimeout(responseTimer);
98
+ socket.destroy();
99
+ if (error)
100
+ reject(error);
101
+ else
102
+ resolve(result);
103
+ };
104
+ socket.setEncoding("utf8");
105
+ socket.once("connect", () => {
106
+ clearTimeout(connectTimer);
107
+ socket.write(requestLine);
108
+ });
109
+ socket.on("data", (chunk) => {
110
+ buffer += chunk;
111
+ if (Buffer.byteLength(buffer, "utf8") > maxIpcMessageBytes) {
112
+ finish(new OpenPetsClientError("response_too_large", "OpenPets IPC response is too large."));
113
+ return;
114
+ }
115
+ const newline = buffer.indexOf("\n");
116
+ if (newline === -1)
117
+ return;
118
+ try {
119
+ const parsed = parseIpcResponse(JSON.parse(buffer.slice(0, newline)));
120
+ if (parsed.ok)
121
+ finish(undefined, parsed.result);
122
+ else
123
+ finish(new OpenPetsClientError(parsed.error.code, parsed.error.message));
124
+ }
125
+ catch (error) {
126
+ finish(error);
127
+ }
128
+ });
129
+ socket.once("error", (error) => finish(new OpenPetsClientError("unavailable", error.message)));
130
+ socket.once("end", () => {
131
+ if (!settled)
132
+ finish(new OpenPetsClientError("connection_closed", "OpenPets closed the IPC connection before responding."));
133
+ });
134
+ });
135
+ }
136
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ export declare const openPetsIpcProtocol = "openpets-ipc";
2
+ export declare const openPetsIpcVersion = 1;
3
+ export declare const maxIpcMessageBytes: number;
4
+ export declare const connectTimeoutMs = 2000;
5
+ export declare const responseTimeoutMs = 3000;
6
+ export declare const allowedReactions: readonly ["idle", "thinking", "working", "editing", "running", "testing", "waiting", "waving", "success", "error", "celebrating"];
7
+ export type OpenPetsReaction = typeof allowedReactions[number];
8
+ export type OpenPetsIpcMethod = "hello" | "status" | "pets.list" | "pets.install" | "lease.acquire" | "lease.heartbeat" | "lease.release" | "pet.react" | "pet.say";
9
+ export interface OpenPetsIpcRequest {
10
+ readonly id: string;
11
+ readonly version: 1;
12
+ readonly token: string;
13
+ readonly method: OpenPetsIpcMethod;
14
+ readonly params?: unknown;
15
+ }
16
+ export interface OpenPetsIpcOkResponse<T = unknown> {
17
+ readonly id: string | null;
18
+ readonly ok: true;
19
+ readonly result: T;
20
+ }
21
+ export interface OpenPetsIpcErrorResponse {
22
+ readonly id: string | null;
23
+ readonly ok: false;
24
+ readonly error: {
25
+ readonly code: string;
26
+ readonly message: string;
27
+ };
28
+ }
29
+ export type OpenPetsIpcResponse<T = unknown> = OpenPetsIpcOkResponse<T> | OpenPetsIpcErrorResponse;
30
+ export declare function parseIpcResponse<T = unknown>(value: unknown): OpenPetsIpcResponse<T>;
31
+ export declare function validateReaction(value: string): OpenPetsReaction;
32
+ export declare class OpenPetsClientError extends Error {
33
+ readonly code: string;
34
+ constructor(code: string, message: string);
35
+ }
36
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
37
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AAEvC,eAAO,MAAM,gBAAgB,mIAYnB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,cAAc,GAAG,eAAe,GAAG,iBAAiB,GAAG,eAAe,GAAG,WAAW,GAAG,SAAS,CAAC;AAEpK,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,GAAG,OAAO;IAChD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,OAAO,IAAI,qBAAqB,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC;AAEnG,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAapF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAKhE;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAGnD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE"}
@@ -0,0 +1,48 @@
1
+ export const openPetsIpcProtocol = "openpets-ipc";
2
+ export const openPetsIpcVersion = 1;
3
+ export const maxIpcMessageBytes = 16 * 1024;
4
+ export const connectTimeoutMs = 2_000;
5
+ export const responseTimeoutMs = 3_000;
6
+ export const allowedReactions = [
7
+ "idle",
8
+ "thinking",
9
+ "working",
10
+ "editing",
11
+ "running",
12
+ "testing",
13
+ "waiting",
14
+ "waving",
15
+ "success",
16
+ "error",
17
+ "celebrating",
18
+ ];
19
+ export function parseIpcResponse(value) {
20
+ if (!isRecord(value))
21
+ throw new OpenPetsClientError("invalid_response", "IPC response must be an object.");
22
+ if (typeof value.id !== "string" && value.id !== null)
23
+ throw new OpenPetsClientError("invalid_response", "IPC response id is invalid.");
24
+ if (value.ok === true) {
25
+ return { id: value.id, ok: true, result: value.result };
26
+ }
27
+ if (value.ok === false && isRecord(value.error) && typeof value.error.code === "string" && typeof value.error.message === "string") {
28
+ return { id: value.id, ok: false, error: { code: value.error.code, message: value.error.message } };
29
+ }
30
+ throw new OpenPetsClientError("invalid_response", "IPC response shape is invalid.");
31
+ }
32
+ export function validateReaction(value) {
33
+ if (!allowedReactions.includes(value)) {
34
+ throw new OpenPetsClientError("invalid_reaction", "Invalid OpenPets reaction.");
35
+ }
36
+ return value;
37
+ }
38
+ export class OpenPetsClientError extends Error {
39
+ code;
40
+ constructor(code, message) {
41
+ super(message);
42
+ this.code = code;
43
+ }
44
+ }
45
+ export function isRecord(value) {
46
+ return typeof value === "object" && value !== null;
47
+ }
48
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAClD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AACpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC5C,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AACtC,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEvC,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,MAAM;IACN,UAAU;IACV,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,QAAQ;IACR,SAAS;IACT,OAAO;IACP,aAAa;CACL,CAAC;AA8BX,MAAM,UAAU,gBAAgB,CAAc,KAAc;IAC1D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,iCAAiC,CAAC,CAAC;IAC3G,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI;QAAE,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,6BAA6B,CAAC,CAAC;IAExI,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAW,EAAE,CAAC;IAC/D,CAAC;IAED,IAAI,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACnI,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IACtG,CAAC;IAED,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,gCAAgC,CAAC,CAAC;AACtF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAyB,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,KAAyB,CAAC;AACnC,CAAC;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACvB;IAArB,YAAqB,IAAY,EAAE,OAAe;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QADI,SAAI,GAAJ,IAAI,CAAQ;IAEjC,CAAC;CACF;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=smoke.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smoke.d.ts","sourceRoot":"","sources":["../src/smoke.ts"],"names":[],"mappings":""}
package/dist/smoke.js ADDED
@@ -0,0 +1,28 @@
1
+ import { createOpenPetsClient } from "./index.js";
2
+ import { validateReaction } from "./protocol.js";
3
+ const client = createOpenPetsClient();
4
+ const [command = "status", first, second] = process.argv.slice(2);
5
+ try {
6
+ const result = command === "hello"
7
+ ? await client.hello()
8
+ : command === "status"
9
+ ? await client.status()
10
+ : command === "react"
11
+ ? await client.react(validateReaction(first ?? "idle"))
12
+ : command === "say"
13
+ ? await client.say(first ?? "Working on it", second ? { reaction: validateReaction(second) } : undefined)
14
+ : command === "invalid-token"
15
+ ? await runInvalidTokenCheck()
16
+ : await client.status();
17
+ console.log(JSON.stringify(result, null, 2));
18
+ }
19
+ catch (error) {
20
+ console.error(error instanceof Error ? error.message : String(error));
21
+ process.exitCode = 1;
22
+ }
23
+ async function runInvalidTokenCheck() {
24
+ const { readDiscoveryFile, sendRequest } = await import("./index.js");
25
+ const discovery = readDiscoveryFile();
26
+ return sendRequest({ ...discovery, token: "invalid-token-value" }, "hello", {});
27
+ }
28
+ //# sourceMappingURL=smoke.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smoke.js","sourceRoot":"","sources":["../src/smoke.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,eAAe,CAAC;AAExE,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;AACtC,MAAM,CAAC,OAAO,GAAG,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAElE,IAAI,CAAC;IACH,MAAM,MAAM,GAAG,OAAO,KAAK,OAAO;QAChC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,EAAE;QACtB,CAAC,CAAC,OAAO,KAAK,QAAQ;YACpB,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE;YACvB,CAAC,CAAC,OAAO,KAAK,OAAO;gBACnB,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;gBACvD,CAAC,CAAC,OAAO,KAAK,KAAK;oBACjB,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,MAAM,CAAqB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;oBAC7H,CAAC,CAAC,OAAO,KAAK,eAAe;wBAC3B,CAAC,CAAC,MAAM,oBAAoB,EAAE;wBAC9B,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IAElC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,KAAK,UAAU,oBAAoB;IACjC,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;IACtC,OAAO,WAAW,CAAC,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;AAClF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@burmese/client",
3
+ "version": "3.1.0",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/wannabeepolymath/pet-assistant.git",
8
+ "directory": "packages/client"
9
+ },
10
+ "type": "module",
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ }
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^25.6.2",
27
+ "typescript": "^6.0.3"
28
+ },
29
+ "scripts": {
30
+ "test": "pnpm test:build && node .test-dist/contracts/client-protocol.contract.js",
31
+ "test:build": "tsc -p tsconfig.tests.json",
32
+ "check": "pnpm typecheck && pnpm build && pnpm test",
33
+ "typecheck": "tsc --noEmit",
34
+ "build": "tsc",
35
+ "smoke": "pnpm build && node dist/smoke.js",
36
+ "smoke:status": "pnpm smoke status",
37
+ "smoke:react": "pnpm smoke react",
38
+ "smoke:say": "pnpm smoke say",
39
+ "smoke:invalid-token": "pnpm smoke invalid-token"
40
+ }
41
+ }