@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.
- package/dist/index.js +108 -8
- 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:
|
|
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
|
|
3513
|
-
|
|
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
|
-
|
|
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.
|
|
4
|
-
"topoloPublishedVersion": "0.8.
|
|
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.
|
|
29
|
-
"@topolo/sdk": "0.11.
|
|
28
|
+
"@topolo/mcp": "0.11.3",
|
|
29
|
+
"@topolo/sdk": "0.11.3",
|
|
30
30
|
"commander": "^12.1.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|