@topolo/cli 0.8.3 → 0.8.5

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.
Files changed (2) hide show
  1. package/dist/index.js +108 -8
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { platform as platform2 } from "os";
9
9
  import { createInterface } from "readline/promises";
10
10
  import { stdin as input } from "process";
11
11
 
12
- // node_modules/@topolo/sdk/dist/index.js
12
+ // ../../node_modules/.pnpm/@topolo+sdk@0.11.3/node_modules/@topolo/sdk/dist/index.js
13
13
  function applyAuthHeaders(headers, credential) {
14
14
  if (credential.kind === "api_key") {
15
15
  headers.set("X-Api-Key", credential.apiKey);
@@ -764,6 +764,18 @@ function prepareTopoloUrlSessionUploadInput(contractValue, byteLength, input2, o
764
764
  function topoloUploadResponseValue(response, path) {
765
765
  return path.split(".").reduce((value, segment) => isRecord2(value) ? value[segment] : void 0, response);
766
766
  }
767
+ function redactTopoloUrlSessionResult(response, uploadUrlPath) {
768
+ const redacted = cloneUploadValue(response);
769
+ let target = redacted;
770
+ const segments = uploadUrlPath.split(".");
771
+ for (const segment of segments.slice(0, -1)) {
772
+ target = isRecord2(target) ? target[segment] : void 0;
773
+ }
774
+ const finalSegment = segments.at(-1);
775
+ if (finalSegment && isRecord2(target) && finalSegment in target) target[finalSegment] = "[REDACTED]";
776
+ redactUploadHeaders(redacted);
777
+ return redacted;
778
+ }
767
779
  function decodeTopoloActionBase64(contentBase64) {
768
780
  if (!isCanonicalBase64(contentBase64)) {
769
781
  throw new TopoloSdkError("invalid_upload_content", "Upload content must be canonical base64.");
@@ -782,6 +794,28 @@ function mapUploadInput(contract, contentBase64, input2, options) {
782
794
  [contract.fileNameField]: fileName
783
795
  };
784
796
  }
797
+ function cloneUploadValue(value) {
798
+ if (Array.isArray(value)) return value.map(cloneUploadValue);
799
+ if (!isRecord2(value)) return value;
800
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneUploadValue(item)]));
801
+ }
802
+ function redactUploadHeaders(value) {
803
+ if (Array.isArray(value)) {
804
+ value.forEach(redactUploadHeaders);
805
+ return;
806
+ }
807
+ if (!isRecord2(value)) return;
808
+ for (const [key, item] of Object.entries(value)) {
809
+ if (key.toLowerCase() === "headers" && isRecord2(item)) {
810
+ for (const header of Object.keys(item)) {
811
+ if (["authorization", "cookie", "set-cookie", "proxy-authorization", "x-api-key"].includes(header.toLowerCase())) {
812
+ item[header] = "[REDACTED]";
813
+ }
814
+ }
815
+ }
816
+ redactUploadHeaders(item);
817
+ }
818
+ }
785
819
  function validateUploadMetadata(contract, byteLength, options) {
786
820
  const fileName = options.fileName.trim();
787
821
  const contentType = options.contentType.trim().toLowerCase();
@@ -1423,7 +1457,7 @@ var TopoloClient = class _TopoloClient {
1423
1457
  throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.uploadUrlPath}.`);
1424
1458
  }
1425
1459
  await this.putUrlSessionContent(entry, uploadUrl, contract.uploadMethod, bytes, options);
1426
- if (!contract.complete) return initiation;
1460
+ if (!contract.complete) return redactTopoloUrlSessionResult(initiation, contract.uploadUrlPath);
1427
1461
  const completionValue = topoloUploadResponseValue(initiation, contract.complete.responsePath);
1428
1462
  if (typeof completionValue !== "string" || !completionValue.trim()) {
1429
1463
  throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.complete.responsePath}.`);
@@ -1950,6 +1984,7 @@ function mergeSignals(a, b) {
1950
1984
  function compactActionCatalogEntry(action) {
1951
1985
  const required = Array.isArray(action.inputSchema["required"]) ? action.inputSchema["required"].filter((value) => typeof value === "string") : [];
1952
1986
  const examples = action.agentContract?.examples ?? [];
1987
+ const example = compactExample(examples[0]?.input);
1953
1988
  return {
1954
1989
  actionId: action.actionId,
1955
1990
  title: action.title,
@@ -1961,13 +1996,75 @@ function compactActionCatalogEntry(action) {
1961
1996
  requiredPermission: action.requiredPermission,
1962
1997
  requiredInput: required,
1963
1998
  resourceType: action.agentContract?.resource?.kind ?? null,
1964
- exampleInput: examples[0]?.input ?? null,
1999
+ exampleInput: example.value,
2000
+ exampleInputAvailable: example.available,
1965
2001
  docsUrl: action.agentContract?.docsUrl ?? null
1966
2002
  };
1967
2003
  }
2004
+ function compactExample(input2) {
2005
+ if (!input2) return { value: null, available: false };
2006
+ const serialized = JSON.stringify(input2);
2007
+ if (serialized.length > 512) return { value: null, available: true };
2008
+ return { value: input2, available: true };
2009
+ }
1968
2010
  function compactActionValidation(actionId, validation) {
1969
2011
  return { actionId, valid: validation.valid, errors: validation.errors };
1970
2012
  }
2013
+ var ACTION_RESULT_MAX_DEPTH = 4;
2014
+ var ACTION_RESULT_MAX_KEYS = 16;
2015
+ var ACTION_RESULT_MAX_ITEMS = 5;
2016
+ var ACTION_RESULT_MAX_SCALARS = 120;
2017
+ var ACTION_RESULT_MAX_STRING = 512;
2018
+ function resultKeyPriority(key) {
2019
+ if (/^(id|requestId|status|state|url|slug|name|title|version)$/i.test(key)) return 0;
2020
+ if (/(Id|Ids|Url|Uri|Status|State|Count|Total|Version|CreatedAt|UpdatedAt)$/i.test(key)) return 1;
2021
+ return 2;
2022
+ }
2023
+ function compactResultValue(value, depth, budget) {
2024
+ if (value === null || typeof value === "boolean" || typeof value === "number") {
2025
+ budget.scalars += 1;
2026
+ return value;
2027
+ }
2028
+ if (typeof value === "string") {
2029
+ budget.scalars += 1;
2030
+ if (value.length <= ACTION_RESULT_MAX_STRING) return value;
2031
+ return `${value.slice(0, ACTION_RESULT_MAX_STRING)}\u2026 (${value.length} chars)`;
2032
+ }
2033
+ if (depth >= ACTION_RESULT_MAX_DEPTH || budget.scalars >= ACTION_RESULT_MAX_SCALARS) {
2034
+ return Array.isArray(value) ? { count: value.length, omitted: true } : { omitted: true };
2035
+ }
2036
+ if (Array.isArray(value)) {
2037
+ const items = value.slice(0, ACTION_RESULT_MAX_ITEMS).map((item) => compactResultValue(item, depth + 1, budget));
2038
+ return value.length > ACTION_RESULT_MAX_ITEMS ? { count: value.length, items, omittedItems: value.length - ACTION_RESULT_MAX_ITEMS } : items;
2039
+ }
2040
+ if (typeof value === "object") {
2041
+ const record = value;
2042
+ const allKeys = Object.keys(record);
2043
+ const keys = allKeys.sort((left, right) => resultKeyPriority(left) - resultKeyPriority(right)).slice(0, ACTION_RESULT_MAX_KEYS);
2044
+ const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(record[key], depth + 1, budget)]));
2045
+ if (allKeys.length > keys.length) compact["_omittedKeys"] = allKeys.length - keys.length;
2046
+ return compact;
2047
+ }
2048
+ budget.scalars += 1;
2049
+ return String(value);
2050
+ }
2051
+ function compactActionResult(actionId, result) {
2052
+ let normalized = result;
2053
+ if (typeof result === "string") {
2054
+ try {
2055
+ normalized = JSON.parse(result);
2056
+ } catch {
2057
+ normalized = result;
2058
+ }
2059
+ }
2060
+ return { actionId, result: compactResultValue(normalized, 0, { scalars: 0 }) };
2061
+ }
2062
+ function preferExactActionMatches(actions, query) {
2063
+ const expected = query?.trim().toLowerCase();
2064
+ if (!expected) return actions;
2065
+ const exact = actions.filter((action) => [action.actionId, action.name, action.toolName].some((candidate) => candidate?.trim().toLowerCase() === expected));
2066
+ return exact.length ? exact : actions;
2067
+ }
1971
2068
  function compactActionPlan(plan) {
1972
2069
  return {
1973
2070
  actionId: plan.action.actionId,
@@ -2253,7 +2350,7 @@ function createTopolo(options) {
2253
2350
  };
2254
2351
  }
2255
2352
 
2256
- // node_modules/@topolo/sdk/dist/node/credential-store.js
2353
+ // ../../node_modules/.pnpm/@topolo+sdk@0.11.3/node_modules/@topolo/sdk/dist/node/credential-store.js
2257
2354
  import { mkdir, readFile, writeFile, chmod, rm, stat } from "fs/promises";
2258
2355
  import { existsSync } from "fs";
2259
2356
  import { homedir, platform } from "os";
@@ -3509,8 +3606,9 @@ function registerActionsCommand(program2) {
3509
3606
  cursor: opts.cursor,
3510
3607
  limit: parsePageSize(opts.limit)
3511
3608
  });
