@opengeni/ogtool 1.3.0 → 1.4.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 CHANGED
@@ -44,7 +44,7 @@ Commands:
44
44
  - `ogtool list [--json] [--query <substring>] [--limit <1..100>] [--offset <integer>]`
45
45
  - `ogtool list --full` — the previous full catalog JSON, including identity and schemas
46
46
  - `ogtool show <tool-path-or-model-name>` — one tool's details and schemas as JSON
47
- - `ogtool call <tool-path-or-model-name> [json-object]`
47
+ - `ogtool call <tool-path-or-model-name> [json-object] [--full]` — the tool result as JSON. A text block that only repeats `structuredContent` as JSON is omitted, so the payload prints once; prose, differing text, annotated text, and non-text blocks stay. `--full` prints the exact result.
48
48
  - `ogtool declarations [output-file]`
49
49
  - `ogtool doctor`
50
50
  - `ogtool --version`
@@ -40,6 +40,7 @@ var __export = (target, all) => {
40
40
  // src/cli.ts
41
41
  var exports_cli = {};
42
42
  __export(exports_cli, {
43
+ callOutput: () => callOutput,
43
44
  listProjection: () => listProjection,
44
45
  parseToolArguments: () => parseToolArguments,
45
46
  resolveTool: () => resolveTool,
@@ -14676,6 +14677,65 @@ var CodemodeDispatchAck = exports_external.object({
14676
14677
  status: exports_external.enum(["accepted", "already_running", "terminal", "unavailable", "rejected"])
14677
14678
  }).strict();
14678
14679
 
14680
+ // ../contracts/src/mcp-structured-content.ts
14681
+ function omitStructuredContentTextDuplicates(result) {
14682
+ const structured = result.structuredContent;
14683
+ if (structured === undefined || structured === null)
14684
+ return result;
14685
+ const content = result.content.filter((entry) => !isPlainTextBlock(entry) || !textDuplicatesStructuredContent(entry.text, structured));
14686
+ if (content.length === result.content.length)
14687
+ return result;
14688
+ return { ...result, content };
14689
+ }
14690
+ function isPlainTextBlock(entry) {
14691
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
14692
+ return false;
14693
+ const record2 = entry;
14694
+ if (record2.type !== "text" || typeof record2.text !== "string")
14695
+ return false;
14696
+ return Object.keys(record2).every((key) => key === "type" || key === "text");
14697
+ }
14698
+ function textDuplicatesStructuredContent(text, structured) {
14699
+ const trimmed = text.replace(/^[ \t\n\r]+/u, "");
14700
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("["))
14701
+ return false;
14702
+ let parsed;
14703
+ try {
14704
+ parsed = JSON.parse(text);
14705
+ } catch {
14706
+ return false;
14707
+ }
14708
+ return !containsUnsafeInteger(parsed) && jsonValuesEqual(parsed, structured);
14709
+ }
14710
+ function containsUnsafeInteger(value) {
14711
+ if (typeof value === "number")
14712
+ return Number.isInteger(value) && !Number.isSafeInteger(value);
14713
+ if (Array.isArray(value))
14714
+ return value.some(containsUnsafeInteger);
14715
+ if (value && typeof value === "object")
14716
+ return Object.values(value).some(containsUnsafeInteger);
14717
+ return false;
14718
+ }
14719
+ function jsonValuesEqual(left, right) {
14720
+ if (left === right)
14721
+ return true;
14722
+ if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) {
14723
+ return false;
14724
+ }
14725
+ if (Array.isArray(left) || Array.isArray(right)) {
14726
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
14727
+ return false;
14728
+ }
14729
+ return left.every((entry, index) => jsonValuesEqual(entry, right[index]));
14730
+ }
14731
+ const leftRecord = left;
14732
+ const rightRecord = right;
14733
+ const leftKeys = Object.keys(leftRecord);
14734
+ if (leftKeys.length !== Object.keys(rightRecord).length)
14735
+ return false;
14736
+ return leftKeys.every((key) => Object.hasOwn(rightRecord, key) && jsonValuesEqual(leftRecord[key], rightRecord[key]));
14737
+ }
14738
+
14679
14739
  // ../tool-gateway/src/catalog.ts
