roe-typescript 0.1.3 → 1.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 +30 -14
- package/dist/api/agents.d.ts +33 -25
- package/dist/api/agents.js +213 -114
- package/dist/api/policies.d.ts +15 -9
- package/dist/api/policies.js +75 -31
- package/dist/api/users.d.ts +18 -0
- package/dist/api/users.js +17 -0
- package/dist/client.d.ts +6 -2
- package/dist/client.js +17 -5
- package/dist/exceptions.js +8 -0
- package/dist/generated/client.d.ts +12 -0
- package/dist/generated/client.js +55 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +9 -5
- package/dist/models/file.d.ts +17 -0
- package/dist/models/file.js +36 -0
- package/dist/models/job.d.ts +28 -4
- package/dist/models/job.js +39 -21
- package/dist/utils/dynamicInputs.d.ts +30 -0
- package/dist/utils/dynamicInputs.js +114 -0
- package/dist/utils/middleware.d.ts +6 -0
- package/dist/utils/middleware.js +80 -0
- package/package.json +6 -6
- package/dist/models/agent.d.ts +0 -78
- package/dist/models/agent.js +0 -40
- package/dist/models/policy.d.ts +0 -19
- package/dist/models/policy.js +0 -1
- package/dist/models/responses.d.ts +0 -81
- package/dist/models/responses.js +0 -48
- package/dist/models/user.d.ts +0 -6
- package/dist/models/user.js +0 -1
- package/dist/utils/httpClient.d.ts +0 -30
- package/dist/utils/httpClient.js +0 -232
package/dist/api/policies.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { PaginationHelper } from "../utils/pagination.js";
|
|
2
1
|
import { isUuidString } from "../utils/fileDetection.js";
|
|
3
2
|
import { BadRequestError, RoeAPIException } from "../exceptions.js";
|
|
4
3
|
/**
|
|
@@ -14,77 +13,122 @@ export class PolicyVersionsAPI {
|
|
|
14
13
|
constructor(policiesApi) {
|
|
15
14
|
this.policiesApi = policiesApi;
|
|
16
15
|
}
|
|
17
|
-
get
|
|
18
|
-
return this.policiesApi.
|
|
16
|
+
get raw() {
|
|
17
|
+
return this.policiesApi.raw;
|
|
18
|
+
}
|
|
19
|
+
get organizationId() {
|
|
20
|
+
return this.policiesApi.config.organizationId;
|
|
19
21
|
}
|
|
20
22
|
async list(policyId) {
|
|
21
23
|
validateUuid(policyId, "policyId");
|
|
22
|
-
const data = await this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const { data } = await this.raw.GET("/v1/policies/{policy_id}/versions/", {
|
|
25
|
+
params: {
|
|
26
|
+
path: { policy_id: policyId },
|
|
27
|
+
query: { organization_id: this.organizationId },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
return data?.results ?? [];
|
|
26
31
|
}
|
|
27
32
|
async retrieve(policyId, versionId) {
|
|
28
33
|
validateUuid(policyId, "policyId");
|
|
29
34
|
validateUuid(versionId, "versionId");
|
|
30
|
-
|
|
35
|
+
const { data } = await this.raw.GET("/v1/policies/{policy_id}/versions/{version_id}/", {
|
|
36
|
+
params: {
|
|
37
|
+
path: { policy_id: policyId, version_id: versionId },
|
|
38
|
+
query: { organization_id: this.organizationId },
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return data;
|
|
31
42
|
}
|
|
32
43
|
async create(params) {
|
|
33
44
|
validateUuid(params.policyId, "policyId");
|
|
34
45
|
if (params.baseVersionId !== undefined)
|
|
35
46
|
validateUuid(params.baseVersionId, "baseVersionId");
|
|
36
|
-
const
|
|
47
|
+
const body = {
|
|
48
|
+
content: params.content,
|
|
49
|
+
};
|
|
37
50
|
if (params.versionName !== undefined)
|
|
38
|
-
|
|
51
|
+
body.version_name = params.versionName;
|
|
39
52
|
if (params.baseVersionId !== undefined)
|
|
40
|
-
|
|
41
|
-
const data = await this.
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
body.base_version_id = params.baseVersionId;
|
|
54
|
+
const { data } = await this.raw.POST("/v1/policies/{policy_id}/versions/", {
|
|
55
|
+
params: {
|
|
56
|
+
path: { policy_id: params.policyId },
|
|
57
|
+
query: { organization_id: this.organizationId },
|
|
58
|
+
},
|
|
59
|
+
body,
|
|
44
60
|
});
|
|
45
|
-
if (!data
|
|
61
|
+
if (!data?.id) {
|
|
46
62
|
throw new RoeAPIException(`Unexpected response from server: ${JSON.stringify(data)}`);
|
|
47
63
|
}
|
|
48
|
-
// POST returns partial
|
|
64
|
+
// POST returns a partial CreatePolicyVersion; re-fetch to get the full version.
|
|
49
65
|
return this.retrieve(params.policyId, data.id);
|
|
50
66
|
}
|
|
51
67
|
}
|
|
52
68
|
export class PoliciesAPI {
|
|
53
|
-
constructor(config,
|
|
69
|
+
constructor(config, raw) {
|
|
54
70
|
this.config = config;
|
|
55
|
-
this.
|
|
71
|
+
this.raw = raw;
|
|
56
72
|
this.versions = new PolicyVersionsAPI(this);
|
|
57
73
|
}
|
|
58
74
|
async list(params) {
|
|
59
|
-
const
|
|
60
|
-
|
|
75
|
+
const { data } = await this.raw.GET("/v1/policies/", {
|
|
76
|
+
params: {
|
|
77
|
+
query: {
|
|
78
|
+
organization_id: this.config.organizationId,
|
|
79
|
+
page: params?.page,
|
|
80
|
+
page_size: params?.pageSize,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
return data;
|
|
61
85
|
}
|
|
62
86
|
async retrieve(policyId) {
|
|
63
87
|
validateUuid(policyId, "policyId");
|
|
64
|
-
|
|
88
|
+
const { data } = await this.raw.GET("/v1/policies/{id}/", {
|
|
89
|
+
params: {
|
|
90
|
+
path: { id: policyId },
|
|
91
|
+
query: { organization_id: this.config.organizationId },
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return data;
|
|
65
95
|
}
|
|
66
96
|
async create(params) {
|
|
67
|
-
const
|
|
97
|
+
const body = {
|
|
68
98
|
name: params.name,
|
|
69
99
|
content: params.content,
|
|
70
100
|
description: params.description ?? "",
|
|
71
|
-
|
|
101
|
+
version_name: params.versionName ?? "version 1",
|
|
72
102
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
103
|
+
const { data } = await this.raw.POST("/v1/policies/", {
|
|
104
|
+
params: { query: { organization_id: this.config.organizationId } },
|
|
105
|
+
body,
|
|
106
|
+
});
|
|
107
|
+
return data;
|
|
76
108
|
}
|
|
77
109
|
async update(policyId, updates) {
|
|
78
110
|
validateUuid(policyId, "policyId");
|
|
79
|
-
const
|
|
111
|
+
const body = {};
|
|
80
112
|
if (updates.name !== undefined)
|
|
81
|
-
|
|
113
|
+
body.name = updates.name;
|
|
82
114
|
if (updates.description !== undefined)
|
|
83
|
-
|
|
84
|
-
|
|
115
|
+
body.description = updates.description;
|
|
116
|
+
const { data } = await this.raw.PATCH("/v1/policies/{id}/", {
|
|
117
|
+
params: {
|
|
118
|
+
path: { id: policyId },
|
|
119
|
+
query: { organization_id: this.config.organizationId },
|
|
120
|
+
},
|
|
121
|
+
body,
|
|
122
|
+
});
|
|
123
|
+
return data;
|
|
85
124
|
}
|
|
86
125
|
async delete(policyId) {
|
|
87
126
|
validateUuid(policyId, "policyId");
|
|
88
|
-
await this.
|
|
127
|
+
await this.raw.DELETE("/v1/policies/{id}/", {
|
|
128
|
+
params: {
|
|
129
|
+
path: { id: policyId },
|
|
130
|
+
query: { organization_id: this.config.organizationId },
|
|
131
|
+
},
|
|
132
|
+
});
|
|
89
133
|
}
|
|
90
134
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RoeConfig } from "../config.js";
|
|
2
|
+
import type { RoeRawClient } from "../generated/client.js";
|
|
3
|
+
import type { components } from "../generated/schema.js";
|
|
4
|
+
type User = components["schemas"]["UserInfo"];
|
|
5
|
+
export declare class UsersAPI {
|
|
6
|
+
readonly config: RoeConfig;
|
|
7
|
+
readonly raw: RoeRawClient;
|
|
8
|
+
constructor(config: RoeConfig, raw: RoeRawClient);
|
|
9
|
+
/**
|
|
10
|
+
* Retrieve the currently authenticated user.
|
|
11
|
+
*
|
|
12
|
+
* Wraps `GET /v1/users/current_user/`. The OpenAPI spec marks the response
|
|
13
|
+
* body as undocumented, so we read JSON via openapi-fetch's `parseAs` escape
|
|
14
|
+
* hatch and cast the result to `UserInfo` (the schema the backend returns).
|
|
15
|
+
*/
|
|
16
|
+
me(): Promise<User>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class UsersAPI {
|
|
2
|
+
constructor(config, raw) {
|
|
3
|
+
this.config = config;
|
|
4
|
+
this.raw = raw;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Retrieve the currently authenticated user.
|
|
8
|
+
*
|
|
9
|
+
* Wraps `GET /v1/users/current_user/`. The OpenAPI spec marks the response
|
|
10
|
+
* body as undocumented, so we read JSON via openapi-fetch's `parseAs` escape
|
|
11
|
+
* hatch and cast the result to `UserInfo` (the schema the backend returns).
|
|
12
|
+
*/
|
|
13
|
+
async me() {
|
|
14
|
+
const result = await this.raw.GET("/v1/users/current_user/", { parseAs: "json" });
|
|
15
|
+
return result.data;
|
|
16
|
+
}
|
|
17
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { AgentsAPI } from "./api/agents.js";
|
|
2
2
|
import { PoliciesAPI } from "./api/policies.js";
|
|
3
|
+
import { UsersAPI } from "./api/users.js";
|
|
3
4
|
import { RoeAuth } from "./auth.js";
|
|
4
5
|
import { RoeConfig, RoeConfigInput } from "./config.js";
|
|
5
|
-
import {
|
|
6
|
+
import { type RoeRawClient } from "./generated/client.js";
|
|
6
7
|
export declare class RoeClient {
|
|
7
8
|
readonly config: RoeConfig;
|
|
8
9
|
readonly auth: RoeAuth;
|
|
9
|
-
readonly
|
|
10
|
+
readonly raw: RoeRawClient;
|
|
10
11
|
private readonly _agents;
|
|
11
12
|
private readonly _policies;
|
|
13
|
+
private readonly _users;
|
|
12
14
|
constructor(input?: RoeConfigInput);
|
|
13
15
|
get agents(): AgentsAPI;
|
|
14
16
|
get policies(): PoliciesAPI;
|
|
17
|
+
get users(): UsersAPI;
|
|
18
|
+
/** No-op kept for source compatibility; the raw client manages its own connections. */
|
|
15
19
|
close(): void;
|
|
16
20
|
}
|
package/dist/client.js
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { AgentsAPI } from "./api/agents.js";
|
|
2
2
|
import { PoliciesAPI } from "./api/policies.js";
|
|
3
|
+
import { UsersAPI } from "./api/users.js";
|
|
3
4
|
import { RoeAuth } from "./auth.js";
|
|
4
5
|
import { RoeConfig } from "./config.js";
|
|
5
|
-
import {
|
|
6
|
+
import { createRoeRawClient } from "./generated/client.js";
|
|
6
7
|
export class RoeClient {
|
|
7
8
|
constructor(input = {}) {
|
|
8
9
|
this.config = RoeConfig.fromEnv(input);
|
|
9
10
|
this.auth = new RoeAuth(this.config);
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
this.raw = createRoeRawClient({
|
|
12
|
+
baseUrl: this.config.baseUrl,
|
|
13
|
+
// Auth headers are injected per-request by authMiddleware; no static
|
|
14
|
+
// headers option here so a stale snapshot can't shadow the live token.
|
|
15
|
+
auth: this.auth,
|
|
16
|
+
maxRetries: this.config.maxRetries,
|
|
17
|
+
});
|
|
18
|
+
this._agents = new AgentsAPI(this.config, this.raw);
|
|
19
|
+
this._policies = new PoliciesAPI(this.config, this.raw);
|
|
20
|
+
this._users = new UsersAPI(this.config, this.raw);
|
|
13
21
|
}
|
|
14
22
|
get agents() {
|
|
15
23
|
return this._agents;
|
|
@@ -17,7 +25,11 @@ export class RoeClient {
|
|
|
17
25
|
get policies() {
|
|
18
26
|
return this._policies;
|
|
19
27
|
}
|
|
28
|
+
get users() {
|
|
29
|
+
return this._users;
|
|
30
|
+
}
|
|
31
|
+
/** No-op kept for source compatibility; the raw client manages its own connections. */
|
|
20
32
|
close() {
|
|
21
|
-
|
|
33
|
+
/* no-op */
|
|
22
34
|
}
|
|
23
35
|
}
|
package/dist/exceptions.js
CHANGED
|
@@ -74,6 +74,14 @@ export function extractErrorMessage(data, statusCode) {
|
|
|
74
74
|
return firstError.message;
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
for (const value of Object.values(obj)) {
|
|
78
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
79
|
+
return value.map(String).join("; ");
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === "string" && value.length > 0) {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
77
85
|
}
|
|
78
86
|
// Fallback to generic message
|
|
79
87
|
return `HTTP ${statusCode} error`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { components, paths } from "./schema.js";
|
|
2
|
+
import type { RoeAuth } from "../auth.js";
|
|
3
|
+
export type RoeApiComponents = components;
|
|
4
|
+
export type RoeApiPaths = paths;
|
|
5
|
+
export type RoeRawClient = ReturnType<typeof createRoeRawClient>;
|
|
6
|
+
export declare function createRoeRawClient(options: {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
headers?: HeadersInit;
|
|
9
|
+
fetch?: typeof fetch;
|
|
10
|
+
auth?: RoeAuth;
|
|
11
|
+
maxRetries?: number;
|
|
12
|
+
}): import("openapi-fetch").Client<paths, `${string}/${string}`>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Hand-maintained boilerplate. Lives under src/generated/ alongside schema.d.ts
|
|
2
|
+
// because it imports from it. Drift CI regenerates schema.d.ts and verifies
|
|
3
|
+
// this file is unchanged.
|
|
4
|
+
import createClient from "openapi-fetch";
|
|
5
|
+
import { authMiddleware, errorMiddleware, retryMiddleware, } from "../utils/middleware.js";
|
|
6
|
+
// Default fetch: resolves globalThis.fetch at call time so vitest's
|
|
7
|
+
// vi.stubGlobal('fetch', ...) is honored. In Node 18+ globalThis.fetch is
|
|
8
|
+
// undici-backed, so streaming bodies work end-to-end. For the multipart-with-
|
|
9
|
+
// Node-stream case in AgentsAPI we'll import undici.fetch directly.
|
|
10
|
+
const defaultFetch = (...args) => globalThis.fetch(...args);
|
|
11
|
+
function isNetworkFetchError(error) {
|
|
12
|
+
return error instanceof TypeError && /fetch failed/i.test(error.message);
|
|
13
|
+
}
|
|
14
|
+
function networkRetryFetch(fetchImpl, maxRetries) {
|
|
15
|
+
return async (input, init) => {
|
|
16
|
+
let attempt = 0;
|
|
17
|
+
while (true) {
|
|
18
|
+
try {
|
|
19
|
+
// `Request` bodies are single-use. Clone before each attempt so a
|
|
20
|
+
// transient socket failure can be retried without disturbing the
|
|
21
|
+
// original request object openapi-fetch handed us.
|
|
22
|
+
const retryableInput = maxRetries > 0 && input instanceof Request ? input.clone() : input;
|
|
23
|
+
return await fetchImpl(retryableInput, init);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (attempt >= maxRetries || !isNetworkFetchError(error)) {
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
const backoffMs = Math.min(1000 * 2 ** attempt, 10000);
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
31
|
+
attempt += 1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function createRoeRawClient(options) {
|
|
37
|
+
const maxRetries = options.maxRetries ?? 0;
|
|
38
|
+
const client = createClient({
|
|
39
|
+
baseUrl: options.baseUrl,
|
|
40
|
+
headers: options.headers,
|
|
41
|
+
fetch: networkRetryFetch(options.fetch ?? defaultFetch, maxRetries),
|
|
42
|
+
});
|
|
43
|
+
// openapi-fetch runs onRequest in registration order and onResponse in
|
|
44
|
+
// REVERSE registration order. We want responses to flow auth -> retry ->
|
|
45
|
+
// error so the error middleware sees the final post-retry response.
|
|
46
|
+
// Registration order is therefore the inverse: error -> retry -> auth.
|
|
47
|
+
client.use(errorMiddleware);
|
|
48
|
+
if (maxRetries > 0) {
|
|
49
|
+
client.use(retryMiddleware(maxRetries));
|
|
50
|
+
}
|
|
51
|
+
if (options.auth) {
|
|
52
|
+
client.use(authMiddleware(options.auth));
|
|
53
|
+
}
|
|
54
|
+
return client;
|
|
55
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,11 +2,13 @@ export { RoeClient } from "./client.js";
|
|
|
2
2
|
export { RoeConfig } from "./config.js";
|
|
3
3
|
export type { RoeConfigInput } from "./config.js";
|
|
4
4
|
export { RoeAuth } from "./auth.js";
|
|
5
|
+
export { createRoeRawClient } from "./generated/client.js";
|
|
6
|
+
export type { RoeApiComponents, RoeApiPaths, RoeRawClient } from "./generated/client.js";
|
|
7
|
+
export type { components, paths } from "./generated/schema.js";
|
|
5
8
|
export * from "./exceptions.js";
|
|
6
9
|
export * from "./api/agents.js";
|
|
7
10
|
export * from "./api/policies.js";
|
|
8
|
-
export * from "./
|
|
9
|
-
export
|
|
10
|
-
export
|
|
11
|
-
export
|
|
12
|
-
export * from "./models/file.js";
|
|
11
|
+
export * from "./api/users.js";
|
|
12
|
+
export { Job, JobBatch, JobStatus } from "./models/job.js";
|
|
13
|
+
export type { JobResult, JobBatchItem } from "./models/job.js";
|
|
14
|
+
export { FileUpload } from "./models/file.js";
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
export { RoeClient } from "./client.js";
|
|
2
2
|
export { RoeConfig } from "./config.js";
|
|
3
3
|
export { RoeAuth } from "./auth.js";
|
|
4
|
+
export { createRoeRawClient } from "./generated/client.js";
|
|
4
5
|
export * from "./exceptions.js";
|
|
5
6
|
export * from "./api/agents.js";
|
|
6
7
|
export * from "./api/policies.js";
|
|
7
|
-
export * from "./
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export * from "./api/users.js";
|
|
9
|
+
export { Job, JobBatch, JobStatus } from "./models/job.js";
|
|
10
|
+
export { FileUpload } from "./models/file.js";
|
|
11
|
+
// Hand-written response models removed in v1.0 (Agent, BaseAgent, Policy, PolicyVersion,
|
|
12
|
+
// AgentVersion, AgentDatum, AgentJobResult, AgentJobStatus, Reference, JobDataDeleteResponse,
|
|
13
|
+
// PaginatedResponse, UserInfo). Import the generated equivalents instead, e.g.:
|
|
14
|
+
// import type { components } from "@roe-ai/typescript";
|
|
15
|
+
// type Policy = components["schemas"]["Policy"];
|
package/dist/models/file.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export declare class FileUpload {
|
|
|
10
10
|
readonly filename?: string;
|
|
11
11
|
readonly mimeType?: string;
|
|
12
12
|
private _openedStream;
|
|
13
|
+
/**
|
|
14
|
+
* Cached Blob built from the stream-backed source. Streams can only be read
|
|
15
|
+
* once, so we buffer their bytes on the first `toBlob()` call and reuse the
|
|
16
|
+
* result on retries — otherwise repeated calls would silently upload an
|
|
17
|
+
* empty file.
|
|
18
|
+
*/
|
|
19
|
+
private _streamBlob;
|
|
13
20
|
constructor(init: FileUploadInit);
|
|
14
21
|
get effectiveFilename(): string;
|
|
15
22
|
get effectiveMimeType(): string;
|
|
@@ -18,6 +25,16 @@ export declare class FileUpload {
|
|
|
18
25
|
* Track opened streams so they can be closed later.
|
|
19
26
|
*/
|
|
20
27
|
open(): NodeJS.ReadableStream;
|
|
28
|
+
/**
|
|
29
|
+
* Loads the file contents as a Blob with the resolved MIME type.
|
|
30
|
+
*
|
|
31
|
+
* Path-backed uploads are read fully into memory before the Blob is built.
|
|
32
|
+
* That keeps the wrapper cross-runtime (browser + Node) at the cost of
|
|
33
|
+
* peak RAM = file size. Real-world files are well under MAX_FILE_SIZE,
|
|
34
|
+
* but if you hit memory pressure switch to a streaming `Readable` and
|
|
35
|
+
* use `undici`-flavored FormData.
|
|
36
|
+
*/
|
|
37
|
+
toBlob(): Promise<Blob>;
|
|
21
38
|
/**
|
|
22
39
|
* Closes any opened file stream to prevent resource leaks.
|
|
23
40
|
* Should be called after the file upload is complete.
|
package/dist/models/file.js
CHANGED
|
@@ -7,6 +7,13 @@ const MAX_FILE_SIZE = 2 * 1024 * 1024 * 1024;
|
|
|
7
7
|
export class FileUpload {
|
|
8
8
|
constructor(init) {
|
|
9
9
|
this._openedStream = null;
|
|
10
|
+
/**
|
|
11
|
+
* Cached Blob built from the stream-backed source. Streams can only be read
|
|
12
|
+
* once, so we buffer their bytes on the first `toBlob()` call and reuse the
|
|
13
|
+
* result on retries — otherwise repeated calls would silently upload an
|
|
14
|
+
* empty file.
|
|
15
|
+
*/
|
|
16
|
+
this._streamBlob = null;
|
|
10
17
|
this.path = init.path;
|
|
11
18
|
this.file = init.file;
|
|
12
19
|
this.filename = init.filename;
|
|
@@ -58,6 +65,35 @@ export class FileUpload {
|
|
|
58
65
|
}
|
|
59
66
|
throw new Error("No file source available");
|
|
60
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Loads the file contents as a Blob with the resolved MIME type.
|
|
70
|
+
*
|
|
71
|
+
* Path-backed uploads are read fully into memory before the Blob is built.
|
|
72
|
+
* That keeps the wrapper cross-runtime (browser + Node) at the cost of
|
|
73
|
+
* peak RAM = file size. Real-world files are well under MAX_FILE_SIZE,
|
|
74
|
+
* but if you hit memory pressure switch to a streaming `Readable` and
|
|
75
|
+
* use `undici`-flavored FormData.
|
|
76
|
+
*/
|
|
77
|
+
async toBlob() {
|
|
78
|
+
if (this.path) {
|
|
79
|
+
const buf = await fs.promises.readFile(this.path);
|
|
80
|
+
return new Blob([buf], { type: this.effectiveMimeType });
|
|
81
|
+
}
|
|
82
|
+
if (this.file) {
|
|
83
|
+
// Streams are single-shot. Buffer the bytes once and cache the resulting
|
|
84
|
+
// Blob so retry attempts (which call `toBlob()` again) don't end up
|
|
85
|
+
// uploading a zero-byte body.
|
|
86
|
+
if (this._streamBlob)
|
|
87
|
+
return this._streamBlob;
|
|
88
|
+
const chunks = [];
|
|
89
|
+
for await (const chunk of this.file) {
|
|
90
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
91
|
+
}
|
|
92
|
+
this._streamBlob = new Blob([Buffer.concat(chunks)], { type: this.effectiveMimeType });
|
|
93
|
+
return this._streamBlob;
|
|
94
|
+
}
|
|
95
|
+
throw new Error("No file source available");
|
|
96
|
+
}
|
|
61
97
|
/**
|
|
62
98
|
* Closes any opened file stream to prevent resource leaks.
|
|
63
99
|
* Should be called after the file upload is complete.
|
package/dist/models/job.d.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { AgentsAPI } from "../api/agents.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { components } from "../generated/schema.js";
|
|
3
|
+
export declare enum JobStatus {
|
|
4
|
+
PENDING = 0,
|
|
5
|
+
STARTED = 1,
|
|
6
|
+
RETRY = 2,
|
|
7
|
+
SUCCESS = 3,
|
|
8
|
+
FAILURE = 4,
|
|
9
|
+
CANCELLED = 5,
|
|
10
|
+
CACHED = 6
|
|
11
|
+
}
|
|
12
|
+
type AgentJobStatus = components["schemas"]["AgentJobStatus"];
|
|
13
|
+
type AgentJobResultResponse = components["schemas"]["AgentJobResultResponse"];
|
|
14
|
+
type AgentJobResultItem = components["schemas"]["AgentJobResultItem"];
|
|
15
|
+
type AgentDatum = components["schemas"]["AgentDatum"];
|
|
16
|
+
/** Result returned by `Job.wait()` — generated body merged with terminal status + error. */
|
|
17
|
+
export type JobResult = AgentJobResultResponse & {
|
|
18
|
+
status: number | null;
|
|
19
|
+
error_message: string | null;
|
|
20
|
+
};
|
|
21
|
+
/** Result returned by `JobBatch.wait()` — per-job item merged with normalized error message. */
|
|
22
|
+
export type JobBatchItem = AgentJobResultItem & {
|
|
23
|
+
outputs: AgentDatum[];
|
|
24
|
+
error_message: string | null;
|
|
25
|
+
};
|
|
3
26
|
export declare class Job {
|
|
4
27
|
private readonly agentsApi;
|
|
5
28
|
private readonly jobId;
|
|
@@ -13,9 +36,9 @@ export declare class Job {
|
|
|
13
36
|
wait(params?: {
|
|
14
37
|
intervalSeconds?: number;
|
|
15
38
|
timeoutSeconds?: number;
|
|
16
|
-
}): Promise<
|
|
39
|
+
}): Promise<JobResult>;
|
|
17
40
|
retrieveStatus(): Promise<AgentJobStatus>;
|
|
18
|
-
retrieveResult(): Promise<
|
|
41
|
+
retrieveResult(): Promise<AgentJobResultResponse>;
|
|
19
42
|
}
|
|
20
43
|
export declare class JobBatch {
|
|
21
44
|
private readonly agentsApi;
|
|
@@ -32,6 +55,7 @@ export declare class JobBatch {
|
|
|
32
55
|
wait(params?: {
|
|
33
56
|
intervalSeconds?: number;
|
|
34
57
|
timeoutSeconds?: number;
|
|
35
|
-
}): Promise<
|
|
58
|
+
}): Promise<JobBatchItem[]>;
|
|
36
59
|
retrieveStatus(): Promise<Record<string, AgentJobStatus>>;
|
|
37
60
|
}
|
|
61
|
+
export {};
|
package/dist/models/job.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
export var JobStatus;
|
|
2
|
+
(function (JobStatus) {
|
|
3
|
+
JobStatus[JobStatus["PENDING"] = 0] = "PENDING";
|
|
4
|
+
JobStatus[JobStatus["STARTED"] = 1] = "STARTED";
|
|
5
|
+
JobStatus[JobStatus["RETRY"] = 2] = "RETRY";
|
|
6
|
+
JobStatus[JobStatus["SUCCESS"] = 3] = "SUCCESS";
|
|
7
|
+
JobStatus[JobStatus["FAILURE"] = 4] = "FAILURE";
|
|
8
|
+
JobStatus[JobStatus["CANCELLED"] = 5] = "CANCELLED";
|
|
9
|
+
JobStatus[JobStatus["CACHED"] = 6] = "CACHED";
|
|
10
|
+
})(JobStatus || (JobStatus = {}));
|
|
2
11
|
export class Job {
|
|
3
12
|
constructor(opts) {
|
|
4
13
|
this.agentsApi = opts.agentsApi;
|
|
@@ -41,9 +50,11 @@ export class Job {
|
|
|
41
50
|
output_tokens: null,
|
|
42
51
|
};
|
|
43
52
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
return {
|
|
54
|
+
...result,
|
|
55
|
+
status: code,
|
|
56
|
+
error_message: status.error_message ?? null,
|
|
57
|
+
};
|
|
47
58
|
}
|
|
48
59
|
if (Date.now() - start > timeoutSeconds * 1000) {
|
|
49
60
|
throw new Error(`Job ${this.jobId} did not complete within ${timeoutSeconds} seconds`);
|
|
@@ -85,18 +96,25 @@ export class JobBatch {
|
|
|
85
96
|
const statusBatch = await this.agentsApi.jobs.retrieveStatusMany(pending);
|
|
86
97
|
const completedIds = [];
|
|
87
98
|
for (const status of statusBatch) {
|
|
99
|
+
// The bulk status endpoint returns AgentJobStatus[]; the schema lacks an `id`
|
|
100
|
+
// field on each, but the backend includes it in practice — extract via cast.
|
|
101
|
+
const id = status.id;
|
|
102
|
+
if (!id) {
|
|
103
|
+
throw new Error("AgentJobStatus response is missing an `id` field; backend schema may have changed");
|
|
104
|
+
}
|
|
88
105
|
const code = status.status;
|
|
89
106
|
if (code === JobStatus.SUCCESS ||
|
|
90
107
|
code === JobStatus.CACHED ||
|
|
91
108
|
code === JobStatus.FAILURE ||
|
|
92
109
|
code === JobStatus.CANCELLED) {
|
|
93
|
-
completedIds.push(
|
|
110
|
+
completedIds.push(id);
|
|
94
111
|
}
|
|
95
112
|
if (code !== undefined && code !== null) {
|
|
96
|
-
this.statuses[
|
|
113
|
+
this.statuses[id] = {
|
|
114
|
+
id,
|
|
97
115
|
status: code,
|
|
98
|
-
timestamp: status.timestamp
|
|
99
|
-
error_message: status.error_message
|
|
116
|
+
timestamp: status.timestamp,
|
|
117
|
+
error_message: status.error_message,
|
|
100
118
|
};
|
|
101
119
|
}
|
|
102
120
|
}
|
|
@@ -112,7 +130,6 @@ export class JobBatch {
|
|
|
112
130
|
return s === JobStatus.FAILURE || s === JobStatus.CANCELLED;
|
|
113
131
|
});
|
|
114
132
|
if (failedIds.length < completedIds.length) {
|
|
115
|
-
// Some successful jobs had their results lost — must propagate
|
|
116
133
|
throw err;
|
|
117
134
|
}
|
|
118
135
|
resultBatch = failedIds.map((id) => ({
|
|
@@ -120,6 +137,7 @@ export class JobBatch {
|
|
|
120
137
|
status: this.statuses[id]?.status ?? null,
|
|
121
138
|
agent_id: null,
|
|
122
139
|
agent_version_id: null,
|
|
140
|
+
cost: null,
|
|
123
141
|
inputs: null,
|
|
124
142
|
result: null,
|
|
125
143
|
corrected_outputs: null,
|
|
@@ -132,11 +150,10 @@ export class JobBatch {
|
|
|
132
150
|
const isFailed = jobStatus === JobStatus.FAILURE || jobStatus === JobStatus.CANCELLED;
|
|
133
151
|
if (!res.agent_id || !res.agent_version_id) {
|
|
134
152
|
if (!isFailed) {
|
|
135
|
-
const id = res.id ??
|
|
153
|
+
const id = res.id ?? "unknown";
|
|
136
154
|
throw new Error(`Job ${id} missing agent identifiers`);
|
|
137
155
|
}
|
|
138
156
|
}
|
|
139
|
-
// Use corrected_outputs as fallback if result is null/undefined
|
|
140
157
|
const rawOutputs = res.result ?? res.corrected_outputs;
|
|
141
158
|
let outputs;
|
|
142
159
|
if (rawOutputs == null) {
|
|
@@ -155,13 +172,9 @@ export class JobBatch {
|
|
|
155
172
|
outputs = rawOutputs;
|
|
156
173
|
}
|
|
157
174
|
this.completed[res.id] = {
|
|
158
|
-
|
|
159
|
-
agent_version_id: res.agent_version_id ?? "",
|
|
160
|
-
inputs: res.inputs ?? [],
|
|
161
|
-
input_tokens: res.input_tokens,
|
|
162
|
-
output_tokens: res.output_tokens,
|
|
163
|
-
outputs,
|
|
175
|
+
...res,
|
|
164
176
|
status: jobStatus,
|
|
177
|
+
outputs,
|
|
165
178
|
error_message: this.statuses[res.id]?.error_message ?? null,
|
|
166
179
|
};
|
|
167
180
|
}
|
|
@@ -192,14 +205,19 @@ export class JobBatch {
|
|
|
192
205
|
if (toQuery.length) {
|
|
193
206
|
const batchStatuses = await this.agentsApi.jobs.retrieveStatusMany(toQuery);
|
|
194
207
|
for (const s of batchStatuses) {
|
|
208
|
+
const id = s.id;
|
|
209
|
+
if (!id) {
|
|
210
|
+
throw new Error("AgentJobStatus response is missing an `id` field; backend schema may have changed");
|
|
211
|
+
}
|
|
195
212
|
if (s.status !== undefined && s.status !== null) {
|
|
196
213
|
const js = {
|
|
214
|
+
id,
|
|
197
215
|
status: s.status,
|
|
198
|
-
timestamp: s.timestamp
|
|
199
|
-
error_message: s.error_message
|
|
216
|
+
timestamp: s.timestamp,
|
|
217
|
+
error_message: s.error_message,
|
|
200
218
|
};
|
|
201
|
-
this.statuses[
|
|
202
|
-
statusMap[
|
|
219
|
+
this.statuses[id] = js;
|
|
220
|
+
statusMap[id] = js;
|
|
203
221
|
}
|
|
204
222
|
}
|
|
205
223
|
}
|