@astralform/js 5.0.0 → 6.0.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/README.md +8 -3
- package/dist/index.cjs +37 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -6
- package/dist/index.d.ts +35 -6
- package/dist/index.js +37 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -883,6 +883,20 @@ interface ConversationAsset {
|
|
|
883
883
|
* failed or the asset has no stored object.
|
|
884
884
|
*/
|
|
885
885
|
url?: string;
|
|
886
|
+
/**
|
|
887
|
+
* The asset's PERMANENT address. Authorization is resolved per request
|
|
888
|
+
* against the caller's live session, so unlike `url` it never expires — but
|
|
889
|
+
* it needs an `Authorization` header, which a browser will not attach to an
|
|
890
|
+
* `<img src>`. Store and link this one; render from `url`.
|
|
891
|
+
*/
|
|
892
|
+
contentUrl?: string;
|
|
893
|
+
/**
|
|
894
|
+
* A still to show for an asset the browser cannot draw from `url` alone.
|
|
895
|
+
* Present only for video: an `<img src>` pointed at an mp4 renders nothing,
|
|
896
|
+
* so a video row would otherwise have no thumbnail. Signed and expiring
|
|
897
|
+
* exactly like `url` — display, not identity, so never store it.
|
|
898
|
+
*/
|
|
899
|
+
posterUrl?: string;
|
|
886
900
|
createdAt: string;
|
|
887
901
|
}
|
|
888
902
|
|
|
@@ -1386,11 +1400,12 @@ declare class ChatSession {
|
|
|
1386
1400
|
/**
|
|
1387
1401
|
* Rename a conversation, server first.
|
|
1388
1402
|
*
|
|
1389
|
-
*
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1403
|
+
* Server first, like the delete below: a failed rename written locally would
|
|
1404
|
+
* leave the sidebar showing a title the server never accepted, and nothing
|
|
1405
|
+
* refetches a conversation that is already in the loaded list. (The delete
|
|
1406
|
+
* used to be the counter-example here — it dropped the row whatever the
|
|
1407
|
+
* server said, on the theory that a failed one was self-correcting. It was
|
|
1408
|
+
* not: the row stayed deleted locally and alive on the server.)
|
|
1394
1409
|
*
|
|
1395
1410
|
* Mirrors the `title_generated` path: the entry in `conversations` is
|
|
1396
1411
|
* mutated in place, which is what every consumer of the list reads.
|
|
@@ -1430,7 +1445,21 @@ declare class LLMNotConfiguredError extends AstralformError {
|
|
|
1430
1445
|
constructor(message?: string);
|
|
1431
1446
|
}
|
|
1432
1447
|
declare class ServerError extends AstralformError {
|
|
1433
|
-
|
|
1448
|
+
/**
|
|
1449
|
+
* The HTTP status, when this came from a response.
|
|
1450
|
+
*
|
|
1451
|
+
* Every status except 401 and 429 collapses into this one class, so without
|
|
1452
|
+
* it a caller cannot tell "the thing is already gone" (404) from "the server
|
|
1453
|
+
* broke" (500) or "this is not yours" (403) — the message is the only other
|
|
1454
|
+
* signal and it is prose. `deleteConversation` is the case that forced it:
|
|
1455
|
+
* it read EVERY failure as already-deleted and dropped the conversation
|
|
1456
|
+
* locally regardless, so a 500 looked exactly like success and the row came
|
|
1457
|
+
* back on the next device.
|
|
1458
|
+
*
|
|
1459
|
+
* Undefined when a ServerError is constructed without a response.
|
|
1460
|
+
*/
|
|
1461
|
+
readonly status?: number;
|
|
1462
|
+
constructor(message?: string, status?: number);
|
|
1434
1463
|
}
|
|
1435
1464
|
declare class ConnectionError extends AstralformError {
|
|
1436
1465
|
constructor(message?: string);
|
package/dist/index.d.ts
CHANGED
|
@@ -883,6 +883,20 @@ interface ConversationAsset {
|
|
|
883
883
|
* failed or the asset has no stored object.
|
|
884
884
|
*/
|
|
885
885
|
url?: string;
|
|
886
|
+
/**
|
|
887
|
+
* The asset's PERMANENT address. Authorization is resolved per request
|
|
888
|
+
* against the caller's live session, so unlike `url` it never expires — but
|
|
889
|
+
* it needs an `Authorization` header, which a browser will not attach to an
|
|
890
|
+
* `<img src>`. Store and link this one; render from `url`.
|
|
891
|
+
*/
|
|
892
|
+
contentUrl?: string;
|
|
893
|
+
/**
|
|
894
|
+
* A still to show for an asset the browser cannot draw from `url` alone.
|
|
895
|
+
* Present only for video: an `<img src>` pointed at an mp4 renders nothing,
|
|
896
|
+
* so a video row would otherwise have no thumbnail. Signed and expiring
|
|
897
|
+
* exactly like `url` — display, not identity, so never store it.
|
|
898
|
+
*/
|
|
899
|
+
posterUrl?: string;
|
|
886
900
|
createdAt: string;
|
|
887
901
|
}
|
|
888
902
|
|
|
@@ -1386,11 +1400,12 @@ declare class ChatSession {
|
|
|
1386
1400
|
/**
|
|
1387
1401
|
* Rename a conversation, server first.
|
|
1388
1402
|
*
|
|
1389
|
-
*
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1403
|
+
* Server first, like the delete below: a failed rename written locally would
|
|
1404
|
+
* leave the sidebar showing a title the server never accepted, and nothing
|
|
1405
|
+
* refetches a conversation that is already in the loaded list. (The delete
|
|
1406
|
+
* used to be the counter-example here — it dropped the row whatever the
|
|
1407
|
+
* server said, on the theory that a failed one was self-correcting. It was
|
|
1408
|
+
* not: the row stayed deleted locally and alive on the server.)
|
|
1394
1409
|
*
|
|
1395
1410
|
* Mirrors the `title_generated` path: the entry in `conversations` is
|
|
1396
1411
|
* mutated in place, which is what every consumer of the list reads.
|
|
@@ -1430,7 +1445,21 @@ declare class LLMNotConfiguredError extends AstralformError {
|
|
|
1430
1445
|
constructor(message?: string);
|
|
1431
1446
|
}
|
|
1432
1447
|
declare class ServerError extends AstralformError {
|
|
1433
|
-
|
|
1448
|
+
/**
|
|
1449
|
+
* The HTTP status, when this came from a response.
|
|
1450
|
+
*
|
|
1451
|
+
* Every status except 401 and 429 collapses into this one class, so without
|
|
1452
|
+
* it a caller cannot tell "the thing is already gone" (404) from "the server
|
|
1453
|
+
* broke" (500) or "this is not yours" (403) — the message is the only other
|
|
1454
|
+
* signal and it is prose. `deleteConversation` is the case that forced it:
|
|
1455
|
+
* it read EVERY failure as already-deleted and dropped the conversation
|
|
1456
|
+
* locally regardless, so a 500 looked exactly like success and the row came
|
|
1457
|
+
* back on the next device.
|
|
1458
|
+
*
|
|
1459
|
+
* Undefined when a ServerError is constructed without a response.
|
|
1460
|
+
*/
|
|
1461
|
+
readonly status?: number;
|
|
1462
|
+
constructor(message?: string, status?: number);
|
|
1434
1463
|
}
|
|
1435
1464
|
declare class ConnectionError extends AstralformError {
|
|
1436
1465
|
constructor(message?: string);
|
package/dist/index.js
CHANGED
|
@@ -26,9 +26,10 @@ var LLMNotConfiguredError = class extends AstralformError {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
var ServerError = class extends AstralformError {
|
|
29
|
-
constructor(message = "Internal server error") {
|
|
29
|
+
constructor(message = "Internal server error", status) {
|
|
30
30
|
super(message, "server_error");
|
|
31
31
|
this.name = "ServerError";
|
|
32
|
+
if (status !== void 0) Object.assign(this, { status });
|
|
32
33
|
}
|
|
33
34
|
};
|
|
34
35
|
var ConnectionError = class extends AstralformError {
|
|
@@ -58,6 +59,16 @@ function generateId() {
|
|
|
58
59
|
return v.toString(16);
|
|
59
60
|
});
|
|
60
61
|
}
|
|
62
|
+
function snakeToCamel(str) {
|
|
63
|
+
return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
64
|
+
}
|
|
65
|
+
function camelizeKeys(obj) {
|
|
66
|
+
const result = {};
|
|
67
|
+
for (const key of Object.keys(obj)) {
|
|
68
|
+
result[snakeToCamel(key)] = obj[key];
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
61
72
|
|
|
62
73
|
// src/rate-limit.ts
|
|
63
74
|
var DEFAULT_MESSAGE = "Rate limit exceeded";
|
|
@@ -208,7 +219,7 @@ async function* streamJobSSE(options) {
|
|
|
208
219
|
case 429:
|
|
209
220
|
throw createRateLimitErrorFromHttp(response, rawText);
|
|
210
221
|
default:
|
|
211
|
-
throw new ServerError(text || `HTTP ${response.status}
|
|
222
|
+
throw new ServerError(text || `HTTP ${response.status}`, response.status);
|
|
212
223
|
}
|
|
213
224
|
}
|
|
214
225
|
if (!response.body) {
|
|
@@ -479,7 +490,10 @@ var AstralformClient = class {
|
|
|
479
490
|
throw createRateLimitErrorFromHttp(response, text);
|
|
480
491
|
default: {
|
|
481
492
|
const safeText = text ? sanitizeErrorText(text) : "";
|
|
482
|
-
throw new ServerError(
|
|
493
|
+
throw new ServerError(
|
|
494
|
+
safeText || `HTTP ${response.status}`,
|
|
495
|
+
response.status
|
|
496
|
+
);
|
|
483
497
|
}
|
|
484
498
|
}
|
|
485
499
|
}
|
|
@@ -516,13 +530,7 @@ var AstralformClient = class {
|
|
|
516
530
|
const safeLimit = Math.max(1, Math.min(200, Math.floor(Number(limit))));
|
|
517
531
|
const safeOffset = Math.max(0, Math.floor(Number(offset)));
|
|
518
532
|
const raw = await this.get(`/v1/conversations?limit=${safeLimit}&offset=${safeOffset}`);
|
|
519
|
-
return raw.map((c) => (
|
|
520
|
-
id: c.id,
|
|
521
|
-
title: c.title,
|
|
522
|
-
messageCount: c.message_count,
|
|
523
|
-
createdAt: c.created_at,
|
|
524
|
-
updatedAt: c.updated_at
|
|
525
|
-
}));
|
|
533
|
+
return raw.map((c) => camelizeKeys(c));
|
|
526
534
|
}
|
|
527
535
|
async getMessages(conversationId) {
|
|
528
536
|
const raw = await this.get(`/v1/conversations/${encodeURIComponent(conversationId)}/messages`);
|
|
@@ -547,13 +555,7 @@ var AstralformClient = class {
|
|
|
547
555
|
*/
|
|
548
556
|
async renameConversation(id, title) {
|
|
549
557
|
const c = await this.patch(`/v1/conversations/${encodeURIComponent(id)}`, { title });
|
|
550
|
-
return
|
|
551
|
-
id: c.id,
|
|
552
|
-
title: c.title,
|
|
553
|
-
messageCount: c.message_count,
|
|
554
|
-
createdAt: c.created_at,
|
|
555
|
-
updatedAt: c.updated_at
|
|
556
|
-
};
|
|
558
|
+
return camelizeKeys(c);
|
|
557
559
|
}
|
|
558
560
|
async deleteConversation(id) {
|
|
559
561
|
await this.del(`/v1/conversations/${encodeURIComponent(id)}`);
|
|
@@ -566,14 +568,7 @@ var AstralformClient = class {
|
|
|
566
568
|
*/
|
|
567
569
|
async getAgents() {
|
|
568
570
|
const raw = await this.get("/v1/agents");
|
|
569
|
-
return raw.map((a) => (
|
|
570
|
-
name: a.name,
|
|
571
|
-
displayName: a.display_name,
|
|
572
|
-
description: a.description,
|
|
573
|
-
isOrchestrator: a.is_orchestrator,
|
|
574
|
-
isEnabled: a.is_enabled,
|
|
575
|
-
avatarUrl: a.avatar_url
|
|
576
|
-
}));
|
|
571
|
+
return raw.map((a) => camelizeKeys(a));
|
|
577
572
|
}
|
|
578
573
|
/**
|
|
579
574
|
* List the models the caller may pick this turn — expanded from the curated
|
|
@@ -600,12 +595,7 @@ var AstralformClient = class {
|
|
|
600
595
|
}
|
|
601
596
|
async getSkills() {
|
|
602
597
|
const raw = await this.get("/v1/skills");
|
|
603
|
-
return raw.map((s) => (
|
|
604
|
-
name: s.name,
|
|
605
|
-
displayName: s.display_name,
|
|
606
|
-
description: s.description,
|
|
607
|
-
isEnabled: s.is_enabled
|
|
608
|
-
}));
|
|
598
|
+
return raw.map((s) => camelizeKeys(s));
|
|
609
599
|
}
|
|
610
600
|
async getConversationEvents(conversationId, jobId) {
|
|
611
601
|
let url = `/v1/conversations/${encodeURIComponent(conversationId)}/events`;
|
|
@@ -676,6 +666,11 @@ var AstralformClient = class {
|
|
|
676
666
|
// undefined so it matches the `url?: string` type and consumers that
|
|
677
667
|
// check `!== undefined` never receive a null.
|
|
678
668
|
url: raw.url ?? void 0,
|
|
669
|
+
// This mapping is an ALLOWLIST — a field the API returns and this
|
|
670
|
+
// function does not name is dropped silently, and no type error says so.
|
|
671
|
+
// `content_url` shipped that way and was invisible to every consumer.
|
|
672
|
+
contentUrl: raw.content_url ?? void 0,
|
|
673
|
+
posterUrl: raw.poster_url ?? void 0,
|
|
679
674
|
createdAt: raw.created_at
|
|
680
675
|
};
|
|
681
676
|
}
|
|
@@ -717,13 +712,7 @@ var AstralformClient = class {
|
|
|
717
712
|
// sending them in API-key mode yields 401.
|
|
718
713
|
async listTeams() {
|
|
719
714
|
const raw = await this.get("/v1/teams");
|
|
720
|
-
return raw.map((t) => (
|
|
721
|
-
id: t.id,
|
|
722
|
-
name: t.name,
|
|
723
|
-
slug: t.slug,
|
|
724
|
-
isDefault: t.is_default,
|
|
725
|
-
role: t.role
|
|
726
|
-
}));
|
|
715
|
+
return raw.map((t) => camelizeKeys(t));
|
|
727
716
|
}
|
|
728
717
|
/**
|
|
729
718
|
* List the team-level agents (formerly "projects") the signed-in user can
|
|
@@ -732,14 +721,7 @@ var AstralformClient = class {
|
|
|
732
721
|
*/
|
|
733
722
|
async listAgents(teamId) {
|
|
734
723
|
const raw = await this.get(`/v1/teams/${encodeURIComponent(teamId)}/agents`);
|
|
735
|
-
return raw.map((a) => (
|
|
736
|
-
id: a.id,
|
|
737
|
-
name: a.name,
|
|
738
|
-
teamId: a.team_id,
|
|
739
|
-
createdAt: a.created_at,
|
|
740
|
-
updatedAt: a.updated_at,
|
|
741
|
-
avatarUrl: a.avatar_url ?? null
|
|
742
|
-
}));
|
|
724
|
+
return raw.map((a) => camelizeKeys(a));
|
|
743
725
|
}
|
|
744
726
|
// --- Jobs API ---
|
|
745
727
|
async createJob(request) {
|
|
@@ -776,13 +758,7 @@ var AstralformClient = class {
|
|
|
776
758
|
};
|
|
777
759
|
if (request.comment != null) body.comment = request.comment;
|
|
778
760
|
const raw = await this.post(`/v1/jobs/${encodeURIComponent(jobId)}/feedback`, body);
|
|
779
|
-
return
|
|
780
|
-
id: raw.id,
|
|
781
|
-
jobId: raw.job_id,
|
|
782
|
-
rating: raw.rating,
|
|
783
|
-
comment: raw.comment,
|
|
784
|
-
createdAt: raw.created_at
|
|
785
|
-
};
|
|
761
|
+
return camelizeKeys(raw);
|
|
786
762
|
}
|
|
787
763
|
async getActiveJob(conversationId) {
|
|
788
764
|
const raw = await this.get(`/v1/conversations/${encodeURIComponent(conversationId)}/active-job`);
|
|
@@ -2170,11 +2146,12 @@ var ChatSession = class {
|
|
|
2170
2146
|
/**
|
|
2171
2147
|
* Rename a conversation, server first.
|
|
2172
2148
|
*
|
|
2173
|
-
*
|
|
2174
|
-
*
|
|
2175
|
-
*
|
|
2176
|
-
*
|
|
2177
|
-
*
|
|
2149
|
+
* Server first, like the delete below: a failed rename written locally would
|
|
2150
|
+
* leave the sidebar showing a title the server never accepted, and nothing
|
|
2151
|
+
* refetches a conversation that is already in the loaded list. (The delete
|
|
2152
|
+
* used to be the counter-example here — it dropped the row whatever the
|
|
2153
|
+
* server said, on the theory that a failed one was self-correcting. It was
|
|
2154
|
+
* not: the row stayed deleted locally and alive on the server.)
|
|
2178
2155
|
*
|
|
2179
2156
|
* Mirrors the `title_generated` path: the entry in `conversations` is
|
|
2180
2157
|
* mutated in place, which is what every consumer of the list reads.
|
|
@@ -2190,7 +2167,8 @@ var ChatSession = class {
|
|
|
2190
2167
|
async deleteConversation(id) {
|
|
2191
2168
|
try {
|
|
2192
2169
|
await this.client.deleteConversation(id);
|
|
2193
|
-
} catch {
|
|
2170
|
+
} catch (err) {
|
|
2171
|
+
if (!(err instanceof ServerError) || err.status !== 404) throw err;
|
|
2194
2172
|
}
|
|
2195
2173
|
await this.storage.deleteConversation(id);
|
|
2196
2174
|
if (this.serverConversationIds.delete(id)) {
|