@topolo/cli 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generate-wrangler.mjs +9 -3
- package/dist/index.js +352 -181
- package/package.json +17 -17
package/dist/index.js
CHANGED
|
@@ -98,13 +98,13 @@ var TopoloHttpError = class extends TopoloSdkError {
|
|
|
98
98
|
path;
|
|
99
99
|
requestId;
|
|
100
100
|
retryAfter;
|
|
101
|
-
constructor(service,
|
|
102
|
-
super(extractHttpErrorCode(body), message ?? `HTTP ${status} from ${service}${
|
|
101
|
+
constructor(service, path9, status, body, message, options = {}) {
|
|
102
|
+
super(extractHttpErrorCode(body), message ?? `HTTP ${status} from ${service}${path9}`);
|
|
103
103
|
this.name = "TopoloHttpError";
|
|
104
104
|
this.status = status;
|
|
105
105
|
this.body = body;
|
|
106
106
|
this.service = service;
|
|
107
|
-
this.path =
|
|
107
|
+
this.path = path9;
|
|
108
108
|
this.requestId = options.requestId;
|
|
109
109
|
this.retryAfter = options.retryAfter;
|
|
110
110
|
}
|
|
@@ -113,6 +113,7 @@ var APP_API_URLS = {
|
|
|
113
113
|
agent: "https://agent.topolo.app",
|
|
114
114
|
auth: "https://auth.topolo.app",
|
|
115
115
|
backup: "https://backup.topolo.app",
|
|
116
|
+
blog: "https://blog.topolo.app",
|
|
116
117
|
books: "https://books.topolo.app",
|
|
117
118
|
brand: "https://brand.topolo.app",
|
|
118
119
|
bugfix: "https://bugfix.topolo.app",
|
|
@@ -125,6 +126,7 @@ var APP_API_URLS = {
|
|
|
125
126
|
compose: "https://compose.topolo.app",
|
|
126
127
|
consent: "https://consent.topolo.app",
|
|
127
128
|
crm: "https://crm.topolo.app",
|
|
129
|
+
design: "https://design.topolo.app",
|
|
128
130
|
developers: "https://developers.topolo.app",
|
|
129
131
|
director: "https://director.topolo.app",
|
|
130
132
|
feed: "https://feed.topolo.app",
|
|
@@ -208,7 +210,9 @@ var APPLICATIONS = {
|
|
|
208
210
|
"domains": [
|
|
209
211
|
"agent.topolo.app"
|
|
210
212
|
],
|
|
211
|
-
"routes": [
|
|
213
|
+
"routes": [
|
|
214
|
+
"agent.topolo.app"
|
|
215
|
+
],
|
|
212
216
|
"appId": "agent"
|
|
213
217
|
},
|
|
214
218
|
{
|
|
@@ -269,6 +273,38 @@ var APPLICATIONS = {
|
|
|
269
273
|
}
|
|
270
274
|
]
|
|
271
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
|
+
},
|
|
272
308
|
"books": {
|
|
273
309
|
"id": "books",
|
|
274
310
|
"appDir": "TopoloBooks",
|
|
@@ -286,7 +322,9 @@ var APPLICATIONS = {
|
|
|
286
322
|
"domains": [
|
|
287
323
|
"books.topolo.app"
|
|
288
324
|
],
|
|
289
|
-
"routes": [
|
|
325
|
+
"routes": [
|
|
326
|
+
"books.topolo.app"
|
|
327
|
+
],
|
|
290
328
|
"appId": "books"
|
|
291
329
|
},
|
|
292
330
|
{
|
|
@@ -436,7 +474,9 @@ var APPLICATIONS = {
|
|
|
436
474
|
"domains": [
|
|
437
475
|
"campaigns.topolo.app"
|
|
438
476
|
],
|
|
439
|
-
"routes": [
|
|
477
|
+
"routes": [
|
|
478
|
+
"campaigns.topolo.app"
|
|
479
|
+
],
|
|
440
480
|
"appId": "campaigns"
|
|
441
481
|
},
|
|
442
482
|
{
|
|
@@ -578,7 +618,9 @@ var APPLICATIONS = {
|
|
|
578
618
|
"domains": [
|
|
579
619
|
"compose.topolo.app"
|
|
580
620
|
],
|
|
581
|
-
"routes": [
|
|
621
|
+
"routes": [
|
|
622
|
+
"compose.topolo.app"
|
|
623
|
+
],
|
|
582
624
|
"appId": "compose"
|
|
583
625
|
},
|
|
584
626
|
{
|
|
@@ -608,7 +650,9 @@ var APPLICATIONS = {
|
|
|
608
650
|
"domains": [
|
|
609
651
|
"consent.topolo.app"
|
|
610
652
|
],
|
|
611
|
-
"routes": [
|
|
653
|
+
"routes": [
|
|
654
|
+
"consent.topolo.app"
|
|
655
|
+
],
|
|
612
656
|
"appId": "consent"
|
|
613
657
|
},
|
|
614
658
|
{
|
|
@@ -646,11 +690,45 @@ var APPLICATIONS = {
|
|
|
646
690
|
"domains": [
|
|
647
691
|
"crm.topolo.app"
|
|
648
692
|
],
|
|
649
|
-
"routes": [
|
|
693
|
+
"routes": [
|
|
694
|
+
"crm.topolo.app"
|
|
695
|
+
],
|
|
650
696
|
"appId": "crm"
|
|
651
697
|
}
|
|
652
698
|
]
|
|
653
699
|
},
|
|
700
|
+
"design": {
|
|
701
|
+
"id": "design",
|
|
702
|
+
"appDir": "TopoloDesign",
|
|
703
|
+
"name": "topolo-design",
|
|
704
|
+
"packageName": "@topolo-io/design",
|
|
705
|
+
"productionUrl": "https://design.topolo.app",
|
|
706
|
+
"services": [
|
|
707
|
+
"design"
|
|
708
|
+
],
|
|
709
|
+
"deployTargets": [
|
|
710
|
+
{
|
|
711
|
+
"name": "topolo-design",
|
|
712
|
+
"kind": "worker",
|
|
713
|
+
"apiUrl": "https://design.topolo.app",
|
|
714
|
+
"domains": [
|
|
715
|
+
"design.topolo.app"
|
|
716
|
+
],
|
|
717
|
+
"routes": [
|
|
718
|
+
"design.topolo.app"
|
|
719
|
+
],
|
|
720
|
+
"appId": "design"
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
"name": "topolo-design-api",
|
|
724
|
+
"kind": "worker",
|
|
725
|
+
"apiUrl": null,
|
|
726
|
+
"domains": [],
|
|
727
|
+
"routes": [],
|
|
728
|
+
"appId": null
|
|
729
|
+
}
|
|
730
|
+
]
|
|
731
|
+
},
|
|
654
732
|
"developers": {
|
|
655
733
|
"id": "developers",
|
|
656
734
|
"appDir": "TopoloDevelopers",
|
|
@@ -706,7 +784,9 @@ var APPLICATIONS = {
|
|
|
706
784
|
"domains": [
|
|
707
785
|
"director.topolo.app"
|
|
708
786
|
],
|
|
709
|
-
"routes": [
|
|
787
|
+
"routes": [
|
|
788
|
+
"director.topolo.app"
|
|
789
|
+
],
|
|
710
790
|
"appId": "director"
|
|
711
791
|
},
|
|
712
792
|
{
|
|
@@ -814,7 +894,9 @@ var APPLICATIONS = {
|
|
|
814
894
|
"domains": [
|
|
815
895
|
"flow.topolo.app"
|
|
816
896
|
],
|
|
817
|
-
"routes": [
|
|
897
|
+
"routes": [
|
|
898
|
+
"flow.topolo.app"
|
|
899
|
+
],
|
|
818
900
|
"appId": "flow"
|
|
819
901
|
},
|
|
820
902
|
{
|
|
@@ -844,7 +926,9 @@ var APPLICATIONS = {
|
|
|
844
926
|
"domains": [
|
|
845
927
|
"forecast.topolo.app"
|
|
846
928
|
],
|
|
847
|
-
"routes": [
|
|
929
|
+
"routes": [
|
|
930
|
+
"forecast.topolo.app"
|
|
931
|
+
],
|
|
848
932
|
"appId": "forecast"
|
|
849
933
|
},
|
|
850
934
|
{
|
|
@@ -938,7 +1022,9 @@ var APPLICATIONS = {
|
|
|
938
1022
|
"domains": [
|
|
939
1023
|
"insights.topolo.app"
|
|
940
1024
|
],
|
|
941
|
-
"routes": [
|
|
1025
|
+
"routes": [
|
|
1026
|
+
"insights.topolo.app"
|
|
1027
|
+
],
|
|
942
1028
|
"appId": "insights"
|
|
943
1029
|
},
|
|
944
1030
|
{
|
|
@@ -968,7 +1054,9 @@ var APPLICATIONS = {
|
|
|
968
1054
|
"domains": [
|
|
969
1055
|
"inventory.topolo.app"
|
|
970
1056
|
],
|
|
971
|
-
"routes": [
|
|
1057
|
+
"routes": [
|
|
1058
|
+
"inventory.topolo.app"
|
|
1059
|
+
],
|
|
972
1060
|
"appId": "inventory"
|
|
973
1061
|
},
|
|
974
1062
|
{
|
|
@@ -1322,7 +1410,9 @@ var APPLICATIONS = {
|
|
|
1322
1410
|
"domains": [
|
|
1323
1411
|
"pay.topolo.app"
|
|
1324
1412
|
],
|
|
1325
|
-
"routes": [
|
|
1413
|
+
"routes": [
|
|
1414
|
+
"pay.topolo.app"
|
|
1415
|
+
],
|
|
1326
1416
|
"appId": "pay"
|
|
1327
1417
|
},
|
|
1328
1418
|
{
|
|
@@ -2277,17 +2367,17 @@ function validateTopoloActionInput(schema, input3) {
|
|
|
2277
2367
|
validateNode(schema, input3, "$", schema, errors, 0);
|
|
2278
2368
|
return { valid: errors.length === 0, errors };
|
|
2279
2369
|
}
|
|
2280
|
-
function inspectSchema(schema,
|
|
2281
|
-
if (depth > 128) return { path:
|
|
2370
|
+
function inspectSchema(schema, path9, root, depth) {
|
|
2371
|
+
if (depth > 128) return { path: path9, code: "schema_depth", message: "Schema recursion exceeded 128 levels." };
|
|
2282
2372
|
const unsupportedKeyword = Object.keys(schema).find((keyword) => !SUPPORTED_SCHEMA_KEYWORDS.has(keyword));
|
|
2283
2373
|
if (unsupportedKeyword) {
|
|
2284
|
-
return { path:
|
|
2374
|
+
return { path: path9, code: "unsupported_schema_keyword", message: `Action schema uses unsupported keyword ${unsupportedKeyword}.` };
|
|
2285
2375
|
}
|
|
2286
2376
|
if (typeof schema.format === "string" && !["uri", "email", "date-time"].includes(schema.format)) {
|
|
2287
|
-
return { path:
|
|
2377
|
+
return { path: path9, code: "unsupported_schema_format", message: `Action schema uses unsupported format ${schema.format}.` };
|
|
2288
2378
|
}
|
|
2289
2379
|
if (typeof schema.$ref === "string" && !resolveLocalReference(root, schema.$ref)) {
|
|
2290
|
-
return { path:
|
|
2380
|
+
return { path: path9, code: "unsupported_schema_ref", message: `Cannot resolve schema reference ${schema.$ref}.` };
|
|
2291
2381
|
}
|
|
2292
2382
|
const children = [];
|
|
2293
2383
|
if (isRecord(schema.properties)) children.push(...Object.entries(schema.properties));
|
|
@@ -2303,7 +2393,7 @@ function inspectSchema(schema, path8, root, depth) {
|
|
|
2303
2393
|
);
|
|
2304
2394
|
for (const [key, child] of children) {
|
|
2305
2395
|
if (!isRecord(child)) continue;
|
|
2306
|
-
const issue = inspectSchema(child, `${
|
|
2396
|
+
const issue = inspectSchema(child, `${path9}.${key}`, root, depth + 1);
|
|
2307
2397
|
if (issue) return issue;
|
|
2308
2398
|
}
|
|
2309
2399
|
return null;
|
|
@@ -2311,129 +2401,129 @@ function inspectSchema(schema, path8, root, depth) {
|
|
|
2311
2401
|
function arraySchemaEntries(keyword, value) {
|
|
2312
2402
|
return Array.isArray(value) ? value.map((candidate, index) => [`${keyword}[${index}]`, candidate]) : [];
|
|
2313
2403
|
}
|
|
2314
|
-
function validateNode(schema, value,
|
|
2404
|
+
function validateNode(schema, value, path9, root, errors, depth) {
|
|
2315
2405
|
if (depth > 128) {
|
|
2316
|
-
errors.push({ path:
|
|
2406
|
+
errors.push({ path: path9, code: "schema_depth", message: "Schema recursion exceeded 128 levels." });
|
|
2317
2407
|
return;
|
|
2318
2408
|
}
|
|
2319
2409
|
if (typeof schema.$ref === "string") {
|
|
2320
2410
|
const resolved = resolveLocalReference(root, schema.$ref);
|
|
2321
2411
|
if (!resolved) {
|
|
2322
|
-
errors.push({ path:
|
|
2412
|
+
errors.push({ path: path9, code: "unsupported_schema_ref", message: `Cannot resolve schema reference ${schema.$ref}.` });
|
|
2323
2413
|
return;
|
|
2324
2414
|
}
|
|
2325
|
-
validateNode(resolved, value,
|
|
2415
|
+
validateNode(resolved, value, path9, root, errors, depth + 1);
|
|
2326
2416
|
}
|
|
2327
2417
|
if (Array.isArray(schema.allOf)) {
|
|
2328
2418
|
for (const candidate of schema.allOf) {
|
|
2329
|
-
if (isRecord(candidate)) validateNode(candidate, value,
|
|
2419
|
+
if (isRecord(candidate)) validateNode(candidate, value, path9, root, errors, depth + 1);
|
|
2330
2420
|
}
|
|
2331
2421
|
}
|
|
2332
2422
|
if (Array.isArray(schema.anyOf) && !schema.anyOf.some((candidate) => candidateMatches(candidate, value, root, depth))) {
|
|
2333
|
-
errors.push({ path:
|
|
2423
|
+
errors.push({ path: path9, code: "any_of", message: "Value does not match any allowed schema." });
|
|
2334
2424
|
return;
|
|
2335
2425
|
}
|
|
2336
2426
|
if (Array.isArray(schema.oneOf)) {
|
|
2337
2427
|
const matches = schema.oneOf.filter((candidate) => candidateMatches(candidate, value, root, depth)).length;
|
|
2338
2428
|
if (matches !== 1) {
|
|
2339
|
-
errors.push({ path:
|
|
2429
|
+
errors.push({ path: path9, code: "one_of", message: "Value must match exactly one allowed schema." });
|
|
2340
2430
|
return;
|
|
2341
2431
|
}
|
|
2342
2432
|
}
|
|
2343
2433
|
if (value === null && schema.nullable === true) return;
|
|
2344
2434
|
if (Object.prototype.hasOwnProperty.call(schema, "const") && !sameJsonValue(value, schema.const)) {
|
|
2345
|
-
errors.push({ path:
|
|
2435
|
+
errors.push({ path: path9, code: "const", message: `Value must equal ${JSON.stringify(schema.const)}.` });
|
|
2346
2436
|
return;
|
|
2347
2437
|
}
|
|
2348
2438
|
if (Array.isArray(schema.enum) && !schema.enum.some((candidate) => sameJsonValue(value, candidate))) {
|
|
2349
|
-
errors.push({ path:
|
|
2439
|
+
errors.push({ path: path9, code: "enum", message: `Value must be one of ${schema.enum.map((item) => JSON.stringify(item)).join(", ")}.` });
|
|
2350
2440
|
return;
|
|
2351
2441
|
}
|
|
2352
2442
|
if (!matchesType(schema.type, value)) {
|
|
2353
|
-
errors.push({ path:
|
|
2443
|
+
errors.push({ path: path9, code: "type", message: `Expected ${String(schema.type)}.` });
|
|
2354
2444
|
return;
|
|
2355
2445
|
}
|
|
2356
|
-
if (typeof value === "string") validateString(schema, value,
|
|
2357
|
-
if (typeof value === "number") validateNumber(schema, value,
|
|
2358
|
-
if (Array.isArray(value)) validateArray(schema, value,
|
|
2359
|
-
if (isRecord(value)) validateObject(schema, value,
|
|
2446
|
+
if (typeof value === "string") validateString(schema, value, path9, errors);
|
|
2447
|
+
if (typeof value === "number") validateNumber(schema, value, path9, errors);
|
|
2448
|
+
if (Array.isArray(value)) validateArray(schema, value, path9, root, errors, depth);
|
|
2449
|
+
if (isRecord(value)) validateObject(schema, value, path9, root, errors, depth);
|
|
2360
2450
|
}
|
|
2361
|
-
function validateObject(schema, value,
|
|
2451
|
+
function validateObject(schema, value, path9, root, errors, depth) {
|
|
2362
2452
|
if (typeof schema.minProperties === "number" && Object.keys(value).length < schema.minProperties) {
|
|
2363
|
-
errors.push({ path:
|
|
2453
|
+
errors.push({ path: path9, code: "min_properties", message: `Expected at least ${schema.minProperties} properties.` });
|
|
2364
2454
|
}
|
|
2365
2455
|
if (typeof schema.maxProperties === "number" && Object.keys(value).length > schema.maxProperties) {
|
|
2366
|
-
errors.push({ path:
|
|
2456
|
+
errors.push({ path: path9, code: "max_properties", message: `Expected at most ${schema.maxProperties} properties.` });
|
|
2367
2457
|
}
|
|
2368
2458
|
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
2369
2459
|
const required = Array.isArray(schema.required) ? schema.required.map(String) : [];
|
|
2370
2460
|
for (const key of required) {
|
|
2371
2461
|
if (!Object.prototype.hasOwnProperty.call(value, key)) {
|
|
2372
|
-
errors.push({ path: `${
|
|
2462
|
+
errors.push({ path: `${path9}.${key}`, code: "required", message: "Required property is missing." });
|
|
2373
2463
|
}
|
|
2374
2464
|
}
|
|
2375
2465
|
if (isRecord(schema.propertyNames)) {
|
|
2376
2466
|
for (const key of Object.keys(value)) {
|
|
2377
|
-
validateNode(schema.propertyNames, key, `${
|
|
2467
|
+
validateNode(schema.propertyNames, key, `${path9}.${key}`, root, errors, depth + 1);
|
|
2378
2468
|
}
|
|
2379
2469
|
}
|
|
2380
2470
|
for (const [key, child] of Object.entries(value)) {
|
|
2381
2471
|
const childSchema = properties[key];
|
|
2382
2472
|
if (isRecord(childSchema)) {
|
|
2383
|
-
validateNode(childSchema, child, `${
|
|
2473
|
+
validateNode(childSchema, child, `${path9}.${key}`, root, errors, depth + 1);
|
|
2384
2474
|
} else if (schema.additionalProperties === false) {
|
|
2385
|
-
errors.push({ path: `${
|
|
2475
|
+
errors.push({ path: `${path9}.${key}`, code: "additional_property", message: "Property is not declared by the action schema." });
|
|
2386
2476
|
} else if (isRecord(schema.additionalProperties)) {
|
|
2387
|
-
validateNode(schema.additionalProperties, child, `${
|
|
2477
|
+
validateNode(schema.additionalProperties, child, `${path9}.${key}`, root, errors, depth + 1);
|
|
2388
2478
|
}
|
|
2389
2479
|
}
|
|
2390
2480
|
}
|
|
2391
|
-
function validateArray(schema, value,
|
|
2481
|
+
function validateArray(schema, value, path9, root, errors, depth) {
|
|
2392
2482
|
if (typeof schema.minItems === "number" && value.length < schema.minItems) {
|
|
2393
|
-
errors.push({ path:
|
|
2483
|
+
errors.push({ path: path9, code: "min_items", message: `Expected at least ${schema.minItems} items.` });
|
|
2394
2484
|
}
|
|
2395
2485
|
if (typeof schema.maxItems === "number" && value.length > schema.maxItems) {
|
|
2396
|
-
errors.push({ path:
|
|
2486
|
+
errors.push({ path: path9, code: "max_items", message: `Expected at most ${schema.maxItems} items.` });
|
|
2397
2487
|
}
|
|
2398
2488
|
if (schema.uniqueItems === true) {
|
|
2399
2489
|
const unique4 = new Set(value.map((item) => JSON.stringify(item)));
|
|
2400
|
-
if (unique4.size !== value.length) errors.push({ path:
|
|
2490
|
+
if (unique4.size !== value.length) errors.push({ path: path9, code: "unique_items", message: "Expected every item to be unique." });
|
|
2401
2491
|
}
|
|
2402
2492
|
if (isRecord(schema.items)) {
|
|
2403
|
-
value.forEach((item, index) => validateNode(schema.items, item, `${
|
|
2493
|
+
value.forEach((item, index) => validateNode(schema.items, item, `${path9}[${index}]`, root, errors, depth + 1));
|
|
2404
2494
|
}
|
|
2405
2495
|
}
|
|
2406
|
-
function validateString(schema, value,
|
|
2496
|
+
function validateString(schema, value, path9, errors) {
|
|
2407
2497
|
if (typeof schema.minLength === "number" && value.length < schema.minLength) {
|
|
2408
|
-
errors.push({ path:
|
|
2498
|
+
errors.push({ path: path9, code: "min_length", message: `Expected at least ${schema.minLength} characters.` });
|
|
2409
2499
|
}
|
|
2410
2500
|
if (typeof schema.maxLength === "number" && value.length > schema.maxLength) {
|
|
2411
|
-
errors.push({ path:
|
|
2501
|
+
errors.push({ path: path9, code: "max_length", message: `Expected at most ${schema.maxLength} characters.` });
|
|
2412
2502
|
}
|
|
2413
2503
|
if (typeof schema.pattern === "string") {
|
|
2414
2504
|
try {
|
|
2415
|
-
if (!new RegExp(schema.pattern).test(value)) errors.push({ path:
|
|
2505
|
+
if (!new RegExp(schema.pattern).test(value)) errors.push({ path: path9, code: "pattern", message: "Value does not match the required pattern." });
|
|
2416
2506
|
} catch {
|
|
2417
|
-
errors.push({ path:
|
|
2507
|
+
errors.push({ path: path9, code: "invalid_schema_pattern", message: "Action schema contains an invalid pattern." });
|
|
2418
2508
|
}
|
|
2419
2509
|
}
|
|
2420
|
-
if (schema.format === "uri" && !isUrl(value)) errors.push({ path:
|
|
2421
|
-
if (schema.format === "email" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)) errors.push({ path:
|
|
2422
|
-
if (schema.format === "date-time" && !isDateTime(value)) errors.push({ path:
|
|
2510
|
+
if (schema.format === "uri" && !isUrl(value)) errors.push({ path: path9, code: "format_uri", message: "Expected an absolute URL." });
|
|
2511
|
+
if (schema.format === "email" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)) errors.push({ path: path9, code: "format_email", message: "Expected an email address." });
|
|
2512
|
+
if (schema.format === "date-time" && !isDateTime(value)) errors.push({ path: path9, code: "format_date_time", message: "Expected an ISO-8601 date-time." });
|
|
2423
2513
|
}
|
|
2424
|
-
function validateNumber(schema, value,
|
|
2425
|
-
if (typeof schema.minimum === "number" && value < schema.minimum) errors.push({ path:
|
|
2426
|
-
if (typeof schema.maximum === "number" && value > schema.maximum) errors.push({ path:
|
|
2514
|
+
function validateNumber(schema, value, path9, errors) {
|
|
2515
|
+
if (typeof schema.minimum === "number" && value < schema.minimum) errors.push({ path: path9, code: "minimum", message: `Expected a value of at least ${schema.minimum}.` });
|
|
2516
|
+
if (typeof schema.maximum === "number" && value > schema.maximum) errors.push({ path: path9, code: "maximum", message: `Expected a value of at most ${schema.maximum}.` });
|
|
2427
2517
|
const exclusiveMinimum = typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : schema.exclusiveMinimum === true && typeof schema.minimum === "number" ? schema.minimum : null;
|
|
2428
2518
|
const exclusiveMaximum = typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : schema.exclusiveMaximum === true && typeof schema.maximum === "number" ? schema.maximum : null;
|
|
2429
2519
|
if (exclusiveMinimum !== null && value <= exclusiveMinimum) {
|
|
2430
|
-
errors.push({ path:
|
|
2520
|
+
errors.push({ path: path9, code: "exclusive_minimum", message: `Expected a value greater than ${exclusiveMinimum}.` });
|
|
2431
2521
|
}
|
|
2432
2522
|
if (exclusiveMaximum !== null && value >= exclusiveMaximum) {
|
|
2433
|
-
errors.push({ path:
|
|
2523
|
+
errors.push({ path: path9, code: "exclusive_maximum", message: `Expected a value less than ${exclusiveMaximum}.` });
|
|
2434
2524
|
}
|
|
2435
2525
|
if (typeof schema.multipleOf === "number" && !isMultipleOf(value, schema.multipleOf)) {
|
|
2436
|
-
errors.push({ path:
|
|
2526
|
+
errors.push({ path: path9, code: "multiple_of", message: `Expected a multiple of ${schema.multipleOf}.` });
|
|
2437
2527
|
}
|
|
2438
2528
|
}
|
|
2439
2529
|
function candidateMatches(candidate, value, root, depth) {
|
|
@@ -2565,8 +2655,8 @@ function prepareTopoloUrlSessionUploadInput(contractValue, byteLength, input3, o
|
|
|
2565
2655
|
}
|
|
2566
2656
|
};
|
|
2567
2657
|
}
|
|
2568
|
-
function topoloUploadResponseValue(response,
|
|
2569
|
-
return
|
|
2658
|
+
function topoloUploadResponseValue(response, path9) {
|
|
2659
|
+
return path9.split(".").reduce((value, segment) => isRecord2(value) ? value[segment] : void 0, response);
|
|
2570
2660
|
}
|
|
2571
2661
|
function decodeTopoloActionBase64(contentBase64) {
|
|
2572
2662
|
if (!isCanonicalBase64(contentBase64)) {
|
|
@@ -2613,11 +2703,11 @@ function parseCompleteContract(value) {
|
|
|
2613
2703
|
};
|
|
2614
2704
|
}
|
|
2615
2705
|
function requiredPath(value, name) {
|
|
2616
|
-
const
|
|
2617
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(
|
|
2706
|
+
const path9 = typeof value === "string" ? value.trim() : "";
|
|
2707
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(path9)) {
|
|
2618
2708
|
throw new TopoloSdkError("invalid_upload_contract", `The action upload contract has an invalid ${name}.`);
|
|
2619
2709
|
}
|
|
2620
|
-
return
|
|
2710
|
+
return path9;
|
|
2621
2711
|
}
|
|
2622
2712
|
function unsupportedInlineProfile() {
|
|
2623
2713
|
return new TopoloSdkError("invalid_upload_contract", "This action does not publish an inline-base64 upload contract.");
|
|
@@ -3572,8 +3662,8 @@ function normalizeActionCatalogEntry(item) {
|
|
|
3572
3662
|
const name = pickActionString(a.name);
|
|
3573
3663
|
const appId = pickActionString(a.app_id) ?? pickActionString(a.appId);
|
|
3574
3664
|
const method = pickActionString(a.method)?.toUpperCase();
|
|
3575
|
-
const
|
|
3576
|
-
if (!actionId || !name || !appId || !method || !
|
|
3665
|
+
const path9 = pickActionString(a.path);
|
|
3666
|
+
if (!actionId || !name || !appId || !method || !path9 || !isHttpMethod(method)) {
|
|
3577
3667
|
return null;
|
|
3578
3668
|
}
|
|
3579
3669
|
return {
|
|
@@ -3588,7 +3678,7 @@ function normalizeActionCatalogEntry(item) {
|
|
|
3588
3678
|
appSlug: pickActionString(a.app_slug) ?? pickActionString(a.appSlug) ?? null,
|
|
3589
3679
|
appName: pickActionString(a.app_name) ?? pickActionString(a.appName) ?? null,
|
|
3590
3680
|
method,
|
|
3591
|
-
path:
|
|
3681
|
+
path: path9,
|
|
3592
3682
|
permissionName: pickActionString(a.permission_name) ?? pickActionString(a.permissionName) ?? "",
|
|
3593
3683
|
requiredPermission: pickActionString(a.required_permission) ?? pickActionString(a.requiredPermission) ?? "",
|
|
3594
3684
|
authorizationMode: pickAuthorizationMode(a.authorization_mode ?? a.authorizationMode),
|
|
@@ -3624,7 +3714,7 @@ function stringArray(value) {
|
|
|
3624
3714
|
}
|
|
3625
3715
|
function mapActionInput(action, input3) {
|
|
3626
3716
|
const remaining = { ...input3 || {} };
|
|
3627
|
-
const
|
|
3717
|
+
const path9 = action.path.replace(/\{([a-zA-Z0-9_]+)\}/g, (_match, key) => {
|
|
3628
3718
|
const value = remaining[key];
|
|
3629
3719
|
if (value === void 0 || value === null || value === "") {
|
|
3630
3720
|
throw new TopoloSdkError(
|
|
@@ -3641,14 +3731,14 @@ function mapActionInput(action, input3) {
|
|
|
3641
3731
|
delete remaining.body;
|
|
3642
3732
|
if (action.method === "GET") {
|
|
3643
3733
|
return {
|
|
3644
|
-
path:
|
|
3734
|
+
path: path9,
|
|
3645
3735
|
query: objectToQuery({ ...remaining, ...explicitQuery })
|
|
3646
3736
|
};
|
|
3647
3737
|
}
|
|
3648
3738
|
const query = objectToQuery(explicitQuery);
|
|
3649
3739
|
const body = explicitBody !== void 0 ? explicitBody : remaining;
|
|
3650
3740
|
return {
|
|
3651
|
-
path:
|
|
3741
|
+
path: path9,
|
|
3652
3742
|
...query ? { query } : {},
|
|
3653
3743
|
body
|
|
3654
3744
|
};
|
|
@@ -3818,8 +3908,8 @@ var TopoloOAuth = class {
|
|
|
3818
3908
|
if (params.clientSecret) body.set("client_secret", params.clientSecret);
|
|
3819
3909
|
return this.#postForm("/api/developer-oauth/token", body);
|
|
3820
3910
|
}
|
|
3821
|
-
async #postForm(
|
|
3822
|
-
const url = new URL(
|
|
3911
|
+
async #postForm(path9, body) {
|
|
3912
|
+
const url = new URL(path9, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
|
|
3823
3913
|
const res = await this.fetchImpl(url.toString(), {
|
|
3824
3914
|
method: "POST",
|
|
3825
3915
|
headers: {
|
|
@@ -3835,7 +3925,7 @@ var TopoloOAuth = class {
|
|
|
3835
3925
|
const err = parsed;
|
|
3836
3926
|
throw new TopoloAuthError(err.error_description || err.error, err.error);
|
|
3837
3927
|
}
|
|
3838
|
-
throw new TopoloHttpError("auth",
|
|
3928
|
+
throw new TopoloHttpError("auth", path9, res.status, parsed);
|
|
3839
3929
|
}
|
|
3840
3930
|
return parsed;
|
|
3841
3931
|
}
|
|
@@ -3898,13 +3988,13 @@ var TopoloHttpError2 = class extends TopoloSdkError2 {
|
|
|
3898
3988
|
path;
|
|
3899
3989
|
requestId;
|
|
3900
3990
|
retryAfter;
|
|
3901
|
-
constructor(service,
|
|
3902
|
-
super(extractHttpErrorCode2(body), message ?? `HTTP ${status} from ${service}${
|
|
3991
|
+
constructor(service, path9, status, body, message, options = {}) {
|
|
3992
|
+
super(extractHttpErrorCode2(body), message ?? `HTTP ${status} from ${service}${path9}`);
|
|
3903
3993
|
this.name = "TopoloHttpError";
|
|
3904
3994
|
this.status = status;
|
|
3905
3995
|
this.body = body;
|
|
3906
3996
|
this.service = service;
|
|
3907
|
-
this.path =
|
|
3997
|
+
this.path = path9;
|
|
3908
3998
|
this.requestId = options.requestId;
|
|
3909
3999
|
this.retryAfter = options.retryAfter;
|
|
3910
4000
|
}
|
|
@@ -3913,6 +4003,7 @@ var APP_API_URLS2 = {
|
|
|
3913
4003
|
agent: "https://agent.topolo.app",
|
|
3914
4004
|
auth: "https://auth.topolo.app",
|
|
3915
4005
|
backup: "https://backup.topolo.app",
|
|
4006
|
+
blog: "https://blog.topolo.app",
|
|
3916
4007
|
books: "https://books.topolo.app",
|
|
3917
4008
|
brand: "https://brand.topolo.app",
|
|
3918
4009
|
bugfix: "https://bugfix.topolo.app",
|
|
@@ -3925,6 +4016,7 @@ var APP_API_URLS2 = {
|
|
|
3925
4016
|
compose: "https://compose.topolo.app",
|
|
3926
4017
|
consent: "https://consent.topolo.app",
|
|
3927
4018
|
crm: "https://crm.topolo.app",
|
|
4019
|
+
design: "https://design.topolo.app",
|
|
3928
4020
|
developers: "https://developers.topolo.app",
|
|
3929
4021
|
director: "https://director.topolo.app",
|
|
3930
4022
|
feed: "https://feed.topolo.app",
|
|
@@ -4037,8 +4129,8 @@ var TopoloOAuth2 = class {
|
|
|
4037
4129
|
if (params.clientSecret) body.set("client_secret", params.clientSecret);
|
|
4038
4130
|
return this.#postForm("/api/developer-oauth/token", body);
|
|
4039
4131
|
}
|
|
4040
|
-
async #postForm(
|
|
4041
|
-
const url = new URL(
|
|
4132
|
+
async #postForm(path9, body) {
|
|
4133
|
+
const url = new URL(path9, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
|
|
4042
4134
|
const res = await this.fetchImpl(url.toString(), {
|
|
4043
4135
|
method: "POST",
|
|
4044
4136
|
headers: {
|
|
@@ -4054,7 +4146,7 @@ var TopoloOAuth2 = class {
|
|
|
4054
4146
|
const err = parsed;
|
|
4055
4147
|
throw new TopoloAuthError2(err.error_description || err.error, err.error);
|
|
4056
4148
|
}
|
|
4057
|
-
throw new TopoloHttpError2("auth",
|
|
4149
|
+
throw new TopoloHttpError2("auth", path9, res.status, parsed);
|
|
4058
4150
|
}
|
|
4059
4151
|
return parsed;
|
|
4060
4152
|
}
|
|
@@ -4084,22 +4176,22 @@ function configPath() {
|
|
|
4084
4176
|
return join(base, "topolo", "config.json");
|
|
4085
4177
|
}
|
|
4086
4178
|
async function readConfig() {
|
|
4087
|
-
const
|
|
4088
|
-
if (!existsSync(
|
|
4179
|
+
const path9 = configPath();
|
|
4180
|
+
if (!existsSync(path9)) return {};
|
|
4089
4181
|
try {
|
|
4090
|
-
const raw = await readFile(
|
|
4182
|
+
const raw = await readFile(path9, "utf8");
|
|
4091
4183
|
return JSON.parse(raw);
|
|
4092
4184
|
} catch {
|
|
4093
4185
|
return {};
|
|
4094
4186
|
}
|
|
4095
4187
|
}
|
|
4096
4188
|
async function writeConfig(config) {
|
|
4097
|
-
const
|
|
4098
|
-
await mkdir(dirname(
|
|
4099
|
-
await writeFile(
|
|
4189
|
+
const path9 = configPath();
|
|
4190
|
+
await mkdir(dirname(path9), { recursive: true });
|
|
4191
|
+
await writeFile(path9, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
4100
4192
|
if (platform() !== "win32") {
|
|
4101
4193
|
try {
|
|
4102
|
-
await chmod(
|
|
4194
|
+
await chmod(path9, 384);
|
|
4103
4195
|
} catch {
|
|
4104
4196
|
}
|
|
4105
4197
|
}
|
|
@@ -4429,9 +4521,9 @@ function readCliVersion() {
|
|
|
4429
4521
|
join2(here, "package.json"),
|
|
4430
4522
|
join2(here, "..", "package.json")
|
|
4431
4523
|
];
|
|
4432
|
-
for (const
|
|
4524
|
+
for (const path9 of candidates) {
|
|
4433
4525
|
try {
|
|
4434
|
-
const pkg = JSON.parse(readFileSync(
|
|
4526
|
+
const pkg = JSON.parse(readFileSync(path9, "utf8"));
|
|
4435
4527
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
4436
4528
|
} catch {
|
|
4437
4529
|
}
|
|
@@ -4791,7 +4883,7 @@ function registerApiCommand(program2) {
|
|
|
4791
4883
|
"-q, --query <kv...>",
|
|
4792
4884
|
"Query params as key=value (repeatable), e.g. --query source=cli page=2"
|
|
4793
4885
|
).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(
|
|
4794
|
-
async (service, method,
|
|
4886
|
+
async (service, method, path9, opts) => {
|
|
4795
4887
|
const mode = resolveOutputMode(opts.json);
|
|
4796
4888
|
try {
|
|
4797
4889
|
const upperMethod = method.toUpperCase();
|
|
@@ -4830,7 +4922,7 @@ function registerApiCommand(program2) {
|
|
|
4830
4922
|
const result = await topolo.client.request({
|
|
4831
4923
|
service,
|
|
4832
4924
|
method: httpMethod,
|
|
4833
|
-
path:
|
|
4925
|
+
path: path9,
|
|
4834
4926
|
body,
|
|
4835
4927
|
...query ? { query } : {},
|
|
4836
4928
|
...resource ? { resource } : {},
|
|
@@ -5018,13 +5110,13 @@ async function requiredClient() {
|
|
|
5018
5110
|
function parseJsonObject(value) {
|
|
5019
5111
|
let source = value;
|
|
5020
5112
|
if (value.startsWith("@")) {
|
|
5021
|
-
const
|
|
5022
|
-
if (!
|
|
5113
|
+
const path9 = value.slice(1);
|
|
5114
|
+
if (!path9) throw new Error("--data @file path must not be empty");
|
|
5023
5115
|
try {
|
|
5024
|
-
source = readFileSync2(
|
|
5116
|
+
source = readFileSync2(path9, "utf8");
|
|
5025
5117
|
} catch (err) {
|
|
5026
5118
|
const detail = err instanceof Error ? err.message : String(err);
|
|
5027
|
-
throw new Error(`Could not read --data file ${
|
|
5119
|
+
throw new Error(`Could not read --data file ${path9}: ${detail}`);
|
|
5028
5120
|
}
|
|
5029
5121
|
}
|
|
5030
5122
|
let parsed;
|
|
@@ -5038,7 +5130,7 @@ function parseJsonObject(value) {
|
|
|
5038
5130
|
}
|
|
5039
5131
|
return parsed;
|
|
5040
5132
|
}
|
|
5041
|
-
function inferContentType(
|
|
5133
|
+
function inferContentType(path9) {
|
|
5042
5134
|
return {
|
|
5043
5135
|
".avif": "image/avif",
|
|
5044
5136
|
".gif": "image/gif",
|
|
@@ -5051,7 +5143,7 @@ function inferContentType(path8) {
|
|
|
5051
5143
|
".png": "image/png",
|
|
5052
5144
|
".svg": "image/svg+xml",
|
|
5053
5145
|
".webp": "image/webp"
|
|
5054
|
-
}[extname(
|
|
5146
|
+
}[extname(path9).toLowerCase()] ?? null;
|
|
5055
5147
|
}
|
|
5056
5148
|
function formatActionRows(rows, nextCursor = null) {
|
|
5057
5149
|
if (rows.length === 0) return "No actions available for this credential.";
|
|
@@ -5113,8 +5205,8 @@ function formatActionPlan(plan) {
|
|
|
5113
5205
|
|
|
5114
5206
|
// src/commands/apps.ts
|
|
5115
5207
|
import { execSync } from "child_process";
|
|
5116
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as
|
|
5117
|
-
import
|
|
5208
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
5209
|
+
import path6 from "path";
|
|
5118
5210
|
|
|
5119
5211
|
// src/commands/app-audit-local.ts
|
|
5120
5212
|
import {
|
|
@@ -6251,14 +6343,14 @@ import { constants } from "fs";
|
|
|
6251
6343
|
import { access } from "fs/promises";
|
|
6252
6344
|
import { relative, resolve } from "path";
|
|
6253
6345
|
function normalizeHomePath(value) {
|
|
6254
|
-
const
|
|
6255
|
-
if (!
|
|
6346
|
+
const path9 = String(value || "/dashboard").trim();
|
|
6347
|
+
if (!path9.startsWith("/") || path9.startsWith("//") || /[?#]/.test(path9)) {
|
|
6256
6348
|
throw new Error("Invalid home path. Use an absolute application path such as /dashboard.");
|
|
6257
6349
|
}
|
|
6258
|
-
if (["/", "/login", "/auth/callback", "/getting-started"].includes(
|
|
6350
|
+
if (["/", "/login", "/auth/callback", "/getting-started"].includes(path9)) {
|
|
6259
6351
|
throw new Error("Home path must be a protected product route, not a reserved entry route.");
|
|
6260
6352
|
}
|
|
6261
|
-
return
|
|
6353
|
+
return path9.replace(/\/$/, "") || "/dashboard";
|
|
6262
6354
|
}
|
|
6263
6355
|
function normalizeHomeLabel(value) {
|
|
6264
6356
|
const label = String(value || "Dashboard").trim();
|
|
@@ -6389,9 +6481,9 @@ function toTitle(value) {
|
|
|
6389
6481
|
function json(value) {
|
|
6390
6482
|
return JSON.stringify(value, null, 2);
|
|
6391
6483
|
}
|
|
6392
|
-
async function pathExists(
|
|
6484
|
+
async function pathExists(path9) {
|
|
6393
6485
|
try {
|
|
6394
|
-
await access(
|
|
6486
|
+
await access(path9, constants.F_OK);
|
|
6395
6487
|
return true;
|
|
6396
6488
|
} catch {
|
|
6397
6489
|
return false;
|
|
@@ -6421,6 +6513,54 @@ function firstPartyAppPath(appDir, _audience) {
|
|
|
6421
6513
|
return `apps/${appDir}`;
|
|
6422
6514
|
}
|
|
6423
6515
|
|
|
6516
|
+
// src/commands/app-scaffold-platform-versions.ts
|
|
6517
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
6518
|
+
import path2 from "path";
|
|
6519
|
+
var versionCache = /* @__PURE__ */ new Map();
|
|
6520
|
+
function platformPackagePin(packageName, options = {}) {
|
|
6521
|
+
const workspaceRoot = resolveTopoloRepoRoot(
|
|
6522
|
+
options.repoRoot ?? process.env.TOPOLO_WORKSPACE_ROOT ?? void 0,
|
|
6523
|
+
options.cwd ?? process.cwd()
|
|
6524
|
+
);
|
|
6525
|
+
if (!workspaceRoot) {
|
|
6526
|
+
throw new Error(
|
|
6527
|
+
`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".`
|
|
6528
|
+
);
|
|
6529
|
+
}
|
|
6530
|
+
const version = platformPackageVersions(workspaceRoot).get(packageName);
|
|
6531
|
+
if (!version) {
|
|
6532
|
+
throw new Error(
|
|
6533
|
+
`Unknown Topolo platform package: ${packageName} \u2014 no published package with that name under ${path2.join(workspaceRoot, "topolo-platform", "packages")}.`
|
|
6534
|
+
);
|
|
6535
|
+
}
|
|
6536
|
+
return `^${version}`;
|
|
6537
|
+
}
|
|
6538
|
+
function platformPackageVersions(workspaceRoot) {
|
|
6539
|
+
const packagesRoot = path2.join(workspaceRoot, "topolo-platform", "packages");
|
|
6540
|
+
const cached = versionCache.get(packagesRoot);
|
|
6541
|
+
if (cached) return cached;
|
|
6542
|
+
const versions = /* @__PURE__ */ new Map();
|
|
6543
|
+
let entries = [];
|
|
6544
|
+
try {
|
|
6545
|
+
entries = readdirSync2(packagesRoot);
|
|
6546
|
+
} catch {
|
|
6547
|
+
}
|
|
6548
|
+
for (const entry of entries) {
|
|
6549
|
+
let packageJson2;
|
|
6550
|
+
try {
|
|
6551
|
+
packageJson2 = JSON.parse(readFileSync4(path2.join(packagesRoot, entry, "package.json"), "utf8"));
|
|
6552
|
+
} catch {
|
|
6553
|
+
continue;
|
|
6554
|
+
}
|
|
6555
|
+
const version = packageJson2.topoloPublishedVersion || packageJson2.version;
|
|
6556
|
+
if (packageJson2.name && /^@topolo(-io)?\//.test(packageJson2.name) && !packageJson2.private && version) {
|
|
6557
|
+
versions.set(packageJson2.name, version);
|
|
6558
|
+
}
|
|
6559
|
+
}
|
|
6560
|
+
versionCache.set(packagesRoot, versions);
|
|
6561
|
+
return versions;
|
|
6562
|
+
}
|
|
6563
|
+
|
|
6424
6564
|
// src/commands/app-scaffold-cloudflare-constants.ts
|
|
6425
6565
|
var CLOUDFLARE_ACCOUNT_ID = "49ef1ba682ad8cfd720c86699ae17521";
|
|
6426
6566
|
var STAGING_CLOUDFLARE_ACCOUNT_ID = "4f4e1c69a3830946f9fea7b1eb7531ac";
|
|
@@ -6453,7 +6593,7 @@ function packageJson(application, target, options) {
|
|
|
6453
6593
|
devDependencies.tailwindcss = "3.4.19";
|
|
6454
6594
|
dependencies["react-router-dom"] = "^7.6.2";
|
|
6455
6595
|
} else {
|
|
6456
|
-
dependencies["@topolo/login"] = topoloPackageVersion("@topolo/login", target);
|
|
6596
|
+
dependencies["@topolo/login"] = topoloPackageVersion("@topolo/login", target, options.repoRoot);
|
|
6457
6597
|
}
|
|
6458
6598
|
}
|
|
6459
6599
|
if (options.api) {
|
|
@@ -6465,13 +6605,13 @@ function packageJson(application, target, options) {
|
|
|
6465
6605
|
scripts["deploy:staging"] = `DEPLOY_ENVIRONMENT=staging CLOUDFLARE_ACCOUNT_ID=${STAGING_CLOUDFLARE_ACCOUNT_ID} python3 .github/scripts/standard_deploy.py local-deploy`;
|
|
6466
6606
|
scripts["deploy:production"] = `DEPLOY_ENVIRONMENT=production CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID} python3 .github/scripts/standard_deploy.py local-deploy`;
|
|
6467
6607
|
} else {
|
|
6468
|
-
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
|
|
6608
|
+
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target, options.repoRoot);
|
|
6469
6609
|
scripts.deploy = "npm run build && wrangler deploy";
|
|
6470
6610
|
}
|
|
6471
6611
|
devDependencies.wrangler = "^4.91.0";
|
|
6472
6612
|
}
|
|
6473
6613
|
for (const packageName of options.packages) {
|
|
6474
|
-
dependencies[packageName] = topoloPackageVersion(packageName, target);
|
|
6614
|
+
dependencies[packageName] = topoloPackageVersion(packageName, target, options.repoRoot);
|
|
6475
6615
|
}
|
|
6476
6616
|
if (target === "platform") {
|
|
6477
6617
|
const testDirs = [];
|
|
@@ -6513,14 +6653,16 @@ function packageJson(application, target, options) {
|
|
|
6513
6653
|
})}
|
|
6514
6654
|
`;
|
|
6515
6655
|
}
|
|
6516
|
-
function topoloPackageVersion(packageName, target) {
|
|
6656
|
+
function topoloPackageVersion(packageName, target, repoRoot) {
|
|
6517
6657
|
if (target === "developer") {
|
|
6518
6658
|
if (packageName === "@topolo/login") return "^0.1.0";
|
|
6519
6659
|
if (packageName === "@topolo-io/auth-client") return "^1.0.0";
|
|
6520
6660
|
if (packageName === "@topolo/sdk") return "^0.8.1";
|
|
6521
6661
|
return "^0.1.0";
|
|
6522
6662
|
}
|
|
6523
|
-
if (packageName.startsWith("@topolo/") || packageName.startsWith("@topolo-io/"))
|
|
6663
|
+
if (packageName.startsWith("@topolo/") || packageName.startsWith("@topolo-io/")) {
|
|
6664
|
+
return platformPackagePin(packageName, { repoRoot });
|
|
6665
|
+
}
|
|
6524
6666
|
throw new Error(`Unknown Topolo platform package: ${packageName}`);
|
|
6525
6667
|
}
|
|
6526
6668
|
function hasScaffoldResources(resources) {
|
|
@@ -7159,6 +7301,10 @@ function envDts(api) {
|
|
|
7159
7301
|
"interface ImportMetaEnv {",
|
|
7160
7302
|
" readonly VITE_TOPOLO_AUTH_URL?: string;",
|
|
7161
7303
|
" readonly VITE_TOPOLO_APP_ID?: string;",
|
|
7304
|
+
" readonly VITE_TOPOLO_INSIGHTS_SITE_KEY?: string;",
|
|
7305
|
+
" readonly VITE_TOPOLO_INSIGHTS_ENDPOINT?: string;",
|
|
7306
|
+
" readonly VITE_TOPOLO_CONSENT_BASE_URL?: string;",
|
|
7307
|
+
" readonly VITE_TOPOLO_CONSENT_SDK_KEY?: string;",
|
|
7162
7308
|
" readonly VITE_TOPOLO_CLIENT_ID?: string;",
|
|
7163
7309
|
" readonly VITE_TOPOLO_REDIRECT_URI?: string;",
|
|
7164
7310
|
" readonly VITE_TOPOLO_SCOPES?: string;"
|
|
@@ -8489,10 +8635,10 @@ function developerWorkerTs(application) {
|
|
|
8489
8635
|
}
|
|
8490
8636
|
|
|
8491
8637
|
// src/commands/app-scaffold-generate.ts
|
|
8492
|
-
import { existsSync as existsSync3, mkdirSync, mkdtempSync, readFileSync as
|
|
8638
|
+
import { existsSync as existsSync3, mkdirSync, mkdtempSync, readFileSync as readFileSync5, rmSync, writeFileSync } from "fs";
|
|
8493
8639
|
import { spawnSync } from "child_process";
|
|
8494
8640
|
import { tmpdir } from "os";
|
|
8495
|
-
import
|
|
8641
|
+
import path3 from "path";
|
|
8496
8642
|
function resolveGeneratorPath() {
|
|
8497
8643
|
const candidates = [
|
|
8498
8644
|
// Published CLI: vendored alongside dist/index.js at build time.
|
|
@@ -8519,18 +8665,18 @@ var GENERATED = {
|
|
|
8519
8665
|
};
|
|
8520
8666
|
function deriveGeneratedConfigs(files, base) {
|
|
8521
8667
|
const generator = resolveGeneratorPath();
|
|
8522
|
-
const root = mkdtempSync(
|
|
8668
|
+
const root = mkdtempSync(path3.join(tmpdir(), "topolo-scaffold-generate-"));
|
|
8523
8669
|
try {
|
|
8524
8670
|
const prefix = `${base}/`;
|
|
8525
8671
|
for (const file of files) {
|
|
8526
8672
|
if (!file.path.startsWith(prefix)) continue;
|
|
8527
|
-
const abs =
|
|
8528
|
-
mkdirSync(
|
|
8673
|
+
const abs = path3.join(root, file.path.slice(prefix.length));
|
|
8674
|
+
mkdirSync(path3.dirname(abs), { recursive: true });
|
|
8529
8675
|
writeFileSync(abs, file.content, "utf8");
|
|
8530
8676
|
}
|
|
8531
8677
|
const result = spawnSync(
|
|
8532
8678
|
process.execPath,
|
|
8533
|
-
[generator, `--manifest=${
|
|
8679
|
+
[generator, `--manifest=${path3.join(root, "topolo.app.json")}`],
|
|
8534
8680
|
{ cwd: root, encoding: "utf8" }
|
|
8535
8681
|
);
|
|
8536
8682
|
if (result.status !== 0) {
|
|
@@ -8547,7 +8693,7 @@ ${result.stdout ?? ""}`);
|
|
|
8547
8693
|
return written.map((rel) => ({
|
|
8548
8694
|
path: `${base}/${rel}`,
|
|
8549
8695
|
...GENERATED,
|
|
8550
|
-
content:
|
|
8696
|
+
content: readFileSync5(path3.join(root, rel), "utf8")
|
|
8551
8697
|
}));
|
|
8552
8698
|
} finally {
|
|
8553
8699
|
rmSync(root, { recursive: true, force: true });
|
|
@@ -8829,6 +8975,13 @@ function topoloAppManifestJson(application, options) {
|
|
|
8829
8975
|
production: "cd ../.. && DEPLOY_ENVIRONMENT=production VITE_TOPOLO_AUTH_URL=https://auth.topolo.app pnpm run build"
|
|
8830
8976
|
},
|
|
8831
8977
|
healthcheckPath: options.api ? "/api/health" : "/",
|
|
8978
|
+
vars: {
|
|
8979
|
+
INSIGHTS_SITE_KEY: {
|
|
8980
|
+
development: "",
|
|
8981
|
+
staging: "",
|
|
8982
|
+
production: ""
|
|
8983
|
+
}
|
|
8984
|
+
},
|
|
8832
8985
|
...options.api ? {
|
|
8833
8986
|
bindings: {
|
|
8834
8987
|
services: [
|
|
@@ -8887,6 +9040,7 @@ function topoloAppManifestJson(application, options) {
|
|
|
8887
9040
|
version: "1.0.0",
|
|
8888
9041
|
pricing: { type: "free", monthlyPrice: null },
|
|
8889
9042
|
supportEmail: "support@topolo.io",
|
|
9043
|
+
repoUrl: `https://github.com/Topolo-io/${application.appDir}`,
|
|
8890
9044
|
auth: {
|
|
8891
9045
|
callbackPath: "/auth/callback",
|
|
8892
9046
|
requestedScopes: unique3([
|
|
@@ -8900,7 +9054,7 @@ function topoloAppManifestJson(application, options) {
|
|
|
8900
9054
|
},
|
|
8901
9055
|
capabilities: options.payload.capabilities,
|
|
8902
9056
|
marketplace: {
|
|
8903
|
-
intent: options.payload.distributionModel === "marketplace" ? "
|
|
9057
|
+
intent: options.payload.distributionModel === "marketplace" ? "publish" : "not_requested",
|
|
8904
9058
|
status: "preview",
|
|
8905
9059
|
shortBlurb: options.payload.experience.tagline,
|
|
8906
9060
|
tagline: options.payload.experience.tagline,
|
|
@@ -9693,7 +9847,7 @@ function applicationScaffoldPayload(application, options = {}) {
|
|
|
9693
9847
|
const containerAppId = normalizeContainerAppId(options.containerAppId, surfaceModel);
|
|
9694
9848
|
const profile = normalizeScaffoldProfile(options.profile);
|
|
9695
9849
|
const ownerType = target === "platform" ? "topolo_first_party" : "third_party";
|
|
9696
|
-
const distributionModel = target === "platform" ? "marketplace" : "organization_internal";
|
|
9850
|
+
const distributionModel = target === "platform" ? "marketplace" : options.distributionModel ?? "organization_internal";
|
|
9697
9851
|
const capabilityComposition = resolveApplicationCapabilities({
|
|
9698
9852
|
target,
|
|
9699
9853
|
browser,
|
|
@@ -9781,7 +9935,8 @@ function applicationScaffoldPayload(application, options = {}) {
|
|
|
9781
9935
|
target,
|
|
9782
9936
|
browser,
|
|
9783
9937
|
api,
|
|
9784
|
-
ciWorkflow
|
|
9938
|
+
ciWorkflow,
|
|
9939
|
+
repoRoot: options.repoRoot
|
|
9785
9940
|
}),
|
|
9786
9941
|
commands: scaffoldCommands({
|
|
9787
9942
|
id,
|
|
@@ -9869,7 +10024,8 @@ function scaffoldFiles(options) {
|
|
|
9869
10024
|
browser: options.browser,
|
|
9870
10025
|
api: options.api,
|
|
9871
10026
|
resources: options.payload.resources,
|
|
9872
|
-
packages: options.payload.capabilityComposition.packages
|
|
10027
|
+
packages: options.payload.capabilityComposition.packages,
|
|
10028
|
+
repoRoot: options.repoRoot
|
|
9873
10029
|
})
|
|
9874
10030
|
},
|
|
9875
10031
|
{
|
|
@@ -10584,7 +10740,7 @@ function buildProvisioningRequest(payload, supportEmail) {
|
|
|
10584
10740
|
...payload.application.appId ? [`${payload.application.id}:read`] : []
|
|
10585
10741
|
])),
|
|
10586
10742
|
marketplace: {
|
|
10587
|
-
intent: payload.distributionModel === "marketplace" ? "submit_when_ready" : "not_requested",
|
|
10743
|
+
intent: payload.target === "platform" && payload.distributionModel === "marketplace" ? "publish" : payload.distributionModel === "marketplace" ? "submit_when_ready" : "not_requested",
|
|
10588
10744
|
status: "preview",
|
|
10589
10745
|
shortBlurb: payload.experience.tagline,
|
|
10590
10746
|
tagline: payload.experience.tagline,
|
|
@@ -10600,7 +10756,7 @@ function buildProvisioningRequest(payload, supportEmail) {
|
|
|
10600
10756
|
pricingType: "free",
|
|
10601
10757
|
monthlyPrice: null,
|
|
10602
10758
|
demoUrl: payload.application.productionUrl,
|
|
10603
|
-
repoUrl: null,
|
|
10759
|
+
repoUrl: payload.target === "platform" ? `https://github.com/Topolo-io/${payload.application.appDir}` : null,
|
|
10604
10760
|
supportEmail,
|
|
10605
10761
|
tags: [],
|
|
10606
10762
|
screenshots: [],
|
|
@@ -10680,18 +10836,18 @@ import { join as join3 } from "path";
|
|
|
10680
10836
|
|
|
10681
10837
|
// src/commands/app-reconcile.ts
|
|
10682
10838
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
10683
|
-
import
|
|
10839
|
+
import path5 from "path";
|
|
10684
10840
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
10685
10841
|
import { stdin as input2, stdout as output } from "process";
|
|
10686
10842
|
|
|
10687
10843
|
// src/commands/app-capability-composition.ts
|
|
10688
10844
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
10689
|
-
import
|
|
10845
|
+
import path4 from "path";
|
|
10690
10846
|
async function reconcileApplicationCapabilitySource(options) {
|
|
10691
|
-
const manifestPath =
|
|
10692
|
-
const root =
|
|
10693
|
-
const packagePath =
|
|
10694
|
-
const contractPath =
|
|
10847
|
+
const manifestPath = path4.resolve(options.manifestPath || "topolo.app.json");
|
|
10848
|
+
const root = path4.dirname(manifestPath);
|
|
10849
|
+
const packagePath = path4.join(root, "package.json");
|
|
10850
|
+
const contractPath = path4.join(root, "topolo.capability-contract.json");
|
|
10695
10851
|
const manifest = parseRecord(await readFile3(manifestPath, "utf8"), manifestPath);
|
|
10696
10852
|
const registration = record(manifest.registration);
|
|
10697
10853
|
if (!registration) throw new Error("topolo.app.json must declare registration before capability composition.");
|
|
@@ -10701,13 +10857,15 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10701
10857
|
const target = options.ownerType === "topolo_first_party" ? "platform" : "developer";
|
|
10702
10858
|
const surfaces = resolveManifestSurfaces(manifest, registration);
|
|
10703
10859
|
const existing = Array.isArray(registration.capabilities) ? registration.capabilities.filter((value) => typeof value === "string") : [];
|
|
10860
|
+
const declaredNotificationRole = record(manifest.notifications)?.role;
|
|
10861
|
+
const inferredCapabilities = declaredNotificationRole === "producer" ? ["notification_producer"] : [];
|
|
10704
10862
|
const capabilities = resolveApplicationCapabilities({
|
|
10705
10863
|
target,
|
|
10706
10864
|
browser: surfaces.browser,
|
|
10707
10865
|
api: surfaces.api,
|
|
10708
10866
|
mobile: surfaces.mobile,
|
|
10709
10867
|
distributionModel: registration.distributionModel === "marketplace" ? "marketplace" : "organization_internal",
|
|
10710
|
-
requested: [...existing, ...options.additions ?? []]
|
|
10868
|
+
requested: [...existing, ...inferredCapabilities, ...options.additions ?? []]
|
|
10711
10869
|
});
|
|
10712
10870
|
const marketplace = record(registration.marketplace);
|
|
10713
10871
|
const unlistedNativeArtifact = options.ownerType === "topolo_first_party" && registration.surfaceModel === "mobile_container" && registration.distributionModel === "organization_internal" && marketplace?.intent === "unlisted";
|
|
@@ -10736,7 +10894,7 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10736
10894
|
const nextDependencies = { ...dependencies };
|
|
10737
10895
|
for (const packageName of capabilities.packages) {
|
|
10738
10896
|
if (typeof nextDependencies[packageName] !== "string") {
|
|
10739
|
-
nextDependencies[packageName] = packageVersion(packageName, target);
|
|
10897
|
+
nextDependencies[packageName] = packageVersion(packageName, target, { repoRoot: options.repoRoot, cwd: root });
|
|
10740
10898
|
}
|
|
10741
10899
|
}
|
|
10742
10900
|
const sortedDependencies = sortRecord(nextDependencies);
|
|
@@ -10746,9 +10904,9 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10746
10904
|
compareChange(changes, "topolo.capability-contract.json", existingContract, expectedContract);
|
|
10747
10905
|
const adapter = target === "platform" ? managedCapabilityAdapter(capabilities.selected) : null;
|
|
10748
10906
|
const apiDirectory = resolveApiDirectory(root, manifest, target);
|
|
10749
|
-
const adapterPath =
|
|
10907
|
+
const adapterPath = path4.join(apiDirectory, "src", "topolo-capabilities.ts");
|
|
10750
10908
|
if (adapter) compareChange(changes, relativePath(root, adapterPath), await readOptionalText(adapterPath), adapter);
|
|
10751
|
-
const pricingPath =
|
|
10909
|
+
const pricingPath = path4.join(root, "pricing-manifest.json");
|
|
10752
10910
|
const pricingManifest = capabilities.selected.includes("billing") ? defaultPricingManifest({
|
|
10753
10911
|
appId: String(manifest.appId || manifest.app_id || "").trim(),
|
|
10754
10912
|
displayName: String(registration.displayName || manifest.name || "Application"),
|
|
@@ -10760,7 +10918,7 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10760
10918
|
const missingResources = capabilities.resources.filter((resource) => !manifestProvidesResource(manifest, resource));
|
|
10761
10919
|
const verticalConfiguration = [];
|
|
10762
10920
|
for (const [capability2, fileName, description] of VERTICAL_CAPABILITY_FILES) {
|
|
10763
|
-
if (capabilities.selected.includes(capability2) && !await fileExists(
|
|
10921
|
+
if (capabilities.selected.includes(capability2) && !await fileExists(path4.join(root, fileName))) {
|
|
10764
10922
|
verticalConfiguration.push(`${fileName}: ${description}`);
|
|
10765
10923
|
}
|
|
10766
10924
|
}
|
|
@@ -10775,7 +10933,7 @@ async function reconcileApplicationCapabilitySource(options) {
|
|
|
10775
10933
|
await writeJson(contractPath, expectedContract);
|
|
10776
10934
|
if (pricingManifest) await writeJson(pricingPath, pricingManifest);
|
|
10777
10935
|
if (adapter) {
|
|
10778
|
-
await mkdir3(
|
|
10936
|
+
await mkdir3(path4.dirname(adapterPath), { recursive: true });
|
|
10779
10937
|
await writeFile4(adapterPath, adapter, "utf8");
|
|
10780
10938
|
}
|
|
10781
10939
|
}
|
|
@@ -10878,11 +11036,8 @@ function manifestProvidesResource(manifest, resource) {
|
|
|
10878
11036
|
}
|
|
10879
11037
|
return false;
|
|
10880
11038
|
}
|
|
10881
|
-
function packageVersion(packageName, target) {
|
|
10882
|
-
if (target === "platform")
|
|
10883
|
-
if (packageName === "@topolo-io/notifications") return "^0.6.8";
|
|
10884
|
-
return "latest";
|
|
10885
|
-
}
|
|
11039
|
+
function packageVersion(packageName, target, resolution) {
|
|
11040
|
+
if (target === "platform") return platformPackagePin(packageName, resolution);
|
|
10886
11041
|
if (packageName === "@topolo/login") return "^0.1.0";
|
|
10887
11042
|
if (packageName === "@topolo/sdk") return "^0.8.1";
|
|
10888
11043
|
throw new Error(`Capability registry exposed private package ${packageName} to a third-party application.`);
|
|
@@ -10959,13 +11114,13 @@ function resolveApiDirectory(root, manifest, target) {
|
|
|
10959
11114
|
for (const value of Object.values(record(manifest.targets) ?? {})) {
|
|
10960
11115
|
const targetRecord = record(value);
|
|
10961
11116
|
if (targetRecord?.kind === "worker" && !targetRecord.assets && typeof targetRecord.dir === "string") {
|
|
10962
|
-
return
|
|
11117
|
+
return path4.resolve(root, targetRecord.dir);
|
|
10963
11118
|
}
|
|
10964
11119
|
}
|
|
10965
|
-
return
|
|
11120
|
+
return path4.join(root, "apps", "api");
|
|
10966
11121
|
}
|
|
10967
11122
|
function relativePath(root, filePath) {
|
|
10968
|
-
return
|
|
11123
|
+
return path4.relative(root, filePath).split(path4.sep).join("/");
|
|
10969
11124
|
}
|
|
10970
11125
|
|
|
10971
11126
|
// src/commands/app-reconcile.ts
|
|
@@ -11035,7 +11190,7 @@ function titleFromSlug(value) {
|
|
|
11035
11190
|
return value.replace(/^topolo[-_]/, "").split(/[-_]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
11036
11191
|
}
|
|
11037
11192
|
async function completeApplicationRegistrationInteractively(options) {
|
|
11038
|
-
const manifestPath =
|
|
11193
|
+
const manifestPath = path5.resolve(options.manifestPath || "topolo.app.json");
|
|
11039
11194
|
const manifest = JSON.parse(await readFile4(manifestPath, "utf8"));
|
|
11040
11195
|
const existing = record2(manifest.registration) || {};
|
|
11041
11196
|
const appId = String(manifest.appId || manifest.app_id || "").trim();
|
|
@@ -11084,7 +11239,7 @@ async function completeApplicationRegistrationInteractively(options) {
|
|
|
11084
11239
|
},
|
|
11085
11240
|
capabilities: Array.isArray(existing.capabilities) ? existing.capabilities : ["login", "launcher"],
|
|
11086
11241
|
marketplace: record2(existing.marketplace) || {
|
|
11087
|
-
intent: options.expectedOwnerType === "topolo_first_party" ? "
|
|
11242
|
+
intent: options.expectedOwnerType === "topolo_first_party" ? "publish" : "not_requested",
|
|
11088
11243
|
status: "preview",
|
|
11089
11244
|
shortBlurb: description,
|
|
11090
11245
|
tagline: description,
|
|
@@ -11115,7 +11270,7 @@ async function completeApplicationRegistrationInteractively(options) {
|
|
|
11115
11270
|
}
|
|
11116
11271
|
}
|
|
11117
11272
|
async function loadApplicationReconcileRequest(options) {
|
|
11118
|
-
const manifestPath =
|
|
11273
|
+
const manifestPath = path5.resolve(options.manifestPath || "topolo.app.json");
|
|
11119
11274
|
const raw = await readFile4(manifestPath, "utf8");
|
|
11120
11275
|
const manifest = JSON.parse(raw);
|
|
11121
11276
|
assertRegistration(manifest.registration);
|
|
@@ -11161,7 +11316,8 @@ async function loadApplicationReconcileRequest(options) {
|
|
|
11161
11316
|
requestedScopes: registration.auth?.requestedScopes ?? [],
|
|
11162
11317
|
capabilities: registration.capabilities,
|
|
11163
11318
|
marketplace: { ...registration.marketplace, appUrl: productionUrl },
|
|
11164
|
-
notifications: manifest.notifications ?? null
|
|
11319
|
+
notifications: manifest.notifications ?? null,
|
|
11320
|
+
actions: manifest.actions ?? []
|
|
11165
11321
|
}
|
|
11166
11322
|
};
|
|
11167
11323
|
}
|
|
@@ -11413,6 +11569,9 @@ function registerAppsCommand(program2) {
|
|
|
11413
11569
|
apps.command("scaffold <application>").description(
|
|
11414
11570
|
"Generate a third-party Topolo application scaffold for business or personal/household use."
|
|
11415
11571
|
).option("--json", "Output JSON").option("--no-json", "Output human-readable checklist").option("--app-dir <path>", "Application directory under the scaffold root").option("--name <name>", "Application display/package source name").option("--package-name <name>", "package.json name").option("--production-url <url>", "Production browser URL").option("--app-id <id>", "Stable SDK/Auth app id for API-backed apps").option("--audience <audience>", "Developer app audience: business or personal").option("--tenancy <tenancy>", "Developer app tenancy: organization or household").option(
|
|
11572
|
+
"--distribution-model <model>",
|
|
11573
|
+
"Developer app distribution: organization_internal or marketplace (marketplace requires approval)"
|
|
11574
|
+
).option(
|
|
11416
11575
|
"--surface-model <surfaceModel>",
|
|
11417
11576
|
"Developer app surface model: web_app, mobile_container, mobile_module, or messaging_surface"
|
|
11418
11577
|
).option("--container-app-id <appId>", "Container app id when --surface-model mobile_module is used").option("--capability <id...>", "Compose one or more approved Topolo capabilities").option("--write", "Write the generated scaffold files to disk").option("--repo-root <path>", "Directory where scaffold files should be written").option("--force", "Overwrite existing scaffold files when --write is set").option("--with-api", "Include API/service files for developer scaffolds").option("--no-api", "Do not include API/service requirements").option("--no-browser", "Do not include browser-shell requirements").option(
|
|
@@ -11433,6 +11592,7 @@ function registerAppsCommand(program2) {
|
|
|
11433
11592
|
target: "developer",
|
|
11434
11593
|
audience: resolveScaffoldAudience(opts.audience),
|
|
11435
11594
|
tenancy: resolveScaffoldTenancy(opts.tenancy),
|
|
11595
|
+
distributionModel: resolveScaffoldDistributionModel(opts.distributionModel),
|
|
11436
11596
|
surfaceModel: resolveScaffoldSurfaceModel(opts.surfaceModel),
|
|
11437
11597
|
containerAppId: opts.containerAppId,
|
|
11438
11598
|
capabilities: opts.capability
|
|
@@ -11519,7 +11679,7 @@ function applicationAuditPayload(application, options = {}) {
|
|
|
11519
11679
|
}
|
|
11520
11680
|
function recordApplicationEvidence(application, options = {}) {
|
|
11521
11681
|
const appId = resolveApplication(application).id;
|
|
11522
|
-
const root =
|
|
11682
|
+
const root = path6.resolve(options.appRoot ?? process.cwd());
|
|
11523
11683
|
if (!existsSync4(root)) {
|
|
11524
11684
|
throw new Error(`Application root does not exist: ${root}`);
|
|
11525
11685
|
}
|
|
@@ -11531,12 +11691,12 @@ function recordApplicationEvidence(application, options = {}) {
|
|
|
11531
11691
|
commit = void 0;
|
|
11532
11692
|
}
|
|
11533
11693
|
}
|
|
11534
|
-
const evidenceDir =
|
|
11535
|
-
const evidencePath =
|
|
11694
|
+
const evidenceDir = path6.join(root, ".topolo");
|
|
11695
|
+
const evidencePath = path6.join(evidenceDir, "evidence.json");
|
|
11536
11696
|
let before = null;
|
|
11537
11697
|
if (existsSync4(evidencePath)) {
|
|
11538
11698
|
try {
|
|
11539
|
-
const raw = JSON.parse(
|
|
11699
|
+
const raw = JSON.parse(readFileSync6(evidencePath, "utf8"));
|
|
11540
11700
|
if (raw && typeof raw === "object" && raw.version === 1) {
|
|
11541
11701
|
before = raw;
|
|
11542
11702
|
}
|
|
@@ -11792,6 +11952,16 @@ function resolveScaffoldTenancy(value) {
|
|
|
11792
11952
|
}
|
|
11793
11953
|
throw new Error("Invalid --tenancy value. Use organization or household.");
|
|
11794
11954
|
}
|
|
11955
|
+
function resolveScaffoldDistributionModel(value) {
|
|
11956
|
+
if (!value) return void 0;
|
|
11957
|
+
const normalized = value.trim().toLowerCase().replace(/-/g, "_");
|
|
11958
|
+
if (normalized === "marketplace" || normalized === "organization_internal") {
|
|
11959
|
+
return normalized;
|
|
11960
|
+
}
|
|
11961
|
+
throw new Error(
|
|
11962
|
+
"Invalid --distribution-model value. Use organization_internal or marketplace."
|
|
11963
|
+
);
|
|
11964
|
+
}
|
|
11795
11965
|
function resolveScaffoldSurfaceModel(value) {
|
|
11796
11966
|
if (!value) return void 0;
|
|
11797
11967
|
const normalized = value.trim().toLowerCase();
|
|
@@ -11831,14 +12001,14 @@ function statusesAtOrWorse(threshold) {
|
|
|
11831
12001
|
}
|
|
11832
12002
|
|
|
11833
12003
|
// src/commands/platform.ts
|
|
11834
|
-
import { readdirSync as
|
|
11835
|
-
import
|
|
12004
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
12005
|
+
import path8 from "path";
|
|
11836
12006
|
|
|
11837
12007
|
// src/commands/app-capability-provision.ts
|
|
11838
12008
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
11839
|
-
import
|
|
12009
|
+
import path7 from "path";
|
|
11840
12010
|
async function provisionApplicationResources(options) {
|
|
11841
|
-
const manifestPath =
|
|
12011
|
+
const manifestPath = path7.resolve(options.manifestPath || "topolo.app.json");
|
|
11842
12012
|
const manifest = parseRecord2(await readFile5(manifestPath, "utf8"), manifestPath);
|
|
11843
12013
|
const environments = requiredRecord(manifest.environments, "topolo.app.json#environments");
|
|
11844
12014
|
const environment = requiredRecord(environments[options.environment], `topolo.app.json#environments.${options.environment}`);
|
|
@@ -12043,7 +12213,8 @@ function registerPlatformCommand(program2) {
|
|
|
12043
12213
|
navigationMode: opts.navigationMode,
|
|
12044
12214
|
workspaceSwitcher: opts.workspaceSwitcher,
|
|
12045
12215
|
uiTemplate: opts.uiTemplate,
|
|
12046
|
-
tagline: opts.tagline
|
|
12216
|
+
tagline: opts.tagline,
|
|
12217
|
+
repoRoot: opts.repoRoot
|
|
12047
12218
|
});
|
|
12048
12219
|
if (opts.workflowOnly && !payload.ciWorkflow) {
|
|
12049
12220
|
throw new Error("--workflow-only is only available for first-party scaffolds.");
|
|
@@ -12110,12 +12281,12 @@ var SKIPPED_SCAN_DIRS = /* @__PURE__ */ new Set([
|
|
|
12110
12281
|
"output"
|
|
12111
12282
|
]);
|
|
12112
12283
|
function scanStagingProductionUrls(rootInput, allowPatterns) {
|
|
12113
|
-
const root =
|
|
12284
|
+
const root = path8.resolve(rootInput);
|
|
12114
12285
|
const allowlist = allowPatterns.map((pattern) => new RegExp(pattern));
|
|
12115
12286
|
const violations = [];
|
|
12116
12287
|
function shouldSkipFile(filePath) {
|
|
12117
|
-
const basename2 =
|
|
12118
|
-
const relativePath2 =
|
|
12288
|
+
const basename2 = path8.basename(filePath);
|
|
12289
|
+
const relativePath2 = path8.relative(root, filePath);
|
|
12119
12290
|
if (basename2 === "package-lock.json" || basename2 === "pnpm-lock.yaml" || basename2 === "yarn.lock") return true;
|
|
12120
12291
|
if (basename2 === "topolo.cloudcontrol.json") return true;
|
|
12121
12292
|
if (basename2 === "wrangler.toml" || basename2 === "wrangler.jsonc") return true;
|
|
@@ -12124,15 +12295,15 @@ function scanStagingProductionUrls(rootInput, allowPatterns) {
|
|
|
12124
12295
|
if (basename2.endsWith(".generated.ts")) return true;
|
|
12125
12296
|
if (basename2.endsWith(".test.ts") || basename2.endsWith(".test.tsx") || basename2.endsWith(".spec.ts")) return true;
|
|
12126
12297
|
if (basename2 === ".env.production") return true;
|
|
12127
|
-
if (relativePath2 ===
|
|
12128
|
-
if (relativePath2.includes(`${
|
|
12129
|
-
if (relativePath2.startsWith(`scripts${
|
|
12130
|
-
return !RUNTIME_EXTENSIONS.has(
|
|
12298
|
+
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;
|
|
12299
|
+
if (relativePath2.includes(`${path8.sep}test${path8.sep}`) || relativePath2.includes(`${path8.sep}tests${path8.sep}`) || relativePath2.includes(`${path8.sep}e2e${path8.sep}`)) return true;
|
|
12300
|
+
if (relativePath2.startsWith(`scripts${path8.sep}`) || relativePath2.includes(`${path8.sep}scripts${path8.sep}`)) return true;
|
|
12301
|
+
return !RUNTIME_EXTENSIONS.has(path8.extname(filePath));
|
|
12131
12302
|
}
|
|
12132
12303
|
function scanFile(filePath) {
|
|
12133
12304
|
if (shouldSkipFile(filePath)) return;
|
|
12134
|
-
const relativePath2 =
|
|
12135
|
-
const content =
|
|
12305
|
+
const relativePath2 = path8.relative(root, filePath);
|
|
12306
|
+
const content = readFileSync7(filePath, "utf8");
|
|
12136
12307
|
const lines = content.split(/\r?\n/);
|
|
12137
12308
|
lines.forEach((line, index) => {
|
|
12138
12309
|
const matches = [...line.matchAll(PRODUCTION_TOPOLO_URL_PATTERN)].map((match) => match[0]);
|
|
@@ -12145,8 +12316,8 @@ function scanStagingProductionUrls(rootInput, allowPatterns) {
|
|
|
12145
12316
|
});
|
|
12146
12317
|
}
|
|
12147
12318
|
function walk(dir) {
|
|
12148
|
-
for (const entry of
|
|
12149
|
-
const fullPath =
|
|
12319
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
12320
|
+
const fullPath = path8.join(dir, entry.name);
|
|
12150
12321
|
if (entry.isDirectory()) {
|
|
12151
12322
|
if (!SKIPPED_SCAN_DIRS.has(entry.name)) walk(fullPath);
|
|
12152
12323
|
continue;
|
|
@@ -12314,7 +12485,7 @@ var DEFAULT_SEED_BASE_URL = "https://seed.stg.topolo.us";
|
|
|
12314
12485
|
function seedBaseUrl() {
|
|
12315
12486
|
return process.env.TOPOLO_SEED_URL || DEFAULT_SEED_BASE_URL;
|
|
12316
12487
|
}
|
|
12317
|
-
async function authedFetch(
|
|
12488
|
+
async function authedFetch(path9, init = {}) {
|
|
12318
12489
|
const credential = await refreshCredentialIfNeeded("staging");
|
|
12319
12490
|
if (!credential) {
|
|
12320
12491
|
throw new Error("No staging credential configured. Run `topolo auth login --env staging` or set TOPOLO_STAGING_API_KEY.");
|
|
@@ -12324,7 +12495,7 @@ async function authedFetch(path8, init = {}) {
|
|
|
12324
12495
|
else headers.set("Authorization", `Bearer ${credential.accessToken}`);
|
|
12325
12496
|
headers.set("X-Topolo-Client", `topolo-cli/${CLI_VERSION}`);
|
|
12326
12497
|
if (init.body && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
|
|
12327
|
-
const response = await fetch(`${seedBaseUrl()}${
|
|
12498
|
+
const response = await fetch(`${seedBaseUrl()}${path9}`, { ...init, headers });
|
|
12328
12499
|
const text = await response.text();
|
|
12329
12500
|
let body;
|
|
12330
12501
|
try {
|
|
@@ -12601,7 +12772,7 @@ function createOAuthClientPayload(options) {
|
|
|
12601
12772
|
logo_url: options.logoUrl
|
|
12602
12773
|
};
|
|
12603
12774
|
}
|
|
12604
|
-
async function authFetchJson(
|
|
12775
|
+
async function authFetchJson(path9, init = {}) {
|
|
12605
12776
|
const environment = selectedEnvironment();
|
|
12606
12777
|
const credential = await refreshCredentialIfNeeded(environment);
|
|
12607
12778
|
if (!credential) {
|
|
@@ -12623,7 +12794,7 @@ async function authFetchJson(path8, init = {}) {
|
|
|
12623
12794
|
throw err;
|
|
12624
12795
|
}
|
|
12625
12796
|
headers.set("Authorization", `Bearer ${credential.accessToken}`);
|
|
12626
|
-
const response = await fetch(`${authBaseUrl}${
|
|
12797
|
+
const response = await fetch(`${authBaseUrl}${path9}`, { ...init, headers });
|
|
12627
12798
|
const text = await response.text();
|
|
12628
12799
|
const payload = text ? JSON.parse(text) : {};
|
|
12629
12800
|
if (!response.ok || payload.success === false) {
|
|
@@ -12878,11 +13049,11 @@ function registerViaOfficialCli(cli, mcp, agentName) {
|
|
|
12878
13049
|
return result.status === 0;
|
|
12879
13050
|
}
|
|
12880
13051
|
async function registerClaudeViaConfig(mcp) {
|
|
12881
|
-
const
|
|
13052
|
+
const path9 = join5(homedir2(), ".claude.json");
|
|
12882
13053
|
let config = {};
|
|
12883
|
-
if (existsSync5(
|
|
13054
|
+
if (existsSync5(path9)) {
|
|
12884
13055
|
try {
|
|
12885
|
-
config = JSON.parse(await readFile7(
|
|
13056
|
+
config = JSON.parse(await readFile7(path9, "utf8"));
|
|
12886
13057
|
} catch {
|
|
12887
13058
|
config = {};
|
|
12888
13059
|
}
|
|
@@ -12895,13 +13066,13 @@ async function registerClaudeViaConfig(mcp) {
|
|
|
12895
13066
|
env: { TOPOLO_AGENT_NAME: AGENT_NAME_FOR.claude }
|
|
12896
13067
|
};
|
|
12897
13068
|
config.mcpServers = servers;
|
|
12898
|
-
await writeFile7(
|
|
13069
|
+
await writeFile7(path9, `${JSON.stringify(config, null, 2)}
|
|
12899
13070
|
`, "utf8");
|
|
12900
13071
|
}
|
|
12901
13072
|
async function registerCodexViaConfig(mcp) {
|
|
12902
|
-
const
|
|
13073
|
+
const path9 = join5(homedir2(), ".codex", "config.toml");
|
|
12903
13074
|
let text = "";
|
|
12904
|
-
if (existsSync5(
|
|
13075
|
+
if (existsSync5(path9)) text = await readFile7(path9, "utf8");
|
|
12905
13076
|
text = text.replace(
|
|
12906
13077
|
/(^|\n)\[mcp_servers\.topolo(?:\.[a-z]+)?\][^\[]*/g,
|
|
12907
13078
|
(match) => match.startsWith("\n") ? "\n" : ""
|
|
@@ -12917,8 +13088,8 @@ command = "${mcp.command}"${argsToml}
|
|
|
12917
13088
|
[mcp_servers.topolo.env]
|
|
12918
13089
|
TOPOLO_AGENT_NAME = "${AGENT_NAME_FOR.codex}"
|
|
12919
13090
|
`;
|
|
12920
|
-
await mkdir4(dirname4(
|
|
12921
|
-
await writeFile7(
|
|
13091
|
+
await mkdir4(dirname4(path9), { recursive: true });
|
|
13092
|
+
await writeFile7(path9, `${text}${block}`, "utf8");
|
|
12922
13093
|
}
|
|
12923
13094
|
async function installForAgent(agent, sourceDir, mcp, opts) {
|
|
12924
13095
|
const skillDir = agent === "claude" ? claudeSkillDir(opts.projectDir) : codexSkillDir();
|