@opengeni/sdk 0.25.0 → 0.26.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 +52 -0
- package/dist/index.d.ts +202 -10
- package/dist/index.js +285 -64
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +316 -56
- package/src/errors.ts +75 -19
- package/src/index.ts +33 -0
- package/src/types.ts +74 -2
- package/src/workspace-instruction-policies.ts +124 -0
package/dist/index.js
CHANGED
|
@@ -2,29 +2,63 @@
|
|
|
2
2
|
var OpenGeniApiError = class extends Error {
|
|
3
3
|
status;
|
|
4
4
|
code;
|
|
5
|
+
retryable;
|
|
6
|
+
correlationId;
|
|
7
|
+
/** True only when an uncontrolled transport failed after a mutation may have been accepted. */
|
|
8
|
+
outcomeUnknown;
|
|
5
9
|
body;
|
|
6
|
-
constructor(status, body) {
|
|
10
|
+
constructor(status, body, options = {}) {
|
|
7
11
|
const decoded = decodeApiErrorBody(body);
|
|
8
|
-
|
|
12
|
+
const correlationId = decoded?.requestId ?? boundedCorrelationId(options.correlationId);
|
|
13
|
+
const gatewayFailure = status >= 502 && status <= 504;
|
|
14
|
+
const fromResponse = options.mutation !== void 0;
|
|
15
|
+
const message = decoded?.message ?? (fromResponse ? "Request failed." : body || "(empty body)");
|
|
16
|
+
const displayMessage = options.displayMessage ?? (gatewayFailure && fromResponse ? "OpenGeni is temporarily unavailable \u2014 retry." : `OpenGeni API ${status}: ${message}`);
|
|
17
|
+
super(correlationId ? `${displayMessage} Reference: ${correlationId}.` : displayMessage);
|
|
9
18
|
this.name = "OpenGeniApiError";
|
|
10
19
|
this.status = status;
|
|
11
|
-
this.code =
|
|
12
|
-
this.
|
|
20
|
+
this.code = options.code ?? decoded?.code ?? (gatewayFailure && fromResponse ? "upstream_unavailable" : void 0);
|
|
21
|
+
this.retryable = options.retryable ?? decoded?.retryable ?? retryableApiStatus(status);
|
|
22
|
+
this.correlationId = correlationId;
|
|
23
|
+
this.outcomeUnknown = options.outcomeUnknown ?? (gatewayFailure && !!options.mutation && !decoded);
|
|
24
|
+
this.body = !fromResponse || decoded ? body : "";
|
|
13
25
|
}
|
|
14
26
|
};
|
|
15
27
|
function decodeApiErrorBody(body) {
|
|
16
|
-
if (!body) return
|
|
28
|
+
if (!body) return null;
|
|
17
29
|
try {
|
|
18
30
|
const decoded = JSON.parse(body);
|
|
19
|
-
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return
|
|
31
|
+
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) return null;
|
|
20
32
|
const record = decoded;
|
|
33
|
+
const nested = record.error && typeof record.error === "object" && !Array.isArray(record.error) ? record.error : record;
|
|
34
|
+
const code = boundedApiField(nested.code);
|
|
35
|
+
const message = boundedApiField(nested.message);
|
|
36
|
+
const requestId = boundedCorrelationId(nested.requestId);
|
|
37
|
+
const retryable = typeof nested.retryable === "boolean" ? nested.retryable : void 0;
|
|
38
|
+
if (!code && !message && !requestId && retryable === void 0) return null;
|
|
21
39
|
return {
|
|
22
|
-
|
|
23
|
-
|
|
40
|
+
code,
|
|
41
|
+
message,
|
|
42
|
+
requestId,
|
|
43
|
+
retryable
|
|
24
44
|
};
|
|
25
45
|
} catch {
|
|
26
|
-
return
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function boundedApiField(value) {
|
|
50
|
+
if (typeof value !== "string") return;
|
|
51
|
+
const bytes = new TextEncoder().encode(value);
|
|
52
|
+
return bytes.byteLength <= 512 ? value : new TextDecoder().decode(bytes.slice(0, 512));
|
|
53
|
+
}
|
|
54
|
+
function retryableApiStatus(status) {
|
|
55
|
+
return status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;
|
|
56
|
+
}
|
|
57
|
+
function boundedCorrelationId(value) {
|
|
58
|
+
if (typeof value !== "string" || value.length > 128 || !/^[\w.:-]+$/.test(value)) {
|
|
59
|
+
return;
|
|
27
60
|
}
|
|
61
|
+
return value;
|
|
28
62
|
}
|
|
29
63
|
var OpenGeniSessionListCursorError = class extends OpenGeniApiError {
|
|
30
64
|
};
|
|
@@ -48,9 +82,7 @@ function isAbortError(error) {
|
|
|
48
82
|
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
49
83
|
}
|
|
50
84
|
function isRetryableStreamError(error) {
|
|
51
|
-
if (error instanceof OpenGeniApiError)
|
|
52
|
-
return error.status === 408 || error.status === 409 || error.status === 425 || error.status === 429 || error.status >= 500;
|
|
53
|
-
}
|
|
85
|
+
if (error instanceof OpenGeniApiError) return error.retryable;
|
|
54
86
|
return error instanceof TypeError;
|
|
55
87
|
}
|
|
56
88
|
|
|
@@ -391,6 +423,7 @@ var SESSION_EVENT_TYPES = [
|
|
|
391
423
|
"terminal.pty.exited",
|
|
392
424
|
"session.title_set",
|
|
393
425
|
"session.mcp.approval_policy.updated",
|
|
426
|
+
"session.tool_policy.updated",
|
|
394
427
|
// Multi-account Codex (P1): the session's inference account changed.
|
|
395
428
|
"codex.account.switched",
|
|
396
429
|
// credential allocator metadata-only per-turn credential selection audit.
|
|
@@ -472,6 +505,7 @@ var KNOWN_PERMISSIONS = [
|
|
|
472
505
|
];
|
|
473
506
|
var OPENGENI_API_CONTRACT_REVISION = "2026-07-turn-instructions-v1";
|
|
474
507
|
var OPENGENI_API_CONTRACT_HEADER = "x-opengeni-api-contract";
|
|
508
|
+
var OPENGENI_CORRELATION_HEADER = "x-opengeni-correlation-id";
|
|
475
509
|
var RETAINED_OUTPUT_DEFAULT_PAGE_BYTES = 256 * 1024;
|
|
476
510
|
var RETAINED_OUTPUT_MAX_PAGE_BYTES = 1024 * 1024;
|
|
477
511
|
var KNOWN_USAGE_EVENT_TYPES = [
|
|
@@ -540,6 +574,14 @@ var OpenGeniClient = class {
|
|
|
540
574
|
request
|
|
541
575
|
);
|
|
542
576
|
}
|
|
577
|
+
/** Replace the durable tool policy or explicitly adopt workspace defaults. */
|
|
578
|
+
async updateSessionToolPolicy(workspaceId, sessionId, request) {
|
|
579
|
+
return await this.requestJson(
|
|
580
|
+
"PUT",
|
|
581
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/tool-policy`,
|
|
582
|
+
request
|
|
583
|
+
);
|
|
584
|
+
}
|
|
543
585
|
/**
|
|
544
586
|
* Replace one attached MCP server's approval policy. The change is captured
|
|
545
587
|
* by the next claimed attempt; already-claimed work keeps its immutable
|
|
@@ -583,7 +625,13 @@ var OpenGeniClient = class {
|
|
|
583
625
|
);
|
|
584
626
|
} catch (error) {
|
|
585
627
|
if (error instanceof OpenGeniApiError && error.status === 410) {
|
|
586
|
-
throw new OpenGeniSessionListCursorError(error.status, error.body
|
|
628
|
+
throw new OpenGeniSessionListCursorError(error.status, error.body, {
|
|
629
|
+
...error.code ? { code: error.code } : {},
|
|
630
|
+
retryable: error.retryable,
|
|
631
|
+
...error.correlationId ? { correlationId: error.correlationId } : {},
|
|
632
|
+
outcomeUnknown: error.outcomeUnknown,
|
|
633
|
+
displayMessage: "The session list changed \u2014 refresh and try again."
|
|
634
|
+
});
|
|
587
635
|
}
|
|
588
636
|
throw error;
|
|
589
637
|
}
|
|
@@ -758,6 +806,7 @@ var OpenGeniClient = class {
|
|
|
758
806
|
throw new TypeError("resultMode=compact requires latest");
|
|
759
807
|
}
|
|
760
808
|
const listOptions = options.resultMode === "compact" ? null : options;
|
|
809
|
+
const correlationId = crypto.randomUUID();
|
|
761
810
|
const response = await this.fetchImpl(
|
|
762
811
|
this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events`, {
|
|
763
812
|
...listOptions?.after !== void 0 ? { after: String(listOptions.after) } : {},
|
|
@@ -776,11 +825,14 @@ var OpenGeniClient = class {
|
|
|
776
825
|
}),
|
|
777
826
|
{
|
|
778
827
|
method: "GET",
|
|
779
|
-
headers: { ...this.headers(), Accept: "application/json" }
|
|
828
|
+
headers: { ...this.headers(correlationId), Accept: "application/json" }
|
|
780
829
|
}
|
|
781
830
|
);
|
|
782
831
|
assertApiContractResponse(response);
|
|
783
|
-
if (!response.ok)
|
|
832
|
+
if (!response.ok) {
|
|
833
|
+
throw await apiErrorFromResponse(response, { method: "GET", correlationId });
|
|
834
|
+
}
|
|
835
|
+
await assertJsonResponse(response, { method: "GET", correlationId });
|
|
784
836
|
const body = await response.json();
|
|
785
837
|
if (options.resultMode === "compact") {
|
|
786
838
|
return body;
|
|
@@ -908,14 +960,15 @@ var OpenGeniClient = class {
|
|
|
908
960
|
const url = this.url(`/v1/workspaces/${workspaceId}/sessions/${sessionId}/events/stream`, {
|
|
909
961
|
after: String(options.after ?? 0)
|
|
910
962
|
});
|
|
963
|
+
const correlationId = crypto.randomUUID();
|
|
911
964
|
const response = await this.fetchImpl(url, {
|
|
912
965
|
method: "GET",
|
|
913
|
-
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
966
|
+
headers: { ...this.headers(correlationId), Accept: "text/event-stream" },
|
|
914
967
|
...options.signal ? { signal: options.signal } : {}
|
|
915
968
|
});
|
|
916
969
|
assertApiContractResponse(response);
|
|
917
970
|
if (!response.ok) {
|
|
918
|
-
throw
|
|
971
|
+
throw await apiErrorFromResponse(response, { method: "GET", correlationId });
|
|
919
972
|
}
|
|
920
973
|
if (!response.body) {
|
|
921
974
|
throw new OpenGeniApiError(response.status, "SSE response did not include a readable body");
|
|
@@ -997,6 +1050,7 @@ var OpenGeniClient = class {
|
|
|
997
1050
|
}
|
|
998
1051
|
/** Count/byte-bounded page plus an explicit continuation cursor. */
|
|
999
1052
|
async listWorkspaceControlEventPage(workspaceId, options = {}) {
|
|
1053
|
+
const correlationId = crypto.randomUUID();
|
|
1000
1054
|
const response = await this.fetchImpl(
|
|
1001
1055
|
this.url(`/v1/workspaces/${workspaceId}/control-events`, {
|
|
1002
1056
|
...options.after !== void 0 ? { after: String(options.after) } : {},
|
|
@@ -1004,13 +1058,14 @@ var OpenGeniClient = class {
|
|
|
1004
1058
|
}),
|
|
1005
1059
|
{
|
|
1006
1060
|
method: "GET",
|
|
1007
|
-
headers: { ...this.headers(), Accept: "application/json" }
|
|
1061
|
+
headers: { ...this.headers(correlationId), Accept: "application/json" }
|
|
1008
1062
|
}
|
|
1009
1063
|
);
|
|
1010
1064
|
assertApiContractResponse(response);
|
|
1011
1065
|
if (!response.ok) {
|
|
1012
|
-
throw
|
|
1066
|
+
throw await apiErrorFromResponse(response, { method: "GET", correlationId });
|
|
1013
1067
|
}
|
|
1068
|
+
await assertJsonResponse(response, { method: "GET", correlationId });
|
|
1014
1069
|
const events = await response.json();
|
|
1015
1070
|
const bytesHeader = response.headers.get("X-OpenGeni-Page-Bytes");
|
|
1016
1071
|
const nextHeader = response.headers.get("X-OpenGeni-Next-After");
|
|
@@ -1035,18 +1090,21 @@ var OpenGeniClient = class {
|
|
|
1035
1090
|
};
|
|
1036
1091
|
}
|
|
1037
1092
|
async openWorkspaceControlEventStream(workspaceId, options = {}) {
|
|
1093
|
+
const correlationId = crypto.randomUUID();
|
|
1038
1094
|
const response = await this.fetchImpl(
|
|
1039
1095
|
this.url(`/v1/workspaces/${workspaceId}/control-events/stream`, {
|
|
1040
1096
|
after: String(options.after ?? 0)
|
|
1041
1097
|
}),
|
|
1042
1098
|
{
|
|
1043
1099
|
method: "GET",
|
|
1044
|
-
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
1100
|
+
headers: { ...this.headers(correlationId), Accept: "text/event-stream" },
|
|
1045
1101
|
...options.signal ? { signal: options.signal } : {}
|
|
1046
1102
|
}
|
|
1047
1103
|
);
|
|
1048
1104
|
assertApiContractResponse(response);
|
|
1049
|
-
if (!response.ok)
|
|
1105
|
+
if (!response.ok) {
|
|
1106
|
+
throw await apiErrorFromResponse(response, { method: "GET", correlationId });
|
|
1107
|
+
}
|
|
1050
1108
|
if (!response.body) {
|
|
1051
1109
|
throw new OpenGeniApiError(response.status, "SSE response did not include a readable body");
|
|
1052
1110
|
}
|
|
@@ -1379,6 +1437,67 @@ var OpenGeniClient = class {
|
|
|
1379
1437
|
async updateWorkspace(workspaceId, request) {
|
|
1380
1438
|
return await this.requestJson("PATCH", `/v1/workspaces/${workspaceId}`, request);
|
|
1381
1439
|
}
|
|
1440
|
+
/** Inspect immutable instruction-policy history, active heads, and activation audit evidence. */
|
|
1441
|
+
async listWorkspaceInstructionPolicies(workspaceId, options = {}) {
|
|
1442
|
+
const params = new URLSearchParams();
|
|
1443
|
+
if (options.kind !== void 0) params.set("kind", options.kind);
|
|
1444
|
+
if (options.scope !== void 0) params.set("scope", options.scope);
|
|
1445
|
+
if (options.roleKey !== void 0) params.set("roleKey", options.roleKey);
|
|
1446
|
+
if (options.afterRevision !== void 0) {
|
|
1447
|
+
params.set("afterRevision", String(options.afterRevision));
|
|
1448
|
+
}
|
|
1449
|
+
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
1450
|
+
const query = params.toString();
|
|
1451
|
+
return await this.requestJson(
|
|
1452
|
+
"GET",
|
|
1453
|
+
`/v1/workspaces/${workspaceId}/instruction-policies${query ? `?${query}` : ""}`
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
async getWorkspaceInstructionPolicyRevision(workspaceId, revisionId) {
|
|
1457
|
+
return await this.requestJson(
|
|
1458
|
+
"GET",
|
|
1459
|
+
`/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}`
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
async createWorkspaceInstructionPolicyDraft(workspaceId, request) {
|
|
1463
|
+
return await this.requestJson(
|
|
1464
|
+
"POST",
|
|
1465
|
+
`/v1/workspaces/${workspaceId}/instruction-policies/drafts`,
|
|
1466
|
+
request
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
/** Import the stored legacy workspace override as an inactive charter draft. */
|
|
1470
|
+
async importLegacyWorkspaceInstructionPolicyDraft(workspaceId, request = {}) {
|
|
1471
|
+
return await this.requestJson(
|
|
1472
|
+
"POST",
|
|
1473
|
+
`/v1/workspaces/${workspaceId}/instruction-policies/import-legacy`,
|
|
1474
|
+
request
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
async diffWorkspaceInstructionPolicyRevisions(workspaceId, request) {
|
|
1478
|
+
const params = new URLSearchParams({
|
|
1479
|
+
fromRevisionId: request.fromRevisionId,
|
|
1480
|
+
toRevisionId: request.toRevisionId
|
|
1481
|
+
});
|
|
1482
|
+
return await this.requestJson(
|
|
1483
|
+
"GET",
|
|
1484
|
+
`/v1/workspaces/${workspaceId}/instruction-policies/diff?${params}`
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
async activateWorkspaceInstructionPolicyRevision(workspaceId, revisionId, request) {
|
|
1488
|
+
return await this.requestJson(
|
|
1489
|
+
"POST",
|
|
1490
|
+
`/v1/workspaces/${workspaceId}/instruction-policies/${encodeURIComponent(revisionId)}/activate`,
|
|
1491
|
+
request
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
async rollbackWorkspaceInstructionPolicyRevision(workspaceId, request) {
|
|
1495
|
+
return await this.requestJson(
|
|
1496
|
+
"POST",
|
|
1497
|
+
`/v1/workspaces/${workspaceId}/instruction-policies/rollback`,
|
|
1498
|
+
request
|
|
1499
|
+
);
|
|
1500
|
+
}
|
|
1382
1501
|
/**
|
|
1383
1502
|
* Delete a workspace and everything in it. Refused (409) for the account's
|
|
1384
1503
|
* only workspace and while it still has a running session. Irreversible.
|
|
@@ -1684,7 +1803,7 @@ var OpenGeniClient = class {
|
|
|
1684
1803
|
body
|
|
1685
1804
|
});
|
|
1686
1805
|
if (!putResponse.ok) {
|
|
1687
|
-
throw
|
|
1806
|
+
throw await apiErrorFromResponse(putResponse, { method: "PUT" });
|
|
1688
1807
|
}
|
|
1689
1808
|
return await this.completeFileUpload(workspaceId, upload.uploadId);
|
|
1690
1809
|
}
|
|
@@ -1709,12 +1828,13 @@ var OpenGeniClient = class {
|
|
|
1709
1828
|
if (options.range && (options.range.length > 128 || /[^\x20-\x7e]/.test(options.range))) {
|
|
1710
1829
|
throw new RangeError("retained artifact range must be at most 128 printable ASCII bytes");
|
|
1711
1830
|
}
|
|
1831
|
+
const correlationId = crypto.randomUUID();
|
|
1712
1832
|
const response = await this.fetchImpl(
|
|
1713
1833
|
this.url(`/v1/workspaces/${workspaceId}/artifacts/${artifactId}/content`),
|
|
1714
1834
|
{
|
|
1715
1835
|
method: "GET",
|
|
1716
1836
|
headers: {
|
|
1717
|
-
...this.headers(),
|
|
1837
|
+
...this.headers(correlationId),
|
|
1718
1838
|
Accept: "application/octet-stream",
|
|
1719
1839
|
...options.range ? { Range: options.range } : {}
|
|
1720
1840
|
},
|
|
@@ -1728,7 +1848,7 @@ var OpenGeniClient = class {
|
|
|
1728
1848
|
throw error;
|
|
1729
1849
|
}
|
|
1730
1850
|
if (!response.ok) {
|
|
1731
|
-
throw
|
|
1851
|
+
throw await apiErrorFromResponse(response, { method: "GET", correlationId });
|
|
1732
1852
|
}
|
|
1733
1853
|
if (response.status !== 200 && response.status !== 206) {
|
|
1734
1854
|
await cancelResponseBody(response, "unexpected retained artifact response status");
|
|
@@ -1800,6 +1920,30 @@ var OpenGeniClient = class {
|
|
|
1800
1920
|
`/v1/workspaces/${workspaceId}/document-bases/${baseId}/documents`
|
|
1801
1921
|
);
|
|
1802
1922
|
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Drop raw text or an already-uploaded file into the workspace's Default
|
|
1925
|
+
* base. When curation is enabled, it may name, summarize, categorize, and
|
|
1926
|
+
* (confidence permitting) file the document into the best-matching base;
|
|
1927
|
+
* provider=none leaves caller metadata and Default placement unchanged.
|
|
1928
|
+
*/
|
|
1929
|
+
async createKnowledgeDrop(workspaceId, request) {
|
|
1930
|
+
return await this.requestJson(
|
|
1931
|
+
"POST",
|
|
1932
|
+
`/v1/workspaces/${workspaceId}/knowledge/drops`,
|
|
1933
|
+
request
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Move a document (and its indexed chunks) to another base. With no
|
|
1938
|
+
* targetBaseId, applies the document's stored curation suggestion.
|
|
1939
|
+
*/
|
|
1940
|
+
async moveDocument(workspaceId, documentId, request = {}) {
|
|
1941
|
+
return await this.requestJson(
|
|
1942
|
+
"POST",
|
|
1943
|
+
`/v1/workspaces/${workspaceId}/documents/${documentId}/move`,
|
|
1944
|
+
request
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
1803
1947
|
/** Retry indexing for a failed document. */
|
|
1804
1948
|
async reindexDocument(workspaceId, baseId, documentId) {
|
|
1805
1949
|
return await this.requestJson(
|
|
@@ -1982,6 +2126,15 @@ var OpenGeniClient = class {
|
|
|
1982
2126
|
);
|
|
1983
2127
|
return response.connection;
|
|
1984
2128
|
}
|
|
2129
|
+
/** Validate and store/reinstall the workspace-shared OpenGeni Slack bot credential. */
|
|
2130
|
+
async connectOpenGeniSlackBot(workspaceId, request) {
|
|
2131
|
+
const response = await this.requestJson(
|
|
2132
|
+
"POST",
|
|
2133
|
+
`/v1/workspaces/${workspaceId}/connections/slack-bot`,
|
|
2134
|
+
request
|
|
2135
|
+
);
|
|
2136
|
+
return response.connection;
|
|
2137
|
+
}
|
|
1985
2138
|
async updateConnection(workspaceId, connectionId, request) {
|
|
1986
2139
|
const response = await this.requestJson(
|
|
1987
2140
|
"PATCH",
|
|
@@ -2010,14 +2163,11 @@ var OpenGeniClient = class {
|
|
|
2010
2163
|
return logoAssetPath ? `${this.baseUrl}/v1/${logoAssetPath}` : null;
|
|
2011
2164
|
}
|
|
2012
2165
|
// --- GitHub ----------------------------------------------------------------------------------
|
|
2013
|
-
/** GitHub App configuration
|
|
2166
|
+
/** GitHub App server configuration plus truthful workspace binding status. */
|
|
2014
2167
|
async getGitHubApp(workspaceId) {
|
|
2015
2168
|
return await this.requestJson("GET", `/v1/workspaces/${workspaceId}/github/app`);
|
|
2016
2169
|
}
|
|
2017
|
-
/**
|
|
2018
|
-
* Compatibility URL for previously issued state. New installation binding is
|
|
2019
|
-
* disabled, so the endpoint validates state and terminates with HTTP 410.
|
|
2020
|
-
*/
|
|
2170
|
+
/** Build the GitHub owner-consent entry URL for fresh workspace-bound state. */
|
|
2021
2171
|
githubConnectUrl(workspaceId, state) {
|
|
2022
2172
|
return this.url(`/v1/workspaces/${workspaceId}/github/connect`, { state });
|
|
2023
2173
|
}
|
|
@@ -2099,12 +2249,13 @@ var OpenGeniClient = class {
|
|
|
2099
2249
|
return await this.requestJson("POST", "/v1/billing/checkout", request);
|
|
2100
2250
|
}
|
|
2101
2251
|
// --- Internals -------------------------------------------------------------
|
|
2102
|
-
headers() {
|
|
2252
|
+
headers(correlationId) {
|
|
2103
2253
|
const extra = typeof this.options.headers === "function" ? this.options.headers() : this.options.headers;
|
|
2104
2254
|
return {
|
|
2105
2255
|
...this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {},
|
|
2106
2256
|
...extra,
|
|
2107
|
-
[OPENGENI_API_CONTRACT_HEADER]: OPENGENI_API_CONTRACT_REVISION
|
|
2257
|
+
[OPENGENI_API_CONTRACT_HEADER]: OPENGENI_API_CONTRACT_REVISION,
|
|
2258
|
+
...correlationId ? { [OPENGENI_CORRELATION_HEADER]: correlationId } : {}
|
|
2108
2259
|
};
|
|
2109
2260
|
}
|
|
2110
2261
|
url(path, query = {}) {
|
|
@@ -2220,36 +2371,62 @@ var OpenGeniClient = class {
|
|
|
2220
2371
|
);
|
|
2221
2372
|
}
|
|
2222
2373
|
async requestJson(method, path, body, query = {}, options = {}) {
|
|
2223
|
-
const
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2374
|
+
const correlationId = crypto.randomUUID();
|
|
2375
|
+
let response;
|
|
2376
|
+
try {
|
|
2377
|
+
response = await this.fetchImpl(this.url(path, query), {
|
|
2378
|
+
method,
|
|
2379
|
+
headers: {
|
|
2380
|
+
...this.headers(correlationId),
|
|
2381
|
+
Accept: "application/json",
|
|
2382
|
+
...body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
2383
|
+
},
|
|
2384
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {},
|
|
2385
|
+
...options.signal ? { signal: options.signal } : {}
|
|
2386
|
+
});
|
|
2387
|
+
} catch (error) {
|
|
2388
|
+
if (isMutationMethod(method)) {
|
|
2389
|
+
throw mutationTransportError(correlationId);
|
|
2390
|
+
}
|
|
2391
|
+
throw error;
|
|
2392
|
+
}
|
|
2233
2393
|
assertApiContractResponse(response);
|
|
2234
2394
|
if (!response.ok) {
|
|
2235
|
-
throw
|
|
2395
|
+
throw await apiErrorFromResponse(response, { method, correlationId });
|
|
2396
|
+
}
|
|
2397
|
+
await assertJsonResponse(response, { method, correlationId });
|
|
2398
|
+
try {
|
|
2399
|
+
return await response.json();
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
if (isMutationMethod(method)) {
|
|
2402
|
+
throw mutationTransportError(correlationId);
|
|
2403
|
+
}
|
|
2404
|
+
throw error;
|
|
2236
2405
|
}
|
|
2237
|
-
return await response.json();
|
|
2238
2406
|
}
|
|
2239
2407
|
/** Like `requestJson` for endpoints that respond with no body (204). */
|
|
2240
2408
|
async requestVoid(method, path, body) {
|
|
2241
|
-
const
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2409
|
+
const correlationId = crypto.randomUUID();
|
|
2410
|
+
let response;
|
|
2411
|
+
try {
|
|
2412
|
+
response = await this.fetchImpl(this.url(path), {
|
|
2413
|
+
method,
|
|
2414
|
+
headers: {
|
|
2415
|
+
...this.headers(correlationId),
|
|
2416
|
+
Accept: "application/json",
|
|
2417
|
+
...body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
2418
|
+
},
|
|
2419
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
2420
|
+
});
|
|
2421
|
+
} catch (error) {
|
|
2422
|
+
if (isMutationMethod(method)) {
|
|
2423
|
+
throw mutationTransportError(correlationId);
|
|
2424
|
+
}
|
|
2425
|
+
throw error;
|
|
2426
|
+
}
|
|
2250
2427
|
assertApiContractResponse(response);
|
|
2251
2428
|
if (!response.ok) {
|
|
2252
|
-
throw
|
|
2429
|
+
throw await apiErrorFromResponse(response, { method, correlationId });
|
|
2253
2430
|
}
|
|
2254
2431
|
}
|
|
2255
2432
|
};
|
|
@@ -2259,25 +2436,62 @@ function assertApiContractResponse(response) {
|
|
|
2259
2436
|
throw new OpenGeniApiContractMismatchError(OPENGENI_API_CONTRACT_REVISION, actual);
|
|
2260
2437
|
}
|
|
2261
2438
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2439
|
+
var API_ERROR_MAX_BYTES = 16 * 1024;
|
|
2440
|
+
async function apiErrorFromResponse(response, context) {
|
|
2441
|
+
return new OpenGeniApiError(response.status, await readBoundedJsonErrorBody(response), {
|
|
2442
|
+
correlationId: response.headers.get(OPENGENI_CORRELATION_HEADER) ?? context.correlationId,
|
|
2443
|
+
mutation: isMutationMethod(context.method)
|
|
2444
|
+
});
|
|
2266
2445
|
}
|
|
2267
|
-
async function
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2446
|
+
async function assertJsonResponse(response, context) {
|
|
2447
|
+
if (isJsonContentType(response.headers.get("content-type"))) return;
|
|
2448
|
+
await cancelResponseBody(response, "unexpected non-JSON API response");
|
|
2449
|
+
throw new OpenGeniApiError(502, "", {
|
|
2450
|
+
code: "upstream_unavailable",
|
|
2451
|
+
retryable: true,
|
|
2452
|
+
correlationId: response.headers.get(OPENGENI_CORRELATION_HEADER) ?? context.correlationId,
|
|
2453
|
+
outcomeUnknown: isMutationMethod(context.method),
|
|
2454
|
+
displayMessage: "OpenGeni is temporarily unavailable \u2014 retry."
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
async function readBoundedJsonErrorBody(response) {
|
|
2458
|
+
if (!isJsonContentType(response.headers.get("content-type"))) {
|
|
2459
|
+
await cancelResponseBody(response, "discarding API error body");
|
|
2460
|
+
return "";
|
|
2461
|
+
}
|
|
2462
|
+
if (Number(response.headers.get("content-length")) > API_ERROR_MAX_BYTES) {
|
|
2463
|
+
await cancelResponseBody(response, "discarding API error body");
|
|
2271
2464
|
return "";
|
|
2272
2465
|
}
|
|
2273
|
-
}
|
|
2274
|
-
async function safeBoundedText(response) {
|
|
2275
2466
|
try {
|
|
2276
|
-
return new TextDecoder().decode(
|
|
2467
|
+
return new TextDecoder().decode(
|
|
2468
|
+
await readBoundedResponseBytes(response, API_ERROR_MAX_BYTES, null)
|
|
2469
|
+
);
|
|
2277
2470
|
} catch {
|
|
2278
2471
|
return "";
|
|
2279
2472
|
}
|
|
2280
2473
|
}
|
|
2474
|
+
function isJsonContentType(value) {
|
|
2475
|
+
return /^(application\/json|[^;]+\+json)\s*(;|$)/i.test(value ?? "");
|
|
2476
|
+
}
|
|
2477
|
+
function isMutationMethod(method) {
|
|
2478
|
+
return method !== "GET" && method !== "HEAD" && method !== "OPTIONS";
|
|
2479
|
+
}
|
|
2480
|
+
function mutationTransportError(correlationId) {
|
|
2481
|
+
return new OpenGeniApiError(0, "", {
|
|
2482
|
+
code: "network_error",
|
|
2483
|
+
retryable: true,
|
|
2484
|
+
correlationId,
|
|
2485
|
+
outcomeUnknown: true,
|
|
2486
|
+
mutation: true,
|
|
2487
|
+
displayMessage: "OpenGeni could not confirm the request \u2014 reconcile before retrying."
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
async function sha256ForUpload(body) {
|
|
2491
|
+
const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body instanceof Blob ? new Uint8Array(await body.arrayBuffer()) : new Uint8Array(body);
|
|
2492
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
2493
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2494
|
+
}
|
|
2281
2495
|
async function cancelResponseBody(response, reason) {
|
|
2282
2496
|
await response.body?.cancel(reason).catch(() => void 0);
|
|
2283
2497
|
}
|
|
@@ -2511,6 +2725,11 @@ function ttydResizeFrame(columns, rows) {
|
|
|
2511
2725
|
return TtydClientCommand.RESIZE + JSON.stringify({ columns, rows });
|
|
2512
2726
|
}
|
|
2513
2727
|
|
|
2728
|
+
// src/workspace-instruction-policies.ts
|
|
2729
|
+
function normalizeWorkspaceInstructionPolicyRoleKey(value) {
|
|
2730
|
+
return value.normalize("NFKC").trim().toLowerCase().replace(/\s+/gu, "-").replace(/-+/g, "-");
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2514
2733
|
// src/transcription.ts
|
|
2515
2734
|
var DEFAULT_WORKSPACE_TRANSCRIPTION_POLICY = {
|
|
2516
2735
|
enabled: false,
|
|
@@ -2729,6 +2948,7 @@ export {
|
|
|
2729
2948
|
KNOWN_USAGE_EVENT_TYPES,
|
|
2730
2949
|
OPENGENI_API_CONTRACT_HEADER,
|
|
2731
2950
|
OPENGENI_API_CONTRACT_REVISION,
|
|
2951
|
+
OPENGENI_CORRELATION_HEADER,
|
|
2732
2952
|
OpenGeniApiContractMismatchError,
|
|
2733
2953
|
OpenGeniApiError,
|
|
2734
2954
|
OpenGeniClient,
|
|
@@ -2747,6 +2967,7 @@ export {
|
|
|
2747
2967
|
formatSseEvent,
|
|
2748
2968
|
isRetryableStreamError,
|
|
2749
2969
|
nextDesktopState,
|
|
2970
|
+
normalizeWorkspaceInstructionPolicyRoleKey,
|
|
2750
2971
|
parseSseStream,
|
|
2751
2972
|
proxySessionEventStream,
|
|
2752
2973
|
resolveWorkspaceTranscriptionPolicy,
|