@qoniai/qoni 0.4.1 → 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 +21 -0
- package/README.md +21 -1
- package/README.zh-CN.md +14 -1
- package/dist/index.cjs +201 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +201 -105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -224,6 +224,7 @@ interface RawRequestInput extends TokenInput {
|
|
|
224
224
|
headers?: Record<string, string>;
|
|
225
225
|
}
|
|
226
226
|
interface RequestPayload {
|
|
227
|
+
signal?: AbortSignal;
|
|
227
228
|
query?: JsonObject;
|
|
228
229
|
body?: JsonObject | BodyInit;
|
|
229
230
|
headers?: Record<string, string>;
|
|
@@ -638,7 +639,7 @@ interface RunWaitOptions {
|
|
|
638
639
|
* with the token + run addressing already bound.
|
|
639
640
|
*/
|
|
640
641
|
interface RunWireOps {
|
|
641
|
-
getDetail(): Promise<JsonObject>;
|
|
642
|
+
getDetail(signal?: AbortSignal): Promise<JsonObject>;
|
|
642
643
|
streamEvents(opts: RunEventsOptions): AsyncIterable<QoniEvent<unknown>>;
|
|
643
644
|
/**
|
|
644
645
|
* Invoke an interaction action by posting to its declared endpoint. The
|
|
@@ -648,7 +649,7 @@ interface RunWireOps {
|
|
|
648
649
|
postAction(action: Action, body: JsonObject | undefined): Promise<void>;
|
|
649
650
|
cancel(reason?: string): Promise<JsonObject>;
|
|
650
651
|
/** Products without artifacts leave this undefined → `result.artifacts` is []. */
|
|
651
|
-
listArtifacts?(): Promise<Artifact[]>;
|
|
652
|
+
listArtifacts?(signal?: AbortSignal): Promise<Artifact[]>;
|
|
652
653
|
}
|
|
653
654
|
/**
|
|
654
655
|
* Handle to a single run. Generic over the product's event set (分型): a
|
|
@@ -782,6 +783,7 @@ declare function createDoAnythingNamespace(transport: QoniTransport): {
|
|
|
782
783
|
createRun: <T = unknown>(input: RuntimeTokenInput & JsonObject) => Promise<QoniResponse<T>>;
|
|
783
784
|
getRun: <T = unknown>(input: RuntimeTokenInput & {
|
|
784
785
|
runId: string;
|
|
786
|
+
signal?: AbortSignal;
|
|
785
787
|
}) => Promise<QoniResponse<T>>;
|
|
786
788
|
events: <T = unknown>(input: RuntimeTokenInput & {
|
|
787
789
|
runId: string;
|
|
@@ -862,6 +864,7 @@ declare function createDeepResearchNamespace(transport: QoniTransport): {
|
|
|
862
864
|
run: <T = unknown>(input: RuntimeTokenInput & JsonObject) => Promise<QoniResponse<T>>;
|
|
863
865
|
get: <T = unknown>(input: RuntimeTokenInput & {
|
|
864
866
|
runId: string;
|
|
867
|
+
signal?: AbortSignal;
|
|
865
868
|
}) => Promise<QoniResponse<T>>;
|
|
866
869
|
events: <T = unknown>(input: RuntimeTokenInput & {
|
|
867
870
|
runId: string;
|
|
@@ -885,6 +888,7 @@ declare function createDeepResearchNamespace(transport: QoniTransport): {
|
|
|
885
888
|
} & JsonObject) => Promise<QoniResponse<T>>;
|
|
886
889
|
listArtifacts: <T = unknown>(input: RuntimeTokenInput & {
|
|
887
890
|
runId: string;
|
|
891
|
+
signal?: AbortSignal;
|
|
888
892
|
}) => Promise<QoniResponse<T>>;
|
|
889
893
|
getArtifact: <T = unknown>(input: RuntimeTokenInput & {
|
|
890
894
|
runId: string;
|
|
@@ -1190,6 +1194,7 @@ declare function createWebSearchNamespace(transport: QoniTransport): {
|
|
|
1190
1194
|
run: <T = unknown>(input: RuntimeTokenInput & JsonObject) => Promise<QoniResponse<T>>;
|
|
1191
1195
|
get: <T = unknown>(input: RuntimeTokenInput & {
|
|
1192
1196
|
runId: string;
|
|
1197
|
+
signal?: AbortSignal;
|
|
1193
1198
|
}) => Promise<QoniResponse<T>>;
|
|
1194
1199
|
events: <T = unknown>(input: RuntimeTokenInput & {
|
|
1195
1200
|
runId: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -224,6 +224,7 @@ interface RawRequestInput extends TokenInput {
|
|
|
224
224
|
headers?: Record<string, string>;
|
|
225
225
|
}
|
|
226
226
|
interface RequestPayload {
|
|
227
|
+
signal?: AbortSignal;
|
|
227
228
|
query?: JsonObject;
|
|
228
229
|
body?: JsonObject | BodyInit;
|
|
229
230
|
headers?: Record<string, string>;
|
|
@@ -638,7 +639,7 @@ interface RunWaitOptions {
|
|
|
638
639
|
* with the token + run addressing already bound.
|
|
639
640
|
*/
|
|
640
641
|
interface RunWireOps {
|
|
641
|
-
getDetail(): Promise<JsonObject>;
|
|
642
|
+
getDetail(signal?: AbortSignal): Promise<JsonObject>;
|
|
642
643
|
streamEvents(opts: RunEventsOptions): AsyncIterable<QoniEvent<unknown>>;
|
|
643
644
|
/**
|
|
644
645
|
* Invoke an interaction action by posting to its declared endpoint. The
|
|
@@ -648,7 +649,7 @@ interface RunWireOps {
|
|
|
648
649
|
postAction(action: Action, body: JsonObject | undefined): Promise<void>;
|
|
649
650
|
cancel(reason?: string): Promise<JsonObject>;
|
|
650
651
|
/** Products without artifacts leave this undefined → `result.artifacts` is []. */
|
|
651
|
-
listArtifacts?(): Promise<Artifact[]>;
|
|
652
|
+
listArtifacts?(signal?: AbortSignal): Promise<Artifact[]>;
|
|
652
653
|
}
|
|
653
654
|
/**
|
|
654
655
|
* Handle to a single run. Generic over the product's event set (分型): a
|
|
@@ -782,6 +783,7 @@ declare function createDoAnythingNamespace(transport: QoniTransport): {
|
|
|
782
783
|
createRun: <T = unknown>(input: RuntimeTokenInput & JsonObject) => Promise<QoniResponse<T>>;
|
|
783
784
|
getRun: <T = unknown>(input: RuntimeTokenInput & {
|
|
784
785
|
runId: string;
|
|
786
|
+
signal?: AbortSignal;
|
|
785
787
|
}) => Promise<QoniResponse<T>>;
|
|
786
788
|
events: <T = unknown>(input: RuntimeTokenInput & {
|
|
787
789
|
runId: string;
|
|
@@ -862,6 +864,7 @@ declare function createDeepResearchNamespace(transport: QoniTransport): {
|
|
|
862
864
|
run: <T = unknown>(input: RuntimeTokenInput & JsonObject) => Promise<QoniResponse<T>>;
|
|
863
865
|
get: <T = unknown>(input: RuntimeTokenInput & {
|
|
864
866
|
runId: string;
|
|
867
|
+
signal?: AbortSignal;
|
|
865
868
|
}) => Promise<QoniResponse<T>>;
|
|
866
869
|
events: <T = unknown>(input: RuntimeTokenInput & {
|
|
867
870
|
runId: string;
|
|
@@ -885,6 +888,7 @@ declare function createDeepResearchNamespace(transport: QoniTransport): {
|
|
|
885
888
|
} & JsonObject) => Promise<QoniResponse<T>>;
|
|
886
889
|
listArtifacts: <T = unknown>(input: RuntimeTokenInput & {
|
|
887
890
|
runId: string;
|
|
891
|
+
signal?: AbortSignal;
|
|
888
892
|
}) => Promise<QoniResponse<T>>;
|
|
889
893
|
getArtifact: <T = unknown>(input: RuntimeTokenInput & {
|
|
890
894
|
runId: string;
|
|
@@ -1190,6 +1194,7 @@ declare function createWebSearchNamespace(transport: QoniTransport): {
|
|
|
1190
1194
|
run: <T = unknown>(input: RuntimeTokenInput & JsonObject) => Promise<QoniResponse<T>>;
|
|
1191
1195
|
get: <T = unknown>(input: RuntimeTokenInput & {
|
|
1192
1196
|
runId: string;
|
|
1197
|
+
signal?: AbortSignal;
|
|
1193
1198
|
}) => Promise<QoniResponse<T>>;
|
|
1194
1199
|
events: <T = unknown>(input: RuntimeTokenInput & {
|
|
1195
1200
|
runId: string;
|
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,
|
|
@@ -2089,7 +2112,9 @@ var Qoni = 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 Qoni = 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 Qoni = 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 Qoni = 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 Qoni = 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,14 +2308,16 @@ var Qoni = 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);
|
|
2272
2314
|
try {
|
|
2273
|
-
reader
|
|
2315
|
+
reader?.releaseLock();
|
|
2274
2316
|
} catch {
|
|
2275
2317
|
}
|
|
2276
2318
|
}
|
|
2277
2319
|
}
|
|
2278
|
-
async fetchWithTimeout(input, init, signal, opts) {
|
|
2320
|
+
async fetchWithTimeout(input, init, consume, signal, opts) {
|
|
2279
2321
|
if (signal?.aborted) throw signal.reason || new DOMException("Aborted", "AbortError");
|
|
2280
2322
|
const controller = new AbortController();
|
|
2281
2323
|
const resolved = resolveLocalGenauthRequest(input);
|
|
@@ -2290,17 +2332,39 @@ var Qoni = class {
|
|
|
2290
2332
|
didTimeout = true;
|
|
2291
2333
|
controller.abort();
|
|
2292
2334
|
}, this.timeoutMs);
|
|
2335
|
+
let response;
|
|
2293
2336
|
try {
|
|
2294
|
-
|
|
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;
|
|
2295
2355
|
} catch (error) {
|
|
2296
|
-
|
|
2356
|
+
void response?.body?.cancel().catch(() => void 0);
|
|
2357
|
+
if (signal?.aborted) throw signal.reason;
|
|
2358
|
+
if (didTimeout || isConnectTimeoutError(error)) {
|
|
2297
2359
|
throw timeoutError(error);
|
|
2298
2360
|
}
|
|
2299
2361
|
if (isFetchNetworkError(error)) throw networkError(error);
|
|
2300
2362
|
throw error;
|
|
2301
2363
|
} finally {
|
|
2302
2364
|
clearTimeout(timeout);
|
|
2303
|
-
|
|
2365
|
+
const cleanup = () => signal?.removeEventListener("abort", abortFromSignal);
|
|
2366
|
+
if (opts?.streamCleanup) opts.streamCleanup(cleanup);
|
|
2367
|
+
else cleanup();
|
|
2304
2368
|
}
|
|
2305
2369
|
}
|
|
2306
2370
|
async baseUrlFor(service) {
|
|
@@ -2356,7 +2420,7 @@ var Qoni = class {
|
|
|
2356
2420
|
pathname,
|
|
2357
2421
|
{}
|
|
2358
2422
|
);
|
|
2359
|
-
const response = await this.fetchWithTimeout(
|
|
2423
|
+
const { response, payload } = await this.fetchWithTimeout(
|
|
2360
2424
|
urlWithBasePath(this.host, pathname),
|
|
2361
2425
|
{
|
|
2362
2426
|
method: "GET",
|
|
@@ -2364,9 +2428,9 @@ var Qoni = class {
|
|
|
2364
2428
|
accept: "application/json",
|
|
2365
2429
|
...headers
|
|
2366
2430
|
}
|
|
2367
|
-
}
|
|
2431
|
+
},
|
|
2432
|
+
async (response2, signal) => ({ response: response2, payload: await readJson(response2, signal) })
|
|
2368
2433
|
);
|
|
2369
|
-
const payload = await readJson(response);
|
|
2370
2434
|
if (!response.ok) {
|
|
2371
2435
|
throw errorFromPayload(
|
|
2372
2436
|
response.status,
|
|
@@ -2460,18 +2524,52 @@ function requestBody(body) {
|
|
|
2460
2524
|
function isBodyInit(value) {
|
|
2461
2525
|
return typeof value === "string" || value instanceof ArrayBuffer || value instanceof Blob || value instanceof FormData || value instanceof URLSearchParams;
|
|
2462
2526
|
}
|
|
2463
|
-
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) {
|
|
2464
2562
|
const contentType = response.headers.get("content-type")?.toLowerCase() || "";
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
if (
|
|
2468
|
-
|
|
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 [];
|
|
2469
2567
|
return body.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
|
|
2470
|
-
} catch {
|
|
2471
|
-
return void 0;
|
|
2472
2568
|
}
|
|
2569
|
+
return JSON.parse(body);
|
|
2570
|
+
} catch {
|
|
2571
|
+
return void 0;
|
|
2473
2572
|
}
|
|
2474
|
-
return response.json().catch(() => void 0);
|
|
2475
2573
|
}
|
|
2476
2574
|
function normalizeResponse(payload, _service) {
|
|
2477
2575
|
if (isJsonObject(payload) && ("data" in payload || "meta" in payload)) {
|
|
@@ -2714,6 +2812,7 @@ function isRetryableStreamError(err) {
|
|
|
2714
2812
|
if (err && typeof err === "object") {
|
|
2715
2813
|
const e = err;
|
|
2716
2814
|
if (e.name === "AbortError") return false;
|
|
2815
|
+
if (e.status === 408) return true;
|
|
2717
2816
|
if (typeof e.retryable === "boolean") return e.retryable;
|
|
2718
2817
|
if (typeof e.status === "number") {
|
|
2719
2818
|
return e.status >= 500 || e.status === 408 || e.status === 429;
|
|
@@ -2820,9 +2919,6 @@ function normalizeDelegateAgentTokenResponse(response) {
|
|
|
2820
2919
|
}
|
|
2821
2920
|
};
|
|
2822
2921
|
}
|
|
2823
|
-
function isAbortError(error) {
|
|
2824
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
2825
|
-
}
|
|
2826
2922
|
function isConnectTimeoutError(error) {
|
|
2827
2923
|
const cause = errorCause(error);
|
|
2828
2924
|
return errorCode(cause) === "UND_ERR_CONNECT_TIMEOUT" || errorName(cause) === "ConnectTimeoutError";
|