@topolo/cli 0.1.23 → 0.1.25
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 +15 -9
- package/dist/index.js +1343 -359
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -67,6 +67,7 @@ var TopoloHttpError = class extends TopoloSdkError {
|
|
|
67
67
|
var SERVICES = {
|
|
68
68
|
agent: "https://agent-api.topolo.app",
|
|
69
69
|
auth: "https://auth.topolo.app",
|
|
70
|
+
backup: "https://backup.topolo.app",
|
|
70
71
|
books: "https://books.topolo.app",
|
|
71
72
|
bugfix: "https://bugfix.topolo.app",
|
|
72
73
|
bytes: "https://bytes-api.topolo.app",
|
|
@@ -77,7 +78,6 @@ var SERVICES = {
|
|
|
77
78
|
compose: "https://compose.topolo.app",
|
|
78
79
|
consent: "https://consent.topolo.app",
|
|
79
80
|
crm: "https://crm-api.topolo.app",
|
|
80
|
-
customer_success: "https://success.topolo.app",
|
|
81
81
|
developers: "https://developers.topolo.app",
|
|
82
82
|
director: "https://director.topolo.app",
|
|
83
83
|
feed: "https://feed-api.topolo.app",
|
|
@@ -91,9 +91,10 @@ var SERVICES = {
|
|
|
91
91
|
localize: "https://localize.topolo.app",
|
|
92
92
|
mail: "https://mail.topolo.app",
|
|
93
93
|
mdm: "https://mdm-api.topolo.app",
|
|
94
|
-
|
|
94
|
+
messages: "https://messages.topolo.app",
|
|
95
95
|
nexus: "https://nexus-api.topolo.app",
|
|
96
96
|
notify: "https://notify.topolo.app",
|
|
97
|
+
observability_watch: "https://observability-watch.topolo.app",
|
|
97
98
|
one: "https://one.topolo.app",
|
|
98
99
|
p2p: "https://p2p.topolo.app",
|
|
99
100
|
pay: "https://pay.topolo.app",
|
|
@@ -107,6 +108,7 @@ var SERVICES = {
|
|
|
107
108
|
socialize: "https://socialize-api.topolo.app",
|
|
108
109
|
spaces: "https://spaces.topolo.app",
|
|
109
110
|
status: "https://status.topolo.app",
|
|
111
|
+
success: "https://success.topolo.app",
|
|
110
112
|
support: "https://support.topolo.app",
|
|
111
113
|
survey: "https://survey.topolo.app",
|
|
112
114
|
voice: "https://voice.topolo.app",
|
|
@@ -123,13 +125,21 @@ var APPLICATIONS = {
|
|
|
123
125
|
"deployTargets": [
|
|
124
126
|
{
|
|
125
127
|
"name": "topolo-admin",
|
|
126
|
-
"kind": "
|
|
127
|
-
"apiUrl":
|
|
128
|
+
"kind": "worker",
|
|
129
|
+
"apiUrl": "https://admin.topolo.app",
|
|
128
130
|
"domains": [
|
|
129
131
|
"admin.topolo.app"
|
|
130
132
|
],
|
|
131
133
|
"routes": [],
|
|
132
134
|
"serviceId": null
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"name": "topolo-admin-api",
|
|
138
|
+
"kind": "worker",
|
|
139
|
+
"apiUrl": null,
|
|
140
|
+
"domains": [],
|
|
141
|
+
"routes": [],
|
|
142
|
+
"serviceId": null
|
|
133
143
|
}
|
|
134
144
|
]
|
|
135
145
|
},
|
|
@@ -146,7 +156,7 @@ var APPLICATIONS = {
|
|
|
146
156
|
{
|
|
147
157
|
"name": "topolo-agent",
|
|
148
158
|
"kind": "worker",
|
|
149
|
-
"apiUrl":
|
|
159
|
+
"apiUrl": "https://agent.topolo.app",
|
|
150
160
|
"domains": [
|
|
151
161
|
"agent.topolo.app"
|
|
152
162
|
],
|
|
@@ -154,7 +164,7 @@ var APPLICATIONS = {
|
|
|
154
164
|
"serviceId": null
|
|
155
165
|
},
|
|
156
166
|
{
|
|
157
|
-
"name": "topolo-agent-
|
|
167
|
+
"name": "topolo-agent-api",
|
|
158
168
|
"kind": "worker",
|
|
159
169
|
"apiUrl": "https://agent-api.topolo.app",
|
|
160
170
|
"domains": [
|
|
@@ -194,16 +204,20 @@ var APPLICATIONS = {
|
|
|
194
204
|
"appDir": "TopoloBackup",
|
|
195
205
|
"name": "topolo-backup",
|
|
196
206
|
"packageName": "topolo-backup",
|
|
197
|
-
"productionUrl":
|
|
198
|
-
"services": [
|
|
207
|
+
"productionUrl": "https://backup.topolo.app",
|
|
208
|
+
"services": [
|
|
209
|
+
"backup"
|
|
210
|
+
],
|
|
199
211
|
"deployTargets": [
|
|
200
212
|
{
|
|
201
213
|
"name": "topolo-backup",
|
|
202
214
|
"kind": "worker",
|
|
203
|
-
"apiUrl":
|
|
204
|
-
"domains": [
|
|
215
|
+
"apiUrl": "https://backup.topolo.app",
|
|
216
|
+
"domains": [
|
|
217
|
+
"backup.topolo.app"
|
|
218
|
+
],
|
|
205
219
|
"routes": [],
|
|
206
|
-
"serviceId":
|
|
220
|
+
"serviceId": "backup"
|
|
207
221
|
}
|
|
208
222
|
]
|
|
209
223
|
},
|
|
@@ -224,10 +238,16 @@ var APPLICATIONS = {
|
|
|
224
238
|
"domains": [
|
|
225
239
|
"books.topolo.app"
|
|
226
240
|
],
|
|
227
|
-
"routes": [
|
|
228
|
-
"books.topolo.app"
|
|
229
|
-
],
|
|
241
|
+
"routes": [],
|
|
230
242
|
"serviceId": "books"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"name": "topolo-books-api",
|
|
246
|
+
"kind": "worker",
|
|
247
|
+
"apiUrl": null,
|
|
248
|
+
"domains": [],
|
|
249
|
+
"routes": [],
|
|
250
|
+
"serviceId": null
|
|
231
251
|
}
|
|
232
252
|
]
|
|
233
253
|
},
|
|
@@ -248,9 +268,7 @@ var APPLICATIONS = {
|
|
|
248
268
|
"domains": [
|
|
249
269
|
"bugfix.topolo.app"
|
|
250
270
|
],
|
|
251
|
-
"routes": [
|
|
252
|
-
"bugfix.topolo.app"
|
|
253
|
-
],
|
|
271
|
+
"routes": [],
|
|
254
272
|
"serviceId": "bugfix"
|
|
255
273
|
}
|
|
256
274
|
]
|
|
@@ -267,8 +285,8 @@ var APPLICATIONS = {
|
|
|
267
285
|
"deployTargets": [
|
|
268
286
|
{
|
|
269
287
|
"name": "topolo-bytes",
|
|
270
|
-
"kind": "
|
|
271
|
-
"apiUrl":
|
|
288
|
+
"kind": "worker",
|
|
289
|
+
"apiUrl": "https://bytes.topolo.app",
|
|
272
290
|
"domains": [
|
|
273
291
|
"bytes.topolo.app"
|
|
274
292
|
],
|
|
@@ -304,10 +322,16 @@ var APPLICATIONS = {
|
|
|
304
322
|
"domains": [
|
|
305
323
|
"calendar.topolo.app"
|
|
306
324
|
],
|
|
307
|
-
"routes": [
|
|
308
|
-
"calendar.topolo.app"
|
|
309
|
-
],
|
|
325
|
+
"routes": [],
|
|
310
326
|
"serviceId": "calendar"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"name": "topolo-calendar-api",
|
|
330
|
+
"kind": "worker",
|
|
331
|
+
"apiUrl": null,
|
|
332
|
+
"domains": [],
|
|
333
|
+
"routes": [],
|
|
334
|
+
"serviceId": null
|
|
311
335
|
}
|
|
312
336
|
]
|
|
313
337
|
},
|
|
@@ -350,22 +374,19 @@ var APPLICATIONS = {
|
|
|
350
374
|
"domains": [
|
|
351
375
|
"chat.topolo.app"
|
|
352
376
|
],
|
|
353
|
-
"routes": [
|
|
354
|
-
"chat.topolo.app"
|
|
355
|
-
],
|
|
377
|
+
"routes": [],
|
|
356
378
|
"serviceId": "chat"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"name": "topolo-chat-api",
|
|
382
|
+
"kind": "worker",
|
|
383
|
+
"apiUrl": null,
|
|
384
|
+
"domains": [],
|
|
385
|
+
"routes": [],
|
|
386
|
+
"serviceId": null
|
|
357
387
|
}
|
|
358
388
|
]
|
|
359
389
|
},
|
|
360
|
-
"cloudcontrol": {
|
|
361
|
-
"id": "cloudcontrol",
|
|
362
|
-
"appDir": "TopoloCloudControl",
|
|
363
|
-
"name": "cloudcontrol",
|
|
364
|
-
"packageName": "topolo-cloudcontrol",
|
|
365
|
-
"productionUrl": null,
|
|
366
|
-
"services": [],
|
|
367
|
-
"deployTargets": []
|
|
368
|
-
},
|
|
369
390
|
"commerce": {
|
|
370
391
|
"id": "commerce",
|
|
371
392
|
"appDir": "TopoloCommerce",
|
|
@@ -388,8 +409,8 @@ var APPLICATIONS = {
|
|
|
388
409
|
},
|
|
389
410
|
{
|
|
390
411
|
"name": "topolo-commerce-guest-web",
|
|
391
|
-
"kind": "
|
|
392
|
-
"apiUrl":
|
|
412
|
+
"kind": "worker",
|
|
413
|
+
"apiUrl": "https://guest.topolo.app",
|
|
393
414
|
"domains": [
|
|
394
415
|
"guest.topolo.app"
|
|
395
416
|
],
|
|
@@ -398,8 +419,8 @@ var APPLICATIONS = {
|
|
|
398
419
|
},
|
|
399
420
|
{
|
|
400
421
|
"name": "topolo-commerce-ops-web",
|
|
401
|
-
"kind": "
|
|
402
|
-
"apiUrl":
|
|
422
|
+
"kind": "worker",
|
|
423
|
+
"apiUrl": "https://commerce.topolo.app",
|
|
403
424
|
"domains": [
|
|
404
425
|
"commerce.topolo.app"
|
|
405
426
|
],
|
|
@@ -408,8 +429,8 @@ var APPLICATIONS = {
|
|
|
408
429
|
},
|
|
409
430
|
{
|
|
410
431
|
"name": "topolo-commerce-station-web",
|
|
411
|
-
"kind": "
|
|
412
|
-
"apiUrl":
|
|
432
|
+
"kind": "worker",
|
|
433
|
+
"apiUrl": "https://station-commerce.topolo.app",
|
|
413
434
|
"domains": [
|
|
414
435
|
"station-commerce.topolo.app"
|
|
415
436
|
],
|
|
@@ -435,10 +456,16 @@ var APPLICATIONS = {
|
|
|
435
456
|
"domains": [
|
|
436
457
|
"compose.topolo.app"
|
|
437
458
|
],
|
|
438
|
-
"routes": [
|
|
439
|
-
"compose.topolo.app"
|
|
440
|
-
],
|
|
459
|
+
"routes": [],
|
|
441
460
|
"serviceId": "compose"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"name": "topolo-compose-api",
|
|
464
|
+
"kind": "worker",
|
|
465
|
+
"apiUrl": null,
|
|
466
|
+
"domains": [],
|
|
467
|
+
"routes": [],
|
|
468
|
+
"serviceId": null
|
|
442
469
|
}
|
|
443
470
|
]
|
|
444
471
|
},
|
|
@@ -459,10 +486,16 @@ var APPLICATIONS = {
|
|
|
459
486
|
"domains": [
|
|
460
487
|
"consent.topolo.app"
|
|
461
488
|
],
|
|
462
|
-
"routes": [
|
|
463
|
-
"consent.topolo.app"
|
|
464
|
-
],
|
|
489
|
+
"routes": [],
|
|
465
490
|
"serviceId": "consent"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"name": "topolo-consent-api",
|
|
494
|
+
"kind": "worker",
|
|
495
|
+
"apiUrl": null,
|
|
496
|
+
"domains": [],
|
|
497
|
+
"routes": [],
|
|
498
|
+
"serviceId": null
|
|
466
499
|
}
|
|
467
500
|
]
|
|
468
501
|
},
|
|
@@ -489,7 +522,7 @@ var APPLICATIONS = {
|
|
|
489
522
|
{
|
|
490
523
|
"name": "topolo-crm-app",
|
|
491
524
|
"kind": "worker",
|
|
492
|
-
"apiUrl":
|
|
525
|
+
"apiUrl": "https://crm.topolo.app",
|
|
493
526
|
"domains": [
|
|
494
527
|
"crm.topolo.app"
|
|
495
528
|
],
|
|
@@ -498,30 +531,6 @@ var APPLICATIONS = {
|
|
|
498
531
|
}
|
|
499
532
|
]
|
|
500
533
|
},
|
|
501
|
-
"customer_success": {
|
|
502
|
-
"id": "customer_success",
|
|
503
|
-
"appDir": "TopoloSuccess",
|
|
504
|
-
"name": "topolo-success",
|
|
505
|
-
"packageName": "topolo-success",
|
|
506
|
-
"productionUrl": "https://success.topolo.app",
|
|
507
|
-
"services": [
|
|
508
|
-
"customer_success"
|
|
509
|
-
],
|
|
510
|
-
"deployTargets": [
|
|
511
|
-
{
|
|
512
|
-
"name": "topolo-success",
|
|
513
|
-
"kind": "worker",
|
|
514
|
-
"apiUrl": "https://success.topolo.app",
|
|
515
|
-
"domains": [
|
|
516
|
-
"success.topolo.app"
|
|
517
|
-
],
|
|
518
|
-
"routes": [
|
|
519
|
-
"success.topolo.app"
|
|
520
|
-
],
|
|
521
|
-
"serviceId": "customer_success"
|
|
522
|
-
}
|
|
523
|
-
]
|
|
524
|
-
},
|
|
525
534
|
"developers": {
|
|
526
535
|
"id": "developers",
|
|
527
536
|
"appDir": "TopoloDevelopers",
|
|
@@ -549,6 +558,14 @@ var APPLICATIONS = {
|
|
|
549
558
|
],
|
|
550
559
|
"routes": [],
|
|
551
560
|
"serviceId": "developers"
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
"name": "topolo-developers-api",
|
|
564
|
+
"kind": "worker",
|
|
565
|
+
"apiUrl": null,
|
|
566
|
+
"domains": [],
|
|
567
|
+
"routes": [],
|
|
568
|
+
"serviceId": null
|
|
552
569
|
}
|
|
553
570
|
]
|
|
554
571
|
},
|
|
@@ -569,10 +586,16 @@ var APPLICATIONS = {
|
|
|
569
586
|
"domains": [
|
|
570
587
|
"director.topolo.app"
|
|
571
588
|
],
|
|
572
|
-
"routes": [
|
|
573
|
-
"director.topolo.app"
|
|
574
|
-
],
|
|
589
|
+
"routes": [],
|
|
575
590
|
"serviceId": "director"
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"name": "topolo-director-api",
|
|
594
|
+
"kind": "worker",
|
|
595
|
+
"apiUrl": null,
|
|
596
|
+
"domains": [],
|
|
597
|
+
"routes": [],
|
|
598
|
+
"serviceId": null
|
|
576
599
|
}
|
|
577
600
|
]
|
|
578
601
|
},
|
|
@@ -586,13 +609,21 @@ var APPLICATIONS = {
|
|
|
586
609
|
"deployTargets": [
|
|
587
610
|
{
|
|
588
611
|
"name": "topolo-docs",
|
|
589
|
-
"kind": "
|
|
590
|
-
"apiUrl":
|
|
612
|
+
"kind": "worker",
|
|
613
|
+
"apiUrl": "https://docs.topolo.app",
|
|
591
614
|
"domains": [
|
|
592
615
|
"docs.topolo.app"
|
|
593
616
|
],
|
|
594
617
|
"routes": [],
|
|
595
618
|
"serviceId": null
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"name": "topolo-docs-api",
|
|
622
|
+
"kind": "worker",
|
|
623
|
+
"apiUrl": null,
|
|
624
|
+
"domains": [],
|
|
625
|
+
"routes": [],
|
|
626
|
+
"serviceId": null
|
|
596
627
|
}
|
|
597
628
|
]
|
|
598
629
|
},
|
|
@@ -601,7 +632,7 @@ var APPLICATIONS = {
|
|
|
601
632
|
"appDir": "TopoloFeed",
|
|
602
633
|
"name": "TopoloFeed",
|
|
603
634
|
"packageName": "topolo-feed",
|
|
604
|
-
"productionUrl": "https://feed
|
|
635
|
+
"productionUrl": "https://feed.topolo.app",
|
|
605
636
|
"services": [
|
|
606
637
|
"feed"
|
|
607
638
|
],
|
|
@@ -613,9 +644,7 @@ var APPLICATIONS = {
|
|
|
613
644
|
"domains": [
|
|
614
645
|
"feed-analytics-api.topolo.app"
|
|
615
646
|
],
|
|
616
|
-
"routes": [
|
|
617
|
-
"feed-analytics-api.topolo.app"
|
|
618
|
-
],
|
|
647
|
+
"routes": [],
|
|
619
648
|
"serviceId": null
|
|
620
649
|
},
|
|
621
650
|
{
|
|
@@ -625,10 +654,18 @@ var APPLICATIONS = {
|
|
|
625
654
|
"domains": [
|
|
626
655
|
"feed-api.topolo.app"
|
|
627
656
|
],
|
|
628
|
-
"routes": [
|
|
629
|
-
"feed-api.topolo.app"
|
|
630
|
-
],
|
|
657
|
+
"routes": [],
|
|
631
658
|
"serviceId": "feed"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
"name": "topolo-feed-web",
|
|
662
|
+
"kind": "worker",
|
|
663
|
+
"apiUrl": "https://feed.topolo.app",
|
|
664
|
+
"domains": [
|
|
665
|
+
"feed.topolo.app"
|
|
666
|
+
],
|
|
667
|
+
"routes": [],
|
|
668
|
+
"serviceId": null
|
|
632
669
|
}
|
|
633
670
|
]
|
|
634
671
|
},
|
|
@@ -649,17 +686,23 @@ var APPLICATIONS = {
|
|
|
649
686
|
"domains": [
|
|
650
687
|
"flow.topolo.app"
|
|
651
688
|
],
|
|
652
|
-
"routes": [
|
|
653
|
-
"flow.topolo.app"
|
|
654
|
-
],
|
|
689
|
+
"routes": [],
|
|
655
690
|
"serviceId": "flow"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
"name": "topolo-flow-api",
|
|
694
|
+
"kind": "worker",
|
|
695
|
+
"apiUrl": null,
|
|
696
|
+
"domains": [],
|
|
697
|
+
"routes": [],
|
|
698
|
+
"serviceId": null
|
|
656
699
|
}
|
|
657
700
|
]
|
|
658
701
|
},
|
|
659
702
|
"forecast": {
|
|
660
703
|
"id": "forecast",
|
|
661
704
|
"appDir": "TopoloForecast",
|
|
662
|
-
"name": "forecast
|
|
705
|
+
"name": "topolo-forecast",
|
|
663
706
|
"packageName": "topolo-forecast",
|
|
664
707
|
"productionUrl": "https://forecast.topolo.app",
|
|
665
708
|
"services": [
|
|
@@ -669,7 +712,7 @@ var APPLICATIONS = {
|
|
|
669
712
|
{
|
|
670
713
|
"name": "topolo-forecast",
|
|
671
714
|
"kind": "worker",
|
|
672
|
-
"apiUrl":
|
|
715
|
+
"apiUrl": "https://forecast.topolo.app",
|
|
673
716
|
"domains": [
|
|
674
717
|
"forecast.topolo.app"
|
|
675
718
|
],
|
|
@@ -705,10 +748,16 @@ var APPLICATIONS = {
|
|
|
705
748
|
"domains": [
|
|
706
749
|
"forms.topolo.app"
|
|
707
750
|
],
|
|
708
|
-
"routes": [
|
|
709
|
-
"forms.topolo.app"
|
|
710
|
-
],
|
|
751
|
+
"routes": [],
|
|
711
752
|
"serviceId": "forms"
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
"name": "topolo-forms-api",
|
|
756
|
+
"kind": "worker",
|
|
757
|
+
"apiUrl": null,
|
|
758
|
+
"domains": [],
|
|
759
|
+
"routes": [],
|
|
760
|
+
"serviceId": null
|
|
712
761
|
}
|
|
713
762
|
]
|
|
714
763
|
},
|
|
@@ -729,10 +778,16 @@ var APPLICATIONS = {
|
|
|
729
778
|
"domains": [
|
|
730
779
|
"home.topolo.app"
|
|
731
780
|
],
|
|
732
|
-
"routes": [
|
|
733
|
-
"home.topolo.app"
|
|
734
|
-
],
|
|
781
|
+
"routes": [],
|
|
735
782
|
"serviceId": "home"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
"name": "topolo-home-api",
|
|
786
|
+
"kind": "worker",
|
|
787
|
+
"apiUrl": null,
|
|
788
|
+
"domains": [],
|
|
789
|
+
"routes": [],
|
|
790
|
+
"serviceId": null
|
|
736
791
|
}
|
|
737
792
|
]
|
|
738
793
|
},
|
|
@@ -753,10 +808,16 @@ var APPLICATIONS = {
|
|
|
753
808
|
"domains": [
|
|
754
809
|
"insights.topolo.app"
|
|
755
810
|
],
|
|
756
|
-
"routes": [
|
|
757
|
-
"insights.topolo.app"
|
|
758
|
-
],
|
|
811
|
+
"routes": [],
|
|
759
812
|
"serviceId": "insights"
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
"name": "topolo-insights-api",
|
|
816
|
+
"kind": "worker",
|
|
817
|
+
"apiUrl": null,
|
|
818
|
+
"domains": [],
|
|
819
|
+
"routes": [],
|
|
820
|
+
"serviceId": null
|
|
760
821
|
}
|
|
761
822
|
]
|
|
762
823
|
},
|
|
@@ -777,10 +838,16 @@ var APPLICATIONS = {
|
|
|
777
838
|
"domains": [
|
|
778
839
|
"inventory.topolo.app"
|
|
779
840
|
],
|
|
780
|
-
"routes": [
|
|
781
|
-
"inventory.topolo.app"
|
|
782
|
-
],
|
|
841
|
+
"routes": [],
|
|
783
842
|
"serviceId": "inventory"
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
"name": "topolo-inventory-api",
|
|
846
|
+
"kind": "worker",
|
|
847
|
+
"apiUrl": null,
|
|
848
|
+
"domains": [],
|
|
849
|
+
"routes": [],
|
|
850
|
+
"serviceId": null
|
|
784
851
|
}
|
|
785
852
|
]
|
|
786
853
|
},
|
|
@@ -797,7 +864,7 @@ var APPLICATIONS = {
|
|
|
797
864
|
{
|
|
798
865
|
"name": "topolo-learn",
|
|
799
866
|
"kind": "worker",
|
|
800
|
-
"apiUrl":
|
|
867
|
+
"apiUrl": "https://learn.topolo.app",
|
|
801
868
|
"domains": [
|
|
802
869
|
"learn.topolo.app"
|
|
803
870
|
],
|
|
@@ -857,10 +924,16 @@ var APPLICATIONS = {
|
|
|
857
924
|
"domains": [
|
|
858
925
|
"mail.topolo.app"
|
|
859
926
|
],
|
|
860
|
-
"routes": [
|
|
861
|
-
"mail.topolo.app"
|
|
862
|
-
],
|
|
927
|
+
"routes": [],
|
|
863
928
|
"serviceId": "mail"
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
"name": "topolo-mail-api",
|
|
932
|
+
"kind": "worker",
|
|
933
|
+
"apiUrl": null,
|
|
934
|
+
"domains": [],
|
|
935
|
+
"routes": [],
|
|
936
|
+
"serviceId": null
|
|
864
937
|
}
|
|
865
938
|
]
|
|
866
939
|
},
|
|
@@ -881,47 +954,51 @@ var APPLICATIONS = {
|
|
|
881
954
|
"domains": [
|
|
882
955
|
"mdm-api.topolo.app"
|
|
883
956
|
],
|
|
884
|
-
"routes": [
|
|
885
|
-
"mdm-api.topolo.app"
|
|
886
|
-
],
|
|
957
|
+
"routes": [],
|
|
887
958
|
"serviceId": "mdm"
|
|
888
959
|
},
|
|
889
960
|
{
|
|
890
961
|
"name": "topolo-mdm-console",
|
|
891
962
|
"kind": "worker",
|
|
892
|
-
"apiUrl":
|
|
963
|
+
"apiUrl": "https://mdm.topolo.app",
|
|
893
964
|
"domains": [
|
|
894
965
|
"mdm.topolo.app"
|
|
895
966
|
],
|
|
896
|
-
"routes": [
|
|
897
|
-
"mdm.topolo.app"
|
|
898
|
-
],
|
|
967
|
+
"routes": [],
|
|
899
968
|
"serviceId": null
|
|
900
969
|
}
|
|
901
970
|
]
|
|
902
971
|
},
|
|
903
|
-
"
|
|
904
|
-
"id": "
|
|
905
|
-
"appDir": "
|
|
906
|
-
"name": "topolo-
|
|
907
|
-
"packageName": "topolo-
|
|
972
|
+
"messages": {
|
|
973
|
+
"id": "messages",
|
|
974
|
+
"appDir": "TopoloMessages",
|
|
975
|
+
"name": "topolo-messages",
|
|
976
|
+
"packageName": "topolo-messages",
|
|
908
977
|
"productionUrl": "https://messages.topolo.app",
|
|
909
978
|
"services": [
|
|
910
|
-
"
|
|
979
|
+
"messages"
|
|
911
980
|
],
|
|
912
981
|
"deployTargets": [
|
|
913
982
|
{
|
|
914
|
-
"name": "topolo-
|
|
983
|
+
"name": "topolo-messages",
|
|
915
984
|
"kind": "worker",
|
|
916
985
|
"apiUrl": "https://messages.topolo.app",
|
|
917
986
|
"domains": [
|
|
918
987
|
"messages.topolo.app"
|
|
919
988
|
],
|
|
920
989
|
"routes": [],
|
|
921
|
-
"serviceId": "
|
|
990
|
+
"serviceId": "messages"
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
"name": "topolo-messages-api",
|
|
994
|
+
"kind": "worker",
|
|
995
|
+
"apiUrl": null,
|
|
996
|
+
"domains": [],
|
|
997
|
+
"routes": [],
|
|
998
|
+
"serviceId": null
|
|
922
999
|
},
|
|
923
1000
|
{
|
|
924
|
-
"name": "topolo-
|
|
1001
|
+
"name": "topolo-messages-dispatch",
|
|
925
1002
|
"kind": "worker",
|
|
926
1003
|
"apiUrl": null,
|
|
927
1004
|
"domains": [],
|
|
@@ -991,9 +1068,22 @@ var APPLICATIONS = {
|
|
|
991
1068
|
"appDir": "TopoloObservabilityWatch",
|
|
992
1069
|
"name": "topolo-observability-watch",
|
|
993
1070
|
"packageName": "topolo-observability-watch",
|
|
994
|
-
"productionUrl":
|
|
995
|
-
"services": [
|
|
996
|
-
|
|
1071
|
+
"productionUrl": "https://observability-watch.topolo.app",
|
|
1072
|
+
"services": [
|
|
1073
|
+
"observability_watch"
|
|
1074
|
+
],
|
|
1075
|
+
"deployTargets": [
|
|
1076
|
+
{
|
|
1077
|
+
"name": "topolo-observability-watch",
|
|
1078
|
+
"kind": "worker",
|
|
1079
|
+
"apiUrl": "https://observability-watch.topolo.app",
|
|
1080
|
+
"domains": [
|
|
1081
|
+
"observability-watch.topolo.app"
|
|
1082
|
+
],
|
|
1083
|
+
"routes": [],
|
|
1084
|
+
"serviceId": "observability_watch"
|
|
1085
|
+
}
|
|
1086
|
+
]
|
|
997
1087
|
},
|
|
998
1088
|
"one": {
|
|
999
1089
|
"id": "one",
|
|
@@ -1007,40 +1097,37 @@ var APPLICATIONS = {
|
|
|
1007
1097
|
"deployTargets": [
|
|
1008
1098
|
{
|
|
1009
1099
|
"name": "topolo-one",
|
|
1010
|
-
"kind": "pages",
|
|
1011
|
-
"apiUrl": null,
|
|
1012
|
-
"domains": [
|
|
1013
|
-
"one.topolo.app"
|
|
1014
|
-
],
|
|
1015
|
-
"routes": [],
|
|
1016
|
-
"serviceId": null
|
|
1017
|
-
},
|
|
1018
|
-
{
|
|
1019
|
-
"name": "topolo-one-website",
|
|
1020
1100
|
"kind": "worker",
|
|
1021
1101
|
"apiUrl": null,
|
|
1022
|
-
"domains": [
|
|
1023
|
-
"topolo.io",
|
|
1024
|
-
"www.topolo.io"
|
|
1025
|
-
],
|
|
1102
|
+
"domains": [],
|
|
1026
1103
|
"routes": [],
|
|
1027
1104
|
"serviceId": null
|
|
1028
1105
|
},
|
|
1029
1106
|
{
|
|
1030
|
-
"name": "topolo-one-
|
|
1107
|
+
"name": "topolo-one-api",
|
|
1031
1108
|
"kind": "worker",
|
|
1032
1109
|
"apiUrl": "https://one.topolo.app",
|
|
1033
1110
|
"domains": [
|
|
1034
|
-
"one.topolo.app"
|
|
1035
|
-
"topolo.app",
|
|
1036
|
-
"www.topolo.app"
|
|
1111
|
+
"one.topolo.app"
|
|
1037
1112
|
],
|
|
1038
1113
|
"routes": [
|
|
1039
1114
|
"one.topolo.app/*",
|
|
1040
|
-
"topolo.app",
|
|
1041
1115
|
"www.topolo.app/*"
|
|
1042
1116
|
],
|
|
1043
1117
|
"serviceId": "one"
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
"name": "topolo-one-website",
|
|
1121
|
+
"kind": "worker",
|
|
1122
|
+
"apiUrl": "https://topolo.io",
|
|
1123
|
+
"domains": [
|
|
1124
|
+
"topolo.io"
|
|
1125
|
+
],
|
|
1126
|
+
"routes": [
|
|
1127
|
+
"topolo.io",
|
|
1128
|
+
"www.topolo.io"
|
|
1129
|
+
],
|
|
1130
|
+
"serviceId": null
|
|
1044
1131
|
}
|
|
1045
1132
|
]
|
|
1046
1133
|
},
|
|
@@ -1061,10 +1148,16 @@ var APPLICATIONS = {
|
|
|
1061
1148
|
"domains": [
|
|
1062
1149
|
"p2p.topolo.app"
|
|
1063
1150
|
],
|
|
1064
|
-
"routes": [
|
|
1065
|
-
"p2p.topolo.app"
|
|
1066
|
-
],
|
|
1151
|
+
"routes": [],
|
|
1067
1152
|
"serviceId": "p2p"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
"name": "topolo-p2p-api",
|
|
1156
|
+
"kind": "worker",
|
|
1157
|
+
"apiUrl": null,
|
|
1158
|
+
"domains": [],
|
|
1159
|
+
"routes": [],
|
|
1160
|
+
"serviceId": null
|
|
1068
1161
|
}
|
|
1069
1162
|
]
|
|
1070
1163
|
},
|
|
@@ -1087,6 +1180,14 @@ var APPLICATIONS = {
|
|
|
1087
1180
|
],
|
|
1088
1181
|
"routes": [],
|
|
1089
1182
|
"serviceId": "pay"
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
"name": "topolo-pay-api",
|
|
1186
|
+
"kind": "worker",
|
|
1187
|
+
"apiUrl": null,
|
|
1188
|
+
"domains": [],
|
|
1189
|
+
"routes": [],
|
|
1190
|
+
"serviceId": null
|
|
1090
1191
|
}
|
|
1091
1192
|
]
|
|
1092
1193
|
},
|
|
@@ -1107,10 +1208,16 @@ var APPLICATIONS = {
|
|
|
1107
1208
|
"domains": [
|
|
1108
1209
|
"people.topolo.app"
|
|
1109
1210
|
],
|
|
1110
|
-
"routes": [
|
|
1111
|
-
"people.topolo.app"
|
|
1112
|
-
],
|
|
1211
|
+
"routes": [],
|
|
1113
1212
|
"serviceId": "people"
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
"name": "topolo-people-api",
|
|
1216
|
+
"kind": "worker",
|
|
1217
|
+
"apiUrl": null,
|
|
1218
|
+
"domains": [],
|
|
1219
|
+
"routes": [],
|
|
1220
|
+
"serviceId": null
|
|
1114
1221
|
}
|
|
1115
1222
|
]
|
|
1116
1223
|
},
|
|
@@ -1131,10 +1238,16 @@ var APPLICATIONS = {
|
|
|
1131
1238
|
"domains": [
|
|
1132
1239
|
"pulse.topolo.app"
|
|
1133
1240
|
],
|
|
1134
|
-
"routes": [
|
|
1135
|
-
"pulse.topolo.app"
|
|
1136
|
-
],
|
|
1241
|
+
"routes": [],
|
|
1137
1242
|
"serviceId": "pulse"
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
"name": "topolo-pulse-api",
|
|
1246
|
+
"kind": "worker",
|
|
1247
|
+
"apiUrl": null,
|
|
1248
|
+
"domains": [],
|
|
1249
|
+
"routes": [],
|
|
1250
|
+
"serviceId": null
|
|
1138
1251
|
}
|
|
1139
1252
|
]
|
|
1140
1253
|
},
|
|
@@ -1156,7 +1269,9 @@ var APPLICATIONS = {
|
|
|
1156
1269
|
"domains": [
|
|
1157
1270
|
"api.ol0.me"
|
|
1158
1271
|
],
|
|
1159
|
-
"routes": [
|
|
1272
|
+
"routes": [
|
|
1273
|
+
"api.ol0.me/*"
|
|
1274
|
+
],
|
|
1160
1275
|
"serviceId": "quro.api"
|
|
1161
1276
|
},
|
|
1162
1277
|
{
|
|
@@ -1174,7 +1289,7 @@ var APPLICATIONS = {
|
|
|
1174
1289
|
{
|
|
1175
1290
|
"name": "quro-ui",
|
|
1176
1291
|
"kind": "worker",
|
|
1177
|
-
"apiUrl":
|
|
1292
|
+
"apiUrl": "https://quro.topolo.app",
|
|
1178
1293
|
"domains": [
|
|
1179
1294
|
"quro.topolo.app"
|
|
1180
1295
|
],
|
|
@@ -1196,7 +1311,7 @@ var APPLICATIONS = {
|
|
|
1196
1311
|
{
|
|
1197
1312
|
"name": "topolo-roadmapper",
|
|
1198
1313
|
"kind": "worker",
|
|
1199
|
-
"apiUrl":
|
|
1314
|
+
"apiUrl": "https://roadmapper.topolo.app",
|
|
1200
1315
|
"domains": [
|
|
1201
1316
|
"roadmapper.topolo.app"
|
|
1202
1317
|
],
|
|
@@ -1232,10 +1347,16 @@ var APPLICATIONS = {
|
|
|
1232
1347
|
"domains": [
|
|
1233
1348
|
"sign.topolo.app"
|
|
1234
1349
|
],
|
|
1235
|
-
"routes": [
|
|
1236
|
-
"sign.topolo.app"
|
|
1237
|
-
],
|
|
1350
|
+
"routes": [],
|
|
1238
1351
|
"serviceId": "sign"
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
"name": "topolo-sign-api",
|
|
1355
|
+
"kind": "worker",
|
|
1356
|
+
"apiUrl": null,
|
|
1357
|
+
"domains": [],
|
|
1358
|
+
"routes": [],
|
|
1359
|
+
"serviceId": null
|
|
1239
1360
|
}
|
|
1240
1361
|
]
|
|
1241
1362
|
},
|
|
@@ -1252,7 +1373,7 @@ var APPLICATIONS = {
|
|
|
1252
1373
|
{
|
|
1253
1374
|
"name": "topolo-social-studio",
|
|
1254
1375
|
"kind": "worker",
|
|
1255
|
-
"apiUrl":
|
|
1376
|
+
"apiUrl": "https://studio.topolo.app",
|
|
1256
1377
|
"domains": [
|
|
1257
1378
|
"studio.topolo.app"
|
|
1258
1379
|
],
|
|
@@ -1291,8 +1412,8 @@ var APPLICATIONS = {
|
|
|
1291
1412
|
"deployTargets": [
|
|
1292
1413
|
{
|
|
1293
1414
|
"name": "topolo-socialize",
|
|
1294
|
-
"kind": "
|
|
1295
|
-
"apiUrl":
|
|
1415
|
+
"kind": "worker",
|
|
1416
|
+
"apiUrl": "https://socialize.topolo.app",
|
|
1296
1417
|
"domains": [
|
|
1297
1418
|
"socialize.topolo.app"
|
|
1298
1419
|
],
|
|
@@ -1336,10 +1457,16 @@ var APPLICATIONS = {
|
|
|
1336
1457
|
"domains": [
|
|
1337
1458
|
"spaces.topolo.app"
|
|
1338
1459
|
],
|
|
1339
|
-
"routes": [
|
|
1340
|
-
"spaces.topolo.app"
|
|
1341
|
-
],
|
|
1460
|
+
"routes": [],
|
|
1342
1461
|
"serviceId": "spaces"
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
"name": "topolo-spaces-api",
|
|
1465
|
+
"kind": "worker",
|
|
1466
|
+
"apiUrl": null,
|
|
1467
|
+
"domains": [],
|
|
1468
|
+
"routes": [],
|
|
1469
|
+
"serviceId": null
|
|
1343
1470
|
}
|
|
1344
1471
|
]
|
|
1345
1472
|
},
|
|
@@ -1360,10 +1487,40 @@ var APPLICATIONS = {
|
|
|
1360
1487
|
"domains": [
|
|
1361
1488
|
"status.topolo.app"
|
|
1362
1489
|
],
|
|
1490
|
+
"routes": [],
|
|
1491
|
+
"serviceId": "status"
|
|
1492
|
+
}
|
|
1493
|
+
]
|
|
1494
|
+
},
|
|
1495
|
+
"success": {
|
|
1496
|
+
"id": "success",
|
|
1497
|
+
"appDir": "TopoloSuccess",
|
|
1498
|
+
"name": "topolo-success",
|
|
1499
|
+
"packageName": "topolo-success",
|
|
1500
|
+
"productionUrl": "https://success.topolo.app",
|
|
1501
|
+
"services": [
|
|
1502
|
+
"success"
|
|
1503
|
+
],
|
|
1504
|
+
"deployTargets": [
|
|
1505
|
+
{
|
|
1506
|
+
"name": "topolo-success",
|
|
1507
|
+
"kind": "worker",
|
|
1508
|
+
"apiUrl": "https://success.topolo.app",
|
|
1509
|
+
"domains": [
|
|
1510
|
+
"success.topolo.app"
|
|
1511
|
+
],
|
|
1363
1512
|
"routes": [
|
|
1364
|
-
"
|
|
1513
|
+
"success.topolo.app"
|
|
1365
1514
|
],
|
|
1366
|
-
"serviceId": "
|
|
1515
|
+
"serviceId": "success"
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
"name": "topolo-success-api",
|
|
1519
|
+
"kind": "worker",
|
|
1520
|
+
"apiUrl": null,
|
|
1521
|
+
"domains": [],
|
|
1522
|
+
"routes": [],
|
|
1523
|
+
"serviceId": null
|
|
1367
1524
|
}
|
|
1368
1525
|
]
|
|
1369
1526
|
},
|
|
@@ -1388,6 +1545,14 @@ var APPLICATIONS = {
|
|
|
1388
1545
|
"support.topolo.app/*"
|
|
1389
1546
|
],
|
|
1390
1547
|
"serviceId": "support"
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
"name": "topolo-support-api",
|
|
1551
|
+
"kind": "worker",
|
|
1552
|
+
"apiUrl": null,
|
|
1553
|
+
"domains": [],
|
|
1554
|
+
"routes": [],
|
|
1555
|
+
"serviceId": null
|
|
1391
1556
|
}
|
|
1392
1557
|
]
|
|
1393
1558
|
},
|
|
@@ -1408,10 +1573,16 @@ var APPLICATIONS = {
|
|
|
1408
1573
|
"domains": [
|
|
1409
1574
|
"survey.topolo.app"
|
|
1410
1575
|
],
|
|
1411
|
-
"routes": [
|
|
1412
|
-
"survey.topolo.app"
|
|
1413
|
-
],
|
|
1576
|
+
"routes": [],
|
|
1414
1577
|
"serviceId": "survey"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
"name": "topolo-survey-api",
|
|
1581
|
+
"kind": "worker",
|
|
1582
|
+
"apiUrl": null,
|
|
1583
|
+
"domains": [],
|
|
1584
|
+
"routes": [],
|
|
1585
|
+
"serviceId": null
|
|
1415
1586
|
}
|
|
1416
1587
|
]
|
|
1417
1588
|
},
|
|
@@ -1432,10 +1603,16 @@ var APPLICATIONS = {
|
|
|
1432
1603
|
"domains": [
|
|
1433
1604
|
"voice.topolo.app"
|
|
1434
1605
|
],
|
|
1435
|
-
"routes": [
|
|
1436
|
-
"voice.topolo.app"
|
|
1437
|
-
],
|
|
1606
|
+
"routes": [],
|
|
1438
1607
|
"serviceId": "voice"
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
"name": "topolo-voice-api",
|
|
1611
|
+
"kind": "worker",
|
|
1612
|
+
"apiUrl": null,
|
|
1613
|
+
"domains": [],
|
|
1614
|
+
"routes": [],
|
|
1615
|
+
"serviceId": null
|
|
1439
1616
|
}
|
|
1440
1617
|
]
|
|
1441
1618
|
},
|
|
@@ -1466,13 +1643,16 @@ var APPLICATIONS = {
|
|
|
1466
1643
|
"domains": [
|
|
1467
1644
|
"sites.topolo.app"
|
|
1468
1645
|
],
|
|
1469
|
-
"routes": [
|
|
1646
|
+
"routes": [
|
|
1647
|
+
"*.sites.topolo.app/*",
|
|
1648
|
+
"sites.topolo.app"
|
|
1649
|
+
],
|
|
1470
1650
|
"serviceId": null
|
|
1471
1651
|
},
|
|
1472
1652
|
{
|
|
1473
1653
|
"name": "topolo-web-studio",
|
|
1474
1654
|
"kind": "worker",
|
|
1475
|
-
"apiUrl":
|
|
1655
|
+
"apiUrl": "https://web.topolo.app",
|
|
1476
1656
|
"domains": [
|
|
1477
1657
|
"web.topolo.app"
|
|
1478
1658
|
],
|
|
@@ -1522,12 +1702,12 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1522
1702
|
summary: "Applications must derive identity, organization, scopes, and service entitlement from TopoloAuth rather than inventing parallel auth state.",
|
|
1523
1703
|
appliesTo: ["browser", "api", "tooling", "agent_surface"],
|
|
1524
1704
|
evidence: [
|
|
1525
|
-
"@topolo/auth-client or @topolo/
|
|
1705
|
+
"@topolo/auth-client or @topolo-io/worker-runtime is used where applicable",
|
|
1526
1706
|
"No public command, tool, or SDK method accepts orgId",
|
|
1527
1707
|
"Service slug and Auth service registration are documented for protected services"
|
|
1528
1708
|
],
|
|
1529
1709
|
implementation: [
|
|
1530
|
-
"Use shared auth packages instead of copying token parsing or browser storage flows",
|
|
1710
|
+
"Use shared auth/runtime packages instead of copying token parsing, gateway, or browser storage flows",
|
|
1531
1711
|
"Derive organization from the credential on every request",
|
|
1532
1712
|
"Route OAuth, refresh, logout, and SSO paths according to the shared Auth contract"
|
|
1533
1713
|
]
|
|
@@ -1560,11 +1740,13 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1560
1740
|
evidence: [
|
|
1561
1741
|
"Auth service catalog contains the service ID and slug",
|
|
1562
1742
|
"Permissions, role bundles, and API-key scopes exist for the service",
|
|
1563
|
-
"SDK, CLI, and MCP service discovery include the callable production API"
|
|
1743
|
+
"SDK, CLI, and MCP service discovery include the callable production API",
|
|
1744
|
+
"CloudControl production HTTP targets classify agent reachability with sdk_service_id or agent_callable:false"
|
|
1564
1745
|
],
|
|
1565
1746
|
implementation: [
|
|
1566
|
-
"Add production Worker api_url metadata
|
|
1567
|
-
"
|
|
1747
|
+
"Add production Worker api_url metadata and sdk_service_id for every agent-callable API target",
|
|
1748
|
+
"Mark browser, documentation, and runtime-only Worker targets with agent_callable:false",
|
|
1749
|
+
"Expose stable service IDs through generated DEFAULT_SERVICE_URLS and live catalog discovery",
|
|
1568
1750
|
"Keep typed SDK/CLI/MCP surfaces in sync when an API contract stabilizes"
|
|
1569
1751
|
]
|
|
1570
1752
|
},
|
|
@@ -1668,8 +1850,10 @@ var APPLICATION_REQUIREMENTS = [
|
|
|
1668
1850
|
function applicationRequirementScopes(application) {
|
|
1669
1851
|
const scopes = /* @__PURE__ */ new Set(["all"]);
|
|
1670
1852
|
const appId = application.id;
|
|
1671
|
-
const hasBrowserSurface = Boolean(application.productionUrl) ||
|
|
1672
|
-
|
|
1853
|
+
const hasBrowserSurface = Boolean(application.productionUrl) || // String() keeps this guard valid after the Pages->Worker migration narrowed
|
|
1854
|
+
// the generated kind union to "worker" (no "pages" literal remains).
|
|
1855
|
+
application.deployTargets.some((target) => String(target.kind) === "pages");
|
|
1856
|
+
const hasApiSurface = application.services.length > 0 || application.deployTargets.some((target) => Boolean(target.serviceId));
|
|
1673
1857
|
if (hasBrowserSurface) scopes.add("browser");
|
|
1674
1858
|
if (hasApiSurface) scopes.add("api");
|
|
1675
1859
|
if (appId === "cli" || appId === "mcp" || appId === "cloudcontrol") {
|
|
@@ -1786,7 +1970,7 @@ function evaluateRequirement(application, requirement) {
|
|
|
1786
1970
|
evidence
|
|
1787
1971
|
);
|
|
1788
1972
|
case "cloudflare-deployment-contract": {
|
|
1789
|
-
if (application.deployTargets.length
|
|
1973
|
+
if (application.deployTargets.length < 1) {
|
|
1790
1974
|
return finding(requirement, "missing", "No CloudControl deploy targets are registered.", evidence);
|
|
1791
1975
|
}
|
|
1792
1976
|
evidence.push(`deployTargets=${application.deployTargets.length}`);
|
|
@@ -1873,6 +2057,7 @@ function resolveServiceUrl(service, overrides) {
|
|
|
1873
2057
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
1874
2058
|
var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1875
2059
|
var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
|
|
2060
|
+
var CATALOG_CACHE_TTL_MS = 60 * 1e3;
|
|
1876
2061
|
var TopoloClient = class _TopoloClient {
|
|
1877
2062
|
credential;
|
|
1878
2063
|
agent;
|
|
@@ -1882,6 +2067,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1882
2067
|
timeoutMs;
|
|
1883
2068
|
fetchImpl;
|
|
1884
2069
|
debug;
|
|
2070
|
+
serviceCatalogCache = null;
|
|
1885
2071
|
/**
|
|
1886
2072
|
* Process-wide cache for slug → service-id lookups. Keyed by
|
|
1887
2073
|
* `${authBaseUrl}|${slug}` so multiple `TopoloClient` instances pointing at
|
|
@@ -1927,7 +2113,8 @@ var TopoloClient = class _TopoloClient {
|
|
|
1927
2113
|
`Mutating ${method} requests require { confirm: true }. This guardrail prevents agents from issuing writes without an explicit human-in-the-loop acknowledgement.`
|
|
1928
2114
|
);
|
|
1929
2115
|
}
|
|
1930
|
-
const
|
|
2116
|
+
const resolvedService = await this.resolveRequestService(opts.service);
|
|
2117
|
+
const baseUrl = resolvedService.baseUrl;
|
|
1931
2118
|
const url = new URL(opts.path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
1932
2119
|
if (opts.query) {
|
|
1933
2120
|
for (const [k, v] of Object.entries(opts.query)) {
|
|
@@ -1937,7 +2124,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1937
2124
|
const headers = new Headers();
|
|
1938
2125
|
headers.set("Accept", "application/json");
|
|
1939
2126
|
if (opts.body !== void 0) headers.set("Content-Type", "application/json");
|
|
1940
|
-
const platformServiceId = resolveConfiguredServiceId(
|
|
2127
|
+
const platformServiceId = resolveConfiguredServiceId(resolvedService.serviceKey, this.serviceIds);
|
|
1941
2128
|
if (platformServiceId) headers.set("X-Service-ID", platformServiceId);
|
|
1942
2129
|
applyAuthHeaders(headers, this.credential);
|
|
1943
2130
|
const requestId = generateRequestId();
|
|
@@ -1950,7 +2137,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1950
2137
|
const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
|
|
1951
2138
|
const urlStr = url.toString();
|
|
1952
2139
|
const startedAt = Date.now();
|
|
1953
|
-
this.emit({ phase: "request", method, service:
|
|
2140
|
+
this.emit({ phase: "request", method, service: resolvedService.serviceKey, path: opts.path, url: urlStr, requestId });
|
|
1954
2141
|
let res;
|
|
1955
2142
|
try {
|
|
1956
2143
|
res = await this.fetchImpl(urlStr, {
|
|
@@ -1963,7 +2150,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1963
2150
|
this.emit({
|
|
1964
2151
|
phase: "error",
|
|
1965
2152
|
method,
|
|
1966
|
-
service:
|
|
2153
|
+
service: resolvedService.serviceKey,
|
|
1967
2154
|
path: opts.path,
|
|
1968
2155
|
url: urlStr,
|
|
1969
2156
|
requestId,
|
|
@@ -1982,7 +2169,7 @@ var TopoloClient = class _TopoloClient {
|
|
|
1982
2169
|
this.emit({
|
|
1983
2170
|
phase: "error",
|
|
1984
2171
|
method,
|
|
1985
|
-
service:
|
|
2172
|
+
service: resolvedService.serviceKey,
|
|
1986
2173
|
path: opts.path,
|
|
1987
2174
|
url: urlStr,
|
|
1988
2175
|
requestId,
|
|
@@ -1997,12 +2184,12 @@ var TopoloClient = class _TopoloClient {
|
|
|
1997
2184
|
extractRequired(parsed)
|
|
1998
2185
|
);
|
|
1999
2186
|
}
|
|
2000
|
-
throw new TopoloHttpError(
|
|
2187
|
+
throw new TopoloHttpError(resolvedService.serviceKey, opts.path, res.status, parsed);
|
|
2001
2188
|
}
|
|
2002
2189
|
this.emit({
|
|
2003
2190
|
phase: "response",
|
|
2004
2191
|
method,
|
|
2005
|
-
service:
|
|
2192
|
+
service: resolvedService.serviceKey,
|
|
2006
2193
|
path: opts.path,
|
|
2007
2194
|
url: urlStr,
|
|
2008
2195
|
requestId,
|
|
@@ -2011,6 +2198,32 @@ var TopoloClient = class _TopoloClient {
|
|
|
2011
2198
|
});
|
|
2012
2199
|
return parsed;
|
|
2013
2200
|
}
|
|
2201
|
+
async resolveRequestService(service) {
|
|
2202
|
+
const serviceKey = normalizeLookupKey(service);
|
|
2203
|
+
if (!serviceKey) {
|
|
2204
|
+
throw new TopoloSdkError("unknown_service", "service is required");
|
|
2205
|
+
}
|
|
2206
|
+
if (serviceKey === "auth") {
|
|
2207
|
+
const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
|
|
2208
|
+
if (overrideUrl) return { serviceKey, baseUrl: overrideUrl };
|
|
2209
|
+
return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS.auth };
|
|
2210
|
+
}
|
|
2211
|
+
const live = await this.resolveLiveCallableService(serviceKey);
|
|
2212
|
+
if (live) {
|
|
2213
|
+
const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
|
|
2214
|
+
return overrideUrl ? { ...live, baseUrl: overrideUrl } : live;
|
|
2215
|
+
}
|
|
2216
|
+
throw new TopoloSdkError(
|
|
2217
|
+
"unknown_service",
|
|
2218
|
+
`Unknown Topolo service "${service}". Run service discovery first and pass a callable service id or slug.`
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
async resolveLiveCallableService(serviceKey) {
|
|
2222
|
+
const catalog = await this.listServices();
|
|
2223
|
+
const match = catalog.find((entry) => catalogEntryMatches(entry, serviceKey));
|
|
2224
|
+
if (!match?.baseUrl || !isCallableCatalogEntry(match)) return null;
|
|
2225
|
+
return { serviceKey, baseUrl: match.baseUrl };
|
|
2226
|
+
}
|
|
2014
2227
|
/**
|
|
2015
2228
|
* Resolve a service-slug → opaque service-id via caller config or TopoloAuth.
|
|
2016
2229
|
*
|
|
@@ -2068,6 +2281,71 @@ var TopoloClient = class _TopoloClient {
|
|
|
2068
2281
|
});
|
|
2069
2282
|
return value;
|
|
2070
2283
|
}
|
|
2284
|
+
/**
|
|
2285
|
+
* List the platform service catalog from the live registry
|
|
2286
|
+
* (`GET /api/services/catalog` on TopoloAuth). Credential-aware: the
|
|
2287
|
+
* caller's credential is attached, so the result only includes services the
|
|
2288
|
+
* credential's organization and effective permissions can use.
|
|
2289
|
+
*
|
|
2290
|
+
* Security boundary: this never falls back to the generated global snapshot.
|
|
2291
|
+
* The live Auth catalog is the authority because it is scoped to the caller's
|
|
2292
|
+
* organization and effective permissions.
|
|
2293
|
+
*/
|
|
2294
|
+
async listServices() {
|
|
2295
|
+
const now = Date.now();
|
|
2296
|
+
if (this.serviceCatalogCache && this.serviceCatalogCache.expiresAt > now) {
|
|
2297
|
+
return this.serviceCatalogCache.entries;
|
|
2298
|
+
}
|
|
2299
|
+
const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
|
|
2300
|
+
const raw = res.data?.services ?? res.services;
|
|
2301
|
+
if (!Array.isArray(raw)) {
|
|
2302
|
+
throw new TopoloSdkError(
|
|
2303
|
+
"service_catalog_unavailable",
|
|
2304
|
+
"TopoloAuth did not return a service catalog for the current credential."
|
|
2305
|
+
);
|
|
2306
|
+
}
|
|
2307
|
+
const entries = [];
|
|
2308
|
+
for (const item of raw) {
|
|
2309
|
+
const s = item;
|
|
2310
|
+
const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
|
|
2311
|
+
if (!serviceId) continue;
|
|
2312
|
+
const permissions = Array.isArray(s.granted_permissions) ? s.granted_permissions.filter((permission) => typeof permission === "string" && permission.trim().length > 0) : [];
|
|
2313
|
+
entries.push({
|
|
2314
|
+
serviceId,
|
|
2315
|
+
slug: typeof s.app_slug === "string" ? s.app_slug : null,
|
|
2316
|
+
name: typeof s.name === "string" ? s.name : null,
|
|
2317
|
+
description: typeof s.description === "string" ? s.description : null,
|
|
2318
|
+
baseUrl: typeof s.base_url === "string" ? s.base_url : null,
|
|
2319
|
+
status: typeof s.status === "string" ? s.status : null,
|
|
2320
|
+
surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
|
|
2321
|
+
launchable: s.launchable === true,
|
|
2322
|
+
permissions
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
this.serviceCatalogCache = {
|
|
2326
|
+
entries,
|
|
2327
|
+
expiresAt: now + CATALOG_CACHE_TTL_MS
|
|
2328
|
+
};
|
|
2329
|
+
return entries;
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* Bundled generated registry for tooling that audits platform packaging, not
|
|
2333
|
+
* for deciding what the current credential may use.
|
|
2334
|
+
*/
|
|
2335
|
+
listBundledServices() {
|
|
2336
|
+
const apps = Object.values(APPLICATIONS);
|
|
2337
|
+
return apps.map((app) => ({
|
|
2338
|
+
serviceId: app.id,
|
|
2339
|
+
slug: app.id,
|
|
2340
|
+
name: app.name ?? app.id,
|
|
2341
|
+
description: null,
|
|
2342
|
+
baseUrl: app.productionUrl ?? null,
|
|
2343
|
+
status: "bundled",
|
|
2344
|
+
surfaceKind: "application",
|
|
2345
|
+
launchable: Boolean(app.productionUrl),
|
|
2346
|
+
permissions: []
|
|
2347
|
+
}));
|
|
2348
|
+
}
|
|
2071
2349
|
/**
|
|
2072
2350
|
* Introspect the current credential. Returns the caller's resolved identity,
|
|
2073
2351
|
* organization, and permission set. Used by CLI `whoami` and by MCP to gate
|
|
@@ -2114,8 +2392,18 @@ function resolveConfiguredServiceId(slug, serviceIds) {
|
|
|
2114
2392
|
}
|
|
2115
2393
|
return null;
|
|
2116
2394
|
}
|
|
2395
|
+
function resolveConfiguredServiceUrl(slug, serviceUrls) {
|
|
2396
|
+
for (const serviceKey of serviceKeyCandidates(slug)) {
|
|
2397
|
+
const configured = serviceUrls?.[serviceKey];
|
|
2398
|
+
if (typeof configured === "string" && configured.trim()) return configured.trim();
|
|
2399
|
+
const envKey = `TOPOLO_SERVICE_URL_${serviceKey.replace(/\./g, "_").toUpperCase()}`;
|
|
2400
|
+
const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
|
|
2401
|
+
if (typeof envValue === "string" && envValue.trim()) return envValue.trim();
|
|
2402
|
+
}
|
|
2403
|
+
return null;
|
|
2404
|
+
}
|
|
2117
2405
|
function serviceKeyCandidates(slug) {
|
|
2118
|
-
const normalized = slug
|
|
2406
|
+
const normalized = normalizeLookupKey(slug);
|
|
2119
2407
|
const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
|
|
2120
2408
|
return [.../* @__PURE__ */ new Set([
|
|
2121
2409
|
normalized,
|
|
@@ -2124,6 +2412,31 @@ function serviceKeyCandidates(slug) {
|
|
|
2124
2412
|
withoutTopolo.replace(/-/g, "_")
|
|
2125
2413
|
])];
|
|
2126
2414
|
}
|
|
2415
|
+
function normalizeLookupKey(value) {
|
|
2416
|
+
return String(value || "").trim().toLowerCase().replace(/\s+/g, "-");
|
|
2417
|
+
}
|
|
2418
|
+
function catalogEntryMatches(entry, needle) {
|
|
2419
|
+
return catalogEntryLookupKeys(entry).includes(needle);
|
|
2420
|
+
}
|
|
2421
|
+
function catalogEntryLookupKeys(entry) {
|
|
2422
|
+
const values = [entry.serviceId, entry.slug, entry.name].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
2423
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2424
|
+
for (const value of values) {
|
|
2425
|
+
const normalized = normalizeLookupKey(value);
|
|
2426
|
+
if (!normalized) continue;
|
|
2427
|
+
for (const candidate of serviceKeyCandidates(normalized)) {
|
|
2428
|
+
keys.add(candidate);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
return [...keys];
|
|
2432
|
+
}
|
|
2433
|
+
function isCallableCatalogEntry(entry) {
|
|
2434
|
+
const kind = normalizeLookupKey(entry.surfaceKind || "");
|
|
2435
|
+
if (kind === "api" || kind === "technical_service" || kind === "service") return true;
|
|
2436
|
+
return Boolean(
|
|
2437
|
+
entry.baseUrl && catalogEntryLookupKeys(entry).some((key) => Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, key))
|
|
2438
|
+
);
|
|
2439
|
+
}
|
|
2127
2440
|
function describeError(body, fallback) {
|
|
2128
2441
|
if (body && typeof body === "object") {
|
|
2129
2442
|
const maybe = body;
|
|
@@ -2961,12 +3274,12 @@ function formatContact(c) {
|
|
|
2961
3274
|
}
|
|
2962
3275
|
|
|
2963
3276
|
// src/commands/api.ts
|
|
2964
|
-
var
|
|
3277
|
+
var STATIC_SERVICE_IDS = new Set(Object.keys(DEFAULT_SERVICE_URLS));
|
|
2965
3278
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
2966
3279
|
var WRITE_METHODS2 = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
2967
3280
|
function registerApiCommand(program2) {
|
|
2968
3281
|
program2.command("api <service> <method> <path>").description(
|
|
2969
|
-
|
|
3282
|
+
"Generic passthrough to a Topolo service. Run `topolo services --json` to discover callable services."
|
|
2970
3283
|
).option("-d, --data <json>", "JSON body (for POST/PUT/PATCH)").option(
|
|
2971
3284
|
"-q, --query <kv...>",
|
|
2972
3285
|
"Query params as key=value (repeatable), e.g. --query source=cli page=2"
|
|
@@ -2981,22 +3294,17 @@ function registerApiCommand(program2) {
|
|
|
2981
3294
|
);
|
|
2982
3295
|
}
|
|
2983
3296
|
const httpMethod = upperMethod;
|
|
2984
|
-
if (!KNOWN_SERVICES.includes(service)) {
|
|
2985
|
-
throw new Error(
|
|
2986
|
-
`Unknown service "${service}". Known services: ${KNOWN_SERVICES.join(", ")}`
|
|
2987
|
-
);
|
|
2988
|
-
}
|
|
2989
3297
|
if (WRITE_METHODS2.has(httpMethod) && !opts.confirm) {
|
|
2990
3298
|
throw new Error(`Mutating ${httpMethod} requires --confirm.`);
|
|
2991
3299
|
}
|
|
2992
3300
|
const environment = selectedEnvironment();
|
|
2993
3301
|
const config = await readConfig();
|
|
2994
3302
|
const profile = resolvedProfileConfig(config, environment);
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
environment
|
|
2999
|
-
|
|
3303
|
+
const configuredUrl = resolveConfiguredServiceUrl2(service, profile.serviceUrls);
|
|
3304
|
+
const staticUrl = STATIC_SERVICE_IDS.has(service) ? DEFAULT_SERVICE_URLS[service] : void 0;
|
|
3305
|
+
if (configuredUrl || staticUrl) {
|
|
3306
|
+
assertServiceUrlForEnvironment(service, configuredUrl ?? staticUrl, environment);
|
|
3307
|
+
}
|
|
3000
3308
|
let body = void 0;
|
|
3001
3309
|
if (opts.data) {
|
|
3002
3310
|
try {
|
|
@@ -3027,6 +3335,21 @@ function registerApiCommand(program2) {
|
|
|
3027
3335
|
}
|
|
3028
3336
|
);
|
|
3029
3337
|
}
|
|
3338
|
+
function resolveConfiguredServiceUrl2(service, serviceUrls) {
|
|
3339
|
+
const normalized = service.trim().toLowerCase();
|
|
3340
|
+
const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
|
|
3341
|
+
const candidates = [
|
|
3342
|
+
normalized,
|
|
3343
|
+
normalized.replace(/-/g, "_"),
|
|
3344
|
+
withoutTopolo,
|
|
3345
|
+
withoutTopolo.replace(/-/g, "_")
|
|
3346
|
+
];
|
|
3347
|
+
for (const candidate of candidates) {
|
|
3348
|
+
const url = serviceUrls?.[candidate];
|
|
3349
|
+
if (typeof url === "string" && url.trim()) return url.trim();
|
|
3350
|
+
}
|
|
3351
|
+
return void 0;
|
|
3352
|
+
}
|
|
3030
3353
|
function parseQueryPairs(pairs) {
|
|
3031
3354
|
if (!pairs || pairs.length === 0) return void 0;
|
|
3032
3355
|
const out = {};
|
|
@@ -3056,8 +3379,8 @@ import {
|
|
|
3056
3379
|
} from "fs";
|
|
3057
3380
|
import path from "path";
|
|
3058
3381
|
var EVIDENCE_FRESHNESS_MS = 36 * 60 * 60 * 1e3;
|
|
3059
|
-
var REQUIRED_AUTH_CLIENT_VERSION = "1.1.
|
|
3060
|
-
var REQUIRED_UI_KIT_VERSION = "0.4.
|
|
3382
|
+
var REQUIRED_AUTH_CLIENT_VERSION = "1.1.9";
|
|
3383
|
+
var REQUIRED_UI_KIT_VERSION = "0.4.67";
|
|
3061
3384
|
function readEvidenceFile(appRoot) {
|
|
3062
3385
|
const candidate = path.join(appRoot, ".topolo", "evidence.json");
|
|
3063
3386
|
if (!isFile(candidate)) return null;
|
|
@@ -3264,11 +3587,12 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3264
3587
|
case "service-registration-and-scopes": {
|
|
3265
3588
|
const declaredServices = application.services ?? [];
|
|
3266
3589
|
if (declaredServices.length === 0) {
|
|
3267
|
-
|
|
3590
|
+
const applicationId = String(application.id);
|
|
3591
|
+
if (applicationId === "cloudcontrol") {
|
|
3268
3592
|
return mergeFinding(finding2, {
|
|
3269
3593
|
status: "met",
|
|
3270
3594
|
reason: "This application is itself a tooling/agent surface and does not register a callable platform service.",
|
|
3271
|
-
evidence: [`tooling=${
|
|
3595
|
+
evidence: [`tooling=${applicationId}`],
|
|
3272
3596
|
nextActions: []
|
|
3273
3597
|
});
|
|
3274
3598
|
}
|
|
@@ -3442,6 +3766,7 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3442
3766
|
case "agent-and-operator-surfaces": {
|
|
3443
3767
|
const declaredServices = application.services ?? [];
|
|
3444
3768
|
const text = evidence.source.text;
|
|
3769
|
+
const applicationId = String(application.id);
|
|
3445
3770
|
const items = [];
|
|
3446
3771
|
if (declaredServices.length > 0) items.push(`services=${declaredServices.join(",")}`);
|
|
3447
3772
|
if (text.includes("TopoloClient")) items.push("source=TopoloClient");
|
|
@@ -3449,11 +3774,11 @@ function enrichFinding(application, finding2, evidence) {
|
|
|
3449
3774
|
items.push("source=confirm-write-gate");
|
|
3450
3775
|
}
|
|
3451
3776
|
if (text.includes("@topolo/sdk")) items.push("source=@topolo/sdk");
|
|
3452
|
-
if (
|
|
3777
|
+
if (applicationId === "cloudcontrol") {
|
|
3453
3778
|
return mergeFinding(finding2, {
|
|
3454
3779
|
status: "met",
|
|
3455
3780
|
reason: "This application is itself an agent/operator surface (CloudControl).",
|
|
3456
|
-
evidence: [`tooling=${
|
|
3781
|
+
evidence: [`tooling=${applicationId}`, ...items],
|
|
3457
3782
|
nextActions: []
|
|
3458
3783
|
});
|
|
3459
3784
|
}
|
|
@@ -4231,6 +4556,8 @@ function scaffoldFiles(options) {
|
|
|
4231
4556
|
const { application, target } = options;
|
|
4232
4557
|
const slug = toSlug(application.id);
|
|
4233
4558
|
const base = target === "platform" ? firstPartyAppPath(application.appDir, options.payload.audience) : application.appDir;
|
|
4559
|
+
const browserBase = target === "platform" ? `${base}/apps/web` : base;
|
|
4560
|
+
const apiBase = target === "platform" ? `${base}/apps/api` : base;
|
|
4234
4561
|
const files = [
|
|
4235
4562
|
{
|
|
4236
4563
|
path: `${base}/package.json`,
|
|
@@ -4277,10 +4604,10 @@ function scaffoldFiles(options) {
|
|
|
4277
4604
|
"uses strict mode",
|
|
4278
4605
|
"includes browser DOM types for React and Worker types for API-backed apps"
|
|
4279
4606
|
],
|
|
4280
|
-
content: tsconfigJson(options.api)
|
|
4607
|
+
content: tsconfigJson(options.api, target)
|
|
4281
4608
|
},
|
|
4282
4609
|
{
|
|
4283
|
-
path: `${
|
|
4610
|
+
path: `${browserBase}/vite.config.ts`,
|
|
4284
4611
|
purpose: "Vite browser build configuration.",
|
|
4285
4612
|
requiredSignals: [
|
|
4286
4613
|
"builds the authenticated browser surface",
|
|
@@ -4289,7 +4616,7 @@ function scaffoldFiles(options) {
|
|
|
4289
4616
|
content: viteConfig(target)
|
|
4290
4617
|
},
|
|
4291
4618
|
{
|
|
4292
|
-
path: `${
|
|
4619
|
+
path: `${browserBase}/index.html`,
|
|
4293
4620
|
purpose: "Browser entry document.",
|
|
4294
4621
|
requiredSignals: [
|
|
4295
4622
|
"mounts the React app at #root",
|
|
@@ -4298,7 +4625,7 @@ function scaffoldFiles(options) {
|
|
|
4298
4625
|
content: indexHtml(application.name)
|
|
4299
4626
|
},
|
|
4300
4627
|
{
|
|
4301
|
-
path: `${
|
|
4628
|
+
path: `${browserBase}/src/main.tsx`,
|
|
4302
4629
|
purpose: "React app entrypoint.",
|
|
4303
4630
|
requiredSignals: [
|
|
4304
4631
|
"renders the generated app component",
|
|
@@ -4307,7 +4634,7 @@ function scaffoldFiles(options) {
|
|
|
4307
4634
|
content: mainTsx()
|
|
4308
4635
|
},
|
|
4309
4636
|
{
|
|
4310
|
-
path: `${
|
|
4637
|
+
path: `${browserBase}/src/env.d.ts`,
|
|
4311
4638
|
purpose: "Vite environment variable typing.",
|
|
4312
4639
|
requiredSignals: [
|
|
4313
4640
|
"loads vite/client import.meta.env types",
|
|
@@ -4327,7 +4654,7 @@ function scaffoldFiles(options) {
|
|
|
4327
4654
|
}
|
|
4328
4655
|
] : [],
|
|
4329
4656
|
{
|
|
4330
|
-
path: `${
|
|
4657
|
+
path: `${browserBase}/src/styles.css`,
|
|
4331
4658
|
purpose: "Minimal app-local styles outside the shared Topolo shell.",
|
|
4332
4659
|
requiredSignals: [
|
|
4333
4660
|
"does not fork the shared shell chrome",
|
|
@@ -4338,6 +4665,19 @@ function scaffoldFiles(options) {
|
|
|
4338
4665
|
];
|
|
4339
4666
|
if (target === "platform") {
|
|
4340
4667
|
files.push(
|
|
4668
|
+
{
|
|
4669
|
+
path: `${base}/topolo.app.json`,
|
|
4670
|
+
purpose: "Canonical app manifest that drives generated Wrangler and CloudControl metadata.",
|
|
4671
|
+
requiredSignals: [
|
|
4672
|
+
"declares web/api targets with canonical names and app-local target directories",
|
|
4673
|
+
"keeps Cloudflare account, domain, service id, and bindings in one manifest source of truth"
|
|
4674
|
+
],
|
|
4675
|
+
content: topoloAppManifestJson(application, {
|
|
4676
|
+
api: options.api,
|
|
4677
|
+
resources: options.payload.resources,
|
|
4678
|
+
profile: options.payload.profile
|
|
4679
|
+
})
|
|
4680
|
+
},
|
|
4341
4681
|
{
|
|
4342
4682
|
path: `${base}/topolo.cloudcontrol.json`,
|
|
4343
4683
|
purpose: "CloudControl source of truth for deploy targets, routes, bindings, and CI metadata.",
|
|
@@ -4463,7 +4803,7 @@ function scaffoldFiles(options) {
|
|
|
4463
4803
|
if (options.browser) {
|
|
4464
4804
|
if (target === "platform") {
|
|
4465
4805
|
files.push({
|
|
4466
|
-
path: `${
|
|
4806
|
+
path: `${browserBase}/src/service-identity.ts`,
|
|
4467
4807
|
purpose: "Central service identity resolver shared by browser and Worker runtime surfaces.",
|
|
4468
4808
|
requiredSignals: [
|
|
4469
4809
|
"exports the generated app id, display name, and stable Auth service slug",
|
|
@@ -4473,7 +4813,7 @@ function scaffoldFiles(options) {
|
|
|
4473
4813
|
});
|
|
4474
4814
|
}
|
|
4475
4815
|
files.push({
|
|
4476
|
-
path: `${
|
|
4816
|
+
path: `${browserBase}/src/App.tsx`,
|
|
4477
4817
|
purpose: "Authenticated browser surface.",
|
|
4478
4818
|
requiredSignals: [
|
|
4479
4819
|
"imports shared TopoloAppShell from @topolo-io/ui-kit",
|
|
@@ -4490,16 +4830,16 @@ function scaffoldFiles(options) {
|
|
|
4490
4830
|
if (options.api) {
|
|
4491
4831
|
files.push(
|
|
4492
4832
|
{
|
|
4493
|
-
path: `${
|
|
4833
|
+
path: `${apiBase}/wrangler.toml`,
|
|
4494
4834
|
purpose: "Cloudflare Worker runtime configuration.",
|
|
4495
4835
|
requiredSignals: [
|
|
4496
4836
|
"bindings match topolo.cloudcontrol.json or topolo.app.json",
|
|
4497
4837
|
"secrets are referenced by name only"
|
|
4498
4838
|
],
|
|
4499
|
-
content: wranglerToml(application, target, options.payload.resources, options.payload.profile)
|
|
4839
|
+
content: target === "platform" ? platformApiWranglerToml(application, options.payload.resources, options.payload.profile) : wranglerToml(application, target, options.payload.resources, options.payload.profile)
|
|
4500
4840
|
},
|
|
4501
4841
|
{
|
|
4502
|
-
path: `${
|
|
4842
|
+
path: `${apiBase}/src/worker.ts`,
|
|
4503
4843
|
purpose: "Protected service boundary.",
|
|
4504
4844
|
requiredSignals: [
|
|
4505
4845
|
"uses @topolo-io/worker-runtime for Worker auth, gatewaying, headers, and request helpers",
|
|
@@ -4514,6 +4854,37 @@ function scaffoldFiles(options) {
|
|
|
4514
4854
|
})
|
|
4515
4855
|
}
|
|
4516
4856
|
);
|
|
4857
|
+
if (target === "platform") {
|
|
4858
|
+
files.push(
|
|
4859
|
+
{
|
|
4860
|
+
path: `${apiBase}/src/service-identity.ts`,
|
|
4861
|
+
purpose: "Runtime service identity resolver for the API Worker.",
|
|
4862
|
+
requiredSignals: [
|
|
4863
|
+
"resolves the concrete Auth service id from Worker vars",
|
|
4864
|
+
"keeps the API Worker independent from browser-only import.meta usage"
|
|
4865
|
+
],
|
|
4866
|
+
content: apiServiceIdentityTs(application)
|
|
4867
|
+
},
|
|
4868
|
+
{
|
|
4869
|
+
path: `${browserBase}/src/worker.ts`,
|
|
4870
|
+
purpose: "Browser Worker that serves assets and forwards API paths to the API Worker binding.",
|
|
4871
|
+
requiredSignals: [
|
|
4872
|
+
"keeps the frontend Worker thin",
|
|
4873
|
+
"routes /api/* to the API service binding instead of owning backend routes locally"
|
|
4874
|
+
],
|
|
4875
|
+
content: webWorkerTs(application)
|
|
4876
|
+
},
|
|
4877
|
+
{
|
|
4878
|
+
path: `${browserBase}/wrangler.toml`,
|
|
4879
|
+
purpose: "Cloudflare Worker static-assets configuration for the browser surface.",
|
|
4880
|
+
requiredSignals: [
|
|
4881
|
+
"serves Vite assets through a Worker with assets",
|
|
4882
|
+
"binds the frontend Worker to the API Worker for /api/* requests"
|
|
4883
|
+
],
|
|
4884
|
+
content: platformWebWranglerToml(application)
|
|
4885
|
+
}
|
|
4886
|
+
);
|
|
4887
|
+
}
|
|
4517
4888
|
}
|
|
4518
4889
|
return files;
|
|
4519
4890
|
}
|
|
@@ -4543,20 +4914,22 @@ function topoloConformanceWorkflow(options) {
|
|
|
4543
4914
|
const docsCheckoutStep = options.appDir === "TopoloDocs" ? [] : [
|
|
4544
4915
|
"",
|
|
4545
4916
|
" - name: Checkout TopoloDocs",
|
|
4917
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4546
4918
|
" uses: actions/checkout@v4",
|
|
4547
4919
|
" with:",
|
|
4548
|
-
" repository: Topolo-io/
|
|
4920
|
+
" repository: Topolo-io/TopoloDocs",
|
|
4549
4921
|
" path: Apps/system/TopoloDocs",
|
|
4550
|
-
" token: ${{
|
|
4922
|
+
" token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}"
|
|
4551
4923
|
];
|
|
4552
4924
|
const cliCheckoutStep = options.appDir === "TopoloCLI" || options.appDir === "TopoloCli" ? [] : [
|
|
4553
4925
|
"",
|
|
4554
4926
|
" - name: Checkout Topolo CLI",
|
|
4927
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4555
4928
|
" uses: actions/checkout@v4",
|
|
4556
4929
|
" with:",
|
|
4557
4930
|
" repository: Topolo-io/TopoloCli",
|
|
4558
4931
|
" path: Apps/system/TopoloCLI",
|
|
4559
|
-
" token: ${{
|
|
4932
|
+
" token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}"
|
|
4560
4933
|
];
|
|
4561
4934
|
return [
|
|
4562
4935
|
"name: Topolo App Conformance",
|
|
@@ -4574,44 +4947,66 @@ function topoloConformanceWorkflow(options) {
|
|
|
4574
4947
|
"jobs:",
|
|
4575
4948
|
" conformance:",
|
|
4576
4949
|
" runs-on: ubuntu-latest",
|
|
4950
|
+
" env:",
|
|
4951
|
+
` HAS_TOPOLO_PACKAGES_APP: \${{ secrets.TOPOLO_PACKAGES_APP_ID != '' && secrets.TOPOLO_PACKAGES_APP_PRIVATE_KEY != '' }}`,
|
|
4952
|
+
` HAS_TOPOLO_CI_REPO_TOKEN: \${{ secrets.TOPOLO_CI_REPO_TOKEN != '' }}`,
|
|
4577
4953
|
" steps:",
|
|
4578
4954
|
" - name: Checkout application",
|
|
4579
4955
|
" uses: actions/checkout@v4",
|
|
4580
4956
|
" with:",
|
|
4581
4957
|
` path: ${appPath}`,
|
|
4958
|
+
"",
|
|
4959
|
+
" - name: Mint Topolo cross-repo token",
|
|
4960
|
+
" id: app-token",
|
|
4961
|
+
` if: env.HAS_TOPOLO_PACKAGES_APP == 'true'`,
|
|
4962
|
+
" uses: actions/create-github-app-token@v1",
|
|
4963
|
+
" with:",
|
|
4964
|
+
` app-id: \${{ secrets.TOPOLO_PACKAGES_APP_ID }}`,
|
|
4965
|
+
` private-key: \${{ secrets.TOPOLO_PACKAGES_APP_PRIVATE_KEY }}`,
|
|
4966
|
+
" owner: Topolo-io",
|
|
4582
4967
|
...docsCheckoutStep,
|
|
4583
4968
|
"",
|
|
4584
4969
|
" - name: Checkout Topolo SDK",
|
|
4970
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4585
4971
|
" uses: actions/checkout@v4",
|
|
4586
4972
|
" with:",
|
|
4587
4973
|
" repository: Topolo-io/topolo-sdk",
|
|
4588
4974
|
" path: Apps/packages/topolo-sdk",
|
|
4589
|
-
" token: ${{
|
|
4975
|
+
" token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}",
|
|
4590
4976
|
...cliCheckoutStep,
|
|
4591
4977
|
"",
|
|
4978
|
+
" - name: Skip conformance without cross-repo token",
|
|
4979
|
+
` if: steps.app-token.outputs.token == '' && env.HAS_TOPOLO_CI_REPO_TOKEN != 'true'`,
|
|
4980
|
+
" run: |",
|
|
4981
|
+
' echo "::warning::Skipping Topolo app conformance because no Topolo cross-repo token is available to this workflow context."',
|
|
4982
|
+
"",
|
|
4592
4983
|
" - name: Setup Node.js",
|
|
4984
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4593
4985
|
" uses: actions/setup-node@v4",
|
|
4594
4986
|
" with:",
|
|
4595
4987
|
" node-version: 20",
|
|
4596
4988
|
"",
|
|
4597
4989
|
" - name: Build Topolo SDK",
|
|
4990
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4598
4991
|
" working-directory: Apps/packages/topolo-sdk",
|
|
4599
4992
|
" run: |",
|
|
4600
4993
|
" npm install --no-audit --no-fund",
|
|
4601
4994
|
" npm run build",
|
|
4602
4995
|
"",
|
|
4603
4996
|
" - name: Build Topolo CLI",
|
|
4997
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4604
4998
|
" working-directory: Apps/system/TopoloCLI",
|
|
4605
4999
|
" run: |",
|
|
4606
5000
|
" npm install --no-audit --no-fund",
|
|
4607
5001
|
" npm run build",
|
|
4608
5002
|
"",
|
|
4609
5003
|
" - name: Run Topolo app conformance audit",
|
|
5004
|
+
` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
|
|
4610
5005
|
" run: |",
|
|
4611
5006
|
` node Apps/system/TopoloCLI/dist/index.js apps audit ${options.id} --local --repo-root "$GITHUB_WORKSPACE" --fail-on needs_review --json`,
|
|
4612
5007
|
"",
|
|
4613
5008
|
" - name: Record green CI evidence",
|
|
4614
|
-
` if: \${{ success() }}`,
|
|
5009
|
+
` if: \${{ success() && (steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true') }}`,
|
|
4615
5010
|
` working-directory: ${appPath}`,
|
|
4616
5011
|
" run: |",
|
|
4617
5012
|
` node "$GITHUB_WORKSPACE/Apps/system/TopoloCLI/dist/index.js" apps record-evidence ${options.id} --build --typecheck --test --commit "$GITHUB_SHA"`
|
|
@@ -4625,9 +5020,9 @@ function packageJson(application, target, options) {
|
|
|
4625
5020
|
vite: "^5.4.0"
|
|
4626
5021
|
};
|
|
4627
5022
|
const scripts = {
|
|
4628
|
-
dev: "vite --host 0.0.0.0",
|
|
4629
|
-
build: "vite build",
|
|
4630
|
-
preview: "vite preview --host 0.0.0.0",
|
|
5023
|
+
dev: target === "platform" ? "vite --config apps/web/vite.config.ts --host 0.0.0.0" : "vite --host 0.0.0.0",
|
|
5024
|
+
build: target === "platform" ? "vite build --config apps/web/vite.config.ts" : "vite build",
|
|
5025
|
+
preview: target === "platform" ? "vite preview --config apps/web/vite.config.ts --host 0.0.0.0" : "vite preview --host 0.0.0.0",
|
|
4631
5026
|
typecheck: "tsc --noEmit"
|
|
4632
5027
|
};
|
|
4633
5028
|
if (options.browser) {
|
|
@@ -4639,27 +5034,30 @@ function packageJson(application, target, options) {
|
|
|
4639
5034
|
if (target === "platform") {
|
|
4640
5035
|
dependencies["@topolo-io/auth-client"] = topoloPackageVersion("@topolo-io/auth-client", target);
|
|
4641
5036
|
dependencies["@topolo-io/design-tokens"] = topoloPackageVersion("@topolo-io/design-tokens", target);
|
|
5037
|
+
dependencies["@topolo-io/onboarding"] = topoloPackageVersion("@topolo-io/onboarding", target);
|
|
5038
|
+
dependencies["@topolo-io/tour"] = topoloPackageVersion("@topolo-io/tour", target);
|
|
4642
5039
|
dependencies["@topolo-io/ui-kit"] = topoloPackageVersion("@topolo-io/ui-kit", target);
|
|
4643
5040
|
} else {
|
|
4644
5041
|
dependencies["@topolo/login"] = topoloPackageVersion("@topolo/login", target);
|
|
4645
5042
|
}
|
|
4646
5043
|
}
|
|
4647
5044
|
if (options.api) {
|
|
5045
|
+
devDependencies["@cloudflare/workers-types"] = "^4.20260413.1";
|
|
4648
5046
|
if (target === "platform") {
|
|
4649
5047
|
dependencies["@topolo-io/worker-runtime"] = topoloPackageVersion("@topolo-io/worker-runtime", target);
|
|
4650
|
-
dependencies["@topolo-io/widget-server"] = topoloPackageVersion("@topolo-io/widget-server", target);
|
|
4651
5048
|
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
|
|
5049
|
+
dependencies.hono = "^4";
|
|
4652
5050
|
scripts.deploy = `node -e 'console.error("\\nX Bare deploy is disabled. Use deploy:staging or deploy:production.\\n"); process.exit(1)'`;
|
|
4653
|
-
scripts["deploy:staging"] = "DEPLOY_ENVIRONMENT=staging VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build && wrangler deploy --env staging";
|
|
4654
|
-
scripts["deploy:production"] = "DEPLOY_ENVIRONMENT=production VITE_TOPOLO_AUTH_URL=https://auth.topolo.app npm run build && wrangler deploy";
|
|
5051
|
+
scripts["deploy:staging"] = "DEPLOY_ENVIRONMENT=staging VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build && wrangler deploy --config apps/api/wrangler.toml --env staging && wrangler deploy --config apps/web/wrangler.toml --env staging";
|
|
5052
|
+
scripts["deploy:production"] = "DEPLOY_ENVIRONMENT=production VITE_TOPOLO_AUTH_URL=https://auth.topolo.app npm run build && wrangler deploy --config apps/api/wrangler.toml --env production && wrangler deploy --config apps/web/wrangler.toml --env production";
|
|
4655
5053
|
} else {
|
|
4656
|
-
dependencies["@topolo
|
|
5054
|
+
dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
|
|
4657
5055
|
scripts.deploy = "npm run build && wrangler deploy";
|
|
4658
5056
|
}
|
|
4659
5057
|
devDependencies.wrangler = "^4.40.2";
|
|
4660
5058
|
}
|
|
4661
5059
|
if (target === "platform" && hasScaffoldResources(options.resources)) {
|
|
4662
|
-
scripts["check:deploy-config"] = "wrangler deploy --dry-run --outdir .wrangler/build";
|
|
5060
|
+
scripts["check:deploy-config"] = "wrangler deploy --config apps/api/wrangler.toml --env staging --dry-run --outdir .wrangler/api-build && wrangler deploy --config apps/web/wrangler.toml --env staging --dry-run --outdir .wrangler/web-build";
|
|
4663
5061
|
}
|
|
4664
5062
|
return `${json({
|
|
4665
5063
|
name: application.packageName,
|
|
@@ -4676,7 +5074,7 @@ function topoloPackageVersion(packageName, target) {
|
|
|
4676
5074
|
if (target === "developer") {
|
|
4677
5075
|
if (packageName === "@topolo/login") return "^0.1.0";
|
|
4678
5076
|
if (packageName === "@topolo-io/auth-client") return "^1.0.0";
|
|
4679
|
-
if (packageName === "@topolo
|
|
5077
|
+
if (packageName === "@topolo/sdk") return "^0.4.1";
|
|
4680
5078
|
return "^0.1.0";
|
|
4681
5079
|
}
|
|
4682
5080
|
const platformPackageFolders = {
|
|
@@ -4686,8 +5084,9 @@ function topoloPackageVersion(packageName, target) {
|
|
|
4686
5084
|
"@topolo-io/auth-middleware": "topolo-auth-middleware",
|
|
4687
5085
|
"@topolo-io/worker-runtime": "topolo-worker-runtime",
|
|
4688
5086
|
"@topolo-io/design-tokens": "topolo-design-tokens",
|
|
4689
|
-
"@topolo-io/
|
|
4690
|
-
"@topolo-io/
|
|
5087
|
+
"@topolo-io/onboarding": "topolo-onboarding",
|
|
5088
|
+
"@topolo-io/tour": "topolo-tour",
|
|
5089
|
+
"@topolo-io/ui-kit": "topolo-ui-kit"
|
|
4691
5090
|
};
|
|
4692
5091
|
const folder = platformPackageFolders[packageName];
|
|
4693
5092
|
if (!folder) {
|
|
@@ -4772,7 +5171,7 @@ function readme(application, target, options) {
|
|
|
4772
5171
|
"- `vite.config.ts` includes the standard React plugin plus React runtime dedupe so linked Topolo packages resolve one browser React runtime."
|
|
4773
5172
|
);
|
|
4774
5173
|
if (options.api) {
|
|
4775
|
-
lines.push("- `@topolo-io/worker-runtime` owns Worker auth validation, `/api/auth/*` gatewaying, security headers, request ids, and BugFix reporting.");
|
|
5174
|
+
lines.push("- `@topolo-io/worker-runtime` owns Worker auth validation, `/api/auth/*` gatewaying, security headers, request ids, Nexus API-call usage monitoring, and BugFix reporting.");
|
|
4776
5175
|
}
|
|
4777
5176
|
if (hasScaffoldResources(options.resources)) {
|
|
4778
5177
|
lines.push(
|
|
@@ -4816,7 +5215,8 @@ function readme(application, target, options) {
|
|
|
4816
5215
|
return `${lines.join("\n")}
|
|
4817
5216
|
`;
|
|
4818
5217
|
}
|
|
4819
|
-
function tsconfigJson(api) {
|
|
5218
|
+
function tsconfigJson(api, target) {
|
|
5219
|
+
const include = target === "platform" ? ["apps/web/src", ...api ? ["apps/api/src"] : []] : ["src"];
|
|
4820
5220
|
return `${json({
|
|
4821
5221
|
compilerOptions: {
|
|
4822
5222
|
target: "ES2022",
|
|
@@ -4834,9 +5234,9 @@ function tsconfigJson(api) {
|
|
|
4834
5234
|
isolatedModules: true,
|
|
4835
5235
|
noEmit: true,
|
|
4836
5236
|
jsx: "react-jsx",
|
|
4837
|
-
types: ["vite/client"]
|
|
5237
|
+
types: api ? ["vite/client", "@cloudflare/workers-types"] : ["vite/client"]
|
|
4838
5238
|
},
|
|
4839
|
-
include
|
|
5239
|
+
include,
|
|
4840
5240
|
references: []
|
|
4841
5241
|
})}
|
|
4842
5242
|
`;
|
|
@@ -4948,7 +5348,33 @@ function serviceIdentityTs(application) {
|
|
|
4948
5348
|
" const serviceId = typeof value === 'string' ? value.trim() : '';",
|
|
4949
5349
|
" if (!serviceId) {",
|
|
4950
5350
|
" throw new Error(`SERVICE_ID is required for ${SCAFFOLD_SERVICE_SLUG}`);",
|
|
4951
|
-
" }",
|
|
5351
|
+
" }",
|
|
5352
|
+
" return serviceId;",
|
|
5353
|
+
"}",
|
|
5354
|
+
"",
|
|
5355
|
+
"export function resolveRuntimeServiceId(env: ServiceIdentityEnv): Promise<string> {",
|
|
5356
|
+
" return Promise.resolve(requireServiceId(env.SERVICE_ID));",
|
|
5357
|
+
"}",
|
|
5358
|
+
""
|
|
5359
|
+
].join("\n");
|
|
5360
|
+
}
|
|
5361
|
+
function apiServiceIdentityTs(application) {
|
|
5362
|
+
const appName = toTitle(application.name);
|
|
5363
|
+
const serviceSlug = application.serviceSlug ?? `topolo-${toSlug(application.id)}`;
|
|
5364
|
+
return [
|
|
5365
|
+
`export const SCAFFOLD_APP_ID = '${application.id}';`,
|
|
5366
|
+
`export const SCAFFOLD_APP_NAME = '${escapeSingleQuoted(appName)}';`,
|
|
5367
|
+
`export const SCAFFOLD_SERVICE_SLUG = '${escapeSingleQuoted(serviceSlug)}';`,
|
|
5368
|
+
"",
|
|
5369
|
+
"interface ServiceIdentityEnv {",
|
|
5370
|
+
" SERVICE_ID?: string;",
|
|
5371
|
+
"}",
|
|
5372
|
+
"",
|
|
5373
|
+
"function requireServiceId(value: unknown): string {",
|
|
5374
|
+
" const serviceId = typeof value === 'string' ? value.trim() : '';",
|
|
5375
|
+
" if (!serviceId) {",
|
|
5376
|
+
" throw new Error(`SERVICE_ID is required for ${SCAFFOLD_SERVICE_SLUG}`);",
|
|
5377
|
+
" }",
|
|
4952
5378
|
" return serviceId;",
|
|
4953
5379
|
"}",
|
|
4954
5380
|
"",
|
|
@@ -5025,10 +5451,11 @@ function viteConfig(target) {
|
|
|
5025
5451
|
" process.env.VITE_TOPOLO_AUTH_URL === 'https://auth.stg.topolo.us';",
|
|
5026
5452
|
"",
|
|
5027
5453
|
" return {",
|
|
5454
|
+
" root: fileURLToPath(new URL('.', import.meta.url)),",
|
|
5028
5455
|
" plugins: [react(), ...(isStagingBuild ? [stagingUrlRewrite()] : [])],",
|
|
5029
5456
|
" resolve: {",
|
|
5030
5457
|
" alias: {",
|
|
5031
|
-
" '@topolo-io/auth-client': fileURLToPath(new URL('
|
|
5458
|
+
" '@topolo-io/auth-client': fileURLToPath(new URL('../../node_modules/@topolo-io/auth-client/dist/index.js', import.meta.url)),",
|
|
5032
5459
|
" },",
|
|
5033
5460
|
" dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],",
|
|
5034
5461
|
" },",
|
|
@@ -5060,6 +5487,8 @@ function mainTsx() {
|
|
|
5060
5487
|
"import React from 'react';",
|
|
5061
5488
|
"import { createRoot } from 'react-dom/client';",
|
|
5062
5489
|
"import '@topolo-io/design-tokens/tokens.css';",
|
|
5490
|
+
"import '@topolo-io/tour/styles.css';",
|
|
5491
|
+
"import '@topolo-io/onboarding/styles.css';",
|
|
5063
5492
|
"import App from './App';",
|
|
5064
5493
|
"import './styles.css';",
|
|
5065
5494
|
"",
|
|
@@ -5162,16 +5591,19 @@ function platformAppTsx(application, options) {
|
|
|
5162
5591
|
const baselineHeading = isPersonal ? "Shared personal baseline" : "Shared workspace baseline";
|
|
5163
5592
|
const baselineDescription = isPersonal ? "This scaffold starts on the shared landing and login surfaces, completes sign-in on the shared callback path, and renders the signed personal app inside the shared Topolo shell." : "This scaffold starts on the shared landing and login surfaces, completes sign-in on the shared callback path, and renders the signed workspace inside the shared Topolo shell.";
|
|
5164
5593
|
return [
|
|
5165
|
-
"import { Component, useEffect, useMemo, useState, type ErrorInfo, type ReactNode } from 'react';",
|
|
5594
|
+
"import { Component, useCallback, useEffect, useMemo, useState, type ErrorInfo, type ReactNode } from 'react';",
|
|
5166
5595
|
"import { createBrowserAuthSessionController, type AuthUser } from '@topolo-io/auth-client';",
|
|
5167
5596
|
"import { LandingPage, LoginPage, TopoloAppShell, TopoloAuthFlowScreen, TopoloLoadingScreen, setupBugReporterErrorCapture, type TopoloAppShellUser } from '@topolo-io/ui-kit';",
|
|
5597
|
+
"import { GettingStarted, type OnboardingGuideStep } from '@topolo-io/onboarding';",
|
|
5598
|
+
"import { IntroTour } from '@topolo-io/tour';",
|
|
5168
5599
|
"import { SCAFFOLD_APP_ID, SCAFFOLD_APP_NAME, SCAFFOLD_SERVICE_SLUG, getBrowserAuthBaseUrl, resolveAppServiceId } from './service-identity';",
|
|
5169
5600
|
"",
|
|
5170
5601
|
"// Wire global error/console capture so BugReporter (enabled in TopoloAppShell below)",
|
|
5171
5602
|
"// can attach the last few errors to any submitted bug report.",
|
|
5172
5603
|
"setupBugReporterErrorCapture();",
|
|
5173
5604
|
"",
|
|
5174
|
-
"type NavItem = 'home';",
|
|
5605
|
+
"type NavItem = 'home' | 'getting-started';",
|
|
5606
|
+
"type WorkspaceView = 'workspace' | 'getting-started';",
|
|
5175
5607
|
"type AppRoute = { kind: 'root' } | { kind: 'login' } | { kind: 'callback' };",
|
|
5176
5608
|
"type BrowserAuthModule = ReturnType<typeof createBrowserAuthSessionController>;",
|
|
5177
5609
|
"",
|
|
@@ -5248,7 +5680,17 @@ function platformAppTsx(application, options) {
|
|
|
5248
5680
|
" const [loading, setLoading] = useState(true);",
|
|
5249
5681
|
" const [authError, setAuthError] = useState('');",
|
|
5250
5682
|
" const [loginAttempted, setLoginAttempted] = useState(false);",
|
|
5683
|
+
" const [view, setView] = useState<WorkspaceView>('workspace');",
|
|
5251
5684
|
" const auth = useMemo(() => serviceId ? getAuthModule(serviceId) : null, [serviceId]);",
|
|
5685
|
+
" const openWorkspace = useCallback(() => setView('workspace'), []);",
|
|
5686
|
+
" const openGettingStarted = useCallback(() => {",
|
|
5687
|
+
" setView('getting-started');",
|
|
5688
|
+
" if (typeof window !== 'undefined') window.scrollTo({ top: 0, behavior: 'smooth' });",
|
|
5689
|
+
" }, []);",
|
|
5690
|
+
" const handleNavSelect = useCallback((id: NavItem) => {",
|
|
5691
|
+
" if (id === 'getting-started') openGettingStarted();",
|
|
5692
|
+
" else openWorkspace();",
|
|
5693
|
+
" }, [openGettingStarted, openWorkspace]);",
|
|
5252
5694
|
"",
|
|
5253
5695
|
" useEffect(() => {",
|
|
5254
5696
|
" let active = true;",
|
|
@@ -5447,11 +5889,11 @@ function platformAppTsx(application, options) {
|
|
|
5447
5889
|
" orgId={user.activeContextType === 'organization' ? user.activeContextId ?? user.orgId ?? null : null}",
|
|
5448
5890
|
" activeContextType={user.activeContextType}",
|
|
5449
5891
|
" activeContextId={user.activeContextId}",
|
|
5450
|
-
|
|
5451
|
-
` navItems={[{ id: 'home', label: '${escapeSingleQuoted(workspaceTitle)}' }]}`,
|
|
5892
|
+
" activeNavItemId={view === 'getting-started' ? 'getting-started' : 'home'}",
|
|
5893
|
+
` navItems={[{ id: 'home', label: '${escapeSingleQuoted(workspaceTitle)}' }, { id: 'getting-started', label: 'Getting started' }]}`,
|
|
5452
5894
|
' navigationMode="topbar"',
|
|
5453
|
-
" onNavItemSelect={
|
|
5454
|
-
` title=
|
|
5895
|
+
" onNavItemSelect={handleNavSelect}",
|
|
5896
|
+
` title={view === 'getting-started' ? 'Getting started' : '${escapeSingleQuoted(workspaceTitle)}'}`,
|
|
5455
5897
|
" authUrl={AUTH_URL}",
|
|
5456
5898
|
" getAccessToken={getAccessToken}",
|
|
5457
5899
|
" onSignOut={async () => {",
|
|
@@ -5463,18 +5905,94 @@ function platformAppTsx(application, options) {
|
|
|
5463
5905
|
" enableCommandPalette",
|
|
5464
5906
|
" enableBugReporter",
|
|
5465
5907
|
" >",
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
"
|
|
5470
|
-
|
|
5471
|
-
"
|
|
5472
|
-
"
|
|
5473
|
-
"
|
|
5908
|
+
" {view === 'getting-started' ? (",
|
|
5909
|
+
" <ScaffoldGettingStarted",
|
|
5910
|
+
" serviceId={activeServiceId}",
|
|
5911
|
+
" organizationId={user.organization?.id ?? user.orgId ?? ''}",
|
|
5912
|
+
" role={user.role}",
|
|
5913
|
+
" onOpenWorkspace={openWorkspace}",
|
|
5914
|
+
" />",
|
|
5915
|
+
" ) : (",
|
|
5916
|
+
' <div className="topolo-starter-workspace">',
|
|
5917
|
+
' <section className="topolo-starter-card" data-tour="workspace-card">',
|
|
5918
|
+
` <h2>${baselineHeading}</h2>`,
|
|
5919
|
+
" <p>",
|
|
5920
|
+
` ${baselineDescription}`,
|
|
5921
|
+
" </p>",
|
|
5922
|
+
" </section>",
|
|
5923
|
+
" </div>",
|
|
5924
|
+
" )}",
|
|
5925
|
+
" <IntroTour",
|
|
5926
|
+
" appName={APP_NAME}",
|
|
5927
|
+
" role={user.role}",
|
|
5928
|
+
" onOpenGettingStarted={openGettingStarted}",
|
|
5929
|
+
" extraSteps={[",
|
|
5930
|
+
" {",
|
|
5931
|
+
" id: 'workspace-card',",
|
|
5932
|
+
` target: '[data-tour="workspace-card"]',`,
|
|
5933
|
+
" title: 'Your workspace',",
|
|
5934
|
+
" content: 'Everything you build renders here, inside the shared Topolo shell.',",
|
|
5935
|
+
" placement: 'bottom' as const,",
|
|
5936
|
+
" beforeStep: () => openWorkspace(),",
|
|
5937
|
+
" },",
|
|
5938
|
+
" ]}",
|
|
5939
|
+
" />",
|
|
5474
5940
|
" </TopoloAppShell>",
|
|
5475
5941
|
" );",
|
|
5476
5942
|
"}",
|
|
5477
5943
|
"",
|
|
5944
|
+
"function ScaffoldGettingStarted(props: {",
|
|
5945
|
+
" serviceId: string;",
|
|
5946
|
+
" organizationId: string;",
|
|
5947
|
+
" role?: string;",
|
|
5948
|
+
" onOpenWorkspace: () => void;",
|
|
5949
|
+
"}) {",
|
|
5950
|
+
" const { serviceId, organizationId, role, onOpenWorkspace } = props;",
|
|
5951
|
+
" const steps: OnboardingGuideStep[] = [",
|
|
5952
|
+
" {",
|
|
5953
|
+
" id: 'explore-workspace',",
|
|
5954
|
+
" title: 'Explore your workspace',",
|
|
5955
|
+
" description:",
|
|
5956
|
+
" 'Your app renders inside the shared Topolo shell \u2014 launcher, command palette, and account menu are already wired in.',",
|
|
5957
|
+
" actionLabel: 'Open workspace',",
|
|
5958
|
+
" onAction: onOpenWorkspace,",
|
|
5959
|
+
" },",
|
|
5960
|
+
" {",
|
|
5961
|
+
" id: 'discover-platform',",
|
|
5962
|
+
" title: 'Move between your Topolo apps',",
|
|
5963
|
+
" description:",
|
|
5964
|
+
" 'Use the launcher to switch between your Topolo apps, and the command palette (Cmd/Ctrl-K) to jump anywhere fast.',",
|
|
5965
|
+
" optional: true,",
|
|
5966
|
+
" },",
|
|
5967
|
+
" {",
|
|
5968
|
+
" id: 'build-feature',",
|
|
5969
|
+
" title: 'Build your first feature',",
|
|
5970
|
+
" description:",
|
|
5971
|
+
" 'Replace the starter card with your product surface, and gate write actions with service scopes before you ship.',",
|
|
5972
|
+
" },",
|
|
5973
|
+
" ];",
|
|
5974
|
+
" return (",
|
|
5975
|
+
' <div className="topolo-starter-workspace">',
|
|
5976
|
+
" <GettingStarted",
|
|
5977
|
+
" authApiBaseUrl={`${AUTH_URL}/api`}",
|
|
5978
|
+
" organizationId={organizationId}",
|
|
5979
|
+
" serviceId={serviceId}",
|
|
5980
|
+
" getAccessToken={() => getAccessToken()}",
|
|
5981
|
+
" role={role || undefined}",
|
|
5982
|
+
" steps={steps}",
|
|
5983
|
+
" eyebrow={`Welcome to ${APP_NAME}`}",
|
|
5984
|
+
" title={`Get started with ${APP_NAME}`}",
|
|
5985
|
+
' subtitle="A short checklist to orient you in your new Topolo workspace."',
|
|
5986
|
+
' completeLabel="Mark done"',
|
|
5987
|
+
' dismissLabel="Skip for now"',
|
|
5988
|
+
" completedContent={`You're all set \u2014 ${APP_NAME} is ready to build on.`}",
|
|
5989
|
+
" completeAction={{ label: 'Go to workspace', onClick: onOpenWorkspace }}",
|
|
5990
|
+
" onDismiss={onOpenWorkspace}",
|
|
5991
|
+
" />",
|
|
5992
|
+
" </div>",
|
|
5993
|
+
" );",
|
|
5994
|
+
"}",
|
|
5995
|
+
"",
|
|
5478
5996
|
"function getRoute(): AppRoute {",
|
|
5479
5997
|
" const pathname = window.location.pathname.replace(/\\/+$/, '') || '/';",
|
|
5480
5998
|
" if (pathname === '/login') return { kind: 'login' };",
|
|
@@ -5627,6 +6145,208 @@ function developerAppTsx(application) {
|
|
|
5627
6145
|
""
|
|
5628
6146
|
].join("\n");
|
|
5629
6147
|
}
|
|
6148
|
+
function webWorkerTs(application) {
|
|
6149
|
+
const serviceName = toTitle(application.name);
|
|
6150
|
+
return [
|
|
6151
|
+
"interface Env {",
|
|
6152
|
+
" API: { fetch(request: Request): Promise<Response> };",
|
|
6153
|
+
"}",
|
|
6154
|
+
"",
|
|
6155
|
+
`const SERVICE_NAME = '${escapeSingleQuoted(serviceName)}';`,
|
|
6156
|
+
"",
|
|
6157
|
+
"export default {",
|
|
6158
|
+
" async fetch(request: Request, env: Env): Promise<Response> {",
|
|
6159
|
+
" const url = new URL(request.url);",
|
|
6160
|
+
" if (url.pathname.startsWith('/api/')) {",
|
|
6161
|
+
" return env.API.fetch(request);",
|
|
6162
|
+
" }",
|
|
6163
|
+
" return Response.json({ ok: true, service: SERVICE_NAME }, { status: 200 });",
|
|
6164
|
+
" },",
|
|
6165
|
+
"};",
|
|
6166
|
+
""
|
|
6167
|
+
].join("\n");
|
|
6168
|
+
}
|
|
6169
|
+
function platformWebWranglerToml(application) {
|
|
6170
|
+
const slug = toSlug(application.id);
|
|
6171
|
+
const host = application.productionUrl ? new URL(application.productionUrl).host : `${slug}.topolo.app`;
|
|
6172
|
+
return [
|
|
6173
|
+
`name = "topolo-${slug}"`,
|
|
6174
|
+
'main = "src/worker.ts"',
|
|
6175
|
+
`compatibility_date = "${CLOUDFLARE_COMPATIBILITY_DATE}"`,
|
|
6176
|
+
'compatibility_flags = ["nodejs_compat"]',
|
|
6177
|
+
`account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
|
|
6178
|
+
"workers_dev = false",
|
|
6179
|
+
"",
|
|
6180
|
+
"[assets]",
|
|
6181
|
+
'directory = "./dist"',
|
|
6182
|
+
'binding = "ASSETS"',
|
|
6183
|
+
'not_found_handling = "single-page-application"',
|
|
6184
|
+
'run_worker_first = ["/api/*"]',
|
|
6185
|
+
"",
|
|
6186
|
+
"[observability]",
|
|
6187
|
+
"enabled = true",
|
|
6188
|
+
"",
|
|
6189
|
+
"[[routes]]",
|
|
6190
|
+
`pattern = "${host}"`,
|
|
6191
|
+
"custom_domain = true",
|
|
6192
|
+
"",
|
|
6193
|
+
"[vars]",
|
|
6194
|
+
`SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6195
|
+
`VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6196
|
+
"",
|
|
6197
|
+
"[[services]]",
|
|
6198
|
+
'binding = "API"',
|
|
6199
|
+
`service = "topolo-${slug}-api"`,
|
|
6200
|
+
"",
|
|
6201
|
+
"[env.staging]",
|
|
6202
|
+
`name = "topolo-${slug}-staging"`,
|
|
6203
|
+
`account_id = "${STAGING_CLOUDFLARE_ACCOUNT_ID}"`,
|
|
6204
|
+
"workers_dev = true",
|
|
6205
|
+
"",
|
|
6206
|
+
"[env.staging.vars]",
|
|
6207
|
+
`SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6208
|
+
`VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6209
|
+
"",
|
|
6210
|
+
"[[env.staging.routes]]",
|
|
6211
|
+
`pattern = "${slug}.stg.topolo.us"`,
|
|
6212
|
+
"custom_domain = true",
|
|
6213
|
+
"",
|
|
6214
|
+
"[env.staging.assets]",
|
|
6215
|
+
'directory = "./dist"',
|
|
6216
|
+
'binding = "ASSETS"',
|
|
6217
|
+
'not_found_handling = "single-page-application"',
|
|
6218
|
+
'run_worker_first = ["/api/*"]',
|
|
6219
|
+
"",
|
|
6220
|
+
"[[env.staging.services]]",
|
|
6221
|
+
'binding = "API"',
|
|
6222
|
+
`service = "topolo-${slug}-api-staging"`,
|
|
6223
|
+
"",
|
|
6224
|
+
"[env.production]",
|
|
6225
|
+
`name = "topolo-${slug}-production"`,
|
|
6226
|
+
`account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
|
|
6227
|
+
"workers_dev = false",
|
|
6228
|
+
"",
|
|
6229
|
+
"[env.production.vars]",
|
|
6230
|
+
`SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6231
|
+
`VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6232
|
+
"",
|
|
6233
|
+
"[[env.production.routes]]",
|
|
6234
|
+
`pattern = "${host}"`,
|
|
6235
|
+
"custom_domain = true",
|
|
6236
|
+
"",
|
|
6237
|
+
"[env.production.assets]",
|
|
6238
|
+
'directory = "./dist"',
|
|
6239
|
+
'binding = "ASSETS"',
|
|
6240
|
+
'not_found_handling = "single-page-application"',
|
|
6241
|
+
'run_worker_first = ["/api/*"]',
|
|
6242
|
+
"",
|
|
6243
|
+
"[[env.production.services]]",
|
|
6244
|
+
'binding = "API"',
|
|
6245
|
+
`service = "topolo-${slug}-api-production"`,
|
|
6246
|
+
""
|
|
6247
|
+
].join("\n");
|
|
6248
|
+
}
|
|
6249
|
+
function platformApiWranglerToml(application, resources, profile) {
|
|
6250
|
+
const slug = toSlug(application.id);
|
|
6251
|
+
const resource = resourceConfig(application, profile);
|
|
6252
|
+
const lines = [
|
|
6253
|
+
`name = "topolo-${slug}-api"`,
|
|
6254
|
+
'main = "src/worker.ts"',
|
|
6255
|
+
`compatibility_date = "${CLOUDFLARE_COMPATIBILITY_DATE}"`,
|
|
6256
|
+
'compatibility_flags = ["nodejs_compat"]',
|
|
6257
|
+
`account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
|
|
6258
|
+
"workers_dev = true",
|
|
6259
|
+
"",
|
|
6260
|
+
"[observability]",
|
|
6261
|
+
"enabled = true",
|
|
6262
|
+
"",
|
|
6263
|
+
"[vars]",
|
|
6264
|
+
'AUTH_BASE_URL = "https://auth.topolo.app"',
|
|
6265
|
+
'NEXUS_GATEWAY_URL = "https://nexus-api.topolo.app"',
|
|
6266
|
+
`NEXUS_APP_ID = "${slug}"`,
|
|
6267
|
+
`SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6268
|
+
`VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6269
|
+
""
|
|
6270
|
+
];
|
|
6271
|
+
appendResourceWranglerBlocks(lines, resources, resource, "production");
|
|
6272
|
+
lines.push(
|
|
6273
|
+
"[env.staging]",
|
|
6274
|
+
`name = "topolo-${slug}-api-staging"`,
|
|
6275
|
+
`account_id = "${STAGING_CLOUDFLARE_ACCOUNT_ID}"`,
|
|
6276
|
+
"workers_dev = true",
|
|
6277
|
+
"",
|
|
6278
|
+
"[env.staging.vars]",
|
|
6279
|
+
'AUTH_BASE_URL = "https://auth.stg.topolo.us"',
|
|
6280
|
+
'NEXUS_GATEWAY_URL = "https://nexus-api.stg.topolo.us"',
|
|
6281
|
+
`NEXUS_APP_ID = "${slug}"`,
|
|
6282
|
+
`SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6283
|
+
`VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6284
|
+
""
|
|
6285
|
+
);
|
|
6286
|
+
appendResourceWranglerBlocks(lines, resources, resource, "staging");
|
|
6287
|
+
lines.push(
|
|
6288
|
+
"[env.production]",
|
|
6289
|
+
`name = "topolo-${slug}-api-production"`,
|
|
6290
|
+
`account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
|
|
6291
|
+
"workers_dev = false",
|
|
6292
|
+
"",
|
|
6293
|
+
"[env.production.vars]",
|
|
6294
|
+
'AUTH_BASE_URL = "https://auth.topolo.app"',
|
|
6295
|
+
'NEXUS_GATEWAY_URL = "https://nexus-api.topolo.app"',
|
|
6296
|
+
`NEXUS_APP_ID = "${slug}"`,
|
|
6297
|
+
`SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6298
|
+
`VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
|
|
6299
|
+
""
|
|
6300
|
+
);
|
|
6301
|
+
appendResourceWranglerBlocks(lines, resources, resource, "production-env");
|
|
6302
|
+
return lines.join("\n");
|
|
6303
|
+
}
|
|
6304
|
+
function appendResourceWranglerBlocks(lines, resources, resource, environment) {
|
|
6305
|
+
const prefix = environment === "staging" ? "env.staging." : environment === "production-env" ? "env.production." : "";
|
|
6306
|
+
const envName = environment === "staging" ? "Staging" : "Production";
|
|
6307
|
+
if (resources.d1) {
|
|
6308
|
+
lines.push(
|
|
6309
|
+
`[[${prefix}d1_databases]]`,
|
|
6310
|
+
`binding = "${resource.d1Binding}"`,
|
|
6311
|
+
`database_name = "${environment === "staging" ? resource.d1StagingName : resource.d1ProductionName}"`,
|
|
6312
|
+
'database_id = "provision-after-scaffold"',
|
|
6313
|
+
'migrations_dir = "migrations"',
|
|
6314
|
+
""
|
|
6315
|
+
);
|
|
6316
|
+
}
|
|
6317
|
+
if (resources.r2) {
|
|
6318
|
+
lines.push(
|
|
6319
|
+
`[[${prefix}r2_buckets]]`,
|
|
6320
|
+
`binding = "${resource.r2Binding}"`,
|
|
6321
|
+
`bucket_name = "${environment === "staging" ? resource.r2StagingName : resource.r2ProductionName}"`,
|
|
6322
|
+
""
|
|
6323
|
+
);
|
|
6324
|
+
}
|
|
6325
|
+
if (resources.queue) {
|
|
6326
|
+
lines.push(
|
|
6327
|
+
`[[${prefix}queues.producers]]`,
|
|
6328
|
+
`binding = "${resource.queueBinding}"`,
|
|
6329
|
+
`queue = "${environment === "staging" ? resource.queueStagingName : resource.queueProductionName}"`,
|
|
6330
|
+
"",
|
|
6331
|
+
`[[${prefix}queues.consumers]]`,
|
|
6332
|
+
`queue = "${environment === "staging" ? resource.queueStagingName : resource.queueProductionName}"`,
|
|
6333
|
+
""
|
|
6334
|
+
);
|
|
6335
|
+
}
|
|
6336
|
+
if (resources.workflow) {
|
|
6337
|
+
lines.push(
|
|
6338
|
+
`[[${prefix}workflows]]`,
|
|
6339
|
+
`binding = "${resource.workflowBinding}"`,
|
|
6340
|
+
`name = "${environment === "staging" ? resource.workflowStagingName : resource.workflowProductionName}"`,
|
|
6341
|
+
`class_name = "${resource.workflowClassName}"`,
|
|
6342
|
+
""
|
|
6343
|
+
);
|
|
6344
|
+
}
|
|
6345
|
+
if (resources.containerProcessing) {
|
|
6346
|
+
lines.push(`# ${envName} container metadata is declared in topolo.app.json and topolo.cloudcontrol.json.`);
|
|
6347
|
+
lines.push("");
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
5630
6350
|
function wranglerToml(application, target, resources, profile) {
|
|
5631
6351
|
const slug = toSlug(application.id);
|
|
5632
6352
|
const workerName = target === "platform" ? `topolo-${slug}` : application.packageName;
|
|
@@ -5668,6 +6388,7 @@ function wranglerToml(application, target, resources, profile) {
|
|
|
5668
6388
|
"",
|
|
5669
6389
|
"[vars]",
|
|
5670
6390
|
'AUTH_BASE_URL = "https://auth.topolo.app"',
|
|
6391
|
+
'NEXUS_GATEWAY_URL = "https://nexus-api.topolo.app"',
|
|
5671
6392
|
runtimeIdentityVar,
|
|
5672
6393
|
""
|
|
5673
6394
|
);
|
|
@@ -5722,6 +6443,7 @@ function wranglerToml(application, target, resources, profile) {
|
|
|
5722
6443
|
"",
|
|
5723
6444
|
"[env.staging.vars]",
|
|
5724
6445
|
'AUTH_BASE_URL = "https://auth.stg.topolo.us"',
|
|
6446
|
+
'NEXUS_GATEWAY_URL = "https://nexus-api.stg.topolo.us"',
|
|
5725
6447
|
runtimeIdentityVar,
|
|
5726
6448
|
""
|
|
5727
6449
|
);
|
|
@@ -5776,8 +6498,8 @@ function platformWorkerTs(application, options) {
|
|
|
5776
6498
|
const resource = resourceConfig(application, options.profile);
|
|
5777
6499
|
const envBindings = [
|
|
5778
6500
|
" AUTH_BASE_URL?: string;",
|
|
6501
|
+
" NEXUS_GATEWAY_URL?: string;",
|
|
5779
6502
|
" SERVICE_ID?: string;",
|
|
5780
|
-
" TOPOLO_SERVICE_API_KEY?: string;",
|
|
5781
6503
|
...options.resources.d1 ? [` ${resource.d1Binding}?: unknown;`] : [],
|
|
5782
6504
|
...options.resources.r2 ? [` ${resource.r2Binding}?: unknown;`] : [],
|
|
5783
6505
|
...options.resources.queue ? [` ${resource.queueBinding}?: { send(message: unknown): Promise<void> };`] : [],
|
|
@@ -5810,8 +6532,9 @@ function platformWorkerTs(application, options) {
|
|
|
5810
6532
|
" }, { status: 200 });"
|
|
5811
6533
|
];
|
|
5812
6534
|
return [
|
|
5813
|
-
"import {
|
|
5814
|
-
"import {
|
|
6535
|
+
"import { Hono } from 'hono';",
|
|
6536
|
+
"import { createTopoloWidgetResponse, type TopoloWidgetDraft } from '@topolo/sdk';",
|
|
6537
|
+
"import { createTopoloWorkerRuntime, type TopoloRuntimeContext } from '@topolo-io/worker-runtime';",
|
|
5815
6538
|
"import { SCAFFOLD_APP_ID, SCAFFOLD_APP_NAME, SCAFFOLD_SERVICE_SLUG, resolveRuntimeServiceId } from './service-identity';",
|
|
5816
6539
|
...options.resources.workflow ? ["import { WorkflowEntrypoint, type WorkflowEvent, type WorkflowStep } from 'cloudflare:workers';"] : [],
|
|
5817
6540
|
"",
|
|
@@ -5819,18 +6542,8 @@ function platformWorkerTs(application, options) {
|
|
|
5819
6542
|
...envBindings,
|
|
5820
6543
|
"}",
|
|
5821
6544
|
"",
|
|
5822
|
-
"
|
|
5823
|
-
"
|
|
5824
|
-
"// is installed. Replace the quick-actions placeholder with org-scoped",
|
|
5825
|
-
"// stats once your data model is ready: ctx.user / ctx.orgId / ctx.orgSlug",
|
|
5826
|
-
"// / ctx.isPlatformAdmin / ctx.contextType / ctx.householdId are all",
|
|
5827
|
-
"// pre-resolved. Builder exceptions degrade to empty widgets, not 500s.",
|
|
5828
|
-
"const widgetHandler = createWidgetHandler<Env>({",
|
|
5829
|
-
" serviceId: SCAFFOLD_SERVICE_SLUG,",
|
|
5830
|
-
` serviceName: '${escapeSingleQuoted(serviceName)}',`,
|
|
5831
|
-
" authBaseUrl: (env) => env.AUTH_BASE_URL,",
|
|
5832
|
-
" serviceApiKey: (env) => env.TOPOLO_SERVICE_API_KEY,",
|
|
5833
|
-
" build: () => [",
|
|
6545
|
+
"function buildWidgets(): TopoloWidgetDraft[] {",
|
|
6546
|
+
" return [",
|
|
5834
6547
|
" {",
|
|
5835
6548
|
" type: 'quick-actions',",
|
|
5836
6549
|
" actions: [",
|
|
@@ -5838,18 +6551,25 @@ function platformWorkerTs(application, options) {
|
|
|
5838
6551
|
" { id: 'view-health', label: 'View health', link: '/api/health' },",
|
|
5839
6552
|
" ],",
|
|
5840
6553
|
" },",
|
|
5841
|
-
" ]
|
|
5842
|
-
"}
|
|
6554
|
+
" ];",
|
|
6555
|
+
"}",
|
|
5843
6556
|
"",
|
|
5844
6557
|
"const runtime = createTopoloWorkerRuntime<Env>({",
|
|
5845
6558
|
" appName: SCAFFOLD_APP_NAME,",
|
|
5846
6559
|
" serviceSlug: SCAFFOLD_SERVICE_SLUG,",
|
|
5847
6560
|
" resolveServiceId: (env) => resolveRuntimeServiceId(env),",
|
|
5848
6561
|
" authBaseUrl: (env) => env.AUTH_BASE_URL,",
|
|
5849
|
-
"
|
|
6562
|
+
" usageMonitoring: {",
|
|
6563
|
+
" appId: SCAFFOLD_APP_ID,",
|
|
6564
|
+
" nexusGatewayUrl: (env) => env.NEXUS_GATEWAY_URL,",
|
|
6565
|
+
" },",
|
|
5850
6566
|
"});",
|
|
5851
6567
|
"",
|
|
5852
|
-
"
|
|
6568
|
+
"async function handleRequest(",
|
|
6569
|
+
" _request: Request,",
|
|
6570
|
+
" env: Env,",
|
|
6571
|
+
" rt: TopoloRuntimeContext<Env>,",
|
|
6572
|
+
"): Promise<Response> {",
|
|
5853
6573
|
" const url = rt.url;",
|
|
5854
6574
|
"",
|
|
5855
6575
|
" let serviceId: string;",
|
|
@@ -5872,9 +6592,15 @@ function platformWorkerTs(application, options) {
|
|
|
5872
6592
|
" }, { status: 200 });",
|
|
5873
6593
|
" }",
|
|
5874
6594
|
"",
|
|
5875
|
-
"
|
|
5876
|
-
"
|
|
5877
|
-
"
|
|
6595
|
+
" if (url.pathname === '/api/widget') {",
|
|
6596
|
+
" const auth = await rt.verifyAuth({ serviceId });",
|
|
6597
|
+
" if (!auth.ok) return rt.authError(auth);",
|
|
6598
|
+
" return rt.json(createTopoloWidgetResponse({",
|
|
6599
|
+
" serviceId,",
|
|
6600
|
+
" serviceName: SCAFFOLD_APP_NAME,",
|
|
6601
|
+
" widgets: buildWidgets(),",
|
|
6602
|
+
" }), { status: 200, headers: { 'Cache-Control': 'public, max-age=15' } });",
|
|
6603
|
+
" }",
|
|
5878
6604
|
"",
|
|
5879
6605
|
" const auth = await rt.verifyAuth({ serviceId });",
|
|
5880
6606
|
"",
|
|
@@ -5887,10 +6613,21 @@ function platformWorkerTs(application, options) {
|
|
|
5887
6613
|
" }",
|
|
5888
6614
|
"",
|
|
5889
6615
|
" return rt.notFound();",
|
|
6616
|
+
"}",
|
|
6617
|
+
"",
|
|
6618
|
+
"const api = new Hono<{ Bindings: Env & { __rt?: TopoloRuntimeContext<Env> } }>();",
|
|
6619
|
+
"api.all('*', (c) => {",
|
|
6620
|
+
" const rt = c.env.__rt;",
|
|
6621
|
+
" if (!rt) return Response.json({ error: 'runtime_context_unavailable' }, { status: 500 });",
|
|
6622
|
+
" return handleRequest(c.req.raw, c.env, rt);",
|
|
5890
6623
|
"});",
|
|
5891
6624
|
"",
|
|
5892
6625
|
"export default {",
|
|
5893
|
-
" fetch:
|
|
6626
|
+
" fetch: runtime.worker((request, env, ctx, rt) => {",
|
|
6627
|
+
" const bindings = env as Env & { __rt?: TopoloRuntimeContext<Env> };",
|
|
6628
|
+
" bindings.__rt = rt;",
|
|
6629
|
+
" return api.fetch(request, bindings, ctx as ExecutionContext);",
|
|
6630
|
+
" }),",
|
|
5894
6631
|
"};",
|
|
5895
6632
|
"",
|
|
5896
6633
|
"function errorMessage(error: unknown): string {",
|
|
@@ -5905,7 +6642,7 @@ function developerWorkerTs(application) {
|
|
|
5905
6642
|
const serviceId = application.serviceId ?? application.id;
|
|
5906
6643
|
const serviceName = application.name || "Topolo App";
|
|
5907
6644
|
return [
|
|
5908
|
-
"import {
|
|
6645
|
+
"import { createTopoloWidgetResponse, type TopoloWidgetDraft } from '@topolo/sdk';",
|
|
5909
6646
|
"",
|
|
5910
6647
|
"export interface Env {",
|
|
5911
6648
|
" AUTH_BASE_URL?: string;",
|
|
@@ -5916,17 +6653,8 @@ function developerWorkerTs(application) {
|
|
|
5916
6653
|
`const DEFAULT_SERVICE_ID = '${serviceId}';`,
|
|
5917
6654
|
`const SERVICE_NAME = '${escapeSingleQuoted(serviceName)}';`,
|
|
5918
6655
|
"",
|
|
5919
|
-
"
|
|
5920
|
-
"
|
|
5921
|
-
"// is installed. Replace the quick-actions placeholder with org-scoped",
|
|
5922
|
-
"// stats once your data model is ready: ctx.user / ctx.orgId / ctx.orgSlug",
|
|
5923
|
-
"// / ctx.isPlatformAdmin / ctx.contextType / ctx.householdId are all",
|
|
5924
|
-
"// pre-resolved. Builder exceptions degrade to empty widgets, not 500s.",
|
|
5925
|
-
"const widgetHandler = createWidgetHandler<Env>({",
|
|
5926
|
-
" serviceId: DEFAULT_SERVICE_ID,",
|
|
5927
|
-
" serviceName: SERVICE_NAME,",
|
|
5928
|
-
" authBaseUrl: (env) => env.AUTH_BASE_URL,",
|
|
5929
|
-
" build: () => [",
|
|
6656
|
+
"function buildWidgets(): TopoloWidgetDraft[] {",
|
|
6657
|
+
" return [",
|
|
5930
6658
|
" {",
|
|
5931
6659
|
" type: 'quick-actions',",
|
|
5932
6660
|
" actions: [",
|
|
@@ -5934,8 +6662,8 @@ function developerWorkerTs(application) {
|
|
|
5934
6662
|
" { id: 'view-health', label: 'View health', link: '/api/health' },",
|
|
5935
6663
|
" ],",
|
|
5936
6664
|
" },",
|
|
5937
|
-
" ]
|
|
5938
|
-
"}
|
|
6665
|
+
" ];",
|
|
6666
|
+
"}",
|
|
5939
6667
|
"",
|
|
5940
6668
|
"export default {",
|
|
5941
6669
|
" async fetch(request: Request, env: Env): Promise<Response> {",
|
|
@@ -5945,8 +6673,15 @@ function developerWorkerTs(application) {
|
|
|
5945
6673
|
" return json({ ok: true, service: env.SERVICE_ID ?? DEFAULT_SERVICE_ID }, { status: 200 }, request);",
|
|
5946
6674
|
" }",
|
|
5947
6675
|
"",
|
|
5948
|
-
"
|
|
5949
|
-
"
|
|
6676
|
+
" if (url.pathname === '/api/widget') {",
|
|
6677
|
+
" const auth = await verifyTopoloRequest(request, env);",
|
|
6678
|
+
" if (!auth.ok) return json({ error: auth.error }, { status: auth.status }, request);",
|
|
6679
|
+
" return json(createTopoloWidgetResponse({",
|
|
6680
|
+
" serviceId: env.SERVICE_ID ?? DEFAULT_SERVICE_ID,",
|
|
6681
|
+
" serviceName: SERVICE_NAME,",
|
|
6682
|
+
" widgets: buildWidgets(),",
|
|
6683
|
+
" }), { status: 200, headers: { 'Cache-Control': 'public, max-age=15' } }, request);",
|
|
6684
|
+
" }",
|
|
5950
6685
|
"",
|
|
5951
6686
|
" const auth = await verifyTopoloRequest(request, env);",
|
|
5952
6687
|
" if (!auth.ok) {",
|
|
@@ -6002,6 +6737,132 @@ function developerWorkerTs(application) {
|
|
|
6002
6737
|
""
|
|
6003
6738
|
].join("\n");
|
|
6004
6739
|
}
|
|
6740
|
+
function topoloAppManifestJson(application, options) {
|
|
6741
|
+
const slug = toSlug(application.id);
|
|
6742
|
+
const resource = resourceConfig(application, options.profile);
|
|
6743
|
+
const targets = {};
|
|
6744
|
+
if (options.api) {
|
|
6745
|
+
const bindings = {};
|
|
6746
|
+
if (options.resources.d1) {
|
|
6747
|
+
bindings.d1 = [
|
|
6748
|
+
{
|
|
6749
|
+
binding: resource.d1Binding,
|
|
6750
|
+
staging: {
|
|
6751
|
+
databaseName: resource.d1StagingName,
|
|
6752
|
+
databaseId: "provision-after-scaffold"
|
|
6753
|
+
},
|
|
6754
|
+
production: {
|
|
6755
|
+
databaseName: resource.d1ProductionName,
|
|
6756
|
+
databaseId: "provision-after-scaffold"
|
|
6757
|
+
},
|
|
6758
|
+
migrationsDir: "migrations"
|
|
6759
|
+
}
|
|
6760
|
+
];
|
|
6761
|
+
}
|
|
6762
|
+
if (options.resources.r2) {
|
|
6763
|
+
bindings.r2 = [
|
|
6764
|
+
{
|
|
6765
|
+
binding: resource.r2Binding,
|
|
6766
|
+
staging: { bucketName: resource.r2StagingName },
|
|
6767
|
+
production: { bucketName: resource.r2ProductionName }
|
|
6768
|
+
}
|
|
6769
|
+
];
|
|
6770
|
+
}
|
|
6771
|
+
if (options.resources.queue) {
|
|
6772
|
+
bindings.queues = [
|
|
6773
|
+
{
|
|
6774
|
+
binding: resource.queueBinding,
|
|
6775
|
+
staging: { queueName: resource.queueStagingName },
|
|
6776
|
+
production: { queueName: resource.queueProductionName },
|
|
6777
|
+
consumer: true
|
|
6778
|
+
}
|
|
6779
|
+
];
|
|
6780
|
+
}
|
|
6781
|
+
if (options.resources.workflow) {
|
|
6782
|
+
bindings.workflows = [
|
|
6783
|
+
{
|
|
6784
|
+
binding: resource.workflowBinding,
|
|
6785
|
+
className: resource.workflowClassName,
|
|
6786
|
+
staging: { name: resource.workflowStagingName },
|
|
6787
|
+
production: { name: resource.workflowProductionName }
|
|
6788
|
+
}
|
|
6789
|
+
];
|
|
6790
|
+
}
|
|
6791
|
+
targets.api = {
|
|
6792
|
+
kind: "worker",
|
|
6793
|
+
dir: "apps/api",
|
|
6794
|
+
wranglerName: `topolo-${slug}-api`,
|
|
6795
|
+
main: "src/worker.ts",
|
|
6796
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
6797
|
+
healthcheckPath: "/api/health",
|
|
6798
|
+
vars: {
|
|
6799
|
+
AUTH_BASE_URL: {
|
|
6800
|
+
staging: "https://auth.stg.topolo.us",
|
|
6801
|
+
production: "https://auth.topolo.app"
|
|
6802
|
+
},
|
|
6803
|
+
NEXUS_GATEWAY_URL: {
|
|
6804
|
+
staging: "https://nexus-api.stg.topolo.us",
|
|
6805
|
+
production: "https://nexus-api.topolo.app"
|
|
6806
|
+
},
|
|
6807
|
+
NEXUS_APP_ID: slug
|
|
6808
|
+
},
|
|
6809
|
+
secrets: ["TOPOLO_SERVICE_CLIENT_SECRET"],
|
|
6810
|
+
...Object.keys(bindings).length ? { bindings } : {}
|
|
6811
|
+
};
|
|
6812
|
+
}
|
|
6813
|
+
targets.web = {
|
|
6814
|
+
kind: "worker",
|
|
6815
|
+
dir: "apps/web",
|
|
6816
|
+
wranglerName: `topolo-${slug}`,
|
|
6817
|
+
subdomain: slug,
|
|
6818
|
+
main: "src/worker.ts",
|
|
6819
|
+
workersDev: false,
|
|
6820
|
+
assets: {
|
|
6821
|
+
directory: "./dist",
|
|
6822
|
+
binding: "ASSETS",
|
|
6823
|
+
notFoundHandling: "single-page-application",
|
|
6824
|
+
runWorkerFirst: ["/api/*"]
|
|
6825
|
+
},
|
|
6826
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
6827
|
+
buildCommand: {
|
|
6828
|
+
staging: "cd ../.. && DEPLOY_ENVIRONMENT=staging VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build",
|
|
6829
|
+
production: "cd ../.. && DEPLOY_ENVIRONMENT=production VITE_TOPOLO_AUTH_URL=https://auth.topolo.app npm run build"
|
|
6830
|
+
},
|
|
6831
|
+
healthcheckPath: options.api ? "/api/health" : "/",
|
|
6832
|
+
...options.api ? {
|
|
6833
|
+
bindings: {
|
|
6834
|
+
services: [
|
|
6835
|
+
{
|
|
6836
|
+
binding: "API",
|
|
6837
|
+
service: {
|
|
6838
|
+
staging: `topolo-${slug}-api-staging`,
|
|
6839
|
+
production: `topolo-${slug}-api-production`
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
]
|
|
6843
|
+
}
|
|
6844
|
+
} : {}
|
|
6845
|
+
};
|
|
6846
|
+
return `${json({
|
|
6847
|
+
$schema: "./../../topolo-platform/tooling/app-manifest-schema.json",
|
|
6848
|
+
name: `topolo-${slug}`,
|
|
6849
|
+
serviceId: application.serviceId,
|
|
6850
|
+
primaryTarget: "web",
|
|
6851
|
+
compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
|
|
6852
|
+
environments: {
|
|
6853
|
+
staging: {
|
|
6854
|
+
accountId: STAGING_CLOUDFLARE_ACCOUNT_ID,
|
|
6855
|
+
domain: "stg.topolo.us"
|
|
6856
|
+
},
|
|
6857
|
+
production: {
|
|
6858
|
+
accountId: CLOUDFLARE_ACCOUNT_ID,
|
|
6859
|
+
domain: "topolo.app"
|
|
6860
|
+
}
|
|
6861
|
+
},
|
|
6862
|
+
targets
|
|
6863
|
+
})}
|
|
6864
|
+
`;
|
|
6865
|
+
}
|
|
6005
6866
|
function cloudcontrolJson(application, options) {
|
|
6006
6867
|
const slug = toSlug(application.id);
|
|
6007
6868
|
const appPath = firstPartyAppPath(application.appDir, options.audience);
|
|
@@ -6110,16 +6971,16 @@ function cloudcontrolJson(application, options) {
|
|
|
6110
6971
|
{
|
|
6111
6972
|
kind: "worker",
|
|
6112
6973
|
environment: "production",
|
|
6113
|
-
target_name: `topolo-${slug}`,
|
|
6974
|
+
target_name: `topolo-${slug}-api-production`,
|
|
6114
6975
|
cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
|
|
6115
|
-
config_path: "wrangler.toml",
|
|
6116
|
-
|
|
6117
|
-
|
|
6976
|
+
config_path: "apps/api/wrangler.toml",
|
|
6977
|
+
workdir: "apps/api",
|
|
6978
|
+
build_command: null,
|
|
6979
|
+
output_dir: null,
|
|
6118
6980
|
production_branch: "main",
|
|
6119
6981
|
healthcheck_path: "/api/health",
|
|
6120
|
-
domains: [
|
|
6121
|
-
routes: [
|
|
6122
|
-
api_url: application.productionUrl,
|
|
6982
|
+
domains: [],
|
|
6983
|
+
routes: [],
|
|
6123
6984
|
service_slug: serviceSlug
|
|
6124
6985
|
},
|
|
6125
6986
|
{
|
|
@@ -6127,36 +6988,73 @@ function cloudcontrolJson(application, options) {
|
|
|
6127
6988
|
environment: "staging",
|
|
6128
6989
|
target_name: `topolo-${slug}-staging`,
|
|
6129
6990
|
cloudflare_account_id: STAGING_CLOUDFLARE_ACCOUNT_ID,
|
|
6130
|
-
config_path: "wrangler.toml",
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6991
|
+
config_path: "apps/web/wrangler.toml",
|
|
6992
|
+
workdir: "apps/web",
|
|
6993
|
+
build_command: "DEPLOY_ENVIRONMENT=staging VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build",
|
|
6994
|
+
output_dir: null,
|
|
6995
|
+
production_branch: "staging",
|
|
6134
6996
|
healthcheck_path: "/api/health",
|
|
6135
6997
|
domains: [stagingHost],
|
|
6136
6998
|
routes: [stagingHost],
|
|
6137
6999
|
api_url: `https://${stagingHost}`,
|
|
7000
|
+
service_slug: serviceSlug,
|
|
7001
|
+
sdk_service_id: application.id,
|
|
7002
|
+
agent_callable: true
|
|
7003
|
+
},
|
|
7004
|
+
{
|
|
7005
|
+
kind: "worker",
|
|
7006
|
+
environment: "staging",
|
|
7007
|
+
target_name: `topolo-${slug}-api-staging`,
|
|
7008
|
+
cloudflare_account_id: STAGING_CLOUDFLARE_ACCOUNT_ID,
|
|
7009
|
+
config_path: "apps/api/wrangler.toml",
|
|
7010
|
+
workdir: "apps/api",
|
|
7011
|
+
build_command: null,
|
|
7012
|
+
output_dir: null,
|
|
7013
|
+
production_branch: "staging",
|
|
7014
|
+
healthcheck_path: "/api/health",
|
|
7015
|
+
domains: [],
|
|
7016
|
+
routes: [],
|
|
6138
7017
|
service_slug: serviceSlug
|
|
7018
|
+
},
|
|
7019
|
+
{
|
|
7020
|
+
kind: "worker",
|
|
7021
|
+
environment: "production",
|
|
7022
|
+
target_name: `topolo-${slug}-production`,
|
|
7023
|
+
cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
|
|
7024
|
+
config_path: "apps/web/wrangler.toml",
|
|
7025
|
+
workdir: "apps/web",
|
|
7026
|
+
build_command: "DEPLOY_ENVIRONMENT=production VITE_TOPOLO_AUTH_URL=https://auth.topolo.app npm run build",
|
|
7027
|
+
output_dir: null,
|
|
7028
|
+
production_branch: "main",
|
|
7029
|
+
healthcheck_path: "/api/health",
|
|
7030
|
+
domains: [host],
|
|
7031
|
+
routes: [host],
|
|
7032
|
+
api_url: application.productionUrl,
|
|
7033
|
+
service_slug: serviceSlug,
|
|
7034
|
+
sdk_service_id: application.id,
|
|
7035
|
+
agent_callable: true
|
|
6139
7036
|
}
|
|
6140
7037
|
] : [
|
|
6141
7038
|
{
|
|
6142
|
-
kind: "
|
|
7039
|
+
kind: "worker",
|
|
6143
7040
|
environment: "production",
|
|
6144
7041
|
target_name: `topolo-${slug}`,
|
|
6145
7042
|
cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
|
|
6146
|
-
config_path:
|
|
7043
|
+
config_path: "wrangler.toml",
|
|
6147
7044
|
build_command: "npm run build",
|
|
6148
7045
|
output_dir: "dist",
|
|
6149
7046
|
production_branch: "main",
|
|
6150
|
-
healthcheck_path:
|
|
7047
|
+
healthcheck_path: "/",
|
|
6151
7048
|
domains: [host],
|
|
6152
7049
|
routes: [host],
|
|
6153
|
-
api_url:
|
|
7050
|
+
api_url: application.productionUrl,
|
|
7051
|
+
agent_callable: false
|
|
6154
7052
|
}
|
|
6155
7053
|
];
|
|
6156
7054
|
return `${json({
|
|
6157
7055
|
name: `topolo-${slug}`,
|
|
6158
7056
|
description: `Canonical documentation for ${toTitle(application.name)} lives in Apps/system/TopoloDocs.`,
|
|
6159
|
-
deploy_method:
|
|
7057
|
+
deploy_method: "wrangler",
|
|
6160
7058
|
ci_provider: "github_actions",
|
|
6161
7059
|
repo_identity: {
|
|
6162
7060
|
provider: "github",
|
|
@@ -6236,7 +7134,7 @@ function systemJson(application, options) {
|
|
|
6236
7134
|
visibility: "internal",
|
|
6237
7135
|
api_contract: {
|
|
6238
7136
|
type: options.api ? "curated" : "none",
|
|
6239
|
-
source: options.api ? `${appPath}/src/worker.ts` : `${appPath}/src/App.tsx`,
|
|
7137
|
+
source: options.api ? `${appPath}/apps/api/src/worker.ts` : `${appPath}/apps/web/src/App.tsx`,
|
|
6240
7138
|
notes: apiNotes
|
|
6241
7139
|
},
|
|
6242
7140
|
primary_hosts: application.productionUrl ? [application.productionUrl] : [],
|
|
@@ -6325,17 +7223,19 @@ function internalDocs(application, options) {
|
|
|
6325
7223
|
"",
|
|
6326
7224
|
"## Architecture",
|
|
6327
7225
|
"",
|
|
6328
|
-
"The generated browser surface uses `@topolo-io/ui-kit` for the shared landing page, shared login page, auth callback state, and shared signed shell. `vite.config.ts` includes the standard React plugin plus React runtime dedupe so linked Topolo packages resolve a single browser React runtime.",
|
|
6329
|
-
options.api ? `The generated app
|
|
7226
|
+
"The generated browser surface in `apps/web` uses `@topolo-io/ui-kit` for the shared landing page, shared login page, auth callback state, and shared signed shell. `apps/web/vite.config.ts` includes the standard React plugin plus React runtime dedupe so linked Topolo packages resolve a single browser React runtime.",
|
|
7227
|
+
options.api ? `The generated app keeps browser service identity in \`${appPath}/apps/web/src/service-identity.ts\` and API runtime service identity in \`${appPath}/apps/api/src/service-identity.ts\`. Runtime code reads the provisioned Auth service id from the API Worker \`SERVICE_ID\` binding and exposes it to the browser through the same-origin service-identity route forwarded by the web Worker.` : `The generated app centralizes browser service identity in \`${appPath}/apps/web/src/service-identity.ts\` and reads Auth identity from the app runtime instead of calling Auth for slug lookups.`,
|
|
6330
7228
|
"",
|
|
6331
7229
|
"## Runtime Surfaces",
|
|
6332
7230
|
"",
|
|
6333
|
-
`- \`${appPath}/src/App.tsx\``,
|
|
6334
|
-
`- \`${appPath}/src/service-identity.ts\``,
|
|
7231
|
+
`- \`${appPath}/apps/web/src/App.tsx\``,
|
|
7232
|
+
`- \`${appPath}/apps/web/src/service-identity.ts\``,
|
|
7233
|
+
`- \`${appPath}/apps/web/wrangler.toml\``,
|
|
6335
7234
|
"- public landing route: `/`",
|
|
6336
7235
|
"- public sign-in route: `/login`",
|
|
6337
7236
|
"- auth callback route: `/auth/callback`",
|
|
6338
|
-
...options.api ? [`- \`${appPath}/src/worker.ts\``] : [],
|
|
7237
|
+
...options.api ? [`- \`${appPath}/apps/api/src/worker.ts\``, `- \`${appPath}/apps/api/wrangler.toml\``] : [],
|
|
7238
|
+
`- \`${appPath}/topolo.app.json\``,
|
|
6339
7239
|
`- \`${appPath}/topolo.cloudcontrol.json\``,
|
|
6340
7240
|
...options.api && application.serviceId ? [`- \`Apps/system/TopoloAuth/catalog/service-manifests/${application.serviceId}.json\``] : [],
|
|
6341
7241
|
"",
|
|
@@ -6494,6 +7394,12 @@ function serviceManifestJson(application, options) {
|
|
|
6494
7394
|
name: "workspace:write",
|
|
6495
7395
|
description: `Manage ${title} ${resourceLabel} data`,
|
|
6496
7396
|
resourcePattern: null
|
|
7397
|
+
},
|
|
7398
|
+
{
|
|
7399
|
+
id: `perm_${permissionPrefix}_usage_write`,
|
|
7400
|
+
name: "usage:write",
|
|
7401
|
+
description: `Record Nexus usage events for ${title}`,
|
|
7402
|
+
resourcePattern: null
|
|
6497
7403
|
}
|
|
6498
7404
|
],
|
|
6499
7405
|
apiKeyScopes: [
|
|
@@ -6508,6 +7414,12 @@ function serviceManifestJson(application, options) {
|
|
|
6508
7414
|
name: "workspace.write",
|
|
6509
7415
|
description: `Manage ${title} ${resourceLabel} data`,
|
|
6510
7416
|
resourcePattern: null
|
|
7417
|
+
},
|
|
7418
|
+
{
|
|
7419
|
+
id: `aks_${permissionPrefix}_usage_write`,
|
|
7420
|
+
name: "usage.write",
|
|
7421
|
+
description: `Record Nexus usage events for ${title}`,
|
|
7422
|
+
resourcePattern: null
|
|
6511
7423
|
}
|
|
6512
7424
|
],
|
|
6513
7425
|
roleBundles: {
|
|
@@ -6948,21 +7860,38 @@ async function applyProvisionedServiceId(manifestPath, serviceId) {
|
|
|
6948
7860
|
// src/commands/apps.ts
|
|
6949
7861
|
import { join as join3 } from "path";
|
|
6950
7862
|
var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
|
|
7863
|
+
var CLOUDCONTROL_LOCAL_APPLICATION = {
|
|
7864
|
+
id: "cloudcontrol",
|
|
7865
|
+
appDir: "TopoloCloudControl",
|
|
7866
|
+
name: "topolo-cloudcontrol",
|
|
7867
|
+
packageName: "topolo-cloudcontrol",
|
|
7868
|
+
productionUrl: null,
|
|
7869
|
+
services: [],
|
|
7870
|
+
deployTargets: []
|
|
7871
|
+
};
|
|
6951
7872
|
function registerAppsCommand(program2) {
|
|
6952
|
-
const apps = program2.command("apps").description("List
|
|
7873
|
+
const apps = program2.command("apps").description("List Topolo platform applications available to the current credential").option("--json", "Output JSON", true).option("--no-json", "Output human-readable table").action(async (opts) => {
|
|
6953
7874
|
const mode = resolveOutputMode(opts.json);
|
|
6954
7875
|
try {
|
|
6955
|
-
const
|
|
7876
|
+
const topolo = await buildClient();
|
|
7877
|
+
if (!topolo) {
|
|
7878
|
+
throw new Error("No credential configured. Run `topolo auth login` or set TOPOLO_API_KEY.");
|
|
7879
|
+
}
|
|
7880
|
+
const rows = applicationRowsFromCatalog(await topolo.client.listServices());
|
|
6956
7881
|
emit({ applications: rows }, mode, () => formatRows(rows));
|
|
6957
7882
|
} catch (err) {
|
|
6958
7883
|
fail(err, mode);
|
|
6959
7884
|
}
|
|
6960
7885
|
});
|
|
6961
|
-
apps.command("get <application>").description(
|
|
7886
|
+
apps.command("get <application>").description("Show one Topolo application available to the current credential.").option("--json", "Output JSON", true).option("--no-json", "Output human-readable details").action(async (application, opts) => {
|
|
6962
7887
|
const mode = resolveOutputMode(opts.json);
|
|
6963
7888
|
try {
|
|
6964
|
-
const
|
|
6965
|
-
|
|
7889
|
+
const topolo = await buildClient();
|
|
7890
|
+
if (!topolo) {
|
|
7891
|
+
throw new Error("No credential configured. Run `topolo auth login` or set TOPOLO_API_KEY.");
|
|
7892
|
+
}
|
|
7893
|
+
const app = resolveCatalogApplication(await topolo.client.listServices(), application);
|
|
7894
|
+
emit({ application: app }, mode, () => formatApplicationRow(app));
|
|
6966
7895
|
} catch (err) {
|
|
6967
7896
|
fail(err, mode);
|
|
6968
7897
|
}
|
|
@@ -7070,24 +7999,32 @@ function registerAppsCommand(program2) {
|
|
|
7070
7999
|
}
|
|
7071
8000
|
});
|
|
7072
8001
|
}
|
|
7073
|
-
function
|
|
7074
|
-
return
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
8002
|
+
function applicationRowsFromCatalog(catalog) {
|
|
8003
|
+
return catalog.filter((entry) => entry.launchable || entry.surfaceKind === "application").map((entry) => ({
|
|
8004
|
+
id: displayCatalogId(entry),
|
|
8005
|
+
name: entry.name ?? entry.slug ?? entry.serviceId,
|
|
8006
|
+
appDir: "-",
|
|
8007
|
+
packageName: null,
|
|
8008
|
+
productionUrl: entry.baseUrl,
|
|
8009
|
+
services: [displayCatalogId(entry)],
|
|
8010
|
+
deployTargets: 0
|
|
8011
|
+
})).sort((left, right) => left.id.localeCompare(right.id));
|
|
8012
|
+
}
|
|
8013
|
+
function resolveCatalogApplication(catalog, application) {
|
|
8014
|
+
const needle = application.trim().toLowerCase();
|
|
8015
|
+
const rows = applicationRowsFromCatalog(catalog);
|
|
8016
|
+
const match = rows.find(
|
|
8017
|
+
(row) => row.id.toLowerCase() === needle || row.name.toLowerCase() === needle || row.services.some((service) => service.toLowerCase() === needle)
|
|
8018
|
+
);
|
|
8019
|
+
if (!match) {
|
|
8020
|
+
throw new Error(`Unknown application "${application}". Run \`topolo apps --json\` to discover applications available to this credential.`);
|
|
8021
|
+
}
|
|
8022
|
+
return match;
|
|
7086
8023
|
}
|
|
7087
8024
|
function resolveApplication(application) {
|
|
7088
8025
|
const id = application.trim().toLowerCase();
|
|
7089
8026
|
if (!APPLICATION_IDS.includes(id)) {
|
|
7090
|
-
throw new Error(`Unknown application "${application}".
|
|
8027
|
+
throw new Error(`Unknown application "${application}". ${formatKnownValues(APPLICATION_IDS, "applications")}`);
|
|
7091
8028
|
}
|
|
7092
8029
|
return APPLICATIONS[id];
|
|
7093
8030
|
}
|
|
@@ -7109,7 +8046,12 @@ function applicationRequirementsPayload(application) {
|
|
|
7109
8046
|
};
|
|
7110
8047
|
}
|
|
7111
8048
|
function applicationAuditPayload(application, options = {}) {
|
|
7112
|
-
const
|
|
8049
|
+
const requestedId = application?.trim().toLowerCase();
|
|
8050
|
+
const report = requestedId === "cloudcontrol" && options.local ? {
|
|
8051
|
+
version: APPLICATION_REQUIREMENTS_VERSION,
|
|
8052
|
+
applications: [auditApplicationRequirements(CLOUDCONTROL_LOCAL_APPLICATION)],
|
|
8053
|
+
migrationQueue: []
|
|
8054
|
+
} : application ? auditAllApplicationRequirements([resolveApplication(application).id]) : auditAllApplicationRequirements();
|
|
7113
8055
|
if (!options.local) return report;
|
|
7114
8056
|
return enrichApplicationAuditWithLocalEvidence(report, { repoRoot: options.repoRoot });
|
|
7115
8057
|
}
|
|
@@ -7232,27 +8174,31 @@ function formatRows(rows) {
|
|
|
7232
8174
|
}
|
|
7233
8175
|
return lines.join("\n");
|
|
7234
8176
|
}
|
|
7235
|
-
function
|
|
7236
|
-
|
|
8177
|
+
function formatApplicationRow(application) {
|
|
8178
|
+
return [
|
|
7237
8179
|
`ID: ${application.id}`,
|
|
7238
8180
|
`Name: ${application.name}`,
|
|
7239
|
-
`Directory: ${application.appDir}`,
|
|
7240
|
-
`Package: ${application.packageName ?? "-"}`,
|
|
7241
8181
|
`URL: ${application.productionUrl ?? "-"}`,
|
|
7242
|
-
`Services: ${application.services.length ? application.services.join(", ") : "-"}
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
lines.push(` - ${parts.join(" ")}`);
|
|
7253
|
-
}
|
|
8182
|
+
`Services: ${application.services.length ? application.services.join(", ") : "-"}`
|
|
8183
|
+
].join("\n");
|
|
8184
|
+
}
|
|
8185
|
+
function displayCatalogId(entry) {
|
|
8186
|
+
for (const value of [entry.slug, entry.name, entry.serviceId]) {
|
|
8187
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
8188
|
+
const candidates = catalogIdCandidates(value);
|
|
8189
|
+
const withoutTopolo = candidates.find((candidate) => !candidate.startsWith("topolo-"));
|
|
8190
|
+
if (withoutTopolo) return withoutTopolo;
|
|
8191
|
+
if (candidates[0]) return candidates[0];
|
|
7254
8192
|
}
|
|
7255
|
-
return
|
|
8193
|
+
return entry.serviceId;
|
|
8194
|
+
}
|
|
8195
|
+
function catalogIdCandidates(value) {
|
|
8196
|
+
const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
8197
|
+
const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
|
|
8198
|
+
return [.../* @__PURE__ */ new Set([
|
|
8199
|
+
normalized,
|
|
8200
|
+
withoutTopolo
|
|
8201
|
+
])].filter(Boolean);
|
|
7256
8202
|
}
|
|
7257
8203
|
function formatRequirements(payload) {
|
|
7258
8204
|
const lines = [
|
|
@@ -7406,6 +8352,12 @@ function normalizeFailOn(value) {
|
|
|
7406
8352
|
}
|
|
7407
8353
|
throw new Error("Invalid --fail-on value. Use missing, needs_review, or partial.");
|
|
7408
8354
|
}
|
|
8355
|
+
function formatKnownValues(values, label) {
|
|
8356
|
+
const sorted = [...values].sort();
|
|
8357
|
+
const preview = sorted.slice(0, 25).join(", ");
|
|
8358
|
+
const suffix = sorted.length > 25 ? `, ... +${sorted.length - 25} more` : "";
|
|
8359
|
+
return `Run discovery for the full ${label} list. Known snapshot sample: ${preview}${suffix}`;
|
|
8360
|
+
}
|
|
7409
8361
|
function statusesAtOrWorse(threshold) {
|
|
7410
8362
|
if (threshold === "missing") return ["missing"];
|
|
7411
8363
|
if (threshold === "needs_review") return ["missing", "needs_review"];
|
|
@@ -7625,26 +8577,34 @@ function registerServicesCommand(program2) {
|
|
|
7625
8577
|
try {
|
|
7626
8578
|
const environment = selectedEnvironment();
|
|
7627
8579
|
const config = await readConfig();
|
|
7628
|
-
const
|
|
8580
|
+
const topolo = await buildClient();
|
|
8581
|
+
if (!topolo) {
|
|
8582
|
+
throw new Error("No credential configured. Run `topolo auth login` or set TOPOLO_API_KEY.");
|
|
8583
|
+
}
|
|
8584
|
+
const rows = buildRows(await topolo.client.listServices(), config.serviceUrls, environment);
|
|
7629
8585
|
emit({ environment, services: rows }, mode, () => formatRows2(rows, environment));
|
|
7630
8586
|
} catch (err) {
|
|
7631
8587
|
fail(err, mode);
|
|
7632
8588
|
}
|
|
7633
8589
|
});
|
|
7634
8590
|
}
|
|
7635
|
-
function buildRows(overrides, environment = selectedEnvironment()) {
|
|
8591
|
+
function buildRows(catalog, overrides, environment = selectedEnvironment()) {
|
|
7636
8592
|
const resolvedOverrides = resolveServiceUrls(overrides, environment);
|
|
7637
|
-
return
|
|
7638
|
-
const
|
|
7639
|
-
const
|
|
8593
|
+
return catalog.map((entry) => {
|
|
8594
|
+
const id = displayServiceId(entry);
|
|
8595
|
+
const defaultUrl = defaultServiceUrl(id, entry.baseUrl);
|
|
8596
|
+
const effectiveUrl = resolveServiceUrl(id, resolvedOverrides) || defaultUrl;
|
|
7640
8597
|
return {
|
|
7641
8598
|
id,
|
|
8599
|
+
serviceId: entry.serviceId,
|
|
8600
|
+
slug: entry.slug,
|
|
7642
8601
|
defaultUrl,
|
|
7643
8602
|
effectiveUrl,
|
|
7644
8603
|
overridden: defaultUrl !== effectiveUrl,
|
|
7645
|
-
environmentSafe: environment !== "staging" || isStagingUrl(effectiveUrl)
|
|
8604
|
+
environmentSafe: environment !== "staging" || isStagingUrl(effectiveUrl),
|
|
8605
|
+
permissions: [...entry.permissions]
|
|
7646
8606
|
};
|
|
7647
|
-
});
|
|
8607
|
+
}).sort((left, right) => left.id.localeCompare(right.id));
|
|
7648
8608
|
}
|
|
7649
8609
|
function formatRows2(rows, environment = selectedEnvironment()) {
|
|
7650
8610
|
const idWidth = Math.max(3, ...rows.map((r) => r.id.length));
|
|
@@ -7665,6 +8625,30 @@ function isStagingUrl(value) {
|
|
|
7665
8625
|
const url = value.toLowerCase();
|
|
7666
8626
|
return url.includes(".stg.topolo.us") || url.includes("localhost") || url.includes("127.0.0.1") || url.includes("topolo-staging");
|
|
7667
8627
|
}
|
|
8628
|
+
function displayServiceId(entry) {
|
|
8629
|
+
for (const value of [entry.slug, entry.name, entry.serviceId]) {
|
|
8630
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
8631
|
+
for (const candidate of serviceIdCandidates(value)) {
|
|
8632
|
+
if (Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, candidate)) {
|
|
8633
|
+
return candidate;
|
|
8634
|
+
}
|
|
8635
|
+
}
|
|
8636
|
+
}
|
|
8637
|
+
return serviceIdCandidates(entry.slug || entry.serviceId)[0] || entry.serviceId;
|
|
8638
|
+
}
|
|
8639
|
+
function defaultServiceUrl(id, fallback) {
|
|
8640
|
+
return DEFAULT_SERVICE_URLS[id] ?? fallback ?? "";
|
|
8641
|
+
}
|
|
8642
|
+
function serviceIdCandidates(value) {
|
|
8643
|
+
const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
8644
|
+
const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
|
|
8645
|
+
return [.../* @__PURE__ */ new Set([
|
|
8646
|
+
normalized,
|
|
8647
|
+
normalized.replace(/-/g, "_"),
|
|
8648
|
+
withoutTopolo,
|
|
8649
|
+
withoutTopolo.replace(/-/g, "_")
|
|
8650
|
+
])].filter(Boolean);
|
|
8651
|
+
}
|
|
7668
8652
|
|
|
7669
8653
|
// src/commands/seed.ts
|
|
7670
8654
|
var DEFAULT_SEED_BASE_URL = "https://seed.stg.topolo.us";
|