@opengeni/sdk 3.3.2 → 3.7.0-canary.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 +80 -6
- package/dist/artifact-client.d.ts +4 -2
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +6 -6
- package/dist/browser.d.ts +5 -2
- package/dist/browser.js +14 -4
- package/dist/{chunk-YXQX7RQT.js → chunk-2E2BFEEN.js} +2 -2
- package/dist/{chunk-JUS2UYRP.js → chunk-3QDQTYOE.js} +3 -1
- package/dist/chunk-3QDQTYOE.js.map +1 -0
- package/dist/{chunk-C2H7HBNP.js → chunk-7S2MZ65L.js} +6 -6
- package/dist/chunk-FMLDFJ2Q.js +246 -0
- package/dist/chunk-FMLDFJ2Q.js.map +1 -0
- package/dist/{chunk-TRNI7KEO.js → chunk-IQTEPOI3.js} +228 -26
- package/dist/chunk-IQTEPOI3.js.map +1 -0
- package/dist/chunk-IRAEPLXG.js +350 -0
- package/dist/chunk-IRAEPLXG.js.map +1 -0
- package/dist/{chunk-QT3KFVDP.js → chunk-MPGVC7EO.js} +10 -2
- package/dist/chunk-MPGVC7EO.js.map +1 -0
- package/dist/{chunk-C4DJPZRU.js → chunk-OO5LPTO7.js} +14 -1
- package/dist/chunk-OO5LPTO7.js.map +1 -0
- package/dist/{chunk-KKRO2VCP.js → chunk-QTBAMHEF.js} +2 -2
- package/dist/client.d.ts +51 -11
- package/dist/codex-realtime-controller.js +2 -2
- package/dist/core.d.ts +5 -2
- package/dist/core.js +17 -7
- package/dist/document-authority.js +5 -5
- package/dist/editable-artifacts.js +4 -4
- package/dist/errors.d.ts +12 -0
- package/dist/github-repositories.d.ts +10 -0
- package/dist/github-repositories.js +36 -0
- package/dist/github-repositories.js.map +1 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +99 -52
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/model-context.d.ts +49 -0
- package/dist/model-picker-order.d.ts +1 -0
- package/dist/model-picker-order.js +10 -0
- package/dist/model-picker-order.js.map +1 -0
- package/dist/proxy.d.ts +7 -5
- package/dist/realtime.js +2 -2
- package/dist/session-titles.d.ts +2 -28
- package/dist/site.d.ts +93 -0
- package/dist/site.js +35 -0
- package/dist/site.js.map +1 -0
- package/dist/stream.d.ts +2 -0
- package/dist/tools-FSTY4FAY.js +12 -0
- package/dist/tools-FSTY4FAY.js.map +1 -0
- package/dist/tools.d.ts +61 -0
- package/dist/types.d.ts +231 -9
- package/dist/workspace-artifacts.d.ts +26 -1
- package/package.json +14 -2
- package/src/artifact-client.ts +16 -1
- package/src/artifacts.ts +1 -0
- package/src/browser.ts +16 -0
- package/src/client.ts +332 -22
- package/src/core.ts +16 -0
- package/src/errors.ts +23 -0
- package/src/github-repositories.ts +60 -0
- package/src/index.ts +94 -1
- package/src/model-context.ts +69 -0
- package/src/model-picker-order.ts +6 -0
- package/src/proxy.ts +17 -6
- package/src/session-titles.ts +8 -82
- package/src/site.ts +544 -0
- package/src/stream.ts +29 -8
- package/src/tools.ts +396 -0
- package/src/types.ts +287 -10
- package/src/workspace-artifacts.ts +33 -2
- package/dist/chunk-C4DJPZRU.js.map +0 -1
- package/dist/chunk-JUS2UYRP.js.map +0 -1
- package/dist/chunk-QT3KFVDP.js.map +0 -1
- package/dist/chunk-TRNI7KEO.js.map +0 -1
- /package/dist/{chunk-YXQX7RQT.js.map → chunk-2E2BFEEN.js.map} +0 -0
- /package/dist/{chunk-C2H7HBNP.js.map → chunk-7S2MZ65L.js.map} +0 -0
- /package/dist/{chunk-KKRO2VCP.js.map → chunk-QTBAMHEF.js.map} +0 -0
package/src/tools.ts
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ToolGatewayCallRequest,
|
|
3
|
+
ToolGatewayCallResponse,
|
|
4
|
+
ToolGatewayApprovalRequest,
|
|
5
|
+
ToolGatewayApprovalResponse,
|
|
6
|
+
ToolGatewayCatalog,
|
|
7
|
+
ToolGatewayCatalogEntry,
|
|
8
|
+
ToolGatewayDeclarationsResponse,
|
|
9
|
+
ToolGatewayIdentity,
|
|
10
|
+
ToolGatewayResult,
|
|
11
|
+
} from "./types";
|
|
12
|
+
import { OpenGeniApiError } from "./errors";
|
|
13
|
+
|
|
14
|
+
export type OpenGeniToolCallOptions = {
|
|
15
|
+
operationId?: string;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
refreshCatalog?: boolean;
|
|
18
|
+
/** Server-issued, single-use capability returned by `$approve`. */
|
|
19
|
+
approvalToken?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type OpenGeniToolFunction<
|
|
23
|
+
TArguments extends Record<string, unknown> = Record<string, unknown>,
|
|
24
|
+
TResult = unknown,
|
|
25
|
+
> = (argumentsValue?: TArguments, options?: OpenGeniToolCallOptions) => Promise<TResult>;
|
|
26
|
+
|
|
27
|
+
export interface OpenGeniDynamicToolNamespace {
|
|
28
|
+
readonly [key: string]: OpenGeniDynamicToolNode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type OpenGeniDynamicToolNode = OpenGeniDynamicToolNamespace & OpenGeniToolFunction;
|
|
32
|
+
|
|
33
|
+
/** Declaration-generation target for one exact workspace catalog. */
|
|
34
|
+
export interface OpenGeniGeneratedTools {}
|
|
35
|
+
|
|
36
|
+
export type OpenGeniWorkspaceTools<
|
|
37
|
+
TCatalog extends Pick<
|
|
38
|
+
ToolGatewayCatalog,
|
|
39
|
+
"version" | "generation" | "digest" | "createdAt" | "entries"
|
|
40
|
+
> = ToolGatewayCatalog,
|
|
41
|
+
> = OpenGeniGeneratedTools &
|
|
42
|
+
OpenGeniDynamicToolNamespace & {
|
|
43
|
+
readonly $catalog: (options?: { refresh?: boolean; signal?: AbortSignal }) => Promise<TCatalog>;
|
|
44
|
+
readonly $call: (
|
|
45
|
+
identity: ToolGatewayIdentity,
|
|
46
|
+
argumentsValue?: Record<string, unknown>,
|
|
47
|
+
options?: OpenGeniToolCallOptions,
|
|
48
|
+
) => Promise<unknown>;
|
|
49
|
+
readonly $approve: (
|
|
50
|
+
identity: ToolGatewayIdentity,
|
|
51
|
+
argumentsValue?: Record<string, unknown>,
|
|
52
|
+
options?: {
|
|
53
|
+
operationId?: string;
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
},
|
|
56
|
+
) => Promise<ToolGatewayApprovalResponse>;
|
|
57
|
+
readonly $declarations: (options?: {
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
}) => Promise<ToolGatewayDeclarationsResponse>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export interface OpenGeniToolTransport {
|
|
63
|
+
requestJson<T>(
|
|
64
|
+
method: string,
|
|
65
|
+
path: string,
|
|
66
|
+
body?: unknown,
|
|
67
|
+
query?: Record<string, string>,
|
|
68
|
+
options?: { signal?: AbortSignal; timeoutMs?: number },
|
|
69
|
+
): Promise<T>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface OpenGeniToolsFacade {
|
|
73
|
+
forWorkspace(workspaceId: string): OpenGeniWorkspaceTools;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class OpenGeniToolCallError extends Error {
|
|
77
|
+
readonly code: string;
|
|
78
|
+
readonly retryable: boolean;
|
|
79
|
+
readonly outcomeUnknown: boolean;
|
|
80
|
+
|
|
81
|
+
constructor(readonly result: ToolGatewayResult) {
|
|
82
|
+
const structured = result.structuredContent as
|
|
83
|
+
| {
|
|
84
|
+
error?: {
|
|
85
|
+
code?: unknown;
|
|
86
|
+
message?: unknown;
|
|
87
|
+
retryable?: unknown;
|
|
88
|
+
outcomeUnknown?: unknown;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
| undefined;
|
|
92
|
+
super(
|
|
93
|
+
typeof structured?.error?.message === "string"
|
|
94
|
+
? structured.error.message
|
|
95
|
+
: "OpenGeni tool call failed",
|
|
96
|
+
);
|
|
97
|
+
this.name = "OpenGeniToolCallError";
|
|
98
|
+
this.code = typeof structured?.error?.code === "string" ? structured.error.code : "tool_error";
|
|
99
|
+
this.retryable = structured?.error?.retryable === true;
|
|
100
|
+
this.outcomeUnknown = structured?.error?.outcomeUnknown === true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** A catalog refresh invalidated the single-use approval capability for this call. */
|
|
105
|
+
export class OpenGeniToolReapprovalRequiredError extends Error {
|
|
106
|
+
readonly code = "tool_reapproval_required" as const;
|
|
107
|
+
readonly retryable = false;
|
|
108
|
+
|
|
109
|
+
constructor(
|
|
110
|
+
readonly operationId: string,
|
|
111
|
+
readonly previousCatalogDigest: string,
|
|
112
|
+
readonly catalogDigest: string,
|
|
113
|
+
readonly identity: ToolGatewayIdentity,
|
|
114
|
+
) {
|
|
115
|
+
super(
|
|
116
|
+
"The workspace tool catalog changed after approval. Request a new approval for the refreshed tool identity and retry with the same operation ID.",
|
|
117
|
+
);
|
|
118
|
+
this.name = "OpenGeniToolReapprovalRequiredError";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export class OpenGeniToolsClient implements OpenGeniToolsFacade {
|
|
123
|
+
constructor(private readonly transport: OpenGeniToolTransport) {}
|
|
124
|
+
|
|
125
|
+
forWorkspace(workspaceId: string): OpenGeniWorkspaceTools {
|
|
126
|
+
const normalizedWorkspaceId = requiredId(workspaceId, "workspaceId");
|
|
127
|
+
let catalogSnapshot: ToolGatewayCatalog | null = null;
|
|
128
|
+
const approvalBindings = new Map<
|
|
129
|
+
string,
|
|
130
|
+
{
|
|
131
|
+
operationId: string;
|
|
132
|
+
catalogDigest: string;
|
|
133
|
+
identity: ToolGatewayIdentity;
|
|
134
|
+
}
|
|
135
|
+
>();
|
|
136
|
+
const catalog = async (
|
|
137
|
+
options: { refresh?: boolean; signal?: AbortSignal } = {},
|
|
138
|
+
): Promise<ToolGatewayCatalog> => {
|
|
139
|
+
if (catalogSnapshot && !options.refresh) return catalogSnapshot;
|
|
140
|
+
const next = await this.transport.requestJson<ToolGatewayCatalog>(
|
|
141
|
+
"GET",
|
|
142
|
+
`/v1/workspaces/${encodeURIComponent(normalizedWorkspaceId)}/tools/catalog`,
|
|
143
|
+
undefined,
|
|
144
|
+
{},
|
|
145
|
+
options.signal ? { signal: options.signal } : {},
|
|
146
|
+
);
|
|
147
|
+
catalogSnapshot = next;
|
|
148
|
+
return next;
|
|
149
|
+
};
|
|
150
|
+
const callResolvedIdentity = async (
|
|
151
|
+
resolveIdentity: (current: ToolGatewayCatalog) => ToolGatewayIdentity,
|
|
152
|
+
argumentsValue: Record<string, unknown>,
|
|
153
|
+
options: OpenGeniToolCallOptions,
|
|
154
|
+
): Promise<unknown> => {
|
|
155
|
+
if (options.approvalToken && !options.operationId) {
|
|
156
|
+
throw new TypeError("operationId is required when using an approval token");
|
|
157
|
+
}
|
|
158
|
+
const operationId = options.operationId ?? crypto.randomUUID();
|
|
159
|
+
const initial = await catalog({
|
|
160
|
+
...(options.refreshCatalog === undefined ? {} : { refresh: options.refreshCatalog }),
|
|
161
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
162
|
+
});
|
|
163
|
+
const invoke = async (current: ToolGatewayCatalog): Promise<unknown> => {
|
|
164
|
+
const identity = resolveIdentity(current);
|
|
165
|
+
const approvalBinding = options.approvalToken
|
|
166
|
+
? approvalBindings.get(options.approvalToken)
|
|
167
|
+
: undefined;
|
|
168
|
+
if (
|
|
169
|
+
approvalBinding &&
|
|
170
|
+
(approvalBinding.operationId !== operationId ||
|
|
171
|
+
approvalBinding.catalogDigest !== current.digest ||
|
|
172
|
+
!sameToolIdentity(approvalBinding.identity, identity))
|
|
173
|
+
) {
|
|
174
|
+
throw new OpenGeniToolReapprovalRequiredError(
|
|
175
|
+
operationId,
|
|
176
|
+
approvalBinding.catalogDigest,
|
|
177
|
+
current.digest,
|
|
178
|
+
identity,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const request: ToolGatewayCallRequest = {
|
|
182
|
+
operationId,
|
|
183
|
+
catalogDigest: current.digest,
|
|
184
|
+
identity,
|
|
185
|
+
arguments: argumentsValue,
|
|
186
|
+
...(options.approvalToken ? { approvalToken: options.approvalToken } : {}),
|
|
187
|
+
};
|
|
188
|
+
let response: ToolGatewayCallResponse;
|
|
189
|
+
try {
|
|
190
|
+
response = await this.transport.requestJson<ToolGatewayCallResponse>(
|
|
191
|
+
"POST",
|
|
192
|
+
`/v1/workspaces/${encodeURIComponent(normalizedWorkspaceId)}/tools/calls`,
|
|
193
|
+
request,
|
|
194
|
+
{},
|
|
195
|
+
options.signal ? { signal: options.signal } : {},
|
|
196
|
+
);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (isCatalogStaleApiError(error)) catalogSnapshot = null;
|
|
199
|
+
else if (options.approvalToken) approvalBindings.delete(options.approvalToken);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
if (options.approvalToken) approvalBindings.delete(options.approvalToken);
|
|
203
|
+
if (response.catalogDigest !== current.digest) {
|
|
204
|
+
catalogSnapshot = null;
|
|
205
|
+
}
|
|
206
|
+
if (response.result.isError) throw new OpenGeniToolCallError(response.result);
|
|
207
|
+
const entry = findCatalogEntry(current, identity);
|
|
208
|
+
return entry?.outputSchema && response.result.structuredContent !== undefined
|
|
209
|
+
? response.result.structuredContent
|
|
210
|
+
: response.result;
|
|
211
|
+
};
|
|
212
|
+
try {
|
|
213
|
+
return await invoke(initial);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (!isCatalogStaleApiError(error)) throw error;
|
|
216
|
+
}
|
|
217
|
+
const refreshed = await catalog({
|
|
218
|
+
refresh: true,
|
|
219
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
220
|
+
});
|
|
221
|
+
const refreshedIdentity = resolveIdentity(refreshed);
|
|
222
|
+
if (options.approvalToken && refreshed.digest !== initial.digest) {
|
|
223
|
+
throw new OpenGeniToolReapprovalRequiredError(
|
|
224
|
+
operationId,
|
|
225
|
+
initial.digest,
|
|
226
|
+
refreshed.digest,
|
|
227
|
+
refreshedIdentity,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
return await invoke(refreshed);
|
|
231
|
+
};
|
|
232
|
+
const callIdentity = async (
|
|
233
|
+
identity: ToolGatewayIdentity,
|
|
234
|
+
argumentsValue: Record<string, unknown> = {},
|
|
235
|
+
options: OpenGeniToolCallOptions = {},
|
|
236
|
+
): Promise<unknown> =>
|
|
237
|
+
await callResolvedIdentity(
|
|
238
|
+
(current) => requireCatalogIdentity(current, identity),
|
|
239
|
+
argumentsValue,
|
|
240
|
+
options,
|
|
241
|
+
);
|
|
242
|
+
const approveIdentity = async (
|
|
243
|
+
identity: ToolGatewayIdentity,
|
|
244
|
+
argumentsValue: Record<string, unknown> = {},
|
|
245
|
+
options: {
|
|
246
|
+
operationId?: string;
|
|
247
|
+
signal?: AbortSignal;
|
|
248
|
+
} = {},
|
|
249
|
+
): Promise<ToolGatewayApprovalResponse> => {
|
|
250
|
+
const operationId = options.operationId ?? crypto.randomUUID();
|
|
251
|
+
const approve = async (current: ToolGatewayCatalog): Promise<ToolGatewayApprovalResponse> => {
|
|
252
|
+
const request: ToolGatewayApprovalRequest = {
|
|
253
|
+
operationId,
|
|
254
|
+
catalogDigest: current.digest,
|
|
255
|
+
identity: requireCatalogIdentity(current, identity),
|
|
256
|
+
arguments: argumentsValue,
|
|
257
|
+
};
|
|
258
|
+
try {
|
|
259
|
+
const response = await this.transport.requestJson<ToolGatewayApprovalResponse>(
|
|
260
|
+
"POST",
|
|
261
|
+
`/v1/workspaces/${encodeURIComponent(normalizedWorkspaceId)}/tools/approvals`,
|
|
262
|
+
request,
|
|
263
|
+
{},
|
|
264
|
+
options.signal ? { signal: options.signal } : {},
|
|
265
|
+
);
|
|
266
|
+
approvalBindings.set(response.approvalToken, {
|
|
267
|
+
operationId: response.operationId,
|
|
268
|
+
catalogDigest: response.catalogDigest,
|
|
269
|
+
identity: response.identity,
|
|
270
|
+
});
|
|
271
|
+
return response;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (isCatalogStaleApiError(error)) catalogSnapshot = null;
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
const initial = await catalog(options.signal ? { signal: options.signal } : {});
|
|
278
|
+
try {
|
|
279
|
+
return await approve(initial);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (!isCatalogStaleApiError(error)) throw error;
|
|
282
|
+
}
|
|
283
|
+
const refreshed = await catalog({
|
|
284
|
+
refresh: true,
|
|
285
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
286
|
+
});
|
|
287
|
+
return await approve(refreshed);
|
|
288
|
+
};
|
|
289
|
+
const invokePath = async (
|
|
290
|
+
path: readonly string[],
|
|
291
|
+
argumentsValue: Record<string, unknown> = {},
|
|
292
|
+
options: OpenGeniToolCallOptions = {},
|
|
293
|
+
): Promise<unknown> => {
|
|
294
|
+
return await callResolvedIdentity(
|
|
295
|
+
(current) => requireCatalogPath(current, path).identity,
|
|
296
|
+
argumentsValue,
|
|
297
|
+
options,
|
|
298
|
+
);
|
|
299
|
+
};
|
|
300
|
+
const node = (path: readonly string[]): OpenGeniDynamicToolNode =>
|
|
301
|
+
new Proxy(
|
|
302
|
+
(async (argumentsValue = {}, options = {}) =>
|
|
303
|
+
await invokePath(path, argumentsValue, options)) as OpenGeniDynamicToolNode,
|
|
304
|
+
{
|
|
305
|
+
get: (_target, property) => {
|
|
306
|
+
// Dynamic namespaces must never become accidental Promise-like
|
|
307
|
+
// values when passed through await/Promise.resolve.
|
|
308
|
+
if (property === "then") return undefined;
|
|
309
|
+
if (typeof property !== "string") return undefined;
|
|
310
|
+
return node([...path, property]);
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
);
|
|
314
|
+
return new Proxy(Object.create(null) as OpenGeniWorkspaceTools, {
|
|
315
|
+
get: (_target, property) => {
|
|
316
|
+
if (property === "then") return undefined;
|
|
317
|
+
if (property === "$catalog") return catalog;
|
|
318
|
+
if (property === "$call") return callIdentity;
|
|
319
|
+
if (property === "$approve") return approveIdentity;
|
|
320
|
+
if (property === "$declarations") {
|
|
321
|
+
return async (options: { signal?: AbortSignal } = {}) =>
|
|
322
|
+
await this.transport.requestJson<ToolGatewayDeclarationsResponse>(
|
|
323
|
+
"GET",
|
|
324
|
+
`/v1/workspaces/${encodeURIComponent(normalizedWorkspaceId)}/tools/declarations`,
|
|
325
|
+
undefined,
|
|
326
|
+
{},
|
|
327
|
+
options.signal ? { signal: options.signal } : {},
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
if (typeof property !== "string") return undefined;
|
|
331
|
+
return node([property]);
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function findCatalogEntry(
|
|
338
|
+
catalog: ToolGatewayCatalog,
|
|
339
|
+
identity: ToolGatewayIdentity,
|
|
340
|
+
): ToolGatewayCatalogEntry | undefined {
|
|
341
|
+
return catalog.entries.find(
|
|
342
|
+
(entry) =>
|
|
343
|
+
entry.identity.serverId === identity.serverId &&
|
|
344
|
+
entry.identity.toolName === identity.toolName,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function sameToolIdentity(left: ToolGatewayIdentity, right: ToolGatewayIdentity): boolean {
|
|
349
|
+
return left.serverId === right.serverId && left.toolName === right.toolName;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function requireCatalogIdentity(
|
|
353
|
+
catalog: ToolGatewayCatalog,
|
|
354
|
+
identity: ToolGatewayIdentity,
|
|
355
|
+
): ToolGatewayIdentity {
|
|
356
|
+
const entry = findCatalogEntry(catalog, identity);
|
|
357
|
+
if (!entry) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
`Tool is not present in the workspace catalog: ${identity.serverId}/${identity.toolName}`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
return entry.identity;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function requireCatalogPath(
|
|
366
|
+
catalog: ToolGatewayCatalog,
|
|
367
|
+
path: readonly string[],
|
|
368
|
+
): ToolGatewayCatalogEntry {
|
|
369
|
+
const entry = catalog.entries.find(
|
|
370
|
+
(candidate) =>
|
|
371
|
+
candidate.codemodePath.length === path.length &&
|
|
372
|
+
candidate.codemodePath.every((segment, index) => segment === path[index]),
|
|
373
|
+
);
|
|
374
|
+
if (!entry) throw new Error(`Tool is not present in the workspace catalog: ${path.join(".")}`);
|
|
375
|
+
return entry;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function isCatalogStaleApiError(
|
|
379
|
+
error: unknown,
|
|
380
|
+
): error is OpenGeniApiError | { code: "catalog_stale" } {
|
|
381
|
+
return (
|
|
382
|
+
(error instanceof OpenGeniApiError &&
|
|
383
|
+
error.status === 409 &&
|
|
384
|
+
error.details?.code === "catalog_stale") ||
|
|
385
|
+
(typeof error === "object" &&
|
|
386
|
+
error !== null &&
|
|
387
|
+
"code" in error &&
|
|
388
|
+
error.code === "catalog_stale")
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function requiredId(value: string, label: string): string {
|
|
393
|
+
const normalized = value.trim();
|
|
394
|
+
if (!normalized) throw new TypeError(`${label} is required`);
|
|
395
|
+
return normalized;
|
|
396
|
+
}
|