@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.
Files changed (2) hide show
  1. package/dist/index.js +108 -16
  2. 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
- // ../../node_modules/.pnpm/@topolo+sdk@0.11.11/node_modules/@topolo/sdk/dist/index.js
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 (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
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" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
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 fetchOnce();
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 fetchOnce();
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
- if (!permissionName.startsWith("workspace:")) return null;
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: "Select a workspace with the application resource catalog before planning or calling this action."
1737
+ message: `Select resourceType "${expectedType}" with the application resource catalog before planning or calling this action.`
1687
1738
  };
1688
1739
  }
1689
- if (resource.resourceType !== "workspace") {
1740
+ if (resource.resourceType !== expectedType) {
1690
1741
  return {
1691
1742
  path: "$.resource.resourceType",
1692
1743
  code: "const",
1693
- message: 'This action requires resourceType "workspace".'
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: "This action requires a workspace resourceId."
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?.resource?.kind ?? null,
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
- // ../../node_modules/.pnpm/@topolo+sdk@0.11.11/node_modules/@topolo/sdk/dist/node/credential-store.js
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 (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
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" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
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.5",
4
- "topoloPublishedVersion": "0.9.5",
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.11",
29
- "@topolo/sdk": "0.11.11",
28
+ "@topolo/mcp": "0.11.12",
29
+ "@topolo/sdk": "0.14.2",
30
30
  "commander": "^12.1.0"
31
31
  },
32
32
  "devDependencies": {