@paneui/core 0.0.5 → 0.0.7
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/dist/client.d.ts +242 -36
- package/dist/client.js +330 -40
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/schemas.d.ts +69 -171
- package/dist/schemas.js +61 -23
- package/dist/stream.d.ts +4 -4
- package/dist/stream.js +3 -3
- package/dist/types.d.ts +96 -31
- package/package.json +4 -4
package/dist/client.js
CHANGED
|
@@ -121,10 +121,11 @@ export class PaneClient {
|
|
|
121
121
|
docsUrl: err?.docs_url,
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
|
-
/** POST /v1/
|
|
124
|
+
/** POST /v1/surfaces — create a surface. */
|
|
125
125
|
async createSession(req) {
|
|
126
|
-
const r = await this.call("POST", "/v1/
|
|
127
|
-
|
|
126
|
+
const r = await this.call("POST", "/v1/surfaces", {
|
|
127
|
+
template: req.template,
|
|
128
|
+
title: req.title,
|
|
128
129
|
input_data: req.input_data,
|
|
129
130
|
participants: req.participants,
|
|
130
131
|
ttl: req.ttl,
|
|
@@ -135,19 +136,19 @@ export class PaneClient {
|
|
|
135
136
|
this.fail(r);
|
|
136
137
|
return this.asObject(r);
|
|
137
138
|
}
|
|
138
|
-
/** GET /v1/
|
|
139
|
-
async getSession(
|
|
140
|
-
const r = await this.call("GET", `/v1/
|
|
139
|
+
/** GET /v1/surfaces/:id — non-blocking surface metadata. */
|
|
140
|
+
async getSession(surfaceId) {
|
|
141
|
+
const r = await this.call("GET", `/v1/surfaces/${encodeURIComponent(surfaceId)}`);
|
|
141
142
|
if (!r.ok)
|
|
142
143
|
this.fail(r);
|
|
143
144
|
return this.asObject(r);
|
|
144
145
|
}
|
|
145
146
|
/**
|
|
146
|
-
* GET /v1/
|
|
147
|
+
* GET /v1/surfaces/:id/events — fetch the event log.
|
|
147
148
|
* `since` is an opaque cursor; `waitSeconds` enables the relay long-poll
|
|
148
149
|
* (0 = non-blocking, capped at 30 by the relay).
|
|
149
150
|
*/
|
|
150
|
-
async getEvents(
|
|
151
|
+
async getEvents(surfaceId, opts = {}) {
|
|
151
152
|
const q = new URLSearchParams();
|
|
152
153
|
if (opts.since != null && opts.since !== "")
|
|
153
154
|
q.set("since", opts.since);
|
|
@@ -155,14 +156,14 @@ export class PaneClient {
|
|
|
155
156
|
q.set("wait", String(Math.floor(opts.waitSeconds)));
|
|
156
157
|
}
|
|
157
158
|
const qs = q.toString();
|
|
158
|
-
const r = await this.call("GET", `/v1/
|
|
159
|
+
const r = await this.call("GET", `/v1/surfaces/${encodeURIComponent(surfaceId)}/events${qs ? "?" + qs : ""}`);
|
|
159
160
|
if (!r.ok)
|
|
160
161
|
this.fail(r);
|
|
161
162
|
return this.asObject(r);
|
|
162
163
|
}
|
|
163
|
-
/** POST /v1/
|
|
164
|
-
async sendEvent(
|
|
165
|
-
const r = await this.call("POST", `/v1/
|
|
164
|
+
/** POST /v1/surfaces/:id/events — append an agent event. */
|
|
165
|
+
async sendEvent(surfaceId, ev) {
|
|
166
|
+
const r = await this.call("POST", `/v1/surfaces/${encodeURIComponent(surfaceId)}/events`, {
|
|
166
167
|
type: ev.type,
|
|
167
168
|
data: ev.data,
|
|
168
169
|
causation_id: ev.causationId,
|
|
@@ -174,11 +175,11 @@ export class PaneClient {
|
|
|
174
175
|
return { event: body.event, deduped: body.deduped ?? false };
|
|
175
176
|
}
|
|
176
177
|
/**
|
|
177
|
-
* POST /v1/
|
|
178
|
-
* Returns the new `
|
|
178
|
+
* POST /v1/templates — create a named, reusable template and its v1 content.
|
|
179
|
+
* Returns the new `template_id` and `version` (1).
|
|
179
180
|
*/
|
|
180
181
|
async createArtifact(req) {
|
|
181
|
-
const r = await this.call("POST", "/v1/
|
|
182
|
+
const r = await this.call("POST", "/v1/templates", {
|
|
182
183
|
name: req.name,
|
|
183
184
|
slug: req.slug,
|
|
184
185
|
description: req.description,
|
|
@@ -193,12 +194,12 @@ export class PaneClient {
|
|
|
193
194
|
return this.asObject(r);
|
|
194
195
|
}
|
|
195
196
|
/**
|
|
196
|
-
* POST /v1/
|
|
197
|
-
* existing
|
|
197
|
+
* POST /v1/templates/:id/versions — append a new immutable version to an
|
|
198
|
+
* existing template. `idOrSlug` accepts the template id or its slug.
|
|
198
199
|
* Returns the new `version` number.
|
|
199
200
|
*/
|
|
200
201
|
async createArtifactVersion(idOrSlug, req) {
|
|
201
|
-
const r = await this.call("POST", `/v1/
|
|
202
|
+
const r = await this.call("POST", `/v1/templates/${encodeURIComponent(idOrSlug)}/versions`, {
|
|
202
203
|
source: req.source,
|
|
203
204
|
type: req.type,
|
|
204
205
|
event_schema: req.event_schema,
|
|
@@ -209,11 +210,11 @@ export class PaneClient {
|
|
|
209
210
|
return this.asObject(r);
|
|
210
211
|
}
|
|
211
212
|
/**
|
|
212
|
-
* PATCH /v1/
|
|
213
|
+
* PATCH /v1/templates/:id — update head metadata (name / slug / description /
|
|
213
214
|
* tags); never the content. Returns the updated lean summary.
|
|
214
215
|
*/
|
|
215
216
|
async updateArtifact(idOrSlug, metadata) {
|
|
216
|
-
const r = await this.call("PATCH", `/v1/
|
|
217
|
+
const r = await this.call("PATCH", `/v1/templates/${encodeURIComponent(idOrSlug)}`, {
|
|
217
218
|
name: metadata.name,
|
|
218
219
|
slug: metadata.slug,
|
|
219
220
|
description: metadata.description,
|
|
@@ -224,33 +225,33 @@ export class PaneClient {
|
|
|
224
225
|
return this.asObject(r);
|
|
225
226
|
}
|
|
226
227
|
/**
|
|
227
|
-
* GET /v1/
|
|
228
|
-
* response is lean (no `source`
|
|
229
|
-
* to list every named
|
|
228
|
+
* GET /v1/templates?q=... — search/list the agent's named templates. The
|
|
229
|
+
* response is lean (no `source` attachment), ranked by `last_used_at`. Omit `query`
|
|
230
|
+
* to list every named template.
|
|
230
231
|
*/
|
|
231
232
|
async searchArtifacts(query) {
|
|
232
233
|
const qs = query != null && query !== "" ? "?q=" + encodeURIComponent(query) : "";
|
|
233
|
-
const r = await this.call("GET", `/v1/
|
|
234
|
+
const r = await this.call("GET", `/v1/templates${qs}`);
|
|
234
235
|
if (!r.ok)
|
|
235
236
|
this.fail(r);
|
|
236
|
-
return this.asObject(r).
|
|
237
|
+
return this.asObject(r).templates;
|
|
237
238
|
}
|
|
238
239
|
/**
|
|
239
|
-
* GET /v1/
|
|
240
|
-
* list). `idOrSlug` accepts the
|
|
240
|
+
* GET /v1/templates/:id — fetch a full template (head metadata + version
|
|
241
|
+
* list). `idOrSlug` accepts the template id or its slug.
|
|
241
242
|
*/
|
|
242
243
|
async getArtifact(idOrSlug) {
|
|
243
|
-
const r = await this.call("GET", `/v1/
|
|
244
|
+
const r = await this.call("GET", `/v1/templates/${encodeURIComponent(idOrSlug)}`);
|
|
244
245
|
if (!r.ok)
|
|
245
246
|
this.fail(r);
|
|
246
247
|
return this.asObject(r);
|
|
247
248
|
}
|
|
248
249
|
/**
|
|
249
|
-
* GET /v1/
|
|
250
|
+
* GET /v1/templates/:id/versions/:version — fetch one version's full
|
|
250
251
|
* content (HTML, event schema, input schema).
|
|
251
252
|
*/
|
|
252
253
|
async getArtifactVersion(idOrSlug, version) {
|
|
253
|
-
const r = await this.call("GET", `/v1/
|
|
254
|
+
const r = await this.call("GET", `/v1/templates/${encodeURIComponent(idOrSlug)}/versions/${encodeURIComponent(String(version))}`);
|
|
254
255
|
if (!r.ok)
|
|
255
256
|
this.fail(r);
|
|
256
257
|
return this.asObject(r);
|
|
@@ -276,10 +277,23 @@ export class PaneClient {
|
|
|
276
277
|
this.fail(r);
|
|
277
278
|
}
|
|
278
279
|
/**
|
|
279
|
-
*
|
|
280
|
+
* POST /v1/agents/claim — bind this agent to a human via a one-shot
|
|
281
|
+
* claim code the human generated in their settings UI. After a
|
|
282
|
+
* successful claim the agent's existing API key continues to work,
|
|
283
|
+
* but the agent (and its surfaces/templates) now belong to the
|
|
284
|
+
* claiming human. One-way operation — there is no unclaim in v1.
|
|
285
|
+
*/
|
|
286
|
+
async claimAgent(code) {
|
|
287
|
+
const r = await this.call("POST", "/v1/agents/claim", { code });
|
|
288
|
+
if (!r.ok)
|
|
289
|
+
this.fail(r);
|
|
290
|
+
return this.asObject(r);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* GET /v1/taste — the calling agent's freeform "taste notes" markdown attachment:
|
|
280
294
|
* presentation preferences the agent has picked up from human feedback over
|
|
281
295
|
* time. Returns `{ taste: null, updated_at: null, bytes: 0 }` when the
|
|
282
|
-
* agent has never written notes. Read this before generating an
|
|
296
|
+
* agent has never written notes. Read this before generating an template so
|
|
283
297
|
* the agent applies prior feedback.
|
|
284
298
|
*/
|
|
285
299
|
async getTaste() {
|
|
@@ -289,7 +303,7 @@ export class PaneClient {
|
|
|
289
303
|
return this.asObject(r);
|
|
290
304
|
}
|
|
291
305
|
/**
|
|
292
|
-
* PUT /v1/taste — whole-
|
|
306
|
+
* PUT /v1/taste — whole-attachment replace of the calling agent's taste notes.
|
|
293
307
|
* Empty/whitespace-only values are rejected by the relay; callers asking to
|
|
294
308
|
* clear must use {@link clearTaste}. The relay caps the payload at the
|
|
295
309
|
* server's `MAX_TASTE_BYTES` (utf8 bytes).
|
|
@@ -319,7 +333,7 @@ export class PaneClient {
|
|
|
319
333
|
const r = await this.call("POST", "/v1/feedback", {
|
|
320
334
|
type: req.type,
|
|
321
335
|
message: req.message,
|
|
322
|
-
|
|
336
|
+
surface_id: req.surfaceId,
|
|
323
337
|
});
|
|
324
338
|
if (!r.ok)
|
|
325
339
|
this.fail(r);
|
|
@@ -342,24 +356,300 @@ export class PaneClient {
|
|
|
342
356
|
return this.asObject(r);
|
|
343
357
|
}
|
|
344
358
|
/**
|
|
345
|
-
*
|
|
346
|
-
*
|
|
359
|
+
* GET /v1/surfaces — list the calling agent's surfaces. Default filter is
|
|
360
|
+
* `status=open` (effective status — respects expiresAt). Response items
|
|
361
|
+
* carry NO secrets: no participant token plaintext, no callback URL, no
|
|
362
|
+
* metadata or input_data. Use `participant_id` from the list as the handle
|
|
363
|
+
* for {@link revokeParticipant}; use {@link mintParticipant} to issue a
|
|
364
|
+
* fresh URL when the original was lost.
|
|
365
|
+
*/
|
|
366
|
+
async listSessions(opts = {}) {
|
|
367
|
+
const q = new URLSearchParams();
|
|
368
|
+
if (opts.status !== undefined)
|
|
369
|
+
q.set("status", opts.status);
|
|
370
|
+
if (opts.limit !== undefined)
|
|
371
|
+
q.set("limit", String(opts.limit));
|
|
372
|
+
if (opts.cursor !== undefined && opts.cursor !== "")
|
|
373
|
+
q.set("cursor", opts.cursor);
|
|
374
|
+
if (opts.template_id !== undefined && opts.template_id !== "")
|
|
375
|
+
q.set("template_id", opts.template_id);
|
|
376
|
+
const qs = q.toString();
|
|
377
|
+
const r = await this.call("GET", `/v1/surfaces${qs ? "?" + qs : ""}`);
|
|
378
|
+
if (!r.ok)
|
|
379
|
+
this.fail(r);
|
|
380
|
+
return this.asObject(r);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* GET /v1/surfaces/:id/participants — list every participant on one
|
|
384
|
+
* surface (active and revoked). Bounded by MAX_PARTICIPANTS_PER_SESSION
|
|
385
|
+
* on the relay, so the full list is returned with no pagination.
|
|
386
|
+
* Use this to find the `participant_id` you need to pass to
|
|
387
|
+
* {@link revokeParticipant}, or to audit revoked rows.
|
|
388
|
+
*/
|
|
389
|
+
async listParticipants(surfaceId) {
|
|
390
|
+
const r = await this.call("GET", `/v1/surfaces/${encodeURIComponent(surfaceId)}/participants`);
|
|
391
|
+
if (!r.ok)
|
|
392
|
+
this.fail(r);
|
|
393
|
+
return this.asObject(r);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* POST /v1/surfaces/:id/participants — mint a fresh participant URL for an
|
|
397
|
+
* existing surface. The one-shot recovery primitive when the original URL
|
|
398
|
+
* was dropped: the surface keeps its event log, template pin, and created_at.
|
|
399
|
+
* v1 supports `kind: "human"` only.
|
|
400
|
+
*
|
|
401
|
+
* The plaintext token is returned EXACTLY ONCE in the response — the relay
|
|
402
|
+
* stores only the hash. Save the response (e.g. pipe to a JSONL log) before
|
|
403
|
+
* delivering the URL to the human.
|
|
404
|
+
*/
|
|
405
|
+
async mintParticipant(surfaceId, opts = {}) {
|
|
406
|
+
const r = await this.call("POST", `/v1/surfaces/${encodeURIComponent(surfaceId)}/participants`, { kind: opts.kind ?? "human" });
|
|
407
|
+
if (!r.ok)
|
|
408
|
+
this.fail(r);
|
|
409
|
+
return this.asObject(r);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* DELETE /v1/surfaces/:id/participants/:participant_id — revoke a single
|
|
413
|
+
* participant URL. The surface's other participants (and the agent's own
|
|
414
|
+
* WebSocket) are untouched. Idempotent: revoking an unknown or already-
|
|
415
|
+
* revoked participant returns 204. The agent participant cannot be revoked
|
|
416
|
+
* via this endpoint — use {@link deleteSession} instead.
|
|
417
|
+
*
|
|
418
|
+
* Existing WebSocket connections held under the revoked token are NOT
|
|
419
|
+
* actively kicked in v1; new HTTP and WS connections are refused.
|
|
420
|
+
*/
|
|
421
|
+
async revokeParticipant(surfaceId, participantId) {
|
|
422
|
+
const r = await this.call("DELETE", `/v1/surfaces/${encodeURIComponent(surfaceId)}/participants/${encodeURIComponent(participantId)}`);
|
|
423
|
+
if (!r.ok)
|
|
424
|
+
this.fail(r);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* DELETE /v1/surfaces/:id — close/delete a surface. Idempotent on the relay
|
|
428
|
+
* side (an already-closed surface still returns 204 with no body).
|
|
347
429
|
*/
|
|
348
430
|
async deleteSession(id) {
|
|
349
|
-
const r = await this.call("DELETE", `/v1/
|
|
431
|
+
const r = await this.call("DELETE", `/v1/surfaces/${encodeURIComponent(id)}`);
|
|
350
432
|
if (!r.ok)
|
|
351
433
|
this.fail(r);
|
|
352
434
|
}
|
|
353
435
|
/**
|
|
354
|
-
* DELETE /v1/
|
|
436
|
+
* DELETE /v1/templates/:id — remove an template and (server-side) all its
|
|
355
437
|
* versions. Strict cascade: the relay refuses with 409 conflict if any
|
|
356
|
-
*
|
|
438
|
+
* surface in any state still references one of the template's versions —
|
|
357
439
|
* surface that as a typed PaneApiError so the CLI can render a hint
|
|
358
440
|
* instead of swallowing it.
|
|
359
441
|
*/
|
|
360
442
|
async deleteArtifact(idOrSlug) {
|
|
361
|
-
const r = await this.call("DELETE", `/v1/
|
|
443
|
+
const r = await this.call("DELETE", `/v1/templates/${encodeURIComponent(idOrSlug)}`);
|
|
444
|
+
if (!r.ok)
|
|
445
|
+
this.fail(r);
|
|
446
|
+
}
|
|
447
|
+
// ------------------------------------------------------------------------
|
|
448
|
+
// Blobs (v0.1.0). Three-scope binary attachments with multipart upload.
|
|
449
|
+
// See proposal pane#152 for the full design.
|
|
450
|
+
// ------------------------------------------------------------------------
|
|
451
|
+
/**
|
|
452
|
+
* Upload a attachment to the relay. Returns a `AttachmentRef` that can be referenced
|
|
453
|
+
* in event payloads (the relay's `format: pane-attachment-id` schema vocab
|
|
454
|
+
* validates the id) or in `pane create --input-data`.
|
|
455
|
+
*
|
|
456
|
+
* Scope defaults to "agent" (reusable across the agent's surfaces). For
|
|
457
|
+
* `scope: "surface"` pass `surfaceId`; for `scope: "template"` pass
|
|
458
|
+
* `templateId`. The agent must own the referenced surface / template;
|
|
459
|
+
* cross-tenant attempts return attachment_not_found.
|
|
460
|
+
*
|
|
461
|
+
* MIME is inferred from `mime` if supplied; otherwise the relay sniffs
|
|
462
|
+
* leading bytes and may reject with mime_mismatch / mime_disallowed.
|
|
463
|
+
*
|
|
464
|
+
* Backed by the relay's multipart `POST /v1/attachments` (the fallback path).
|
|
465
|
+
* For large uploads (>1 MB on hosted Azure) call `presignBlob()` +
|
|
466
|
+
* `confirmBlob()` instead — those use SAS direct-to-storage and don't
|
|
467
|
+
* stream bytes through the relay.
|
|
468
|
+
*/
|
|
469
|
+
async uploadBlob(file, opts = {}) {
|
|
470
|
+
const fd = new FormData();
|
|
471
|
+
let attachment;
|
|
472
|
+
if (file instanceof Blob) {
|
|
473
|
+
attachment = file;
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
// Buffer / Uint8Array path — wrap in a Blob with the declared MIME.
|
|
477
|
+
// Copy into a freshly allocated Uint8Array so the buffer type
|
|
478
|
+
// narrows from `ArrayBufferLike` (which includes SharedArrayBuffer)
|
|
479
|
+
// to `ArrayBuffer` specifically — the Blob constructor accepts only
|
|
480
|
+
// the latter under @types/node ≥25 + TS ≥5.7's generic narrowing of
|
|
481
|
+
// Uint8Array<TArrayBuffer>. `new Uint8Array(length)` returns
|
|
482
|
+
// `Uint8Array<ArrayBuffer>` by construction, satisfying AttachmentPart
|
|
483
|
+
// without a type cast. The extra copy is one walk over the bytes —
|
|
484
|
+
// negligible vs the network upload that follows.
|
|
485
|
+
const src = file instanceof Uint8Array ? file : new Uint8Array(file);
|
|
486
|
+
const u8 = new Uint8Array(src.byteLength);
|
|
487
|
+
u8.set(src);
|
|
488
|
+
attachment = new Blob([u8], {
|
|
489
|
+
type: opts.mime ?? "application/octet-stream",
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
fd.set("file", attachment, opts.filename ?? "attachment");
|
|
493
|
+
if (opts.scope)
|
|
494
|
+
fd.set("scope", opts.scope);
|
|
495
|
+
if (opts.surfaceId)
|
|
496
|
+
fd.set("surface_id", opts.surfaceId);
|
|
497
|
+
if (opts.templateId)
|
|
498
|
+
fd.set("template_id", opts.templateId);
|
|
499
|
+
if (opts.filename)
|
|
500
|
+
fd.set("filename", opts.filename);
|
|
501
|
+
const url = this.base + "/v1/attachments";
|
|
502
|
+
let res;
|
|
503
|
+
try {
|
|
504
|
+
res = await this.fetchImpl(url, {
|
|
505
|
+
method: "POST",
|
|
506
|
+
headers: {
|
|
507
|
+
authorization: "Bearer " + this.apiKey,
|
|
508
|
+
...(this.cliVersion ? { "x-pane-cli-version": this.cliVersion } : {}),
|
|
509
|
+
},
|
|
510
|
+
body: fd,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
catch (e) {
|
|
514
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
515
|
+
throw new PaneApiError(0, "fetch_error", msg);
|
|
516
|
+
}
|
|
517
|
+
const text = await res.text().catch(() => "");
|
|
518
|
+
let data;
|
|
519
|
+
try {
|
|
520
|
+
data = text ? JSON.parse(text) : null;
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
throw new PaneApiError(res.status, "non_json_response", `relay returned a non-JSON body (status ${res.status})`);
|
|
524
|
+
}
|
|
525
|
+
if (!res.ok) {
|
|
526
|
+
this.fail({ ok: false, status: res.status, data });
|
|
527
|
+
}
|
|
528
|
+
return data;
|
|
529
|
+
}
|
|
530
|
+
/** GET /v1/attachments/:id — download bytes as an ArrayBuffer. */
|
|
531
|
+
async downloadBlob(attachmentId) {
|
|
532
|
+
const url = this.base + "/v1/attachments/" + encodeURIComponent(attachmentId);
|
|
533
|
+
const res = await this.fetchImpl(url, {
|
|
534
|
+
method: "GET",
|
|
535
|
+
headers: {
|
|
536
|
+
authorization: "Bearer " + this.apiKey,
|
|
537
|
+
...(this.cliVersion ? { "x-pane-cli-version": this.cliVersion } : {}),
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
if (!res.ok) {
|
|
541
|
+
const text = await res.text().catch(() => "");
|
|
542
|
+
let data;
|
|
543
|
+
try {
|
|
544
|
+
data = text ? JSON.parse(text) : null;
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
data = null;
|
|
548
|
+
}
|
|
549
|
+
this.fail({ ok: false, status: res.status, data });
|
|
550
|
+
}
|
|
551
|
+
return res.arrayBuffer();
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* GET a attachment's metadata only — useful before downloading large attachments, or
|
|
555
|
+
* for `pane attachment show <id>` which doesn't want the bytes. Returns the full
|
|
556
|
+
* AttachmentRef (the same shape POST /v1/attachments returns): id, scope, mime, size,
|
|
557
|
+
* sha256, filename, width, height, status, scope FKs, timestamps.
|
|
558
|
+
*
|
|
559
|
+
* Backed by GET /v1/attachments/:id/metadata which serves the JSON AttachmentRef
|
|
560
|
+
* without streaming the bytes — cheap on the relay and avoids the
|
|
561
|
+
* encrypt-at-rest decrypt cost when only the metadata is needed.
|
|
562
|
+
*/
|
|
563
|
+
async getBlob(attachmentId) {
|
|
564
|
+
const r = await this.call("GET", "/v1/attachments/" + encodeURIComponent(attachmentId) + "/metadata");
|
|
362
565
|
if (!r.ok)
|
|
363
566
|
this.fail(r);
|
|
567
|
+
return this.asObject(r);
|
|
568
|
+
}
|
|
569
|
+
/** DELETE /v1/attachments/:id — soft-delete (idempotent). */
|
|
570
|
+
async deleteBlob(attachmentId) {
|
|
571
|
+
const r = await this.call("DELETE", "/v1/attachments/" + encodeURIComponent(attachmentId));
|
|
572
|
+
if (!r.ok)
|
|
573
|
+
this.fail(r);
|
|
574
|
+
return { deleted: true };
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Mint a `/b/<token>` capability URL for `attachmentId`. Default TTL is set by
|
|
578
|
+
* the relay (24h agent, surface-TTL surface, 30d template). `once: true`
|
|
579
|
+
* tokens self-delete on first GET.
|
|
580
|
+
*/
|
|
581
|
+
async mintBlobToken(attachmentId, opts = {}) {
|
|
582
|
+
const r = await this.call("POST", "/v1/attachments/" + encodeURIComponent(attachmentId) + "/tokens", { ttl_seconds: opts.ttlSeconds, once: opts.once });
|
|
583
|
+
if (!r.ok)
|
|
584
|
+
this.fail(r);
|
|
585
|
+
return r.data;
|
|
586
|
+
}
|
|
587
|
+
/** Revoke a previously-minted token. Idempotent. */
|
|
588
|
+
async revokeBlobToken(attachmentId, tokenId) {
|
|
589
|
+
const r = await this.call("DELETE", "/v1/attachments/" +
|
|
590
|
+
encodeURIComponent(attachmentId) +
|
|
591
|
+
"/tokens/" +
|
|
592
|
+
encodeURIComponent(tokenId));
|
|
593
|
+
if (!r.ok)
|
|
594
|
+
this.fail(r);
|
|
595
|
+
return r.data;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* GET /v1/attachments — list YOUR agent's non-deleted attachments (newest first).
|
|
599
|
+
* Paginated via opaque cursor: when `next_cursor` is non-null, pass it
|
|
600
|
+
* back as `cursor` on the next call.
|
|
601
|
+
*/
|
|
602
|
+
async listBlobs(opts = {}) {
|
|
603
|
+
const params = new URLSearchParams();
|
|
604
|
+
if (opts.cursor !== undefined)
|
|
605
|
+
params.set("cursor", opts.cursor);
|
|
606
|
+
if (opts.limit !== undefined)
|
|
607
|
+
params.set("limit", String(opts.limit));
|
|
608
|
+
const qs = params.toString();
|
|
609
|
+
const r = await this.call("GET", "/v1/attachments" + (qs ? "?" + qs : ""));
|
|
610
|
+
if (!r.ok)
|
|
611
|
+
this.fail(r);
|
|
612
|
+
return r.data;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* GET /v1/attachments/:id/tokens — enumerate the capability tokens minted
|
|
616
|
+
* against one attachment, including revoked rows (for audit). The plaintext
|
|
617
|
+
* token is NEVER returned — it isn't stored, only its sha256 is.
|
|
618
|
+
*/
|
|
619
|
+
async listBlobTokens(attachmentId) {
|
|
620
|
+
const r = await this.call("GET", "/v1/attachments/" + encodeURIComponent(attachmentId) + "/tokens");
|
|
621
|
+
if (!r.ok)
|
|
622
|
+
this.fail(r);
|
|
623
|
+
return r.data;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Issue a presigned PUT URL for direct-to-storage upload. Returns the
|
|
627
|
+
* upload URL + the attachment_id (already reserved in the relay's DB with
|
|
628
|
+
* status=pending) + expiry. After PUTting the bytes to the URL, call
|
|
629
|
+
* `confirmBlob(attachment_id)` to finalise.
|
|
630
|
+
*
|
|
631
|
+
* Filesystem backend returns 501 not_implemented — use uploadBlob()
|
|
632
|
+
* (multipart fallback) instead. Azure backend returns a SAS URL.
|
|
633
|
+
*/
|
|
634
|
+
async presignBlob(opts) {
|
|
635
|
+
const r = await this.call("POST", "/v1/attachments/presign", {
|
|
636
|
+
mime: opts.mime,
|
|
637
|
+
size: opts.size,
|
|
638
|
+
sha256: opts.sha256,
|
|
639
|
+
scope: opts.scope,
|
|
640
|
+
surface_id: opts.surfaceId,
|
|
641
|
+
template_id: opts.templateId,
|
|
642
|
+
filename: opts.filename,
|
|
643
|
+
});
|
|
644
|
+
if (!r.ok)
|
|
645
|
+
this.fail(r);
|
|
646
|
+
return r.data;
|
|
647
|
+
}
|
|
648
|
+
/** Finalise a presigned upload — relay HEADs the bytes, verifies, flips ready. */
|
|
649
|
+
async confirmBlob(attachmentId) {
|
|
650
|
+
const r = await this.call("POST", "/v1/attachments/" + encodeURIComponent(attachmentId) + "/confirm");
|
|
651
|
+
if (!r.ok)
|
|
652
|
+
this.fail(r);
|
|
653
|
+
return r.data;
|
|
364
654
|
}
|
|
365
655
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { PaneClient, PaneApiError } from "./client.js";
|
|
2
|
-
export type { ClientOptions, RelayResponse, CreateArtifactRequest, CreateArtifactVersionRequest, PatchArtifactMetadataRequest, } from "./client.js";
|
|
2
|
+
export type { ClientOptions, RelayResponse, CreateArtifactRequest, CreateArtifactVersionRequest, PatchArtifactMetadataRequest, AttachmentRef, UploadBlobOptions, PresignBlobOptions, AttachmentTokenMintResponse, ListBlobsOptions, AttachmentTokenAuditEntry, AttachmentTokenListResponse, } from "./client.js";
|
|
3
3
|
export { openStream } from "./stream.js";
|
|
4
4
|
export type { OpenStreamOptions, StreamHandlers, StreamHandle, } from "./stream.js";
|
|
5
5
|
export { registerAgent } from "./register.js";
|
|
6
6
|
export type { RegisterAgentOptions, RegisterAgentResult } from "./register.js";
|
|
7
|
-
export { artifactSchema, callbackSchema, createSessionSchema, artifactTypeSchema, createArtifactSchema, createArtifactVersionSchema, patchArtifactMetadataSchema, feedbackTypeSchema, submitFeedbackSchema, } from "./schemas.js";
|
|
8
|
-
export type { CreateSessionInput } from "./schemas.js";
|
|
7
|
+
export { artifactSchema, callbackSchema, createSessionSchema, artifactTypeSchema, createArtifactSchema, createArtifactVersionSchema, patchArtifactMetadataSchema, feedbackTypeSchema, submitFeedbackSchema, listSessionsStatusSchema, listSessionsQuerySchema, mintParticipantSchema, } from "./schemas.js";
|
|
8
|
+
export type { CreateSessionInput, ListSessionsStatus, ListSessionsQuery, MintParticipantInput, } from "./schemas.js";
|
|
9
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,
|
|
10
|
+
export type { AuthorKind, PaneEvent, Template, TemplateType, TemplateVersion, TemplateRecord, TemplateSummary, CreateArtifactResponse, KeyInfo, TasteInfo, FeedbackType, FeedbackSubmission, FeedbackRecord, FeedbackPage, Callback, CreateSessionRequest, CreateSessionResponse, SurfaceState, EventsPage, ParticipantSummary, ParticipantsList, SurfaceSummary, SurfacesPage, MintParticipantResponse, RelayError, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
export { PaneClient, PaneApiError } from "./client.js";
|
|
4
4
|
export { openStream } from "./stream.js";
|
|
5
5
|
export { registerAgent } from "./register.js";
|
|
6
|
-
export { artifactSchema, callbackSchema, createSessionSchema, artifactTypeSchema, createArtifactSchema, createArtifactVersionSchema, patchArtifactMetadataSchema, feedbackTypeSchema, submitFeedbackSchema, } from "./schemas.js";
|
|
6
|
+
export { artifactSchema, callbackSchema, createSessionSchema, artifactTypeSchema, createArtifactSchema, createArtifactVersionSchema, patchArtifactMetadataSchema, feedbackTypeSchema, submitFeedbackSchema, listSessionsStatusSchema, listSessionsQuerySchema, mintParticipantSchema, } from "./schemas.js";
|
|
7
7
|
export { MAX_EVENT_TYPE_LENGTH, MAX_IDEMPOTENCY_KEY_LENGTH, MAX_RESPONSE_SNIPPET_LENGTH, MAX_FRAME_SNIPPET_LENGTH, } from "./limits.js";
|