@personaai/react 0.8.1 → 0.9.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 +14 -10
- package/dist/index.cjs +603 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +222 -3
- package/dist/index.d.ts +222 -3
- package/dist/index.js +598 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -133,7 +133,8 @@ function fetchReader(response, controller) {
|
|
|
133
133
|
function xhrStream(opts) {
|
|
134
134
|
return new Promise((resolveStream, rejectStream) => {
|
|
135
135
|
const xhr = new XMLHttpRequest();
|
|
136
|
-
|
|
136
|
+
const method = opts.method ?? "POST";
|
|
137
|
+
xhr.open(method, opts.url);
|
|
137
138
|
for (const [key, val] of Object.entries(opts.headers))
|
|
138
139
|
xhr.setRequestHeader(key, val);
|
|
139
140
|
let consumed = 0;
|
|
@@ -232,7 +233,7 @@ function xhrStream(opts) {
|
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
|
-
xhr.send(opts.body);
|
|
236
|
+
xhr.send(method === "GET" ? null : opts.body ?? null);
|
|
236
237
|
});
|
|
237
238
|
}
|
|
238
239
|
async function openSSEStream(opts) {
|
|
@@ -244,10 +245,11 @@ async function openSSEStream(opts) {
|
|
|
244
245
|
if (opts.signal.aborted) controller.abort();
|
|
245
246
|
else opts.signal.addEventListener("abort", () => controller.abort());
|
|
246
247
|
}
|
|
248
|
+
const method = opts.method ?? "POST";
|
|
247
249
|
const response = await fetch(opts.url, {
|
|
248
|
-
method
|
|
250
|
+
method,
|
|
249
251
|
headers: opts.headers,
|
|
250
|
-
body: opts.body,
|
|
252
|
+
body: method === "GET" ? void 0 : opts.body,
|
|
251
253
|
signal: controller.signal
|
|
252
254
|
});
|
|
253
255
|
if (!response.ok) {
|
|
@@ -2059,6 +2061,592 @@ function useMcp(options = {}) {
|
|
|
2059
2061
|
};
|
|
2060
2062
|
}
|
|
2061
2063
|
|
|
2064
|
+
// src/hooks/useWorkflows.ts
|
|
2065
|
+
import { useCallback as useCallback10, useEffect as useEffect9, useState as useState9 } from "react";
|
|
2066
|
+
function useWorkflows(options) {
|
|
2067
|
+
const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
|
|
2068
|
+
const { autoFetch = true, search, status, page, limit } = opts;
|
|
2069
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
2070
|
+
const [workflows, setWorkflows] = useState9([]);
|
|
2071
|
+
const [pagination, setPagination] = useState9({
|
|
2072
|
+
page: page ?? 1,
|
|
2073
|
+
limit: limit ?? 20,
|
|
2074
|
+
total: 0,
|
|
2075
|
+
totalPages: 0
|
|
2076
|
+
});
|
|
2077
|
+
const [isLoading, setIsLoading] = useState9(false);
|
|
2078
|
+
const [error, setError] = useState9(null);
|
|
2079
|
+
const fetchWorkflows = useCallback10(async () => {
|
|
2080
|
+
setIsLoading(true);
|
|
2081
|
+
setError(null);
|
|
2082
|
+
try {
|
|
2083
|
+
const params = new URLSearchParams();
|
|
2084
|
+
if (search) params.set("search", search);
|
|
2085
|
+
if (status) params.set("status", status);
|
|
2086
|
+
if (page) params.set("page", String(page));
|
|
2087
|
+
if (limit) params.set("limit", String(limit));
|
|
2088
|
+
const queryStr = params.toString() ? `?${params.toString()}` : "";
|
|
2089
|
+
const res = await fetchWithAuth(`/workflows${queryStr}`);
|
|
2090
|
+
if (!res.ok) {
|
|
2091
|
+
throw new Error(`Failed to list workflows: ${res.statusText}`);
|
|
2092
|
+
}
|
|
2093
|
+
const data = await res.json();
|
|
2094
|
+
const items = Array.isArray(data) ? data : data?.items || data?.workflows || [];
|
|
2095
|
+
setWorkflows(items);
|
|
2096
|
+
if (data?.pagination) {
|
|
2097
|
+
setPagination({
|
|
2098
|
+
page: data.pagination.page ?? 1,
|
|
2099
|
+
limit: data.pagination.limit ?? items.length,
|
|
2100
|
+
total: data.pagination.total ?? items.length,
|
|
2101
|
+
totalPages: data.pagination.totalPages ?? 1
|
|
2102
|
+
});
|
|
2103
|
+
} else {
|
|
2104
|
+
setPagination({
|
|
2105
|
+
page: 1,
|
|
2106
|
+
limit: items.length,
|
|
2107
|
+
total: items.length,
|
|
2108
|
+
totalPages: 1
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
return items;
|
|
2112
|
+
} catch (err) {
|
|
2113
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2114
|
+
setError(errorObj);
|
|
2115
|
+
return [];
|
|
2116
|
+
} finally {
|
|
2117
|
+
setIsLoading(false);
|
|
2118
|
+
}
|
|
2119
|
+
}, [fetchWithAuth, search, status, page, limit]);
|
|
2120
|
+
const createWorkflow = useCallback10(
|
|
2121
|
+
async (input) => {
|
|
2122
|
+
setError(null);
|
|
2123
|
+
try {
|
|
2124
|
+
const res = await fetchWithAuth("/workflows", {
|
|
2125
|
+
method: "POST",
|
|
2126
|
+
headers: { "Content-Type": "application/json" },
|
|
2127
|
+
body: JSON.stringify(input)
|
|
2128
|
+
});
|
|
2129
|
+
if (!res.ok) {
|
|
2130
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2131
|
+
throw new Error(`Failed to create workflow (${res.status}): ${errText}`);
|
|
2132
|
+
}
|
|
2133
|
+
const created = await res.json();
|
|
2134
|
+
setWorkflows((prev) => [created, ...prev]);
|
|
2135
|
+
return created;
|
|
2136
|
+
} catch (err) {
|
|
2137
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2138
|
+
setError(errorObj);
|
|
2139
|
+
throw errorObj;
|
|
2140
|
+
}
|
|
2141
|
+
},
|
|
2142
|
+
[fetchWithAuth]
|
|
2143
|
+
);
|
|
2144
|
+
useEffect9(() => {
|
|
2145
|
+
if (autoFetch) {
|
|
2146
|
+
void fetchWorkflows();
|
|
2147
|
+
}
|
|
2148
|
+
}, [autoFetch, fetchWorkflows]);
|
|
2149
|
+
return {
|
|
2150
|
+
workflows,
|
|
2151
|
+
pagination,
|
|
2152
|
+
isLoading,
|
|
2153
|
+
error,
|
|
2154
|
+
refetch: fetchWorkflows,
|
|
2155
|
+
createWorkflow
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
// src/hooks/useWorkflow.ts
|
|
2160
|
+
import { useCallback as useCallback11, useEffect as useEffect10, useState as useState10 } from "react";
|
|
2161
|
+
function useWorkflow(workflowId, options) {
|
|
2162
|
+
const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
|
|
2163
|
+
const { autoFetch = true } = opts;
|
|
2164
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
2165
|
+
const [workflow, setWorkflow] = useState10(null);
|
|
2166
|
+
const [versions, setVersions] = useState10([]);
|
|
2167
|
+
const [mermaid, setMermaid] = useState10(null);
|
|
2168
|
+
const [isLoading, setIsLoading] = useState10(false);
|
|
2169
|
+
const [error, setError] = useState10(null);
|
|
2170
|
+
const fetchWorkflow = useCallback11(async () => {
|
|
2171
|
+
if (!workflowId) return null;
|
|
2172
|
+
setIsLoading(true);
|
|
2173
|
+
setError(null);
|
|
2174
|
+
try {
|
|
2175
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}`);
|
|
2176
|
+
if (!res.ok) {
|
|
2177
|
+
throw new Error(`Failed to fetch workflow (${res.status}): ${res.statusText}`);
|
|
2178
|
+
}
|
|
2179
|
+
const data = await res.json();
|
|
2180
|
+
setWorkflow(data);
|
|
2181
|
+
return data;
|
|
2182
|
+
} catch (err) {
|
|
2183
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2184
|
+
setError(errorObj);
|
|
2185
|
+
return null;
|
|
2186
|
+
} finally {
|
|
2187
|
+
setIsLoading(false);
|
|
2188
|
+
}
|
|
2189
|
+
}, [fetchWithAuth, workflowId]);
|
|
2190
|
+
const fetchVersions = useCallback11(async () => {
|
|
2191
|
+
if (!workflowId) return [];
|
|
2192
|
+
try {
|
|
2193
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/versions`);
|
|
2194
|
+
if (!res.ok) return [];
|
|
2195
|
+
const data = await res.json();
|
|
2196
|
+
const items = Array.isArray(data) ? data : data?.items || [];
|
|
2197
|
+
setVersions(items);
|
|
2198
|
+
return items;
|
|
2199
|
+
} catch {
|
|
2200
|
+
return [];
|
|
2201
|
+
}
|
|
2202
|
+
}, [fetchWithAuth, workflowId]);
|
|
2203
|
+
const fetchMermaid = useCallback11(async () => {
|
|
2204
|
+
if (!workflowId) return "";
|
|
2205
|
+
try {
|
|
2206
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/mermaid`);
|
|
2207
|
+
if (!res.ok) return "";
|
|
2208
|
+
const data = await res.json();
|
|
2209
|
+
const code = typeof data?.mermaid === "string" ? data.mermaid : String(data ?? "");
|
|
2210
|
+
setMermaid(code);
|
|
2211
|
+
return code;
|
|
2212
|
+
} catch {
|
|
2213
|
+
return "";
|
|
2214
|
+
}
|
|
2215
|
+
}, [fetchWithAuth, workflowId]);
|
|
2216
|
+
const saveDraft = useCallback11(
|
|
2217
|
+
async (draft) => {
|
|
2218
|
+
if (!workflowId) throw new Error("Workflow ID is required");
|
|
2219
|
+
setError(null);
|
|
2220
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/draft`, {
|
|
2221
|
+
method: "POST",
|
|
2222
|
+
headers: { "Content-Type": "application/json" },
|
|
2223
|
+
body: JSON.stringify({ draft })
|
|
2224
|
+
});
|
|
2225
|
+
if (!res.ok) {
|
|
2226
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2227
|
+
throw new Error(`Failed to save draft (${res.status}): ${errText}`);
|
|
2228
|
+
}
|
|
2229
|
+
const updated = await res.json();
|
|
2230
|
+
setWorkflow(updated);
|
|
2231
|
+
return updated;
|
|
2232
|
+
},
|
|
2233
|
+
[fetchWithAuth, workflowId]
|
|
2234
|
+
);
|
|
2235
|
+
const publish = useCallback11(
|
|
2236
|
+
async (summary) => {
|
|
2237
|
+
if (!workflowId) throw new Error("Workflow ID is required");
|
|
2238
|
+
setError(null);
|
|
2239
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/publish`, {
|
|
2240
|
+
method: "POST",
|
|
2241
|
+
headers: { "Content-Type": "application/json" },
|
|
2242
|
+
body: JSON.stringify(summary ? { summary } : {})
|
|
2243
|
+
});
|
|
2244
|
+
if (!res.ok) {
|
|
2245
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2246
|
+
throw new Error(`Failed to publish workflow (${res.status}): ${errText}`);
|
|
2247
|
+
}
|
|
2248
|
+
const versionResult = await res.json();
|
|
2249
|
+
void fetchWorkflow();
|
|
2250
|
+
void fetchVersions();
|
|
2251
|
+
return versionResult;
|
|
2252
|
+
},
|
|
2253
|
+
[fetchWithAuth, workflowId, fetchWorkflow, fetchVersions]
|
|
2254
|
+
);
|
|
2255
|
+
const deleteWorkflow = useCallback11(async () => {
|
|
2256
|
+
if (!workflowId) throw new Error("Workflow ID is required");
|
|
2257
|
+
setError(null);
|
|
2258
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}`, {
|
|
2259
|
+
method: "DELETE"
|
|
2260
|
+
});
|
|
2261
|
+
if (!res.ok) {
|
|
2262
|
+
throw new Error(`Failed to delete workflow (${res.status}): ${res.statusText}`);
|
|
2263
|
+
}
|
|
2264
|
+
setWorkflow(null);
|
|
2265
|
+
}, [fetchWithAuth, workflowId]);
|
|
2266
|
+
useEffect10(() => {
|
|
2267
|
+
if (autoFetch && workflowId) {
|
|
2268
|
+
void fetchWorkflow();
|
|
2269
|
+
}
|
|
2270
|
+
}, [autoFetch, workflowId, fetchWorkflow]);
|
|
2271
|
+
return {
|
|
2272
|
+
workflow,
|
|
2273
|
+
versions,
|
|
2274
|
+
mermaid,
|
|
2275
|
+
isLoading,
|
|
2276
|
+
error,
|
|
2277
|
+
refetch: fetchWorkflow,
|
|
2278
|
+
fetchVersions,
|
|
2279
|
+
fetchMermaid,
|
|
2280
|
+
saveDraft,
|
|
2281
|
+
publish,
|
|
2282
|
+
deleteWorkflow
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
// src/hooks/useWorkflowStream.ts
|
|
2287
|
+
import { useCallback as useCallback12, useEffect as useEffect11, useRef as useRef3, useState as useState11 } from "react";
|
|
2288
|
+
function useWorkflowStream(workflowId, options) {
|
|
2289
|
+
const { baseUrl, getAuthToken, fetchWithAuth, logger } = usePersonaContext();
|
|
2290
|
+
const [status, setStatus] = useState11("idle");
|
|
2291
|
+
const [runId, setRunId] = useState11(null);
|
|
2292
|
+
const [activeNodeId, setActiveNodeId] = useState11(null);
|
|
2293
|
+
const [nodeRuns, setNodeRuns] = useState11(
|
|
2294
|
+
{}
|
|
2295
|
+
);
|
|
2296
|
+
const [text, setText] = useState11("");
|
|
2297
|
+
const [output, setOutput] = useState11(null);
|
|
2298
|
+
const [error, setError] = useState11(null);
|
|
2299
|
+
const [events, setEvents] = useState11([]);
|
|
2300
|
+
const abortControllerRef = useRef3(null);
|
|
2301
|
+
const currentRunIdRef = useRef3(null);
|
|
2302
|
+
currentRunIdRef.current = runId;
|
|
2303
|
+
const optionsRef = useRef3(options);
|
|
2304
|
+
optionsRef.current = options;
|
|
2305
|
+
const cancel = useCallback12(async () => {
|
|
2306
|
+
if (abortControllerRef.current) {
|
|
2307
|
+
abortControllerRef.current.abort();
|
|
2308
|
+
abortControllerRef.current = null;
|
|
2309
|
+
}
|
|
2310
|
+
const currentRun = currentRunIdRef.current;
|
|
2311
|
+
if (currentRun) {
|
|
2312
|
+
try {
|
|
2313
|
+
await fetchWithAuth(`/workflows/runs/${currentRun}/cancel`, {
|
|
2314
|
+
method: "POST"
|
|
2315
|
+
});
|
|
2316
|
+
} catch (err) {
|
|
2317
|
+
logger.warn("Failed to send cancel request to workflow run", {
|
|
2318
|
+
runId: currentRun,
|
|
2319
|
+
error: String(err)
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
setStatus("cancelled");
|
|
2324
|
+
setActiveNodeId(null);
|
|
2325
|
+
}, [fetchWithAuth, logger]);
|
|
2326
|
+
const reset = useCallback12(() => {
|
|
2327
|
+
if (abortControllerRef.current) {
|
|
2328
|
+
abortControllerRef.current.abort();
|
|
2329
|
+
abortControllerRef.current = null;
|
|
2330
|
+
}
|
|
2331
|
+
setStatus("idle");
|
|
2332
|
+
setRunId(null);
|
|
2333
|
+
setActiveNodeId(null);
|
|
2334
|
+
setNodeRuns({});
|
|
2335
|
+
setText("");
|
|
2336
|
+
setOutput(null);
|
|
2337
|
+
setError(null);
|
|
2338
|
+
setEvents([]);
|
|
2339
|
+
}, []);
|
|
2340
|
+
const processStream = useCallback12(
|
|
2341
|
+
async (url, method, body, targetRunId) => {
|
|
2342
|
+
if (abortControllerRef.current) {
|
|
2343
|
+
abortControllerRef.current.abort();
|
|
2344
|
+
}
|
|
2345
|
+
const controller = new AbortController();
|
|
2346
|
+
abortControllerRef.current = controller;
|
|
2347
|
+
setStatus("running");
|
|
2348
|
+
setError(null);
|
|
2349
|
+
const token = getAuthToken ? await getAuthToken() : null;
|
|
2350
|
+
const headers = {
|
|
2351
|
+
Accept: "text/event-stream"
|
|
2352
|
+
};
|
|
2353
|
+
if (token) {
|
|
2354
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2355
|
+
}
|
|
2356
|
+
if (method === "POST") {
|
|
2357
|
+
headers["Content-Type"] = "application/json";
|
|
2358
|
+
}
|
|
2359
|
+
let assignedRunId = targetRunId ?? null;
|
|
2360
|
+
let accumulatedText = "";
|
|
2361
|
+
let finalOutput = null;
|
|
2362
|
+
try {
|
|
2363
|
+
const stream = await openSSEStream({
|
|
2364
|
+
url,
|
|
2365
|
+
method,
|
|
2366
|
+
headers,
|
|
2367
|
+
body,
|
|
2368
|
+
signal: controller.signal
|
|
2369
|
+
});
|
|
2370
|
+
if (!stream.ok) {
|
|
2371
|
+
throw new Error(
|
|
2372
|
+
stream.errorText || `Stream request failed with status ${stream.status}`
|
|
2373
|
+
);
|
|
2374
|
+
}
|
|
2375
|
+
const runIdHeader = stream.getHeader("x-persona-run-id");
|
|
2376
|
+
if (runIdHeader) {
|
|
2377
|
+
assignedRunId = runIdHeader;
|
|
2378
|
+
setRunId(runIdHeader);
|
|
2379
|
+
}
|
|
2380
|
+
let buffer = "";
|
|
2381
|
+
while (true) {
|
|
2382
|
+
const { done, value } = await stream.reader.read();
|
|
2383
|
+
if (done) break;
|
|
2384
|
+
if (!value) continue;
|
|
2385
|
+
buffer += value;
|
|
2386
|
+
const lines = buffer.split("\n");
|
|
2387
|
+
buffer = lines.pop() ?? "";
|
|
2388
|
+
for (const line of lines) {
|
|
2389
|
+
const trimmed = line.trim();
|
|
2390
|
+
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
2391
|
+
if (trimmed.startsWith("data:")) {
|
|
2392
|
+
const rawData = trimmed.slice(5).trim();
|
|
2393
|
+
if (!rawData || rawData === "[DONE]") continue;
|
|
2394
|
+
try {
|
|
2395
|
+
const event = JSON.parse(rawData);
|
|
2396
|
+
setEvents((prev) => [...prev, event]);
|
|
2397
|
+
optionsRef.current?.onEvent?.(event);
|
|
2398
|
+
if ("runId" in event && typeof event.runId === "string" && !assignedRunId) {
|
|
2399
|
+
assignedRunId = event.runId;
|
|
2400
|
+
setRunId(event.runId);
|
|
2401
|
+
}
|
|
2402
|
+
if (event.type === "TEXT_MESSAGE_CHUNK") {
|
|
2403
|
+
accumulatedText += event.delta;
|
|
2404
|
+
setText((prev) => prev + event.delta);
|
|
2405
|
+
} else if (event.type === "CUSTOM") {
|
|
2406
|
+
if (event.name === "workflow_node_started") {
|
|
2407
|
+
const val = event.value;
|
|
2408
|
+
setActiveNodeId(val.nodeId);
|
|
2409
|
+
setNodeRuns((prev) => ({
|
|
2410
|
+
...prev,
|
|
2411
|
+
[val.nodeId]: {
|
|
2412
|
+
nodeId: val.nodeId,
|
|
2413
|
+
nodeType: val.nodeType,
|
|
2414
|
+
nodeLabel: val.nodeLabel,
|
|
2415
|
+
status: "running",
|
|
2416
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2417
|
+
input: val.input
|
|
2418
|
+
}
|
|
2419
|
+
}));
|
|
2420
|
+
optionsRef.current?.onNodeStarted?.(val);
|
|
2421
|
+
} else if (event.name === "workflow_node_completed") {
|
|
2422
|
+
const val = event.value;
|
|
2423
|
+
setNodeRuns((prev) => {
|
|
2424
|
+
const existing = prev[val.nodeId];
|
|
2425
|
+
return {
|
|
2426
|
+
...prev,
|
|
2427
|
+
[val.nodeId]: {
|
|
2428
|
+
nodeId: val.nodeId,
|
|
2429
|
+
nodeType: existing?.nodeType ?? "unknown",
|
|
2430
|
+
nodeLabel: existing?.nodeLabel ?? val.nodeId,
|
|
2431
|
+
status: "completed",
|
|
2432
|
+
startedAt: existing?.startedAt,
|
|
2433
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2434
|
+
input: existing?.input,
|
|
2435
|
+
output: val.output
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
});
|
|
2439
|
+
setActiveNodeId((curr) => curr === val.nodeId ? null : curr);
|
|
2440
|
+
finalOutput = val.output;
|
|
2441
|
+
setOutput(val.output);
|
|
2442
|
+
optionsRef.current?.onNodeCompleted?.(val);
|
|
2443
|
+
} else if (event.name === "workflow_node_failed") {
|
|
2444
|
+
const val = event.value;
|
|
2445
|
+
setNodeRuns((prev) => {
|
|
2446
|
+
const existing = prev[val.nodeId];
|
|
2447
|
+
return {
|
|
2448
|
+
...prev,
|
|
2449
|
+
[val.nodeId]: {
|
|
2450
|
+
nodeId: val.nodeId,
|
|
2451
|
+
nodeType: existing?.nodeType ?? "unknown",
|
|
2452
|
+
nodeLabel: existing?.nodeLabel ?? val.nodeId,
|
|
2453
|
+
status: "failed",
|
|
2454
|
+
startedAt: existing?.startedAt,
|
|
2455
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2456
|
+
input: existing?.input,
|
|
2457
|
+
error: val.error
|
|
2458
|
+
}
|
|
2459
|
+
};
|
|
2460
|
+
});
|
|
2461
|
+
setActiveNodeId((curr) => curr === val.nodeId ? null : curr);
|
|
2462
|
+
optionsRef.current?.onNodeFailed?.(val);
|
|
2463
|
+
}
|
|
2464
|
+
} else if (event.type === "RUN_FINISHED") {
|
|
2465
|
+
setStatus("completed");
|
|
2466
|
+
setActiveNodeId(null);
|
|
2467
|
+
optionsRef.current?.onFinish?.({
|
|
2468
|
+
runId: assignedRunId || "",
|
|
2469
|
+
output: finalOutput,
|
|
2470
|
+
text: accumulatedText
|
|
2471
|
+
});
|
|
2472
|
+
} else if (event.type === "RUN_ERROR") {
|
|
2473
|
+
const runErr = new Error(event.message || event.code || "Workflow run error");
|
|
2474
|
+
setStatus("failed");
|
|
2475
|
+
setError(runErr);
|
|
2476
|
+
setActiveNodeId(null);
|
|
2477
|
+
optionsRef.current?.onError?.(runErr);
|
|
2478
|
+
}
|
|
2479
|
+
} catch {
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
setStatus((prev) => prev === "running" ? "completed" : prev);
|
|
2485
|
+
return finalOutput;
|
|
2486
|
+
} catch (err) {
|
|
2487
|
+
if (controller.signal.aborted) {
|
|
2488
|
+
setStatus("cancelled");
|
|
2489
|
+
return null;
|
|
2490
|
+
}
|
|
2491
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2492
|
+
setStatus("failed");
|
|
2493
|
+
setError(errorObj);
|
|
2494
|
+
setActiveNodeId(null);
|
|
2495
|
+
optionsRef.current?.onError?.(errorObj);
|
|
2496
|
+
throw errorObj;
|
|
2497
|
+
} finally {
|
|
2498
|
+
if (abortControllerRef.current === controller) {
|
|
2499
|
+
abortControllerRef.current = null;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
},
|
|
2503
|
+
[getAuthToken]
|
|
2504
|
+
);
|
|
2505
|
+
const start = useCallback12(
|
|
2506
|
+
async (input, opts) => {
|
|
2507
|
+
const targetWfId = opts?.workflowId || workflowId;
|
|
2508
|
+
if (!targetWfId) {
|
|
2509
|
+
throw new Error("Workflow ID must be provided to start execution");
|
|
2510
|
+
}
|
|
2511
|
+
setNodeRuns({});
|
|
2512
|
+
setText("");
|
|
2513
|
+
setOutput(null);
|
|
2514
|
+
setEvents([]);
|
|
2515
|
+
setActiveNodeId(null);
|
|
2516
|
+
const cleanBase = baseUrl.replace(/\/+$/, "");
|
|
2517
|
+
const url = `${cleanBase}/workflows/${targetWfId}/stream`;
|
|
2518
|
+
const body = JSON.stringify({
|
|
2519
|
+
input: input ?? {},
|
|
2520
|
+
dryRun: opts?.dryRun ?? false
|
|
2521
|
+
});
|
|
2522
|
+
return processStream(url, "POST", body);
|
|
2523
|
+
},
|
|
2524
|
+
[workflowId, baseUrl, processStream]
|
|
2525
|
+
);
|
|
2526
|
+
const resume = useCallback12(
|
|
2527
|
+
async (targetRunId, sinceSeq) => {
|
|
2528
|
+
if (!targetRunId) {
|
|
2529
|
+
throw new Error("Run ID is required to resume stream");
|
|
2530
|
+
}
|
|
2531
|
+
setRunId(targetRunId);
|
|
2532
|
+
const cleanBase = baseUrl.replace(/\/+$/, "");
|
|
2533
|
+
const query = sinceSeq !== void 0 ? `?since=${sinceSeq}` : "";
|
|
2534
|
+
const url = `${cleanBase}/workflows/runs/${targetRunId}/resume${query}`;
|
|
2535
|
+
await processStream(url, "GET", void 0, targetRunId);
|
|
2536
|
+
},
|
|
2537
|
+
[baseUrl, processStream]
|
|
2538
|
+
);
|
|
2539
|
+
useEffect11(() => {
|
|
2540
|
+
return () => {
|
|
2541
|
+
if (abortControllerRef.current) {
|
|
2542
|
+
abortControllerRef.current.abort();
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
}, []);
|
|
2546
|
+
return {
|
|
2547
|
+
runId,
|
|
2548
|
+
status,
|
|
2549
|
+
isRunning: status === "running",
|
|
2550
|
+
activeNodeId,
|
|
2551
|
+
nodeRuns,
|
|
2552
|
+
text,
|
|
2553
|
+
output,
|
|
2554
|
+
error,
|
|
2555
|
+
events,
|
|
2556
|
+
start,
|
|
2557
|
+
cancel,
|
|
2558
|
+
resume,
|
|
2559
|
+
reset
|
|
2560
|
+
};
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
// src/hooks/useWorkflowRuns.ts
|
|
2564
|
+
import { useCallback as useCallback13, useEffect as useEffect12, useState as useState12 } from "react";
|
|
2565
|
+
function useWorkflowRuns(workflowId, options) {
|
|
2566
|
+
const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
|
|
2567
|
+
const { autoFetch = true, page = 1, limit = 20 } = opts;
|
|
2568
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
2569
|
+
const [runs, setRuns] = useState12([]);
|
|
2570
|
+
const [pagination, setPagination] = useState12({
|
|
2571
|
+
page,
|
|
2572
|
+
limit,
|
|
2573
|
+
total: 0,
|
|
2574
|
+
totalPages: 0
|
|
2575
|
+
});
|
|
2576
|
+
const [isLoading, setIsLoading] = useState12(false);
|
|
2577
|
+
const [error, setError] = useState12(null);
|
|
2578
|
+
const fetchRuns = useCallback13(async () => {
|
|
2579
|
+
if (!workflowId) return [];
|
|
2580
|
+
setIsLoading(true);
|
|
2581
|
+
setError(null);
|
|
2582
|
+
try {
|
|
2583
|
+
const query = `?page=${page}&limit=${limit}`;
|
|
2584
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/runs${query}`);
|
|
2585
|
+
if (!res.ok) {
|
|
2586
|
+
throw new Error(`Failed to list workflow runs: ${res.statusText}`);
|
|
2587
|
+
}
|
|
2588
|
+
const data = await res.json();
|
|
2589
|
+
const items = Array.isArray(data) ? data : data?.items || data?.runs || [];
|
|
2590
|
+
setRuns(items);
|
|
2591
|
+
if (data?.pagination) {
|
|
2592
|
+
setPagination({
|
|
2593
|
+
page: data.pagination.page ?? page,
|
|
2594
|
+
limit: data.pagination.limit ?? limit,
|
|
2595
|
+
total: data.pagination.total ?? items.length,
|
|
2596
|
+
totalPages: data.pagination.totalPages ?? 1
|
|
2597
|
+
});
|
|
2598
|
+
}
|
|
2599
|
+
return items;
|
|
2600
|
+
} catch (err) {
|
|
2601
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2602
|
+
setError(errorObj);
|
|
2603
|
+
return [];
|
|
2604
|
+
} finally {
|
|
2605
|
+
setIsLoading(false);
|
|
2606
|
+
}
|
|
2607
|
+
}, [fetchWithAuth, workflowId, page, limit]);
|
|
2608
|
+
const getRun = useCallback13(
|
|
2609
|
+
async (runId) => {
|
|
2610
|
+
if (!runId) throw new Error("Run ID is required");
|
|
2611
|
+
const res = await fetchWithAuth(`/workflows/runs/${runId}`);
|
|
2612
|
+
if (!res.ok) {
|
|
2613
|
+
throw new Error(`Failed to fetch workflow run (${res.status}): ${res.statusText}`);
|
|
2614
|
+
}
|
|
2615
|
+
return await res.json();
|
|
2616
|
+
},
|
|
2617
|
+
[fetchWithAuth]
|
|
2618
|
+
);
|
|
2619
|
+
const cancelRun = useCallback13(
|
|
2620
|
+
async (runId) => {
|
|
2621
|
+
if (!runId) throw new Error("Run ID is required");
|
|
2622
|
+
const res = await fetchWithAuth(`/workflows/runs/${runId}/cancel`, {
|
|
2623
|
+
method: "POST"
|
|
2624
|
+
});
|
|
2625
|
+
if (!res.ok) {
|
|
2626
|
+
throw new Error(`Failed to cancel workflow run (${res.status}): ${res.statusText}`);
|
|
2627
|
+
}
|
|
2628
|
+
setRuns(
|
|
2629
|
+
(prev) => prev.map((r) => r._id === runId ? { ...r, status: "cancelled" } : r)
|
|
2630
|
+
);
|
|
2631
|
+
},
|
|
2632
|
+
[fetchWithAuth]
|
|
2633
|
+
);
|
|
2634
|
+
useEffect12(() => {
|
|
2635
|
+
if (autoFetch && workflowId) {
|
|
2636
|
+
void fetchRuns();
|
|
2637
|
+
}
|
|
2638
|
+
}, [autoFetch, workflowId, fetchRuns]);
|
|
2639
|
+
return {
|
|
2640
|
+
runs,
|
|
2641
|
+
pagination,
|
|
2642
|
+
isLoading,
|
|
2643
|
+
error,
|
|
2644
|
+
refetch: fetchRuns,
|
|
2645
|
+
getRun,
|
|
2646
|
+
cancelRun
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2062
2650
|
// src/index.ts
|
|
2063
2651
|
import {
|
|
2064
2652
|
createLogger as createLogger2,
|
|
@@ -2067,7 +2655,7 @@ import {
|
|
|
2067
2655
|
getLogLevel,
|
|
2068
2656
|
isLevelEnabled
|
|
2069
2657
|
} from "@personaai/logger";
|
|
2070
|
-
var VERSION = "0.
|
|
2658
|
+
var VERSION = "0.9.0";
|
|
2071
2659
|
export {
|
|
2072
2660
|
PersonaProvider,
|
|
2073
2661
|
VERSION,
|
|
@@ -2087,6 +2675,10 @@ export {
|
|
|
2087
2675
|
useMemory,
|
|
2088
2676
|
usePersonaContext,
|
|
2089
2677
|
useThreads,
|
|
2090
|
-
useVoice
|
|
2678
|
+
useVoice,
|
|
2679
|
+
useWorkflow,
|
|
2680
|
+
useWorkflowRuns,
|
|
2681
|
+
useWorkflowStream,
|
|
2682
|
+
useWorkflows
|
|
2091
2683
|
};
|
|
2092
2684
|
//# sourceMappingURL=index.js.map
|