14680
14740
  var import_node_crypto = require("node:crypto");
14681
14741
 
@@ -15469,7 +15529,7 @@ async function abortableDelay(delayMs, signal) {
15469
15529
  // package.json
15470
15530
  var package_default = {
15471
15531
  name: "@opengeni/ogtool",
15472
- version: "1.3.0",
15532
+ version: "1.4.0",
15473
15533
  description: "Codemode CLI and typed client entrypoint for an OpenGeni execution attempt.",
15474
15534
  license: "Apache-2.0",
15475
15535
  repository: {
@@ -15506,7 +15566,8 @@ var package_default = {
15506
15566
  prepublishOnly: "bash ../../scripts/prepublish-guard"
15507
15567
  },
15508
15568
  dependencies: {
15509
- "@opengeni/codemode": "workspace:*"
15569
+ "@opengeni/codemode": "workspace:*",
15570
+ "@opengeni/contracts": "workspace:*"
15510
15571
  },
15511
15572
  devDependencies: {
15512
15573
  tsup: "^8.5.0",
@@ -15598,7 +15659,7 @@ function usage(exitCode = 1) {
15598
15659
  " ogtool list [--full | --json]",
15599
15660
  " [--query <substring>] [--limit <1..100>] [--offset <nonnegative integer>]",
15600
15661
  " ogtool show <tool-path-or-model-name>",
15601
- " ogtool call <tool-path-or-model-name> [json-object]",
15662
+ " ogtool call <tool-path-or-model-name> [json-object] [--full]",
15602
15663
  " ogtool read <operation-id>",
15603
15664
  " ogtool resume <operation-id>",
15604
15665
  " ogtool declarations [output-file]",
@@ -15606,6 +15667,7 @@ function usage(exitCode = 1) {
15606
15667
  " ogtool --version",
15607
15668
  "",
15608
15669
  "list returns all authorized tools by default; --limit/--offset are opt-in slices",
15670
+ "call omits a text block that only repeats structuredContent; --full prints the exact result",
15609
15671
  "",
15610
15672
  "requires OPENGENI_CODEMODE_URL and OPENGENI_CODEMODE_TOKEN or OPENGENI_CODEMODE_TOKEN_FILE"
15611
15673
  ].join(`
@@ -15726,6 +15788,11 @@ function showOutput(catalog, name) {
15726
15788
  return `${output}
15727
15789
  `;
15728
15790
  }
15791
+ function callOutput(result, full = false) {
15792
+ const printed = full ? result : omitStructuredContentTextDuplicates(result);
15793
+ return `${JSON.stringify(printed, null, 2)}
15794
+ `;
15795
+ }
15729
15796
  function parseToolArguments(raw) {
15730
15797
  if (raw === undefined)
15731
15798
  return {};
@@ -15784,17 +15851,17 @@ async function main() {
15784
15851
  return;
15785
15852
  }
15786
15853
  if (command === "call") {
15787
- const name = process.argv[3];
15854
+ const full = args.includes("--full");
15855
+ const [name, rawArguments] = args.filter((arg) => arg !== "--full");
15788
15856
  if (!name)
15789
15857
  throw new Error("tool path or model name is required");
15790
- const argumentsValue = parseToolArguments(process.argv[4]);
15858
+ const argumentsValue = parseToolArguments(rawArguments);
15791
15859
  const catalog = await client.catalog();
15792
15860
  const entry = resolveTool(catalog, name);
15793
15861
  const result = await client.call(entry.identity, argumentsValue, {
15794
15862
  operationId: import_node_crypto3.randomUUID()
15795
15863
  });
15796
- process.stdout.write(`${JSON.stringify(result, null, 2)}
15797
- `);
15864
+ process.stdout.write(callOutput(result, full));
15798
15865
  return;
15799
15866
  }
15800
15867
  if (command === "declarations") {
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import { type AttemptToolCatalog, type AttemptToolCatalogEntry } from "@opengeni/codemode";
2
+ import { type AttemptToolResult } from "@opengeni/contracts";
2
3
  export declare function resolveTool(catalog: AttemptToolCatalog, name: string): AttemptToolCatalogEntry;
3
4
  export declare function listProjection(catalog: AttemptToolCatalog): Record<string, unknown>;
4
5
  export declare function showOutput(catalog: AttemptToolCatalog, name: string): string;
6
+ /**
7
+ * `call` prints the payload once by default: a text block that only repeats
8
+ * `structuredContent` as JSON is omitted. `--full` prints the exact result.
9
+ * The native client (`codemode call`) applies the same rule.
10
+ */
11
+ export declare function callOutput(result: AttemptToolResult, full?: boolean): string;
5
12
  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": "1.3.0",
3
+ "version": "1.4.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,8 @@
37
37
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
38
38
  },
39
39
  "dependencies": {
40
- "@opengeni/codemode": "1.3.0"
40
+ "@opengeni/codemode": "1.4.0",
41
+ "@opengeni/contracts": "1.4.0"
41
42
  },
42
43
  "engines": {
43
44
  "node": ">=18"
package/src/cli.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  type AttemptToolCatalog,
9
9
  type AttemptToolCatalogEntry,
10
10
  } from "@opengeni/codemode";
11
+ import { omitStructuredContentTextDuplicates, type AttemptToolResult } from "@opengeni/contracts";
11
12
  import packageManifest from "../package.json" with { type: "json" };
12
13
  import { compactOutput, parseListOptions } from "./catalog-discovery";
13
14
 
@@ -20,7 +21,7 @@ function usage(exitCode = 1): void {
20
21
  " ogtool list [--full | --json]",
21
22
  " [--query <substring>] [--limit <1..100>] [--offset <nonnegative integer>]",
22
23
  " ogtool show <tool-path-or-model-name>",
23
- " ogtool call <tool-path-or-model-name> [json-object]",
24
+ " ogtool call <tool-path-or-model-name> [json-object] [--full]",
24
25
  " ogtool read <operation-id>",
25
26
  " ogtool resume <operation-id>",
26
27
  " ogtool declarations [output-file]",
@@ -28,6 +29,7 @@ function usage(exitCode = 1): void {
28
29
  " ogtool --version",
29
30
  "",
30
31
  "list returns all authorized tools by default; --limit/--offset are opt-in slices",
32
+ "call omits a text block that only repeats structuredContent; --full prints the exact result",
31
33
  "",
32
34
  "requires OPENGENI_CODEMODE_URL and OPENGENI_CODEMODE_TOKEN or OPENGENI_CODEMODE_TOKEN_FILE",
33
35
  ].join("\n");
@@ -164,6 +166,16 @@ export function showOutput(catalog: AttemptToolCatalog, name: string): string {
164
166
  return `${output}\n`;
165
167
  }
166
168
 
169
+ /**
170
+ * `call` prints the payload once by default: a text block that only repeats
171
+ * `structuredContent` as JSON is omitted. `--full` prints the exact result.
172
+ * The native client (`codemode call`) applies the same rule.
173
+ */
174
+ export function callOutput(result: AttemptToolResult, full = false): string {
175
+ const printed = full ? result : omitStructuredContentTextDuplicates(result);
176
+ return `${JSON.stringify(printed, null, 2)}\n`;
177
+ }
178
+
167
179
  export function parseToolArguments(raw: string | undefined): Record<string, unknown> {
168
180
  if (raw === undefined) return {};
169
181
  let value: unknown;
@@ -223,15 +235,16 @@ async function main(): Promise<void> {
223
235
  return;
224
236
  }
225
237
  if (command === "call") {
226
- const name = process.argv[3];
238
+ const full = args.includes("--full");
239
+ const [name, rawArguments] = args.filter((arg) => arg !== "--full");
227
240
  if (!name) throw new Error("tool path or model name is required");
228
- const argumentsValue = parseToolArguments(process.argv[4]);
241
+ const argumentsValue = parseToolArguments(rawArguments);
229
242
  const catalog = await client.catalog();
230
243
  const entry = resolveTool(catalog, name);
231
244
  const result = await client.call(entry.identity, argumentsValue, {
232
245
  operationId: randomUUID(),
233
246
  });
234
- process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
247
+ process.stdout.write(callOutput(result, full));
235
248
  return;
236
249
  }
237
250
  if (command === "declarations") {