@deepnoodle/mobius 0.0.20 → 0.0.22
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 +31 -45
- package/dist/api/index.d.ts +198 -240
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/schema.d.ts +5387 -7233
- package/dist/api/schema.d.ts.map +1 -1
- package/dist/client.d.ts +64 -152
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +164 -358
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/signing.d.ts +43 -0
- package/dist/signing.d.ts.map +1 -0
- package/dist/signing.js +101 -0
- package/dist/signing.js.map +1 -0
- package/dist/webhook.d.ts +5 -17
- package/dist/webhook.d.ts.map +1 -1
- package/dist/webhook.js +38 -50
- package/dist/webhook.js.map +1 -1
- package/dist/worker.d.ts +40 -138
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +315 -456
- package/dist/worker.js.map +1 -1
- package/package.json +4 -4
package/dist/client.js
CHANGED
|
@@ -3,74 +3,24 @@ export { RateLimitError } from "./retry.js";
|
|
|
3
3
|
export const DEFAULT_BASE_URL = "https://api.mobiusops.ai";
|
|
4
4
|
export const DEFAULT_PROJECT = "default";
|
|
5
5
|
export const DEFAULT_NAMESPACE = DEFAULT_PROJECT;
|
|
6
|
-
export class LeaseLostError extends Error {
|
|
7
|
-
constructor(jobId) {
|
|
8
|
-
super(`lease lost for job ${jobId}`);
|
|
9
|
-
this.jobId = jobId;
|
|
10
|
-
this.name = "LeaseLostError";
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Thrown when the server returns HTTP 401 on a worker-loop request.
|
|
15
|
-
* The credential has been revoked mid-execution; the process needs
|
|
16
|
-
* to restart under a fresh credential. Distinct from
|
|
17
|
-
* {@link LeaseLostError} (409 — lease reclaimed by scheduler) because
|
|
18
|
-
* the remedy is operational, not workflow-level.
|
|
19
|
-
*/
|
|
20
6
|
export class AuthRevokedError extends Error {
|
|
21
|
-
constructor(
|
|
22
|
-
super(
|
|
23
|
-
? `mobius: credential revoked (job ${jobId})`
|
|
24
|
-
: "mobius: credential revoked");
|
|
25
|
-
this.jobId = jobId;
|
|
7
|
+
constructor() {
|
|
8
|
+
super("mobius: credential revoked");
|
|
26
9
|
this.name = "AuthRevokedError";
|
|
27
10
|
}
|
|
28
11
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Thrown when the server returns HTTP 409 with `worker_instance_conflict`
|
|
31
|
-
* on a claim. Another live process has already registered this
|
|
32
|
-
* `worker_instance_id` in the project under a different session token.
|
|
33
|
-
* Surfaces from {@link Worker.run} as a hard error so the operator
|
|
34
|
-
* notices the misconfiguration instead of the worker silently retrying —
|
|
35
|
-
* fix by configuring a unique instance ID per process or by relying on
|
|
36
|
-
* the SDK's auto-detection.
|
|
37
|
-
*/
|
|
38
|
-
export class WorkerInstanceConflictError extends Error {
|
|
39
|
-
constructor(workerInstanceId, projectHandle, message) {
|
|
40
|
-
super(message ??
|
|
41
|
-
(workerInstanceId
|
|
42
|
-
? `mobius: worker_instance_id ${JSON.stringify(workerInstanceId)} is already registered in project ${JSON.stringify(projectHandle)} by another live process; configure a unique instance ID per process or rely on auto-detection`
|
|
43
|
-
: "mobius: worker instance conflict"));
|
|
44
|
-
this.workerInstanceId = workerInstanceId;
|
|
45
|
-
this.projectHandle = projectHandle;
|
|
46
|
-
this.name = "WorkerInstanceConflictError";
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Thrown from {@link Client} construction when the API key or project
|
|
51
|
-
* options are malformed (e.g. a project-pinned key whose handle prefix
|
|
52
|
-
* doesn't match the server's handle regex, or a handle conflict
|
|
53
|
-
* between `WithProjectHandle` and the handle embedded in the key).
|
|
54
|
-
*/
|
|
55
12
|
export class ConfigError extends Error {
|
|
56
13
|
constructor(message) {
|
|
57
14
|
super(message);
|
|
58
15
|
this.name = "ConfigError";
|
|
59
16
|
}
|
|
60
17
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const slash = apiKey.indexOf("/");
|
|
67
|
-
if (slash < 0)
|
|
68
|
-
return null;
|
|
69
|
-
const handle = apiKey.slice(0, slash);
|
|
70
|
-
if (!HANDLE_RE.test(handle)) {
|
|
71
|
-
throw new ConfigError(`invalid project handle prefix in API key: ${JSON.stringify(handle)}`);
|
|
18
|
+
export class LeaseLostError extends Error {
|
|
19
|
+
constructor(jobId) {
|
|
20
|
+
super(`lease lost for job ${jobId}`);
|
|
21
|
+
this.jobId = jobId;
|
|
22
|
+
this.name = "LeaseLostError";
|
|
72
23
|
}
|
|
73
|
-
return handle;
|
|
74
24
|
}
|
|
75
25
|
export class PayloadTooLargeError extends Error {
|
|
76
26
|
constructor(jobId) {
|
|
@@ -79,12 +29,6 @@ export class PayloadTooLargeError extends Error {
|
|
|
79
29
|
this.name = "PayloadTooLargeError";
|
|
80
30
|
}
|
|
81
31
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Legacy per-job rate-limit error raised by {@link Client.emitJobEvents}.
|
|
84
|
-
* Subclass of {@link RateLimitError} so callers catching the newer,
|
|
85
|
-
* transport-raised {@link RateLimitError} also catch this. New code
|
|
86
|
-
* should prefer {@link RateLimitError}.
|
|
87
|
-
*/
|
|
88
32
|
export class RateLimitedError extends RateLimitError {
|
|
89
33
|
constructor(jobId, retryAfter) {
|
|
90
34
|
super({
|
|
@@ -97,21 +41,34 @@ export class RateLimitedError extends RateLimitError {
|
|
|
97
41
|
this.jobId = jobId;
|
|
98
42
|
}
|
|
99
43
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
44
|
+
export class WorkerInstanceConflictError extends Error {
|
|
45
|
+
constructor(workerInstanceId, projectHandle, message) {
|
|
46
|
+
super(message ??
|
|
47
|
+
(workerInstanceId
|
|
48
|
+
? `mobius: worker_instance_id ${JSON.stringify(workerInstanceId)} is already registered in project ${JSON.stringify(projectHandle)} by another live process`
|
|
49
|
+
: "mobius: worker instance conflict"));
|
|
50
|
+
this.workerInstanceId = workerInstanceId;
|
|
51
|
+
this.projectHandle = projectHandle;
|
|
52
|
+
this.name = "WorkerInstanceConflictError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const HANDLE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
56
|
+
function extractHandleFromApiKey(apiKey) {
|
|
57
|
+
if (!apiKey.startsWith("mbx_") && !apiKey.startsWith("mbc_"))
|
|
58
|
+
return null;
|
|
59
|
+
const dot = apiKey.lastIndexOf(".");
|
|
60
|
+
if (dot < 0 || dot === apiKey.length - 1)
|
|
61
|
+
return null;
|
|
62
|
+
const handle = apiKey.slice(dot + 1);
|
|
63
|
+
if (!HANDLE_RE.test(handle)) {
|
|
64
|
+
throw new ConfigError(`invalid project handle suffix in API key: ${JSON.stringify(handle)}`);
|
|
65
|
+
}
|
|
66
|
+
return handle;
|
|
67
|
+
}
|
|
108
68
|
export class Client {
|
|
109
69
|
constructor(opts) {
|
|
110
70
|
this.baseURL = (opts.baseURL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
111
71
|
const explicitProject = opts.project ?? opts.namespace;
|
|
112
|
-
// Project-pinned keys arrive as "<handle>/mbx_<secret>". Split the
|
|
113
|
-
// handle off and use it as the project; any explicit project option
|
|
114
|
-
// must either match or be the default sentinel.
|
|
115
72
|
const handleInKey = extractHandleFromApiKey(opts.apiKey);
|
|
116
73
|
if (handleInKey != null) {
|
|
117
74
|
if (explicitProject != null &&
|
|
@@ -131,99 +88,109 @@ export class Client {
|
|
|
131
88
|
this.timeoutMs = opts.timeoutMs ?? 60000;
|
|
132
89
|
this.fetchFn = wrapFetchWithRetry((input, init) => globalThis.fetch(input, init), { maxRetries: opts.retry ?? DEFAULT_MAX_RETRIES });
|
|
133
90
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
91
|
+
workerSocketURL() {
|
|
92
|
+
const url = new URL(this.baseURL);
|
|
93
|
+
if (url.protocol === "http:")
|
|
94
|
+
url.protocol = "ws:";
|
|
95
|
+
if (url.protocol === "https:")
|
|
96
|
+
url.protocol = "wss:";
|
|
97
|
+
url.pathname = `${url.pathname.replace(/\/$/, "")}/v1/projects/${encodeURIComponent(this.project)}/workers/socket`;
|
|
98
|
+
url.search = "";
|
|
99
|
+
return url.toString();
|
|
100
|
+
}
|
|
101
|
+
async listAutomations(opts = {}) {
|
|
102
|
+
const resp = await this.request(withQuery("/v1/projects/:project/automations", opts), {
|
|
103
|
+
method: "GET",
|
|
104
|
+
});
|
|
105
|
+
return (await resp.json());
|
|
106
|
+
}
|
|
107
|
+
async getAutomation(handle) {
|
|
108
|
+
const resp = await this.request(`/v1/projects/:project/automations/${encodeURIComponent(handle)}`, { method: "GET" });
|
|
109
|
+
return (await resp.json());
|
|
110
|
+
}
|
|
111
|
+
async createAutomation(opts) {
|
|
112
|
+
const body = removeUndefined({
|
|
113
|
+
name: opts.name,
|
|
114
|
+
handle: opts.handle,
|
|
115
|
+
description: opts.description,
|
|
116
|
+
default_agent_id: opts.default_agent_id,
|
|
117
|
+
default_inputs: opts.default_inputs,
|
|
118
|
+
settings: opts.settings,
|
|
119
|
+
tags: opts.tags,
|
|
120
|
+
triggers: opts.triggers,
|
|
121
|
+
});
|
|
122
|
+
const resp = await this.request("/v1/projects/:project/automations", {
|
|
123
|
+
method: "POST",
|
|
124
|
+
body,
|
|
125
|
+
});
|
|
159
126
|
return (await resp.json());
|
|
160
127
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const resp = await this.request(`/v1/projects
|
|
164
|
-
if (resp.status === 401)
|
|
165
|
-
throw new AuthRevokedError(jobId);
|
|
166
|
-
if (resp.status === 409)
|
|
167
|
-
throw new LeaseLostError(jobId);
|
|
128
|
+
async updateAutomation(handle, opts) {
|
|
129
|
+
const body = removeUndefined(opts);
|
|
130
|
+
const resp = await this.request(`/v1/projects/:project/automations/${encodeURIComponent(handle)}`, { method: "PATCH", body });
|
|
168
131
|
return (await resp.json());
|
|
169
132
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const resp = await this.request(`/v1/projects/${encodeURIComponent(this.project)}/jobs/${encodeURIComponent(jobId)}/complete`, { method: "POST", body: req });
|
|
173
|
-
if (resp.status === 401)
|
|
174
|
-
throw new AuthRevokedError(jobId);
|
|
175
|
-
if (resp.status === 409)
|
|
176
|
-
throw new LeaseLostError(jobId);
|
|
133
|
+
async deleteAutomation(handle) {
|
|
134
|
+
await this.request(`/v1/projects/:project/automations/${encodeURIComponent(handle)}`, { method: "DELETE" });
|
|
177
135
|
}
|
|
178
|
-
async
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const resp = await this.request(`/v1/projects/${encodeURIComponent(this.project)}/jobs/${encodeURIComponent(jobId)}/events`, { method: "POST", body: req });
|
|
182
|
-
if (resp.status === 401)
|
|
183
|
-
throw new AuthRevokedError(jobId);
|
|
184
|
-
if (resp.status === 409)
|
|
185
|
-
throw new LeaseLostError(jobId);
|
|
186
|
-
if (resp.status === 413)
|
|
187
|
-
throw new PayloadTooLargeError(jobId);
|
|
136
|
+
async listAutomationVersions(handle) {
|
|
137
|
+
const resp = await this.request(`/v1/projects/:project/automations/${encodeURIComponent(handle)}/versions`, { method: "GET" });
|
|
138
|
+
return (await resp.json());
|
|
188
139
|
}
|
|
189
|
-
async
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
attempt: req.attempt,
|
|
194
|
-
events: [{ type: req.type, payload: req.payload }],
|
|
140
|
+
async createAutomationVersion(handle, spec, opts = {}) {
|
|
141
|
+
const body = removeUndefined({
|
|
142
|
+
spec: spec,
|
|
143
|
+
compiled_plan: opts.compiled_plan,
|
|
195
144
|
});
|
|
145
|
+
const resp = await this.request(`/v1/projects/:project/automations/${encodeURIComponent(handle)}/versions`, { method: "POST", body });
|
|
146
|
+
const version = (await resp.json());
|
|
147
|
+
if (opts.publish) {
|
|
148
|
+
await this.publishAutomationVersion(handle, version.version);
|
|
149
|
+
}
|
|
150
|
+
return version;
|
|
196
151
|
}
|
|
197
|
-
async
|
|
198
|
-
const resp = await this.request(`/v1/projects/${encodeURIComponent(
|
|
152
|
+
async publishAutomationVersion(handle, version) {
|
|
153
|
+
const resp = await this.request(`/v1/projects/:project/automations/${encodeURIComponent(handle)}/versions/${version}/publication`, { method: "POST" });
|
|
199
154
|
return (await resp.json());
|
|
200
155
|
}
|
|
201
|
-
async
|
|
202
|
-
|
|
156
|
+
async startRun(automationHandle, opts = {}) {
|
|
157
|
+
return this.startAutomationRun(automationHandle, opts);
|
|
158
|
+
}
|
|
159
|
+
async startAutomationRun(automationHandle, opts = {}) {
|
|
160
|
+
const body = removeUndefined({
|
|
161
|
+
inputs: opts.inputs,
|
|
162
|
+
source: opts.source,
|
|
163
|
+
external_id: opts.external_id,
|
|
164
|
+
});
|
|
165
|
+
const resp = await this.request(`/v1/projects/:project/automations/${encodeURIComponent(automationHandle)}/runs`, { method: "POST", body });
|
|
203
166
|
return (await resp.json());
|
|
204
167
|
}
|
|
205
168
|
async listRuns(opts = {}) {
|
|
206
|
-
const
|
|
207
|
-
|
|
169
|
+
const resp = await this.request(withQuery("/v1/projects/:project/runs", opts), {
|
|
170
|
+
method: "GET",
|
|
171
|
+
});
|
|
208
172
|
return (await resp.json());
|
|
209
173
|
}
|
|
210
174
|
async getRun(runId) {
|
|
211
|
-
const resp = await this.request(`/v1/projects
|
|
175
|
+
const resp = await this.request(`/v1/projects/:project/runs/${encodeURIComponent(runId)}`, { method: "GET" });
|
|
212
176
|
return (await resp.json());
|
|
213
177
|
}
|
|
214
|
-
async cancelRun(runId) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
await this.request(`/v1/projects/${encodeURIComponent(this.project)}/runs/${encodeURIComponent(runId)}/resumptions`, { method: "POST" });
|
|
178
|
+
async cancelRun(runId, reason) {
|
|
179
|
+
const body = removeUndefined({ reason });
|
|
180
|
+
const resp = await this.request(`/v1/projects/:project/runs/${encodeURIComponent(runId)}/cancellations`, { method: "POST", body });
|
|
181
|
+
return (await resp.json());
|
|
219
182
|
}
|
|
220
|
-
async
|
|
221
|
-
const
|
|
183
|
+
async signalRun(runId, stepKey, result) {
|
|
184
|
+
const body = removeUndefined({
|
|
185
|
+
step_key: stepKey,
|
|
186
|
+
result,
|
|
187
|
+
});
|
|
188
|
+
const resp = await this.request(`/v1/projects/:project/runs/${encodeURIComponent(runId)}/signals`, { method: "POST", body });
|
|
222
189
|
return (await resp.json());
|
|
223
190
|
}
|
|
224
191
|
async *watchRun(runId, opts = {}) {
|
|
225
|
-
const path = withQuery(`/v1/projects
|
|
226
|
-
const resp = await this.fetchFn(this.
|
|
192
|
+
const path = withQuery(`/v1/projects/:project/runs/${encodeURIComponent(runId)}/events.stream`, opts.since && opts.since > 0 ? { since_sequence: opts.since } : {});
|
|
193
|
+
const resp = await this.fetchFn(this.url(path), {
|
|
227
194
|
method: "GET",
|
|
228
195
|
headers: this.headers,
|
|
229
196
|
signal: opts.signal,
|
|
@@ -249,16 +216,11 @@ export class Client {
|
|
|
249
216
|
if (isTerminalRunStatus(run.status))
|
|
250
217
|
return run;
|
|
251
218
|
try {
|
|
252
|
-
for await (const ev of this.watchRun(runId, {
|
|
253
|
-
since
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
since = ev.seq;
|
|
258
|
-
if (ev.type !== "run_updated")
|
|
259
|
-
continue;
|
|
260
|
-
const status = ev.data.status;
|
|
261
|
-
if (status === "completed" || status === "failed") {
|
|
219
|
+
for await (const ev of this.watchRun(runId, { ...opts, since })) {
|
|
220
|
+
if (ev.sequence > since)
|
|
221
|
+
since = ev.sequence;
|
|
222
|
+
const status = ev.payload?.status;
|
|
223
|
+
if (typeof status === "string" && isTerminalRunStatus(status)) {
|
|
262
224
|
return await this.getRun(runId);
|
|
263
225
|
}
|
|
264
226
|
}
|
|
@@ -270,59 +232,6 @@ export class Client {
|
|
|
270
232
|
await delay(reconnectDelayMs, opts.signal);
|
|
271
233
|
}
|
|
272
234
|
}
|
|
273
|
-
async listWorkflows(opts = {}) {
|
|
274
|
-
const path = withQuery(`/v1/projects/${encodeURIComponent(this.project)}/workflows`, opts);
|
|
275
|
-
const resp = await this.request(path, { method: "GET" });
|
|
276
|
-
return (await resp.json());
|
|
277
|
-
}
|
|
278
|
-
async getWorkflow(workflowId) {
|
|
279
|
-
const resp = await this.request(`/v1/projects/${encodeURIComponent(this.project)}/workflows/${encodeURIComponent(workflowId)}`, { method: "GET" });
|
|
280
|
-
return (await resp.json());
|
|
281
|
-
}
|
|
282
|
-
async createWorkflow(spec, opts = {}) {
|
|
283
|
-
const body = createWorkflowRequest(spec, opts);
|
|
284
|
-
const resp = await this.request(`/v1/projects/${encodeURIComponent(this.project)}/workflows`, { method: "POST", body });
|
|
285
|
-
return (await resp.json());
|
|
286
|
-
}
|
|
287
|
-
async updateWorkflow(workflowId, opts = {}) {
|
|
288
|
-
const body = removeUndefined({
|
|
289
|
-
name: opts.name,
|
|
290
|
-
description: opts.description,
|
|
291
|
-
published_as_tool: opts.published_as_tool,
|
|
292
|
-
spec: opts.spec,
|
|
293
|
-
tags: opts.tags,
|
|
294
|
-
});
|
|
295
|
-
const resp = await this.request(`/v1/projects/${encodeURIComponent(this.project)}/workflows/${encodeURIComponent(workflowId)}`, { method: "PATCH", body });
|
|
296
|
-
return (await resp.json());
|
|
297
|
-
}
|
|
298
|
-
async ensureWorkflow(spec, opts = {}) {
|
|
299
|
-
const desired = normalizeWorkflowOptions(spec, opts);
|
|
300
|
-
const existing = await this.findWorkflow(desired);
|
|
301
|
-
if (!existing) {
|
|
302
|
-
return {
|
|
303
|
-
definition: await this.createWorkflow(spec, desired),
|
|
304
|
-
created: true,
|
|
305
|
-
updated: false,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
const current = await this.getWorkflow(existing.id);
|
|
309
|
-
const update = workflowUpdateForDiff(current, spec, desired);
|
|
310
|
-
if (!update) {
|
|
311
|
-
return { definition: current, created: false, updated: false };
|
|
312
|
-
}
|
|
313
|
-
return {
|
|
314
|
-
definition: await this.updateWorkflow(current.id, update),
|
|
315
|
-
created: false,
|
|
316
|
-
updated: true,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
async syncWorkflows(defs) {
|
|
320
|
-
const results = [];
|
|
321
|
-
for (const def of defs) {
|
|
322
|
-
results.push(await this.ensureWorkflow(def.spec, def.options ?? {}));
|
|
323
|
-
}
|
|
324
|
-
return results;
|
|
325
|
-
}
|
|
326
235
|
async request(path, opts) {
|
|
327
236
|
const timeout = AbortSignal.timeout(this.timeoutMs);
|
|
328
237
|
const signal = opts.signal ? anySignal(opts.signal, timeout) : timeout;
|
|
@@ -331,56 +240,37 @@ export class Client {
|
|
|
331
240
|
headers: this.headers,
|
|
332
241
|
signal,
|
|
333
242
|
};
|
|
334
|
-
if (opts.body != null)
|
|
243
|
+
if (opts.body != null)
|
|
335
244
|
init.body = JSON.stringify(opts.body);
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
resp.status !== 401 &&
|
|
341
|
-
resp.status !== 409 &&
|
|
342
|
-
resp.status !== 413 &&
|
|
343
|
-
resp.status !== 429) {
|
|
245
|
+
const resp = await this.fetchFn(this.url(path), init);
|
|
246
|
+
if (!resp.ok && resp.status !== 204) {
|
|
247
|
+
if (resp.status === 401)
|
|
248
|
+
throw new AuthRevokedError();
|
|
344
249
|
const text = await resp.text().catch(() => "");
|
|
345
250
|
throw new Error(`mobius API ${opts.method} ${path}: HTTP ${resp.status}: ${text}`);
|
|
346
251
|
}
|
|
347
252
|
return resp;
|
|
348
253
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
throw new Error("mobius: ensure workflow requires a handle, name, or spec name");
|
|
352
|
-
}
|
|
353
|
-
let cursor = "";
|
|
354
|
-
for (;;) {
|
|
355
|
-
const page = await this.listWorkflows({ cursor, limit: 100 });
|
|
356
|
-
for (const item of page.items) {
|
|
357
|
-
if (desired.handle && item.handle === desired.handle)
|
|
358
|
-
return item;
|
|
359
|
-
if (!desired.handle && item.name === desired.name)
|
|
360
|
-
return item;
|
|
361
|
-
}
|
|
362
|
-
if (!page.has_more || !page.next_cursor)
|
|
363
|
-
return null;
|
|
364
|
-
cursor = page.next_cursor;
|
|
365
|
-
}
|
|
254
|
+
url(path) {
|
|
255
|
+
return this.baseURL + path.replace(":project", encodeURIComponent(this.project));
|
|
366
256
|
}
|
|
367
257
|
}
|
|
258
|
+
export function isTerminalRunStatus(status) {
|
|
259
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
260
|
+
}
|
|
368
261
|
function anySignal(...signals) {
|
|
369
262
|
const controller = new AbortController();
|
|
370
|
-
for (const
|
|
371
|
-
if (
|
|
372
|
-
controller.abort(
|
|
263
|
+
for (const signal of signals) {
|
|
264
|
+
if (signal.aborted) {
|
|
265
|
+
controller.abort(signal.reason);
|
|
373
266
|
break;
|
|
374
267
|
}
|
|
375
|
-
|
|
268
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
376
269
|
once: true,
|
|
377
270
|
});
|
|
378
271
|
}
|
|
379
272
|
return controller.signal;
|
|
380
273
|
}
|
|
381
|
-
export function isTerminalRunStatus(status) {
|
|
382
|
-
return status === "completed" || status === "failed";
|
|
383
|
-
}
|
|
384
274
|
function withQuery(path, params) {
|
|
385
275
|
const query = new URLSearchParams();
|
|
386
276
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -397,133 +287,49 @@ function withQuery(path, params) {
|
|
|
397
287
|
const qs = query.toString();
|
|
398
288
|
return qs ? `${path}?${qs}` : path;
|
|
399
289
|
}
|
|
400
|
-
function createWorkflowRequest(spec, opts) {
|
|
401
|
-
const normalized = normalizeWorkflowOptions(spec, opts);
|
|
402
|
-
return removeUndefined({
|
|
403
|
-
name: normalized.name,
|
|
404
|
-
handle: normalized.handle,
|
|
405
|
-
description: normalized.description,
|
|
406
|
-
published_as_tool: normalized.published_as_tool,
|
|
407
|
-
spec,
|
|
408
|
-
tags: normalized.tags,
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
function normalizeWorkflowOptions(spec, opts) {
|
|
412
|
-
return { ...opts, name: opts.name || spec.name };
|
|
413
|
-
}
|
|
414
|
-
function workflowUpdateForDiff(current, spec, desired) {
|
|
415
|
-
const update = {};
|
|
416
|
-
if (desired.name && current.name !== desired.name)
|
|
417
|
-
update.name = desired.name;
|
|
418
|
-
if (desired.description && current.description !== desired.description) {
|
|
419
|
-
update.description = desired.description;
|
|
420
|
-
}
|
|
421
|
-
if (desired.published_as_tool !== undefined &&
|
|
422
|
-
current.published_as_tool !== desired.published_as_tool) {
|
|
423
|
-
update.published_as_tool = desired.published_as_tool;
|
|
424
|
-
}
|
|
425
|
-
if (desired.tags !== undefined && !jsonEqual(current.tags, desired.tags)) {
|
|
426
|
-
update.tags = desired.tags;
|
|
427
|
-
}
|
|
428
|
-
if (!jsonEqual(current.spec, spec))
|
|
429
|
-
update.spec = spec;
|
|
430
|
-
return Object.keys(update).length > 0 ? update : null;
|
|
431
|
-
}
|
|
432
|
-
function jsonEqual(a, b) {
|
|
433
|
-
return stableJsonStringify(a) === stableJsonStringify(b);
|
|
434
|
-
}
|
|
435
|
-
function stableJsonStringify(value) {
|
|
436
|
-
if (value === null || typeof value !== "object") {
|
|
437
|
-
return JSON.stringify(value);
|
|
438
|
-
}
|
|
439
|
-
if (Array.isArray(value)) {
|
|
440
|
-
return `[${value.map((item) => stableJsonStringify(item) ?? "null").join(",")}]`;
|
|
441
|
-
}
|
|
442
|
-
const obj = value;
|
|
443
|
-
const entries = Object.keys(obj)
|
|
444
|
-
.sort()
|
|
445
|
-
.filter((key) => obj[key] !== undefined)
|
|
446
|
-
.map((key) => `${JSON.stringify(key)}:${stableJsonStringify(obj[key])}`);
|
|
447
|
-
return `{${entries.join(",")}}`;
|
|
448
|
-
}
|
|
449
290
|
function removeUndefined(obj) {
|
|
450
|
-
return Object.fromEntries(Object.entries(obj).filter(([,
|
|
291
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
|
|
292
|
+
}
|
|
293
|
+
async function delay(ms, signal) {
|
|
294
|
+
if (ms <= 0)
|
|
295
|
+
return;
|
|
296
|
+
await new Promise((resolve, reject) => {
|
|
297
|
+
const timer = setTimeout(resolve, ms);
|
|
298
|
+
if (signal) {
|
|
299
|
+
signal.addEventListener("abort", () => {
|
|
300
|
+
clearTimeout(timer);
|
|
301
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
302
|
+
}, { once: true });
|
|
303
|
+
}
|
|
304
|
+
});
|
|
451
305
|
}
|
|
452
306
|
async function* parseSSE(body) {
|
|
453
307
|
const reader = body.getReader();
|
|
454
308
|
const decoder = new TextDecoder();
|
|
455
309
|
let buffer = "";
|
|
456
|
-
|
|
457
|
-
let id = "";
|
|
458
|
-
let dataLines = [];
|
|
459
|
-
const dispatch = () => {
|
|
460
|
-
if (dataLines.length === 0) {
|
|
461
|
-
event = "";
|
|
462
|
-
return null;
|
|
463
|
-
}
|
|
464
|
-
const out = {
|
|
465
|
-
event: event || "message",
|
|
466
|
-
id,
|
|
467
|
-
data: dataLines.join("\n"),
|
|
468
|
-
};
|
|
469
|
-
event = "";
|
|
470
|
-
dataLines = [];
|
|
471
|
-
return out;
|
|
472
|
-
};
|
|
473
|
-
const processLine = (rawLine) => {
|
|
474
|
-
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
475
|
-
if (line === "")
|
|
476
|
-
return dispatch();
|
|
477
|
-
if (line.startsWith(":"))
|
|
478
|
-
return null;
|
|
479
|
-
const colon = line.indexOf(":");
|
|
480
|
-
const field = colon >= 0 ? line.slice(0, colon) : line;
|
|
481
|
-
let value = colon >= 0 ? line.slice(colon + 1) : "";
|
|
482
|
-
if (value.startsWith(" "))
|
|
483
|
-
value = value.slice(1);
|
|
484
|
-
if (field === "event")
|
|
485
|
-
event = value;
|
|
486
|
-
if (field === "id" && !value.includes("\0"))
|
|
487
|
-
id = value;
|
|
488
|
-
if (field === "data")
|
|
489
|
-
dataLines.push(value);
|
|
490
|
-
return null;
|
|
491
|
-
};
|
|
492
|
-
for (;;) {
|
|
493
|
-
const { value, done } = await reader.read();
|
|
494
|
-
if (done)
|
|
495
|
-
break;
|
|
496
|
-
buffer += decoder.decode(value, { stream: true });
|
|
310
|
+
try {
|
|
497
311
|
for (;;) {
|
|
498
|
-
const
|
|
499
|
-
if (
|
|
312
|
+
const { value, done } = await reader.read();
|
|
313
|
+
if (done)
|
|
500
314
|
break;
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
315
|
+
buffer += decoder.decode(value, { stream: true });
|
|
316
|
+
for (;;) {
|
|
317
|
+
const match = /\r?\n\r?\n/.exec(buffer);
|
|
318
|
+
if (!match)
|
|
319
|
+
break;
|
|
320
|
+
const raw = buffer.slice(0, match.index);
|
|
321
|
+
buffer = buffer.slice(match.index + match[0].length);
|
|
322
|
+
const data = raw
|
|
323
|
+
.split(/\r?\n/)
|
|
324
|
+
.filter((line) => line.startsWith("data:"))
|
|
325
|
+
.map((line) => line.slice(5).trimStart())
|
|
326
|
+
.join("\n");
|
|
327
|
+
yield { data };
|
|
328
|
+
}
|
|
506
329
|
}
|
|
507
330
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const evt = processLine(buffer);
|
|
511
|
-
if (evt)
|
|
512
|
-
yield evt;
|
|
331
|
+
finally {
|
|
332
|
+
reader.releaseLock();
|
|
513
333
|
}
|
|
514
|
-
const evt = dispatch();
|
|
515
|
-
if (evt)
|
|
516
|
-
yield evt;
|
|
517
|
-
}
|
|
518
|
-
function delay(ms, signal) {
|
|
519
|
-
if (signal?.aborted)
|
|
520
|
-
return Promise.reject(signal.reason);
|
|
521
|
-
return new Promise((resolve, reject) => {
|
|
522
|
-
const timeout = setTimeout(resolve, ms);
|
|
523
|
-
signal?.addEventListener("abort", () => {
|
|
524
|
-
clearTimeout(timeout);
|
|
525
|
-
reject(signal.reason);
|
|
526
|
-
}, { once: true });
|
|
527
|
-
});
|
|
528
334
|
}
|
|
529
335
|
//# sourceMappingURL=client.js.map
|