@topolo/cli 0.9.5 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +108 -16
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { platform as platform2 } from "os";
|
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { stdin as input } from "process";
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// node_modules/@topolo/sdk/dist/index.js
|
|
13
13
|
function applyAuthHeaders(headers, credential) {
|
|
14
14
|
if (credential.kind === "api_key") {
|
|
15
15
|
headers.set("X-Api-Key", credential.apiKey);
|
|
@@ -86,6 +86,7 @@ var ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
|
86
86
|
var TOPOLO_REQUEST_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
87
87
|
var CLOUDFLARE_RAY_ID_PATTERN = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
88
88
|
var PERMISSION_PATTERN = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
89
|
+
var PUBLIC_FIELD_PATH_PATTERN = /^[$.a-z0-9_[\]-]{1,160}$/i;
|
|
89
90
|
var RETRY_AFTER_DATE_PATTERN = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
90
91
|
var PUBLIC_DETAIL_SOURCES = /* @__PURE__ */ new Set([
|
|
91
92
|
"api_key_introspection",
|
|
@@ -198,7 +199,11 @@ function extractTopoloServiceErrorDetails(body) {
|
|
|
198
199
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
199
200
|
}
|
|
200
201
|
if (candidate === void 0) candidate = record;
|
|
201
|
-
if (
|
|
202
|
+
if (Array.isArray(candidate)) {
|
|
203
|
+
const paths = canonicalTopoloValidationPaths(candidate);
|
|
204
|
+
return paths.length ? { paths } : void 0;
|
|
205
|
+
}
|
|
206
|
+
if (!candidate || typeof candidate !== "object") return void 0;
|
|
202
207
|
const source = candidate;
|
|
203
208
|
const safe = {};
|
|
204
209
|
for (const [key, value] of Object.entries(source)) {
|
|
@@ -235,12 +240,42 @@ function extractTopoloServiceErrorDetails(body) {
|
|
|
235
240
|
continue;
|
|
236
241
|
}
|
|
237
242
|
if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
|
|
238
|
-
const paths = value.filter((item) => typeof item === "string" &&
|
|
243
|
+
const paths = value.filter((item) => typeof item === "string" && PUBLIC_FIELD_PATH_PATTERN.test(item));
|
|
239
244
|
if (paths.length) safe[key] = paths;
|
|
240
245
|
}
|
|
241
246
|
}
|
|
242
247
|
return Object.keys(safe).length ? safe : void 0;
|
|
243
248
|
}
|
|
249
|
+
function canonicalTopoloValidationPaths(issues) {
|
|
250
|
+
const paths = [];
|
|
251
|
+
for (const issue of issues) {
|
|
252
|
+
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
253
|
+
const record = issue;
|
|
254
|
+
const path = canonicalTopoloValidationPath(record.field) ?? canonicalTopoloValidationPath(record.path);
|
|
255
|
+
if (path && !paths.includes(path)) paths.push(path);
|
|
256
|
+
if (paths.length === 32) break;
|
|
257
|
+
}
|
|
258
|
+
return paths;
|
|
259
|
+
}
|
|
260
|
+
function canonicalTopoloValidationPath(value) {
|
|
261
|
+
if (typeof value === "string") {
|
|
262
|
+
const path2 = value.trim();
|
|
263
|
+
return PUBLIC_FIELD_PATH_PATTERN.test(path2) ? path2 : void 0;
|
|
264
|
+
}
|
|
265
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 32) return void 0;
|
|
266
|
+
const segments = value.map((segment) => {
|
|
267
|
+
if (typeof segment === "number" && Number.isSafeInteger(segment) && segment >= 0) {
|
|
268
|
+
return String(segment);
|
|
269
|
+
}
|
|
270
|
+
if (typeof segment === "string" && /^[a-z0-9_$-]{1,80}$/i.test(segment)) {
|
|
271
|
+
return segment;
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
});
|
|
275
|
+
if (segments.includes(null)) return void 0;
|
|
276
|
+
const path = segments.join(".");
|
|
277
|
+
return PUBLIC_FIELD_PATH_PATTERN.test(path) ? path : void 0;
|
|
278
|
+
}
|
|
244
279
|
function publicTopoloErrorMessage(error) {
|
|
245
280
|
const code = canonicalTopoloErrorCode(
|
|
246
281
|
typeof error === "string" ? error : error.code ?? extractTopoloServiceErrorCode(error.body)
|
|
@@ -1017,16 +1052,29 @@ var TopoloClient = class _TopoloClient {
|
|
|
1017
1052
|
clearTimeout(timeoutId);
|
|
1018
1053
|
}
|
|
1019
1054
|
};
|
|
1055
|
+
const fetchWithReadRetry = async () => {
|
|
1056
|
+
const maxAttempts = method === "GET" ? 2 : 1;
|
|
1057
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1058
|
+
try {
|
|
1059
|
+
return await fetchOnce();
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
const transportFailure = !(error instanceof TopoloSdkError) && !opts.signal?.aborted;
|
|
1062
|
+
if (!transportFailure || attempt === maxAttempts) throw error;
|
|
1063
|
+
await new Promise((resolve3) => setTimeout(resolve3, 250));
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
throw new TopoloSdkError("network_error", "Topolo read retry loop exhausted.");
|
|
1067
|
+
};
|
|
1020
1068
|
let res;
|
|
1021
1069
|
try {
|
|
1022
1070
|
const attemptedCredential = this.credential;
|
|
1023
|
-
res = await
|
|
1071
|
+
res = await fetchWithReadRetry();
|
|
1024
1072
|
if (res.status === 401 && attemptedCredential.kind === "access_token" && this.refreshCredential) {
|
|
1025
1073
|
await res.body?.cancel().catch(() => void 0);
|
|
1026
1074
|
const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
|
|
1027
1075
|
if (refreshed) {
|
|
1028
1076
|
applyAuthHeaders(headers, refreshed);
|
|
1029
|
-
res = await
|
|
1077
|
+
res = await fetchWithReadRetry();
|
|
1030
1078
|
}
|
|
1031
1079
|
}
|
|
1032
1080
|
} catch (err) {
|
|
@@ -1678,26 +1726,29 @@ var TopoloClient = class _TopoloClient {
|
|
|
1678
1726
|
};
|
|
1679
1727
|
function validateActionResource(entry, resource) {
|
|
1680
1728
|
const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
|
|
1681
|
-
|
|
1729
|
+
const declaredContext = entry.agentContract?.resourceContext;
|
|
1730
|
+
const declaredKind = typeof declaredContext?.kind === "string" ? declaredContext.kind.trim() : "";
|
|
1731
|
+
const expectedType = declaredKind || (permissionName.startsWith("workspace:") ? "workspace" : "");
|
|
1732
|
+
if (!expectedType || declaredContext?.required === false && !permissionName.startsWith("workspace:")) return null;
|
|
1682
1733
|
if (!resource) {
|
|
1683
1734
|
return {
|
|
1684
1735
|
path: "$.resource",
|
|
1685
1736
|
code: "required",
|
|
1686
|
-
message:
|
|
1737
|
+
message: `Select resourceType "${expectedType}" with the application resource catalog before planning or calling this action.`
|
|
1687
1738
|
};
|
|
1688
1739
|
}
|
|
1689
|
-
if (resource.resourceType !==
|
|
1740
|
+
if (resource.resourceType !== expectedType) {
|
|
1690
1741
|
return {
|
|
1691
1742
|
path: "$.resource.resourceType",
|
|
1692
1743
|
code: "const",
|
|
1693
|
-
message:
|
|
1744
|
+
message: `This action requires resourceType "${expectedType}".`
|
|
1694
1745
|
};
|
|
1695
1746
|
}
|
|
1696
1747
|
if (!resource.resourceId.trim()) {
|
|
1697
1748
|
return {
|
|
1698
1749
|
path: "$.resource.resourceId",
|
|
1699
1750
|
code: "required",
|
|
1700
|
-
message:
|
|
1751
|
+
message: `This action requires a ${expectedType} resourceId.`
|
|
1701
1752
|
};
|
|
1702
1753
|
}
|
|
1703
1754
|
return null;
|
|
@@ -2069,7 +2120,8 @@ function compactActionCatalogEntry(action) {
|
|
|
2069
2120
|
agentAccess: action.agentAccess ?? (action.requiresConfirmation ? "confirm" : action.readOnly ? "auto" : "confirm"),
|
|
2070
2121
|
requiredPermission: action.requiredPermission,
|
|
2071
2122
|
requiredInput: required,
|
|
2072
|
-
resourceType: action.agentContract?.
|
|
2123
|
+
resourceType: action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null),
|
|
2124
|
+
resultResourceType: action.agentContract?.resource?.kind ?? null,
|
|
2073
2125
|
exampleInput: example.value,
|
|
2074
2126
|
exampleInputAvailable: example.available,
|
|
2075
2127
|
docsUrl: action.agentContract?.docsUrl ?? null
|
|
@@ -2432,7 +2484,7 @@ function createTopolo(options) {
|
|
|
2432
2484
|
};
|
|
2433
2485
|
}
|
|
2434
2486
|
|
|
2435
|
-
//
|
|
2487
|
+
// node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
2436
2488
|
import { mkdir, readFile, writeFile, chmod, rm, stat } from "fs/promises";
|
|
2437
2489
|
import { existsSync } from "fs";
|
|
2438
2490
|
import { homedir, platform } from "os";
|
|
@@ -2495,6 +2547,7 @@ var ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9_]{0,63}$/;
|
|
|
2495
2547
|
var TOPOLO_REQUEST_ID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
2496
2548
|
var CLOUDFLARE_RAY_ID_PATTERN2 = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
2497
2549
|
var PERMISSION_PATTERN2 = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
2550
|
+
var PUBLIC_FIELD_PATH_PATTERN2 = /^[$.a-z0-9_[\]-]{1,160}$/i;
|
|
2498
2551
|
var RETRY_AFTER_DATE_PATTERN2 = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
2499
2552
|
var PUBLIC_DETAIL_SOURCES2 = /* @__PURE__ */ new Set([
|
|
2500
2553
|
"api_key_introspection",
|
|
@@ -2580,7 +2633,11 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
2580
2633
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
2581
2634
|
}
|
|
2582
2635
|
if (candidate === void 0) candidate = record;
|
|
2583
|
-
if (
|
|
2636
|
+
if (Array.isArray(candidate)) {
|
|
2637
|
+
const paths = canonicalTopoloValidationPaths2(candidate);
|
|
2638
|
+
return paths.length ? { paths } : void 0;
|
|
2639
|
+
}
|
|
2640
|
+
if (!candidate || typeof candidate !== "object") return void 0;
|
|
2584
2641
|
const source = candidate;
|
|
2585
2642
|
const safe = {};
|
|
2586
2643
|
for (const [key, value] of Object.entries(source)) {
|
|
@@ -2617,12 +2674,42 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
2617
2674
|
continue;
|
|
2618
2675
|
}
|
|
2619
2676
|
if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
|
|
2620
|
-
const paths = value.filter((item) => typeof item === "string" &&
|
|
2677
|
+
const paths = value.filter((item) => typeof item === "string" && PUBLIC_FIELD_PATH_PATTERN2.test(item));
|
|
2621
2678
|
if (paths.length) safe[key] = paths;
|
|
2622
2679
|
}
|
|
2623
2680
|
}
|
|
2624
2681
|
return Object.keys(safe).length ? safe : void 0;
|
|
2625
2682
|
}
|
|
2683
|
+
function canonicalTopoloValidationPaths2(issues) {
|
|
2684
|
+
const paths = [];
|
|
2685
|
+
for (const issue of issues) {
|
|
2686
|
+
if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
|
|
2687
|
+
const record = issue;
|
|
2688
|
+
const path = canonicalTopoloValidationPath2(record.field) ?? canonicalTopoloValidationPath2(record.path);
|
|
2689
|
+
if (path && !paths.includes(path)) paths.push(path);
|
|
2690
|
+
if (paths.length === 32) break;
|
|
2691
|
+
}
|
|
2692
|
+
return paths;
|
|
2693
|
+
}
|
|
2694
|
+
function canonicalTopoloValidationPath2(value) {
|
|
2695
|
+
if (typeof value === "string") {
|
|
2696
|
+
const path2 = value.trim();
|
|
2697
|
+
return PUBLIC_FIELD_PATH_PATTERN2.test(path2) ? path2 : void 0;
|
|
2698
|
+
}
|
|
2699
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 32) return void 0;
|
|
2700
|
+
const segments = value.map((segment) => {
|
|
2701
|
+
if (typeof segment === "number" && Number.isSafeInteger(segment) && segment >= 0) {
|
|
2702
|
+
return String(segment);
|
|
2703
|
+
}
|
|
2704
|
+
if (typeof segment === "string" && /^[a-z0-9_$-]{1,80}$/i.test(segment)) {
|
|
2705
|
+
return segment;
|
|
2706
|
+
}
|
|
2707
|
+
return null;
|
|
2708
|
+
});
|
|
2709
|
+
if (segments.includes(null)) return void 0;
|
|
2710
|
+
const path = segments.join(".");
|
|
2711
|
+
return PUBLIC_FIELD_PATH_PATTERN2.test(path) ? path : void 0;
|
|
2712
|
+
}
|
|
2626
2713
|
var TopoloHttpError2 = class extends TopoloSdkError2 {
|
|
2627
2714
|
status;
|
|
2628
2715
|
body;
|
|
@@ -3372,6 +3459,11 @@ async function runDeviceFlow(options) {
|
|
|
3372
3459
|
|
|
3373
3460
|
Waiting for approval (expires in ${expiresMin} min)`
|
|
3374
3461
|
);
|
|
3462
|
+
} else {
|
|
3463
|
+
process.stderr.write(
|
|
3464
|
+
`Open ${verifyUrl} and confirm code ${start.user_code} to sign in (expires in ${expiresMin} min).
|
|
3465
|
+
`
|
|
3466
|
+
);
|
|
3375
3467
|
}
|
|
3376
3468
|
const spinner = options.mode === "pretty" ? startSpinner() : null;
|
|
3377
3469
|
let token;
|
|
@@ -5202,9 +5294,9 @@ function registerContextCommand(program2) {
|
|
|
5202
5294
|
const wantPersonal = /^(personal|me)$/i.test(target.trim());
|
|
5203
5295
|
let resolved;
|
|
5204
5296
|
const needle = target.trim().toLowerCase();
|
|
5205
|
-
const storedEntry = Object.entries(profile.contexts ?? {}).find(([, candidate]) => {
|
|
5297
|
+
const storedEntry = Object.entries(profile.contexts ?? {}).find(([key2, candidate]) => {
|
|
5206
5298
|
if (wantPersonal) return candidate.contextType === "personal";
|
|
5207
|
-
return candidate.contextType === "organization" && (candidate.organizationId === target || candidate.organizationSlug?.toLowerCase() === needle || candidate.organizationName?.toLowerCase() === needle);
|
|
5299
|
+
return key2.toLowerCase() === needle || candidate.contextType === "organization" && (candidate.organizationId === target || candidate.organizationSlug?.toLowerCase() === needle || candidate.organizationName?.toLowerCase() === needle);
|
|
5208
5300
|
});
|
|
5209
5301
|
const storedContext = storedEntry?.[1];
|
|
5210
5302
|
if (storedEntry && storedContext?.credential) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"topoloPublishedVersion": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
|
+
"topoloPublishedVersion": "0.9.7",
|
|
5
5
|
"description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"author": "Topolo",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
|
-
"@topolo/mcp": "0.11.
|
|
29
|
-
"@topolo/sdk": "0.
|
|
28
|
+
"@topolo/mcp": "0.11.12",
|
|
29
|
+
"@topolo/sdk": "0.14.2",
|
|
30
30
|
"commander": "^12.1.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|