3512
- const output = opts.detail ? result : { actions: result.actions.map(compactActionCatalogEntry), page: result.page };
3513
- emit(output, mode, () => formatActionRows(result.actions, result.page.nextCursor));
3609
+ const actions2 = preferExactActionMatches(result.actions, opts.query);
3610
+ const output = opts.detail ? { ...result, actions: actions2 } : { actions: actions2.map(compactActionCatalogEntry), page: result.page };
3611
+ emit(output, mode, () => formatActionRows(actions2, result.page.nextCursor));
3514
3612
  } catch (err) {
3515
3613
  fail(err, mode);
3516
3614
  }
@@ -3528,7 +3626,7 @@ function registerActionsCommand(program2) {
3528
3626
  fail(err, mode);
3529
3627
  }
3530
3628
  });
3531
- actions.command("call <action>").description("Call a permission-scoped Topolo action").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Acknowledge a mutating action").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
3629
+ actions.command("call <action>").description("Call a permission-scoped Topolo action").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Acknowledge a mutating action").option("--detail", "Return the complete action response instead of a bounded agent projection").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
3532
3630
  const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
3533
3631
  try {
3534
3632
  const topolo = await buildClient();
@@ -3541,7 +3639,8 @@ function registerActionsCommand(program2) {
3541
3639
  confirm: opts.confirm === true,
3542
3640
  ...resource ? { resource } : {}
3543
3641
  });
