assistant-cloud 0.1.43 → 0.2.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 +40 -0
- package/dist/AssistantCloud.d.ts +4 -0
- package/dist/AssistantCloud.d.ts.map +1 -1
- package/dist/AssistantCloud.js +11 -5
- package/dist/AssistantCloud.js.map +1 -1
- package/dist/AssistantCloudAPI.d.ts +17 -1
- package/dist/AssistantCloudAPI.d.ts.map +1 -1
- package/dist/AssistantCloudAPI.js +20 -9
- package/dist/AssistantCloudAPI.js.map +1 -1
- package/dist/AssistantCloudAuthStrategy.d.ts +4 -1
- package/dist/AssistantCloudAuthStrategy.d.ts.map +1 -1
- package/dist/AssistantCloudAuthStrategy.js +11 -1
- package/dist/AssistantCloudAuthStrategy.js.map +1 -1
- package/dist/AssistantCloudEvents.d.ts +32 -0
- package/dist/AssistantCloudEvents.d.ts.map +1 -0
- package/dist/AssistantCloudEvents.js +115 -0
- package/dist/AssistantCloudEvents.js.map +1 -0
- package/dist/AssistantCloudFiles.d.ts +12 -1
- package/dist/AssistantCloudFiles.d.ts.map +1 -1
- package/dist/AssistantCloudFiles.js +13 -1
- package/dist/AssistantCloudFiles.js.map +1 -1
- package/dist/AssistantCloudRuns.d.ts +11 -0
- package/dist/AssistantCloudRuns.d.ts.map +1 -1
- package/dist/AssistantCloudRuns.js.map +1 -1
- package/dist/AssistantCloudScores.d.ts +27 -0
- package/dist/AssistantCloudScores.d.ts.map +1 -0
- package/dist/AssistantCloudScores.js +30 -0
- package/dist/AssistantCloudScores.js.map +1 -0
- package/dist/AssistantCloudThreadMessages.d.ts +11 -1
- package/dist/AssistantCloudThreadMessages.d.ts.map +1 -1
- package/dist/AssistantCloudThreadMessages.js +12 -1
- package/dist/AssistantCloudThreadMessages.js.map +1 -1
- package/dist/AssistantCloudThreads.d.ts +8 -0
- package/dist/AssistantCloudThreads.d.ts.map +1 -1
- package/dist/AssistantCloudThreads.js +9 -1
- package/dist/AssistantCloudThreads.js.map +1 -1
- package/dist/CloudMessagePersistence.d.ts +4 -0
- package/dist/CloudMessagePersistence.d.ts.map +1 -1
- package/dist/CloudMessagePersistence.js +38 -11
- package/dist/CloudMessagePersistence.js.map +1 -1
- package/dist/cloudResponse.d.ts +4 -1
- package/dist/cloudResponse.d.ts.map +1 -1
- package/dist/cloudResponse.js +14 -1
- package/dist/cloudResponse.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -2
- package/dist/runTelemetry.d.ts +70 -7
- package/dist/runTelemetry.d.ts.map +1 -1
- package/dist/runTelemetry.js +144 -1
- package/dist/runTelemetry.js.map +1 -1
- package/dist/telemetry/index.d.ts +32 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +62 -0
- package/dist/telemetry/index.js.map +1 -0
- package/package.json +28 -4
- package/src/AssistantCloud.ts +20 -8
- package/src/AssistantCloudAPI.ts +42 -10
- package/src/AssistantCloudAuthStrategy.ts +13 -0
- package/src/AssistantCloudEvents.test.ts +124 -0
- package/src/AssistantCloudEvents.ts +185 -0
- package/src/AssistantCloudFiles.test.ts +59 -0
- package/src/AssistantCloudFiles.ts +39 -0
- package/src/AssistantCloudRuns.ts +12 -1
- package/src/AssistantCloudScores.test.ts +113 -0
- package/src/AssistantCloudScores.ts +61 -0
- package/src/AssistantCloudThreadMessages.test.ts +51 -0
- package/src/AssistantCloudThreadMessages.ts +33 -0
- package/src/AssistantCloudThreads.test.ts +21 -0
- package/src/AssistantCloudThreads.ts +21 -0
- package/src/CloudMessagePersistence.ts +48 -16
- package/src/cloudResponse.test.ts +34 -1
- package/src/cloudResponse.ts +30 -0
- package/src/index.ts +24 -1
- package/src/runTelemetry.test.ts +325 -0
- package/src/runTelemetry.ts +257 -6
- package/src/telemetry/index.test.ts +252 -0
- package/src/telemetry/index.ts +98 -0
- package/src/tests/AssistantCloud.test.ts +22 -0
- package/src/tests/AssistantCloudAPI.test.ts +44 -4
- package/src/tests/AssistantCloudAuthStrategy.test.ts +71 -2
- package/src/tests/CloudMessagePersistence.test.ts +115 -0
package/README.md
CHANGED
|
@@ -31,6 +31,46 @@ export function Provider({ children }: { children: React.ReactNode }) {
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Server telemetry
|
|
35
|
+
|
|
36
|
+
Send AI SDK 7 GenAI spans to Assistant Cloud from a Next.js `instrumentation.ts` file. The `assistant-cloud/telemetry` entry needs the OpenTelemetry packages installed next to it:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
npm i @vercel/otel @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-http
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { registerOTel } from "@vercel/otel";
|
|
44
|
+
import {
|
|
45
|
+
createAssistantCloudSpanProcessor,
|
|
46
|
+
createAssistantCloudTraceExporter,
|
|
47
|
+
} from "assistant-cloud/telemetry";
|
|
48
|
+
|
|
49
|
+
export function register() {
|
|
50
|
+
registerOTel({
|
|
51
|
+
serviceName: "my-app",
|
|
52
|
+
spanProcessors: [
|
|
53
|
+
"auto",
|
|
54
|
+
createAssistantCloudSpanProcessor(
|
|
55
|
+
createAssistantCloudTraceExporter({
|
|
56
|
+
apiKey: process.env.ASSISTANT_API_KEY!,
|
|
57
|
+
}),
|
|
58
|
+
),
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Pass the active server trace ID to the browser with `messageMetadata` in the route that calls `streamText`:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { withAssistantCloudTraceMetadata } from "assistant-cloud/telemetry";
|
|
68
|
+
|
|
69
|
+
return result.toUIMessageStreamResponse({
|
|
70
|
+
messageMetadata: withAssistantCloudTraceMetadata(),
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
34
74
|
## Authentication
|
|
35
75
|
|
|
36
76
|
| Mode | Required fields | Use case |
|
package/dist/AssistantCloud.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { AssistantCloudAuthTokens } from "./AssistantCloudAuthTokens.js";
|
|
|
4
4
|
import { AssistantCloudThreads } from "./AssistantCloudThreads.js";
|
|
5
5
|
import { AssistantCloudProjects } from "./AssistantCloudProjects.js";
|
|
6
6
|
import { AssistantCloudFiles } from "./AssistantCloudFiles.js";
|
|
7
|
+
import { AssistantCloudEvents } from "./AssistantCloudEvents.js";
|
|
8
|
+
import { AssistantCloudScores } from "./AssistantCloudScores.js";
|
|
7
9
|
//#region src/AssistantCloud.d.ts
|
|
8
10
|
declare class AssistantCloud {
|
|
9
11
|
readonly threads: AssistantCloudThreads;
|
|
@@ -13,6 +15,8 @@ declare class AssistantCloud {
|
|
|
13
15
|
};
|
|
14
16
|
readonly runs: AssistantCloudRuns;
|
|
15
17
|
readonly files: AssistantCloudFiles;
|
|
18
|
+
readonly events: AssistantCloudEvents;
|
|
19
|
+
readonly scores: AssistantCloudScores;
|
|
16
20
|
readonly telemetry: AssistantCloudTelemetryConfig;
|
|
17
21
|
constructor(config: AssistantCloudConfig);
|
|
18
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloud.d.ts","names":[],"sources":["../src/AssistantCloud.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"AssistantCloud.d.ts","names":[],"sources":["../src/AssistantCloud.ts"],"mappings":";;;;;;;;;cAaa;WACK,SAAO;WACP,UAAQ;WACR;;;WACA,MAAI;WACJ,OAAK;WACL,QAAM;WACN,QAAM;WACN,WAAW;EAEf,YAAA,QAAQ"}
|
package/dist/AssistantCloud.js
CHANGED
|
@@ -4,6 +4,8 @@ import { AssistantCloudThreads } from "./AssistantCloudThreads.js";
|
|
|
4
4
|
import { AssistantCloudProjects } from "./AssistantCloudProjects.js";
|
|
5
5
|
import { AssistantCloudRuns } from "./AssistantCloudRuns.js";
|
|
6
6
|
import { AssistantCloudFiles } from "./AssistantCloudFiles.js";
|
|
7
|
+
import { AssistantCloudEvents } from "./AssistantCloudEvents.js";
|
|
8
|
+
import { AssistantCloudScores } from "./AssistantCloudScores.js";
|
|
7
9
|
//#region src/AssistantCloud.ts
|
|
8
10
|
var AssistantCloud = class {
|
|
9
11
|
threads;
|
|
@@ -11,19 +13,23 @@ var AssistantCloud = class {
|
|
|
11
13
|
auth;
|
|
12
14
|
runs;
|
|
13
15
|
files;
|
|
16
|
+
events;
|
|
17
|
+
scores;
|
|
14
18
|
telemetry;
|
|
15
19
|
constructor(config) {
|
|
16
20
|
const api = new AssistantCloudAPI(config);
|
|
17
|
-
this.threads = new AssistantCloudThreads(api);
|
|
18
|
-
this.projects = new AssistantCloudProjects(api);
|
|
19
|
-
this.auth = { tokens: new AssistantCloudAuthTokens(api) };
|
|
20
|
-
this.runs = new AssistantCloudRuns(api);
|
|
21
|
-
this.files = new AssistantCloudFiles(api);
|
|
22
21
|
const t = config.telemetry;
|
|
23
22
|
this.telemetry = t === false ? { enabled: false } : t === true || t === void 0 ? { enabled: true } : {
|
|
24
23
|
...t,
|
|
25
24
|
enabled: t.enabled !== false
|
|
26
25
|
};
|
|
26
|
+
this.threads = new AssistantCloudThreads(api);
|
|
27
|
+
this.projects = new AssistantCloudProjects(api);
|
|
28
|
+
this.auth = { tokens: new AssistantCloudAuthTokens(api) };
|
|
29
|
+
this.runs = new AssistantCloudRuns(api);
|
|
30
|
+
this.files = new AssistantCloudFiles(api);
|
|
31
|
+
this.events = new AssistantCloudEvents(api, () => this.telemetry.enabled !== false && this.telemetry.events !== false);
|
|
32
|
+
this.scores = new AssistantCloudScores(api);
|
|
27
33
|
}
|
|
28
34
|
};
|
|
29
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloud.js","names":[],"sources":["../src/AssistantCloud.ts"],"sourcesContent":["import {\n AssistantCloudAPI,\n type AssistantCloudConfig,\n type AssistantCloudTelemetryConfig,\n} from \"./AssistantCloudAPI\";\nimport { AssistantCloudAuthTokens } from \"./AssistantCloudAuthTokens\";\nimport { AssistantCloudProjects } from \"./AssistantCloudProjects\";\nimport { AssistantCloudRuns } from \"./AssistantCloudRuns\";\nimport { AssistantCloudThreads } from \"./AssistantCloudThreads\";\nimport { AssistantCloudFiles } from \"./AssistantCloudFiles\";\n\nexport class AssistantCloud {\n public readonly threads;\n public readonly projects;\n public readonly auth;\n public readonly runs;\n public readonly files;\n public readonly telemetry: AssistantCloudTelemetryConfig;\n\n constructor(config: AssistantCloudConfig) {\n const api = new AssistantCloudAPI(config);\n this.threads = new AssistantCloudThreads(api);\n this.projects = new AssistantCloudProjects(api);\n this.auth = {\n tokens: new AssistantCloudAuthTokens(api),\n };\n this.runs = new AssistantCloudRuns(api);\n this.files = new AssistantCloudFiles(api);\n
|
|
1
|
+
{"version":3,"file":"AssistantCloud.js","names":[],"sources":["../src/AssistantCloud.ts"],"sourcesContent":["import {\n AssistantCloudAPI,\n type AssistantCloudConfig,\n type AssistantCloudTelemetryConfig,\n} from \"./AssistantCloudAPI\";\nimport { AssistantCloudAuthTokens } from \"./AssistantCloudAuthTokens\";\nimport { AssistantCloudProjects } from \"./AssistantCloudProjects\";\nimport { AssistantCloudRuns } from \"./AssistantCloudRuns\";\nimport { AssistantCloudThreads } from \"./AssistantCloudThreads\";\nimport { AssistantCloudFiles } from \"./AssistantCloudFiles\";\nimport { AssistantCloudEvents } from \"./AssistantCloudEvents\";\nimport { AssistantCloudScores } from \"./AssistantCloudScores\";\n\nexport class AssistantCloud {\n public readonly threads;\n public readonly projects;\n public readonly auth;\n public readonly runs;\n public readonly files;\n public readonly events;\n public readonly scores;\n public readonly telemetry: AssistantCloudTelemetryConfig;\n\n constructor(config: AssistantCloudConfig) {\n const api = new AssistantCloudAPI(config);\n const t = config.telemetry;\n this.telemetry =\n t === false\n ? { enabled: false }\n : t === true || t === undefined\n ? { enabled: true }\n : {\n ...t,\n enabled: t.enabled !== false,\n };\n\n this.threads = new AssistantCloudThreads(api);\n this.projects = new AssistantCloudProjects(api);\n this.auth = {\n tokens: new AssistantCloudAuthTokens(api),\n };\n this.runs = new AssistantCloudRuns(api);\n this.files = new AssistantCloudFiles(api);\n this.events = new AssistantCloudEvents(\n api,\n () => this.telemetry.enabled !== false && this.telemetry.events !== false,\n );\n this.scores = new AssistantCloudScores(api);\n }\n}\n"],"mappings":";;;;;;;;;AAaA,IAAa,iBAAb,MAA4B;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,QAA8B;EACxC,MAAM,MAAM,IAAI,kBAAkB,MAAM;EACxC,MAAM,IAAI,OAAO;EACjB,KAAK,YACH,MAAM,QACF,EAAE,SAAS,MAAM,IACjB,MAAM,QAAQ,MAAM,KAAA,IAClB,EAAE,SAAS,KAAK,IAChB;GACE,GAAG;GACH,SAAS,EAAE,YAAY;EACzB;EAER,KAAK,UAAU,IAAI,sBAAsB,GAAG;EAC5C,KAAK,WAAW,IAAI,uBAAuB,GAAG;EAC9C,KAAK,OAAO,EACV,QAAQ,IAAI,yBAAyB,GAAG,EAC1C;EACA,KAAK,OAAO,IAAI,mBAAmB,GAAG;EACtC,KAAK,QAAQ,IAAI,oBAAoB,GAAG;EACxC,KAAK,SAAS,IAAI,qBAChB,WACM,KAAK,UAAU,YAAY,SAAS,KAAK,UAAU,WAAW,KACtE;EACA,KAAK,SAAS,IAAI,qBAAqB,GAAG;CAC5C;AACF"}
|
|
@@ -2,7 +2,20 @@ import { AssistantCloudAuthStrategy } from "./AssistantCloudAuthStrategy.js";
|
|
|
2
2
|
import { AssistantCloudRunReport } from "./AssistantCloudRuns.js";
|
|
3
3
|
//#region src/AssistantCloudAPI.d.ts
|
|
4
4
|
type AssistantCloudTelemetryConfig = {
|
|
5
|
+
/**
|
|
6
|
+
* Enables Assistant Cloud telemetry. Defaults to `true`. Set to `false` to
|
|
7
|
+
* disable both run reports and engagement events.
|
|
8
|
+
*/
|
|
5
9
|
enabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Enables Assistant Cloud engagement events. Defaults to `true` when
|
|
12
|
+
* telemetry is enabled. Set to `false` to keep run reports while disabling
|
|
13
|
+
* engagement events.
|
|
14
|
+
*/
|
|
15
|
+
events?: boolean;
|
|
16
|
+
release?: string;
|
|
17
|
+
environment?: string;
|
|
18
|
+
tags?: string[];
|
|
6
19
|
/**
|
|
7
20
|
* Called before each telemetry report is sent.
|
|
8
21
|
* Return a modified report to enrich it (e.g. add `model_id`),
|
|
@@ -37,13 +50,16 @@ type AssistantCloudConfig = ({
|
|
|
37
50
|
};
|
|
38
51
|
declare class CloudAPIError extends Error {
|
|
39
52
|
readonly status: number;
|
|
40
|
-
|
|
53
|
+
readonly code?: string;
|
|
54
|
+
readonly details?: Record<string, unknown>;
|
|
55
|
+
constructor(message: string, status: number, code?: string, details?: Record<string, unknown>);
|
|
41
56
|
}
|
|
42
57
|
type MakeRequestOptions = {
|
|
43
58
|
method?: "POST" | "PUT" | "DELETE" | undefined;
|
|
44
59
|
headers?: Record<string, string> | undefined;
|
|
45
60
|
query?: Record<string, string | number | boolean> | undefined;
|
|
46
61
|
body?: object | undefined;
|
|
62
|
+
keepalive?: boolean | undefined;
|
|
47
63
|
};
|
|
48
64
|
declare class AssistantCloudAPI {
|
|
49
65
|
_auth: AssistantCloudAuthStrategy;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloudAPI.d.ts","names":[],"sources":["../src/AssistantCloudAPI.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"AssistantCloudAPI.d.ts","names":[],"sources":["../src/AssistantCloudAPI.ts"],"mappings":";;;KASY;;;;;EAKV;;;;;;EAMA;EACA;EACA;EACA;;;;;;EAMA,gBACE,QAAQ,4BACL;;KAGK;EAEN;EACA,iBAAiB;;EAGjB;EACA;EACA;EACA;;EAGA;EACA;;;;;;;;;;;;;EAcJ,sBAAsB;;cAGX,sBAAsB;WACjB;WACA;WACA,UAAU;EAGxB,YAAA,iBACA,gBACA,eACA,UAAU;;KAUT;EACH;EACA,UAAU;EACV,QAAQ;EACR;EACA;;cAGW;EACJ,OAAO;EACP;EAEK,YAAA,QAAQ;EAuBP,kBAAc;EAId,eACX,kBACA,UAAS,qBAAuB,QAAA;EAiErB,YAAY,kBAAkB,UAAS,qBAAuB"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { AssistantCloudAPIKeyAuthStrategy, AssistantCloudAnonymousAuthStrategy, AssistantCloudJWTAuthStrategy } from "./AssistantCloudAuthStrategy.js";
|
|
1
|
+
import { AssistantCloudAPIKeyAuthStrategy, AssistantCloudAnonymousAuthStrategy, AssistantCloudJWTAuthStrategy, normalizeBaseUrl } from "./AssistantCloudAuthStrategy.js";
|
|
2
2
|
//#region src/AssistantCloudAPI.ts
|
|
3
3
|
var CloudAPIError = class extends Error {
|
|
4
4
|
status;
|
|
5
|
-
|
|
5
|
+
code;
|
|
6
|
+
details;
|
|
7
|
+
constructor(message, status, code, details) {
|
|
6
8
|
super(message);
|
|
7
9
|
this.status = status;
|
|
10
|
+
if (code !== void 0) this.code = code;
|
|
11
|
+
if (details !== void 0) this.details = details;
|
|
8
12
|
this.name = "CloudAPIError";
|
|
9
13
|
}
|
|
10
14
|
};
|
|
11
|
-
const normalizeBaseUrl = (baseUrl) => {
|
|
12
|
-
if (!baseUrl || !baseUrl.endsWith("/")) return baseUrl;
|
|
13
|
-
return baseUrl.slice(0, -1);
|
|
14
|
-
};
|
|
15
15
|
var AssistantCloudAPI = class {
|
|
16
16
|
_auth;
|
|
17
17
|
_baseUrl;
|
|
@@ -49,17 +49,28 @@ var AssistantCloudAPI = class {
|
|
|
49
49
|
const response = await fetch(url, {
|
|
50
50
|
method: options.method ?? "GET",
|
|
51
51
|
headers,
|
|
52
|
-
body: options.body ? JSON.stringify(options.body) : null
|
|
52
|
+
body: options.body ? JSON.stringify(options.body) : null,
|
|
53
|
+
...options.keepalive ? { keepalive: true } : {}
|
|
53
54
|
});
|
|
54
55
|
this._auth.readAuthHeaders(response.headers);
|
|
55
56
|
if (!response.ok) {
|
|
56
57
|
const text = await response.text();
|
|
57
58
|
let message;
|
|
59
|
+
let code;
|
|
60
|
+
let details;
|
|
58
61
|
try {
|
|
59
62
|
const body = JSON.parse(text);
|
|
60
|
-
if (typeof body
|
|
63
|
+
if (typeof body === "object" && body !== null && !Array.isArray(body)) {
|
|
64
|
+
const record = body;
|
|
65
|
+
if (typeof record.message === "string" && record.message.length > 0) message = record.message;
|
|
66
|
+
if (typeof record.error === "string") {
|
|
67
|
+
code = record.error;
|
|
68
|
+
details = { ...record };
|
|
69
|
+
delete details.error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
61
72
|
} catch {}
|
|
62
|
-
throw new CloudAPIError(message ?? `Request failed with status ${response.status}, ${text}`, response.status);
|
|
73
|
+
throw new CloudAPIError(message ?? `Request failed with status ${response.status}, ${text}`, response.status, code, details);
|
|
63
74
|
}
|
|
64
75
|
return response;
|
|
65
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloudAPI.js","names":[],"sources":["../src/AssistantCloudAPI.ts"],"sourcesContent":["import {\n type AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n AssistantCloudAnonymousAuthStrategy,\n} from \"./AssistantCloudAuthStrategy\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nexport type AssistantCloudTelemetryConfig = {\n enabled?: boolean;\n /**\n * Called before each telemetry report is sent.\n * Return a modified report to enrich it (e.g. add `model_id`),\n * or return `null` to skip the report.\n */\n beforeReport?: (\n report: AssistantCloudRunReport,\n ) => AssistantCloudRunReport | null;\n};\n\nexport type AssistantCloudConfig = (\n | {\n baseUrl: string;\n authToken: () => Promise<string | null>;\n }\n | {\n baseUrl?: string;\n apiKey: string;\n userId: string;\n workspaceId: string;\n }\n | {\n baseUrl: string;\n anonymous: true;\n }\n) & {\n /**\n * Client-side run telemetry reporting. Default: `true`.\n *\n * When enabled, the SDK automatically reports run metadata (status, step\n * count, tool calls, and token usage) to Assistant Cloud after each\n * assistant message is saved. No message content is sent.\n *\n * - `true` / `undefined` — enabled with defaults\n * - `false` — disabled\n * - `{ beforeReport }` — enabled with a hook to enrich or filter reports\n */\n telemetry?: boolean | AssistantCloudTelemetryConfig;\n};\n\nexport class CloudAPIError extends Error {\n public readonly status: number;\n\n constructor(message: string
|
|
1
|
+
{"version":3,"file":"AssistantCloudAPI.js","names":[],"sources":["../src/AssistantCloudAPI.ts"],"sourcesContent":["import {\n type AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n AssistantCloudAnonymousAuthStrategy,\n normalizeBaseUrl,\n} from \"./AssistantCloudAuthStrategy\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nexport type AssistantCloudTelemetryConfig = {\n /**\n * Enables Assistant Cloud telemetry. Defaults to `true`. Set to `false` to\n * disable both run reports and engagement events.\n */\n enabled?: boolean;\n /**\n * Enables Assistant Cloud engagement events. Defaults to `true` when\n * telemetry is enabled. Set to `false` to keep run reports while disabling\n * engagement events.\n */\n events?: boolean;\n release?: string;\n environment?: string;\n tags?: string[];\n /**\n * Called before each telemetry report is sent.\n * Return a modified report to enrich it (e.g. add `model_id`),\n * or return `null` to skip the report.\n */\n beforeReport?: (\n report: AssistantCloudRunReport,\n ) => AssistantCloudRunReport | null;\n};\n\nexport type AssistantCloudConfig = (\n | {\n baseUrl: string;\n authToken: () => Promise<string | null>;\n }\n | {\n baseUrl?: string;\n apiKey: string;\n userId: string;\n workspaceId: string;\n }\n | {\n baseUrl: string;\n anonymous: true;\n }\n) & {\n /**\n * Client-side run telemetry reporting. Default: `true`.\n *\n * When enabled, the SDK automatically reports run metadata (status, step\n * count, tool calls, and token usage) to Assistant Cloud after each\n * assistant message is saved. No message content is sent.\n *\n * - `true` / `undefined` — enabled with defaults\n * - `false` — disabled\n * - `{ beforeReport }` — enabled with a hook to enrich or filter reports\n */\n telemetry?: boolean | AssistantCloudTelemetryConfig;\n};\n\nexport class CloudAPIError extends Error {\n public readonly status: number;\n public readonly code?: string;\n public readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n status: number,\n code?: string,\n details?: Record<string, unknown>,\n ) {\n super(message);\n this.status = status;\n if (code !== undefined) this.code = code;\n if (details !== undefined) this.details = details;\n this.name = \"CloudAPIError\";\n }\n}\n\ntype MakeRequestOptions = {\n method?: \"POST\" | \"PUT\" | \"DELETE\" | undefined;\n headers?: Record<string, string> | undefined;\n query?: Record<string, string | number | boolean> | undefined;\n body?: object | undefined;\n keepalive?: boolean | undefined;\n};\n\nexport class AssistantCloudAPI {\n public _auth: AssistantCloudAuthStrategy;\n public _baseUrl;\n\n constructor(config: AssistantCloudConfig) {\n if (\"authToken\" in config) {\n this._baseUrl = normalizeBaseUrl(config.baseUrl);\n this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);\n } else if (\"apiKey\" in config) {\n this._baseUrl = normalizeBaseUrl(\n config.baseUrl ?? \"https://backend.assistant-api.com\",\n );\n this._auth = new AssistantCloudAPIKeyAuthStrategy(\n config.apiKey,\n config.userId,\n config.workspaceId,\n );\n } else if (\"anonymous\" in config) {\n this._baseUrl = normalizeBaseUrl(config.baseUrl);\n this._auth = new AssistantCloudAnonymousAuthStrategy(this._baseUrl);\n } else {\n throw new Error(\n \"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration\",\n );\n }\n }\n\n public async initializeAuth() {\n return !!(await this._auth.getAuthHeaders());\n }\n\n public async makeRawRequest(\n endpoint: string,\n options: MakeRequestOptions = {},\n ) {\n const authHeaders = await this._auth.getAuthHeaders();\n if (!authHeaders) throw new Error(\"Authorization failed\");\n\n const headers = {\n ...authHeaders,\n ...options.headers,\n \"Content-Type\": \"application/json\",\n };\n\n const queryParams = new URLSearchParams();\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value === false) continue;\n if (value === true) {\n queryParams.set(key, \"true\");\n } else {\n queryParams.set(key, value.toString());\n }\n }\n }\n\n const url = new URL(`${this._baseUrl}/v1${endpoint}`);\n url.search = queryParams.toString();\n\n const response = await fetch(url, {\n method: options.method ?? \"GET\",\n headers,\n body: options.body ? JSON.stringify(options.body) : null,\n ...(options.keepalive ? { keepalive: true } : {}),\n });\n\n this._auth.readAuthHeaders(response.headers);\n\n if (!response.ok) {\n const text = await response.text();\n let message: string | undefined;\n let code: string | undefined;\n let details: Record<string, unknown> | undefined;\n try {\n const body = JSON.parse(text) as unknown;\n if (typeof body === \"object\" && body !== null && !Array.isArray(body)) {\n const record = body as Record<string, unknown>;\n if (typeof record.message === \"string\" && record.message.length > 0) {\n message = record.message;\n }\n if (typeof record.error === \"string\") {\n code = record.error;\n details = { ...record };\n delete details.error;\n }\n }\n } catch {}\n throw new CloudAPIError(\n message ?? `Request failed with status ${response.status}, ${text}`,\n response.status,\n code,\n details,\n );\n }\n\n return response;\n }\n\n public async makeRequest(endpoint: string, options: MakeRequestOptions = {}) {\n const response = await this.makeRawRequest(endpoint, options);\n if (\n response.status === 204 ||\n response.headers.get(\"content-length\") === \"0\"\n )\n return undefined;\n\n const text = await response.text();\n if (text.trim() === \"\") return undefined;\n\n return JSON.parse(text);\n }\n}\n"],"mappings":"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"}
|
|
@@ -22,6 +22,9 @@ declare class AssistantCloudAPIKeyAuthStrategy implements AssistantCloudAuthStra
|
|
|
22
22
|
getAuthHeaders(): Promise<Record<string, string>>;
|
|
23
23
|
readAuthHeaders(): void;
|
|
24
24
|
}
|
|
25
|
+
declare const normalizeBaseUrl: (baseUrl: string) => string;
|
|
26
|
+
/** The refresh token of the anonymous identity this browser holds for `baseUrl`, or null when it has none. */
|
|
27
|
+
declare const readAnonymousRefreshToken: (baseUrl: string) => string | null;
|
|
25
28
|
declare class AssistantCloudAnonymousAuthStrategy implements AssistantCloudAuthStrategy {
|
|
26
29
|
readonly strategy = "anon";
|
|
27
30
|
private baseUrl;
|
|
@@ -31,5 +34,5 @@ declare class AssistantCloudAnonymousAuthStrategy implements AssistantCloudAuthS
|
|
|
31
34
|
readAuthHeaders(headers: Headers): void;
|
|
32
35
|
}
|
|
33
36
|
//#endregion
|
|
34
|
-
export { AssistantCloudAPIKeyAuthStrategy, AssistantCloudAnonymousAuthStrategy, AssistantCloudAuthStrategy, AssistantCloudJWTAuthStrategy };
|
|
37
|
+
export { AssistantCloudAPIKeyAuthStrategy, AssistantCloudAnonymousAuthStrategy, AssistantCloudAuthStrategy, AssistantCloudJWTAuthStrategy, normalizeBaseUrl, readAnonymousRefreshToken };
|
|
35
38
|
//# sourceMappingURL=AssistantCloudAuthStrategy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloudAuthStrategy.d.ts","names":[],"sources":["../src/AssistantCloudAuthStrategy.ts"],"mappings":";KAkCY;WACD;EACT,kBAAkB,QAAQ;EAC1B,gBAAgB,SAAS;;cAmFd,yCAAyC;;WACpC;UAER;UACA;UACA;EAGI,YAAA,yBAAyB;EAIxB,kBAAkB,QAAQ;UA0BzB;EAUP,gBAAgB,SAAS;;cAcrB,4CAA4C;;WACvC;EAMJ,YAAA,gBAAgB,gBAAgB;EAM/B,kBAAkB,QAAQ;EAQhC;;
|
|
1
|
+
{"version":3,"file":"AssistantCloudAuthStrategy.d.ts","names":[],"sources":["../src/AssistantCloudAuthStrategy.ts"],"mappings":";KAkCY;WACD;EACT,kBAAkB,QAAQ;EAC1B,gBAAgB,SAAS;;cAmFd,yCAAyC;;WACpC;UAER;UACA;UACA;EAGI,YAAA,yBAAyB;EAIxB,kBAAkB,QAAQ;UA0BzB;EAUP,gBAAgB,SAAS;;cAcrB,4CAA4C;;WACvC;EAMJ,YAAA,gBAAgB,gBAAgB;EAM/B,kBAAkB,QAAQ;EAQhC;;cAuDI,mBAAoB;;cAMpB,4BAA6B;cAwE7B,+CAA+C;WAC1C;UAER;UACA;EAEI,YAAA;EAwFC,kBAAkB,QAAQ;EAIhC,gBAAgB,SAAS"}
|
|
@@ -152,6 +152,16 @@ const readRefreshToken = (baseUrl) => {
|
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
};
|
|
155
|
+
const normalizeBaseUrl = (baseUrl) => {
|
|
156
|
+
if (!baseUrl || !baseUrl.endsWith("/")) return baseUrl;
|
|
157
|
+
return baseUrl.slice(0, -1);
|
|
158
|
+
};
|
|
159
|
+
/** The refresh token of the anonymous identity this browser holds for `baseUrl`, or null when it has none. */
|
|
160
|
+
const readAnonymousRefreshToken = (baseUrl) => {
|
|
161
|
+
const refreshToken = readRefreshToken(normalizeBaseUrl(baseUrl));
|
|
162
|
+
if (!refreshToken) return null;
|
|
163
|
+
return new Date(refreshToken.expires_at).getTime() - Date.now() > 3e4 ? refreshToken.token : null;
|
|
164
|
+
};
|
|
155
165
|
const writeRefreshToken = (baseUrl, refreshToken) => {
|
|
156
166
|
const storage = getLocalStorage();
|
|
157
167
|
if (!storage) return;
|
|
@@ -239,6 +249,6 @@ var AssistantCloudAnonymousAuthStrategy = class {
|
|
|
239
249
|
}
|
|
240
250
|
};
|
|
241
251
|
//#endregion
|
|
242
|
-
export { AssistantCloudAPIKeyAuthStrategy, AssistantCloudAnonymousAuthStrategy, AssistantCloudJWTAuthStrategy };
|
|
252
|
+
export { AssistantCloudAPIKeyAuthStrategy, AssistantCloudAnonymousAuthStrategy, AssistantCloudJWTAuthStrategy, normalizeBaseUrl, readAnonymousRefreshToken };
|
|
243
253
|
|
|
244
254
|
//# sourceMappingURL=AssistantCloudAuthStrategy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloudAuthStrategy.js","names":[],"sources":["../src/AssistantCloudAuthStrategy.ts"],"sourcesContent":["import {\n CloudResponseError,\n readCloudRecord,\n readCloudString,\n} from \"./cloudResponse\";\n\nconst AUTH_TOKEN_REQUEST_TIMEOUT_MS = 30_000;\n\nconst withAuthTokenDeadline = async <T>(\n operation: string,\n run: (signal: AbortSignal) => Promise<T>,\n): Promise<T> => {\n const controller = new AbortController();\n let timedOut = false;\n const timeout = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, AUTH_TOKEN_REQUEST_TIMEOUT_MS);\n\n try {\n return await run(controller.signal);\n } catch (error) {\n if (timedOut) {\n throw new Error(\n `Assistant Cloud ${operation} timed out after ${AUTH_TOKEN_REQUEST_TIMEOUT_MS}ms`,\n { cause: error },\n );\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n};\n\nexport type AssistantCloudAuthStrategy = {\n readonly strategy: \"anon\" | \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\nconst getJwtExpiry = (jwt: string): number => {\n try {\n const parts = jwt.split(\".\");\n const bodyPart = parts[1];\n if (!bodyPart) {\n throw new Error(\"Invalid JWT format\");\n }\n\n // Convert from Base64Url to Base64 and add padding if necessary\n let base64 = bodyPart.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n\n // Decode the Base64 string and parse the payload\n const payload = atob(base64);\n const payloadObj = JSON.parse(payload);\n const exp = payloadObj.exp;\n\n if (!exp || typeof exp !== \"number\") {\n throw new Error('JWT does not contain a valid \"exp\" field');\n }\n\n // Convert expiration time to milliseconds\n return exp * 1000;\n } catch (error) {\n throw new Error(`Unable to determine the token expiry: ${error}`);\n }\n};\n\ntype RefreshToken = {\n token: string;\n expires_at: string;\n};\n\nconst readNonEmptyCloudString = (value: unknown, field: string): string => {\n const result = readCloudString(value, field);\n if (result.length === 0) {\n throw new CloudResponseError(\n `Invalid Assistant Cloud response for \"${field}\": expected a non-empty string`,\n );\n }\n return result;\n};\n\nconst readRefreshTokenResponse = (\n value: unknown,\n field: string,\n): RefreshToken => {\n const refreshToken = readCloudRecord(value, field);\n return {\n token: readNonEmptyCloudString(refreshToken.token, `${field}.token`),\n expires_at: readNonEmptyCloudString(\n refreshToken.expires_at,\n `${field}.expires_at`,\n ),\n };\n};\n\nconst readAuthTokenResponse = async (\n response: Response,\n field: string,\n): Promise<{ data: Record<string, unknown>; accessToken: string }> => {\n let value: unknown;\n try {\n value = await response.json();\n } catch {\n throw new CloudResponseError(\n `Invalid Assistant Cloud response for \"${field}\": expected valid JSON`,\n );\n }\n\n const data = readCloudRecord(value, field);\n const accessToken = readNonEmptyCloudString(\n data.access_token,\n `${field}.access_token`,\n );\n return { data, accessToken };\n};\n\nexport class AssistantCloudJWTAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"jwt\";\n\n private cachedToken: string | null = null;\n private tokenExpiry: number | null = null;\n private tokenRequest: Promise<Record<string, string> | false> | null = null;\n #authTokenCallback: () => Promise<string | null>;\n\n constructor(authTokenCallback: () => Promise<string | null>) {\n this.#authTokenCallback = authTokenCallback;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n const currentTime = Date.now();\n\n // Use cached token if it's valid for at least 30 more seconds\n if (\n this.cachedToken &&\n this.tokenExpiry &&\n this.tokenExpiry - currentTime > 30 * 1000\n ) {\n return { Authorization: `Bearer ${this.cachedToken}` };\n }\n\n if (!this.tokenRequest) {\n this.tokenRequest = this.fetchAuthHeaders();\n }\n\n const tokenRequest = this.tokenRequest;\n try {\n return await tokenRequest;\n } finally {\n if (this.tokenRequest === tokenRequest) {\n this.tokenRequest = null;\n }\n }\n }\n\n private async fetchAuthHeaders(): Promise<Record<string, string> | false> {\n const token = await this.#authTokenCallback();\n if (!token) return false;\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n\n return { Authorization: `Bearer ${token}` };\n }\n\n public readAuthHeaders(headers: Headers) {\n const authHeader = headers.get(\"Authorization\");\n if (!authHeader) return;\n\n const [scheme, token] = authHeader.split(\" \");\n if (scheme !== \"Bearer\" || !token) {\n throw new Error(\"Invalid auth header received\");\n }\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n }\n}\n\nexport class AssistantCloudAPIKeyAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"api-key\";\n\n #apiKey: string;\n #userId: string;\n #workspaceId: string;\n\n constructor(apiKey: string, userId: string, workspaceId: string) {\n this.#apiKey = apiKey;\n this.#userId = userId;\n this.#workspaceId = workspaceId;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string>> {\n return {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Aui-User-Id\": this.#userId,\n \"Aui-Workspace-Id\": this.#workspaceId,\n };\n }\n\n public readAuthHeaders() {\n // No operation needed for API key auth\n }\n}\n\nconst LEGACY_AUI_REFRESH_TOKEN_NAME = \"aui:refresh_token\";\n\nconst getRefreshTokenName = (baseUrl: string): string =>\n `${LEGACY_AUI_REFRESH_TOKEN_NAME}:${baseUrl}`;\n\nconst removeLegacyRefreshToken = (storage: Storage): void => {\n try {\n storage.removeItem(LEGACY_AUI_REFRESH_TOKEN_NAME);\n } catch {}\n};\n\nconst getLocalStorage = (): Storage | null => {\n if (!(\"localStorage\" in globalThis)) return null;\n try {\n return (globalThis as { localStorage: Storage }).localStorage;\n } catch {\n return null;\n }\n};\n\nconst readRefreshToken = (baseUrl: string): RefreshToken | undefined => {\n const storage = getLocalStorage();\n if (!storage) return undefined;\n try {\n const name = getRefreshTokenName(baseUrl);\n const value = storage.getItem(name);\n if (value) {\n removeLegacyRefreshToken(storage);\n return JSON.parse(value) as RefreshToken;\n }\n\n const legacyValue = storage.getItem(LEGACY_AUI_REFRESH_TOKEN_NAME);\n if (!legacyValue) return undefined;\n\n let refreshToken: RefreshToken;\n try {\n refreshToken = JSON.parse(legacyValue) as RefreshToken;\n } catch {\n removeLegacyRefreshToken(storage);\n return undefined;\n }\n\n storage.setItem(name, legacyValue);\n removeLegacyRefreshToken(storage);\n return refreshToken;\n } catch {\n return undefined;\n }\n};\n\nconst writeRefreshToken = (\n baseUrl: string,\n refreshToken: RefreshToken,\n): void => {\n const storage = getLocalStorage();\n if (!storage) return;\n try {\n storage.setItem(getRefreshTokenName(baseUrl), JSON.stringify(refreshToken));\n } catch {}\n};\n\nconst removeRefreshToken = (baseUrl: string): void => {\n const storage = getLocalStorage();\n if (!storage) return;\n try {\n storage.removeItem(getRefreshTokenName(baseUrl));\n } catch {}\n};\n\n// In-flight sharing follows refresh-token storage scope to isolate server requests.\nconst anonymousAuthTokenRequests = new WeakMap<\n Storage,\n Map<string, Promise<string | null>>\n>();\n\nconst getWebLockManager = (): LockManager | null => {\n if (!(\"navigator\" in globalThis)) return null;\n return (\n (globalThis as { navigator?: { locks?: LockManager } }).navigator?.locks ??\n null\n );\n};\n\nconst getAnonymousAuthLockName = (baseUrl: string): string =>\n `assistant-cloud:anonymous-auth:${baseUrl}`;\n\nconst getSharedAnonymousAuthToken = (\n baseUrl: string,\n requestToken: () => Promise<string | null>,\n): Promise<string | null> => {\n const storage = getLocalStorage();\n if (!storage) return requestToken();\n\n let storageRequests = anonymousAuthTokenRequests.get(storage);\n if (!storageRequests) {\n storageRequests = new Map();\n anonymousAuthTokenRequests.set(storage, storageRequests);\n }\n\n const activeRequest = storageRequests.get(baseUrl);\n if (activeRequest) return activeRequest;\n\n const locks = getWebLockManager();\n const request = locks\n ? locks.request(getAnonymousAuthLockName(baseUrl), requestToken)\n : requestToken();\n const sharedRequest = request.finally(() => {\n if (storageRequests.get(baseUrl) === sharedRequest) {\n storageRequests.delete(baseUrl);\n }\n });\n storageRequests.set(baseUrl, sharedRequest);\n return sharedRequest;\n};\n\nexport class AssistantCloudAnonymousAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"anon\";\n\n private baseUrl: string;\n private jwtStrategy: AssistantCloudJWTAuthStrategy;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl;\n const requestAuthToken = async (): Promise<string | null> => {\n const currentTime = Date.now();\n const storedRefreshToken = readRefreshToken(this.baseUrl);\n\n if (storedRefreshToken) {\n const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();\n if (refreshExpiry - currentTime > 30 * 1000) {\n const refreshedAccessToken = await withAuthTokenDeadline(\n \"refresh token request\",\n async (signal) => {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/refresh`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n refresh_token: storedRefreshToken.token,\n }),\n signal,\n },\n );\n\n if (response.ok) {\n const { data, accessToken } = await readAuthTokenResponse(\n response,\n \"refresh auth token response\",\n );\n if (data.refresh_token != null) {\n writeRefreshToken(\n this.baseUrl,\n readRefreshTokenResponse(\n data.refresh_token,\n \"refresh auth token response.refresh_token\",\n ),\n );\n }\n return accessToken;\n }\n\n if (response.status === 429 || response.status >= 500) {\n throw new Error(\n `Assistant Cloud token refresh failed with status ${response.status}`,\n );\n }\n\n return null;\n },\n );\n if (refreshedAccessToken !== null) return refreshedAccessToken;\n } else {\n removeRefreshToken(this.baseUrl);\n }\n }\n\n // No valid refresh token; request a new anonymous token\n return withAuthTokenDeadline(\n \"anonymous token request\",\n async (signal) => {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/anonymous`,\n { method: \"POST\", signal },\n );\n\n if (!response.ok) return null;\n\n const { data, accessToken } = await readAuthTokenResponse(\n response,\n \"anonymous auth token response\",\n );\n\n writeRefreshToken(\n this.baseUrl,\n readRefreshTokenResponse(\n data.refresh_token,\n \"anonymous auth token response.refresh_token\",\n ),\n );\n return accessToken;\n },\n );\n };\n this.jwtStrategy = new AssistantCloudJWTAuthStrategy(() =>\n getSharedAnonymousAuthToken(this.baseUrl, requestAuthToken),\n );\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n return this.jwtStrategy.getAuthHeaders();\n }\n\n public readAuthHeaders(headers: Headers): void {\n this.jwtStrategy.readAuthHeaders(headers);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssistantCloudAuthStrategy.js","names":[],"sources":["../src/AssistantCloudAuthStrategy.ts"],"sourcesContent":["import {\n CloudResponseError,\n readCloudRecord,\n readCloudString,\n} from \"./cloudResponse\";\n\nconst AUTH_TOKEN_REQUEST_TIMEOUT_MS = 30_000;\n\nconst withAuthTokenDeadline = async <T>(\n operation: string,\n run: (signal: AbortSignal) => Promise<T>,\n): Promise<T> => {\n const controller = new AbortController();\n let timedOut = false;\n const timeout = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, AUTH_TOKEN_REQUEST_TIMEOUT_MS);\n\n try {\n return await run(controller.signal);\n } catch (error) {\n if (timedOut) {\n throw new Error(\n `Assistant Cloud ${operation} timed out after ${AUTH_TOKEN_REQUEST_TIMEOUT_MS}ms`,\n { cause: error },\n );\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n};\n\nexport type AssistantCloudAuthStrategy = {\n readonly strategy: \"anon\" | \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\nconst getJwtExpiry = (jwt: string): number => {\n try {\n const parts = jwt.split(\".\");\n const bodyPart = parts[1];\n if (!bodyPart) {\n throw new Error(\"Invalid JWT format\");\n }\n\n // Convert from Base64Url to Base64 and add padding if necessary\n let base64 = bodyPart.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n\n // Decode the Base64 string and parse the payload\n const payload = atob(base64);\n const payloadObj = JSON.parse(payload);\n const exp = payloadObj.exp;\n\n if (!exp || typeof exp !== \"number\") {\n throw new Error('JWT does not contain a valid \"exp\" field');\n }\n\n // Convert expiration time to milliseconds\n return exp * 1000;\n } catch (error) {\n throw new Error(`Unable to determine the token expiry: ${error}`);\n }\n};\n\ntype RefreshToken = {\n token: string;\n expires_at: string;\n};\n\nconst readNonEmptyCloudString = (value: unknown, field: string): string => {\n const result = readCloudString(value, field);\n if (result.length === 0) {\n throw new CloudResponseError(\n `Invalid Assistant Cloud response for \"${field}\": expected a non-empty string`,\n );\n }\n return result;\n};\n\nconst readRefreshTokenResponse = (\n value: unknown,\n field: string,\n): RefreshToken => {\n const refreshToken = readCloudRecord(value, field);\n return {\n token: readNonEmptyCloudString(refreshToken.token, `${field}.token`),\n expires_at: readNonEmptyCloudString(\n refreshToken.expires_at,\n `${field}.expires_at`,\n ),\n };\n};\n\nconst readAuthTokenResponse = async (\n response: Response,\n field: string,\n): Promise<{ data: Record<string, unknown>; accessToken: string }> => {\n let value: unknown;\n try {\n value = await response.json();\n } catch {\n throw new CloudResponseError(\n `Invalid Assistant Cloud response for \"${field}\": expected valid JSON`,\n );\n }\n\n const data = readCloudRecord(value, field);\n const accessToken = readNonEmptyCloudString(\n data.access_token,\n `${field}.access_token`,\n );\n return { data, accessToken };\n};\n\nexport class AssistantCloudJWTAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"jwt\";\n\n private cachedToken: string | null = null;\n private tokenExpiry: number | null = null;\n private tokenRequest: Promise<Record<string, string> | false> | null = null;\n #authTokenCallback: () => Promise<string | null>;\n\n constructor(authTokenCallback: () => Promise<string | null>) {\n this.#authTokenCallback = authTokenCallback;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n const currentTime = Date.now();\n\n // Use cached token if it's valid for at least 30 more seconds\n if (\n this.cachedToken &&\n this.tokenExpiry &&\n this.tokenExpiry - currentTime > 30 * 1000\n ) {\n return { Authorization: `Bearer ${this.cachedToken}` };\n }\n\n if (!this.tokenRequest) {\n this.tokenRequest = this.fetchAuthHeaders();\n }\n\n const tokenRequest = this.tokenRequest;\n try {\n return await tokenRequest;\n } finally {\n if (this.tokenRequest === tokenRequest) {\n this.tokenRequest = null;\n }\n }\n }\n\n private async fetchAuthHeaders(): Promise<Record<string, string> | false> {\n const token = await this.#authTokenCallback();\n if (!token) return false;\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n\n return { Authorization: `Bearer ${token}` };\n }\n\n public readAuthHeaders(headers: Headers) {\n const authHeader = headers.get(\"Authorization\");\n if (!authHeader) return;\n\n const [scheme, token] = authHeader.split(\" \");\n if (scheme !== \"Bearer\" || !token) {\n throw new Error(\"Invalid auth header received\");\n }\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n }\n}\n\nexport class AssistantCloudAPIKeyAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"api-key\";\n\n #apiKey: string;\n #userId: string;\n #workspaceId: string;\n\n constructor(apiKey: string, userId: string, workspaceId: string) {\n this.#apiKey = apiKey;\n this.#userId = userId;\n this.#workspaceId = workspaceId;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string>> {\n return {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Aui-User-Id\": this.#userId,\n \"Aui-Workspace-Id\": this.#workspaceId,\n };\n }\n\n public readAuthHeaders() {\n // No operation needed for API key auth\n }\n}\n\nconst LEGACY_AUI_REFRESH_TOKEN_NAME = \"aui:refresh_token\";\n\nconst getRefreshTokenName = (baseUrl: string): string =>\n `${LEGACY_AUI_REFRESH_TOKEN_NAME}:${baseUrl}`;\n\nconst removeLegacyRefreshToken = (storage: Storage): void => {\n try {\n storage.removeItem(LEGACY_AUI_REFRESH_TOKEN_NAME);\n } catch {}\n};\n\nconst getLocalStorage = (): Storage | null => {\n if (!(\"localStorage\" in globalThis)) return null;\n try {\n return (globalThis as { localStorage: Storage }).localStorage;\n } catch {\n return null;\n }\n};\n\nconst readRefreshToken = (baseUrl: string): RefreshToken | undefined => {\n const storage = getLocalStorage();\n if (!storage) return undefined;\n try {\n const name = getRefreshTokenName(baseUrl);\n const value = storage.getItem(name);\n if (value) {\n removeLegacyRefreshToken(storage);\n return JSON.parse(value) as RefreshToken;\n }\n\n const legacyValue = storage.getItem(LEGACY_AUI_REFRESH_TOKEN_NAME);\n if (!legacyValue) return undefined;\n\n let refreshToken: RefreshToken;\n try {\n refreshToken = JSON.parse(legacyValue) as RefreshToken;\n } catch {\n removeLegacyRefreshToken(storage);\n return undefined;\n }\n\n storage.setItem(name, legacyValue);\n removeLegacyRefreshToken(storage);\n return refreshToken;\n } catch {\n return undefined;\n }\n};\n\nexport const normalizeBaseUrl = (baseUrl: string): string => {\n if (!baseUrl || !baseUrl.endsWith(\"/\")) return baseUrl;\n return baseUrl.slice(0, -1);\n};\n\n/** The refresh token of the anonymous identity this browser holds for `baseUrl`, or null when it has none. */\nexport const readAnonymousRefreshToken = (baseUrl: string): string | null => {\n const refreshToken = readRefreshToken(normalizeBaseUrl(baseUrl));\n if (!refreshToken) return null;\n const refreshExpiry = new Date(refreshToken.expires_at).getTime();\n return refreshExpiry - Date.now() > 30 * 1000 ? refreshToken.token : null;\n};\n\nconst writeRefreshToken = (\n baseUrl: string,\n refreshToken: RefreshToken,\n): void => {\n const storage = getLocalStorage();\n if (!storage) return;\n try {\n storage.setItem(getRefreshTokenName(baseUrl), JSON.stringify(refreshToken));\n } catch {}\n};\n\nconst removeRefreshToken = (baseUrl: string): void => {\n const storage = getLocalStorage();\n if (!storage) return;\n try {\n storage.removeItem(getRefreshTokenName(baseUrl));\n } catch {}\n};\n\n// In-flight sharing follows refresh-token storage scope to isolate server requests.\nconst anonymousAuthTokenRequests = new WeakMap<\n Storage,\n Map<string, Promise<string | null>>\n>();\n\nconst getWebLockManager = (): LockManager | null => {\n if (!(\"navigator\" in globalThis)) return null;\n return (\n (globalThis as { navigator?: { locks?: LockManager } }).navigator?.locks ??\n null\n );\n};\n\nconst getAnonymousAuthLockName = (baseUrl: string): string =>\n `assistant-cloud:anonymous-auth:${baseUrl}`;\n\nconst getSharedAnonymousAuthToken = (\n baseUrl: string,\n requestToken: () => Promise<string | null>,\n): Promise<string | null> => {\n const storage = getLocalStorage();\n if (!storage) return requestToken();\n\n let storageRequests = anonymousAuthTokenRequests.get(storage);\n if (!storageRequests) {\n storageRequests = new Map();\n anonymousAuthTokenRequests.set(storage, storageRequests);\n }\n\n const activeRequest = storageRequests.get(baseUrl);\n if (activeRequest) return activeRequest;\n\n const locks = getWebLockManager();\n const request = locks\n ? locks.request(getAnonymousAuthLockName(baseUrl), requestToken)\n : requestToken();\n const sharedRequest = request.finally(() => {\n if (storageRequests.get(baseUrl) === sharedRequest) {\n storageRequests.delete(baseUrl);\n }\n });\n storageRequests.set(baseUrl, sharedRequest);\n return sharedRequest;\n};\n\nexport class AssistantCloudAnonymousAuthStrategy implements AssistantCloudAuthStrategy {\n public readonly strategy = \"anon\";\n\n private baseUrl: string;\n private jwtStrategy: AssistantCloudJWTAuthStrategy;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl;\n const requestAuthToken = async (): Promise<string | null> => {\n const currentTime = Date.now();\n const storedRefreshToken = readRefreshToken(this.baseUrl);\n\n if (storedRefreshToken) {\n const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();\n if (refreshExpiry - currentTime > 30 * 1000) {\n const refreshedAccessToken = await withAuthTokenDeadline(\n \"refresh token request\",\n async (signal) => {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/refresh`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n refresh_token: storedRefreshToken.token,\n }),\n signal,\n },\n );\n\n if (response.ok) {\n const { data, accessToken } = await readAuthTokenResponse(\n response,\n \"refresh auth token response\",\n );\n if (data.refresh_token != null) {\n writeRefreshToken(\n this.baseUrl,\n readRefreshTokenResponse(\n data.refresh_token,\n \"refresh auth token response.refresh_token\",\n ),\n );\n }\n return accessToken;\n }\n\n if (response.status === 429 || response.status >= 500) {\n throw new Error(\n `Assistant Cloud token refresh failed with status ${response.status}`,\n );\n }\n\n return null;\n },\n );\n if (refreshedAccessToken !== null) return refreshedAccessToken;\n } else {\n removeRefreshToken(this.baseUrl);\n }\n }\n\n // No valid refresh token; request a new anonymous token\n return withAuthTokenDeadline(\n \"anonymous token request\",\n async (signal) => {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/anonymous`,\n { method: \"POST\", signal },\n );\n\n if (!response.ok) return null;\n\n const { data, accessToken } = await readAuthTokenResponse(\n response,\n \"anonymous auth token response\",\n );\n\n writeRefreshToken(\n this.baseUrl,\n readRefreshTokenResponse(\n data.refresh_token,\n \"anonymous auth token response.refresh_token\",\n ),\n );\n return accessToken;\n },\n );\n };\n this.jwtStrategy = new AssistantCloudJWTAuthStrategy(() =>\n getSharedAnonymousAuthToken(this.baseUrl, requestAuthToken),\n );\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n return this.jwtStrategy.getAuthHeaders();\n }\n\n public readAuthHeaders(headers: Headers): void {\n this.jwtStrategy.readAuthHeaders(headers);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AssistantCloudAPI } from "./AssistantCloudAPI.js";
|
|
2
|
+
//#region src/AssistantCloudEvents.d.ts
|
|
3
|
+
type AssistantCloudEventKind = "message_sent" | "message_edited" | "run_stopped" | "message_regenerated" | "message_copied" | "branch_switched" | "suggestions_shown" | "suggestion_clicked" | "attachment_added" | "attachment_failed" | "thread_switched" | "tool_approved" | "tool_rejected" | "speech_started" | "voice_started" | "error_shown";
|
|
4
|
+
type AssistantCloudEvent = {
|
|
5
|
+
kind: AssistantCloudEventKind;
|
|
6
|
+
thread_id?: string | undefined;
|
|
7
|
+
message_id?: string | undefined;
|
|
8
|
+
run_id?: string | undefined;
|
|
9
|
+
value?: number | undefined;
|
|
10
|
+
props?: Readonly<Record<string, string | number | boolean>> | undefined;
|
|
11
|
+
};
|
|
12
|
+
declare class AssistantCloudEvents {
|
|
13
|
+
private buffer;
|
|
14
|
+
private timer;
|
|
15
|
+
private flushing;
|
|
16
|
+
private readonly cloud;
|
|
17
|
+
private readonly isEnabled;
|
|
18
|
+
private listening;
|
|
19
|
+
constructor(cloud: AssistantCloudAPI, isEnabled: () => boolean);
|
|
20
|
+
track(event: AssistantCloudEvent): void;
|
|
21
|
+
private listen;
|
|
22
|
+
private unlisten;
|
|
23
|
+
dispose(): void;
|
|
24
|
+
private onVisibilityChange;
|
|
25
|
+
private flush;
|
|
26
|
+
private flushPending;
|
|
27
|
+
private scheduleFlush;
|
|
28
|
+
private clearFlushTimer;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { AssistantCloudEvent, AssistantCloudEventKind, AssistantCloudEvents };
|
|
32
|
+
//# sourceMappingURL=AssistantCloudEvents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantCloudEvents.d.ts","names":[],"sources":["../src/AssistantCloudEvents.ts"],"mappings":";;KAEY;KAkBA;EACV,MAAM;EACN;EACA;EACA;EACA;EACA,QAAQ,SAAS;;cAON;UACH;UACA;UACA;mBAES;mBACA;UAET;EAEI,YAAA,OAAO,mBAAmB;EAK/B,MAAM,OAAO;UAaZ;UAaA;EAOD;UAMC;UAMA;UAyBM;UAaN;UAQA"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
//#region src/AssistantCloudEvents.ts
|
|
2
|
+
const FLUSH_SIZE = 20;
|
|
3
|
+
const MAX_BATCH_SIZE = 50;
|
|
4
|
+
const FLUSH_DELAY_MS = 2e3;
|
|
5
|
+
var AssistantCloudEvents = class {
|
|
6
|
+
buffer = [];
|
|
7
|
+
timer;
|
|
8
|
+
flushing;
|
|
9
|
+
cloud;
|
|
10
|
+
isEnabled;
|
|
11
|
+
listening = false;
|
|
12
|
+
constructor(cloud, isEnabled) {
|
|
13
|
+
this.cloud = cloud;
|
|
14
|
+
this.isEnabled = isEnabled;
|
|
15
|
+
}
|
|
16
|
+
track(event) {
|
|
17
|
+
if (!this.isEnabled()) return;
|
|
18
|
+
this.listen();
|
|
19
|
+
this.buffer.push(normalizeEvent(event));
|
|
20
|
+
if (this.buffer.length >= FLUSH_SIZE) {
|
|
21
|
+
this.flush();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.scheduleFlush();
|
|
25
|
+
}
|
|
26
|
+
listen() {
|
|
27
|
+
if (this.listening || typeof window === "undefined" || typeof document === "undefined") return;
|
|
28
|
+
this.listening = true;
|
|
29
|
+
window.addEventListener("pagehide", this.flush);
|
|
30
|
+
document.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
31
|
+
}
|
|
32
|
+
unlisten() {
|
|
33
|
+
if (!this.listening) return;
|
|
34
|
+
this.listening = false;
|
|
35
|
+
window.removeEventListener("pagehide", this.flush);
|
|
36
|
+
document.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
37
|
+
}
|
|
38
|
+
dispose() {
|
|
39
|
+
this.unlisten();
|
|
40
|
+
this.clearFlushTimer();
|
|
41
|
+
this.flush();
|
|
42
|
+
}
|
|
43
|
+
onVisibilityChange = () => {
|
|
44
|
+
if (document.visibilityState === "hidden") this.flush();
|
|
45
|
+
};
|
|
46
|
+
flush = async () => {
|
|
47
|
+
if (!this.isEnabled()) {
|
|
48
|
+
this.buffer = [];
|
|
49
|
+
this.clearFlushTimer();
|
|
50
|
+
this.unlisten();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (this.flushing) return this.flushing;
|
|
54
|
+
this.clearFlushTimer();
|
|
55
|
+
const task = this.flushPending();
|
|
56
|
+
this.flushing = task;
|
|
57
|
+
task.then(() => {
|
|
58
|
+
if (this.flushing !== task) return;
|
|
59
|
+
this.flushing = void 0;
|
|
60
|
+
if (this.buffer.length === 0) {
|
|
61
|
+
this.clearFlushTimer();
|
|
62
|
+
this.unlisten();
|
|
63
|
+
} else this.flush();
|
|
64
|
+
});
|
|
65
|
+
return task;
|
|
66
|
+
};
|
|
67
|
+
async flushPending() {
|
|
68
|
+
while (this.buffer.length > 0) {
|
|
69
|
+
const events = this.buffer.splice(0, MAX_BATCH_SIZE);
|
|
70
|
+
try {
|
|
71
|
+
await this.cloud.makeRequest("/events", {
|
|
72
|
+
method: "POST",
|
|
73
|
+
body: { events },
|
|
74
|
+
keepalive: true
|
|
75
|
+
});
|
|
76
|
+
} catch {}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
scheduleFlush() {
|
|
80
|
+
if (this.timer !== void 0) return;
|
|
81
|
+
this.timer = setTimeout(() => {
|
|
82
|
+
this.timer = void 0;
|
|
83
|
+
this.flush();
|
|
84
|
+
}, FLUSH_DELAY_MS);
|
|
85
|
+
}
|
|
86
|
+
clearFlushTimer() {
|
|
87
|
+
if (this.timer === void 0) return;
|
|
88
|
+
clearTimeout(this.timer);
|
|
89
|
+
this.timer = void 0;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const normalizeEvent = (event) => {
|
|
93
|
+
const props = normalizeProps(event.props);
|
|
94
|
+
return {
|
|
95
|
+
kind: event.kind,
|
|
96
|
+
...normalizeId(event.thread_id) ? { thread_id: event.thread_id } : {},
|
|
97
|
+
...normalizeId(event.message_id) ? { message_id: event.message_id } : {},
|
|
98
|
+
...normalizeId(event.run_id) ? { run_id: event.run_id } : {},
|
|
99
|
+
...isNonNegativeInteger(event.value) ? { value: event.value } : {},
|
|
100
|
+
...props ? { props } : {}
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
const normalizeId = (value) => value && value.length <= 48 ? value : void 0;
|
|
104
|
+
const isNonNegativeInteger = (value) => value !== void 0 && Number.isInteger(value) && value >= 0;
|
|
105
|
+
const normalizeProps = (props) => {
|
|
106
|
+
if (!props) return void 0;
|
|
107
|
+
const entries = Object.entries(props);
|
|
108
|
+
if (entries.some(([, value]) => typeof value === "string" && value.length > 256 || typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean")) return;
|
|
109
|
+
const value = Object.fromEntries(entries);
|
|
110
|
+
return new TextEncoder().encode(JSON.stringify(value)).byteLength <= 1024 ? value : void 0;
|
|
111
|
+
};
|
|
112
|
+
//#endregion
|
|
113
|
+
export { AssistantCloudEvents };
|
|
114
|
+
|
|
115
|
+
//# sourceMappingURL=AssistantCloudEvents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantCloudEvents.js","names":[],"sources":["../src/AssistantCloudEvents.ts"],"sourcesContent":["import type { AssistantCloudAPI } from \"./AssistantCloudAPI\";\n\nexport type AssistantCloudEventKind =\n | \"message_sent\"\n | \"message_edited\"\n | \"run_stopped\"\n | \"message_regenerated\"\n | \"message_copied\"\n | \"branch_switched\"\n | \"suggestions_shown\"\n | \"suggestion_clicked\"\n | \"attachment_added\"\n | \"attachment_failed\"\n | \"thread_switched\"\n | \"tool_approved\"\n | \"tool_rejected\"\n | \"speech_started\"\n | \"voice_started\"\n | \"error_shown\";\n\nexport type AssistantCloudEvent = {\n kind: AssistantCloudEventKind;\n thread_id?: string | undefined;\n message_id?: string | undefined;\n run_id?: string | undefined;\n value?: number | undefined;\n props?: Readonly<Record<string, string | number | boolean>> | undefined;\n};\n\nconst FLUSH_SIZE = 20;\nconst MAX_BATCH_SIZE = 50;\nconst FLUSH_DELAY_MS = 2_000;\n\nexport class AssistantCloudEvents {\n private buffer: AssistantCloudEvent[] = [];\n private timer: ReturnType<typeof setTimeout> | undefined;\n private flushing: Promise<void> | undefined;\n\n private readonly cloud: AssistantCloudAPI;\n private readonly isEnabled: () => boolean;\n\n private listening = false;\n\n constructor(cloud: AssistantCloudAPI, isEnabled: () => boolean) {\n this.cloud = cloud;\n this.isEnabled = isEnabled;\n }\n\n public track(event: AssistantCloudEvent): void {\n if (!this.isEnabled()) return;\n\n this.listen();\n this.buffer.push(normalizeEvent(event));\n if (this.buffer.length >= FLUSH_SIZE) {\n void this.flush();\n return;\n }\n\n this.scheduleFlush();\n }\n\n private listen(): void {\n if (\n this.listening ||\n typeof window === \"undefined\" ||\n typeof document === \"undefined\"\n ) {\n return;\n }\n this.listening = true;\n window.addEventListener(\"pagehide\", this.flush);\n document.addEventListener(\"visibilitychange\", this.onVisibilityChange);\n }\n\n private unlisten(): void {\n if (!this.listening) return;\n this.listening = false;\n window.removeEventListener(\"pagehide\", this.flush);\n document.removeEventListener(\"visibilitychange\", this.onVisibilityChange);\n }\n\n public dispose(): void {\n this.unlisten();\n this.clearFlushTimer();\n void this.flush();\n }\n\n private onVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") {\n void this.flush();\n }\n };\n\n private flush = async (): Promise<void> => {\n if (!this.isEnabled()) {\n this.buffer = [];\n this.clearFlushTimer();\n this.unlisten();\n return;\n }\n if (this.flushing) return this.flushing;\n\n this.clearFlushTimer();\n const task = this.flushPending();\n this.flushing = task;\n void task.then(() => {\n if (this.flushing !== task) return;\n this.flushing = undefined;\n if (this.buffer.length === 0) {\n this.clearFlushTimer();\n this.unlisten();\n } else {\n void this.flush();\n }\n });\n return task;\n };\n\n private async flushPending(): Promise<void> {\n while (this.buffer.length > 0) {\n const events = this.buffer.splice(0, MAX_BATCH_SIZE);\n try {\n await this.cloud.makeRequest(\"/events\", {\n method: \"POST\",\n body: { events },\n keepalive: true,\n });\n } catch {}\n }\n }\n\n private scheduleFlush() {\n if (this.timer !== undefined) return;\n this.timer = setTimeout(() => {\n this.timer = undefined;\n void this.flush();\n }, FLUSH_DELAY_MS);\n }\n\n private clearFlushTimer() {\n if (this.timer === undefined) return;\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n}\n\nconst normalizeEvent = (event: AssistantCloudEvent): AssistantCloudEvent => {\n const props = normalizeProps(event.props);\n return {\n kind: event.kind,\n ...(normalizeId(event.thread_id) ? { thread_id: event.thread_id } : {}),\n ...(normalizeId(event.message_id) ? { message_id: event.message_id } : {}),\n ...(normalizeId(event.run_id) ? { run_id: event.run_id } : {}),\n ...(isNonNegativeInteger(event.value) ? { value: event.value } : {}),\n ...(props ? { props } : {}),\n };\n};\n\nconst normalizeId = (value: string | undefined): string | undefined =>\n value && value.length <= 48 ? value : undefined;\n\nconst isNonNegativeInteger = (value: number | undefined): value is number =>\n value !== undefined && Number.isInteger(value) && value >= 0;\n\nconst normalizeProps = (\n props: AssistantCloudEvent[\"props\"],\n): AssistantCloudEvent[\"props\"] => {\n if (!props) return undefined;\n const entries = Object.entries(props);\n if (\n entries.some(\n ([, value]) =>\n (typeof value === \"string\" && value.length > 256) ||\n (typeof value !== \"string\" &&\n typeof value !== \"number\" &&\n typeof value !== \"boolean\"),\n )\n ) {\n return undefined;\n }\n const value = Object.fromEntries(entries);\n return new TextEncoder().encode(JSON.stringify(value)).byteLength <= 1024\n ? value\n : undefined;\n};\n"],"mappings":"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"}
|
|
@@ -17,13 +17,24 @@ type GeneratePresignedUploadUrlResponse = {
|
|
|
17
17
|
signedUrl: string;
|
|
18
18
|
expiresAt: string;
|
|
19
19
|
publicUrl: string;
|
|
20
|
+
key?: string;
|
|
21
|
+
};
|
|
22
|
+
type GeneratePresignedDownloadUrlResponse = {
|
|
23
|
+
signedUrl: string;
|
|
24
|
+
expiresAt: string;
|
|
25
|
+
key: string;
|
|
20
26
|
};
|
|
21
27
|
declare class AssistantCloudFiles {
|
|
22
28
|
private cloud;
|
|
23
29
|
constructor(cloud: AssistantCloudAPI);
|
|
24
30
|
pdfToImages(body: PdfToImagesRequestBody): Promise<PdfToImagesResponse>;
|
|
25
31
|
generatePresignedUploadUrl(body: GeneratePresignedUploadUrlRequestBody): Promise<GeneratePresignedUploadUrlResponse>;
|
|
32
|
+
generatePresignedDownloadUrl(body: {
|
|
33
|
+
key: string;
|
|
34
|
+
} | {
|
|
35
|
+
url: string;
|
|
36
|
+
}): Promise<GeneratePresignedDownloadUrlResponse>;
|
|
26
37
|
}
|
|
27
38
|
//#endregion
|
|
28
|
-
export { AssistantCloudFiles };
|
|
39
|
+
export { AssistantCloudFiles, GeneratePresignedDownloadUrlResponse };
|
|
29
40
|
//# sourceMappingURL=AssistantCloudFiles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCloudFiles.d.ts","names":[],"sources":["../src/AssistantCloudFiles.ts"],"mappings":";;KAQK;EACH;EACA;;KAGG;EACH;EACA;EACA;;KAGG;EACH;;KAGG;EACH;EACA;EACA;EACA;;cAGW;UACH;EAEI,YAAA,OAAO;EAIN,YACX,MAAM,yBACL,QAAQ;EAyBE,2BACX,MAAM,wCACL,QAAQ"}
|
|
1
|
+
{"version":3,"file":"AssistantCloudFiles.d.ts","names":[],"sources":["../src/AssistantCloudFiles.ts"],"mappings":";;KAQK;EACH;EACA;;KAGG;EACH;EACA;EACA;;KAGG;EACH;;KAGG;EACH;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;;cAGW;UACH;EAEI,YAAA,OAAO;EAIN,YACX,MAAM,yBACL,QAAQ;EAyBE,2BACX,MAAM,wCACL,QAAQ;EAqCE,6BACX;IAAQ;;IAAkB;MACzB,QAAQ"}
|
|
@@ -25,7 +25,19 @@ var AssistantCloudFiles = class {
|
|
|
25
25
|
success: readCloudBoolean(response.success, "presigned upload response.success"),
|
|
26
26
|
signedUrl: readCloudString(response.signedUrl, "presigned upload response.signedUrl"),
|
|
27
27
|
expiresAt: readCloudString(response.expiresAt, "presigned upload response.expiresAt"),
|
|
28
|
-
publicUrl: readCloudString(response.publicUrl, "presigned upload response.publicUrl")
|
|
28
|
+
publicUrl: readCloudString(response.publicUrl, "presigned upload response.publicUrl"),
|
|
29
|
+
..."key" in response ? { key: readCloudString(response.key, "presigned upload response.key") } : {}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async generatePresignedDownloadUrl(body) {
|
|
33
|
+
const response = readCloudRecord(await this.cloud.makeRequest("/files/attachments/generate-presigned-download-url", {
|
|
34
|
+
method: "POST",
|
|
35
|
+
body
|
|
36
|
+
}), "presigned download response");
|
|
37
|
+
return {
|
|
38
|
+
signedUrl: readCloudString(response.signedUrl, "presigned download response.signedUrl"),
|
|
39
|
+
expiresAt: readCloudString(response.expiresAt, "presigned download response.expiresAt"),
|
|
40
|
+
key: readCloudString(response.key, "presigned download response.key")
|
|
29
41
|
};
|
|
30
42
|
}
|
|
31
43
|
};
|