@topolo/cli 0.12.11 → 0.12.13
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 +211 -66
- package/package.json +4 -4
- package/skills/topolo/SKILL.md +2 -0
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.
|
|
12
|
+
// node_modules/.pnpm/@topolo+sdk@0.23.1/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);
|
|
@@ -83,6 +83,9 @@ var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
83
83
|
"unknown_service"
|
|
84
84
|
]);
|
|
85
85
|
var ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
86
|
+
var SERVICE_ERROR_CODE_PATTERN = /^[a-z][a-z0-9]*(?:_[a-z][a-z0-9]*){1,9}$/;
|
|
87
|
+
var SERVICE_MESSAGE_MAX_LENGTH = 300;
|
|
88
|
+
var SERVICE_ISSUE_LIMIT = 32;
|
|
86
89
|
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
90
|
var CLOUDFLARE_RAY_ID_PATTERN = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
88
91
|
var PERMISSION_PATTERN = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
@@ -107,6 +110,24 @@ function canonicalTopoloErrorCode(value) {
|
|
|
107
110
|
if (!ERROR_CODE_PATTERN.test(candidate) || !PUBLIC_ERROR_CODES.has(candidate)) return null;
|
|
108
111
|
return candidate;
|
|
109
112
|
}
|
|
113
|
+
function canonicalTopoloServiceErrorCode(value) {
|
|
114
|
+
const platform4 = canonicalTopoloErrorCode(value);
|
|
115
|
+
if (platform4) return platform4;
|
|
116
|
+
if (typeof value !== "string") return null;
|
|
117
|
+
const candidate = value.trim();
|
|
118
|
+
if (candidate.length > 64 || !SERVICE_ERROR_CODE_PATTERN.test(candidate)) return null;
|
|
119
|
+
const wordLike = candidate.split("_").every((segment) => {
|
|
120
|
+
const digits = segment.replace(/[a-z]/g, "").length;
|
|
121
|
+
return digits === 0 || digits < segment.length - digits && segment.length <= 8;
|
|
122
|
+
});
|
|
123
|
+
return wordLike ? candidate : null;
|
|
124
|
+
}
|
|
125
|
+
function sanitizeTopoloServiceMessage(value) {
|
|
126
|
+
if (typeof value !== "string") return void 0;
|
|
127
|
+
const text3 = value.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g, " ").replace(/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi, "[url]").replace(/[^\s@<>()[\],;:"']+@[^\s@<>()[\],;:"']+\.[a-z]{2,}/gi, "[email]").replace(/[A-Za-z0-9_\-+/=.]{20,}/g, (run) => /[0-9]/.test(run) && /[A-Za-z]/.test(run) ? "[redacted]" : run).replace(/\s+/g, " ").trim();
|
|
128
|
+
if (!text3) return void 0;
|
|
129
|
+
return text3.length > SERVICE_MESSAGE_MAX_LENGTH ? `${text3.slice(0, SERVICE_MESSAGE_MAX_LENGTH - 1).trimEnd()}\u2026` : text3;
|
|
130
|
+
}
|
|
110
131
|
function canonicalTopoloRequestId(value) {
|
|
111
132
|
if (typeof value !== "string") return void 0;
|
|
112
133
|
const candidate = value.trim();
|
|
@@ -131,7 +152,7 @@ var TopoloAuthError = class extends TopoloSdkError {
|
|
|
131
152
|
retryAfter;
|
|
132
153
|
details;
|
|
133
154
|
constructor(message2, code = "auth_error", options = {}) {
|
|
134
|
-
super(
|
|
155
|
+
super(canonicalTopoloServiceErrorCode(code) ?? "auth_error", message2);
|
|
135
156
|
this.name = "TopoloAuthError";
|
|
136
157
|
this.status = options.status ?? 401;
|
|
137
158
|
this.body = options.body;
|
|
@@ -155,7 +176,7 @@ var TopoloPermissionError = class extends TopoloSdkError {
|
|
|
155
176
|
details;
|
|
156
177
|
constructor(message2, required, options = {}) {
|
|
157
178
|
super(
|
|
158
|
-
|
|
179
|
+
canonicalTopoloServiceErrorCode(options.code) ?? serviceErrorCode(options.body, "permission_denied"),
|
|
159
180
|
message2
|
|
160
181
|
);
|
|
161
182
|
this.name = "TopoloPermissionError";
|
|
@@ -173,36 +194,49 @@ var TopoloPermissionError = class extends TopoloSdkError {
|
|
|
173
194
|
};
|
|
174
195
|
function extractTopoloServiceErrorCode(body) {
|
|
175
196
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
176
|
-
const
|
|
177
|
-
const topLevel =
|
|
197
|
+
const record3 = body;
|
|
198
|
+
const topLevel = canonicalTopoloServiceErrorCode(record3.code);
|
|
178
199
|
if (topLevel) return topLevel;
|
|
179
|
-
const nested =
|
|
180
|
-
const nestedString =
|
|
200
|
+
const nested = record3.error;
|
|
201
|
+
const nestedString = canonicalTopoloServiceErrorCode(nested);
|
|
181
202
|
if (nestedString) return nestedString;
|
|
182
203
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
183
204
|
const code = nested.code;
|
|
184
|
-
const nestedCode =
|
|
205
|
+
const nestedCode = canonicalTopoloServiceErrorCode(code);
|
|
185
206
|
if (nestedCode) return nestedCode;
|
|
186
207
|
}
|
|
187
208
|
return null;
|
|
188
209
|
}
|
|
210
|
+
function extractTopoloServiceErrorMessage(body) {
|
|
211
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
212
|
+
const record3 = body;
|
|
213
|
+
const nested = record3.error;
|
|
214
|
+
const candidates = [];
|
|
215
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
216
|
+
candidates.push(nested.message);
|
|
217
|
+
}
|
|
218
|
+
candidates.push(record3.message, record3.error_description);
|
|
219
|
+
if (typeof nested === "string" && !canonicalTopoloServiceErrorCode(nested)) candidates.push(nested);
|
|
220
|
+
for (const candidate of candidates) {
|
|
221
|
+
const message2 = sanitizeTopoloServiceMessage(candidate);
|
|
222
|
+
if (message2) return message2;
|
|
223
|
+
}
|
|
224
|
+
return void 0;
|
|
225
|
+
}
|
|
189
226
|
function serviceErrorCode(body, fallback) {
|
|
190
227
|
return extractTopoloServiceErrorCode(body) ?? fallback;
|
|
191
228
|
}
|
|
192
229
|
function extractTopoloServiceErrorDetails(body) {
|
|
193
230
|
if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
|
|
194
|
-
const
|
|
195
|
-
let candidate =
|
|
196
|
-
const nested =
|
|
231
|
+
const record3 = body;
|
|
232
|
+
let candidate = record3.details;
|
|
233
|
+
const nested = record3.error;
|
|
197
234
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
198
235
|
const nestedRecord = nested;
|
|
199
236
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
200
237
|
}
|
|
201
|
-
if (candidate === void 0) candidate =
|
|
202
|
-
if (Array.isArray(candidate))
|
|
203
|
-
const paths = canonicalTopoloValidationPaths(candidate);
|
|
204
|
-
return paths.length ? { paths } : void 0;
|
|
205
|
-
}
|
|
238
|
+
if (candidate === void 0) candidate = record3;
|
|
239
|
+
if (Array.isArray(candidate)) return validationIssueDetails(canonicalTopoloValidationIssues(candidate)) ?? void 0;
|
|
206
240
|
if (!candidate || typeof candidate !== "object") return void 0;
|
|
207
241
|
const source = candidate;
|
|
208
242
|
const safe = {};
|
|
@@ -244,18 +278,59 @@ function extractTopoloServiceErrorDetails(body) {
|
|
|
244
278
|
if (paths.length) safe[key] = paths;
|
|
245
279
|
}
|
|
246
280
|
}
|
|
281
|
+
const issues = [
|
|
282
|
+
...Array.isArray(source.issues) ? canonicalTopoloValidationIssues(source.issues) : [],
|
|
283
|
+
...flattenedValidationIssues(source.formErrors, source.fieldErrors)
|
|
284
|
+
].slice(0, SERVICE_ISSUE_LIMIT);
|
|
285
|
+
Object.assign(safe, validationIssueDetails(issues));
|
|
247
286
|
return Object.keys(safe).length ? safe : void 0;
|
|
248
287
|
}
|
|
249
|
-
function
|
|
288
|
+
function validationIssueDetails(issues) {
|
|
289
|
+
if (!issues.length) return null;
|
|
250
290
|
const paths = [];
|
|
251
|
-
for (const
|
|
252
|
-
if (
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
291
|
+
for (const issue22 of issues) {
|
|
292
|
+
if (issue22.path && !paths.includes(issue22.path)) paths.push(issue22.path);
|
|
293
|
+
}
|
|
294
|
+
return paths.length ? { paths, issues } : { issues };
|
|
295
|
+
}
|
|
296
|
+
function issue(path, message2) {
|
|
297
|
+
const text3 = sanitizeTopoloServiceMessage(message2);
|
|
298
|
+
if (!path && !text3) return null;
|
|
299
|
+
return { ...path ? { path } : {}, ...text3 ? { message: text3 } : {} };
|
|
300
|
+
}
|
|
301
|
+
function canonicalTopoloValidationIssues(values) {
|
|
302
|
+
const issues = [];
|
|
303
|
+
for (const value of values) {
|
|
304
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
305
|
+
const record3 = value;
|
|
306
|
+
const path = canonicalTopoloValidationPath(record3.field) ?? canonicalTopoloValidationPath(record3.path);
|
|
307
|
+
if (!path) continue;
|
|
308
|
+
const projected = issue(path, record3.message);
|
|
309
|
+
if (projected) issues.push(projected);
|
|
310
|
+
if (issues.length === SERVICE_ISSUE_LIMIT) break;
|
|
311
|
+
}
|
|
312
|
+
return issues;
|
|
313
|
+
}
|
|
314
|
+
function flattenedValidationIssues(formErrors, fieldErrors) {
|
|
315
|
+
const issues = [];
|
|
316
|
+
if (Array.isArray(formErrors)) {
|
|
317
|
+
for (const message2 of formErrors) {
|
|
318
|
+
const projected = issue(void 0, message2);
|
|
319
|
+
if (projected) issues.push(projected);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (fieldErrors && typeof fieldErrors === "object" && !Array.isArray(fieldErrors)) {
|
|
323
|
+
for (const [key, messages] of Object.entries(fieldErrors)) {
|
|
324
|
+
const path = canonicalTopoloValidationPath(key);
|
|
325
|
+
if (!path) continue;
|
|
326
|
+
const list = Array.isArray(messages) && messages.length ? messages : [void 0];
|
|
327
|
+
for (const message2 of list) {
|
|
328
|
+
const projected = issue(path, message2);
|
|
329
|
+
if (projected) issues.push(projected);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
257
332
|
}
|
|
258
|
-
return
|
|
333
|
+
return issues;
|
|
259
334
|
}
|
|
260
335
|
function canonicalTopoloValidationPath(value) {
|
|
261
336
|
if (typeof value === "string") {
|
|
@@ -277,7 +352,9 @@ function canonicalTopoloValidationPath(value) {
|
|
|
277
352
|
return PUBLIC_FIELD_PATH_PATTERN.test(path) ? path : void 0;
|
|
278
353
|
}
|
|
279
354
|
function publicTopoloErrorMessage(error) {
|
|
280
|
-
const
|
|
355
|
+
const serviceMessage = typeof error === "string" ? void 0 : extractTopoloServiceErrorMessage(error.body);
|
|
356
|
+
if (serviceMessage) return serviceMessage;
|
|
357
|
+
const code = canonicalTopoloServiceErrorCode(
|
|
281
358
|
typeof error === "string" ? error : error.code ?? extractTopoloServiceErrorCode(error.body)
|
|
282
359
|
) ?? "request_failed";
|
|
283
360
|
if (/(?:organization|org).*(?:enroll|membership|member|admission)/.test(code)) {
|
|
@@ -294,7 +371,7 @@ function publicTopoloErrorMessage(error) {
|
|
|
294
371
|
return `Topolo request failed (${code}).`;
|
|
295
372
|
}
|
|
296
373
|
function topoloErrorHint(error) {
|
|
297
|
-
const code =
|
|
374
|
+
const code = canonicalTopoloServiceErrorCode(
|
|
298
375
|
typeof error === "string" ? error : error.code ?? extractTopoloServiceErrorCode(error.body)
|
|
299
376
|
);
|
|
300
377
|
if (!code) return void 0;
|
|
@@ -360,8 +437,8 @@ function sensitive(key, value) {
|
|
|
360
437
|
const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
361
438
|
if (normalized === "capability") {
|
|
362
439
|
if (typeof value !== "string") return value !== null && value !== void 0 && typeof value !== "boolean" && typeof value !== "number";
|
|
363
|
-
const
|
|
364
|
-
return Boolean(
|
|
440
|
+
const text3 = value.trim();
|
|
441
|
+
return Boolean(text3) && !/^[a-z][a-z0-9_-]{0,31}$/.test(text3);
|
|
365
442
|
}
|
|
366
443
|
return ["authorization", "cookie", "setcookie", "proxyauthorization"].includes(normalized) || /(token|secret|password|privatekey|apikey)$/.test(normalized);
|
|
367
444
|
}
|
|
@@ -645,7 +722,7 @@ function inspectSchema(schema, path, root, depth) {
|
|
|
645
722
|
if (unsupportedKeyword) {
|
|
646
723
|
return { path, code: "unsupported_schema_keyword", message: `Action schema uses unsupported keyword ${unsupportedKeyword}.` };
|
|
647
724
|
}
|
|
648
|
-
if (typeof schema.format === "string" && !["uri", "email", "date-time"].includes(schema.format)) {
|
|
725
|
+
if (typeof schema.format === "string" && !["uri", "email", "date-time", "uuid"].includes(schema.format)) {
|
|
649
726
|
return { path, code: "unsupported_schema_format", message: `Action schema uses unsupported format ${schema.format}.` };
|
|
650
727
|
}
|
|
651
728
|
if (typeof schema.$ref === "string" && !resolveLocalReference(root, schema.$ref)) {
|
|
@@ -665,8 +742,8 @@ function inspectSchema(schema, path, root, depth) {
|
|
|
665
742
|
);
|
|
666
743
|
for (const [key, child] of children) {
|
|
667
744
|
if (!isRecord2(child)) continue;
|
|
668
|
-
const
|
|
669
|
-
if (
|
|
745
|
+
const issue22 = inspectSchema(child, `${path}.${key}`, root, depth + 1);
|
|
746
|
+
if (issue22) return issue22;
|
|
670
747
|
}
|
|
671
748
|
return null;
|
|
672
749
|
}
|
|
@@ -782,6 +859,7 @@ function validateString(schema, value, path, errors) {
|
|
|
782
859
|
if (schema.format === "uri" && !isUrl(value)) errors.push({ path, code: "format_uri", message: "Expected an absolute URL." });
|
|
783
860
|
if (schema.format === "email" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)) errors.push({ path, code: "format_email", message: "Expected an email address." });
|
|
784
861
|
if (schema.format === "date-time" && !isDateTime(value)) errors.push({ path, code: "format_date_time", message: "Expected an ISO-8601 date-time." });
|
|
862
|
+
if (schema.format === "uuid" && (value.length !== 36 || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value))) errors.push({ path, code: "format_uuid", message: "Expected a hyphenated UUID." });
|
|
785
863
|
}
|
|
786
864
|
function validateNumber(schema, value, path, errors) {
|
|
787
865
|
if (typeof schema.minimum === "number" && value < schema.minimum) errors.push({ path, code: "minimum", message: `Expected a value of at least ${schema.minimum}.` });
|
|
@@ -1839,14 +1917,14 @@ var TopoloClient = class _TopoloClient {
|
|
|
1839
1917
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1840
1918
|
throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is unavailable.");
|
|
1841
1919
|
}
|
|
1842
|
-
const
|
|
1843
|
-
if (
|
|
1920
|
+
const record3 = raw;
|
|
1921
|
+
if (record3.algorithm !== "sha256-v1" || typeof record3.digest !== "string" || !/^[a-f0-9]{64}$/.test(record3.digest) || !Number.isSafeInteger(record3.actionCount) || Number(record3.actionCount) < 0) {
|
|
1844
1922
|
throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is invalid.");
|
|
1845
1923
|
}
|
|
1846
1924
|
return {
|
|
1847
1925
|
algorithm: "sha256-v1",
|
|
1848
|
-
digest:
|
|
1849
|
-
actionCount: Number(
|
|
1926
|
+
digest: record3.digest,
|
|
1927
|
+
actionCount: Number(record3.actionCount)
|
|
1850
1928
|
};
|
|
1851
1929
|
}
|
|
1852
1930
|
/** @deprecated Prefer searchActions() so callers retain the continuation cursor. */
|
|
@@ -1982,7 +2060,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1982
2060
|
input: selectedInput,
|
|
1983
2061
|
inputSource: input2 ? "provided" : example ? "example" : "empty",
|
|
1984
2062
|
missingFields: [...new Set(
|
|
1985
|
-
plan.validation.errors.filter((
|
|
2063
|
+
plan.validation.errors.filter((issue22) => issue22.code === "required").map((issue22) => issue22.path)
|
|
1986
2064
|
)],
|
|
1987
2065
|
plan
|
|
1988
2066
|
};
|
|
@@ -2047,7 +2125,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2047
2125
|
if (!validation.valid) {
|
|
2048
2126
|
throw new TopoloSdkError(
|
|
2049
2127
|
"invalid_action_input",
|
|
2050
|
-
`Topolo action "${entry.actionId}" input is invalid: ${validation.errors.map((
|
|
2128
|
+
`Topolo action "${entry.actionId}" input is invalid: ${validation.errors.map((issue22) => `${issue22.path} ${issue22.message}`).join("; ")}`
|
|
2051
2129
|
);
|
|
2052
2130
|
}
|
|
2053
2131
|
const resourceIssue = validateActionResource(entry, options.resource);
|
|
@@ -2564,10 +2642,10 @@ function extractRequired(body) {
|
|
|
2564
2642
|
if (Array.isArray(maybe.required)) return canonicalTopoloRequired(maybe.required);
|
|
2565
2643
|
for (const nested of [maybe.details, maybe.error]) {
|
|
2566
2644
|
if (!nested || typeof nested !== "object" || Array.isArray(nested)) continue;
|
|
2567
|
-
const
|
|
2568
|
-
if (Array.isArray(
|
|
2569
|
-
if (
|
|
2570
|
-
const required =
|
|
2645
|
+
const record3 = nested;
|
|
2646
|
+
if (Array.isArray(record3.required)) return canonicalTopoloRequired(record3.required);
|
|
2647
|
+
if (record3.details && typeof record3.details === "object" && !Array.isArray(record3.details)) {
|
|
2648
|
+
const required = record3.details.required;
|
|
2571
2649
|
if (Array.isArray(required)) return canonicalTopoloRequired(required);
|
|
2572
2650
|
}
|
|
2573
2651
|
}
|
|
@@ -2642,8 +2720,8 @@ function compactResultValue(value, depth, budget) {
|
|
|
2642
2720
|
return value.length > ACTION_RESULT_MAX_ITEMS ? { count: value.length, items, omittedItems: value.length - ACTION_RESULT_MAX_ITEMS } : items;
|
|
2643
2721
|
}
|
|
2644
2722
|
if (typeof value === "object") {
|
|
2645
|
-
const
|
|
2646
|
-
const allKeys = Object.keys(
|
|
2723
|
+
const record3 = value;
|
|
2724
|
+
const allKeys = Object.keys(record3);
|
|
2647
2725
|
const ranked = allKeys.sort((left, right) => resultKeyPriority(left) - resultKeyPriority(right));
|
|
2648
2726
|
const prioritized = ranked.filter((key) => resultKeyPriority(key) < 2);
|
|
2649
2727
|
const ordinary = ranked.filter((key) => resultKeyPriority(key) === 2);
|
|
@@ -2652,7 +2730,7 @@ function compactResultValue(value, depth, budget) {
|
|
|
2652
2730
|
...prioritized,
|
|
2653
2731
|
...ordinary.slice(0, ordinaryLimit)
|
|
2654
2732
|
].slice(0, ACTION_RESULT_MAX_KEYS);
|
|
2655
|
-
const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(
|
|
2733
|
+
const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(record3[key], depth + 1, budget)]));
|
|
2656
2734
|
if (allKeys.length > keys.length) compact["_omittedKeys"] = allKeys.length - keys.length;
|
|
2657
2735
|
return compact;
|
|
2658
2736
|
}
|
|
@@ -3416,7 +3494,7 @@ function createTopolo(options) {
|
|
|
3416
3494
|
};
|
|
3417
3495
|
}
|
|
3418
3496
|
|
|
3419
|
-
// node_modules/.pnpm/@topolo+sdk@0.
|
|
3497
|
+
// node_modules/.pnpm/@topolo+sdk@0.23.1/node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
3420
3498
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2, rm as rm2, stat } from "fs/promises";
|
|
3421
3499
|
import { existsSync as existsSync2 } from "fs";
|
|
3422
3500
|
import { homedir as homedir2, platform } from "os";
|
|
@@ -3483,6 +3561,9 @@ var PUBLIC_ERROR_CODES2 = /* @__PURE__ */ new Set([
|
|
|
3483
3561
|
"unknown_service"
|
|
3484
3562
|
]);
|
|
3485
3563
|
var ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9_]{0,63}$/;
|
|
3564
|
+
var SERVICE_ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9]*(?:_[a-z][a-z0-9]*){1,9}$/;
|
|
3565
|
+
var SERVICE_MESSAGE_MAX_LENGTH2 = 300;
|
|
3566
|
+
var SERVICE_ISSUE_LIMIT2 = 32;
|
|
3486
3567
|
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;
|
|
3487
3568
|
var CLOUDFLARE_RAY_ID_PATTERN2 = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
3488
3569
|
var PERMISSION_PATTERN2 = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
|
|
@@ -3507,6 +3588,24 @@ function canonicalTopoloErrorCode2(value) {
|
|
|
3507
3588
|
if (!ERROR_CODE_PATTERN2.test(candidate) || !PUBLIC_ERROR_CODES2.has(candidate)) return null;
|
|
3508
3589
|
return candidate;
|
|
3509
3590
|
}
|
|
3591
|
+
function canonicalTopoloServiceErrorCode2(value) {
|
|
3592
|
+
const platform22 = canonicalTopoloErrorCode2(value);
|
|
3593
|
+
if (platform22) return platform22;
|
|
3594
|
+
if (typeof value !== "string") return null;
|
|
3595
|
+
const candidate = value.trim();
|
|
3596
|
+
if (candidate.length > 64 || !SERVICE_ERROR_CODE_PATTERN2.test(candidate)) return null;
|
|
3597
|
+
const wordLike = candidate.split("_").every((segment) => {
|
|
3598
|
+
const digits = segment.replace(/[a-z]/g, "").length;
|
|
3599
|
+
return digits === 0 || digits < segment.length - digits && segment.length <= 8;
|
|
3600
|
+
});
|
|
3601
|
+
return wordLike ? candidate : null;
|
|
3602
|
+
}
|
|
3603
|
+
function sanitizeTopoloServiceMessage2(value) {
|
|
3604
|
+
if (typeof value !== "string") return void 0;
|
|
3605
|
+
const text2 = value.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g, " ").replace(/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi, "[url]").replace(/[^\s@<>()[\],;:"']+@[^\s@<>()[\],;:"']+\.[a-z]{2,}/gi, "[email]").replace(/[A-Za-z0-9_\-+/=.]{20,}/g, (run) => /[0-9]/.test(run) && /[A-Za-z]/.test(run) ? "[redacted]" : run).replace(/\s+/g, " ").trim();
|
|
3606
|
+
if (!text2) return void 0;
|
|
3607
|
+
return text2.length > SERVICE_MESSAGE_MAX_LENGTH2 ? `${text2.slice(0, SERVICE_MESSAGE_MAX_LENGTH2 - 1).trimEnd()}\u2026` : text2;
|
|
3608
|
+
}
|
|
3510
3609
|
function canonicalTopoloRequestId2(value) {
|
|
3511
3610
|
if (typeof value !== "string") return void 0;
|
|
3512
3611
|
const candidate = value.trim();
|
|
@@ -3531,7 +3630,7 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
|
3531
3630
|
retryAfter;
|
|
3532
3631
|
details;
|
|
3533
3632
|
constructor(message2, code = "auth_error", options = {}) {
|
|
3534
|
-
super(
|
|
3633
|
+
super(canonicalTopoloServiceErrorCode2(code) ?? "auth_error", message2);
|
|
3535
3634
|
this.name = "TopoloAuthError";
|
|
3536
3635
|
this.status = options.status ?? 401;
|
|
3537
3636
|
this.body = options.body;
|
|
@@ -3547,14 +3646,14 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
|
3547
3646
|
function extractTopoloServiceErrorCode2(body) {
|
|
3548
3647
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
3549
3648
|
const record2 = body;
|
|
3550
|
-
const topLevel =
|
|
3649
|
+
const topLevel = canonicalTopoloServiceErrorCode2(record2.code);
|
|
3551
3650
|
if (topLevel) return topLevel;
|
|
3552
3651
|
const nested = record2.error;
|
|
3553
|
-
const nestedString =
|
|
3652
|
+
const nestedString = canonicalTopoloServiceErrorCode2(nested);
|
|
3554
3653
|
if (nestedString) return nestedString;
|
|
3555
3654
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
3556
3655
|
const code = nested.code;
|
|
3557
|
-
const nestedCode =
|
|
3656
|
+
const nestedCode = canonicalTopoloServiceErrorCode2(code);
|
|
3558
3657
|
if (nestedCode) return nestedCode;
|
|
3559
3658
|
}
|
|
3560
3659
|
return null;
|
|
@@ -3572,10 +3671,7 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
3572
3671
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
3573
3672
|
}
|
|
3574
3673
|
if (candidate === void 0) candidate = record2;
|
|
3575
|
-
if (Array.isArray(candidate))
|
|
3576
|
-
const paths = canonicalTopoloValidationPaths2(candidate);
|
|
3577
|
-
return paths.length ? { paths } : void 0;
|
|
3578
|
-
}
|
|
3674
|
+
if (Array.isArray(candidate)) return validationIssueDetails2(canonicalTopoloValidationIssues2(candidate)) ?? void 0;
|
|
3579
3675
|
if (!candidate || typeof candidate !== "object") return void 0;
|
|
3580
3676
|
const source = candidate;
|
|
3581
3677
|
const safe = {};
|
|
@@ -3617,18 +3713,59 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
3617
3713
|
if (paths.length) safe[key] = paths;
|
|
3618
3714
|
}
|
|
3619
3715
|
}
|
|
3716
|
+
const issues = [
|
|
3717
|
+
...Array.isArray(source.issues) ? canonicalTopoloValidationIssues2(source.issues) : [],
|
|
3718
|
+
...flattenedValidationIssues2(source.formErrors, source.fieldErrors)
|
|
3719
|
+
].slice(0, SERVICE_ISSUE_LIMIT2);
|
|
3720
|
+
Object.assign(safe, validationIssueDetails2(issues));
|
|
3620
3721
|
return Object.keys(safe).length ? safe : void 0;
|
|
3621
3722
|
}
|
|
3622
|
-
function
|
|
3723
|
+
function validationIssueDetails2(issues) {
|
|
3724
|
+
if (!issues.length) return null;
|
|
3623
3725
|
const paths = [];
|
|
3624
|
-
for (const
|
|
3625
|
-
if (
|
|
3626
|
-
|
|
3726
|
+
for (const issue22 of issues) {
|
|
3727
|
+
if (issue22.path && !paths.includes(issue22.path)) paths.push(issue22.path);
|
|
3728
|
+
}
|
|
3729
|
+
return paths.length ? { paths, issues } : { issues };
|
|
3730
|
+
}
|
|
3731
|
+
function issue2(path, message2) {
|
|
3732
|
+
const text2 = sanitizeTopoloServiceMessage2(message2);
|
|
3733
|
+
if (!path && !text2) return null;
|
|
3734
|
+
return { ...path ? { path } : {}, ...text2 ? { message: text2 } : {} };
|
|
3735
|
+
}
|
|
3736
|
+
function canonicalTopoloValidationIssues2(values) {
|
|
3737
|
+
const issues = [];
|
|
3738
|
+
for (const value of values) {
|
|
3739
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
3740
|
+
const record2 = value;
|
|
3627
3741
|
const path = canonicalTopoloValidationPath2(record2.field) ?? canonicalTopoloValidationPath2(record2.path);
|
|
3628
|
-
if (
|
|
3629
|
-
|
|
3742
|
+
if (!path) continue;
|
|
3743
|
+
const projected = issue2(path, record2.message);
|
|
3744
|
+
if (projected) issues.push(projected);
|
|
3745
|
+
if (issues.length === SERVICE_ISSUE_LIMIT2) break;
|
|
3630
3746
|
}
|
|
3631
|
-
return
|
|
3747
|
+
return issues;
|
|
3748
|
+
}
|
|
3749
|
+
function flattenedValidationIssues2(formErrors, fieldErrors) {
|
|
3750
|
+
const issues = [];
|
|
3751
|
+
if (Array.isArray(formErrors)) {
|
|
3752
|
+
for (const message2 of formErrors) {
|
|
3753
|
+
const projected = issue2(void 0, message2);
|
|
3754
|
+
if (projected) issues.push(projected);
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
if (fieldErrors && typeof fieldErrors === "object" && !Array.isArray(fieldErrors)) {
|
|
3758
|
+
for (const [key, messages] of Object.entries(fieldErrors)) {
|
|
3759
|
+
const path = canonicalTopoloValidationPath2(key);
|
|
3760
|
+
if (!path) continue;
|
|
3761
|
+
const list = Array.isArray(messages) && messages.length ? messages : [void 0];
|
|
3762
|
+
for (const message2 of list) {
|
|
3763
|
+
const projected = issue2(path, message2);
|
|
3764
|
+
if (projected) issues.push(projected);
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
return issues;
|
|
3632
3769
|
}
|
|
3633
3770
|
function canonicalTopoloValidationPath2(value) {
|
|
3634
3771
|
if (typeof value === "string") {
|
|
@@ -4462,6 +4599,10 @@ function fail2(err, mode) {
|
|
|
4462
4599
|
`);
|
|
4463
4600
|
if (payload.status) process.stderr.write(`HTTP: ${payload.status}
|
|
4464
4601
|
`);
|
|
4602
|
+
for (const issue3 of validationIssues(payload.details)) {
|
|
4603
|
+
process.stderr.write(`Issue: ${[issue3.path, issue3.message].filter(Boolean).join(": ")}
|
|
4604
|
+
`);
|
|
4605
|
+
}
|
|
4465
4606
|
if (payload.requestId) process.stderr.write(`Request: ${payload.requestId}
|
|
4466
4607
|
`);
|
|
4467
4608
|
if (payload.hint) process.stderr.write(`Hint: ${payload.hint}
|
|
@@ -4469,6 +4610,10 @@ function fail2(err, mode) {
|
|
|
4469
4610
|
}
|
|
4470
4611
|
process.exit(payload.exitCode);
|
|
4471
4612
|
}
|
|
4613
|
+
function validationIssues(details) {
|
|
4614
|
+
const issues = details?.issues;
|
|
4615
|
+
return Array.isArray(issues) ? issues : [];
|
|
4616
|
+
}
|
|
4472
4617
|
function normalizeError(err) {
|
|
4473
4618
|
if (err instanceof Error) {
|
|
4474
4619
|
const code = err.code;
|
|
@@ -4486,9 +4631,9 @@ function normalizeError(err) {
|
|
|
4486
4631
|
path: err.path,
|
|
4487
4632
|
requestId: err.requestId,
|
|
4488
4633
|
retryAfter: err.retryAfter,
|
|
4489
|
-
details
|
|
4490
|
-
|
|
4491
|
-
),
|
|
4634
|
+
// SDK HTTP errors already carry sanitized details; projecting them again
|
|
4635
|
+
// would drop form-level issues that have no field path.
|
|
4636
|
+
details: err instanceof TopoloHttpError || err instanceof TopoloAuthError || err instanceof TopoloPermissionError ? err.details : extractTopoloServiceErrorDetails(suppliedDetails === void 0 ? body : { details: suppliedDetails }),
|
|
4492
4637
|
hint: topoloErrorHint({ code, body }) ?? (code ? fallbackHintMap[code] : void 0),
|
|
4493
4638
|
exitCode: status === 401 || code === "auth_error" || code === "confirmation_required" ? 2 : status === 403 || code === "permission_denied" ? 3 : code === "immutable_drift" ? 4 : 1
|
|
4494
4639
|
};
|
|
@@ -4833,7 +4978,7 @@ function stderrDebugLogger(event) {
|
|
|
4833
4978
|
return;
|
|
4834
4979
|
}
|
|
4835
4980
|
const statusPart = event.status ? ` ${event.status}` : "";
|
|
4836
|
-
const errorCode =
|
|
4981
|
+
const errorCode = canonicalTopoloServiceErrorCode(event.error) ?? "request_failed";
|
|
4837
4982
|
process.stderr.write(
|
|
4838
4983
|
`[topolo] \u2717${statusPart} ${event.method} ${event.service} ${event.path} (${event.durationMs}ms, req=${requestId}, code=${errorCode})
|
|
4839
4984
|
`
|
|
@@ -5087,7 +5232,7 @@ Approval required. ${opened ? "Opening" : "Open"} ${approval.verificationUrl}` :
|
|
|
5087
5232
|
const topolo = await requiredClient();
|
|
5088
5233
|
const result = await topolo.client.validateActionInput(action, parseJsonObject(opts.data || "{}"));
|
|
5089
5234
|
const output = detailRequested(opts.detail, command) ? result : compactActionValidation(result.action.actionId, result.validation);
|
|
5090
|
-
emit(output, mode, () => result.validation.valid ? `Valid input for ${result.action.actionId}.` : result.validation.errors.map((
|
|
5235
|
+
emit(output, mode, () => result.validation.valid ? `Valid input for ${result.action.actionId}.` : result.validation.errors.map((issue3) => `${issue3.path}: ${issue3.message}`).join("\n"));
|
|
5091
5236
|
} catch (err) {
|
|
5092
5237
|
fail2(err, mode);
|
|
5093
5238
|
}
|
|
@@ -5367,7 +5512,7 @@ function formatActionPlan(plan) {
|
|
|
5367
5512
|
];
|
|
5368
5513
|
if (plan.request) lines.push(`Request: ${plan.request.method} ${plan.request.apiBaseUrl || plan.request.service}${plan.request.path}`);
|
|
5369
5514
|
if (plan.request?.resource) lines.push(`Resource: ${plan.request.resource.resourceType}:${plan.request.resource.resourceId}`);
|
|
5370
|
-
if (plan.validation.errors.length) lines.push("", ...plan.validation.errors.map((
|
|
5515
|
+
if (plan.validation.errors.length) lines.push("", ...plan.validation.errors.map((issue3) => `${issue3.path}: ${issue3.message}`));
|
|
5371
5516
|
if (plan.verification.length) lines.push("", "Verify:", ...plan.verification.map((step) => `- ${step}`));
|
|
5372
5517
|
if (plan.rollbackActionId) lines.push("", `Rollback: ${plan.rollbackActionId}`);
|
|
5373
5518
|
return lines.join("\n");
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
3
|
"packageManager": "pnpm@10.17.1",
|
|
4
|
-
"version": "0.12.
|
|
5
|
-
"topoloPublishedVersion": "0.12.
|
|
4
|
+
"version": "0.12.13",
|
|
5
|
+
"topoloPublishedVersion": "0.12.13",
|
|
6
6
|
"description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
|
|
7
7
|
"license": "UNLICENSED",
|
|
8
8
|
"author": "Topolo",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
31
|
-
"@topolo/mcp": "0.14.
|
|
32
|
-
"@topolo/sdk": "0.
|
|
31
|
+
"@topolo/mcp": "0.14.6",
|
|
32
|
+
"@topolo/sdk": "0.23.1",
|
|
33
33
|
"commander": "^12.1.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
package/skills/topolo/SKILL.md
CHANGED
|
@@ -289,6 +289,8 @@ In JSON mode, branch on `error.code`, not message text:
|
|
|
289
289
|
- exit 3 / permission denied: surface the missing permission or target denial; do not retry or widen access.
|
|
290
290
|
- `invalid_action_input`: correct only the reported schema paths.
|
|
291
291
|
- `confirmation_required`: plan, obtain approval, then retry once with confirmation.
|
|
292
|
+
- a service-published refusal code (for example `outreach_requires_mailbox_sender`): apply that action's published recovery for the code; `error.message` is the service's sanitized explanation and `error.details.issues` lists field-level problems.
|
|
293
|
+
- `http_error`: the service returned no structured code; use `status` and `requestId`.
|
|
292
294
|
- unknown action/service: repeat bounded discovery; never substitute a guessed identifier.
|
|
293
295
|
- missing docs/schema/examples/verification/recovery: stop the affected operation and report the missing publication contract.
|
|
294
296
|
|