@opengeni/ogtool 0.3.43 → 0.3.44-canary.36199476632001
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 +5 -4
- package/dist/bin/ogtool.cjs +122 -11
- package/package.json +2 -2
- package/src/cli.ts +0 -7
package/README.md
CHANGED
|
@@ -80,10 +80,11 @@ the total/offset footer. Both `--flag value` and `--flag=value` are supported.
|
|
|
80
80
|
`--full` rejects `--json`, `--query`, `--limit`, and `--offset`, including explicitly
|
|
81
81
|
supplied defaults. Unknown/duplicate flags and extra arguments are errors.
|
|
82
82
|
`show` accepts the same exact path/model-name/identity aliases as `call`
|
|
83
|
-
and rejects unknown or ambiguous names.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
and rejects unknown or ambiguous names. It returns the complete single-tool JSON,
|
|
84
|
+
including large schemas. To keep a large schema out of agent context, redirect
|
|
85
|
+
`ogtool show <path> > tool.json` and read only the needed file ranges; use
|
|
86
|
+
`ogtool declarations <output-file>` for generated TypeScript types. Existing
|
|
87
|
+
scripts parsing the old `list` JSON must use `list --full`.
|
|
87
88
|
|
|
88
89
|
The Connected Machine fallback, `"$OPENGENI_CODEMODE_NATIVE_CLIENT" codemode`,
|
|
89
90
|
supports the same `list`, `list --json`, `list --full`, and `show` discovery behavior.
|
package/dist/bin/ogtool.cjs
CHANGED
|
@@ -14731,7 +14731,15 @@ function generateToolDeclarations(catalog, options) {
|
|
|
14731
14731
|
return [
|
|
14732
14732
|
`// Generated by ${options.generatedBy}. Do not edit.`,
|
|
14733
14733
|
`// ${options.catalogDigestLabel ?? "Tool catalog digest"}: ${catalog.digest}`,
|
|
14734
|
-
|
|
14734
|
+
...options.typedResultWrapperType ? [
|
|
14735
|
+
"// Typed calls normally return their structured output. If image blocks are present,",
|
|
14736
|
+
"// they return CodemodeMultimodalValue<T> with local image paths and original content."
|
|
14737
|
+
] : [],
|
|
14738
|
+
`import type { ${[
|
|
14739
|
+
options.callOptionsType,
|
|
14740
|
+
options.fallbackResultType,
|
|
14741
|
+
...options.typedResultWrapperType ? [options.typedResultWrapperType] : []
|
|
14742
|
+
].join(", ")} } from ${JSON.stringify(options.moduleSpecifier)};`,
|
|
14735
14743
|
"",
|
|
14736
14744
|
`declare module ${JSON.stringify(options.moduleSpecifier)} {`,
|
|
14737
14745
|
` interface ${options.interfaceName} {`,
|
|
@@ -14776,7 +14784,8 @@ function renderChildren(node, indent, options) {
|
|
|
14776
14784
|
if (child.entry) {
|
|
14777
14785
|
const entry = child.entry;
|
|
14778
14786
|
const input = jsonSchemaToTypeScript(entry.inputSchema);
|
|
14779
|
-
const
|
|
14787
|
+
const structuredOutput = entry.outputSchema ? jsonSchemaToTypeScript(entry.outputSchema) : null;
|
|
14788
|
+
const output = structuredOutput ? options.typedResultWrapperType ? `${structuredOutput} | ${options.typedResultWrapperType}<${structuredOutput}>` : structuredOutput : options.fallbackResultType;
|
|
14780
14789
|
const description = boundedDoc(entry.description ?? entry.title);
|
|
14781
14790
|
if (description)
|
|
14782
14791
|
lines.push(...renderDoc(description, indent));
|
|
@@ -14943,6 +14952,103 @@ function spaces(count) {
|
|
|
14943
14952
|
function isObject2(value) {
|
|
14944
14953
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
14945
14954
|
}
|
|
14955
|
+
|
|
14956
|
+
// ../codemode/src/images.ts
|
|
14957
|
+
var import_promises = require("node:fs/promises");
|
|
14958
|
+
var import_node_os = require("node:os");
|
|
14959
|
+
var import_node_path = require("node:path");
|
|
14960
|
+
var MAX_IMAGE_BASE64_CHARS = 16 * 1024 * 1024;
|
|
14961
|
+
var TEMP_IMAGE_PREFIX = "opengeni-codemode-image-";
|
|
14962
|
+
var TEMP_IMAGE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
14963
|
+
var TEMP_IMAGE_PRUNE_INTERVAL_MS = 60 * 60 * 1000;
|
|
14964
|
+
var lastPruneAt = 0;
|
|
14965
|
+
async function materializeCodemodeImages(result, options = {}) {
|
|
14966
|
+
const blocks = result.content.filter((content) => content.type === "image");
|
|
14967
|
+
if (options.requireOne && blocks.length !== 1) {
|
|
14968
|
+
throw new Error("Codemode browser screenshot must return exactly one image block");
|
|
14969
|
+
}
|
|
14970
|
+
if (blocks.length === 0)
|
|
14971
|
+
return [];
|
|
14972
|
+
if (options.saveTo !== undefined && blocks.length !== 1) {
|
|
14973
|
+
throw new Error("An explicit image path requires exactly one image block");
|
|
14974
|
+
}
|
|
14975
|
+
let totalBase64Chars = 0;
|
|
14976
|
+
const decoded = blocks.map((block) => {
|
|
14977
|
+
const mimeType = supportedMimeType(block.mimeType);
|
|
14978
|
+
totalBase64Chars += block.data.length;
|
|
14979
|
+
if (block.data.length === 0 || block.data.length > MAX_IMAGE_BASE64_CHARS || totalBase64Chars > MAX_IMAGE_BASE64_CHARS) {
|
|
14980
|
+
throw new Error("Codemode image exceeds the Code Mode result limit");
|
|
14981
|
+
}
|
|
14982
|
+
const bytes = Buffer.from(block.data, "base64");
|
|
14983
|
+
if (bytes.length === 0 || bytes.toString("base64") !== block.data || !matchesImageType(bytes, mimeType)) {
|
|
14984
|
+
throw new Error("Codemode image bytes are invalid");
|
|
14985
|
+
}
|
|
14986
|
+
return { bytes, mimeType };
|
|
14987
|
+
});
|
|
14988
|
+
let directory;
|
|
14989
|
+
if (options.saveTo === undefined) {
|
|
14990
|
+
await pruneExpiredPrivateImages();
|
|
14991
|
+
directory = await import_promises.mkdtemp(import_node_path.join(import_node_os.tmpdir(), TEMP_IMAGE_PREFIX));
|
|
14992
|
+
} else {
|
|
14993
|
+
if (options.saveTo.length === 0)
|
|
14994
|
+
throw new TypeError("Image saveTo must be a nonempty path");
|
|
14995
|
+
await import_promises.mkdir(import_node_path.dirname(import_node_path.resolve(options.saveTo)), { recursive: true });
|
|
14996
|
+
}
|
|
14997
|
+
const images = [];
|
|
14998
|
+
try {
|
|
14999
|
+
for (const [index, image] of decoded.entries()) {
|
|
15000
|
+
const extension = image.mimeType === "image/jpeg" ? "jpg" : image.mimeType.split("/")[1];
|
|
15001
|
+
const path = options.saveTo === undefined ? import_node_path.join(directory, decoded.length === 1 ? `frame.${extension}` : `image-${index + 1}.${extension}`) : import_node_path.resolve(options.saveTo);
|
|
15002
|
+
await import_promises.writeFile(path, image.bytes, { flag: "wx", mode: 384 });
|
|
15003
|
+
images.push({ path, mimeType: image.mimeType, sizeBytes: image.bytes.length });
|
|
15004
|
+
}
|
|
15005
|
+
} catch (error51) {
|
|
15006
|
+
if (directory)
|
|
15007
|
+
await import_promises.rm(directory, { recursive: true, force: true });
|
|
15008
|
+
throw error51;
|
|
15009
|
+
}
|
|
15010
|
+
return images;
|
|
15011
|
+
}
|
|
15012
|
+
async function pruneExpiredPrivateImages() {
|
|
15013
|
+
const now = Date.now();
|
|
15014
|
+
if (now - lastPruneAt < TEMP_IMAGE_PRUNE_INTERVAL_MS)
|
|
15015
|
+
return;
|
|
15016
|
+
lastPruneAt = now;
|
|
15017
|
+
if (typeof process.getuid !== "function")
|
|
15018
|
+
return;
|
|
15019
|
+
try {
|
|
15020
|
+
let removed = 0;
|
|
15021
|
+
for (const entry of await import_promises.readdir(import_node_os.tmpdir(), { withFileTypes: true })) {
|
|
15022
|
+
if (!entry.isDirectory() || !/^opengeni-codemode-image-[A-Za-z0-9]{6}$/u.test(entry.name)) {
|
|
15023
|
+
continue;
|
|
15024
|
+
}
|
|
15025
|
+
const path = import_node_path.join(import_node_os.tmpdir(), entry.name);
|
|
15026
|
+
const info = await import_promises.lstat(path);
|
|
15027
|
+
if (!info.isDirectory() || info.uid !== process.getuid() || (info.mode & 63) !== 0 || now - info.mtimeMs < TEMP_IMAGE_MAX_AGE_MS) {
|
|
15028
|
+
continue;
|
|
15029
|
+
}
|
|
15030
|
+
await import_promises.rm(path, { recursive: true, force: true });
|
|
15031
|
+
if (++removed >= 64)
|
|
15032
|
+
break;
|
|
15033
|
+
}
|
|
15034
|
+
} catch {}
|
|
15035
|
+
}
|
|
15036
|
+
function supportedMimeType(mimeType) {
|
|
15037
|
+
if (mimeType === "image/png" || mimeType === "image/jpeg" || mimeType === "image/webp") {
|
|
15038
|
+
return mimeType;
|
|
15039
|
+
}
|
|
15040
|
+
throw new Error(`Codemode image has unsupported type: ${mimeType}`);
|
|
15041
|
+
}
|
|
15042
|
+
function matchesImageType(bytes, mimeType) {
|
|
15043
|
+
switch (mimeType) {
|
|
15044
|
+
case "image/png":
|
|
15045
|
+
return bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from("89504e470d0a1a0a", "hex"));
|
|
15046
|
+
case "image/jpeg":
|
|
15047
|
+
return bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
|
|
15048
|
+
case "image/webp":
|
|
15049
|
+
return bytes.length >= 12 && bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP";
|
|
15050
|
+
}
|
|
15051
|
+
}
|
|
14946
15052
|
// ../codemode/src/declarations.ts
|
|
14947
15053
|
function generateCodemodeDeclarations(catalog, options = {}) {
|
|
14948
15054
|
const verified = parseVerifiedAttemptToolCatalog(catalog);
|
|
@@ -14951,6 +15057,7 @@ function generateCodemodeDeclarations(catalog, options = {}) {
|
|
|
14951
15057
|
interfaceName: "CodemodeGeneratedTools",
|
|
14952
15058
|
callOptionsType: "CodemodeCallOptions",
|
|
14953
15059
|
fallbackResultType: "CodemodeToolResult",
|
|
15060
|
+
typedResultWrapperType: "CodemodeMultimodalValue",
|
|
14954
15061
|
generatedBy: "@opengeni/codemode via @opengeni/tool-gateway",
|
|
14955
15062
|
catalogDigestLabel: "Attempt catalog digest"
|
|
14956
15063
|
});
|
|
@@ -15132,7 +15239,15 @@ class CodemodeClient {
|
|
|
15132
15239
|
if (!result.structuredContent) {
|
|
15133
15240
|
throw new CodemodeToolContractError(`Codemode tool ${path.join(".")} declared outputSchema but returned no structured content`);
|
|
15134
15241
|
}
|
|
15135
|
-
|
|
15242
|
+
if (!result.content.some((content) => content.type === "image")) {
|
|
15243
|
+
return result.structuredContent;
|
|
15244
|
+
}
|
|
15245
|
+
const images2 = await materializeCodemodeImages(result);
|
|
15246
|
+
return {
|
|
15247
|
+
structuredContent: result.structuredContent,
|
|
15248
|
+
images: images2,
|
|
15249
|
+
otherContent: result.content.filter((content) => content.type !== "image")
|
|
15250
|
+
};
|
|
15136
15251
|
}
|
|
15137
15252
|
async submit(operationId, catalogDigest, identity, argumentsValue, signal) {
|
|
15138
15253
|
const response = await this.request("/calls", {
|
|
@@ -15285,14 +15400,14 @@ function parseCodemodeApiError(input) {
|
|
|
15285
15400
|
}
|
|
15286
15401
|
async function abortableDelay(delayMs, signal) {
|
|
15287
15402
|
if (!signal) {
|
|
15288
|
-
await new Promise((
|
|
15403
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
15289
15404
|
return;
|
|
15290
15405
|
}
|
|
15291
15406
|
throwIfAborted(signal);
|
|
15292
|
-
await new Promise((
|
|
15407
|
+
await new Promise((resolve2, reject) => {
|
|
15293
15408
|
const timer = setTimeout(() => {
|
|
15294
15409
|
signal.removeEventListener("abort", onAbort);
|
|
15295
|
-
|
|
15410
|
+
resolve2();
|
|
15296
15411
|
}, delayMs);
|
|
15297
15412
|
const onAbort = () => {
|
|
15298
15413
|
clearTimeout(timer);
|
|
@@ -15304,7 +15419,7 @@ async function abortableDelay(delayMs, signal) {
|
|
|
15304
15419
|
// package.json
|
|
15305
15420
|
var package_default = {
|
|
15306
15421
|
name: "@opengeni/ogtool",
|
|
15307
|
-
version: "0.3.
|
|
15422
|
+
version: "0.3.44-canary.36199476632001",
|
|
15308
15423
|
description: "Codemode CLI and typed client entrypoint for an OpenGeni execution attempt.",
|
|
15309
15424
|
license: "Apache-2.0",
|
|
15310
15425
|
repository: {
|
|
@@ -15552,14 +15667,10 @@ function listProjection(catalog) {
|
|
|
15552
15667
|
}))
|
|
15553
15668
|
};
|
|
15554
15669
|
}
|
|
15555
|
-
var SHOW_MAX_BYTES = 64 * 1024;
|
|
15556
15670
|
function showOutput(catalog, name) {
|
|
15557
15671
|
const entry = resolveTool(catalog, name);
|
|
15558
15672
|
const projection = listProjection({ ...catalog, entries: [entry] });
|
|
15559
15673
|
const output = JSON.stringify(projection.tools[0], null, 2);
|
|
15560
|
-
if (Buffer.byteLength(output, "utf8") + 1 > SHOW_MAX_BYTES) {
|
|
15561
|
-
throw new Error("Tool details exceed 65536 bytes; use list --full or declarations <output-file>");
|
|
15562
|
-
}
|
|
15563
15674
|
return `${output}
|
|
15564
15675
|
`;
|
|
15565
15676
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/ogtool",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.44-canary.36199476632001",
|
|
4
4
|
"description": "Codemode CLI and typed client entrypoint for an OpenGeni execution attempt.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@opengeni/codemode": "^0.6.
|
|
40
|
+
"@opengeni/codemode": "^0.6.2-canary.36199476632001"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18"
|
package/src/cli.ts
CHANGED
|
@@ -153,17 +153,10 @@ export function listProjection(catalog: AttemptToolCatalog): Record<string, unkn
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
const SHOW_MAX_BYTES = 64 * 1024;
|
|
157
|
-
|
|
158
156
|
export function showOutput(catalog: AttemptToolCatalog, name: string): string {
|
|
159
157
|
const entry = resolveTool(catalog, name);
|
|
160
158
|
const projection = listProjection({ ...catalog, entries: [entry] });
|
|
161
159
|
const output = JSON.stringify((projection.tools as unknown[])[0], null, 2);
|
|
162
|
-
if (Buffer.byteLength(output, "utf8") + 1 > SHOW_MAX_BYTES) {
|
|
163
|
-
throw new Error(
|
|
164
|
-
"Tool details exceed 65536 bytes; use list --full or declarations <output-file>",
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
160
|
return `${output}\n`;
|
|
168
161
|
}
|
|
169
162
|
|