@topolo/cli 0.1.23 → 0.1.24

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 +8 -2
  2. package/dist/index.js +1244 -319
  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,31 @@ 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
+ const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
2207
+ if (overrideUrl) return { serviceKey, baseUrl: overrideUrl };
2208
+ const staticUrl = DEFAULT_SERVICE_URLS[serviceKey];
2209
+ if (staticUrl) return { serviceKey, baseUrl: staticUrl };
2210
+ const live = await this.resolveLiveCallableService(serviceKey);
2211
+ if (live) return live;
2212
+ throw new TopoloSdkError(
2213
+ "unknown_service",
2214
+ `Unknown Topolo service "${service}". Run service discovery first and pass a callable service id or slug.`
2215
+ );
2216
+ }
2217
+ async resolveLiveCallableService(serviceKey) {
2218
+ const catalog = await this.listServices();
2219
+ const match = catalog.find((entry) => catalogEntryMatches(entry, serviceKey));
2220
+ if (!match?.baseUrl || !isCallableCatalogEntry(match)) return null;
2221
+ return {
2222
+ serviceKey: normalizeLookupKey(match.slug || match.name || match.serviceId) || serviceKey,
2223
+ baseUrl: match.baseUrl
2224
+ };
2225
+ }
2014
2226
  /**
2015
2227
  * Resolve a service-slug → opaque service-id via caller config or TopoloAuth.
2016
2228
  *
@@ -2068,6 +2280,68 @@ var TopoloClient = class _TopoloClient {
2068
2280
  });
2069
2281
  return value;
2070
2282
  }
2283
+ /**
2284
+ * List the platform service catalog from the live registry
2285
+ * (`GET /api/services/catalog` on TopoloAuth). Credential-aware: the
2286
+ * caller's credential is attached, so an authenticated caller in an
2287
+ * entitled org also receives the `developer_only` services its role
2288
+ * permits; an unauthenticated/agent caller receives `active` only.
2289
+ *
2290
+ * Resilient: on any network/HTTP failure this falls back to the
2291
+ * build-time generated `APPLICATIONS` snapshot so offline/degraded
2292
+ * callers still get a usable (if static) catalog. The live result is
2293
+ * authoritative; the snapshot is a floor, not a cap.
2294
+ */
2295
+ async listServices() {
2296
+ const now = Date.now();
2297
+ if (this.serviceCatalogCache && this.serviceCatalogCache.expiresAt > now) {
2298
+ return this.serviceCatalogCache.entries;
2299
+ }
2300
+ try {
2301
+ const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
2302
+ const raw = res.data?.services ?? res.services;
2303
+ if (Array.isArray(raw) && raw.length > 0) {
2304
+ const entries = [];
2305
+ for (const item of raw) {
2306
+ const s = item;
2307
+ const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
2308
+ if (!serviceId) continue;
2309
+ entries.push({
2310
+ serviceId,
2311
+ slug: typeof s.app_slug === "string" ? s.app_slug : null,
2312
+ name: typeof s.name === "string" ? s.name : null,
2313
+ description: typeof s.description === "string" ? s.description : null,
2314
+ baseUrl: typeof s.base_url === "string" ? s.base_url : null,
2315
+ status: typeof s.status === "string" ? s.status : null,
2316
+ surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
2317
+ launchable: s.launchable === true
2318
+ });
2319
+ }
2320
+ if (entries.length > 0) {
2321
+ this.serviceCatalogCache = {
2322
+ entries,
2323
+ expiresAt: now + CATALOG_CACHE_TTL_MS
2324
+ };
2325
+ return entries;
2326
+ }
2327
+ }
2328
+ } catch {
2329
+ }
2330
+ return this.generatedCatalogFallback();
2331
+ }
2332
+ generatedCatalogFallback() {
2333
+ const apps = Object.values(APPLICATIONS);
2334
+ return apps.map((app) => ({
2335
+ serviceId: app.id,
2336
+ slug: app.id,
2337
+ name: app.name ?? app.id,
2338
+ description: null,
2339
+ baseUrl: app.productionUrl ?? null,
2340
+ status: "active",
2341
+ surfaceKind: "application",
2342
+ launchable: Boolean(app.productionUrl)
2343
+ }));
2344
+ }
2071
2345
  /**
2072
2346
  * Introspect the current credential. Returns the caller's resolved identity,
2073
2347
  * organization, and permission set. Used by CLI `whoami` and by MCP to gate
@@ -2114,8 +2388,18 @@ function resolveConfiguredServiceId(slug, serviceIds) {
2114
2388
  }
2115
2389
  return null;
2116
2390
  }
2391
+ function resolveConfiguredServiceUrl(slug, serviceUrls) {
2392
+ for (const serviceKey of serviceKeyCandidates(slug)) {
2393
+ const configured = serviceUrls?.[serviceKey];
2394
+ if (typeof configured === "string" && configured.trim()) return configured.trim();
2395
+ const envKey = `TOPOLO_SERVICE_URL_${serviceKey.replace(/\./g, "_").toUpperCase()}`;
2396
+ const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
2397
+ if (typeof envValue === "string" && envValue.trim()) return envValue.trim();
2398
+ }
2399
+ return null;
2400
+ }
2117
2401
  function serviceKeyCandidates(slug) {
2118
- const normalized = slug.trim().toLowerCase();
2402
+ const normalized = normalizeLookupKey(slug);
2119
2403
  const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
2120
2404
  return [.../* @__PURE__ */ new Set([
2121
2405
  normalized,
@@ -2124,6 +2408,33 @@ function serviceKeyCandidates(slug) {
2124
2408
  withoutTopolo.replace(/-/g, "_")
2125
2409
  ])];
2126
2410
  }
