@qoniai/qoni 0.4.0 → 0.4.2
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/CHANGELOG.md +61 -4
- package/README.md +28 -14
- package/README.zh-CN.md +21 -14
- package/dist/index.cjs +208 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +208 -109
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/qoni-sdk/SKILL.md +6 -6
- package/skills/qoni-sdk/agents/openai.yaml +1 -1
package/dist/index.js
CHANGED
|
@@ -768,11 +768,11 @@ var RunHandle = class {
|
|
|
768
768
|
}
|
|
769
769
|
/** Drive the run to a terminal state and return the settled result. */
|
|
770
770
|
async wait(opts = {}) {
|
|
771
|
-
const { signal, timedOut } = withTimeout(opts.signal, opts.timeoutMs);
|
|
771
|
+
const { signal, timedOut, dispose } = withTimeout(opts.signal, opts.timeoutMs);
|
|
772
772
|
let lastEventId;
|
|
773
773
|
let eventIdAtLastClose;
|
|
774
774
|
let sawCleanClose = false;
|
|
775
|
-
let
|
|
775
|
+
let terminalDetail;
|
|
776
776
|
let terminalDoneReason;
|
|
777
777
|
let screenshotIndex = 0;
|
|
778
778
|
try {
|
|
@@ -780,24 +780,28 @@ var RunHandle = class {
|
|
|
780
780
|
for await (const event of this.events({
|
|
781
781
|
signal,
|
|
782
782
|
lastEventId,
|
|
783
|
-
onWireEvent:
|
|
783
|
+
onWireEvent: (wire) => waitWithSignal(() => {
|
|
784
|
+
if (wire.id !== void 0) lastEventId = wire.id;
|
|
785
|
+
return opts.onWireEvent?.(wire);
|
|
786
|
+
}, signal),
|
|
784
787
|
onReconnect: opts.onReconnect
|
|
785
788
|
})) {
|
|
786
|
-
|
|
787
|
-
await opts.onEvent?.(event);
|
|
789
|
+
await waitWithSignal(() => opts.onEvent?.(event), signal);
|
|
788
790
|
if (event.type === "screenshot") {
|
|
789
|
-
await opts.onScreenshot?.(event.image, screenshotIndex++);
|
|
791
|
+
await waitWithSignal(() => opts.onScreenshot?.(event.image, screenshotIndex++), signal);
|
|
790
792
|
} else if (event.type === "interaction") {
|
|
791
|
-
await opts.onInteraction?.(this.interactionHandle(event.data), event);
|
|
793
|
+
await waitWithSignal(() => opts.onInteraction?.(this.interactionHandle(event.data), event), signal);
|
|
792
794
|
}
|
|
793
795
|
if (event.isTerminal) {
|
|
794
|
-
terminalPayload = isJsonObject(event.raw.data) ? event.raw.data : void 0;
|
|
795
796
|
if (event.type === "done") terminalDoneReason = event.data.terminalReason || void 0;
|
|
796
797
|
break settled;
|
|
797
798
|
}
|
|
798
799
|
}
|
|
799
|
-
const detail2 = await this.ops.getDetail();
|
|
800
|
-
if (isTerminalRunStatus(detail2.status))
|
|
800
|
+
const detail2 = await waitWithSignal(() => this.ops.getDetail(signal), signal);
|
|
801
|
+
if (isTerminalRunStatus(normalizeRunStatus(this.id, detail2).status)) {
|
|
802
|
+
terminalDetail = detail2;
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
801
805
|
if (sawCleanClose && lastEventId === eventIdAtLastClose) {
|
|
802
806
|
throw new QoniError(
|
|
803
807
|
`run event stream ended before a terminal event while the run is still "${String(detail2.status)}" \u2014 re-attach or poll status()`,
|
|
@@ -807,6 +811,11 @@ var RunHandle = class {
|
|
|
807
811
|
sawCleanClose = true;
|
|
808
812
|
eventIdAtLastClose = lastEventId;
|
|
809
813
|
}
|
|
814
|
+
const detail = terminalDetail ?? await waitWithSignal(() => this.ops.getDetail(signal), signal);
|
|
815
|
+
this.#adoptSessionRef(detail);
|
|
816
|
+
const result = settleRunResult(this.id, detail, [], terminalDoneReason);
|
|
817
|
+
result.artifacts = this.ops.listArtifacts ? await waitWithSignal(() => this.ops.listArtifacts(signal), signal) : [];
|
|
818
|
+
return result;
|
|
810
819
|
} catch (err) {
|
|
811
820
|
if (timedOut() && !opts.signal?.aborted) {
|
|
812
821
|
throw new QoniTimeoutError(
|
|
@@ -815,17 +824,9 @@ var RunHandle = class {
|
|
|
815
824
|
);
|
|
816
825
|
}
|
|
817
826
|
throw err;
|
|
827
|
+
} finally {
|
|
828
|
+
dispose();
|
|
818
829
|
}
|
|
819
|
-
let detail;
|
|
820
|
-
try {
|
|
821
|
-
detail = await this.ops.getDetail();
|
|
822
|
-
} catch (err) {
|
|
823
|
-
if (!terminalPayload) throw err;
|
|
824
|
-
detail = terminalPayload;
|
|
825
|
-
}
|
|
826
|
-
this.#adoptSessionRef(detail);
|
|
827
|
-
const artifacts = this.ops.listArtifacts ? await this.ops.listArtifacts() : [];
|
|
828
|
-
return settleRunResult(this.id, detail, artifacts, terminalDoneReason);
|
|
829
830
|
}
|
|
830
831
|
/**
|
|
831
832
|
* Wrap an {@link Interaction} (from `event.data` of an `interaction` event)
|
|
@@ -878,15 +879,12 @@ function normalizeRunStatus(id, detail) {
|
|
|
878
879
|
function settleRunResult(runId, detail, artifacts, fallbackTerminalReason) {
|
|
879
880
|
const terminalReason = (typeof detail.terminal_reason === "string" ? detail.terminal_reason : void 0) ?? fallbackTerminalReason;
|
|
880
881
|
const isTaskSuccessful = typeof detail.is_task_successful === "boolean" ? detail.is_task_successful : void 0;
|
|
881
|
-
|
|
882
|
-
if (isTerminalRunStatus(
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
status = "failed";
|
|
888
|
-
} else {
|
|
889
|
-
status = "succeeded";
|
|
882
|
+
const status = normalizeRunStatus(runId, detail).status;
|
|
883
|
+
if (!isTerminalRunStatus(status)) {
|
|
884
|
+
throw new QoniError(
|
|
885
|
+
"run detail did not confirm a terminal state \u2014 re-attach or poll status()",
|
|
886
|
+
{ code: "upstream.failed", retryable: true, body: detail }
|
|
887
|
+
);
|
|
890
888
|
}
|
|
891
889
|
return {
|
|
892
890
|
runId,
|
|
@@ -913,19 +911,40 @@ function isVideoFrame(wire) {
|
|
|
913
911
|
return type === "browser_video_frame" || type === "run.browser_video_frame";
|
|
914
912
|
}
|
|
915
913
|
function withTimeout(signal, timeoutMs) {
|
|
916
|
-
if (!timeoutMs) return { signal, timedOut: () => false
|
|
914
|
+
if (!timeoutMs) return { signal, timedOut: () => false, dispose: () => {
|
|
915
|
+
} };
|
|
917
916
|
const controller = new AbortController();
|
|
918
917
|
let fired = false;
|
|
919
918
|
const timer = setTimeout(() => {
|
|
920
919
|
fired = true;
|
|
921
920
|
controller.abort();
|
|
922
921
|
}, timeoutMs);
|
|
923
|
-
|
|
922
|
+
const onAbort = () => controller.abort(signal?.reason);
|
|
923
|
+
const dispose = () => {
|
|
924
|
+
clearTimeout(timer);
|
|
925
|
+
signal?.removeEventListener("abort", onAbort);
|
|
926
|
+
};
|
|
927
|
+
controller.signal.addEventListener("abort", dispose, { once: true });
|
|
924
928
|
if (signal) {
|
|
925
|
-
if (signal.aborted)
|
|
926
|
-
else signal.addEventListener("abort",
|
|
929
|
+
if (signal.aborted) onAbort();
|
|
930
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
927
931
|
}
|
|
928
|
-
return { signal: controller.signal, timedOut: () => fired };
|
|
932
|
+
return { signal: controller.signal, timedOut: () => fired, dispose };
|
|
933
|
+
}
|
|
934
|
+
function waitWithSignal(operation, signal) {
|
|
935
|
+
if (!signal) return Promise.resolve().then(operation);
|
|
936
|
+
return new Promise((resolve, reject) => {
|
|
937
|
+
const onAbort = () => reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
938
|
+
if (signal.aborted) {
|
|
939
|
+
onAbort();
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
943
|
+
Promise.resolve().then(() => {
|
|
944
|
+
signal.throwIfAborted();
|
|
945
|
+
return operation();
|
|
946
|
+
}).then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
947
|
+
});
|
|
929
948
|
}
|
|
930
949
|
|
|
931
950
|
// src/deep-research.ts
|
|
@@ -940,6 +959,7 @@ function createDeepResearchNamespace(transport) {
|
|
|
940
959
|
`/deep_research/runs/${encodeURIComponent(input.runId)}`,
|
|
941
960
|
input.token,
|
|
942
961
|
{
|
|
962
|
+
signal: input.signal,
|
|
943
963
|
requiredScopes: ["webagent.deep_research:read"]
|
|
944
964
|
}
|
|
945
965
|
),
|
|
@@ -985,6 +1005,7 @@ function createDeepResearchNamespace(transport) {
|
|
|
985
1005
|
`/deep_research/runs/${encodeURIComponent(input.runId)}/artifacts`,
|
|
986
1006
|
input.token,
|
|
987
1007
|
{
|
|
1008
|
+
signal: input.signal,
|
|
988
1009
|
requiredScopes: ["webagent.deep_research:read"]
|
|
989
1010
|
}
|
|
990
1011
|
),
|
|
@@ -999,7 +1020,7 @@ function createDeepResearchNamespace(transport) {
|
|
|
999
1020
|
};
|
|
1000
1021
|
function buildOps(token, runId) {
|
|
1001
1022
|
return {
|
|
1002
|
-
getDetail: async () => asRecord((await api.get({ token, runId })).data),
|
|
1023
|
+
getDetail: async (signal) => asRecord((await api.get({ token, runId, signal })).data),
|
|
1003
1024
|
streamEvents: (opts) => api.events({
|
|
1004
1025
|
token,
|
|
1005
1026
|
runId,
|
|
@@ -1019,8 +1040,8 @@ function createDeepResearchNamespace(transport) {
|
|
|
1019
1040
|
);
|
|
1020
1041
|
},
|
|
1021
1042
|
cancel: async () => asRecord((await api.cancel({ token, runId })).data),
|
|
1022
|
-
listArtifacts: async () => {
|
|
1023
|
-
const res = await api.listArtifacts({ token, runId });
|
|
1043
|
+
listArtifacts: async (signal) => {
|
|
1044
|
+
const res = await api.listArtifacts({ token, runId, signal });
|
|
1024
1045
|
const items = Array.isArray(res.data?.items) ? res.data.items : [];
|
|
1025
1046
|
return items.filter(isJsonObject).map((item) => toArtifact(item, token, runId));
|
|
1026
1047
|
}
|
|
@@ -1140,6 +1161,7 @@ function createDoAnythingNamespace(transport) {
|
|
|
1140
1161
|
`/do_anything/runs/${encodeURIComponent(input.runId)}`,
|
|
1141
1162
|
input.token,
|
|
1142
1163
|
{
|
|
1164
|
+
signal: input.signal,
|
|
1143
1165
|
requiredScopes: ["webagent.do_anything:read"]
|
|
1144
1166
|
}
|
|
1145
1167
|
),
|
|
@@ -1197,7 +1219,7 @@ function createDoAnythingNamespace(transport) {
|
|
|
1197
1219
|
};
|
|
1198
1220
|
function buildOps(token, runId, capture) {
|
|
1199
1221
|
return {
|
|
1200
|
-
getDetail: async () => asRecord2((await api.getRun({ token, runId })).data),
|
|
1222
|
+
getDetail: async (signal) => asRecord2((await api.getRun({ token, runId, signal })).data),
|
|
1201
1223
|
streamEvents: (opts) => api.events({
|
|
1202
1224
|
token,
|
|
1203
1225
|
runId,
|
|
@@ -1791,6 +1813,7 @@ function createWebSearchNamespace(transport) {
|
|
|
1791
1813
|
`/web_search/runs/${encodeURIComponent(input.runId)}`,
|
|
1792
1814
|
input.token,
|
|
1793
1815
|
{
|
|
1816
|
+
signal: input.signal,
|
|
1794
1817
|
requiredScopes: ["webagent.web_search:read"]
|
|
1795
1818
|
}
|
|
1796
1819
|
),
|
|
@@ -1816,7 +1839,7 @@ function createWebSearchNamespace(transport) {
|
|
|
1816
1839
|
};
|
|
1817
1840
|
function buildOps(token, runId) {
|
|
1818
1841
|
return {
|
|
1819
|
-
getDetail: async () => asRecord4((await api.get({ token, runId })).data),
|
|
1842
|
+
getDetail: async (signal) => asRecord4((await api.get({ token, runId, signal })).data),
|
|
1820
1843
|
streamEvents: (opts) => api.events({
|
|
1821
1844
|
token,
|
|
1822
1845
|
runId,
|
|
@@ -1909,8 +1932,8 @@ function renameKeys5(value, mapping) {
|
|
|
1909
1932
|
}
|
|
1910
1933
|
|
|
1911
1934
|
// src/client.ts
|
|
1912
|
-
var DEFAULT_QONI_HOST = "https://
|
|
1913
|
-
var
|
|
1935
|
+
var DEFAULT_QONI_HOST = "https://dashboard.qoni.ai";
|
|
1936
|
+
var Qoni = class {
|
|
1914
1937
|
constructor(options) {
|
|
1915
1938
|
this.options = options;
|
|
1916
1939
|
const accessKey = options.accessKey ?? options.accessKeyId;
|
|
@@ -2089,7 +2112,9 @@ var QoniAgentKit = class {
|
|
|
2089
2112
|
}
|
|
2090
2113
|
}
|
|
2091
2114
|
async webAgentJson(method, path, token, payload = {}) {
|
|
2115
|
+
payload.signal?.throwIfAborted();
|
|
2092
2116
|
const runtimeToken = await this.productTokenFor("webagent", token, payload.requiredScopes);
|
|
2117
|
+
payload.signal?.throwIfAborted();
|
|
2093
2118
|
const baseUrl = await this.baseUrlFor("webagent");
|
|
2094
2119
|
return this.jsonRequest(
|
|
2095
2120
|
baseUrl,
|
|
@@ -2100,9 +2125,11 @@ var QoniAgentKit = class {
|
|
|
2100
2125
|
// Binary GET against the WebAgent API — artifact content downloads. JSON
|
|
2101
2126
|
// error payloads still map to typed QoniErrors.
|
|
2102
2127
|
async webAgentBytes(path, token, payload = {}) {
|
|
2128
|
+
payload.signal?.throwIfAborted();
|
|
2103
2129
|
const runtimeToken = await this.productTokenFor("webagent", token, payload.requiredScopes);
|
|
2130
|
+
payload.signal?.throwIfAborted();
|
|
2104
2131
|
const baseUrl = await this.baseUrlFor("webagent");
|
|
2105
|
-
|
|
2132
|
+
return this.fetchWithTimeout(
|
|
2106
2133
|
urlWithBasePath(baseUrl, this.webAgentPath(runtimeToken, path), payload.query),
|
|
2107
2134
|
{
|
|
2108
2135
|
method: "GET",
|
|
@@ -2111,20 +2138,22 @@ var QoniAgentKit = class {
|
|
|
2111
2138
|
authorization: `Bearer ${runtimeToken}`,
|
|
2112
2139
|
...payload.headers || {}
|
|
2113
2140
|
}
|
|
2114
|
-
}
|
|
2141
|
+
},
|
|
2142
|
+
async (response, signal) => {
|
|
2143
|
+
if (!response.ok) {
|
|
2144
|
+
throw errorFromPayload(
|
|
2145
|
+
response.status,
|
|
2146
|
+
await readJson(response, signal),
|
|
2147
|
+
`Qoni request failed with HTTP ${response.status}`
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
return {
|
|
2151
|
+
bytes: await readBody(response, signal),
|
|
2152
|
+
mime: response.headers.get("content-type") || "application/octet-stream"
|
|
2153
|
+
};
|
|
2154
|
+
},
|
|
2155
|
+
payload.signal
|
|
2115
2156
|
);
|
|
2116
|
-
if (!response.ok) {
|
|
2117
|
-
throw errorFromPayload(
|
|
2118
|
-
response.status,
|
|
2119
|
-
await readJson(response),
|
|
2120
|
-
`Qoni request failed with HTTP ${response.status}`
|
|
2121
|
-
);
|
|
2122
|
-
}
|
|
2123
|
-
const buffer = await response.arrayBuffer();
|
|
2124
|
-
return {
|
|
2125
|
-
bytes: new Uint8Array(buffer),
|
|
2126
|
-
mime: response.headers.get("content-type") || "application/octet-stream"
|
|
2127
|
-
};
|
|
2128
2157
|
}
|
|
2129
2158
|
async *webAgentSSE(path, token, payload = {}) {
|
|
2130
2159
|
const runtimeToken = await this.productTokenFor("webagent", token, payload.requiredScopes);
|
|
@@ -2164,15 +2193,16 @@ var QoniAgentKit = class {
|
|
|
2164
2193
|
});
|
|
2165
2194
|
}
|
|
2166
2195
|
async jsonRequest(baseUrl, pathname, options) {
|
|
2167
|
-
const response = await this.fetchWithTimeout(
|
|
2196
|
+
const { response, payload } = await this.fetchWithTimeout(
|
|
2168
2197
|
urlWithBasePath(baseUrl, pathname, options.query),
|
|
2169
2198
|
{
|
|
2170
2199
|
method: options.method,
|
|
2171
2200
|
headers: requestHeaders(options),
|
|
2172
2201
|
body: requestBody(options.body)
|
|
2173
|
-
}
|
|
2202
|
+
},
|
|
2203
|
+
async (response2, signal) => ({ response: response2, payload: await readJson(response2, signal) }),
|
|
2204
|
+
options.signal
|
|
2174
2205
|
);
|
|
2175
|
-
const payload = await readJson(response);
|
|
2176
2206
|
if (!response.ok) {
|
|
2177
2207
|
throw errorFromPayload(
|
|
2178
2208
|
response.status,
|
|
@@ -2222,45 +2252,55 @@ var QoniAgentKit = class {
|
|
|
2222
2252
|
}
|
|
2223
2253
|
}
|
|
2224
2254
|
async *sseConnectOnce(baseUrl, pathname, options) {
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
accept: "text/event-stream",
|
|
2231
|
-
authorization: `Bearer ${options.token}`,
|
|
2232
|
-
...options.headers || {},
|
|
2233
|
-
...options.lastEventId ? { "last-event-id": options.lastEventId } : {}
|
|
2234
|
-
}
|
|
2235
|
-
},
|
|
2236
|
-
options.signal,
|
|
2237
|
-
{ streamBody: true }
|
|
2238
|
-
);
|
|
2239
|
-
if (!response.ok) {
|
|
2240
|
-
throw errorFromPayload(
|
|
2241
|
-
response.status,
|
|
2242
|
-
await readJson(response),
|
|
2243
|
-
`Qoni stream failed with HTTP ${response.status}`
|
|
2244
|
-
);
|
|
2245
|
-
}
|
|
2246
|
-
if (!response.body) {
|
|
2247
|
-
throw errorFromPayload(502, void 0, "Qoni stream response has no body");
|
|
2248
|
-
}
|
|
2249
|
-
const reader = response.body.getReader();
|
|
2255
|
+
let cleanup;
|
|
2256
|
+
let reader;
|
|
2257
|
+
const onAbort = () => {
|
|
2258
|
+
void reader?.cancel(options.signal?.reason).catch(() => void 0);
|
|
2259
|
+
};
|
|
2250
2260
|
const decoder = new TextDecoder();
|
|
2251
2261
|
let buffer = "";
|
|
2252
2262
|
try {
|
|
2263
|
+
const response = await this.fetchWithTimeout(
|
|
2264
|
+
urlWithBasePath(baseUrl, pathname, options.query),
|
|
2265
|
+
{
|
|
2266
|
+
method: "GET",
|
|
2267
|
+
headers: {
|
|
2268
|
+
accept: "text/event-stream",
|
|
2269
|
+
authorization: `Bearer ${options.token}`,
|
|
2270
|
+
...options.headers || {},
|
|
2271
|
+
...options.lastEventId ? { "last-event-id": options.lastEventId } : {}
|
|
2272
|
+
}
|
|
2273
|
+
},
|
|
2274
|
+
async (response2, signal) => {
|
|
2275
|
+
if (!response2.ok) {
|
|
2276
|
+
throw errorFromPayload(
|
|
2277
|
+
response2.status,
|
|
2278
|
+
await readJson(response2, signal),
|
|
2279
|
+
`Qoni stream failed with HTTP ${response2.status}`
|
|
2280
|
+
);
|
|
2281
|
+
}
|
|
2282
|
+
return response2;
|
|
2283
|
+
},
|
|
2284
|
+
options.signal,
|
|
2285
|
+
{ streamCleanup: (dispose) => {
|
|
2286
|
+
cleanup = dispose;
|
|
2287
|
+
} }
|
|
2288
|
+
);
|
|
2289
|
+
if (!response.body) {
|
|
2290
|
+
throw errorFromPayload(502, void 0, "Qoni stream response has no body");
|
|
2291
|
+
}
|
|
2292
|
+
reader = response.body.getReader();
|
|
2293
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2253
2294
|
while (true) {
|
|
2254
|
-
|
|
2255
|
-
await reader.cancel().catch(() => void 0);
|
|
2256
|
-
throw options.signal.reason || new DOMException("Aborted", "AbortError");
|
|
2257
|
-
}
|
|
2295
|
+
options.signal?.throwIfAborted();
|
|
2258
2296
|
const { done, value } = await reader.read();
|
|
2297
|
+
options.signal?.throwIfAborted();
|
|
2259
2298
|
if (done) break;
|
|
2260
2299
|
buffer += decoder.decode(value, { stream: true });
|
|
2261
2300
|
const chunks = buffer.split(/\r?\n\r?\n/);
|
|
2262
2301
|
buffer = chunks.pop() || "";
|
|
2263
2302
|
for (const chunk of chunks) {
|
|
2303
|
+
options.signal?.throwIfAborted();
|
|
2264
2304
|
const event = parseSSEEvent(chunk);
|
|
2265
2305
|
if (event) yield event;
|
|
2266
2306
|
}
|
|
@@ -2268,10 +2308,16 @@ var QoniAgentKit = class {
|
|
|
2268
2308
|
const finalEvent = parseSSEEvent(buffer);
|
|
2269
2309
|
if (finalEvent) yield finalEvent;
|
|
2270
2310
|
} finally {
|
|
2271
|
-
|
|
2311
|
+
cleanup?.();
|
|
2312
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
2313
|
+
void reader?.cancel().catch(() => void 0);
|
|
2314
|
+
try {
|
|
2315
|
+
reader?.releaseLock();
|
|
2316
|
+
} catch {
|
|
2317
|
+
}
|
|
2272
2318
|
}
|
|
2273
2319
|
}
|
|
2274
|
-
async fetchWithTimeout(input, init, signal, opts) {
|
|
2320
|
+
async fetchWithTimeout(input, init, consume, signal, opts) {
|
|
2275
2321
|
if (signal?.aborted) throw signal.reason || new DOMException("Aborted", "AbortError");
|
|
2276
2322
|
const controller = new AbortController();
|
|
2277
2323
|
const resolved = resolveLocalGenauthRequest(input);
|
|
@@ -2286,24 +2332,46 @@ var QoniAgentKit = class {
|
|
|
2286
2332
|
didTimeout = true;
|
|
2287
2333
|
controller.abort();
|
|
2288
2334
|
}, this.timeoutMs);
|
|
2335
|
+
let response;
|
|
2289
2336
|
try {
|
|
2290
|
-
|
|
2337
|
+
response = await new Promise((resolve, reject) => {
|
|
2338
|
+
const onAbort = () => reject(controller.signal.reason);
|
|
2339
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
2340
|
+
Promise.resolve().then(() => {
|
|
2341
|
+
controller.signal.throwIfAborted();
|
|
2342
|
+
return this.fetchImpl(resolved.url, { ...requestInit, signal: controller.signal });
|
|
2343
|
+
}).then((response2) => {
|
|
2344
|
+
if (controller.signal.aborted) {
|
|
2345
|
+
void response2.body?.cancel(controller.signal.reason).catch(() => void 0);
|
|
2346
|
+
reject(controller.signal.reason);
|
|
2347
|
+
} else {
|
|
2348
|
+
resolve(response2);
|
|
2349
|
+
}
|
|
2350
|
+
}, reject).finally(() => controller.signal.removeEventListener("abort", onAbort));
|
|
2351
|
+
});
|
|
2352
|
+
const result = await consume(response, controller.signal);
|
|
2353
|
+
controller.signal.throwIfAborted();
|
|
2354
|
+
return result;
|
|
2291
2355
|
} catch (error) {
|
|
2292
|
-
|
|
2356
|
+
void response?.body?.cancel().catch(() => void 0);
|
|
2357
|
+
if (signal?.aborted) throw signal.reason;
|
|
2358
|
+
if (didTimeout || isConnectTimeoutError(error)) {
|
|
2293
2359
|
throw timeoutError(error);
|
|
2294
2360
|
}
|
|
2295
2361
|
if (isFetchNetworkError(error)) throw networkError(error);
|
|
2296
2362
|
throw error;
|
|
2297
2363
|
} finally {
|
|
2298
2364
|
clearTimeout(timeout);
|
|
2299
|
-
|
|
2365
|
+
const cleanup = () => signal?.removeEventListener("abort", abortFromSignal);
|
|
2366
|
+
if (opts?.streamCleanup) opts.streamCleanup(cleanup);
|
|
2367
|
+
else cleanup();
|
|
2300
2368
|
}
|
|
2301
2369
|
}
|
|
2302
2370
|
async baseUrlFor(service) {
|
|
2303
2371
|
const explicit = this.explicitBaseUrlFor(service);
|
|
2304
2372
|
if (explicit) return explicit;
|
|
2305
|
-
const runtime = await this.runtimeConfig();
|
|
2306
2373
|
if (service === "qoni") return this.host;
|
|
2374
|
+
const runtime = await this.runtimeConfig();
|
|
2307
2375
|
const hostOrigin = originBaseUrl(this.host);
|
|
2308
2376
|
if (service === "genauth") return runtime.genauthBaseUrl ?? hostOrigin;
|
|
2309
2377
|
if (service === "gumem") return runtime.gumemBaseUrl ?? hostOrigin;
|
|
@@ -2352,7 +2420,7 @@ var QoniAgentKit = class {
|
|
|
2352
2420
|
pathname,
|
|
2353
2421
|
{}
|
|
2354
2422
|
);
|
|
2355
|
-
const response = await this.fetchWithTimeout(
|
|
2423
|
+
const { response, payload } = await this.fetchWithTimeout(
|
|
2356
2424
|
urlWithBasePath(this.host, pathname),
|
|
2357
2425
|
{
|
|
2358
2426
|
method: "GET",
|
|
@@ -2360,9 +2428,9 @@ var QoniAgentKit = class {
|
|
|
2360
2428
|
accept: "application/json",
|
|
2361
2429
|
...headers
|
|
2362
2430
|
}
|
|
2363
|
-
}
|
|
2431
|
+
},
|
|
2432
|
+
async (response2, signal) => ({ response: response2, payload: await readJson(response2, signal) })
|
|
2364
2433
|
);
|
|
2365
|
-
const payload = await readJson(response);
|
|
2366
2434
|
if (!response.ok) {
|
|
2367
2435
|
throw errorFromPayload(
|
|
2368
2436
|
response.status,
|
|
@@ -2456,18 +2524,52 @@ function requestBody(body) {
|
|
|
2456
2524
|
function isBodyInit(value) {
|
|
2457
2525
|
return typeof value === "string" || value instanceof ArrayBuffer || value instanceof Blob || value instanceof FormData || value instanceof URLSearchParams;
|
|
2458
2526
|
}
|
|
2459
|
-
async function
|
|
2527
|
+
async function readBody(response, signal) {
|
|
2528
|
+
if (!response.body) {
|
|
2529
|
+
signal.throwIfAborted();
|
|
2530
|
+
return new Uint8Array();
|
|
2531
|
+
}
|
|
2532
|
+
const reader = response.body.getReader();
|
|
2533
|
+
const onAbort = () => {
|
|
2534
|
+
void reader.cancel(signal.reason).catch(() => void 0);
|
|
2535
|
+
};
|
|
2536
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2537
|
+
try {
|
|
2538
|
+
const chunks = [];
|
|
2539
|
+
let size = 0;
|
|
2540
|
+
for (; ; ) {
|
|
2541
|
+
signal.throwIfAborted();
|
|
2542
|
+
const { done, value } = await reader.read();
|
|
2543
|
+
signal.throwIfAborted();
|
|
2544
|
+
if (done) break;
|
|
2545
|
+
chunks.push(value);
|
|
2546
|
+
size += value.byteLength;
|
|
2547
|
+
}
|
|
2548
|
+
const bytes = new Uint8Array(size);
|
|
2549
|
+
let offset = 0;
|
|
2550
|
+
for (const chunk of chunks) {
|
|
2551
|
+
bytes.set(chunk, offset);
|
|
2552
|
+
offset += chunk.byteLength;
|
|
2553
|
+
}
|
|
2554
|
+
return bytes;
|
|
2555
|
+
} finally {
|
|
2556
|
+
signal.removeEventListener("abort", onAbort);
|
|
2557
|
+
void reader.cancel().catch(() => void 0);
|
|
2558
|
+
reader.releaseLock();
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
async function readJson(response, signal) {
|
|
2460
2562
|
const contentType = response.headers.get("content-type")?.toLowerCase() || "";
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
if (
|
|
2464
|
-
|
|
2563
|
+
const body = new TextDecoder().decode(await readBody(response, signal));
|
|
2564
|
+
try {
|
|
2565
|
+
if (contentType.includes("ndjson") || contentType.includes("jsonl")) {
|
|
2566
|
+
if (!body.trim()) return [];
|
|
2465
2567
|
return body.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
|
|
2466
|
-
} catch {
|
|
2467
|
-
return void 0;
|
|
2468
2568
|
}
|
|
2569
|
+
return JSON.parse(body);
|
|
2570
|
+
} catch {
|
|
2571
|
+
return void 0;
|
|
2469
2572
|
}
|
|
2470
|
-
return response.json().catch(() => void 0);
|
|
2471
2573
|
}
|
|
2472
2574
|
function normalizeResponse(payload, _service) {
|
|
2473
2575
|
if (isJsonObject(payload) && ("data" in payload || "meta" in payload)) {
|
|
@@ -2710,6 +2812,7 @@ function isRetryableStreamError(err) {
|
|
|
2710
2812
|
if (err && typeof err === "object") {
|
|
2711
2813
|
const e = err;
|
|
2712
2814
|
if (e.name === "AbortError") return false;
|
|
2815
|
+
if (e.status === 408) return true;
|
|
2713
2816
|
if (typeof e.retryable === "boolean") return e.retryable;
|
|
2714
2817
|
if (typeof e.status === "number") {
|
|
2715
2818
|
return e.status >= 500 || e.status === 408 || e.status === 429;
|
|
@@ -2816,9 +2919,6 @@ function normalizeDelegateAgentTokenResponse(response) {
|
|
|
2816
2919
|
}
|
|
2817
2920
|
};
|
|
2818
2921
|
}
|
|
2819
|
-
function isAbortError(error) {
|
|
2820
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
2821
|
-
}
|
|
2822
2922
|
function isConnectTimeoutError(error) {
|
|
2823
2923
|
const cause = errorCause(error);
|
|
2824
2924
|
return errorCode(cause) === "UND_ERR_CONNECT_TIMEOUT" || errorName(cause) === "ConnectTimeoutError";
|
|
@@ -2843,8 +2943,7 @@ export {
|
|
|
2843
2943
|
InteractionStatuses,
|
|
2844
2944
|
InteractionTypes,
|
|
2845
2945
|
MonitorHandle,
|
|
2846
|
-
|
|
2847
|
-
QoniAgentKit,
|
|
2946
|
+
Qoni,
|
|
2848
2947
|
QoniAuthError,
|
|
2849
2948
|
QoniDelegationRequiredError,
|
|
2850
2949
|
QoniError,
|