3544
- emit(result, mode, () => JSON.stringify(result, null, 2));
3642
+ const output = detailRequested(opts.detail, command) ? result : compactActionResult(action, result);
3643
+ emit(output, mode, () => JSON.stringify(output, null, 2));
3545
3644
  } catch (err) {
3546
3645
  fail(err, mode);
3547
3646
  }
@@ -3762,6 +3861,7 @@ function inferContentType(path) {
3762
3861
  ".pdf": "application/pdf",
3763
3862
  ".png": "image/png",
3764
3863
  ".svg": "image/svg+xml",
3864
+ ".txt": "text/plain",
3765
3865
  ".webp": "image/webp"
3766
3866
  }[extname(path).toLowerCase()] ?? null;
3767
3867
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
- "version": "0.8.3",
4
- "topoloPublishedVersion": "0.8.3",
3
+ "version": "0.8.5",
4
+ "topoloPublishedVersion": "0.8.5",
5
5
  "description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
6
6
  "license": "UNLICENSED",
7
7
  "author": "Topolo",
@@ -19,14 +19,14 @@
19
19
  "dev": "tsup --watch",
20
20
  "typecheck": "tsc --noEmit",
21
21
  "start": "node dist/index.js",
22
- "test": "vitest run",
22
+ "test": "if test \"$TOPOLO_BUILD_READY\" = 1; then vitest run; else python3 ../../tooling/with-resource-lock.py ../topolo-sdk -- sh -c 'pnpm --dir ../topolo-sdk build && exec vitest run'; fi",
23
23
  "test:watch": "vitest",
24
- "coverage": "vitest run --coverage"
24
+ "coverage": "if test \"$TOPOLO_BUILD_READY\" = 1; then vitest run --coverage; else python3 ../../tooling/with-resource-lock.py ../topolo-sdk -- sh -c 'pnpm --dir ../topolo-sdk build && exec vitest run --coverage'; fi"
25
25
  },
26
26
  "dependencies": {
27
27
  "@modelcontextprotocol/sdk": "^1.0.0",
28
- "@topolo/mcp": "0.11.1",
29
- "@topolo/sdk": "0.11.1",
28
+ "@topolo/mcp": "0.11.3",
29
+ "@topolo/sdk": "0.11.3",
30
30
  "commander": "^12.1.0"
31
31
  },
32
32
  "devDependencies": {