@paneui/core 0.0.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/LICENSE +21 -0
- package/README.md +31 -0
- package/dist/client.d.ts +161 -0
- package/dist/client.js +281 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/limits.d.ts +8 -0
- package/dist/limits.js +10 -0
- package/dist/register.d.ts +25 -0
- package/dist/register.js +62 -0
- package/dist/schemas.d.ts +197 -0
- package/dist/schemas.js +82 -0
- package/dist/stream.d.ts +51 -0
- package/dist/stream.js +97 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.js +7 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lalit Singh
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @paneui/core
|
|
2
|
+
|
|
3
|
+
Typed client for the Pane relay HTTP + WebSocket API. Framework-free: no argv,
|
|
4
|
+
no MCP, no server dependencies — just the relay protocol expressed as typed
|
|
5
|
+
operations.
|
|
6
|
+
|
|
7
|
+
## Runtime requirement: Node.js >= 20
|
|
8
|
+
|
|
9
|
+
`@paneui/core` targets the **Node.js** runtime (>= 20, as declared in
|
|
10
|
+
`package.json`'s `engines`). It is *framework*-free, not *runtime*-free.
|
|
11
|
+
|
|
12
|
+
The WebSocket transport (`openStream`) uses the [`ws`](https://www.npmjs.com/package/ws)
|
|
13
|
+
package rather than the global `WebSocket`. `ws` exposes a Node-style event API
|
|
14
|
+
(`socket.on("message", ...)`, custom upgrade headers such as `Authorization`)
|
|
15
|
+
that the browser `WebSocket` does not, and the relay protocol relies on it.
|
|
16
|
+
Because of this, `@paneui/core` is **not** intended to run in a browser or other
|
|
17
|
+
non-Node runtime as-is.
|
|
18
|
+
|
|
19
|
+
The HTTP surface (`PaneClient`, `registerAgent`) uses the standard `fetch` API
|
|
20
|
+
and is runtime-agnostic; only `openStream` carries the Node constraint.
|
|
21
|
+
|
|
22
|
+
If you need a browser client, treat that as separate future work — it would
|
|
23
|
+
need a `ws`-vs-global-`WebSocket` abstraction rather than the unconditional
|
|
24
|
+
`import { WebSocket } from "ws"` used today.
|
|
25
|
+
|
|
26
|
+
## Exports
|
|
27
|
+
|
|
28
|
+
- `PaneClient` / `PaneApiError` — typed HTTP operations against a relay.
|
|
29
|
+
- `openStream` — WebSocket stream (replay-on-connect, then live). **Node only.**
|
|
30
|
+
- `registerAgent` — agent registration helper.
|
|
31
|
+
- `artifactSchema`, `callbackSchema`, `createSessionSchema` — Zod schemas.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { ArtifactRecord, ArtifactSummary, ArtifactType, ArtifactVersion, CreateArtifactResponse, CreateSessionRequest, CreateSessionResponse, EventsPage, KeyInfo, PaneEvent, SessionState } from "./types.js";
|
|
2
|
+
export interface ClientOptions {
|
|
3
|
+
/** Relay base URL, e.g. https://pane.example.com. Trailing slash is trimmed. */
|
|
4
|
+
url: string;
|
|
5
|
+
/** Agent API key (bearer token). */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** Optional fetch override (defaults to global fetch). */
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
}
|
|
10
|
+
/** Low-level relay response: ok flag, HTTP status, parsed JSON body. */
|
|
11
|
+
export interface RelayResponse {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
status: number;
|
|
14
|
+
data: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Request body for POST /v1/artifacts — create a named, reusable artifact plus
|
|
18
|
+
* its v1 content. Mirrors `createArtifactSchema` from ./schemas.js.
|
|
19
|
+
*/
|
|
20
|
+
export interface CreateArtifactRequest {
|
|
21
|
+
name: string;
|
|
22
|
+
slug?: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
tags?: string[];
|
|
25
|
+
source: string;
|
|
26
|
+
type: ArtifactType;
|
|
27
|
+
event_schema?: unknown;
|
|
28
|
+
input_schema?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Request body for POST /v1/artifacts/:id/versions — append a new immutable
|
|
32
|
+
* version (content only). Mirrors `createArtifactVersionSchema`.
|
|
33
|
+
*/
|
|
34
|
+
export interface CreateArtifactVersionRequest {
|
|
35
|
+
source: string;
|
|
36
|
+
type: ArtifactType;
|
|
37
|
+
event_schema?: unknown;
|
|
38
|
+
input_schema?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Request body for PATCH /v1/artifacts/:id — head metadata only (never
|
|
42
|
+
* content). Mirrors `patchArtifactMetadataSchema`.
|
|
43
|
+
*/
|
|
44
|
+
export interface PatchArtifactMetadataRequest {
|
|
45
|
+
name?: string;
|
|
46
|
+
slug?: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
tags?: string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* An error thrown by the typed operations when the relay returns a non-2xx
|
|
52
|
+
* response (or the request fails outright). Carries the HTTP status and the
|
|
53
|
+
* relay error envelope so callers can branch on `code`.
|
|
54
|
+
*/
|
|
55
|
+
export declare class PaneApiError extends Error {
|
|
56
|
+
readonly status: number;
|
|
57
|
+
readonly code: string;
|
|
58
|
+
readonly details: unknown;
|
|
59
|
+
/** Agent-friendly remediation hint, when the relay supplies one. */
|
|
60
|
+
readonly hint?: string;
|
|
61
|
+
/** Whether retrying the same request may succeed (e.g. 429). */
|
|
62
|
+
readonly retryable?: boolean;
|
|
63
|
+
/** Documentation URL for this error class (mapped from the wire's `docs_url`). */
|
|
64
|
+
readonly docsUrl?: string;
|
|
65
|
+
constructor(status: number, code: string, message: string, details?: unknown, extra?: {
|
|
66
|
+
hint?: string;
|
|
67
|
+
retryable?: boolean;
|
|
68
|
+
docsUrl?: string;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export declare class PaneClient {
|
|
72
|
+
private readonly base;
|
|
73
|
+
private readonly apiKey;
|
|
74
|
+
private readonly fetchImpl;
|
|
75
|
+
constructor(opts: ClientOptions);
|
|
76
|
+
/** Relay base URL (trailing slash trimmed). */
|
|
77
|
+
get baseUrl(): string;
|
|
78
|
+
/** WebSocket base URL derived from the relay base URL (http→ws, https→wss). */
|
|
79
|
+
get wsBaseUrl(): string;
|
|
80
|
+
/**
|
|
81
|
+
* Low-level HTTP helper. Mirrors the relay API contract: Bearer auth,
|
|
82
|
+
* JSON bodies, 204 handled. Never throws on non-2xx — returns `ok: false`.
|
|
83
|
+
* Network failures return `{ ok: false, status: 0, ... }`.
|
|
84
|
+
*/
|
|
85
|
+
call(method: string, path: string, body?: object): Promise<RelayResponse>;
|
|
86
|
+
/** Assert a 2xx body is a non-null object before treating it as typed JSON. */
|
|
87
|
+
private asObject;
|
|
88
|
+
/** Throw a PaneApiError from a failed RelayResponse. */
|
|
89
|
+
private fail;
|
|
90
|
+
/** POST /v1/sessions — create a session. */
|
|
91
|
+
createSession(req: CreateSessionRequest): Promise<CreateSessionResponse>;
|
|
92
|
+
/** GET /v1/sessions/:id — non-blocking session metadata. */
|
|
93
|
+
getSession(sessionId: string): Promise<SessionState>;
|
|
94
|
+
/**
|
|
95
|
+
* GET /v1/sessions/:id/events — fetch the event log.
|
|
96
|
+
* `since` is an opaque cursor; `waitSeconds` enables the relay long-poll
|
|
97
|
+
* (0 = non-blocking, capped at 30 by the relay).
|
|
98
|
+
*/
|
|
99
|
+
getEvents(sessionId: string, opts?: {
|
|
100
|
+
since?: string | null;
|
|
101
|
+
waitSeconds?: number;
|
|
102
|
+
}): Promise<EventsPage>;
|
|
103
|
+
/** POST /v1/sessions/:id/events — append an agent event. */
|
|
104
|
+
sendEvent(sessionId: string, ev: {
|
|
105
|
+
type: string;
|
|
106
|
+
data: unknown;
|
|
107
|
+
causationId?: string;
|
|
108
|
+
idempotencyKey?: string;
|
|
109
|
+
}): Promise<{
|
|
110
|
+
event: PaneEvent;
|
|
111
|
+
deduped: boolean;
|
|
112
|
+
}>;
|
|
113
|
+
/**
|
|
114
|
+
* POST /v1/artifacts — create a named, reusable artifact and its v1 content.
|
|
115
|
+
* Returns the new `artifact_id` and `version` (1).
|
|
116
|
+
*/
|
|
117
|
+
createArtifact(req: CreateArtifactRequest): Promise<CreateArtifactResponse>;
|
|
118
|
+
/**
|
|
119
|
+
* POST /v1/artifacts/:id/versions — append a new immutable version to an
|
|
120
|
+
* existing artifact. `idOrSlug` accepts the artifact id or its slug.
|
|
121
|
+
* Returns the new `version` number.
|
|
122
|
+
*/
|
|
123
|
+
createArtifactVersion(idOrSlug: string, req: CreateArtifactVersionRequest): Promise<CreateArtifactResponse>;
|
|
124
|
+
/**
|
|
125
|
+
* PATCH /v1/artifacts/:id — update head metadata (name / slug / description /
|
|
126
|
+
* tags); never the content. Returns the updated lean summary.
|
|
127
|
+
*/
|
|
128
|
+
updateArtifact(idOrSlug: string, metadata: PatchArtifactMetadataRequest): Promise<ArtifactSummary>;
|
|
129
|
+
/**
|
|
130
|
+
* GET /v1/artifacts?q=... — search/list the agent's named artifacts. The
|
|
131
|
+
* response is lean (no `source` blob), ranked by `last_used_at`. Omit `query`
|
|
132
|
+
* to list every named artifact.
|
|
133
|
+
*/
|
|
134
|
+
searchArtifacts(query?: string): Promise<ArtifactSummary[]>;
|
|
135
|
+
/**
|
|
136
|
+
* GET /v1/artifacts/:id — fetch a full artifact (head metadata + version
|
|
137
|
+
* list). `idOrSlug` accepts the artifact id or its slug.
|
|
138
|
+
*/
|
|
139
|
+
getArtifact(idOrSlug: string): Promise<ArtifactRecord>;
|
|
140
|
+
/**
|
|
141
|
+
* GET /v1/artifacts/:id/versions/:version — fetch one version's full
|
|
142
|
+
* content (HTML, event schema, input schema).
|
|
143
|
+
*/
|
|
144
|
+
getArtifactVersion(idOrSlug: string, version: number): Promise<ArtifactVersion>;
|
|
145
|
+
/**
|
|
146
|
+
* GET /v1/keys — the calling agent's own key info. The relay scopes this to
|
|
147
|
+
* the authenticated agent: it returns one key (the caller's), not a list.
|
|
148
|
+
*/
|
|
149
|
+
listKeys(): Promise<KeyInfo>;
|
|
150
|
+
/**
|
|
151
|
+
* DELETE /v1/keys/:id — revoke an API key. The relay only permits revoking
|
|
152
|
+
* the caller's OWN key (any other id is rejected 403): this is a
|
|
153
|
+
* self-destruct. Returns 204 with no body on success.
|
|
154
|
+
*/
|
|
155
|
+
revokeKey(id: string): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* DELETE /v1/sessions/:id — close/delete a session. Idempotent on the relay
|
|
158
|
+
* side (an already-closed session still returns 204 with no body).
|
|
159
|
+
*/
|
|
160
|
+
deleteSession(id: string): Promise<void>;
|
|
161
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// Pane relay HTTP client. Pure: no argv, no process.env reads, no MCP.
|
|
2
|
+
// The caller supplies the relay base URL + API key explicitly.
|
|
3
|
+
import { MAX_RESPONSE_SNIPPET_LENGTH } from "./limits.js";
|
|
4
|
+
/**
|
|
5
|
+
* An error thrown by the typed operations when the relay returns a non-2xx
|
|
6
|
+
* response (or the request fails outright). Carries the HTTP status and the
|
|
7
|
+
* relay error envelope so callers can branch on `code`.
|
|
8
|
+
*/
|
|
9
|
+
export class PaneApiError extends Error {
|
|
10
|
+
status;
|
|
11
|
+
code;
|
|
12
|
+
details;
|
|
13
|
+
/** Agent-friendly remediation hint, when the relay supplies one. */
|
|
14
|
+
hint;
|
|
15
|
+
/** Whether retrying the same request may succeed (e.g. 429). */
|
|
16
|
+
retryable;
|
|
17
|
+
/** Documentation URL for this error class (mapped from the wire's `docs_url`). */
|
|
18
|
+
docsUrl;
|
|
19
|
+
constructor(status, code, message, details, extra) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "PaneApiError";
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.details = details;
|
|
25
|
+
this.hint = extra?.hint;
|
|
26
|
+
this.retryable = extra?.retryable;
|
|
27
|
+
this.docsUrl = extra?.docsUrl;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class PaneClient {
|
|
31
|
+
base;
|
|
32
|
+
apiKey;
|
|
33
|
+
fetchImpl;
|
|
34
|
+
constructor(opts) {
|
|
35
|
+
this.base = opts.url.replace(/\/$/, "");
|
|
36
|
+
this.apiKey = opts.apiKey;
|
|
37
|
+
this.fetchImpl = opts.fetch ?? fetch;
|
|
38
|
+
}
|
|
39
|
+
/** Relay base URL (trailing slash trimmed). */
|
|
40
|
+
get baseUrl() {
|
|
41
|
+
return this.base;
|
|
42
|
+
}
|
|
43
|
+
/** WebSocket base URL derived from the relay base URL (http→ws, https→wss). */
|
|
44
|
+
get wsBaseUrl() {
|
|
45
|
+
const u = new URL(this.base);
|
|
46
|
+
u.protocol = u.protocol === "https:" ? "wss:" : "ws:";
|
|
47
|
+
return u.toString().replace(/\/$/, "");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Low-level HTTP helper. Mirrors the relay API contract: Bearer auth,
|
|
51
|
+
* JSON bodies, 204 handled. Never throws on non-2xx — returns `ok: false`.
|
|
52
|
+
* Network failures return `{ ok: false, status: 0, ... }`.
|
|
53
|
+
*/
|
|
54
|
+
async call(method, path, body) {
|
|
55
|
+
const url = this.base + path;
|
|
56
|
+
let res;
|
|
57
|
+
try {
|
|
58
|
+
res = await this.fetchImpl(url, {
|
|
59
|
+
method,
|
|
60
|
+
headers: {
|
|
61
|
+
authorization: "Bearer " + this.apiKey,
|
|
62
|
+
...(body ? { "content-type": "application/json" } : {}),
|
|
63
|
+
},
|
|
64
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
status: 0,
|
|
72
|
+
data: { error: { code: "fetch_error", message: msg } },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
let data = null;
|
|
76
|
+
if (res.status !== 204) {
|
|
77
|
+
const text = await res.text().catch(() => "");
|
|
78
|
+
if (text !== "") {
|
|
79
|
+
try {
|
|
80
|
+
data = JSON.parse(text);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Body was not JSON (HTML error page, plain-text proxy error, …).
|
|
84
|
+
// Don't discard it — surface the raw text so callers can diagnose.
|
|
85
|
+
const snippet = text.length > MAX_RESPONSE_SNIPPET_LENGTH
|
|
86
|
+
? text.slice(0, MAX_RESPONSE_SNIPPET_LENGTH) + "…"
|
|
87
|
+
: text;
|
|
88
|
+
data = {
|
|
89
|
+
error: {
|
|
90
|
+
code: "non_json_response",
|
|
91
|
+
message: `relay returned a non-JSON body (status ${res.status})`,
|
|
92
|
+
details: { body: snippet },
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { ok: res.ok, status: res.status, data };
|
|
99
|
+
}
|
|
100
|
+
/** Assert a 2xx body is a non-null object before treating it as typed JSON. */
|
|
101
|
+
asObject(r) {
|
|
102
|
+
if (r.data === null ||
|
|
103
|
+
typeof r.data !== "object" ||
|
|
104
|
+
Array.isArray(r.data)) {
|
|
105
|
+
throw new PaneApiError(r.status, "invalid_response", `relay returned a ${r.status} with a non-object body`, { body: r.data });
|
|
106
|
+
}
|
|
107
|
+
return r.data;
|
|
108
|
+
}
|
|
109
|
+
/** Throw a PaneApiError from a failed RelayResponse. */
|
|
110
|
+
fail(r) {
|
|
111
|
+
const err = r.data?.error;
|
|
112
|
+
throw new PaneApiError(r.status, err?.code ?? "relay_error", err?.message ?? `relay returned ${r.status}`, err?.details, {
|
|
113
|
+
hint: err?.hint,
|
|
114
|
+
retryable: err?.retryable,
|
|
115
|
+
docsUrl: err?.docs_url,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** POST /v1/sessions — create a session. */
|
|
119
|
+
async createSession(req) {
|
|
120
|
+
const r = await this.call("POST", "/v1/sessions", {
|
|
121
|
+
artifact: req.artifact,
|
|
122
|
+
input_data: req.input_data,
|
|
123
|
+
participants: req.participants,
|
|
124
|
+
ttl: req.ttl,
|
|
125
|
+
metadata: req.metadata,
|
|
126
|
+
callback: req.callback,
|
|
127
|
+
});
|
|
128
|
+
if (!r.ok)
|
|
129
|
+
this.fail(r);
|
|
130
|
+
return this.asObject(r);
|
|
131
|
+
}
|
|
132
|
+
/** GET /v1/sessions/:id — non-blocking session metadata. */
|
|
133
|
+
async getSession(sessionId) {
|
|
134
|
+
const r = await this.call("GET", `/v1/sessions/${encodeURIComponent(sessionId)}`);
|
|
135
|
+
if (!r.ok)
|
|
136
|
+
this.fail(r);
|
|
137
|
+
return this.asObject(r);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* GET /v1/sessions/:id/events — fetch the event log.
|
|
141
|
+
* `since` is an opaque cursor; `waitSeconds` enables the relay long-poll
|
|
142
|
+
* (0 = non-blocking, capped at 30 by the relay).
|
|
143
|
+
*/
|
|
144
|
+
async getEvents(sessionId, opts = {}) {
|
|
145
|
+
const q = new URLSearchParams();
|
|
146
|
+
if (opts.since != null && opts.since !== "")
|
|
147
|
+
q.set("since", opts.since);
|
|
148
|
+
if (opts.waitSeconds != null && opts.waitSeconds > 0) {
|
|
149
|
+
q.set("wait", String(Math.floor(opts.waitSeconds)));
|
|
150
|
+
}
|
|
151
|
+
const qs = q.toString();
|
|
152
|
+
const r = await this.call("GET", `/v1/sessions/${encodeURIComponent(sessionId)}/events${qs ? "?" + qs : ""}`);
|
|
153
|
+
if (!r.ok)
|
|
154
|
+
this.fail(r);
|
|
155
|
+
return this.asObject(r);
|
|
156
|
+
}
|
|
157
|
+
/** POST /v1/sessions/:id/events — append an agent event. */
|
|
158
|
+
async sendEvent(sessionId, ev) {
|
|
159
|
+
const r = await this.call("POST", `/v1/sessions/${encodeURIComponent(sessionId)}/events`, {
|
|
160
|
+
type: ev.type,
|
|
161
|
+
data: ev.data,
|
|
162
|
+
causation_id: ev.causationId,
|
|
163
|
+
idempotency_key: ev.idempotencyKey,
|
|
164
|
+
});
|
|
165
|
+
if (!r.ok)
|
|
166
|
+
this.fail(r);
|
|
167
|
+
const body = this.asObject(r);
|
|
168
|
+
return { event: body.event, deduped: body.deduped ?? false };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* POST /v1/artifacts — create a named, reusable artifact and its v1 content.
|
|
172
|
+
* Returns the new `artifact_id` and `version` (1).
|
|
173
|
+
*/
|
|
174
|
+
async createArtifact(req) {
|
|
175
|
+
const r = await this.call("POST", "/v1/artifacts", {
|
|
176
|
+
name: req.name,
|
|
177
|
+
slug: req.slug,
|
|
178
|
+
description: req.description,
|
|
179
|
+
tags: req.tags,
|
|
180
|
+
source: req.source,
|
|
181
|
+
type: req.type,
|
|
182
|
+
event_schema: req.event_schema,
|
|
183
|
+
input_schema: req.input_schema,
|
|
184
|
+
});
|
|
185
|
+
if (!r.ok)
|
|
186
|
+
this.fail(r);
|
|
187
|
+
return this.asObject(r);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* POST /v1/artifacts/:id/versions — append a new immutable version to an
|
|
191
|
+
* existing artifact. `idOrSlug` accepts the artifact id or its slug.
|
|
192
|
+
* Returns the new `version` number.
|
|
193
|
+
*/
|
|
194
|
+
async createArtifactVersion(idOrSlug, req) {
|
|
195
|
+
const r = await this.call("POST", `/v1/artifacts/${encodeURIComponent(idOrSlug)}/versions`, {
|
|
196
|
+
source: req.source,
|
|
197
|
+
type: req.type,
|
|
198
|
+
event_schema: req.event_schema,
|
|
199
|
+
input_schema: req.input_schema,
|
|
200
|
+
});
|
|
201
|
+
if (!r.ok)
|
|
202
|
+
this.fail(r);
|
|
203
|
+
return this.asObject(r);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* PATCH /v1/artifacts/:id — update head metadata (name / slug / description /
|
|
207
|
+
* tags); never the content. Returns the updated lean summary.
|
|
208
|
+
*/
|
|
209
|
+
async updateArtifact(idOrSlug, metadata) {
|
|
210
|
+
const r = await this.call("PATCH", `/v1/artifacts/${encodeURIComponent(idOrSlug)}`, {
|
|
211
|
+
name: metadata.name,
|
|
212
|
+
slug: metadata.slug,
|
|
213
|
+
description: metadata.description,
|
|
214
|
+
tags: metadata.tags,
|
|
215
|
+
});
|
|
216
|
+
if (!r.ok)
|
|
217
|
+
this.fail(r);
|
|
218
|
+
return this.asObject(r);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* GET /v1/artifacts?q=... — search/list the agent's named artifacts. The
|
|
222
|
+
* response is lean (no `source` blob), ranked by `last_used_at`. Omit `query`
|
|
223
|
+
* to list every named artifact.
|
|
224
|
+
*/
|
|
225
|
+
async searchArtifacts(query) {
|
|
226
|
+
const qs = query != null && query !== "" ? "?q=" + encodeURIComponent(query) : "";
|
|
227
|
+
const r = await this.call("GET", `/v1/artifacts${qs}`);
|
|
228
|
+
if (!r.ok)
|
|
229
|
+
this.fail(r);
|
|
230
|
+
return this.asObject(r).artifacts;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* GET /v1/artifacts/:id — fetch a full artifact (head metadata + version
|
|
234
|
+
* list). `idOrSlug` accepts the artifact id or its slug.
|
|
235
|
+
*/
|
|
236
|
+
async getArtifact(idOrSlug) {
|
|
237
|
+
const r = await this.call("GET", `/v1/artifacts/${encodeURIComponent(idOrSlug)}`);
|
|
238
|
+
if (!r.ok)
|
|
239
|
+
this.fail(r);
|
|
240
|
+
return this.asObject(r);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* GET /v1/artifacts/:id/versions/:version — fetch one version's full
|
|
244
|
+
* content (HTML, event schema, input schema).
|
|
245
|
+
*/
|
|
246
|
+
async getArtifactVersion(idOrSlug, version) {
|
|
247
|
+
const r = await this.call("GET", `/v1/artifacts/${encodeURIComponent(idOrSlug)}/versions/${encodeURIComponent(String(version))}`);
|
|
248
|
+
if (!r.ok)
|
|
249
|
+
this.fail(r);
|
|
250
|
+
return this.asObject(r);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* GET /v1/keys — the calling agent's own key info. The relay scopes this to
|
|
254
|
+
* the authenticated agent: it returns one key (the caller's), not a list.
|
|
255
|
+
*/
|
|
256
|
+
async listKeys() {
|
|
257
|
+
const r = await this.call("GET", "/v1/keys");
|
|
258
|
+
if (!r.ok)
|
|
259
|
+
this.fail(r);
|
|
260
|
+
return this.asObject(r);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* DELETE /v1/keys/:id — revoke an API key. The relay only permits revoking
|
|
264
|
+
* the caller's OWN key (any other id is rejected 403): this is a
|
|
265
|
+
* self-destruct. Returns 204 with no body on success.
|
|
266
|
+
*/
|
|
267
|
+
async revokeKey(id) {
|
|
268
|
+
const r = await this.call("DELETE", `/v1/keys/${encodeURIComponent(id)}`);
|
|
269
|
+
if (!r.ok)
|
|
270
|
+
this.fail(r);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* DELETE /v1/sessions/:id — close/delete a session. Idempotent on the relay
|
|
274
|
+
* side (an already-closed session still returns 204 with no body).
|
|
275
|
+
*/
|
|
276
|
+
async deleteSession(id) {
|
|
277
|
+
const r = await this.call("DELETE", `/v1/sessions/${encodeURIComponent(id)}`);
|
|
278
|
+
if (!r.ok)
|
|
279
|
+
this.fail(r);
|
|
280
|
+
}
|
|
281
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { PaneClient, PaneApiError } from "./client.js";
|
|
2
|
+
export type { ClientOptions, RelayResponse, CreateArtifactRequest, CreateArtifactVersionRequest, PatchArtifactMetadataRequest, } from "./client.js";
|
|
3
|
+
export { openStream } from "./stream.js";
|
|
4
|
+
export type { OpenStreamOptions, StreamHandlers, StreamHandle, } from "./stream.js";
|
|
5
|
+
export { registerAgent } from "./register.js";
|
|
6
|
+
export type { RegisterAgentOptions, RegisterAgentResult } from "./register.js";
|
|
7
|
+
export { artifactSchema, callbackSchema, createSessionSchema, artifactTypeSchema, createArtifactSchema, createArtifactVersionSchema, patchArtifactMetadataSchema, } from "./schemas.js";
|
|
8
|
+
export type { CreateSessionInput } from "./schemas.js";
|
|
9
|
+
export { MAX_EVENT_TYPE_LENGTH, MAX_IDEMPOTENCY_KEY_LENGTH, MAX_RESPONSE_SNIPPET_LENGTH, MAX_FRAME_SNIPPET_LENGTH, } from "./limits.js";
|
|
10
|
+
export type { AuthorKind, PaneEvent, Artifact, ArtifactType, ArtifactVersion, ArtifactRecord, ArtifactSummary, CreateArtifactResponse, KeyInfo, Callback, CreateSessionRequest, CreateSessionResponse, SessionState, EventsPage, RelayError, } from "./types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// @paneui/core — typed client for the Pane relay HTTP + WebSocket API.
|
|
2
|
+
// Pure and framework-free: no argv, no MCP, no server deps.
|
|
3
|
+
export { PaneClient, PaneApiError } from "./client.js";
|
|
4
|
+
export { openStream } from "./stream.js";
|
|
5
|
+
export { registerAgent } from "./register.js";
|
|
6
|
+
export { artifactSchema, callbackSchema, createSessionSchema, artifactTypeSchema, createArtifactSchema, createArtifactVersionSchema, patchArtifactMetadataSchema, } from "./schemas.js";
|
|
7
|
+
export { MAX_EVENT_TYPE_LENGTH, MAX_IDEMPOTENCY_KEY_LENGTH, MAX_RESPONSE_SNIPPET_LENGTH, MAX_FRAME_SNIPPET_LENGTH, } from "./limits.js";
|
package/dist/limits.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Maximum length of an event type string, in characters. */
|
|
2
|
+
export declare const MAX_EVENT_TYPE_LENGTH = 64;
|
|
3
|
+
/** Maximum length of an idempotency key string, in characters. */
|
|
4
|
+
export declare const MAX_IDEMPOTENCY_KEY_LENGTH = 128;
|
|
5
|
+
/** Maximum number of characters from a raw response body to include in error details. */
|
|
6
|
+
export declare const MAX_RESPONSE_SNIPPET_LENGTH = 500;
|
|
7
|
+
/** Maximum number of characters from a raw stream frame to include in error messages. */
|
|
8
|
+
export declare const MAX_FRAME_SNIPPET_LENGTH = 200;
|
package/dist/limits.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Shared protocol limits used across transports (HTTP and WebSocket).
|
|
2
|
+
// Defined once here so both relay and client code import the same constants.
|
|
3
|
+
/** Maximum length of an event type string, in characters. */
|
|
4
|
+
export const MAX_EVENT_TYPE_LENGTH = 64;
|
|
5
|
+
/** Maximum length of an idempotency key string, in characters. */
|
|
6
|
+
export const MAX_IDEMPOTENCY_KEY_LENGTH = 128;
|
|
7
|
+
/** Maximum number of characters from a raw response body to include in error details. */
|
|
8
|
+
export const MAX_RESPONSE_SNIPPET_LENGTH = 500;
|
|
9
|
+
/** Maximum number of characters from a raw stream frame to include in error messages. */
|
|
10
|
+
export const MAX_FRAME_SNIPPET_LENGTH = 200;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface RegisterAgentOptions {
|
|
2
|
+
/** Relay base URL, e.g. https://pane.example.com. Trailing slash is trimmed. */
|
|
3
|
+
url: string;
|
|
4
|
+
/** Optional agent display name; the relay defaults it if omitted. */
|
|
5
|
+
name?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Shared registration secret. Sent as `Authorization: Bearer <secret>`.
|
|
8
|
+
* Only needed when the relay runs REGISTRATION_MODE=secret; ignored by
|
|
9
|
+
* relays in open mode and rejected (404) by relays in closed mode.
|
|
10
|
+
*/
|
|
11
|
+
secret?: string;
|
|
12
|
+
/** Optional fetch override (defaults to global fetch). */
|
|
13
|
+
fetch?: typeof fetch;
|
|
14
|
+
}
|
|
15
|
+
export interface RegisterAgentResult {
|
|
16
|
+
agent_id: string;
|
|
17
|
+
api_key: string;
|
|
18
|
+
key_prefix: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Provision a fresh agent + API key from the relay. Mirrors PaneClient.call's
|
|
22
|
+
* never-throw-raw style: network/parse failures and non-2xx responses are
|
|
23
|
+
* surfaced as PaneApiError.
|
|
24
|
+
*/
|
|
25
|
+
export declare function registerAgent(opts: RegisterAgentOptions): Promise<RegisterAgentResult>;
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Standalone agent self-registration: POST /v1/register.
|
|
2
|
+
//
|
|
3
|
+
// Unlike PaneClient operations this needs no bearer API key — registration is
|
|
4
|
+
// the call that *obtains* one. Whether the relay endpoint is reachable depends
|
|
5
|
+
// on its REGISTRATION_MODE: a `secret`-mode relay requires the shared
|
|
6
|
+
// registration secret to be passed as a Bearer token (see the `secret` option
|
|
7
|
+
// below). Abuse is bounded server-side by a per-IP rate limit (a 429 surfaces
|
|
8
|
+
// here as a PaneApiError with status 429).
|
|
9
|
+
import { PaneApiError } from "./client.js";
|
|
10
|
+
import { MAX_RESPONSE_SNIPPET_LENGTH } from "./limits.js";
|
|
11
|
+
/**
|
|
12
|
+
* Provision a fresh agent + API key from the relay. Mirrors PaneClient.call's
|
|
13
|
+
* never-throw-raw style: network/parse failures and non-2xx responses are
|
|
14
|
+
* surfaced as PaneApiError.
|
|
15
|
+
*/
|
|
16
|
+
export async function registerAgent(opts) {
|
|
17
|
+
const base = opts.url.replace(/\/$/, "");
|
|
18
|
+
const fetchImpl = opts.fetch ?? fetch;
|
|
19
|
+
const body = {};
|
|
20
|
+
if (opts.name !== undefined)
|
|
21
|
+
body["name"] = opts.name;
|
|
22
|
+
const headers = {
|
|
23
|
+
"content-type": "application/json",
|
|
24
|
+
};
|
|
25
|
+
// Sent only when the relay runs REGISTRATION_MODE=secret; harmless otherwise.
|
|
26
|
+
if (opts.secret !== undefined && opts.secret !== "") {
|
|
27
|
+
headers["authorization"] = `Bearer ${opts.secret}`;
|
|
28
|
+
}
|
|
29
|
+
let res;
|
|
30
|
+
try {
|
|
31
|
+
res = await fetchImpl(base + "/v1/register", {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers,
|
|
34
|
+
body: JSON.stringify(body),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
39
|
+
throw new PaneApiError(0, "fetch_error", msg);
|
|
40
|
+
}
|
|
41
|
+
let data = null;
|
|
42
|
+
const text = await res.text().catch(() => "");
|
|
43
|
+
if (text !== "") {
|
|
44
|
+
try {
|
|
45
|
+
data = JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
const snippet = text.length > MAX_RESPONSE_SNIPPET_LENGTH
|
|
49
|
+
? text.slice(0, MAX_RESPONSE_SNIPPET_LENGTH) + "…"
|
|
50
|
+
: text;
|
|
51
|
+
throw new PaneApiError(res.status, "non_json_response", `relay returned a non-JSON body (status ${res.status})`, { body: snippet });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const err = data?.error;
|
|
56
|
+
throw new PaneApiError(res.status, err?.code ?? "relay_error", err?.message ?? `relay returned ${res.status}`, err?.details);
|
|
57
|
+
}
|
|
58
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
59
|
+
throw new PaneApiError(res.status, "invalid_response", `relay returned a ${res.status} with a non-object body`, { body: data });
|
|
60
|
+
}
|
|
61
|
+
return data;
|
|
62
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const artifactTypeSchema: z.ZodEnum<["html-inline", "html-ref"]>;
|
|
3
|
+
export declare const artifactSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
4
|
+
type: z.ZodLiteral<"html-inline">;
|
|
5
|
+
source: z.ZodString;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
type: "html-inline";
|
|
8
|
+
source: string;
|
|
9
|
+
}, {
|
|
10
|
+
type: "html-inline";
|
|
11
|
+
source: string;
|
|
12
|
+
}>, z.ZodObject<{
|
|
13
|
+
type: z.ZodLiteral<"html-ref">;
|
|
14
|
+
source: z.ZodString;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
type: "html-ref";
|
|
17
|
+
source: string;
|
|
18
|
+
}, {
|
|
19
|
+
type: "html-ref";
|
|
20
|
+
source: string;
|
|
21
|
+
}>]>;
|
|
22
|
+
export declare const callbackSchema: z.ZodObject<{
|
|
23
|
+
url: z.ZodString;
|
|
24
|
+
events: z.ZodArray<z.ZodString, "many">;
|
|
25
|
+
secret: z.ZodString;
|
|
26
|
+
}, "strip", z.ZodTypeAny, {
|
|
27
|
+
url: string;
|
|
28
|
+
events: string[];
|
|
29
|
+
secret: string;
|
|
30
|
+
}, {
|
|
31
|
+
url: string;
|
|
32
|
+
events: string[];
|
|
33
|
+
secret: string;
|
|
34
|
+
}>;
|
|
35
|
+
export declare const createSessionSchema: z.ZodObject<{
|
|
36
|
+
artifact: z.ZodEffects<z.ZodUnion<[z.ZodObject<{
|
|
37
|
+
id: z.ZodString;
|
|
38
|
+
version: z.ZodOptional<z.ZodNumber>;
|
|
39
|
+
}, "strip", z.ZodTypeAny, {
|
|
40
|
+
id: string;
|
|
41
|
+
version?: number | undefined;
|
|
42
|
+
}, {
|
|
43
|
+
id: string;
|
|
44
|
+
version?: number | undefined;
|
|
45
|
+
}>, z.ZodObject<{
|
|
46
|
+
source: z.ZodString;
|
|
47
|
+
type: z.ZodEnum<["html-inline", "html-ref"]>;
|
|
48
|
+
event_schema: z.ZodOptional<z.ZodUnknown>;
|
|
49
|
+
}, "strip", z.ZodTypeAny, {
|
|
50
|
+
type: "html-inline" | "html-ref";
|
|
51
|
+
source: string;
|
|
52
|
+
event_schema?: unknown;
|
|
53
|
+
}, {
|
|
54
|
+
type: "html-inline" | "html-ref";
|
|
55
|
+
source: string;
|
|
56
|
+
event_schema?: unknown;
|
|
57
|
+
}>]>, {
|
|
58
|
+
type: "html-inline" | "html-ref";
|
|
59
|
+
source: string;
|
|
60
|
+
event_schema?: unknown;
|
|
61
|
+
} | {
|
|
62
|
+
id: string;
|
|
63
|
+
version?: number | undefined;
|
|
64
|
+
}, {
|
|
65
|
+
type: "html-inline" | "html-ref";
|
|
66
|
+
source: string;
|
|
67
|
+
event_schema?: unknown;
|
|
68
|
+
} | {
|
|
69
|
+
id: string;
|
|
70
|
+
version?: number | undefined;
|
|
71
|
+
}>;
|
|
72
|
+
input_data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
73
|
+
participants: z.ZodOptional<z.ZodObject<{
|
|
74
|
+
humans: z.ZodNumber;
|
|
75
|
+
}, "strip", z.ZodTypeAny, {
|
|
76
|
+
humans: number;
|
|
77
|
+
}, {
|
|
78
|
+
humans: number;
|
|
79
|
+
}>>;
|
|
80
|
+
ttl: z.ZodOptional<z.ZodNumber>;
|
|
81
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
82
|
+
callback: z.ZodOptional<z.ZodObject<{
|
|
83
|
+
url: z.ZodString;
|
|
84
|
+
events: z.ZodArray<z.ZodString, "many">;
|
|
85
|
+
secret: z.ZodString;
|
|
86
|
+
}, "strip", z.ZodTypeAny, {
|
|
87
|
+
url: string;
|
|
88
|
+
events: string[];
|
|
89
|
+
secret: string;
|
|
90
|
+
}, {
|
|
91
|
+
url: string;
|
|
92
|
+
events: string[];
|
|
93
|
+
secret: string;
|
|
94
|
+
}>>;
|
|
95
|
+
}, "strip", z.ZodTypeAny, {
|
|
96
|
+
artifact: {
|
|
97
|
+
type: "html-inline" | "html-ref";
|
|
98
|
+
source: string;
|
|
99
|
+
event_schema?: unknown;
|
|
100
|
+
} | {
|
|
101
|
+
id: string;
|
|
102
|
+
version?: number | undefined;
|
|
103
|
+
};
|
|
104
|
+
input_data?: Record<string, unknown> | undefined;
|
|
105
|
+
participants?: {
|
|
106
|
+
humans: number;
|
|
107
|
+
} | undefined;
|
|
108
|
+
ttl?: number | undefined;
|
|
109
|
+
metadata?: Record<string, unknown> | undefined;
|
|
110
|
+
callback?: {
|
|
111
|
+
url: string;
|
|
112
|
+
events: string[];
|
|
113
|
+
secret: string;
|
|
114
|
+
} | undefined;
|
|
115
|
+
}, {
|
|
116
|
+
artifact: {
|
|
117
|
+
type: "html-inline" | "html-ref";
|
|
118
|
+
source: string;
|
|
119
|
+
event_schema?: unknown;
|
|
120
|
+
} | {
|
|
121
|
+
id: string;
|
|
122
|
+
version?: number | undefined;
|
|
123
|
+
};
|
|
124
|
+
input_data?: Record<string, unknown> | undefined;
|
|
125
|
+
participants?: {
|
|
126
|
+
humans: number;
|
|
127
|
+
} | undefined;
|
|
128
|
+
ttl?: number | undefined;
|
|
129
|
+
metadata?: Record<string, unknown> | undefined;
|
|
130
|
+
callback?: {
|
|
131
|
+
url: string;
|
|
132
|
+
events: string[];
|
|
133
|
+
secret: string;
|
|
134
|
+
} | undefined;
|
|
135
|
+
}>;
|
|
136
|
+
export declare const createArtifactSchema: z.ZodObject<{
|
|
137
|
+
name: z.ZodString;
|
|
138
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
139
|
+
description: z.ZodOptional<z.ZodString>;
|
|
140
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
141
|
+
source: z.ZodString;
|
|
142
|
+
type: z.ZodEnum<["html-inline", "html-ref"]>;
|
|
143
|
+
event_schema: z.ZodOptional<z.ZodUnknown>;
|
|
144
|
+
input_schema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
145
|
+
}, "strip", z.ZodTypeAny, {
|
|
146
|
+
type: "html-inline" | "html-ref";
|
|
147
|
+
source: string;
|
|
148
|
+
name: string;
|
|
149
|
+
event_schema?: unknown;
|
|
150
|
+
slug?: string | undefined;
|
|
151
|
+
description?: string | undefined;
|
|
152
|
+
tags?: string[] | undefined;
|
|
153
|
+
input_schema?: Record<string, unknown> | undefined;
|
|
154
|
+
}, {
|
|
155
|
+
type: "html-inline" | "html-ref";
|
|
156
|
+
source: string;
|
|
157
|
+
name: string;
|
|
158
|
+
event_schema?: unknown;
|
|
159
|
+
slug?: string | undefined;
|
|
160
|
+
description?: string | undefined;
|
|
161
|
+
tags?: string[] | undefined;
|
|
162
|
+
input_schema?: Record<string, unknown> | undefined;
|
|
163
|
+
}>;
|
|
164
|
+
export declare const createArtifactVersionSchema: z.ZodObject<{
|
|
165
|
+
source: z.ZodString;
|
|
166
|
+
type: z.ZodEnum<["html-inline", "html-ref"]>;
|
|
167
|
+
event_schema: z.ZodOptional<z.ZodUnknown>;
|
|
168
|
+
input_schema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
169
|
+
}, "strip", z.ZodTypeAny, {
|
|
170
|
+
type: "html-inline" | "html-ref";
|
|
171
|
+
source: string;
|
|
172
|
+
event_schema?: unknown;
|
|
173
|
+
input_schema?: Record<string, unknown> | undefined;
|
|
174
|
+
}, {
|
|
175
|
+
type: "html-inline" | "html-ref";
|
|
176
|
+
source: string;
|
|
177
|
+
event_schema?: unknown;
|
|
178
|
+
input_schema?: Record<string, unknown> | undefined;
|
|
179
|
+
}>;
|
|
180
|
+
export declare const patchArtifactMetadataSchema: z.ZodObject<{
|
|
181
|
+
name: z.ZodOptional<z.ZodString>;
|
|
182
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
183
|
+
description: z.ZodOptional<z.ZodString>;
|
|
184
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
185
|
+
}, "strip", z.ZodTypeAny, {
|
|
186
|
+
name?: string | undefined;
|
|
187
|
+
slug?: string | undefined;
|
|
188
|
+
description?: string | undefined;
|
|
189
|
+
tags?: string[] | undefined;
|
|
190
|
+
}, {
|
|
191
|
+
name?: string | undefined;
|
|
192
|
+
slug?: string | undefined;
|
|
193
|
+
description?: string | undefined;
|
|
194
|
+
tags?: string[] | undefined;
|
|
195
|
+
}>;
|
|
196
|
+
/** @deprecated use `CreateSessionRequest` from ./types.js (same type). */
|
|
197
|
+
export type CreateSessionInput = z.infer<typeof createSessionSchema>;
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Zod schemas for the Pane relay request shapes. These let callers (the CLI,
|
|
2
|
+
// other clients) validate user-supplied input — e.g. an inline JSON artifact
|
|
3
|
+
// or callback config — before it hits the relay, producing clear errors.
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
// The artifact `type` discriminant. `html-inline` carries raw HTML in `source`;
|
|
6
|
+
// `html-ref` carries a URL. The relay rejects `html-ref` in this release.
|
|
7
|
+
export const artifactTypeSchema = z.enum(["html-inline", "html-ref"]);
|
|
8
|
+
// Discriminated on `type`: both require a non-empty `source`. Kept for callers
|
|
9
|
+
// that want to validate a bare artifact (no event schema attached).
|
|
10
|
+
export const artifactSchema = z.discriminatedUnion("type", [
|
|
11
|
+
z.object({ type: z.literal("html-inline"), source: z.string().min(1) }),
|
|
12
|
+
z.object({ type: z.literal("html-ref"), source: z.string().min(1) }),
|
|
13
|
+
]);
|
|
14
|
+
export const callbackSchema = z.object({
|
|
15
|
+
url: z.string().url(),
|
|
16
|
+
events: z.array(z.string().min(1)).min(1),
|
|
17
|
+
secret: z.string().min(8),
|
|
18
|
+
});
|
|
19
|
+
// The inline artifact form for POST /v1/sessions — carries the event schema
|
|
20
|
+
// INSIDE the artifact object (one-off, no registered artifact). The relay
|
|
21
|
+
// transparently creates an anonymous artifact behind it.
|
|
22
|
+
const inlineArtifactSchema = z.object({
|
|
23
|
+
source: z.string().min(1),
|
|
24
|
+
type: artifactTypeSchema,
|
|
25
|
+
// Optional: omit for a view-only one-off (a report/dashboard the human only
|
|
26
|
+
// views — the session then accepts no page/agent events).
|
|
27
|
+
event_schema: z.unknown().optional(),
|
|
28
|
+
});
|
|
29
|
+
// The reference form for POST /v1/sessions — instances an existing named
|
|
30
|
+
// artifact. `id` accepts the artifact id or its slug; `version` is optional
|
|
31
|
+
// and defaults to the artifact's latest version.
|
|
32
|
+
const refArtifactSchema = z.object({
|
|
33
|
+
id: z.string().min(1),
|
|
34
|
+
version: z.number().int().positive().optional(),
|
|
35
|
+
});
|
|
36
|
+
// The session-create `artifact` field: exactly one of the two forms. A union
|
|
37
|
+
// (not a discriminated union — the two forms share no discriminant key) with a
|
|
38
|
+
// refine enforcing exactly-one-of `id` / `source`.
|
|
39
|
+
const sessionArtifactSchema = z
|
|
40
|
+
.union([refArtifactSchema, inlineArtifactSchema])
|
|
41
|
+
.refine((a) => {
|
|
42
|
+
const hasId = "id" in a && a.id !== undefined;
|
|
43
|
+
const hasSource = "source" in a && a.source !== undefined;
|
|
44
|
+
return hasId !== hasSource;
|
|
45
|
+
}, {
|
|
46
|
+
message: "artifact must carry exactly one of `id` (reference an existing artifact) or `source` (inline a one-off artifact)",
|
|
47
|
+
});
|
|
48
|
+
export const createSessionSchema = z.object({
|
|
49
|
+
artifact: sessionArtifactSchema,
|
|
50
|
+
input_data: z.record(z.unknown()).optional(),
|
|
51
|
+
participants: z.object({ humans: z.number().int().positive() }).optional(),
|
|
52
|
+
ttl: z.number().int().positive().optional(),
|
|
53
|
+
metadata: z.record(z.unknown()).optional(),
|
|
54
|
+
callback: callbackSchema.optional(),
|
|
55
|
+
});
|
|
56
|
+
// POST /v1/artifacts — create a named, reusable artifact plus its v1 content.
|
|
57
|
+
export const createArtifactSchema = z.object({
|
|
58
|
+
name: z.string().min(1),
|
|
59
|
+
slug: z.string().min(1).optional(),
|
|
60
|
+
description: z.string().optional(),
|
|
61
|
+
tags: z.array(z.string().min(1)).optional(),
|
|
62
|
+
source: z.string().min(1),
|
|
63
|
+
type: artifactTypeSchema,
|
|
64
|
+
// Optional: omit for a view-only artifact (no event vocabulary).
|
|
65
|
+
event_schema: z.unknown().optional(),
|
|
66
|
+
input_schema: z.record(z.unknown()).optional(),
|
|
67
|
+
});
|
|
68
|
+
// POST /v1/artifacts/:id/versions — append a new version (content only).
|
|
69
|
+
export const createArtifactVersionSchema = z.object({
|
|
70
|
+
source: z.string().min(1),
|
|
71
|
+
type: artifactTypeSchema,
|
|
72
|
+
// Optional: omit for a view-only artifact (no event vocabulary).
|
|
73
|
+
event_schema: z.unknown().optional(),
|
|
74
|
+
input_schema: z.record(z.unknown()).optional(),
|
|
75
|
+
});
|
|
76
|
+
// PATCH /v1/artifacts/:id — update head metadata only (never content).
|
|
77
|
+
export const patchArtifactMetadataSchema = z.object({
|
|
78
|
+
name: z.string().min(1).optional(),
|
|
79
|
+
slug: z.string().min(1).optional(),
|
|
80
|
+
description: z.string().optional(),
|
|
81
|
+
tags: z.array(z.string().min(1)).optional(),
|
|
82
|
+
});
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { WebSocket } from "ws";
|
|
2
|
+
import type { PaneEvent } from "./types.js";
|
|
3
|
+
export interface OpenStreamOptions {
|
|
4
|
+
/** WebSocket base URL, e.g. wss://pane.example.com (no trailing slash). */
|
|
5
|
+
wsBaseUrl: string;
|
|
6
|
+
/** Session id. */
|
|
7
|
+
sessionId: string;
|
|
8
|
+
/** Agent (or participant) bearer token. */
|
|
9
|
+
token: string;
|
|
10
|
+
/** Opaque cursor: replay only events strictly after this id. */
|
|
11
|
+
since?: string | null;
|
|
12
|
+
}
|
|
13
|
+
/** Callbacks for a live stream. */
|
|
14
|
+
export interface StreamHandlers {
|
|
15
|
+
/** Fired for every event envelope (replayed and live). */
|
|
16
|
+
onEvent?: (event: PaneEvent) => void;
|
|
17
|
+
/** Fired once when the initial replay finishes. */
|
|
18
|
+
onReplayComplete?: () => void;
|
|
19
|
+
/** Fired on a relay error frame. */
|
|
20
|
+
onRelayError?: (error: {
|
|
21
|
+
code?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
details?: unknown;
|
|
24
|
+
}) => void;
|
|
25
|
+
/** Fired when the socket closes (cleanly or otherwise). */
|
|
26
|
+
onClose?: (info: {
|
|
27
|
+
code: number;
|
|
28
|
+
reason: string;
|
|
29
|
+
}) => void;
|
|
30
|
+
/** Fired on a transport-level error. */
|
|
31
|
+
onError?: (err: Error) => void;
|
|
32
|
+
}
|
|
33
|
+
/** A live handle to an open stream. */
|
|
34
|
+
export interface StreamHandle {
|
|
35
|
+
/** Send an event frame into the session. */
|
|
36
|
+
send(frame: {
|
|
37
|
+
type: string;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
causation_id?: string;
|
|
40
|
+
idempotency_key?: string;
|
|
41
|
+
}): void;
|
|
42
|
+
/** Close the stream. */
|
|
43
|
+
close(): void;
|
|
44
|
+
/** The underlying ws socket (escape hatch). */
|
|
45
|
+
readonly socket: WebSocket;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Open a WebSocket stream to a Pane session. Replays on connect, then streams
|
|
49
|
+
* live. Returns a handle for sending frames and closing.
|
|
50
|
+
*/
|
|
51
|
+
export declare function openStream(opts: OpenStreamOptions, handlers: StreamHandlers): StreamHandle;
|
package/dist/stream.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// WebSocket client for WS /v1/sessions/:id/stream.
|
|
2
|
+
//
|
|
3
|
+
// The relay protocol (see the relay's src/ws/handler.ts):
|
|
4
|
+
// - on connect, the relay replays every event since `?since=` (or from the
|
|
5
|
+
// start), then sends a `{ kind: "system.replay.complete" }` marker;
|
|
6
|
+
// - thereafter it pushes live events as they land;
|
|
7
|
+
// - each frame is a JSON object: either a PaneEvent envelope, the replay
|
|
8
|
+
// marker, an `{ ack, deduped }` for frames we sent, or an `{ error }`.
|
|
9
|
+
//
|
|
10
|
+
// Note: `system.participant.joined` / `system.participant.left` (and other
|
|
11
|
+
// `system.*` events) arrive as ordinary `PaneEvent` envelopes — they are not a
|
|
12
|
+
// distinct frame kind, and may be interleaved with the initial replay stream
|
|
13
|
+
// just like any other event.
|
|
14
|
+
//
|
|
15
|
+
// `openStream` exposes this as a typed event emitter over the `ws` package.
|
|
16
|
+
import { WebSocket } from "ws";
|
|
17
|
+
import { MAX_FRAME_SNIPPET_LENGTH } from "./limits.js";
|
|
18
|
+
/**
|
|
19
|
+
* Open a WebSocket stream to a Pane session. Replays on connect, then streams
|
|
20
|
+
* live. Returns a handle for sending frames and closing.
|
|
21
|
+
*/
|
|
22
|
+
export function openStream(opts, handlers) {
|
|
23
|
+
const base = opts.wsBaseUrl.replace(/\/$/, "");
|
|
24
|
+
const u = new URL(`${base}/v1/sessions/${encodeURIComponent(opts.sessionId)}/stream`);
|
|
25
|
+
if (opts.since != null && opts.since !== "") {
|
|
26
|
+
u.searchParams.set("since", opts.since);
|
|
27
|
+
}
|
|
28
|
+
// Token via Authorization header (Node ws supports it); the relay also
|
|
29
|
+
// accepts ?token= but the header keeps it out of any URL access log.
|
|
30
|
+
const socket = new WebSocket(u.toString(), {
|
|
31
|
+
headers: { authorization: "Bearer " + opts.token },
|
|
32
|
+
});
|
|
33
|
+
socket.on("message", (raw) => {
|
|
34
|
+
const text = raw.toString();
|
|
35
|
+
let msg;
|
|
36
|
+
try {
|
|
37
|
+
msg = JSON.parse(text);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
// A malformed frame must never be silently dropped — a dropped event
|
|
41
|
+
// makes `watch --type X` hang forever. Surface it as a transport error.
|
|
42
|
+
const snippet = text.length > MAX_FRAME_SNIPPET_LENGTH
|
|
43
|
+
? text.slice(0, MAX_FRAME_SNIPPET_LENGTH) + "…"
|
|
44
|
+
: text;
|
|
45
|
+
handlers.onError?.(new Error(`failed to parse stream frame as JSON (${e instanceof Error ? e.message : String(e)}): ${snippet}`));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!msg || typeof msg !== "object") {
|
|
49
|
+
handlers.onError?.(new Error(`unexpected non-object stream frame: ${text.slice(0, MAX_FRAME_SNIPPET_LENGTH)}`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const obj = msg;
|
|
53
|
+
if (obj["kind"] === "system.replay.complete") {
|
|
54
|
+
handlers.onReplayComplete?.();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if ("error" in obj) {
|
|
58
|
+
handlers.onRelayError?.(obj["error"]);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if ("ack" in obj) {
|
|
62
|
+
// Ack for a frame we sent; nothing to surface by default.
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (typeof obj["id"] === "string" && typeof obj["type"] === "string") {
|
|
66
|
+
handlers.onEvent?.(obj);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Unrecognized frame shape — route to onError rather than dropping it.
|
|
70
|
+
handlers.onError?.(new Error(`unrecognized stream frame: ${JSON.stringify(obj).slice(0, MAX_FRAME_SNIPPET_LENGTH)}`));
|
|
71
|
+
});
|
|
72
|
+
socket.on("close", (code, reason) => {
|
|
73
|
+
handlers.onClose?.({ code, reason: reason.toString() });
|
|
74
|
+
});
|
|
75
|
+
socket.on("error", (err) => {
|
|
76
|
+
handlers.onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
send(frame) {
|
|
80
|
+
if (socket.readyState !== WebSocket.OPEN) {
|
|
81
|
+
throw new Error(`cannot send frame: stream socket is not open (readyState=${socket.readyState})`);
|
|
82
|
+
}
|
|
83
|
+
socket.send(JSON.stringify(frame));
|
|
84
|
+
},
|
|
85
|
+
close() {
|
|
86
|
+
try {
|
|
87
|
+
socket.close();
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
console.debug("[pane] stream close error:", e instanceof Error ? e.message : String(e));
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
get socket() {
|
|
94
|
+
return socket;
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { createSessionSchema } from "./schemas.js";
|
|
3
|
+
export type AuthorKind = "human" | "agent" | "system";
|
|
4
|
+
/** A single event envelope as emitted by the relay. */
|
|
5
|
+
export interface PaneEvent {
|
|
6
|
+
id: string;
|
|
7
|
+
session_id: string;
|
|
8
|
+
author: {
|
|
9
|
+
kind: AuthorKind;
|
|
10
|
+
id: string;
|
|
11
|
+
};
|
|
12
|
+
ts: string;
|
|
13
|
+
type: string;
|
|
14
|
+
data: unknown;
|
|
15
|
+
causation_id: string | null;
|
|
16
|
+
idempotency_key: string | null;
|
|
17
|
+
}
|
|
18
|
+
/** The artifact content type. `html-ref` is rejected by the relay for now. */
|
|
19
|
+
export type ArtifactType = "html-inline" | "html-ref";
|
|
20
|
+
/**
|
|
21
|
+
* An artifact: discriminated on `type`. `html-inline` carries raw HTML in
|
|
22
|
+
* `source`; `html-ref` carries a URL the relay/shell fetches on the human's
|
|
23
|
+
* behalf. The discriminant keeps the type↔source coupling explicit.
|
|
24
|
+
*/
|
|
25
|
+
export type Artifact = {
|
|
26
|
+
type: "html-inline";
|
|
27
|
+
source: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: "html-ref";
|
|
30
|
+
source: string;
|
|
31
|
+
};
|
|
32
|
+
/** Optional webhook callback config. */
|
|
33
|
+
export interface Callback {
|
|
34
|
+
url: string;
|
|
35
|
+
events: string[];
|
|
36
|
+
secret: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Request body for POST /v1/sessions. Derived from `createSessionSchema` so the
|
|
40
|
+
* runtime validator and the static type cannot drift.
|
|
41
|
+
*/
|
|
42
|
+
export type CreateSessionRequest = z.infer<typeof createSessionSchema>;
|
|
43
|
+
/** Response from POST /v1/sessions. */
|
|
44
|
+
export interface CreateSessionResponse {
|
|
45
|
+
session_id: string;
|
|
46
|
+
tokens: {
|
|
47
|
+
humans: string[];
|
|
48
|
+
agent: string;
|
|
49
|
+
};
|
|
50
|
+
urls: {
|
|
51
|
+
humans: string[];
|
|
52
|
+
agent_stream: string;
|
|
53
|
+
};
|
|
54
|
+
expires_at: string;
|
|
55
|
+
}
|
|
56
|
+
/** Response from GET /v1/sessions/:id. */
|
|
57
|
+
export interface SessionState {
|
|
58
|
+
session_id: string;
|
|
59
|
+
status: string;
|
|
60
|
+
/** The artifact version this session is pinned to. */
|
|
61
|
+
artifact_id: string;
|
|
62
|
+
artifact_version_id: string;
|
|
63
|
+
artifact_version: number;
|
|
64
|
+
metadata: Record<string, unknown> | null;
|
|
65
|
+
input_data: Record<string, unknown> | null;
|
|
66
|
+
created_at: string;
|
|
67
|
+
expires_at: string;
|
|
68
|
+
}
|
|
69
|
+
/** Response from GET /v1/sessions/:id/events. */
|
|
70
|
+
export interface EventsPage {
|
|
71
|
+
events: PaneEvent[];
|
|
72
|
+
next_cursor: string | null;
|
|
73
|
+
}
|
|
74
|
+
/** One immutable version of an artifact's content. */
|
|
75
|
+
export interface ArtifactVersion {
|
|
76
|
+
id: string;
|
|
77
|
+
version: number;
|
|
78
|
+
type: ArtifactType;
|
|
79
|
+
source: string;
|
|
80
|
+
event_schema: unknown;
|
|
81
|
+
input_schema: Record<string, unknown> | null;
|
|
82
|
+
created_at: string;
|
|
83
|
+
}
|
|
84
|
+
/** A full artifact — head metadata plus its version list. */
|
|
85
|
+
export interface Artifact_ {
|
|
86
|
+
id: string;
|
|
87
|
+
slug: string | null;
|
|
88
|
+
name: string | null;
|
|
89
|
+
description: string | null;
|
|
90
|
+
tags: string[] | null;
|
|
91
|
+
latest_version: number;
|
|
92
|
+
last_used_at: string | null;
|
|
93
|
+
created_at: string;
|
|
94
|
+
updated_at: string;
|
|
95
|
+
versions: ArtifactVersion[];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A full artifact — head metadata plus its version list. (`ArtifactRecord` is
|
|
99
|
+
* the public name; `Artifact` is kept as the older inline-artifact union.)
|
|
100
|
+
*/
|
|
101
|
+
export type ArtifactRecord = Artifact_;
|
|
102
|
+
/**
|
|
103
|
+
* A lean artifact summary for list/search responses — head metadata only, no
|
|
104
|
+
* `source` blob. See GET /v1/artifacts.
|
|
105
|
+
*/
|
|
106
|
+
export interface ArtifactSummary {
|
|
107
|
+
id: string;
|
|
108
|
+
slug: string | null;
|
|
109
|
+
name: string | null;
|
|
110
|
+
description: string | null;
|
|
111
|
+
tags: string[] | null;
|
|
112
|
+
latest_version: number;
|
|
113
|
+
last_used_at: string | null;
|
|
114
|
+
}
|
|
115
|
+
/** Response from POST /v1/artifacts and POST /v1/artifacts/:id/versions. */
|
|
116
|
+
export interface CreateArtifactResponse {
|
|
117
|
+
artifact_id: string;
|
|
118
|
+
version: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Response from GET /v1/keys — the calling agent's own key info. The relay
|
|
122
|
+
* scopes this to the authenticated agent: it returns ONE key (the caller's),
|
|
123
|
+
* not a list.
|
|
124
|
+
*/
|
|
125
|
+
export interface KeyInfo {
|
|
126
|
+
agent_id: string;
|
|
127
|
+
name: string | null;
|
|
128
|
+
key_prefix: string;
|
|
129
|
+
created_at: string;
|
|
130
|
+
last_used_at: string | null;
|
|
131
|
+
revoked_at: string | null;
|
|
132
|
+
}
|
|
133
|
+
/** A relay error envelope. */
|
|
134
|
+
export interface RelayError {
|
|
135
|
+
code: string;
|
|
136
|
+
message?: string;
|
|
137
|
+
details?: unknown;
|
|
138
|
+
/** Agent-friendly remediation hint. */
|
|
139
|
+
hint?: string;
|
|
140
|
+
/** Whether retrying the same request may succeed. */
|
|
141
|
+
retryable?: boolean;
|
|
142
|
+
/** Documentation URL for this error class (snake_case on the wire). */
|
|
143
|
+
docs_url?: string;
|
|
144
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Wire types for the Pane relay HTTP + WebSocket API.
|
|
2
|
+
//
|
|
3
|
+
// These mirror the relay's public response shapes (see the relay's
|
|
4
|
+
// src/types.ts, src/http/serialize.ts and src/http/routes/*). They are
|
|
5
|
+
// re-declared here rather than imported from @paneui/relay so that @paneui/core
|
|
6
|
+
// stays pure and framework-free — no Prisma, no Hono, no server deps.
|
|
7
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@paneui/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Pane relay client: typed HTTP + WebSocket operations against a Pane relay. Framework-free.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"pane",
|
|
9
|
+
"agent",
|
|
10
|
+
"websocket",
|
|
11
|
+
"relay",
|
|
12
|
+
"human-in-the-loop"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/aerolalit/paneui#readme",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/aerolalit/paneui.git",
|
|
18
|
+
"directory": "packages/core"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/aerolalit/paneui/issues"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"main": "dist/index.js",
|
|
30
|
+
"types": "dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:unit": "vitest run"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"ws": "^8.20.1",
|
|
50
|
+
"zod": "^3.23.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.7.0",
|
|
54
|
+
"@types/ws": "^8.18.1",
|
|
55
|
+
"typescript": "^5.6.0",
|
|
56
|
+
"vitest": "^4.1.6"
|
|
57
|
+
}
|
|
58
|
+
}
|