@opengeni/codemode 0.2.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/LICENSE +190 -0
- package/README.md +71 -0
- package/dist/artifacts.d.ts +99 -0
- package/dist/declarations.d.ts +12 -0
- package/dist/environment.d.ts +28 -0
- package/dist/index.d.ts +161 -0
- package/dist/index.js +1543 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction.d.ts +1529 -0
- package/dist/structured.d.ts +11 -0
- package/package.json +43 -0
- package/src/artifacts.ts +313 -0
- package/src/declarations.ts +345 -0
- package/src/environment.ts +101 -0
- package/src/index.ts +736 -0
- package/src/interaction.ts +726 -0
- package/src/structured.ts +52 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1543 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createHash, randomUUID } from "crypto";
|
|
3
|
+
import Ajv from "ajv";
|
|
4
|
+
import Ajv2019 from "ajv/dist/2019.js";
|
|
5
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
6
|
+
import {
|
|
7
|
+
ATTEMPT_TOOL_CATALOG_VERSION,
|
|
8
|
+
ATTEMPT_TOOL_CATALOG_MAX_BYTES,
|
|
9
|
+
AttemptToolCall,
|
|
10
|
+
AttemptToolCatalog,
|
|
11
|
+
AttemptToolCatalogEntry,
|
|
12
|
+
AttemptToolResult,
|
|
13
|
+
CodemodeCallSubmission,
|
|
14
|
+
CodemodeOperation,
|
|
15
|
+
CodemodeDispatchAck,
|
|
16
|
+
CodemodeDispatchRequest
|
|
17
|
+
} from "@opengeni/contracts";
|
|
18
|
+
|
|
19
|
+
// src/environment.ts
|
|
20
|
+
import { readFile } from "fs/promises";
|
|
21
|
+
var CODEMODE_ENVIRONMENT = {
|
|
22
|
+
url: "OPENGENI_CODEMODE_URL",
|
|
23
|
+
tokenFile: "OPENGENI_CODEMODE_TOKEN_FILE"
|
|
24
|
+
};
|
|
25
|
+
var cachedEnvironmentClient = null;
|
|
26
|
+
function environmentCodemodeClient(environment = process.env) {
|
|
27
|
+
const baseUrl = requiredEnvironment(environment, CODEMODE_ENVIRONMENT.url);
|
|
28
|
+
const tokenFile = requiredEnvironment(environment, CODEMODE_ENVIRONMENT.tokenFile);
|
|
29
|
+
const key = `${baseUrl}\0${tokenFile}`;
|
|
30
|
+
if (environment === process.env && cachedEnvironmentClient?.key === key) {
|
|
31
|
+
return cachedEnvironmentClient.client;
|
|
32
|
+
}
|
|
33
|
+
const client = new CodemodeClient({
|
|
34
|
+
baseUrl,
|
|
35
|
+
token: async () => await readBearerFile(tokenFile)
|
|
36
|
+
});
|
|
37
|
+
if (environment === process.env) cachedEnvironmentClient = { key, client };
|
|
38
|
+
return client;
|
|
39
|
+
}
|
|
40
|
+
function createCodemodeTools(client = () => environmentCodemodeClient()) {
|
|
41
|
+
const node = (path) => new Proxy(
|
|
42
|
+
async (args = {}, options = {}) => await (await client()).callPathValue(path, args, options),
|
|
43
|
+
{
|
|
44
|
+
get(_target, property) {
|
|
45
|
+
if (property === "then") return void 0;
|
|
46
|
+
if (property === Symbol.toStringTag) return "CodemodeTool";
|
|
47
|
+
if (typeof property !== "string") return void 0;
|
|
48
|
+
return node([...path, property]);
|
|
49
|
+
},
|
|
50
|
+
set() {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
return new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
56
|
+
get(_target, property) {
|
|
57
|
+
if (property === "then") return void 0;
|
|
58
|
+
if (property === Symbol.toStringTag) return "CodemodeTools";
|
|
59
|
+
if (typeof property !== "string") return void 0;
|
|
60
|
+
return node([property]);
|
|
61
|
+
},
|
|
62
|
+
set() {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
var tools = createCodemodeTools();
|
|
68
|
+
async function readBearerFile(path) {
|
|
69
|
+
let token;
|
|
70
|
+
try {
|
|
71
|
+
token = (await readFile(path, "utf8")).trim();
|
|
72
|
+
} catch {
|
|
73
|
+
throw new Error(`${CODEMODE_ENVIRONMENT.tokenFile} is not readable`);
|
|
74
|
+
}
|
|
75
|
+
if (!token) throw new Error(`${CODEMODE_ENVIRONMENT.tokenFile} is empty`);
|
|
76
|
+
return token;
|
|
77
|
+
}
|
|
78
|
+
function requiredEnvironment(environment, name) {
|
|
79
|
+
const value = environment[name]?.trim();
|
|
80
|
+
if (!value) throw new Error(`${name} is required`);
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/artifacts.ts
|
|
85
|
+
import { randomBytes } from "crypto";
|
|
86
|
+
|
|
87
|
+
// src/structured.ts
|
|
88
|
+
var CodemodeToolExecutionError = class extends Error {
|
|
89
|
+
constructor(result) {
|
|
90
|
+
const error = toolError(result);
|
|
91
|
+
super(error.message);
|
|
92
|
+
this.result = result;
|
|
93
|
+
this.name = "CodemodeToolExecutionError";
|
|
94
|
+
this.code = error.code;
|
|
95
|
+
this.retryable = error.retryable;
|
|
96
|
+
}
|
|
97
|
+
code;
|
|
98
|
+
retryable;
|
|
99
|
+
};
|
|
100
|
+
function codemodeClientProvider(client = () => environmentCodemodeClient()) {
|
|
101
|
+
return typeof client === "function" ? client : () => client;
|
|
102
|
+
}
|
|
103
|
+
async function callStructured(client, path, args, options) {
|
|
104
|
+
const result = await (await client()).callPath(path, args, options);
|
|
105
|
+
if (result.isError) throw new CodemodeToolExecutionError(result);
|
|
106
|
+
if (!result.structuredContent) {
|
|
107
|
+
throw new Error(`Codemode tool ${path.join(".")} returned no structured content`);
|
|
108
|
+
}
|
|
109
|
+
return result.structuredContent;
|
|
110
|
+
}
|
|
111
|
+
function toolError(result) {
|
|
112
|
+
const structured = result.structuredContent;
|
|
113
|
+
const error = structured?.error;
|
|
114
|
+
return {
|
|
115
|
+
code: typeof error?.code === "string" ? error.code : "tool_error",
|
|
116
|
+
message: typeof error?.message === "string" ? error.message : "Codemode tool failed",
|
|
117
|
+
retryable: error?.retryable === true
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/artifacts.ts
|
|
122
|
+
var PATH = {
|
|
123
|
+
list: ["artifacts", "list"],
|
|
124
|
+
create: ["artifacts", "create"],
|
|
125
|
+
import: ["artifacts", "import"],
|
|
126
|
+
get: ["artifacts", "get"],
|
|
127
|
+
inspect: ["artifacts", "inspect"],
|
|
128
|
+
apply: ["artifacts", "apply"],
|
|
129
|
+
export: ["artifacts", "export"],
|
|
130
|
+
exportStatus: ["artifacts", "exportStatus"]
|
|
131
|
+
};
|
|
132
|
+
var codemodeArtifactIds = Object.freeze({
|
|
133
|
+
stable: createStableArtifactId,
|
|
134
|
+
document: createDocumentArtifactId
|
|
135
|
+
});
|
|
136
|
+
var CodemodeArtifactCollection = class {
|
|
137
|
+
constructor(client) {
|
|
138
|
+
this.client = client;
|
|
139
|
+
}
|
|
140
|
+
ids = codemodeArtifactIds;
|
|
141
|
+
async list(options = {}, callOptions = {}) {
|
|
142
|
+
return (await callStructured(
|
|
143
|
+
this.client,
|
|
144
|
+
PATH.list,
|
|
145
|
+
options,
|
|
146
|
+
callOptions
|
|
147
|
+
)).artifacts;
|
|
148
|
+
}
|
|
149
|
+
async create(modality, title, callOptions = {}) {
|
|
150
|
+
const metadata = await callStructured(
|
|
151
|
+
this.client,
|
|
152
|
+
PATH.create,
|
|
153
|
+
{ modality, title },
|
|
154
|
+
callOptions
|
|
155
|
+
);
|
|
156
|
+
return new CodemodeArtifact(this.client, metadata.id, metadata);
|
|
157
|
+
}
|
|
158
|
+
async import(fileId, modality, title, callOptions = {}) {
|
|
159
|
+
const metadata = await callStructured(
|
|
160
|
+
this.client,
|
|
161
|
+
PATH.import,
|
|
162
|
+
{ fileId, modality, title },
|
|
163
|
+
callOptions
|
|
164
|
+
);
|
|
165
|
+
return new CodemodeArtifact(this.client, metadata.id, metadata);
|
|
166
|
+
}
|
|
167
|
+
use(artifact) {
|
|
168
|
+
return typeof artifact === "string" ? new CodemodeArtifact(this.client, artifact) : new CodemodeArtifact(this.client, artifact.id, artifact);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
var DOCUMENT_ID_PREFIX = Object.freeze({
|
|
172
|
+
paragraph: "p",
|
|
173
|
+
table: "dt",
|
|
174
|
+
"page-break": "pb",
|
|
175
|
+
section: "sec",
|
|
176
|
+
header: "hdr",
|
|
177
|
+
footer: "ftr",
|
|
178
|
+
comment: "dc",
|
|
179
|
+
"tracked-change": "chg"
|
|
180
|
+
});
|
|
181
|
+
var MAX_U64 = 0xffffffffffffffffn;
|
|
182
|
+
var MAX_DOCUMENT_COUNTER = BigInt(Number.MAX_SAFE_INTEGER) - 1n;
|
|
183
|
+
function createStableArtifactId() {
|
|
184
|
+
const bytes = randomBytes(16);
|
|
185
|
+
if (bytes.subarray(0, 8).every((byte) => byte === 0)) bytes[0] = 1;
|
|
186
|
+
if (bytes.subarray(8, 16).every((byte) => byte === 0)) bytes[8] = 1;
|
|
187
|
+
return bytes.toString("hex");
|
|
188
|
+
}
|
|
189
|
+
function createDocumentArtifactId(kind, namespaceInput) {
|
|
190
|
+
const namespace = parseDocumentNamespace(namespaceInput);
|
|
191
|
+
const bytes = randomBytes(7);
|
|
192
|
+
bytes[0] = (bytes[0] ?? 0) & 31;
|
|
193
|
+
let counter = 0n;
|
|
194
|
+
for (const byte of bytes) counter = counter << 8n | BigInt(byte);
|
|
195
|
+
if (counter === 0n) counter = 1n;
|
|
196
|
+
if (counter > MAX_DOCUMENT_COUNTER) {
|
|
197
|
+
throw new Error("Generated document id counter exceeds the canonical limit");
|
|
198
|
+
}
|
|
199
|
+
return `${DOCUMENT_ID_PREFIX[kind]}/${namespace.toString(16).padStart(16, "0")}${counter.toString(16).padStart(16, "0")}`;
|
|
200
|
+
}
|
|
201
|
+
function parseDocumentNamespace(input) {
|
|
202
|
+
if (typeof input === "number" && (!Number.isSafeInteger(input) || input < 0) || typeof input === "string" && !/^(?:0|[1-9][0-9]*)$/u.test(input)) {
|
|
203
|
+
throw new TypeError("Document id namespace must be an unsigned decimal integer");
|
|
204
|
+
}
|
|
205
|
+
const namespace = BigInt(input);
|
|
206
|
+
if (namespace < 0n || namespace > MAX_U64) {
|
|
207
|
+
throw new RangeError("Document id namespace is outside the uint64 range");
|
|
208
|
+
}
|
|
209
|
+
return namespace;
|
|
210
|
+
}
|
|
211
|
+
var CodemodeArtifact = class {
|
|
212
|
+
constructor(client, id, metadata = null) {
|
|
213
|
+
this.client = client;
|
|
214
|
+
this.id = id;
|
|
215
|
+
this.metadata = metadata;
|
|
216
|
+
}
|
|
217
|
+
metadata;
|
|
218
|
+
async get(callOptions = {}) {
|
|
219
|
+
const metadata = await callStructured(
|
|
220
|
+
this.client,
|
|
221
|
+
PATH.get,
|
|
222
|
+
{ artifactId: this.id },
|
|
223
|
+
callOptions
|
|
224
|
+
);
|
|
225
|
+
this.metadata = metadata;
|
|
226
|
+
return metadata;
|
|
227
|
+
}
|
|
228
|
+
async inspect(query, callOptions = {}) {
|
|
229
|
+
const modality = (await this.currentMetadata(callOptions)).modality;
|
|
230
|
+
const result = await callStructured(this.client, PATH.inspect, { artifactId: this.id, modality, request: query }, callOptions);
|
|
231
|
+
this.metadata = result.artifact;
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
async apply(commands, callOptions = {}) {
|
|
235
|
+
const metadata = await this.currentMetadata(callOptions);
|
|
236
|
+
const result = await callStructured(
|
|
237
|
+
this.client,
|
|
238
|
+
PATH.apply,
|
|
239
|
+
{
|
|
240
|
+
artifactId: this.id,
|
|
241
|
+
modality: metadata.modality,
|
|
242
|
+
expectedHeadSequence: metadata.headSequence,
|
|
243
|
+
expectedStateHash: metadata.stateHash,
|
|
244
|
+
commands
|
|
245
|
+
},
|
|
246
|
+
callOptions
|
|
247
|
+
);
|
|
248
|
+
this.metadata = result.artifact;
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
async export(format, options = {}, callOptions = {}) {
|
|
252
|
+
const started = await callStructured(this.client, PATH.export, { artifactId: this.id, format, options }, callOptions);
|
|
253
|
+
this.metadata = started.artifact;
|
|
254
|
+
return new CodemodeArtifactExport(
|
|
255
|
+
this.client,
|
|
256
|
+
started.artifact.id,
|
|
257
|
+
started.versionId,
|
|
258
|
+
started.jobId,
|
|
259
|
+
started.sourceHeadSequence,
|
|
260
|
+
started.sourceStateHash
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
async currentMetadata(callOptions) {
|
|
264
|
+
return this.metadata ?? await this.get(callOptions);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var CodemodeArtifactExport = class {
|
|
268
|
+
constructor(client, artifactId, versionId, jobId, sourceHeadSequence, sourceStateHash) {
|
|
269
|
+
this.client = client;
|
|
270
|
+
this.artifactId = artifactId;
|
|
271
|
+
this.versionId = versionId;
|
|
272
|
+
this.jobId = jobId;
|
|
273
|
+
this.sourceHeadSequence = sourceHeadSequence;
|
|
274
|
+
this.sourceStateHash = sourceStateHash;
|
|
275
|
+
}
|
|
276
|
+
async status(callOptions = {}) {
|
|
277
|
+
return await callStructured(
|
|
278
|
+
this.client,
|
|
279
|
+
PATH.exportStatus,
|
|
280
|
+
{
|
|
281
|
+
artifactId: this.artifactId,
|
|
282
|
+
versionId: this.versionId,
|
|
283
|
+
jobId: this.jobId
|
|
284
|
+
},
|
|
285
|
+
callOptions
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// src/interaction.ts
|
|
291
|
+
var PATH2 = {
|
|
292
|
+
discover: ["interaction", "discover"],
|
|
293
|
+
browserOpen: ["interaction", "browser", "open"],
|
|
294
|
+
browserTabs: ["interaction", "browser", "tabs"],
|
|
295
|
+
browserObserve: ["interaction", "browser", "observe"],
|
|
296
|
+
browserAct: ["interaction", "browser", "act"],
|
|
297
|
+
browserDebug: ["interaction", "browser", "debug"],
|
|
298
|
+
browserIdentity: ["interaction", "browser", "identity"],
|
|
299
|
+
browserPublish: ["interaction", "browser", "publish"],
|
|
300
|
+
browserLifecycle: ["interaction", "browser", "lifecycle"],
|
|
301
|
+
computerOpen: ["interaction", "computer", "open"],
|
|
302
|
+
computerTargets: ["interaction", "computer", "targets"],
|
|
303
|
+
computerObserve: ["interaction", "computer", "observe"],
|
|
304
|
+
computerAct: ["interaction", "computer", "act"],
|
|
305
|
+
computerLifecycle: ["interaction", "computer", "lifecycle"]
|
|
306
|
+
};
|
|
307
|
+
var OpenGeniCodemode = class {
|
|
308
|
+
browsers;
|
|
309
|
+
computers;
|
|
310
|
+
artifacts;
|
|
311
|
+
constructor(client = () => environmentCodemodeClient()) {
|
|
312
|
+
const provider = codemodeClientProvider(client);
|
|
313
|
+
this.browsers = new CodemodeBrowserCollection(provider);
|
|
314
|
+
this.computers = new CodemodeComputerCollection(provider);
|
|
315
|
+
this.artifacts = new CodemodeArtifactCollection(provider);
|
|
316
|
+
}
|
|
317
|
+
async discover(options = {}, callOptions = {}) {
|
|
318
|
+
return await callStructured(this.browsers.client, PATH2.discover, options, callOptions);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
var CodemodeBrowserCollection = class {
|
|
322
|
+
constructor(client) {
|
|
323
|
+
this.client = client;
|
|
324
|
+
this.identities = new CodemodeBrowserIdentityCollection(client);
|
|
325
|
+
}
|
|
326
|
+
identities;
|
|
327
|
+
async list(options = {}, callOptions = {}) {
|
|
328
|
+
return (await callStructured(this.client, PATH2.discover, options, callOptions)).browsers;
|
|
329
|
+
}
|
|
330
|
+
async open(options = {}, callOptions = {}) {
|
|
331
|
+
const opened = await callStructured(
|
|
332
|
+
this.client,
|
|
333
|
+
PATH2.browserOpen,
|
|
334
|
+
options,
|
|
335
|
+
callOptions
|
|
336
|
+
);
|
|
337
|
+
return new CodemodeBrowser(this.client, opened.session.id);
|
|
338
|
+
}
|
|
339
|
+
use(browserSessionId) {
|
|
340
|
+
return new CodemodeBrowser(this.client, browserSessionId);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
var CodemodeBrowserIdentityCollection = class {
|
|
344
|
+
constructor(client) {
|
|
345
|
+
this.client = client;
|
|
346
|
+
}
|
|
347
|
+
async list(options = {}, callOptions = {}) {
|
|
348
|
+
const response = await callStructured(this.client, PATH2.browserIdentity, { operation: "list", ...options }, callOptions);
|
|
349
|
+
return response.result;
|
|
350
|
+
}
|
|
351
|
+
async get(identityId, callOptions = {}) {
|
|
352
|
+
const response = await callStructured(
|
|
353
|
+
this.client,
|
|
354
|
+
PATH2.browserIdentity,
|
|
355
|
+
{ operation: "get", identityId },
|
|
356
|
+
callOptions
|
|
357
|
+
);
|
|
358
|
+
return response.result;
|
|
359
|
+
}
|
|
360
|
+
async create(name, callOptions = {}) {
|
|
361
|
+
const response = await callStructured(this.client, PATH2.browserIdentity, { operation: "create", name }, callOptions);
|
|
362
|
+
return response.result;
|
|
363
|
+
}
|
|
364
|
+
async revisions(identityId, callOptions = {}) {
|
|
365
|
+
const response = await callStructured(this.client, PATH2.browserIdentity, { operation: "revisions", identityId }, callOptions);
|
|
366
|
+
return response.result;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
var CodemodeBrowser = class {
|
|
370
|
+
constructor(client, id) {
|
|
371
|
+
this.client = client;
|
|
372
|
+
this.id = id;
|
|
373
|
+
this.tabs = new CodemodeBrowserTabCollection(client, id);
|
|
374
|
+
}
|
|
375
|
+
tabs;
|
|
376
|
+
async refresh(callOptions = {}) {
|
|
377
|
+
const result = await callStructured(
|
|
378
|
+
this.client,
|
|
379
|
+
PATH2.browserOpen,
|
|
380
|
+
{ browserSessionId: this.id },
|
|
381
|
+
callOptions
|
|
382
|
+
);
|
|
383
|
+
return result.session;
|
|
384
|
+
}
|
|
385
|
+
async observe(targetId, callOptions = {}) {
|
|
386
|
+
return await this.tabs.use(await this.tabs.resolveId(targetId, callOptions)).observe(callOptions);
|
|
387
|
+
}
|
|
388
|
+
async act(action, options = {}, callOptions = {}) {
|
|
389
|
+
const targetId = await this.tabs.resolveId(options.targetId, callOptions);
|
|
390
|
+
return await this.tabs.use(targetId).act(action, options, callOptions);
|
|
391
|
+
}
|
|
392
|
+
async diagnostics(targetId, options = {}, callOptions = {}) {
|
|
393
|
+
return await callStructured(
|
|
394
|
+
this.client,
|
|
395
|
+
PATH2.browserDebug,
|
|
396
|
+
{
|
|
397
|
+
browserSessionId: this.id,
|
|
398
|
+
targetId: await this.tabs.resolveId(targetId, callOptions),
|
|
399
|
+
...options
|
|
400
|
+
},
|
|
401
|
+
callOptions
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
async publish(input, callOptions = {}) {
|
|
405
|
+
return await callStructured(
|
|
406
|
+
this.client,
|
|
407
|
+
PATH2.browserPublish,
|
|
408
|
+
{ browserSessionId: this.id, ...input },
|
|
409
|
+
callOptions
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
async suspend(callOptions = {}) {
|
|
413
|
+
return await this.lifecycle("suspend", callOptions);
|
|
414
|
+
}
|
|
415
|
+
async resume(callOptions = {}) {
|
|
416
|
+
return await this.lifecycle("resume", callOptions);
|
|
417
|
+
}
|
|
418
|
+
async end(callOptions = {}) {
|
|
419
|
+
return await this.lifecycle("end", callOptions);
|
|
420
|
+
}
|
|
421
|
+
async lifecycle(action, callOptions) {
|
|
422
|
+
return await callStructured(
|
|
423
|
+
this.client,
|
|
424
|
+
PATH2.browserLifecycle,
|
|
425
|
+
{ browserSessionId: this.id, action },
|
|
426
|
+
callOptions
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var CodemodeBrowserTabCollection = class {
|
|
431
|
+
constructor(client, browserSessionId) {
|
|
432
|
+
this.client = client;
|
|
433
|
+
this.browserSessionId = browserSessionId;
|
|
434
|
+
}
|
|
435
|
+
async list(callOptions = {}) {
|
|
436
|
+
return await callStructured(
|
|
437
|
+
this.client,
|
|
438
|
+
PATH2.browserTabs,
|
|
439
|
+
{ operation: "list", browserSessionId: this.browserSessionId },
|
|
440
|
+
callOptions
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
async selected(callOptions = {}) {
|
|
444
|
+
return this.use(await this.resolveId(void 0, callOptions));
|
|
445
|
+
}
|
|
446
|
+
use(targetId) {
|
|
447
|
+
return new CodemodeBrowserTab(this.client, this.browserSessionId, targetId);
|
|
448
|
+
}
|
|
449
|
+
async open(url, callOptions = {}) {
|
|
450
|
+
const before = await this.list(callOptions);
|
|
451
|
+
const after = await callStructured(
|
|
452
|
+
this.client,
|
|
453
|
+
PATH2.browserTabs,
|
|
454
|
+
{
|
|
455
|
+
operation: "open",
|
|
456
|
+
browserSessionId: this.browserSessionId,
|
|
457
|
+
...url ? { url } : {}
|
|
458
|
+
},
|
|
459
|
+
callOptions
|
|
460
|
+
);
|
|
461
|
+
const beforeIds = new Set(before.targets.map((target) => target.id));
|
|
462
|
+
const opened = after.targets.find((target) => !beforeIds.has(target.id)) ?? after.targets.find((target) => target.selected);
|
|
463
|
+
if (!opened) throw new Error("Browser opened no discoverable tab");
|
|
464
|
+
return this.use(opened.id);
|
|
465
|
+
}
|
|
466
|
+
async select(targetId, callOptions = {}) {
|
|
467
|
+
const after = await callStructured(
|
|
468
|
+
this.client,
|
|
469
|
+
PATH2.browserTabs,
|
|
470
|
+
{ operation: "select", browserSessionId: this.browserSessionId, targetId },
|
|
471
|
+
callOptions
|
|
472
|
+
);
|
|
473
|
+
if (!after.targets.some((target) => target.id === targetId && target.selected)) {
|
|
474
|
+
throw new Error("Browser did not select the requested tab");
|
|
475
|
+
}
|
|
476
|
+
return this.use(targetId);
|
|
477
|
+
}
|
|
478
|
+
async close(targetId, callOptions = {}) {
|
|
479
|
+
return await callStructured(
|
|
480
|
+
this.client,
|
|
481
|
+
PATH2.browserTabs,
|
|
482
|
+
{ operation: "close", browserSessionId: this.browserSessionId, targetId },
|
|
483
|
+
callOptions
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
async resolveId(targetId, callOptions) {
|
|
487
|
+
const listed = await this.list(callOptions);
|
|
488
|
+
if (targetId) {
|
|
489
|
+
if (!listed.targets.some((target) => target.id === targetId)) {
|
|
490
|
+
throw new Error(`Browser tab is unavailable: ${targetId}`);
|
|
491
|
+
}
|
|
492
|
+
return targetId;
|
|
493
|
+
}
|
|
494
|
+
const selected = listed.targets.filter((target) => target.selected);
|
|
495
|
+
if (selected.length === 1) return selected[0].id;
|
|
496
|
+
if (listed.targets.length === 1) return listed.targets[0].id;
|
|
497
|
+
if (listed.targets.length === 0) throw new Error("Browser has no tabs");
|
|
498
|
+
throw new Error("Browser target is ambiguous; select an exact tab");
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
var CodemodeBrowserTab = class {
|
|
502
|
+
constructor(client, browserSessionId, id) {
|
|
503
|
+
this.client = client;
|
|
504
|
+
this.browserSessionId = browserSessionId;
|
|
505
|
+
this.id = id;
|
|
506
|
+
}
|
|
507
|
+
async observe(callOptions = {}) {
|
|
508
|
+
return await callStructured(
|
|
509
|
+
this.client,
|
|
510
|
+
PATH2.browserObserve,
|
|
511
|
+
{ browserSessionId: this.browserSessionId, targetId: this.id },
|
|
512
|
+
callOptions
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
async act(action, fences = {}, callOptions = {}) {
|
|
516
|
+
return await callStructured(
|
|
517
|
+
this.client,
|
|
518
|
+
PATH2.browserAct,
|
|
519
|
+
{ browserSessionId: this.browserSessionId, targetId: this.id, action, ...fences },
|
|
520
|
+
callOptions
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
async navigate(url, callOptions = {}) {
|
|
524
|
+
return await this.act({ type: "navigate", url }, {}, callOptions);
|
|
525
|
+
}
|
|
526
|
+
getByRole(role, options = {}) {
|
|
527
|
+
return new CodemodeBrowserLocator(this, { kind: "role", role, ...options });
|
|
528
|
+
}
|
|
529
|
+
getByText(text) {
|
|
530
|
+
return new CodemodeBrowserLocator(this, { kind: "text", text });
|
|
531
|
+
}
|
|
532
|
+
getByLabel(text) {
|
|
533
|
+
return new CodemodeBrowserLocator(this, { kind: "label", text });
|
|
534
|
+
}
|
|
535
|
+
getByPlaceholder(text) {
|
|
536
|
+
return new CodemodeBrowserLocator(this, { kind: "placeholder", text });
|
|
537
|
+
}
|
|
538
|
+
getByTestId(value) {
|
|
539
|
+
return new CodemodeBrowserLocator(this, { kind: "test_id", value });
|
|
540
|
+
}
|
|
541
|
+
locator(selector) {
|
|
542
|
+
return new CodemodeBrowserLocator(this, { kind: "css", selector });
|
|
543
|
+
}
|
|
544
|
+
ref(ref) {
|
|
545
|
+
return new CodemodeBrowserLocator(this, { kind: "ref", ref });
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
var CodemodeBrowserLocator = class {
|
|
549
|
+
constructor(tab, locator) {
|
|
550
|
+
this.tab = tab;
|
|
551
|
+
this.locator = locator;
|
|
552
|
+
}
|
|
553
|
+
async click(options = {}, callOptions = {}) {
|
|
554
|
+
return await this.tab.act(
|
|
555
|
+
{ type: "click", locator: this.locator, ...options },
|
|
556
|
+
{},
|
|
557
|
+
callOptions
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
async doubleClick(callOptions = {}) {
|
|
561
|
+
return await this.tab.act({ type: "double_click", locator: this.locator }, {}, callOptions);
|
|
562
|
+
}
|
|
563
|
+
async hover(callOptions = {}) {
|
|
564
|
+
return await this.tab.act({ type: "hover", locator: this.locator }, {}, callOptions);
|
|
565
|
+
}
|
|
566
|
+
async fill(value, callOptions = {}) {
|
|
567
|
+
return await this.tab.act({ type: "fill", locator: this.locator, value }, {}, callOptions);
|
|
568
|
+
}
|
|
569
|
+
async type(text, callOptions = {}) {
|
|
570
|
+
return await this.tab.act({ type: "type", locator: this.locator, text }, {}, callOptions);
|
|
571
|
+
}
|
|
572
|
+
async press(key, callOptions = {}) {
|
|
573
|
+
return await this.tab.act({ type: "press", locator: this.locator, key }, {}, callOptions);
|
|
574
|
+
}
|
|
575
|
+
async select(values, callOptions = {}) {
|
|
576
|
+
return await this.tab.act({ type: "select", locator: this.locator, values }, {}, callOptions);
|
|
577
|
+
}
|
|
578
|
+
async check(checked = true, callOptions = {}) {
|
|
579
|
+
return await this.tab.act({ type: "check", locator: this.locator, checked }, {}, callOptions);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
var CodemodeComputerCollection = class {
|
|
583
|
+
constructor(client) {
|
|
584
|
+
this.client = client;
|
|
585
|
+
}
|
|
586
|
+
async list(options = {}, callOptions = {}) {
|
|
587
|
+
return (await callStructured(this.client, PATH2.discover, options, callOptions)).computers;
|
|
588
|
+
}
|
|
589
|
+
async open(options = {}, callOptions = {}) {
|
|
590
|
+
const opened = await callStructured(
|
|
591
|
+
this.client,
|
|
592
|
+
PATH2.computerOpen,
|
|
593
|
+
options,
|
|
594
|
+
callOptions
|
|
595
|
+
);
|
|
596
|
+
return new CodemodeComputer(this.client, opened.session.id);
|
|
597
|
+
}
|
|
598
|
+
use(computerSessionId) {
|
|
599
|
+
return new CodemodeComputer(this.client, computerSessionId);
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
var CodemodeComputer = class {
|
|
603
|
+
constructor(client, id) {
|
|
604
|
+
this.client = client;
|
|
605
|
+
this.id = id;
|
|
606
|
+
this.targets = new CodemodeComputerTargetCollection(client, id);
|
|
607
|
+
this.apps = this.targets;
|
|
608
|
+
}
|
|
609
|
+
targets;
|
|
610
|
+
apps;
|
|
611
|
+
async refresh(callOptions = {}) {
|
|
612
|
+
const result = await callStructured(
|
|
613
|
+
this.client,
|
|
614
|
+
PATH2.computerOpen,
|
|
615
|
+
{ computerSessionId: this.id },
|
|
616
|
+
callOptions
|
|
617
|
+
);
|
|
618
|
+
return result.session;
|
|
619
|
+
}
|
|
620
|
+
async observe(targetId, callOptions = {}) {
|
|
621
|
+
return await this.targets.use(await this.targets.resolveId(targetId, callOptions)).observe(callOptions);
|
|
622
|
+
}
|
|
623
|
+
async act(action, options = {}, callOptions = {}) {
|
|
624
|
+
return await this.targets.use(await this.targets.resolveId(options.targetId, callOptions)).act(action, options, callOptions);
|
|
625
|
+
}
|
|
626
|
+
async launch(applicationId, callOptions = {}) {
|
|
627
|
+
const targetId = await this.targets.resolveId(void 0, callOptions);
|
|
628
|
+
await this.targets.use(targetId).act({ type: "launch", applicationId }, {}, callOptions);
|
|
629
|
+
return await this.targets.list(callOptions);
|
|
630
|
+
}
|
|
631
|
+
async end(callOptions = {}) {
|
|
632
|
+
return await callStructured(
|
|
633
|
+
this.client,
|
|
634
|
+
PATH2.computerLifecycle,
|
|
635
|
+
{ computerSessionId: this.id, action: "end" },
|
|
636
|
+
callOptions
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
var CodemodeComputerTargetCollection = class {
|
|
641
|
+
constructor(client, computerSessionId) {
|
|
642
|
+
this.client = client;
|
|
643
|
+
this.computerSessionId = computerSessionId;
|
|
644
|
+
}
|
|
645
|
+
async list(callOptions = {}) {
|
|
646
|
+
return await callStructured(
|
|
647
|
+
this.client,
|
|
648
|
+
PATH2.computerTargets,
|
|
649
|
+
{ computerSessionId: this.computerSessionId },
|
|
650
|
+
callOptions
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
use(targetId) {
|
|
654
|
+
return new CodemodeComputerTarget(this.client, this.computerSessionId, targetId);
|
|
655
|
+
}
|
|
656
|
+
async focused(callOptions = {}) {
|
|
657
|
+
return this.use(await this.resolveId(void 0, callOptions));
|
|
658
|
+
}
|
|
659
|
+
async resolveId(targetId, callOptions) {
|
|
660
|
+
const listed = await this.list(callOptions);
|
|
661
|
+
if (targetId) {
|
|
662
|
+
if (!listed.targets.some((target) => target.id === targetId)) {
|
|
663
|
+
throw new Error(`Computer target is unavailable: ${targetId}`);
|
|
664
|
+
}
|
|
665
|
+
return targetId;
|
|
666
|
+
}
|
|
667
|
+
const focused = listed.targets.filter((target) => target.focused);
|
|
668
|
+
if (focused.length === 1) return focused[0].id;
|
|
669
|
+
if (listed.targets.length === 1) return listed.targets[0].id;
|
|
670
|
+
if (listed.targets.length === 0)
|
|
671
|
+
throw new Error("Computer has no app, window, or screen targets");
|
|
672
|
+
throw new Error("Computer target is ambiguous; select an exact app or window");
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
var CodemodeComputerTarget = class {
|
|
676
|
+
constructor(client, computerSessionId, id) {
|
|
677
|
+
this.client = client;
|
|
678
|
+
this.computerSessionId = computerSessionId;
|
|
679
|
+
this.id = id;
|
|
680
|
+
}
|
|
681
|
+
async observe(callOptions = {}) {
|
|
682
|
+
return await callStructured(
|
|
683
|
+
this.client,
|
|
684
|
+
PATH2.computerObserve,
|
|
685
|
+
{ computerSessionId: this.computerSessionId, targetId: this.id },
|
|
686
|
+
callOptions
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
async act(action, fences = {}, callOptions = {}) {
|
|
690
|
+
return await callStructured(
|
|
691
|
+
this.client,
|
|
692
|
+
PATH2.computerAct,
|
|
693
|
+
{ computerSessionId: this.computerSessionId, targetId: this.id, action, ...fences },
|
|
694
|
+
callOptions
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
getByRole(role, options = {}) {
|
|
698
|
+
return new CodemodeComputerLocator(this, { kind: "role", role, ...options });
|
|
699
|
+
}
|
|
700
|
+
getByText(text, exact) {
|
|
701
|
+
return new CodemodeComputerLocator(this, { kind: "text", text, ...exact ? { exact } : {} });
|
|
702
|
+
}
|
|
703
|
+
getByLabel(text, exact) {
|
|
704
|
+
return new CodemodeComputerLocator(this, { kind: "label", text, ...exact ? { exact } : {} });
|
|
705
|
+
}
|
|
706
|
+
getByIdentifier(value) {
|
|
707
|
+
return new CodemodeComputerLocator(this, { kind: "identifier", value });
|
|
708
|
+
}
|
|
709
|
+
ref(ref) {
|
|
710
|
+
return new CodemodeComputerLocator(this, { kind: "ref", ref });
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
var CodemodeComputerLocator = class {
|
|
714
|
+
constructor(target, locator) {
|
|
715
|
+
this.target = target;
|
|
716
|
+
this.locator = locator;
|
|
717
|
+
}
|
|
718
|
+
async invoke(callOptions = {}) {
|
|
719
|
+
return await this.semantic("invoke", void 0, callOptions);
|
|
720
|
+
}
|
|
721
|
+
async focus(callOptions = {}) {
|
|
722
|
+
return await this.semantic("focus", void 0, callOptions);
|
|
723
|
+
}
|
|
724
|
+
async setValue(value, callOptions = {}) {
|
|
725
|
+
return await this.semantic("set_value", value, callOptions);
|
|
726
|
+
}
|
|
727
|
+
async select(callOptions = {}) {
|
|
728
|
+
return await this.semantic("select", void 0, callOptions);
|
|
729
|
+
}
|
|
730
|
+
async expand(callOptions = {}) {
|
|
731
|
+
return await this.semantic("expand", void 0, callOptions);
|
|
732
|
+
}
|
|
733
|
+
async collapse(callOptions = {}) {
|
|
734
|
+
return await this.semantic("collapse", void 0, callOptions);
|
|
735
|
+
}
|
|
736
|
+
async semantic(action, value, callOptions) {
|
|
737
|
+
return await this.target.act(
|
|
738
|
+
{
|
|
739
|
+
type: "semantic",
|
|
740
|
+
locator: this.locator,
|
|
741
|
+
action,
|
|
742
|
+
...value === void 0 ? {} : { value }
|
|
743
|
+
},
|
|
744
|
+
{},
|
|
745
|
+
callOptions
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
function createOpenGeniCodemode(client = () => environmentCodemodeClient()) {
|
|
750
|
+
return new OpenGeniCodemode(client);
|
|
751
|
+
}
|
|
752
|
+
var openGeni = createOpenGeniCodemode();
|
|
753
|
+
|
|
754
|
+
// src/declarations.ts
|
|
755
|
+
function generateCodemodeDeclarations(catalog, options = {}) {
|
|
756
|
+
const verified = parseVerifiedAttemptToolCatalog(catalog);
|
|
757
|
+
const moduleSpecifier = options.moduleSpecifier ?? "@opengeni/codemode";
|
|
758
|
+
const root = namespaceNode();
|
|
759
|
+
for (const entry of verified.entries) insertEntry(root, entry);
|
|
760
|
+
return [
|
|
761
|
+
"// Generated by @opengeni/codemode. Do not edit.",
|
|
762
|
+
`// Attempt catalog digest: ${verified.digest}`,
|
|
763
|
+
`import type { CodemodeCallOptions, CodemodeToolResult } from ${JSON.stringify(moduleSpecifier)};`,
|
|
764
|
+
"",
|
|
765
|
+
`declare module ${JSON.stringify(moduleSpecifier)} {`,
|
|
766
|
+
" interface CodemodeGeneratedTools {",
|
|
767
|
+
...renderChildren(root, 4),
|
|
768
|
+
" }",
|
|
769
|
+
"}",
|
|
770
|
+
"",
|
|
771
|
+
"export {};",
|
|
772
|
+
""
|
|
773
|
+
].join("\n");
|
|
774
|
+
}
|
|
775
|
+
function jsonSchemaToTypeScript(schema) {
|
|
776
|
+
return schemaType(schema, schema, /* @__PURE__ */ new Set(), 0);
|
|
777
|
+
}
|
|
778
|
+
function namespaceNode() {
|
|
779
|
+
return { children: /* @__PURE__ */ new Map(), entry: null };
|
|
780
|
+
}
|
|
781
|
+
function insertEntry(root, entry) {
|
|
782
|
+
let node = root;
|
|
783
|
+
for (const [index, segment] of entry.codemodePath.entries()) {
|
|
784
|
+
if (node.entry) {
|
|
785
|
+
throw new Error(
|
|
786
|
+
`Codemode declaration path ${entry.codemodePath.join(".")} extends a tool leaf`
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
let child = node.children.get(segment);
|
|
790
|
+
if (!child) {
|
|
791
|
+
child = namespaceNode();
|
|
792
|
+
node.children.set(segment, child);
|
|
793
|
+
}
|
|
794
|
+
node = child;
|
|
795
|
+
if (index === entry.codemodePath.length - 1) {
|
|
796
|
+
if (node.entry || node.children.size > 0) {
|
|
797
|
+
throw new Error(`Codemode declaration path ${entry.codemodePath.join(".")} collides`);
|
|
798
|
+
}
|
|
799
|
+
node.entry = entry;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function renderChildren(node, indent) {
|
|
804
|
+
const lines = [];
|
|
805
|
+
for (const [name, child] of [...node.children].sort(
|
|
806
|
+
([left], [right]) => left.localeCompare(right)
|
|
807
|
+
)) {
|
|
808
|
+
if (child.entry) {
|
|
809
|
+
lines.push(...renderTool(name, child.entry, indent));
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
lines.push(`${spaces(indent)}readonly ${name}: {`);
|
|
813
|
+
lines.push(...renderChildren(child, indent + 2));
|
|
814
|
+
lines.push(`${spaces(indent)}};`);
|
|
815
|
+
}
|
|
816
|
+
return lines;
|
|
817
|
+
}
|
|
818
|
+
function renderTool(name, entry, indent) {
|
|
819
|
+
const input = schemaType(entry.inputSchema, entry.inputSchema, /* @__PURE__ */ new Set(), 0);
|
|
820
|
+
const output = entry.outputSchema ? schemaType(entry.outputSchema, entry.outputSchema, /* @__PURE__ */ new Set(), 0) : "CodemodeToolResult";
|
|
821
|
+
const optionalArguments = rootObjectArgumentsAreOptional(entry.inputSchema);
|
|
822
|
+
const description = boundedDoc(entry.description ?? entry.title);
|
|
823
|
+
return [
|
|
824
|
+
...description ? renderDoc(description, indent) : [],
|
|
825
|
+
`${spaces(indent)}readonly ${name}: (`,
|
|
826
|
+
`${spaces(indent + 2)}argumentsValue${optionalArguments ? "?" : ""}: ${input},`,
|
|
827
|
+
`${spaces(indent + 2)}options?: CodemodeCallOptions,`,
|
|
828
|
+
`${spaces(indent)}) => Promise<${output}>;`
|
|
829
|
+
];
|
|
830
|
+
}
|
|
831
|
+
function rootObjectArgumentsAreOptional(schema) {
|
|
832
|
+
if (!isSchemaObject(schema)) return false;
|
|
833
|
+
const required = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
|
|
834
|
+
return required.length === 0 && (schema.type === "object" || isSchemaObject(schema.properties));
|
|
835
|
+
}
|
|
836
|
+
function schemaType(schema, rootSchema, resolvingRefs, depth) {
|
|
837
|
+
if (depth > 48 || schema === true) return "unknown";
|
|
838
|
+
if (schema === false) return "never";
|
|
839
|
+
if (!isSchemaObject(schema)) return "unknown";
|
|
840
|
+
if (typeof schema.$ref === "string") {
|
|
841
|
+
const reference = schema.$ref;
|
|
842
|
+
if (!reference.startsWith("#/") || resolvingRefs.has(reference)) return "unknown";
|
|
843
|
+
const resolved = resolveLocalReference(rootSchema, reference);
|
|
844
|
+
if (resolved === void 0) return "unknown";
|
|
845
|
+
const next = new Set(resolvingRefs);
|
|
846
|
+
next.add(reference);
|
|
847
|
+
return schemaType(resolved, rootSchema, next, depth + 1);
|
|
848
|
+
}
|
|
849
|
+
if (Object.hasOwn(schema, "const")) return literalType(schema.const);
|
|
850
|
+
if (Array.isArray(schema.enum)) {
|
|
851
|
+
return union(schema.enum.map(literalType));
|
|
852
|
+
}
|
|
853
|
+
const composites = [];
|
|
854
|
+
if (Array.isArray(schema.oneOf)) {
|
|
855
|
+
composites.push(
|
|
856
|
+
union(schema.oneOf.map((part) => schemaType(part, rootSchema, resolvingRefs, depth + 1)))
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
if (Array.isArray(schema.anyOf)) {
|
|
860
|
+
composites.push(
|
|
861
|
+
union(schema.anyOf.map((part) => schemaType(part, rootSchema, resolvingRefs, depth + 1)))
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
if (Array.isArray(schema.allOf)) {
|
|
865
|
+
composites.push(
|
|
866
|
+
intersection(
|
|
867
|
+
schema.allOf.map((part) => schemaType(part, rootSchema, resolvingRefs, depth + 1))
|
|
868
|
+
)
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
if (composites.length > 0) {
|
|
872
|
+
const composed = intersection(composites);
|
|
873
|
+
return schema.nullable === true ? union([composed, "null"]) : composed;
|
|
874
|
+
}
|
|
875
|
+
const declaredTypes = Array.isArray(schema.type) ? schema.type.filter((value) => typeof value === "string") : typeof schema.type === "string" ? [schema.type] : inferredSchemaTypes(schema);
|
|
876
|
+
const rendered = declaredTypes.map(
|
|
877
|
+
(type) => typeType(type, schema, rootSchema, resolvingRefs, depth + 1)
|
|
878
|
+
);
|
|
879
|
+
if (schema.nullable === true) rendered.push("null");
|
|
880
|
+
return union(rendered.length > 0 ? rendered : ["unknown"]);
|
|
881
|
+
}
|
|
882
|
+
function inferredSchemaTypes(schema) {
|
|
883
|
+
if (isSchemaObject(schema.properties) || Object.hasOwn(schema, "additionalProperties")) {
|
|
884
|
+
return ["object"];
|
|
885
|
+
}
|
|
886
|
+
if (Object.hasOwn(schema, "items") || Array.isArray(schema.prefixItems)) return ["array"];
|
|
887
|
+
return [];
|
|
888
|
+
}
|
|
889
|
+
function typeType(type, schema, rootSchema, resolvingRefs, depth) {
|
|
890
|
+
switch (type) {
|
|
891
|
+
case "null":
|
|
892
|
+
return "null";
|
|
893
|
+
case "boolean":
|
|
894
|
+
return "boolean";
|
|
895
|
+
case "integer":
|
|
896
|
+
case "number":
|
|
897
|
+
return "number";
|
|
898
|
+
case "string":
|
|
899
|
+
return "string";
|
|
900
|
+
case "array":
|
|
901
|
+
return arrayType(schema, rootSchema, resolvingRefs, depth);
|
|
902
|
+
case "object":
|
|
903
|
+
return objectType(schema, rootSchema, resolvingRefs, depth);
|
|
904
|
+
default:
|
|
905
|
+
return "unknown";
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
function arrayType(schema, rootSchema, resolvingRefs, depth) {
|
|
909
|
+
if (Array.isArray(schema.prefixItems)) {
|
|
910
|
+
const tuple = schema.prefixItems.map(
|
|
911
|
+
(item2) => schemaType(item2, rootSchema, resolvingRefs, depth + 1)
|
|
912
|
+
);
|
|
913
|
+
if (schema.items === false) return `readonly [${tuple.join(", ")}]`;
|
|
914
|
+
const rest = schema.items === void 0 || schema.items === true ? "unknown" : schemaType(schema.items, rootSchema, resolvingRefs, depth + 1);
|
|
915
|
+
return `readonly [${tuple.join(", ")}${tuple.length > 0 ? ", " : ""}...${rest}[]]`;
|
|
916
|
+
}
|
|
917
|
+
const item = schema.items === void 0 || schema.items === true ? "unknown" : schemaType(schema.items, rootSchema, resolvingRefs, depth + 1);
|
|
918
|
+
return `readonly (${item})[]`;
|
|
919
|
+
}
|
|
920
|
+
function objectType(schema, rootSchema, resolvingRefs, depth) {
|
|
921
|
+
const properties = isSchemaObject(schema.properties) ? schema.properties : {};
|
|
922
|
+
const required = new Set(
|
|
923
|
+
Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : []
|
|
924
|
+
);
|
|
925
|
+
const entries = Object.entries(properties).sort(([left], [right]) => left.localeCompare(right));
|
|
926
|
+
const fields = entries.map(([name, propertySchema]) => {
|
|
927
|
+
const key = identifierOrQuoted(name);
|
|
928
|
+
const optional = required.has(name) ? "" : "?";
|
|
929
|
+
return `readonly ${key}${optional}: ${schemaType(
|
|
930
|
+
propertySchema,
|
|
931
|
+
rootSchema,
|
|
932
|
+
resolvingRefs,
|
|
933
|
+
depth + 1
|
|
934
|
+
)}`;
|
|
935
|
+
});
|
|
936
|
+
for (const missing of [...required].filter((name) => !Object.hasOwn(properties, name)).sort()) {
|
|
937
|
+
fields.push(`readonly ${identifierOrQuoted(missing)}: unknown`);
|
|
938
|
+
}
|
|
939
|
+
const additional = schema.additionalProperties;
|
|
940
|
+
if (additional !== false) {
|
|
941
|
+
if (entries.length === 0 && additional !== void 0 && additional !== true) {
|
|
942
|
+
return `Readonly<Record<string, ${schemaType(
|
|
943
|
+
additional,
|
|
944
|
+
rootSchema,
|
|
945
|
+
resolvingRefs,
|
|
946
|
+
depth + 1
|
|
947
|
+
)}>>`;
|
|
948
|
+
}
|
|
949
|
+
fields.push("readonly [key: string]: unknown");
|
|
950
|
+
}
|
|
951
|
+
return fields.length === 0 ? "Record<string, never>" : `{ ${fields.join("; ")} }`;
|
|
952
|
+
}
|
|
953
|
+
function resolveLocalReference(rootSchema, reference) {
|
|
954
|
+
let current = rootSchema;
|
|
955
|
+
for (const encoded of reference.slice(2).split("/")) {
|
|
956
|
+
if (!isSchemaObject(current)) return void 0;
|
|
957
|
+
const segment = encoded.replace(/~1/gu, "/").replace(/~0/gu, "~");
|
|
958
|
+
if (!Object.hasOwn(current, segment)) return void 0;
|
|
959
|
+
current = current[segment];
|
|
960
|
+
}
|
|
961
|
+
return current;
|
|
962
|
+
}
|
|
963
|
+
function literalType(value) {
|
|
964
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
965
|
+
return JSON.stringify(value);
|
|
966
|
+
}
|
|
967
|
+
if (Array.isArray(value)) return `readonly [${value.map(literalType).join(", ")}]`;
|
|
968
|
+
if (isSchemaObject(value)) {
|
|
969
|
+
return `{ ${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `readonly ${identifierOrQuoted(key)}: ${literalType(child)}`).join("; ")} }`;
|
|
970
|
+
}
|
|
971
|
+
return "unknown";
|
|
972
|
+
}
|
|
973
|
+
function union(types) {
|
|
974
|
+
const unique = [...new Set(types)];
|
|
975
|
+
if (unique.includes("unknown")) return "unknown";
|
|
976
|
+
if (unique.length === 0) return "never";
|
|
977
|
+
return unique.length === 1 ? unique[0] : unique.map(parenthesizeComposite).join(" | ");
|
|
978
|
+
}
|
|
979
|
+
function intersection(types) {
|
|
980
|
+
const unique = [...new Set(types.filter((type) => type !== "unknown"))];
|
|
981
|
+
if (unique.length === 0) return "unknown";
|
|
982
|
+
return unique.length === 1 ? unique[0] : unique.map(parenthesizeComposite).join(" & ");
|
|
983
|
+
}
|
|
984
|
+
function parenthesizeComposite(type) {
|
|
985
|
+
return /[|&]/u.test(type) ? `(${type})` : type;
|
|
986
|
+
}
|
|
987
|
+
function identifierOrQuoted(value) {
|
|
988
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(value) ? value : JSON.stringify(value);
|
|
989
|
+
}
|
|
990
|
+
function boundedDoc(value) {
|
|
991
|
+
if (!value) return null;
|
|
992
|
+
const normalized = value.replace(/\s+/gu, " ").trim().replace(/\*\//gu, "*\\/");
|
|
993
|
+
if (!normalized) return null;
|
|
994
|
+
return normalized.length <= 512 ? normalized : `${normalized.slice(0, 509)}...`;
|
|
995
|
+
}
|
|
996
|
+
function renderDoc(value, indent) {
|
|
997
|
+
return [`${spaces(indent)}/** ${value} */`];
|
|
998
|
+
}
|
|
999
|
+
function spaces(count) {
|
|
1000
|
+
return " ".repeat(count);
|
|
1001
|
+
}
|
|
1002
|
+
function isSchemaObject(value) {
|
|
1003
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// src/index.ts
|
|
1007
|
+
var AttemptToolCatalogStaleError = class extends Error {
|
|
1008
|
+
code = "catalog_stale";
|
|
1009
|
+
constructor() {
|
|
1010
|
+
super("Codemode catalog is stale for the active execution attempt");
|
|
1011
|
+
this.name = "AttemptToolCatalogStaleError";
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
var AttemptToolNotFoundError = class extends Error {
|
|
1015
|
+
code = "tool_not_found";
|
|
1016
|
+
constructor() {
|
|
1017
|
+
super("Tool is not present in the active execution attempt catalog");
|
|
1018
|
+
this.name = "AttemptToolNotFoundError";
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
var AttemptToolApprovalRequiredError = class extends Error {
|
|
1022
|
+
code = "approval_required";
|
|
1023
|
+
constructor() {
|
|
1024
|
+
super("Tool requires human approval and must be invoked through the agent");
|
|
1025
|
+
this.name = "AttemptToolApprovalRequiredError";
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
var AttemptToolCatalogIntegrityError = class extends Error {
|
|
1029
|
+
code = "catalog_integrity_failed";
|
|
1030
|
+
constructor() {
|
|
1031
|
+
super("Attempt tool catalog digest does not match its authoritative content");
|
|
1032
|
+
this.name = "AttemptToolCatalogIntegrityError";
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
var AttemptToolCatalogTooLargeError = class extends Error {
|
|
1036
|
+
code = "catalog_too_large";
|
|
1037
|
+
constructor() {
|
|
1038
|
+
super("Attempt tool catalog exceeds the maximum serialized size");
|
|
1039
|
+
this.name = "AttemptToolCatalogTooLargeError";
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
var AttemptToolInputValidationError = class extends Error {
|
|
1043
|
+
code = "invalid_tool_arguments";
|
|
1044
|
+
constructor() {
|
|
1045
|
+
super("Tool arguments do not match the attempt catalog input schema");
|
|
1046
|
+
this.name = "AttemptToolInputValidationError";
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
var AttemptToolOutputValidationError = class extends Error {
|
|
1050
|
+
code = "invalid_tool_result";
|
|
1051
|
+
constructor() {
|
|
1052
|
+
super("Tool result does not match the attempt catalog output schema");
|
|
1053
|
+
this.name = "AttemptToolOutputValidationError";
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
var CodemodeTransportError = class extends Error {
|
|
1057
|
+
constructor(message, status = null) {
|
|
1058
|
+
super(message);
|
|
1059
|
+
this.status = status;
|
|
1060
|
+
this.name = "CodemodeTransportError";
|
|
1061
|
+
}
|
|
1062
|
+
code = "codemode_transport_error";
|
|
1063
|
+
};
|
|
1064
|
+
var CodemodeOperationError = class extends Error {
|
|
1065
|
+
constructor(operation, code) {
|
|
1066
|
+
super(operation.errorMessage ?? `Codemode operation ${operation.state}`);
|
|
1067
|
+
this.operation = operation;
|
|
1068
|
+
this.code = code;
|
|
1069
|
+
this.name = "CodemodeOperationError";
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
var CodemodeToolCallError = class extends Error {
|
|
1073
|
+
constructor(result) {
|
|
1074
|
+
const error = structuredToolError(result);
|
|
1075
|
+
super(error.message);
|
|
1076
|
+
this.result = result;
|
|
1077
|
+
this.name = "CodemodeToolCallError";
|
|
1078
|
+
this.code = error.code;
|
|
1079
|
+
this.retryable = error.retryable;
|
|
1080
|
+
}
|
|
1081
|
+
code;
|
|
1082
|
+
retryable;
|
|
1083
|
+
};
|
|
1084
|
+
var CodemodeToolContractError = class extends Error {
|
|
1085
|
+
code = "invalid_tool_result";
|
|
1086
|
+
constructor(message) {
|
|
1087
|
+
super(message);
|
|
1088
|
+
this.name = "CodemodeToolContractError";
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
var CodemodeClient = class {
|
|
1092
|
+
constructor(options) {
|
|
1093
|
+
this.options = options;
|
|
1094
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/u, "");
|
|
1095
|
+
if (!/^https?:\/\//u.test(this.baseUrl)) {
|
|
1096
|
+
throw new Error("Codemode baseUrl must be an absolute HTTP(S) URL");
|
|
1097
|
+
}
|
|
1098
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
1099
|
+
this.pollIntervalMs = boundedPositiveInteger(options.pollIntervalMs ?? 500, 50, 3e4);
|
|
1100
|
+
this.timeoutMs = boundedPositiveInteger(options.timeoutMs ?? 10 * 6e4, 1e3, 60 * 6e4);
|
|
1101
|
+
}
|
|
1102
|
+
baseUrl;
|
|
1103
|
+
fetchImpl;
|
|
1104
|
+
pollIntervalMs;
|
|
1105
|
+
timeoutMs;
|
|
1106
|
+
catalogSnapshot = null;
|
|
1107
|
+
async catalog(options = {}) {
|
|
1108
|
+
if (this.catalogSnapshot && !options.refresh) return this.catalogSnapshot;
|
|
1109
|
+
const response = await this.request("/catalog", {
|
|
1110
|
+
method: "GET",
|
|
1111
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1112
|
+
});
|
|
1113
|
+
const catalog = parseVerifiedAttemptToolCatalog(await response.json());
|
|
1114
|
+
this.catalogSnapshot = catalog;
|
|
1115
|
+
return catalog;
|
|
1116
|
+
}
|
|
1117
|
+
async tools(options = {}) {
|
|
1118
|
+
return compileCodemodeTools(await this.catalog(options), this);
|
|
1119
|
+
}
|
|
1120
|
+
async call(identity, argumentsValue = {}, options = {}) {
|
|
1121
|
+
const catalog = await this.catalog(options.signal ? { signal: options.signal } : {});
|
|
1122
|
+
if (!catalog.entries.some(
|
|
1123
|
+
(entry) => entry.identity.serverId === identity.serverId && entry.identity.toolName === identity.toolName
|
|
1124
|
+
)) {
|
|
1125
|
+
throw new AttemptToolNotFoundError();
|
|
1126
|
+
}
|
|
1127
|
+
const operationId = options.operationId ?? randomUUID();
|
|
1128
|
+
const deadline = Date.now() + boundedPositiveInteger(options.timeoutMs ?? this.timeoutMs, 1e3, 60 * 6e4);
|
|
1129
|
+
let submitted = false;
|
|
1130
|
+
let operation = null;
|
|
1131
|
+
let nextNotifyAt = 0;
|
|
1132
|
+
while (true) {
|
|
1133
|
+
throwIfAborted(options.signal);
|
|
1134
|
+
if (Date.now() >= deadline) {
|
|
1135
|
+
throw new CodemodeTransportError(
|
|
1136
|
+
`Codemode operation ${operationId} did not settle before the client deadline`
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
const shouldNotify = !submitted || operation?.state === "queued" && Date.now() >= nextNotifyAt;
|
|
1140
|
+
if (shouldNotify) {
|
|
1141
|
+
submitted = true;
|
|
1142
|
+
nextNotifyAt = Date.now() + 2e3;
|
|
1143
|
+
try {
|
|
1144
|
+
operation = await this.submit(
|
|
1145
|
+
operationId,
|
|
1146
|
+
catalog.digest,
|
|
1147
|
+
identity,
|
|
1148
|
+
argumentsValue,
|
|
1149
|
+
options.signal
|
|
1150
|
+
);
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
try {
|
|
1153
|
+
operation = await this.read(operationId, options.signal);
|
|
1154
|
+
} catch {
|
|
1155
|
+
throw error;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
} else {
|
|
1159
|
+
operation = await this.read(operationId, options.signal);
|
|
1160
|
+
}
|
|
1161
|
+
if (operation.state === "completed") return AttemptToolResult.parse(operation.result);
|
|
1162
|
+
if (["failed", "outcome_unknown", "cancelled"].includes(operation.state)) {
|
|
1163
|
+
throw new CodemodeOperationError(
|
|
1164
|
+
operation,
|
|
1165
|
+
operation.errorCode ?? `codemode_${operation.state}`
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
await abortableDelay(this.pollIntervalMs, options.signal);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
/** Resolve and call one exact generated namespace path without parsing a wire name. */
|
|
1172
|
+
async callPath(path, argumentsValue = {}, options = {}) {
|
|
1173
|
+
if (path.length < 2 || path.some((segment) => segment.length === 0)) {
|
|
1174
|
+
throw new AttemptToolNotFoundError();
|
|
1175
|
+
}
|
|
1176
|
+
const catalog = await this.catalog(options.signal ? { signal: options.signal } : {});
|
|
1177
|
+
const matches = catalog.entries.filter(
|
|
1178
|
+
(entry) => entry.codemodePath.length === path.length && entry.codemodePath.every((segment, index) => segment === path[index])
|
|
1179
|
+
);
|
|
1180
|
+
if (matches.length !== 1) throw new AttemptToolNotFoundError();
|
|
1181
|
+
return await this.call(matches[0].identity, argumentsValue, options);
|
|
1182
|
+
}
|
|
1183
|
+
/** Return structured content when the catalog declares it; otherwise retain the full MCP result. */
|
|
1184
|
+
async callPathValue(path, argumentsValue = {}, options = {}) {
|
|
1185
|
+
if (path.length < 2 || path.some((segment) => segment.length === 0)) {
|
|
1186
|
+
throw new AttemptToolNotFoundError();
|
|
1187
|
+
}
|
|
1188
|
+
const catalog = await this.catalog(options.signal ? { signal: options.signal } : {});
|
|
1189
|
+
const matches = catalog.entries.filter(
|
|
1190
|
+
(entry2) => entry2.codemodePath.length === path.length && entry2.codemodePath.every((segment, index) => segment === path[index])
|
|
1191
|
+
);
|
|
1192
|
+
if (matches.length !== 1) throw new AttemptToolNotFoundError();
|
|
1193
|
+
const entry = matches[0];
|
|
1194
|
+
const result = await this.call(entry.identity, argumentsValue, options);
|
|
1195
|
+
if (!entry.outputSchema) return result;
|
|
1196
|
+
if (result.isError) throw new CodemodeToolCallError(result);
|
|
1197
|
+
if (!result.structuredContent) {
|
|
1198
|
+
throw new CodemodeToolContractError(
|
|
1199
|
+
`Codemode tool ${path.join(".")} declared outputSchema but returned no structured content`
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
return result.structuredContent;
|
|
1203
|
+
}
|
|
1204
|
+
async submit(operationId, catalogDigest, identity, argumentsValue, signal) {
|
|
1205
|
+
const response = await this.request("/calls", {
|
|
1206
|
+
method: "POST",
|
|
1207
|
+
...signal ? { signal } : {},
|
|
1208
|
+
headers: { "content-type": "application/json" },
|
|
1209
|
+
body: JSON.stringify({ operationId, catalogDigest, identity, arguments: argumentsValue })
|
|
1210
|
+
});
|
|
1211
|
+
return CodemodeCallSubmission.parse(await response.json()).operation;
|
|
1212
|
+
}
|
|
1213
|
+
async read(operationId, signal) {
|
|
1214
|
+
const response = await this.request(`/calls/${operationId}`, {
|
|
1215
|
+
method: "GET",
|
|
1216
|
+
...signal ? { signal } : {}
|
|
1217
|
+
});
|
|
1218
|
+
return CodemodeOperation.parse(await response.json());
|
|
1219
|
+
}
|
|
1220
|
+
async request(path, init) {
|
|
1221
|
+
const token = typeof this.options.token === "function" ? await this.options.token() : this.options.token;
|
|
1222
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
1223
|
+
...init,
|
|
1224
|
+
headers: {
|
|
1225
|
+
...Object.fromEntries(new Headers(init.headers).entries()),
|
|
1226
|
+
authorization: `Bearer ${token}`
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
if (!response.ok) {
|
|
1230
|
+
let message = `Codemode request failed with HTTP ${response.status}`;
|
|
1231
|
+
try {
|
|
1232
|
+
const payload = await response.json();
|
|
1233
|
+
if (typeof payload.error?.message === "string") message = payload.error.message;
|
|
1234
|
+
} catch {
|
|
1235
|
+
}
|
|
1236
|
+
throw new CodemodeTransportError(message, response.status);
|
|
1237
|
+
}
|
|
1238
|
+
return response;
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
function compileCodemodeTools(catalog, client) {
|
|
1242
|
+
const verified = parseVerifiedAttemptToolCatalog(catalog);
|
|
1243
|
+
const root = /* @__PURE__ */ Object.create(null);
|
|
1244
|
+
for (const entry of verified.entries) {
|
|
1245
|
+
let cursor = root;
|
|
1246
|
+
for (const segment of entry.codemodePath.slice(0, -1)) {
|
|
1247
|
+
let existing = cursor[segment];
|
|
1248
|
+
if (typeof existing === "function") throw new Error("Codemode path collides with a tool");
|
|
1249
|
+
if (!existing) {
|
|
1250
|
+
existing = /* @__PURE__ */ Object.create(null);
|
|
1251
|
+
cursor[segment] = existing;
|
|
1252
|
+
}
|
|
1253
|
+
cursor = existing;
|
|
1254
|
+
}
|
|
1255
|
+
const leaf = entry.codemodePath.at(-1);
|
|
1256
|
+
const invoke = async (args = {}, options = {}) => await client.callPathValue(entry.codemodePath, args, options);
|
|
1257
|
+
Object.defineProperty(invoke, "entry", { value: entry, enumerable: false });
|
|
1258
|
+
cursor[leaf] = invoke;
|
|
1259
|
+
}
|
|
1260
|
+
return root;
|
|
1261
|
+
}
|
|
1262
|
+
var AttemptToolEnvironment = class {
|
|
1263
|
+
constructor(catalog, definitions, authorize) {
|
|
1264
|
+
this.authorize = authorize;
|
|
1265
|
+
this.catalog = catalog;
|
|
1266
|
+
for (const definition of definitions) {
|
|
1267
|
+
this.byIdentity.set(identityKey(definition.entry.identity), definition);
|
|
1268
|
+
this.byModelName.set(definition.entry.modelName, definition);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
catalog;
|
|
1272
|
+
byIdentity = /* @__PURE__ */ new Map();
|
|
1273
|
+
byModelName = /* @__PURE__ */ new Map();
|
|
1274
|
+
async call(input, context = {}) {
|
|
1275
|
+
const call = AttemptToolCall.parse(input);
|
|
1276
|
+
if (call.catalogDigest !== this.catalog.digest) {
|
|
1277
|
+
throw new AttemptToolCatalogStaleError();
|
|
1278
|
+
}
|
|
1279
|
+
const definition = this.byIdentity.get(identityKey(call.identity));
|
|
1280
|
+
if (!definition) {
|
|
1281
|
+
throw new AttemptToolNotFoundError();
|
|
1282
|
+
}
|
|
1283
|
+
if (call.caller.kind === "codemode" && definition.entry.approval === "human") {
|
|
1284
|
+
throw new AttemptToolApprovalRequiredError();
|
|
1285
|
+
}
|
|
1286
|
+
if (!definition.validateInput(call.arguments)) {
|
|
1287
|
+
throw new AttemptToolInputValidationError();
|
|
1288
|
+
}
|
|
1289
|
+
await this.authorize?.({ call, entry: definition.entry });
|
|
1290
|
+
const result = AttemptToolResult.parse(
|
|
1291
|
+
await definition.execute(call.arguments, {
|
|
1292
|
+
operationId: call.operationId,
|
|
1293
|
+
caller: call.caller,
|
|
1294
|
+
...context.transportMeta === void 0 ? {} : { transportMeta: context.transportMeta },
|
|
1295
|
+
...context.signal === void 0 ? {} : { signal: context.signal }
|
|
1296
|
+
})
|
|
1297
|
+
);
|
|
1298
|
+
if (!result.isError && definition.validateOutput) {
|
|
1299
|
+
if (result.structuredContent === void 0 || !definition.validateOutput(result.structuredContent)) {
|
|
1300
|
+
throw new AttemptToolOutputValidationError();
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
async callModel(input) {
|
|
1306
|
+
const definition = this.byModelName.get(input.modelName);
|
|
1307
|
+
if (!definition) {
|
|
1308
|
+
throw new AttemptToolNotFoundError();
|
|
1309
|
+
}
|
|
1310
|
+
const call = AttemptToolCall.parse({
|
|
1311
|
+
operationId: input.operationId ?? randomUUID(),
|
|
1312
|
+
catalogDigest: this.catalog.digest,
|
|
1313
|
+
identity: definition.entry.identity,
|
|
1314
|
+
arguments: input.arguments,
|
|
1315
|
+
caller: { kind: "model", subjectId: input.subjectId }
|
|
1316
|
+
});
|
|
1317
|
+
return await this.call(call, {
|
|
1318
|
+
...input.transportMeta === void 0 ? {} : { transportMeta: input.transportMeta },
|
|
1319
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
function createAttemptToolEnvironment(input) {
|
|
1324
|
+
const createdAt = (input.createdAt ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1325
|
+
const paths = allocateCodemodePaths(input.definitions);
|
|
1326
|
+
const schemaValidators = createSchemaValidators();
|
|
1327
|
+
const compiled = input.definitions.map((definition, index) => {
|
|
1328
|
+
const { execute, codemodePath: _path, ...entryInput } = definition;
|
|
1329
|
+
const entry = AttemptToolCatalogEntry.parse({
|
|
1330
|
+
...entryInput,
|
|
1331
|
+
codemodePath: paths[index]
|
|
1332
|
+
});
|
|
1333
|
+
return {
|
|
1334
|
+
entry,
|
|
1335
|
+
execute,
|
|
1336
|
+
validateInput: compileCatalogSchema(schemaValidators, entry.inputSchema),
|
|
1337
|
+
validateOutput: entry.outputSchema ? compileCatalogSchema(schemaValidators, entry.outputSchema) : null
|
|
1338
|
+
};
|
|
1339
|
+
});
|
|
1340
|
+
const unsigned = {
|
|
1341
|
+
version: ATTEMPT_TOOL_CATALOG_VERSION,
|
|
1342
|
+
...input.scope,
|
|
1343
|
+
generation: input.generation,
|
|
1344
|
+
createdAt,
|
|
1345
|
+
entries: compiled.map(({ entry }) => entry)
|
|
1346
|
+
};
|
|
1347
|
+
const catalog = AttemptToolCatalog.parse({
|
|
1348
|
+
...unsigned,
|
|
1349
|
+
digest: digestAttemptToolCatalog(unsigned)
|
|
1350
|
+
});
|
|
1351
|
+
assertCatalogSize(catalog);
|
|
1352
|
+
return new AttemptToolEnvironment(catalog, compiled, input.authorize);
|
|
1353
|
+
}
|
|
1354
|
+
function digestAttemptToolCatalog(catalog) {
|
|
1355
|
+
const { createdAt: _createdAt, ...authoritative } = catalog;
|
|
1356
|
+
return digestCanonicalJson(authoritative);
|
|
1357
|
+
}
|
|
1358
|
+
function digestCodemodeOperationRequest(input) {
|
|
1359
|
+
return digestCanonicalJson(input);
|
|
1360
|
+
}
|
|
1361
|
+
function codemodeDispatchSubject(workspaceId, attemptId) {
|
|
1362
|
+
if (!UUID_PATTERN.test(workspaceId) || !UUID_PATTERN.test(attemptId)) {
|
|
1363
|
+
throw new Error("Codemode dispatch subject requires UUID workspace and attempt ids");
|
|
1364
|
+
}
|
|
1365
|
+
return `codemode.${workspaceId}.${attemptId}.dispatch`;
|
|
1366
|
+
}
|
|
1367
|
+
function encodeCodemodeDispatchRequest(input) {
|
|
1368
|
+
return new TextEncoder().encode(JSON.stringify(CodemodeDispatchRequest.parse(input)));
|
|
1369
|
+
}
|
|
1370
|
+
function decodeCodemodeDispatchRequest(input) {
|
|
1371
|
+
return CodemodeDispatchRequest.parse(JSON.parse(new TextDecoder().decode(input)));
|
|
1372
|
+
}
|
|
1373
|
+
function encodeCodemodeDispatchAck(input) {
|
|
1374
|
+
return new TextEncoder().encode(JSON.stringify(CodemodeDispatchAck.parse(input)));
|
|
1375
|
+
}
|
|
1376
|
+
function decodeCodemodeDispatchAck(input) {
|
|
1377
|
+
return CodemodeDispatchAck.parse(JSON.parse(new TextDecoder().decode(input)));
|
|
1378
|
+
}
|
|
1379
|
+
function parseVerifiedAttemptToolCatalog(input) {
|
|
1380
|
+
const catalog = AttemptToolCatalog.parse(input);
|
|
1381
|
+
assertCatalogSize(catalog);
|
|
1382
|
+
const { digest, ...unsigned } = catalog;
|
|
1383
|
+
if (digestAttemptToolCatalog(unsigned) !== digest) {
|
|
1384
|
+
throw new AttemptToolCatalogIntegrityError();
|
|
1385
|
+
}
|
|
1386
|
+
return catalog;
|
|
1387
|
+
}
|
|
1388
|
+
function assertCatalogSize(catalog) {
|
|
1389
|
+
if (new TextEncoder().encode(JSON.stringify(catalog)).byteLength > ATTEMPT_TOOL_CATALOG_MAX_BYTES) {
|
|
1390
|
+
throw new AttemptToolCatalogTooLargeError();
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
function createSchemaValidators() {
|
|
1394
|
+
const options = {
|
|
1395
|
+
allErrors: false,
|
|
1396
|
+
coerceTypes: false,
|
|
1397
|
+
strict: false,
|
|
1398
|
+
useDefaults: false,
|
|
1399
|
+
validateFormats: false
|
|
1400
|
+
};
|
|
1401
|
+
return {
|
|
1402
|
+
draft7: new Ajv(options),
|
|
1403
|
+
draft2019: new Ajv2019(options),
|
|
1404
|
+
draft2020: new Ajv2020(options)
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
function compileCatalogSchema(validators, schema) {
|
|
1408
|
+
const dialect = typeof schema.$schema === "string" ? schema.$schema : "";
|
|
1409
|
+
if (dialect.includes("2020-12")) return validators.draft2020.compile(schema);
|
|
1410
|
+
if (dialect.includes("2019-09")) return validators.draft2019.compile(schema);
|
|
1411
|
+
return validators.draft7.compile(schema);
|
|
1412
|
+
}
|
|
1413
|
+
function allocateCodemodePaths(definitions) {
|
|
1414
|
+
const bases = definitions.map(
|
|
1415
|
+
(definition) => (definition.codemodePath?.length ? definition.codemodePath : [definition.identity.serverId, definition.identity.toolName]).map(safeNamespaceSegment)
|
|
1416
|
+
);
|
|
1417
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1418
|
+
for (const path of bases) {
|
|
1419
|
+
const key = path.join("\0");
|
|
1420
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
1421
|
+
}
|
|
1422
|
+
return bases.map((base, index) => {
|
|
1423
|
+
const key = base.join("\0");
|
|
1424
|
+
if (counts.get(key) === 1) return base;
|
|
1425
|
+
const suffix = `_${shortIdentityDigest(definitions[index].identity)}`;
|
|
1426
|
+
const last = base.at(-1);
|
|
1427
|
+
return [...base.slice(0, -1), `${last.slice(0, 128 - suffix.length)}${suffix}`];
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
function safeNamespaceSegment(value) {
|
|
1431
|
+
let normalized = value.replace(/[^A-Za-z0-9_$]/gu, "_");
|
|
1432
|
+
if (!/^[A-Za-z_$]/u.test(normalized)) normalized = `_${normalized}`;
|
|
1433
|
+
if (["__proto__", "prototype", "constructor"].includes(normalized)) {
|
|
1434
|
+
normalized = `_${normalized}`;
|
|
1435
|
+
}
|
|
1436
|
+
return normalized.slice(0, 128) || "_";
|
|
1437
|
+
}
|
|
1438
|
+
function shortIdentityDigest(identity) {
|
|
1439
|
+
return createHash("sha256").update(identityKey(identity), "utf8").digest("hex").slice(0, 10);
|
|
1440
|
+
}
|
|
1441
|
+
function identityKey(identity) {
|
|
1442
|
+
return `${identity.serverId}\0${identity.toolName}`;
|
|
1443
|
+
}
|
|
1444
|
+
function digestCanonicalJson(value) {
|
|
1445
|
+
return createHash("sha256").update(JSON.stringify(canonicalJsonValue(value)), "utf8").digest("hex");
|
|
1446
|
+
}
|
|
1447
|
+
function canonicalJsonValue(value) {
|
|
1448
|
+
if (Array.isArray(value)) return value.map(canonicalJsonValue);
|
|
1449
|
+
if (value !== null && typeof value === "object") {
|
|
1450
|
+
return Object.fromEntries(
|
|
1451
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, canonicalJsonValue(entry)])
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
return value;
|
|
1455
|
+
}
|
|
1456
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
1457
|
+
function boundedPositiveInteger(value, minimum, maximum) {
|
|
1458
|
+
if (!Number.isFinite(value)) return minimum;
|
|
1459
|
+
return Math.max(minimum, Math.min(maximum, Math.floor(value)));
|
|
1460
|
+
}
|
|
1461
|
+
function throwIfAborted(signal) {
|
|
1462
|
+
if (signal?.aborted) throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
1463
|
+
}
|
|
1464
|
+
function structuredToolError(result) {
|
|
1465
|
+
const structured = result.structuredContent;
|
|
1466
|
+
const error = structured?.error;
|
|
1467
|
+
return {
|
|
1468
|
+
code: typeof error?.code === "string" ? error.code : "tool_error",
|
|
1469
|
+
message: typeof error?.message === "string" ? error.message : "Codemode tool failed",
|
|
1470
|
+
retryable: error?.retryable === true
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
async function abortableDelay(delayMs, signal) {
|
|
1474
|
+
if (!signal) {
|
|
1475
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
throwIfAborted(signal);
|
|
1479
|
+
await new Promise((resolve, reject) => {
|
|
1480
|
+
const timer = setTimeout(() => {
|
|
1481
|
+
signal.removeEventListener("abort", onAbort);
|
|
1482
|
+
resolve();
|
|
1483
|
+
}, delayMs);
|
|
1484
|
+
const onAbort = () => {
|
|
1485
|
+
clearTimeout(timer);
|
|
1486
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1487
|
+
};
|
|
1488
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
export {
|
|
1492
|
+
AttemptToolApprovalRequiredError,
|
|
1493
|
+
AttemptToolCatalogIntegrityError,
|
|
1494
|
+
AttemptToolCatalogStaleError,
|
|
1495
|
+
AttemptToolCatalogTooLargeError,
|
|
1496
|
+
AttemptToolEnvironment,
|
|
1497
|
+
AttemptToolInputValidationError,
|
|
1498
|
+
AttemptToolNotFoundError,
|
|
1499
|
+
AttemptToolOutputValidationError,
|
|
1500
|
+
CODEMODE_ENVIRONMENT,
|
|
1501
|
+
CodemodeArtifact,
|
|
1502
|
+
CodemodeArtifactCollection,
|
|
1503
|
+
CodemodeArtifactExport,
|
|
1504
|
+
CodemodeBrowser,
|
|
1505
|
+
CodemodeBrowserCollection,
|
|
1506
|
+
CodemodeBrowserIdentityCollection,
|
|
1507
|
+
CodemodeBrowserLocator,
|
|
1508
|
+
CodemodeBrowserTab,
|
|
1509
|
+
CodemodeBrowserTabCollection,
|
|
1510
|
+
CodemodeClient,
|
|
1511
|
+
CodemodeComputer,
|
|
1512
|
+
CodemodeComputerCollection,
|
|
1513
|
+
CodemodeComputerLocator,
|
|
1514
|
+
CodemodeComputerTarget,
|
|
1515
|
+
CodemodeComputerTargetCollection,
|
|
1516
|
+
CodemodeOperationError,
|
|
1517
|
+
CodemodeToolCallError,
|
|
1518
|
+
CodemodeToolContractError,
|
|
1519
|
+
CodemodeToolExecutionError,
|
|
1520
|
+
CodemodeTransportError,
|
|
1521
|
+
OpenGeniCodemode,
|
|
1522
|
+
callStructured,
|
|
1523
|
+
codemodeArtifactIds,
|
|
1524
|
+
codemodeClientProvider,
|
|
1525
|
+
codemodeDispatchSubject,
|
|
1526
|
+
compileCodemodeTools,
|
|
1527
|
+
createAttemptToolEnvironment,
|
|
1528
|
+
createCodemodeTools,
|
|
1529
|
+
createOpenGeniCodemode,
|
|
1530
|
+
decodeCodemodeDispatchAck,
|
|
1531
|
+
decodeCodemodeDispatchRequest,
|
|
1532
|
+
digestAttemptToolCatalog,
|
|
1533
|
+
digestCodemodeOperationRequest,
|
|
1534
|
+
encodeCodemodeDispatchAck,
|
|
1535
|
+
encodeCodemodeDispatchRequest,
|
|
1536
|
+
environmentCodemodeClient,
|
|
1537
|
+
generateCodemodeDeclarations,
|
|
1538
|
+
jsonSchemaToTypeScript,
|
|
1539
|
+
openGeni,
|
|
1540
|
+
parseVerifiedAttemptToolCatalog,
|
|
1541
|
+
tools
|
|
1542
|
+
};
|
|
1543
|
+
//# sourceMappingURL=index.js.map
|