@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.
Files changed (3) hide show
  1. package/README.md +15 -9
  2. package/dist/index.js +1343 -359
  3. 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
- messaging: "https://messages.topolo.app",
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": "pages",
127
- "apiUrl": null,
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": null,
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-worker",
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": null,
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": null,
204
- "domains": [],
215
+ "apiUrl": "https://backup.topolo.app",
216
+ "domains": [
217
+ "backup.topolo.app"
218
+ ],
205
219
  "routes": [],
206
- "serviceId": null
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": "pages",
271
- "apiUrl": null,
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": "pages",
392
- "apiUrl": null,
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": "pages",
402
- "apiUrl": null,
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": "pages",
412
- "apiUrl": null,
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": null,
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": "pages",
590
- "apiUrl": null,
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-api.topolo.app",
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-cloud",
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": null,
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": null,
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": null,
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
- "messaging": {
904
- "id": "messaging",
905
- "appDir": "TopoloMessaging",
906
- "name": "topolo-messaging",
907
- "packageName": "topolo-messaging",
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
- "messaging"
979
+ "messages"
911
980
  ],
912
981
  "deployTargets": [
913
982
  {
914
- "name": "topolo-messaging",
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": "messaging"
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-messaging-dispatch",
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": null,
995
- "services": [],
996
- "deployTargets": []
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-worker",
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": null,
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": null,
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": null,
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": "pages",
1295
- "apiUrl": null,
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
- "status.topolo.app"
1513
+ "success.topolo.app"
1365
1514
  ],
1366
- "serviceId": "status"
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": null,
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/auth-middleware is used where applicable",
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 before adding an SDK service ID",
1567
- "Expose stable service IDs through DEFAULT_SERVICE_URLS",
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) || application.deployTargets.some((target) => target.kind === "pages");
1672
- const hasApiSurface = application.services.length > 0 || application.deployTargets.some((target) => Boolean(target.apiUrl));
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 === 0) {
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 baseUrl = resolveServiceUrl(opts.service, this.serviceUrls);
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(opts.service, this.serviceIds);
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: opts.service, path: opts.path, url: urlStr, requestId });
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: opts.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: opts.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(opts.service, opts.path, res.status, parsed);
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: opts.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.trim().toLowerCase();
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 KNOWN_SERVICES = Object.keys(DEFAULT_SERVICE_URLS).sort();
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
- `Generic passthrough to a Topolo service. Known services: ${KNOWN_SERVICES.join(", ")}.`
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
- assertServiceUrlForEnvironment(
2996
- service,
2997
- resolveServiceUrl(service, profile.serviceUrls),
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.8";
3060
- var REQUIRED_UI_KIT_VERSION = "0.4.63";
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
- if (application.id === "cloudcontrol") {
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=${application.id}`],
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 (application.id === "cloudcontrol") {
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=${application.id}`, ...items],
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: `${base}/vite.config.ts`,
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: `${base}/index.html`,
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: `${base}/src/main.tsx`,
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: `${base}/src/env.d.ts`,
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: `${base}/src/styles.css`,
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: `${base}/src/service-identity.ts`,
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: `${base}/src/App.tsx`,
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: `${base}/wrangler.toml`,
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: `${base}/src/worker.ts`,
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/docs",
4920
+ " repository: Topolo-io/TopoloDocs",
4549
4921
  " path: Apps/system/TopoloDocs",
4550
- " token: ${{ secrets.TOPOLO_CI_REPO_TOKEN || github.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: ${{ secrets.TOPOLO_CI_REPO_TOKEN || github.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: ${{ secrets.TOPOLO_CI_REPO_TOKEN || github.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-io/widget-server"] = topoloPackageVersion("@topolo-io/widget-server", target);
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-io/widget-server") return "^0.1.1";
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/ui-kit": "topolo-ui-kit",
4690
- "@topolo-io/widget-server": "topolo-widget-server"
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: ["src"],
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('./node_modules/@topolo-io/auth-client/dist/index.js', import.meta.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
- ' activeNavItemId="home"',
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={() => undefined}",
5454
- ` title="${escapeDoubleQuoted(workspaceTitle)}"`,
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
- ' <div className="topolo-starter-workspace">',
5467
- ' <section className="topolo-starter-card">',
5468
- ` <h2>${baselineHeading}</h2>`,
5469
- " <p>",
5470
- ` ${baselineDescription}`,
5471
- " </p>",
5472
- " </section>",
5473
- " </div>",
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 { createWidgetHandler } from '@topolo-io/widget-server';",
5814
- "import { createTopoloWorkerRuntime } from '@topolo-io/worker-runtime';",
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
- "// Required by the platform `native-dashboard-widget` requirement \u2014",
5823
- "// TopoloOne renders this in the user's live workspace whenever the app",
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
- " serviceApiKey: (env) => env.TOPOLO_SERVICE_API_KEY,",
6562
+ " usageMonitoring: {",
6563
+ " appId: SCAFFOLD_APP_ID,",
6564
+ " nexusGatewayUrl: (env) => env.NEXUS_GATEWAY_URL,",
6565
+ " },",
5850
6566
  "});",
5851
6567
  "",
5852
- "const handleFetch = runtime.worker(async (request, env, _ctx, rt): Promise<Response> => {",
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
- " // /api/widget delegates fully to @topolo-io/widget-server \u2014 it owns",
5876
- " // auth, context parsing, response wrapping, and error degradation.",
5877
- " if (url.pathname === '/api/widget') return widgetHandler(request, env);",
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: handleFetch,",
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 { createWidgetHandler } from '@topolo-io/widget-server';",
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
- "// Required by the platform `native-dashboard-widget` requirement \u2014",
5920
- "// TopoloOne renders this in the user's live workspace whenever the app",
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
- " // /api/widget delegates fully to @topolo-io/widget-server.",
5949
- " if (url.pathname === '/api/widget') return widgetHandler(request, env);",
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
- build_command: "npm run build",
6117
- output_dir: "dist",
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: [host],
6121
- routes: [host],
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
- build_command: "VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build",
6132
- output_dir: "dist",
6133
- production_branch: "main",
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: "pages",
7039
+ kind: "worker",
6143
7040
  environment: "production",
6144
7041
  target_name: `topolo-${slug}`,
6145
7042
  cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
6146
- config_path: null,
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: null,
7047
+ healthcheck_path: "/",
6151
7048
  domains: [host],
6152
7049
  routes: [host],
6153
- api_url: null
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: options.api ? "wrangler" : "cloudflare_pages",
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 centralizes service identity in \`${appPath}/src/service-identity.ts\`. Runtime code reads the provisioned Auth service id from the Worker \`SERVICE_ID\` binding and exposes it to the browser through the same-origin service-identity route.` : `The generated app centralizes browser service identity in \`${appPath}/src/service-identity.ts\` and reads Auth identity from the app runtime instead of calling Auth for slug lookups.`,
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 every Topolo platform application known to the SDK catalog").option("--json", "Output JSON", true).option("--no-json", "Output human-readable table").action((opts) => {
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 rows = applicationRows();
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(`Show one Topolo application. Known applications: ${APPLICATION_IDS.join(", ")}.`).option("--json", "Output JSON", true).option("--no-json", "Output human-readable details").action((application, opts) => {
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 app = resolveApplication(application);
6965
- emit({ application: app }, mode, () => formatApplication(app));
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 applicationRows() {
7074
- return APPLICATION_IDS.map((id) => {
7075
- const app = APPLICATIONS[id];
7076
- return {
7077
- id,
7078
- name: app.name,
7079
- appDir: app.appDir,
7080
- packageName: app.packageName,
7081
- productionUrl: app.productionUrl,
7082
- services: [...app.services],
7083
- deployTargets: app.deployTargets.length
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}". Known applications: ${APPLICATION_IDS.join(", ")}`);
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 report = application ? auditAllApplicationRequirements([resolveApplication(application).id]) : auditAllApplicationRequirements();
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 formatApplication(application) {
7236
- const lines = [
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
- "Targets:"
7244
- ];
7245
- if (application.deployTargets.length === 0) {
7246
- lines.push(" - none");
7247
- } else {
7248
- for (const target of application.deployTargets) {
7249
- const parts = [`${target.name} (${target.kind})`];
7250
- if (target.serviceId) parts.push(`service=${target.serviceId}`);
7251
- if (target.apiUrl) parts.push(`api=${target.apiUrl}`);
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 lines.join("\n");
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 rows = buildRows(config.serviceUrls, environment);
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 Object.keys(DEFAULT_SERVICE_URLS).sort().map((id) => {
7638
- const defaultUrl = DEFAULT_SERVICE_URLS[id];
7639
- const effectiveUrl = resolveServiceUrl(id, resolvedOverrides);
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";