@topolo/cli 0.12.11 → 0.12.12
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 +189 -45
- 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.22.0/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 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();
|
|
128
|
+
if (!text2) return void 0;
|
|
129
|
+
return text2.length > SERVICE_MESSAGE_MAX_LENGTH ? `${text2.slice(0, SERVICE_MESSAGE_MAX_LENGTH - 1).trimEnd()}\u2026` : text2;
|
|
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";
|
|
@@ -174,18 +195,34 @@ var TopoloPermissionError = class extends TopoloSdkError {
|
|
|
174
195
|
function extractTopoloServiceErrorCode(body) {
|
|
175
196
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
176
197
|
const record2 = body;
|
|
177
|
-
const topLevel =
|
|
198
|
+
const topLevel = canonicalTopoloServiceErrorCode(record2.code);
|
|
178
199
|
if (topLevel) return topLevel;
|
|
179
200
|
const nested = record2.error;
|
|
180
|
-
const nestedString =
|
|
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 record2 = body;
|
|
213
|
+
const nested = record2.error;
|
|
214
|
+
const candidates = [];
|
|
215
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
216
|
+
candidates.push(nested.message);
|
|
217
|
+
}
|
|
218
|
+
candidates.push(record2.message, record2.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
|
}
|
|
@@ -199,10 +236,7 @@ function extractTopoloServiceErrorDetails(body) {
|
|
|
199
236
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
200
237
|
}
|
|
201
238
|
if (candidate === void 0) candidate = record2;
|
|
202
|
-
if (Array.isArray(candidate))
|
|
203
|
-
const paths = canonicalTopoloValidationPaths(candidate);
|
|
204
|
-
return paths.length ? { paths } : void 0;
|
|
205
|
-
}
|
|
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
|
-
|
|
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 text2 = sanitizeTopoloServiceMessage(message2);
|
|
298
|
+
if (!path && !text2) return null;
|
|
299
|
+
return { ...path ? { path } : {}, ...text2 ? { message: text2 } : {} };
|
|
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 record2 = value;
|
|
254
306
|
const path = canonicalTopoloValidationPath(record2.field) ?? canonicalTopoloValidationPath(record2.path);
|
|
255
|
-
if (
|
|
256
|
-
|
|
307
|
+
if (!path) continue;
|
|
308
|
+
const projected = issue(path, record2.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;
|
|
@@ -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
|
}
|
|
@@ -1982,7 +2059,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1982
2059
|
input: selectedInput,
|
|
1983
2060
|
inputSource: input2 ? "provided" : example ? "example" : "empty",
|
|
1984
2061
|
missingFields: [...new Set(
|
|
1985
|
-
plan.validation.errors.filter((
|
|
2062
|
+
plan.validation.errors.filter((issue22) => issue22.code === "required").map((issue22) => issue22.path)
|
|
1986
2063
|
)],
|
|
1987
2064
|
plan
|
|
1988
2065
|
};
|
|
@@ -2047,7 +2124,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2047
2124
|
if (!validation.valid) {
|
|
2048
2125
|
throw new TopoloSdkError(
|
|
2049
2126
|
"invalid_action_input",
|
|
2050
|
-
`Topolo action "${entry.actionId}" input is invalid: ${validation.errors.map((
|
|
2127
|
+
`Topolo action "${entry.actionId}" input is invalid: ${validation.errors.map((issue22) => `${issue22.path} ${issue22.message}`).join("; ")}`
|
|
2051
2128
|
);
|
|
2052
2129
|
}
|
|
2053
2130
|
const resourceIssue = validateActionResource(entry, options.resource);
|
|
@@ -3416,7 +3493,7 @@ function createTopolo(options) {
|
|
|
3416
3493
|
};
|
|
3417
3494
|
}
|
|
3418
3495
|
|
|
3419
|
-
// node_modules/.pnpm/@topolo+sdk@0.
|
|
3496
|
+
// node_modules/.pnpm/@topolo+sdk@0.22.0/node_modules/@topolo/sdk/dist/node/credential-store.js
|
|
3420
3497
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2, rm as rm2, stat } from "fs/promises";
|
|
3421
3498
|
import { existsSync as existsSync2 } from "fs";
|
|
3422
3499
|
import { homedir as homedir2, platform } from "os";
|
|
@@ -3483,6 +3560,9 @@ var PUBLIC_ERROR_CODES2 = /* @__PURE__ */ new Set([
|
|
|
3483
3560
|
"unknown_service"
|
|
3484
3561
|
]);
|
|
3485
3562
|
var ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9_]{0,63}$/;
|
|
3563
|
+
var SERVICE_ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9]*(?:_[a-z][a-z0-9]*){1,9}$/;
|
|
3564
|
+
var SERVICE_MESSAGE_MAX_LENGTH2 = 300;
|
|
3565
|
+
var SERVICE_ISSUE_LIMIT2 = 32;
|
|
3486
3566
|
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
3567
|
var CLOUDFLARE_RAY_ID_PATTERN2 = /^[0-9a-f]{16}-[A-Z]{3}$/;
|
|
3488
3568
|
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 +3587,24 @@ function canonicalTopoloErrorCode2(value) {
|
|
|
3507
3587
|
if (!ERROR_CODE_PATTERN2.test(candidate) || !PUBLIC_ERROR_CODES2.has(candidate)) return null;
|
|
3508
3588
|
return candidate;
|
|
3509
3589
|
}
|
|
3590
|
+
function canonicalTopoloServiceErrorCode2(value) {
|
|
3591
|
+
const platform22 = canonicalTopoloErrorCode2(value);
|
|
3592
|
+
if (platform22) return platform22;
|
|
3593
|
+
if (typeof value !== "string") return null;
|
|
3594
|
+
const candidate = value.trim();
|
|
3595
|
+
if (candidate.length > 64 || !SERVICE_ERROR_CODE_PATTERN2.test(candidate)) return null;
|
|
3596
|
+
const wordLike = candidate.split("_").every((segment) => {
|
|
3597
|
+
const digits = segment.replace(/[a-z]/g, "").length;
|
|
3598
|
+
return digits === 0 || digits < segment.length - digits && segment.length <= 8;
|
|
3599
|
+
});
|
|
3600
|
+
return wordLike ? candidate : null;
|
|
3601
|
+
}
|
|
3602
|
+
function sanitizeTopoloServiceMessage2(value) {
|
|
3603
|
+
if (typeof value !== "string") return void 0;
|
|
3604
|
+
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();
|
|
3605
|
+
if (!text2) return void 0;
|
|
3606
|
+
return text2.length > SERVICE_MESSAGE_MAX_LENGTH2 ? `${text2.slice(0, SERVICE_MESSAGE_MAX_LENGTH2 - 1).trimEnd()}\u2026` : text2;
|
|
3607
|
+
}
|
|
3510
3608
|
function canonicalTopoloRequestId2(value) {
|
|
3511
3609
|
if (typeof value !== "string") return void 0;
|
|
3512
3610
|
const candidate = value.trim();
|
|
@@ -3531,7 +3629,7 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
|
3531
3629
|
retryAfter;
|
|
3532
3630
|
details;
|
|
3533
3631
|
constructor(message2, code = "auth_error", options = {}) {
|
|
3534
|
-
super(
|
|
3632
|
+
super(canonicalTopoloServiceErrorCode2(code) ?? "auth_error", message2);
|
|
3535
3633
|
this.name = "TopoloAuthError";
|
|
3536
3634
|
this.status = options.status ?? 401;
|
|
3537
3635
|
this.body = options.body;
|
|
@@ -3547,14 +3645,14 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
|
3547
3645
|
function extractTopoloServiceErrorCode2(body) {
|
|
3548
3646
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
3549
3647
|
const record2 = body;
|
|
3550
|
-
const topLevel =
|
|
3648
|
+
const topLevel = canonicalTopoloServiceErrorCode2(record2.code);
|
|
3551
3649
|
if (topLevel) return topLevel;
|
|
3552
3650
|
const nested = record2.error;
|
|
3553
|
-
const nestedString =
|
|
3651
|
+
const nestedString = canonicalTopoloServiceErrorCode2(nested);
|
|
3554
3652
|
if (nestedString) return nestedString;
|
|
3555
3653
|
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
3556
3654
|
const code = nested.code;
|
|
3557
|
-
const nestedCode =
|
|
3655
|
+
const nestedCode = canonicalTopoloServiceErrorCode2(code);
|
|
3558
3656
|
if (nestedCode) return nestedCode;
|
|
3559
3657
|
}
|
|
3560
3658
|
return null;
|
|
@@ -3572,10 +3670,7 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
3572
3670
|
if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
|
|
3573
3671
|
}
|
|
3574
3672
|
if (candidate === void 0) candidate = record2;
|
|
3575
|
-
if (Array.isArray(candidate))
|
|
3576
|
-
const paths = canonicalTopoloValidationPaths2(candidate);
|
|
3577
|
-
return paths.length ? { paths } : void 0;
|
|
3578
|
-
}
|
|
3673
|
+
if (Array.isArray(candidate)) return validationIssueDetails2(canonicalTopoloValidationIssues2(candidate)) ?? void 0;
|
|
3579
3674
|
if (!candidate || typeof candidate !== "object") return void 0;
|
|
3580
3675
|
const source = candidate;
|
|
3581
3676
|
const safe = {};
|
|
@@ -3617,18 +3712,59 @@ function extractTopoloServiceErrorDetails2(body) {
|
|
|
3617
3712
|
if (paths.length) safe[key] = paths;
|
|
3618
3713
|
}
|
|
3619
3714
|
}
|
|
3715
|
+
const issues = [
|
|
3716
|
+
...Array.isArray(source.issues) ? canonicalTopoloValidationIssues2(source.issues) : [],
|
|
3717
|
+
...flattenedValidationIssues2(source.formErrors, source.fieldErrors)
|
|
3718
|
+
].slice(0, SERVICE_ISSUE_LIMIT2);
|
|
3719
|
+
Object.assign(safe, validationIssueDetails2(issues));
|
|
3620
3720
|
return Object.keys(safe).length ? safe : void 0;
|
|
3621
3721
|
}
|
|
3622
|
-
function
|
|
3722
|
+
function validationIssueDetails2(issues) {
|
|
3723
|
+
if (!issues.length) return null;
|
|
3623
3724
|
const paths = [];
|
|
3624
|
-
for (const
|
|
3625
|
-
if (
|
|
3626
|
-
|
|
3725
|
+
for (const issue22 of issues) {
|
|
3726
|
+
if (issue22.path && !paths.includes(issue22.path)) paths.push(issue22.path);
|
|
3727
|
+
}
|
|
3728
|
+
return paths.length ? { paths, issues } : { issues };
|
|
3729
|
+
}
|
|
3730
|
+
function issue2(path, message2) {
|
|
3731
|
+
const text2 = sanitizeTopoloServiceMessage2(message2);
|
|
3732
|
+
if (!path && !text2) return null;
|
|
3733
|
+
return { ...path ? { path } : {}, ...text2 ? { message: text2 } : {} };
|
|
3734
|
+
}
|
|
3735
|
+
function canonicalTopoloValidationIssues2(values) {
|
|
3736
|
+
const issues = [];
|
|
3737
|
+
for (const value of values) {
|
|
3738
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
3739
|
+
const record2 = value;
|
|
3627
3740
|
const path = canonicalTopoloValidationPath2(record2.field) ?? canonicalTopoloValidationPath2(record2.path);
|
|
3628
|
-
if (
|
|
3629
|
-
|
|
3741
|
+
if (!path) continue;
|
|
3742
|
+
const projected = issue2(path, record2.message);
|
|
3743
|
+
if (projected) issues.push(projected);
|
|
3744
|
+
if (issues.length === SERVICE_ISSUE_LIMIT2) break;
|
|
3745
|
+
}
|
|
3746
|
+
return issues;
|
|
3747
|
+
}
|
|
3748
|
+
function flattenedValidationIssues2(formErrors, fieldErrors) {
|
|
3749
|
+
const issues = [];
|
|
3750
|
+
if (Array.isArray(formErrors)) {
|
|
3751
|
+
for (const message2 of formErrors) {
|
|
3752
|
+
const projected = issue2(void 0, message2);
|
|
3753
|
+
if (projected) issues.push(projected);
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
if (fieldErrors && typeof fieldErrors === "object" && !Array.isArray(fieldErrors)) {
|
|
3757
|
+
for (const [key, messages] of Object.entries(fieldErrors)) {
|
|
3758
|
+
const path = canonicalTopoloValidationPath2(key);
|
|
3759
|
+
if (!path) continue;
|
|
3760
|
+
const list = Array.isArray(messages) && messages.length ? messages : [void 0];
|
|
3761
|
+
for (const message2 of list) {
|
|
3762
|
+
const projected = issue2(path, message2);
|
|
3763
|
+
if (projected) issues.push(projected);
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3630
3766
|
}
|
|
3631
|
-
return
|
|
3767
|
+
return issues;
|
|
3632
3768
|
}
|
|
3633
3769
|
function canonicalTopoloValidationPath2(value) {
|
|
3634
3770
|
if (typeof value === "string") {
|
|
@@ -4462,6 +4598,10 @@ function fail2(err, mode) {
|
|
|
4462
4598
|
`);
|
|
4463
4599
|
if (payload.status) process.stderr.write(`HTTP: ${payload.status}
|
|
4464
4600
|
`);
|
|
4601
|
+
for (const issue3 of validationIssues(payload.details)) {
|
|
4602
|
+
process.stderr.write(`Issue: ${[issue3.path, issue3.message].filter(Boolean).join(": ")}
|
|
4603
|
+
`);
|
|
4604
|
+
}
|
|
4465
4605
|
if (payload.requestId) process.stderr.write(`Request: ${payload.requestId}
|
|
4466
4606
|
`);
|
|
4467
4607
|
if (payload.hint) process.stderr.write(`Hint: ${payload.hint}
|
|
@@ -4469,6 +4609,10 @@ function fail2(err, mode) {
|
|
|
4469
4609
|
}
|
|
4470
4610
|
process.exit(payload.exitCode);
|
|
4471
4611
|
}
|
|
4612
|
+
function validationIssues(details) {
|
|
4613
|
+
const issues = details?.issues;
|
|
4614
|
+
return Array.isArray(issues) ? issues : [];
|
|
4615
|
+
}
|
|
4472
4616
|
function normalizeError(err) {
|
|
4473
4617
|
if (err instanceof Error) {
|
|
4474
4618
|
const code = err.code;
|
|
@@ -4486,9 +4630,9 @@ function normalizeError(err) {
|
|
|
4486
4630
|
path: err.path,
|
|
4487
4631
|
requestId: err.requestId,
|
|
4488
4632
|
retryAfter: err.retryAfter,
|
|
4489
|
-
details
|
|
4490
|
-
|
|
4491
|
-
),
|
|
4633
|
+
// SDK HTTP errors already carry sanitized details; projecting them again
|
|
4634
|
+
// would drop form-level issues that have no field path.
|
|
4635
|
+
details: err instanceof TopoloHttpError || err instanceof TopoloAuthError || err instanceof TopoloPermissionError ? err.details : extractTopoloServiceErrorDetails(suppliedDetails === void 0 ? body : { details: suppliedDetails }),
|
|
4492
4636
|
hint: topoloErrorHint({ code, body }) ?? (code ? fallbackHintMap[code] : void 0),
|
|
4493
4637
|
exitCode: status === 401 || code === "auth_error" || code === "confirmation_required" ? 2 : status === 403 || code === "permission_denied" ? 3 : code === "immutable_drift" ? 4 : 1
|
|
4494
4638
|
};
|
|
@@ -4833,7 +4977,7 @@ function stderrDebugLogger(event) {
|
|
|
4833
4977
|
return;
|
|
4834
4978
|
}
|
|
4835
4979
|
const statusPart = event.status ? ` ${event.status}` : "";
|
|
4836
|
-
const errorCode =
|
|
4980
|
+
const errorCode = canonicalTopoloServiceErrorCode(event.error) ?? "request_failed";
|
|
4837
4981
|
process.stderr.write(
|
|
4838
4982
|
`[topolo] \u2717${statusPart} ${event.method} ${event.service} ${event.path} (${event.durationMs}ms, req=${requestId}, code=${errorCode})
|
|
4839
4983
|
`
|
|
@@ -5087,7 +5231,7 @@ Approval required. ${opened ? "Opening" : "Open"} ${approval.verificationUrl}` :
|
|
|
5087
5231
|
const topolo = await requiredClient();
|
|
5088
5232
|
const result = await topolo.client.validateActionInput(action, parseJsonObject(opts.data || "{}"));
|
|
5089
5233
|
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((
|
|
5234
|
+
emit(output, mode, () => result.validation.valid ? `Valid input for ${result.action.actionId}.` : result.validation.errors.map((issue3) => `${issue3.path}: ${issue3.message}`).join("\n"));
|
|
5091
5235
|
} catch (err) {
|
|
5092
5236
|
fail2(err, mode);
|
|
5093
5237
|
}
|
|
@@ -5367,7 +5511,7 @@ function formatActionPlan(plan) {
|
|
|
5367
5511
|
];
|
|
5368
5512
|
if (plan.request) lines.push(`Request: ${plan.request.method} ${plan.request.apiBaseUrl || plan.request.service}${plan.request.path}`);
|
|
5369
5513
|
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((
|
|
5514
|
+
if (plan.validation.errors.length) lines.push("", ...plan.validation.errors.map((issue3) => `${issue3.path}: ${issue3.message}`));
|
|
5371
5515
|
if (plan.verification.length) lines.push("", "Verify:", ...plan.verification.map((step) => `- ${step}`));
|
|
5372
5516
|
if (plan.rollbackActionId) lines.push("", `Rollback: ${plan.rollbackActionId}`);
|
|
5373
5517
|
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.12",
|
|
5
|
+
"topoloPublishedVersion": "0.12.12",
|
|
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.5",
|
|
32
|
+
"@topolo/sdk": "0.22.0",
|
|
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
|
|