2411
+ function normalizeLookupKey(value) {
2412
+ return String(value || "").trim().toLowerCase();
2413
+ }
2414
+ function catalogEntryMatches(entry, needle) {
2415
+ return catalogEntryLookupKeys(entry).includes(needle);
2416
+ }
2417
+ function catalogEntryLookupKeys(entry) {
2418
+ const values = [entry.serviceId, entry.slug, entry.name].filter((value) => typeof value === "string" && value.trim().length > 0);
2419
+ const keys = /* @__PURE__ */ new Set();
2420
+ for (const value of values) {
2421
+ const normalized = normalizeLookupKey(value);
2422
+ if (!normalized) continue;
2423
+ keys.add(normalized);
2424
+ keys.add(normalized.replace(/-/g, "_"));
2425
+ if (normalized.startsWith("topolo-")) {
2426
+ const withoutTopolo = normalized.slice("topolo-".length);
2427
+ keys.add(withoutTopolo);
2428
+ keys.add(withoutTopolo.replace(/-/g, "_"));
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(entry.slug && Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, catalogEntryLookupKeys(entry)[0] ?? ""));
2437
+ }
2127
2438
  function describeError(body, fallback) {
2128
2439
  if (body && typeof body === "object") {
2129
2440
  const maybe = body;
@@ -2961,12 +3272,12 @@ function formatContact(c) {
2961
3272
  }
2962
3273
 
2963
3274
  // src/commands/api.ts
2964
- var KNOWN_SERVICES = Object.keys(DEFAULT_SERVICE_URLS).sort();
3275
+ var STATIC_SERVICE_IDS = new Set(Object.keys(DEFAULT_SERVICE_URLS));
2965
3276
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
2966
3277
  var WRITE_METHODS2 = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2967
3278
  function registerApiCommand(program2) {
2968
3279
  program2.command("api <service> <method> <path>").description(
2969
- `Generic passthrough to a Topolo service. Known services: ${KNOWN_SERVICES.join(", ")}.`
3280
+ "Generic passthrough to a Topolo service. Run `topolo services --json` to discover callable services."
2970
3281
  ).option("-d, --data <json>", "JSON body (for POST/PUT/PATCH)").option(
2971
3282
  "-q, --query <kv...>",
2972
3283
  "Query params as key=value (repeatable), e.g. --query source=cli page=2"
@@ -2981,22 +3292,17 @@ function registerApiCommand(program2) {
2981
3292
  );
2982
3293
  }
2983
3294
  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
3295
  if (WRITE_METHODS2.has(httpMethod) && !opts.confirm) {
2990
3296
  throw new Error(`Mutating ${httpMethod} requires --confirm.`);
2991
3297
  }
2992
3298
  const environment = selectedEnvironment();
2993
3299
  const config = await readConfig();
2994
3300
  const profile = resolvedProfileConfig(config, environment);
2995
- assertServiceUrlForEnvironment(
2996
- service,
2997
- resolveServiceUrl(service, profile.serviceUrls),
2998
- environment
2999
- );
3301
+ const configuredUrl = resolveConfiguredServiceUrl2(service, profile.serviceUrls);
3302
+ const staticUrl = STATIC_SERVICE_IDS.has(service) ? DEFAULT_SERVICE_URLS[service] : void 0;
3303
+ if (configuredUrl || staticUrl) {
3304
+ assertServiceUrlForEnvironment(service, configuredUrl ?? staticUrl, environment);
3305
+ }
3000
3306
  let body = void 0;
3001
3307
  if (opts.data) {
3002
3308
  try {
@@ -3027,6 +3333,21 @@ function registerApiCommand(program2) {
3027
3333
  }
3028
3334
  );
3029
3335
  }
3336
+ function resolveConfiguredServiceUrl2(service, serviceUrls) {
3337
+ const normalized = service.trim().toLowerCase();
3338
+ const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
3339
+ const candidates = [
3340
+ normalized,
3341
+ normalized.replace(/-/g, "_"),
3342
+ withoutTopolo,
3343
+ withoutTopolo.replace(/-/g, "_")
3344
+ ];
3345
+ for (const candidate of candidates) {
3346
+ const url = serviceUrls?.[candidate];
3347
+ if (typeof url === "string" && url.trim()) return url.trim();
3348
+ }
3349
+ return void 0;
3350
+ }
3030
3351
  function parseQueryPairs(pairs) {
3031
3352
  if (!pairs || pairs.length === 0) return void 0;
3032
3353
  const out = {};
@@ -3056,8 +3377,8 @@ import {
3056
3377
  } from "fs";
3057
3378
  import path from "path";
3058
3379
  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";
3380
+ var REQUIRED_AUTH_CLIENT_VERSION = "1.1.9";
3381
+ var REQUIRED_UI_KIT_VERSION = "0.4.67";
3061
3382
  function readEvidenceFile(appRoot) {
3062
3383
  const candidate = path.join(appRoot, ".topolo", "evidence.json");
3063
3384
  if (!isFile(candidate)) return null;
@@ -3264,11 +3585,12 @@ function enrichFinding(application, finding2, evidence) {
3264
3585
  case "service-registration-and-scopes": {
3265
3586
  const declaredServices = application.services ?? [];
3266
3587
  if (declaredServices.length === 0) {
3267
- if (application.id === "cloudcontrol") {
3588
+ const applicationId = String(application.id);
3589
+ if (applicationId === "cloudcontrol") {
3268
3590
  return mergeFinding(finding2, {
3269
3591
  status: "met",
3270
3592
  reason: "This application is itself a tooling/agent surface and does not register a callable platform service.",
3271
- evidence: [`tooling=${application.id}`],
3593
+ evidence: [`tooling=${applicationId}`],
3272
3594
  nextActions: []
3273
3595
  });
3274
3596
  }
@@ -3442,6 +3764,7 @@ function enrichFinding(application, finding2, evidence) {
3442
3764
  case "agent-and-operator-surfaces": {
3443
3765
  const declaredServices = application.services ?? [];
3444
3766
  const text = evidence.source.text;
3767
+ const applicationId = String(application.id);
3445
3768
  const items = [];
3446
3769
  if (declaredServices.length > 0) items.push(`services=${declaredServices.join(",")}`);
3447
3770
  if (text.includes("TopoloClient")) items.push("source=TopoloClient");
@@ -3449,11 +3772,11 @@ function enrichFinding(application, finding2, evidence) {
3449
3772
  items.push("source=confirm-write-gate");
3450
3773
  }
3451
3774
  if (text.includes("@topolo/sdk")) items.push("source=@topolo/sdk");
3452
- if (application.id === "cloudcontrol") {
3775
+ if (applicationId === "cloudcontrol") {
3453
3776
  return mergeFinding(finding2, {
3454
3777
  status: "met",
3455
3778
  reason: "This application is itself an agent/operator surface (CloudControl).",
3456
- evidence: [`tooling=${application.id}`, ...items],
3779
+ evidence: [`tooling=${applicationId}`, ...items],
3457
3780
  nextActions: []
3458
3781
  });
3459
3782
  }
@@ -4231,6 +4554,8 @@ function scaffoldFiles(options) {
4231
4554
  const { application, target } = options;
4232
4555
  const slug = toSlug(application.id);
4233
4556
  const base = target === "platform" ? firstPartyAppPath(application.appDir, options.payload.audience) : application.appDir;
4557
+ const browserBase = target === "platform" ? `${base}/apps/web` : base;
4558
+ const apiBase = target === "platform" ? `${base}/apps/api` : base;
4234
4559
  const files = [
4235
4560
  {
4236
4561
  path: `${base}/package.json`,
@@ -4277,10 +4602,10 @@ function scaffoldFiles(options) {
4277
4602
  "uses strict mode",
4278
4603
  "includes browser DOM types for React and Worker types for API-backed apps"
4279
4604
  ],
4280
- content: tsconfigJson(options.api)
4605
+ content: tsconfigJson(options.api, target)
4281
4606
  },
4282
4607
  {
4283
- path: `${base}/vite.config.ts`,
4608
+ path: `${browserBase}/vite.config.ts`,
4284
4609
  purpose: "Vite browser build configuration.",
4285
4610
  requiredSignals: [
4286
4611
  "builds the authenticated browser surface",
@@ -4289,7 +4614,7 @@ function scaffoldFiles(options) {
4289
4614
  content: viteConfig(target)
4290
4615
  },
4291
4616
  {
4292
- path: `${base}/index.html`,
4617
+ path: `${browserBase}/index.html`,
4293
4618
  purpose: "Browser entry document.",
4294
4619
  requiredSignals: [
4295
4620
  "mounts the React app at #root",
@@ -4298,7 +4623,7 @@ function scaffoldFiles(options) {
4298
4623
  content: indexHtml(application.name)
4299
4624
  },
4300
4625
  {
4301
- path: `${base}/src/main.tsx`,
4626
+ path: `${browserBase}/src/main.tsx`,
4302
4627
  purpose: "React app entrypoint.",
4303
4628
  requiredSignals: [
4304
4629
  "renders the generated app component",
@@ -4307,7 +4632,7 @@ function scaffoldFiles(options) {
4307
4632
  content: mainTsx()
4308
4633
  },
4309
4634
  {
4310
- path: `${base}/src/env.d.ts`,
4635
+ path: `${browserBase}/src/env.d.ts`,
4311
4636
  purpose: "Vite environment variable typing.",
4312
4637
  requiredSignals: [
4313
4638
  "loads vite/client import.meta.env types",
@@ -4327,7 +4652,7 @@ function scaffoldFiles(options) {
4327
4652
  }
4328
4653
  ] : [],
4329
4654
  {
4330
- path: `${base}/src/styles.css`,
4655
+ path: `${browserBase}/src/styles.css`,
4331
4656
  purpose: "Minimal app-local styles outside the shared Topolo shell.",
4332
4657
  requiredSignals: [
4333
4658
  "does not fork the shared shell chrome",
@@ -4338,6 +4663,19 @@ function scaffoldFiles(options) {
4338
4663
  ];
4339
4664
  if (target === "platform") {
4340
4665
  files.push(
4666
+ {
4667
+ path: `${base}/topolo.app.json`,
4668
+ purpose: "Canonical app manifest that drives generated Wrangler and CloudControl metadata.",
4669
+ requiredSignals: [
4670
+ "declares web/api targets with canonical names and app-local target directories",
4671
+ "keeps Cloudflare account, domain, service id, and bindings in one manifest source of truth"
4672
+ ],
4673
+ content: topoloAppManifestJson(application, {
4674
+ api: options.api,
4675
+ resources: options.payload.resources,
4676
+ profile: options.payload.profile
4677
+ })
4678
+ },
4341
4679
  {
4342
4680
  path: `${base}/topolo.cloudcontrol.json`,
4343
4681
  purpose: "CloudControl source of truth for deploy targets, routes, bindings, and CI metadata.",
@@ -4463,7 +4801,7 @@ function scaffoldFiles(options) {
4463
4801
  if (options.browser) {
4464
4802
  if (target === "platform") {
4465
4803
  files.push({
4466
- path: `${base}/src/service-identity.ts`,
4804
+ path: `${browserBase}/src/service-identity.ts`,
4467
4805
  purpose: "Central service identity resolver shared by browser and Worker runtime surfaces.",
4468
4806
  requiredSignals: [
4469
4807
  "exports the generated app id, display name, and stable Auth service slug",
@@ -4473,7 +4811,7 @@ function scaffoldFiles(options) {
4473
4811
  });
4474
4812
  }
4475
4813
  files.push({
4476
- path: `${base}/src/App.tsx`,
4814
+ path: `${browserBase}/src/App.tsx`,
4477
4815
  purpose: "Authenticated browser surface.",
4478
4816
  requiredSignals: [
4479
4817
  "imports shared TopoloAppShell from @topolo-io/ui-kit",
@@ -4490,16 +4828,16 @@ function scaffoldFiles(options) {
4490
4828
  if (options.api) {
4491
4829
  files.push(
4492
4830
  {
4493
- path: `${base}/wrangler.toml`,
4831
+ path: `${apiBase}/wrangler.toml`,
4494
4832
  purpose: "Cloudflare Worker runtime configuration.",
4495
4833
  requiredSignals: [
4496
4834
  "bindings match topolo.cloudcontrol.json or topolo.app.json",
4497
4835
  "secrets are referenced by name only"
4498
4836
  ],
4499
- content: wranglerToml(application, target, options.payload.resources, options.payload.profile)
4837
+ content: target === "platform" ? platformApiWranglerToml(application, options.payload.resources, options.payload.profile) : wranglerToml(application, target, options.payload.resources, options.payload.profile)
4500
4838
  },
4501
4839
  {
4502
- path: `${base}/src/worker.ts`,
4840
+ path: `${apiBase}/src/worker.ts`,
4503
4841
  purpose: "Protected service boundary.",
4504
4842
  requiredSignals: [
4505
4843
  "uses @topolo-io/worker-runtime for Worker auth, gatewaying, headers, and request helpers",
@@ -4514,6 +4852,37 @@ function scaffoldFiles(options) {
4514
4852
  })
4515
4853
  }
4516
4854
  );
4855
+ if (target === "platform") {
4856
+ files.push(
4857
+ {
4858
+ path: `${apiBase}/src/service-identity.ts`,
4859
+ purpose: "Runtime service identity resolver for the API Worker.",
4860
+ requiredSignals: [
4861
+ "resolves the concrete Auth service id from Worker vars",
4862
+ "keeps the API Worker independent from browser-only import.meta usage"
4863
+ ],
4864
+ content: apiServiceIdentityTs(application)
4865
+ },
4866
+ {
4867
+ path: `${browserBase}/src/worker.ts`,
4868
+ purpose: "Browser Worker that serves assets and forwards API paths to the API Worker binding.",
4869
+ requiredSignals: [
4870
+ "keeps the frontend Worker thin",
4871
+ "routes /api/* to the API service binding instead of owning backend routes locally"
4872
+ ],
4873
+ content: webWorkerTs(application)
4874
+ },
4875
+ {
4876
+ path: `${browserBase}/wrangler.toml`,
4877
+ purpose: "Cloudflare Worker static-assets configuration for the browser surface.",
4878
+ requiredSignals: [
4879
+ "serves Vite assets through a Worker with assets",
4880
+ "binds the frontend Worker to the API Worker for /api/* requests"
4881
+ ],
4882
+ content: platformWebWranglerToml(application)
4883
+ }
4884
+ );
4885
+ }
4517
4886
  }
4518
4887
  return files;
4519
4888
  }
@@ -4543,20 +4912,22 @@ function topoloConformanceWorkflow(options) {
4543
4912
  const docsCheckoutStep = options.appDir === "TopoloDocs" ? [] : [
4544
4913
  "",
4545
4914
  " - name: Checkout TopoloDocs",
4915
+ ` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
4546
4916
  " uses: actions/checkout@v4",
4547
4917
  " with:",
4548
- " repository: Topolo-io/docs",
4918
+ " repository: Topolo-io/TopoloDocs",
4549
4919
  " path: Apps/system/TopoloDocs",
4550
- " token: ${{ secrets.TOPOLO_CI_REPO_TOKEN || github.token }}"
4920
+ " token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}"
4551
4921
  ];
4552
4922
  const cliCheckoutStep = options.appDir === "TopoloCLI" || options.appDir === "TopoloCli" ? [] : [
4553
4923
  "",
4554
4924
  " - name: Checkout Topolo CLI",
4925
+ ` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
4555
4926
  " uses: actions/checkout@v4",
4556
4927
  " with:",
4557
4928
  " repository: Topolo-io/TopoloCli",
4558
4929
  " path: Apps/system/TopoloCLI",
4559
- " token: ${{ secrets.TOPOLO_CI_REPO_TOKEN || github.token }}"
4930
+ " token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}"
4560
4931
  ];
4561
4932
  return [
4562
4933
  "name: Topolo App Conformance",
@@ -4574,44 +4945,66 @@ function topoloConformanceWorkflow(options) {
4574
4945
  "jobs:",
4575
4946
  " conformance:",
4576
4947
  " runs-on: ubuntu-latest",
4948
+ " env:",
4949
+ ` HAS_TOPOLO_PACKAGES_APP: \${{ secrets.TOPOLO_PACKAGES_APP_ID != '' && secrets.TOPOLO_PACKAGES_APP_PRIVATE_KEY != '' }}`,
4950
+ ` HAS_TOPOLO_CI_REPO_TOKEN: \${{ secrets.TOPOLO_CI_REPO_TOKEN != '' }}`,
4577
4951
  " steps:",
4578
4952
  " - name: Checkout application",
4579
4953
  " uses: actions/checkout@v4",
4580
4954
  " with:",
4581
4955
  ` path: ${appPath}`,
4956
+ "",
4957
+ " - name: Mint Topolo cross-repo token",
4958
+ " id: app-token",
4959
+ ` if: env.HAS_TOPOLO_PACKAGES_APP == 'true'`,
4960
+ " uses: actions/create-github-app-token@v1",
4961
+ " with:",
4962
+ ` app-id: \${{ secrets.TOPOLO_PACKAGES_APP_ID }}`,
4963
+ ` private-key: \${{ secrets.TOPOLO_PACKAGES_APP_PRIVATE_KEY }}`,
4964
+ " owner: Topolo-io",
4582
4965
  ...docsCheckoutStep,
4583
4966
  "",
4584
4967
  " - name: Checkout Topolo SDK",
4968
+ ` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
4585
4969
  " uses: actions/checkout@v4",
4586
4970
  " with:",
4587
4971
  " repository: Topolo-io/topolo-sdk",
4588
4972
  " path: Apps/packages/topolo-sdk",
4589
- " token: ${{ secrets.TOPOLO_CI_REPO_TOKEN || github.token }}",
4973
+ " token: ${{ steps.app-token.outputs.token || secrets.TOPOLO_CI_REPO_TOKEN }}",
4590
4974
  ...cliCheckoutStep,
4591
4975
  "",
4976
+ " - name: Skip conformance without cross-repo token",
4977
+ ` if: steps.app-token.outputs.token == '' && env.HAS_TOPOLO_CI_REPO_TOKEN != 'true'`,
4978
+ " run: |",
4979
+ ' echo "::warning::Skipping Topolo app conformance because no Topolo cross-repo token is available to this workflow context."',
4980
+ "",
4592
4981
  " - name: Setup Node.js",
4982
+ ` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
4593
4983
  " uses: actions/setup-node@v4",
4594
4984
  " with:",
4595
4985
  " node-version: 20",
4596
4986
  "",
4597
4987
  " - name: Build Topolo SDK",
4988
+ ` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
4598
4989
  " working-directory: Apps/packages/topolo-sdk",
4599
4990
  " run: |",
4600
4991
  " npm install --no-audit --no-fund",
4601
4992
  " npm run build",
4602
4993
  "",
4603
4994
  " - name: Build Topolo CLI",
4995
+ ` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
4604
4996
  " working-directory: Apps/system/TopoloCLI",
4605
4997
  " run: |",
4606
4998
  " npm install --no-audit --no-fund",
4607
4999
  " npm run build",
4608
5000
  "",
4609
5001
  " - name: Run Topolo app conformance audit",
5002
+ ` if: steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true'`,
4610
5003
  " run: |",
4611
5004
  ` node Apps/system/TopoloCLI/dist/index.js apps audit ${options.id} --local --repo-root "$GITHUB_WORKSPACE" --fail-on needs_review --json`,
4612
5005
  "",
4613
5006
  " - name: Record green CI evidence",
4614
- ` if: \${{ success() }}`,
5007
+ ` if: \${{ success() && (steps.app-token.outputs.token != '' || env.HAS_TOPOLO_CI_REPO_TOKEN == 'true') }}`,
4615
5008
  ` working-directory: ${appPath}`,
4616
5009
  " run: |",
4617
5010
  ` node "$GITHUB_WORKSPACE/Apps/system/TopoloCLI/dist/index.js" apps record-evidence ${options.id} --build --typecheck --test --commit "$GITHUB_SHA"`
@@ -4625,9 +5018,9 @@ function packageJson(application, target, options) {
4625
5018
  vite: "^5.4.0"
4626
5019
  };
4627
5020
  const scripts = {
4628
- dev: "vite --host 0.0.0.0",
4629
- build: "vite build",
4630
- preview: "vite preview --host 0.0.0.0",
5021
+ dev: target === "platform" ? "vite --config apps/web/vite.config.ts --host 0.0.0.0" : "vite --host 0.0.0.0",
5022
+ build: target === "platform" ? "vite build --config apps/web/vite.config.ts" : "vite build",
5023
+ preview: target === "platform" ? "vite preview --config apps/web/vite.config.ts --host 0.0.0.0" : "vite preview --host 0.0.0.0",
4631
5024
  typecheck: "tsc --noEmit"
4632
5025
  };
4633
5026
  if (options.browser) {
@@ -4639,27 +5032,30 @@ function packageJson(application, target, options) {
4639
5032
  if (target === "platform") {
4640
5033
  dependencies["@topolo-io/auth-client"] = topoloPackageVersion("@topolo-io/auth-client", target);
4641
5034
  dependencies["@topolo-io/design-tokens"] = topoloPackageVersion("@topolo-io/design-tokens", target);
5035
+ dependencies["@topolo-io/onboarding"] = topoloPackageVersion("@topolo-io/onboarding", target);
5036
+ dependencies["@topolo-io/tour"] = topoloPackageVersion("@topolo-io/tour", target);
4642
5037
  dependencies["@topolo-io/ui-kit"] = topoloPackageVersion("@topolo-io/ui-kit", target);
4643
5038
  } else {
4644
5039
  dependencies["@topolo/login"] = topoloPackageVersion("@topolo/login", target);
4645
5040
  }
4646
5041
  }
4647
5042
  if (options.api) {
5043
+ devDependencies["@cloudflare/workers-types"] = "^4.20260413.1";
4648
5044
  if (target === "platform") {
4649
5045
  dependencies["@topolo-io/worker-runtime"] = topoloPackageVersion("@topolo-io/worker-runtime", target);
4650
- dependencies["@topolo-io/widget-server"] = topoloPackageVersion("@topolo-io/widget-server", target);
4651
5046
  dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
5047
+ dependencies.hono = "^4";
4652
5048
  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";
5049
+ 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";
5050
+ 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
5051
  } else {
4656
- dependencies["@topolo-io/widget-server"] = topoloPackageVersion("@topolo-io/widget-server", target);
5052
+ dependencies["@topolo/sdk"] = topoloPackageVersion("@topolo/sdk", target);
4657
5053
  scripts.deploy = "npm run build && wrangler deploy";
4658
5054
  }
4659
5055
  devDependencies.wrangler = "^4.40.2";
4660
5056
  }
4661
5057
  if (target === "platform" && hasScaffoldResources(options.resources)) {
4662
- scripts["check:deploy-config"] = "wrangler deploy --dry-run --outdir .wrangler/build";
5058
+ 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
5059
  }
4664
5060
  return `${json({
4665
5061
  name: application.packageName,
@@ -4676,7 +5072,7 @@ function topoloPackageVersion(packageName, target) {
4676
5072
  if (target === "developer") {
4677
5073
  if (packageName === "@topolo/login") return "^0.1.0";
4678
5074
  if (packageName === "@topolo-io/auth-client") return "^1.0.0";
4679
- if (packageName === "@topolo-io/widget-server") return "^0.1.1";
5075
+ if (packageName === "@topolo/sdk") return "^0.4.1";
4680
5076
  return "^0.1.0";
4681
5077
  }
4682
5078
  const platformPackageFolders = {
@@ -4686,8 +5082,9 @@ function topoloPackageVersion(packageName, target) {
4686
5082
  "@topolo-io/auth-middleware": "topolo-auth-middleware",
4687
5083
  "@topolo-io/worker-runtime": "topolo-worker-runtime",
4688
5084
  "@topolo-io/design-tokens": "topolo-design-tokens",
4689
- "@topolo-io/ui-kit": "topolo-ui-kit",
4690
- "@topolo-io/widget-server": "topolo-widget-server"
5085
+ "@topolo-io/onboarding": "topolo-onboarding",
5086
+ "@topolo-io/tour": "topolo-tour",
5087
+ "@topolo-io/ui-kit": "topolo-ui-kit"
4691
5088
  };
4692
5089
  const folder = platformPackageFolders[packageName];
4693
5090
  if (!folder) {
@@ -4772,7 +5169,7 @@ function readme(application, target, options) {
4772
5169
  "- `vite.config.ts` includes the standard React plugin plus React runtime dedupe so linked Topolo packages resolve one browser React runtime."
4773
5170
  );
4774
5171
  if (options.api) {
4775
- lines.push("- `@topolo-io/worker-runtime` owns Worker auth validation, `/api/auth/*` gatewaying, security headers, request ids, and BugFix reporting.");
5172
+ 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
5173
  }
4777
5174
  if (hasScaffoldResources(options.resources)) {
4778
5175
  lines.push(
@@ -4816,7 +5213,8 @@ function readme(application, target, options) {
4816
5213
  return `${lines.join("\n")}
4817
5214
  `;
4818
5215
  }
4819
- function tsconfigJson(api) {
5216
+ function tsconfigJson(api, target) {
5217
+ const include = target === "platform" ? ["apps/web/src", ...api ? ["apps/api/src"] : []] : ["src"];
4820
5218
  return `${json({
4821
5219
  compilerOptions: {
4822
5220
  target: "ES2022",
@@ -4834,9 +5232,9 @@ function tsconfigJson(api) {
4834
5232
  isolatedModules: true,
4835
5233
  noEmit: true,
4836
5234
  jsx: "react-jsx",
4837
- types: ["vite/client"]
5235
+ types: api ? ["vite/client", "@cloudflare/workers-types"] : ["vite/client"]
4838
5236
  },
4839
- include: ["src"],
5237
+ include,
4840
5238
  references: []
4841
5239
  })}
4842
5240
  `;
@@ -4958,6 +5356,32 @@ function serviceIdentityTs(application) {
4958
5356
  ""
4959
5357
  ].join("\n");
4960
5358
  }
5359
+ function apiServiceIdentityTs(application) {
5360
+ const appName = toTitle(application.name);
5361
+ const serviceSlug = application.serviceSlug ?? `topolo-${toSlug(application.id)}`;
5362
+ return [
5363
+ `export const SCAFFOLD_APP_ID = '${application.id}';`,
5364
+ `export const SCAFFOLD_APP_NAME = '${escapeSingleQuoted(appName)}';`,
5365
+ `export const SCAFFOLD_SERVICE_SLUG = '${escapeSingleQuoted(serviceSlug)}';`,
5366
+ "",
5367
+ "interface ServiceIdentityEnv {",
5368
+ " SERVICE_ID?: string;",
5369
+ "}",
5370
+ "",
5371
+ "function requireServiceId(value: unknown): string {",
5372
+ " const serviceId = typeof value === 'string' ? value.trim() : '';",
5373
+ " if (!serviceId) {",
5374
+ " throw new Error(`SERVICE_ID is required for ${SCAFFOLD_SERVICE_SLUG}`);",
5375
+ " }",
5376
+ " return serviceId;",
5377
+ "}",
5378
+ "",
5379
+ "export function resolveRuntimeServiceId(env: ServiceIdentityEnv): Promise<string> {",
5380
+ " return Promise.resolve(requireServiceId(env.SERVICE_ID));",
5381
+ "}",
5382
+ ""
5383
+ ].join("\n");
5384
+ }
4961
5385
  function cloudflareWorkersDts() {
4962
5386
  return [
4963
5387
  "declare module 'cloudflare:workers' {",
@@ -5025,10 +5449,11 @@ function viteConfig(target) {
5025
5449
  " process.env.VITE_TOPOLO_AUTH_URL === 'https://auth.stg.topolo.us';",
5026
5450
  "",
5027
5451
  " return {",
5452
+ " root: fileURLToPath(new URL('.', import.meta.url)),",
5028
5453
  " plugins: [react(), ...(isStagingBuild ? [stagingUrlRewrite()] : [])],",
5029
5454
  " resolve: {",
5030
5455
  " alias: {",
5031
- " '@topolo-io/auth-client': fileURLToPath(new URL('./node_modules/@topolo-io/auth-client/dist/index.js', import.meta.url)),",
5456
+ " '@topolo-io/auth-client': fileURLToPath(new URL('../../node_modules/@topolo-io/auth-client/dist/index.js', import.meta.url)),",
5032
5457
  " },",
5033
5458
  " dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],",
5034
5459
  " },",
@@ -5060,6 +5485,8 @@ function mainTsx() {
5060
5485
  "import React from 'react';",
5061
5486
  "import { createRoot } from 'react-dom/client';",
5062
5487
  "import '@topolo-io/design-tokens/tokens.css';",
5488
+ "import '@topolo-io/tour/styles.css';",
5489
+ "import '@topolo-io/onboarding/styles.css';",
5063
5490
  "import App from './App';",
5064
5491
  "import './styles.css';",
5065
5492
  "",
@@ -5162,16 +5589,19 @@ function platformAppTsx(application, options) {
5162
5589
  const baselineHeading = isPersonal ? "Shared personal baseline" : "Shared workspace baseline";
5163
5590
  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
5591
  return [
5165
- "import { Component, useEffect, useMemo, useState, type ErrorInfo, type ReactNode } from 'react';",
5592
+ "import { Component, useCallback, useEffect, useMemo, useState, type ErrorInfo, type ReactNode } from 'react';",
5166
5593
  "import { createBrowserAuthSessionController, type AuthUser } from '@topolo-io/auth-client';",
5167
5594
  "import { LandingPage, LoginPage, TopoloAppShell, TopoloAuthFlowScreen, TopoloLoadingScreen, setupBugReporterErrorCapture, type TopoloAppShellUser } from '@topolo-io/ui-kit';",
5595
+ "import { GettingStarted, type OnboardingGuideStep } from '@topolo-io/onboarding';",
5596
+ "import { IntroTour } from '@topolo-io/tour';",
5168
5597
  "import { SCAFFOLD_APP_ID, SCAFFOLD_APP_NAME, SCAFFOLD_SERVICE_SLUG, getBrowserAuthBaseUrl, resolveAppServiceId } from './service-identity';",
5169
5598
  "",
5170
5599
  "// Wire global error/console capture so BugReporter (enabled in TopoloAppShell below)",
5171
5600
  "// can attach the last few errors to any submitted bug report.",
5172
5601
  "setupBugReporterErrorCapture();",
5173
5602
  "",
5174
- "type NavItem = 'home';",
5603
+ "type NavItem = 'home' | 'getting-started';",
5604
+ "type WorkspaceView = 'workspace' | 'getting-started';",
5175
5605
  "type AppRoute = { kind: 'root' } | { kind: 'login' } | { kind: 'callback' };",
5176
5606
  "type BrowserAuthModule = ReturnType<typeof createBrowserAuthSessionController>;",
5177
5607
  "",
@@ -5248,7 +5678,17 @@ function platformAppTsx(application, options) {
5248
5678
  " const [loading, setLoading] = useState(true);",
5249
5679
  " const [authError, setAuthError] = useState('');",
5250
5680
  " const [loginAttempted, setLoginAttempted] = useState(false);",
5681
+ " const [view, setView] = useState<WorkspaceView>('workspace');",
5251
5682
  " const auth = useMemo(() => serviceId ? getAuthModule(serviceId) : null, [serviceId]);",
5683
+ " const openWorkspace = useCallback(() => setView('workspace'), []);",
5684
+ " const openGettingStarted = useCallback(() => {",
5685
+ " setView('getting-started');",
5686
+ " if (typeof window !== 'undefined') window.scrollTo({ top: 0, behavior: 'smooth' });",
5687
+ " }, []);",
5688
+ " const handleNavSelect = useCallback((id: NavItem) => {",
5689
+ " if (id === 'getting-started') openGettingStarted();",
5690
+ " else openWorkspace();",
5691
+ " }, [openGettingStarted, openWorkspace]);",
5252
5692
  "",
5253
5693
  " useEffect(() => {",
5254
5694
  " let active = true;",
@@ -5447,11 +5887,11 @@ function platformAppTsx(application, options) {
5447
5887
  " orgId={user.activeContextType === 'organization' ? user.activeContextId ?? user.orgId ?? null : null}",
5448
5888
  " activeContextType={user.activeContextType}",
5449
5889
  " activeContextId={user.activeContextId}",
5450
- ' activeNavItemId="home"',
5451
- ` navItems={[{ id: 'home', label: '${escapeSingleQuoted(workspaceTitle)}' }]}`,
5890
+ " activeNavItemId={view === 'getting-started' ? 'getting-started' : 'home'}",
5891
+ ` navItems={[{ id: 'home', label: '${escapeSingleQuoted(workspaceTitle)}' }, { id: 'getting-started', label: 'Getting started' }]}`,
5452
5892
  ' navigationMode="topbar"',
5453
- " onNavItemSelect={() => undefined}",
5454
- ` title="${escapeDoubleQuoted(workspaceTitle)}"`,
5893
+ " onNavItemSelect={handleNavSelect}",
5894
+ ` title={view === 'getting-started' ? 'Getting started' : '${escapeSingleQuoted(workspaceTitle)}'}`,
5455
5895
  " authUrl={AUTH_URL}",
5456
5896
  " getAccessToken={getAccessToken}",
5457
5897
  " onSignOut={async () => {",
@@ -5463,18 +5903,94 @@ function platformAppTsx(application, options) {
5463
5903
  " enableCommandPalette",
5464
5904
  " enableBugReporter",
5465
5905
  " >",
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>",
5906
+ " {view === 'getting-started' ? (",
5907
+ " <ScaffoldGettingStarted",
5908
+ " serviceId={activeServiceId}",
5909
+ " organizationId={user.organization?.id ?? user.orgId ?? ''}",
5910
+ " role={user.role}",
5911
+ " onOpenWorkspace={openWorkspace}",
5912
+ " />",
5913
+ " ) : (",
5914
+ ' <div className="topolo-starter-workspace">',
5915
+ ' <section className="topolo-starter-card" data-tour="workspace-card">',
5916
+ ` <h2>${baselineHeading}</h2>`,
5917
+ " <p>",
5918
+ ` ${baselineDescription}`,
5919
+ " </p>",
5920
+ " </section>",
5921
+ " </div>",
5922
+ " )}",
5923
+ " <IntroTour",
5924
+ " appName={APP_NAME}",
5925
+ " role={user.role}",
5926
+ " onOpenGettingStarted={openGettingStarted}",
5927
+ " extraSteps={[",
5928
+ " {",
5929
+ " id: 'workspace-card',",
5930
+ ` target: '[data-tour="workspace-card"]',`,
5931
+ " title: 'Your workspace',",
5932
+ " content: 'Everything you build renders here, inside the shared Topolo shell.',",
5933
+ " placement: 'bottom' as const,",
5934
+ " beforeStep: () => openWorkspace(),",
5935
+ " },",
5936
+ " ]}",
5937
+ " />",
5474
5938
  " </TopoloAppShell>",
5475
5939
  " );",
5476
5940
  "}",
5477
5941
  "",
5942
+ "function ScaffoldGettingStarted(props: {",
5943
+ " serviceId: string;",
5944
+ " organizationId: string;",
5945
+ " role?: string;",
5946
+ " onOpenWorkspace: () => void;",
5947
+ "}) {",
5948
+ " const { serviceId, organizationId, role, onOpenWorkspace } = props;",
5949
+ " const steps: OnboardingGuideStep[] = [",
5950
+ " {",
5951
+ " id: 'explore-workspace',",
5952
+ " title: 'Explore your workspace',",
5953
+ " description:",
5954
+ " 'Your app renders inside the shared Topolo shell \u2014 launcher, command palette, and account menu are already wired in.',",
5955
+ " actionLabel: 'Open workspace',",
5956
+ " onAction: onOpenWorkspace,",
5957
+ " },",
5958
+ " {",
5959
+ " id: 'discover-platform',",
5960
+ " title: 'Move between your Topolo apps',",
5961
+ " description:",
5962
+ " 'Use the launcher to switch between your Topolo apps, and the command palette (Cmd/Ctrl-K) to jump anywhere fast.',",
5963
+ " optional: true,",
5964
+ " },",
5965
+ " {",
5966
+ " id: 'build-feature',",
5967
+ " title: 'Build your first feature',",
5968
+ " description:",
5969
+ " 'Replace the starter card with your product surface, and gate write actions with service scopes before you ship.',",
5970
+ " },",
5971
+ " ];",
5972
+ " return (",
5973
+ ' <div className="topolo-starter-workspace">',
5974
+ " <GettingStarted",
5975
+ " authApiBaseUrl={`${AUTH_URL}/api`}",
5976
+ " organizationId={organizationId}",
5977
+ " serviceId={serviceId}",
5978
+ " getAccessToken={() => getAccessToken()}",
5979
+ " role={role || undefined}",
5980
+ " steps={steps}",
5981
+ " eyebrow={`Welcome to ${APP_NAME}`}",
5982
+ " title={`Get started with ${APP_NAME}`}",
5983
+ ' subtitle="A short checklist to orient you in your new Topolo workspace."',
5984
+ ' completeLabel="Mark done"',
5985
+ ' dismissLabel="Skip for now"',
5986
+ " completedContent={`You're all set \u2014 ${APP_NAME} is ready to build on.`}",
5987
+ " completeAction={{ label: 'Go to workspace', onClick: onOpenWorkspace }}",
5988
+ " onDismiss={onOpenWorkspace}",
5989
+ " />",
5990
+ " </div>",
5991
+ " );",
5992
+ "}",
5993
+ "",
5478
5994
  "function getRoute(): AppRoute {",
5479
5995
  " const pathname = window.location.pathname.replace(/\\/+$/, '') || '/';",
5480
5996
  " if (pathname === '/login') return { kind: 'login' };",
@@ -5627,6 +6143,208 @@ function developerAppTsx(application) {
5627
6143
  ""
5628
6144
  ].join("\n");
5629
6145
  }
6146
+ function webWorkerTs(application) {
6147
+ const serviceName = toTitle(application.name);
6148
+ return [
6149
+ "interface Env {",
6150
+ " API: { fetch(request: Request): Promise<Response> };",
6151
+ "}",
6152
+ "",
6153
+ `const SERVICE_NAME = '${escapeSingleQuoted(serviceName)}';`,
6154
+ "",
6155
+ "export default {",
6156
+ " async fetch(request: Request, env: Env): Promise<Response> {",
6157
+ " const url = new URL(request.url);",
6158
+ " if (url.pathname.startsWith('/api/')) {",
6159
+ " return env.API.fetch(request);",
6160
+ " }",
6161
+ " return Response.json({ ok: true, service: SERVICE_NAME }, { status: 200 });",
6162
+ " },",
6163
+ "};",
6164
+ ""
6165
+ ].join("\n");
6166
+ }
6167
+ function platformWebWranglerToml(application) {
6168
+ const slug = toSlug(application.id);
6169
+ const host = application.productionUrl ? new URL(application.productionUrl).host : `${slug}.topolo.app`;
6170
+ return [
6171
+ `name = "topolo-${slug}"`,
6172
+ 'main = "src/worker.ts"',
6173
+ `compatibility_date = "${CLOUDFLARE_COMPATIBILITY_DATE}"`,
6174
+ 'compatibility_flags = ["nodejs_compat"]',
6175
+ `account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
6176
+ "workers_dev = false",
6177
+ "",
6178
+ "[assets]",
6179
+ 'directory = "./dist"',
6180
+ 'binding = "ASSETS"',
6181
+ 'not_found_handling = "single-page-application"',
6182
+ 'run_worker_first = ["/api/*"]',
6183
+ "",
6184
+ "[observability]",
6185
+ "enabled = true",
6186
+ "",
6187
+ "[[routes]]",
6188
+ `pattern = "${host}"`,
6189
+ "custom_domain = true",
6190
+ "",
6191
+ "[vars]",
6192
+ `SERVICE_ID = "${application.serviceId ?? ""}"`,
6193
+ `VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
6194
+ "",
6195
+ "[[services]]",
6196
+ 'binding = "API"',
6197
+ `service = "topolo-${slug}-api"`,
6198
+ "",
6199
+ "[env.staging]",
6200
+ `name = "topolo-${slug}-staging"`,
6201
+ `account_id = "${STAGING_CLOUDFLARE_ACCOUNT_ID}"`,
6202
+ "workers_dev = true",
6203
+ "",
6204
+ "[env.staging.vars]",
6205
+ `SERVICE_ID = "${application.serviceId ?? ""}"`,
6206
+ `VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
6207
+ "",
6208
+ "[[env.staging.routes]]",
6209
+ `pattern = "${slug}.stg.topolo.us"`,
6210
+ "custom_domain = true",
6211
+ "",
6212
+ "[env.staging.assets]",
6213
+ 'directory = "./dist"',
6214
+ 'binding = "ASSETS"',
6215
+ 'not_found_handling = "single-page-application"',
6216
+ 'run_worker_first = ["/api/*"]',
6217
+ "",
6218
+ "[[env.staging.services]]",
6219
+ 'binding = "API"',
6220
+ `service = "topolo-${slug}-api-staging"`,
6221
+ "",
6222
+ "[env.production]",
6223
+ `name = "topolo-${slug}-production"`,
6224
+ `account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
6225
+ "workers_dev = false",
6226
+ "",
6227
+ "[env.production.vars]",
6228
+ `SERVICE_ID = "${application.serviceId ?? ""}"`,
6229
+ `VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
6230
+ "",
6231
+ "[[env.production.routes]]",
6232
+ `pattern = "${host}"`,
6233
+ "custom_domain = true",
6234
+ "",
6235
+ "[env.production.assets]",
6236
+ 'directory = "./dist"',
6237
+ 'binding = "ASSETS"',
6238
+ 'not_found_handling = "single-page-application"',
6239
+ 'run_worker_first = ["/api/*"]',
6240
+ "",
6241
+ "[[env.production.services]]",
6242
+ 'binding = "API"',
6243
+ `service = "topolo-${slug}-api-production"`,
6244
+ ""
6245
+ ].join("\n");
6246
+ }
6247
+ function platformApiWranglerToml(application, resources, profile) {
6248
+ const slug = toSlug(application.id);
6249
+ const resource = resourceConfig(application, profile);
6250
+ const lines = [
6251
+ `name = "topolo-${slug}-api"`,
6252
+ 'main = "src/worker.ts"',
6253
+ `compatibility_date = "${CLOUDFLARE_COMPATIBILITY_DATE}"`,
6254
+ 'compatibility_flags = ["nodejs_compat"]',
6255
+ `account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
6256
+ "workers_dev = true",
6257
+ "",
6258
+ "[observability]",
6259
+ "enabled = true",
6260
+ "",
6261
+ "[vars]",
6262
+ 'AUTH_BASE_URL = "https://auth.topolo.app"',
6263
+ 'NEXUS_GATEWAY_URL = "https://nexus-api.topolo.app"',
6264
+ `NEXUS_APP_ID = "${slug}"`,
6265
+ `SERVICE_ID = "${application.serviceId ?? ""}"`,
6266
+ `VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
6267
+ ""
6268
+ ];
6269
+ appendResourceWranglerBlocks(lines, resources, resource, "production");
6270
+ lines.push(
6271
+ "[env.staging]",
6272
+ `name = "topolo-${slug}-api-staging"`,
6273
+ `account_id = "${STAGING_CLOUDFLARE_ACCOUNT_ID}"`,
6274
+ "workers_dev = true",
6275
+ "",
6276
+ "[env.staging.vars]",
6277
+ 'AUTH_BASE_URL = "https://auth.stg.topolo.us"',
6278
+ 'NEXUS_GATEWAY_URL = "https://nexus-api.stg.topolo.us"',
6279
+ `NEXUS_APP_ID = "${slug}"`,
6280
+ `SERVICE_ID = "${application.serviceId ?? ""}"`,
6281
+ `VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
6282
+ ""
6283
+ );
6284
+ appendResourceWranglerBlocks(lines, resources, resource, "staging");
6285
+ lines.push(
6286
+ "[env.production]",
6287
+ `name = "topolo-${slug}-api-production"`,
6288
+ `account_id = "${CLOUDFLARE_ACCOUNT_ID}"`,
6289
+ "workers_dev = false",
6290
+ "",
6291
+ "[env.production.vars]",
6292
+ 'AUTH_BASE_URL = "https://auth.topolo.app"',
6293
+ 'NEXUS_GATEWAY_URL = "https://nexus-api.topolo.app"',
6294
+ `NEXUS_APP_ID = "${slug}"`,
6295
+ `SERVICE_ID = "${application.serviceId ?? ""}"`,
6296
+ `VITE_TOPOLO_SERVICE_ID = "${application.serviceId ?? ""}"`,
6297
+ ""
6298
+ );
6299
+ appendResourceWranglerBlocks(lines, resources, resource, "production-env");
6300
+ return lines.join("\n");
6301
+ }
6302
+ function appendResourceWranglerBlocks(lines, resources, resource, environment) {
6303
+ const prefix = environment === "staging" ? "env.staging." : environment === "production-env" ? "env.production." : "";
6304
+ const envName = environment === "staging" ? "Staging" : "Production";
6305
+ if (resources.d1) {
6306
+ lines.push(
6307
+ `[[${prefix}d1_databases]]`,
6308
+ `binding = "${resource.d1Binding}"`,
6309
+ `database_name = "${environment === "staging" ? resource.d1StagingName : resource.d1ProductionName}"`,
6310
+ 'database_id = "provision-after-scaffold"',
6311
+ 'migrations_dir = "migrations"',
6312
+ ""
6313
+ );
6314
+ }
6315
+ if (resources.r2) {
6316
+ lines.push(
6317
+ `[[${prefix}r2_buckets]]`,
6318
+ `binding = "${resource.r2Binding}"`,
6319
+ `bucket_name = "${environment === "staging" ? resource.r2StagingName : resource.r2ProductionName}"`,
6320
+ ""
6321
+ );
6322
+ }
6323
+ if (resources.queue) {
6324
+ lines.push(
6325
+ `[[${prefix}queues.producers]]`,
6326
+ `binding = "${resource.queueBinding}"`,
6327
+ `queue = "${environment === "staging" ? resource.queueStagingName : resource.queueProductionName}"`,
6328
+ "",
6329
+ `[[${prefix}queues.consumers]]`,
6330
+ `queue = "${environment === "staging" ? resource.queueStagingName : resource.queueProductionName}"`,
6331
+ ""
6332
+ );
6333
+ }
6334
+ if (resources.workflow) {
6335
+ lines.push(
6336
+ `[[${prefix}workflows]]`,
6337
+ `binding = "${resource.workflowBinding}"`,
6338
+ `name = "${environment === "staging" ? resource.workflowStagingName : resource.workflowProductionName}"`,
6339
+ `class_name = "${resource.workflowClassName}"`,
6340
+ ""
6341
+ );
6342
+ }
6343
+ if (resources.containerProcessing) {
6344
+ lines.push(`# ${envName} container metadata is declared in topolo.app.json and topolo.cloudcontrol.json.`);
6345
+ lines.push("");
6346
+ }
6347
+ }
5630
6348
  function wranglerToml(application, target, resources, profile) {
5631
6349
  const slug = toSlug(application.id);
5632
6350
  const workerName = target === "platform" ? `topolo-${slug}` : application.packageName;
@@ -5668,6 +6386,7 @@ function wranglerToml(application, target, resources, profile) {
5668
6386
  "",
5669
6387
  "[vars]",
5670
6388
  'AUTH_BASE_URL = "https://auth.topolo.app"',
6389
+ 'NEXUS_GATEWAY_URL = "https://nexus-api.topolo.app"',
5671
6390
  runtimeIdentityVar,
5672
6391
  ""
5673
6392
  );
@@ -5722,6 +6441,7 @@ function wranglerToml(application, target, resources, profile) {
5722
6441
  "",
5723
6442
  "[env.staging.vars]",
5724
6443
  'AUTH_BASE_URL = "https://auth.stg.topolo.us"',
6444
+ 'NEXUS_GATEWAY_URL = "https://nexus-api.stg.topolo.us"',
5725
6445
  runtimeIdentityVar,
5726
6446
  ""
5727
6447
  );
@@ -5776,8 +6496,8 @@ function platformWorkerTs(application, options) {
5776
6496
  const resource = resourceConfig(application, options.profile);
5777
6497
  const envBindings = [
5778
6498
  " AUTH_BASE_URL?: string;",
6499
+ " NEXUS_GATEWAY_URL?: string;",
5779
6500
  " SERVICE_ID?: string;",
5780
- " TOPOLO_SERVICE_API_KEY?: string;",
5781
6501
  ...options.resources.d1 ? [` ${resource.d1Binding}?: unknown;`] : [],
5782
6502
  ...options.resources.r2 ? [` ${resource.r2Binding}?: unknown;`] : [],
5783
6503
  ...options.resources.queue ? [` ${resource.queueBinding}?: { send(message: unknown): Promise<void> };`] : [],
@@ -5810,8 +6530,9 @@ function platformWorkerTs(application, options) {
5810
6530
  " }, { status: 200 });"
5811
6531
  ];
5812
6532
  return [
5813
- "import { createWidgetHandler } from '@topolo-io/widget-server';",
5814
- "import { createTopoloWorkerRuntime } from '@topolo-io/worker-runtime';",
6533
+ "import { Hono } from 'hono';",
6534
+ "import { createTopoloWidgetResponse, type TopoloWidgetDraft } from '@topolo/sdk';",
6535
+ "import { createTopoloWorkerRuntime, type TopoloRuntimeContext } from '@topolo-io/worker-runtime';",
5815
6536
  "import { SCAFFOLD_APP_ID, SCAFFOLD_APP_NAME, SCAFFOLD_SERVICE_SLUG, resolveRuntimeServiceId } from './service-identity';",
5816
6537
  ...options.resources.workflow ? ["import { WorkflowEntrypoint, type WorkflowEvent, type WorkflowStep } from 'cloudflare:workers';"] : [],
5817
6538
  "",
@@ -5819,18 +6540,8 @@ function platformWorkerTs(application, options) {
5819
6540
  ...envBindings,
5820
6541
  "}",
5821
6542
  "",
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: () => [",
6543
+ "function buildWidgets(): TopoloWidgetDraft[] {",
6544
+ " return [",
5834
6545
  " {",
5835
6546
  " type: 'quick-actions',",
5836
6547
  " actions: [",
@@ -5838,18 +6549,25 @@ function platformWorkerTs(application, options) {
5838
6549
  " { id: 'view-health', label: 'View health', link: '/api/health' },",
5839
6550
  " ],",
5840
6551
  " },",
5841
- " ],",
5842
- "});",
6552
+ " ];",
6553
+ "}",
5843
6554
  "",
5844
6555
  "const runtime = createTopoloWorkerRuntime<Env>({",
5845
6556
  " appName: SCAFFOLD_APP_NAME,",
5846
6557
  " serviceSlug: SCAFFOLD_SERVICE_SLUG,",
5847
6558
  " resolveServiceId: (env) => resolveRuntimeServiceId(env),",
5848
6559
  " authBaseUrl: (env) => env.AUTH_BASE_URL,",
5849
- " serviceApiKey: (env) => env.TOPOLO_SERVICE_API_KEY,",
6560
+ " usageMonitoring: {",
6561
+ " appId: SCAFFOLD_APP_ID,",
6562
+ " nexusGatewayUrl: (env) => env.NEXUS_GATEWAY_URL,",
6563
+ " },",
5850
6564
  "});",
5851
6565
  "",
5852
- "const handleFetch = runtime.worker(async (request, env, _ctx, rt): Promise<Response> => {",
6566
+ "async function handleRequest(",
6567
+ " _request: Request,",
6568
+ " env: Env,",
6569
+ " rt: TopoloRuntimeContext<Env>,",
6570
+ "): Promise<Response> {",
5853
6571
  " const url = rt.url;",
5854
6572
  "",
5855
6573
  " let serviceId: string;",
@@ -5872,9 +6590,15 @@ function platformWorkerTs(application, options) {
5872
6590
  " }, { status: 200 });",
5873
6591
  " }",
5874
6592
  "",
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);",
6593
+ " if (url.pathname === '/api/widget') {",
6594
+ " const auth = await rt.verifyAuth({ serviceId });",
6595
+ " if (!auth.ok) return rt.authError(auth);",
6596
+ " return rt.json(createTopoloWidgetResponse({",
6597
+ " serviceId,",
6598
+ " serviceName: SCAFFOLD_APP_NAME,",
6599
+ " widgets: buildWidgets(),",
6600
+ " }), { status: 200, headers: { 'Cache-Control': 'public, max-age=15' } });",
6601
+ " }",
5878
6602
  "",
5879
6603
  " const auth = await rt.verifyAuth({ serviceId });",
5880
6604
  "",
@@ -5887,10 +6611,21 @@ function platformWorkerTs(application, options) {
5887
6611
  " }",
5888
6612
  "",
5889
6613
  " return rt.notFound();",
6614
+ "}",
6615
+ "",
6616
+ "const api = new Hono<{ Bindings: Env & { __rt?: TopoloRuntimeContext<Env> } }>();",
6617
+ "api.all('*', (c) => {",
6618
+ " const rt = c.env.__rt;",
6619
+ " if (!rt) return Response.json({ error: 'runtime_context_unavailable' }, { status: 500 });",
6620
+ " return handleRequest(c.req.raw, c.env, rt);",
5890
6621
  "});",
5891
6622
  "",
5892
6623
  "export default {",
5893
- " fetch: handleFetch,",
6624
+ " fetch: runtime.worker((request, env, ctx, rt) => {",
6625
+ " const bindings = env as Env & { __rt?: TopoloRuntimeContext<Env> };",
6626
+ " bindings.__rt = rt;",
6627
+ " return api.fetch(request, bindings, ctx as ExecutionContext);",
6628
+ " }),",
5894
6629
  "};",
5895
6630
  "",
5896
6631
  "function errorMessage(error: unknown): string {",
@@ -5905,7 +6640,7 @@ function developerWorkerTs(application) {
5905
6640
  const serviceId = application.serviceId ?? application.id;
5906
6641
  const serviceName = application.name || "Topolo App";
5907
6642
  return [
5908
- "import { createWidgetHandler } from '@topolo-io/widget-server';",
6643
+ "import { createTopoloWidgetResponse, type TopoloWidgetDraft } from '@topolo/sdk';",
5909
6644
  "",
5910
6645
  "export interface Env {",
5911
6646
  " AUTH_BASE_URL?: string;",
@@ -5916,17 +6651,8 @@ function developerWorkerTs(application) {
5916
6651
  `const DEFAULT_SERVICE_ID = '${serviceId}';`,
5917
6652
  `const SERVICE_NAME = '${escapeSingleQuoted(serviceName)}';`,
5918
6653
  "",
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: () => [",
6654
+ "function buildWidgets(): TopoloWidgetDraft[] {",
6655
+ " return [",
5930
6656
  " {",
5931
6657
  " type: 'quick-actions',",
5932
6658
  " actions: [",
@@ -5934,8 +6660,8 @@ function developerWorkerTs(application) {
5934
6660
  " { id: 'view-health', label: 'View health', link: '/api/health' },",
5935
6661
  " ],",
5936
6662
  " },",
5937
- " ],",
5938
- "});",
6663
+ " ];",
6664
+ "}",
5939
6665
  "",
5940
6666
  "export default {",
5941
6667
  " async fetch(request: Request, env: Env): Promise<Response> {",
@@ -5945,8 +6671,15 @@ function developerWorkerTs(application) {
5945
6671
  " return json({ ok: true, service: env.SERVICE_ID ?? DEFAULT_SERVICE_ID }, { status: 200 }, request);",
5946
6672
  " }",
5947
6673
  "",
5948
- " // /api/widget delegates fully to @topolo-io/widget-server.",
5949
- " if (url.pathname === '/api/widget') return widgetHandler(request, env);",
6674
+ " if (url.pathname === '/api/widget') {",
6675
+ " const auth = await verifyTopoloRequest(request, env);",
6676
+ " if (!auth.ok) return json({ error: auth.error }, { status: auth.status }, request);",
6677
+ " return json(createTopoloWidgetResponse({",
6678
+ " serviceId: env.SERVICE_ID ?? DEFAULT_SERVICE_ID,",
6679
+ " serviceName: SERVICE_NAME,",
6680
+ " widgets: buildWidgets(),",
6681
+ " }), { status: 200, headers: { 'Cache-Control': 'public, max-age=15' } }, request);",
6682
+ " }",
5950
6683
  "",
5951
6684
  " const auth = await verifyTopoloRequest(request, env);",
5952
6685
  " if (!auth.ok) {",
@@ -6002,6 +6735,126 @@ function developerWorkerTs(application) {
6002
6735
  ""
6003
6736
  ].join("\n");
6004
6737
  }
6738
+ function topoloAppManifestJson(application, options) {
6739
+ const slug = toSlug(application.id);
6740
+ const resource = resourceConfig(application, options.profile);
6741
+ const targets = {};
6742
+ if (options.api) {
6743
+ const bindings = {};
6744
+ if (options.resources.d1) {
6745
+ bindings.d1 = [
6746
+ {
6747
+ binding: resource.d1Binding,
6748
+ staging: {
6749
+ databaseName: resource.d1StagingName,
6750
+ databaseId: "provision-after-scaffold"
6751
+ },
6752
+ production: {
6753
+ databaseName: resource.d1ProductionName,
6754
+ databaseId: "provision-after-scaffold"
6755
+ },
6756
+ migrationsDir: "migrations"
6757
+ }
6758
+ ];
6759
+ }
6760
+ if (options.resources.r2) {
6761
+ bindings.r2 = [
6762
+ {
6763
+ binding: resource.r2Binding,
6764
+ staging: { bucketName: resource.r2StagingName },
6765
+ production: { bucketName: resource.r2ProductionName }
6766
+ }
6767
+ ];
6768
+ }
6769
+ if (options.resources.queue) {
6770
+ bindings.queues = [
6771
+ {
6772
+ binding: resource.queueBinding,
6773
+ staging: { queueName: resource.queueStagingName },
6774
+ production: { queueName: resource.queueProductionName },
6775
+ consumer: true
6776
+ }
6777
+ ];
6778
+ }
6779
+ if (options.resources.workflow) {
6780
+ bindings.workflows = [
6781
+ {
6782
+ binding: resource.workflowBinding,
6783
+ className: resource.workflowClassName,
6784
+ staging: { name: resource.workflowStagingName },
6785
+ production: { name: resource.workflowProductionName }
6786
+ }
6787
+ ];
6788
+ }
6789
+ targets.api = {
6790
+ kind: "worker",
6791
+ dir: "apps/api",
6792
+ wranglerName: `topolo-${slug}-api`,
6793
+ main: "src/worker.ts",
6794
+ compatibilityFlags: ["nodejs_compat"],
6795
+ healthcheckPath: "/api/health",
6796
+ vars: {
6797
+ AUTH_BASE_URL: {
6798
+ staging: "https://auth.stg.topolo.us",
6799
+ production: "https://auth.topolo.app"
6800
+ }
6801
+ },
6802
+ ...Object.keys(bindings).length ? { bindings } : {}
6803
+ };
6804
+ }
6805
+ targets.web = {
6806
+ kind: "worker",
6807
+ dir: "apps/web",
6808
+ wranglerName: `topolo-${slug}`,
6809
+ subdomain: slug,
6810
+ main: "src/worker.ts",
6811
+ workersDev: false,
6812
+ assets: {
6813
+ directory: "./dist",
6814
+ binding: "ASSETS",
6815
+ notFoundHandling: "single-page-application",
6816
+ runWorkerFirst: ["/api/*"]
6817
+ },
6818
+ compatibilityFlags: ["nodejs_compat"],
6819
+ buildCommand: {
6820
+ staging: "cd ../.. && DEPLOY_ENVIRONMENT=staging VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build",
6821
+ production: "cd ../.. && DEPLOY_ENVIRONMENT=production VITE_TOPOLO_AUTH_URL=https://auth.topolo.app npm run build"
6822
+ },
6823
+ healthcheckPath: options.api ? "/api/health" : "/",
6824
+ ...options.api ? {
6825
+ bindings: {
6826
+ services: [
6827
+ {
6828
+ binding: "API",
6829
+ service: {
6830
+ staging: `topolo-${slug}-api-staging`,
6831
+ production: `topolo-${slug}-api-production`
6832
+ }
6833
+ }
6834
+ ]
6835
+ }
6836
+ } : {}
6837
+ };
6838
+ return `${json({
6839
+ $schema: "./../../topolo-platform/tooling/app-manifest-schema.json",
6840
+ name: `topolo-${slug}`,
6841
+ serviceId: application.serviceId,
6842
+ primaryTarget: "web",
6843
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
6844
+ environments: {
6845
+ staging: {
6846
+ accountId: STAGING_CLOUDFLARE_ACCOUNT_ID,
6847
+ domain: "stg.topolo.us"
6848
+ },
6849
+ production: {
6850
+ accountId: CLOUDFLARE_ACCOUNT_ID,
6851
+ domain: "topolo.app"
6852
+ }
6853
+ },
6854
+ targets
6855
+ })}
6856
+ `;
6857
+ }
6005
6858
  function cloudcontrolJson(application, options) {
6006
6859
  const slug = toSlug(application.id);
6007
6860
  const appPath = firstPartyAppPath(application.appDir, options.audience);
@@ -6110,16 +6963,16 @@ function cloudcontrolJson(application, options) {
6110
6963
  {
6111
6964
  kind: "worker",
6112
6965
  environment: "production",
6113
- target_name: `topolo-${slug}`,
6966
+ target_name: `topolo-${slug}-api-production`,
6114
6967
  cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
6115
- config_path: "wrangler.toml",
6116
- build_command: "npm run build",
6117
- output_dir: "dist",
6968
+ config_path: "apps/api/wrangler.toml",
6969
+ workdir: "apps/api",
6970
+ build_command: null,
6971
+ output_dir: null,
6118
6972
  production_branch: "main",
6119
6973
  healthcheck_path: "/api/health",
6120
- domains: [host],
6121
- routes: [host],
6122
- api_url: application.productionUrl,
6974
+ domains: [],
6975
+ routes: [],
6123
6976
  service_slug: serviceSlug
6124
6977
  },
6125
6978
  {
@@ -6127,36 +6980,73 @@ function cloudcontrolJson(application, options) {
6127
6980
  environment: "staging",
6128
6981
  target_name: `topolo-${slug}-staging`,
6129
6982
  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",
6983
+ config_path: "apps/web/wrangler.toml",
6984
+ workdir: "apps/web",
6985
+ build_command: "DEPLOY_ENVIRONMENT=staging VITE_TOPOLO_AUTH_URL=https://auth.stg.topolo.us npm run build",
6986
+ output_dir: null,
6987
+ production_branch: "staging",
6134
6988
  healthcheck_path: "/api/health",
6135
6989
  domains: [stagingHost],
6136
6990
  routes: [stagingHost],
6137
6991
  api_url: `https://${stagingHost}`,
6992
+ service_slug: serviceSlug,
6993
+ sdk_service_id: application.id,
6994
+ agent_callable: true
6995
+ },
6996
+ {
6997
+ kind: "worker",
6998
+ environment: "staging",
6999
+ target_name: `topolo-${slug}-api-staging`,
7000
+ cloudflare_account_id: STAGING_CLOUDFLARE_ACCOUNT_ID,
7001
+ config_path: "apps/api/wrangler.toml",
7002
+ workdir: "apps/api",
7003
+ build_command: null,
7004
+ output_dir: null,
7005
+ production_branch: "staging",
7006
+ healthcheck_path: "/api/health",
7007
+ domains: [],
7008
+ routes: [],
6138
7009
  service_slug: serviceSlug
7010
+ },
7011
+ {
7012
+ kind: "worker",
7013
+ environment: "production",
7014
+ target_name: `topolo-${slug}-production`,
7015
+ cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
7016
+ config_path: "apps/web/wrangler.toml",
7017
+ workdir: "apps/web",
7018
+ build_command: "DEPLOY_ENVIRONMENT=production VITE_TOPOLO_AUTH_URL=https://auth.topolo.app npm run build",
7019
+ output_dir: null,
7020
+ production_branch: "main",
7021
+ healthcheck_path: "/api/health",
7022
+ domains: [host],
7023
+ routes: [host],
7024
+ api_url: application.productionUrl,
7025
+ service_slug: serviceSlug,
7026
+ sdk_service_id: application.id,
7027
+ agent_callable: true
6139
7028
  }
6140
7029
  ] : [
6141
7030
  {
6142
- kind: "pages",
7031
+ kind: "worker",
6143
7032
  environment: "production",
6144
7033
  target_name: `topolo-${slug}`,
6145
7034
  cloudflare_account_id: CLOUDFLARE_ACCOUNT_ID,
6146
- config_path: null,
7035
+ config_path: "wrangler.toml",
6147
7036
  build_command: "npm run build",
6148
7037
  output_dir: "dist",
6149
7038
  production_branch: "main",
6150
- healthcheck_path: null,
7039
+ healthcheck_path: "/",
6151
7040
  domains: [host],
6152
7041
  routes: [host],
6153
- api_url: null
7042
+ api_url: application.productionUrl,
7043
+ agent_callable: false
6154
7044
  }
6155
7045
  ];
6156
7046
  return `${json({
6157
7047
  name: `topolo-${slug}`,
6158
7048
  description: `Canonical documentation for ${toTitle(application.name)} lives in Apps/system/TopoloDocs.`,
6159
- deploy_method: options.api ? "wrangler" : "cloudflare_pages",
7049
+ deploy_method: "wrangler",
6160
7050
  ci_provider: "github_actions",
6161
7051
  repo_identity: {
6162
7052
  provider: "github",
@@ -6236,7 +7126,7 @@ function systemJson(application, options) {
6236
7126
  visibility: "internal",
6237
7127
  api_contract: {
6238
7128
  type: options.api ? "curated" : "none",
6239
- source: options.api ? `${appPath}/src/worker.ts` : `${appPath}/src/App.tsx`,
7129
+ source: options.api ? `${appPath}/apps/api/src/worker.ts` : `${appPath}/apps/web/src/App.tsx`,
6240
7130
  notes: apiNotes
6241
7131
  },
6242
7132
  primary_hosts: application.productionUrl ? [application.productionUrl] : [],
@@ -6325,17 +7215,19 @@ function internalDocs(application, options) {
6325
7215
  "",
6326
7216
  "## Architecture",
6327
7217
  "",
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.`,
7218
+ "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.",
7219
+ 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
7220
  "",
6331
7221
  "## Runtime Surfaces",
6332
7222
  "",
6333
- `- \`${appPath}/src/App.tsx\``,
6334
- `- \`${appPath}/src/service-identity.ts\``,
7223
+ `- \`${appPath}/apps/web/src/App.tsx\``,
7224
+ `- \`${appPath}/apps/web/src/service-identity.ts\``,
7225
+ `- \`${appPath}/apps/web/wrangler.toml\``,
6335
7226
  "- public landing route: `/`",
6336
7227
  "- public sign-in route: `/login`",
6337
7228
  "- auth callback route: `/auth/callback`",
6338
- ...options.api ? [`- \`${appPath}/src/worker.ts\``] : [],
7229
+ ...options.api ? [`- \`${appPath}/apps/api/src/worker.ts\``, `- \`${appPath}/apps/api/wrangler.toml\``] : [],
7230
+ `- \`${appPath}/topolo.app.json\``,
6339
7231
  `- \`${appPath}/topolo.cloudcontrol.json\``,
6340
7232
  ...options.api && application.serviceId ? [`- \`Apps/system/TopoloAuth/catalog/service-manifests/${application.serviceId}.json\``] : [],
6341
7233
  "",
@@ -6494,6 +7386,12 @@ function serviceManifestJson(application, options) {
6494
7386
  name: "workspace:write",
6495
7387
  description: `Manage ${title} ${resourceLabel} data`,
6496
7388
  resourcePattern: null
7389
+ },
7390
+ {
7391
+ id: `perm_${permissionPrefix}_usage_write`,
7392
+ name: "usage:write",
7393
+ description: `Record Nexus usage events for ${title}`,
7394
+ resourcePattern: null
6497
7395
  }
6498
7396
  ],
6499
7397
  apiKeyScopes: [
@@ -6508,6 +7406,12 @@ function serviceManifestJson(application, options) {
6508
7406
  name: "workspace.write",
6509
7407
  description: `Manage ${title} ${resourceLabel} data`,
6510
7408
  resourcePattern: null
7409
+ },
7410
+ {
7411
+ id: `aks_${permissionPrefix}_usage_write`,
7412
+ name: "usage.write",
7413
+ description: `Record Nexus usage events for ${title}`,
7414
+ resourcePattern: null
6511
7415
  }
6512
7416
  ],
6513
7417
  roleBundles: {
@@ -6948,6 +7852,15 @@ async function applyProvisionedServiceId(manifestPath, serviceId) {
6948
7852
  // src/commands/apps.ts
6949
7853
  import { join as join3 } from "path";
6950
7854
  var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
7855
+ var CLOUDCONTROL_LOCAL_APPLICATION = {
7856
+ id: "cloudcontrol",
7857
+ appDir: "TopoloCloudControl",
7858
+ name: "topolo-cloudcontrol",
7859
+ packageName: "topolo-cloudcontrol",
7860
+ productionUrl: null,
7861
+ services: [],
7862
+ deployTargets: []
7863
+ };
6951
7864
  function registerAppsCommand(program2) {
6952
7865
  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) => {
6953
7866
  const mode = resolveOutputMode(opts.json);
@@ -6958,7 +7871,7 @@ function registerAppsCommand(program2) {
6958
7871
  fail(err, mode);
6959
7872
  }
6960
7873
  });
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) => {
7874
+ apps.command("get <application>").description("Show one Topolo application. Run `topolo apps --json` to discover application IDs.").option("--json", "Output JSON", true).option("--no-json", "Output human-readable details").action((application, opts) => {
6962
7875
  const mode = resolveOutputMode(opts.json);
6963
7876
  try {
6964
7877
  const app = resolveApplication(application);
@@ -7087,7 +8000,7 @@ function applicationRows() {
7087
8000
  function resolveApplication(application) {
7088
8001
  const id = application.trim().toLowerCase();
7089
8002
  if (!APPLICATION_IDS.includes(id)) {
7090
- throw new Error(`Unknown application "${application}". Known applications: ${APPLICATION_IDS.join(", ")}`);
8003
+ throw new Error(`Unknown application "${application}". ${formatKnownValues(APPLICATION_IDS, "applications")}`);
7091
8004
  }
7092
8005
  return APPLICATIONS[id];
7093
8006
  }
@@ -7109,7 +8022,12 @@ function applicationRequirementsPayload(application) {
7109
8022
  };
7110
8023
  }
7111
8024
  function applicationAuditPayload(application, options = {}) {
7112
- const report = application ? auditAllApplicationRequirements([resolveApplication(application).id]) : auditAllApplicationRequirements();
8025
+ const requestedId = application?.trim().toLowerCase();
8026
+ const report = requestedId === "cloudcontrol" && options.local ? {
8027
+ version: APPLICATION_REQUIREMENTS_VERSION,
8028
+ applications: [auditApplicationRequirements(CLOUDCONTROL_LOCAL_APPLICATION)],
8029
+ migrationQueue: []
8030
+ } : application ? auditAllApplicationRequirements([resolveApplication(application).id]) : auditAllApplicationRequirements();
7113
8031
  if (!options.local) return report;
7114
8032
  return enrichApplicationAuditWithLocalEvidence(report, { repoRoot: options.repoRoot });
7115
8033
  }
@@ -7233,6 +8151,7 @@ function formatRows(rows) {
7233
8151
  return lines.join("\n");
7234
8152
  }
7235
8153
  function formatApplication(application) {
8154
+ const deployTargets = application.deployTargets;
7236
8155
  const lines = [
7237
8156
  `ID: ${application.id}`,
7238
8157
  `Name: ${application.name}`,
@@ -7242,10 +8161,10 @@ function formatApplication(application) {
7242
8161
  `Services: ${application.services.length ? application.services.join(", ") : "-"}`,
7243
8162
  "Targets:"
7244
8163
  ];
7245
- if (application.deployTargets.length === 0) {
8164
+ if (deployTargets.length === 0) {
7246
8165
  lines.push(" - none");
7247
8166
  } else {
7248
- for (const target of application.deployTargets) {
8167
+ for (const target of deployTargets) {
7249
8168
  const parts = [`${target.name} (${target.kind})`];
7250
8169
  if (target.serviceId) parts.push(`service=${target.serviceId}`);
7251
8170
  if (target.apiUrl) parts.push(`api=${target.apiUrl}`);
@@ -7406,6 +8325,12 @@ function normalizeFailOn(value) {
7406
8325
  }
7407
8326
  throw new Error("Invalid --fail-on value. Use missing, needs_review, or partial.");
7408
8327
  }
8328
+ function formatKnownValues(values, label) {
8329
+ const sorted = [...values].sort();
8330
+ const preview = sorted.slice(0, 25).join(", ");
8331
+ const suffix = sorted.length > 25 ? `, ... +${sorted.length - 25} more` : "";
8332
+ return `Run discovery for the full ${label} list. Known snapshot sample: ${preview}${suffix}`;
8333
+ }
7409
8334
  function statusesAtOrWorse(threshold) {
7410
8335
  if (threshold === "missing") return ["missing"];
7411
8336
  if (threshold === "needs_review") return ["missing", "needs_review"];