@topolo/cli 0.6.4 → 0.6.6
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/README.md +4 -0
- package/dist/generate-wrangler.mjs +9 -3
- package/dist/index.js +687 -189
- package/package.json +17 -17
package/dist/index.js
CHANGED
|
@@ -37,19 +37,54 @@ var TopoloSdkError = class extends Error {
|
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
var TopoloAuthError = class extends TopoloSdkError {
|
|
40
|
-
|
|
40
|
+
status;
|
|
41
|
+
body;
|
|
42
|
+
service;
|
|
43
|
+
path;
|
|
44
|
+
requestId;
|
|
45
|
+
retryAfter;
|
|
46
|
+
constructor(message, code = "auth_error", options = {}) {
|
|
41
47
|
super(code, message);
|
|
42
48
|
this.name = "TopoloAuthError";
|
|
49
|
+
this.status = options.status ?? 401;
|
|
50
|
+
this.body = options.body;
|
|
51
|
+
this.service = options.service;
|
|
52
|
+
this.path = options.path;
|
|
53
|
+
this.requestId = options.requestId;
|
|
54
|
+
this.retryAfter = options.retryAfter;
|
|
43
55
|
}
|
|
44
56
|
};
|
|
45
57
|
var TopoloPermissionError = class extends TopoloSdkError {
|
|
46
58
|
required;
|
|
47
|
-
|
|
59
|
+
status;
|
|
60
|
+
body;
|
|
61
|
+
service;
|
|
62
|
+
path;
|
|
63
|
+
requestId;
|
|
64
|
+
retryAfter;
|
|
65
|
+
constructor(message, required, options = {}) {
|
|
48
66
|
super("permission_denied", message);
|
|
49
67
|
this.name = "TopoloPermissionError";
|
|
50
68
|
this.required = required;
|
|
69
|
+
this.status = options.status ?? 403;
|
|
70
|
+
this.body = options.body;
|
|
71
|
+
this.service = options.service;
|
|
72
|
+
this.path = options.path;
|
|
73
|
+
this.requestId = options.requestId;
|
|
74
|
+
this.retryAfter = options.retryAfter;
|
|
51
75
|
}
|
|
52
76
|
};
|
|
77
|
+
function extractHttpErrorCode(body) {
|
|
78
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return "http_error";
|
|
79
|
+
const record4 = body;
|
|
80
|
+
if (typeof record4.code === "string" && record4.code.trim()) return record4.code.trim();
|
|
81
|
+
const nested = record4.error;
|
|
82
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
83
|
+
const code = nested.code;
|
|
84
|
+
if (typeof code === "string" && code.trim()) return code.trim();
|
|
85
|
+
}
|
|
86
|
+
return "http_error";
|
|
87
|
+
}
|
|
53
88
|
var TopoloConfirmationRequiredError = class extends TopoloSdkError {
|
|
54
89
|
constructor(message) {
|
|
55
90
|
super("confirmation_required", message);
|
|
@@ -61,19 +96,24 @@ var TopoloHttpError = class extends TopoloSdkError {
|
|
|
61
96
|
body;
|
|
62
97
|
service;
|
|
63
98
|
path;
|
|
64
|
-
|
|
65
|
-
|
|
99
|
+
requestId;
|
|
100
|
+
retryAfter;
|
|
101
|
+
constructor(service, path9, status, body, message, options = {}) {
|
|
102
|
+
super(extractHttpErrorCode(body), message ?? `HTTP ${status} from ${service}${path9}`);
|
|
66
103
|
this.name = "TopoloHttpError";
|
|
67
104
|
this.status = status;
|
|
68
105
|
this.body = body;
|
|
69
106
|
this.service = service;
|
|
70
|
-
this.path =
|
|
107
|
+
this.path = path9;
|
|
108
|
+
this.requestId = options.requestId;
|
|
109
|
+
this.retryAfter = options.retryAfter;
|
|
71
110
|
}
|
|
72
111
|
};
|
|
73
112
|
var APP_API_URLS = {
|
|
74
113
|
agent: "https://agent.topolo.app",
|
|
75
114
|
auth: "https://auth.topolo.app",
|
|
76
115
|
backup: "https://backup.topolo.app",
|
|
116
|
+
blog: "https://blog.topolo.app",
|
|
77
117
|
books: "https://books.topolo.app",
|
|
78
118
|
brand: "https://brand.topolo.app",
|
|
79
119
|
bugfix: "https://bugfix.topolo.app",
|
|
@@ -86,6 +126,7 @@ var APP_API_URLS = {
|
|
|
86
126
|
compose: "https://compose.topolo.app",
|
|
87
127
|
consent: "https://consent.topolo.app",
|
|
88
128
|
crm: "https://crm.topolo.app",
|
|
129
|
+
design: "https://design.topolo.app",
|
|
89
130
|
developers: "https://developers.topolo.app",
|
|
90
131
|
director: "https://director.topolo.app",
|
|
91
132
|
feed: "https://feed.topolo.app",
|
|
@@ -169,7 +210,9 @@ var APPLICATIONS = {
|
|
|
169
210
|
"domains": [
|
|
170
211
|
"agent.topolo.app"
|
|
171
212
|
],
|
|
172
|
-
"routes": [
|
|
213
|
+
"routes": [
|
|
214
|
+
"agent.topolo.app"
|
|
215
|
+
],
|
|
173
216
|
"appId": "agent"
|
|
174
217
|
},
|
|
175
218
|
{
|
|
@@ -230,6 +273,38 @@ var APPLICATIONS = {
|
|
|
230
273
|
}
|
|
231
274
|
]
|
|
232
275
|
},
|
|
276
|
+
"blog": {
|
|
277
|
+
"id": "blog",
|
|
278
|
+
"appDir": "TopoloBlog",
|
|
279
|
+
"name": "topolo-blog",
|
|
280
|
+
"packageName": "topolo-blog",
|
|
281
|
+
"productionUrl": "https://blog.topolo.app",
|
|
282
|
+
"services": [
|
|
283
|
+
"blog"
|
|
284
|
+
],
|
|
285
|
+
"deployTargets": [
|
|
286
|
+
{
|
|
287
|
+
"name": "topolo-blog",
|
|
288
|
+
"kind": "worker",
|
|
289
|
+
"apiUrl": "https://blog.topolo.app",
|
|
290
|
+
"domains": [
|
|
291
|
+
"blog.topolo.app"
|
|
292
|
+
],
|
|
293
|
+
"routes": [
|
|
294
|
+
"blog.topolo.app"
|
|
295
|
+
],
|
|
296
|
+
"appId": "blog"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"name": "topolo-blog-api",
|
|
300
|
+
"kind": "worker",
|
|
301
|
+
"apiUrl": null,
|
|
302
|
+
"domains": [],
|
|
303
|
+
"routes": [],
|
|
304
|
+
"appId": null
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
},
|
|
233
308
|
"books": {
|
|
234
309
|
"id": "books",
|
|
235
310
|
"appDir": "TopoloBooks",
|
|
@@ -247,7 +322,9 @@ var APPLICATIONS = {
|
|
|
247
322
|
"domains": [
|
|
248
323
|
"books.topolo.app"
|
|
249
324
|
],
|
|
250
|
-
"routes": [
|
|
325
|
+
"routes": [
|
|
326
|
+
"books.topolo.app"
|
|
327
|
+
],
|
|
251
328
|
"appId": "books"
|
|
252
329
|
},
|
|
253
330
|
{
|
|
@@ -397,7 +474,9 @@ var APPLICATIONS = {
|
|
|
397
474
|
"domains": [
|
|
398
475
|
"campaigns.topolo.app"
|
|
399
476
|
],
|
|
400
|
-
"routes": [
|
|
477
|
+
"routes": [
|
|
478
|
+
"campaigns.topolo.app"
|
|
479
|
+
],
|
|
401
480
|
"appId": "campaigns"
|
|
402
481
|
},
|
|
403
482
|
{
|
|
@@ -607,11 +686,45 @@ var APPLICATIONS = {
|
|
|
607
686
|
"domains": [
|
|
608
687
|
"crm.topolo.app"
|
|
609
688
|
],
|
|
610
|
-
"routes": [
|
|
689
|
+
"routes": [
|
|
690
|
+
"crm.topolo.app"
|
|
691
|
+
],
|
|
611
692
|
"appId": "crm"
|
|
612
693
|
}
|
|
613
694
|
]
|
|
614
695
|
},
|
|
696
|
+
"design": {
|
|
697
|
+
"id": "design",
|
|
698
|
+
"appDir": "TopoloDesign",
|
|
699
|
+
"name": "topolo-design",
|
|
700
|
+
"packageName": "@topolo-io/design",
|
|
701
|
+
"productionUrl": "https://design.topolo.app",
|
|
702
|
+
"services": [
|
|
703
|
+
"design"
|
|
704
|
+
],
|
|
705
|
+
"deployTargets": [
|
|
706
|
+
{
|
|
707
|
+
"name": "topolo-design",
|
|
708
|
+
"kind": "worker",
|
|
709
|
+
"apiUrl": "https://design.topolo.app",
|
|
710
|
+
"domains": [
|
|
711
|
+
"design.topolo.app"
|
|
712
|
+
],
|
|
713
|
+
"routes": [
|
|
714
|
+
"design.topolo.app"
|
|
715
|
+
],
|
|
716
|
+
"appId": "design"
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
"name": "topolo-design-api",
|
|
720
|
+
"kind": "worker",
|
|
721
|
+
"apiUrl": null,
|
|
722
|
+
"domains": [],
|
|
723
|
+
"routes": [],
|
|
724
|
+
"appId": null
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
},
|
|
615
728
|
"developers": {
|
|
616
729
|
"id": "developers",
|
|
617
730
|
"appDir": "TopoloDevelopers",
|
|
@@ -835,7 +948,9 @@ var APPLICATIONS = {
|
|
|
835
948
|
"domains": [
|
|
836
949
|
"forms.topolo.app"
|
|
837
950
|
],
|
|
838
|
-
"routes": [
|
|
951
|
+
"routes": [
|
|
952
|
+
"forms.topolo.app"
|
|
953
|
+
],
|
|
839
954
|
"appId": "forms"
|
|
840
955
|
},
|
|
841
956
|
{
|
|
@@ -897,7 +1012,9 @@ var APPLICATIONS = {
|
|
|
897
1012
|
"domains": [
|
|
898
1013
|
"insights.topolo.app"
|
|
899
1014
|
],
|
|
900
|
-
"routes": [
|
|
1015
|
+
"routes": [
|
|
1016
|
+
"insights.topolo.app"
|
|
1017
|
+
],
|
|
901
1018
|
"appId": "insights"
|
|
902
1019
|
},
|
|
903
1020
|
{
|
|
@@ -1281,7 +1398,9 @@ var APPLICATIONS = {
|
|
|
1281
1398
|
"domains": [
|
|
1282
1399
|
"pay.topolo.app"
|
|
1283
1400
|
],
|
|
1284
|
-
"routes": [
|
|
1401
|
+
"routes": [
|
|
1402
|
+
"pay.topolo.app"
|
|
1403
|
+
],
|
|
1285
1404
|
"appId": "pay"
|
|
1286
1405
|
},
|
|
1287
1406
|
{
|
|
@@ -1366,7 +1485,9 @@ var APPLICATIONS = {
|
|
|
1366
1485
|
"domains": [
|
|
1367
1486
|
"quro.topolo.app"
|
|
1368
1487
|
],
|
|
1369
|
-
"routes": [
|
|
1488
|
+
"routes": [
|
|
1489
|
+
"quro.topolo.app"
|
|
1490
|
+
],
|
|
1370
1491
|
"appId": null
|
|
1371
1492
|
}
|
|
1372
1493
|
]
|
|
@@ -1650,7 +1771,9 @@ var APPLICATIONS = {
|
|
|
1650
1771
|
"domains": [
|
|
1651
1772
|
"survey.topolo.app"
|
|
1652
1773
|
],
|
|
1653
|
-
"routes": [
|
|
1774
|
+
"routes": [
|
|
1775
|
+
"survey.topolo.app"
|
|
1776
|
+
],
|
|
1654
1777
|
"appId": "survey"
|
|
1655
1778
|
},
|
|
1656
1779
|
{
|
|
@@ -2232,17 +2355,17 @@ function validateTopoloActionInput(schema, input3) {
|
|
|
2232
2355
|
validateNode(schema, input3, "$", schema, errors, 0);
|
|
2233
2356
|
return { valid: errors.length === 0, errors };
|
|
2234
2357
|
}
|
|
2235
|
-
function inspectSchema(schema,
|
|
2236
|
-
if (depth > 128) return { path:
|
|
2358
|
+
function inspectSchema(schema, path9, root, depth) {
|
|
2359
|
+
if (depth > 128) return { path: path9, code: "schema_depth", message: "Schema recursion exceeded 128 levels." };
|
|
2237
2360
|
const unsupportedKeyword = Object.keys(schema).find((keyword) => !SUPPORTED_SCHEMA_KEYWORDS.has(keyword));
|
|
2238
2361
|
if (unsupportedKeyword) {
|
|
2239
|
-
return { path:
|
|
2362
|
+
return { path: path9, code: "unsupported_schema_keyword", message: `Action schema uses unsupported keyword ${unsupportedKeyword}.` };
|
|
2240
2363
|
}
|
|
2241
2364
|
if (typeof schema.format === "string" && !["uri", "email", "date-time"].includes(schema.format)) {
|
|
2242
|
-
return { path:
|
|
2365
|
+
return { path: path9, code: "unsupported_schema_format", message: `Action schema uses unsupported format ${schema.format}.` };
|
|
2243
2366
|
}
|
|
2244
2367
|
if (typeof schema.$ref === "string" && !resolveLocalReference(root, schema.$ref)) {
|
|
2245
|
-
return { path:
|
|
2368
|
+
return { path: path9, code: "unsupported_schema_ref", message: `Cannot resolve schema reference ${schema.$ref}.` };
|
|
2246
2369
|
}
|
|
2247
2370
|
const children = [];
|
|
2248
2371
|
if (isRecord(schema.properties)) children.push(...Object.entries(schema.properties));
|
|
@@ -2258,7 +2381,7 @@ function inspectSchema(schema, path8, root, depth) {
|
|
|
2258
2381
|
);
|
|
2259
2382
|
for (const [key, child] of children) {
|
|
2260
2383
|
if (!isRecord(child)) continue;
|
|
2261
|
-
const issue = inspectSchema(child, `${
|
|
2384
|
+
const issue = inspectSchema(child, `${path9}.${key}`, root, depth + 1);
|
|
2262
2385
|
if (issue) return issue;
|
|
2263
2386
|
}
|
|
2264
2387
|
return null;
|
|
@@ -2266,129 +2389,129 @@ function inspectSchema(schema, path8, root, depth) {
|
|
|
2266
2389
|
function arraySchemaEntries(keyword, value) {
|
|
2267
2390
|
return Array.isArray(value) ? value.map((candidate, index) => [`${keyword}[${index}]`, candidate]) : [];
|
|
2268
2391
|
}
|
|
2269
|
-
function validateNode(schema, value,
|
|
2392
|
+
function validateNode(schema, value, path9, root, errors, depth) {
|
|
2270
2393
|
if (depth > 128) {
|
|
2271
|
-
errors.push({ path:
|
|
2394
|
+
errors.push({ path: path9, code: "schema_depth", message: "Schema recursion exceeded 128 levels." });
|
|
2272
2395
|
return;
|
|
2273
2396
|
}
|
|
2274
2397
|
if (typeof schema.$ref === "string") {
|
|
2275
2398
|
const resolved = resolveLocalReference(root, schema.$ref);
|
|
2276
2399
|
if (!resolved) {
|
|
2277
|
-
errors.push({ path:
|
|
2400
|
+
errors.push({ path: path9, code: "unsupported_schema_ref", message: `Cannot resolve schema reference ${schema.$ref}.` });
|
|
2278
2401
|
return;
|
|
2279
2402
|
}
|
|
2280
|
-
validateNode(resolved, value,
|
|
2403
|
+
validateNode(resolved, value, path9, root, errors, depth + 1);
|
|
2281
2404
|
}
|
|
2282
2405
|
if (Array.isArray(schema.allOf)) {
|
|
2283
2406
|
for (const candidate of schema.allOf) {
|
|
2284
|
-
if (isRecord(candidate)) validateNode(candidate, value,
|
|
2407
|
+
if (isRecord(candidate)) validateNode(candidate, value, path9, root, errors, depth + 1);
|
|
2285
2408
|
}
|
|
2286
2409
|
}
|
|
2287
2410
|
if (Array.isArray(schema.anyOf) && !schema.anyOf.some((candidate) => candidateMatches(candidate, value, root, depth))) {
|
|
2288
|
-
errors.push({ path:
|
|
2411
|
+
errors.push({ path: path9, code: "any_of", message: "Value does not match any allowed schema." });
|
|
2289
2412
|
return;
|
|
2290
2413
|
}
|
|
2291
2414
|
if (Array.isArray(schema.oneOf)) {
|
|
2292
2415
|
const matches = schema.oneOf.filter((candidate) => candidateMatches(candidate, value, root, depth)).length;
|
|
2293
2416
|
if (matches !== 1) {
|
|
2294
|
-
errors.push({ path:
|
|
2417
|
+
errors.push({ path: path9, code: "one_of", message: "Value must match exactly one allowed schema." });
|
|
2295
2418
|
return;
|
|
2296
2419
|
}
|
|
2297
2420
|
}
|
|
2298
2421
|
if (value === null && schema.nullable === true) return;
|
|
2299
2422
|
if (Object.prototype.hasOwnProperty.call(schema, "const") && !sameJsonValue(value, schema.const)) {
|
|
2300
|
-
errors.push({ path:
|
|
2423
|
+
errors.push({ path: path9, code: "const", message: `Value must equal ${JSON.stringify(schema.const)}.` });
|
|
2301
2424
|
return;
|
|
2302
2425
|
}
|
|
2303
2426
|
if (Array.isArray(schema.enum) && !schema.enum.some((candidate) => sameJsonValue(value, candidate))) {
|
|
2304
|
-
errors.push({ path:
|
|
2427
|
+
errors.push({ path: path9, code: "enum", message: `Value must be one of ${schema.enum.map((item) => JSON.stringify(item)).join(", ")}.` });
|
|
2305
2428
|
return;
|
|
2306
2429
|
}
|
|
2307
2430
|
if (!matchesType(schema.type, value)) {
|
|
2308
|
-
errors.push({ path:
|
|
2431
|
+
errors.push({ path: path9, code: "type", message: `Expected ${String(schema.type)}.` });
|
|
2309
2432
|
return;
|
|
2310
2433
|
}
|
|
2311
|
-
if (typeof value === "string") validateString(schema, value,
|
|
2312
|
-
if (typeof value === "number") validateNumber(schema, value,
|
|
2313
|
-
if (Array.isArray(value)) validateArray(schema, value,
|
|
2314
|
-
if (isRecord(value)) validateObject(schema, value,
|
|
2434
|
+
if (typeof value === "string") validateString(schema, value, path9, errors);
|
|
2435
|
+
if (typeof value === "number") validateNumber(schema, value, path9, errors);
|
|
2436
|
+
if (Array.isArray(value)) validateArray(schema, value, path9, root, errors, depth);
|
|
2437
|
+
if (isRecord(value)) validateObject(schema, value, path9, root, errors, depth);
|
|
2315
2438
|
}
|
|
2316
|
-
function validateObject(schema, value,
|
|
2439
|
+
function validateObject(schema, value, path9, root, errors, depth) {
|
|
2317
2440
|
if (typeof schema.minProperties === "number" && Object.keys(value).length < schema.minProperties) {
|
|
2318
|
-
errors.push({ path:
|
|
2441
|
+
errors.push({ path: path9, code: "min_properties", message: `Expected at least ${schema.minProperties} properties.` });
|
|
2319
2442
|
}
|
|
2320
2443
|
if (typeof schema.maxProperties === "number" && Object.keys(value).length > schema.maxProperties) {
|
|
2321
|
-
errors.push({ path:
|
|
2444
|
+
errors.push({ path: path9, code: "max_properties", message: `Expected at most ${schema.maxProperties} properties.` });
|
|
2322
2445
|
}
|
|
2323
2446
|
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
2324
2447
|
const required = Array.isArray(schema.required) ? schema.required.map(String) : [];
|
|
2325
2448
|
for (const key of required) {
|
|
2326
2449
|
if (!Object.prototype.hasOwnProperty.call(value, key)) {
|
|
2327
|
-
errors.push({ path: `${
|
|
2450
|
+
errors.push({ path: `${path9}.${key}`, code: "required", message: "Required property is missing." });
|
|
2328
2451
|
}
|
|
2329
2452
|
}
|
|
2330
2453
|
if (isRecord(schema.propertyNames)) {
|
|
2331
2454
|
for (const key of Object.keys(value)) {
|
|
2332
|
-
validateNode(schema.propertyNames, key, `${
|
|
2455
|
+
validateNode(schema.propertyNames, key, `${path9}.${key}`, root, errors, depth + 1);
|
|
2333
2456
|
}
|
|
2334
2457
|
}
|
|
2335
2458
|
for (const [key, child] of Object.entries(value)) {
|
|
2336
2459
|
const childSchema = properties[key];
|
|
2337
2460
|
if (isRecord(childSchema)) {
|
|
2338
|
-
validateNode(childSchema, child, `${
|
|
2461
|
+
validateNode(childSchema, child, `${path9}.${key}`, root, errors, depth + 1);
|
|
2339
2462
|
} else if (schema.additionalProperties === false) {
|
|
2340
|
-
errors.push({ path: `${
|
|
2463
|
+
errors.push({ path: `${path9}.${key}`, code: "additional_property", message: "Property is not declared by the action schema." });
|
|
2341
2464
|
} else if (isRecord(schema.additionalProperties)) {
|
|
2342
|
-
validateNode(schema.additionalProperties, child, `${
|
|
2465
|
+
validateNode(schema.additionalProperties, child, `${path9}.${key}`, root, errors, depth + 1);
|
|
2343
2466
|
}
|
|
2344
2467
|
}
|
|
2345
2468
|
}
|
|
2346
|
-
function validateArray(schema, value,
|
|
2469
|
+
function validateArray(schema, value, path9, root, errors, depth) {
|
|
2347
2470
|
if (typeof schema.minItems === "number" && value.length < schema.minItems) {
|
|
2348
|
-
errors.push({ path:
|
|
2471
|
+
errors.push({ path: path9, code: "min_items", message: `Expected at least ${schema.minItems} items.` });
|
|
2349
2472
|
}
|
|
2350
2473
|
if (typeof schema.maxItems === "number" && value.length > schema.maxItems) {
|
|
2351
|
-
errors.push({ path:
|
|
2474
|
+
errors.push({ path: path9, code: "max_items", message: `Expected at most ${schema.maxItems} items.` });
|
|
2352
2475
|
}
|
|
2353
2476
|
if (schema.uniqueItems === true) {
|
|
2354
2477
|
const unique4 = new Set(value.map((item) => JSON.stringify(item)));
|
|
2355
|
-
if (unique4.size !== value.length) errors.push({ path:
|
|
2478
|
+
if (unique4.size !== value.length) errors.push({ path: path9, code: "unique_items", message: "Expected every item to be unique." });
|
|
2356
2479
|
}
|
|
2357
2480
|
if (isRecord(schema.items)) {
|
|
2358
|
-
value.forEach((item, index) => validateNode(schema.items, item, `${
|
|
2481
|
+
value.forEach((item, index) => validateNode(schema.items, item, `${path9}[${index}]`, root, errors, depth + 1));
|
|
2359
2482
|
}
|
|
2360
2483
|
}
|
|
2361
|
-
function validateString(schema, value,
|
|
2484
|
+
function validateString(schema, value, path9, errors) {
|
|
2362
2485
|
if (typeof schema.minLength === "number" && value.length < schema.minLength) {
|
|
2363
|
-
errors.push({ path:
|
|
2486
|
+
errors.push({ path: path9, code: "min_length", message: `Expected at least ${schema.minLength} characters.` });
|
|
2364
2487
|
}
|
|
2365
2488
|
if (typeof schema.maxLength === "number" && value.length > schema.maxLength) {
|
|
2366
|
-
errors.push({ path:
|
|
2489
|
+
errors.push({ path: path9, code: "max_length", message: `Expected at most ${schema.maxLength} characters.` });
|
|
2367
2490
|
}
|
|
2368
2491
|
if (typeof schema.pattern === "string") {
|
|
2369
2492
|
try {
|
|
2370
|
-
if (!new RegExp(schema.pattern).test(value)) errors.push({ path:
|
|
2493
|
+
if (!new RegExp(schema.pattern).test(value)) errors.push({ path: path9, code: "pattern", message: "Value does not match the required pattern." });
|
|
2371
2494
|
} catch {
|
|
2372
|
-
errors.push({ path:
|
|
2495
|
+
errors.push({ path: path9, code: "invalid_schema_pattern", message: "Action schema contains an invalid pattern." });
|
|
2373
2496
|
}
|
|
2374
2497
|
}
|
|
2375
|
-
if (schema.format === "uri" && !isUrl(value)) errors.push({ path:
|
|
2376
|
-
if (schema.format === "email" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)) errors.push({ path:
|
|
2377
|
-
if (schema.format === "date-time" && !isDateTime(value)) errors.push({ path:
|
|
2498
|
+
if (schema.format === "uri" && !isUrl(value)) errors.push({ path: path9, code: "format_uri", message: "Expected an absolute URL." });
|
|
2499
|
+
if (schema.format === "email" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)) errors.push({ path: path9, code: "format_email", message: "Expected an email address." });
|
|
2500
|
+
if (schema.format === "date-time" && !isDateTime(value)) errors.push({ path: path9, code: "format_date_time", message: "Expected an ISO-8601 date-time." });
|
|
2378
2501
|
}
|
|
2379
|
-
function validateNumber(schema, value,
|
|
2380
|
-
if (typeof schema.minimum === "number" && value < schema.minimum) errors.push({ path:
|
|
2381
|
-
if (typeof schema.maximum === "number" && value > schema.maximum) errors.push({ path:
|
|
2502
|
+
function validateNumber(schema, value, path9, errors) {
|
|
2503
|
+
if (typeof schema.minimum === "number" && value < schema.minimum) errors.push({ path: path9, code: "minimum", message: `Expected a value of at least ${schema.minimum}.` });
|
|
2504
|
+
if (typeof schema.maximum === "number" && value > schema.maximum) errors.push({ path: path9, code: "maximum", message: `Expected a value of at most ${schema.maximum}.` });
|
|
2382
2505
|
const exclusiveMinimum = typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : schema.exclusiveMinimum === true && typeof schema.minimum === "number" ? schema.minimum : null;
|
|
2383
2506
|
const exclusiveMaximum = typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : schema.exclusiveMaximum === true && typeof schema.maximum === "number" ? schema.maximum : null;
|
|
2384
2507
|
if (exclusiveMinimum !== null && value <= exclusiveMinimum) {
|
|
2385
|
-
errors.push({ path:
|
|
2508
|
+
errors.push({ path: path9, code: "exclusive_minimum", message: `Expected a value greater than ${exclusiveMinimum}.` });
|
|
2386
2509
|
}
|
|
2387
2510
|
if (exclusiveMaximum !== null && value >= exclusiveMaximum) {
|
|
2388
|
-
errors.push({ path:
|
|
2511
|
+
errors.push({ path: path9, code: "exclusive_maximum", message: `Expected a value less than ${exclusiveMaximum}.` });
|
|
2389
2512
|
}
|
|
2390
2513
|
if (typeof schema.multipleOf === "number" && !isMultipleOf(value, schema.multipleOf)) {
|
|
2391
|
-
errors.push({ path:
|
|
2514
|
+
errors.push({ path: path9, code: "multiple_of", message: `Expected a multiple of ${schema.multipleOf}.` });
|
|
2392
2515
|
}
|
|
2393
2516
|
}
|
|
2394
2517
|
function candidateMatches(candidate, value, root, depth) {
|
|
@@ -2441,6 +2564,166 @@ function isMultipleOf(value, divisor) {
|
|
|
2441
2564
|
const quotient = value / divisor;
|
|
2442
2565
|
return Math.abs(quotient - Math.round(quotient)) < Number.EPSILON * Math.max(1, Math.abs(quotient)) * 4;
|
|
2443
2566
|
}
|
|
2567
|
+
function parseTopoloActionUploadContract(value) {
|
|
2568
|
+
if (!isRecord2(value) || value.profile !== "inline-base64" && value.profile !== "url-session") {
|
|
2569
|
+
throw new TopoloSdkError("upload_contract_unavailable", "This action does not publish a supported upload contract.");
|
|
2570
|
+
}
|
|
2571
|
+
const contentTypeField = requiredField(value.contentTypeField, "contentTypeField");
|
|
2572
|
+
const fileNameField = requiredField(value.fileNameField, "fileNameField");
|
|
2573
|
+
const maxBytes = Number(value.maxBytes);
|
|
2574
|
+
const acceptedContentTypes = Array.isArray(value.acceptedContentTypes) ? value.acceptedContentTypes.map((item) => String(item).trim().toLowerCase()).filter(Boolean) : [];
|
|
2575
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0 || acceptedContentTypes.length === 0) {
|
|
2576
|
+
throw new TopoloSdkError("invalid_upload_contract", "The action upload contract must declare maxBytes and acceptedContentTypes.");
|
|
2577
|
+
}
|
|
2578
|
+
if (value.profile === "inline-base64") {
|
|
2579
|
+
return {
|
|
2580
|
+
profile: "inline-base64",
|
|
2581
|
+
contentField: requiredField(value.contentField, "contentField"),
|
|
2582
|
+
contentTypeField,
|
|
2583
|
+
fileNameField,
|
|
2584
|
+
maxBytes,
|
|
2585
|
+
acceptedContentTypes
|
|
2586
|
+
};
|
|
2587
|
+
}
|
|
2588
|
+
const uploadMethod = value.uploadMethod === "PUT" || value.uploadMethod === "POST" ? value.uploadMethod : null;
|
|
2589
|
+
const uploadUrlPath = requiredPath(value.uploadUrlPath, "uploadUrlPath");
|
|
2590
|
+
const complete = value.complete === void 0 ? void 0 : parseCompleteContract(value.complete);
|
|
2591
|
+
if (!uploadMethod) throw new TopoloSdkError("invalid_upload_contract", "The action upload contract has an invalid uploadMethod.");
|
|
2592
|
+
return {
|
|
2593
|
+
profile: "url-session",
|
|
2594
|
+
fileNameField,
|
|
2595
|
+
contentTypeField,
|
|
2596
|
+
sizeField: requiredField(value.sizeField, "sizeField"),
|
|
2597
|
+
maxBytes,
|
|
2598
|
+
acceptedContentTypes,
|
|
2599
|
+
uploadUrlPath,
|
|
2600
|
+
uploadMethod,
|
|
2601
|
+
...complete ? { complete } : {}
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
function prepareTopoloActionUploadInput(contractValue, content, input3, options) {
|
|
2605
|
+
const contract = parseTopoloActionUploadContract(contractValue);
|
|
2606
|
+
if (contract.profile !== "inline-base64") throw unsupportedInlineProfile();
|
|
2607
|
+
const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
|
|
2608
|
+
if (bytes.byteLength === 0) {
|
|
2609
|
+
throw new TopoloSdkError("invalid_upload_content", "Upload content must not be empty.");
|
|
2610
|
+
}
|
|
2611
|
+
if (bytes.byteLength > contract.maxBytes) {
|
|
2612
|
+
throw new TopoloSdkError("upload_too_large", `Upload content exceeds the ${contract.maxBytes}-byte action limit.`);
|
|
2613
|
+
}
|
|
2614
|
+
return mapUploadInput(contract, bytesToBase64(bytes), input3, options);
|
|
2615
|
+
}
|
|
2616
|
+
function prepareTopoloActionBase64UploadInput(contractValue, contentBase64, input3, options) {
|
|
2617
|
+
const contract = parseTopoloActionUploadContract(contractValue);
|
|
2618
|
+
if (contract.profile !== "inline-base64") throw unsupportedInlineProfile();
|
|
2619
|
+
if (!isCanonicalBase64(contentBase64)) {
|
|
2620
|
+
throw new TopoloSdkError("invalid_upload_content", "Upload content must be canonical base64.");
|
|
2621
|
+
}
|
|
2622
|
+
const padding = contentBase64.endsWith("==") ? 2 : contentBase64.endsWith("=") ? 1 : 0;
|
|
2623
|
+
const byteLength = contentBase64.length / 4 * 3 - padding;
|
|
2624
|
+
if (byteLength === 0) throw new TopoloSdkError("invalid_upload_content", "Upload content must not be empty.");
|
|
2625
|
+
if (byteLength > contract.maxBytes) {
|
|
2626
|
+
throw new TopoloSdkError("upload_too_large", `Upload content exceeds the ${contract.maxBytes}-byte action limit.`);
|
|
2627
|
+
}
|
|
2628
|
+
return mapUploadInput(contract, contentBase64, input3, options);
|
|
2629
|
+
}
|
|
2630
|
+
function prepareTopoloUrlSessionUploadInput(contractValue, byteLength, input3, options) {
|
|
2631
|
+
const contract = parseTopoloActionUploadContract(contractValue);
|
|
2632
|
+
if (contract.profile !== "url-session") {
|
|
2633
|
+
throw new TopoloSdkError("invalid_upload_contract", "This action does not publish a URL-session upload contract.");
|
|
2634
|
+
}
|
|
2635
|
+
validateUploadMetadata(contract, byteLength, options);
|
|
2636
|
+
return {
|
|
2637
|
+
contract,
|
|
2638
|
+
input: {
|
|
2639
|
+
...input3,
|
|
2640
|
+
[contract.fileNameField]: options.fileName.trim(),
|
|
2641
|
+
[contract.contentTypeField]: options.contentType.trim().toLowerCase(),
|
|
2642
|
+
[contract.sizeField]: byteLength
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
function topoloUploadResponseValue(response, path9) {
|
|
2647
|
+
return path9.split(".").reduce((value, segment) => isRecord2(value) ? value[segment] : void 0, response);
|
|
2648
|
+
}
|
|
2649
|
+
function decodeTopoloActionBase64(contentBase64) {
|
|
2650
|
+
if (!isCanonicalBase64(contentBase64)) {
|
|
2651
|
+
throw new TopoloSdkError("invalid_upload_content", "Upload content must be canonical base64.");
|
|
2652
|
+
}
|
|
2653
|
+
const binary = atob(contentBase64);
|
|
2654
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
2655
|
+
}
|
|
2656
|
+
function mapUploadInput(contract, contentBase64, input3, options) {
|
|
2657
|
+
validateUploadMetadata(contract, Math.max(1, decodedBase64Size(contentBase64)), options);
|
|
2658
|
+
const fileName = options.fileName.trim();
|
|
2659
|
+
const contentType = options.contentType.trim().toLowerCase();
|
|
2660
|
+
return {
|
|
2661
|
+
...input3,
|
|
2662
|
+
[contract.contentField]: contentBase64,
|
|
2663
|
+
[contract.contentTypeField]: contentType,
|
|
2664
|
+
[contract.fileNameField]: fileName
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
function validateUploadMetadata(contract, byteLength, options) {
|
|
2668
|
+
const fileName = options.fileName.trim();
|
|
2669
|
+
const contentType = options.contentType.trim().toLowerCase();
|
|
2670
|
+
if (!fileName) throw new TopoloSdkError("invalid_upload_file_name", "Upload fileName must not be empty.");
|
|
2671
|
+
if (!Number.isSafeInteger(byteLength) || byteLength <= 0) {
|
|
2672
|
+
throw new TopoloSdkError("invalid_upload_content", "Upload content must not be empty.");
|
|
2673
|
+
}
|
|
2674
|
+
if (byteLength > contract.maxBytes) {
|
|
2675
|
+
throw new TopoloSdkError("upload_too_large", `Upload content exceeds the ${contract.maxBytes}-byte action limit.`);
|
|
2676
|
+
}
|
|
2677
|
+
if (!matchesContentType(contentType, contract.acceptedContentTypes)) {
|
|
2678
|
+
throw new TopoloSdkError("unsupported_upload_content_type", `Content type "${contentType}" is not accepted by this action.`);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
function decodedBase64Size(contentBase64) {
|
|
2682
|
+
const padding = contentBase64.endsWith("==") ? 2 : contentBase64.endsWith("=") ? 1 : 0;
|
|
2683
|
+
return contentBase64.length / 4 * 3 - padding;
|
|
2684
|
+
}
|
|
2685
|
+
function parseCompleteContract(value) {
|
|
2686
|
+
if (!isRecord2(value)) throw new TopoloSdkError("invalid_upload_contract", "The action upload completion contract is invalid.");
|
|
2687
|
+
return {
|
|
2688
|
+
actionId: requiredPath(value.actionId, "complete.actionId"),
|
|
2689
|
+
inputField: requiredField(value.inputField, "complete.inputField"),
|
|
2690
|
+
responsePath: requiredPath(value.responsePath, "complete.responsePath")
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
function requiredPath(value, name) {
|
|
2694
|
+
const path9 = typeof value === "string" ? value.trim() : "";
|
|
2695
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(path9)) {
|
|
2696
|
+
throw new TopoloSdkError("invalid_upload_contract", `The action upload contract has an invalid ${name}.`);
|
|
2697
|
+
}
|
|
2698
|
+
return path9;
|
|
2699
|
+
}
|
|
2700
|
+
function unsupportedInlineProfile() {
|
|
2701
|
+
return new TopoloSdkError("invalid_upload_contract", "This action does not publish an inline-base64 upload contract.");
|
|
2702
|
+
}
|
|
2703
|
+
function isCanonicalBase64(value) {
|
|
2704
|
+
return value.length > 0 && value.length % 4 === 0 && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
|
|
2705
|
+
}
|
|
2706
|
+
function requiredField(value, name) {
|
|
2707
|
+
const field = typeof value === "string" ? value.trim() : "";
|
|
2708
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(field)) {
|
|
2709
|
+
throw new TopoloSdkError("invalid_upload_contract", `The action upload contract has an invalid ${name}.`);
|
|
2710
|
+
}
|
|
2711
|
+
return field;
|
|
2712
|
+
}
|
|
2713
|
+
function matchesContentType(contentType, accepted) {
|
|
2714
|
+
return accepted.some((candidate) => candidate === "*/*" || candidate === contentType || candidate.endsWith("/*") && contentType.startsWith(candidate.slice(0, -1)));
|
|
2715
|
+
}
|
|
2716
|
+
function bytesToBase64(bytes) {
|
|
2717
|
+
let binary = "";
|
|
2718
|
+
const chunkSize = 32768;
|
|
2719
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
2720
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
|
|
2721
|
+
}
|
|
2722
|
+
return btoa(binary);
|
|
2723
|
+
}
|
|
2724
|
+
function isRecord2(value) {
|
|
2725
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2726
|
+
}
|
|
2444
2727
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
2445
2728
|
var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
|
|
2446
2729
|
var TOPOLO_ACTION_NAME_HEADER = "X-Topolo-Action-Name";
|
|
@@ -2517,6 +2800,9 @@ var TopoloClient = class _TopoloClient {
|
|
|
2517
2800
|
if (v !== void 0) url.searchParams.set(k, String(v));
|
|
2518
2801
|
}
|
|
2519
2802
|
}
|
|
2803
|
+
if (opts.body !== void 0 && opts.rawBody !== void 0) {
|
|
2804
|
+
throw new TopoloSdkError("invalid_request_body", "body and rawBody are mutually exclusive.");
|
|
2805
|
+
}
|
|
2520
2806
|
const headers = new Headers();
|
|
2521
2807
|
headers.set("Accept", "application/json");
|
|
2522
2808
|
if (opts.body !== void 0) headers.set("Content-Type", "application/json");
|
|
@@ -2551,7 +2837,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2551
2837
|
return await this.fetchImpl(urlStr, {
|
|
2552
2838
|
method,
|
|
2553
2839
|
headers: new Headers(headers),
|
|
2554
|
-
body: opts.body !== void 0 ? JSON.stringify(opts.body) : null,
|
|
2840
|
+
body: opts.rawBody ?? (opts.body !== void 0 ? JSON.stringify(opts.body) : null),
|
|
2555
2841
|
signal
|
|
2556
2842
|
});
|
|
2557
2843
|
} finally {
|
|
@@ -2599,14 +2885,32 @@ var TopoloClient = class _TopoloClient {
|
|
|
2599
2885
|
error: message,
|
|
2600
2886
|
status: res.status
|
|
2601
2887
|
});
|
|
2602
|
-
|
|
2888
|
+
const errorContext = {
|
|
2889
|
+
status: res.status,
|
|
2890
|
+
body: parsed,
|
|
2891
|
+
service: resolvedService.serviceKey,
|
|
2892
|
+
path: opts.path,
|
|
2893
|
+
requestId: res.headers.get("X-Topolo-Request-Id") || requestId,
|
|
2894
|
+
retryAfter: res.headers.get("Retry-After") || void 0
|
|
2895
|
+
};
|
|
2896
|
+
if (res.status === 401) {
|
|
2897
|
+
throw new TopoloAuthError(describeError(parsed, "Unauthorized"), "auth_error", errorContext);
|
|
2898
|
+
}
|
|
2603
2899
|
if (res.status === 403) {
|
|
2604
2900
|
throw new TopoloPermissionError(
|
|
2605
2901
|
describeError(parsed, "Permission denied"),
|
|
2606
|
-
extractRequired(parsed)
|
|
2902
|
+
extractRequired(parsed),
|
|
2903
|
+
errorContext
|
|
2607
2904
|
);
|
|
2608
2905
|
}
|
|
2609
|
-
throw new TopoloHttpError(
|
|
2906
|
+
throw new TopoloHttpError(
|
|
2907
|
+
resolvedService.serviceKey,
|
|
2908
|
+
opts.path,
|
|
2909
|
+
res.status,
|
|
2910
|
+
parsed,
|
|
2911
|
+
message,
|
|
2912
|
+
errorContext
|
|
2913
|
+
);
|
|
2610
2914
|
}
|
|
2611
2915
|
this.emit({
|
|
2612
2916
|
phase: "response",
|
|
@@ -2765,7 +3069,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
2765
3069
|
}
|
|
2766
3070
|
const entries = [];
|
|
2767
3071
|
for (const item of raw) {
|
|
2768
|
-
if (!
|
|
3072
|
+
if (!isRecord3(item)) continue;
|
|
2769
3073
|
const s = item;
|
|
2770
3074
|
const appId = typeof s.app_id === "string" ? s.app_id : typeof s.id === "string" ? s.id : null;
|
|
2771
3075
|
if (!appId) continue;
|
|
@@ -2840,8 +3144,8 @@ var TopoloClient = class _TopoloClient {
|
|
|
2840
3144
|
"app_topolo_auth.service_api_key_resources.list",
|
|
2841
3145
|
{ appId: service.appId }
|
|
2842
3146
|
);
|
|
2843
|
-
const payload =
|
|
2844
|
-
if (!
|
|
3147
|
+
const payload = isRecord3(response) && isRecord3(response.data) ? response.data : response;
|
|
3148
|
+
if (!isRecord3(payload) || !Array.isArray(payload.resources)) {
|
|
2845
3149
|
throw new TopoloSdkError("resource_catalog_unavailable", "TopoloAuth did not return bindable resources.");
|
|
2846
3150
|
}
|
|
2847
3151
|
return {
|
|
@@ -2947,6 +3251,71 @@ var TopoloClient = class _TopoloClient {
|
|
|
2947
3251
|
const entry = await this.getAction(action);
|
|
2948
3252
|
return this.executeResolvedAction(entry, input3, options);
|
|
2949
3253
|
}
|
|
3254
|
+
async uploadAction(action, content, input3, options) {
|
|
3255
|
+
const entry = await this.getAction(action);
|
|
3256
|
+
const contract = parseTopoloActionUploadContract(entry.agentContract?.upload);
|
|
3257
|
+
if (contract.profile === "url-session") {
|
|
3258
|
+
const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
|
|
3259
|
+
return this.executeUrlSessionUpload(entry, contract, bytes, input3, options);
|
|
3260
|
+
}
|
|
3261
|
+
const uploadInput = prepareTopoloActionUploadInput(entry.agentContract?.upload, content, input3, options);
|
|
3262
|
+
return this.executeResolvedAction(entry, uploadInput, options);
|
|
3263
|
+
}
|
|
3264
|
+
async uploadActionBase64(action, contentBase64, input3, options) {
|
|
3265
|
+
const entry = await this.getAction(action);
|
|
3266
|
+
const contract = parseTopoloActionUploadContract(entry.agentContract?.upload);
|
|
3267
|
+
if (contract.profile === "url-session") {
|
|
3268
|
+
return this.executeUrlSessionUpload(entry, contract, decodeTopoloActionBase64(contentBase64), input3, options);
|
|
3269
|
+
}
|
|
3270
|
+
const uploadInput = prepareTopoloActionBase64UploadInput(entry.agentContract?.upload, contentBase64, input3, options);
|
|
3271
|
+
return this.executeResolvedAction(entry, uploadInput, options);
|
|
3272
|
+
}
|
|
3273
|
+
async executeUrlSessionUpload(entry, contract, bytes, input3, options) {
|
|
3274
|
+
const prepared = prepareTopoloUrlSessionUploadInput(contract, bytes.byteLength, input3, options);
|
|
3275
|
+
const initiation = await this.executeResolvedAction(entry, prepared.input, options);
|
|
3276
|
+
const uploadUrl = topoloUploadResponseValue(initiation, contract.uploadUrlPath);
|
|
3277
|
+
if (typeof uploadUrl !== "string" || !uploadUrl.trim()) {
|
|
3278
|
+
throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.uploadUrlPath}.`);
|
|
3279
|
+
}
|
|
3280
|
+
await this.putUrlSessionContent(entry, uploadUrl, contract.uploadMethod, bytes, options);
|
|
3281
|
+
if (!contract.complete) return initiation;
|
|
3282
|
+
const completionValue = topoloUploadResponseValue(initiation, contract.complete.responsePath);
|
|
3283
|
+
if (typeof completionValue !== "string" || !completionValue.trim()) {
|
|
3284
|
+
throw new TopoloSdkError("invalid_upload_response", `Upload action response is missing ${contract.complete.responsePath}.`);
|
|
3285
|
+
}
|
|
3286
|
+
const completion = await this.getAction(contract.complete.actionId);
|
|
3287
|
+
return this.executeResolvedAction(completion, {
|
|
3288
|
+
...options.completionInput ?? {},
|
|
3289
|
+
[contract.complete.inputField]: completionValue
|
|
3290
|
+
}, options);
|
|
3291
|
+
}
|
|
3292
|
+
async putUrlSessionContent(entry, uploadUrl, method, bytes, options) {
|
|
3293
|
+
const service = entry.appSlug || entry.appId;
|
|
3294
|
+
const resolved = entry.apiBaseUrl ? { serviceKey: normalizeLookupKey(service), baseUrl: requireHttpOrigin(entry.apiBaseUrl) } : await this.resolveRequestService(service);
|
|
3295
|
+
const target = new URL(uploadUrl, resolved.baseUrl.endsWith("/") ? resolved.baseUrl : `${resolved.baseUrl}/`);
|
|
3296
|
+
const body = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
3297
|
+
if (target.origin === new URL(resolved.baseUrl).origin) {
|
|
3298
|
+
await this.request({
|
|
3299
|
+
service,
|
|
3300
|
+
baseUrl: resolved.baseUrl,
|
|
3301
|
+
path: target.toString(),
|
|
3302
|
+
method,
|
|
3303
|
+
rawBody: body,
|
|
3304
|
+
headers: { "Content-Type": options.contentType.trim().toLowerCase() },
|
|
3305
|
+
resource: options.resource,
|
|
3306
|
+
confirm: options.confirm
|
|
3307
|
+
});
|
|
3308
|
+
return;
|
|
3309
|
+
}
|
|
3310
|
+
const response = await this.fetchImpl(target, {
|
|
3311
|
+
method,
|
|
3312
|
+
headers: { "Content-Type": options.contentType.trim().toLowerCase() },
|
|
3313
|
+
body
|
|
3314
|
+
});
|
|
3315
|
+
if (!response.ok) {
|
|
3316
|
+
throw new TopoloHttpError(service, target.pathname, response.status, await response.text(), `Upload transport failed with HTTP ${response.status}.`);
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
2950
3319
|
async validateActionInput(action, input3 = {}) {
|
|
2951
3320
|
const entry = await this.getAction(action);
|
|
2952
3321
|
return { action: entry, validation: validateTopoloActionInput(entry.inputSchema, input3) };
|
|
@@ -3105,7 +3474,7 @@ function resolveConfiguredAppId(slug, appIds) {
|
|
|
3105
3474
|
return null;
|
|
3106
3475
|
}
|
|
3107
3476
|
function normalizeApiKeyResourceMetadata(input3) {
|
|
3108
|
-
if (!
|
|
3477
|
+
if (!isRecord3(input3)) return null;
|
|
3109
3478
|
const resourceType = readString(input3.resourceType ?? input3.resource_type);
|
|
3110
3479
|
const label = readString(input3.label);
|
|
3111
3480
|
const emptyLabel = readString(input3.emptyLabel ?? input3.empty_label);
|
|
@@ -3139,7 +3508,7 @@ function normalizeApiKeyResourceMetadataList(input3) {
|
|
|
3139
3508
|
return resources;
|
|
3140
3509
|
}
|
|
3141
3510
|
function normalizeBindableResource(input3) {
|
|
3142
|
-
if (!
|
|
3511
|
+
if (!isRecord3(input3)) return null;
|
|
3143
3512
|
const resourceType = readString(input3.resourceType ?? input3.resource_type);
|
|
3144
3513
|
const resourceId = readString(input3.resourceId ?? input3.resource_id);
|
|
3145
3514
|
if (!resourceType || !resourceId) return null;
|
|
@@ -3150,13 +3519,13 @@ function normalizeBindableResource(input3) {
|
|
|
3150
3519
|
slug: readOptionalString(input3.slug),
|
|
3151
3520
|
status: readOptionalString(input3.status),
|
|
3152
3521
|
organizationId: readOptionalString(input3.organizationId ?? input3.organization_id),
|
|
3153
|
-
metadata:
|
|
3522
|
+
metadata: isRecord3(input3.metadata) ? input3.metadata : {}
|
|
3154
3523
|
};
|
|
3155
3524
|
}
|
|
3156
3525
|
function normalizeResourceBindings(input3) {
|
|
3157
3526
|
if (!Array.isArray(input3)) return [];
|
|
3158
3527
|
return input3.flatMap((binding) => {
|
|
3159
|
-
if (!
|
|
3528
|
+
if (!isRecord3(binding)) return [];
|
|
3160
3529
|
const resourceType = readString(binding.resourceType ?? binding.resource_type);
|
|
3161
3530
|
const resourceId = readString(binding.resourceId ?? binding.resource_id);
|
|
3162
3531
|
return resourceType && resourceId ? [{ resourceType, resourceId }] : [];
|
|
@@ -3169,7 +3538,7 @@ function readOptionalString(value) {
|
|
|
3169
3538
|
const normalized = readString(value);
|
|
3170
3539
|
return normalized || null;
|
|
3171
3540
|
}
|
|
3172
|
-
function
|
|
3541
|
+
function isRecord3(value) {
|
|
3173
3542
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3174
3543
|
}
|
|
3175
3544
|
function resolveConfiguredServiceUrl(slug, serviceUrls) {
|
|
@@ -3241,14 +3610,14 @@ function catalogSearchQuery(options) {
|
|
|
3241
3610
|
return query;
|
|
3242
3611
|
}
|
|
3243
3612
|
function normalizeCatalogPage(value) {
|
|
3244
|
-
if (!
|
|
3613
|
+
if (!isRecord3(value)) return { nextCursor: null, hasMore: false };
|
|
3245
3614
|
return {
|
|
3246
3615
|
nextCursor: typeof value.nextCursor === "string" && value.nextCursor ? value.nextCursor : null,
|
|
3247
3616
|
hasMore: value.hasMore === true
|
|
3248
3617
|
};
|
|
3249
3618
|
}
|
|
3250
3619
|
function normalizeServiceCatalogEntry(item) {
|
|
3251
|
-
if (!
|
|
3620
|
+
if (!isRecord3(item)) return null;
|
|
3252
3621
|
const appId = typeof item.app_id === "string" ? item.app_id : typeof item.id === "string" ? item.id : null;
|
|
3253
3622
|
if (!appId) return null;
|
|
3254
3623
|
const permissions = Array.isArray(item.granted_permissions) ? item.granted_permissions.filter(
|
|
@@ -3281,8 +3650,8 @@ function normalizeActionCatalogEntry(item) {
|
|
|
3281
3650
|
const name = pickActionString(a.name);
|
|
3282
3651
|
const appId = pickActionString(a.app_id) ?? pickActionString(a.appId);
|
|
3283
3652
|
const method = pickActionString(a.method)?.toUpperCase();
|
|
3284
|
-
const
|
|
3285
|
-
if (!actionId || !name || !appId || !method || !
|
|
3653
|
+
const path9 = pickActionString(a.path);
|
|
3654
|
+
if (!actionId || !name || !appId || !method || !path9 || !isHttpMethod(method)) {
|
|
3286
3655
|
return null;
|
|
3287
3656
|
}
|
|
3288
3657
|
return {
|
|
@@ -3297,7 +3666,7 @@ function normalizeActionCatalogEntry(item) {
|
|
|
3297
3666
|
appSlug: pickActionString(a.app_slug) ?? pickActionString(a.appSlug) ?? null,
|
|
3298
3667
|
appName: pickActionString(a.app_name) ?? pickActionString(a.appName) ?? null,
|
|
3299
3668
|
method,
|
|
3300
|
-
path:
|
|
3669
|
+
path: path9,
|
|
3301
3670
|
permissionName: pickActionString(a.permission_name) ?? pickActionString(a.permissionName) ?? "",
|
|
3302
3671
|
requiredPermission: pickActionString(a.required_permission) ?? pickActionString(a.requiredPermission) ?? "",
|
|
3303
3672
|
authorizationMode: pickAuthorizationMode(a.authorization_mode ?? a.authorizationMode),
|
|
@@ -3333,7 +3702,7 @@ function stringArray(value) {
|
|
|
3333
3702
|
}
|
|
3334
3703
|
function mapActionInput(action, input3) {
|
|
3335
3704
|
const remaining = { ...input3 || {} };
|
|
3336
|
-
const
|
|
3705
|
+
const path9 = action.path.replace(/\{([a-zA-Z0-9_]+)\}/g, (_match, key) => {
|
|
3337
3706
|
const value = remaining[key];
|
|
3338
3707
|
if (value === void 0 || value === null || value === "") {
|
|
3339
3708
|
throw new TopoloSdkError(
|
|
@@ -3350,14 +3719,14 @@ function mapActionInput(action, input3) {
|
|
|
3350
3719
|
delete remaining.body;
|
|
3351
3720
|
if (action.method === "GET") {
|
|
3352
3721
|
return {
|
|
3353
|
-
path:
|
|
3722
|
+
path: path9,
|
|
3354
3723
|
query: objectToQuery({ ...remaining, ...explicitQuery })
|
|
3355
3724
|
};
|
|
3356
3725
|
}
|
|
3357
3726
|
const query = objectToQuery(explicitQuery);
|
|
3358
3727
|
const body = explicitBody !== void 0 ? explicitBody : remaining;
|
|
3359
3728
|
return {
|
|
3360
|
-
path:
|
|
3729
|
+
path: path9,
|
|
3361
3730
|
...query ? { query } : {},
|
|
3362
3731
|
body
|
|
3363
3732
|
};
|
|
@@ -3527,8 +3896,8 @@ var TopoloOAuth = class {
|
|
|
3527
3896
|
if (params.clientSecret) body.set("client_secret", params.clientSecret);
|
|
3528
3897
|
return this.#postForm("/api/developer-oauth/token", body);
|
|
3529
3898
|
}
|
|
3530
|
-
async #postForm(
|
|
3531
|
-
const url = new URL(
|
|
3899
|
+
async #postForm(path9, body) {
|
|
3900
|
+
const url = new URL(path9, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
|
|
3532
3901
|
const res = await this.fetchImpl(url.toString(), {
|
|
3533
3902
|
method: "POST",
|
|
3534
3903
|
headers: {
|
|
@@ -3544,7 +3913,7 @@ var TopoloOAuth = class {
|
|
|
3544
3913
|
const err = parsed;
|
|
3545
3914
|
throw new TopoloAuthError(err.error_description || err.error, err.error);
|
|
3546
3915
|
}
|
|
3547
|
-
throw new TopoloHttpError("auth",
|
|
3916
|
+
throw new TopoloHttpError("auth", path9, res.status, parsed);
|
|
3548
3917
|
}
|
|
3549
3918
|
return parsed;
|
|
3550
3919
|
}
|
|
@@ -3572,29 +3941,57 @@ var TopoloSdkError2 = class extends Error {
|
|
|
3572
3941
|
}
|
|
3573
3942
|
};
|
|
3574
3943
|
var TopoloAuthError2 = class extends TopoloSdkError2 {
|
|
3575
|
-
|
|
3944
|
+
status;
|
|
3945
|
+
body;
|
|
3946
|
+
service;
|
|
3947
|
+
path;
|
|
3948
|
+
requestId;
|
|
3949
|
+
retryAfter;
|
|
3950
|
+
constructor(message, code = "auth_error", options = {}) {
|
|
3576
3951
|
super(code, message);
|
|
3577
3952
|
this.name = "TopoloAuthError";
|
|
3953
|
+
this.status = options.status ?? 401;
|
|
3954
|
+
this.body = options.body;
|
|
3955
|
+
this.service = options.service;
|
|
3956
|
+
this.path = options.path;
|
|
3957
|
+
this.requestId = options.requestId;
|
|
3958
|
+
this.retryAfter = options.retryAfter;
|
|
3578
3959
|
}
|
|
3579
3960
|
};
|
|
3961
|
+
function extractHttpErrorCode2(body) {
|
|
3962
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return "http_error";
|
|
3963
|
+
const record4 = body;
|
|
3964
|
+
if (typeof record4.code === "string" && record4.code.trim()) return record4.code.trim();
|
|
3965
|
+
const nested = record4.error;
|
|
3966
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
3967
|
+
const code = nested.code;
|
|
3968
|
+
if (typeof code === "string" && code.trim()) return code.trim();
|
|
3969
|
+
}
|
|
3970
|
+
return "http_error";
|
|
3971
|
+
}
|
|
3580
3972
|
var TopoloHttpError2 = class extends TopoloSdkError2 {
|
|
3581
3973
|
status;
|
|
3582
3974
|
body;
|
|
3583
3975
|
service;
|
|
3584
3976
|
path;
|
|
3585
|
-
|
|
3586
|
-
|
|
3977
|
+
requestId;
|
|
3978
|
+
retryAfter;
|
|
3979
|
+
constructor(service, path9, status, body, message, options = {}) {
|
|
3980
|
+
super(extractHttpErrorCode2(body), message ?? `HTTP ${status} from ${service}${path9}`);
|
|
3587
3981
|
this.name = "TopoloHttpError";
|
|
3588
3982
|
this.status = status;
|
|
3589
3983
|
this.body = body;
|
|
3590
3984
|
this.service = service;
|
|
3591
|
-
this.path =
|
|
3985
|
+
this.path = path9;
|
|
3986
|
+
this.requestId = options.requestId;
|
|
3987
|
+
this.retryAfter = options.retryAfter;
|
|
3592
3988
|
}
|
|
3593
3989
|
};
|
|
3594
3990
|
var APP_API_URLS2 = {
|
|
3595
3991
|
agent: "https://agent.topolo.app",
|
|
3596
3992
|
auth: "https://auth.topolo.app",
|
|
3597
3993
|
backup: "https://backup.topolo.app",
|
|
3994
|
+
blog: "https://blog.topolo.app",
|
|
3598
3995
|
books: "https://books.topolo.app",
|
|
3599
3996
|
brand: "https://brand.topolo.app",
|
|
3600
3997
|
bugfix: "https://bugfix.topolo.app",
|
|
@@ -3607,6 +4004,7 @@ var APP_API_URLS2 = {
|
|
|
3607
4004
|
compose: "https://compose.topolo.app",
|
|
3608
4005
|
consent: "https://consent.topolo.app",
|
|
3609
4006
|
crm: "https://crm.topolo.app",
|
|
4007
|
+
design: "https://design.topolo.app",
|
|
3610
4008
|
developers: "https://developers.topolo.app",
|
|
3611
4009
|
director: "https://director.topolo.app",
|
|
3612
4010
|
feed: "https://feed.topolo.app",
|
|
@@ -3719,8 +4117,8 @@ var TopoloOAuth2 = class {
|
|
|
3719
4117
|
if (params.clientSecret) body.set("client_secret", params.clientSecret);
|
|
3720
4118
|
return this.#postForm("/api/developer-oauth/token", body);
|
|
3721
4119
|
}
|
|
3722
|
-
async #postForm(
|
|
3723
|
-
const url = new URL(
|
|
4120
|
+
async #postForm(path9, body) {
|
|
4121
|
+
const url = new URL(path9, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
|
|
3724
4122
|
const res = await this.fetchImpl(url.toString(), {
|
|
3725
4123
|
method: "POST",
|
|
3726
4124
|
headers: {
|
|
@@ -3736,7 +4134,7 @@ var TopoloOAuth2 = class {
|
|
|
3736
4134
|
const err = parsed;
|
|
3737
4135
|
throw new TopoloAuthError2(err.error_description || err.error, err.error);
|
|
3738
4136
|
}
|
|
3739
|
-
throw new TopoloHttpError2("auth",
|
|
4137
|
+
throw new TopoloHttpError2("auth", path9, res.status, parsed);
|
|
3740
4138
|
}
|
|
3741
4139
|
return parsed;
|
|
3742
4140
|
}
|
|
@@ -3766,22 +4164,22 @@ function configPath() {
|
|
|
3766
4164
|
return join(base, "topolo", "config.json");
|
|
3767
4165
|
}
|
|
3768
4166
|
async function readConfig() {
|
|
3769
|
-
const
|
|
3770
|
-
if (!existsSync(
|
|
4167
|
+
const path9 = configPath();
|
|
4168
|
+
if (!existsSync(path9)) return {};
|
|
3771
4169
|
try {
|
|
3772
|
-
const raw = await readFile(
|
|
4170
|
+
const raw = await readFile(path9, "utf8");
|
|
3773
4171
|
return JSON.parse(raw);
|
|
3774
4172
|
} catch {
|
|
3775
4173
|
return {};
|
|
3776
4174
|
}
|
|
3777
4175
|
}
|
|
3778
4176
|
async function writeConfig(config) {
|
|
3779
|
-
const
|
|
3780
|
-
await mkdir(dirname(
|
|
3781
|
-
await writeFile(
|
|
4177
|
+
const path9 = configPath();
|
|
4178
|
+
await mkdir(dirname(path9), { recursive: true });
|
|
4179
|
+
await writeFile(path9, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
3782
4180
|
if (platform() !== "win32") {
|
|
3783
4181
|
try {
|
|
3784
|
-
await chmod(
|
|
4182
|
+
await chmod(path9, 384);
|
|
3785
4183
|
} catch {
|
|
3786
4184
|
}
|
|
3787
4185
|
}
|
|
@@ -4065,6 +4463,10 @@ function fail(err, mode) {
|
|
|
4065
4463
|
process.stderr.write(`Error: ${payload.message}
|
|
4066
4464
|
`);
|
|
4067
4465
|
if (payload.code) process.stderr.write(`Code: ${payload.code}
|
|
4466
|
+
`);
|
|
4467
|
+
if (payload.status) process.stderr.write(`HTTP: ${payload.status}
|
|
4468
|
+
`);
|
|
4469
|
+
if (payload.requestId) process.stderr.write(`Request: ${payload.requestId}
|
|
4068
4470
|
`);
|
|
4069
4471
|
if (payload.hint) process.stderr.write(`Hint: ${payload.hint}
|
|
4070
4472
|
`);
|
|
@@ -4083,6 +4485,12 @@ function normalizeError(err) {
|
|
|
4083
4485
|
return {
|
|
4084
4486
|
message: err.message,
|
|
4085
4487
|
code,
|
|
4488
|
+
status: err.status,
|
|
4489
|
+
service: err.service,
|
|
4490
|
+
path: err.path,
|
|
4491
|
+
requestId: err.requestId,
|
|
4492
|
+
retryAfter: err.retryAfter,
|
|
4493
|
+
details: err.body,
|
|
4086
4494
|
hint: code ? hintMap[code] : void 0,
|
|
4087
4495
|
exitCode: code === "auth_error" || code === "confirmation_required" ? 2 : code === "permission_denied" ? 3 : code === "immutable_drift" ? 4 : 1
|
|
4088
4496
|
};
|
|
@@ -4101,9 +4509,9 @@ function readCliVersion() {
|
|
|
4101
4509
|
join2(here, "package.json"),
|
|
4102
4510
|
join2(here, "..", "package.json")
|
|
4103
4511
|
];
|
|
4104
|
-
for (const
|
|
4512
|
+
for (const path9 of candidates) {
|
|
4105
4513
|
try {
|
|
4106
|
-
const pkg = JSON.parse(readFileSync(
|
|
4514
|
+
const pkg = JSON.parse(readFileSync(path9, "utf8"));
|
|
4107
4515
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
4108
4516
|
} catch {
|
|
4109
4517
|
}
|
|
@@ -4451,6 +4859,7 @@ function formatPretty(info) {
|
|
|
4451
4859
|
|
|
4452
4860
|
// src/commands/actions.ts
|
|
4453
4861
|
import { readFileSync as readFileSync2 } from "fs";
|
|
4862
|
+
import { basename, extname } from "path";
|
|
4454
4863
|
|
|
4455
4864
|
// src/commands/api.ts
|
|
4456
4865
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
@@ -4462,7 +4871,7 @@ function registerApiCommand(program2) {
|
|
|
4462
4871
|
"-q, --query <kv...>",
|
|
4463
4872
|
"Query params as key=value (repeatable), e.g. --query source=cli page=2"
|
|
4464
4873
|
).option("--resource-type <type>", "Cataloged app-local workspace resource alias for resource-scoped API keys").option("--resource-id <id>", "Workspace resource id for resource-scoped API keys").option("--confirm", "Acknowledge a mutating request").option("--json", "Output JSON", true).option("--no-json", "Output human-readable text").action(
|
|
4465
|
-
async (service, method,
|
|
4874
|
+
async (service, method, path9, opts) => {
|
|
4466
4875
|
const mode = resolveOutputMode(opts.json);
|
|
4467
4876
|
try {
|
|
4468
4877
|
const upperMethod = method.toUpperCase();
|
|
@@ -4501,7 +4910,7 @@ function registerApiCommand(program2) {
|
|
|
4501
4910
|
const result = await topolo.client.request({
|
|
4502
4911
|
service,
|
|
4503
4912
|
method: httpMethod,
|
|
4504
|
-
path:
|
|
4913
|
+
path: path9,
|
|
4505
4914
|
body,
|
|
4506
4915
|
...query ? { query } : {},
|
|
4507
4916
|
...resource ? { resource } : {},
|
|
@@ -4605,6 +5014,30 @@ function registerActionsCommand(program2) {
|
|
|
4605
5014
|
fail(err, mode);
|
|
4606
5015
|
}
|
|
4607
5016
|
});
|
|
5017
|
+
actions.command("upload <action>").description("Upload one local file through an action-declared upload contract").requiredOption("--file <path>", "Local file to upload").option("--content-type <type>", "MIME type; inferred for common file extensions").option("-d, --data <json-or-@file>", "Additional JSON action input or @path to a JSON file", "{}").option("--complete-data <json-or-@file>", "Additional input for a declared completion action", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Acknowledge a mutating upload action").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
5018
|
+
const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
|
|
5019
|
+
try {
|
|
5020
|
+
const topolo = await requiredClient();
|
|
5021
|
+
const contentType = opts.contentType?.trim().toLowerCase() || inferContentType(opts.file);
|
|
5022
|
+
if (!contentType) throw new Error("--content-type is required for this file extension");
|
|
5023
|
+
const resource = parseResourceOption(opts.resourceType, opts.resourceId);
|
|
5024
|
+
const result = await topolo.client.uploadAction(
|
|
5025
|
+
action,
|
|
5026
|
+
readFileSync2(opts.file),
|
|
5027
|
+
parseJsonObject(opts.data || "{}"),
|
|
5028
|
+
{
|
|
5029
|
+
fileName: basename(opts.file),
|
|
5030
|
+
contentType,
|
|
5031
|
+
completionInput: parseJsonObject(opts.completeData || "{}"),
|
|
5032
|
+
confirm: opts.confirm === true,
|
|
5033
|
+
...resource ? { resource } : {}
|
|
5034
|
+
}
|
|
5035
|
+
);
|
|
5036
|
+
emit(result, mode, () => JSON.stringify(result, null, 2));
|
|
5037
|
+
} catch (err) {
|
|
5038
|
+
fail(err, mode);
|
|
5039
|
+
}
|
|
5040
|
+
});
|
|
4608
5041
|
actions.command("validate <action>").description("Validate action input locally against the published JSON Schema").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
|
|
4609
5042
|
const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
|
|
4610
5043
|
try {
|
|
@@ -4665,13 +5098,13 @@ async function requiredClient() {
|
|
|
4665
5098
|
function parseJsonObject(value) {
|
|
4666
5099
|
let source = value;
|
|
4667
5100
|
if (value.startsWith("@")) {
|
|
4668
|
-
const
|
|
4669
|
-
if (!
|
|
5101
|
+
const path9 = value.slice(1);
|
|
5102
|
+
if (!path9) throw new Error("--data @file path must not be empty");
|
|
4670
5103
|
try {
|
|
4671
|
-
source = readFileSync2(
|
|
5104
|
+
source = readFileSync2(path9, "utf8");
|
|
4672
5105
|
} catch (err) {
|
|
4673
5106
|
const detail = err instanceof Error ? err.message : String(err);
|
|
4674
|
-
throw new Error(`Could not read --data file ${
|
|
5107
|
+
throw new Error(`Could not read --data file ${path9}: ${detail}`);
|
|
4675
5108
|
}
|
|
4676
5109
|
}
|
|
4677
5110
|
let parsed;
|
|
@@ -4685,6 +5118,21 @@ function parseJsonObject(value) {
|
|
|
4685
5118
|
}
|
|
4686
5119
|
return parsed;
|
|
4687
5120
|
}
|
|
5121
|
+
function inferContentType(path9) {
|
|
5122
|
+
return {
|
|
5123
|
+
".avif": "image/avif",
|
|
5124
|
+
".gif": "image/gif",
|
|
5125
|
+
".heic": "image/heic",
|
|
5126
|
+
".jpeg": "image/jpeg",
|
|
5127
|
+
".jpg": "image/jpeg",
|
|
5128
|
+
".mov": "video/quicktime",
|
|
5129
|
+
".mp4": "video/mp4",
|
|
5130
|
+
".pdf": "application/pdf",
|
|
5131
|
+
".png": "image/png",
|
|
5132
|
+
".svg": "image/svg+xml",
|
|
5133
|
+
".webp": "image/webp"
|
|
5134
|
+
}[extname(path9).toLowerCase()] ?? null;
|
|
5135
|
+
}
|
|
4688
5136
|
function formatActionRows(rows, nextCursor = null) {
|
|
4689
5137
|
if (rows.length === 0) return "No actions available for this credential.";
|
|
4690
5138
|
const serviceWidth = Math.max(7, ...rows.map((row) => (row.appSlug || row.appId).length));
|
|
@@ -4745,8 +5193,8 @@ function formatActionPlan(plan) {
|
|
|
4745
5193
|
|
|
4746
5194
|
// src/commands/apps.ts
|
|
4747
5195
|
import { execSync } from "child_process";
|
|
4748
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as
|
|
4749
|
-
import
|
|
5196
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
5197
|
+
import path6 from "path";
|
|
4750
5198
|
|
|
4751
5199
|
// src/commands/app-audit-local.ts
|
|
4752
5200
|
import {
|
|
@@ -5883,14 +6331,14 @@ import { constants } from "fs";
|
|
|
5883
6331
|
import { access } from "fs/promises";
|
|
5884
6332
|
import { relative, resolve } from "path";
|
|
5885
6333
|
function normalizeHomePath(value) {
|
|
5886
|
-
const
|
|
5887
|
-
if (!
|
|
6334
|
+
const path9 = String(value || "/dashboard").trim();
|
|
6335
|
+
if (!path9.startsWith("/") || path9.startsWith("//") || /[?#]/.test(path9)) {
|
|
5888
6336
|
throw new Error("Invalid home path. Use an absolute application path such as /dashboard.");
|
|
5889
6337
|
}
|
|
5890
|
-
if (["/", "/login", "/auth/callback", "/getting-started"].includes(
|
|
6338
|
+
if (["/", "/login", "/auth/callback", "/getting-started"].includes(path9)) {
|
|
5891
6339
|
throw new Error("Home path must be a protected product route, not a reserved entry route.");
|
|
5892
6340
|
}
|
|
5893
|
-
return
|
|
6341
|
+
return path9.replace(/\/$/, "") || "/dashboard";
|
|
5894
6342
|
}
|
|
5895
6343
|
function normalizeHomeLabel(value) {
|
|
5896
6344
|
const label = String(value || "Dashboard").trim();
|
|
@@ -6021,9 +6469,9 @@ function toTitle(value) {
|
|
|
6021
6469
|
function json(value) {
|
|
6022
6470
|
return JSON.stringify(value, null, 2);
|
|
6023
6471
|
}
|
|
6024
|
-
async function pathExists(
|
|
6472
|
+
async function pathExists(path9) {
|
|
6025
6473
|
try {
|
|
6026
|
-
await access(
|
|
6474
|
+
await access(path9, constants.F_OK);
|
|
6027
6475
|
return true;
|
|
6028
6476
|
} catch {
|
|
6029
6477
|
return false;
|
|
@@ -6053,6 +6501,54 @@ function firstPartyAppPath(appDir, _audience) {
|
|
|
6053
6501
|
return `apps/${appDir}`;
|
|
6054
6502
|
}
|
|
6055
6503
|
|
|
6504
|
+
// src/commands/app-scaffold-platform-versions.ts
|
|
6505
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
6506
|
+
import path2 from "path";
|
|
6507
|
+
var versionCache = /* @__PURE__ */ new Map();
|
|
6508
|
+
function platformPackagePin(packageName, options = {}) {
|
|
6509
|
+
const workspaceRoot = resolveTopoloRepoRoot(
|
|
6510
|
+
options.repoRoot ?? process.env.TOPOLO_WORKSPACE_ROOT ?? void 0,
|
|
6511
|
+
options.cwd ?? process.cwd()
|
|
6512
|
+
);
|
|
6513
|
+
if (!workspaceRoot) {
|
|
6514
|
+
throw new Error(
|
|
6515
|
+
`Cannot resolve a concrete version for ${packageName}: platform-target scaffolds must run inside the Topolo workspace checkout (a directory containing apps/ and system-apps/), or receive --repo-root. The fleet pinning policy requires exact ^<latest> pins, never "latest".`
|
|
6516
|
+
);
|
|
6517
|
+
}
|
|
6518
|
+
const version = platformPackageVersions(workspaceRoot).get(packageName);
|
|
6519
|
+
if (!version) {
|
|
6520
|
+
throw new Error(
|
|
6521
|
+
`Unknown Topolo platform package: ${packageName} \u2014 no published package with that name under ${path2.join(workspaceRoot, "topolo-platform", "packages")}.`
|
|
6522
|
+
);
|
|
6523
|
+
}
|
|
6524
|
+
return `^${version}`;
|
|
6525
|
+
}
|
|
6526
|
+
function platformPackageVersions(workspaceRoot) {
|
|
6527
|
+
const packagesRoot = path2.join(workspaceRoot, "topolo-platform", "packages");
|
|
6528
|
+
const cached = versionCache.get(packagesRoot);
|
|
6529
|
+
if (cached) return cached;
|
|
6530
|
+
const versions = /* @__PURE__ */ new Map();
|
|
6531
|
+
let entries = [];
|
|
6532
|
+
try {
|
|
6533
|
+
entries = readdirSync2(packagesRoot);
|
|
6534
|
+
} catch {
|
|
6535
|
+
}
|
|
6536
|
+
for (const entry of entries) {
|
|
6537
|
+
let packageJson2;
|
|
6538
|
+
try {
|
|
6539
|
+
packageJson2 = JSON.parse(readFileSync4(path2.join(packagesRoot, entry, "package.json"), "utf8"));
|
|
6540
|
+
} catch {
|
|
6541
|
+
continue;
|
|
6542
|
+
}
|
|
6543
|
+
const version = packageJson2.topoloPublishedVersion || packageJson2.version;
|
|
6544
|
+
if (packageJson2.name && /^@topolo(-io)?\//.test(packageJson2.name) && !packageJson2.private && version) {
|
|
6545
|
+
versions.set(packageJson2.name, version);
|
|
6546
|
+
}
|
|
6547
|
+
}
|
|
6548
|
+
versionCache.set(packagesRoot, versions);
|
|
6549
|
+
return versions;
|
|
6550
|
+
}
|
|
6551
|
+
|
|
6056
6552
|
// src/commands/app-scaffold-cloudflare-constants.ts
|
|
6057
6553
|
var CLOUDFLARE_ACCOUNT_ID = "49ef1ba682ad8cfd720c86699ae17521";
|
|
6058
6554
|
var STAGING_CLOUDFLARE_ACCOUNT_ID = "4f4e1c69a3830946f9fea7b1eb7531ac";
|
|
@@ -6085,7 +6581,7 @@ function packageJson(application, target, options) {
|
|
|
6085
6581
|
devDependencies.tailwindcss = "3.4.19";
|
|
6086
6582
|
dependencies["react-router-dom"] = "^7.6.2";
|
|
6087
6583
|
} else {
|
|
6088
|
-
dependencies["@topolo/login"] = topoloPackageVersion("@topolo/login", target);
|
|
6584
|
+
dependencies["@topolo/login"] = topoloPackageVersion("@topolo/login", target, options.repoRoot);
|
|
6089
6585
|
}
|
|
6090
6586
|
}
|
|
6091
6587
|
if (options.api) {
|
|
@@ -6097,13 +6593,13 @@ function packageJson(application, target, options) {
|
|
|
6097
6593
|
scripts["deploy:staging"] = `DEPLOY_ENVIRONMENT=staging CLOUDFLARE_ACCOUNT_ID=${STAGING_CLOUDFLARE_ACCOUNT_ID} python3 .github/scripts/standard_deploy.py local-deploy`;
|
|
6098
6594
|
scripts["deploy:production"] = `DEPLOY_ENVIRONMENT=production CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID} python3 .github/scripts/standard_deploy.py local-deploy`;
|
|
6099
6595
|
} else {
|
|
6100
|
-
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
|
|
6596
|
+
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target, options.repoRoot);
|
|
6101
6597
|
scripts.deploy = "npm run build && wrangler deploy";
|
|
6102
6598
|
}
|
|
6103
6599
|
devDependencies.wrangler = "^4.91.0";
|
|
6104
6600
|
}
|
|
6105
6601
|
for (const packageName of options.packages) {
|
|
6106
|
-
dependencies[packageName] = topoloPackageVersion(packageName, target);
|
|
6602
|
+
dependencies[packageName] = topoloPackageVersion(packageName, target, options.repoRoot);
|
|
6107
6603
|
}
|
|
6108
6604
|
if (target === "platform") {
|
|
6109
6605
|
const testDirs = [];
|
|
@@ -6145,14 +6641,16 @@ function packageJson(application, target, options) {
|
|
|
6145
6641
|
})}
|
|
6146
6642
|
`;
|
|
6147
6643
|
}
|
|
6148
|
-
function topoloPackageVersion(packageName, target) {
|
|
6644
|
+
function topoloPackageVersion(packageName, target, repoRoot) {
|
|
6149
6645
|
if (target === "developer") {
|
|
6150
6646
|
if (packageName === "@topolo/login") return "^0.1.0";
|
|
6151
6647
|
if (packageName === "@topolo-io/auth-client") return "^1.0.0";
|
|
6152
6648
|
if (packageName === "@topolo/sdk") return "^0.8.1";
|
|
6153
6649
|
return "^0.1.0";
|
|
6154
6650
|
}
|
|
6155
|
-
if (packageName.startsWith("@topolo/") || packageName.startsWith("@topolo-io/"))
|
|
6651
|
+
if (packageName.startsWith("@topolo/") || packageName.startsWith("@topolo-io/")) {
|
|
6652
|
+
return platformPackagePin(packageName, { repoRoot });
|
|
6653
|
+
}
|
|
6156
6654
|
throw new Error(`Unknown Topolo platform package: ${packageName}`);
|
|
6157
6655
|
}
|
|
6158
6656
|
function hasScaffoldResources(resources) {
|
|
@@ -8121,10 +8619,10 @@ function developerWorkerTs(application) {
|
|
|
8121
8619
|
}
|
|
8122
8620
|
|
|
8123
8621
|
// src/commands/app-scaffold-generate.ts
|
|
8124
|
-
import { existsSync as existsSync3, mkdirSync, mkdtempSync, readFileSync as
|
|
8622
|
+
import { existsSync as existsSync3, mkdirSync, mkdtempSync, readFileSync as readFileSync5, rmSync, writeFileSync } from "fs";
|
|
8125
8623
|
import { spawnSync } from "child_process";
|
|
8126
8624
|
import { tmpdir } from "os";
|
|
8127
|
-
import
|
|
8625
|
+
import path3 from "path";
|
|
8128
8626
|
function resolveGeneratorPath() {
|
|
8129
8627
|
const candidates = [
|
|
8130
8628
|
// Published CLI: vendored alongside dist/index.js at build time.
|
|
@@ -8151,18 +8649,18 @@ var GENERATED = {
|
|
|
8151
8649
|
};
|
|
8152
8650
|
function deriveGeneratedConfigs(files, base) {
|
|
8153
8651
|
const generator = resolveGeneratorPath();
|
|
8154
|
-
const root = mkdtempSync(
|
|
8652
|
+
const root = mkdtempSync(path3.join(tmpdir(), "topolo-scaffold-generate-"));
|
|
8155
8653
|
try {
|
|
8156
8654
|
const prefix = `${base}/`;
|
|
8157
8655
|
for (const file of files) {
|
|
8158
8656
|
if (!file.path.startsWith(prefix)) continue;
|
|
8159
|
-
const abs =
|
|
8160
|
-
mkdirSync(
|
|
8657
|
+
const abs = path3.join(root, file.path.slice(prefix.length));
|
|
8658
|
+
mkdirSync(path3.dirname(abs), { recursive: true });
|
|
8161
8659
|
writeFileSync(abs, file.content, "utf8");
|
|
8162
8660
|
}
|
|
8163
8661
|
const result = spawnSync(
|
|
8164
8662
|
process.execPath,
|
|
8165
|
-
[generator, `--manifest=${
|
|
8663
|
+
[generator, `--manifest=${path3.join(root, "topolo.app.json")}`],
|
|
8166
8664
|
{ cwd: root, encoding: "utf8" }
|
|
8167
8665
|
);
|
|
8168
8666
|
if (result.status !== 0) {
|
|
@@ -8179,7 +8677,7 @@ ${result.stdout ?? ""}`);
|
|
|
8179
8677
|
return written.map((rel) => ({
|
|
8180
8678
|
path: `${base}/${rel}`,
|
|
8181
8679
|
...GENERATED,
|
|
8182
|
-
content:
|
|
8680
|
+
content: readFileSync5(path3.join(root, rel), "utf8")
|
|
8183
8681
|
}));
|
|
8184
8682
|
} finally {
|
|
8185
8683
|
rmSync(root, { recursive: true, force: true });
|
|
@@ -9413,7 +9911,8 @@ function applicationScaffoldPayload(application, options = {}) {
|
|
|
9413
9911
|
target,
|
|
9414
9912
|
browser,
|
|
9415
9913
|
api,
|
|
9416
|
-
ciWorkflow
|
|
9914
|
+
ciWorkflow,
|
|
9915
|
+
repoRoot: options.repoRoot
|
|
9417
9916
|
}),
|
|
9418
9917
|
commands: scaffoldCommands({
|
|
9419
9918
|
id,
|
|
@@ -9501,7 +10000,8 @@ function scaffoldFiles(options) {
|
|
|
9501
10000
|
browser: options.browser,
|
|
9502
10001
|
api: options.api,
|
|
9503
10002
|
resources: options.payload.resources,
|
|
9504
|
-
packages: options.payload.capabilityComposition.packages
|
|
10003
|
+
packages: options.payload.capabilityComposition.packages,
|
|
10004
|
+
repoRoot: options.repoRoot
|
|
9505
10005
|
})
|
|
9506
10006
|
},
|
|
9507
10007
|
{
|
|
@@ -10312,18 +10812,18 @@ import { join as join3 } from "path";
|
|
|
10312
10812
|
|
|
10313
10813
|
// src/commands/app-reconcile.ts
|
|
10314
10814
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
10315
|
-
import
|
|
10815
|
+
import path5 from "path";
|
|
10316
10816
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
10317
10817
|
import { stdin as input2, stdout as output } from "process";
|
|
10318
10818
|
|
|
10319
10819
|
// src/commands/app-capability-composition.ts
|
|
10320
10820
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
10321
|
-
import
|
|
10821
|
+
import path4 from "path";
|
|
10322
10822
|
async function reconcileApplicationCapabilitySource(options) {
|
|
10323
|
-
const manifestPath =
|
|
10324
|
-
const root =
|
|
10325
|
-
const packagePath =
|
|
10326
|
-
const contractPath =
|
|
10823
|
+
const manifestPath = path4.resolve(options.manifestPath || "topolo.app.json");
|
|
10824
|
+
const root = path4.dirname(manifestPath);
|
|
10825
|
+
const packagePath = path4.join(root, "package.json");
|
|
10826
|
+
const contractPath = path4.join(root, "topolo.capability-contract.json");
|
|
10327
10827
|
const manifest = parseRecord(await readFile3(manifestPath, "utf8"), manifestPath);
|
|
10328
10828
|
const registration = record(manifest.registration);
|
|
10329
10829
|
if (!registration) throw new Error("topolo.app.json must declare registration before capability composition.");
|
|
@@ -10368,7 +10868,7 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10368
10868
|
const nextDependencies = { ...dependencies };
|
|
10369
10869
|
for (const packageName of capabilities.packages) {
|
|
10370
10870
|
if (typeof nextDependencies[packageName] !== "string") {
|
|
10371
|
-
nextDependencies[packageName] = packageVersion(packageName, target);
|
|
10871
|
+
nextDependencies[packageName] = packageVersion(packageName, target, { repoRoot: options.repoRoot, cwd: root });
|
|
10372
10872
|
}
|
|
10373
10873
|
}
|
|
10374
10874
|
const sortedDependencies = sortRecord(nextDependencies);
|
|
@@ -10378,9 +10878,9 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10378
10878
|
compareChange(changes, "topolo.capability-contract.json", existingContract, expectedContract);
|
|
10379
10879
|
const adapter = target === "platform" ? managedCapabilityAdapter(capabilities.selected) : null;
|
|
10380
10880
|
const apiDirectory = resolveApiDirectory(root, manifest, target);
|
|
10381
|
-
const adapterPath =
|
|
10881
|
+
const adapterPath = path4.join(apiDirectory, "src", "topolo-capabilities.ts");
|
|
10382
10882
|
if (adapter) compareChange(changes, relativePath(root, adapterPath), await readOptionalText(adapterPath), adapter);
|
|
10383
|
-
const pricingPath =
|
|
10883
|
+
const pricingPath = path4.join(root, "pricing-manifest.json");
|
|
10384
10884
|
const pricingManifest = capabilities.selected.includes("billing") ? defaultPricingManifest({
|
|
10385
10885
|
appId: String(manifest.appId || manifest.app_id || "").trim(),
|
|
10386
10886
|
displayName: String(registration.displayName || manifest.name || "Application"),
|
|
@@ -10392,7 +10892,7 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10392
10892
|
const missingResources = capabilities.resources.filter((resource) => !manifestProvidesResource(manifest, resource));
|
|
10393
10893
|
const verticalConfiguration = [];
|
|
10394
10894
|
for (const [capability2, fileName, description] of VERTICAL_CAPABILITY_FILES) {
|
|
10395
|
-
if (capabilities.selected.includes(capability2) && !await fileExists(
|
|
10895
|
+
if (capabilities.selected.includes(capability2) && !await fileExists(path4.join(root, fileName))) {
|
|
10396
10896
|
verticalConfiguration.push(`${fileName}: ${description}`);
|
|
10397
10897
|
}
|
|
10398
10898
|
}
|
|
@@ -10407,7 +10907,7 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10407
10907
|
await writeJson(contractPath, expectedContract);
|
|
10408
10908
|
if (pricingManifest) await writeJson(pricingPath, pricingManifest);
|
|
10409
10909
|
if (adapter) {
|
|
10410
|
-
await mkdir3(
|
|
10910
|
+
await mkdir3(path4.dirname(adapterPath), { recursive: true });
|
|
10411
10911
|
await writeFile4(adapterPath, adapter, "utf8");
|
|
10412
10912
|
}
|
|
10413
10913
|
}
|
|
@@ -10510,11 +11010,8 @@ function manifestProvidesResource(manifest, resource) {
|
|
|
10510
11010
|
}
|
|
10511
11011
|
return false;
|
|
10512
11012
|
}
|
|
10513
|
-
function packageVersion(packageName, target) {
|
|
10514
|
-
if (target === "platform")
|
|
10515
|
-
if (packageName === "@topolo-io/notifications") return "^0.6.8";
|
|
10516
|
-
return "latest";
|
|
10517
|
-
}
|
|
11013
|
+
function packageVersion(packageName, target, resolution) {
|
|
11014
|
+
if (target === "platform") return platformPackagePin(packageName, resolution);
|
|
10518
11015
|
if (packageName === "@topolo/login") return "^0.1.0";
|
|
10519
11016
|
if (packageName === "@topolo/sdk") return "^0.8.1";
|
|
10520
11017
|
throw new Error(`Capability registry exposed private package ${packageName} to a third-party application.`);
|
|
@@ -10591,13 +11088,13 @@ function resolveApiDirectory(root, manifest, target) {
|
|
|
10591
11088
|
for (const value of Object.values(record(manifest.targets) ?? {})) {
|
|
10592
11089
|
const targetRecord = record(value);
|
|
10593
11090
|
if (targetRecord?.kind === "worker" && !targetRecord.assets && typeof targetRecord.dir === "string") {
|
|
10594
|
-
return
|
|
11091
|
+
return path4.resolve(root, targetRecord.dir);
|
|
10595
11092
|
}
|
|
10596
11093
|
}
|
|
10597
|
-
return
|
|
11094
|
+
return path4.join(root, "apps", "api");
|
|
10598
11095
|
}
|
|
10599
11096
|
function relativePath(root, filePath) {
|
|
10600
|
-
return
|
|
11097
|
+
return path4.relative(root, filePath).split(path4.sep).join("/");
|
|
10601
11098
|
}
|
|
10602
11099
|
|
|
10603
11100
|
// src/commands/app-reconcile.ts
|
|
@@ -10667,7 +11164,7 @@ function titleFromSlug(value) {
|
|
|
10667
11164
|
return value.replace(/^topolo[-_]/, "").split(/[-_]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
10668
11165
|
}
|
|
10669
11166
|
async function completeApplicationRegistrationInteractively(options) {
|
|
10670
|
-
const manifestPath =
|
|
11167
|
+
const manifestPath = path5.resolve(options.manifestPath || "topolo.app.json");
|
|
10671
11168
|
const manifest = JSON.parse(await readFile4(manifestPath, "utf8"));
|
|
10672
11169
|
const existing = record2(manifest.registration) || {};
|
|
10673
11170
|
const appId = String(manifest.appId || manifest.app_id || "").trim();
|
|
@@ -10747,7 +11244,7 @@ async function completeApplicationRegistrationInteractively(options) {
|
|
|
10747
11244
|
}
|
|
10748
11245
|
}
|
|
10749
11246
|
async function loadApplicationReconcileRequest(options) {
|
|
10750
|
-
const manifestPath =
|
|
11247
|
+
const manifestPath = path5.resolve(options.manifestPath || "topolo.app.json");
|
|
10751
11248
|
const raw = await readFile4(manifestPath, "utf8");
|
|
10752
11249
|
const manifest = JSON.parse(raw);
|
|
10753
11250
|
assertRegistration(manifest.registration);
|
|
@@ -11151,7 +11648,7 @@ function applicationAuditPayload(application, options = {}) {
|
|
|
11151
11648
|
}
|
|
11152
11649
|
function recordApplicationEvidence(application, options = {}) {
|
|
11153
11650
|
const appId = resolveApplication(application).id;
|
|
11154
|
-
const root =
|
|
11651
|
+
const root = path6.resolve(options.appRoot ?? process.cwd());
|
|
11155
11652
|
if (!existsSync4(root)) {
|
|
11156
11653
|
throw new Error(`Application root does not exist: ${root}`);
|
|
11157
11654
|
}
|
|
@@ -11163,12 +11660,12 @@ function recordApplicationEvidence(application, options = {}) {
|
|
|
11163
11660
|
commit = void 0;
|
|
11164
11661
|
}
|
|
11165
11662
|
}
|
|
11166
|
-
const evidenceDir =
|
|
11167
|
-
const evidencePath =
|
|
11663
|
+
const evidenceDir = path6.join(root, ".topolo");
|
|
11664
|
+
const evidencePath = path6.join(evidenceDir, "evidence.json");
|
|
11168
11665
|
let before = null;
|
|
11169
11666
|
if (existsSync4(evidencePath)) {
|
|
11170
11667
|
try {
|
|
11171
|
-
const raw = JSON.parse(
|
|
11668
|
+
const raw = JSON.parse(readFileSync6(evidencePath, "utf8"));
|
|
11172
11669
|
if (raw && typeof raw === "object" && raw.version === 1) {
|
|
11173
11670
|
before = raw;
|
|
11174
11671
|
}
|
|
@@ -11463,14 +11960,14 @@ function statusesAtOrWorse(threshold) {
|
|
|
11463
11960
|
}
|
|
11464
11961
|
|
|
11465
11962
|
// src/commands/platform.ts
|
|
11466
|
-
import { readdirSync as
|
|
11467
|
-
import
|
|
11963
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
11964
|
+
import path8 from "path";
|
|
11468
11965
|
|
|
11469
11966
|
// src/commands/app-capability-provision.ts
|
|
11470
11967
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
11471
|
-
import
|
|
11968
|
+
import path7 from "path";
|
|
11472
11969
|
async function provisionApplicationResources(options) {
|
|
11473
|
-
const manifestPath =
|
|
11970
|
+
const manifestPath = path7.resolve(options.manifestPath || "topolo.app.json");
|
|
11474
11971
|
const manifest = parseRecord2(await readFile5(manifestPath, "utf8"), manifestPath);
|
|
11475
11972
|
const environments = requiredRecord(manifest.environments, "topolo.app.json#environments");
|
|
11476
11973
|
const environment = requiredRecord(environments[options.environment], `topolo.app.json#environments.${options.environment}`);
|
|
@@ -11675,7 +12172,8 @@ function registerPlatformCommand(program2) {
|
|
|
11675
12172
|
navigationMode: opts.navigationMode,
|
|
11676
12173
|
workspaceSwitcher: opts.workspaceSwitcher,
|
|
11677
12174
|
uiTemplate: opts.uiTemplate,
|
|
11678
|
-
tagline: opts.tagline
|
|
12175
|
+
tagline: opts.tagline,
|
|
12176
|
+
repoRoot: opts.repoRoot
|
|
11679
12177
|
});
|
|
11680
12178
|
if (opts.workflowOnly && !payload.ciWorkflow) {
|
|
11681
12179
|
throw new Error("--workflow-only is only available for first-party scaffolds.");
|
|
@@ -11742,29 +12240,29 @@ var SKIPPED_SCAN_DIRS = /* @__PURE__ */ new Set([
|
|
|
11742
12240
|
"output"
|
|
11743
12241
|
]);
|
|
11744
12242
|
function scanStagingProductionUrls(rootInput, allowPatterns) {
|
|
11745
|
-
const root =
|
|
12243
|
+
const root = path8.resolve(rootInput);
|
|
11746
12244
|
const allowlist = allowPatterns.map((pattern) => new RegExp(pattern));
|
|
11747
12245
|
const violations = [];
|
|
11748
12246
|
function shouldSkipFile(filePath) {
|
|
11749
|
-
const
|
|
11750
|
-
const relativePath2 =
|
|
11751
|
-
if (
|
|
11752
|
-
if (
|
|
11753
|
-
if (
|
|
11754
|
-
if (
|
|
11755
|
-
if (/^cloudcontrol-deployment-report-.+\.json$/.test(
|
|
11756
|
-
if (
|
|
11757
|
-
if (
|
|
11758
|
-
if (
|
|
11759
|
-
if (relativePath2 ===
|
|
11760
|
-
if (relativePath2.includes(`${
|
|
11761
|
-
if (relativePath2.startsWith(`scripts${
|
|
11762
|
-
return !RUNTIME_EXTENSIONS.has(
|
|
12247
|
+
const basename2 = path8.basename(filePath);
|
|
12248
|
+
const relativePath2 = path8.relative(root, filePath);
|
|
12249
|
+
if (basename2 === "package-lock.json" || basename2 === "pnpm-lock.yaml" || basename2 === "yarn.lock") return true;
|
|
12250
|
+
if (basename2 === "topolo.cloudcontrol.json") return true;
|
|
12251
|
+
if (basename2 === "wrangler.toml" || basename2 === "wrangler.jsonc") return true;
|
|
12252
|
+
if (basename2 === "consumer-graph.json") return true;
|
|
12253
|
+
if (/^cloudcontrol-deployment-report-.+\.json$/.test(basename2)) return true;
|
|
12254
|
+
if (basename2.endsWith(".generated.ts")) return true;
|
|
12255
|
+
if (basename2.endsWith(".test.ts") || basename2.endsWith(".test.tsx") || basename2.endsWith(".spec.ts")) return true;
|
|
12256
|
+
if (basename2 === ".env.production") return true;
|
|
12257
|
+
if (relativePath2 === path8.join("src", "environment.ts") || relativePath2 === path8.join("src", "environmentUrls.ts") || relativePath2.endsWith(path8.join("src", "environment.ts")) || relativePath2.endsWith(path8.join("src", "lib", "environment.ts")) || relativePath2 === path8.join("packages", "topolo-ui-kit", "src", "environmentUrls.ts")) return true;
|
|
12258
|
+
if (relativePath2.includes(`${path8.sep}test${path8.sep}`) || relativePath2.includes(`${path8.sep}tests${path8.sep}`) || relativePath2.includes(`${path8.sep}e2e${path8.sep}`)) return true;
|
|
12259
|
+
if (relativePath2.startsWith(`scripts${path8.sep}`) || relativePath2.includes(`${path8.sep}scripts${path8.sep}`)) return true;
|
|
12260
|
+
return !RUNTIME_EXTENSIONS.has(path8.extname(filePath));
|
|
11763
12261
|
}
|
|
11764
12262
|
function scanFile(filePath) {
|
|
11765
12263
|
if (shouldSkipFile(filePath)) return;
|
|
11766
|
-
const relativePath2 =
|
|
11767
|
-
const content =
|
|
12264
|
+
const relativePath2 = path8.relative(root, filePath);
|
|
12265
|
+
const content = readFileSync7(filePath, "utf8");
|
|
11768
12266
|
const lines = content.split(/\r?\n/);
|
|
11769
12267
|
lines.forEach((line, index) => {
|
|
11770
12268
|
const matches = [...line.matchAll(PRODUCTION_TOPOLO_URL_PATTERN)].map((match) => match[0]);
|
|
@@ -11777,8 +12275,8 @@ function scanStagingProductionUrls(rootInput, allowPatterns) {
|
|
|
11777
12275
|
});
|
|
11778
12276
|
}
|
|
11779
12277
|
function walk(dir) {
|
|
11780
|
-
for (const entry of
|
|
11781
|
-
const fullPath =
|
|
12278
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
12279
|
+
const fullPath = path8.join(dir, entry.name);
|
|
11782
12280
|
if (entry.isDirectory()) {
|
|
11783
12281
|
if (!SKIPPED_SCAN_DIRS.has(entry.name)) walk(fullPath);
|
|
11784
12282
|
continue;
|
|
@@ -11946,7 +12444,7 @@ var DEFAULT_SEED_BASE_URL = "https://seed.stg.topolo.us";
|
|
|
11946
12444
|
function seedBaseUrl() {
|
|
11947
12445
|
return process.env.TOPOLO_SEED_URL || DEFAULT_SEED_BASE_URL;
|
|
11948
12446
|
}
|
|
11949
|
-
async function authedFetch(
|
|
12447
|
+
async function authedFetch(path9, init = {}) {
|
|
11950
12448
|
const credential = await refreshCredentialIfNeeded("staging");
|
|
11951
12449
|
if (!credential) {
|
|
11952
12450
|
throw new Error("No staging credential configured. Run `topolo auth login --env staging` or set TOPOLO_STAGING_API_KEY.");
|
|
@@ -11956,7 +12454,7 @@ async function authedFetch(path8, init = {}) {
|
|
|
11956
12454
|
else headers.set("Authorization", `Bearer ${credential.accessToken}`);
|
|
11957
12455
|
headers.set("X-Topolo-Client", `topolo-cli/${CLI_VERSION}`);
|
|
11958
12456
|
if (init.body && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
|
|
11959
|
-
const response = await fetch(`${seedBaseUrl()}${
|
|
12457
|
+
const response = await fetch(`${seedBaseUrl()}${path9}`, { ...init, headers });
|
|
11960
12458
|
const text = await response.text();
|
|
11961
12459
|
let body;
|
|
11962
12460
|
try {
|
|
@@ -12233,7 +12731,7 @@ function createOAuthClientPayload(options) {
|
|
|
12233
12731
|
logo_url: options.logoUrl
|
|
12234
12732
|
};
|
|
12235
12733
|
}
|
|
12236
|
-
async function authFetchJson(
|
|
12734
|
+
async function authFetchJson(path9, init = {}) {
|
|
12237
12735
|
const environment = selectedEnvironment();
|
|
12238
12736
|
const credential = await refreshCredentialIfNeeded(environment);
|
|
12239
12737
|
if (!credential) {
|
|
@@ -12255,7 +12753,7 @@ async function authFetchJson(path8, init = {}) {
|
|
|
12255
12753
|
throw err;
|
|
12256
12754
|
}
|
|
12257
12755
|
headers.set("Authorization", `Bearer ${credential.accessToken}`);
|
|
12258
|
-
const response = await fetch(`${authBaseUrl}${
|
|
12756
|
+
const response = await fetch(`${authBaseUrl}${path9}`, { ...init, headers });
|
|
12259
12757
|
const text = await response.text();
|
|
12260
12758
|
const payload = text ? JSON.parse(text) : {};
|
|
12261
12759
|
if (!response.ok || payload.success === false) {
|
|
@@ -12510,11 +13008,11 @@ function registerViaOfficialCli(cli, mcp, agentName) {
|
|
|
12510
13008
|
return result.status === 0;
|
|
12511
13009
|
}
|
|
12512
13010
|
async function registerClaudeViaConfig(mcp) {
|
|
12513
|
-
const
|
|
13011
|
+
const path9 = join5(homedir2(), ".claude.json");
|
|
12514
13012
|
let config = {};
|
|
12515
|
-
if (existsSync5(
|
|
13013
|
+
if (existsSync5(path9)) {
|
|
12516
13014
|
try {
|
|
12517
|
-
config = JSON.parse(await readFile7(
|
|
13015
|
+
config = JSON.parse(await readFile7(path9, "utf8"));
|
|
12518
13016
|
} catch {
|
|
12519
13017
|
config = {};
|
|
12520
13018
|
}
|
|
@@ -12527,13 +13025,13 @@ async function registerClaudeViaConfig(mcp) {
|
|
|
12527
13025
|
env: { TOPOLO_AGENT_NAME: AGENT_NAME_FOR.claude }
|
|
12528
13026
|
};
|
|
12529
13027
|
config.mcpServers = servers;
|
|
12530
|
-
await writeFile7(
|
|
13028
|
+
await writeFile7(path9, `${JSON.stringify(config, null, 2)}
|
|
12531
13029
|
`, "utf8");
|
|
12532
13030
|
}
|
|
12533
13031
|
async function registerCodexViaConfig(mcp) {
|
|
12534
|
-
const
|
|
13032
|
+
const path9 = join5(homedir2(), ".codex", "config.toml");
|
|
12535
13033
|
let text = "";
|
|
12536
|
-
if (existsSync5(
|
|
13034
|
+
if (existsSync5(path9)) text = await readFile7(path9, "utf8");
|
|
12537
13035
|
text = text.replace(
|
|
12538
13036
|
/(^|\n)\[mcp_servers\.topolo(?:\.[a-z]+)?\][^\[]*/g,
|
|
12539
13037
|
(match) => match.startsWith("\n") ? "\n" : ""
|
|
@@ -12549,8 +13047,8 @@ command = "${mcp.command}"${argsToml}
|
|
|
12549
13047
|
[mcp_servers.topolo.env]
|
|
12550
13048
|
TOPOLO_AGENT_NAME = "${AGENT_NAME_FOR.codex}"
|
|
12551
13049
|
`;
|
|
12552
|
-
await mkdir4(dirname4(
|
|
12553
|
-
await writeFile7(
|
|
13050
|
+
await mkdir4(dirname4(path9), { recursive: true });
|
|
13051
|
+
await writeFile7(path9, `${text}${block}`, "utf8");
|
|
12554
13052
|
}
|
|
12555
13053
|
async function installForAgent(agent, sourceDir, mcp, opts) {
|
|
12556
13054
|
const skillDir = agent === "claude" ? claudeSkillDir(opts.projectDir) : codexSkillDir();
|