@opengeni/ogtool 0.3.26 → 0.3.31-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 +47 -2
- package/dist/bin/ogtool.cjs +654 -9274
- package/dist/catalog-discovery.d.ts +12 -0
- package/dist/cli.d.ts +1 -0
- package/package.json +2 -2
- package/src/catalog-discovery.ts +111 -0
- package/src/cli.ts +40 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AttemptToolCatalog, AttemptToolCatalogEntry } from "@opengeni/codemode";
|
|
2
|
+
export declare function shortDescription(entry: AttemptToolCatalogEntry): string;
|
|
3
|
+
export type ListOptions = {
|
|
4
|
+
full: boolean;
|
|
5
|
+
json: boolean;
|
|
6
|
+
query: string;
|
|
7
|
+
limit: number;
|
|
8
|
+
offset: number;
|
|
9
|
+
};
|
|
10
|
+
export declare function parseListOptions(args: string[]): ListOptions;
|
|
11
|
+
/** Render first, then emit: the bound includes metadata, escaping, hints, and newline. */
|
|
12
|
+
export declare function compactOutput(catalog: AttemptToolCatalog, options: ListOptions): string;
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AttemptToolCatalog, type AttemptToolCatalogEntry } from "@opengeni/codemode";
|
|
2
2
|
export declare function resolveTool(catalog: AttemptToolCatalog, name: string): AttemptToolCatalogEntry;
|
|
3
3
|
export declare function listProjection(catalog: AttemptToolCatalog): Record<string, unknown>;
|
|
4
|
+
export declare function showOutput(catalog: AttemptToolCatalog, name: string): string;
|
|
4
5
|
export declare function parseToolArguments(raw: string | undefined): Record<string, unknown>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/ogtool",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.31-canary.0",
|
|
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.4.
|
|
40
|
+
"@opengeni/codemode": "^0.4.27-canary.0"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { AttemptToolCatalog, AttemptToolCatalogEntry } from "@opengeni/codemode";
|
|
2
|
+
|
|
3
|
+
const DESCRIPTION_MAX_CHARS = 160;
|
|
4
|
+
const LIST_MAX_BYTES = 16 * 1024;
|
|
5
|
+
|
|
6
|
+
function normalizedDescription(entry: AttemptToolCatalogEntry): string {
|
|
7
|
+
return (entry.description || entry.title || "")
|
|
8
|
+
.split(/\p{White_Space}+/u)
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.join(" ");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function shortDescription(entry: AttemptToolCatalogEntry): string {
|
|
14
|
+
const text = normalizedDescription(entry);
|
|
15
|
+
const characters = Array.from(text);
|
|
16
|
+
return characters.length <= DESCRIPTION_MAX_CHARS
|
|
17
|
+
? text
|
|
18
|
+
: `${characters.slice(0, DESCRIPTION_MAX_CHARS - 1).join("")}…`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Terminal-only presentation: never mutate catalog, query, or JSON content. */
|
|
22
|
+
function terminalDescription(text: string): string {
|
|
23
|
+
return text.replace(
|
|
24
|
+
/[\u0000-\u001f\u007f-\u009f]/gu,
|
|
25
|
+
(character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type ListOptions = {
|
|
30
|
+
full: boolean;
|
|
31
|
+
json: boolean;
|
|
32
|
+
query: string;
|
|
33
|
+
limit: number;
|
|
34
|
+
offset: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function parseListOptions(args: string[]): ListOptions {
|
|
38
|
+
const options: ListOptions = { full: false, json: false, query: "", limit: 50, offset: 0 };
|
|
39
|
+
const seen = new Set<string>();
|
|
40
|
+
const invalid = () =>
|
|
41
|
+
new Error(
|
|
42
|
+
"usage: ogtool list [--full | --json] [--query <substring>] [--limit <1..100>] [--offset <nonnegative integer>]",
|
|
43
|
+
);
|
|
44
|
+
for (let index = 0; index < args.length; index++) {
|
|
45
|
+
const argument = args[index]!;
|
|
46
|
+
const equals = argument.indexOf("=");
|
|
47
|
+
const flag = equals < 0 ? argument : argument.slice(0, equals);
|
|
48
|
+
if (seen.has(flag)) throw invalid();
|
|
49
|
+
seen.add(flag);
|
|
50
|
+
if (flag === "--full" || flag === "--json") {
|
|
51
|
+
if (equals >= 0) throw invalid();
|
|
52
|
+
options[flag === "--full" ? "full" : "json"] = true;
|
|
53
|
+
} else if (flag === "--query" || flag === "--limit" || flag === "--offset") {
|
|
54
|
+
const value = equals < 0 ? args[++index] : argument.slice(equals + 1);
|
|
55
|
+
if (value === undefined || (equals < 0 && value.startsWith("-"))) throw invalid();
|
|
56
|
+
if (flag === "--query") options.query = value;
|
|
57
|
+
else {
|
|
58
|
+
if (!/^[0-9]+$/u.test(value) || !Number.isSafeInteger(Number(value))) throw invalid();
|
|
59
|
+
const number = Number(value);
|
|
60
|
+
if (flag === "--limit" && (number < 1 || number > 100)) throw invalid();
|
|
61
|
+
options[flag === "--limit" ? "limit" : "offset"] = number;
|
|
62
|
+
}
|
|
63
|
+
} else throw invalid();
|
|
64
|
+
}
|
|
65
|
+
if (options.full && seen.size !== 1) throw invalid();
|
|
66
|
+
return options;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Render first, then emit: the bound includes metadata, escaping, hints, and newline. */
|
|
70
|
+
export function compactOutput(catalog: AttemptToolCatalog, options: ListOptions): string {
|
|
71
|
+
const matches = catalog.entries.filter(
|
|
72
|
+
(entry) =>
|
|
73
|
+
entry.codemodePath.join(".").includes(options.query) ||
|
|
74
|
+
normalizedDescription(entry).includes(options.query),
|
|
75
|
+
);
|
|
76
|
+
const tools = matches.slice(options.offset, options.offset + options.limit).map((entry) => ({
|
|
77
|
+
path: entry.codemodePath.join("."),
|
|
78
|
+
description: shortDescription(entry),
|
|
79
|
+
}));
|
|
80
|
+
const textLines = options.json
|
|
81
|
+
? []
|
|
82
|
+
: tools.map(
|
|
83
|
+
(tool) =>
|
|
84
|
+
`${tool.path}${tool.description ? ` — ${terminalDescription(tool.description)}` : ""}\n`,
|
|
85
|
+
);
|
|
86
|
+
for (;;) {
|
|
87
|
+
const nextOffset =
|
|
88
|
+
options.offset + tools.length < matches.length ? options.offset + tools.length : null;
|
|
89
|
+
const page = {
|
|
90
|
+
catalogDigest: catalog.digest,
|
|
91
|
+
total: matches.length,
|
|
92
|
+
offset: options.offset,
|
|
93
|
+
nextOffset,
|
|
94
|
+
tools,
|
|
95
|
+
};
|
|
96
|
+
const output = options.json
|
|
97
|
+
? `${JSON.stringify(page)}\n`
|
|
98
|
+
: textLines.slice(0, tools.length).join("") +
|
|
99
|
+
`# total: ${page.total}; offset: ${page.offset}; nextOffset: ${nextOffset ?? "none"}\n` +
|
|
100
|
+
(nextOffset === null
|
|
101
|
+
? ""
|
|
102
|
+
: `# Continue with --offset ${nextOffset} (keep the same --query and --limit).\n`);
|
|
103
|
+
if (Buffer.byteLength(output, "utf8") <= LIST_MAX_BYTES) return output;
|
|
104
|
+
if (tools.length <= 1) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"One tool exceeds the 16384-byte compact page limit; use list --full redirected to a file",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
tools.pop();
|
|
110
|
+
}
|
|
111
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type AttemptToolCatalogEntry,
|
|
8
8
|
} from "@opengeni/codemode";
|
|
9
9
|
import packageManifest from "../package.json" with { type: "json" };
|
|
10
|
+
import { compactOutput, parseListOptions } from "./catalog-discovery";
|
|
10
11
|
|
|
11
12
|
const PACKAGE_NAME = "@opengeni/ogtool";
|
|
12
13
|
const VERSION = packageManifest.version;
|
|
@@ -14,7 +15,9 @@ const VERSION = packageManifest.version;
|
|
|
14
15
|
function usage(exitCode = 1): void {
|
|
15
16
|
const output = [
|
|
16
17
|
"usage:",
|
|
17
|
-
" ogtool list",
|
|
18
|
+
" ogtool list [--full | --json]",
|
|
19
|
+
" [--query <substring>] [--limit <1..100>] [--offset <nonnegative integer>]",
|
|
20
|
+
" ogtool show <tool-path-or-model-name>",
|
|
18
21
|
" ogtool call <tool-path-or-model-name> [json-object]",
|
|
19
22
|
" ogtool declarations [output-file]",
|
|
20
23
|
" ogtool doctor",
|
|
@@ -148,6 +151,20 @@ export function listProjection(catalog: AttemptToolCatalog): Record<string, unkn
|
|
|
148
151
|
};
|
|
149
152
|
}
|
|
150
153
|
|
|
154
|
+
const SHOW_MAX_BYTES = 64 * 1024;
|
|
155
|
+
|
|
156
|
+
export function showOutput(catalog: AttemptToolCatalog, name: string): string {
|
|
157
|
+
const entry = resolveTool(catalog, name);
|
|
158
|
+
const projection = listProjection({ ...catalog, entries: [entry] });
|
|
159
|
+
const output = JSON.stringify((projection.tools as unknown[])[0], null, 2);
|
|
160
|
+
if (Buffer.byteLength(output, "utf8") + 1 > SHOW_MAX_BYTES) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
"Tool details exceed 65536 bytes; use list --full or declarations <output-file>",
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return `${output}\n`;
|
|
166
|
+
}
|
|
167
|
+
|
|
151
168
|
export function parseToolArguments(raw: string | undefined): Record<string, unknown> {
|
|
152
169
|
if (raw === undefined) return {};
|
|
153
170
|
let value: unknown;
|
|
@@ -172,9 +189,30 @@ async function main(): Promise<void> {
|
|
|
172
189
|
if (command === "doctor") return doctor();
|
|
173
190
|
if (!command) return usage();
|
|
174
191
|
|
|
192
|
+
const args = process.argv.slice(3);
|
|
193
|
+
if (
|
|
194
|
+
(command === "list" || command === "show") &&
|
|
195
|
+
args.length === 1 &&
|
|
196
|
+
(args[0] === "--help" || args[0] === "-h")
|
|
197
|
+
)
|
|
198
|
+
return usage(0);
|
|
199
|
+
const listOptions = command === "list" ? parseListOptions(args) : null;
|
|
200
|
+
if (command === "show" && (args.length !== 1 || !args[0] || args[0].startsWith("-"))) {
|
|
201
|
+
throw new Error("usage: ogtool show <tool-path-or-model-name>");
|
|
202
|
+
}
|
|
203
|
+
|
|
175
204
|
const client = configuredClient();
|
|
176
205
|
if (command === "list") {
|
|
177
|
-
|
|
206
|
+
const catalog = await client.catalog();
|
|
207
|
+
if (listOptions!.full) {
|
|
208
|
+
process.stdout.write(`${JSON.stringify(listProjection(catalog), null, 2)}\n`);
|
|
209
|
+
} else {
|
|
210
|
+
process.stdout.write(compactOutput(catalog, listOptions!));
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (command === "show") {
|
|
215
|
+
process.stdout.write(showOutput(await client.catalog(), args[0]!));
|
|
178
216
|
return;
|
|
179
217
|
}
|
|
180
218
|
if (command === "call") {
|