@uru-intelligence/cli 0.4.15 → 0.4.17
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/CHANGELOG.md +8 -0
- package/dist/library.mjs +9 -4
- package/dist/uru.mjs +1441 -129
- package/package.json +2 -4
package/dist/uru.mjs
CHANGED
|
@@ -80,6 +80,20 @@ var cliRemoteOperationContracts = [
|
|
|
80
80
|
errorResponses: [],
|
|
81
81
|
reason: "The tool parameters and result are intentionally open-world transport values. Individual tools remain CLI-only until their backend-owned exact output schemas are available."
|
|
82
82
|
},
|
|
83
|
+
{
|
|
84
|
+
id: "tool_control_plane.catalog.list",
|
|
85
|
+
operationId: "list_catalog_api_tool_control_plane_catalog_get",
|
|
86
|
+
method: "get",
|
|
87
|
+
path: "/api/tool-control-plane/catalog",
|
|
88
|
+
authentication: "bearer_required",
|
|
89
|
+
workspaceAuthorization: "optional_selector_server_validated",
|
|
90
|
+
inputSchema: "zListCatalogApiToolControlPlaneCatalogGetQuery",
|
|
91
|
+
successStatus: "200",
|
|
92
|
+
outputSchema: "CanonicalToolCatalogEntry[]",
|
|
93
|
+
responseShape: "exact",
|
|
94
|
+
stream: "none",
|
|
95
|
+
errorResponses: []
|
|
96
|
+
},
|
|
83
97
|
{
|
|
84
98
|
id: "auth.logout",
|
|
85
99
|
operationId: "logout_user_api_auth_logout_post",
|
|
@@ -936,6 +950,134 @@ var CURRENT_GEM_SDK_REPLACEMENT = new Map([
|
|
|
936
950
|
var externalCompanyMcpDeniedToolIds = new Set([
|
|
937
951
|
"MAGUIRE_WRITE_SQL"
|
|
938
952
|
]);
|
|
953
|
+
// ../../packages/platform-operations/src/tool_address.ts
|
|
954
|
+
var TOOL_OWNER_CLASSES = [
|
|
955
|
+
"platform",
|
|
956
|
+
"personal",
|
|
957
|
+
"adopted",
|
|
958
|
+
"company"
|
|
959
|
+
];
|
|
960
|
+
function isToolOwnerClass(value) {
|
|
961
|
+
return typeof value === "string" && TOOL_OWNER_CLASSES.includes(value);
|
|
962
|
+
}
|
|
963
|
+
var TOOL_CONNECTION_TYPES = [
|
|
964
|
+
"platform",
|
|
965
|
+
"composio",
|
|
966
|
+
"managed_mcp",
|
|
967
|
+
"external_mcp",
|
|
968
|
+
"company"
|
|
969
|
+
];
|
|
970
|
+
function isToolConnectionType(value) {
|
|
971
|
+
return typeof value === "string" && TOOL_CONNECTION_TYPES.includes(value);
|
|
972
|
+
}
|
|
973
|
+
var TOOL_CONNECTION_TYPE_LABELS = {
|
|
974
|
+
platform: "Uru",
|
|
975
|
+
composio: "Marketplace",
|
|
976
|
+
managed_mcp: "Managed MCP",
|
|
977
|
+
external_mcp: "Custom MCP",
|
|
978
|
+
company: "Company"
|
|
979
|
+
};
|
|
980
|
+
function toolConnectionTypeLabel(type) {
|
|
981
|
+
return TOOL_CONNECTION_TYPE_LABELS[type];
|
|
982
|
+
}
|
|
983
|
+
function parseToolAddress(address) {
|
|
984
|
+
const segments = address.trim().split(".");
|
|
985
|
+
if (segments.length !== 4) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
const [provider, owner, connection, tool] = segments;
|
|
989
|
+
if (provider === undefined || owner === undefined || connection === undefined || tool === undefined || provider === "" || tool === "" || !isToolOwnerClass(owner)) {
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
return { provider, owner, connection, tool };
|
|
993
|
+
}
|
|
994
|
+
var TOOL_SEARCH_MIN_TERM_LENGTH = 2;
|
|
995
|
+
var TOOL_SEARCH_MIN_PENALTY_TERM_LENGTH = 3;
|
|
996
|
+
function scoreToolRelevance(fields, query, options = {}) {
|
|
997
|
+
const terms = query.toLowerCase().split(/[^a-z0-9]+/u).filter((term) => term.length >= TOOL_SEARCH_MIN_TERM_LENGTH);
|
|
998
|
+
if (terms.length === 0) {
|
|
999
|
+
return 0;
|
|
1000
|
+
}
|
|
1001
|
+
const address = fields.address.toLowerCase();
|
|
1002
|
+
const tool = fields.tool.toLowerCase();
|
|
1003
|
+
const text = `${fields.title ?? ""} ${fields.description} ${fields.category ?? ""}`.toLowerCase().trim();
|
|
1004
|
+
let score = 0;
|
|
1005
|
+
for (const term of terms) {
|
|
1006
|
+
let hit = false;
|
|
1007
|
+
if (tool.includes(term)) {
|
|
1008
|
+
score += 3;
|
|
1009
|
+
hit = true;
|
|
1010
|
+
} else if (address.includes(term)) {
|
|
1011
|
+
score += 2;
|
|
1012
|
+
hit = true;
|
|
1013
|
+
}
|
|
1014
|
+
if (text.includes(term)) {
|
|
1015
|
+
score += 1;
|
|
1016
|
+
hit = true;
|
|
1017
|
+
}
|
|
1018
|
+
if (!hit && options.missPenalty === true && term.length >= TOOL_SEARCH_MIN_PENALTY_TERM_LENGTH) {
|
|
1019
|
+
score -= 2;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return score;
|
|
1023
|
+
}
|
|
1024
|
+
// ../../packages/platform-operations/src/tool_refusal_envelope.ts
|
|
1025
|
+
var TOOL_REFUSAL_STATUSES = [
|
|
1026
|
+
"approval_required",
|
|
1027
|
+
"refused_by_policy"
|
|
1028
|
+
];
|
|
1029
|
+
function isToolRefusalStatus(value) {
|
|
1030
|
+
return typeof value === "string" && TOOL_REFUSAL_STATUSES.includes(value);
|
|
1031
|
+
}
|
|
1032
|
+
var TOOL_REFUSAL_ENVELOPE_FIELDS = [
|
|
1033
|
+
"status",
|
|
1034
|
+
"questionId",
|
|
1035
|
+
"writeClass",
|
|
1036
|
+
"toolName"
|
|
1037
|
+
];
|
|
1038
|
+
function asRecord(value) {
|
|
1039
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
return value;
|
|
1043
|
+
}
|
|
1044
|
+
function readToolRefusalEnvelope(value) {
|
|
1045
|
+
const record = asRecord(value);
|
|
1046
|
+
if (record === null) {
|
|
1047
|
+
return { outcome: "absent" };
|
|
1048
|
+
}
|
|
1049
|
+
const status = record["status"];
|
|
1050
|
+
if (!isToolRefusalStatus(status)) {
|
|
1051
|
+
return { outcome: "absent" };
|
|
1052
|
+
}
|
|
1053
|
+
const declared = TOOL_REFUSAL_ENVELOPE_FIELDS;
|
|
1054
|
+
for (const key of Object.keys(record).sort()) {
|
|
1055
|
+
if (!declared.includes(key)) {
|
|
1056
|
+
return { outcome: "undeclared_field", field: key };
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
const questionId = record["questionId"];
|
|
1060
|
+
if (typeof questionId !== "string" && questionId !== null) {
|
|
1061
|
+
return { outcome: "malformed_field", field: "questionId" };
|
|
1062
|
+
}
|
|
1063
|
+
const writeClass = record["writeClass"];
|
|
1064
|
+
if (typeof writeClass !== "string") {
|
|
1065
|
+
return { outcome: "malformed_field", field: "writeClass" };
|
|
1066
|
+
}
|
|
1067
|
+
const toolName = record["toolName"];
|
|
1068
|
+
if (toolName !== undefined && typeof toolName !== "string") {
|
|
1069
|
+
return { outcome: "malformed_field", field: "toolName" };
|
|
1070
|
+
}
|
|
1071
|
+
return {
|
|
1072
|
+
outcome: "envelope",
|
|
1073
|
+
envelope: {
|
|
1074
|
+
status,
|
|
1075
|
+
questionId,
|
|
1076
|
+
writeClass,
|
|
1077
|
+
...toolName === undefined ? {} : { toolName }
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
939
1081
|
// ../../packages/platform-operations/src/wrapper_tool_names.ts
|
|
940
1082
|
var WRAPPER_LIST_TOOLS_SUFFIX = "__list_tools";
|
|
941
1083
|
var WRAPPER_EXECUTE_TOOL_SUFFIX = "__execute_tool";
|
|
@@ -1103,11 +1245,7 @@ var remoteContractsByCommand = {
|
|
|
1103
1245
|
"platform.tools.schema",
|
|
1104
1246
|
...TOOL_EXECUTION_CONTRACTS
|
|
1105
1247
|
],
|
|
1106
|
-
tools: [
|
|
1107
|
-
"platform.tools.list",
|
|
1108
|
-
"platform.tools.schema",
|
|
1109
|
-
...TOOL_EXECUTION_CONTRACTS
|
|
1110
|
-
],
|
|
1248
|
+
tools: ["tool_control_plane.catalog.list", ...TOOL_EXECUTION_CONTRACTS],
|
|
1111
1249
|
init: TOOL_EXECUTION_CONTRACTS,
|
|
1112
1250
|
build: TOOL_EXECUTION_CONTRACTS,
|
|
1113
1251
|
deploy: TOOL_EXECUTION_CONTRACTS,
|
|
@@ -1150,7 +1288,7 @@ var cliCommandSpecs = [
|
|
|
1150
1288
|
spec("mcp", "Print or install MCP client configuration", "optional", "optional", "write", TEXT_AND_JSON),
|
|
1151
1289
|
spec("complete", "Generate shell completion metadata", "none", "none", "read", TEXT_AND_JSON),
|
|
1152
1290
|
spec("update", "Check for a newer CLI and print the install command", "none", "none", "read", TEXT_AND_JSON),
|
|
1153
|
-
spec("tools", "
|
|
1291
|
+
spec("tools", "Search, inspect and run every tool you can reach", "required", "required", "write", ALL_OUTPUTS),
|
|
1154
1292
|
spec("init", "Initialize a Gem project", "required", "required", "write", TEXT_AND_JSON),
|
|
1155
1293
|
spec("build", "Build a Gem", "required", "required", "write", TEXT_AND_JSON),
|
|
1156
1294
|
spec("deploy", "Save and deploy a Gem Version", "required", "required", "write", ALL_OUTPUTS),
|
|
@@ -1196,6 +1334,14 @@ var booleanFlag = (token) => ({
|
|
|
1196
1334
|
});
|
|
1197
1335
|
var valueFlag = (token, options = {}) => ({ token, value: "required", ...options });
|
|
1198
1336
|
var paramsJsonFlag = () => valueFlag("--params-json", { aliases: ["--args-json"] });
|
|
1337
|
+
var toolParamsJsonFlag = () => valueFlag("--params-json", { aliases: ["--args-json", "-d", "--data"] });
|
|
1338
|
+
var toolCatalogFilterFlags = [
|
|
1339
|
+
valueFlag("--namespace"),
|
|
1340
|
+
valueFlag("--owner"),
|
|
1341
|
+
valueFlag("--type"),
|
|
1342
|
+
valueFlag("--category"),
|
|
1343
|
+
valueFlag("--limit")
|
|
1344
|
+
];
|
|
1199
1345
|
var enumFlag = (token, values) => ({ token, value: "one_of", values });
|
|
1200
1346
|
function pathSpec(path, binding, positionals = [], localFlags = [], options = {}) {
|
|
1201
1347
|
return {
|
|
@@ -1322,33 +1468,61 @@ var cliCommandPathSpecs = [
|
|
|
1322
1468
|
positionals: [required("operation-id")],
|
|
1323
1469
|
localFlags: [paramsJsonFlag()]
|
|
1324
1470
|
},
|
|
1471
|
+
{
|
|
1472
|
+
path: ["tools", "search"],
|
|
1473
|
+
binding: "tools",
|
|
1474
|
+
helpKey: "tools",
|
|
1475
|
+
positionals: [variadic("task")],
|
|
1476
|
+
localFlags: [...toolCatalogFilterFlags, valueFlag("--offset")]
|
|
1477
|
+
},
|
|
1325
1478
|
{
|
|
1326
1479
|
path: ["tools", "ls"],
|
|
1480
|
+
aliases: [["tools", "list"]],
|
|
1327
1481
|
binding: "tools",
|
|
1328
1482
|
helpKey: "tools",
|
|
1329
1483
|
positionals: [],
|
|
1330
|
-
localFlags: []
|
|
1484
|
+
localFlags: [...toolCatalogFilterFlags]
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
path: ["tools", "connections"],
|
|
1488
|
+
binding: "tools",
|
|
1489
|
+
helpKey: "tools",
|
|
1490
|
+
positionals: [],
|
|
1491
|
+
localFlags: [valueFlag("--owner"), valueFlag("--type")]
|
|
1331
1492
|
},
|
|
1332
1493
|
{
|
|
1333
1494
|
path: ["tools", "schema"],
|
|
1334
1495
|
binding: "tools",
|
|
1335
1496
|
helpKey: "tools",
|
|
1336
|
-
positionals: [required("
|
|
1497
|
+
positionals: [required("address")],
|
|
1337
1498
|
localFlags: []
|
|
1338
1499
|
},
|
|
1339
1500
|
{
|
|
1340
1501
|
path: ["tools", "inspect"],
|
|
1341
1502
|
binding: "tools",
|
|
1342
1503
|
helpKey: "tools",
|
|
1343
|
-
positionals: [required("
|
|
1504
|
+
positionals: [required("address")],
|
|
1344
1505
|
localFlags: []
|
|
1345
1506
|
},
|
|
1346
1507
|
{
|
|
1347
1508
|
path: ["tools", "run"],
|
|
1348
1509
|
binding: "tools",
|
|
1349
1510
|
helpKey: "tools",
|
|
1350
|
-
positionals: [required("
|
|
1351
|
-
localFlags: [
|
|
1511
|
+
positionals: [required("address")],
|
|
1512
|
+
localFlags: [
|
|
1513
|
+
toolParamsJsonFlag(),
|
|
1514
|
+
booleanFlag("--get-schema"),
|
|
1515
|
+
booleanFlag("--dry-run"),
|
|
1516
|
+
booleanFlag("--parallel"),
|
|
1517
|
+
valueFlag("--mcp-url")
|
|
1518
|
+
]
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
path: ["tools", "approve"],
|
|
1522
|
+
binding: "tools",
|
|
1523
|
+
helpKey: "tools",
|
|
1524
|
+
positionals: [required("question-id")],
|
|
1525
|
+
localFlags: [booleanFlag("--accept"), booleanFlag("--decline")]
|
|
1352
1526
|
},
|
|
1353
1527
|
...libraryPathSpecs(),
|
|
1354
1528
|
...datasetPathSpecs(),
|
|
@@ -1632,7 +1806,7 @@ function topLevelPathSpecs() {
|
|
|
1632
1806
|
pathSpec(["secrets"], "secrets"),
|
|
1633
1807
|
pathSpec(["env"], "env"),
|
|
1634
1808
|
pathSpec(["operations"], "operations"),
|
|
1635
|
-
pathSpec(["tools"], "tools"),
|
|
1809
|
+
pathSpec(["tools"], "tools", [variadic("address")], [valueFlag("--match"), valueFlag("--limit")]),
|
|
1636
1810
|
pathSpec(["mcp"], "mcp")
|
|
1637
1811
|
];
|
|
1638
1812
|
}
|
|
@@ -1765,6 +1939,25 @@ function gemPathSpecs() {
|
|
|
1765
1939
|
...gemRefFlags
|
|
1766
1940
|
]),
|
|
1767
1941
|
pathSpec(["gems", "status"], "gems", [optional("gem")], gemRefFlags),
|
|
1942
|
+
pathSpec(["gems", "source", "status"], "gems", [optional("gem")], gemRefFlags),
|
|
1943
|
+
pathSpec(["gems", "source", "refresh"], "gems", [optional("gem")], gemRefFlags),
|
|
1944
|
+
pathSpec(["gems", "source", "copy"], "gems", [optional("gem")], gemRefFlags),
|
|
1945
|
+
pathSpec(["gems", "source", "retry"], "gems", [optional("gem")], [...gemRefFlags, valueFlag("--import-id"), valueFlag("--idempotency-key")]),
|
|
1946
|
+
pathSpec(["gems", "source", "cancel"], "gems", [optional("gem")], [...gemRefFlags, valueFlag("--import-id"), booleanFlag("--yes")]),
|
|
1947
|
+
pathSpec(["gems", "source", "policy"], "gems", [optional("gem")], [
|
|
1948
|
+
...gemRefFlags,
|
|
1949
|
+
valueFlag("--expected-revision"),
|
|
1950
|
+
valueFlag("--automatic-publishing"),
|
|
1951
|
+
valueFlag("--previews-enabled")
|
|
1952
|
+
], { aliases: [["gems", "source", "change-policy"]] }),
|
|
1953
|
+
pathSpec(["gems", "source", "disconnect"], "gems", [optional("gem")], [...gemRefFlags, valueFlag("--expected-revision"), booleanFlag("--yes")]),
|
|
1954
|
+
pathSpec(["gems", "source", "change-source"], "gems", [optional("gem")], [
|
|
1955
|
+
...gemRefFlags,
|
|
1956
|
+
valueFlag("--expected-revision"),
|
|
1957
|
+
valueFlag("--ref"),
|
|
1958
|
+
valueFlag("--app-directory"),
|
|
1959
|
+
valueFlag("--commit-sha")
|
|
1960
|
+
], { aliases: [["gems", "source", "source-change"]] }),
|
|
1768
1961
|
pathSpec(["gems", "read"], "gems", [required("gem-path"), required("file")]),
|
|
1769
1962
|
pathSpec(["gems", "write"], "gems", [required("gem-path"), required("file")], [valueFlag("--file"), valueFlag("--content"), booleanFlag("--no-create")]),
|
|
1770
1963
|
pathSpec(["gems", "versions", "save"], "gems", [optional("gem")], [
|
|
@@ -2388,7 +2581,21 @@ var cliRootHelpGroups = [
|
|
|
2388
2581
|
{
|
|
2389
2582
|
command: "tools",
|
|
2390
2583
|
lines: [
|
|
2391
|
-
|
|
2584
|
+
' uru tools search "<task>" [--limit N] [--namespace NS] [--owner OWNER] [--type TYPE] [--category CAT]',
|
|
2585
|
+
" Rank every reachable tool against a task",
|
|
2586
|
+
" uru tools ls [--namespace NS] [--owner OWNER] [--type TYPE] [--category CAT]",
|
|
2587
|
+
" Every tool you can reach, by connection",
|
|
2588
|
+
" --owner platform|personal|adopted|company (whose it is)",
|
|
2589
|
+
" --type platform|composio|managed_mcp|external_mcp|company (what kind)",
|
|
2590
|
+
" uru tools <partial address> --help [--match TEXT] [--limit N]",
|
|
2591
|
+
" Drill into <provider>.<owner>.<connection>.<tool>",
|
|
2592
|
+
" uru tools schema <address> Input and output shape for one tool",
|
|
2593
|
+
" uru tools inspect <address> Owner, type, connection, write class, policy answer",
|
|
2594
|
+
" uru tools run <address> -d '{...}' Run a platform or connector tool",
|
|
2595
|
+
" uru tools run <address> --get-schema|--dry-run|--parallel",
|
|
2596
|
+
" uru tools approve <question-id> --accept|--decline Answer a call that parked for approval",
|
|
2597
|
+
" uru tools connections [--owner OWNER] [--type TYPE]",
|
|
2598
|
+
" Every connection behind your tools"
|
|
2392
2599
|
]
|
|
2393
2600
|
},
|
|
2394
2601
|
{
|
|
@@ -2462,7 +2669,7 @@ function spec(id, summary, auth, workspace, safety, outputModes, cancellation =
|
|
|
2462
2669
|
// package.json
|
|
2463
2670
|
var package_default = {
|
|
2464
2671
|
name: "@uru-intelligence/cli",
|
|
2465
|
-
version: "0.4.
|
|
2672
|
+
version: "0.4.17",
|
|
2466
2673
|
private: false,
|
|
2467
2674
|
description: "Uru full-platform command line interface",
|
|
2468
2675
|
repository: {
|
|
@@ -2492,9 +2699,7 @@ var package_default = {
|
|
|
2492
2699
|
"type-check": "tsc --noEmit",
|
|
2493
2700
|
test: "bun test",
|
|
2494
2701
|
"test:unit": "bun test",
|
|
2495
|
-
|
|
2496
|
-
"format:check": "prettier --check . --ignore-path ../../.prettierignore",
|
|
2497
|
-
ci: "bun run lint && bun run type-check && bun run test:unit && bun run docs:check && bun run changelog:check && bun run format:check && bun run build && bun run smoke:package && bun run smoke:binaries",
|
|
2702
|
+
ci: "bun run lint && bun run type-check && bun run test:unit && bun run docs:check && bun run changelog:check && bun run build && bun run smoke:package && bun run smoke:binaries",
|
|
2498
2703
|
build: "node scripts/build.mjs",
|
|
2499
2704
|
prepack: "bun run build",
|
|
2500
2705
|
"docs:generate": "bun scripts/generate-docs.mjs",
|
|
@@ -9548,6 +9753,7 @@ var zBulkPermissionsUpdateResponse = object({
|
|
|
9548
9753
|
updatedCount: int()
|
|
9549
9754
|
});
|
|
9550
9755
|
var zCanonicalToolCatalogEntry = object({
|
|
9756
|
+
address: string2(),
|
|
9551
9757
|
agentAliasFragment: string2().nullish(),
|
|
9552
9758
|
agentVisibleNamespace: string2().nullish(),
|
|
9553
9759
|
annotations: record(string2(), unknown()).nullish(),
|
|
@@ -9569,6 +9775,7 @@ var zCanonicalToolCatalogEntry = object({
|
|
|
9569
9775
|
namespace: string2(),
|
|
9570
9776
|
normalizedSubtoolName: string2(),
|
|
9571
9777
|
outputSchema: record(string2(), unknown()).nullish(),
|
|
9778
|
+
owner: _enum(["platform", "personal", "adopted", "company"]),
|
|
9572
9779
|
ownerUserId: uuid2().nullable(),
|
|
9573
9780
|
personaLinkId: uuid2().nullish(),
|
|
9574
9781
|
personaName: string2().nullish(),
|
|
@@ -9577,6 +9784,8 @@ var zCanonicalToolCatalogEntry = object({
|
|
|
9577
9784
|
serviceSlug: string2(),
|
|
9578
9785
|
sourceUserName: string2().nullish(),
|
|
9579
9786
|
title: string2().nullish(),
|
|
9787
|
+
type: _enum(["platform", "composio", "managed_mcp", "external_mcp", "company"]),
|
|
9788
|
+
typeLabel: string2(),
|
|
9580
9789
|
visibilitySource: string2().nullish(),
|
|
9581
9790
|
workspaceId: uuid2().nullable(),
|
|
9582
9791
|
wrapperToolName: string2()
|
|
@@ -19410,6 +19619,9 @@ var zListAvailableToolDefinitionsApiToolControlPlaneAvailableGetQuery = object({
|
|
|
19410
19619
|
enabledOnly: _enum(["true", "false"]).optional().default("true")
|
|
19411
19620
|
});
|
|
19412
19621
|
var zListAvailableToolDefinitionsApiToolControlPlaneAvailableGetResponse = array(zToolDefinitionResponse);
|
|
19622
|
+
var zListCatalogApiToolControlPlaneCatalogGetHeaders = object({
|
|
19623
|
+
"X-Uru-Workspace-Id": string2().min(1).optional()
|
|
19624
|
+
});
|
|
19413
19625
|
var zListCatalogApiToolControlPlaneCatalogGetQuery = object({
|
|
19414
19626
|
enabledOnly: _enum(["true", "false"]).optional().default("true")
|
|
19415
19627
|
});
|
|
@@ -20389,8 +20601,7 @@ function parseOAuthCallbackUrl(callbackUrl, expectedState) {
|
|
|
20389
20601
|
}
|
|
20390
20602
|
const error2 = url2.searchParams.get("error");
|
|
20391
20603
|
if (error2 !== null && error2 !== "") {
|
|
20392
|
-
|
|
20393
|
-
throw new CliError(description === null || description === "" ? `OAuth authorization failed: ${error2}` : `OAuth authorization failed: ${error2}: ${description}`, { code: ExitCode.AuthRequired, errorCode: "oauth_authorization_failed" });
|
|
20604
|
+
throw new CliError("OAuth authorization failed. Start sign-in again and approve access.", { code: ExitCode.AuthRequired, errorCode: "oauth_authorization_failed" });
|
|
20394
20605
|
}
|
|
20395
20606
|
const code = url2.searchParams.get("code") ?? "";
|
|
20396
20607
|
if (code === "") {
|
|
@@ -20723,12 +20934,21 @@ function errorMessage2(payload) {
|
|
|
20723
20934
|
if (!isRecord(payload)) {
|
|
20724
20935
|
return;
|
|
20725
20936
|
}
|
|
20726
|
-
const description = payload["error_description"];
|
|
20727
|
-
if (typeof description === "string" && description.trim() !== "") {
|
|
20728
|
-
return description;
|
|
20729
|
-
}
|
|
20730
20937
|
const error2 = payload["error"];
|
|
20731
|
-
|
|
20938
|
+
switch (error2) {
|
|
20939
|
+
case "access_denied":
|
|
20940
|
+
return "Authorization was denied. Start sign-in again.";
|
|
20941
|
+
case "invalid_grant":
|
|
20942
|
+
return "Authorization expired or was revoked. Sign in again.";
|
|
20943
|
+
case "invalid_client":
|
|
20944
|
+
return "The sign-in client is not accepted by the server.";
|
|
20945
|
+
case "expired_token":
|
|
20946
|
+
return "The sign-in request expired. Start sign-in again.";
|
|
20947
|
+
case "temporarily_unavailable":
|
|
20948
|
+
return "The sign-in service is temporarily unavailable.";
|
|
20949
|
+
default:
|
|
20950
|
+
return;
|
|
20951
|
+
}
|
|
20732
20952
|
}
|
|
20733
20953
|
function defaultSleep(milliseconds) {
|
|
20734
20954
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
@@ -20964,32 +21184,6 @@ function formatWorkspaces(workspaces, style = PLAIN_VISUAL, currentWorkspaceId)
|
|
|
20964
21184
|
currentId !== undefined && workspace.id === currentId ? "current" : ""
|
|
20965
21185
|
]));
|
|
20966
21186
|
}
|
|
20967
|
-
function formatTools(tools, style = PLAIN_VISUAL) {
|
|
20968
|
-
if (tools.length === 0) {
|
|
20969
|
-
return "No tools found";
|
|
20970
|
-
}
|
|
20971
|
-
if (!style.rich) {
|
|
20972
|
-
return tools.map((tool) => {
|
|
20973
|
-
const description = tool.description === undefined ? "" : ` ${tool.description}`;
|
|
20974
|
-
return `${tool.name}${description}`;
|
|
20975
|
-
}).join(`
|
|
20976
|
-
`);
|
|
20977
|
-
}
|
|
20978
|
-
return formatTable(style, ["NAME", "DESCRIPTION"], tools.map((tool) => [tool.name, tool.description ?? ""]));
|
|
20979
|
-
}
|
|
20980
|
-
function formatToolContract(contract) {
|
|
20981
|
-
const name = typeof contract["name"] === "string" ? contract["name"] : "unknown";
|
|
20982
|
-
const description = typeof contract["description"] === "string" ? contract["description"] : "";
|
|
20983
|
-
const schema = contract["inputSchema"];
|
|
20984
|
-
const lines = [`Name: ${name}`];
|
|
20985
|
-
if (description !== "") {
|
|
20986
|
-
lines.push(`Description: ${description}`);
|
|
20987
|
-
}
|
|
20988
|
-
lines.push("Input schema:");
|
|
20989
|
-
lines.push(JSON.stringify(schema ?? {}, null, 2));
|
|
20990
|
-
return lines.join(`
|
|
20991
|
-
`);
|
|
20992
|
-
}
|
|
20993
21187
|
function formatOperationContract(contract) {
|
|
20994
21188
|
const id = typeof contract["id"] === "string" ? contract["id"] : "unknown";
|
|
20995
21189
|
const family = typeof contract["family"] === "string" ? contract["family"] : "unknown";
|
|
@@ -21222,7 +21416,7 @@ or --output-format stream-json before the command to emit NDJSON events.
|
|
|
21222
21416
|
Print one explicit active Link URL. When a Gem has more than one active Link,
|
|
21223
21417
|
use --link to choose the Link rather than guessing which URL you mean.
|
|
21224
21418
|
`,
|
|
21225
|
-
gems: `Usage: uru gems <inspect|validate|build|deploy|verify|fs|status|read|write|edits|versions|releases|links|runs|capabilities|schedules|bindings|components|delegations|api|domains|analytics|power|logs> [...]
|
|
21419
|
+
gems: `Usage: uru gems <inspect|validate|build|deploy|verify|fs|status|source|read|write|edits|versions|releases|links|runs|capabilities|schedules|bindings|components|delegations|api|domains|analytics|power|logs> [...]
|
|
21226
21420
|
uru gems validate [library/name.gem] [--wait] [--timeout-ms 600000] [--poll-interval-ms 2000]
|
|
21227
21421
|
uru gems build [library/name.gem] [--force] [--wait] [--timeout-ms 600000] [--poll-interval-ms 2000]
|
|
21228
21422
|
|
|
@@ -21511,13 +21705,29 @@ env pull writes a metadata-only template (names and placeholders). It never
|
|
|
21511
21705
|
downloads remote secret values. env run overlays local values and redacts
|
|
21512
21706
|
those values from captured output.
|
|
21513
21707
|
`,
|
|
21514
|
-
tools: `Usage: uru tools
|
|
21515
|
-
uru tools
|
|
21516
|
-
uru tools
|
|
21517
|
-
uru tools
|
|
21708
|
+
tools: `Usage: uru tools search "<task>" [--limit N] [--namespace NS] [--owner OWNER]
|
|
21709
|
+
uru tools ls [--namespace NS] [--owner OWNER]
|
|
21710
|
+
uru tools <partial address> --help [--match TEXT] [--limit N]
|
|
21711
|
+
uru tools schema <address>
|
|
21712
|
+
uru tools inspect <address>
|
|
21713
|
+
uru tools run <address> -d '{...}' [--get-schema] [--dry-run] [--parallel]
|
|
21714
|
+
uru tools connections
|
|
21518
21715
|
|
|
21519
|
-
|
|
21520
|
-
|
|
21716
|
+
Every tool you can reach: platform tools, your own connections, connections you
|
|
21717
|
+
adopted from a workspace offer, workspace connections, and MCP servers of any type.
|
|
21718
|
+
|
|
21719
|
+
An address is <provider>.<owner>.<connection>.<tool>, and owner is one of
|
|
21720
|
+
platform, personal, adopted, company. The connection type (Uru, Marketplace,
|
|
21721
|
+
Managed MCP, Custom MCP, Company) and its category print beside the address
|
|
21722
|
+
and filter with --type and --category. The flat <namespace>__<tool> wire name
|
|
21723
|
+
resolves to the same tool.
|
|
21724
|
+
|
|
21725
|
+
\`--get-schema\` and \`--dry-run\` run nothing. \`--dry-run\` prints the resolved
|
|
21726
|
+
call and the policy answer. \`--parallel\` takes a JSON array and returns one
|
|
21727
|
+
result per input object. \`-d\` is the same flag as --params-json.
|
|
21728
|
+
|
|
21729
|
+
A refusal is always named: no_match, catalog_unavailable, namespace_unavailable,
|
|
21730
|
+
not_connected, not_adopted, approval_required, refused_by_policy.
|
|
21521
21731
|
`,
|
|
21522
21732
|
operations: `Usage: uru operations ls [--family <family>]
|
|
21523
21733
|
uru operations inspect <operation-id>
|
|
@@ -23309,7 +23519,10 @@ function adaptRegistryFamilyParams(params) {
|
|
|
23309
23519
|
var REGISTRY_FAMILY_SNAKE_CASE_ALIASES = new Set([
|
|
23310
23520
|
"automation_id",
|
|
23311
23521
|
"dataset_ids",
|
|
23522
|
+
"idempotency_key",
|
|
23312
23523
|
"item_id",
|
|
23524
|
+
"label_id",
|
|
23525
|
+
"label_value",
|
|
23313
23526
|
"max_results"
|
|
23314
23527
|
]);
|
|
23315
23528
|
function canonicalCliFieldToToolProtocol(key) {
|
|
@@ -23321,54 +23534,6 @@ function canonicalCliFieldToToolProtocol(key) {
|
|
|
23321
23534
|
}
|
|
23322
23535
|
return key.replace(/[A-Z]/g, (character) => `_${character.toLowerCase()}`);
|
|
23323
23536
|
}
|
|
23324
|
-
async function dispatchTools(ctx, sub, third, command) {
|
|
23325
|
-
if (sub === "ls") {
|
|
23326
|
-
await withWorkspace(ctx);
|
|
23327
|
-
const tools = await client(ctx).listTools();
|
|
23328
|
-
ctx.json ? writeJson(ctx.io, tools.map((tool) => tool.raw)) : writeText(ctx.io, formatTools(tools, visualFromTerminal(ctx.terminal, ctx.outputFormat)));
|
|
23329
|
-
return;
|
|
23330
|
-
}
|
|
23331
|
-
if (sub === "schema") {
|
|
23332
|
-
await withWorkspace(ctx);
|
|
23333
|
-
const name = requireCommandValue(third, "Usage: uru tools schema <name>");
|
|
23334
|
-
emitValue(ctx, await client(ctx).getToolSchema(name));
|
|
23335
|
-
return;
|
|
23336
|
-
}
|
|
23337
|
-
if (sub === "inspect") {
|
|
23338
|
-
await withWorkspace(ctx);
|
|
23339
|
-
const name = requireCommandValue(third, "Usage: uru tools inspect <name>");
|
|
23340
|
-
const schema = await client(ctx).getToolSchema(name);
|
|
23341
|
-
const contract = toolInspectContract(name, schema);
|
|
23342
|
-
emitValue(ctx, contract, formatToolContract(contract));
|
|
23343
|
-
return;
|
|
23344
|
-
}
|
|
23345
|
-
if (sub === "run") {
|
|
23346
|
-
await withWorkspace(ctx);
|
|
23347
|
-
const name = requireCommandValue(third, "Usage: uru tools run <name> --params-json '{...}'");
|
|
23348
|
-
const params = await paramsJson(command.slice(3), ["tools", "run"], paramsJsonOptions(ctx));
|
|
23349
|
-
const operation = getPlatformOperationForToolCall(name, params);
|
|
23350
|
-
if (operation !== undefined) {
|
|
23351
|
-
requireOperationConfirmation(ctx, operation, params);
|
|
23352
|
-
} else if (isRegisteredPlatformToolName(name)) {
|
|
23353
|
-
throw new CliError(unmappedPlatformToolCallMessage(name, params));
|
|
23354
|
-
}
|
|
23355
|
-
emitValue(ctx, await client(ctx).runTool(name, params));
|
|
23356
|
-
return;
|
|
23357
|
-
}
|
|
23358
|
-
throw new CliError("Usage: uru tools ls|schema|inspect|run");
|
|
23359
|
-
}
|
|
23360
|
-
function unmappedPlatformToolCallMessage(toolName, params) {
|
|
23361
|
-
const op2 = typeof params["op"] === "string" ? params["op"] : undefined;
|
|
23362
|
-
const normalizedTool = toolName.trim().toLowerCase();
|
|
23363
|
-
const normalizedOp = op2?.trim().toLowerCase();
|
|
23364
|
-
if (normalizedTool === "library_fs" && (normalizedOp === "ls" || normalizedOp === "stat" || normalizedOp === "read" || normalizedOp === "search")) {
|
|
23365
|
-
return `library_fs does not serve read op=${normalizedOp}. ` + "Use library_query (or `uru library ls|stat|search|read` / " + "`uru operations run library.<op>`).";
|
|
23366
|
-
}
|
|
23367
|
-
if (normalizedOp !== undefined) {
|
|
23368
|
-
return `No registered safety policy for ${toolName} op=${normalizedOp}. ` + "Refuse to execute. Check `uru operations search` for the supported operation id.";
|
|
23369
|
-
}
|
|
23370
|
-
return `No registered safety policy for ${toolName}. ` + "Refuse to execute without a mapped platform operation.";
|
|
23371
|
-
}
|
|
23372
23537
|
async function dispatchLibrary(ctx, sub, args) {
|
|
23373
23538
|
if (sub === "ls") {
|
|
23374
23539
|
const flags = parseLocalFlags(args, ["library", "ls"]);
|
|
@@ -24084,18 +24249,6 @@ function copyOptionalFlag(flags, target, flagName, paramName) {
|
|
|
24084
24249
|
target[paramName] = value;
|
|
24085
24250
|
}
|
|
24086
24251
|
}
|
|
24087
|
-
function toolInspectContract(name, schema) {
|
|
24088
|
-
if (!isJsonObject2(schema)) {
|
|
24089
|
-
return { name, inputSchema: {} };
|
|
24090
|
-
}
|
|
24091
|
-
const inputSchema = isJsonObject2(schema["inputSchema"]) ? schema["inputSchema"] : schema;
|
|
24092
|
-
const description = typeof schema["description"] === "string" ? schema["description"] : undefined;
|
|
24093
|
-
return {
|
|
24094
|
-
name: typeof schema["name"] === "string" ? schema["name"] : name,
|
|
24095
|
-
...description === undefined ? {} : { description },
|
|
24096
|
-
inputSchema
|
|
24097
|
-
};
|
|
24098
|
-
}
|
|
24099
24252
|
function familyPrefix(family) {
|
|
24100
24253
|
if (family === "automations") {
|
|
24101
24254
|
return "automation";
|
|
@@ -25126,8 +25279,275 @@ async function dispatchGemFs(ctx, verb, args, linkedProject) {
|
|
|
25126
25279
|
}
|
|
25127
25280
|
}
|
|
25128
25281
|
|
|
25282
|
+
// src/gem-git-source-commands.ts
|
|
25283
|
+
import { randomUUID } from "node:crypto";
|
|
25284
|
+
var GIT_SOURCE_PATH = "/api/v1/artifacts";
|
|
25285
|
+
async function dispatchGemGitSource(ctx, verb, args, linkedProject) {
|
|
25286
|
+
if (verb === undefined || verb === "help") {
|
|
25287
|
+
throw new CliError("Usage: uru gems source status|refresh|retry|cancel|copy|change-source|policy|disconnect <gem> ...");
|
|
25288
|
+
}
|
|
25289
|
+
if ((verb === "cancel" || verb === "disconnect") && !ctx.yes) {
|
|
25290
|
+
throw new CliError(`${verb} is destructive. Re-run with --yes to confirm.`, {
|
|
25291
|
+
errorCode: "confirmation_required",
|
|
25292
|
+
confirmationRequired: true,
|
|
25293
|
+
suggestedCommand: suggestCommandWithYes(ctx.argv)
|
|
25294
|
+
});
|
|
25295
|
+
}
|
|
25296
|
+
const flags = parseLocalFlags(args, ["gems", "source", verb]);
|
|
25297
|
+
const reference = await resolveArtifactReference(ctx, flags, linkedProject);
|
|
25298
|
+
if (verb === "copy") {
|
|
25299
|
+
const result2 = await client(ctx).api(`/api/platform/artifacts/${encodeURIComponent(reference.slug)}/clone`, { method: "POST" });
|
|
25300
|
+
emitValue(ctx, result2, formatCloneResult(result2));
|
|
25301
|
+
return;
|
|
25302
|
+
}
|
|
25303
|
+
if (verb === "status") {
|
|
25304
|
+
const result2 = await client(ctx).api(`${GIT_SOURCE_PATH}/${encodeURIComponent(reference.artifactId)}/git-source`, { method: "GET" });
|
|
25305
|
+
emitValue(ctx, result2, formatGitSourceState(result2));
|
|
25306
|
+
return;
|
|
25307
|
+
}
|
|
25308
|
+
const command = commandFromFlags(verb, flags);
|
|
25309
|
+
const result = await client(ctx).api(`${GIT_SOURCE_PATH}/${encodeURIComponent(reference.artifactId)}/git-source/commands`, { method: "POST", body: command });
|
|
25310
|
+
emitValue(ctx, result, formatGitSourceState(result));
|
|
25311
|
+
}
|
|
25312
|
+
function commandFromFlags(verb, flags) {
|
|
25313
|
+
switch (verb) {
|
|
25314
|
+
case "refresh":
|
|
25315
|
+
return toJsonCommand({ action: "refresh" });
|
|
25316
|
+
case "retry":
|
|
25317
|
+
return toJsonCommand({
|
|
25318
|
+
action: "retry",
|
|
25319
|
+
importId: requiredImportId(flags, "retry"),
|
|
25320
|
+
idempotencyKey: flags.values["--idempotency-key"]?.trim() || randomUUID()
|
|
25321
|
+
});
|
|
25322
|
+
case "cancel":
|
|
25323
|
+
return toJsonCommand({
|
|
25324
|
+
action: "cancel",
|
|
25325
|
+
importId: requiredImportId(flags, "cancel")
|
|
25326
|
+
});
|
|
25327
|
+
case "policy":
|
|
25328
|
+
case "change-policy":
|
|
25329
|
+
return toJsonCommand({
|
|
25330
|
+
action: "change_policy",
|
|
25331
|
+
expectedRevision: requiredInteger(flags, "--expected-revision"),
|
|
25332
|
+
automaticPublishing: requiredBoolean(flags, "--automatic-publishing"),
|
|
25333
|
+
previewsEnabled: requiredBoolean(flags, "--previews-enabled")
|
|
25334
|
+
});
|
|
25335
|
+
case "disconnect":
|
|
25336
|
+
return toJsonCommand({
|
|
25337
|
+
action: "disconnect",
|
|
25338
|
+
expectedRevision: requiredInteger(flags, "--expected-revision")
|
|
25339
|
+
});
|
|
25340
|
+
case "change-source":
|
|
25341
|
+
case "source-change":
|
|
25342
|
+
return toJsonCommand({
|
|
25343
|
+
action: "change_source",
|
|
25344
|
+
expectedRevision: requiredInteger(flags, "--expected-revision"),
|
|
25345
|
+
ref: requiredValue2(flags, "--ref"),
|
|
25346
|
+
appDirectory: requiredValue2(flags, "--app-directory"),
|
|
25347
|
+
commitSha: requiredValue2(flags, "--commit-sha")
|
|
25348
|
+
});
|
|
25349
|
+
default:
|
|
25350
|
+
throw new CliError("Usage: uru gems source status|refresh|retry|cancel|copy|change-source|policy|disconnect <gem> ...");
|
|
25351
|
+
}
|
|
25352
|
+
}
|
|
25353
|
+
async function resolveArtifactReference(ctx, flags, linkedProject) {
|
|
25354
|
+
const reference = gemPathFromArgs(flags, linkedProject);
|
|
25355
|
+
const inspected = await executeOperation(ctx, "gem.inspect", {
|
|
25356
|
+
path: reference
|
|
25357
|
+
});
|
|
25358
|
+
const artifactId = (isUuid(reference) ? reference : undefined) ?? findArtifactId(inspected);
|
|
25359
|
+
if (artifactId === undefined) {
|
|
25360
|
+
throw new CliError(`Gem inspection did not return an artifact id for ${reference}.`);
|
|
25361
|
+
}
|
|
25362
|
+
return {
|
|
25363
|
+
artifactId,
|
|
25364
|
+
slug: findArtifactSlug(inspected) ?? artifactId
|
|
25365
|
+
};
|
|
25366
|
+
}
|
|
25367
|
+
function findArtifactId(value) {
|
|
25368
|
+
if (isJsonObject2(value)) {
|
|
25369
|
+
const id = stringField2(value, [
|
|
25370
|
+
"artifact_id",
|
|
25371
|
+
"artifactId",
|
|
25372
|
+
"gem_id",
|
|
25373
|
+
"gemId"
|
|
25374
|
+
]);
|
|
25375
|
+
if (id !== undefined && isUuid(id)) {
|
|
25376
|
+
return id;
|
|
25377
|
+
}
|
|
25378
|
+
for (const nested of Object.values(value)) {
|
|
25379
|
+
const found = findArtifactId(nested);
|
|
25380
|
+
if (found !== undefined) {
|
|
25381
|
+
return found;
|
|
25382
|
+
}
|
|
25383
|
+
}
|
|
25384
|
+
return;
|
|
25385
|
+
}
|
|
25386
|
+
if (Array.isArray(value)) {
|
|
25387
|
+
for (const nested of value) {
|
|
25388
|
+
const found = findArtifactId(nested);
|
|
25389
|
+
if (found !== undefined) {
|
|
25390
|
+
return found;
|
|
25391
|
+
}
|
|
25392
|
+
}
|
|
25393
|
+
}
|
|
25394
|
+
return;
|
|
25395
|
+
}
|
|
25396
|
+
function isUuid(value) {
|
|
25397
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value);
|
|
25398
|
+
}
|
|
25399
|
+
function findArtifactSlug(value) {
|
|
25400
|
+
if (isJsonObject2(value)) {
|
|
25401
|
+
const slug = stringField2(value, [
|
|
25402
|
+
"artifact_slug",
|
|
25403
|
+
"artifactSlug",
|
|
25404
|
+
"slug"
|
|
25405
|
+
]);
|
|
25406
|
+
if (slug !== undefined && !slug.includes("/")) {
|
|
25407
|
+
return slug;
|
|
25408
|
+
}
|
|
25409
|
+
for (const nested of Object.values(value)) {
|
|
25410
|
+
const found = findArtifactSlug(nested);
|
|
25411
|
+
if (found !== undefined) {
|
|
25412
|
+
return found;
|
|
25413
|
+
}
|
|
25414
|
+
}
|
|
25415
|
+
return;
|
|
25416
|
+
}
|
|
25417
|
+
if (Array.isArray(value)) {
|
|
25418
|
+
for (const nested of value) {
|
|
25419
|
+
const found = findArtifactSlug(nested);
|
|
25420
|
+
if (found !== undefined) {
|
|
25421
|
+
return found;
|
|
25422
|
+
}
|
|
25423
|
+
}
|
|
25424
|
+
}
|
|
25425
|
+
return;
|
|
25426
|
+
}
|
|
25427
|
+
function requiredImportId(flags, verb) {
|
|
25428
|
+
const importId = flags.values["--import-id"]?.trim();
|
|
25429
|
+
if (!importId) {
|
|
25430
|
+
throw new CliError(`Usage: uru gems source ${verb} [gem] --import-id <import-id>`);
|
|
25431
|
+
}
|
|
25432
|
+
return importId;
|
|
25433
|
+
}
|
|
25434
|
+
function requiredValue2(flags, token) {
|
|
25435
|
+
const value = flags.values[token]?.trim();
|
|
25436
|
+
if (value === undefined || value === "") {
|
|
25437
|
+
throw new CliError(`Missing ${token}.`);
|
|
25438
|
+
}
|
|
25439
|
+
return value;
|
|
25440
|
+
}
|
|
25441
|
+
function requiredInteger(flags, token) {
|
|
25442
|
+
const raw = requiredValue2(flags, token);
|
|
25443
|
+
const parsed = Number(raw);
|
|
25444
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
25445
|
+
throw new CliError(`${token} must be a non-negative integer.`);
|
|
25446
|
+
}
|
|
25447
|
+
return parsed;
|
|
25448
|
+
}
|
|
25449
|
+
function requiredBoolean(flags, token) {
|
|
25450
|
+
const raw = requiredValue2(flags, token).toLowerCase();
|
|
25451
|
+
if (raw === "true" || raw === "1" || raw === "yes" || raw === "on") {
|
|
25452
|
+
return true;
|
|
25453
|
+
}
|
|
25454
|
+
if (raw === "false" || raw === "0" || raw === "no" || raw === "off") {
|
|
25455
|
+
return false;
|
|
25456
|
+
}
|
|
25457
|
+
throw new CliError(`${token} must be true or false.`);
|
|
25458
|
+
}
|
|
25459
|
+
function toJsonCommand(command) {
|
|
25460
|
+
switch (command.action) {
|
|
25461
|
+
case "refresh":
|
|
25462
|
+
return { action: "refresh" };
|
|
25463
|
+
case "retry":
|
|
25464
|
+
return {
|
|
25465
|
+
action: "retry",
|
|
25466
|
+
importId: command.importId,
|
|
25467
|
+
idempotencyKey: command.idempotencyKey
|
|
25468
|
+
};
|
|
25469
|
+
case "cancel":
|
|
25470
|
+
return { action: "cancel", importId: command.importId };
|
|
25471
|
+
case "change_policy":
|
|
25472
|
+
return {
|
|
25473
|
+
action: "change_policy",
|
|
25474
|
+
expectedRevision: command.expectedRevision,
|
|
25475
|
+
automaticPublishing: command.automaticPublishing,
|
|
25476
|
+
previewsEnabled: command.previewsEnabled
|
|
25477
|
+
};
|
|
25478
|
+
case "publish":
|
|
25479
|
+
return {
|
|
25480
|
+
action: "publish",
|
|
25481
|
+
importId: command.importId,
|
|
25482
|
+
expectedRevision: command.expectedRevision
|
|
25483
|
+
};
|
|
25484
|
+
case "approve_preview":
|
|
25485
|
+
return {
|
|
25486
|
+
action: "approve_preview",
|
|
25487
|
+
previewId: command.previewId,
|
|
25488
|
+
headSha: command.headSha,
|
|
25489
|
+
resourcePolicyHash: command.resourcePolicyHash
|
|
25490
|
+
};
|
|
25491
|
+
case "expire_preview":
|
|
25492
|
+
return { action: "expire_preview", previewId: command.previewId };
|
|
25493
|
+
case "disconnect":
|
|
25494
|
+
return {
|
|
25495
|
+
action: "disconnect",
|
|
25496
|
+
expectedRevision: command.expectedRevision
|
|
25497
|
+
};
|
|
25498
|
+
case "reconnect":
|
|
25499
|
+
return {
|
|
25500
|
+
action: "reconnect",
|
|
25501
|
+
expectedRevision: command.expectedRevision
|
|
25502
|
+
};
|
|
25503
|
+
case "change_source":
|
|
25504
|
+
return {
|
|
25505
|
+
action: "change_source",
|
|
25506
|
+
expectedRevision: command.expectedRevision,
|
|
25507
|
+
ref: command.ref,
|
|
25508
|
+
appDirectory: command.appDirectory,
|
|
25509
|
+
commitSha: command.commitSha
|
|
25510
|
+
};
|
|
25511
|
+
}
|
|
25512
|
+
throw new CliError("Unsupported Git source command.");
|
|
25513
|
+
}
|
|
25514
|
+
function formatGitSourceState(value) {
|
|
25515
|
+
if (!isJsonObject2(value)) {
|
|
25516
|
+
return JSON.stringify(value, null, 2);
|
|
25517
|
+
}
|
|
25518
|
+
const artifactId = stringField2(value, ["artifactId", "artifact_id"]);
|
|
25519
|
+
const binding = isJsonObject2(value["binding"]) ? value["binding"] : undefined;
|
|
25520
|
+
const policy = isJsonObject2(value["policy"]) ? value["policy"] : undefined;
|
|
25521
|
+
const lines = [
|
|
25522
|
+
artifactId === undefined ? undefined : `Artifact: ${artifactId}`,
|
|
25523
|
+
binding === undefined ? "Source binding: none" : `Source binding: ${binding["active"] === true ? "active" : "inactive"}`,
|
|
25524
|
+
binding === undefined ? undefined : stringField2(binding, ["ref"]) === undefined ? undefined : `Ref: ${stringField2(binding, ["ref"])}`,
|
|
25525
|
+
binding === undefined ? undefined : stringField2(binding, ["currentCommitSha", "current_commit_sha"]) === undefined ? undefined : `Current commit: ${stringField2(binding, [
|
|
25526
|
+
"currentCommitSha",
|
|
25527
|
+
"current_commit_sha"
|
|
25528
|
+
])}`,
|
|
25529
|
+
policy === undefined ? undefined : `Policy: automatic publishing ${policy["automaticPublishing"] === true ? "on" : "off"}, previews ${policy["previewsEnabled"] === true ? "on" : "off"}`
|
|
25530
|
+
].filter((line) => line !== undefined);
|
|
25531
|
+
return lines.length === 0 ? JSON.stringify(value, null, 2) : lines.join(`
|
|
25532
|
+
`);
|
|
25533
|
+
}
|
|
25534
|
+
function formatCloneResult(value) {
|
|
25535
|
+
const record2 = isJsonObject2(value) ? value : undefined;
|
|
25536
|
+
const artifactId = record2 === undefined ? undefined : stringField2(record2, ["id", "artifactId", "artifact_id"]);
|
|
25537
|
+
const title = record2 === undefined ? undefined : stringField2(record2, ["title"]);
|
|
25538
|
+
if (artifactId === undefined && title === undefined) {
|
|
25539
|
+
return JSON.stringify(value, null, 2);
|
|
25540
|
+
}
|
|
25541
|
+
return [
|
|
25542
|
+
"Gem copied.",
|
|
25543
|
+
artifactId === undefined ? undefined : `Artifact: ${artifactId}`,
|
|
25544
|
+
title === undefined ? undefined : `Title: ${title}`
|
|
25545
|
+
].filter((line) => line !== undefined).join(`
|
|
25546
|
+
`);
|
|
25547
|
+
}
|
|
25548
|
+
|
|
25129
25549
|
// src/held-idempotency-keys.ts
|
|
25130
|
-
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
25550
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
25131
25551
|
import { constants } from "node:fs";
|
|
25132
25552
|
import { chmod, link, mkdir as mkdir3, open, readFile as readFile7, rename, rm as rm2 } from "node:fs/promises";
|
|
25133
25553
|
import { dirname as dirname4, join as join2 } from "node:path";
|
|
@@ -25142,7 +25562,7 @@ async function holdIdempotencyKey(io, stateHome, intent, now = new Date) {
|
|
|
25142
25562
|
return withHeldEntriesLock(path, async () => {
|
|
25143
25563
|
const stored = await readHeldEntries(path);
|
|
25144
25564
|
const existing = stored[fingerprint];
|
|
25145
|
-
const owner = `${process.pid}:${
|
|
25565
|
+
const owner = `${process.pid}:${randomUUID2()}`;
|
|
25146
25566
|
if (existing !== undefined) {
|
|
25147
25567
|
const updated = {
|
|
25148
25568
|
...existing,
|
|
@@ -25154,7 +25574,7 @@ async function holdIdempotencyKey(io, stateHome, intent, now = new Date) {
|
|
|
25154
25574
|
release: () => releaseHold(io, path, fingerprint, existing.key, owner)
|
|
25155
25575
|
};
|
|
25156
25576
|
}
|
|
25157
|
-
const key =
|
|
25577
|
+
const key = randomUUID2();
|
|
25158
25578
|
await writeHeldEntries(path, {
|
|
25159
25579
|
...stored,
|
|
25160
25580
|
[fingerprint]: { key, heldAt: now.toISOString(), owners: [owner] }
|
|
@@ -25264,7 +25684,7 @@ async function withHeldEntriesLock(path, action) {
|
|
|
25264
25684
|
continue;
|
|
25265
25685
|
}
|
|
25266
25686
|
try {
|
|
25267
|
-
const lockToken =
|
|
25687
|
+
const lockToken = randomUUID2();
|
|
25268
25688
|
const contents = `${process.pid}
|
|
25269
25689
|
${lockToken}
|
|
25270
25690
|
`;
|
|
@@ -25347,7 +25767,7 @@ async function tryCreateHeldLock(path) {
|
|
|
25347
25767
|
}
|
|
25348
25768
|
try {
|
|
25349
25769
|
const contents = `${process.pid}
|
|
25350
|
-
${
|
|
25770
|
+
${randomUUID2()}
|
|
25351
25771
|
`;
|
|
25352
25772
|
await handle.writeFile(contents, "utf8");
|
|
25353
25773
|
await handle.sync();
|
|
@@ -25406,7 +25826,7 @@ async function readHeldLockObservation(path) {
|
|
|
25406
25826
|
}
|
|
25407
25827
|
}
|
|
25408
25828
|
async function removeHeldLockIfMatches(path, expected) {
|
|
25409
|
-
const quarantinePath = `${path}.${process.pid}.${
|
|
25829
|
+
const quarantinePath = `${path}.${process.pid}.${randomUUID2()}.quarantine`;
|
|
25410
25830
|
try {
|
|
25411
25831
|
await rename(path, quarantinePath);
|
|
25412
25832
|
await syncParentDirectory(path);
|
|
@@ -25466,7 +25886,7 @@ async function writeHeldEntries(path, entries) {
|
|
|
25466
25886
|
}
|
|
25467
25887
|
const body = `${JSON.stringify(entries, null, 2)}
|
|
25468
25888
|
`;
|
|
25469
|
-
const temporaryPath = `${path}.${process.pid}.${
|
|
25889
|
+
const temporaryPath = `${path}.${process.pid}.${randomUUID2()}.tmp`;
|
|
25470
25890
|
let handle = null;
|
|
25471
25891
|
try {
|
|
25472
25892
|
handle = await open(temporaryPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, constants.S_IRUSR | constants.S_IWUSR);
|
|
@@ -26309,6 +26729,11 @@ async function dispatchGems(ctx, sub, args, linkedProject) {
|
|
|
26309
26729
|
await dispatchGemFs(ctx, verb, rest, linkedProject);
|
|
26310
26730
|
return;
|
|
26311
26731
|
}
|
|
26732
|
+
if (sub === "source") {
|
|
26733
|
+
const [verb, ...rest] = args;
|
|
26734
|
+
await dispatchGemGitSource(ctx, verb, rest, linkedProject);
|
|
26735
|
+
return;
|
|
26736
|
+
}
|
|
26312
26737
|
if (await dispatchGemLifecycleResourceCommand(ctx, sub, args, linkedProject)) {
|
|
26313
26738
|
return;
|
|
26314
26739
|
}
|
|
@@ -26348,7 +26773,7 @@ async function dispatchGems(ctx, sub, args, linkedProject) {
|
|
|
26348
26773
|
await gemWrite(ctx, args);
|
|
26349
26774
|
return;
|
|
26350
26775
|
}
|
|
26351
|
-
throw new CliError("Usage: uru gems inspect|validate|build|deploy|verify|fs|edits|versions|releases|links|runs|capabilities|schedules|bindings|components|delegations|api|domains|analytics|power|checks|status|read|write");
|
|
26776
|
+
throw new CliError("Usage: uru gems inspect|validate|build|deploy|verify|fs|edits|versions|releases|links|runs|capabilities|schedules|bindings|components|delegations|api|domains|analytics|power|checks|status|source|read|write");
|
|
26352
26777
|
}
|
|
26353
26778
|
async function dispatchGemOperationalResourceCommand(ctx, sub, args, linkedProject) {
|
|
26354
26779
|
if (sub === "domains") {
|
|
@@ -27647,6 +28072,892 @@ function safeProjectPath(path) {
|
|
|
27647
28072
|
return parts.join("/");
|
|
27648
28073
|
}
|
|
27649
28074
|
|
|
28075
|
+
// src/tools-catalog.ts
|
|
28076
|
+
function isPlatformTool(tool) {
|
|
28077
|
+
return tool.type === "platform";
|
|
28078
|
+
}
|
|
28079
|
+
var TOOL_REFUSALS = {
|
|
28080
|
+
noMatch: "no_match",
|
|
28081
|
+
catalogUnavailable: "catalog_unavailable",
|
|
28082
|
+
namespaceUnavailable: "namespace_unavailable",
|
|
28083
|
+
notConnected: "not_connected",
|
|
28084
|
+
notAdopted: "not_adopted",
|
|
28085
|
+
approvalRequired: "approval_required",
|
|
28086
|
+
refusedByPolicy: "refused_by_policy"
|
|
28087
|
+
};
|
|
28088
|
+
function refuse(errorCode, message, suggestedCommand, cause) {
|
|
28089
|
+
const wrapped = cause instanceof CliError ? cause : undefined;
|
|
28090
|
+
return new CliError(message, {
|
|
28091
|
+
errorCode,
|
|
28092
|
+
...suggestedCommand === undefined ? {} : { suggestedCommand },
|
|
28093
|
+
...wrapped?.errorId === undefined ? {} : { errorId: wrapped.errorId },
|
|
28094
|
+
...wrapped?.httpStatus === undefined ? {} : { httpStatus: wrapped.httpStatus },
|
|
28095
|
+
...cause === undefined ? {} : { cause }
|
|
28096
|
+
});
|
|
28097
|
+
}
|
|
28098
|
+
function stringField5(row, key) {
|
|
28099
|
+
const value = row[key];
|
|
28100
|
+
return typeof value === "string" ? value : "";
|
|
28101
|
+
}
|
|
28102
|
+
function nullableStringField(row, key) {
|
|
28103
|
+
const value = row[key];
|
|
28104
|
+
return typeof value === "string" && value.trim() !== "" ? value : null;
|
|
28105
|
+
}
|
|
28106
|
+
function objectField(row, key) {
|
|
28107
|
+
const value = row[key];
|
|
28108
|
+
return isJsonObject(value) ? value : null;
|
|
28109
|
+
}
|
|
28110
|
+
function readCatalogRow(value) {
|
|
28111
|
+
if (!isJsonObject(value)) {
|
|
28112
|
+
return null;
|
|
28113
|
+
}
|
|
28114
|
+
const address = stringField5(value, "address");
|
|
28115
|
+
const owner = stringField5(value, "owner");
|
|
28116
|
+
const type = stringField5(value, "type");
|
|
28117
|
+
const parsed = parseToolAddress(address);
|
|
28118
|
+
if (parsed === null || !isToolOwnerClass(owner) || parsed.owner !== owner || !isToolConnectionType(type)) {
|
|
28119
|
+
return null;
|
|
28120
|
+
}
|
|
28121
|
+
const { provider, connection, tool } = parsed;
|
|
28122
|
+
const namespace = nullableStringField(value, "agentVisibleNamespace") ?? stringField5(value, "namespace");
|
|
28123
|
+
const subtool = stringField5(value, "normalizedSubtoolName");
|
|
28124
|
+
return {
|
|
28125
|
+
address,
|
|
28126
|
+
owner,
|
|
28127
|
+
type,
|
|
28128
|
+
typeLabel: toolConnectionTypeLabel(type),
|
|
28129
|
+
provider,
|
|
28130
|
+
connection,
|
|
28131
|
+
tool,
|
|
28132
|
+
flatName: type === "platform" ? subtool.toLowerCase() : `${namespace}__${tool}`,
|
|
28133
|
+
namespace,
|
|
28134
|
+
wrapperToolName: stringField5(value, "wrapperToolName"),
|
|
28135
|
+
providerNativeToolName: stringField5(value, "providerNativeToolName") || subtool,
|
|
28136
|
+
capabilityId: stringField5(value, "capabilityId"),
|
|
28137
|
+
connectionId: stringField5(value, "connectionId"),
|
|
28138
|
+
connectionLabel: stringField5(value, "displayName") || connection,
|
|
28139
|
+
description: stringField5(value, "description"),
|
|
28140
|
+
title: nullableStringField(value, "title"),
|
|
28141
|
+
category: nullableStringField(value, "category"),
|
|
28142
|
+
writeClass: stringField5(value, "classification") || "unknown",
|
|
28143
|
+
enabled: value["enabled"] !== false,
|
|
28144
|
+
inputSchema: objectField(value, "inputSchema") ?? {},
|
|
28145
|
+
outputSchema: objectField(value, "outputSchema")
|
|
28146
|
+
};
|
|
28147
|
+
}
|
|
28148
|
+
async function fetchToolCatalog(ctx) {
|
|
28149
|
+
let body;
|
|
28150
|
+
try {
|
|
28151
|
+
body = await client(ctx).api("/api/tool-control-plane/catalog", {
|
|
28152
|
+
method: "GET"
|
|
28153
|
+
});
|
|
28154
|
+
} catch (error2) {
|
|
28155
|
+
throw refuse(TOOL_REFUSALS.catalogUnavailable, `The tool catalog could not be read: ${error2 instanceof Error ? error2.message : String(error2)}`, "uru doctor", error2);
|
|
28156
|
+
}
|
|
28157
|
+
if (!Array.isArray(body)) {
|
|
28158
|
+
throw refuse(TOOL_REFUSALS.catalogUnavailable, "The tool catalog answered with something other than a list of tools.");
|
|
28159
|
+
}
|
|
28160
|
+
const tools = [];
|
|
28161
|
+
for (const row of body) {
|
|
28162
|
+
const tool = readCatalogRow(row);
|
|
28163
|
+
if (tool !== null) {
|
|
28164
|
+
tools.push(tool);
|
|
28165
|
+
}
|
|
28166
|
+
}
|
|
28167
|
+
return tools.sort((left, right) => left.address.localeCompare(right.address));
|
|
28168
|
+
}
|
|
28169
|
+
function filterTools(tools, filters) {
|
|
28170
|
+
const namespace = filters.namespace?.trim().toLowerCase();
|
|
28171
|
+
const owner = filters.owner?.trim().toLowerCase();
|
|
28172
|
+
const type = filters.type?.trim().toLowerCase();
|
|
28173
|
+
const category = filters.category?.trim().toLowerCase();
|
|
28174
|
+
return tools.filter((tool) => {
|
|
28175
|
+
if (namespace !== undefined && namespace !== "") {
|
|
28176
|
+
if (tool.namespace.toLowerCase() !== namespace && tool.provider.toLowerCase() !== namespace) {
|
|
28177
|
+
return false;
|
|
28178
|
+
}
|
|
28179
|
+
}
|
|
28180
|
+
if (owner !== undefined && owner !== "" && tool.owner !== owner) {
|
|
28181
|
+
return false;
|
|
28182
|
+
}
|
|
28183
|
+
if (type !== undefined && type !== "" && tool.type !== type) {
|
|
28184
|
+
return false;
|
|
28185
|
+
}
|
|
28186
|
+
if (category !== undefined && category !== "" && (tool.category ?? "").toLowerCase() !== category) {
|
|
28187
|
+
return false;
|
|
28188
|
+
}
|
|
28189
|
+
return true;
|
|
28190
|
+
});
|
|
28191
|
+
}
|
|
28192
|
+
function candidateNames(tool) {
|
|
28193
|
+
return [
|
|
28194
|
+
tool.address.toLowerCase(),
|
|
28195
|
+
tool.flatName.toLowerCase(),
|
|
28196
|
+
tool.wrapperToolName.toLowerCase(),
|
|
28197
|
+
tool.providerNativeToolName.toLowerCase(),
|
|
28198
|
+
tool.tool.toLowerCase()
|
|
28199
|
+
];
|
|
28200
|
+
}
|
|
28201
|
+
function resolveTool(tools, query) {
|
|
28202
|
+
const wanted = query.trim().toLowerCase();
|
|
28203
|
+
if (wanted === "") {
|
|
28204
|
+
return { kind: "no_match", near: [] };
|
|
28205
|
+
}
|
|
28206
|
+
for (let rank = 0;rank < 5; rank += 1) {
|
|
28207
|
+
const matches = tools.filter((tool) => candidateNames(tool)[rank] === wanted);
|
|
28208
|
+
if (matches.length === 1) {
|
|
28209
|
+
return { kind: "exact", tool: matches[0] };
|
|
28210
|
+
}
|
|
28211
|
+
if (matches.length > 1) {
|
|
28212
|
+
return { kind: "ambiguous", matches };
|
|
28213
|
+
}
|
|
28214
|
+
}
|
|
28215
|
+
return { kind: "no_match", near: nearAddresses(tools, wanted) };
|
|
28216
|
+
}
|
|
28217
|
+
function nearAddresses(tools, query, limit = 5) {
|
|
28218
|
+
const parts = query.toLowerCase().split(/[.\s_]+/u).filter((part) => part.length > 1);
|
|
28219
|
+
if (parts.length === 0) {
|
|
28220
|
+
return [];
|
|
28221
|
+
}
|
|
28222
|
+
const scored = tools.map((tool) => {
|
|
28223
|
+
const haystack = `${tool.address} ${tool.flatName}`.toLowerCase();
|
|
28224
|
+
const hits = parts.filter((part) => haystack.includes(part)).length;
|
|
28225
|
+
return { tool, hits };
|
|
28226
|
+
}).filter((entry) => entry.hits > 0);
|
|
28227
|
+
scored.sort((left, right) => right.hits - left.hits || left.tool.address.localeCompare(right.tool.address));
|
|
28228
|
+
return scored.slice(0, limit).map((entry) => entry.tool);
|
|
28229
|
+
}
|
|
28230
|
+
function noMatchError(query, near) {
|
|
28231
|
+
const suffix = near.length === 0 ? " Run `uru tools ls` to see every tool you can reach." : ` Did you mean: ${near.map((tool) => tool.address).join(", ")}?`;
|
|
28232
|
+
return refuse(TOOL_REFUSALS.noMatch, `No tool answers ${JSON.stringify(query)}.${suffix}`, "uru tools search");
|
|
28233
|
+
}
|
|
28234
|
+
async function fetchUnadoptedOffers(ctx) {
|
|
28235
|
+
let body;
|
|
28236
|
+
try {
|
|
28237
|
+
body = await client(ctx).api("/api/tool-control-plane/connection-offers", {
|
|
28238
|
+
method: "GET"
|
|
28239
|
+
});
|
|
28240
|
+
} catch {
|
|
28241
|
+
return [];
|
|
28242
|
+
}
|
|
28243
|
+
if (!Array.isArray(body)) {
|
|
28244
|
+
return [];
|
|
28245
|
+
}
|
|
28246
|
+
const offers = [];
|
|
28247
|
+
for (const row of body) {
|
|
28248
|
+
if (!isJsonObject(row))
|
|
28249
|
+
continue;
|
|
28250
|
+
const adoption = row["currentAdoptionStatus"];
|
|
28251
|
+
if (row["canAdopt"] !== true || adoption === "pending" || adoption === "approved") {
|
|
28252
|
+
continue;
|
|
28253
|
+
}
|
|
28254
|
+
const offerId = row["id"];
|
|
28255
|
+
const label = row["connectionDisplayName"];
|
|
28256
|
+
const service = row["connectionServiceName"];
|
|
28257
|
+
if (typeof offerId !== "string" || typeof label !== "string" || typeof service !== "string") {
|
|
28258
|
+
continue;
|
|
28259
|
+
}
|
|
28260
|
+
offers.push({ offerId, label, service: service.toLowerCase() });
|
|
28261
|
+
}
|
|
28262
|
+
return offers.sort((left, right) => left.label.localeCompare(right.label));
|
|
28263
|
+
}
|
|
28264
|
+
function offersMatching(offers, query) {
|
|
28265
|
+
const terms = query.toLowerCase().split(/[^a-z0-9]+/u).filter((term) => term.length > 1);
|
|
28266
|
+
if (terms.length === 0) {
|
|
28267
|
+
return [];
|
|
28268
|
+
}
|
|
28269
|
+
return offers.filter((offer) => {
|
|
28270
|
+
const haystack = `${offer.service} ${offer.label}`.toLowerCase();
|
|
28271
|
+
return terms.some((term) => haystack.includes(term));
|
|
28272
|
+
});
|
|
28273
|
+
}
|
|
28274
|
+
function notAdoptedError(query, offers) {
|
|
28275
|
+
const labels = offers.map((offer) => offer.label).join(", ");
|
|
28276
|
+
return refuse(TOOL_REFUSALS.notAdopted, `No tool answers ${JSON.stringify(query)} yet. This workspace offers ` + `${labels}, and you have not adopted it. Ask the owner or an admin ` + "to approve the adoption.", "uru tools connections");
|
|
28277
|
+
}
|
|
28278
|
+
function ambiguousError(query, matches) {
|
|
28279
|
+
return refuse(TOOL_REFUSALS.noMatch, `${JSON.stringify(query)} names ${String(matches.length)} tools. Use one full address: ${matches.slice(0, 5).map((tool) => tool.address).join(", ")}.`, "uru tools ls");
|
|
28280
|
+
}
|
|
28281
|
+
function searchTools(tools, query, limit, offset = 0) {
|
|
28282
|
+
const scored = tools.map((tool) => ({
|
|
28283
|
+
tool,
|
|
28284
|
+
score: scoreToolRelevance({
|
|
28285
|
+
address: tool.address,
|
|
28286
|
+
tool: tool.tool,
|
|
28287
|
+
title: tool.title,
|
|
28288
|
+
description: tool.description,
|
|
28289
|
+
category: tool.category
|
|
28290
|
+
}, query)
|
|
28291
|
+
})).filter((entry) => entry.score > 0);
|
|
28292
|
+
scored.sort((left, right) => right.score - left.score || left.tool.address.localeCompare(right.tool.address));
|
|
28293
|
+
return { hits: scored.slice(offset, offset + limit), total: scored.length, offset };
|
|
28294
|
+
}
|
|
28295
|
+
function summarizeConnections(tools) {
|
|
28296
|
+
const byConnection = new Map;
|
|
28297
|
+
for (const tool of tools) {
|
|
28298
|
+
const key = `${tool.provider}.${tool.owner}.${tool.connection}`;
|
|
28299
|
+
const existing = byConnection.get(key);
|
|
28300
|
+
byConnection.set(key, {
|
|
28301
|
+
connection: key,
|
|
28302
|
+
owner: tool.owner,
|
|
28303
|
+
provider: tool.provider,
|
|
28304
|
+
type: tool.type,
|
|
28305
|
+
typeLabel: tool.typeLabel,
|
|
28306
|
+
label: existing?.label ?? tool.connectionLabel,
|
|
28307
|
+
connectionId: existing?.connectionId ?? tool.connectionId,
|
|
28308
|
+
toolCount: (existing?.toolCount ?? 0) + 1,
|
|
28309
|
+
disabledToolCount: (existing?.disabledToolCount ?? 0) + (tool.enabled ? 0 : 1)
|
|
28310
|
+
});
|
|
28311
|
+
}
|
|
28312
|
+
return [...byConnection.values()].sort((left, right) => left.connection.localeCompare(right.connection));
|
|
28313
|
+
}
|
|
28314
|
+
function addressBranches(tools, prefix) {
|
|
28315
|
+
const wanted = prefix.trim().toLowerCase().replace(/\.+$/u, "");
|
|
28316
|
+
const depth = wanted === "" ? 0 : wanted.split(".").length;
|
|
28317
|
+
const branches = new Map;
|
|
28318
|
+
for (const tool of tools) {
|
|
28319
|
+
const segments = tool.address.split(".");
|
|
28320
|
+
if (wanted !== "" && !tool.address.toLowerCase().startsWith(`${wanted}.`)) {
|
|
28321
|
+
continue;
|
|
28322
|
+
}
|
|
28323
|
+
const segment = segments[depth];
|
|
28324
|
+
if (segment === undefined) {
|
|
28325
|
+
continue;
|
|
28326
|
+
}
|
|
28327
|
+
const existing = branches.get(segment);
|
|
28328
|
+
branches.set(segment, {
|
|
28329
|
+
count: (existing?.count ?? 0) + 1,
|
|
28330
|
+
leaf: depth === segments.length - 1
|
|
28331
|
+
});
|
|
28332
|
+
}
|
|
28333
|
+
return [...branches.entries()].map(([segment, value]) => ({
|
|
28334
|
+
segment,
|
|
28335
|
+
prefix: wanted === "" ? segment : `${wanted}.${segment}`,
|
|
28336
|
+
toolCount: value.count,
|
|
28337
|
+
leaf: value.leaf
|
|
28338
|
+
})).sort((left, right) => left.segment.localeCompare(right.segment));
|
|
28339
|
+
}
|
|
28340
|
+
|
|
28341
|
+
// src/tools-mcp.ts
|
|
28342
|
+
var MCP_PROTOCOL_VERSION = "2026-07-28";
|
|
28343
|
+
var PROTOCOL_VERSION_META_KEY = "io.modelcontextprotocol/protocolVersion";
|
|
28344
|
+
var CLIENT_CAPABILITIES_META_KEY = "io.modelcontextprotocol/clientCapabilities";
|
|
28345
|
+
var CLIENT_INFO_META_KEY = "io.modelcontextprotocol/clientInfo";
|
|
28346
|
+
function encodeMcpParamValue(value) {
|
|
28347
|
+
if (/^[ -~]*$/u.test(value)) {
|
|
28348
|
+
return value;
|
|
28349
|
+
}
|
|
28350
|
+
return `=?base64?${Buffer.from(value, "utf8").toString("base64")}?=`;
|
|
28351
|
+
}
|
|
28352
|
+
function resolveMcpUrl(args) {
|
|
28353
|
+
const chosen = args.explicit?.trim() || mcpProxyUrlFromApiUrl(args.apiUrl);
|
|
28354
|
+
if (chosen === undefined || chosen === "") {
|
|
28355
|
+
throw new CliError(`No MCP proxy is known for ${args.apiUrl}. Pass --mcp-url <url> to name one.`, { errorCode: "namespace_unavailable" });
|
|
28356
|
+
}
|
|
28357
|
+
const url2 = new URL(chosen);
|
|
28358
|
+
if (url2.pathname === "" || url2.pathname === "/") {
|
|
28359
|
+
url2.pathname = "/mcp";
|
|
28360
|
+
}
|
|
28361
|
+
return url2.toString().replace(/\/$/u, "");
|
|
28362
|
+
}
|
|
28363
|
+
|
|
28364
|
+
class McpCallError extends Error {
|
|
28365
|
+
status;
|
|
28366
|
+
data;
|
|
28367
|
+
constructor(message, status, data) {
|
|
28368
|
+
super(message);
|
|
28369
|
+
this.name = "McpCallError";
|
|
28370
|
+
this.status = status;
|
|
28371
|
+
this.data = data ?? null;
|
|
28372
|
+
}
|
|
28373
|
+
}
|
|
28374
|
+
function errorBodyOf(text) {
|
|
28375
|
+
try {
|
|
28376
|
+
const parsed = JSON.parse(text);
|
|
28377
|
+
return isJsonObject(parsed) ? parsed : null;
|
|
28378
|
+
} catch {
|
|
28379
|
+
return null;
|
|
28380
|
+
}
|
|
28381
|
+
}
|
|
28382
|
+
function parseEventStream(body) {
|
|
28383
|
+
let last;
|
|
28384
|
+
for (const rawEvent of body.split(/\n\n/u)) {
|
|
28385
|
+
const data = rawEvent.split(`
|
|
28386
|
+
`).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).join("");
|
|
28387
|
+
if (data === "") {
|
|
28388
|
+
continue;
|
|
28389
|
+
}
|
|
28390
|
+
last = JSON.parse(data);
|
|
28391
|
+
}
|
|
28392
|
+
if (last === undefined) {
|
|
28393
|
+
throw new McpCallError("The MCP event stream carried no data frame.");
|
|
28394
|
+
}
|
|
28395
|
+
return last;
|
|
28396
|
+
}
|
|
28397
|
+
async function mcpRequest(options, method, params) {
|
|
28398
|
+
const headers = {
|
|
28399
|
+
"content-type": "application/json",
|
|
28400
|
+
accept: "application/json, text/event-stream",
|
|
28401
|
+
authorization: `Bearer ${options.token}`,
|
|
28402
|
+
"mcp-protocol-version": MCP_PROTOCOL_VERSION,
|
|
28403
|
+
"mcp-method": method
|
|
28404
|
+
};
|
|
28405
|
+
const name = params["name"];
|
|
28406
|
+
if (method === "tools/call" && typeof name === "string") {
|
|
28407
|
+
headers["mcp-name"] = encodeMcpParamValue(name);
|
|
28408
|
+
}
|
|
28409
|
+
if (options.workspaceId !== undefined && options.workspaceId !== "") {
|
|
28410
|
+
headers["x-uru-workspace-id"] = options.workspaceId;
|
|
28411
|
+
}
|
|
28412
|
+
const timeout = AbortSignal.timeout(options.timeoutMs ?? 60000);
|
|
28413
|
+
const signal = options.signal === undefined ? timeout : AbortSignal.any([options.signal, timeout]);
|
|
28414
|
+
const response = await options.fetchImpl(options.url, {
|
|
28415
|
+
method: "POST",
|
|
28416
|
+
headers,
|
|
28417
|
+
signal,
|
|
28418
|
+
body: JSON.stringify({
|
|
28419
|
+
jsonrpc: "2.0",
|
|
28420
|
+
id: 1,
|
|
28421
|
+
method,
|
|
28422
|
+
params: {
|
|
28423
|
+
...params,
|
|
28424
|
+
_meta: {
|
|
28425
|
+
[PROTOCOL_VERSION_META_KEY]: MCP_PROTOCOL_VERSION,
|
|
28426
|
+
[CLIENT_CAPABILITIES_META_KEY]: {},
|
|
28427
|
+
[CLIENT_INFO_META_KEY]: { name: "uru-cli", version: "1" }
|
|
28428
|
+
}
|
|
28429
|
+
}
|
|
28430
|
+
})
|
|
28431
|
+
});
|
|
28432
|
+
const text = await response.text();
|
|
28433
|
+
if (!response.ok) {
|
|
28434
|
+
throw new McpCallError(`MCP ${method} failed with HTTP ${String(response.status)}: ${text.slice(0, 500)}`, response.status, errorBodyOf(text));
|
|
28435
|
+
}
|
|
28436
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
28437
|
+
const payload = contentType.includes("text/event-stream") ? parseEventStream(text) : JSON.parse(text);
|
|
28438
|
+
const envelope = Array.isArray(payload) ? payload[0] : payload;
|
|
28439
|
+
if (!isJsonObject(envelope)) {
|
|
28440
|
+
throw new McpCallError(`MCP ${method} returned a non-object response.`);
|
|
28441
|
+
}
|
|
28442
|
+
const error2 = envelope["error"];
|
|
28443
|
+
if (isJsonObject(error2)) {
|
|
28444
|
+
const code = error2["code"];
|
|
28445
|
+
const message = error2["message"];
|
|
28446
|
+
const data = error2["data"];
|
|
28447
|
+
throw new McpCallError(`MCP ${method} error ${typeof code === "number" ? String(code) : "unknown"}: ${typeof message === "string" ? message : "no message"}`, undefined, isJsonObject(data) ? data : null);
|
|
28448
|
+
}
|
|
28449
|
+
return envelope["result"] ?? null;
|
|
28450
|
+
}
|
|
28451
|
+
async function callConnectorTool(options, call) {
|
|
28452
|
+
return mcpRequest(options, "tools/call", {
|
|
28453
|
+
name: call.wrapperToolName,
|
|
28454
|
+
arguments: {
|
|
28455
|
+
tool_name: call.providerNativeToolName,
|
|
28456
|
+
parameters: call.parameters
|
|
28457
|
+
}
|
|
28458
|
+
});
|
|
28459
|
+
}
|
|
28460
|
+
|
|
28461
|
+
// src/tools-commands.ts
|
|
28462
|
+
var TOOLS_SUBCOMMANDS = new Set([
|
|
28463
|
+
"search",
|
|
28464
|
+
"ls",
|
|
28465
|
+
"list",
|
|
28466
|
+
"schema",
|
|
28467
|
+
"inspect",
|
|
28468
|
+
"run",
|
|
28469
|
+
"connections",
|
|
28470
|
+
"approve"
|
|
28471
|
+
]);
|
|
28472
|
+
function parseParkedCallEnvelope(value) {
|
|
28473
|
+
if (!isJsonObject(value)) {
|
|
28474
|
+
return null;
|
|
28475
|
+
}
|
|
28476
|
+
const nested = value["parked"];
|
|
28477
|
+
if (isJsonObject(nested)) {
|
|
28478
|
+
return parseParkedCallEnvelope(nested);
|
|
28479
|
+
}
|
|
28480
|
+
const read = readToolRefusalEnvelope(value);
|
|
28481
|
+
return read.outcome === "envelope" ? read.envelope : null;
|
|
28482
|
+
}
|
|
28483
|
+
function parkedLines(envelope) {
|
|
28484
|
+
return [
|
|
28485
|
+
`Status ${envelope.status}`,
|
|
28486
|
+
`Question id ${envelope.questionId ?? "(none — nothing to approve)"}`,
|
|
28487
|
+
...envelope.toolName === undefined ? [] : [`Tool ${envelope.toolName}`],
|
|
28488
|
+
`Write class ${envelope.writeClass}`,
|
|
28489
|
+
...envelope.questionId === null ? [] : [
|
|
28490
|
+
`Answer it uru tools approve ${envelope.questionId} --accept`,
|
|
28491
|
+
` uru tools approve ${envelope.questionId} --decline`
|
|
28492
|
+
]
|
|
28493
|
+
];
|
|
28494
|
+
}
|
|
28495
|
+
function isToolsAddressDrillDown(command) {
|
|
28496
|
+
const [first, second] = command;
|
|
28497
|
+
return first === "tools" && second !== undefined && !second.startsWith("-") && !TOOLS_SUBCOMMANDS.has(second);
|
|
28498
|
+
}
|
|
28499
|
+
function limitFlag(flags, fallback) {
|
|
28500
|
+
const raw = flags.values["--limit"];
|
|
28501
|
+
if (raw === undefined) {
|
|
28502
|
+
return fallback;
|
|
28503
|
+
}
|
|
28504
|
+
const parsed = Number.parseInt(raw, 10);
|
|
28505
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
28506
|
+
throw new CliError("--limit must be a positive whole number.");
|
|
28507
|
+
}
|
|
28508
|
+
return parsed;
|
|
28509
|
+
}
|
|
28510
|
+
function offsetFlag(flags) {
|
|
28511
|
+
const raw = flags.values["--offset"];
|
|
28512
|
+
if (raw === undefined) {
|
|
28513
|
+
return 0;
|
|
28514
|
+
}
|
|
28515
|
+
const parsed = Number.parseInt(raw, 10);
|
|
28516
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
28517
|
+
throw new CliError("--offset must be a whole number of 0 or more.");
|
|
28518
|
+
}
|
|
28519
|
+
return parsed;
|
|
28520
|
+
}
|
|
28521
|
+
function ownerFlag(flags) {
|
|
28522
|
+
const owner = flags.values["--owner"];
|
|
28523
|
+
if (owner === undefined) {
|
|
28524
|
+
return;
|
|
28525
|
+
}
|
|
28526
|
+
if (!TOOL_OWNER_CLASSES.includes(owner)) {
|
|
28527
|
+
throw new CliError(`--owner must be one of ${TOOL_OWNER_CLASSES.join(", ")}. ` + "It says whose connection a tool runs on; use --type for what kind it is.");
|
|
28528
|
+
}
|
|
28529
|
+
return owner;
|
|
28530
|
+
}
|
|
28531
|
+
function typeFlag(flags) {
|
|
28532
|
+
const type = flags.values["--type"];
|
|
28533
|
+
if (type === undefined) {
|
|
28534
|
+
return;
|
|
28535
|
+
}
|
|
28536
|
+
if (!TOOL_CONNECTION_TYPES.includes(type)) {
|
|
28537
|
+
throw new CliError(`--type must be one of ${TOOL_CONNECTION_TYPES.join(", ")}.`);
|
|
28538
|
+
}
|
|
28539
|
+
return type;
|
|
28540
|
+
}
|
|
28541
|
+
function catalogFilters(flags) {
|
|
28542
|
+
return {
|
|
28543
|
+
namespace: flags.values["--namespace"],
|
|
28544
|
+
owner: ownerFlag(flags),
|
|
28545
|
+
type: typeFlag(flags),
|
|
28546
|
+
category: flags.values["--category"]
|
|
28547
|
+
};
|
|
28548
|
+
}
|
|
28549
|
+
async function toolParameters(ctx, flags) {
|
|
28550
|
+
const raw = flags.values["--params-json"];
|
|
28551
|
+
if (raw === undefined) {
|
|
28552
|
+
return {};
|
|
28553
|
+
}
|
|
28554
|
+
const text = await readParamsJsonSource(raw, {
|
|
28555
|
+
cwd: ctx.cwd,
|
|
28556
|
+
...ctx.stdin === undefined ? {} : { stdin: ctx.stdin }
|
|
28557
|
+
});
|
|
28558
|
+
return parseParamsJson(text, "--params-json");
|
|
28559
|
+
}
|
|
28560
|
+
function toolRow(tool) {
|
|
28561
|
+
const write = tool.writeClass === "unknown" ? "?" : tool.writeClass;
|
|
28562
|
+
const summary = tool.description.split(`
|
|
28563
|
+
`)[0]?.trim() ?? "";
|
|
28564
|
+
const kind = tool.category === null ? tool.typeLabel : `${tool.typeLabel}/${tool.category}`;
|
|
28565
|
+
return ` ${tool.address} [${write}] ${kind} ${summary}`;
|
|
28566
|
+
}
|
|
28567
|
+
function toolJson(tool) {
|
|
28568
|
+
return {
|
|
28569
|
+
address: tool.address,
|
|
28570
|
+
owner: tool.owner,
|
|
28571
|
+
type: tool.type,
|
|
28572
|
+
typeLabel: tool.typeLabel,
|
|
28573
|
+
provider: tool.provider,
|
|
28574
|
+
connection: tool.connection,
|
|
28575
|
+
connectionLabel: tool.connectionLabel,
|
|
28576
|
+
tool: tool.tool,
|
|
28577
|
+
flatName: tool.flatName,
|
|
28578
|
+
writeClass: tool.writeClass,
|
|
28579
|
+
enabled: tool.enabled,
|
|
28580
|
+
description: tool.description,
|
|
28581
|
+
...tool.title === null ? {} : { title: tool.title },
|
|
28582
|
+
...tool.category === null ? {} : { category: tool.category }
|
|
28583
|
+
};
|
|
28584
|
+
}
|
|
28585
|
+
function groupedListing(tools) {
|
|
28586
|
+
if (tools.length === 0) {
|
|
28587
|
+
return "No tools. Connect an app in Uru, or ask for access to a workspace offer.";
|
|
28588
|
+
}
|
|
28589
|
+
const lines = [];
|
|
28590
|
+
let currentConnection = "";
|
|
28591
|
+
for (const tool of tools) {
|
|
28592
|
+
const connection = `${tool.provider}.${tool.owner}.${tool.connection}`;
|
|
28593
|
+
if (connection !== currentConnection) {
|
|
28594
|
+
currentConnection = connection;
|
|
28595
|
+
lines.push(`${connection} ${tool.typeLabel} (${tool.connectionLabel})`);
|
|
28596
|
+
}
|
|
28597
|
+
lines.push(toolRow(tool));
|
|
28598
|
+
}
|
|
28599
|
+
lines.push("");
|
|
28600
|
+
lines.push(`${String(tools.length)} tools.`);
|
|
28601
|
+
return lines.join(`
|
|
28602
|
+
`);
|
|
28603
|
+
}
|
|
28604
|
+
async function toolsSearch(ctx, args) {
|
|
28605
|
+
const flags = parseLocalFlags(args, ["tools", "search"]);
|
|
28606
|
+
const query = flags.positionals.join(" ").trim();
|
|
28607
|
+
if (query === "") {
|
|
28608
|
+
throw new CliError('Usage: uru tools search "<task>" [--limit N] [--offset N]');
|
|
28609
|
+
}
|
|
28610
|
+
const catalog = filterTools(await fetchToolCatalog(ctx), catalogFilters(flags));
|
|
28611
|
+
const limit = limitFlag(flags, 20);
|
|
28612
|
+
const offset = offsetFlag(flags);
|
|
28613
|
+
const { hits, total } = searchTools(catalog, query, limit, offset);
|
|
28614
|
+
if (total === 0) {
|
|
28615
|
+
const offers = offersMatching(await fetchUnadoptedOffers(ctx), query);
|
|
28616
|
+
if (offers.length > 0) {
|
|
28617
|
+
throw notAdoptedError(query, offers);
|
|
28618
|
+
}
|
|
28619
|
+
throw noMatchError(query, []);
|
|
28620
|
+
}
|
|
28621
|
+
const nextOffset = offset + hits.length;
|
|
28622
|
+
const hasMore = nextOffset < total;
|
|
28623
|
+
emitValue(ctx, {
|
|
28624
|
+
hits: hits.map((hit) => ({ ...toolJson(hit.tool), score: hit.score })),
|
|
28625
|
+
total,
|
|
28626
|
+
hasMore,
|
|
28627
|
+
nextOffset
|
|
28628
|
+
}, [
|
|
28629
|
+
...hits.map((hit) => `${toolRow(hit.tool)} (score ${String(hit.score)})`),
|
|
28630
|
+
"",
|
|
28631
|
+
`${String(hits.length)} of ${String(total)} matches${offset > 0 ? `, from ${String(offset)}` : ""}.${hasMore ? ` Next page: --offset ${String(nextOffset)}.` : ""}`
|
|
28632
|
+
].join(`
|
|
28633
|
+
`));
|
|
28634
|
+
}
|
|
28635
|
+
async function toolsList(ctx, args) {
|
|
28636
|
+
const flags = parseLocalFlags(args, ["tools", "ls"]);
|
|
28637
|
+
const tools = filterTools(await fetchToolCatalog(ctx), catalogFilters(flags));
|
|
28638
|
+
emitValue(ctx, tools.map(toolJson), groupedListing(tools));
|
|
28639
|
+
}
|
|
28640
|
+
async function toolsConnections(ctx, args) {
|
|
28641
|
+
const flags = parseLocalFlags(args, ["tools", "connections"]);
|
|
28642
|
+
const tools = filterTools(await fetchToolCatalog(ctx), {
|
|
28643
|
+
owner: ownerFlag(flags),
|
|
28644
|
+
type: typeFlag(flags)
|
|
28645
|
+
});
|
|
28646
|
+
const connections = summarizeConnections(tools);
|
|
28647
|
+
emitValue(ctx, connections.map((connection) => ({ ...connection })), connections.length === 0 ? "No connections." : connections.map((connection) => ` ${connection.connection} ${connection.typeLabel} ${connection.label} ${String(connection.toolCount)} tools${connection.disabledToolCount > 0 ? ` (${String(connection.disabledToolCount)} disabled)` : ""}`).join(`
|
|
28648
|
+
`));
|
|
28649
|
+
}
|
|
28650
|
+
async function requireTool(ctx, query) {
|
|
28651
|
+
const catalog = await fetchToolCatalog(ctx);
|
|
28652
|
+
const resolution = resolveTool(catalog, query);
|
|
28653
|
+
if (resolution.kind === "no_match") {
|
|
28654
|
+
throw noMatchError(query, resolution.near);
|
|
28655
|
+
}
|
|
28656
|
+
if (resolution.kind === "ambiguous") {
|
|
28657
|
+
throw ambiguousError(query, resolution.matches);
|
|
28658
|
+
}
|
|
28659
|
+
return { tool: resolution.tool, catalog };
|
|
28660
|
+
}
|
|
28661
|
+
async function toolSchemaAnswer(ctx, tool) {
|
|
28662
|
+
if (isPlatformTool(tool)) {
|
|
28663
|
+
return client(ctx).getToolSchema(tool.tool);
|
|
28664
|
+
}
|
|
28665
|
+
return {
|
|
28666
|
+
address: tool.address,
|
|
28667
|
+
inputSchema: tool.inputSchema,
|
|
28668
|
+
outputSchema: tool.outputSchema
|
|
28669
|
+
};
|
|
28670
|
+
}
|
|
28671
|
+
async function toolsSchema(ctx, args) {
|
|
28672
|
+
const flags = parseLocalFlags(args, ["tools", "schema"]);
|
|
28673
|
+
const query = flags.positionals[0];
|
|
28674
|
+
if (query === undefined) {
|
|
28675
|
+
throw new CliError("Usage: uru tools schema <address>");
|
|
28676
|
+
}
|
|
28677
|
+
const { tool } = await requireTool(ctx, query);
|
|
28678
|
+
emitValue(ctx, await toolSchemaAnswer(ctx, tool));
|
|
28679
|
+
}
|
|
28680
|
+
function policyAnswer(tool) {
|
|
28681
|
+
return {
|
|
28682
|
+
owner: tool.owner,
|
|
28683
|
+
connection: `${tool.provider}.${tool.owner}.${tool.connection}`,
|
|
28684
|
+
connectionLabel: tool.connectionLabel,
|
|
28685
|
+
type: tool.type,
|
|
28686
|
+
typeLabel: tool.typeLabel,
|
|
28687
|
+
category: tool.category,
|
|
28688
|
+
writeClass: tool.writeClass,
|
|
28689
|
+
namespaceServing: tool.enabled,
|
|
28690
|
+
execution: isPlatformTool(tool) ? "platform_execute_route" : "mcp_proxy_tools_call"
|
|
28691
|
+
};
|
|
28692
|
+
}
|
|
28693
|
+
async function toolsInspect(ctx, args) {
|
|
28694
|
+
const flags = parseLocalFlags(args, ["tools", "inspect"]);
|
|
28695
|
+
const query = flags.positionals[0];
|
|
28696
|
+
if (query === undefined) {
|
|
28697
|
+
throw new CliError("Usage: uru tools inspect <address>");
|
|
28698
|
+
}
|
|
28699
|
+
const { tool } = await requireTool(ctx, query);
|
|
28700
|
+
const answer = policyAnswer(tool);
|
|
28701
|
+
emitValue(ctx, { ...toolJson(tool), policy: answer }, [
|
|
28702
|
+
`Address ${tool.address}`,
|
|
28703
|
+
`Owner ${tool.owner}`,
|
|
28704
|
+
`Connection ${tool.connectionLabel} (${tool.connection})`,
|
|
28705
|
+
`Type ${tool.typeLabel} (${tool.type})`,
|
|
28706
|
+
`Category ${tool.category ?? "-"}`,
|
|
28707
|
+
`Write class ${tool.writeClass}`,
|
|
28708
|
+
`Serving ${tool.enabled ? "yes" : "no"}`,
|
|
28709
|
+
`Runs through ${String(answer["execution"])}`,
|
|
28710
|
+
`Flat name ${tool.flatName}`,
|
|
28711
|
+
"",
|
|
28712
|
+
tool.description
|
|
28713
|
+
].join(`
|
|
28714
|
+
`));
|
|
28715
|
+
}
|
|
28716
|
+
function assertServing(tool) {
|
|
28717
|
+
if (tool.enabled) {
|
|
28718
|
+
return;
|
|
28719
|
+
}
|
|
28720
|
+
throw refuse(TOOL_REFUSALS.namespaceUnavailable, `${tool.address} is in your catalog but its connection is not serving tools right now.`, `uru tools inspect ${tool.address}`);
|
|
28721
|
+
}
|
|
28722
|
+
function connectorRefusal(tool, error2) {
|
|
28723
|
+
const message = error2.message;
|
|
28724
|
+
const lower = message.toLowerCase();
|
|
28725
|
+
const envelope = parseParkedCallEnvelope(error2.data);
|
|
28726
|
+
if (envelope !== null && envelope.questionId !== null) {
|
|
28727
|
+
return refuse(envelope.status === "approval_required" ? TOOL_REFUSALS.approvalRequired : TOOL_REFUSALS.refusedByPolicy, [
|
|
28728
|
+
`${tool.address} did not run: ${envelope.status}.`,
|
|
28729
|
+
...parkedLines(envelope)
|
|
28730
|
+
].join(`
|
|
28731
|
+
`), `uru tools approve ${envelope.questionId} --accept`);
|
|
28732
|
+
}
|
|
28733
|
+
if (lower.includes("approval")) {
|
|
28734
|
+
return refuse(TOOL_REFUSALS.approvalRequired, `${tool.address} needs an approval before it runs: ${message}`, "uru tools inspect " + tool.address);
|
|
28735
|
+
}
|
|
28736
|
+
if (lower.includes("not connected") || lower.includes("missing exact connection") || lower.includes("connected_account")) {
|
|
28737
|
+
return refuse(TOOL_REFUSALS.notConnected, `${tool.address} has no live connection: ${message}. Connect ${tool.provider} in Uru, then retry.`, `uru tools connections`);
|
|
28738
|
+
}
|
|
28739
|
+
if (error2.status === 403 || lower.includes("policy") || lower.includes("denied")) {
|
|
28740
|
+
return refuse(TOOL_REFUSALS.refusedByPolicy, `${tool.address} was refused by workspace policy: ${message}`, `uru tools inspect ${tool.address}`);
|
|
28741
|
+
}
|
|
28742
|
+
if (error2.status === 404) {
|
|
28743
|
+
return refuse(TOOL_REFUSALS.namespaceUnavailable, `${tool.address} is in your catalog but the proxy does not serve it right now: ${message}`);
|
|
28744
|
+
}
|
|
28745
|
+
return refuse(TOOL_REFUSALS.refusedByPolicy, message);
|
|
28746
|
+
}
|
|
28747
|
+
async function runOneCall(ctx, tool, parameters, mcpUrl) {
|
|
28748
|
+
if (isPlatformTool(tool)) {
|
|
28749
|
+
const operation = getPlatformOperationForToolCall(tool.tool, parameters);
|
|
28750
|
+
if (operation !== undefined) {
|
|
28751
|
+
requireOperationConfirmation(ctx, operation, parameters);
|
|
28752
|
+
} else if (isRegisteredPlatformToolName(tool.tool)) {
|
|
28753
|
+
throw new CliError(unmappedPlatformToolCallMessage(tool.tool, parameters));
|
|
28754
|
+
}
|
|
28755
|
+
return client(ctx).runTool(tool.tool, parameters);
|
|
28756
|
+
}
|
|
28757
|
+
if (ctx.token === undefined) {
|
|
28758
|
+
throw new CliError("Authentication required. Run `uru login`, pass --token, or set URU_TOKEN.");
|
|
28759
|
+
}
|
|
28760
|
+
try {
|
|
28761
|
+
return await callConnectorTool({
|
|
28762
|
+
url: resolveMcpUrl({ apiUrl: ctx.apiUrl, explicit: mcpUrl }),
|
|
28763
|
+
token: ctx.token,
|
|
28764
|
+
workspaceId: ctx.workspace,
|
|
28765
|
+
fetchImpl: ctx.fetch ?? fetch,
|
|
28766
|
+
signal: ctx.abortSignal
|
|
28767
|
+
}, {
|
|
28768
|
+
wrapperToolName: tool.wrapperToolName,
|
|
28769
|
+
providerNativeToolName: tool.providerNativeToolName,
|
|
28770
|
+
parameters
|
|
28771
|
+
});
|
|
28772
|
+
} catch (error2) {
|
|
28773
|
+
if (error2 instanceof McpCallError) {
|
|
28774
|
+
throw connectorRefusal(tool, error2);
|
|
28775
|
+
}
|
|
28776
|
+
throw error2;
|
|
28777
|
+
}
|
|
28778
|
+
}
|
|
28779
|
+
async function toolsRun(ctx, args) {
|
|
28780
|
+
const flags = parseLocalFlags(args, ["tools", "run"]);
|
|
28781
|
+
const query = flags.positionals[0];
|
|
28782
|
+
if (query === undefined) {
|
|
28783
|
+
throw new CliError("Usage: uru tools run <address> -d '{...}'");
|
|
28784
|
+
}
|
|
28785
|
+
const { tool } = await requireTool(ctx, query);
|
|
28786
|
+
if (flags.booleans.has("--get-schema")) {
|
|
28787
|
+
emitValue(ctx, await toolSchemaAnswer(ctx, tool));
|
|
28788
|
+
return;
|
|
28789
|
+
}
|
|
28790
|
+
const parameters = await toolParameters(ctx, flags);
|
|
28791
|
+
const parallel = flags.booleans.has("--parallel");
|
|
28792
|
+
if (parallel && !Array.isArray(parameters)) {
|
|
28793
|
+
throw new CliError("--parallel needs a JSON array of input objects.");
|
|
28794
|
+
}
|
|
28795
|
+
if (!parallel && !isJsonObject(parameters)) {
|
|
28796
|
+
throw new CliError("-d must be a JSON object. Use --parallel for an array.");
|
|
28797
|
+
}
|
|
28798
|
+
if (flags.booleans.has("--dry-run")) {
|
|
28799
|
+
emitValue(ctx, {
|
|
28800
|
+
dryRun: true,
|
|
28801
|
+
address: tool.address,
|
|
28802
|
+
wrapperToolName: tool.wrapperToolName,
|
|
28803
|
+
providerNativeToolName: tool.providerNativeToolName,
|
|
28804
|
+
parameters,
|
|
28805
|
+
policy: policyAnswer(tool)
|
|
28806
|
+
}, [
|
|
28807
|
+
`Dry run — nothing was executed.`,
|
|
28808
|
+
`Address ${tool.address}`,
|
|
28809
|
+
`Connection ${tool.connectionLabel} (${tool.connection})`,
|
|
28810
|
+
`Write class ${tool.writeClass}`,
|
|
28811
|
+
`Serving ${tool.enabled ? "yes" : "no"}`,
|
|
28812
|
+
`Runs through ${String(policyAnswer(tool)["execution"])}`
|
|
28813
|
+
].join(`
|
|
28814
|
+
`));
|
|
28815
|
+
return;
|
|
28816
|
+
}
|
|
28817
|
+
assertServing(tool);
|
|
28818
|
+
const mcpUrl = flags.values["--mcp-url"];
|
|
28819
|
+
if (parallel) {
|
|
28820
|
+
const inputs = parameters;
|
|
28821
|
+
const results = [];
|
|
28822
|
+
for (const input of inputs) {
|
|
28823
|
+
if (!isJsonObject(input)) {
|
|
28824
|
+
throw new CliError("--parallel needs every array entry to be an object.");
|
|
28825
|
+
}
|
|
28826
|
+
results.push(await runOneCall(ctx, tool, input, mcpUrl));
|
|
28827
|
+
}
|
|
28828
|
+
emitValue(ctx, { address: tool.address, results });
|
|
28829
|
+
return;
|
|
28830
|
+
}
|
|
28831
|
+
const result = await runOneCall(ctx, tool, parameters, mcpUrl);
|
|
28832
|
+
const parked = parseParkedCallEnvelope(result);
|
|
28833
|
+
if (parked !== null) {
|
|
28834
|
+
emitValue(ctx, result, [
|
|
28835
|
+
`${tool.address} did not run yet.`,
|
|
28836
|
+
...parkedLines(parked)
|
|
28837
|
+
].join(`
|
|
28838
|
+
`));
|
|
28839
|
+
return;
|
|
28840
|
+
}
|
|
28841
|
+
emitValue(ctx, result);
|
|
28842
|
+
}
|
|
28843
|
+
async function toolsApprove(ctx, args) {
|
|
28844
|
+
const flags = parseLocalFlags(args, ["tools", "approve"]);
|
|
28845
|
+
const questionId = flags.positionals[0]?.trim();
|
|
28846
|
+
if (questionId === undefined || questionId === "") {
|
|
28847
|
+
throw new CliError("Usage: uru tools approve <question-id> --accept | --decline");
|
|
28848
|
+
}
|
|
28849
|
+
const accept = flags.booleans.has("--accept");
|
|
28850
|
+
const decline = flags.booleans.has("--decline");
|
|
28851
|
+
if (accept === decline) {
|
|
28852
|
+
throw new CliError("Choose exactly one of --accept or --decline for `uru tools approve`.");
|
|
28853
|
+
}
|
|
28854
|
+
const decision = accept ? "accept" : "decline";
|
|
28855
|
+
const answer = await client(ctx).api(`/api/tool-approvals/${encodeURIComponent(questionId)}`, { method: "POST", body: { decision } });
|
|
28856
|
+
if (!isJsonObject(answer)) {
|
|
28857
|
+
throw new CliError("The approval answer route returned a non-object response.");
|
|
28858
|
+
}
|
|
28859
|
+
const alreadyAnswered = answer["alreadyResolved"] === true;
|
|
28860
|
+
const settled = typeof answer["decision"] === "string" ? answer["decision"] : "unknown";
|
|
28861
|
+
emitValue(ctx, answer, [
|
|
28862
|
+
alreadyAnswered ? `This call was already answered: ${settled}.` : `Answered: ${settled}.`,
|
|
28863
|
+
`Question id ${questionId}`
|
|
28864
|
+
].join(`
|
|
28865
|
+
`));
|
|
28866
|
+
}
|
|
28867
|
+
async function toolsDrillDown(ctx, command) {
|
|
28868
|
+
const flags = parseLocalFlags(command.slice(1), ["tools"]);
|
|
28869
|
+
const prefix = flags.positionals.join(".").trim();
|
|
28870
|
+
const catalog = await fetchToolCatalog(ctx);
|
|
28871
|
+
const match = flags.values["--match"]?.trim().toLowerCase();
|
|
28872
|
+
const resolution = resolveTool(catalog, prefix);
|
|
28873
|
+
if (resolution.kind === "exact") {
|
|
28874
|
+
const tool = resolution.tool;
|
|
28875
|
+
emitValue(ctx, {
|
|
28876
|
+
...toolJson(tool),
|
|
28877
|
+
inputSchema: tool.inputSchema,
|
|
28878
|
+
outputSchema: tool.outputSchema
|
|
28879
|
+
}, [
|
|
28880
|
+
`${tool.address} [${tool.writeClass}]`,
|
|
28881
|
+
tool.description,
|
|
28882
|
+
"",
|
|
28883
|
+
"Input",
|
|
28884
|
+
JSON.stringify(tool.inputSchema, null, 2),
|
|
28885
|
+
"",
|
|
28886
|
+
"Output",
|
|
28887
|
+
JSON.stringify(tool.outputSchema ?? {}, null, 2)
|
|
28888
|
+
].join(`
|
|
28889
|
+
`));
|
|
28890
|
+
return;
|
|
28891
|
+
}
|
|
28892
|
+
const branches = addressBranches(catalog, prefix).filter((branch) => match === undefined || branch.segment.toLowerCase().includes(match));
|
|
28893
|
+
if (branches.length === 0) {
|
|
28894
|
+
throw noMatchError(prefix, resolution.kind === "no_match" ? resolution.near : []);
|
|
28895
|
+
}
|
|
28896
|
+
const limited = branches.slice(0, limitFlag(flags, 50));
|
|
28897
|
+
emitValue(ctx, { prefix, branches: limited.map((branch) => ({ ...branch })), total: branches.length }, [
|
|
28898
|
+
`${prefix}.*`,
|
|
28899
|
+
...limited.map((branch) => ` ${branch.prefix}${branch.leaf ? "" : ".*"} ${String(branch.toolCount)} tools`),
|
|
28900
|
+
"",
|
|
28901
|
+
`${String(limited.length)} of ${String(branches.length)} branches.`
|
|
28902
|
+
].join(`
|
|
28903
|
+
`));
|
|
28904
|
+
}
|
|
28905
|
+
async function dispatchTools(ctx, command) {
|
|
28906
|
+
const sub = command[1];
|
|
28907
|
+
const args = command.slice(2);
|
|
28908
|
+
await withWorkspace(ctx);
|
|
28909
|
+
if (sub === "search") {
|
|
28910
|
+
await toolsSearch(ctx, args);
|
|
28911
|
+
return;
|
|
28912
|
+
}
|
|
28913
|
+
if (sub === "ls" || sub === "list") {
|
|
28914
|
+
await toolsList(ctx, args);
|
|
28915
|
+
return;
|
|
28916
|
+
}
|
|
28917
|
+
if (sub === "connections") {
|
|
28918
|
+
await toolsConnections(ctx, args);
|
|
28919
|
+
return;
|
|
28920
|
+
}
|
|
28921
|
+
if (sub === "schema") {
|
|
28922
|
+
await toolsSchema(ctx, args);
|
|
28923
|
+
return;
|
|
28924
|
+
}
|
|
28925
|
+
if (sub === "inspect") {
|
|
28926
|
+
await toolsInspect(ctx, args);
|
|
28927
|
+
return;
|
|
28928
|
+
}
|
|
28929
|
+
if (sub === "run") {
|
|
28930
|
+
await toolsRun(ctx, args);
|
|
28931
|
+
return;
|
|
28932
|
+
}
|
|
28933
|
+
if (sub === "approve") {
|
|
28934
|
+
await toolsApprove(ctx, args);
|
|
28935
|
+
return;
|
|
28936
|
+
}
|
|
28937
|
+
if (isToolsAddressDrillDown(command)) {
|
|
28938
|
+
await toolsDrillDown(ctx, command);
|
|
28939
|
+
return;
|
|
28940
|
+
}
|
|
28941
|
+
throw new CliError("Usage: uru tools search|ls|schema|inspect|run|approve|connections, or `uru tools <partial address> --help`.");
|
|
28942
|
+
}
|
|
28943
|
+
function unmappedPlatformToolCallMessage(toolName, params) {
|
|
28944
|
+
const op2 = typeof params["op"] === "string" ? params["op"] : undefined;
|
|
28945
|
+
const mistakenOperation = typeof params["operation"] === "string" ? params["operation"] : undefined;
|
|
28946
|
+
const normalizedTool = toolName.trim().toLowerCase();
|
|
28947
|
+
const normalizedOp = op2?.trim().toLowerCase();
|
|
28948
|
+
if (mistakenOperation !== undefined && mistakenOperation.trim() !== "" && (op2 === undefined || op2.trim() === "")) {
|
|
28949
|
+
const suggested = mistakenOperation.trim();
|
|
28950
|
+
return `Platform tools take \`op\`, not \`operation\` (got operation=${suggested}). ` + `Retry with op=${suggested}.`;
|
|
28951
|
+
}
|
|
28952
|
+
if (normalizedTool === "library_fs" && (normalizedOp === "ls" || normalizedOp === "stat" || normalizedOp === "read" || normalizedOp === "search")) {
|
|
28953
|
+
return `library_fs does not serve read op=${normalizedOp}. ` + "Use library_query (or `uru library ls|stat|search|read` / " + "`uru operations run library.<op>`).";
|
|
28954
|
+
}
|
|
28955
|
+
if (normalizedOp !== undefined) {
|
|
28956
|
+
return `No registered safety policy for ${toolName} op=${normalizedOp}. ` + "Refuse to execute. Check `uru operations search` for the supported operation id.";
|
|
28957
|
+
}
|
|
28958
|
+
return `No registered safety policy for ${toolName}. ` + "Refuse to execute without a mapped platform operation.";
|
|
28959
|
+
}
|
|
28960
|
+
|
|
27650
28961
|
// src/token-env-secret-commands.ts
|
|
27651
28962
|
import { spawn } from "node:child_process";
|
|
27652
28963
|
import { chmod as chmod2, mkdir as mkdir6, readFile as readFile9, writeFile as writeFile5 } from "node:fs/promises";
|
|
@@ -28349,7 +29660,7 @@ var cliCommandRuntimeBindings = {
|
|
|
28349
29660
|
await dispatchUpdate(ctx, command.slice(1));
|
|
28350
29661
|
},
|
|
28351
29662
|
tools: async ({ ctx, command }) => {
|
|
28352
|
-
await dispatchTools(ctx, command
|
|
29663
|
+
await dispatchTools(ctx, command);
|
|
28353
29664
|
},
|
|
28354
29665
|
init: async ({ ctx, command }) => {
|
|
28355
29666
|
await withWorkspace(ctx);
|
|
@@ -28509,7 +29820,7 @@ async function dispatchRegistryFamilyCommand({
|
|
|
28509
29820
|
// src/config.ts
|
|
28510
29821
|
import { chmod as chmod3, mkdir as mkdir7, open as open2, readFile as readFile10, rename as rename3, rm as rm4 } from "node:fs/promises";
|
|
28511
29822
|
import { constants as constants2 } from "node:fs";
|
|
28512
|
-
import { randomUUID as
|
|
29823
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
28513
29824
|
import { homedir as homedir2 } from "node:os";
|
|
28514
29825
|
import { dirname as dirname7, join as join5 } from "node:path";
|
|
28515
29826
|
function resolveConfigHome(env = process.env) {
|
|
@@ -28545,7 +29856,7 @@ function createConfigStore(path = configPath()) {
|
|
|
28545
29856
|
await mkdir7(dirname7(path), { recursive: true });
|
|
28546
29857
|
const body = `${JSON.stringify(sanitizeConfig(config2), null, 2)}
|
|
28547
29858
|
`;
|
|
28548
|
-
const temporaryPath = `${path}.${process.pid}.${
|
|
29859
|
+
const temporaryPath = `${path}.${process.pid}.${randomUUID3()}.tmp`;
|
|
28549
29860
|
let handle = null;
|
|
28550
29861
|
try {
|
|
28551
29862
|
handle = await open2(temporaryPath, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, constants2.S_IRUSR | constants2.S_IWUSR);
|
|
@@ -29153,7 +30464,8 @@ async function runCli(options) {
|
|
|
29153
30464
|
writeText(io, CLI_VERSION);
|
|
29154
30465
|
return ExitCode.Ok;
|
|
29155
30466
|
}
|
|
29156
|
-
|
|
30467
|
+
const helpIsAddressDrillDown = parsed.flags.help && isToolsAddressDrillDown(parsed.command);
|
|
30468
|
+
if (parsed.flags.help && !helpIsAddressDrillDown || parsed.command.length === 0) {
|
|
29157
30469
|
const helpTerminal = detectTerminalCapabilities({
|
|
29158
30470
|
flags: parsed.flags,
|
|
29159
30471
|
...options.env === undefined ? {} : { env: options.env },
|