@renderinc/sdk 0.2.0 → 0.3.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 +32 -22
- package/dist/experimental/experimental.d.ts +6 -2
- package/dist/experimental/experimental.d.ts.map +1 -1
- package/dist/experimental/experimental.js +9 -3
- package/dist/experimental/index.d.ts +2 -2
- package/dist/experimental/index.d.ts.map +1 -1
- package/dist/experimental/index.js +6 -5
- package/dist/experimental/object/api.d.ts +12 -0
- package/dist/experimental/object/api.d.ts.map +1 -0
- package/dist/experimental/object/api.js +61 -0
- package/dist/experimental/object/client.d.ts +23 -0
- package/dist/experimental/object/client.d.ts.map +1 -0
- package/dist/experimental/object/client.js +168 -0
- package/dist/experimental/object/index.d.ts +5 -0
- package/dist/experimental/object/index.d.ts.map +1 -0
- package/dist/experimental/object/index.js +8 -0
- package/dist/experimental/object/types.d.ts +66 -0
- package/dist/experimental/object/types.d.ts.map +1 -0
- package/dist/generated/schema.d.ts +289 -22
- package/dist/generated/schema.d.ts.map +1 -1
- package/dist/workflows/uds.d.ts.map +1 -1
- package/dist/workflows/uds.js +27 -51
- package/package.json +12 -8
- package/CHANGELOG.md +0 -26
- package/biome.json +0 -84
- package/dist/workflows/client/errors.d.ts +0 -25
- package/dist/workflows/client/errors.d.ts.map +0 -1
- package/dist/workflows/client/errors.js +0 -56
- package/dist/workflows/client/schema.d.ts +0 -9322
- package/dist/workflows/client/schema.d.ts.map +0 -1
- package/dist/workflows/client/workflows.d.ts +0 -15
- package/dist/workflows/client/workflows.d.ts.map +0 -1
- package/dist/workflows/client/workflows.js +0 -63
- package/examples/client/main.ts +0 -42
- package/examples/client/package-lock.json +0 -601
- package/examples/client/package.json +0 -16
- package/examples/client/tsconfig.json +0 -17
- package/examples/task/main.ts +0 -90
- package/examples/task/package-lock.json +0 -585
- package/examples/task/package.json +0 -16
- package/examples/task/tsconfig.json +0 -17
- package/src/errors.ts +0 -73
- package/src/experimental/blob/api.ts +0 -91
- package/src/experimental/blob/client.ts +0 -317
- package/src/experimental/blob/index.ts +0 -22
- package/src/experimental/blob/types.ts +0 -131
- package/src/experimental/experimental.ts +0 -33
- package/src/experimental/index.ts +0 -24
- package/src/generated/schema.ts +0 -12729
- package/src/index.ts +0 -7
- package/src/render.ts +0 -35
- package/src/utils/create-api-client.ts +0 -13
- package/src/utils/get-base-url.ts +0 -16
- package/src/version.ts +0 -37
- package/src/workflows/client/client.ts +0 -142
- package/src/workflows/client/create-client.ts +0 -17
- package/src/workflows/client/index.ts +0 -3
- package/src/workflows/client/sse.ts +0 -95
- package/src/workflows/client/types.ts +0 -56
- package/src/workflows/executor.ts +0 -124
- package/src/workflows/index.ts +0 -7
- package/src/workflows/registry.test.ts +0 -76
- package/src/workflows/registry.ts +0 -88
- package/src/workflows/runner.ts +0 -38
- package/src/workflows/schema.ts +0 -348
- package/src/workflows/task.ts +0 -117
- package/src/workflows/types.ts +0 -89
- package/src/workflows/uds.ts +0 -179
- package/test-types.ts +0 -14
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -23
- package/vite.config.ts +0 -7
- /package/dist/{workflows/client/schema.js → experimental/object/types.js} +0 -0
package/src/index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// Main Render SDK class
|
|
2
|
-
|
|
3
|
-
export * from "./errors.js";
|
|
4
|
-
// Experimental features - types only (instances accessed via render.experimental)
|
|
5
|
-
export type { ExperimentalClient } from "./experimental/index.js";
|
|
6
|
-
export { Render } from "./render.js";
|
|
7
|
-
export { getUserAgent, VERSION } from "./version.js";
|
package/src/render.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { Client } from "openapi-fetch";
|
|
2
|
-
import { RenderError } from "./errors.js";
|
|
3
|
-
import { ExperimentalClient } from "./experimental/experimental.js";
|
|
4
|
-
import type { paths } from "./generated/schema.js";
|
|
5
|
-
import { createApiClient } from "./utils/create-api-client.js";
|
|
6
|
-
import { getBaseUrl } from "./utils/get-base-url.js";
|
|
7
|
-
import { WorkflowsClient } from "./workflows/client/index.js";
|
|
8
|
-
import type { ClientOptions } from "./workflows/client/types.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Main Render SDK class providing access to all Render products
|
|
12
|
-
*/
|
|
13
|
-
export class Render {
|
|
14
|
-
public readonly workflows: WorkflowsClient;
|
|
15
|
-
public readonly experimental: ExperimentalClient;
|
|
16
|
-
|
|
17
|
-
private readonly apiClient: Client<paths>;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create a new Render SDK instance
|
|
21
|
-
* @param options Client configuration options
|
|
22
|
-
*/
|
|
23
|
-
constructor(options?: ClientOptions) {
|
|
24
|
-
const token = options?.token || process.env.RENDER_API_KEY;
|
|
25
|
-
if (!token) {
|
|
26
|
-
throw new RenderError(
|
|
27
|
-
"API token is required. Provide it via options.token or RENDER_API_KEY environment variable.",
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
const baseUrl = getBaseUrl(options);
|
|
31
|
-
this.apiClient = createApiClient(baseUrl, token);
|
|
32
|
-
this.workflows = new WorkflowsClient(this.apiClient, baseUrl, token);
|
|
33
|
-
this.experimental = new ExperimentalClient(this.apiClient);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import createClient, { type Client } from "openapi-fetch";
|
|
2
|
-
import type { paths } from "../generated/schema";
|
|
3
|
-
import { getUserAgent } from "../version.js";
|
|
4
|
-
|
|
5
|
-
export function createApiClient(baseUrl: string, token: string): Client<paths> {
|
|
6
|
-
return createClient<paths>({
|
|
7
|
-
baseUrl: `${baseUrl}/v1`,
|
|
8
|
-
headers: {
|
|
9
|
-
Authorization: `Bearer ${token}`,
|
|
10
|
-
"User-Agent": getUserAgent(),
|
|
11
|
-
},
|
|
12
|
-
});
|
|
13
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
interface GetBaseUrlProps {
|
|
2
|
-
useLocalDev?: boolean;
|
|
3
|
-
localDevUrl?: string;
|
|
4
|
-
baseUrl?: string;
|
|
5
|
-
}
|
|
6
|
-
export function getBaseUrl(options?: GetBaseUrlProps): string {
|
|
7
|
-
let baseUrl: string;
|
|
8
|
-
const useLocalDev = options?.useLocalDev ?? process.env.RENDER_USE_LOCAL_DEV === "true";
|
|
9
|
-
|
|
10
|
-
if (useLocalDev) {
|
|
11
|
-
baseUrl = options?.localDevUrl || process.env.RENDER_LOCAL_DEV_URL || "http://localhost:8120";
|
|
12
|
-
} else {
|
|
13
|
-
baseUrl = options?.baseUrl || "https://api.render.com";
|
|
14
|
-
}
|
|
15
|
-
return baseUrl;
|
|
16
|
-
}
|
package/src/version.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Version information for the Render SDK.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get the SDK version from package.json.
|
|
10
|
-
* __dirname points to dist/ after compilation, package.json is one level up.
|
|
11
|
-
*/
|
|
12
|
-
function getVersion(): string {
|
|
13
|
-
try {
|
|
14
|
-
const pkgPath = join(__dirname, "..", "package.json");
|
|
15
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { version?: string };
|
|
16
|
-
return pkg.version ?? "unknown";
|
|
17
|
-
} catch {
|
|
18
|
-
return "unknown";
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** The current version of the Render TypeScript SDK. */
|
|
23
|
-
export const VERSION = getVersion();
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Get the User-Agent string for the SDK.
|
|
27
|
-
*
|
|
28
|
-
* Returns a string like:
|
|
29
|
-
* render-sdk-typescript/0.1.0 (node/20.10.0; darwin/arm64)
|
|
30
|
-
*/
|
|
31
|
-
export function getUserAgent(): string {
|
|
32
|
-
const nodeVersion = process.version.replace("v", "");
|
|
33
|
-
const platform = process.platform;
|
|
34
|
-
const arch = process.arch;
|
|
35
|
-
|
|
36
|
-
return `render-sdk-typescript/${VERSION} (node/${nodeVersion}; ${platform}/${arch})`;
|
|
37
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import type { Client as ApiClient } from "openapi-fetch";
|
|
2
|
-
import { AbortError, ClientError, ServerError } from "../../errors.js";
|
|
3
|
-
import type { paths } from "../../generated/schema.js";
|
|
4
|
-
import { SSEClient } from "./sse.js";
|
|
5
|
-
import type {
|
|
6
|
-
ListTaskRunsParams,
|
|
7
|
-
TaskData,
|
|
8
|
-
TaskIdentifier,
|
|
9
|
-
TaskRun,
|
|
10
|
-
TaskRunDetails,
|
|
11
|
-
} from "./types.js";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Helper function to handle API errors and throw appropriate custom error types
|
|
15
|
-
*/
|
|
16
|
-
function handleApiError(error: any, response: Response, context: string): never {
|
|
17
|
-
const statusCode = response.status;
|
|
18
|
-
const errorMessage = `${context}: ${error}`;
|
|
19
|
-
|
|
20
|
-
if (statusCode >= 500) {
|
|
21
|
-
throw new ServerError(errorMessage, statusCode, error);
|
|
22
|
-
} else if (statusCode >= 400) {
|
|
23
|
-
throw new ClientError(errorMessage, statusCode, error);
|
|
24
|
-
}
|
|
25
|
-
throw new ClientError(errorMessage, statusCode, error);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Main Render SDK Client
|
|
30
|
-
*/
|
|
31
|
-
export class WorkflowsClient {
|
|
32
|
-
private readonly sse: SSEClient;
|
|
33
|
-
private readonly apiClient: ApiClient<paths>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Create a new Render SDK client
|
|
37
|
-
* @param options Client configuration options
|
|
38
|
-
* @returns New client instance
|
|
39
|
-
*/
|
|
40
|
-
constructor(apiClient: ApiClient<paths>, baseUrl: string, token: string) {
|
|
41
|
-
this.sse = new SSEClient(baseUrl, token);
|
|
42
|
-
this.apiClient = apiClient;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async runTask(
|
|
46
|
-
taskIdentifier: TaskIdentifier,
|
|
47
|
-
inputData: TaskData,
|
|
48
|
-
signal?: AbortSignal,
|
|
49
|
-
): Promise<TaskRunDetails> {
|
|
50
|
-
if (signal?.aborted) {
|
|
51
|
-
throw new AbortError();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let taskRunId: string | null = null;
|
|
55
|
-
const abortHandler = async () => {
|
|
56
|
-
if (taskRunId) {
|
|
57
|
-
await this.cancelTaskRun(taskRunId);
|
|
58
|
-
throw new AbortError();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
// Register abort handler before making the request
|
|
64
|
-
signal?.addEventListener("abort", abortHandler);
|
|
65
|
-
|
|
66
|
-
const { data, error, response } = await this.apiClient.POST("/task-runs", {
|
|
67
|
-
body: {
|
|
68
|
-
task: taskIdentifier,
|
|
69
|
-
input: inputData,
|
|
70
|
-
},
|
|
71
|
-
signal,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
if (error) {
|
|
75
|
-
handleApiError(error, response, "Failed to run task");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
taskRunId = data.id;
|
|
79
|
-
|
|
80
|
-
// Pass signal to waitForTask so it can handle cancellation during wait
|
|
81
|
-
return await this.waitForTask(data.id, signal);
|
|
82
|
-
} catch (err) {
|
|
83
|
-
// Handle DOMException AbortError from fetch
|
|
84
|
-
if (err instanceof DOMException && err.name === "AbortError") {
|
|
85
|
-
throw new AbortError();
|
|
86
|
-
}
|
|
87
|
-
throw err;
|
|
88
|
-
} finally {
|
|
89
|
-
signal?.removeEventListener("abort", abortHandler);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private async waitForTask(taskRunId: string, signal?: AbortSignal): Promise<TaskRunDetails> {
|
|
94
|
-
return this.sse.waitOnTaskRun(taskRunId, signal);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Get task run details by ID
|
|
99
|
-
* @param taskRunId Task run ID
|
|
100
|
-
* @returns Task run details
|
|
101
|
-
*/
|
|
102
|
-
|
|
103
|
-
async getTaskRun(taskRunId: string): Promise<TaskRunDetails> {
|
|
104
|
-
const { data, error, response } = await this.apiClient.GET("/task-runs/{taskRunId}", {
|
|
105
|
-
params: { path: { taskRunId } },
|
|
106
|
-
});
|
|
107
|
-
if (error) {
|
|
108
|
-
handleApiError(error, response, "Failed to get task run");
|
|
109
|
-
}
|
|
110
|
-
return data;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Cancel a task run
|
|
115
|
-
* @param taskRunId Task run ID
|
|
116
|
-
*/
|
|
117
|
-
|
|
118
|
-
private async cancelTaskRun(taskRunId: string): Promise<void> {
|
|
119
|
-
const { error, response } = await this.apiClient.DELETE("/task-runs/{taskRunId}", {
|
|
120
|
-
params: { path: { taskRunId } },
|
|
121
|
-
});
|
|
122
|
-
if (error) {
|
|
123
|
-
handleApiError(error, response, "Failed to cancel task run");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* List task runs with optional filters
|
|
129
|
-
* @param params Filter parameters
|
|
130
|
-
* @returns List of task runs
|
|
131
|
-
*/
|
|
132
|
-
|
|
133
|
-
async listTaskRuns(params: ListTaskRunsParams): Promise<TaskRun[]> {
|
|
134
|
-
const { data, error, response } = await this.apiClient.GET("/task-runs", {
|
|
135
|
-
params: { query: params },
|
|
136
|
-
});
|
|
137
|
-
if (error) {
|
|
138
|
-
handleApiError(error, response, "Failed to list task runs");
|
|
139
|
-
}
|
|
140
|
-
return data;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { RenderError } from "../../errors";
|
|
2
|
-
import { createApiClient } from "../../utils/create-api-client";
|
|
3
|
-
import { getBaseUrl } from "../../utils/get-base-url";
|
|
4
|
-
import { WorkflowsClient } from "./client";
|
|
5
|
-
import type { ClientOptions } from "./types";
|
|
6
|
-
|
|
7
|
-
export function createWorkflowsClient(options?: ClientOptions): WorkflowsClient {
|
|
8
|
-
const token = options?.token || process.env.RENDER_API_KEY;
|
|
9
|
-
if (!token) {
|
|
10
|
-
throw new RenderError(
|
|
11
|
-
"API token is required. Provide it via options.token or RENDER_API_KEY environment variable.",
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
const baseUrl = getBaseUrl(options);
|
|
15
|
-
const apiClient = createApiClient(baseUrl, token);
|
|
16
|
-
return new WorkflowsClient(apiClient, baseUrl, token);
|
|
17
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { EventSource } from "eventsource";
|
|
2
|
-
import { AbortError } from "../../errors.js";
|
|
3
|
-
import { getUserAgent } from "../../version.js";
|
|
4
|
-
import type { TaskRunDetails } from "./types.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Task event types emitted by the SSE stream
|
|
8
|
-
*/
|
|
9
|
-
export enum TaskEventType {
|
|
10
|
-
COMPLETED = "task.completed",
|
|
11
|
-
FAILED = "task.failed",
|
|
12
|
-
RUNNING = "task.running",
|
|
13
|
-
PENDING = "task.pending",
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* SSE Client for streaming task run events
|
|
18
|
-
*/
|
|
19
|
-
export class SSEClient {
|
|
20
|
-
constructor(
|
|
21
|
-
private readonly baseUrl: string,
|
|
22
|
-
private readonly token: string,
|
|
23
|
-
) {}
|
|
24
|
-
|
|
25
|
-
async waitOnTaskRun(taskRunId: string, signal?: AbortSignal): Promise<TaskRunDetails> {
|
|
26
|
-
return new Promise((resolve, reject) => {
|
|
27
|
-
let eventSource: EventSource | null = null;
|
|
28
|
-
|
|
29
|
-
const abortHandler = () => {
|
|
30
|
-
cleanup();
|
|
31
|
-
reject(new AbortError());
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const cleanup = () => {
|
|
35
|
-
if (eventSource) {
|
|
36
|
-
eventSource.removeEventListener(TaskEventType.COMPLETED, eventHandler);
|
|
37
|
-
eventSource.removeEventListener(TaskEventType.FAILED, eventHandler);
|
|
38
|
-
eventSource.removeEventListener("error", errorHandler);
|
|
39
|
-
eventSource.close();
|
|
40
|
-
eventSource = null;
|
|
41
|
-
}
|
|
42
|
-
signal?.removeEventListener("abort", abortHandler);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const eventHandler = (event: MessageEvent) => {
|
|
46
|
-
try {
|
|
47
|
-
const details = JSON.parse(event.data) as TaskRunDetails;
|
|
48
|
-
cleanup();
|
|
49
|
-
resolve(details);
|
|
50
|
-
} catch (e) {
|
|
51
|
-
cleanup();
|
|
52
|
-
reject(new Error(`Failed to parse task run details: ${e}`));
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const errorHandler = (error: any) => {
|
|
57
|
-
cleanup();
|
|
58
|
-
reject(new Error(`SSE connection error: ${error.message || "Unknown error"}`));
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Check if already aborted
|
|
62
|
-
if (signal?.aborted) {
|
|
63
|
-
reject(new AbortError());
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Listen for abort signal
|
|
68
|
-
signal?.addEventListener("abort", abortHandler);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const url = new URL("/v1/task-runs/events", this.baseUrl);
|
|
72
|
-
url.searchParams.append("taskRunIds", taskRunId);
|
|
73
|
-
|
|
74
|
-
eventSource = new EventSource(url.toString(), {
|
|
75
|
-
fetch: (input, init) =>
|
|
76
|
-
fetch(input, {
|
|
77
|
-
...init,
|
|
78
|
-
headers: {
|
|
79
|
-
...init?.headers,
|
|
80
|
-
Authorization: `Bearer ${this.token}`,
|
|
81
|
-
"User-Agent": getUserAgent(),
|
|
82
|
-
},
|
|
83
|
-
}),
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
eventSource.addEventListener(TaskEventType.COMPLETED, eventHandler);
|
|
87
|
-
eventSource.addEventListener(TaskEventType.FAILED, eventHandler);
|
|
88
|
-
eventSource.addEventListener("error", errorHandler);
|
|
89
|
-
} catch (e) {
|
|
90
|
-
cleanup();
|
|
91
|
-
reject(e);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { components, paths } from "../../generated/schema";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Task identifier in the format "workflow-slug/task-name"
|
|
5
|
-
*/
|
|
6
|
-
export type TaskIdentifier = string;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Task input data as an array of parameters
|
|
10
|
-
*/
|
|
11
|
-
export type TaskData = Array<any>;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Task run status enum
|
|
15
|
-
*/
|
|
16
|
-
export enum TaskRunStatus {
|
|
17
|
-
PENDING = "pending",
|
|
18
|
-
RUNNING = "running",
|
|
19
|
-
COMPLETED = "completed",
|
|
20
|
-
FAILED = "failed",
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Basic task run information
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
export type TaskRun = components["schemas"]["TaskRun"];
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Detailed task run information including results and errors
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
export type TaskRunDetails = components["schemas"]["TaskRunDetails"];
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Parameters for listing task runs
|
|
37
|
-
*/
|
|
38
|
-
export type ListTaskRunsParams = paths["/task-runs"]["get"]["parameters"]["query"];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Request body for running a task
|
|
42
|
-
*/
|
|
43
|
-
export interface RunTaskRequest {
|
|
44
|
-
task: TaskIdentifier;
|
|
45
|
-
input: TaskData;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Client configuration options
|
|
50
|
-
*/
|
|
51
|
-
export interface ClientOptions {
|
|
52
|
-
token?: string;
|
|
53
|
-
baseUrl?: string;
|
|
54
|
-
useLocalDev?: boolean;
|
|
55
|
-
localDevUrl?: string;
|
|
56
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { RenderError } from "../errors.js";
|
|
2
|
-
import { TaskRegistry } from "./registry.js";
|
|
3
|
-
import { setCurrentContext } from "./task.js";
|
|
4
|
-
import type { TaskContext, TaskFunction, TaskResult } from "./types.js";
|
|
5
|
-
import { UDSClient } from "./uds.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Implementation of TaskResult
|
|
9
|
-
*/
|
|
10
|
-
class TaskResultImpl<T> implements TaskResult<T> {
|
|
11
|
-
constructor(
|
|
12
|
-
private readonly subtaskId: string,
|
|
13
|
-
private readonly udsClient: UDSClient,
|
|
14
|
-
) {}
|
|
15
|
-
|
|
16
|
-
async get(): Promise<T> {
|
|
17
|
-
// Poll for subtask result
|
|
18
|
-
const pollInterval = 500; // half a second
|
|
19
|
-
|
|
20
|
-
while (true) {
|
|
21
|
-
const result = await this.udsClient.getSubtaskResult(this.subtaskId);
|
|
22
|
-
|
|
23
|
-
if (!result.still_running && result.complete) {
|
|
24
|
-
if (result.complete.output) {
|
|
25
|
-
const json = Buffer.from(result.complete.output, "base64").toString();
|
|
26
|
-
const decoded = JSON.parse(json);
|
|
27
|
-
return decoded[0];
|
|
28
|
-
}
|
|
29
|
-
return undefined as T;
|
|
30
|
-
} else if (!result.still_running && result.error) {
|
|
31
|
-
throw new RenderError(`Subtask failed: ${result.error}`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Still pending, wait and retry
|
|
35
|
-
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Implementation of TaskContext
|
|
42
|
-
*/
|
|
43
|
-
class TaskContextImpl implements TaskContext {
|
|
44
|
-
constructor(private readonly udsClient: UDSClient) {}
|
|
45
|
-
|
|
46
|
-
executeTask<TArgs extends any[], TResult>(
|
|
47
|
-
_task: TaskFunction<TArgs, TResult>,
|
|
48
|
-
taskName: string,
|
|
49
|
-
...args: TArgs
|
|
50
|
-
): TaskResult<TResult> {
|
|
51
|
-
const registry = TaskRegistry.getInstance();
|
|
52
|
-
|
|
53
|
-
if (!registry.has(taskName)) {
|
|
54
|
-
throw new RenderError(`Task '${taskName}' is not registered`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Execute subtask via UDS
|
|
58
|
-
const subtaskIdPromise = this.udsClient.runSubtask(taskName, args);
|
|
59
|
-
|
|
60
|
-
// Return a TaskResult that will poll for completion
|
|
61
|
-
return {
|
|
62
|
-
get: async () => {
|
|
63
|
-
const subtaskId = await subtaskIdPromise;
|
|
64
|
-
const result = new TaskResultImpl<TResult>(subtaskId, this.udsClient);
|
|
65
|
-
return result.get();
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Task executor that runs tasks via Unix socket communication
|
|
73
|
-
*/
|
|
74
|
-
export class TaskExecutor {
|
|
75
|
-
private readonly udsClient: UDSClient;
|
|
76
|
-
private readonly context: TaskContext;
|
|
77
|
-
|
|
78
|
-
constructor(socketPath: string) {
|
|
79
|
-
this.udsClient = new UDSClient(socketPath);
|
|
80
|
-
this.context = new TaskContextImpl(this.udsClient);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Execute a single task
|
|
85
|
-
*/
|
|
86
|
-
async executeTask(): Promise<void> {
|
|
87
|
-
const registry = TaskRegistry.getInstance();
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
// Get task input
|
|
91
|
-
const input = await this.udsClient.getInput();
|
|
92
|
-
const taskName = input.task_name;
|
|
93
|
-
const inputData = JSON.parse(Buffer.from(input.input, "base64").toString());
|
|
94
|
-
|
|
95
|
-
// Get task from registry
|
|
96
|
-
const taskMetadata = registry.get(taskName);
|
|
97
|
-
if (!taskMetadata) {
|
|
98
|
-
throw new RenderError(`Task '${taskName}' not found in registry`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Execute task with context
|
|
102
|
-
const result = await setCurrentContext(this.context, async () => {
|
|
103
|
-
return await taskMetadata.func(...inputData);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// Send result
|
|
107
|
-
await this.udsClient.sendCallback(result);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
// Send error
|
|
110
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
111
|
-
await this.udsClient.sendCallback(undefined, errorMessage);
|
|
112
|
-
throw error;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Register all tasks with the workflow system
|
|
118
|
-
*/
|
|
119
|
-
async registerTasks(): Promise<void> {
|
|
120
|
-
const registry = TaskRegistry.getInstance();
|
|
121
|
-
const tasks = registry.getAllTasks();
|
|
122
|
-
await this.udsClient.registerTasks(tasks);
|
|
123
|
-
}
|
|
124
|
-
}
|
package/src/workflows/index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// Export client API
|
|
2
|
-
export * from "./client/index.js";
|
|
3
|
-
export { TaskExecutor } from "./executor.js";
|
|
4
|
-
export { TaskRegistry } from "./registry.js";
|
|
5
|
-
export { run, startTaskServer } from "./runner.js";
|
|
6
|
-
export { getCurrentContext, setCurrentContext, task } from "./task.js";
|
|
7
|
-
export * from "./types.js";
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { TaskRegistry } from "./registry.js";
|
|
2
|
-
|
|
3
|
-
describe("TaskRegistry", () => {
|
|
4
|
-
let registry: TaskRegistry;
|
|
5
|
-
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
// Get a fresh registry for each test
|
|
8
|
-
registry = TaskRegistry.getInstance();
|
|
9
|
-
registry.clear();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
describe("register", () => {
|
|
13
|
-
it("should register a task with timeout_seconds", () => {
|
|
14
|
-
const taskFn = () => 42;
|
|
15
|
-
|
|
16
|
-
registry.register(taskFn, {
|
|
17
|
-
name: "timeout_task",
|
|
18
|
-
timeoutSeconds: 120,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const task = registry.get("timeout_task");
|
|
22
|
-
expect(task).toBeDefined();
|
|
23
|
-
expect(task?.options?.timeout_seconds).toBe(120);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("should register a task without timeout_seconds", () => {
|
|
27
|
-
const taskFn = () => 42;
|
|
28
|
-
|
|
29
|
-
registry.register(taskFn, {
|
|
30
|
-
name: "no_timeout_task",
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const task = registry.get("no_timeout_task");
|
|
34
|
-
expect(task).toBeDefined();
|
|
35
|
-
expect(task?.options).toBeUndefined();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("should register a task with both retry and timeout_seconds", () => {
|
|
39
|
-
const taskFn = () => 42;
|
|
40
|
-
|
|
41
|
-
registry.register(taskFn, {
|
|
42
|
-
name: "both_options_task",
|
|
43
|
-
timeoutSeconds: 300,
|
|
44
|
-
retry: {
|
|
45
|
-
maxRetries: 3,
|
|
46
|
-
waitDurationMs: 1000,
|
|
47
|
-
backoffScaling: 2.0,
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const task = registry.get("both_options_task");
|
|
52
|
-
expect(task).toBeDefined();
|
|
53
|
-
expect(task?.options?.timeout_seconds).toBe(300);
|
|
54
|
-
expect(task?.options?.retry?.max_retries).toBe(3);
|
|
55
|
-
expect(task?.options?.retry?.wait_duration_ms).toBe(1000);
|
|
56
|
-
expect(task?.options?.retry?.factor).toBe(2.0);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("should register a task with only retry options", () => {
|
|
60
|
-
const taskFn = () => 42;
|
|
61
|
-
|
|
62
|
-
registry.register(taskFn, {
|
|
63
|
-
name: "retry_only_task",
|
|
64
|
-
retry: {
|
|
65
|
-
maxRetries: 2,
|
|
66
|
-
waitDurationMs: 500,
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const task = registry.get("retry_only_task");
|
|
71
|
-
expect(task).toBeDefined();
|
|
72
|
-
expect(task?.options?.retry?.max_retries).toBe(2);
|
|
73
|
-
expect(task?.options?.timeout_seconds).toBeUndefined();
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|